func format(t *testing.T, input string, expected string) { g, err := parser.ParseGrammar(input) if err != nil { t.Errorf("parse error: %v given input: <%s>", err, input) return } g.Format() output := g.String() if expected != output { expectedStr := strings.Replace(strings.Replace(expected, " ", "_", -1), "\t", "<tab>", -1) outputStr := strings.Replace(strings.Replace(output, " ", "_", -1), "\t", "<tab>", -1) t.Errorf("format failure: expected \n<%s>, but got \n<%s>", expectedStr, outputStr) } input = expected g2, err := parser.ParseGrammar(input) if err != nil { t.Errorf("parse error2: %v given input: <%s>", err, input) return } g2.Format() output = g.String() if expected != output { expectedStr := strings.Replace(strings.Replace(expected, " ", "_", -1), "\t", "<tab>", -1) outputStr := strings.Replace(strings.Replace(output, " ", "_", -1), "\t", "<tab>", -1) t.Errorf("format failure2: expected \n<%s>, but got \n<%s>", expectedStr, outputStr) } }
func BenchmarkCompileOrProtoName(b *testing.B) { st, err := parser.ParseGrammar(typewriterOrQueryStr) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { if _, err := Compile(st); err != nil { b.Fatal(err) } } }
func benchCompile(b *testing.B, str string) { st, err := parser.ParseGrammar(str) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { if _, err := Compile(st); err != nil { b.Fatal(err) } } }
func TestExplosionAndSameTree(t *testing.T) { var input = `( .A:.B:.DeepLevel:.DeeperLevel:.DeepestLevel:->contains($string,"el") & ( .A:.B:.Rs:._:->eq("~a",$string) & ( .A:.B:.NumI32:->contains($int,[]int{28,1,52}) & ( .A:.B:.NumI64:>= 1 & ( .A:.B:.NumU32:<= uint(4) & ( .A:.B:.NumU64:== uint(4) & ( .A:.B:.YesNo:== true & ( .A:.B:.BS:== []byte{0x3, 0x2, 0x1, 0x0} & .A:.B:.Uuid: == []byte{0x3, 0x2, 0x1, 0x0} ) ) ) ) ) ) ) )` g, err := parser.ParseGrammar(input) if err != nil { t.Fatal(err) } // This one causes a state explosion of over 14000 states. // Since we know field names can't repeat the simplification can be made for record (JSON and proto) like serialization formats, but not for XML. g = interp.NewSimplifier(g).OptimizeForRecord().Grammar() t.Logf("%v", g) a, err := Compile(g) if err != nil { t.Fatal(err) } t.Logf("number of states %d", len(a.accept)) if len(a.accept) > 1000 { t.Fatal("number of states exploded") } }
func TestRecursionNegative(t *testing.T) { negatives := []string{ `A:@main`, `#main = A:@main`, `#main = (A:* | B:@main)`, `#main = (A:* | @ref) #ref = C:@main`, `#main = (A:* | @ref) #ref = (B:* | D:@main | <empty>)`, } for _, n := range negatives { g, err := parser.ParseGrammar(n) if err != nil { t.Fatal(err) } if HasRecursion(g) { t.Errorf("unexpected recursion for %v", g) } } }
func TestRecursionPositive(t *testing.T) { positives := []string{ `@main`, `#main = @main`, `#main = (A:* | @main)`, `#main = (A:* | @ref) #ref = @main`, `#main = (A:* | @ref) #ref = (B:* | @main | <empty>)`, `#main = A:@ref #ref = @ref`, } for _, p := range positives { g, err := parser.ParseGrammar(p) if err != nil { t.Fatal(err) } if !HasRecursion(g) { t.Errorf("expected recursion for %v", g) } } }
//Parse parses the relapse string into an ast (abstract syntax tree) func Parse(relapse string) (*ast.Grammar, error) { return relapseparser.ParseGrammar(relapse) }