diff --git a/cmd/vartan/parse.go b/cmd/vartan/parse.go index 2f06732..d1fc80e 100644 --- a/cmd/vartan/parse.go +++ b/cmd/vartan/parse.go @@ -9,6 +9,7 @@ import ( "github.com/nihei9/vartan/driver" spec "github.com/nihei9/vartan/spec/grammar" + "github.com/nihei9/vartan/tester" "github.com/spf13/cobra" ) @@ -17,9 +18,15 @@ var parseFlags = struct { onlyParse *bool cst *bool disableLAC *bool - json *bool + format *string }{} +const ( + outputFormatText = "text" + outputFormatTree = "tree" + outputFormatJSON = "json" +) + func init() { cmd := &cobra.Command{ Use: "parse ", @@ -32,7 +39,7 @@ func init() { parseFlags.onlyParse = cmd.Flags().Bool("only-parse", false, "when this option is enabled, the parser performs only parse and doesn't semantic actions") parseFlags.cst = cmd.Flags().Bool("cst", false, "when this option is enabled, the parser generates a CST") parseFlags.disableLAC = cmd.Flags().Bool("disable-lac", false, "disable LAC (lookahead correction)") - parseFlags.json = cmd.Flags().Bool("json", false, "enable JSON output") + parseFlags.format = cmd.Flags().StringP("format", "f", "text", "output format: one of text|tree|json") rootCmd.AddCommand(cmd) } @@ -40,6 +47,11 @@ func runParse(cmd *cobra.Command, args []string) error { if *parseFlags.onlyParse && *parseFlags.cst { return fmt.Errorf("You cannot enable --only-parse and --cst at the same time") } + if *parseFlags.format != outputFormatText && + *parseFlags.format != outputFormatTree && + *parseFlags.format != outputFormatJSON { + return fmt.Errorf("invalid output format: %v", *parseFlags.format) + } cg, err := readCompiledGrammar(args[0]) if err != nil { @@ -108,13 +120,17 @@ func runParse(cmd *cobra.Command, args []string) error { tree = tb.Tree() } if tree != nil { - if *parseFlags.json { + switch *parseFlags.format { + case "tree": + b := tester.ConvertSyntaxTreeToTestableTree(tree).Format() + fmt.Fprintln(os.Stdout, string(b)) + case "json": b, err := json.Marshal(tree) if err != nil { return err } fmt.Fprintln(os.Stdout, string(b)) - } else { + default: driver.PrintTree(os.Stdout, tree) } } diff --git a/spec/test/parser.go b/spec/test/parser.go index 0513ee3..175c89e 100644 --- a/spec/test/parser.go +++ b/spec/test/parser.go @@ -57,6 +57,30 @@ func (t *Tree) path() string { return fmt.Sprintf("%v.[%v]%v", t.Parent.path(), t.Offset, t.Kind) } +func (t *Tree) Format() []byte { + var b bytes.Buffer + t.format(&b, 0) + return b.Bytes() +} + +func (t *Tree) format(buf *bytes.Buffer, depth int) { + for i := 0; i < depth; i++ { + buf.WriteString(" ") + } + buf.WriteString("(") + buf.WriteString(t.Kind) + if len(t.Children) > 0 { + buf.WriteString("\n") + for i, c := range t.Children { + c.format(buf, depth+1) + if i < len(t.Children)-1 { + buf.WriteString("\n") + } + } + } + buf.WriteString(")") +} + func DiffTree(expected, actual *Tree) []*TreeDiff { if expected == nil && actual == nil { return nil diff --git a/spec/test/parser_test.go b/spec/test/parser_test.go index 6e77f6d..41b7189 100644 --- a/spec/test/parser_test.go +++ b/spec/test/parser_test.go @@ -7,6 +7,25 @@ import ( "testing" ) +func TestTree_Format(t *testing.T) { + expected := `(a + (b + (c)) + (d) + (e))` + tree := NewTree("a", + NewTree("b", + NewTree("c"), + ), + NewTree("d"), + NewTree("e"), + ) + actual := string(tree.Format()) + if actual != expected { + t.Fatalf("unexpected format:\n%v", actual) + } +} + func TestDiffTree(t *testing.T) { tests := []struct { t1 *Tree diff --git a/tester/tester.go b/tester/tester.go index ef3ca61..70d4800 100644 --- a/tester/tester.go +++ b/tester/tester.go @@ -152,7 +152,7 @@ func runTest(g *gspec.CompiledGrammar, c *TestCaseWithMetadata) *TestResult { } // When a parse tree exists, the test continues regardless of whether or not syntax errors occurred. - diffs := tspec.DiffTree(genTree(tb.Tree()).Fill(), c.TestCase.Output) + diffs := tspec.DiffTree(ConvertSyntaxTreeToTestableTree(tb.Tree()).Fill(), c.TestCase.Output) if len(diffs) > 0 { return &TestResult{ TestCasePath: c.FilePath, @@ -165,12 +165,12 @@ func runTest(g *gspec.CompiledGrammar, c *TestCaseWithMetadata) *TestResult { } } -func genTree(dTree *driver.Node) *tspec.Tree { +func ConvertSyntaxTreeToTestableTree(dTree *driver.Node) *tspec.Tree { var children []*tspec.Tree if len(dTree.Children) > 0 { children = make([]*tspec.Tree, len(dTree.Children)) for i, c := range dTree.Children { - children[i] = genTree(c) + children[i] = ConvertSyntaxTreeToTestableTree(c) } } return tspec.NewTree(dTree.KindName, children...)