예제 #1
0
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
}
예제 #2
0
파일: cover.go 프로젝트: pierrec/go-fuzz
func (f *File) print(w io.Writer) {
	cfg := printer.Config{
		Mode:     printer.SourcePos,
		Tabwidth: 8,
		Indent:   0,
	}
	cfg.Fprint(w, f.fset, f.astFile)
}
예제 #3
0
파일: jsencode.go 프로젝트: nsf/gortfm
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()
}
예제 #4
0
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)
				}
			}
		}
	}
}
예제 #5
0
// 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
}
예제 #6
0
파일: main.go 프로젝트: nbaum/x
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)
	}
}
예제 #7
0
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
}
예제 #8
0
파일: main.go 프로젝트: jonfk/gore
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
}
예제 #9
0
파일: generate.go 프로젝트: nange/graffiti
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
}
예제 #10
0
파일: lib.go 프로젝트: Shyp/bump_version
// 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)
}
예제 #11
0
파일: build.go 프로젝트: eswdd/bosun
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)
	}
}
예제 #12
0
파일: format.go 프로젝트: RobHoyt/go
// 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
}
예제 #13
0
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
}
예제 #14
0
파일: main.go 프로젝트: Pinkxa/gophernotes
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)
	}
}
예제 #15
0
파일: peg.go 프로젝트: taruti/peg
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")
}
예제 #16
0
파일: peg.go 프로젝트: hellcoderz/peg
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")
}
예제 #17
0
파일: ident.go 프로젝트: zmb3/gogetdoc
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()
}