From 24fd80555cb35d9fc63cca9e8697bf156f41780b Mon Sep 17 00:00:00 2001 From: Ryo Nihei Date: Sat, 14 May 2022 15:45:09 +0900 Subject: 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. --- grammar/grammar.go | 46 ++++++++++++++++++++++---------- grammar/grammar_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 14 deletions(-) (limited to 'grammar') 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 @@ -1708,6 +1708,25 @@ s : foo ; +foo + : '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'; `, @@ -1931,6 +1950,25 @@ s : foo ; +foo + : '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'; `, @@ -2154,6 +2192,25 @@ s : foo ; +foo + : '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'; `, @@ -2663,6 +2720,20 @@ s : foo #prec ; +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDirInvalidParam}, + }, + { + caption: "the `#prec` directive cannot be applied to an error symbol", + specSrc: ` +#name test; + +s + : foo #prec error + ; + foo : 'foo'; `, -- cgit v1.2.3