package main import ( "errors" "fmt" ) type visitor interface { visitASTDefinitionsNode(a *astDefinitionsNode) (string, error) visitName(n *nameNode) (string, error) visitIdentifier(i *identifierNode) (string, error) visitField(g *fieldNode) (string, error) visitDefinition(d *definitionNode) (string, error) } type node interface { accept(v visitor) (string, error) } type astDefinitionsNode struct { name string definitions []node } // accept implements node. func (a *astDefinitionsNode) accept(v visitor) (string, error) { return v.visitASTDefinitionsNode(a) } var _ node = new(astDefinitionsNode) type nameNode struct { value string } // accept implements node. func (n *nameNode) accept(v visitor) (string, error) { return v.visitName(n) } var _ node = new(nameNode) type identifierNode struct { value string } // accept implements node. func (i *identifierNode) accept(v visitor) (string, error) { return v.visitIdentifier(i) } var _ node = new(identifierNode) type fieldNode struct { left node right node } // accept implements node. func (f *fieldNode) accept(v visitor) (string, error) { return v.visitField(f) } var _ node = new(fieldNode) type definitionNode struct { identifier node fields []node } // accept implements node. func (d *definitionNode) accept(v visitor) (string, error) { return v.visitDefinition(d) } var _ node = new(definitionNode) type parser struct { tokens []*token current int } func (p *parser) peek() *token { return p.tokens[p.current] } func (p *parser) isAtEnd() bool { return p.peek().Type == tokenTypeEOF } func (p *parser) check(t tokenType) bool { if p.isAtEnd() { return false } return p.peek().Type == t } func (p *parser) match(types ...tokenType) bool { for _, t := range types { if p.check(t) { p.advance() return true } } return false } func (p *parser) advance() *token { t := p.tokens[p.current] p.current += 1 return t } func (p *parser) consume(t tokenType, msg string) (*token, error) { if p.check(t) { return p.advance(), nil } return nil, errors.New(msg) } func (p *parser) previous() *token { return p.tokens[p.current-1] } func (p *parser) astDefinitions() (node, error) { if p.match(tokenTypeName) { name := p.previous() defs := []node{} for !p.isAtEnd() { def, err := p.definition() if err != nil { return nil, err } defs = append(defs, def) } return &astDefinitionsNode{ name: name.Lexeme, definitions: defs, }, nil } return nil, errors.New("expected name definition at start of file") } // definition -> identifier "[" field+ "]" func (p *parser) definition() (node, error) { id, err := p.identifier() if err != nil { return nil, err } if p.match(tokenTypeLeftBracket) { fields := []node{} for !p.check(tokenTypeRightBracket) && !p.isAtEnd() { f, err := p.field() if err != nil { return nil, err } fields = append(fields, f) } if p.isAtEnd() { return nil, errors.New(fmt.Sprintf("expected ']' after field definitions in '%s', got EOF", debug(id))) } _, err := p.consume(tokenTypeRightBracket, fmt.Sprintf("expected ']' after field definitions in '%s', got EOF", debug(id))) if err != nil { return nil, err } return &definitionNode{ identifier: id, fields: fields, }, nil } return nil, errors.New(fmt.Sprintf("expected '[' after identifier '%s'", debug(id))) } // field -> identifier "=" identifier ";" func (p *parser) field() (node, error) { left, err := p.identifier() if err != nil { return nil, err } if p.match(tokenTypeEqual) { right, err := p.identifier() if err != nil { return nil, err } if p.match(tokenTypeSemicolon) { return &fieldNode{ left: left, right: right, }, nil } return nil, errors.New(fmt.Sprintf("expected ';' at end of field '%s'", debug(left))) } return nil, errors.New(fmt.Sprintf("expected '=' after identifier '%s'", debug(left))) } func (p *parser) identifier() (node, error) { if p.match(tokenTypeIdentifier) { return &identifierNode{ value: p.previous().Lexeme, }, nil } return nil, errors.New("expected identifier") } func parse(tokens []*token) (node, error) { p := new(parser) p.tokens = tokens p.current = 0 return p.astDefinitions() }