diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 00:49:58 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 00:49:58 +0900 |
commit | 389dd0121475bdba7dea54f4cb02287fa48718da (patch) | |
tree | 4b2f811a8ce085cef72cdaa54c33d6cc6cb2270f | |
parent | Add tests for compiler (diff) | |
download | urubu-389dd0121475bdba7dea54f4cb02287fa48718da.tar.gz urubu-389dd0121475bdba7dea54f4cb02287fa48718da.tar.xz |
Prohibit specifying associativity and precedence multiple times for a symbol
-rw-r--r-- | grammar/grammar.go | 30 | ||||
-rw-r--r-- | grammar/grammar_test.go | 100 | ||||
-rw-r--r-- | grammar/semantic_error.go | 1 |
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") |