1*1c12ee1eSDan Willemsen// Copyright 2018 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsenpackage text 6*1c12ee1eSDan Willemsen 7*1c12ee1eSDan Willemsenimport ( 8*1c12ee1eSDan Willemsen "bytes" 9*1c12ee1eSDan Willemsen "fmt" 10*1c12ee1eSDan Willemsen "math" 11*1c12ee1eSDan Willemsen "strconv" 12*1c12ee1eSDan Willemsen "strings" 13*1c12ee1eSDan Willemsen 14*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/flags" 15*1c12ee1eSDan Willemsen) 16*1c12ee1eSDan Willemsen 17*1c12ee1eSDan Willemsen// Kind represents a token kind expressible in the textproto format. 18*1c12ee1eSDan Willemsentype Kind uint8 19*1c12ee1eSDan Willemsen 20*1c12ee1eSDan Willemsen// Kind values. 21*1c12ee1eSDan Willemsenconst ( 22*1c12ee1eSDan Willemsen Invalid Kind = iota 23*1c12ee1eSDan Willemsen EOF 24*1c12ee1eSDan Willemsen Name // Name indicates the field name. 25*1c12ee1eSDan Willemsen Scalar // Scalar are scalar values, e.g. "string", 47, ENUM_LITERAL, true. 26*1c12ee1eSDan Willemsen MessageOpen 27*1c12ee1eSDan Willemsen MessageClose 28*1c12ee1eSDan Willemsen ListOpen 29*1c12ee1eSDan Willemsen ListClose 30*1c12ee1eSDan Willemsen 31*1c12ee1eSDan Willemsen // comma and semi-colon are only for parsing in between values and should not be exposed. 32*1c12ee1eSDan Willemsen comma 33*1c12ee1eSDan Willemsen semicolon 34*1c12ee1eSDan Willemsen 35*1c12ee1eSDan Willemsen // bof indicates beginning of file, which is the default token 36*1c12ee1eSDan Willemsen // kind at the beginning of parsing. 37*1c12ee1eSDan Willemsen bof = Invalid 38*1c12ee1eSDan Willemsen) 39*1c12ee1eSDan Willemsen 40*1c12ee1eSDan Willemsenfunc (t Kind) String() string { 41*1c12ee1eSDan Willemsen switch t { 42*1c12ee1eSDan Willemsen case Invalid: 43*1c12ee1eSDan Willemsen return "<invalid>" 44*1c12ee1eSDan Willemsen case EOF: 45*1c12ee1eSDan Willemsen return "eof" 46*1c12ee1eSDan Willemsen case Scalar: 47*1c12ee1eSDan Willemsen return "scalar" 48*1c12ee1eSDan Willemsen case Name: 49*1c12ee1eSDan Willemsen return "name" 50*1c12ee1eSDan Willemsen case MessageOpen: 51*1c12ee1eSDan Willemsen return "{" 52*1c12ee1eSDan Willemsen case MessageClose: 53*1c12ee1eSDan Willemsen return "}" 54*1c12ee1eSDan Willemsen case ListOpen: 55*1c12ee1eSDan Willemsen return "[" 56*1c12ee1eSDan Willemsen case ListClose: 57*1c12ee1eSDan Willemsen return "]" 58*1c12ee1eSDan Willemsen case comma: 59*1c12ee1eSDan Willemsen return "," 60*1c12ee1eSDan Willemsen case semicolon: 61*1c12ee1eSDan Willemsen return ";" 62*1c12ee1eSDan Willemsen default: 63*1c12ee1eSDan Willemsen return fmt.Sprintf("<invalid:%v>", uint8(t)) 64*1c12ee1eSDan Willemsen } 65*1c12ee1eSDan Willemsen} 66*1c12ee1eSDan Willemsen 67*1c12ee1eSDan Willemsen// NameKind represents different types of field names. 68*1c12ee1eSDan Willemsentype NameKind uint8 69*1c12ee1eSDan Willemsen 70*1c12ee1eSDan Willemsen// NameKind values. 71*1c12ee1eSDan Willemsenconst ( 72*1c12ee1eSDan Willemsen IdentName NameKind = iota + 1 73*1c12ee1eSDan Willemsen TypeName 74*1c12ee1eSDan Willemsen FieldNumber 75*1c12ee1eSDan Willemsen) 76*1c12ee1eSDan Willemsen 77*1c12ee1eSDan Willemsenfunc (t NameKind) String() string { 78*1c12ee1eSDan Willemsen switch t { 79*1c12ee1eSDan Willemsen case IdentName: 80*1c12ee1eSDan Willemsen return "IdentName" 81*1c12ee1eSDan Willemsen case TypeName: 82*1c12ee1eSDan Willemsen return "TypeName" 83*1c12ee1eSDan Willemsen case FieldNumber: 84*1c12ee1eSDan Willemsen return "FieldNumber" 85*1c12ee1eSDan Willemsen default: 86*1c12ee1eSDan Willemsen return fmt.Sprintf("<invalid:%v>", uint8(t)) 87*1c12ee1eSDan Willemsen } 88*1c12ee1eSDan Willemsen} 89*1c12ee1eSDan Willemsen 90*1c12ee1eSDan Willemsen// Bit mask in Token.attrs to indicate if a Name token is followed by the 91*1c12ee1eSDan Willemsen// separator char ':'. The field name separator char is optional for message 92*1c12ee1eSDan Willemsen// field or repeated message field, but required for all other types. Decoder 93*1c12ee1eSDan Willemsen// simply indicates whether a Name token is followed by separator or not. It is 94*1c12ee1eSDan Willemsen// up to the prototext package to validate. 95*1c12ee1eSDan Willemsenconst hasSeparator = 1 << 7 96*1c12ee1eSDan Willemsen 97*1c12ee1eSDan Willemsen// Scalar value types. 98*1c12ee1eSDan Willemsenconst ( 99*1c12ee1eSDan Willemsen numberValue = iota + 1 100*1c12ee1eSDan Willemsen stringValue 101*1c12ee1eSDan Willemsen literalValue 102*1c12ee1eSDan Willemsen) 103*1c12ee1eSDan Willemsen 104*1c12ee1eSDan Willemsen// Bit mask in Token.numAttrs to indicate that the number is a negative. 105*1c12ee1eSDan Willemsenconst isNegative = 1 << 7 106*1c12ee1eSDan Willemsen 107*1c12ee1eSDan Willemsen// Token provides a parsed token kind and value. Values are provided by the 108*1c12ee1eSDan Willemsen// different accessor methods. 109*1c12ee1eSDan Willemsentype Token struct { 110*1c12ee1eSDan Willemsen // Kind of the Token object. 111*1c12ee1eSDan Willemsen kind Kind 112*1c12ee1eSDan Willemsen // attrs contains metadata for the following Kinds: 113*1c12ee1eSDan Willemsen // Name: hasSeparator bit and one of NameKind. 114*1c12ee1eSDan Willemsen // Scalar: one of numberValue, stringValue, literalValue. 115*1c12ee1eSDan Willemsen attrs uint8 116*1c12ee1eSDan Willemsen // numAttrs contains metadata for numberValue: 117*1c12ee1eSDan Willemsen // - highest bit is whether negative or positive. 118*1c12ee1eSDan Willemsen // - lower bits indicate one of numDec, numHex, numOct, numFloat. 119*1c12ee1eSDan Willemsen numAttrs uint8 120*1c12ee1eSDan Willemsen // pos provides the position of the token in the original input. 121*1c12ee1eSDan Willemsen pos int 122*1c12ee1eSDan Willemsen // raw bytes of the serialized token. 123*1c12ee1eSDan Willemsen // This is a subslice into the original input. 124*1c12ee1eSDan Willemsen raw []byte 125*1c12ee1eSDan Willemsen // str contains parsed string for the following: 126*1c12ee1eSDan Willemsen // - stringValue of Scalar kind 127*1c12ee1eSDan Willemsen // - numberValue of Scalar kind 128*1c12ee1eSDan Willemsen // - TypeName of Name kind 129*1c12ee1eSDan Willemsen str string 130*1c12ee1eSDan Willemsen} 131*1c12ee1eSDan Willemsen 132*1c12ee1eSDan Willemsen// Kind returns the token kind. 133*1c12ee1eSDan Willemsenfunc (t Token) Kind() Kind { 134*1c12ee1eSDan Willemsen return t.kind 135*1c12ee1eSDan Willemsen} 136*1c12ee1eSDan Willemsen 137*1c12ee1eSDan Willemsen// RawString returns the read value in string. 138*1c12ee1eSDan Willemsenfunc (t Token) RawString() string { 139*1c12ee1eSDan Willemsen return string(t.raw) 140*1c12ee1eSDan Willemsen} 141*1c12ee1eSDan Willemsen 142*1c12ee1eSDan Willemsen// Pos returns the token position from the input. 143*1c12ee1eSDan Willemsenfunc (t Token) Pos() int { 144*1c12ee1eSDan Willemsen return t.pos 145*1c12ee1eSDan Willemsen} 146*1c12ee1eSDan Willemsen 147*1c12ee1eSDan Willemsen// NameKind returns IdentName, TypeName or FieldNumber. 148*1c12ee1eSDan Willemsen// It panics if type is not Name. 149*1c12ee1eSDan Willemsenfunc (t Token) NameKind() NameKind { 150*1c12ee1eSDan Willemsen if t.kind == Name { 151*1c12ee1eSDan Willemsen return NameKind(t.attrs &^ hasSeparator) 152*1c12ee1eSDan Willemsen } 153*1c12ee1eSDan Willemsen panic(fmt.Sprintf("Token is not a Name type: %s", t.kind)) 154*1c12ee1eSDan Willemsen} 155*1c12ee1eSDan Willemsen 156*1c12ee1eSDan Willemsen// HasSeparator returns true if the field name is followed by the separator char 157*1c12ee1eSDan Willemsen// ':', else false. It panics if type is not Name. 158*1c12ee1eSDan Willemsenfunc (t Token) HasSeparator() bool { 159*1c12ee1eSDan Willemsen if t.kind == Name { 160*1c12ee1eSDan Willemsen return t.attrs&hasSeparator != 0 161*1c12ee1eSDan Willemsen } 162*1c12ee1eSDan Willemsen panic(fmt.Sprintf("Token is not a Name type: %s", t.kind)) 163*1c12ee1eSDan Willemsen} 164*1c12ee1eSDan Willemsen 165*1c12ee1eSDan Willemsen// IdentName returns the value for IdentName type. 166*1c12ee1eSDan Willemsenfunc (t Token) IdentName() string { 167*1c12ee1eSDan Willemsen if t.kind == Name && t.attrs&uint8(IdentName) != 0 { 168*1c12ee1eSDan Willemsen return string(t.raw) 169*1c12ee1eSDan Willemsen } 170*1c12ee1eSDan Willemsen panic(fmt.Sprintf("Token is not an IdentName: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) 171*1c12ee1eSDan Willemsen} 172*1c12ee1eSDan Willemsen 173*1c12ee1eSDan Willemsen// TypeName returns the value for TypeName type. 174*1c12ee1eSDan Willemsenfunc (t Token) TypeName() string { 175*1c12ee1eSDan Willemsen if t.kind == Name && t.attrs&uint8(TypeName) != 0 { 176*1c12ee1eSDan Willemsen return t.str 177*1c12ee1eSDan Willemsen } 178*1c12ee1eSDan Willemsen panic(fmt.Sprintf("Token is not a TypeName: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) 179*1c12ee1eSDan Willemsen} 180*1c12ee1eSDan Willemsen 181*1c12ee1eSDan Willemsen// FieldNumber returns the value for FieldNumber type. It returns a 182*1c12ee1eSDan Willemsen// non-negative int32 value. Caller will still need to validate for the correct 183*1c12ee1eSDan Willemsen// field number range. 184*1c12ee1eSDan Willemsenfunc (t Token) FieldNumber() int32 { 185*1c12ee1eSDan Willemsen if t.kind != Name || t.attrs&uint8(FieldNumber) == 0 { 186*1c12ee1eSDan Willemsen panic(fmt.Sprintf("Token is not a FieldNumber: %s:%s", t.kind, NameKind(t.attrs&^hasSeparator))) 187*1c12ee1eSDan Willemsen } 188*1c12ee1eSDan Willemsen // Following should not return an error as it had already been called right 189*1c12ee1eSDan Willemsen // before this Token was constructed. 190*1c12ee1eSDan Willemsen num, _ := strconv.ParseInt(string(t.raw), 10, 32) 191*1c12ee1eSDan Willemsen return int32(num) 192*1c12ee1eSDan Willemsen} 193*1c12ee1eSDan Willemsen 194*1c12ee1eSDan Willemsen// String returns the string value for a Scalar type. 195*1c12ee1eSDan Willemsenfunc (t Token) String() (string, bool) { 196*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != stringValue { 197*1c12ee1eSDan Willemsen return "", false 198*1c12ee1eSDan Willemsen } 199*1c12ee1eSDan Willemsen return t.str, true 200*1c12ee1eSDan Willemsen} 201*1c12ee1eSDan Willemsen 202*1c12ee1eSDan Willemsen// Enum returns the literal value for a Scalar type for use as enum literals. 203*1c12ee1eSDan Willemsenfunc (t Token) Enum() (string, bool) { 204*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != literalValue || (len(t.raw) > 0 && t.raw[0] == '-') { 205*1c12ee1eSDan Willemsen return "", false 206*1c12ee1eSDan Willemsen } 207*1c12ee1eSDan Willemsen return string(t.raw), true 208*1c12ee1eSDan Willemsen} 209*1c12ee1eSDan Willemsen 210*1c12ee1eSDan Willemsen// Bool returns the bool value for a Scalar type. 211*1c12ee1eSDan Willemsenfunc (t Token) Bool() (bool, bool) { 212*1c12ee1eSDan Willemsen if t.kind != Scalar { 213*1c12ee1eSDan Willemsen return false, false 214*1c12ee1eSDan Willemsen } 215*1c12ee1eSDan Willemsen switch t.attrs { 216*1c12ee1eSDan Willemsen case literalValue: 217*1c12ee1eSDan Willemsen if b, ok := boolLits[string(t.raw)]; ok { 218*1c12ee1eSDan Willemsen return b, true 219*1c12ee1eSDan Willemsen } 220*1c12ee1eSDan Willemsen case numberValue: 221*1c12ee1eSDan Willemsen // Unsigned integer representation of 0 or 1 is permitted: 00, 0x0, 01, 222*1c12ee1eSDan Willemsen // 0x1, etc. 223*1c12ee1eSDan Willemsen n, err := strconv.ParseUint(t.str, 0, 64) 224*1c12ee1eSDan Willemsen if err == nil { 225*1c12ee1eSDan Willemsen switch n { 226*1c12ee1eSDan Willemsen case 0: 227*1c12ee1eSDan Willemsen return false, true 228*1c12ee1eSDan Willemsen case 1: 229*1c12ee1eSDan Willemsen return true, true 230*1c12ee1eSDan Willemsen } 231*1c12ee1eSDan Willemsen } 232*1c12ee1eSDan Willemsen } 233*1c12ee1eSDan Willemsen return false, false 234*1c12ee1eSDan Willemsen} 235*1c12ee1eSDan Willemsen 236*1c12ee1eSDan Willemsen// These exact boolean literals are the ones supported in C++. 237*1c12ee1eSDan Willemsenvar boolLits = map[string]bool{ 238*1c12ee1eSDan Willemsen "t": true, 239*1c12ee1eSDan Willemsen "true": true, 240*1c12ee1eSDan Willemsen "True": true, 241*1c12ee1eSDan Willemsen "f": false, 242*1c12ee1eSDan Willemsen "false": false, 243*1c12ee1eSDan Willemsen "False": false, 244*1c12ee1eSDan Willemsen} 245*1c12ee1eSDan Willemsen 246*1c12ee1eSDan Willemsen// Uint64 returns the uint64 value for a Scalar type. 247*1c12ee1eSDan Willemsenfunc (t Token) Uint64() (uint64, bool) { 248*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != numberValue || 249*1c12ee1eSDan Willemsen t.numAttrs&isNegative > 0 || t.numAttrs&numFloat > 0 { 250*1c12ee1eSDan Willemsen return 0, false 251*1c12ee1eSDan Willemsen } 252*1c12ee1eSDan Willemsen n, err := strconv.ParseUint(t.str, 0, 64) 253*1c12ee1eSDan Willemsen if err != nil { 254*1c12ee1eSDan Willemsen return 0, false 255*1c12ee1eSDan Willemsen } 256*1c12ee1eSDan Willemsen return n, true 257*1c12ee1eSDan Willemsen} 258*1c12ee1eSDan Willemsen 259*1c12ee1eSDan Willemsen// Uint32 returns the uint32 value for a Scalar type. 260*1c12ee1eSDan Willemsenfunc (t Token) Uint32() (uint32, bool) { 261*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != numberValue || 262*1c12ee1eSDan Willemsen t.numAttrs&isNegative > 0 || t.numAttrs&numFloat > 0 { 263*1c12ee1eSDan Willemsen return 0, false 264*1c12ee1eSDan Willemsen } 265*1c12ee1eSDan Willemsen n, err := strconv.ParseUint(t.str, 0, 32) 266*1c12ee1eSDan Willemsen if err != nil { 267*1c12ee1eSDan Willemsen return 0, false 268*1c12ee1eSDan Willemsen } 269*1c12ee1eSDan Willemsen return uint32(n), true 270*1c12ee1eSDan Willemsen} 271*1c12ee1eSDan Willemsen 272*1c12ee1eSDan Willemsen// Int64 returns the int64 value for a Scalar type. 273*1c12ee1eSDan Willemsenfunc (t Token) Int64() (int64, bool) { 274*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != numberValue || t.numAttrs&numFloat > 0 { 275*1c12ee1eSDan Willemsen return 0, false 276*1c12ee1eSDan Willemsen } 277*1c12ee1eSDan Willemsen if n, err := strconv.ParseInt(t.str, 0, 64); err == nil { 278*1c12ee1eSDan Willemsen return n, true 279*1c12ee1eSDan Willemsen } 280*1c12ee1eSDan Willemsen // C++ accepts large positive hex numbers as negative values. 281*1c12ee1eSDan Willemsen // This feature is here for proto1 backwards compatibility purposes. 282*1c12ee1eSDan Willemsen if flags.ProtoLegacy && (t.numAttrs == numHex) { 283*1c12ee1eSDan Willemsen if n, err := strconv.ParseUint(t.str, 0, 64); err == nil { 284*1c12ee1eSDan Willemsen return int64(n), true 285*1c12ee1eSDan Willemsen } 286*1c12ee1eSDan Willemsen } 287*1c12ee1eSDan Willemsen return 0, false 288*1c12ee1eSDan Willemsen} 289*1c12ee1eSDan Willemsen 290*1c12ee1eSDan Willemsen// Int32 returns the int32 value for a Scalar type. 291*1c12ee1eSDan Willemsenfunc (t Token) Int32() (int32, bool) { 292*1c12ee1eSDan Willemsen if t.kind != Scalar || t.attrs != numberValue || t.numAttrs&numFloat > 0 { 293*1c12ee1eSDan Willemsen return 0, false 294*1c12ee1eSDan Willemsen } 295*1c12ee1eSDan Willemsen if n, err := strconv.ParseInt(t.str, 0, 32); err == nil { 296*1c12ee1eSDan Willemsen return int32(n), true 297*1c12ee1eSDan Willemsen } 298*1c12ee1eSDan Willemsen // C++ accepts large positive hex numbers as negative values. 299*1c12ee1eSDan Willemsen // This feature is here for proto1 backwards compatibility purposes. 300*1c12ee1eSDan Willemsen if flags.ProtoLegacy && (t.numAttrs == numHex) { 301*1c12ee1eSDan Willemsen if n, err := strconv.ParseUint(t.str, 0, 32); err == nil { 302*1c12ee1eSDan Willemsen return int32(n), true 303*1c12ee1eSDan Willemsen } 304*1c12ee1eSDan Willemsen } 305*1c12ee1eSDan Willemsen return 0, false 306*1c12ee1eSDan Willemsen} 307*1c12ee1eSDan Willemsen 308*1c12ee1eSDan Willemsen// Float64 returns the float64 value for a Scalar type. 309*1c12ee1eSDan Willemsenfunc (t Token) Float64() (float64, bool) { 310*1c12ee1eSDan Willemsen if t.kind != Scalar { 311*1c12ee1eSDan Willemsen return 0, false 312*1c12ee1eSDan Willemsen } 313*1c12ee1eSDan Willemsen switch t.attrs { 314*1c12ee1eSDan Willemsen case literalValue: 315*1c12ee1eSDan Willemsen if f, ok := floatLits[strings.ToLower(string(t.raw))]; ok { 316*1c12ee1eSDan Willemsen return f, true 317*1c12ee1eSDan Willemsen } 318*1c12ee1eSDan Willemsen case numberValue: 319*1c12ee1eSDan Willemsen n, err := strconv.ParseFloat(t.str, 64) 320*1c12ee1eSDan Willemsen if err == nil { 321*1c12ee1eSDan Willemsen return n, true 322*1c12ee1eSDan Willemsen } 323*1c12ee1eSDan Willemsen nerr := err.(*strconv.NumError) 324*1c12ee1eSDan Willemsen if nerr.Err == strconv.ErrRange { 325*1c12ee1eSDan Willemsen return n, true 326*1c12ee1eSDan Willemsen } 327*1c12ee1eSDan Willemsen } 328*1c12ee1eSDan Willemsen return 0, false 329*1c12ee1eSDan Willemsen} 330*1c12ee1eSDan Willemsen 331*1c12ee1eSDan Willemsen// Float32 returns the float32 value for a Scalar type. 332*1c12ee1eSDan Willemsenfunc (t Token) Float32() (float32, bool) { 333*1c12ee1eSDan Willemsen if t.kind != Scalar { 334*1c12ee1eSDan Willemsen return 0, false 335*1c12ee1eSDan Willemsen } 336*1c12ee1eSDan Willemsen switch t.attrs { 337*1c12ee1eSDan Willemsen case literalValue: 338*1c12ee1eSDan Willemsen if f, ok := floatLits[strings.ToLower(string(t.raw))]; ok { 339*1c12ee1eSDan Willemsen return float32(f), true 340*1c12ee1eSDan Willemsen } 341*1c12ee1eSDan Willemsen case numberValue: 342*1c12ee1eSDan Willemsen n, err := strconv.ParseFloat(t.str, 64) 343*1c12ee1eSDan Willemsen if err == nil { 344*1c12ee1eSDan Willemsen // Overflows are treated as (-)infinity. 345*1c12ee1eSDan Willemsen return float32(n), true 346*1c12ee1eSDan Willemsen } 347*1c12ee1eSDan Willemsen nerr := err.(*strconv.NumError) 348*1c12ee1eSDan Willemsen if nerr.Err == strconv.ErrRange { 349*1c12ee1eSDan Willemsen return float32(n), true 350*1c12ee1eSDan Willemsen } 351*1c12ee1eSDan Willemsen } 352*1c12ee1eSDan Willemsen return 0, false 353*1c12ee1eSDan Willemsen} 354*1c12ee1eSDan Willemsen 355*1c12ee1eSDan Willemsen// These are the supported float literals which C++ permits case-insensitive 356*1c12ee1eSDan Willemsen// variants of these. 357*1c12ee1eSDan Willemsenvar floatLits = map[string]float64{ 358*1c12ee1eSDan Willemsen "nan": math.NaN(), 359*1c12ee1eSDan Willemsen "inf": math.Inf(1), 360*1c12ee1eSDan Willemsen "infinity": math.Inf(1), 361*1c12ee1eSDan Willemsen "-inf": math.Inf(-1), 362*1c12ee1eSDan Willemsen "-infinity": math.Inf(-1), 363*1c12ee1eSDan Willemsen} 364*1c12ee1eSDan Willemsen 365*1c12ee1eSDan Willemsen// TokenEquals returns true if given Tokens are equal, else false. 366*1c12ee1eSDan Willemsenfunc TokenEquals(x, y Token) bool { 367*1c12ee1eSDan Willemsen return x.kind == y.kind && 368*1c12ee1eSDan Willemsen x.attrs == y.attrs && 369*1c12ee1eSDan Willemsen x.numAttrs == y.numAttrs && 370*1c12ee1eSDan Willemsen x.pos == y.pos && 371*1c12ee1eSDan Willemsen bytes.Equal(x.raw, y.raw) && 372*1c12ee1eSDan Willemsen x.str == y.str 373*1c12ee1eSDan Willemsen} 374