|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package text | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "fmt" | 
|  | "math" | 
|  | "strconv" | 
|  | "strings" | 
|  |  | 
|  | "google.golang.org/protobuf/internal/flags" | 
|  | ) | 
|  |  | 
|  | // Kind represents a token kind expressible in the textproto format. | 
|  | type Kind uint8 | 
|  |  | 
|  | // Kind values. | 
|  | const ( | 
|  | Invalid Kind = iota | 
|  | EOF | 
|  | Name   // Name indicates the field name. | 
|  | Scalar // Scalar are scalar values, e.g. "string", 47, ENUM_LITERAL, true. | 
|  | MessageOpen | 
|  | MessageClose | 
|  | ListOpen | 
|  | ListClose | 
|  |  | 
|  | // comma and semi-colon are only for parsing in between values and should not be exposed. | 
|  | comma | 
|  | semicolon | 
|  |  | 
|  | // bof indicates beginning of file, which is the default token | 
|  | // kind at the beginning of parsing. | 
|  | bof = Invalid | 
|  | ) | 
|  |  | 
|  | func (t Kind) String() string { | 
|  | switch t { | 
|  | case Invalid: | 
|  | return "<invalid>" | 
|  | case EOF: | 
|  | return "eof" | 
|  | case Scalar: | 
|  | return "scalar" | 
|  | case Name: | 
|  | return "name" | 
|  | case MessageOpen: | 
|  | return "{" | 
|  | case MessageClose: | 
|  | return "}" | 
|  | case ListOpen: | 
|  | return "[" | 
|  | case ListClose: | 
|  | return "]" | 
|  | case comma: | 
|  | return "," | 
|  | case semicolon: | 
|  | return ";" | 
|  | default: | 
|  | return fmt.Sprintf("<invalid:%v>", uint8(t)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // NameKind represents different types of field names. | 
|  | type NameKind uint8 | 
|  |  | 
|  | // NameKind values. | 
|  | const ( | 
|  | IdentName NameKind = iota + 1 | 
|  | TypeName | 
|  | FieldNumber | 
|  | ) | 
|  |  | 
|  | func (t NameKind) String() string { | 
|  | switch t { | 
|  | case IdentName: | 
|  | return "IdentName" | 
|  | case TypeName: | 
|  | return "TypeName" | 
|  | case FieldNumber: | 
|  | return "FieldNumber" | 
|  | default: | 
|  | return fmt.Sprintf("<invalid:%v>", uint8(t)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Bit mask in Token.attrs to indicate if a Name token is followed by the | 
|  | // separator char ':'. The field name separator char is optional for message | 
|  | // field or repeated message field, but required for all other types. Decoder | 
|  | // simply indicates whether a Name token is followed by separator or not.  It is | 
|  | // up to the prototext package to validate. | 
|  | const hasSeparator = 1 << 7 | 
|  |  | 
|  | // Scalar value types. | 
|  | const ( | 
|  | numberValue = iota + 1 | 
|  | stringValue | 
|  | literalValue | 
|  | ) | 
|  |  | 
|  | // Bit mask in Token.numAttrs to indicate that the number is a negative. | 
|  | const isNegative = 1 << 7 | 
|  |  | 
|  | // Token provides a parsed token kind and value. Values are provided by the | 
|  | // different accessor methods. | 
|  | type Token struct { | 
|  | // Kind of the Token object. | 
|  | kind Kind | 
|  | // attrs contains metadata for the following Kinds: | 
|  | // Name: hasSeparator bit and one of NameKind. | 
|  | // Scalar: one of numberValue, stringValue, literalValue. | 
|  | attrs uint8 | 
|  | // numAttrs contains metadata for numberValue: | 
|  | // - highest bit is whether negative or positive. | 
|  | // - lower bits indicate one of numDec, numHex, numOct, numFloat. | 
|  | numAttrs uint8 | 
|  | // pos provides the position of the token in the original input. | 
|  | pos int | 
|  | // raw bytes of the serialized token. | 
|  | // This is a subslice into the original input. | 
|  | raw []byte | 
|  | // str contains parsed string for the following: | 
|  | // - stringValue of Scalar kind | 
|  | // - numberValue of Scalar kind | 
|  | // - TypeName of Name kind | 
|  | str string | 
|  | } | 
|  |  | 
|  | // Kind returns the token kind. | 
|  | func (t Token) Kind() Kind { | 
|  | return t.kind | 
|  | } | 
|  |  | 
|  | // RawString returns the read value in string. | 
|  | func (t Token) RawString() string { | 
|  | return string(t.raw) | 
|  | } | 
|  |  | 
|  | // Pos returns the token position from the input. | 
|  | func (t Token) Pos() int { | 
|  | return t.pos | 
|  | } | 
|  |  | 
|  | // NameKind returns IdentName, TypeName or FieldNumber. | 
|  | // It panics if type is not Name. | 
|  | func (t Token) NameKind() NameKind { | 
|  | if t.kind == Name { | 
|  | return NameKind(t.attrs &^ hasSeparator) | 
|  | } | 
|  | panic(fmt.Sprintf("Token is not a Name type: %s", t.kind)) | 
|  | } | 
|  |  | 
|  | // HasSeparator returns true if the field name is followed by the separator char | 
|  | // ':', else false. It panics if type is not Name. | 
|  | func (t Token) HasSeparator() bool { | 
|  | if t.kind == Name { | 
|  | return t.attrs&hasSeparator != 0 | 
|  | } | 
|  | panic(fmt.Sprintf("Token is not a Name type: %s", t.kind)) | 
|  | } | 
|  |  | 
|  | // IdentName returns the value for IdentName type. | 
|  | func (t Token) IdentName() string { | 
|  | if t.kind == Name && t.attrs&uint8(IdentName) != 0 { | 
|  | return string(t.raw) | 
|  | } | 
|  | panic(fmt.Sprintf("Token is not an IdentName: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) | 
|  | } | 
|  |  | 
|  | // TypeName returns the value for TypeName type. | 
|  | func (t Token) TypeName() string { | 
|  | if t.kind == Name && t.attrs&uint8(TypeName) != 0 { | 
|  | return t.str | 
|  | } | 
|  | panic(fmt.Sprintf("Token is not a TypeName: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) | 
|  | } | 
|  |  | 
|  | // FieldNumber returns the value for FieldNumber type. It returns a | 
|  | // non-negative int32 value. Caller will still need to validate for the correct | 
|  | // field number range. | 
|  | func (t Token) FieldNumber() int32 { | 
|  | if t.kind != Name || t.attrs&uint8(FieldNumber) == 0 { | 
|  | panic(fmt.Sprintf("Token is not a FieldNumber: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) | 
|  | } | 
|  | // Following should not return an error as it had already been called right | 
|  | // before this Token was constructed. | 
|  | num, _ := strconv.ParseInt(string(t.raw), 10, 32) | 
|  | return int32(num) | 
|  | } | 
|  |  | 
|  | // String returns the string value for a Scalar type. | 
|  | func (t Token) String() (string, bool) { | 
|  | if t.kind != Scalar || t.attrs != stringValue { | 
|  | return "", false | 
|  | } | 
|  | return t.str, true | 
|  | } | 
|  |  | 
|  | // Enum returns the literal value for a Scalar type for use as enum literals. | 
|  | func (t Token) Enum() (string, bool) { | 
|  | if t.kind != Scalar || t.attrs != literalValue || (len(t.raw) > 0 && t.raw[0] == '-') { | 
|  | return "", false | 
|  | } | 
|  | return string(t.raw), true | 
|  | } | 
|  |  | 
|  | // Bool returns the bool value for a Scalar type. | 
|  | func (t Token) Bool() (bool, bool) { | 
|  | if t.kind != Scalar { | 
|  | return false, false | 
|  | } | 
|  | switch t.attrs { | 
|  | case literalValue: | 
|  | if b, ok := boolLits[string(t.raw)]; ok { | 
|  | return b, true | 
|  | } | 
|  | case numberValue: | 
|  | // Unsigned integer representation of 0 or 1 is permitted: 00, 0x0, 01, | 
|  | // 0x1, etc. | 
|  | n, err := strconv.ParseUint(t.str, 0, 64) | 
|  | if err == nil { | 
|  | switch n { | 
|  | case 0: | 
|  | return false, true | 
|  | case 1: | 
|  | return true, true | 
|  | } | 
|  | } | 
|  | } | 
|  | return false, false | 
|  | } | 
|  |  | 
|  | // These exact boolean literals are the ones supported in C++. | 
|  | var boolLits = map[string]bool{ | 
|  | "t":     true, | 
|  | "true":  true, | 
|  | "True":  true, | 
|  | "f":     false, | 
|  | "false": false, | 
|  | "False": false, | 
|  | } | 
|  |  | 
|  | // Uint64 returns the uint64 value for a Scalar type. | 
|  | func (t Token) Uint64() (uint64, bool) { | 
|  | if t.kind != Scalar || t.attrs != numberValue || | 
|  | t.numAttrs&isNegative > 0 || t.numAttrs&numFloat > 0 { | 
|  | return 0, false | 
|  | } | 
|  | n, err := strconv.ParseUint(t.str, 0, 64) | 
|  | if err != nil { | 
|  | return 0, false | 
|  | } | 
|  | return n, true | 
|  | } | 
|  |  | 
|  | // Uint32 returns the uint32 value for a Scalar type. | 
|  | func (t Token) Uint32() (uint32, bool) { | 
|  | if t.kind != Scalar || t.attrs != numberValue || | 
|  | t.numAttrs&isNegative > 0 || t.numAttrs&numFloat > 0 { | 
|  | return 0, false | 
|  | } | 
|  | n, err := strconv.ParseUint(t.str, 0, 32) | 
|  | if err != nil { | 
|  | return 0, false | 
|  | } | 
|  | return uint32(n), true | 
|  | } | 
|  |  | 
|  | // Int64 returns the int64 value for a Scalar type. | 
|  | func (t Token) Int64() (int64, bool) { | 
|  | if t.kind != Scalar || t.attrs != numberValue || t.numAttrs&numFloat > 0 { | 
|  | return 0, false | 
|  | } | 
|  | if n, err := strconv.ParseInt(t.str, 0, 64); err == nil { | 
|  | return n, true | 
|  | } | 
|  | // C++ accepts large positive hex numbers as negative values. | 
|  | // This feature is here for proto1 backwards compatibility purposes. | 
|  | if flags.ProtoLegacy && (t.numAttrs == numHex) { | 
|  | if n, err := strconv.ParseUint(t.str, 0, 64); err == nil { | 
|  | return int64(n), true | 
|  | } | 
|  | } | 
|  | return 0, false | 
|  | } | 
|  |  | 
|  | // Int32 returns the int32 value for a Scalar type. | 
|  | func (t Token) Int32() (int32, bool) { | 
|  | if t.kind != Scalar || t.attrs != numberValue || t.numAttrs&numFloat > 0 { | 
|  | return 0, false | 
|  | } | 
|  | if n, err := strconv.ParseInt(t.str, 0, 32); err == nil { | 
|  | return int32(n), true | 
|  | } | 
|  | // C++ accepts large positive hex numbers as negative values. | 
|  | // This feature is here for proto1 backwards compatibility purposes. | 
|  | if flags.ProtoLegacy && (t.numAttrs == numHex) { | 
|  | if n, err := strconv.ParseUint(t.str, 0, 32); err == nil { | 
|  | return int32(n), true | 
|  | } | 
|  | } | 
|  | return 0, false | 
|  | } | 
|  |  | 
|  | // Float64 returns the float64 value for a Scalar type. | 
|  | func (t Token) Float64() (float64, bool) { | 
|  | if t.kind != Scalar { | 
|  | return 0, false | 
|  | } | 
|  | switch t.attrs { | 
|  | case literalValue: | 
|  | if f, ok := floatLits[strings.ToLower(string(t.raw))]; ok { | 
|  | return f, true | 
|  | } | 
|  | case numberValue: | 
|  | n, err := strconv.ParseFloat(t.str, 64) | 
|  | if err == nil { | 
|  | return n, true | 
|  | } | 
|  | nerr := err.(*strconv.NumError) | 
|  | if nerr.Err == strconv.ErrRange { | 
|  | return n, true | 
|  | } | 
|  | } | 
|  | return 0, false | 
|  | } | 
|  |  | 
|  | // Float32 returns the float32 value for a Scalar type. | 
|  | func (t Token) Float32() (float32, bool) { | 
|  | if t.kind != Scalar { | 
|  | return 0, false | 
|  | } | 
|  | switch t.attrs { | 
|  | case literalValue: | 
|  | if f, ok := floatLits[strings.ToLower(string(t.raw))]; ok { | 
|  | return float32(f), true | 
|  | } | 
|  | case numberValue: | 
|  | n, err := strconv.ParseFloat(t.str, 64) | 
|  | if err == nil { | 
|  | // Overflows are treated as (-)infinity. | 
|  | return float32(n), true | 
|  | } | 
|  | nerr := err.(*strconv.NumError) | 
|  | if nerr.Err == strconv.ErrRange { | 
|  | return float32(n), true | 
|  | } | 
|  | } | 
|  | return 0, false | 
|  | } | 
|  |  | 
|  | // These are the supported float literals which C++ permits case-insensitive | 
|  | // variants of these. | 
|  | var floatLits = map[string]float64{ | 
|  | "nan":       math.NaN(), | 
|  | "inf":       math.Inf(1), | 
|  | "infinity":  math.Inf(1), | 
|  | "-inf":      math.Inf(-1), | 
|  | "-infinity": math.Inf(-1), | 
|  | } | 
|  |  | 
|  | // TokenEquals returns true if given Tokens are equal, else false. | 
|  | func TokenEquals(x, y Token) bool { | 
|  | return x.kind == y.kind && | 
|  | x.attrs == y.attrs && | 
|  | x.numAttrs == y.numAttrs && | 
|  | x.pos == y.pos && | 
|  | bytes.Equal(x.raw, y.raw) && | 
|  | x.str == y.str | 
|  | } |