aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-04-16 01:44:33 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-04-16 01:45:10 +0900
commit9a9444bdc00e2a738fb0aa7cac4afa8a123d679b (patch)
tree409797eff699062b8aa5d9df2d1ab9beaf356552
parentProhibit specifying associativity and precedence multiple times for a symbol (diff)
downloadurubu-9a9444bdc00e2a738fb0aa7cac4afa8a123d679b.tar.gz
urubu-9a9444bdc00e2a738fb0aa7cac4afa8a123d679b.tar.xz
Prohibit using the same element multiple times in the #ast directive
-rw-r--r--grammar/grammar.go20
-rw-r--r--grammar/grammar_test.go28
-rw-r--r--grammar/semantic_error.go1
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")
)