229 lines
4.2 KiB
Go
229 lines
4.2 KiB
Go
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()
|
|
}
|