// Abstract Syntax Tree package ast import ( "fmt" "strings" ) import . "src.eruta.nl/beoran/ll1/common" // Species is the kind of Ast node it is. It also has some methods on it. type Species interface { Self() Species String() string } // BasicSpecies is a basic implementation of a species. type BasicSpecies struct { Name string } func (bs BasicSpecies) Self() Species { return bs } func (bs BasicSpecies) String() string { return bs.Name } func MakeSpecies(name string) Species { return BasicSpecies{Name: name} } // Astro is an abstract syntax tree that is read only type Astro interface { Value Species Parent() Ast Children() []Ast Token() Token } // Ast is an abstract syntax tree with read/write capabilities type Ast interface { Astro SetParent(Ast) AppendChild(child Ast) Ast } // BasicAst is a basic implementation of an AST. type BasicAst struct { Species parent Ast children []Ast token Token } func (ast BasicAst) Value() Value { return ast.token.Value() } func AppendChild(parent Ast, child Ast) Ast { basicParent := parent.(*BasicAst) return basicParent.AppendChild(child) } func New(kind Species, parent Ast, children []Ast, token Token) *BasicAst { ast := &BasicAst{Species: kind, parent: parent, token: token} return ast.AppendChildren(children...) } func (ast *BasicAst) AppendChildren(children ...Ast) *BasicAst { for _, child := range children { ast.AppendChild(child) } return ast } func (ast *BasicAst) AppendChild(child Ast) Ast { child.SetParent(ast) ast.children = append(ast.children, child) return ast } func NewChild(ast Ast, spec Species, token Token) Ast { child := New(spec, ast, make([]Ast, 0), token) ast.AppendChild(child) return child } func (ast BasicAst) IsKind(Species Species) bool { return ast.Species == Species } func (ast BasicAst) IsError() bool { return ast.token.Kind() == ErrorKind } func (ast BasicAst) IsNone() bool { return ast.Species == nil } func (ast BasicAst) Token() Token { return ast.token } func (ast BasicAst) Parent() Ast { return ast.parent } func (ast BasicAst) Children() []Ast { return ast.children } func (ast BasicAst) Self() Species { return ast.Species } func (ast *BasicAst) SetParent(parent Ast) { ast.parent = parent } func (ast BasicAst) Child(index int) Ast { count := len(ast.children) if index < 0 || index > count { return nil } return ast.children[index] } func Walk(ast Astro, walker func(node Astro) Astro) Astro { if found := walker(ast); found != nil { return found } for _, child := range ast.Children() { if found := Walk(child, walker); found != nil { return found } } return nil } func (ast BasicAst) String() string { specname := "" if ast.Species != nil { specname = ast.Species.String() } tokval := "" if ast.token != nil { tokval = ast.token.Text() /* if ast.token.Value() != nil { tokval = ast.token.Value().String() } */ } return fmt.Sprintf("Ast %s: %s", specname, tokval) } func Display(ast Astro) { Walk(ast, func(node Astro) Astro { depth := Depth(node) fmt.Printf("%s", strings.Repeat("--", depth)) if node != nil { fmt.Printf("Ast: %s\n", node.String()) } else { fmt.Printf("Ast: nil node\n") } return nil }) } func Dump(ast Astro) string { result := "" Walk(ast, func(node Astro) Astro { depth := Depth(node) result += fmt.Sprintf("%s", strings.Repeat("--", depth)) if node != nil { result += fmt.Sprintf("Ast: %s\n", node.String()) } else { result += fmt.Sprintf("Ast: nil node\n") } return nil }) return result } func Depth(ast Astro) int { var depth int = 0 parent := ast.Parent() for parent != nil { depth++ parent = parent.Parent() } return depth } func CountChildren(ast Astro) int { return len(ast.Children()) } func IsError(ast Astro) bool { return ast.Token().Kind() == ErrorKind } func Errors(ast Astro) []Astro { res := make([]Astro, 0) Walk(ast, func(node Astro) Astro { if node != nil && IsError(ast) { res = append(res, node) } return nil }) return res } func EmptyAstArray() []Ast { return make([]Ast, 0) } func NewEmptyAst(species Species) *BasicAst { return NewAstWithToken(species, nil) } func NewAstNone() *BasicAst { return NewEmptyAst(nil) } func NewAstWithToken(Species Species, token Token) *BasicAst { return New(Species, nil, EmptyAstArray(), token) } // If AST has errors, return it as a merged error, otherwise returns nil func MergeErrors(ast Ast) error { errlist := Errors(ast) if len(errlist) < 1 { return nil } sep := "" res := "" for _, err := range errlist { res = fmt.Sprintf("%s%s%s", res, sep, err) sep = "\n" } return fmt.Errorf("%s", res) }