generate: Add PDL parser
This commit is contained in:
206
generate/parse.go
Normal file
206
generate/parse.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package generate
|
||||
|
||||
import "io"
|
||||
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] Type {
|
||||
"U8": TypeInt { Bits: 8 },
|
||||
"U16": TypeInt { Bits: 16 },
|
||||
"U32": TypeInt { Bits: 32 },
|
||||
"U64": TypeInt { Bits: 64 },
|
||||
"U128": TypeInt { Bits: 128 },
|
||||
"U256": TypeInt { Bits: 256 },
|
||||
"I8": TypeInt { Bits: 8, Signed: true },
|
||||
"I16": TypeInt { Bits: 16, Signed: true },
|
||||
"I32": TypeInt { Bits: 32, Signed: true },
|
||||
"I64": TypeInt { Bits: 64, Signed: true },
|
||||
"I128": TypeInt { Bits: 128, Signed: true },
|
||||
"I256": TypeInt { Bits: 256, Signed: true },
|
||||
"F16": TypeFloat { Bits: 16 },
|
||||
"F32": TypeFloat { Bits: 32 },
|
||||
"F64": TypeFloat { Bits: 64 },
|
||||
"F128": TypeFloat { Bits: 128 },
|
||||
"F256": TypeFloat { Bits: 256 },
|
||||
"String": TypeString { },
|
||||
"Buffer": TypeBuffer { },
|
||||
"Table": TypeTable { },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ParseReader(reader io.Reader) (*Protocol, error) {
|
||||
lx, err := Lex("test.pdl", 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 {
|
||||
err := this.ExpectDesc("message or typedef", TokenMethod, TokenIdent)
|
||||
if err != nil { return err }
|
||||
if this.EOF() { return nil }
|
||||
|
||||
switch this.Kind() {
|
||||
case TokenMethod: return this.parseMessage()
|
||||
case TokenIdent: return this.parseTypedef()
|
||||
}
|
||||
panic("bug")
|
||||
}
|
||||
|
||||
func (this *parser) parseMessage() 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,
|
||||
Type: typ,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *parser) parseTypedef() 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] = 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:
|
||||
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 {
|
||||
err := this.ExpectDesc("table field", TokenKey, TokenRBrace)
|
||||
if err != nil { return TypeTableDefined { }, err }
|
||||
if this.Is(TokenRBrace) {
|
||||
break
|
||||
}
|
||||
key, field, err := this.parseField()
|
||||
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() (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 }
|
||||
typ, err := this.parseType()
|
||||
if err != nil { return 0, Field { }, err }
|
||||
return uint16(key), Field {
|
||||
Name: name,
|
||||
Type: typ,
|
||||
}, 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
|
||||
}
|
||||
Reference in New Issue
Block a user