aboutsummaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
Diffstat (limited to 'driver')
-rw-r--r--driver/parser_test.go20
-rw-r--r--driver/semantic_action.go22
-rw-r--r--driver/syntax_error_test.go170
3 files changed, 202 insertions, 10 deletions
diff --git a/driver/parser_test.go b/driver/parser_test.go
index 5c7addd..9e232f7 100644
--- a/driver/parser_test.go
+++ b/driver/parser_test.go
@@ -18,6 +18,10 @@ func termNode(kind string, text string, children ...*Node) *Node {
}
}
+func anonTermNode(text string, children ...*Node) *Node {
+ return termNode("", text, children...)
+}
+
func errorNode() *Node {
return &Node{
Type: NodeTypeError,
@@ -65,7 +69,7 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
nonTermNode("term",
nonTermNode("term",
nonTermNode("factor",
- termNode("x_3", "("),
+ anonTermNode("("),
nonTermNode("expr",
nonTermNode("expr",
nonTermNode("term",
@@ -74,10 +78,10 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
),
),
),
- termNode("x_1", "+"),
+ anonTermNode("+"),
nonTermNode("term",
nonTermNode("factor",
- termNode("x_3", "("),
+ anonTermNode("("),
nonTermNode("expr",
nonTermNode("expr",
nonTermNode("term",
@@ -86,27 +90,27 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
),
),
),
- termNode("x_1", "+"),
+ anonTermNode("+"),
nonTermNode("term",
nonTermNode("factor",
termNode("id", "c"),
),
),
),
- termNode("x_4", ")"),
+ anonTermNode(")"),
),
),
),
- termNode("x_4", ")"),
+ anonTermNode(")"),
),
),
- termNode("x_2", "*"),
+ anonTermNode("*"),
nonTermNode("factor",
termNode("id", "d"),
),
),
),
- termNode("x_1", "+"),
+ anonTermNode("+"),
nonTermNode("term",
nonTermNode("factor",
termNode("id", "e"),
diff --git a/driver/semantic_action.go b/driver/semantic_action.go
index 73f3bb0..54d3291 100644
--- a/driver/semantic_action.go
+++ b/driver/semantic_action.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "strconv"
)
// SemanticActionSet is a set of semantic actions a parser calls.
@@ -270,6 +271,19 @@ func (n *Node) MarshalJSON() ([]byte, error) {
KindName: n.KindName,
})
case NodeTypeTerminal:
+ if n.KindName == "" {
+ return json.Marshal(struct {
+ Type NodeType `json:"type"`
+ Text string `json:"text"`
+ Row int `json:"row"`
+ Col int `json:"col"`
+ }{
+ Type: n.Type,
+ Text: n.Text,
+ Row: n.Row,
+ Col: n.Col,
+ })
+ }
return json.Marshal(struct {
Type NodeType `json:"type"`
KindName string `json:"kind_name"`
@@ -324,9 +338,13 @@ func printTree(w io.Writer, node *Node, ruledLine string, childRuledLinePrefix s
switch node.Type {
case NodeTypeError:
- fmt.Fprintf(w, "%v!%v\n", ruledLine, node.KindName)
+ fmt.Fprintf(w, "%v%v\n", ruledLine, node.KindName)
case NodeTypeTerminal:
- fmt.Fprintf(w, "%v%v %#v\n", ruledLine, node.KindName, node.Text)
+ if node.KindName == "" {
+ fmt.Fprintf(w, "%v<anonymous> %v\n", ruledLine, strconv.Quote(node.Text))
+ } else {
+ fmt.Fprintf(w, "%v%v %v\n", ruledLine, node.KindName, strconv.Quote(node.Text))
+ }
case NodeTypeNonTerminal:
fmt.Fprintf(w, "%v%v\n", ruledLine, node.KindName)
diff --git a/driver/syntax_error_test.go b/driver/syntax_error_test.go
index 1480390..f68f595 100644
--- a/driver/syntax_error_test.go
+++ b/driver/syntax_error_test.go
@@ -2,6 +2,7 @@ package driver
import (
"fmt"
+ "sort"
"strings"
"testing"
@@ -153,3 +154,172 @@ c
})
}
}
+
+func TestParserWithSyntaxErrorAndExpectedLookahead(t *testing.T) {
+ tests := []struct {
+ caption string
+ specSrc string
+ src string
+ cause string
+ expected []string
+ }{
+ {
+ caption: "the parser reports an expected lookahead symbol",
+ specSrc: `
+#name test;
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `bar`,
+ cause: `bar`,
+ expected: []string{
+ "foo",
+ },
+ },
+ {
+ caption: "the parser reports expected lookahead symbols",
+ specSrc: `
+#name test;
+
+s
+ : foo
+ | bar
+ ;
+
+foo
+ : 'foo';
+bar
+ : 'bar';
+`,
+ src: `baz`,
+ cause: `baz`,
+ expected: []string{
+ "foo",
+ "bar",
+ },
+ },
+ {
+ caption: "the parser may report the EOF as an expected lookahead symbol",
+ specSrc: `
+#name test;
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `foobar`,
+ cause: `bar`,
+ expected: []string{
+ "<eof>",
+ },
+ },
+ {
+ caption: "the parser may report the EOF and others as expected lookahead symbols",
+ specSrc: `
+#name test;
+
+s
+ : foo
+ |
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `bar`,
+ cause: `bar`,
+ expected: []string{
+ "foo",
+ "<eof>",
+ },
+ },
+ {
+ caption: "when an anonymous symbol is expected, an expected symbol list contains an alias of the anonymous symbol",
+ specSrc: `
+#name test;
+
+s
+ : foo 'bar'
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `foobaz`,
+ cause: `baz`,
+ expected: []string{
+ "bar",
+ },
+ },
+ }
+ for i, tt := range tests {
+ t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) {
+ ast, err := spec.Parse(strings.NewReader(tt.specSrc))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ b := grammar.GrammarBuilder{
+ AST: ast,
+ }
+ g, err := b.Build()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gram, _, err := grammar.Compile(g)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ toks, err := NewTokenStream(gram, strings.NewReader(tt.src))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p, err := NewParser(toks, NewGrammar(gram))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = p.Parse()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ synErrs := p.SyntaxErrors()
+ if synErrs == nil {
+ t.Fatalf("expected one syntax error, but it didn't occur")
+ }
+ if len(synErrs) != 1 {
+ t.Fatalf("too many syntax errors: %v errors", len(synErrs))
+ }
+ synErr := synErrs[0]
+ if string(synErr.Token.Lexeme()) != tt.cause {
+ t.Fatalf("unexpected lexeme: want: %v, got: %v", tt.cause, string(synErr.Token.Lexeme()))
+ }
+ if len(synErr.ExpectedTerminals) != len(tt.expected) {
+ t.Fatalf("unexpected lookahead symbols: want: %v, got: %v", tt.expected, synErr.ExpectedTerminals)
+ }
+ sort.Slice(tt.expected, func(i, j int) bool {
+ return tt.expected[i] < tt.expected[j]
+ })
+ sort.Slice(synErr.ExpectedTerminals, func(i, j int) bool {
+ return synErr.ExpectedTerminals[i] < synErr.ExpectedTerminals[j]
+ })
+ for i, e := range tt.expected {
+ if synErr.ExpectedTerminals[i] != e {
+ t.Errorf("unexpected lookahead symbol: want: %v, got: %v", e, synErr.ExpectedTerminals[i])
+ }
+ }
+ })
+ }
+}