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
|
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: `
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: `
seq
: seq elem ';'
| elem ';'
| error ';' #recover
;
elem
: a b c
;
ws: "[\u{0009}\u{0020}]+" #skip;
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: `
seq
: seq elem ';'
| elem ';'
| error ';' #recover
;
elem
: a b c
;
ws: "[\u{0009}\u{0020}]+" #skip;
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: `
seq
: seq elem ';'
| elem ';'
| error '*' '*' ';'
;
elem
: a b c
;
ws: "[\u{0009}\u{0020}]+" #skip;
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, grammar.SpecifyClass(grammar.ClassLALR))
if err != nil {
t.Fatal(err)
}
p, err := NewParser(gram, strings.NewReader(tt.src))
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))
}
})
}
}
|