aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2021-09-06 23:59:43 +0900
committerRyo Nihei <nihei.dev@gmail.com>2021-09-07 02:17:35 +0900
commited2c20102659f4c8aef0e88ea604e91fb56f25f6 (patch)
treec46e6bc40e9373237fb7cad2730d410132b27caa
parentUpdate CHANGELOG (diff)
downloadurubu-ed2c20102659f4c8aef0e88ea604e91fb56f25f6.tar.gz
urubu-ed2c20102659f4c8aef0e88ea604e91fb56f25f6.tar.xz
Change semantic action APIs
The driver reports whether it recovered from an error to the semantic action APIs via the argument `recovered`.
-rw-r--r--driver/parser.go14
-rw-r--r--driver/semantic_action.go13
-rw-r--r--driver/semantic_action_test.go33
3 files changed, 42 insertions, 18 deletions
diff --git a/driver/parser.go b/driver/parser.go
index 1e7af29..af257e2 100644
--- a/driver/parser.go
+++ b/driver/parser.go
@@ -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()
@@ -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)
@@ -123,7 +127,7 @@ ACTION_LOOP:
}
if p.semAct != nil {
- p.semAct.Reduce(prodNum)
+ p.semAct.Reduce(prodNum, recovered)
}
default: // Error
if p.onError {
diff --git a/driver/semantic_action.go b/driver/semantic_action.go
index b31c35b..421a722 100644
--- a/driver/semantic_action.go
+++ b/driver/semantic_action.go
@@ -10,8 +10,8 @@ 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
@@ -19,8 +19,9 @@ type SemanticActionSet interface {
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()
@@ -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
@@ -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.
diff --git a/driver/semantic_action_test.go b/driver/semantic_action_test.go
index a2c2bb0..0856b3d 100644
--- a/driver/semantic_action_test.go
+++ b/driver/semantic_action_test.go
@@ -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() {
@@ -46,6 +54,7 @@ func TestParserWithSemanticAction(t *testing.T) {
seq
: seq elem semicolon
| elem semicolon
+ | error star star semicolon
| error semicolon #recover
;
elem
@@ -54,6 +63,7 @@ elem
ws: "[\u{0009}\u{0020}]+" #skip;
semicolon: ';';
+star: '*';
char: "[a-z]";
`
@@ -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",