diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2021-07-31 14:00:52 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2021-07-31 14:00:52 +0900 |
commit | 02ebb9bb630de5bc589e509e10c1d82629ab6487 (patch) | |
tree | 8905b07cdcfba957ecc86f9a4b0245f64409bbc0 | |
parent | Prevent terminals used in productions from being skipped (diff) | |
download | urubu-02ebb9bb630de5bc589e509e10c1d82629ab6487.tar.gz urubu-02ebb9bb630de5bc589e509e10c1d82629ab6487.tar.xz |
Detect duplicate names between terminals and non-terminals
-rw-r--r-- | driver/parser_test.go | 26 | ||||
-rw-r--r-- | grammar/grammar.go | 13 | ||||
-rw-r--r-- | grammar/semantic_error.go | 3 |
3 files changed, 38 insertions, 4 deletions
diff --git a/driver/parser_test.go b/driver/parser_test.go index 8145d7b..5763d63 100644 --- a/driver/parser_test.go +++ b/driver/parser_test.go @@ -369,6 +369,32 @@ bar: "bar"; `, specErr: true, }, + // A terminal and a non-terminal (start symbol) are duplicates. + { + specSrc: ` +a + : foo + ; +foo: "foo"; +a: "a"; +`, + specErr: true, + }, + // A terminal and a non-terminal (not start symbol) are duplicates. + { + specSrc: ` +a + : foo + ; +b + : bar + ; +foo: "foo"; +bar: "bar"; +b: "a"; +`, + specErr: true, + }, } for i, tt := range tests { t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) { diff --git a/grammar/grammar.go b/grammar/grammar.go index 60f1705..3268902 100644 --- a/grammar/grammar.go +++ b/grammar/grammar.go @@ -203,7 +203,7 @@ func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolT for _, prod := range root.LexProductions { if _, exist := symTab.toSymbol(prod.LHS); exist { b.errs = append(b.errs, &verr.SpecError{ - Cause: semErrDuplicateSym, + Cause: semErrDuplicateTerminal, Detail: prod.LHS, Row: prod.Pos.Row, }) @@ -280,7 +280,7 @@ func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolT for _, fragment := range root.Fragments { if _, exist := checkedFragments[fragment.LHS]; exist { b.errs = append(b.errs, &verr.SpecError{ - Cause: semErrDuplicateSym, + Cause: semErrDuplicateTerminal, Detail: fragment.LHS, Row: fragment.Pos.Row, }) @@ -432,10 +432,17 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd prods.append(p) for _, prod := range root.Productions { - _, err := symTab.registerNonTerminalSymbol(prod.LHS) + sym, err := symTab.registerNonTerminalSymbol(prod.LHS) if err != nil { return nil, err } + if sym.isTerminal() { + b.errs = append(b.errs, &verr.SpecError{ + Cause: semErrDuplicateName, + Detail: prod.LHS, + Row: prod.Pos.Row, + }) + } } for _, prod := range root.Productions { diff --git a/grammar/semantic_error.go b/grammar/semantic_error.go index 4dcd313..2d722a8 100644 --- a/grammar/semantic_error.go +++ b/grammar/semantic_error.go @@ -21,7 +21,8 @@ var ( semErrNoProduction = newSemanticError("a grammar needs at least one production") semErrUndefinedSym = newSemanticError("undefined symbol") semErrDuplicateProduction = newSemanticError("duplicate production") - semErrDuplicateSym = newSemanticError("duplicate symbol") + semErrDuplicateTerminal = newSemanticError("duplicate terminal") + semErrDuplicateName = newSemanticError("duplicate names are not allowed between terminals and non-terminals") semErrDirInvalidName = newSemanticError("invalid directive name") semErrDirInvalidParam = newSemanticError("invalid parameter") ) |