aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-05-03 21:40:43 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-05-10 23:14:34 +0900
commitf7484ef11af39585989dbbcad701551c561fa67c (patch)
tree828b4225802ed442e92017a81d01b5442d2b4fb6
parentUpdate CHANGELOG (diff)
downloadurubu-f7484ef11af39585989dbbcad701551c561fa67c.tar.gz
urubu-f7484ef11af39585989dbbcad701551c561fa67c.tar.xz
Add --json option to vartan-parse command
-rw-r--r--cmd/vartan/parse.go13
-rw-r--r--driver/parser_test.go12
-rw-r--r--driver/semantic_action.go94
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)
+ }
}
}