diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-05-14 15:45:09 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-05-15 20:42:28 +0900 |
commit | 24fd80555cb35d9fc63cca9e8697bf156f41780b (patch) | |
tree | 0bb5402b8061f63ebef0ff779e59d0d50e20db9e | |
parent | Prohibit applying the expansion operator to anything other than identifiers (diff) | |
download | cotia-24fd80555cb35d9fc63cca9e8697bf156f41780b.tar.gz cotia-24fd80555cb35d9fc63cca9e8697bf156f41780b.tar.xz |
Prohibit applying #left, #right, #assign, and #prec to an error symbol
The shift of the error symbol is an operation forced by the driver.
Therefore it is impossible to change this behavior by giving precedence
to the error symbol. If we desire to change the precedence of a production
rule with the error symbol, we can use #prec directive.
-rw-r--r-- | grammar/grammar.go | 46 | ||||
-rw-r--r-- | grammar/grammar_test.go | 71 |
2 files changed, 103 insertions, 14 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go index 82eff5d..410236b 100644 --- a/grammar/grammar.go +++ b/grammar/grammar.go @@ -155,7 +155,7 @@ func (b *GrammarBuilder) Build() (*Grammar, error) { return nil, b.errs } - pa, err := b.genPrecAndAssoc(symTabAndLexSpec.symTab, prodsAndActs) + pa, err := b.genPrecAndAssoc(symTabAndLexSpec.symTab, symTabAndLexSpec.errSym, prodsAndActs) if err != nil { return nil, err } @@ -999,32 +999,41 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd }) continue LOOP_RHS } + param := dir.Parameters[0] switch { - case dir.Parameters[0].ID != "": - sym, ok := symTab.toSymbol(dir.Parameters[0].ID) + case param.ID != "": + sym, ok := symTab.toSymbol(param.ID) if !ok { b.errs = append(b.errs, &verr.SpecError{ Cause: semErrDirInvalidParam, - Detail: fmt.Sprintf("unknown terminal symbol: %v", dir.Parameters[0].ID), - Row: dir.Pos.Row, - Col: dir.Pos.Col, + Detail: fmt.Sprintf("unknown terminal symbol: %v", param.ID), + Row: param.Pos.Row, + Col: param.Pos.Col, }) continue LOOP_RHS } + if sym == errSym { + b.errs = append(b.errs, &verr.SpecError{ + Cause: semErrDirInvalidParam, + Detail: fmt.Sprintf("'%v' directive cannot be applied to an error symbol", dir.Name), + Row: param.Pos.Row, + Col: param.Pos.Col, + }) + } if !sym.isTerminal() { b.errs = append(b.errs, &verr.SpecError{ Cause: semErrDirInvalidParam, - Detail: fmt.Sprintf("the symbol must be a terminal: %v", dir.Parameters[0].ID), - Row: dir.Pos.Row, - Col: dir.Pos.Col, + Detail: fmt.Sprintf("the symbol must be a terminal: %v", param.ID), + Row: param.Pos.Row, + Col: param.Pos.Col, }) continue LOOP_RHS } prodPrecsTerm[p.id] = sym - prodPrecPoss[p.id] = &dir.Parameters[0].Pos - case dir.Parameters[0].OrderedSymbol != "": - prodPrecsOrdSym[p.id] = dir.Parameters[0].OrderedSymbol - prodPrecPoss[p.id] = &dir.Parameters[0].Pos + prodPrecPoss[p.id] = ¶m.Pos + case param.OrderedSymbol != "": + prodPrecsOrdSym[p.id] = param.OrderedSymbol + prodPrecPoss[p.id] = ¶m.Pos } case "recover": if len(dir.Parameters) > 0 { @@ -1061,7 +1070,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd }, nil } -func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prodsAndActs *productionsAndActions) (*precAndAssoc, error) { +func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, errSym symbol, prodsAndActs *productionsAndActions) (*precAndAssoc, error) { termPrec := map[symbolNum]int{} termAssoc := map[symbolNum]assocType{} ordSymPrec := map[string]int{} @@ -1136,6 +1145,15 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prodsAndActs *prod }) return nil, nil } + if sym == errSym { + b.errs = append(b.errs, &verr.SpecError{ + Cause: semErrDirInvalidParam, + Detail: fmt.Sprintf("'%v' directive cannot be applied to an error symbol", dir.Name), + Row: p.Pos.Row, + Col: p.Pos.Col, + }) + return nil, nil + } if !sym.isTerminal() { b.errs = append(b.errs, &verr.SpecError{ Cause: semErrDirInvalidParam, diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go index 5a8bb4a..aeeffac 100644 --- a/grammar/grammar_test.go +++ b/grammar/grammar_test.go @@ -1714,6 +1714,25 @@ foo errs: []*SemanticError{semErrDirInvalidParam}, }, { + caption: "the `#left` directive cannot be applied to an error symbol", + specSrc: ` +#name test; + +#prec ( + #left error +); + +s + : foo ';' + | error ';' + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDirInvalidParam}, + }, + { caption: "the `#left` directive cannot take an undefined symbol", specSrc: ` #name test; @@ -1937,6 +1956,25 @@ foo errs: []*SemanticError{semErrDirInvalidParam}, }, { + caption: "the `#right` directive cannot be applied to an error symbol", + specSrc: ` +#name test; + +#prec ( + #right error +); + +s + : foo ';' + | error ';' + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDirInvalidParam}, + }, + { caption: "the `#right` directive cannot take an undefined symbol", specSrc: ` #name test; @@ -2160,6 +2198,25 @@ foo errs: []*SemanticError{semErrDirInvalidParam}, }, { + caption: "the `#assign` directive cannot be applied to an error symbol", + specSrc: ` +#name test; + +#prec ( + #assign error +); + +s + : foo ';' + | error ';' + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDirInvalidParam}, + }, + { caption: "the `#assign` directive cannot take an undefined symbol", specSrc: ` #name test; @@ -2669,6 +2726,20 @@ foo errs: []*SemanticError{semErrDirInvalidParam}, }, { + caption: "the `#prec` directive cannot be applied to an error symbol", + specSrc: ` +#name test; + +s + : foo #prec error + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDirInvalidParam}, + }, + { caption: "the `#prec` directive cannot take an undefined symbol", specSrc: ` #name test; |