aboutsummaryrefslogtreecommitdiff
path: root/spec/parser_test.go
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2021-08-22 15:39:27 +0900
committerRyo Nihei <nihei.dev@gmail.com>2021-08-22 15:44:03 +0900
commit4d879b95d5368d578a39baaefba0de743a764105 (patch)
treea0e78f5c9916405545f1e7b139f9427b1cd190cc /spec/parser_test.go
parentResolve conflicts by default rules (diff)
downloadurubu-4d879b95d5368d578a39baaefba0de743a764105.tar.gz
urubu-4d879b95d5368d578a39baaefba0de743a764105.tar.xz
Support %left and %right to specify precedences and associativities
Diffstat (limited to 'spec/parser_test.go')
-rw-r--r--spec/parser_test.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/spec/parser_test.go b/spec/parser_test.go
index 29d9051..4821562 100644
--- a/spec/parser_test.go
+++ b/spec/parser_test.go
@@ -8,6 +8,18 @@ import (
)
func TestParse(t *testing.T) {
+ leftAssoc := func(params ...*ParameterNode) *DirectiveNode {
+ return &DirectiveNode{
+ Name: "left",
+ Parameters: params,
+ }
+ }
+ rightAssoc := func(params ...*ParameterNode) *DirectiveNode {
+ return &DirectiveNode{
+ Name: "right",
+ Parameters: params,
+ }
+ }
prod := func(lhs string, alts ...*AlternativeNode) *ProductionNode {
return &ProductionNode{
LHS: lhs,
@@ -537,6 +549,80 @@ fragment number: "[0-9]";
},
},
},
+ {
+ caption: "a grammar can contain left and right associativities",
+ src: `
+%left l1 l2
+%left l3
+%right r1 r2
+%right r3
+
+s
+ : id l1 id l2 id l3 id
+ | id r1 id r2 id r3 id
+ ;
+
+whitespaces: "[\u{0009}\u{0020}]+" #skip;
+l1: 'l1';
+l2: 'l2';
+l3: 'l3';
+r1: 'r1';
+r2: 'r2';
+r3: 'r3';
+id: "[A-Za-z0-9_]+";
+`,
+ ast: &RootNode{
+ MetaData: []*DirectiveNode{
+ withDirPos(
+ leftAssoc(
+ withParamPos(idParam("l1"), newPosition(2)),
+ withParamPos(idParam("l2"), newPosition(2)),
+ ),
+ newPosition(2),
+ ),
+ withDirPos(
+ leftAssoc(
+ withParamPos(idParam("l3"), newPosition(3)),
+ ),
+ newPosition(3),
+ ),
+ withDirPos(
+ rightAssoc(
+ withParamPos(idParam("r1"), newPosition(4)),
+ withParamPos(idParam("r2"), newPosition(4)),
+ ),
+ newPosition(4),
+ ),
+ withDirPos(
+ rightAssoc(
+ withParamPos(idParam("r3"), newPosition(5)),
+ ),
+ newPosition(5),
+ ),
+ },
+ Productions: []*ProductionNode{
+ prod("s",
+ alt(id(`id`), id(`l1`), id(`id`), id(`l2`), id(`id`), id(`l3`), id(`id`)),
+ alt(id(`id`), id(`r1`), id(`id`), id(`r2`), id(`id`), id(`r3`), id(`id`)),
+ ),
+ },
+ LexProductions: []*ProductionNode{
+ prod("whitespaces",
+ withAltDir(
+ alt(pat(`[\u{0009}\u{0020}]+`)),
+ dir("skip"),
+ ),
+ ),
+ prod("l1", alt(pat(`l1`))),
+ prod("l2", alt(pat(`l2`))),
+ prod("l3", alt(pat(`l3`))),
+ prod("r1", alt(pat(`r1`))),
+ prod("r2", alt(pat(`r2`))),
+ prod("r3", alt(pat(`r3`))),
+ prod("id", alt(pat(`[A-Za-z0-9_]+`))),
+ },
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.caption, func(t *testing.T) {
@@ -571,6 +657,12 @@ func testRootNode(t *testing.T, root, expected *RootNode, checkPosition bool) {
if len(root.Productions) != len(expected.Productions) {
t.Fatalf("unexpected length of productions; want: %v, got: %v", len(expected.Productions), len(root.Productions))
}
+ if len(root.MetaData) != len(expected.MetaData) {
+ t.Fatalf("unexpected length of meta data; want: %v, got: %v", len(expected.MetaData), len(root.MetaData))
+ }
+ for i, md := range root.MetaData {
+ testDirective(t, md, expected.MetaData[i], true)
+ }
for i, prod := range root.Productions {
testProductionNode(t, prod, expected.Productions[i], checkPosition)
}