diff --git a/lib/oga/xml/parser.rll b/lib/oga/xml/parser.rll index 513aff2..e55a7f6 100644 --- a/lib/oga/xml/parser.rll +++ b/lib/oga/xml/parser.rll @@ -222,6 +222,35 @@ string_body_follow %inner { + ## + # Hash mapping token types and dedicated error labels. + # + # @return [Hash] + # + TOKEN_ERROR_MAPPING = { + :T_STRING => 'string', + :T_TEXT => 'text', + :T_DOCTYPE_START => 'doctype start', + :T_DOCTYPE_END => 'doctype closing tag', + :T_DOCTYPE_TYPE => 'doctype type', + :T_DOCTYPE_NAME => 'doctype name', + :T_DOCTYPE_INLINE => 'inline doctype rules', + :T_CDATA => 'CDATA', + :T_COMMENT => 'comment', + :T_ELEM_START => 'element start', + :T_ELEM_NAME => 'element name', + :T_ELEM_NS => 'element namespace', + :T_ELEM_END => 'element closing tag', + :T_ATTR => 'attribute', + :T_ATTR_NS => 'attribute namespace', + :T_XML_DECL_START => 'XML declaration start', + :T_XML_DECL_END => 'XML declaration end', + :T_PROC_INS_START => 'processing-instruction start', + :T_PROC_INS_NAME => 'processing-instruction name', + :T_PROC_INS_END => 'processing-instruction closing tag', + -1 => 'end of input' + } + ## # @param [String|IO] data The input to parse. # @param [Hash] options @@ -258,6 +287,30 @@ string_body_follow yield [-1, -1] end + ## + # @param [Fixnum] stack_type + # @param [Fixnum] stack_value + # @param [Symbol] token_type + # @param [String] token_value + # + def parser_error(stack_type, stack_value, token_type, token_value) + case id_to_type(stack_type) + when :rule + message = "Unexpected #{token_type} for rule #{stack_value}" + when :terminal + expected = id_to_terminal(stack_value) + expected = TOKEN_ERROR_MAPPING[expected] || expected + got = TOKEN_ERROR_MAPPING[token_type] || token_type + message = "Unexpected #{got}, expected #{expected} instead" + when :eof + message = 'Unexpected end of input' + end + + message += " on line #{@line}" + + raise LL::ParserError, message + end + ## # @see [LL::Driver#parse] # diff --git a/spec/oga/xml/parser/error_spec.rb b/spec/oga/xml/parser/error_spec.rb index 6b9b27c..aa74b38 100644 --- a/spec/oga/xml/parser/error_spec.rb +++ b/spec/oga/xml/parser/error_spec.rb @@ -12,8 +12,8 @@ describe Oga::XML::Parser do EOF end - it 'raises a Racc::ParseError' do - expect { parse(@invalid_xml) }.to raise_error(Racc::ParseError) + it 'raises a LL::ParserError' do + expect { parse(@invalid_xml) }.to raise_error(LL::ParserError) end it 'includes the line number when using a String as input' do @@ -25,7 +25,8 @@ describe Oga::XML::Parser do end it 'uses more friendly error messages when available' do - parse_error('').should =~ /Unexpected element closing tag/ + parse_error('').should == + 'Unexpected end of input, expected element closing tag instead on line 1' end end end diff --git a/spec/support/parsing.rb b/spec/support/parsing.rb index efcc69f..d5f07d6 100644 --- a/spec/support/parsing.rb +++ b/spec/support/parsing.rb @@ -93,7 +93,7 @@ module Oga # def parse_error(xml) parse(xml) - rescue Racc::ParseError => error + rescue LL::ParserError => error return error.message end end # ParsingHelpers diff --git a/task/parser.rake b/task/parser.rake index 553d599..f5ebe59 100644 --- a/task/parser.rake +++ b/task/parser.rake @@ -2,6 +2,10 @@ rule '.rb' => '.y' do |task| sh "racc -l -o #{task.name} #{task.source}" end +rule '.rb' => '.rll' do |task| + sh "ruby-ll #{task.source} -o #{task.name}" +end + desc 'Generates the parser' task :parser => [ 'lib/oga/xml/parser.rb',