aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2021-07-31 14:00:52 +0900
committerRyo Nihei <nihei.dev@gmail.com>2021-07-31 14:00:52 +0900
commit02ebb9bb630de5bc589e509e10c1d82629ab6487 (patch)
tree8905b07cdcfba957ecc86f9a4b0245f64409bbc0
parentPrevent terminals used in productions from being skipped (diff)
downloadurubu-02ebb9bb630de5bc589e509e10c1d82629ab6487.tar.gz
urubu-02ebb9bb630de5bc589e509e10c1d82629ab6487.tar.xz
Detect duplicate names between terminals and non-terminals
-rw-r--r--driver/parser_test.go26
-rw-r--r--grammar/grammar.go13
-rw-r--r--grammar/semantic_error.go3
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")
)