Home | History | Annotate | Download | only in antlr3
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 =begin LICENSE
      5 
      6 [The "BSD licence"]
      7 Copyright (c) 2009-2010 Kyle Yetter
      8 All rights reserved.
      9 
     10 Redistribution and use in source and binary forms, with or without
     11 modification, are permitted provided that the following conditions
     12 are 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 
     22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31 THIS 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 (at] yahoo.com)
     38 module ANTLR3
     39 
     40 # for compatibility with rubinius, which does not implement StopIteration yet
     41 unless defined?( StopIteration )
     42   StopIteration = Class.new( StandardError )
     43 end
     44 
     45 module Error
     46 
     47 =begin rdoc ANTLR3::Error::BacktrackingFailed
     48 
     49 error:: BacktrackingFailed
     50 used by:: all recognizers
     51 occurs 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
     55 notes::
     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 
     66 class 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 
     98 The base class of the variety of syntax errors that can occur during the
     99 recognition process. These errors all typically concern an expectation built in
    100 to the recognizer by the rules of a grammar and an input symbol which failed to
    101 fit the expectation.
    102 
    103 =end
    104 
    105 class 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   
    167 private
    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
    205 end
    206 
    207 =begin rdoc ANTLR3::Error::MismatchedToken
    208 
    209 type:: MismatchedToken
    210 used by:: lexers and parsers
    211 occurs 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 
    217 class 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
    228 end
    229 
    230 =begin rdoc ANTLR3::Error::UnwantedToken
    231 
    232 TODO: this does not appear to be used by any code
    233 
    234 =end
    235 
    236 class 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
    246 end
    247 
    248 =begin rdoc ANTLR3::Error::MissingToken
    249 
    250 error:: MissingToken
    251 used by:: parsers and tree parsers
    252 occurs 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 
    258 grammar:
    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 
    276 in 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 
    290 class 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
    311 end
    312 
    313 =begin rdoc ANTLR3::Error::MismatchedRange
    314 
    315 error:: MismatchedRange
    316 used by:: all recognizers 
    317 occurs 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 
    324 class 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
    336 end
    337 
    338 =begin rdoc ANTLR3::Error::MismatchedSet
    339 
    340 error:: MismatchedSet
    341 used by:: all recognizers
    342 occurs 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 
    348 class 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
    359 end
    360 
    361 =begin rdoc ANTLR3::Error::MismatchedNotSet
    362 
    363 error:: MismatchedNotSet
    364 used by:: all recognizers
    365 occurs when::
    366   A recognizer expected to match symbol that is not in some set of symbols but
    367   failed.
    368 
    369 =end
    370 
    371 class MismatchedNotSet < MismatchedSet
    372   def message
    373     '%s: %p != %p' %
    374       [ self.class, unexpected_type, @expecting ]
    375   end
    376 end
    377 
    378 =begin rdoc ANTLR3::Error::NoViableAlternative
    379 
    380 error:: NoViableAlternative
    381 used by:: all recognizers
    382 occurs 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 
    387 In ANTLR terminology, a rule is composed of one or more _alternatives_,
    388 specifications seperated by <tt>|</tt> characters. An alternative is composed of
    389 a series of elements, including _subrules_ -- rule specifications enclosed
    390 within parentheses. When recognition code enters a rule method (or a subrule
    391 block) that has multiple alternatives, the recognizer must decide which one of
    392 the multiple possible paths to follow by checking a number of future input
    393 symbols. Thus, NoViableAlternative errors indicate that the current input does
    394 not fit any of the possible paths.
    395 
    396 In lexers, this error is often raised by the main +tokens!+ rule, which must
    397 choose between all possible token rules. If raised by +tokens+, it means the
    398 current input does not appear to be part of any token specification.
    399 
    400 =end
    401 
    402 class 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
    415 end
    416 
    417 =begin rdoc ANTLR3::Error::EarlyExit
    418 
    419 error:: EarlyExit
    420 used by:: all recognizers
    421 occurs 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 
    429 consider a grammar like:
    430   lexer grammar EarlyExitDemo;
    431   ...
    432   ID: 'a'..'z' ('0'..'9')+;
    433   
    434 now 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 
    446 class 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   
    458 end
    459 
    460 =begin rdoc ANTLR3::Error::FailedPredicate
    461 
    462 error:: FailedPredicate
    463 used by:: all recognizers
    464 occurs 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 
    470 class 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
    485 end
    486 
    487 =begin rdoc ANTLR3::Error::MismatchedTreeNode
    488 
    489 error:: MismatchedTreeNode
    490 used by:: tree parsers
    491 occurs 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 
    498 class 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
    509 end
    510 
    511 =begin rdoc ANTLR3::Error::RewriteCardinalityError
    512 
    513 error:: RewriteCardinalityError
    514 used by:: tree-rewriting parsers and tree parsers
    515 occurs 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 
    522 class 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
    532 end
    533 
    534 =begin rdoc ANTLR3::Error::RewriteEarlyExit
    535 
    536 error:: RewriteEarlyExit
    537 used by:: tree-rewriting parsers and tree parsers
    538 occurs when::
    539   A tree-rewrite rule requires one or more occurence of a symbol, but none
    540   have been seen.
    541 
    542 =end
    543 
    544 class RewriteEarlyExit < RewriteCardinalityError
    545   attr_accessor :element_description
    546   def initialize( element_description = nil )
    547     super( element_description )
    548   end
    549 end
    550 
    551 =begin rdoc ANTLR3::Error::RewriteEmptyStream
    552 
    553 error:: RewriteEmptyStream
    554 used by:: tree-rewriting parsers and tree parsers
    555 
    556 =end
    557 
    558 class RewriteEmptyStream < RewriteCardinalityError; end
    559 
    560 =begin rdoc ANTLR3::Error::TreeInconsistency
    561 
    562 error:: TreeInconsistency
    563 used by:: classes that deal with tree structures
    564 occurs when::
    565   A tree node's data is inconsistent with the overall structure to which it
    566   belongs.
    567 
    568 situations that result in tree inconsistencies:
    569 
    570 1. A node has a child node with a +@parent+ attribute different than the node.
    571 2. A node has a child at index +n+, but the child's +@child_index+ value is not
    572    +n+
    573 3. An adaptor encountered a situation where multiple tree nodes have been
    574    simultaneously requested as a new tree root.
    575 
    576 =end
    577 
    578 class 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
    596 end
    597 
    598 module_function
    599 
    600 def MismatchedToken( expecting, input = @input )
    601   MismatchedToken.new( expecting, input )
    602 end
    603 
    604 def UnwantedToken( expecting, input = @input )
    605   UnwantedToken.new( expecting, input )
    606 end
    607 
    608 def MissingToken( expecting, inserted, input = @input )
    609   MissingToken.new( expecting, input, inserted )
    610 end
    611 
    612 def MismatchedRange( min, max, input = @input )
    613   MismatchedRange.new( min, max, input )
    614 end
    615 
    616 def MismatchedSet( expecting, input = @input )
    617   MismatchedSet.new( expecting, input )
    618 end
    619 
    620 def MismatchedNotSet( expecting, input = @input )
    621   MismatchedNotSet.new( expecting, input )
    622 end
    623 
    624 def NoViableAlternative( description, decision, state, input = @input )
    625   NoViableAlternative.new( description, decision, state, input )
    626 end
    627 
    628 def EarlyExit( decision, input = @input )
    629   EarlyExit.new( decision, input )
    630 end
    631 
    632 def FailedPredicate( rule, predicate, input = @input )
    633   FailedPredicate.new( input, rule, predicate )
    634 end
    635 
    636 def MismatchedTreeNode( expecting, input = @input )
    637   MismatchedTreeNode.new( expecting, input )
    638 end
    639 
    640 def RewriteCardinalityError( element_description )
    641   RewriteCardinalityError.new( element_description )
    642 end
    643 
    644 def RewriteEarlyExit( element_description = nil )
    645   RewriteEarlyExit.new( element_description )
    646 end
    647 
    648 def RewriteEmptyStream( element_description )
    649   RewriteEmptyStream.new( element_description )
    650 end
    651 
    652 end
    653   
    654 include Error
    655 
    656 =begin rdoc ANTLR3::Bug
    657 
    658 
    659 
    660 =end
    661 
    662 class 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
    668 end
    669 
    670 end
    671