Example #1
0
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
}
Example #2
0
//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
}
Example #3
0
//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
}
Example #4
0
//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
}
Example #5
0
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")
		}
	}

}
Example #6
0
//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{}),
	}
}