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