1#!/usr/bin/ruby 2# encoding: utf-8 3 4=begin LICENSE 5 6[The "BSD licence"] 7Copyright (c) 2009-2010 Kyle Yetter 8All rights reserved. 9 10Redistribution and use in source and binary forms, with or without 11modification, are permitted provided that the following conditions 12are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in the 18 documentation and/or other materials provided with the distribution. 19 3. The name of the author may not be used to endorse or promote products 20 derived from this software without specific prior written permission. 21 22THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 33=end 34 35# ANTLR3 exception hierarchy 36# - ported from the ANTLR3 Python Runtime library by 37# Kyle Yetter (kcy5b@yahoo.com) 38module ANTLR3 39 40# for compatibility with rubinius, which does not implement StopIteration yet 41unless defined?( StopIteration ) 42 StopIteration = Class.new( StandardError ) 43end 44 45module Error 46 47=begin rdoc ANTLR3::Error::BacktrackingFailed 48 49error:: BacktrackingFailed 50used by:: all recognizers 51occurs when:: 52 recognizer is in backtracking mode (i.e. r.state.backtracking > 0) 53 and the decision path the recognizer is currently attempting 54 hit a point of failure 55notes:: 56 - functions more as an internal signal, simillar to exception 57 classes such as StopIteration and SystemExit 58 - used to inform the recognizer that it needs to rewind 59 the input to the point at which it started the decision 60 and then either try another possible decision path or 61 declare failure 62 - not a subclass of RecognitionError 63 64=end 65 66class BacktrackingFailed < StandardError; end 67 68 # To avoid English-only error messages and to generally make things 69 # as flexible as possible, these exceptions are not created with strings, 70 # but rather the information necessary to generate an error. Then 71 # the various reporting methods in Parser and Lexer can be overridden 72 # to generate a localized error message. For example, MismatchedToken 73 # exceptions are built with the expected token type. 74 # So, don't expect getMessage() to return anything. 75 # 76 # Note that as of Java 1.4, you can access the stack trace, which means 77 # that you can compute the complete trace of rules from the start symbol. 78 # This gives you considerable context information with which to generate 79 # useful error messages. 80 # 81 # ANTLR generates code that throws exceptions upon recognition error and 82 # also generates code to catch these exceptions in each rule. If you 83 # want to quit upon first error, you can turn off the automatic error 84 # handling mechanism using rulecatch action, but you still need to 85 # override methods mismatch and recoverFromMismatchSet. 86 # 87 # In general, the recognition exceptions can track where in a grammar a 88 # problem occurred and/or what was the expected input. While the parser 89 # knows its state (such as current input symbol and line info) that 90 # state can change before the exception is reported so current token index 91 # is computed and stored at exception time. From this info, you can 92 # perhaps print an entire line of input not just a single token, for example. 93 # Better to just say the recognizer had a problem and then let the parser 94 # figure out a fancy report. 95 96=begin rdoc ANTLR3::Error::RecognitionError 97 98The base class of the variety of syntax errors that can occur during the 99recognition process. These errors all typically concern an expectation built in 100to the recognizer by the rules of a grammar and an input symbol which failed to 101fit the expectation. 102 103=end 104 105class RecognitionError < StandardError 106 include ANTLR3::Constants 107 attr_accessor :input, :index, :line, :column, :symbol, :token, :source_name 108 109 def initialize( input = nil ) 110 @index = @line = @column = nil 111 @approximate_line_info = false 112 if @input = input 113 @index = input.index 114 @source_name = @input.source_name rescue nil 115 case @input 116 when TokenStream 117 @token = @symbol = input.look 118 @line = @symbol.line 119 @column = @symbol.column 120 when CharacterStream 121 @token = @symbol = input.peek || EOF 122 @line = @input.line 123 @column = @input.column 124 when AST::TreeNodeStream 125 @symbol = @input.look 126 if @symbol.respond_to?( :line ) and @symbol.respond_to?( :column ) 127 @line, @column = @symbol.line, @symbol.column 128 else 129 extract_from_node_stream( @input ) 130 end 131 else 132 @symbol = @input.look 133 if @symbol.respond_to?( :line ) and @symbol.respond_to?( :column ) 134 @line, @column = @symbol.line, @symbol.column 135 elsif @input.respond_to?( :line ) and @input.respond_to?( :column ) 136 @line, @column = @input.line, @input.column 137 end 138 end 139 end 140 super( message ) 141 end 142 143 def approximate_line_info? 144 @approximate_line_info 145 end 146 147 def unexpected_type 148 case @input 149 when TokenStream 150 @symbol.type 151 when AST::TreeNodeStream 152 adaptor = @input.adaptor 153 return adaptor.type( @symbol ) 154 else 155 return @symbol 156 end 157 end 158 159 def location 160 if @source_name then "in #@source_name @ line #@line:#@column" 161 else "line #@line:#@column" 162 end 163 end 164 165 alias inspect message 166 167private 168 169 def extract_from_node_stream( nodes ) 170 adaptor = nodes.adaptor 171 payload = adaptor.token( @symbol ) 172 173 if payload 174 @token = payload 175 if payload.line <= 0 176 i = -1 177 while prior_node = nodes.look( i ) 178 prior_payload = adaptor.token( prior_node ) 179 if prior_payload and prior_payload.line > 0 180 @line = prior_payload.line 181 @column = prior_payload.column 182 @approximate_line_info = true 183 break 184 end 185 i -= 1 186 end 187 else 188 @line = payload.line 189 @column = payload.column 190 end 191 elsif @symbol.is_a?( AST::Tree ) 192 @line = @symbol.line 193 @column = @symbol.column 194 @symbol.is_a?( AST::CommonTree ) and @token = @symbol.token 195 else 196 type = adaptor.type( @symbol ) 197 text = adaptor.text( @symbol ) 198 token_class = @input.token_class rescue CommonToken 199 @token = token_class.new 200 @token.type = type 201 @token.text = text 202 @token 203 end 204 end 205end 206 207=begin rdoc ANTLR3::Error::MismatchedToken 208 209type:: MismatchedToken 210used by:: lexers and parsers 211occurs when:: 212 The recognizer expected to match a symbol <tt>x</tt> at the current input 213 position, but it saw a different symbol <tt>y</tt> instead. 214 215=end 216 217class MismatchedToken < RecognitionError 218 attr_reader :expecting 219 220 def initialize( expecting, input ) 221 @expecting = expecting 222 super( input ) 223 end 224 225 def message 226 "%s: %p %p" % [ self.class, unexpected_type, @expecting.inspect ] 227 end 228end 229 230=begin rdoc ANTLR3::Error::UnwantedToken 231 232TODO: this does not appear to be used by any code 233 234=end 235 236class UnwantedToken < MismatchedToken 237 def unexpected_token 238 return @token 239 end 240 241 def message 242 exp = @expecting == INVALID_TOKEN_TYPE ? '' : ", expected %p" % @expecting 243 text = @symbol.text rescue nil 244 "%s: found=%p%s" % [ self.class, text, exp ] 245 end 246end 247 248=begin rdoc ANTLR3::Error::MissingToken 249 250error:: MissingToken 251used by:: parsers and tree parsers 252occurs when:: 253 The recognizer expected to match some symbol, but it sees a different symbol. 254 The symbol it sees is actually what the recognizer expected to match next. 255 256=== Example 257 258grammar: 259 260 grammar MissingTokenExample; 261 262 options { language = Ruby; } 263 264 @members { 265 def report_error(e) 266 raise e 267 end 268 } 269 270 missing: A B C; 271 272 A: 'a'; 273 B: 'b'; 274 C: 'c'; 275 276in ruby: 277 278 require 'MissingTokenExampleLexer' 279 require 'MissingTokenExampleParser' 280 281 lexer = MissingTokenExample::Lexer.new( "ac" ) # <= notice the missing 'b' 282 tokens = ANTLR3::CommonTokenStream.new( lexer ) 283 parser = MissingTokenExample::Parser.new( tokens ) 284 285 parser.missing 286 # raises ANTLR3::Error::MissingToken: at "c" 287 288=end 289 290class MissingToken < MismatchedToken 291 attr_accessor :inserted 292 def initialize( expecting, input, inserted ) 293 super( expecting, input ) 294 @inserted = inserted 295 end 296 297 def missing_type 298 return @expecting 299 end 300 301 def message 302 if @inserted and @symbol 303 "%s: inserted %p at %p" % 304 [ self.class, @inserted, @symbol.text ] 305 else 306 msg = self.class.to_s 307 msg << ': at %p' % token.text unless @token.nil? 308 return msg 309 end 310 end 311end 312 313=begin rdoc ANTLR3::Error::MismatchedRange 314 315error:: MismatchedRange 316used by:: all recognizers 317occurs when:: 318 A recognizer expected to match an input symbol (either a character value or 319 an integer token type value) that falls into a range of possible values, but 320 instead it saw a symbol that falls outside the expected range. 321 322=end 323 324class MismatchedRange < RecognitionError 325 attr_accessor :min, :max 326 def initialize( min, max, input ) 327 @min = min 328 @max = max 329 super( input ) 330 end 331 332 def message 333 "%s: %p not in %p..%p" % 334 [ self.class, unexpected_type, @min, @max ] 335 end 336end 337 338=begin rdoc ANTLR3::Error::MismatchedSet 339 340error:: MismatchedSet 341used by:: all recognizers 342occurs when:: 343 A recognizer expects the current input symbol to be a member of a set of 344 possible symbol values, but the current symbol does not match. 345 346=end 347 348class MismatchedSet < RecognitionError 349 attr_accessor :expecting 350 def initialize( expecting, input ) 351 super( input ) 352 @expecting = expecting 353 end 354 355 def message 356 "%s: %p not in %p" % 357 [ self.class, unexpected_type, @expecting ] 358 end 359end 360 361=begin rdoc ANTLR3::Error::MismatchedNotSet 362 363error:: MismatchedNotSet 364used by:: all recognizers 365occurs when:: 366 A recognizer expected to match symbol that is not in some set of symbols but 367 failed. 368 369=end 370 371class MismatchedNotSet < MismatchedSet 372 def message 373 '%s: %p != %p' % 374 [ self.class, unexpected_type, @expecting ] 375 end 376end 377 378=begin rdoc ANTLR3::Error::NoViableAlternative 379 380error:: NoViableAlternative 381used by:: all recognizers 382occurs when:: 383 A recognizer must choose between multiple possible recognition paths based 384 upon the current and future input symbols, but it has determined that 385 the input does not suit any of the possible recognition alternatives. 386 387In ANTLR terminology, a rule is composed of one or more _alternatives_, 388specifications seperated by <tt>|</tt> characters. An alternative is composed of 389a series of elements, including _subrules_ -- rule specifications enclosed 390within parentheses. When recognition code enters a rule method (or a subrule 391block) that has multiple alternatives, the recognizer must decide which one of 392the multiple possible paths to follow by checking a number of future input 393symbols. Thus, NoViableAlternative errors indicate that the current input does 394not fit any of the possible paths. 395 396In lexers, this error is often raised by the main +tokens!+ rule, which must 397choose between all possible token rules. If raised by +tokens+, it means the 398current input does not appear to be part of any token specification. 399 400=end 401 402class NoViableAlternative < RecognitionError 403 attr_accessor :grammar_decision_description, :decision_number, :state_number 404 def initialize( grammar_decision_description, decision_number, state_number, input ) 405 @grammar_decision_description = grammar_decision_description 406 @decision_number = decision_number 407 @state_number = state_number 408 super( input ) 409 end 410 411 def message 412 '%s: %p != [%p]' % 413 [ self.class, unexpected_type, @grammar_decision_description ] 414 end 415end 416 417=begin rdoc ANTLR3::Error::EarlyExit 418 419error:: EarlyExit 420used by:: all recognizers 421occurs when:: 422 The recognizer is in a <tt>(..)+</tt> subrule, meaning the recognizer must 423 match the body of the subrule one or more times. If it fails to match at least 424 one occurence of the subrule, the recognizer will raise an EarlyExit 425 exception. 426 427== Example 428 429consider a grammar like: 430 lexer grammar EarlyExitDemo; 431 ... 432 ID: 'a'..'z' ('0'..'9')+; 433 434now in ruby 435 436 require 'EarlyExitDemo' 437 438 input = ANTLR3::StringStream.new( "ab" ) 439 lexer = EarlyExitDemo::Lexer.new( input ) 440 lexer.next_token 441 # -> raises EarlyExit: line 1:1 required (...)+ loop did not match 442 # anything at character "b" 443 444=end 445 446class EarlyExit < RecognitionError 447 attr_accessor :decision_number 448 449 def initialize( decision_number, input ) 450 @decision_number = decision_number 451 super( input ) 452 end 453 454 def message 455 "The recognizer did not match anything for a (..)+ loop." 456 end 457 458end 459 460=begin rdoc ANTLR3::Error::FailedPredicate 461 462error:: FailedPredicate 463used by:: all recognizers 464occurs when:: 465 A recognizer is in a rule with a predicate action element, and the predicating 466 action code evaluated to a +false+ value. 467 468=end 469 470class FailedPredicate < RecognitionError 471 attr_accessor :input, :rule_name, :predicate_text 472 def initialize( input, rule_name, predicate_text ) 473 @rule_name = rule_name 474 @predicate_text = predicate_text 475 super( input ) 476 end 477 478 def inspect 479 '%s(%s, { %s }?)' % [ self.class.name, @rule_name, @predicate_text ] 480 end 481 482 def message 483 "rule #@rule_name failed predicate: { #@predicate_text }?" 484 end 485end 486 487=begin rdoc ANTLR3::Error::MismatchedTreeNode 488 489error:: MismatchedTreeNode 490used by:: tree parsers 491occurs when:: 492 A tree parser expects to match a tree node containing a specific type of 493 token, but the current tree node's token type does not match. It's essentially 494 the same as MismatchedToken, but used specifically for tree nodes. 495 496=end 497 498class MismatchedTreeNode < RecognitionError 499 attr_accessor :expecting, :input 500 def initialize( expecting, input ) 501 @expecting = expecting 502 super( input ) 503 end 504 505 def message 506 '%s: %p != %p' % 507 [ self.class, unexpected_type, @expecting ] 508 end 509end 510 511=begin rdoc ANTLR3::Error::RewriteCardinalityError 512 513error:: RewriteCardinalityError 514used by:: tree-rewriting parsers and tree parsers 515occurs when:: 516 There is an inconsistency between the number of appearances of some symbol 517 on the left side of a rewrite rule and the number of the same symbol 518 seen on the right side of a rewrite rule 519 520=end 521 522class RewriteCardinalityError < StandardError 523 attr_accessor :element_description 524 def initialize( element_description ) 525 @element_description = element_description 526 super( message ) 527 end 528 529 def message 530 "%s: %s" % [ self.class, @element_description ] 531 end 532end 533 534=begin rdoc ANTLR3::Error::RewriteEarlyExit 535 536error:: RewriteEarlyExit 537used by:: tree-rewriting parsers and tree parsers 538occurs when:: 539 A tree-rewrite rule requires one or more occurence of a symbol, but none 540 have been seen. 541 542=end 543 544class RewriteEarlyExit < RewriteCardinalityError 545 attr_accessor :element_description 546 def initialize( element_description = nil ) 547 super( element_description ) 548 end 549end 550 551=begin rdoc ANTLR3::Error::RewriteEmptyStream 552 553error:: RewriteEmptyStream 554used by:: tree-rewriting parsers and tree parsers 555 556=end 557 558class RewriteEmptyStream < RewriteCardinalityError; end 559 560=begin rdoc ANTLR3::Error::TreeInconsistency 561 562error:: TreeInconsistency 563used by:: classes that deal with tree structures 564occurs when:: 565 A tree node's data is inconsistent with the overall structure to which it 566 belongs. 567 568situations that result in tree inconsistencies: 569 5701. A node has a child node with a +@parent+ attribute different than the node. 5712. A node has a child at index +n+, but the child's +@child_index+ value is not 572 +n+ 5733. An adaptor encountered a situation where multiple tree nodes have been 574 simultaneously requested as a new tree root. 575 576=end 577 578class TreeInconsistency < StandardError 579 def self.failed_index_check!( expected, real ) 580 new( 581 "%s: child indexes don't match -> expected %d found %d" % 582 [ self, expected, real ] 583 ) 584 end 585 586 def self.failed_parent_check!( expected, real ) 587 new( 588 "%s: parents don't match; expected %p found %p" % 589 [ self, expected, real ] 590 ) 591 end 592 593 def self.multiple_roots! 594 new "%s: attempted to change more than one node to root" % self 595 end 596end 597 598module_function 599 600def MismatchedToken( expecting, input = @input ) 601 MismatchedToken.new( expecting, input ) 602end 603 604def UnwantedToken( expecting, input = @input ) 605 UnwantedToken.new( expecting, input ) 606end 607 608def MissingToken( expecting, inserted, input = @input ) 609 MissingToken.new( expecting, input, inserted ) 610end 611 612def MismatchedRange( min, max, input = @input ) 613 MismatchedRange.new( min, max, input ) 614end 615 616def MismatchedSet( expecting, input = @input ) 617 MismatchedSet.new( expecting, input ) 618end 619 620def MismatchedNotSet( expecting, input = @input ) 621 MismatchedNotSet.new( expecting, input ) 622end 623 624def NoViableAlternative( description, decision, state, input = @input ) 625 NoViableAlternative.new( description, decision, state, input ) 626end 627 628def EarlyExit( decision, input = @input ) 629 EarlyExit.new( decision, input ) 630end 631 632def FailedPredicate( rule, predicate, input = @input ) 633 FailedPredicate.new( input, rule, predicate ) 634end 635 636def MismatchedTreeNode( expecting, input = @input ) 637 MismatchedTreeNode.new( expecting, input ) 638end 639 640def RewriteCardinalityError( element_description ) 641 RewriteCardinalityError.new( element_description ) 642end 643 644def RewriteEarlyExit( element_description = nil ) 645 RewriteEarlyExit.new( element_description ) 646end 647 648def RewriteEmptyStream( element_description ) 649 RewriteEmptyStream.new( element_description ) 650end 651 652end 653 654include Error 655 656=begin rdoc ANTLR3::Bug 657 658 659 660=end 661 662class Bug < StandardError 663 def initialize( message = nil, *args ) 664 message = "something occurred that should not occur within unmodified, " << 665 "ANTLR-generated source code: #{ message }" 666 super( message, *args ) 667 end 668end 669 670end 671