lox-go/ast/gen/parser.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()
}