| // 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 | |
| } |