// multaut displays a multi-NFA and multi-DFA func multaut(w http.ResponseWriter, r *http.Request) { // must read all input before writing anything exprlist := getexprs(r) nx := len(exprlist) // parse and echo the input treelist := make([]rx.Node, 0, nx) putheader(w, r, "Multi-expression Automata") fmt.Fprintf(w, "<P class=xleading>%d expressions:\n", nx) for i, s := range exprlist { fmt.Fprintf(w, "<BR><B>%c:</B> %s\n", rx.AcceptLabels[i], hx(s)) tree, err := rx.Parse(s) if !showerror(w, err) { treelist = append(treelist, rx.Augment(tree, i)) } } if nx > 0 && len(treelist) == nx { // if no errors dfa := rx.MultiDFA(treelist) // build combined DFA dmin := dfa.Minimize() showaut(w, dmin, exprlist) } putfooter(w, r) }
// combos responds to a comparison request for multiple expressions func combos(w http.ResponseWriter, r *http.Request) { // must read all input before writing anything // first get the expressions, trimming leading/trailing blanks exprlist := getexprs(r) nx := len(exprlist) // then get the test strings; these are not trimmed testlist := make([]string, 0) for i := 0; i < maxTest; i++ { arg := r.FormValue(fmt.Sprintf("v%d", baseTest+i)) if arg != "" { testlist = append(testlist, arg) } } // parse and echo the input putheader(w, r, "Compare Expressions") fmt.Fprintf(w, "<P class=xleading>%d expressions:\n", nx) treelist := lpxc(w, exprlist) if nx > 0 && len(treelist) == nx { // if no errors dfa := rx.MultiDFA(treelist) // build combined DFA trylist := make([]string, 0) // list of strings to try // tests from form submission for _, x := range testlist { trylist = append(trylist, x) } trylist = append(trylist, DRAWLINE) // examples from DFA synthx := dfa.Synthesize() // synthesize from DFA for _, x := range synthx { // put results on list trylist = append(trylist, x.Example) } // DON'T separate parse tree examples // (confusing in the the absence of a published explanation) // trylist = append(trylist, DRAWLINE) // examples from parse tree for i := 0; i < nx; i++ { trylist = append(trylist, rx.Specimen(treelist[i], 1)) trylist = append(trylist, rx.Specimen(treelist[i], 2)) trylist = append(trylist, rx.Specimen(treelist[i], 3)) trylist = append(trylist, rx.Specimen(treelist[i], 5)) } fmt.Fprintf(w, "<H2>Results</H2>\n") showgrid(w, dfa, nx, trylist) // show examples } fmt.Fprintln(w, "<P> ") formlink(w, "/multaut", exprlist, "Show automata states") formlink(w, "/drawNFA", exprlist, "Draw the NFA") formlink(w, "/drawDFA", exprlist, "Draw the DFA") fmt.Fprint(w, "<h2>Try again?</h2>") putform(w, "/combos", "Enter regular expressions:", nExpr, exprlist, nTest, testlist) putfooter(w, r) }
func main() { // get command line options nways := 1 if len(os.Args) == 3 { nways, _ = strconv.Atoi(os.Args[2]) } if len(os.Args) < 2 || len(os.Args) > 3 || nways < 1 { log.Fatal("usage: rxtime exprfile [n]") } filename := os.Args[1] // load expressions from file exprs := rx.LoadExpressions(filename, nil) nexprs := len(exprs) if nways < 1 || nways > nexprs { log.Fatal(fmt.Sprintf( "cannot combine %d expressions(s) in %d way(s)", nexprs, nways)) } // record individual complexity scores and make augmented parse trees cx := make([]int, nexprs) for i, t := range exprs { cx[i] = rx.ComplexityScore(t.Tree) t.Tree = rx.Augment(t.Tree, i) } // initialize index list for first combination {0,1,2...} xlist := make([]int, nways) for i := range xlist { xlist[i] = i } // try all possible n-way combinations by varying the index list tlist := make([]rx.Node, nways) for xlist != nil { for i, x := range xlist { tlist[i] = exprs[x].Tree } _ = rxsys.Interval() // reset timer dfa1 := rx.MultiDFA(tlist) // make DFA t1 := rxsys.Interval().Seconds() // measure time dfa2 := dfa1.Minimize() // minimize DFA t2 := rxsys.Interval().Seconds() // measure time fmt.Printf("%6d %6d %8.3f %8.3f", len(dfa1.Dstates), len(dfa2.Dstates), t1, t2) if nways == 1 { fmt.Printf(" %6d", cx[xlist[0]]) } fmt.Print(" {") for _, x := range xlist { fmt.Printf(" %d", x) } fmt.Print(" }\n") xlist = advance(xlist, nexprs) // get next combination } }
// main is the overall controller func main() { setup() // initialize defer pprof.StopCPUProfile() // may have started profiling exprs, trees := load() // load input timestamp(fmt.Sprintf( "load %d expressions", len(exprs))) if !*opt['m'] { // if nothing uses a combined DFA return } if *opt['i'] { // if preceded by individual processing fmt.Println() rx.ShowLabel(os.Stdout, "MERGING EXPRESSIONS") fmt.Println() if *opt['R'] { rand.Seed(int64(seedvalue)) } } dfa := rx.MultiDFA(trees) timestamp(fmt.Sprintf( "make merged DFA of %d states", len(dfa.Dstates))) dfa = showDFA(dfa, "Combined Tree", *opt['t']) // generate graphs if requested label := exprs[0].Expr if len(exprs) > 1 { label = fmt.Sprintf("%d expressions", len(exprs)) } if *val['N'] != "" { rx.WriteGraph(*val['N'], func(w io.Writer) { dfa.GraphNFA(w, "NFA: "+label) }) } if *val['D'] != "" { labels := "" if len(exprs) > 1 && len(exprs) <= len(rx.AcceptLabels) { labels = rx.AcceptLabels } rx.WriteGraph(*val['D'], func(w io.Writer) { dfa.ToDot(w, "DFA: "+label, labels) }) } }
func main() { rflag := flag.Bool("R", false, "reproducible output") flag.Parse() if *rflag { rand.Seed(0) } else { rand.Seed(int64(time.Now().Nanosecond())) } // load and process regexps exprs := make([]*RegEx, 0) tlist := make([]rx.Node, 0) rx.LoadExpressions(rx.OneInputFile(), func(l *rx.RegExParsed) { if l.Err != nil { fmt.Fprintln(os.Stderr, l.Err) } if l.Tree != nil { atree := rx.Augment(l.Tree, len(tlist)) tlist = append(tlist, atree) exprs = append(exprs, &RegEx{len(exprs), l.Expr}) } }) // echo the input with index numbers fmt.Print(`{"Expressions":`) rx.Jlist(os.Stdout, exprs) fmt.Println(",") // build the DFA and produce examples synthx := rx.MultiDFA(tlist).Synthesize() // convert into expected form with int array replacing BitSet results := make([]*Example, 0, len(synthx)) for _, x := range synthx { results = append(results, &Example{x.State, x.RXset.Members(), x.Example}) } // output the array of synthesized examples fmt.Print(`"Examples":`) rx.Jlist(os.Stdout, results) fmt.Println("}") }
// draw produces a Dot file for rendering a DFA or NFA in the user's browser. func draw(w http.ResponseWriter, r *http.Request, which string) { exprlist := getexprs(r) // must load data before writing anything nx := len(exprlist) putheader(w, r, which+" Graph") // write page header fmt.Fprintln(w, "<P class=xleading>") treelist := make([]rx.Node, 0) for i, e := range exprlist { if nx > 1 { fmt.Fprintf(w, "%c. ", rx.AcceptLabels[i]) } fmt.Fprintf(w, "%s<BR>\n", hx(e)) tree, err := rx.Parse(e) if !showerror(w, err) { treelist = append(treelist, rx.Augment(tree, i)) } } if nx > 0 && len(treelist) == nx { // if no errors dfa := rx.MultiDFA(treelist) // build combined DFA dmin := dfa.Minimize() // minimize it fmt.Fprintln(w, `<script type="text/vnd.graphviz" id="graph">`) if which == "NFA" { dmin.GraphNFA(w, "") } else if nx == 1 { dmin.ToDot(w, "", "") } else { which = "Multi" dmin.ToDot(w, "", rx.AcceptLabels) } fmt.Fprintln(w, `</script>`) tDraw.Execute(w, which) } putfooter(w, r) }
// refcompare advertises the Compare page func refcompare(w http.ResponseWriter) { fmt.Fprintf(w, ` <P> The <A HREF="/compare">Compare</A> page accepts multiple expressions and shows how their languages overlap or differ. The results page shows synthesized examples and indicates which expressions they match. You can also submit your own examples for testing. Again, there are links to produce automata diagrams. <DIV CLASS="inblock xleading"> %d expressions: `, len(HomeCompare)) treelist := lpxc(w, HomeCompare) dfa := rx.MultiDFA(treelist) synthx := dfa.Synthesize() trylist := make([]string, 0) for _, x := range synthx { trylist = append(trylist, x.Example) } fmt.Fprintln(w, `<BR><BR>`) showgrid(w, dfa, len(treelist), trylist) fmt.Fprintln(w, `<DIV class="rside smaller">`) gencomp(w, "submit this example to see full output", HomeCompare) fmt.Fprintln(w, `</DIV></DIV>`) }
func main() { args := os.Args if len(args) != 3 { log.Fatal("usage: rxx efile sfile") } ename := args[1] sfile := rx.MkScanner(args[2]) labels := "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" elist := make([]tester, 0, len(labels)) // load and compile regexps fmt.Println() tlist := make([]rx.Node, 0) // list of valid parse trees rx.LoadExpressions(ename, func(x *rx.RegExParsed) { spec := x.Expr ptree := x.Tree err := x.Err if err != nil { fmt.Printf("ERR %s\n", spec) elist = append(elist, tester{" ", spec, nil, 0}) } else if ptree == nil { fmt.Printf(" %s\n", spec) elist = append(elist, tester{" ", spec, nil, 0}) } else { i := len(tlist) if i >= len(labels) { log.Fatal("too many regular expressions") } label := string(labels[i : i+1]) fmt.Printf("%s: %s\n", label, spec) atree := rx.Augment(ptree, len(tlist)) tlist = append(tlist, atree) elist = append(elist, tester{label, spec, ptree, i}) } }) dfa := rx.MultiDFA(tlist) _ = dfa.Minimize() // should have no effect _ = dfa.Minimize() // should again have no effect dfa = dfa.Minimize() // not necessary, but a good stress test dfa = dfa.Minimize() // especially if done more than once // read and test candidate strings fmt.Println() for sfile.Scan() { s := string(sfile.Bytes()) results := dfa.Accepts(s) if results == nil { results = &rx.BitSet{} } for _, e := range elist { if e.tree == nil { fmt.Print(" ") } else { if results.Test(e.index) { fmt.Print(e.label) } else { fmt.Print("-") } } } fmt.Printf(" %s\n", s) } rx.CkErr(sfile.Err()) }
//determines best question by the number of candidates that accept the question word. func askBulk2(answers *rx.BitSet, qns []string, perm []int, cand []*rx.DFA, expns []*rx.RegExParsed, numPerGrp int) (*rx.BitSet, []string) { numGroups := math.Floor(float64(len(cand) / numPerGrp)) word := "" if len(cand) < numPerGrp { numPerGrp = len(cand) } fmt.Println("length cand = ", len(cand)) subjlist := make([]int, 0, *group) subjtrees := make([]rx.Node, 0, *group) expressions := make([]*RegEx, 0, len(cand)) numKeep := make([]int, 0, int(numGroups)) overlap := make([]*rx.BitSet, 0, int(numGroups)) it := 0 subjlist = make([]int, 0, *group) subjtrees = make([]rx.Node, 0, *group) expressions = make([]*RegEx, 0, len(cand)) //create the grouping for j := it * int(numPerGrp); j < (it+1)*numPerGrp; j++ { subjlist = append(subjlist, perm[j]) subjtrees = append(subjtrees, cand[perm[j]].Tree) expressions = append(expressions, &RegEx{perm[j], expns[perm[j]].Expr}) fmt.Printf("rx { %d } %s \n", perm[j], expns[perm[j]].Expr) } //construct multi DFA fmt.Printf("Constructing multiDFA\n") dfa := rx.MultiDFA(subjtrees) //dfa = dfa.Minimize() h := dfa.Synthesize() for _, ex := range h { fmt.Printf("eg %s %s\n", ex.RXset, ex.Example) } u := make([]*rx.BitSet, 0, len(h)) for i := 0; i < len(h); i++ { u = append(u, h[i].RXset) } //candidates alive := new(rx.BitSet) for i := 0; i < len(expressions); i++ { alive.Set(expressions[i].Index) } total := 0 max := 0 diff := float64(len(cand)) half := math.Floor(float64(len(cand) / 2)) maxWord := "" over := new(rx.BitSet) prevWord := make([]string, 0, len(cand)) //iterate through all example words generated for j := 0; j < len(h); j++ { total = 0 over = new(rx.BitSet) //tracks which candidates accept the question word //iterate through all candidates for i := 0; i < len(cand); i++ { //if the candidate accepts the example word, increment total and update bitset if cand[i].Accepts(h[j].Example) != nil { total++ over.Set(i) } } //order question words according to the number of candidates that accept them if math.Abs(float64(total)-half) < float64(diff) { diff = math.Abs(float64(total) - half) max = total maxWord = h[j].Example prevWord = append(prevWord, maxWord) overlap = append(overlap, over) } } fmt.Println("max = ", max, " max word: ", maxWord) qns = append(qns, maxWord) numKeep = append(numKeep, int(max)) fmt.Println("alive =", len(cand), ", max eliminate = ", max, ", maxWord =", maxWord) fmt.Printf("\n") answers = new(rx.BitSet) maybe := 0 for i := 0; i < len(qns); i++ { //if answer is maybe, ask the next question in the list begin: if maybe > 0 { if len(prevWord) == 1 { qns[i] = h[maybe].Example fmt.Println("Does your language accept this word: ", h[maybe].Example) } else { qns[i] = prevWord[len(prevWord)-maybe-1] fmt.Printf("Does your language accept this word: %s, max over = %s\n", prevWord[len(prevWord)-maybe-1], overlap[len(overlap)-maybe-1].String()) } } else { fmt.Println("Does your language accept this word: ", qns[i]) } var input string _, e1 := fmt.Scan(&input) if e1 != nil { fmt.Printf("err = %#v\n", e1) os.Exit(2) } if input == "Yes" || input == "y" || input == "Y" || input == "yes" { answers.Set(i) } else if input == "No" || input == "N" || input == "n" || input == "no" { } else if input == "maybe" || input == "m" || input == "M" { maybe++ goto begin } else { fmt.Println("That is not a valid expression, please try again.") fmt.Println("Does your language accept this word: ", word) goto begin } } fmt.Println("answers = ", answers.String()) for i := 0; i < len(qns); i++ { fmt.Println(qns[i]) } return answers, qns }
//used when candidate set partitioned. Asks the best question from each partition and records answers func askBulk(answers *rx.BitSet, qns []string, perm []int, cand []*rx.DFA, expns []*rx.RegExParsed, numPerGrp int) (*rx.BitSet, []string) { numGroups := math.Floor(float64(len(cand) / numPerGrp)) fmt.Println("num groups = ", numGroups) word := "" //modTest := math.Mod(float64(len(cand)), numGroups) //fmt.Println("testMod = ", modTest) subjlist := make([]int, 0, *group) subjtrees := make([]rx.Node, 0, *group) expressions := make([]*RegEx, 0, len(cand)) for it := 0; it < int(numGroups/2); it++ { subjlist = make([]int, 0, *group) subjtrees = make([]rx.Node, 0, *group) expressions = make([]*RegEx, 0, len(cand)) //create the grouping for j := it * int(numPerGrp); j < (it+1)*numPerGrp; j++ { subjlist = append(subjlist, perm[j]) subjtrees = append(subjtrees, cand[perm[j]].Tree) expressions = append(expressions, &RegEx{perm[j], expns[perm[j]].Expr}) fmt.Printf("rx { %d } %s\n", perm[j], expns[perm[j]].Expr) } //construct multiDFA fmt.Printf("Constructing multiDFA it = %d\n", it) dfa := rx.MultiDFA(subjtrees) //dfa = dfa.Minimize() h := dfa.Synthesize() //for _, ex := range h { //fmt.Printf("eg %s %s\n", ex.RXset, ex.Example) //} u := make([]*rx.BitSet, 0, len(h)) //possible questions for i := 0; i < len(h); i++ { u = append(u, h[i].RXset) } //candidate set alive := new(rx.BitSet) for i := 0; i < len(expressions); i++ { alive.Set(expressions[i].Index) } //fmt.Println("alive = ", alive.String()); //find best question and ask it i := 0 n := float64(alive.Count() / 2) size := math.Floor(n) if alive.Count() < 4 && math.Mod(float64(alive.Count()), 2) == 1 { size = size + 1 } cur := u[i].Count() c := float64(cur) hM := float64(math.Abs(c - size)) word = h[i].Example for i := 0; i < len(u); i++ { c = float64(u[i].Count()) if math.Abs(c-size) == 0 { word = h[i].Example break } else if math.Abs(c-size) < hM { hM = math.Abs(c - size) word = h[i].Example } else { /*do nothing*/ } } fmt.Println("before qns append") qns = append(qns, word) } //rate questions and print rating rate := rateQns(qns, cand) for i := 0; i < len(rate); i++ { fmt.Println(rate[i], " dfas accepted this word ", qns[i]) } for i := 0; i < len(qns); i++ { begin: //ask questions fmt.Println("Does your language accept this word (): ", qns[i]) var input string _, e1 := fmt.Scan(&input) if e1 != nil { fmt.Printf("err = %#v\n", e1) os.Exit(2) } if input == "Yes" || input == "y" || input == "Y" || input == "yes" { answers.Set(i) } else if input == "No" || input == "N" || input == "n" || input == "no" { } else { fmt.Println("That is not a valid expression, please try again.") fmt.Println("Does your language accept this word: ", word) goto begin } } fmt.Println("answers = ", answers.String()) return answers, qns }