diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 16:06:31 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 16:06:31 +0900 |
commit | 8bf4d234d0b983d92378ba91660cae30e35f16f0 (patch) | |
tree | e39a78975fb3005b0e02f6552723a887511acc83 | |
parent | Add tests for driver (diff) | |
download | urubu-8bf4d234d0b983d92378ba91660cae30e35f16f0.tar.gz urubu-8bf4d234d0b983d92378ba91660cae30e35f16f0.tar.xz |
Prohibit ambiguous symbol in an #ast directive
-rw-r--r-- | grammar/grammar.go | 21 | ||||
-rw-r--r-- | grammar/grammar_test.go | 28 | ||||
-rw-r--r-- | grammar/semantic_error.go | 1 |
3 files changed, 49 insertions, 1 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go index b405429..4e5b3b2 100644 --- a/grammar/grammar.go +++ b/grammar/grammar.go @@ -701,6 +701,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd for _, alt := range prod.RHS { altSyms := make([]symbol, len(alt.Elements)) offsets := map[string]int{} + ambiguousIDOffsets := map[string]struct{}{} for i, elem := range alt.Elements { var sym symbol if elem.Pattern != "" { @@ -756,7 +757,15 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd // 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 + if _, exist := offsets[elem.ID]; exist { + // When the same symbol appears multiple times in an alternative, the symbol is ambiguous. When we need + // to specify the symbol in a directive, we cannot use the name of the ambiguous symbol. Instead, specify + // a label to resolve the ambiguity. + delete(offsets, elem.ID) + ambiguousIDOffsets[elem.ID] = struct{}{} + } else { + offsets[elem.ID] = i + } } } @@ -842,6 +851,16 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd continue LOOP_RHS } + if _, ambiguous := ambiguousIDOffsets[param.ID]; ambiguous { + b.errs = append(b.errs, &verr.SpecError{ + Cause: semErrAmbiguousElem, + Detail: fmt.Sprintf("'%v' is ambiguous", param.ID), + Row: param.Pos.Row, + Col: param.Pos.Col, + }) + continue LOOP_RHS + } + offset, ok := offsets[param.ID] if !ok { b.errs = append(b.errs, &verr.SpecError{ diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go index 1448d3c..0295798 100644 --- a/grammar/grammar_test.go +++ b/grammar/grammar_test.go @@ -1024,6 +1024,34 @@ foo errs: []*SemanticError{semErrDuplicateElem}, }, { + caption: "symbol `foo` is ambiguous because it appears in an alternative twice", + specSrc: ` +%name test + +s + : foo foo #ast foo + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrAmbiguousElem}, + }, + { + caption: "symbol `foo` is ambiguous because it appears in an alternative twice, even if one of them has a label", + specSrc: ` +%name test + +s + : foo@x foo #ast foo + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrAmbiguousElem}, + }, + { 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 a9543ef..079f826 100644 --- a/grammar/semantic_error.go +++ b/grammar/semantic_error.go @@ -35,6 +35,7 @@ var ( semErrDirInvalidParam = newSemanticError("invalid parameter") semErrDuplicateDir = newSemanticError("a directive must not be duplicated") semErrDuplicateElem = newSemanticError("duplicate element") + semErrAmbiguousElem = newSemanticError("ambiguous element") semErrInvalidProdDir = newSemanticError("invalid production directive") semErrInvalidAltDir = newSemanticError("invalid alternative directive") ) |