func new(g *ast.Grammar, record bool) (*Mem, error) { simp := interp.NewSimplifier(g) if record { simp = simp.OptimizeForRecord() } g = simp.Grammar() refs := ast.NewRefLookup(g) m := &Mem{ refs: refs, simplifier: simp, patterns: newPatternsSet(), zis: newIntsSet(), stackElms: newPairSet(), nullables: newBitsetSet(), Calls: []*CallNode{}, Returns: []map[int]int{}, Escapables: []bool{}, StateToNullable: []int{}, Accept: []bool{}, } start := m.patterns.add([]*ast.Pattern{refs["main"]}) e := &exprToFunc{m: make(map[*ast.Expr]funcs.Bool)} for _, p := range refs { p.Walk(e) if e.err != nil { return nil, e.err } } m.funcs = e.m m.Start = start return m, nil }
//FieldNamesToNumbers rewrites field names contained in the grammar to their respective field numbers found in the protocol buffer filedescriptorset. //This allows for more speedy field comparisons in validation when used in conjunction with the ProtoNumParser. func FieldNamesToNumbers(pkgName, msgName string, desc *descriptor.FileDescriptorSet, grammar *ast.Grammar) (*ast.Grammar, error) { g := grammar.Clone() descMap, err := proto.NewDescriptorMap(pkgName, msgName, desc) if err != nil { return nil, err } root := descMap.GetRoot() refs := ast.NewRefLookup(g) nameToNumber := &nameToNumber{ refs: make(map[string]*context), descMap: descMap, } nameToNumber.refs["main"] = &context{root, false} newRefs := make(map[string]*ast.Pattern) oldContexts := 0 newContexts := 1 for oldContexts != newContexts { oldContexts = newContexts for name, context := range nameToNumber.refs { newp, err := nameToNumber.translate(context, refs[name]) if err != nil { return nil, err } newRefs[name] = newp } newContexts = len(nameToNumber.refs) } return ast.NewGrammar(newRefs), nil }
//Interpret interprets the grammar given the parser and returns whether the parser is valid given the grammar. //Interpret uses derivatives and simplification to recusively derive the resulting grammar. //This resulting grammar's nullability then represents the result of the function. //This implementation does not handle immediate recursion, see the HasRecursion function. func Interpret(g *ast.Grammar, parser parser.Interface) (bool, error) { refs := ast.NewRefLookup(g) finals, err := deriv(refs, []*ast.Pattern{refs["main"]}, parser) if err != nil { return false, err } return Nullable(refs, finals[0]), nil }
//HasRecursion returns whether the grammar contains any references that does not have a TreeNode pattern in between. //For example: // // #main = @main // #main = (A:* | @main) // //Recursion can still be used when a TreeNode pattern is placed between references, for example: // // #main = (A:@main | <empty>) func HasRecursion(g *ast.Grammar) bool { refs := ast.NewRefLookup(g) for name := range refs { visited := make(map[*ast.Pattern]bool) if hasRecursion(visited, refs, refs[name]) { return true } } return false }
func check(t *testing.T, g *ast.Grammar) { refs := ast.NewRefLookup(g) for name := range refs { if !onlyUintNames(refs[name]) { t.Fatalf("expected only uint names") } if anyStringNames(refs[name]) { t.Fatalf("expected only uint names") } } }
//NewSimplifier returns a new Simplifier that is used to simplify a grammar and its patterns. func NewSimplifier(g *ast.Grammar) Simplifier { return &simplifier{ refs: ast.NewRefLookup(g), cache: make(map[*ast.Pattern]struct{}), } }