wrote dsl for ast boiler plate generation
This commit is contained in:
parent
b244f7e3b2
commit
e0dd8ff9d5
16 changed files with 915 additions and 60 deletions
229
ast/gen/parser.go
Normal file
229
ast/gen/parser.go
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue