From 7271e46bbcb11acf860c91eddfe12dd7eed5ccad Mon Sep 17 00:00:00 2001 From: Ryo Nihei Date: Thu, 26 Aug 2021 23:16:09 +0900 Subject: Add error symbol and #recover directive to recover from an error state --- driver/syntax_error_test.go | 130 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 driver/syntax_error_test.go (limited to 'driver/syntax_error_test.go') 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)) + } + }) + } +} -- cgit v1.2.3