package generate import "io" import "strings" import "strconv" import "git.tebibyte.media/sashakoshka/goparse" func Parse(lx parse.Lexer) (*Protocol, error) { protocol := defaultProtocol() par := parser { Parser: parse.Parser { Lexer: lx, TokenNames: tokenNames, }, protocol: &protocol, } err := par.parse() if err != nil { return nil, err } return par.protocol, nil } func defaultProtocol() Protocol { return Protocol { Messages: make(map[uint16] Message), Types: map[string] Typedef { }, } } func ParseReader(fileName string, reader io.Reader) (*Protocol, error) { lx, err := Lex(fileName, reader) if err != nil { return nil, err } return Parse(lx) } type parser struct { parse.Parser protocol *Protocol } func (this *parser) parse() error { err := this.Next() if err != nil { return err } for this.Token.Kind != parse.EOF { err = this.parseTopLevel() if err != nil { return err } } return nil } func (this *parser) parseTopLevel() error { doc := "" for { err := this.ExpectDesc("message or typedef", TokenMethod, TokenIdent, TokenComment) if err != nil { return err } if this.EOF() { return nil } if this.Kind() == TokenComment { if doc != "" { doc += "\n" } doc += this.parseComment(this.Value()) this.Next() } else { break } } switch this.Kind() { case TokenMethod: return this.parseMessage(doc) case TokenIdent: return this.parseTypedef(doc) } panic("bug") } func (this *parser) parseMessage(doc string) error { err := this.Expect(TokenMethod) if err != nil { return err } method, err := this.parseHexNumber(this.Value(), 0xFFFF) if err != nil { return err } err = this.ExpectNext(TokenIdent) if err != nil { return err } name := this.Value() err = this.Next() if err != nil { return err } typ, err := this.parseType() if err != nil { return err } this.protocol.Messages[uint16(method)] = Message { Name: name, Doc: doc, Type: typ, } return nil } func (this *parser) parseTypedef(doc string) error { err := this.Expect(TokenIdent) if err != nil { return err } name := this.Value() err = this.Next() if err != nil { return err } typ, err := this.parseType() if err != nil { return err } this.protocol.Types[name] = Typedef { Doc: doc, Type: typ, } return nil } func (this *parser) parseType() (Type, error) { err := this.ExpectDesc("type", TokenIdent, TokenLBracket, TokenLBrace) if err != nil { return nil, err } switch this.Kind() { case TokenIdent: switch this.Value() { case "U8": return TypeInt { Bits: 8 }, this.Next() case "U16": return TypeInt { Bits: 16 }, this.Next() case "U32": return TypeInt { Bits: 32 }, this.Next() case "U64": return TypeInt { Bits: 64 }, this.Next() case "U128": return TypeInt { Bits: 128 }, this.Next() case "U256": return TypeInt { Bits: 256 }, this.Next() case "I8": return TypeInt { Bits: 8, Signed: true }, this.Next() case "I16": return TypeInt { Bits: 16, Signed: true }, this.Next() case "I32": return TypeInt { Bits: 32, Signed: true }, this.Next() case "I64": return TypeInt { Bits: 64, Signed: true }, this.Next() case "I128": return TypeInt { Bits: 128, Signed: true }, this.Next() case "I256": return TypeInt { Bits: 256, Signed: true }, this.Next() case "F16": return TypeFloat { Bits: 16 }, this.Next() case "F32": return TypeFloat { Bits: 32 }, this.Next() case "F64": return TypeFloat { Bits: 64 }, this.Next() case "F128": return TypeFloat { Bits: 128 }, this.Next() case "F256": return TypeFloat { Bits: 256 }, this.Next() case "String": return TypeString { }, this.Next() case "Buffer": return TypeBuffer { }, this.Next() case "Table": return TypeTable { }, this.Next() case "Any": return TypeAny { }, this.Next() case "Bool": return TypeBool { }, this.Next() } return this.parseTypeNamed() case TokenLBracket: return this.parseTypeArray() case TokenLBrace: return this.parseTypeTable() } panic("bug") } func (this *parser) parseTypeNamed() (TypeNamed, error) { err := this.Expect(TokenIdent) if err != nil { return TypeNamed { }, err } name := this.Value() err = this.Next() if err != nil { return TypeNamed { }, err } return TypeNamed { Name: name }, nil } func (this *parser) parseTypeArray() (TypeArray, error) { err := this.Expect(TokenLBracket) if err != nil { return TypeArray { }, err } err = this.ExpectNext(TokenRBracket) if err != nil { return TypeArray { }, err } err = this.Next() if err != nil { return TypeArray { }, err } typ, err := this.parseType() if err != nil { return TypeArray { }, err } return TypeArray { Element: typ }, nil } func (this *parser) parseTypeTable() (TypeTableDefined, error) { err := this.Expect(TokenLBrace) if err != nil { return TypeTableDefined { }, err } err = this.Next() if err != nil { return TypeTableDefined { }, err } typ := TypeTableDefined { Fields: make(map[uint16] Field), } for { doc := "" for { err := this.ExpectDesc("table field", TokenKey, TokenRBrace, TokenComment) if err != nil { return TypeTableDefined { }, err } if this.Kind() == TokenComment { if doc != "" { doc += "\n" } doc += this.parseComment(this.Value()) this.Next() } else { break } } if this.Is(TokenRBrace) { break } key, field, err := this.parseField(doc) if err != nil { return TypeTableDefined { }, err } typ.Fields[key] = field err = this.Expect(TokenComma, TokenRBrace) if err != nil { return TypeTableDefined { }, err } if this.Is(TokenRBrace) { break } err = this.Next() if err != nil { return TypeTableDefined { }, err } } err = this.Next() if err != nil { return TypeTableDefined { }, err } return typ, nil } func (this *parser) parseField(doc string) (uint16, Field, error) { err := this.Expect(TokenKey) if err != nil { return 0, Field { }, err } key, err := this.parseHexNumber(this.Value(), 0xFFFF) if err != nil { return 0, Field { }, err } err = this.ExpectNext(TokenIdent) if err != nil { return 0, Field { }, err } name := this.Value() err = this.Next() if err != nil { return 0, Field { }, err } option := false if this.Kind() == TokenOption { option = true err = this.Next() if err != nil { return 0, Field { }, err } } typ, err := this.parseType() if err != nil { return 0, Field { }, err } return uint16(key), Field { Name: name, Doc: doc, Type: typ, Option: option, }, nil } func (this *parser) parseHexNumber(input string, maxValue int64) (int64, error) { number, err := strconv.ParseInt(input, 16, 64) if err != nil { return 0, parse.Errorf(this.Pos(), "%v", err) } if maxValue > 0 && number > maxValue { return 0, parse.Errorf(this.Pos(), "value too large (max %X)", maxValue) } return number, nil } func (this *parser) parseComment(input string) string { return strings.TrimPrefix(strings.TrimPrefix(input, "//"), " ") }