func generateRunner(filename string, testMains []*TestMain) os.Error { src := bytes.NewBufferString("") fmt.Fprint(src, "package main\n\n") fmt.Fprint(src, "import \"sync\"\n") fmt.Fprint(src, "import \"testing\"\n") fmt.Fprint(src, "import (\n") for _, testMain := range testMains { name := testMain.underscorePkgName() fmt.Fprintf(src, "%s \"%s\"\n", name, testMain.pkgName) } fmt.Fprint(src, ")\n") fmt.Fprint(src, "func main() {\n") fmt.Fprint(src, "wg := new(sync.WaitGroup)\n") for _, testMain := range testMains { pkgName := testMain.underscorePkgName() fmt.Fprint(src, "wg.Add(1)\n") fmt.Fprint(src, "go func() {\n") fmt.Fprint(src, "tests := []testing.InternalTest{\n") for _, test := range testMain.tests { testFunc := pkgName + "." + test fmt.Fprintf(src, "{\"%s\", %s},\n", testMain.pkgName+"."+test, testFunc) } fmt.Fprint(src, "}\n") fmt.Fprint(src, "benchmarks := []testing.InternalBenchmark{\n") for _, bench := range testMain.benchmarks { benchFunc := pkgName + "." + bench fmt.Fprintf(src, "{\"%s\", %s},\n", testMain.pkgName+"."+bench, benchFunc) } fmt.Fprint(src, "}\n") fmt.Fprintf(src, "for i := 0; i < %d; i++ {\n", iters) fmt.Fprint(src, "testing.Main(regexp.MatchString, tests)\n") fmt.Fprint(src, "testing.RunBenchmarks(regexp.MatchString, benchmarks)\n") fmt.Fprint(src, "}\n") fmt.Fprint(src, "wg.Done()\n") fmt.Fprint(src, "}()\n\n") } fmt.Fprint(src, "wg.Wait()\n") fmt.Fprint(src, "}\n") file, err := os.Open(filename, os.O_CREAT|os.O_TRUNC|os.O_WRONLY, 0666) if err != nil { return err } defer file.Close() //fmt.Printf("%s\n", string(src.Bytes())) fileNode, err := parser.ParseFile(token.NewFileSet(), filename, src.Bytes(), 0) if err != nil { panic(err) } config := printer.Config{printer.TabIndent, 8} _, err = config.Fprint(file, token.NewFileSet(), fileNode) if err != nil { return err } return nil }
func (f *File) print(w io.Writer) { cfg := printer.Config{ Mode: printer.SourcePos, Tabwidth: 8, Indent: 0, } cfg.Fprint(w, f.fset, f.astFile) }
func codeToString(fset *token.FileSet, i interface{}) string { b := bytes.NewBuffer(make([]byte, 0, 128)) tmpb := bytes.NewBuffer(make([]byte, 0, 128)) flags := printer.UseSpaces | printer.TabIndent config := printer.Config{flags, 8} config.Fprint(tmpb, fset, i) template.HTMLEscape(b, tmpb.Bytes()) return b.String() }
func Generate(prog *loader.Program, getFileBytes func(string) ([]byte, error), writerFor func(importPath, filename string) io.WriteCloser) { for _, pkgInfo := range prog.InitialPackages() { defs = pkgInfo.Defs _types = pkgInfo.Types pkg = pkgInfo.Pkg path := pkg.Path() if !pkgInfo.Importable && strings.HasSuffix(pkgInfo.Pkg.Name(), "_test") { // EXTERNAL TEST package, strip the _test to find the path path = strings.TrimSuffix(pkg.Path(), "_test") } idents.pkgScope = generatePackageScopeIdent(pkgInfo) for i, f := range pkgInfo.Files { fs = prog.Fset fname := fs.Position(f.Pos()).Filename if strings.HasSuffix(fname, "/C") { continue } b, err := getFileBytes(fname) if err != nil { fmt.Fprint(os.Stderr, "Error reading file:", err) os.Exit(1) } b = normalizeCRLF(b) quotedContents := rawQuote(string(b)) ast1, fs1 := parseCgoFile(fname, b) if ast1 != nil { f = ast1 fs = fs1 } generateGodebugIdentifiers(f) ast.Walk(&visitor{context: f, scopeVar: idents.fileScope}, f) importName := idents.godebug if importName == "godebug" { importName = "" } astutil.AddNamedImport(fs, f, importName, "github.com/mailgun/godebug/lib") cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} out := writerFor(path, fname) defer out.Close() _ = cfg.Fprint(out, fs, f) fmt.Fprintln(out, "\nvar", idents.fileContents, "=", quotedContents) if i == 0 { err := generatePackageFile(idents.pkgScope, pkgInfo, out) if err != nil { fmt.Fprint(os.Stderr, "Error writing package file:", err) os.Exit(1) } } } } }
// Generates a Go file with the given name using the provided template and // template data. func (g *goFileGenerator) Generate(filename, tmpl string, data interface{}) ([]byte, error) { funcs := template.FuncMap{ "import": g.Import, "formatType": g.FormatType, } for k, v := range g.templateFuncs { funcs[k] = v } t, err := template.New(filename).Delims("<", ">").Funcs(funcs).Parse(tmpl) if err != nil { return nil, fmt.Errorf("failed to parse template %q: %v", filename, err) } var buff bytes.Buffer if err := t.Execute(&buff, data); err != nil { return nil, err } fset := token.NewFileSet() f, err := parser.ParseFile(fset, filename, buff.Bytes(), parser.ParseComments) if err != nil { return nil, fmt.Errorf("failed to parse generated code: %v:\n%s", err, buff.String()) } if len(f.Imports) > 0 { return nil, fmt.Errorf( "plain imports are not allowed with GoFileFromTemplate: use the import function") } importPaths := make([]string, 0, len(g.imports)) for path := range g.imports { importPaths = append(importPaths, path) } sort.Strings(importPaths) for _, path := range importPaths { astutil.AddNamedImport(fset, f, g.imports[path], path) } cfg := printer.Config{ Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8, } buff = bytes.Buffer{} if err := cfg.Fprint(&buff, fset, f); err != nil { return nil, err // TODO wrap error } return buff.Bytes(), nil }
func main() { log.SetFlags(0) if len(os.Args) != 2 { log.Fatalf("Usage: %s FILE", os.Args[0]) } f, err := parser.ParseFile(fset, os.Args[1], nil, parser.ParseComments) if err != nil { log.Fatal(err) } ast.Walk(&Visitor{}, f) config := printer.Config{Mode: 0, Tabwidth: 8} if err := config.Fprint(os.Stdout, fset, f); err != nil { log.Fatal(err) } }
func (g *generator) Write(w io.Writer, fs *token.FileSet) error { // TODO constants first, types next, and functions after that if _, err := w.Write([]byte(generatedByHeader)); err != nil { return err } if _, err := fmt.Fprintf(w, "package %s\n\n", g.PackageName); err != nil { return err } cfg := printer.Config{ Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8, } if importDecl := g.importDecl(); importDecl != nil { if err := cfg.Fprint(w, fs, importDecl); err != nil { return err } } if _, err := io.WriteString(w, "\n"); err != nil { return err } for _, decl := range g.decls { if _, err := io.WriteString(w, "\n"); err != nil { return err } if err := cfg.Fprint(w, fs, decl); err != nil { return err } if _, err := io.WriteString(w, "\n"); err != nil { return err } } g.decls = nil g.importer = newImporter(g.Namespace.Child()) // init can appear multiple times in the same package across different // files g.Namespace.Forget("init") return nil }
func (s *Session) source(space bool) (string, error) { normalizeNodePos(s.mainFunc()) var config *printer.Config if space { config = &printer.Config{ Mode: printer.UseSpaces, Tabwidth: 4, } } else { config = &printer.Config{ Tabwidth: 8, } } var buf bytes.Buffer err := config.Fprint(&buf, s.Fset, s.File) return buf.String(), err }
func gen(o Options, fset *token.FileSet, n ast.Node) ([]byte, error) { v := &visitor{Options: o} ast.Walk(v, n) if v.err != nil { return nil, v.err } if !v.changed { return nil, nil } c := printer.Config{Mode: printer.RawFormat} buf := &bytes.Buffer{} if err := c.Fprint(buf, fset, n); err != nil { return nil, fmt.Errorf("error printing output: %s", err) } b, err := format.Source(buf.Bytes()) if err != nil { return nil, err } return b, nil }
// BumpInFile finds a constant named VERSION, version, or Version in the file // with the given filename, increments the version per the given VersionType, // and writes the file back to disk. Returns the incremented Version object. func BumpInFile(vtype VersionType, filename string) (*Version, error) { fset := token.NewFileSet() parsedFile, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) if err != nil { return nil, err } for _, decl := range parsedFile.Decls { switch gd := decl.(type) { case *ast.GenDecl: if gd.Tok != token.CONST { continue } spec, _ := gd.Specs[0].(*ast.ValueSpec) if strings.ToUpper(spec.Names[0].Name) == "VERSION" { value, _ := spec.Values[0].(*ast.BasicLit) if value.Kind != token.STRING { return nil, fmt.Errorf("VERSION is not a string, was %#v\n", value.Value) } version, err := changeVersion(vtype, value.Value) if err != nil { return nil, err } value.Value = fmt.Sprintf("\"%s\"", version.String()) f, err := os.Create(filename) if err != nil { return nil, err } cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} err = cfg.Fprint(f, fset, parsedFile) if err != nil { return nil, err } return version, nil } default: continue } } return nil, fmt.Errorf("No VERSION const found in %s", filename) }
func rewrite(name, id, hash, base string) { cmdir := filepath.Join(base, "cmd", name) path := filepath.Join(cmdir, "main.go") mainfile, err := os.OpenFile(path, os.O_RDWR, 0660) if err != nil { log.Fatal(err) } defer mainfile.Close() fset := token.NewFileSet() f, err := parser.ParseFile(fset, path, mainfile, parser.ParseComments) if err != nil { log.Fatal(err) } for _, d := range f.Decls { d, ok := d.(*ast.GenDecl) if !ok { continue } if d.Tok != token.CONST { continue } for _, spec := range d.Specs { spec, ok := spec.(*ast.ValueSpec) if !ok { continue } if len(spec.Names) != 1 || len(spec.Values) != 1 { continue } value, ok := spec.Values[0].(*ast.BasicLit) if !ok { continue } switch spec.Names[0].Name { case "VersionDate": value.Value = id case "VersionID": value.Value = hash } } } var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} var buf bytes.Buffer if err := config.Fprint(&buf, fset, f); err != nil { log.Fatal(err) } if _, err := mainfile.Seek(0, os.SEEK_SET); err != nil { log.Fatal(err) } if _, err := io.Copy(mainfile, &buf); err != nil { log.Fatal(err) } }
// Format formats the given package file originally obtained from src // and adjusts the result based on the original source via sourceAdj // and indentAdj. func Format( fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte, cfg printer.Config, ) ([]byte, error) { if sourceAdj == nil { // Complete source file. var buf bytes.Buffer err := cfg.Fprint(&buf, fset, file) if err != nil { return nil, err } return buf.Bytes(), nil } // Partial source file. // Determine and prepend leading space. i, j := 0, 0 for j < len(src) && IsSpace(src[j]) { if src[j] == '\n' { i = j + 1 // byte offset of last line in leading space } j++ } var res []byte res = append(res, src[:i]...) // Determine and prepend indentation of first code line. // Spaces are ignored unless there are no tabs, // in which case spaces count as one tab. indent := 0 hasSpace := false for _, b := range src[i:j] { switch b { case ' ': hasSpace = true case '\t': indent++ } } if indent == 0 && hasSpace { indent = 1 } for i := 0; i < indent; i++ { res = append(res, '\t') } // Format the source. // Write it without any leading and trailing space. cfg.Indent = indent + indentAdj var buf bytes.Buffer err := cfg.Fprint(&buf, fset, file) if err != nil { return nil, err } res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...) // Determine and append trailing space. i = len(src) for i > 0 && IsSpace(src[i-1]) { i-- } return append(res, src[i:]...), nil }
func writeSingleTest(testMain *TestMain, testName string, testType int, filename string) os.Error { src := bytes.NewBufferString("") fmt.Fprint(src, "// "+testMain.pkgName+"."+testName+"\n") fmt.Fprint(src, "//\n") fmt.Fprint(src, "package main\n\n") fmt.Fprint(src, "import \"sync\"\n") fmt.Fprint(src, "import \"testing\"\n") if testMain.pkgName != "regexp" { fmt.Fprint(src, "import \"regexp\"\n") } fmt.Fprintf(src, "import %s \"%s\"\n", testMain.underscorePkgName(), testMain.pkgName) fmt.Fprint(src, "\nfunc main() {\n") fmt.Fprint(src, "wg := new(sync.WaitGroup)\n") pkgName := testMain.underscorePkgName() fmt.Fprintf(src, "for i := 0; i < %d; i++ {\n", iters) fmt.Fprint(src, "wg.Add(1)\n") fmt.Fprint(src, "go func() {\n") if testType == 0 { fmt.Fprint(src, "tests := []testing.InternalTest{\n") testFunc := pkgName + "." + testName fmt.Fprintf(src, "{\"%s\", %s},\n", testMain.pkgName+"."+testName, testFunc) fmt.Fprint(src, "}\n") fmt.Fprint(src, "testing.Main(regexp.MatchString, tests)\n") } else if testType == 1 { fmt.Fprint(src, "benchmarks := []testing.InternalBenchmark{\n") benchFunc := pkgName + "." + testName fmt.Fprintf(src, "{\"%s\", %s},\n", testMain.pkgName+"."+testName, benchFunc) fmt.Fprint(src, "}\n") fmt.Fprint(src, "testing.RunBenchmarks(regexp.MatchString, benchmarks)\n") } fmt.Fprint(src, "wg.Done()\n") fmt.Fprint(src, "}()\n") fmt.Fprint(src, "}\n\n") fmt.Fprint(src, "wg.Wait()\n") fmt.Fprint(src, "}\n") //fmt.Printf("%s\n", string(src.Bytes())) file, err := os.Open(filename, os.O_CREAT|os.O_TRUNC|os.O_WRONLY, 0666) if err != nil { return err } defer file.Close() fileset := token.NewFileSet() fileNode, err := parser.ParseFile(fileset, filename, src.Bytes(), parser.ParseComments) if err != nil { panic(err) } config := printer.Config{printer.TabIndent, 8} _, err = config.Fprint(file, fileset, fileNode) if err != nil { return err } return nil }
func main() { flag.Usage = usage flag.Parse() if flag.NArg() == 0 { flag.Usage() } fileContents := map[string]string{} fset := token.NewFileSet() for i := 0; i < flag.NArg(); i++ { arg := flag.Arg(i) fi, err := os.Stat(arg) dieIf(err) if fi.IsDir() { if i != 0 { die("you can only specify exact one directory") } fis, err := ioutil.ReadDir(arg) dieIf(err) for _, fi := range fis { if fi.IsDir() { continue } name := fi.Name() if !strings.HasSuffix(name, ".go") { continue } if name[0] == '_' || name[0] == '.' { continue } filename := filepath.Join(arg, name) b, err := ioutil.ReadFile(filename) dieIf(err) fileContents[filename] = string(b) } } else { b, err := ioutil.ReadFile(arg) dieIf(err) fileContents[arg] = string(b) } } ff, err := parseFiles(fset, fileContents) dieIf(err) if *flagRevert { err = quickfix.RevertQuickFix(fset, ff) } else { err = quickfix.QuickFix(fset, ff) } dieIf(err) for _, f := range ff { filename := fset.File(f.Pos()).Name() var buf bytes.Buffer conf := printer.Config{ Tabwidth: 8, Mode: printer.UseSpaces | printer.TabIndent, } err := conf.Fprint(&buf, fset, f) dieIf(err) if buf.String() == fileContents[filename] { // no change, skip this file continue } out := os.Stdout if *flagWrite { out, err = os.Create(filename) dieIf(err) } buf.WriteTo(out) } }
func (t *Tree) Compile(file string) { counts := [TypeLast]uint{} for element := t.Front(); element != nil; element = element.Next() { node := element.Value.(Node) switch node.GetType() { case TypePackage: t.PackageName = node.String() case TypePeg: t.StructName = node.String() t.StructVariables = node.Front().String() case TypeRule: t.Rules[node.String()] = node t.RuleNames = append(t.RuleNames, node) } } /*Needed for undefined rules!*/ for name, r := range t.Rules { if r.String() == "" { r := &node{Type: TypeRule, string: name, id: t.ruleId} r.PushBack(&node{Type: TypeNil, string: "<nil>"}) t.ruleId++ t.Rules[name] = r t.PushBack(r) } } t.RulesCount = len(t.Rules) join([]func(){ func() { var countTypes func(node Node) countTypes = func(node Node) { nodeType := node.GetType() id := counts[nodeType] counts[nodeType]++ switch nodeType { case TypeAction: node.SetId(int(id)) t.Actions = append(t.Actions, node) case TypeRule, TypeAlternate, TypeUnorderedAlternate, TypeSequence, TypePeekFor, TypePeekNot, TypeQuery, TypeStar, TypePlus, TypePush: for element := node.Front(); element != nil; element = element.Next() { countTypes(element) } } } for _, rule := range t.Rules { countTypes(rule) } }, func() { var countRules func(node Node) ruleReached := make([]bool, len(t.Rules)) countRules = func(node Node) { switch node.GetType() { case TypeRule: name, id := node.String(), node.GetId() if count, ok := t.rulesCount[name]; ok { t.rulesCount[name] = count + 1 } else { t.rulesCount[name] = 1 } if ruleReached[id] { return } ruleReached[id] = true countRules(node.Front()) case TypeName: countRules(t.Rules[node.String()]) case TypeAlternate, TypeUnorderedAlternate, TypeSequence, TypePeekFor, TypePeekNot, TypeQuery, TypeStar, TypePlus, TypePush: for element := node.Front(); element != nil; element = element.Next() { countRules(element) } } } for element := t.Front(); element != nil; element = element.Next() { node := element.Value.(Node) if node.GetType() == TypeRule { countRules(node) break } } }, func() { var checkRecursion func(node Node) bool ruleReached := make([]bool, len(t.Rules)) checkRecursion = func(node Node) bool { switch node.GetType() { case TypeRule: id := node.GetId() if ruleReached[id] { fmt.Fprintf(os.Stderr, "possible infinite left recursion in rule '%v'\n", node) return false } ruleReached[id] = true consumes := checkRecursion(node.Front()) ruleReached[id] = false return consumes case TypeAlternate: for element := node.Front(); element != nil; element = element.Next() { if !checkRecursion(element) { return false } } return true case TypeSequence: for element := node.Front(); element != nil; element = element.Next() { if checkRecursion(element) { return true } } case TypeName: return checkRecursion(t.Rules[node.String()]) case TypePlus, TypePush: return checkRecursion(node.Front()) case TypeCharacter, TypeString: return len(node.String()) > 0 case TypeDot, TypeRange: return true } return false } for _, rule := range t.Rules { checkRecursion(rule) } }}) var ast func(node, rule Node) ast = func(node, rule Node) { if node.GetType() == TypePush { node.PushBack(rule.Copy()) if node.Front() != nil { ast(node.Front(), rule) } return } for element := node.Front(); element != nil; element = element.Next() { ast(element, rule) } } for _, rule := range t.Rules { ast(rule, rule) expression := rule.Front() copy := expression.Copy() expression.Init() expression.SetType(TypeImplicitPush) expression.PushBack(copy) expression.PushBack(rule.Copy()) } /*var estimateMemory func(node Node, depth int) (estimates []estimate, consumed int) ruleReached := make([]bool, len(t.Rules)) estimateMemory = func(node Node, depth int) (estimates []estimate, consumed int) { switch node.GetType() { case TypeRule: id := node.GetId() if ruleReached[id] { return } ruleReached[id] = true estimates, consumed = estimateMemory(node.Front(), depth) ruleReached[id] = false return case TypeAlternate: consumes := math.MaxInt32 //need to account for emtpy alternate!!! for element := node.Front(); element != nil; element = element.Next() { e, c := estimateMemory(element, depth) if c == 0 { consumed = 0 for i, _ := range estimates { estimates[i].consumes = false } return } else if c < consumes { consumed, consumes, estimates = c, c, e } } return case TypeSequence: for element := node.Front(); element != nil; element = element.Next() { e, c := estimateMemory(element, depth) if c != 0 { consumed += c for _, est := range e { estimates = append(estimates, est) } } } return case TypePlus: estimates, consumed = estimateMemory(node.Front(), depth) for _, e := range estimates { e.consumes = false estimates = append(estimates, e) } return case TypeName: estimates, consumed = estimateMemory(t.Rules[node.String()], depth) return case TypePush, TypeImplicitPush: depth++ token := node.Front() estimates, consumed = estimateMemory(token, depth) e := estimate{consumed: consumed, depth: depth, consumes: true, name: token.Next().String(), estimates: estimates} estimates = make([]estimate, 1) estimates[0] = e return case TypeCharacter, TypeString: consumed = len(node.String()) return case TypeDot, TypeRange: consumed = 1 return } return } var printEstimates func(estimates []estimate) printEstimates = func(estimates []estimate) { for _, e := range estimates { for c := 0; c < e.depth; c++ { fmt.Printf(" ") } fmt.Printf("%v %v %v %v\n", e.consumed, e.consumes, e.name, float32(e.depth)/float32(e.consumed)) printEstimates(e.estimates) } } for ruleName, rule := range t.Rules { fmt.Printf("%s\n", ruleName) estimates, _ := estimateMemory(rule, 0) printEstimates(estimates) }*/ if t._switch { var optimizeAlternates func(node Node) (consumes, eof, peek bool, class *characterClass) cache := make([]struct { reached, consumes, eof, peek bool class *characterClass }, len(t.Rules)) optimizeAlternates = func(n Node) (consumes, eof, peek bool, class *characterClass) { switch n.GetType() { case TypeRule: cache := &cache[n.GetId()] if cache.reached { consumes, eof, peek, class = cache.consumes, cache.eof, cache.peek, cache.class return } cache.reached = true consumes, eof, peek, class = optimizeAlternates(n.Front()) cache.consumes, cache.eof, cache.peek, cache.class = consumes, eof, peek, class case TypeName: consumes, eof, peek, class = optimizeAlternates(t.Rules[n.String()]) case TypeDot: consumes, class = true, new(characterClass) for index, _ := range *class { class[index] = 0xff } case TypeString, TypeCharacter: consumes, class = true, new(characterClass) class.add(n.String()[0]) case TypeRange: consumes, class = true, new(characterClass) element := n.Front() lower := element.String()[0] element = element.Next() upper := element.String()[0] for c := lower; c <= upper; c++ { class.add(c) } case TypeAlternate: consumes, peek, class = true, true, new(characterClass) mconsumes, meof, mpeek, properties, c := consumes, eof, peek, make([]struct { intersects bool class *characterClass }, n.Len()), 0 for element := n.Front(); element != nil; element = element.Next() { mconsumes, meof, mpeek, properties[c].class = optimizeAlternates(element) consumes, eof, peek = consumes && mconsumes, eof || meof, peek && mpeek if properties[c].class != nil { class.union(properties[c].class) } c++ } if eof { break } intersections := 2 compare: for ai, a := range properties[0 : len(properties)-1] { for _, b := range properties[ai+1:] { for i, v := range *a.class { if (b.class[i] & v) != 0 { intersections++ properties[ai].intersects = true continue compare } } } } if intersections < len(properties) { c, unordered, ordered, max := 0, &node{Type: TypeUnorderedAlternate}, &node{Type: TypeAlternate}, 0 for element := n.Front(); element != nil; element = element.Next() { if properties[c].intersects { ordered.PushBack(element.Copy()) } else { class := &node{Type: TypeUnorderedAlternate} for d := 0; d < 256; d++ { if properties[c].class.has(uint8(d)) { class.PushBack(&node{Type: TypeCharacter, string: string(d)}) } } sequence, predicate, length := &node{Type: TypeSequence}, &node{Type: TypePeekFor}, properties[c].class.len() if length == 0 { class.PushBack(&node{Type: TypeNil, string: "<nil>"}) } predicate.PushBack(class) sequence.PushBack(predicate) sequence.PushBack(element.Copy()) if element.GetType() == TypeNil { unordered.PushBack(sequence) } else if length > max { unordered.PushBack(sequence) max = length } else { unordered.PushFront(sequence) } } c++ } n.Init() if ordered.Front() == nil { n.SetType(TypeUnorderedAlternate) for element := unordered.Front(); element != nil; element = element.Next() { n.PushBack(element.Copy()) } } else { for element := ordered.Front(); element != nil; element = element.Next() { n.PushBack(element.Copy()) } n.PushBack(unordered) } } case TypeSequence: sequence := n meof, classes, c, element := eof, make([]struct { peek bool class *characterClass }, sequence.Len()), 0, sequence.Front() for ; !consumes && element != nil; element, c = element.Next(), c+1 { consumes, meof, classes[c].peek, classes[c].class = optimizeAlternates(element) eof, peek = eof || meof, peek || classes[c].peek } eof, peek, class = !consumes && eof, !consumes && peek, new(characterClass) for c--; c >= 0; c-- { if classes[c].class != nil { if classes[c].peek { class.intersection(classes[c].class) } else { class.union(classes[c].class) } } } for ; element != nil; element = element.Next() { optimizeAlternates(element) } case TypePeekNot: peek = true _, eof, _, class = optimizeAlternates(n.Front()) eof = !eof class = class.copy() class.complement() case TypePeekFor: peek = true fallthrough case TypeQuery, TypeStar: _, eof, _, class = optimizeAlternates(n.Front()) case TypePlus, TypePush: consumes, eof, peek, class = optimizeAlternates(n.Front()) case TypeAction, TypeNil: class = new(characterClass) } return } for element := t.Front(); element != nil; element = element.Next() { n := element.Value.(Node) if n.GetType() == TypeRule { optimizeAlternates(n.(*node)) break } } } out, error := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if error != nil { fmt.Printf("%v: %v\n", file, error) return } defer out.Close() var buffer bytes.Buffer defer func() { fileSet := token.NewFileSet() code, error := parser.ParseFile(fileSet, file, &buffer, parser.ParseComments) if error != nil { buffer.WriteTo(out) fmt.Printf("%v: %v\n", file, error) return } formatter := printer.Config{printer.TabIndent | printer.UseSpaces, 8} _, error = formatter.Fprint(out, fileSet, code) if error != nil { buffer.WriteTo(out) fmt.Printf("%v: %v\n", file, error) return } }() print := func(format string, a ...interface{}) { fmt.Fprintf(&buffer, format, a...) } printSave := func(n uint) { print("\n position%d, tokenIndex%d := position, tokenIndex", n, n) } printRestore := func(n uint) { print(" position, tokenIndex = position%d, tokenIndex%d", n, n) } printTemplate := func(s string) { if error := template.Must(template.New("peg").Parse(s)).Execute(&buffer, t); error != nil { panic(error) } } if t.HasActions = counts[TypeAction] > 0; t.HasActions { bits := 0 for length := len(t.Actions); length != 0; length >>= 1 { bits++ } switch { case bits < 8: bits = 8 case bits < 16: bits = 16 case bits < 32: bits = 32 case bits < 64: bits = 64 } t.Bits = bits printSave = func(n uint) { print("\n position%d, tokenIndex%d, thunkPosition%d := position, tokenIndex, thunkPosition", n, n, n) } printRestore = func(n uint) { print(" position, tokenIndex, thunkPosition = position%d, tokenIndex%d, thunkPosition%d", n, n, n) } } t.HasCommit = counts[TypeCommit] > 0 t.HasDot = counts[TypeDot] > 0 t.HasCharacter = counts[TypeCharacter] > 0 t.HasString = counts[TypeString] > 0 t.HasRange = counts[TypeRange] > 0 var printRule func(n Node) var compile func(expression Node, ko uint) var label uint labels := make(map[uint]bool) printBegin := func() { print("\n {") } printEnd := func() { print("\n }") } printLabel := func(n uint) { print("\n") if labels[n] { print(" l%d:\t", n) } } printJump := func(n uint) { print("\n goto l%d", n) labels[n] = true } printRule = func(n Node) { switch n.GetType() { case TypeRule: print("%v <- ", n) printRule(n.Front()) case TypeDot: print(".") case TypeName: print("%v", n) case TypeCharacter: print("'%v'", escape(n.String())) case TypeString: s := escape(n.String()) print("'%v'", s[1:len(s)-1]) case TypeRange: element := n.Front() lower := element element = element.Next() upper := element print("[%v-%v]", lower, upper) case TypePredicate: print("&{%v}", n) case TypeAction: print("{%v}", n) case TypeCommit: print("commit") case TypeAlternate: print("(") element := n.Front() printRule(element) for element = element.Next(); element != nil; element = element.Next() { print(" / ") printRule(element) } print(")") case TypeUnorderedAlternate: print("(") element := n.Front() printRule(element) for element = element.Next(); element != nil; element = element.Next() { print(" | ") printRule(element) } print(")") case TypeSequence: print("(") element := n.Front() printRule(element) for element = element.Next(); element != nil; element = element.Next() { print(" ") printRule(element) } print(")") case TypePeekFor: print("&") printRule(n.Front()) case TypePeekNot: print("!") printRule(n.Front()) case TypeQuery: printRule(n.Front()) print("?") case TypeStar: printRule(n.Front()) print("*") case TypePlus: printRule(n.Front()) print("+") case TypePush, TypeImplicitPush: print("<") printRule(n.Front()) print(">") case TypeNil: default: fmt.Fprintf(os.Stderr, "illegal node type: %v\n", n.GetType()) } } compile = func(n Node, ko uint) { switch n.GetType() { case TypeRule: fmt.Fprintf(os.Stderr, "internal error #1 (%v)\n", n) case TypeDot: print("\n if !matchDot() {") printJump(ko) print("}") case TypeName: name := n.String() rule := t.Rules[name] if t.inline && t.rulesCount[name] == 1 { compile(rule.Front(), ko) return } print("\n if !p.rules[%d]() {", rule.GetId()) printJump(ko) print("}") case TypeRange: element := n.Front() lower := element element = element.Next() upper := element print("\n if !matchRange('%v', '%v') {", escape(lower.String()), escape(upper.String())) printJump(ko) print("}") case TypeCharacter: print("\n if !matchChar('%v') {", escape(n.String())) printJump(ko) print("}") case TypeString: print("\n if !matchString(%v) {", strconv.Quote(n.String())) printJump(ko) print("}") case TypePredicate: print("\n if !(%v) {", n) printJump(ko) print("}") case TypeAction: print("\n do(%d)", n.GetId()) case TypeCommit: /*print("\n if !(commit(thunkPosition0)) {")*/ print("\n if !(commit(0)) {") printJump(ko) print("}") case TypePush: begin := label label++ element := n.Front() printBegin() if t.HasActions { print("\n begin = position") } print("\nbegin%d := position", begin) compile(element, ko) if t.HasActions { print("\n end = position") } print("\nif begin%d != position {p.Add(Rule%v, begin%d, position, tokenIndex)", begin, element.Next(), begin) print("\ntokenIndex++}") printEnd() case TypeImplicitPush: begin := label label++ element := n.Front() printBegin() print("\nbegin%d := position", begin) compile(element, ko) print("\nif begin%d != position {p.Add(Rule%v, begin%d, position, tokenIndex)", begin, element.Next(), begin) print("\ntokenIndex++}") printEnd() case TypeAlternate: ok := label label++ printBegin() element := n.Front() if element.Next() != nil { printSave(ok) } for element.Next() != nil { next := label label++ compile(element, next) printJump(ok) printLabel(next) printRestore(ok) element = element.Next() } compile(element, ko) printEnd() printLabel(ok) case TypeUnorderedAlternate: done, ok := ko, label label++ printBegin() print("\n if position == len(p.Buffer) {") printJump(done) print("}") print("\n switch p.Buffer[position] {") element := n.Front() for ; element.Next() != nil; element = element.Next() { sequence := element.Front() class := sequence.Front() sequence = sequence.Next() print("\n case") comma := false for character := class.Front(); character != nil; character = character.Next() { if comma { print(",") } else { comma = true } print(" '%s'", escape(character.String())) } print(":") compile(sequence, done) print("\nbreak") } print("\n default:") compile(element.Front().Next(), done) print("\nbreak") print("\n }") printEnd() printLabel(ok) case TypeSequence: for element := n.Front(); element != nil; element = element.Next() { compile(element, ko) } case TypePeekFor: ok := label label++ printBegin() printSave(ok) compile(n.Front(), ko) printRestore(ok) printEnd() case TypePeekNot: ok := label label++ printBegin() printSave(ok) compile(n.Front(), ok) printJump(ko) printLabel(ok) printRestore(ok) printEnd() case TypeQuery: qko := label label++ qok := label label++ printBegin() printSave(qko) compile(n.Front(), qko) printJump(qok) printLabel(qko) printRestore(qko) printEnd() printLabel(qok) case TypeStar: again := label label++ out := label label++ printLabel(again) printBegin() printSave(out) compile(n.Front(), out) printJump(again) printLabel(out) printRestore(out) printEnd() case TypePlus: again := label label++ out := label label++ compile(n.Front(), ko) printLabel(again) printBegin() printSave(out) compile(n.Front(), out) printJump(again) printLabel(out) printRestore(out) printEnd() case TypeNil: default: fmt.Fprintf(os.Stderr, "illegal node type: %v\n", n.GetType()) } } /* lets figure out which jump labels are going to be used with this dry compile */ printTemp, print := print, func(format string, a ...interface{}) {} for element := t.Front(); element != nil; element = element.Next() { rule := element.Value.(Node) if rule.GetType() != TypeRule { continue } expression := rule.Front() if expression.GetType() == TypeNil { continue } ko := label label++ if count, ok := t.rulesCount[rule.String()]; !ok { continue } else if t.inline && count == 1 && ko != 0 { continue } compile(expression, ko) } print, label = printTemp, 0 /* now for the real compile pass */ printTemplate(PEG_HEADER_TEMPLATE) for element := t.Front(); element != nil; element = element.Next() { rule := element.Value.(Node) if rule.GetType() != TypeRule { continue } expression := rule.Front() if expression.GetType() == TypeNil { fmt.Fprintf(os.Stderr, "rule '%v' used but not defined\n", rule) print("\n nil,") continue } ko := label label++ print("\n /* %v ", rule.GetId()) printRule(rule) print(" */") if count, ok := t.rulesCount[rule.String()]; !ok { fmt.Fprintf(os.Stderr, "rule '%v' defined but not used\n", rule) print("\n nil,") continue } else if t.inline && count == 1 && ko != 0 { print("\n nil,") continue } print("\n func() bool {") if labels[ko] { printSave(ko) } compile(expression, ko) print("\n return true") if labels[ko] { printLabel(ko) printRestore(ko) print("\n return false") } print("\n },") } print("\n }") print("\n}\n") }
func (t *Tree) Compile(file string) { t.EndSymbol = '\u0004' t.RulesCount++ counts := [TypeLast]uint{} { var rule *node var link func(node Node) link = func(n Node) { nodeType := n.GetType() id := counts[nodeType] counts[nodeType]++ switch nodeType { case TypeAction: n.SetId(int(id)) copy, name := n.Copy(), fmt.Sprintf("Action%v", id) t.Actions = append(t.Actions, copy) n.Init() n.SetType(TypeName) n.SetString(name) n.SetId(t.RulesCount) emptyRule := &node{Type: TypeRule, string: name, id: t.RulesCount} implicitPush := &node{Type: TypeImplicitPush} emptyRule.PushBack(implicitPush) implicitPush.PushBack(copy) implicitPush.PushBack(emptyRule.Copy()) t.PushBack(emptyRule) t.RulesCount++ t.Rules[name] = emptyRule t.RuleNames = append(t.RuleNames, emptyRule) case TypeName: name := n.String() if _, ok := t.Rules[name]; !ok { emptyRule := &node{Type: TypeRule, string: name, id: t.RulesCount} implicitPush := &node{Type: TypeImplicitPush} emptyRule.PushBack(implicitPush) implicitPush.PushBack(&node{Type: TypeNil, string: "<nil>"}) implicitPush.PushBack(emptyRule.Copy()) t.PushBack(emptyRule) t.RulesCount++ t.Rules[name] = emptyRule t.RuleNames = append(t.RuleNames, emptyRule) } case TypePush: copy, name := rule.Copy(), "PegText" copy.SetString(name) if _, ok := t.Rules[name]; !ok { emptyRule := &node{Type: TypeRule, string: name, id: t.RulesCount} emptyRule.PushBack(&node{Type: TypeNil, string: "<nil>"}) t.PushBack(emptyRule) t.RulesCount++ t.Rules[name] = emptyRule t.RuleNames = append(t.RuleNames, emptyRule) } n.PushBack(copy) fallthrough case TypeImplicitPush: link(n.Front()) case TypeRule, TypeAlternate, TypeUnorderedAlternate, TypeSequence, TypePeekFor, TypePeekNot, TypeQuery, TypeStar, TypePlus: for _, node := range n.Slice() { link(node) } } } /* first pass */ for _, node := range t.Slice() { switch node.GetType() { case TypePackage: t.PackageName = node.String() case TypePeg: t.StructName = node.String() t.StructVariables = node.Front().String() case TypeRule: if _, ok := t.Rules[node.String()]; !ok { expression := node.Front() copy := expression.Copy() expression.Init() expression.SetType(TypeImplicitPush) expression.PushBack(copy) expression.PushBack(node.Copy()) t.Rules[node.String()] = node t.RuleNames = append(t.RuleNames, node) } } } /* second pass */ for _, node := range t.Slice() { if node.GetType() == TypeRule { rule = node link(node) } } } join([]func(){ func() { var countRules func(node Node) ruleReached := make([]bool, t.RulesCount) countRules = func(node Node) { switch node.GetType() { case TypeRule: name, id := node.String(), node.GetId() if count, ok := t.rulesCount[name]; ok { t.rulesCount[name] = count + 1 } else { t.rulesCount[name] = 1 } if ruleReached[id] { return } ruleReached[id] = true countRules(node.Front()) case TypeName: countRules(t.Rules[node.String()]) case TypeImplicitPush, TypePush: countRules(node.Front()) case TypeAlternate, TypeUnorderedAlternate, TypeSequence, TypePeekFor, TypePeekNot, TypeQuery, TypeStar, TypePlus: for _, element := range node.Slice() { countRules(element) } } } for _, node := range t.Slice() { if node.GetType() == TypeRule { countRules(node) break } } }, func() { var checkRecursion func(node Node) bool ruleReached := make([]bool, t.RulesCount) checkRecursion = func(node Node) bool { switch node.GetType() { case TypeRule: id := node.GetId() if ruleReached[id] { fmt.Fprintf(os.Stderr, "possible infinite left recursion in rule '%v'\n", node) return false } ruleReached[id] = true consumes := checkRecursion(node.Front()) ruleReached[id] = false return consumes case TypeAlternate: for _, element := range node.Slice() { if !checkRecursion(element) { return false } } return true case TypeSequence: for _, element := range node.Slice() { if checkRecursion(element) { return true } } case TypeName: return checkRecursion(t.Rules[node.String()]) case TypePlus, TypePush, TypeImplicitPush: return checkRecursion(node.Front()) case TypeCharacter, TypeString: return len(node.String()) > 0 case TypeDot, TypeRange: return true } return false } for _, node := range t.Slice() { if node.GetType() == TypeRule { checkRecursion(node) } } }}) if t._switch { var optimizeAlternates func(node Node) (consumes bool, s *set) cache, firstPass := make([]struct { reached, consumes bool s *set }, t.RulesCount), true optimizeAlternates = func(n Node) (consumes bool, s *set) { /*n.debug()*/ switch n.GetType() { case TypeRule: cache := &cache[n.GetId()] if cache.reached { consumes, s = cache.consumes, cache.s return } cache.reached = true consumes, s = optimizeAlternates(n.Front()) cache.consumes, cache.s = consumes, s case TypeName: consumes, s = optimizeAlternates(t.Rules[n.String()]) case TypeDot: consumes, s = true, &set{} /* TypeDot set doesn't include the EndSymbol */ s.add(byte(t.EndSymbol)) s.complement() case TypeString, TypeCharacter: consumes, s = true, &set{} s.add(n.String()[0]) case TypeRange: consumes, s = true, &set{} element := n.Front() lower := element.String()[0] element = element.Next() upper := element.String()[0] for c := lower; c <= upper; c++ { s.add(c) } case TypeAlternate: consumes, s = true, &set{} mconsumes, properties, c := consumes, make([]struct { intersects bool s *set }, n.Len()), 0 for _, element := range n.Slice() { mconsumes, properties[c].s = optimizeAlternates(element) consumes = consumes && mconsumes if properties[c].s == nil { /* recursive definition, so set has yet to be completed */ } else { s.union(properties[c].s) } c++ } if firstPass { break } intersections := 2 compare: for ai, a := range properties[0 : len(properties)-1] { for _, b := range properties[ai+1:] { if a.s.intersects(b.s) { intersections++ properties[ai].intersects = true continue compare } } } if intersections >= len(properties) { break } c, unordered, ordered, max := 0, &node{Type: TypeUnorderedAlternate}, &node{Type: TypeAlternate}, 0 for _, element := range n.Slice() { if properties[c].intersects { ordered.PushBack(element.Copy()) } else { class := &node{Type: TypeUnorderedAlternate} for d := 0; d < 256; d++ { if properties[c].s.has(uint8(d)) { class.PushBack(&node{Type: TypeCharacter, string: string(d)}) } } sequence, predicate, length := &node{Type: TypeSequence}, &node{Type: TypePeekFor}, properties[c].s.len() if length == 0 { class.PushBack(&node{Type: TypeNil, string: "<nil>"}) } predicate.PushBack(class) sequence.PushBack(predicate) sequence.PushBack(element.Copy()) if element.GetType() == TypeNil { unordered.PushBack(sequence) } else if length > max { unordered.PushBack(sequence) max = length } else { unordered.PushFront(sequence) } } c++ } n.Init() if ordered.Front() == nil { n.SetType(TypeUnorderedAlternate) for _, element := range unordered.Slice() { n.PushBack(element.Copy()) } } else { for _, element := range ordered.Slice() { n.PushBack(element.Copy()) } n.PushBack(unordered) } case TypeSequence: classes, elements := make([]struct { s *set }, n.Len()), n.Slice() for c, element := range elements { consumes, classes[c].s = optimizeAlternates(element) if consumes { elements, classes = elements[c+1:], classes[:c+1] break } } s = &set{} for c := len(classes) - 1; c >= 0; c-- { if classes[c].s != nil { s.union(classes[c].s) } } for _, element := range elements { optimizeAlternates(element) } case TypePeekNot, TypePeekFor: optimizeAlternates(n.Front()) s = &set{} case TypeQuery, TypeStar: _, s = optimizeAlternates(n.Front()) case TypePlus, TypePush, TypeImplicitPush: consumes, s = optimizeAlternates(n.Front()) case TypeAction, TypeNil: s = &set{} } return } for _, element := range t.Slice() { if element.GetType() == TypeRule { optimizeAlternates(element) break } } for i, _ := range cache { cache[i].reached = false } firstPass = false for _, element := range t.Slice() { if element.GetType() == TypeRule { optimizeAlternates(element) break } } } out, error := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if error != nil { fmt.Printf("%v: %v\n", file, error) return } defer out.Close() var buffer bytes.Buffer defer func() { fileSet := token.NewFileSet() code, error := parser.ParseFile(fileSet, file, &buffer, parser.ParseComments) if error != nil { buffer.WriteTo(out) fmt.Printf("%v: %v\n", file, error) return } formatter := printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8} error = formatter.Fprint(out, fileSet, code) if error != nil { buffer.WriteTo(out) fmt.Printf("%v: %v\n", file, error) return } }() print := func(format string, a ...interface{}) { fmt.Fprintf(&buffer, format, a...) } printSave := func(n uint) { print("\n position%d, tokenIndex%d, depth%d := position, tokenIndex, depth", n, n, n) } printRestore := func(n uint) { print(" position, tokenIndex, depth = position%d, tokenIndex%d, depth%d", n, n, n) } printTemplate := func(s string) { if error := template.Must(template.New("peg").Parse(s)).Execute(&buffer, t); error != nil { panic(error) } } t.HasActions = counts[TypeAction] > 0 t.HasCommit = counts[TypeCommit] > 0 t.HasDot = counts[TypeDot] > 0 t.HasCharacter = counts[TypeCharacter] > 0 t.HasString = counts[TypeString] > 0 t.HasRange = counts[TypeRange] > 0 var printRule func(n Node) var compile func(expression Node, ko uint) var label uint labels := make(map[uint]bool) printBegin := func() { print("\n {") } printEnd := func() { print("\n }") } printLabel := func(n uint) { print("\n") if labels[n] { print(" l%d:\t", n) } } printJump := func(n uint) { print("\n goto l%d", n) labels[n] = true } printRule = func(n Node) { switch n.GetType() { case TypeRule: print("%v <- ", n) printRule(n.Front()) case TypeDot: print(".") case TypeName: print("%v", n) case TypeCharacter: print("'%v'", escape(n.String())) case TypeString: s := escape(n.String()) print("'%v'", s[1:len(s)-1]) case TypeRange: element := n.Front() lower := element element = element.Next() upper := element print("[%v-%v]", lower, upper) case TypePredicate: print("&{%v}", n) case TypeAction: print("{%v}", n) case TypeCommit: print("commit") case TypeAlternate: print("(") elements := n.Slice() printRule(elements[0]) for _, element := range elements[1:] { print(" / ") printRule(element) } print(")") case TypeUnorderedAlternate: print("(") elements := n.Slice() printRule(elements[0]) for _, element := range elements[1:] { print(" | ") printRule(element) } print(")") case TypeSequence: print("(") elements := n.Slice() printRule(elements[0]) for _, element := range elements[1:] { print(" ") printRule(element) } print(")") case TypePeekFor: print("&") printRule(n.Front()) case TypePeekNot: print("!") printRule(n.Front()) case TypeQuery: printRule(n.Front()) print("?") case TypeStar: printRule(n.Front()) print("*") case TypePlus: printRule(n.Front()) print("+") case TypePush, TypeImplicitPush: print("<") printRule(n.Front()) print(">") case TypeNil: default: fmt.Fprintf(os.Stderr, "illegal node type: %v\n", n.GetType()) } } compile = func(n Node, ko uint) { switch n.GetType() { case TypeRule: fmt.Fprintf(os.Stderr, "internal error #1 (%v)\n", n) case TypeDot: print("\n if !matchDot() {") /*print("\n if buffer[position] == END_SYMBOL {")*/ printJump(ko) /*print("}\nposition++")*/ print("}") case TypeName: name := n.String() rule := t.Rules[name] if t.inline && t.rulesCount[name] == 1 { compile(rule.Front(), ko) return } print("\n if !rules[Rule%v]() {", name /*rule.GetId()*/) printJump(ko) print("}") case TypeRange: element := n.Front() lower := element element = element.Next() upper := element /*print("\n if !matchRange('%v', '%v') {", escape(lower.String()), escape(upper.String()))*/ print("\n if c := buffer[position]; c < rune('%v') || c > rune('%v') {", escape(lower.String()), escape(upper.String())) printJump(ko) print("}\nposition++") case TypeCharacter: /*print("\n if !matchChar('%v') {", escape(n.String()))*/ print("\n if buffer[position] != rune('%v') {", escape(n.String())) printJump(ko) print("}\nposition++") case TypeString: print("\n if !matchString(%v) {", strconv.Quote(n.String())) printJump(ko) print("}") case TypePredicate: print("\n if !(%v) {", n) printJump(ko) print("}") case TypeAction: case TypeCommit: case TypePush: fallthrough case TypeImplicitPush: ok, element := label, n.Front() label++ nodeType, rule := element.GetType(), element.Next() printBegin() if nodeType == TypeAction { print("\nadd(Rule%v, position)", rule) } else { print("\nposition%d := position", ok) print("\ndepth++") compile(element, ko) print("\ndepth--") print("\nadd(Rule%v, position%d)", rule, ok) } printEnd() case TypeAlternate: ok := label label++ printBegin() elements := n.Slice() printSave(ok) for _, element := range elements[:len(elements)-1] { next := label label++ compile(element, next) printJump(ok) printLabel(next) printRestore(ok) } compile(elements[len(elements)-1], ko) printEnd() printLabel(ok) case TypeUnorderedAlternate: done, ok := ko, label label++ printBegin() print("\n switch buffer[position] {") elements := n.Slice() elements, last := elements[:len(elements)-1], elements[len(elements)-1].Front().Next() for _, element := range elements { sequence := element.Front() class := sequence.Front() sequence = sequence.Next() print("\n case") comma := false for _, character := range class.Slice() { if comma { print(",") } else { comma = true } print(" '%s'", escape(character.String())) } print(":") compile(sequence, done) print("\nbreak") } print("\n default:") compile(last, done) print("\nbreak") print("\n }") printEnd() printLabel(ok) case TypeSequence: for _, element := range n.Slice() { compile(element, ko) } case TypePeekFor: ok := label label++ printBegin() printSave(ok) compile(n.Front(), ko) printRestore(ok) printEnd() case TypePeekNot: ok := label label++ printBegin() printSave(ok) compile(n.Front(), ok) printJump(ko) printLabel(ok) printRestore(ok) printEnd() case TypeQuery: qko := label label++ qok := label label++ printBegin() printSave(qko) compile(n.Front(), qko) printJump(qok) printLabel(qko) printRestore(qko) printEnd() printLabel(qok) case TypeStar: again := label label++ out := label label++ printLabel(again) printBegin() printSave(out) compile(n.Front(), out) printJump(again) printLabel(out) printRestore(out) printEnd() case TypePlus: again := label label++ out := label label++ compile(n.Front(), ko) printLabel(again) printBegin() printSave(out) compile(n.Front(), out) printJump(again) printLabel(out) printRestore(out) printEnd() case TypeNil: default: fmt.Fprintf(os.Stderr, "illegal node type: %v\n", n.GetType()) } } /* lets figure out which jump labels are going to be used with this dry compile */ printTemp, print := print, func(format string, a ...interface{}) {} for _, element := range t.Slice() { if element.GetType() != TypeRule { continue } expression := element.Front() if expression.GetType() == TypeNil { continue } ko := label label++ if count, ok := t.rulesCount[element.String()]; !ok { continue } else if t.inline && count == 1 && ko != 0 { continue } compile(expression, ko) } print, label = printTemp, 0 /* now for the real compile pass */ printTemplate(PEG_HEADER_TEMPLATE) for _, element := range t.Slice() { if element.GetType() != TypeRule { continue } expression := element.Front() if expression.GetType() == TypeNil { fmt.Fprintf(os.Stderr, "rule '%v' used but not defined\n", element) print("\n nil,") continue } ko := label label++ print("\n /* %v ", element.GetId()) printRule(element) print(" */") if count, ok := t.rulesCount[element.String()]; !ok { fmt.Fprintf(os.Stderr, "rule '%v' defined but not used\n", element) print("\n nil,") continue } else if t.inline && count == 1 && ko != 0 { print("\n nil,") continue } print("\n func() bool {") if labels[ko] { printSave(ko) } compile(expression, ko) print("\n return true") if labels[ko] { printLabel(ko) printRestore(ko) print("\n return false") } print("\n },") } print("\n }\n p.rules = rules") print("\n}\n") }
func formatNode(n ast.Node, obj types.Object, prog *loader.Program) string { var nc ast.Node // Render a copy of the node with no documentation. // We emit the documentation ourself. switch n := n.(type) { case *ast.FuncDecl: cp := *n cp.Doc = nil // Don't print the whole function body cp.Body = nil nc = &cp case *ast.GenDecl: cp := *n cp.Doc = nil if len(n.Specs) > 0 { // Only print this one type, not all the types in the // gendecl switch n.Specs[0].(type) { case *ast.TypeSpec: spec := findTypeSpec(n, obj.Pos()) if spec != nil { specCp := *spec if *showUnexportedFields == false { trimUnexportedElems(&specCp) } specCp.Doc = nil cp.Specs = []ast.Spec{&specCp} } cp.Lparen = 0 cp.Rparen = 0 case *ast.ValueSpec: spec := findVarSpec(n, obj.Pos()) if spec != nil { specCp := *spec specCp.Doc = nil cp.Specs = []ast.Spec{&specCp} } cp.Lparen = 0 cp.Rparen = 0 } } nc = &cp case *ast.Field: // Not supported by go/printer // TODO(dominikh): Methods in interfaces are syntactically // represented as fields. Using types.Object.String for those // causes them to look different from real functions. // go/printer doesn't include the import paths in names, while // Object.String does. Fix that. return obj.String() default: return obj.String() } buf := &bytes.Buffer{} cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} err := cfg.Fprint(buf, prog.Fset, nc) if err != nil { return obj.String() } return buf.String() }