diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2021-08-26 23:16:09 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2021-08-26 23:18:49 +0900 |
commit | 7271e46bbcb11acf860c91eddfe12dd7eed5ccad (patch) | |
tree | fafbf797ca806ff1e4cc68acaaaa6db66aec632d /driver/syntax_error_test.go | |
parent | Update CHANGELOG (diff) | |
download | cotia-7271e46bbcb11acf860c91eddfe12dd7eed5ccad.tar.gz cotia-7271e46bbcb11acf860c91eddfe12dd7eed5ccad.tar.xz |
Add error symbol and #recover directive to recover from an error state
Diffstat (limited to 'driver/syntax_error_test.go')
-rw-r--r-- | driver/syntax_error_test.go | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/driver/syntax_error_test.go b/driver/syntax_error_test.go new file mode 100644 index 0000000..427f0d1 --- /dev/null +++ b/driver/syntax_error_test.go @@ -0,0 +1,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), MakeAST(), MakeCST()) + 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)) + } + }) + } +} |