aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2021-08-22 19:15:03 +0900
committerRyo Nihei <nihei.dev@gmail.com>2021-08-22 19:15:03 +0900
commit02674d7264aea363a8f7b7839ab77ce64ba720db (patch)
treea849d566004b7d1e711fa4c667a54a5eab322c78
parentSupport %left and %right to specify precedences and associativities (diff)
downloadcotia-02674d7264aea363a8f7b7839ab77ce64ba720db.tar.gz
cotia-02674d7264aea363a8f7b7839ab77ce64ba720db.tar.xz
Add a column number to an error message
-rw-r--r--error/error.go5
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--grammar/grammar.go28
-rw-r--r--spec/lexer.go62
-rw-r--r--spec/lexer_test.go10
-rw-r--r--spec/parser_test.go82
7 files changed, 116 insertions, 77 deletions
diff --git a/error/error.go b/error/error.go
index 1781f2a..0e5d3af 100644
--- a/error/error.go
+++ b/error/error.go
@@ -39,6 +39,7 @@ type SpecError struct {
FilePath string
SourceName string
Row int
+ Col int
}
func (e *SpecError) Error() string {
@@ -46,8 +47,8 @@ func (e *SpecError) Error() string {
if e.SourceName != "" {
fmt.Fprintf(&b, "%v: ", e.SourceName)
}
- if e.Row != 0 {
- fmt.Fprintf(&b, "%v: ", e.Row)
+ if e.Row != 0 && e.Col != 0 {
+ fmt.Fprintf(&b, "%v:%v: ", e.Row, e.Col)
}
fmt.Fprintf(&b, "error: %v", e.Cause)
if e.Detail != "" {
diff --git a/go.mod b/go.mod
index 91f6e97..003e9d0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,6 @@ module github.com/nihei9/vartan
go 1.16
require (
- github.com/nihei9/maleeni v0.3.0
+ github.com/nihei9/maleeni v0.4.0
github.com/spf13/cobra v1.1.3
)
diff --git a/go.sum b/go.sum
index 27a01be..46b065f 100644
--- a/go.sum
+++ b/go.sum
@@ -118,8 +118,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nihei9/maleeni v0.3.0 h1:jNKxnHcoegf7iy1K253dl3nUlrV0BvZDr8a5pp+AF6E=
-github.com/nihei9/maleeni v0.3.0/go.mod h1:BWTzKvhWqb4xDo5koZCQJ2eLdkgKL2WY7KlXa7J0tSg=
+github.com/nihei9/maleeni v0.4.0 h1:ca1V9U7lZuf5c01Ls4HqlYf7eyhDWdGBv+uyBkChdf0=
+github.com/nihei9/maleeni v0.4.0/go.mod h1:d5x5jHHuema6IUi+aDPczMZQ4AlNokcKbEgB5T+70dI=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
diff --git a/grammar/grammar.go b/grammar/grammar.go
index 450c526..c4bbae3 100644
--- a/grammar/grammar.go
+++ b/grammar/grammar.go
@@ -116,6 +116,7 @@ func (b *GrammarBuilder) Build() (*Grammar, error) {
Cause: semErrTermCannotBeSkipped,
Detail: sym,
Row: prod.Pos.Row,
+ Col: prod.Pos.Col,
})
continue
}
@@ -128,6 +129,7 @@ func (b *GrammarBuilder) Build() (*Grammar, error) {
Cause: semErrUnusedProduction,
Detail: sym,
Row: prod.Pos.Row,
+ Col: prod.Pos.Col,
})
}
@@ -136,6 +138,7 @@ func (b *GrammarBuilder) Build() (*Grammar, error) {
Cause: semErrUnusedTerminal,
Detail: sym,
Row: prod.Pos.Row,
+ Col: prod.Pos.Col,
})
}
@@ -310,6 +313,7 @@ func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolT
Cause: semErrDuplicateTerminal,
Detail: prod.LHS,
Row: prod.Pos.Row,
+ Col: prod.Pos.Col,
})
continue
}
@@ -341,6 +345,7 @@ func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolT
Cause: semErrDuplicateTerminal,
Detail: fragment.LHS,
Row: fragment.Pos.Row,
+ Col: fragment.Pos.Col,
})
continue
}
@@ -376,6 +381,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'mode' directive needs an ID parameter"),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
for _, param := range dir.Parameters {
@@ -384,6 +390,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'mode' directive needs an ID parameter"),
Row: param.Pos.Row,
+ Col: param.Pos.Col,
}, nil
}
modes = append(modes, mlspec.LexModeName(param.ID))
@@ -393,6 +400,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidName,
Detail: dir.Name,
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
}
@@ -410,6 +418,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'skip' directive needs no parameter"),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
skip = true
@@ -419,6 +428,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'push' directive needs an ID parameter"),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
push = mlspec.LexModeName(dir.Parameters[0].ID)
@@ -428,6 +438,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'pop' directive needs no parameter"),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
pop = true
@@ -436,6 +447,7 @@ func genLexEntry(prod *spec.ProductionNode) (*mlspec.LexEntry, bool, *verr.SpecE
Cause: semErrDirInvalidName,
Detail: dir.Name,
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
}, nil
}
}
@@ -499,6 +511,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDuplicateName,
Detail: prod.LHS,
Row: prod.Pos.Row,
+ Col: prod.Pos.Col,
})
}
}
@@ -530,6 +543,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrUndefinedSym,
Detail: elem.ID,
Row: elem.Pos.Row,
+ Col: elem.Pos.Col,
})
continue LOOP_RHS
}
@@ -545,10 +559,13 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
// Report the line number of a duplicate alternative.
// When the alternative is empty, we report the position of its LHS.
var row int
+ var col int
if len(alt.Elements) > 0 {
row = alt.Elements[0].Pos.Row
+ col = alt.Elements[0].Pos.Col
} else {
row = prod.Pos.Row
+ col = prod.Pos.Col
}
var detail string
@@ -574,6 +591,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDuplicateProduction,
Detail: detail,
Row: row,
+ Col: col,
})
continue LOOP_RHS
}
@@ -588,6 +606,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'ast' directive needs a tree parameter"),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
})
continue LOOP_RHS
}
@@ -598,6 +617,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("a name of a tree structure must be the same ID as an LHS of a production; LHS: %v", lhsText),
Row: param.Pos.Row,
+ Col: param.Pos.Col,
})
continue LOOP_RHS
}
@@ -608,6 +628,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("a position must be less than or equal to the length of an alternativ (%v)", len(alt.Elements)),
Row: c.Pos.Row,
+ Col: c.Pos.Col,
})
continue LOOP_RHS
}
@@ -620,6 +641,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("the expansion symbol cannot be applied to a pattern ($%v: %v)", c.Position, elem.Pattern),
Row: c.Pos.Row,
+ Col: c.Pos.Col,
})
continue LOOP_RHS
}
@@ -633,6 +655,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("the expansion symbol cannot be applied to a terminal symbol ($%v: %v)", c.Position, elem.ID),
Row: c.Pos.Row,
+ Col: c.Pos.Col,
})
continue LOOP_RHS
}
@@ -649,6 +672,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
Cause: semErrDirInvalidName,
Detail: fmt.Sprintf("invalid directive name '%v'", dir.Name),
Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
})
continue LOOP_RHS
}
@@ -679,6 +703,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
return nil, &verr.SpecError{
Cause: semErrMDInvalidName,
Row: md.Pos.Row,
+ Col: md.Pos.Col,
}
}
@@ -687,6 +712,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
Cause: semErrMDInvalidParam,
Detail: "associativity needs at least one symbol",
Row: md.Pos.Row,
+ Col: md.Pos.Col,
}
}
@@ -697,6 +723,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
Cause: semErrMDInvalidParam,
Detail: fmt.Sprintf("'%v' is undefined", p.ID),
Row: p.Pos.Row,
+ Col: p.Pos.Col,
}
}
if !sym.isTerminal() {
@@ -704,6 +731,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
Cause: semErrMDInvalidParam,
Detail: fmt.Sprintf("associativity can take only terminal symbol ('%v' is a non-terminal)", p.ID),
Row: p.Pos.Row,
+ Col: p.Pos.Col,
}
}
diff --git a/spec/lexer.go b/spec/lexer.go
index da0cf74..258faae 100644
--- a/spec/lexer.go
+++ b/spec/lexer.go
@@ -37,11 +37,13 @@ const (
type Position struct {
Row int
+ Col int
}
-func newPosition(row int) Position {
+func newPosition(row, col int) Position {
return Position{
Row: row,
+ Col: col,
}
}
@@ -101,7 +103,6 @@ type lexer struct {
s *mlspec.CompiledLexSpec
d *mldriver.Lexer
buf *token
- row int
}
//go:embed clexspec.json
@@ -118,9 +119,8 @@ func newLexer(src io.Reader) (*lexer, error) {
return nil, err
}
return &lexer{
- s: s,
- d: d,
- row: 1,
+ s: s,
+ d: d,
}, nil
}
@@ -159,7 +159,7 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
return nil, err
}
if tok.Invalid {
- return newInvalidToken(tok.Text(), newPosition(l.row)), nil
+ return newInvalidToken(tok.Text(), newPosition(tok.Row+1, tok.Col+1)), nil
}
if tok.EOF {
return newEOFToken(), nil
@@ -176,20 +176,19 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
switch tok.KindName {
case "newline":
- row := l.row
- l.row++
- return newSymbolToken(tokenKindNewline, newPosition(row)), nil
+ return newSymbolToken(tokenKindNewline, newPosition(tok.Row+1, tok.Col+1)), nil
case "kw_fragment":
- return newSymbolToken(tokenKindKWFragment, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindKWFragment, newPosition(tok.Row+1, tok.Col+1)), nil
case "identifier":
if strings.HasPrefix(tok.Text(), "_") {
return nil, &verr.SpecError{
Cause: synErrAutoGenID,
Detail: tok.Text(),
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
}
- return newIDToken(tok.Text(), newPosition(l.row)), nil
+ return newIDToken(tok.Text(), newPosition(tok.Row+1, tok.Col+1)), nil
case "terminal_open":
var b strings.Builder
for {
@@ -200,7 +199,8 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
if tok.EOF {
return nil, &verr.SpecError{
Cause: synErrUnclosedTerminal,
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
}
switch tok.KindName {
@@ -210,17 +210,19 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
case "escape_symbol":
return nil, &verr.SpecError{
Cause: synErrIncompletedEscSeq,
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
case "terminal_close":
pat := b.String()
if pat == "" {
return nil, &verr.SpecError{
Cause: synErrEmptyPattern,
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
}
- return newTerminalPatternToken(pat, newPosition(l.row)), nil
+ return newTerminalPatternToken(pat, newPosition(tok.Row+1, tok.Col+1)), nil
}
}
case "literal_pattern":
@@ -228,22 +230,23 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
if pat == "" {
return nil, &verr.SpecError{
Cause: synErrEmptyPattern,
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
}
- return newTerminalPatternToken(mlspec.EscapePattern(pat), newPosition(l.row)), nil
+ return newTerminalPatternToken(mlspec.EscapePattern(pat), newPosition(tok.Row+1, tok.Col+1)), nil
case "colon":
- return newSymbolToken(tokenKindColon, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindColon, newPosition(tok.Row+1, tok.Col+1)), nil
case "or":
- return newSymbolToken(tokenKindOr, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindOr, newPosition(tok.Row+1, tok.Col+1)), nil
case "semicolon":
- return newSymbolToken(tokenKindSemicolon, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindSemicolon, newPosition(tok.Row+1, tok.Col+1)), nil
case "directive_marker":
- return newSymbolToken(tokenKindDirectiveMarker, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindDirectiveMarker, newPosition(tok.Row+1, tok.Col+1)), nil
case "tree_node_open":
- return newSymbolToken(tokenKindTreeNodeOpen, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindTreeNodeOpen, newPosition(tok.Row+1, tok.Col+1)), nil
case "tree_node_close":
- return newSymbolToken(tokenKindTreeNodeClose, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindTreeNodeClose, newPosition(tok.Row+1, tok.Col+1)), nil
case "position":
// Remove '$' character and convert to an integer.
num, err := strconv.Atoi(tok.Text()[1:])
@@ -253,15 +256,16 @@ func (l *lexer) lexAndSkipWSs() (*token, error) {
if num == 0 {
return nil, &verr.SpecError{
Cause: synErrZeroPos,
- Row: l.row,
+ Row: tok.Row + 1,
+ Col: tok.Col + 1,
}
}
- return newPositionToken(num, newPosition(l.row)), nil
+ return newPositionToken(num, newPosition(tok.Row+1, tok.Col+1)), nil
case "expansion":
- return newSymbolToken(tokenKindExpantion, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindExpantion, newPosition(tok.Row+1, tok.Col+1)), nil
case "metadata_marker":
- return newSymbolToken(tokenKindMetaDataMarker, newPosition(l.row)), nil
+ return newSymbolToken(tokenKindMetaDataMarker, newPosition(tok.Row+1, tok.Col+1)), nil
default:
- return newInvalidToken(tok.Text(), newPosition(l.row)), nil
+ return newInvalidToken(tok.Text(), newPosition(tok.Row+1, tok.Col+1)), nil
}
}
diff --git a/spec/lexer_test.go b/spec/lexer_test.go
index e5d999e..51e5f59 100644
--- a/spec/lexer_test.go
+++ b/spec/lexer_test.go
@@ -9,23 +9,23 @@ import (
func TestLexer_Run(t *testing.T) {
idTok := func(text string) *token {
- return newIDToken(text, newPosition(1))
+ return newIDToken(text, newPosition(1, 0))
}
termPatTok := func(text string) *token {
- return newTerminalPatternToken(text, newPosition(1))
+ return newTerminalPatternToken(text, newPosition(1, 0))
}
symTok := func(kind tokenKind) *token {
- return newSymbolToken(kind, newPosition(1))
+ return newSymbolToken(kind, newPosition(1, 0))
}
posTok := func(num int) *token {
- return newPositionToken(num, newPosition(1))
+ return newPositionToken(num, newPosition(1, 0))
}
invalidTok := func(text string) *token {
- return newInvalidToken(text, newPosition(1))
+ return newInvalidToken(text, newPosition(1, 0))
}
tests := []struct {
diff --git a/spec/parser_test.go b/spec/parser_test.go
index 4821562..d20f6f9 100644
--- a/spec/parser_test.go
+++ b/spec/parser_test.go
@@ -111,6 +111,12 @@ func TestParse(t *testing.T) {
frag.Pos = pos
return frag
}
+ newPos := func(row int) Position {
+ return Position{
+ Row: row,
+ Col: 0,
+ }
+ }
tests := []struct {
caption string
@@ -256,13 +262,13 @@ foo: 'foo';
alt(
withElemPos(
id("foo"),
- newPosition(6),
+ newPos(6),
),
),
- newPosition(6),
+ newPos(6),
),
),
- newPosition(5),
+ newPos(5),
),
},
LexProductions: []*ProductionNode{
@@ -272,13 +278,13 @@ foo: 'foo';
alt(
withElemPos(
pat(`foo`),
- newPosition(13),
+ newPos(13),
),
),
- newPosition(13),
+ newPos(13),
),
),
- newPosition(13),
+ newPos(13),
),
},
},
@@ -463,42 +469,42 @@ fragment number: "[0-9]";
withAltPos(
withAltDir(
alt(
- withElemPos(id("exp"), newPosition(4)),
- withElemPos(pat(`\+`), newPosition(4)),
- withElemPos(id("id"), newPosition(4)),
+ withElemPos(id("exp"), newPos(4)),
+ withElemPos(pat(`\+`), newPos(4)),
+ withElemPos(id("id"), newPos(4)),
),
withDirPos(
dir("ast",
withParamPos(
treeParam("exp",
- withTreeChildPos(pos(1), newPosition(4)),
- withTreeChildPos(pos(2), newPosition(4))),
- newPosition(4),
+ withTreeChildPos(pos(1), newPos(4)),
+ withTreeChildPos(pos(2), newPos(4))),
+ newPos(4),
),
),
- newPosition(4),
+ newPos(4),
),
),
- newPosition(4),
+ newPos(4),
),
withAltPos(
alt(
- withElemPos(id("id"), newPosition(5)),
+ withElemPos(id("id"), newPos(5)),
),
- newPosition(5),
+ newPos(5),
),
),
withDirPos(
dir("mode",
withParamPos(
idParam("default"),
- newPosition(2),
+ newPos(2),
),
),
- newPosition(2),
+ newPos(2),
),
),
- newPosition(3),
+ newPos(3),
),
},
LexProductions: []*ProductionNode{
@@ -509,18 +515,18 @@ fragment number: "[0-9]";
alt(
withElemPos(
pat(`\u{0020}+`),
- newPosition(7),
+ newPos(7),
),
),
withDirPos(
dir("skip"),
- newPosition(7),
+ newPos(7),
),
),
- newPosition(7),
+ newPos(7),
),
),
- newPosition(7),
+ newPos(7),
),
withProdPos(
prod("id",
@@ -528,23 +534,23 @@ fragment number: "[0-9]";
alt(
withElemPos(
pat(`\f{letter}(\f{letter}|\f{number})*`),
- newPosition(8),
+ newPos(8),
),
),
- newPosition(8),
+ newPos(8),
),
),
- newPosition(8),
+ newPos(8),
),
},
Fragments: []*FragmentNode{
withFragmentPos(
frag("letter", "[A-Za-z_]"),
- newPosition(9),
+ newPos(9),
),
withFragmentPos(
frag("number", "[0-9]"),
- newPosition(10),
+ newPos(10),
),
},
},
@@ -575,29 +581,29 @@ id: "[A-Za-z0-9_]+";
MetaData: []*DirectiveNode{
withDirPos(
leftAssoc(
- withParamPos(idParam("l1"), newPosition(2)),
- withParamPos(idParam("l2"), newPosition(2)),
+ withParamPos(idParam("l1"), newPos(2)),
+ withParamPos(idParam("l2"), newPos(2)),
),
- newPosition(2),
+ newPos(2),
),
withDirPos(
leftAssoc(
- withParamPos(idParam("l3"), newPosition(3)),
+ withParamPos(idParam("l3"), newPos(3)),
),
- newPosition(3),
+ newPos(3),
),
withDirPos(
rightAssoc(
- withParamPos(idParam("r1"), newPosition(4)),
- withParamPos(idParam("r2"), newPosition(4)),
+ withParamPos(idParam("r1"), newPos(4)),
+ withParamPos(idParam("r2"), newPos(4)),
),
- newPosition(4),
+ newPos(4),
),
withDirPos(
rightAssoc(
- withParamPos(idParam("r3"), newPosition(5)),
+ withParamPos(idParam("r3"), newPos(5)),
),
- newPosition(5),
+ newPos(5),
),
},
Productions: []*ProductionNode{