aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-04-16 13:25:39 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-04-16 15:24:12 +0900
commit533c4545213b01d12a800c1c9d4ce2c85a12ae48 (patch)
treef97770d899f0bb927a31614f138b08a761a87a22
parentAdd tests for compiler (diff)
downloadurubu-533c4545213b01d12a800c1c9d4ce2c85a12ae48.tar.gz
urubu-533c4545213b01d12a800c1c9d4ce2c85a12ae48.tar.xz
Add tests for driver
-rw-r--r--driver/parser_test.go158
1 files changed, 155 insertions, 3 deletions
diff --git a/driver/parser_test.go b/driver/parser_test.go
index d6fccc5..fa8307e 100644
--- a/driver/parser_test.go
+++ b/driver/parser_test.go
@@ -17,6 +17,13 @@ func termNode(kind string, text string, children ...*Node) *Node {
}
}
+func errorNode() *Node {
+ return &Node{
+ KindName: "error",
+ Error: true,
+ }
+}
+
func nonTermNode(kind string, children ...*Node) *Node {
return &Node{
KindName: kind,
@@ -28,6 +35,7 @@ func TestParser_Parse(t *testing.T) {
tests := []struct {
specSrc string
src string
+ synErr bool
cst *Node
ast *Node
}{
@@ -278,6 +286,54 @@ bar #mode default
`,
src: `foobar`,
},
+ // When #push and #pop are applied to the same symbol, #pop will run first, then #push.
+ {
+ specSrc: `
+%name test
+
+s
+ : foo bar baz
+ ;
+
+foo #push m1
+ : 'foo';
+bar #mode m1 #pop #push m2
+ : 'bar';
+baz #mode m2
+ : 'baz';
+`,
+ src: `foobarbaz`,
+ ast: nonTermNode("s",
+ termNode("foo", "foo"),
+ termNode("bar", "bar"),
+ termNode("baz", "baz"),
+ ),
+ },
+ // When #push and #pop are applied to the same symbol, #pop will run first, then #push, even if #push appears first
+ // in a definition. That is, the order in which #push and #pop appear in grammar has nothing to do with the order in which
+ // they are executed.
+ {
+ specSrc: `
+%name test
+
+s
+ : foo bar baz
+ ;
+
+foo #push m1
+ : 'foo';
+bar #mode m1 #push m2 #pop
+ : 'bar';
+baz #mode m2
+ : 'baz';
+`,
+ src: `foobarbaz`,
+ ast: nonTermNode("s",
+ termNode("foo", "foo"),
+ termNode("bar", "bar"),
+ termNode("baz", "baz"),
+ ),
+ },
// The parser can skips specified tokens.
{
specSrc: `
@@ -349,6 +405,45 @@ id
termNode("id", "Langly"),
),
},
+ // The '...' operator can expand child nodes.
+ {
+ specSrc: `
+%name test
+
+s
+ : a #ast a...
+ ;
+a
+ : a ',' foo #ast a... foo
+ | foo
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `foo,foo,foo`,
+ ast: nonTermNode("s",
+ termNode("foo", "foo"),
+ termNode("foo", "foo"),
+ termNode("foo", "foo"),
+ ),
+ },
+ // The '...' operator also can applied to an element having no children.
+ {
+ specSrc: `
+%name test
+
+s
+ : a ';' #ast a...
+ ;
+
+a
+ :
+ ;
+`,
+ src: `;`,
+ ast: nonTermNode("s"),
+ },
// A label can be a parameter of #ast directive.
{
specSrc: `
@@ -382,6 +477,23 @@ num: "0|[1-9][0-9]*";
),
),
},
+ // An AST can contain a symbol name, even if the symbol has a label. That is, unused labels are allowed.
+ {
+ specSrc: `
+%name test
+
+s
+ : foo@x ';' #ast foo
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `foo;`,
+ ast: nonTermNode("s",
+ termNode("foo", "foo"),
+ ),
+ },
// The 'prec' directive can set precedence and associativity of a production.
{
specSrc: `
@@ -458,6 +570,44 @@ id
`,
src: `foo bar baz ;`,
},
+ // The 'error' symbol can appear in an #ast directive.
+ {
+ specSrc: `
+%name test
+
+s
+ : foo ';'
+ | error ';' #ast error
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `bar;`,
+ synErr: true,
+ ast: nonTermNode("s",
+ errorNode(),
+ ),
+ },
+ // The 'error' symbol can have a label, and an #ast can reference it.
+ {
+ specSrc: `
+%name test
+
+s
+ : foo ';'
+ | error@e ';' #ast e
+ ;
+
+foo
+ : 'foo';
+`,
+ src: `bar;`,
+ synErr: true,
+ ast: nonTermNode("s",
+ errorNode(),
+ ),
+ },
// The grammar can contain the 'recover' directive.
{
specSrc: `
@@ -546,8 +696,10 @@ bar: 'bar';
t.Fatal(err)
}
- if len(p.SyntaxErrors()) > 0 {
- t.Fatalf("unexpected syntax errors occurred: %+v", p.SyntaxErrors())
+ if !tt.synErr && len(p.SyntaxErrors()) > 0 {
+ for _, synErr := range p.SyntaxErrors() {
+ t.Fatalf("unexpected syntax errors occurred: %v", synErr)
+ }
}
switch {
@@ -564,7 +716,7 @@ bar: 'bar';
func testTree(t *testing.T, node, expected *Node) {
t.Helper()
- if node.KindName != expected.KindName || node.Text != expected.Text {
+ if node.KindName != expected.KindName || node.Text != expected.Text || node.Error != expected.Error {
t.Fatalf("unexpected node; want: %+v, got: %+v", expected, node)
}
if len(node.Children) != len(expected.Children) {