diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-05-03 21:40:43 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-05-10 23:14:34 +0900 |
commit | f7484ef11af39585989dbbcad701551c561fa67c (patch) | |
tree | 828b4225802ed442e92017a81d01b5442d2b4fb6 | |
parent | Update CHANGELOG (diff) | |
download | urubu-f7484ef11af39585989dbbcad701551c561fa67c.tar.gz urubu-f7484ef11af39585989dbbcad701551c561fa67c.tar.xz |
Add --json option to vartan-parse command
-rw-r--r-- | cmd/vartan/parse.go | 13 | ||||
-rw-r--r-- | driver/parser_test.go | 12 | ||||
-rw-r--r-- | driver/semantic_action.go | 94 |
3 files changed, 91 insertions, 28 deletions
diff --git a/cmd/vartan/parse.go b/cmd/vartan/parse.go index b65f72f..77bb9f0 100644 --- a/cmd/vartan/parse.go +++ b/cmd/vartan/parse.go @@ -17,6 +17,7 @@ var parseFlags = struct { onlyParse *bool cst *bool disableLAC *bool + json *bool }{} func init() { @@ -31,6 +32,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") rootCmd.AddCommand(cmd) } @@ -158,7 +160,16 @@ func runParse(cmd *cobra.Command, args []string) (retErr error) { if len(synErrs) > 0 { fmt.Println("") } - driver.PrintTree(os.Stdout, tree) + + if *parseFlags.json { + b, err := json.Marshal(tree) + if err != nil { + return err + } + fmt.Fprintln(os.Stdout, string(b)) + } else { + driver.PrintTree(os.Stdout, tree) + } } } diff --git a/driver/parser_test.go b/driver/parser_test.go index fa8307e..4795e29 100644 --- a/driver/parser_test.go +++ b/driver/parser_test.go @@ -11,6 +11,7 @@ import ( func termNode(kind string, text string, children ...*Node) *Node { return &Node{ + Type: NodeTypeTerminal, KindName: kind, Text: text, Children: children, @@ -19,13 +20,14 @@ func termNode(kind string, text string, children ...*Node) *Node { func errorNode() *Node { return &Node{ + Type: NodeTypeError, KindName: "error", - Error: true, } } func nonTermNode(kind string, children ...*Node) *Node { return &Node{ + Type: NodeTypeNonTerminal, KindName: kind, Children: children, } @@ -131,8 +133,8 @@ bar_text: "bar"; `, src: ``, cst: nonTermNode("s", - termNode("foo", ""), - termNode("bar", ""), + nonTermNode("foo"), + nonTermNode("bar"), ), }, // The driver can reduce productions that have the empty alternative and can generate a CST (and AST) node. @@ -154,7 +156,7 @@ bar_text: "bar"; `, src: `bar`, cst: nonTermNode("s", - termNode("foo", ""), + nonTermNode("foo"), nonTermNode("bar", termNode("bar_text", "bar"), ), @@ -716,7 +718,7 @@ bar: 'bar'; func testTree(t *testing.T, node, expected *Node) { t.Helper() - if node.KindName != expected.KindName || node.Text != expected.Text || node.Error != expected.Error { + if node.Type != expected.Type || node.KindName != expected.KindName || node.Text != expected.Text { t.Fatalf("unexpected node; want: %+v, got: %+v", expected, node) } if len(node.Children) != len(expected.Children) { diff --git a/driver/semantic_action.go b/driver/semantic_action.go index 7c52e24..73f3bb0 100644 --- a/driver/semantic_action.go +++ b/driver/semantic_action.go @@ -1,6 +1,7 @@ package driver import ( + "encoding/json" "fmt" "io" ) @@ -68,6 +69,7 @@ func NewDefaultSyntaxTreeBuilder() *DefaulSyntaxTreeBuilder { // Shift is a implementation of SyntaxTreeBuilder.Shift. func (b *DefaulSyntaxTreeBuilder) Shift(kindName string, text string, row, col int) SyntaxTreeNode { return &Node{ + Type: NodeTypeTerminal, KindName: kindName, Text: text, Row: row, @@ -78,8 +80,8 @@ func (b *DefaulSyntaxTreeBuilder) Shift(kindName string, text string, row, col i // ShiftError is a implementation of SyntaxTreeBuilder.ShiftError. func (b *DefaulSyntaxTreeBuilder) ShiftError(kindName string) SyntaxTreeNode { return &Node{ + Type: NodeTypeError, KindName: kindName, - Error: true, } } @@ -90,6 +92,7 @@ func (b *DefaulSyntaxTreeBuilder) Reduce(kindName string, children []SyntaxTreeN cNodes[i] = c.(*Node) } return &Node{ + Type: NodeTypeNonTerminal, KindName: kindName, Children: cNodes, } @@ -238,14 +241,61 @@ func (s *semanticStack) pop(n int) []SyntaxTreeNode { return fs } +type NodeType int + +const ( + NodeTypeError = 0 + NodeTypeTerminal = 1 + NodeTypeNonTerminal = 2 +) + // Node is a implementation of SyntaxTreeNode interface. type Node struct { + Type NodeType KindName string Text string Row int Col int Children []*Node - Error bool +} + +func (n *Node) MarshalJSON() ([]byte, error) { + switch n.Type { + case NodeTypeError: + return json.Marshal(struct { + Type NodeType `json:"type"` + KindName string `json:"kind_name"` + }{ + Type: n.Type, + KindName: n.KindName, + }) + case NodeTypeTerminal: + return json.Marshal(struct { + Type NodeType `json:"type"` + KindName string `json:"kind_name"` + Text string `json:"text"` + Row int `json:"row"` + Col int `json:"col"` + }{ + Type: n.Type, + KindName: n.KindName, + Text: n.Text, + Row: n.Row, + Col: n.Col, + }) + case NodeTypeNonTerminal: + return json.Marshal(struct { + Type NodeType `json:"type"` + KindName string `json:"kind_name"` + Children []*Node `json:"children"` + }{ + Type: n.Type, + KindName: n.KindName, + Children: n.Children, + }) + default: + return nil, fmt.Errorf("invalid node type: %v", n.Type) + } } // ChildCount is a implementation of SyntaxTreeNode.ChildCount. @@ -272,31 +322,31 @@ func printTree(w io.Writer, node *Node, ruledLine string, childRuledLinePrefix s return } - switch { - case node.Error: + switch node.Type { + case NodeTypeError: fmt.Fprintf(w, "%v!%v\n", ruledLine, node.KindName) - case node.Text != "": + case NodeTypeTerminal: fmt.Fprintf(w, "%v%v %#v\n", ruledLine, node.KindName, node.Text) - default: + case NodeTypeNonTerminal: fmt.Fprintf(w, "%v%v\n", ruledLine, node.KindName) - } - num := len(node.Children) - for i, child := range node.Children { - var line string - if num > 1 && i < num-1 { - line = "├─ " - } else { - line = "└─ " - } + num := len(node.Children) + for i, child := range node.Children { + var line string + if num > 1 && i < num-1 { + line = "├─ " + } else { + line = "└─ " + } - var prefix string - if i >= num-1 { - prefix = " " - } else { - prefix = "│ " - } + var prefix string + if i >= num-1 { + prefix = " " + } else { + prefix = "│ " + } - printTree(w, child, childRuledLinePrefix+line, childRuledLinePrefix+prefix) + printTree(w, child, childRuledLinePrefix+line, childRuledLinePrefix+prefix) + } } } |