Skip to content

Commit

Permalink
Generate a tree walker API to allow simple access to important nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Gordon committed Feb 13, 2020
1 parent b818867 commit 160bcc0
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 57 deletions.
37 changes: 36 additions & 1 deletion cmd/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,16 @@ func (c {{.CtxName}}) All{{.ChildName}}() []{{.RetType}} {
func (c {{.CtxName}}) One{{.ChildName}}() {{.RetType}} {
return {{.RetType}}{ast.First(c.Node, "{{.Child}}")}
}
`

const walkerTemplate = `
func Walk{{.CtxName}}(node {{.CtxName}}, ops WalkerOps) {
if fn := ops.Enter{{.CtxName}}; fn != nil { fn(tree) }
for _, child := range node.All{{.ChildName}}() { Walk{{.ChildName}}(child, ops) }
if fn := ops.Exit{{.CtxName}}; fn != nil { fn(tree) }
}
`

Expand Down Expand Up @@ -325,10 +335,14 @@ func makeContextTypes(tree ast.Node) string {
if err != nil {
panic(err)
}
tokentmpl, err := template.New("funcs").Parse(tokenGetterTemplate)
tokentmpl, err := template.New("token").Parse(tokenGetterTemplate)
if err != nil {
panic(err)
}

var walkerOpsBuf bytes.Buffer
walkerOpsBuf.WriteString("\ntype WalkerOps struct {\n")

allIdents := wbnf.IdentMap(tree.(ast.Branch))
for _, rule := range sortMapKeys(allIdents) {
idents := allIdents[rule]
Expand All @@ -342,6 +356,14 @@ func (c %s) String() string {
}
`, typename))
}
// walker func start
var walkerbuf bytes.Buffer
walkerbuf.WriteString(strings.ReplaceAll(`func Walk{{.CtxName}}(node {{.CtxName}}, ops WalkerOps) {
if fn := ops.Enter{{.CtxName}}; fn != nil { fn(node) }`+"\n", "{{.CtxName}}", typename))

walkerOpsBuf.WriteString(strings.ReplaceAll("Enter{{.CtxName}} func ({{.CtxName}})\n", "{{.CtxName}}", typename))
walkerOpsBuf.WriteString(strings.ReplaceAll("Exit{{.CtxName}} func ({{.CtxName}})\n", "{{.CtxName}}", typename))

for _, id := range idents {
if id == "@" {
id = rule
Expand Down Expand Up @@ -371,14 +393,27 @@ func (c %s) Choice() int {
data.RetType = strcase.ToCamel(strings.ToLower(parts[1])) + "Node"
}
tmpl.Execute(&out, data)
if !strings.Contains(id, "@") {
text := strings.ReplaceAll("for _, child := range node.All{{}}() { Walk{{}}Node(child, ops) }\n", "{{}}",
data.ChildName)
walkerbuf.WriteString(text)
}
}

walkerbuf.WriteString(strings.ReplaceAll("\n if fn := ops.Exit{{.CtxName}}; fn != nil { fn(node) } }\n",
"{{.CtxName}}", typename))
out.Write(walkerbuf.Bytes())
}

walkerOpsBuf.WriteString("\n}\n")
out.Write(walkerOpsBuf.Bytes())

return out.String()
}

func makeExternalApiFuncs(startRule string) string {
tmpl := `
func (w WalkerOps) Walk(tree {{.CtxName}}) { Walk{{.CtxName}}(tree, w) }
func (c {{.CtxName}}) GetAstNode() ast.Node { return c.Node }
func New{{.CtxName}}(from ast.Node) {{.CtxName}} { return {{.CtxName}}{ from } }
Expand Down
70 changes: 15 additions & 55 deletions wbnf/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,42 @@ import (
"fmt"
"regexp"
"strconv"

"github.com/arr-ai/wbnf/parser"
)

func findDefinedRules(tree GrammarNode) map[string]struct{} {
out := map[string]struct{}{}
for _, stmt := range tree.AllStmt() {
for _, prod := range stmt.AllProd() {
out[prod.OneIdent().Scanner().String()] = struct{}{}
out[prod.OneIdent().String()] = struct{}{}
}
}
return out
}

func validate(tree GrammarNode) error {
v := validator{}
return v.Validate(tree)
}

type validator struct {
knownRules map[string]struct{}
err []error
}

func (v *validator) Error() string {
return fmt.Sprint(v.err)
}

func (v *validator) Validate(tree GrammarNode) error {
v.knownRules = findDefinedRules(tree)

for _, stmt := range tree.AllStmt() {
v.validateStmt(stmt)
v := validator{
knownRules: findDefinedRules(tree),
}

if len(v.err) > 0 {
return v
ops := WalkerOps{
EnterAtomNode: v.validateAtom,
EnterQuantNode: v.validateQuant,
}
return nil
}
ops.Walk(tree)

func (v *validator) validateStmt(tree StmtNode) {
for _, prod := range tree.AllProd() {
v.validateProd(prod)
if len(v.err) == 0 {
return nil
}
return &v
}

func (v *validator) validateProd(tree ProdNode) {
name := tree.OneIdent().Scanner().String()
if name == parser.WrapRE.String() {
// todo: validate the .wrapRE rule
} else {
for _, term := range tree.AllTerm() {
v.validateTerm(term)
}
}
}

func (v *validator) validateTerm(tree TermNode) {
for _, term := range tree.AllTerm() {
v.validateTerm(term)
}
for _, named := range tree.AllNamed() {
v.validateNamed(named)
}
for _, quant := range tree.AllQuant() {
v.validateQuant(quant)
}
type validator struct {
knownRules map[string]struct{}
err []error
}

func (v *validator) validateNamed(tree NamedNode) {
if atom := tree.OneAtom(); atom.Node != nil {
v.validateAtom(atom)
}
func (v *validator) Error() string {
return fmt.Sprint(v.err)
}

func (v *validator) validateAtom(tree AtomNode) {
Expand All @@ -96,8 +58,6 @@ func (v *validator) validateAtom(tree AtomNode) {
}
} else if x := tree.OneRef(); x.Node != nil {

} else if x := tree.OneTerm(); x.Node != nil {
v.validateTerm(x)
}
}

Expand Down
Loading

0 comments on commit 160bcc0

Please sign in to comment.