From 3ac8f9376ae3edc61428ec6ca4ef10bf859ae170 Mon Sep 17 00:00:00 2001 From: inglorion Date: Sat, 28 Jan 2012 20:32:02 -0800 Subject: [PATCH] Made parser able to report multiple errors per call, using new Voodoo::Parser::MultipleErrors class --- lib/voodoo/compiler.rb | 10 ++++-- lib/voodoo/parser.rb | 51 +++++++++++++++++++++++---- test/errors.err | 13 +++++++ test/errors.voo | 15 ++++++++ test/test_parser.rb | 94 +++++++++++++++++++++++++++++++++++--------------- 5 files changed, 145 insertions(+), 38 deletions(-) diff --git a/lib/voodoo/compiler.rb b/lib/voodoo/compiler.rb index c05e7ac..ba1fd1c 100644 --- a/lib/voodoo/compiler.rb +++ b/lib/voodoo/compiler.rb @@ -54,13 +54,17 @@ module Voodoo @generator.add section, statement end + rescue Parser::MultipleErrors => e + errors.concat e.errors + rescue Parser::ParseError => e errors << e - if errors.length >= 100 - raise Error.new(errors) - end end + if errors.length >= 100 + # Too many errors, give up. + raise Error.new(errors) + end end if errors.empty? diff --git a/lib/voodoo/parser.rb b/lib/voodoo/parser.rb index 19e5e04..dd390d9 100644 --- a/lib/voodoo/parser.rb +++ b/lib/voodoo/parser.rb @@ -77,6 +77,41 @@ module Voodoo attr_reader :cause end + # Class wrapping multiple Parser::Errors. + class MultipleErrors < Parser::Error + def initialize errors + @errors = errors + super(nil, errors[0].input_name, errors[0].start_line, + errors[0].start_column, nil) + end + + attr_reader :errors + + def message + if @message == nil + msg = "Multiple errors:\n\n" + @errors.each do |error| + msg << error.input_name << ":" if error.input_name + msg << "#{error.start_line}: " << error.message + if error.text != nil + msg << "\n\n #{error.text.gsub("\n", "\n ")}" + end + msg << "\n" + end + @message = msg + end + @message + end + + def text + if @text == nil + texts = @errors.map {|error| error.text} + @text = texts.join "\n" + end + @text + end + end + # Parses a top-level element. # Returns an array containing the parts of the element. # @@ -124,7 +159,7 @@ module Voodoo # Parses a body for a function or a conditional def parse_body kind body = [] - error = nil + errors = [] case kind when :function kind_text = 'function definition' @@ -139,6 +174,7 @@ module Voodoo if statement == nil done = true parse_error "End of input while inside #{kind_text}", nil + elsif statement[0] == :end # Done parsing body done = true @@ -161,15 +197,16 @@ module Voodoo end rescue => e # Got some kind of error. Still try to parse the rest of the body. - # Save the error if it was the first one. - if error == nil - error = e - end + errors << e end end - if error != nil - raise error + # Raise error if we had just one. + # If we had more than one, raise a MultipleErrors instance. + if errors.length == 1 + raise errors[0] + elsif errors.length > 1 + raise MultipleErrors.new errors end body diff --git a/test/errors.err b/test/errors.err index 75a7bfa..04d5e14 100644 --- a/test/errors.err +++ b/test/errors.err @@ -30,3 +30,16 @@ errors.voo:28: Invalid character (") in at-expression; expecting number or symbo add 2 @"wrong" +errors.voo:32: get-word should have exactly 2 parameters + + let x get-word x + +errors.voo:37: get-word should have exactly 2 parameters + + let x get-word x + +errors.voo:38: set-word should have exactly 3 parameters + + set-word x + +errors.voo:44: End of input while inside block diff --git a/test/errors.voo b/test/errors.voo index d0916a3..f9faf3a 100644 --- a/test/errors.voo +++ b/test/errors.voo @@ -26,3 +26,18 @@ end function # At-expression should have a number or a symbol add 2 @"wrong" + +# Function with errorneous statement in it +function + let x get-word x +end function + +# Function with multiple errorneous statements in it +function + let x get-word x + set-word x +end function + +# Block should be closed before end of file +block + let x 4 diff --git a/test/test_parser.rb b/test/test_parser.rb index 757ab14..a300258 100755 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -81,39 +81,32 @@ def parser_test name, input, expected, exception = nil if error == nil # Got no exception. if result == expected - puts "pass" + pass_test else - puts "FAIL: result did not match expected result" - $stdout.flush - $stderr.puts "Got:\n#{result.inspect}\nExpected:\n#{expected.inspect}" - increment_errors + fail_test("result did not match expected result", + "Got:\n#{result.inspect}\nExpected:\n#{expected.inspect}") end else # Wasnt't expecting exception, but got one. - puts "FAIL: unexpected exception of type #{error.class}" - $stdout.flush - $stderr.puts "#{error.class}:#{error.message}\n" + - error.backtrace.join("\n") - increment_errors + fail_test("unexpected exception of type #{error.class}", + "#{error.class}:#{error.message}\n" + + error.backtrace.join("\n")) end else # Expecting exception. if error == nil # Got no exception. - puts "FAIL: expected exception of class #{exception.class}, " + + fail_test "expected exception of class #{exception.class}, " + "but got none" - increment_errors elsif error.class != exception.class # Got exception, but of wrong type. - puts "FAIL: expected exception of class #{exception.class}, " + - "but got one of type #{error.class}" - $stdout.flush - $stderr.puts "#{error.class}:#{error.message}\n" + - error.backtrace.join("\n") - increment_errors + fail_test("expected exception of class #{exception.class}, " + + "but got one of type #{error.class}", + "#{error.class}:#{error.message}\n" + + error.backtrace.join("\n")) else # Got exception of expected type. - puts "pass" + pass_test end end end @@ -132,17 +125,15 @@ def parser_test_error name, input, error_type, line, column # Test I/O error on the first character. parser_test_ex(name, input) do |result, error| if error.class != error_type - puts "FAIL: expecting exception of class #{error_type}, " + - "but got #{error.class}" - $stdout.flush - $stderr.puts "#{error.class}:#{error.message}\n" + - error.backtrace.join("\n") - increment_errors + fail_test("expecting exception of class #{error_type}, " + + "but got #{error.class}", + "#{error.class}:#{error.message}\n" + + error.backtrace.join("\n")) elsif error.start_line != line || error.start_column != column - puts "FAIL: wrong position. Expected line #{line}, column #{column}" + - "; got line #{error.start_line}, column #{error.start_column}" + fail_test("wrong position. Expected line #{line}, column #{column}" + + "; got line #{error.start_line}, column #{error.start_column}") else - puts "pass" + pass_test end end end @@ -193,6 +184,53 @@ def test_parser BrokenStringReader.new("section data\nfoo:", 13), Voodoo::Parser::ParserInternalError, 2, 0) + + parser_test_ex("multiple_errors", "block\nlet\nset\n") do |result,error| + expected_message = <