diff --git a/lib/syntax_tree/erb/parser.rb b/lib/syntax_tree/erb/parser.rb index 7572aea..736441f 100644 --- a/lib/syntax_tree/erb/parser.rb +++ b/lib/syntax_tree/erb/parser.rb @@ -5,14 +5,12 @@ module ERB class Parser # This is the parent class of any kind of errors that will be raised by # the parser. - class ParseError < StandardError - end # This error occurs when a certain token is expected in a certain place # but is not found. Sometimes this is handled internally because some # elements are optional. Other times it is not and it is raised to end the # parsing process. - class MissingTokenError < ParseError + class MissingTokenError < SyntaxTree::Parser::ParseError end attr_reader :source, :tokens @@ -52,7 +50,13 @@ def parse_any_tag if tag.is_a?(Doctype) if @found_doctype - raise(ParseError, "Only one doctype element is allowed") + raise( + SyntaxTree::Parser::ParseError.new( + "Only one doctype element is allowed", + tag.location.start_line, + 0 + ) + ) else @found_doctype = true end @@ -123,8 +127,13 @@ def make_tokens # abc enum.yield :text, $&, index, line else - raise ParseError, - "Unexpected character at #{index}: #{source[index]}" + raise( + SyntaxTree::Parser::ParseError.new( + "Unexpected character at #{index}: #{source[index]}", + line, + 0 + ) + ) end in :erb_start case source[index..] @@ -207,8 +216,13 @@ def make_tokens # abc enum.yield :text, $&, index, line else - raise ParseError, - "Unexpected character in string at #{index}: #{source[index]}" + raise( + SyntaxTree::Parser::ParseError.new( + "Unexpected character in string at #{index}: #{source[index]}", + line, + 0 + ) + ) end in :string_double_quote case source[index..] @@ -230,8 +244,13 @@ def make_tokens # abc enum.yield :text, $&, index, line else - raise ParseError, - "Unexpected character in string at #{index}: #{source[index]}" + raise( + SyntaxTree::Parser::ParseError.new( + "Unexpected character in string at #{index}: #{source[index]}", + line, + 0 + ) + ) end in :inside case source[index..] @@ -284,8 +303,13 @@ def make_tokens enum.yield :string_open_single_quote, $&, index, line state << :string_single_quote else - raise ParseError, - "Unexpected character at #{index}: #{source[index]}" + raise( + SyntaxTree::Parser::ParseError.new( + "Unexpected character at #{index}: #{source[index]}", + line, + 0 + ) + ) end end @@ -304,7 +328,13 @@ def consume(expected) type, value, index, line = tokens.peek if expected != type - raise MissingTokenError, "expected #{expected} got #{type}" + raise( + MissingTokenError.new( + "expected #{expected} got #{type}", + line, + index + ) + ) end tokens.next @@ -335,7 +365,9 @@ def maybe # Otherwise we'll return the value returned by the block. def atleast result = yield - raise MissingTokenError if result.nil? + if result.nil? + raise(MissingTokenError.new("No matching token", nil, nil)) + end result end @@ -372,7 +404,13 @@ def parse_html_opening_tag name = consume(:name) if name.value =~ /\A[@:#]/ - raise ParseError, "Invalid html-tag name #{name}" + raise( + SyntaxTree::Parser::ParseError.new( + "Invalid html-tag name #{name}", + name.location.start_line, + 0 + ) + ) end attributes = @@ -431,15 +469,21 @@ def parse_html_element if closing.nil? raise( - ParseError, - "Missing closing tag for <#{opening.name.value}> at #{opening.location}" + SyntaxTree::Parser::ParseError.new( + "Missing closing tag for <#{opening.name.value}> at #{opening.location}", + opening.location.start_line, + 0 + ) ) end if closing.name.value != opening.name.value raise( - ParseError, - "Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}> at #{closing.location}" + SyntaxTree::Parser::ParseError.new( + "Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}> at #{closing.location}", + closing.location.start_line, + 0 + ) ) end @@ -462,8 +506,11 @@ def parse_erb_case(erb_node) unless erb_tag.is_a?(ErbCaseWhen) || erb_tag.is_a?(ErbElse) || erb_tag.is_a?(ErbEnd) raise( - ParseError, - "Found no matching erb-tag to the if-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching erb-tag to the if-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end @@ -484,8 +531,11 @@ def parse_erb_case(erb_node) ) else raise( - ParseError, - "Found no matching when- or else-tag to the case-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching when- or else-tag to the case-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end end @@ -501,8 +551,11 @@ def parse_erb_if(erb_node) unless erb_tag.is_a?(ErbControl) || erb_tag.is_a?(ErbEnd) raise( - ParseError, - "Found no matching erb-tag to the if-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching erb-tag to the if-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end @@ -530,8 +583,11 @@ def parse_erb_if(erb_node) ) else raise( - ParseError, - "Found no matching elsif- or else-tag to the if-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching elsif- or else-tag to the if-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end end @@ -543,8 +599,11 @@ def parse_erb_else(erb_node) unless erb_end.is_a?(ErbEnd) raise( - ParseError, - "Found no matching end-tag for the else-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching end-tag for the else-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end @@ -582,8 +641,11 @@ def parse_erb_tag if !closing_tag.is_a?(ErbClose) raise( - ParseError, - "Found no matching closing tag for the erb-tag at #{opening_tag.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching closing tag for the erb-tag at #{opening_tag.location}", + opening_tag.location.start_line, + 0 + ) ) end @@ -615,8 +677,11 @@ def parse_erb_tag unless erb_end.is_a?(ErbEnd) raise( - ParseError, - "Found no matching end-tag for the do-tag at #{erb_node.location}" + SyntaxTree::Parser::ParseError.new( + "Found no matching end-tag for the do-tag at #{erb_node.location}", + erb_node.location.start_line, + 0 + ) ) end @@ -630,13 +695,23 @@ def parse_erb_tag erb_node end end - rescue MissingTokenError => error + rescue SyntaxTree::Parser::ParseError => error # If we have parsed tokens that we cannot process after we parsed <%, we should throw a ParseError # and not let it be handled by a `maybe`. if opening_tag + message = + if error.message.include?("Could not parse ERB-tag") + error.message + else + "Could not parse ERB-tag: #{error.message}" + end + raise( - ParseError, - "Could not parse ERB-tag at #{opening_tag.location}" + SyntaxTree::Parser::ParseError.new( + message, + opening_tag.location.start_line, + 0 + ) ) else raise(error) diff --git a/test/erb_test.rb b/test/erb_test.rb index 85a4f8d..cb4aed1 100644 --- a/test/erb_test.rb +++ b/test/erb_test.rb @@ -12,19 +12,19 @@ def test_empty_file end def test_missing_erb_end_tag - assert_raises(SyntaxTree::ERB::Parser::ParseError) do + assert_raises(SyntaxTree::Parser::ParseError) do ERB.parse("<% if no_end_tag %>") end end def test_missing_erb_block_end_tag - assert_raises(SyntaxTree::ERB::Parser::ParseError) do + assert_raises(SyntaxTree::Parser::ParseError) do ERB.parse("<% no_end_tag do %>") end end def test_missing_erb_case_end_tag - assert_raises(SyntaxTree::ERB::Parser::ParseError) do + assert_raises(SyntaxTree::Parser::ParseError) do ERB.parse("<% case variabel %>\n<% when 1>\n Hello\n") end end @@ -35,6 +35,23 @@ def test_erb_code_with_non_ascii assert_instance_of(SyntaxTree::ERB::ErbNode, parsed.elements.first) end + def test_erb_errors + example = <<-HTML +