aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-04-16 00:49:58 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-04-16 00:49:58 +0900
commit389dd0121475bdba7dea54f4cb02287fa48718da (patch)
tree4b2f811a8ce085cef72cdaa54c33d6cc6cb2270f
parentAdd tests for compiler (diff)
downloadurubu-389dd0121475bdba7dea54f4cb02287fa48718da.tar.gz
urubu-389dd0121475bdba7dea54f4cb02287fa48718da.tar.xz
Prohibit specifying associativity and precedence multiple times for a symbol
-rw-r--r--grammar/grammar.go30
-rw-r--r--grammar/grammar_test.go100
-rw-r--r--grammar/semantic_error.go1
3 files changed, 130 insertions, 1 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go
index babfb10..c5b8a0c 100644
--- a/grammar/grammar.go
+++ b/grammar/grammar.go
@@ -980,7 +980,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
})
return nil, nil
}
-
+ ASSOC_PARAM_LOOP:
for _, p := range md.Parameters {
if p.ID == "" {
b.errs = append(b.errs, &verr.SpecError{
@@ -1011,6 +1011,31 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
})
return nil, nil
}
+ if prec, alreadySet := termPrec[sym.num()]; alreadySet {
+ if prec == precN {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDuplicateAssoc,
+ Detail: fmt.Sprintf("'%v' already has the same associativity and precedence", p.ID),
+ Row: p.Pos.Row,
+ Col: p.Pos.Col,
+ })
+ } else if assoc := termAssoc[sym.num()]; assoc == assocTy {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDuplicateAssoc,
+ Detail: fmt.Sprintf("'%v' already has different precedence", p.ID),
+ Row: p.Pos.Row,
+ Col: p.Pos.Col,
+ })
+ } else {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDuplicateAssoc,
+ Detail: fmt.Sprintf("'%v' already has different associativity and precedence", p.ID),
+ Row: p.Pos.Row,
+ Col: p.Pos.Col,
+ })
+ }
+ break ASSOC_PARAM_LOOP
+ }
termPrec[sym.num()] = precN
termAssoc[sym.num()] = assocTy
@@ -1019,6 +1044,9 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
precN++
}
}
+ if len(b.errs) > 0 {
+ return nil, nil
+ }
prodPrec := map[productionNum]int{}
prodAssoc := map[productionNum]assocType{}
diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go
index 40c07aa..a90ee8b 100644
--- a/grammar/grammar_test.go
+++ b/grammar/grammar_test.go
@@ -442,6 +442,56 @@ foo
`,
errs: []*SemanticError{semErrMDInvalidParam},
},
+ {
+ caption: "the `%left` cannot be specified multiple times for a symbol",
+ specSrc: `
+%name test
+
+%left foo foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
+ {
+ caption: "a symbol cannot have different precedence",
+ specSrc: `
+%name test
+
+%left foo
+%left foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
+ {
+ caption: "a symbol cannot have different associativity",
+ specSrc: `
+%name test
+
+%right foo
+%left foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
}
rightTests := []*specErrTest{
@@ -525,6 +575,56 @@ foo
`,
errs: []*SemanticError{semErrMDInvalidParam},
},
+ {
+ caption: "the `%right` cannot be specified multiple times for a symbol",
+ specSrc: `
+%name test
+
+%right foo foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
+ {
+ caption: "a symbol cannot have different precedence",
+ specSrc: `
+%name test
+
+%right foo
+%right foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
+ {
+ caption: "a symbol cannot have different associativity",
+ specSrc: `
+%name test
+
+%left foo
+%right foo
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDuplicateAssoc},
+ },
}
errorSymTests := []*specErrTest{
diff --git a/grammar/semantic_error.go b/grammar/semantic_error.go
index 8e5b558..c013cbc 100644
--- a/grammar/semantic_error.go
+++ b/grammar/semantic_error.go
@@ -18,6 +18,7 @@ var (
semErrMDInvalidName = newSemanticError("invalid meta data name")
semErrMDInvalidParam = newSemanticError("invalid parameter")
semErrMDMissingName = newSemanticError("name is missing")
+ semErrDuplicateAssoc = newSemanticError("associativity and precedence cannot be specified multiple times for a symbol")
semErrUnusedProduction = newSemanticError("unused production")
semErrUnusedTerminal = newSemanticError("unused terminal")
semErrTermCannotBeSkipped = newSemanticError("a terminal used in productions cannot be skipped")