diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 01:44:33 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 01:45:10 +0900 |
commit | 9a9444bdc00e2a738fb0aa7cac4afa8a123d679b (patch) | |
tree | 409797eff699062b8aa5d9df2d1ab9beaf356552 | |
parent | Prohibit specifying associativity and precedence multiple times for a symbol (diff) | |
download | urubu-9a9444bdc00e2a738fb0aa7cac4afa8a123d679b.tar.gz urubu-9a9444bdc00e2a738fb0aa7cac4afa8a123d679b.tar.xz |
Prohibit using the same element multiple times in the #ast directive
-rw-r--r-- | grammar/grammar.go | 20 | ||||
-rw-r--r-- | grammar/grammar_test.go | 28 | ||||
-rw-r--r-- | grammar/semantic_error.go | 1 |
3 files changed, 45 insertions, 4 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go index c5b8a0c..b405429 100644 --- a/grammar/grammar.go +++ b/grammar/grammar.go @@ -752,10 +752,11 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd continue LOOP_RHS } offsets[elem.Label.Name] = i - } else { - if elem.ID != "" { - offsets[elem.ID] = i - } + } + // A symbol having a label can be specified by both the label and the symbol name. + // So record the symbol's position, whether or not it has a label. + if elem.ID != "" { + offsets[elem.ID] = i } } @@ -829,6 +830,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd continue LOOP_RHS } astAct := make([]*astActionEntry, len(dir.Parameters)) + consumedOffsets := map[int]struct{}{} for i, param := range dir.Parameters { if param.ID == "" { b.errs = append(b.errs, &verr.SpecError{ @@ -850,6 +852,16 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd }) continue LOOP_RHS } + if _, consumed := consumedOffsets[offset]; consumed { + b.errs = append(b.errs, &verr.SpecError{ + Cause: semErrDuplicateElem, + Detail: param.ID, + Row: param.Pos.Row, + Col: param.Pos.Col, + }) + continue LOOP_RHS + } + consumedOffsets[offset] = struct{}{} if param.Expansion { elem := alt.Elements[offset] diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go index a90ee8b..2bb2358 100644 --- a/grammar/grammar_test.go +++ b/grammar/grammar_test.go @@ -772,6 +772,34 @@ bar errs: []*SemanticError{semErrDirInvalidParam}, }, { + caption: "a symbol can appear in the `#ast` directive only once", + specSrc: ` +%name test + +s + : foo #ast foo foo + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDuplicateElem}, + }, + { + caption: "a symbol can appear in the `#ast` directive only once, even if the symbol has a label", + specSrc: ` +%name test + +s + : foo@x #ast foo x + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDuplicateElem}, + }, + { caption: "the expansion operator cannot be applied to a terminal symbol", specSrc: ` %name test diff --git a/grammar/semantic_error.go b/grammar/semantic_error.go index c013cbc..a9543ef 100644 --- a/grammar/semantic_error.go +++ b/grammar/semantic_error.go @@ -34,6 +34,7 @@ var ( semErrDirInvalidName = newSemanticError("invalid directive name") semErrDirInvalidParam = newSemanticError("invalid parameter") semErrDuplicateDir = newSemanticError("a directive must not be duplicated") + semErrDuplicateElem = newSemanticError("duplicate element") semErrInvalidProdDir = newSemanticError("invalid production directive") semErrInvalidAltDir = newSemanticError("invalid alternative directive") ) |