Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/errorrecovery/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
regenerate:
gocc er.bnf
gocc er.bnf
2 changes: 1 addition & 1 deletion example/errorrecovery/er.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ StmtList :
Stmt :
id << ast.NewStmt($0) >>
| error
;
;
2 changes: 1 addition & 1 deletion example/errorrecovery/er_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
77 changes: 75 additions & 2 deletions internal/parser/gen/golang/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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 {
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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)
Expand Down