From 9a9444bdc00e2a738fb0aa7cac4afa8a123d679b Mon Sep 17 00:00:00 2001 From: Ryo Nihei Date: Sat, 16 Apr 2022 01:44:33 +0900 Subject: Prohibit using the same element multiple times in the #ast directive --- grammar/grammar.go | 20 ++++++++++++++++---- grammar/grammar_test.go | 28 ++++++++++++++++++++++++++++ grammar/semantic_error.go | 1 + 3 files changed, 45 insertions(+), 4 deletions(-) (limited to 'grammar') 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 @@ -771,6 +771,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: ` 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") ) -- cgit v1.2.3