Skip to content

Commit

Permalink
Change semantic action APIs
Browse files Browse the repository at this point in the history
The driver reports whether it recovered from an error to the semantic action APIs via the argument `recovered`.
  • Loading branch information
nihei9 committed Sep 6, 2021
1 parent 8cbb194 commit ed2c201
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 18 deletions.
14 changes: 9 additions & 5 deletions driver/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,22 @@ ACTION_LOOP:
case act < 0: // Shift
nextState := act * -1

recovered := false
if p.onError {
p.shiftCount++

// When the parser performs shift three times, the parser recovers from the error state.
if p.shiftCount < 3 {
p.shiftCount++
} else {
if p.shiftCount >= 3 {
p.onError = false
p.shiftCount = 0
recovered = true
}
}

p.shift(nextState)

if p.semAct != nil {
p.semAct.Shift(tok)
p.semAct.Shift(tok, recovered)
}

tok, err = p.nextToken()
Expand All @@ -108,9 +110,11 @@ ACTION_LOOP:
case act > 0: // Reduce
prodNum := act

recovered := false
if p.onError && p.gram.ParsingTable.RecoverProductions[prodNum] != 0 {
p.onError = false
p.shiftCount = 0
recovered = true
}

accepted := p.reduce(prodNum)
Expand All @@ -123,7 +127,7 @@ ACTION_LOOP:
}

if p.semAct != nil {
p.semAct.Reduce(prodNum)
p.semAct.Reduce(prodNum, recovered)
}
default: // Error
if p.onError {
Expand Down
13 changes: 7 additions & 6 deletions driver/semantic_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import (

type SemanticActionSet interface {
// Shift runs when the driver shifts a symbol onto the state stack. `tok` is a token corresponding to
// the symbol.
Shift(tok *mldriver.Token)
// the symbol. When the driver recovered from an error state by shifting the token, `recovered` is true.
Shift(tok *mldriver.Token, recovered bool)

// Shift runs when the driver shifts a symbol onto the state stack. `tok` is a token corresponding to
// the symbol. This function doesn't take a token as an argument because a token corresponding to
// the error symbol doesn't exist.
ShiftError()

// Reduce runs when the driver reduces an RHS of a production to its LHS. `prodNum` is a number of
// the production.
Reduce(prodNum int)
// the production. When the driver recovered from an error state by reducing the production,
// `recovered` is true.
Reduce(prodNum int, recovered bool)

// Accept runs when the driver accepts an input.
Accept()
Expand Down Expand Up @@ -96,7 +97,7 @@ func NewSyntaxTreeActionSet(gram *spec.CompiledGrammar, makeAST bool, makeCST bo
}
}

func (a *SyntaxTreeActionSet) Shift(tok *mldriver.Token) {
func (a *SyntaxTreeActionSet) Shift(tok *mldriver.Token, recovered bool) {
term := a.tokenToTerminal(tok)

var ast *Node
Expand Down Expand Up @@ -146,7 +147,7 @@ func (a *SyntaxTreeActionSet) ShiftError() {
})
}

func (a *SyntaxTreeActionSet) Reduce(prodNum int) {
func (a *SyntaxTreeActionSet) Reduce(prodNum int, recovered bool) {
lhs := a.gram.ParsingTable.LHSSymbols[prodNum]

// When an alternative is empty, `n` will be 0, and `handle` will be empty slice.
Expand Down
33 changes: 26 additions & 7 deletions driver/semantic_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,26 @@ type testSemAct struct {
actLog []string
}

func (a *testSemAct) Shift(tok *mldriver.Token) {
a.actLog = append(a.actLog, fmt.Sprintf("shift/%v", tok.KindName))
func (a *testSemAct) Shift(tok *mldriver.Token, recovered bool) {
if recovered {
a.actLog = append(a.actLog, fmt.Sprintf("shift/%v/recovered", tok.KindName))
} else {
a.actLog = append(a.actLog, fmt.Sprintf("shift/%v", tok.KindName))
}
}

func (a *testSemAct) ShiftError() {
a.actLog = append(a.actLog, "shift/error")
}

func (a *testSemAct) Reduce(prodNum int) {
func (a *testSemAct) Reduce(prodNum int, recovered bool) {
lhsSym := a.gram.ParsingTable.LHSSymbols[prodNum]
lhsText := a.gram.ParsingTable.NonTerminals[lhsSym]
a.actLog = append(a.actLog, fmt.Sprintf("reduce/%v", lhsText))
if recovered {
a.actLog = append(a.actLog, fmt.Sprintf("reduce/%v/recovered", lhsText))
} else {
a.actLog = append(a.actLog, fmt.Sprintf("reduce/%v", lhsText))
}
}

func (a *testSemAct) Accept() {
Expand All @@ -46,6 +54,7 @@ func TestParserWithSemanticAction(t *testing.T) {
seq
: seq elem semicolon
| elem semicolon
| error star star semicolon
| error semicolon #recover
;
elem
Expand All @@ -54,6 +63,7 @@ elem
ws: "[\u{0009}\u{0020}]+" #skip;
semicolon: ';';
star: '*';
char: "[a-z]";
`

Expand Down Expand Up @@ -102,25 +112,34 @@ char: "[a-z]";
{
caption: "when a grammar has `error` symbol, the driver calls `TrapError` and `ShiftError`.",
specSrc: specSrcWithErrorProd,
src: `a; b !; c d !; e f g;`,
src: `a; b !; c d !; e ! * *; h i j;`,
actLog: []string{
"shift/char",
"trap/1",
"shift/error",
"shift/semicolon",
"reduce/seq",
"reduce/seq/recovered",

"shift/char",
"trap/2",
"shift/error",
"shift/semicolon",
"reduce/seq",
"reduce/seq/recovered",

"shift/char",
"shift/char",
"trap/3",
"shift/error",
"shift/semicolon",
"reduce/seq/recovered",

"shift/char",
"trap/2",
"shift/error",
"shift/star",
"shift/star",
// When the driver shifts three times, it recovers from an error.
"shift/semicolon/recovered",
"reduce/seq",

"shift/char",
Expand Down

0 comments on commit ed2c201

Please sign in to comment.