aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-04-16 16:06:31 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-04-16 16:06:31 +0900
commit8bf4d234d0b983d92378ba91660cae30e35f16f0 (patch)
treee39a78975fb3005b0e02f6552723a887511acc83
parentAdd tests for driver (diff)
downloadurubu-8bf4d234d0b983d92378ba91660cae30e35f16f0.tar.gz
urubu-8bf4d234d0b983d92378ba91660cae30e35f16f0.tar.xz
Prohibit ambiguous symbol in an #ast directive
-rw-r--r--grammar/grammar.go21
-rw-r--r--grammar/grammar_test.go28
-rw-r--r--grammar/semantic_error.go1
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")
)