Skip to content

Commit

Permalink
Add #assign directive
Browse files Browse the repository at this point in the history
An #assign directive changes only precedence.
  • Loading branch information
nihei9 committed May 10, 2022
1 parent 3eb0e88 commit 2438fa4
Show file tree
Hide file tree
Showing 5 changed files with 507 additions and 13 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ foobar

`#left` and `#right` directives allow you to define precedence and associativiry of symbols. `#left`/`#right` each assign the left/right associativity to symbols.

If you want to change precedence, `#assign` directive helps you. `#assign` directive changes only precedence, not associativity.

When the right-most terminal symbol of an alternative has precedence or associativity defined explicitly, the alternative inherits its precedence and associativity.

`#prec` directive assigns the same precedence as a specified symbol to an alternative.
Expand Down
165 changes: 152 additions & 13 deletions driver/conflict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,122 @@ id
),
},
{
caption: "left and right associativities can be mixed",
caption: "terminal symbols with an #assign directive defined earlier in the grammar have higher precedence",
specSrc: `
#name test;
#prec (
#assign a1
#assign a2
);
expr
: expr a2 expr
| expr a1 expr
| id
;
whitespaces #skip
: "[\u{0009}\u{0020}]+";
a1
: 'a1';
a2
: 'a2';
id
: "[A-Za-z0-9_]+";
`,
src: `a a2 b a1 c a1 d a2 e`,
cst: nonTermNode("expr",
nonTermNode("expr",
termNode("id", "a"),
),
termNode("a2", "a2"),
nonTermNode("expr",
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "b"),
),
termNode("a1", "a1"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "c"),
),
termNode("a1", "a1"),
nonTermNode("expr",
termNode("id", "d"),
),
),
),
termNode("a2", "a2"),
nonTermNode("expr",
termNode("id", "e"),
),
),
),
},
{
caption: "terminal symbols with an #assign directive defined in the same line have the same precedence",
specSrc: `
#name test;
#prec (
#assign a1 a2
);
expr
: expr a2 expr
| expr a1 expr
| id
;
whitespaces #skip
: "[\u{0009}\u{0020}]+";
a1
: 'a1';
a2
: 'a2';
id
: "[A-Za-z0-9_]+";
`,
src: `a a2 b a1 c a1 d a2 e`,
cst: nonTermNode("expr",
nonTermNode("expr",
termNode("id", "a"),
),
termNode("a2", "a2"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "b"),
),
termNode("a1", "a1"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "c"),
),
termNode("a1", "a1"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "d"),
),
termNode("a2", "a2"),
nonTermNode("expr",
termNode("id", "e"),
),
),
),
),
),
},
{
caption: "#left, #right, and #assign can be mixed",
specSrc: `
#name test;
#prec (
#left mul div
#left add sub
#assign else
#assign then
#right assign
);
Expand All @@ -290,18 +399,24 @@ expr
| expr sub expr
| expr mul expr
| expr div expr
| expr assign expr
| id
| expr assign expr
| if expr then expr
| if expr then expr else expr
| id
;
ws #skip: "[\u{0009}\u{0020}]+";
if: 'if';
then: 'then';
else: 'else';
id: "[A-Za-z0-9_]+";
add: '+';
sub: '-';
mul: '*';
div: '/';
assign: '=';
`,
src: `x=y=a+b*c-d/e`,
src: `x = y = a + b * c - d / e + if f then if g then h else i`,
cst: nonTermNode(
"expr",
nonTermNode("expr",
Expand All @@ -316,27 +431,51 @@ assign: '=';
nonTermNode("expr",
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "a"),
nonTermNode("expr",
termNode("id", "a"),
),
termNode("add", "+"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "b"),
),
termNode("mul", "*"),
nonTermNode("expr",
termNode("id", "c"),
),
),
),
termNode("add", "+"),
termNode("sub", "-"),
nonTermNode("expr",
nonTermNode("expr",
termNode("id", "b"),
termNode("id", "d"),
),
termNode("mul", "*"),
termNode("div", "/"),
nonTermNode("expr",
termNode("id", "c"),
termNode("id", "e"),
),
),
),
termNode("sub", "-"),
termNode("add", "+"),
nonTermNode("expr",
termNode("if", "if"),
nonTermNode("expr",
termNode("id", "d"),
termNode("id", "f"),
),
termNode("div", "/"),
termNode("then", "then"),
nonTermNode("expr",
termNode("id", "e"),
termNode("if", "if"),
nonTermNode("expr",
termNode("id", "g"),
),
termNode("then", "then"),
nonTermNode("expr",
termNode("id", "h"),
),
termNode("else", "else"),
nonTermNode("expr",
termNode("id", "i"),
),
),
),
),
Expand Down
2 changes: 2 additions & 0 deletions grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,8 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
assocTy = assocTypeLeft
case "right":
assocTy = assocTypeRight
case "assign":
assocTy = assocTypeNil
default:
b.errs = append(b.errs, &verr.SpecError{
Cause: semErrDirInvalidName,
Expand Down
Loading

0 comments on commit 2438fa4

Please sign in to comment.