diff --git a/README.md b/README.md index b998659..e4dbd19 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,17 @@ CLI --- ``` -php-parser [-php5 -noDump] ... +php-parser [flags] ... ``` +| flag | type | description | +|-------|------|----------------------------------------------| +| -d |string| dump format: [custom, go, json, pretty-json] | +| -p | bool | show positions | +| -c | bool | show comments | +| -r | bool | resolve names | +| -php5 | bool | parse as PHP5 | + Dump AST to stdout. Example diff --git a/main.go b/main.go index 151d3b7..ac260c7 100644 --- a/main.go +++ b/main.go @@ -17,11 +17,18 @@ import ( var wg sync.WaitGroup var usePhp5 *bool -var noDump *bool +var dumpType string +var showPositions *bool +var showComments *bool +var showResolvedNs *bool func main() { - usePhp5 = flag.Bool("php5", false, "use PHP5 parserWorker") - noDump = flag.Bool("noDump", false, "disable dumping to stdout") + usePhp5 = flag.Bool("php5", false, "parse as PHP5") + showPositions = flag.Bool("p", false, "show positions") + showComments = flag.Bool("c", false, "show comments") + showResolvedNs = flag.Bool("r", false, "resolve names") + flag.StringVar(&dumpType, "d", "", "dump format: [custom, go, json, pretty_json]") + flag.Parse() pathCh := make(chan string) @@ -95,19 +102,53 @@ func printer(result <-chan parser.Parser) { fmt.Println(e) } - if !*noDump { - nsResolver := visitor.NewNamespaceResolver() + var nsResolver *visitor.NamespaceResolver + if *showResolvedNs { + nsResolver = visitor.NewNamespaceResolver() parserWorker.GetRootNode().Walk(nsResolver) + } + var comments parser.Comments + if *showComments { + comments = parserWorker.GetComments() + } + + var positions parser.Positions + if *showPositions { + positions = parserWorker.GetPositions() + } + + switch dumpType { + case "custom": dumper := &visitor.Dumper{ Writer: os.Stdout, - Indent: " | ", - Comments: parserWorker.GetComments(), - Positions: parserWorker.GetPositions(), + Indent: "| ", + Comments: comments, + Positions: positions, NsResolver: nsResolver, } parserWorker.GetRootNode().Walk(dumper) + case "json": + dumper := &visitor.JsonDumper{ + Writer: os.Stdout, + Comments: comments, + Positions: positions, + NsResolver: nsResolver, + } + parserWorker.GetRootNode().Walk(dumper) + case "pretty_json": + dumper := &visitor.PrettyJsonDumper{ + Writer: os.Stdout, + Comments: comments, + Positions: positions, + NsResolver: nsResolver, + } + parserWorker.GetRootNode().Walk(dumper) + case "go": + dumper := &visitor.GoDumper{Writer: os.Stdout} + parserWorker.GetRootNode().Walk(dumper) } + wg.Done() } } diff --git a/visitor/dumper.go b/visitor/dumper.go index e69f3a1..862c3ca 100644 --- a/visitor/dumper.go +++ b/visitor/dumper.go @@ -32,13 +32,13 @@ func (d *Dumper) EnterNode(w walker.Walkable) bool { if d.Positions != nil { if p := d.Positions[n]; p != nil { - fmt.Fprintf(d.Writer, "%v\"Position\": %s;\n", d.Indent+" ", *p) + fmt.Fprintf(d.Writer, "%v\"Position\": %s\n", d.Indent+" ", *p) } } if d.NsResolver != nil { if namespacedName, ok := d.NsResolver.ResolvedNames[n]; ok { - fmt.Fprintf(d.Writer, "%v\"NamespacedName\": %s;\n", d.Indent+" ", namespacedName) + fmt.Fprintf(d.Writer, "%v\"NamespacedName\": %q\n", d.Indent+" ", namespacedName) } } @@ -53,7 +53,12 @@ func (d *Dumper) EnterNode(w walker.Walkable) bool { if a := n.Attributes(); len(a) > 0 { for key, attr := range a { - fmt.Fprintf(d.Writer, "%v\"%v\": %v;\n", d.Indent+" ", key, attr) + switch attr.(type) { + case string: + fmt.Fprintf(d.Writer, "%v\"%v\": %q\n", d.Indent+" ", key, attr) + default: + fmt.Fprintf(d.Writer, "%v\"%v\": %v\n", d.Indent+" ", key, attr) + } } } diff --git a/visitor/dumper_test.go b/visitor/dumper_test.go index 8fedd3b..127af48 100644 --- a/visitor/dumper_test.go +++ b/visitor/dumper_test.go @@ -39,84 +39,84 @@ func ExampleDumper() { nodes.Walk(dumper) // Unordered output: - //| [*node.Root] - //| "Position": Pos{Line: 3-11 Pos: 10-143}; - //| "Stmts": - //| [*stmt.Namespace] - //| "Position": Pos{Line: 3-11 Pos: 10-143}; - //| "NamespaceName": - //| [*name.Name] - //| "Position": Pos{Line: 3-3 Pos: 20-22}; - //| "Parts": - //| [*name.NamePart] - //| "Position": Pos{Line: 3-3 Pos: 20-22}; - //| "Value": Foo; - //| "Stmts": - //| [*stmt.Class] - //| "Position": Pos{Line: 4-10 Pos: 29-139}; - //| "NamespacedName": Foo\Bar; - //| "PhpDocComment": ; - //| "ClassName": - //| [*node.Identifier] - //| "Position": Pos{Line: 4-4 Pos: 35-37}; - //| "Value": Bar; - //| "Stmts": - //| [*stmt.ClassMethod] - //| "Position": Pos{Line: 5-9 Pos: 45-134}; - //| "ReturnsRef": false; - //| "PhpDocComment": ; - //| "MethodName": - //| [*node.Identifier] - //| "Position": Pos{Line: 5-5 Pos: 61-72}; - //| "Value": FunctionName; - //| "Modifiers": - //| [*node.Identifier] - //| "Position": Pos{Line: 5-5 Pos: 45-50}; - //| "Value": public; - //| "Params": - //| [*node.Parameter] - //| "Position": Pos{Line: 5-5 Pos: 74-89}; - //| "ByRef": false; - //| "Variadic": false; - //| "VariableType": - //| [*name.Name] - //| "Position": Pos{Line: 5-5 Pos: 74-77}; - //| "NamespacedName": Foo\Type; - //| "Parts": - //| [*name.NamePart] - //| "Position": Pos{Line: 5-5 Pos: 74-77}; - //| "Value": Type; - //| "Variable": - //| [*expr.Variable] - //| "Position": Pos{Line: 5-5 Pos: 79-82}; - //| "VarName": - //| [*node.Identifier] - //| "Position": Pos{Line: 5-5 Pos: 79-82}; - //| "Value": var; - //| "DefaultValue": - //| [*expr.ConstFetch] - //| "Position": Pos{Line: 5-5 Pos: 86-89}; - //| "Constant": - //| [*name.Name] - //| "Position": Pos{Line: 5-5 Pos: 86-89}; - //| "NamespacedName": null; - //| "Parts": - //| [*name.NamePart] - //| "Position": Pos{Line: 5-5 Pos: 86-89}; - //| "Value": null; - //| "Stmt": - //| [*stmt.StmtList] - //| "Position": Pos{Line: 6-9 Pos: 96-134}; - //| "Stmts": - //| [*stmt.Expression] - //| "Position": Pos{Line: 8-8 Pos: 124-128}; - //| "Expr": - //| [*expr.Variable] - //| "Position": Pos{Line: 8-8 Pos: 124-127}; - //| "Comments": - //| "// some comment\n" before "VariableToken" - //| "VarName": - //| [*node.Identifier] - //| "Position": Pos{Line: 8-8 Pos: 124-127}; - //| "Value": var; + // | [*node.Root] + // | "Position": Pos{Line: 3-11 Pos: 10-143} + // | "Stmts": + // | [*stmt.Namespace] + // | "Position": Pos{Line: 3-11 Pos: 10-143} + // | "NamespaceName": + // | [*name.Name] + // | "Position": Pos{Line: 3-3 Pos: 20-22} + // | "Parts": + // | [*name.NamePart] + // | "Position": Pos{Line: 3-3 Pos: 20-22} + // | "Value": "Foo" + // | "Stmts": + // | [*stmt.Class] + // | "Position": Pos{Line: 4-10 Pos: 29-139} + // | "NamespacedName": "Foo\\Bar" + // | "PhpDocComment": "" + // | "ClassName": + // | [*node.Identifier] + // | "Position": Pos{Line: 4-4 Pos: 35-37} + // | "Value": "Bar" + // | "Stmts": + // | [*stmt.ClassMethod] + // | "Position": Pos{Line: 5-9 Pos: 45-134} + // | "ReturnsRef": false + // | "PhpDocComment": "" + // | "MethodName": + // | [*node.Identifier] + // | "Position": Pos{Line: 5-5 Pos: 61-72} + // | "Value": "FunctionName" + // | "Modifiers": + // | [*node.Identifier] + // | "Position": Pos{Line: 5-5 Pos: 45-50} + // | "Value": "public" + // | "Params": + // | [*node.Parameter] + // | "Position": Pos{Line: 5-5 Pos: 74-89} + // | "Variadic": false + // | "ByRef": false + // | "VariableType": + // | [*name.Name] + // | "Position": Pos{Line: 5-5 Pos: 74-77} + // | "NamespacedName": "Foo\\Type" + // | "Parts": + // | [*name.NamePart] + // | "Position": Pos{Line: 5-5 Pos: 74-77} + // | "Value": "Type" + // | "Variable": + // | [*expr.Variable] + // | "Position": Pos{Line: 5-5 Pos: 79-82} + // | "VarName": + // | [*node.Identifier] + // | "Position": Pos{Line: 5-5 Pos: 79-82} + // | "Value": "var" + // | "DefaultValue": + // | [*expr.ConstFetch] + // | "Position": Pos{Line: 5-5 Pos: 86-89} + // | "Constant": + // | [*name.Name] + // | "Position": Pos{Line: 5-5 Pos: 86-89} + // | "NamespacedName": "null" + // | "Parts": + // | [*name.NamePart] + // | "Position": Pos{Line: 5-5 Pos: 86-89} + // | "Value": "null" + // | "Stmt": + // | [*stmt.StmtList] + // | "Position": Pos{Line: 6-9 Pos: 96-134} + // | "Stmts": + // | [*stmt.Expression] + // | "Position": Pos{Line: 8-8 Pos: 124-128} + // | "Expr": + // | [*expr.Variable] + // | "Position": Pos{Line: 8-8 Pos: 124-127} + // | "Comments": + // | "// some comment\n" before "VariableToken" + // | "VarName": + // | [*node.Identifier] + // | "Position": Pos{Line: 8-8 Pos: 124-127} + // | "Value": "var" } diff --git a/visitor/json_dumper.go b/visitor/json_dumper.go new file mode 100644 index 0000000..9903000 --- /dev/null +++ b/visitor/json_dumper.go @@ -0,0 +1,97 @@ +// Package visitor contains walker.visitor implementations +package visitor + +import ( + "fmt" + "io" + "reflect" + + "github.com/z7zmey/php-parser/node" + "github.com/z7zmey/php-parser/parser" + + "github.com/z7zmey/php-parser/walker" +) + +type JsonDumper struct { + Writer io.Writer + Comments parser.Comments + Positions parser.Positions + NsResolver *NamespaceResolver +} + +// EnterNode is invoked at every node in hierarchy +func (d *JsonDumper) EnterNode(w walker.Walkable) bool { + n := w.(node.Node) + + nodeType := reflect.TypeOf(n).String() + + fmt.Fprintf(d.Writer, "{%q:%q", "type", nodeType) + + if d.Positions != nil { + if p := d.Positions[n]; p != nil { + fmt.Fprintf(d.Writer, ",%q:{%q:%d,%q:%d,%q:%d,%q:%d}", + "position", + "startPos", p.StartPos, + "endPos", p.EndPos, + "startLine", p.StartLine, + "endLine", p.EndLine) + } + } + + if d.NsResolver != nil { + if namespacedName, ok := d.NsResolver.ResolvedNames[n]; ok { + fmt.Fprintf(d.Writer, ",%q:%q", "namespacedName", namespacedName) + } + } + + if d.Comments != nil { + if c := d.Comments[n]; len(c) > 0 { + fmt.Fprintf(d.Writer, ",%q:[", "comments") + + for k, cc := range c { + if k == 0 { + fmt.Fprintf(d.Writer, "%q", cc) + } else { + fmt.Fprintf(d.Writer, ",%q", cc) + } + } + + fmt.Fprint(d.Writer, "]") + } + } + + if a := n.Attributes(); len(a) > 0 { + for key, attr := range a { + switch attr.(type) { + case string: + fmt.Fprintf(d.Writer, ",\"%s\":%q", key, attr) + default: + fmt.Fprintf(d.Writer, ",\"%s\":%v", key, attr) + } + } + } + + return true +} + +// LeaveNode is invoked after node process +func (d *JsonDumper) LeaveNode(n walker.Walkable) { + fmt.Fprint(d.Writer, "}") +} + +func (d *JsonDumper) EnterChildNode(key string, w walker.Walkable) { + fmt.Fprintf(d.Writer, ",%q:", key) +} + +func (d *JsonDumper) LeaveChildNode(key string, w walker.Walkable) { + // do nothing +} + +func (d *JsonDumper) EnterChildList(key string, w walker.Walkable) { + fmt.Fprintf(d.Writer, ",%q:[", key) + +} + +func (d *JsonDumper) LeaveChildList(key string, w walker.Walkable) { + fmt.Fprint(d.Writer, "]") +} diff --git a/visitor/json_dumper_test.go b/visitor/json_dumper_test.go new file mode 100644 index 0000000..b53c39f --- /dev/null +++ b/visitor/json_dumper_test.go @@ -0,0 +1,43 @@ +//Package visitor contains walker.visitor implementations +package visitor_test + +import ( + "bytes" + "os" + + "github.com/z7zmey/php-parser/php7" + "github.com/z7zmey/php-parser/visitor" +) + +func ExampleJsonDumper() { + src := ` 0 { + fmt.Fprint(d.Writer, ",\n") + d.printIndent(d.Writer) + fmt.Fprint(d.Writer, "\"comments\": [\n") + d.depth++ + for k, cc := range c { + if k == 0 { + d.printIndent(d.Writer) + fmt.Fprintf(d.Writer, "%q", cc) + } else { + fmt.Fprint(d.Writer, ",\n") + d.printIndent(d.Writer) + fmt.Fprintf(d.Writer, "%q", cc) + } + } + d.depth-- + fmt.Fprint(d.Writer, "\n") + d.printIndent(d.Writer) + fmt.Fprint(d.Writer, "]") + } + } + + if a := n.Attributes(); len(a) > 0 { + for key, attr := range a { + fmt.Fprint(d.Writer, ",\n") + d.printIndent(d.Writer) + switch attr.(type) { + case string: + fmt.Fprintf(d.Writer, "\"%s\": %q", key, attr) + default: + fmt.Fprintf(d.Writer, "\"%s\": %v", key, attr) + } + } + } + + return true +} + +// LeaveNode is invoked after node process +func (d *PrettyJsonDumper) LeaveNode(n walker.Walkable) { + d.depth-- + fmt.Fprint(d.Writer, "\n") + d.printIndent(d.Writer) + fmt.Fprint(d.Writer, "}") +} + +func (d *PrettyJsonDumper) EnterChildNode(key string, w walker.Walkable) { + fmt.Fprint(d.Writer, ",\n") + d.printIndent(d.Writer) + fmt.Fprintf(d.Writer, "%q: ", key) + d.isChildNode = true +} + +func (d *PrettyJsonDumper) LeaveChildNode(key string, w walker.Walkable) { + // do nothing +} + +func (d *PrettyJsonDumper) EnterChildList(key string, w walker.Walkable) { + fmt.Fprint(d.Writer, ",\n") + d.printIndent(d.Writer) + fmt.Fprintf(d.Writer, "%q: [\n", key) + d.depth++ +} + +func (d *PrettyJsonDumper) LeaveChildList(key string, w walker.Walkable) { + d.depth-- + fmt.Fprint(d.Writer, "\n") + d.printIndent(d.Writer) + fmt.Fprint(d.Writer, "]") +} diff --git a/visitor/pretty_json_dumper_test.go b/visitor/pretty_json_dumper_test.go new file mode 100644 index 0000000..c77ec87 --- /dev/null +++ b/visitor/pretty_json_dumper_test.go @@ -0,0 +1,269 @@ +//Package visitor contains walker.visitor implementations +package visitor_test + +import ( + "bytes" + "os" + + "github.com/z7zmey/php-parser/php7" + "github.com/z7zmey/php-parser/visitor" +) + +func ExamplePrettyJsonDumper() { + src := `