|  | // Copyright 2019 The Fuchsia 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 parser | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "encoding/binary" | 
|  | "fmt" | 
|  | "io" | 
|  | "strconv" | 
|  | "strings" | 
|  | "text/scanner" | 
|  |  | 
|  | "gidl/ir" | 
|  | ) | 
|  |  | 
|  | type Parser struct { | 
|  | scanner    scanner.Scanner | 
|  | lookaheads []token | 
|  | // All supported languages, used to validate bindings allowlist/denylist. | 
|  | allLanguages ir.LanguageList | 
|  | } | 
|  |  | 
|  | func NewParser(name string, input io.Reader, allLanguages []string) *Parser { | 
|  | var p Parser | 
|  | p.scanner.Position.Filename = name | 
|  | p.scanner.Init(input) | 
|  | p.allLanguages = allLanguages | 
|  | return &p | 
|  | } | 
|  |  | 
|  | func (p *Parser) Parse() (ir.All, error) { | 
|  | var result ir.All | 
|  | for !p.peekTokenKind(tEof) { | 
|  | if err := p.parseSection(&result); err != nil { | 
|  | return ir.All{}, err | 
|  | } | 
|  | } | 
|  | // TODO(FIDL-754) Add validation checks for error codes after parsing. | 
|  | return result, nil | 
|  | } | 
|  |  | 
|  | type tokenKind uint | 
|  |  | 
|  | const ( | 
|  | _ tokenKind = iota | 
|  | tEof | 
|  | tText | 
|  | tString | 
|  | tLacco | 
|  | tRacco | 
|  | tComma | 
|  | tColon | 
|  | tNeg | 
|  | tLparen | 
|  | tRparen | 
|  | tEqual | 
|  | tLsquare | 
|  | tRsquare | 
|  | ) | 
|  |  | 
|  | var tokenKindStrings = []string{ | 
|  | "<invalid>", | 
|  | "<eof>", | 
|  | "<text>", | 
|  | "<string>", | 
|  | "{", | 
|  | "}", | 
|  | ",", | 
|  | ":", | 
|  | "-", | 
|  | "(", | 
|  | ")", | 
|  | "=", | 
|  | "[", | 
|  | "]", | 
|  | } | 
|  |  | 
|  | var ( | 
|  | isUnitTokenKind = make(map[tokenKind]bool) | 
|  | textToTokenKind = make(map[string]tokenKind) | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | for index, text := range tokenKindStrings { | 
|  | if strings.HasPrefix(text, "<") && strings.HasSuffix(text, ">") { | 
|  | continue | 
|  | } | 
|  | kind := tokenKind(index) | 
|  | isUnitTokenKind[kind] = true | 
|  | textToTokenKind[text] = kind | 
|  | } | 
|  | } | 
|  |  | 
|  | func (kind tokenKind) String() string { | 
|  | if index := int(kind); index < len(tokenKindStrings) { | 
|  | return tokenKindStrings[index] | 
|  | } | 
|  | return fmt.Sprintf("%d", kind) | 
|  | } | 
|  |  | 
|  | type token struct { | 
|  | kind         tokenKind | 
|  | value        string | 
|  | line, column int | 
|  | } | 
|  |  | 
|  | func (t token) String() string { | 
|  | if isUnitTokenKind[t.kind] { | 
|  | return t.kind.String() | 
|  | } else { | 
|  | return t.value | 
|  | } | 
|  | } | 
|  |  | 
|  | type bodyElement uint | 
|  |  | 
|  | const ( | 
|  | _ bodyElement = iota | 
|  | isType | 
|  | isValue | 
|  | isBytes | 
|  | isErr | 
|  | isBindingsAllowlist | 
|  | isBindingsDenylist | 
|  | ) | 
|  |  | 
|  | func (kind bodyElement) String() string { | 
|  | switch kind { | 
|  | case isType: | 
|  | return "type" | 
|  | case isValue: | 
|  | return "value" | 
|  | case isBytes: | 
|  | return "bytes" | 
|  | case isErr: | 
|  | return "err" | 
|  | case isBindingsAllowlist: | 
|  | return "bindings_allowlist" | 
|  | case isBindingsDenylist: | 
|  | return "bindings_denylist" | 
|  | default: | 
|  | panic("unsupported kind") | 
|  | } | 
|  | } | 
|  |  | 
|  | type body struct { | 
|  | Type              string | 
|  | Value             ir.Value | 
|  | Encodings         []ir.Encoding | 
|  | Err               ir.ErrorCode | 
|  | BindingsAllowlist *ir.LanguageList | 
|  | BindingsDenylist  *ir.LanguageList | 
|  | } | 
|  |  | 
|  | type sectionMetadata struct { | 
|  | requiredKinds map[bodyElement]bool | 
|  | optionalKinds map[bodyElement]bool | 
|  | setter        func(name string, body body, all *ir.All) | 
|  | } | 
|  |  | 
|  | var sections = map[string]sectionMetadata{ | 
|  | "success": { | 
|  | requiredKinds: map[bodyElement]bool{isValue: true, isBytes: true}, | 
|  | optionalKinds: map[bodyElement]bool{isBindingsAllowlist: true, isBindingsDenylist: true}, | 
|  | setter: func(name string, body body, all *ir.All) { | 
|  | encodeSuccess := ir.EncodeSuccess{ | 
|  | Name:              name, | 
|  | Value:             body.Value, | 
|  | Encodings:         body.Encodings, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | BindingsDenylist:  body.BindingsDenylist, | 
|  | } | 
|  | all.EncodeSuccess = append(all.EncodeSuccess, encodeSuccess) | 
|  | decodeSuccess := ir.DecodeSuccess{ | 
|  | Name:              name, | 
|  | Value:             body.Value, | 
|  | Encodings:         body.Encodings, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | BindingsDenylist:  body.BindingsDenylist, | 
|  | } | 
|  | all.DecodeSuccess = append(all.DecodeSuccess, decodeSuccess) | 
|  | }, | 
|  | }, | 
|  | "encode_success": { | 
|  | requiredKinds: map[bodyElement]bool{isValue: true, isBytes: true}, | 
|  | optionalKinds: map[bodyElement]bool{isBindingsAllowlist: true}, | 
|  | setter: func(name string, body body, all *ir.All) { | 
|  | result := ir.EncodeSuccess{ | 
|  | Name:              name, | 
|  | Value:             body.Value, | 
|  | Encodings:         body.Encodings, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | } | 
|  | all.EncodeSuccess = append(all.EncodeSuccess, result) | 
|  | }, | 
|  | }, | 
|  | "decode_success": { | 
|  | requiredKinds: map[bodyElement]bool{isValue: true, isBytes: true}, | 
|  | optionalKinds: map[bodyElement]bool{isBindingsAllowlist: true}, | 
|  | setter: func(name string, body body, all *ir.All) { | 
|  | result := ir.DecodeSuccess{ | 
|  | Name:              name, | 
|  | Value:             body.Value, | 
|  | Encodings:         body.Encodings, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | } | 
|  | all.DecodeSuccess = append(all.DecodeSuccess, result) | 
|  | }, | 
|  | }, | 
|  | "encode_failure": { | 
|  | requiredKinds: map[bodyElement]bool{isValue: true, isErr: true}, | 
|  | optionalKinds: map[bodyElement]bool{isBindingsAllowlist: true, isBindingsDenylist: true}, | 
|  | setter: func(name string, body body, all *ir.All) { | 
|  | result := ir.EncodeFailure{ | 
|  | Name:              name, | 
|  | Value:             body.Value, | 
|  | WireFormats:       ir.AllWireFormats(), | 
|  | Err:               body.Err, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | BindingsDenylist:  body.BindingsDenylist, | 
|  | } | 
|  | all.EncodeFailure = append(all.EncodeFailure, result) | 
|  | }, | 
|  | }, | 
|  | "decode_failure": { | 
|  | requiredKinds: map[bodyElement]bool{isType: true, isBytes: true, isErr: true}, | 
|  | optionalKinds: map[bodyElement]bool{isBindingsAllowlist: true, isBindingsDenylist: true}, | 
|  | setter: func(name string, body body, all *ir.All) { | 
|  | result := ir.DecodeFailure{ | 
|  | Name:              name, | 
|  | Type:              body.Type, | 
|  | Encodings:         body.Encodings, | 
|  | Err:               body.Err, | 
|  | BindingsAllowlist: body.BindingsAllowlist, | 
|  | BindingsDenylist:  body.BindingsDenylist, | 
|  | } | 
|  | all.DecodeFailure = append(all.DecodeFailure, result) | 
|  | }, | 
|  | }, | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseSection(all *ir.All) error { | 
|  | section, name, err := p.parsePreamble() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | body, err := p.parseBody(section.requiredKinds, section.optionalKinds) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | section.setter(name, body, all) | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parsePreamble() (sectionMetadata, string, error) { | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return sectionMetadata{}, "", p.failExpectedToken(tText, tok) | 
|  | } | 
|  |  | 
|  | section, ok := sections[tok.value] | 
|  | if !ok { | 
|  | return sectionMetadata{}, "", p.newParseError(tok, "unknown section %s", tok.value) | 
|  | } | 
|  |  | 
|  | tok, ok = p.consumeToken(tLparen) | 
|  | if !ok { | 
|  | return sectionMetadata{}, "", p.failExpectedToken(tLparen, tok) | 
|  | } | 
|  |  | 
|  | tok, ok = p.consumeToken(tString) | 
|  | if !ok { | 
|  | return sectionMetadata{}, "", p.failExpectedToken(tString, tok) | 
|  | } | 
|  | name := tok.value | 
|  |  | 
|  | tok, ok = p.consumeToken(tRparen) | 
|  | if !ok { | 
|  | return sectionMetadata{}, "", p.failExpectedToken(tRparen, tok) | 
|  | } | 
|  |  | 
|  | return section, name, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseBody(requiredKinds map[bodyElement]bool, optionalKinds map[bodyElement]bool) (body, error) { | 
|  | var ( | 
|  | result      body | 
|  | parsedKinds = make(map[bodyElement]bool) | 
|  | bodyTok     = p.peekToken() | 
|  | ) | 
|  | err := p.parseCommaSeparated(tLacco, tRacco, func() error { | 
|  | return p.parseSingleBodyElement(&result, parsedKinds) | 
|  | }) | 
|  | if err != nil { | 
|  | return result, err | 
|  | } | 
|  | for requiredKind := range requiredKinds { | 
|  | if !parsedKinds[requiredKind] { | 
|  | return result, p.newParseError(bodyTok, "missing required parameter '%s'", requiredKind) | 
|  | } | 
|  | } | 
|  | for parsedKind := range parsedKinds { | 
|  | if !requiredKinds[parsedKind] && !optionalKinds[parsedKind] { | 
|  | return result, p.newParseError(bodyTok, "parameter '%s' does not apply to element", parsedKind) | 
|  | } | 
|  | } | 
|  | return result, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseSingleBodyElement(result *body, all map[bodyElement]bool) error { | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } | 
|  | if tok, ok := p.consumeToken(tEqual); !ok { | 
|  | return p.failExpectedToken(tEqual, tok) | 
|  | } | 
|  | var kind bodyElement | 
|  | switch tok.value { | 
|  | case "type": | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } | 
|  | result.Type = tok.value | 
|  | kind = isType | 
|  | case "value": | 
|  | val, err := p.parseValue() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result.Value = val | 
|  | kind = isValue | 
|  | case "bytes": | 
|  | encodings, err := p.parseByteSection() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result.Encodings = encodings | 
|  | kind = isBytes | 
|  | case "err": | 
|  | errorCode, err := p.parseErrorCode() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result.Err = errorCode | 
|  | kind = isErr | 
|  | case "bindings_allowlist": | 
|  | languages, err := p.parseLanguageList() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result.BindingsAllowlist = &languages | 
|  | kind = isBindingsAllowlist | 
|  | case "bindings_denylist": | 
|  | languages, err := p.parseLanguageList() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result.BindingsDenylist = &languages | 
|  | kind = isBindingsDenylist | 
|  | default: | 
|  | return p.newParseError(tok, "must be type, value, bytes, err, bindings_allowlist or bindings_denylist") | 
|  | } | 
|  | if all[kind] { | 
|  | return p.newParseError(tok, "duplicate %s found", kind) | 
|  | } | 
|  | all[kind] = true | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseValue() (interface{}, error) { | 
|  | switch p.peekToken().kind { | 
|  | case tText: | 
|  | tok := p.nextToken() | 
|  | if '0' <= tok.value[0] && tok.value[0] <= '9' { | 
|  | return parseNum(tok, false) | 
|  | } | 
|  | if tok.value == "null" { | 
|  | return nil, nil | 
|  | } | 
|  | if tok.value == "true" { | 
|  | return true, nil | 
|  | } | 
|  | if tok.value == "false" { | 
|  | return false, nil | 
|  | } | 
|  | return p.parseObject(tok.value) | 
|  | case tLsquare: | 
|  | return p.parseSlice() | 
|  | case tString: | 
|  | return p.nextToken().value, nil | 
|  | case tNeg: | 
|  | p.nextToken() | 
|  | if tok, ok := p.consumeToken(tText); !ok { | 
|  | return nil, p.failExpectedToken(tText, tok) | 
|  | } else { | 
|  | return parseNum(tok, true) | 
|  | } | 
|  | default: | 
|  | return nil, p.newParseError(p.peekToken(), "expected value") | 
|  | } | 
|  | } | 
|  |  | 
|  | func parseNum(tok token, neg bool) (interface{}, error) { | 
|  | if strings.Contains(tok.value, ".") { | 
|  | val, err := strconv.ParseFloat(tok.value, 64) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if neg { | 
|  | return -val, nil | 
|  | } else { | 
|  | return val, nil | 
|  | } | 
|  | } else { | 
|  | val, err := strconv.ParseUint(tok.value, 0, 64) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if neg { | 
|  | return -int64(val), nil | 
|  | } else { | 
|  | return uint64(val), nil | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseObject(name string) (interface{}, error) { | 
|  | obj := ir.Object{Name: name} | 
|  | err := p.parseCommaSeparated(tLacco, tRacco, func() error { | 
|  | tokFieldName, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tokFieldName) | 
|  | } | 
|  | if tok, ok := p.consumeToken(tColon); !ok { | 
|  | return p.failExpectedToken(tColon, tok) | 
|  | } | 
|  | val, err := p.parseValue() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | obj.Fields = append(obj.Fields, ir.Field{Key: decodeFieldKey(tokFieldName.value), Value: val}) | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return obj, nil | 
|  | } | 
|  |  | 
|  | // Field can be referenced by either name or ordinal. | 
|  | func decodeFieldKey(field string) ir.FieldKey { | 
|  | if ord, err := strconv.ParseInt(field, 0, 64); err == nil { | 
|  | return ir.FieldKey{Ordinal: uint64(ord)} | 
|  | } | 
|  | return ir.FieldKey{Name: field} | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseErrorCode() (ir.ErrorCode, error) { | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return "", p.failExpectedToken(tText, tok) | 
|  | } | 
|  | code := ir.ErrorCode(tok.value) | 
|  | if _, ok := ir.AllErrorCodes[code]; !ok { | 
|  | return "", p.newParseError(tok, "unknown error code") | 
|  | } | 
|  | return code, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseSlice() ([]interface{}, error) { | 
|  | var result []interface{} | 
|  | err := p.parseCommaSeparated(tLsquare, tRsquare, func() error { | 
|  | val, err := p.parseValue() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | result = append(result, val) | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return result, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseTextSlice() ([]string, error) { | 
|  | var result []string | 
|  | err := p.parseCommaSeparated(tLsquare, tRsquare, func() error { | 
|  | if tok, ok := p.consumeToken(tText); !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } else { | 
|  | result = append(result, tok.value) | 
|  | return nil | 
|  | } | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return result, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseLanguageList() (ir.LanguageList, error) { | 
|  | var result ir.LanguageList | 
|  | err := p.parseCommaSeparated(tLsquare, tRsquare, func() error { | 
|  | if tok, ok := p.consumeToken(tText); !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } else if !p.allLanguages.Includes(tok.value) { | 
|  | return p.newParseError(tok, "invalid language '%s'; must be one of: %s", | 
|  | tok.value, strings.Join(p.allLanguages, ", ")) | 
|  | } else { | 
|  | result = append(result, tok.value) | 
|  | return nil | 
|  | } | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return result, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByteSection() ([]ir.Encoding, error) { | 
|  | firstTok := p.peekToken() | 
|  | if firstTok.kind == tLsquare { | 
|  | if b, err := p.parseByteList(); err == nil { | 
|  | return []ir.Encoding{{ | 
|  | // Default to the V1 wire format. | 
|  | WireFormat: ir.V1WireFormat, | 
|  | Bytes:      b, | 
|  | }}, nil | 
|  | } else { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | var res []ir.Encoding | 
|  | wireFormats := map[ir.WireFormat]struct{}{} | 
|  | err := p.parseCommaSeparated(tLacco, tRacco, func() error { | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } | 
|  | if teq, ok := p.consumeToken(tEqual); !ok { | 
|  | return p.failExpectedToken(tEqual, teq) | 
|  | } | 
|  | b, err := p.parseByteList() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | wf, err := ir.WireFormatByName(tok.value) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | if _, ok := wireFormats[wf]; ok { | 
|  | return p.newParseError(tok, "duplicate wire format: %s", tok.value) | 
|  | } | 
|  | wireFormats[wf] = struct{}{} | 
|  | res = append(res, ir.Encoding{ | 
|  | WireFormat: wf, | 
|  | Bytes:      b, | 
|  | }) | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if len(res) == 0 { | 
|  | return nil, p.newParseError(firstTok, "no bytes provided for any wire format") | 
|  | } | 
|  | return res, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByteList() ([]byte, error) { | 
|  | var bytes []byte | 
|  | err := p.parseCommaSeparated(tLsquare, tRsquare, func() error { | 
|  | // Read the byte size. | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } | 
|  | if p.peekTokenKind(tText) { | 
|  | // First token was the label. Now get the byte size. | 
|  | tok, _ = p.consumeToken(tText) | 
|  | } | 
|  |  | 
|  | byteSize, err := strconv.ParseUint(tok.value, 10, 64) | 
|  | if err != nil { | 
|  | return p.newParseError(tok, "error parsing byte block size: %v", err) | 
|  | } | 
|  | if byteSize == 0 { | 
|  | return p.newParseError(tok, "expected non-zero byte size") | 
|  | } | 
|  |  | 
|  | if tok, ok := p.consumeToken(tColon); !ok { | 
|  | return p.failExpectedToken(tColon, tok) | 
|  | } | 
|  |  | 
|  | // Read the type. | 
|  | tok, ok = p.consumeToken(tText) | 
|  | if !ok { | 
|  | return p.failExpectedToken(tText, tok) | 
|  | } | 
|  | var handler func(byteSize int) ([]byte, error) | 
|  | switch tok.value { | 
|  | case "raw": | 
|  | handler = p.parseByteBlockRaw | 
|  | case "num": | 
|  | handler = p.parseByteBlockNum | 
|  | case "padding": | 
|  | handler = p.parseByteBlockPadding | 
|  | default: | 
|  | return p.newParseError(tok, "unknown byte block type %q", tok.value) | 
|  | } | 
|  | if b, err := handler(int(byteSize)); err != nil { | 
|  | return err | 
|  | } else if len(b) != int(byteSize) { | 
|  | return p.newParseError(tok, "byte block produced of incorrect size. got %d, want %d", len(b), byteSize) | 
|  | } else { | 
|  | bytes = append(bytes, b...) | 
|  | } | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return bytes, nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByteBlockRaw(byteSize int) ([]byte, error) { | 
|  | res := make([]byte, 0, byteSize) | 
|  | err := p.parseCommaSeparated(tLparen, tRparen, func() error { | 
|  | b, err := p.parseByte() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | res = append(res, b) | 
|  | return nil | 
|  | }) | 
|  | return res, err | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByteBlockNum(byteSize int) ([]byte, error) { | 
|  | if tok, ok := p.consumeToken(tLparen); !ok { | 
|  | return nil, p.failExpectedToken(tLparen, tok) | 
|  | } | 
|  | neg := p.peekTokenKind(tNeg) | 
|  | if neg { | 
|  | p.consumeToken(tNeg) | 
|  | } | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return nil, p.failExpectedToken(tText, tok) | 
|  | } | 
|  | buf := make([]byte, 8) | 
|  | uintVal, err := strconv.ParseUint(tok.value, 0, 64) | 
|  | if err != nil { | 
|  | return nil, p.newParseError(tok, "error parsing unsigned integer num: %v", err) | 
|  | } | 
|  | if neg { | 
|  | intVal := -int64(uintVal) | 
|  | if intVal>>(uint(byteSize)*8-1) < -1 { | 
|  | return nil, p.newParseError(tok, "num value %d exceeds byte size %d", intVal, byteSize) | 
|  | } | 
|  | uintVal = uint64(intVal) | 
|  | } else { | 
|  | if uintVal>>(uint(byteSize)*8) > 0 { | 
|  | return nil, p.newParseError(tok, "num value %d exceeds byte size %d", uintVal, byteSize) | 
|  | } | 
|  | } | 
|  | binary.LittleEndian.PutUint64(buf, uintVal) | 
|  | if tok, ok := p.consumeToken(tRparen); !ok { | 
|  | return nil, p.failExpectedToken(tRparen, tok) | 
|  | } | 
|  | return buf[:byteSize], nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByteBlockPadding(byteSize int) ([]byte, error) { | 
|  | if !p.peekTokenKind(tLparen) { | 
|  | return bytes.Repeat([]byte{0}, byteSize), nil | 
|  | } | 
|  | if tok, ok := p.consumeToken(tLparen); !ok { | 
|  | return nil, p.failExpectedToken(tLparen, tok) | 
|  | } | 
|  | b, err := p.parseByte() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if tok, ok := p.consumeToken(tRparen); !ok { | 
|  | return nil, p.failExpectedToken(tRparen, tok) | 
|  | } | 
|  | return bytes.Repeat([]byte{b}, byteSize), nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseByte() (byte, error) { | 
|  | tok, ok := p.consumeToken(tText) | 
|  | if !ok { | 
|  | return 0, p.failExpectedToken(tText, tok) | 
|  | } | 
|  | if len(tok.value) == 3 && tok.value[0] == '\'' && tok.value[2] == '\'' { | 
|  | return tok.value[1], nil | 
|  | } | 
|  | b, err := strconv.ParseUint(tok.value, 0, 8) | 
|  | if err != nil { | 
|  | return 0, p.newParseError(tok, err.Error()) | 
|  | } | 
|  | return byte(b), nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) parseCommaSeparated(beginTok, endTok tokenKind, handler func() error) error { | 
|  | if tok, ok := p.consumeToken(beginTok); !ok { | 
|  | return p.failExpectedToken(beginTok, tok) | 
|  | } | 
|  | for !p.peekTokenKind(endTok) { | 
|  | if err := handler(); err != nil { | 
|  | return err | 
|  | } | 
|  | if !p.peekTokenKind(endTok) { | 
|  | if tok, ok := p.consumeToken(tComma); !ok { | 
|  | return p.failExpectedToken(tComma, tok) | 
|  | } | 
|  | } | 
|  | } | 
|  | if tok, ok := p.consumeToken(endTok); !ok { | 
|  | return p.failExpectedToken(endTok, tok) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (p *Parser) consumeToken(kind tokenKind) (token, bool) { | 
|  | tok := p.nextToken() | 
|  | return tok, tok.kind == kind | 
|  | } | 
|  |  | 
|  | func (p *Parser) peekTokenKind(kind tokenKind) bool { | 
|  | return p.peekToken().kind == kind | 
|  | } | 
|  |  | 
|  | func (p *Parser) peekToken() token { | 
|  | if len(p.lookaheads) == 0 { | 
|  | tok := p.nextToken() | 
|  | p.lookaheads = append(p.lookaheads, tok) | 
|  | } | 
|  | return p.lookaheads[0] | 
|  | } | 
|  |  | 
|  | func (p *Parser) nextToken() token { | 
|  | if len(p.lookaheads) != 0 { | 
|  | var tok token | 
|  | tok, p.lookaheads = p.lookaheads[0], p.lookaheads[1:] | 
|  | return tok | 
|  | } | 
|  | return p.scanToken() | 
|  | } | 
|  |  | 
|  | func (p *Parser) scanToken() token { | 
|  | // eof | 
|  | if tok := p.scanner.Scan(); tok == scanner.EOF { | 
|  | return token{tEof, "", 0, 0} | 
|  | } | 
|  | pos := p.scanner.Position | 
|  |  | 
|  | // unit tokens | 
|  | text := p.scanner.TokenText() | 
|  | if kind, ok := textToTokenKind[text]; ok { | 
|  | return token{kind, text, pos.Line, pos.Column} | 
|  | } | 
|  |  | 
|  | // string | 
|  | if text[0] == '"' { | 
|  | // TODO escape | 
|  | return token{tString, text[1 : len(text)-1], pos.Line, pos.Column} | 
|  | } | 
|  |  | 
|  | // text | 
|  | return token{tText, text, pos.Line, pos.Column} | 
|  | } | 
|  |  | 
|  | type parseError struct { | 
|  | input        string | 
|  | line, column int | 
|  | message      string | 
|  | } | 
|  |  | 
|  | // Assert parseError implements error interface | 
|  | var _ error = &parseError{} | 
|  |  | 
|  | func (err *parseError) Error() string { | 
|  | return fmt.Sprintf("%s:%d:%d: %s", err.input, err.line, err.column, err.message) | 
|  | } | 
|  |  | 
|  | func (p *Parser) failExpectedToken(want tokenKind, got token) error { | 
|  | return p.newParseError(got, "unexpected tokenKind: want %q, got %q (value: %q)", want, got.kind, got.value) | 
|  | } | 
|  |  | 
|  | func (p *Parser) newParseError(tok token, format string, a ...interface{}) error { | 
|  | return &parseError{ | 
|  | input:   p.scanner.Position.Filename, | 
|  | line:    tok.line, | 
|  | column:  tok.column, | 
|  | message: fmt.Sprintf(format, a...), | 
|  | } | 
|  | } |