1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
package driver
import (
"fmt"
"strings"
"testing"
"github.com/nihei9/vartan/grammar"
"github.com/nihei9/vartan/spec"
)
func TestParserWithSyntaxErrors(t *testing.T) {
tests := []struct {
caption string
specSrc string
src string
synErrCount int
}{
{
caption: "the parser can report a syntax error",
specSrc: `
#name test;
s
: foo
;
foo: 'foo';
`,
src: `bar`,
synErrCount: 1,
},
{
caption: "when the parser reduced a production having the reduce directive, the parser will recover from an error state",
specSrc: `
#name test;
seq
: seq elem ';'
| elem ';'
| error ';' #recover
;
elem
: a b c
;
ws #skip
: "[\u{0009}\u{0020}]+";
a
: 'a';
b
: 'b';
c
: 'c';
`,
src: `!; a!; ab!;`,
synErrCount: 3,
},
{
caption: "After the parser shifts the error symbol, symbols are ignored until a symbol the parser can perform shift appears",
specSrc: `
#name test;
seq
: seq elem ';'
| elem ';'
| error ';' #recover
;
elem
: a b c
;
ws #skip
: "[\u{0009}\u{0020}]+";
a
: 'a';
b
: 'b';
c
: 'c';
`,
// After the parser trasits to the error state reading the first invalid symbol ('!'),
// the second and third invalid symbols ('!') are ignored.
src: `! ! !; a!; ab!;`,
synErrCount: 3,
},
{
caption: "when the parser performs shift three times, the parser recovers from the error state",
specSrc: `
#name test;
seq
: seq elem ';'
| elem ';'
| error '*' '*' ';'
;
elem
: a b c
;
ws #skip
: "[\u{0009}\u{0020}]+";
a
: 'a';
b
: 'b';
c
: 'c';
`,
src: `!**; a!**; ab!**; abc!`,
synErrCount: 4,
},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) {
ast, err := spec.Parse(strings.NewReader(tt.specSrc))
if err != nil {
t.Fatal(err)
}
b := grammar.GrammarBuilder{
AST: ast,
}
g, err := b.Build()
if err != nil {
t.Fatal(err)
}
gram, err := grammar.Compile(g)
if err != nil {
t.Fatal(err)
}
toks, err := NewTokenStream(gram, strings.NewReader(tt.src))
if err != nil {
t.Fatal(err)
}
p, err := NewParser(toks, NewGrammar(gram))
if err != nil {
t.Fatal(err)
}
err = p.Parse()
if err != nil {
t.Fatal(err)
}
synErrs := p.SyntaxErrors()
if len(synErrs) != tt.synErrCount {
t.Fatalf("unexpected syntax error; want: %v error(s), got: %v error(s)", tt.synErrCount, len(synErrs))
}
})
}
}
|