diff --git a/example/errorrecovery/Makefile b/example/errorrecovery/Makefile index 5831fc7d..ceb61ede 100644 --- a/example/errorrecovery/Makefile +++ b/example/errorrecovery/Makefile @@ -1,2 +1,2 @@ regenerate: - gocc er.bnf \ No newline at end of file + gocc er.bnf diff --git a/example/errorrecovery/er.bnf b/example/errorrecovery/er.bnf index 47ec84b6..f423f706 100644 --- a/example/errorrecovery/er.bnf +++ b/example/errorrecovery/er.bnf @@ -22,4 +22,4 @@ StmtList : Stmt : id << ast.NewStmt($0) >> | error -; \ No newline at end of file +; diff --git a/example/errorrecovery/er_test.go b/example/errorrecovery/er_test.go index 8af87bb3..de8550ea 100644 --- a/example/errorrecovery/er_test.go +++ b/example/errorrecovery/er_test.go @@ -11,7 +11,7 @@ import ( ) func TestFail(t *testing.T) { - sml, err := test([]byte("a b ; d e f")) + sml, err := test([]byte("a ; b")) if err != nil { t.Fail() } diff --git a/internal/parser/gen/golang/parser.go b/internal/parser/gen/golang/parser.go index 3eaf788f..bd11d540 100644 --- a/internal/parser/gen/golang/parser.go +++ b/internal/parser/gen/golang/parser.go @@ -173,7 +173,7 @@ func (p *Parser) Error(err error, scanner Scanner) (recovered bool, errorAttrib errorAttrib = &parseError.Error{ Err: err, ErrorToken: p.nextToken, - ErrorSymbols: p.popNonRecoveryStates(), + ErrorSymbols: []parseError.ErrorSymbol{}, ExpectedTokens: make([]string, 0, 8), } for t, action := range actionTab[p.stack.top()].actions { @@ -182,8 +182,58 @@ func (p *Parser) Error(err error, scanner Scanner) (recovered bool, errorAttrib } } + lostSymbols := []*token.Token{} + // The current input symbol is invalid. Keep scanning the input symbols + // until the next valid symbol is encountered. + p.nextToken = scanner.Scan() + for len(p.nextToken.Lit) > 0 { + if action := actionTab[p.stack.top()].actions[p.nextToken.Type]; action != nil { + break + } else { + lostSymbols = append(lostSymbols, p.nextToken) + p.nextToken = scanner.Scan() + } + } + {{- if .Debug }} + fmt.Printf("Lost %d error symbol(s)\n", len(lostSymbols)) + fmt.Printf("Next valid input symbol: %s\n", p.nextToken.Lit) + {{- end }} + + // If the action corresponding to the found valid input symbol is a + // reduce, perform it. If it is a shift, defer performing the action + // until the error attribute is pushed to the stack. + for again := true; again; { + action := actionTab[p.stack.top()].actions[p.nextToken.Type] + if action == nil { + break + } + switch act := action.(type) { + case reduce: + prod := productionsTable[int(act)] + attrib, err := prod.ReduceFunc(p.stack.popN(prod.NumSymbols)) + if err != nil { + return false, errorAttrib + } else { + p.stack.push(gotoTab[p.stack.top()][prod.NTType], attrib) + } + case shift: + // Defer pushing the valid input symbol. + again = false + case accept: + again = false + default: + panic("unknown action: " + action.String()) + } + } + if action := actionTab[p.stack.top()].actions[token.TokMap.Type("error")]; action != nil { - p.stack.push(int(action.(shift)), errorAttrib) // action can only be shift + {{- if .Debug }} + // NOTE: In this case we push the errorAttrib and not the + // invalid symbol. + fmt.Println("Pushing errorAttrib, look below for modified stack top\n") + {{- end }} + // Action corresponding to errorAttrib can only be a shift. + p.stack.push(int(action.(shift)), errorAttrib) } else { return } @@ -203,6 +253,9 @@ func (p *Parser) Error(err error, scanner Scanner) (recovered bool, errorAttrib func (p *Parser) popNonRecoveryStates() (removedAttribs []parseError.ErrorSymbol) { if rs, ok := p.firstRecoveryState(); ok { + {{- if .Debug }} + fmt.Printf("Number of pops performed to reach the next recovery state: %d\n", p.stack.topIndex()-rs) + {{- end }} errorSymbols := p.stack.popN(p.stack.topIndex() - rs) removedAttribs = make([]parseError.ErrorSymbol, len(errorSymbols)) for i, e := range errorSymbols { @@ -211,6 +264,19 @@ func (p *Parser) popNonRecoveryStates() (removedAttribs []parseError.ErrorSymbol } else { removedAttribs = []parseError.ErrorSymbol{} } + + {{- if .Debug }} + fmt.Printf("Attribute corresponding to the popped non-recovery states: ") + for _, v := range removedAttribs { + switch attr := v.(type) { + case *token.Token: + fmt.Printf("%s ", attr.Lit) + default: + fmt.Printf("%v ", attr) + } + } + fmt.Println() + {{- end }} return } @@ -221,6 +287,7 @@ func (p *Parser) firstRecoveryState() (recoveryState int, canRecover bool) { recoveryState-- canRecover = actionTab[p.stack.peek(recoveryState)].canRecover } + fmt.Printf("Next recovery state: %d\n", recoveryState) return } @@ -243,8 +310,14 @@ func (p *Parser) Parse(scanner Scanner) (res interface{}, err error) { p.Reset() p.nextToken = scanner.Scan() for acc := false; !acc; { + {{- if .Debug }} + fmt.Println(p.stack.String()) + {{- end }} action := actionTab[p.stack.top()].actions[p.nextToken.Type] if action == nil { + {{- if .Debug }} + fmt.Println("~~ No action exists for the current stack top and next input symbol ~~") + {{- end }} if recovered, errAttrib := p.Error(nil, scanner); !recovered { p.nextToken = errAttrib.ErrorToken return nil, p.newError(nil)