rltk 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rltk/parser.rb CHANGED
@@ -16,158 +16,70 @@ require 'rltk/cfg'
16
16
 
17
17
  module RLTK # :nodoc:
18
18
 
19
- # A BadToken exception indicates that a token was observed in the input
20
- # stream that wasn't used in the grammar's definition.
21
- class BadToken < Exception
19
+ # A BadToken error indicates that a token was observed in the input stream
20
+ # that wasn't used in the grammar's definition.
21
+ class BadToken < StandardError
22
+ # @return [String] String representation of the error.
22
23
  def to_s
23
24
  'Unexpected token. Token not present in grammar definition.'
24
25
  end
25
26
  end
26
27
 
27
- # A NotInLanguage exception is raised whenever there is no valid parse tree
28
+ # A NotInLanguage error is raised whenever there is no valid parse tree
28
29
  # for a given token stream. In other words, the input string is not in the
29
30
  # defined language.
30
- class NotInLanguage < Exception
31
+ class NotInLanguage < StandardError
32
+ # @return [String] String representation of the error.
31
33
  def to_s
32
34
  'String not in language.'
33
35
  end
34
36
  end
35
37
 
36
- # An exception of this type is raised when the parser encountered a error
37
- # that was handled by an error production.
38
- class HandledError < Exception
38
+ # An error of this type is raised when the parser encountered a error that
39
+ # was handled by an error production.
40
+ class HandledError < StandardError
39
41
 
40
42
  # The errors as reported by the parser.
43
+ #
44
+ # @return [Array<Object>]
41
45
  attr_reader :errors
42
46
 
43
- # The result that would have been returned by the call to _parse_.
47
+ # The result that would have been returned by the call to *parse*.
44
48
  attr_reader :result
45
49
 
46
- # Instantiate a new HandledError object with _errors_.
50
+ # Instantiate a new HandledError object with *errors*.
51
+ #
52
+ # @param [Array<Object>] errors Errors added to the parsing environment by calls to {Parser::Environment#error}.
53
+ # @param [Object] result Object resulting from parsing Tokens before the error occurred.
47
54
  def initialize(errors, result)
48
55
  @errors = errors
49
56
  @result = result
50
57
  end
51
58
  end
52
59
 
53
- # Used for errors that occure during parser construction.
54
- class ParserConstructionError < Exception; end
60
+ # Used for exceptions that occure during parser construction.
61
+ class ParserConstructionException < Exception; end
55
62
 
56
- # Used for runtime errors that are the parsers fault. These should never
57
- # be observed in the wild.
58
- class InternalParserError < Exception; end
63
+ # Used for runtime exceptions that are the parsers fault. These should
64
+ # never be observed in the wild.
65
+ class InternalParserException < Exception; end
59
66
 
60
67
  # The Parser class may be sub-classed to produce new parsers. These
61
68
  # parsers have a lot of features, and are described in the main
62
69
  # documentation.
63
70
  class Parser
71
+ # @return [Environment] Environment used by the instantiated parser.
72
+ attr_reader :env
64
73
 
65
- # Called when the Parser class is sub-classed, this method adds a
66
- # ParserCore to the new class, and installs some needed class and
67
- # instance methods.
68
- def Parser.inherited(klass)
69
- klass.class_exec do
70
- @core = ParserCore.new
71
-
72
- # Returns this class's ParserCore object.
73
- def self.core
74
- @core
75
- end
76
-
77
- # Routes method calls to the new subclass to the ParserCore
78
- # object.
79
- def self.method_missing(method, *args, &proc)
80
- @core.send(method, *args, &proc)
81
- end
82
-
83
- # Alias for RLTK::Parser::ParserCore.p that needs to be
84
- # manually connected.
85
- def self.p(*args, &proc)
86
- @core.p(*args, &proc)
87
- end
88
-
89
- # Parses the given token stream using a newly instantiated
90
- # environment. See ParserCore.parse for a description of
91
- # the _opts_ option hash.
92
- def self.parse(tokens, opts = {})
93
- opts[:env] ||= self::Environment.new
94
-
95
- @core.parse(tokens, opts)
96
- end
97
-
98
- # Instantiates a new parser and creates an environment to be
99
- # used for subsequent calls.
100
- def initialize
101
- @env = self.class::Environment.new
102
- end
103
-
104
- # Returns the environment used by the instantiated parser.
105
- def env
106
- @env
107
- end
108
-
109
- # Parses the given token stream using the encapsulated
110
- # environment. See ParserCore.parse for a description of
111
- # the _opts_ option hash.
112
- def parse(tokens, opts = {})
113
- self.class.core.parse(tokens, {:env => @env}.update(opts))
114
- end
115
- end
116
- end
117
-
118
- # All actions passed to ParserCore.rule and ParserCore.clause are
119
- # evaluated inside an instance of the Environment class or its
120
- # subclass (which must have the same name).
121
- class Environment
122
- # Indicates if an error was encountered and handled.
123
- attr_accessor :he
124
-
125
- # A list of all objects added using the _error_ method.
126
- attr_reader :errors
127
-
128
- # Instantiate a new Environment object.
129
- def initialize
130
- self.reset
131
- end
132
-
133
- # Adds an object to the list of errors.
134
- def error(o)
135
- @errors << o
136
- end
137
-
138
- # Returns a StreamPosition object for the symbol at location n,
139
- # indexed from zero.
140
- def pos(n)
141
- @positions[n]
142
- end
143
-
144
- # Reset any variables that need to be re-initialized between
145
- # parse calls.
146
- def reset
147
- @errors = Array.new
148
- @he = false
149
- end
150
-
151
- # Setter for the _positions_ array.
152
- def set_positions(positions)
153
- @positions = positions
154
- end
155
- end
74
+ #################
75
+ # Class Methods #
76
+ #################
156
77
 
157
- # The ParserCore class provides mos of the functionality of the Parser
158
- # class. A ParserCore is instantiated for each subclass of Parser,
159
- # thereby allowing multiple parsers to be defined inside a single Ruby
160
- # program.
161
- class ParserCore
162
-
163
- # The grammar that can be parsed by this ParserCore. The grammar
164
- # is used internally and should not be manipulated outside of the
165
- # ParserCore object.
166
- attr_reader :grammar
167
-
168
- # Instantiates a new ParserCore object with the needed data
169
- # structures.
170
- def initialize
78
+ class << self
79
+ # Installs instance class varialbes into a class.
80
+ #
81
+ # @return [void]
82
+ def install_icvars
171
83
  @curr_lhs = nil
172
84
  @curr_prec = nil
173
85
 
@@ -212,10 +124,22 @@ module RLTK # :nodoc:
212
124
  end
213
125
  end
214
126
 
215
- # If _state_ (or its equivalent) is not in the state list it is
127
+ # Called when the Lexer class is sub-classed, it installes
128
+ # necessary instance class variables.
129
+ #
130
+ # @return [void]
131
+ def inherited(klass)
132
+ klass.install_icvars
133
+ end
134
+
135
+ # If *state* (or its equivalent) is not in the state list it is
216
136
  # added and it's ID is returned. If there is already a state
217
- # with the same items as _state_ in the state list its ID is
218
- # returned and _state_ is discarded.
137
+ # with the same items as *state* in the state list its ID is
138
+ # returned and *state* is discarded.
139
+ #
140
+ # @param [State] state State to add to the parser.
141
+ #
142
+ # @return [Integer] The ID of the state.
219
143
  def add_state(state)
220
144
  if (id = @states.index(state))
221
145
  id
@@ -230,7 +154,9 @@ module RLTK # :nodoc:
230
154
 
231
155
  # Calling this method will cause the parser to pass right-hand
232
156
  # side values as arrays instead of splats. This method must be
233
- # called before ANY calls to ParserCore.production.
157
+ # called before ANY calls to Parser.production.
158
+ #
159
+ # @return [void]
234
160
  def array_args
235
161
  if @grammar.productions.length == 0
236
162
  @args = :array
@@ -262,8 +188,12 @@ module RLTK # :nodoc:
262
188
  end
263
189
  end
264
190
 
265
- # Build a hash with the default options for ParserCore.finalize
266
- # and then update it with the values from _opts_.
191
+ # Build a hash with the default options for Parser.finalize
192
+ # and then update it with the values from *opts*.
193
+ #
194
+ # @param [Hash{Symbol => Object}] opts Hash containing options for finalize.
195
+ #
196
+ # @return [Hash{Symbol => Object}]
267
197
  def build_finalize_opts(opts)
268
198
  opts[:explain] = self.get_io(opts[:explain])
269
199
 
@@ -274,33 +204,41 @@ module RLTK # :nodoc:
274
204
  :use => false
275
205
  }.update(opts)
276
206
  end
207
+ private :build_finalize_opts
277
208
 
278
- # Build a hash with the default options for ParserCore.parse and
279
- # then update it with the values from _opts_.
209
+ # Build a hash with the default options for Parser.parse and
210
+ # then update it with the values from *opts*.
211
+ #
212
+ # @param [Hash{Symbol => Object}] opts Hash containing options for parse.
213
+ #
214
+ # @return [Hash{Symbol => Object}]
280
215
  def build_parse_opts(opts)
281
216
  opts[:parse_tree] = self.get_io(opts[:parse_tree])
282
217
  opts[:verbose] = self.get_io(opts[:verbose])
283
218
 
284
219
  {
285
220
  :accept => :first,
286
- :env => Environment.new,
221
+ :env => self::Environment.new,
287
222
  :parse_tree => false,
288
223
  :verbose => false
289
224
  }.update(opts)
290
225
  end
226
+ private :build_parse_opts
291
227
 
292
228
  # This method is used to (surprise) check the sanity of the
293
229
  # constructed parser. It checks to make sure all non-terminals
294
230
  # used in the grammar definition appear on the left-hand side of
295
231
  # one or more productions, and that none of the parser's states
296
232
  # have invalid actions. If a problem is encountered a
297
- # ParserConstructionError is raised.
233
+ # ParserConstructionException is raised.
234
+ #
235
+ # @return [void]
298
236
  def check_sanity
299
237
  # Check to make sure all non-terminals appear on the
300
238
  # left-hand side of some production.
301
239
  @grammar.nonterms.each do |sym|
302
240
  if not @lh_sides.values.include?(sym)
303
- raise ParserConstructionError, "Non-terminal #{sym} does not appear on the left-hand side of any production."
241
+ raise ParserConstructionException, "Non-terminal #{sym} does not appear on the left-hand side of any production."
304
242
  end
305
243
  end
306
244
 
@@ -312,11 +250,11 @@ module RLTK # :nodoc:
312
250
  actions.each do |action|
313
251
  if action.is_a?(Accept)
314
252
  if sym != :EOS
315
- raise ParserConstructionError, "Accept action found for terminal #{sym} in state #{state.id}."
253
+ raise ParserConstructionException, "Accept action found for terminal #{sym} in state #{state.id}."
316
254
  end
317
255
 
318
256
  elsif not (action.is_a?(GoTo) or action.is_a?(Reduce) or action.is_a?(Shift))
319
- raise ParserConstructionError, "Object of type #{action.class} found in actions for terminal " +
257
+ raise ParserConstructionException, "Object of type #{action.class} found in actions for terminal " +
320
258
  "#{sym} in state #{state.id}."
321
259
 
322
260
  end
@@ -328,10 +266,10 @@ module RLTK # :nodoc:
328
266
  else
329
267
  # Here we check actions for non-terminals.
330
268
  if actions.length > 1
331
- raise ParserConstructionError, "State #{state.id} has multiple GoTo actions for non-terminal #{sym}."
269
+ raise ParserConstructionException, "State #{state.id} has multiple GoTo actions for non-terminal #{sym}."
332
270
 
333
271
  elsif actions.length == 1 and not actions.first.is_a?(GoTo)
334
- raise ParserConstructionError, "State #{state.id} has non-GoTo action for non-terminal #{sym}."
272
+ raise ParserConstructionException, "State #{state.id} has non-GoTo action for non-terminal #{sym}."
335
273
 
336
274
  end
337
275
  end
@@ -340,7 +278,13 @@ module RLTK # :nodoc:
340
278
  end
341
279
 
342
280
  # This method checks to see if the parser would be in parse state
343
- # _dest_ after starting in state _start_ and reading _symbols_.
281
+ # *dest* after starting in state *start* and reading *symbols*.
282
+ #
283
+ # @param [Symbol] start Symbol representing a CFG production.
284
+ # @param [Symbol] dest Symbol representing a CFG production.
285
+ # @param [Array<Symbol>] symbols Grammar symbols.
286
+ #
287
+ # @return [Boolean] If the destination symbol is reachable from the start symbol after reading *symbols*.
344
288
  def check_reachability(start, dest, symbols)
345
289
  path_exists = true
346
290
  cur_state = start
@@ -365,9 +309,15 @@ module RLTK # :nodoc:
365
309
  end
366
310
 
367
311
  # Declares a new clause inside of a production. The right-hand
368
- # side is specified by _expression_ and the precedence of this
369
- # production can be changed by setting the _precedence_ argument
312
+ # side is specified by *expression* and the precedence of this
313
+ # production can be changed by setting the *precedence* argument
370
314
  # to some terminal symbol.
315
+ #
316
+ # @param [String] expression Right-hand side of a production.
317
+ # @param [Symbol] precedence Symbol representing the precedence of this production.
318
+ # @param [Proc] action Action to be taken when the production is reduced.
319
+ #
320
+ # @return [void]
371
321
  def clause(expression, precedence = nil, &action)
372
322
  # Use the curr_prec only if it isn't overridden for this
373
323
  # clause.
@@ -378,7 +328,7 @@ module RLTK # :nodoc:
378
328
  # Check to make sure the action's arity matches the number
379
329
  # of symbols on the right-hand side.
380
330
  if @args == :splat and action.arity != production.rhs.length
381
- raise ParserConstructionError, 'Incorrect number of arguments to action. Action arity must match the number of ' +
331
+ raise ParserConstructionException, 'Incorrect number of arguments to action. Action arity must match the number of ' +
382
332
  'terminals and non-terminals in the clause.'
383
333
  end
384
334
 
@@ -389,11 +339,12 @@ module RLTK # :nodoc:
389
339
  # last terminal in the production.
390
340
  @production_precs[production.id] = precedence || production.last_terminal
391
341
  end
392
-
393
342
  alias :c :clause
394
343
 
395
344
  # Removes resources that were needed to generate the parser but
396
345
  # aren't needed when actually parsing input.
346
+ #
347
+ # @return [void]
397
348
  def clean
398
349
  # We've told the developer about conflicts by now.
399
350
  @conflicts = nil
@@ -416,11 +367,15 @@ module RLTK # :nodoc:
416
367
 
417
368
  # This function will print a description of the parser to the
418
369
  # provided IO object.
370
+ #
371
+ # @param [IO] io Input/Output object used for printing the parser's explanation.
372
+ #
373
+ # @return [void]
419
374
  def explain(io)
420
375
  if @grammar and not @states.empty?
421
- io.puts("###############")
422
- io.puts("# Productions #")
423
- io.puts("###############")
376
+ io.puts('###############')
377
+ io.puts('# Productions #')
378
+ io.puts('###############')
424
379
  io.puts
425
380
 
426
381
  # Print the productions.
@@ -438,9 +393,9 @@ module RLTK # :nodoc:
438
393
  io.puts
439
394
  end
440
395
 
441
- io.puts("##########")
442
- io.puts("# Tokens #")
443
- io.puts("##########")
396
+ io.puts('##########')
397
+ io.puts('# Tokens #')
398
+ io.puts('##########')
444
399
  io.puts
445
400
 
446
401
  @grammar.terms.sort {|a,b| a.to_s <=> b.to_s }.each do |term|
@@ -455,9 +410,9 @@ module RLTK # :nodoc:
455
410
 
456
411
  io.puts
457
412
 
458
- io.puts("#####################")
459
- io.puts("# Table Information #")
460
- io.puts("#####################")
413
+ io.puts('#####################')
414
+ io.puts('# Table Information #')
415
+ io.puts('#####################')
461
416
  io.puts
462
417
 
463
418
  io.puts("\tStart symbol: #{@grammar.start_symbol}")
@@ -476,9 +431,9 @@ module RLTK # :nodoc:
476
431
  io.puts if not @conflicts.empty?
477
432
 
478
433
  # Print the parse table.
479
- io.puts("###############")
480
- io.puts("# Parse Table #")
481
- io.puts("###############")
434
+ io.puts('###############')
435
+ io.puts('# Parse Table #')
436
+ io.puts('###############')
482
437
  io.puts
483
438
 
484
439
  @states.each do |state|
@@ -524,7 +479,7 @@ module RLTK # :nodoc:
524
479
  # Close any IO objects that aren't $stdout.
525
480
  io.close if io.is_a?(IO) and io != $stdout
526
481
  else
527
- raise ParserConstructionError, 'Parser.explain called outside of finalize.'
482
+ raise ParserConstructionException, 'Parser.explain called outside of finalize.'
528
483
  end
529
484
  end
530
485
 
@@ -532,7 +487,7 @@ module RLTK # :nodoc:
532
487
  # of states and their actions, and the resolution of conflicts
533
488
  # using lookahead and precedence information.
534
489
  #
535
- # The _opts_ hash may contain the following options, which are
490
+ # The *opts* hash may contain the following options, which are
536
491
  # described in more detail in the main documentation:
537
492
  #
538
493
  # * :explain - To explain the parser or not.
@@ -540,12 +495,16 @@ module RLTK # :nodoc:
540
495
  # * :precedence - To use precedence info for conflict resolution.
541
496
  # * :use - A file name or object that is used to load/save the parser.
542
497
  #
543
- # No calls to ParserCore.production may appear after the call to
544
- # ParserCore.finalize.
498
+ # No calls to {Parser.production} may appear after the call to
499
+ # Parser.finalize.
500
+ #
501
+ # @param [Hash{Symbol => Object}] opts Options describing how to finalize the parser.
502
+ #
503
+ # @return [void]
545
504
  def finalize(opts = {})
546
505
 
547
506
  # Get the full options hash.
548
- opts = self.build_finalize_opts(opts)
507
+ opts = build_finalize_opts(opts)
549
508
 
550
509
  # Get the name of the file in which the parser is defined.
551
510
  def_file = caller()[2].split(':')[0]
@@ -557,8 +516,15 @@ module RLTK # :nodoc:
557
516
  (opts[:use].is_a?(File) and opts[:use].mtime > File.mtime(def_file))
558
517
  )
559
518
 
519
+ file = self.get_io(opts[:use], 'r')
520
+
560
521
  # Un-marshal our saved data structures.
561
- @lh_sides, @states, @symbols = Marshal.load(self.get_io(opts[:use], 'r'))
522
+ file.flock(File::LOCK_SH)
523
+ @lh_sides, @states, @symbols = Marshal.load(file)
524
+ file.flock(File::LOCK_UN)
525
+
526
+ # Close the file if we opened it.
527
+ file.close if opts[:use].is_a?(String)
562
528
 
563
529
  # Remove any un-needed data and return.
564
530
  return self.clean
@@ -645,10 +611,24 @@ module RLTK # :nodoc:
645
611
  self.clean
646
612
 
647
613
  # Store the parser's final data structures if requested.
648
- Marshal.dump([@lh_sides, @states, @symbols], self.get_io(opts[:use])) if opts[:use]
614
+ if opts[:use]
615
+ io = self.get_io(opts[:use])
616
+
617
+ io.flock(File::LOCK_EX) if io.is_a?(File)
618
+ Marshal.dump([@lh_sides, @states, @symbols], io)
619
+ io.flock(File::LOCK_UN) if io.is_a?(File)
620
+
621
+ # Close the IO object if we opened it.
622
+ io.close if opts[:use].is_a?(String)
623
+ end
649
624
  end
650
625
 
651
626
  # Converts an object into an IO object as appropriate.
627
+ #
628
+ # @param [Object] o Object to be converted into an IO object.
629
+ # @param [String] mode String representing the mode to open the IO object in.
630
+ #
631
+ # @return [IO, false] The IO object or false if a conversion wasn't possible.
652
632
  def get_io(o, mode = 'w')
653
633
  if o.is_a?(TrueClass)
654
634
  $stdout
@@ -661,6 +641,11 @@ module RLTK # :nodoc:
661
641
  end
662
642
  end
663
643
 
644
+ # @return [CFG] The grammar that can be parsed by this Parser.
645
+ def grammar
646
+ @grammar.clone
647
+ end
648
+
664
649
  # This method generates and memoizes the G' grammar used to
665
650
  # calculate the LALR(1) lookahead sets. Information about this
666
651
  # grammar and its use can be found in the following paper:
@@ -668,6 +653,8 @@ module RLTK # :nodoc:
668
653
  # Simple Computation of LALR(1) Lookahed Sets
669
654
  # Manuel E. Bermudez and George Logothetis
670
655
  # Information Processing Letters 31 - 1989
656
+ #
657
+ # @return [CFG]
671
658
  def grammar_prime
672
659
  if not @grammar_prime
673
660
  @grammar_prime = CFG.new
@@ -699,13 +686,23 @@ module RLTK # :nodoc:
699
686
  end
700
687
 
701
688
  # Inform the parser core that a conflict has been detected.
689
+ #
690
+ # @param [Integer] state_id ID of the state where the conflict was encountered.
691
+ # @param [:RR, :SR] type Reduce/Reduce or Shift/Reduce conflict.
692
+ # @param [Symbol] sym Symbol that caused the conflict.
693
+ #
694
+ # @return [void]
702
695
  def inform_conflict(state_id, type, sym)
703
696
  @conflicts[state_id] << [type, sym]
704
697
  end
705
698
 
706
- # This method is used to specify that the symbols in _symbols_
707
- # are left associative. Subsequent calls to this method will
699
+ # This method is used to specify that the symbols in *symbols*
700
+ # are left-associative. Subsequent calls to this method will
708
701
  # give their arguments higher precedence.
702
+ #
703
+ # @param [Array<Symbol>] symbols Symbols that are left associative.
704
+ #
705
+ # @return [void]
709
706
  def left(*symbols)
710
707
  prec_level = @prec_counts[:left] += 1
711
708
 
@@ -714,8 +711,12 @@ module RLTK # :nodoc:
714
711
  end
715
712
  end
716
713
 
717
- # This method is used to specify that the symbols in _symbols_
714
+ # This method is used to specify that the symbols in *symbols*
718
715
  # are non-associative.
716
+ #
717
+ # @param [Array<Symbol>] symbols Symbols that are non-associative.
718
+ #
719
+ # @return [void]
719
720
  def nonassoc(*symbols)
720
721
  prec_level = @prec_counts[:non] += 1
721
722
 
@@ -738,9 +739,13 @@ module RLTK # :nodoc:
738
739
  #
739
740
  # Additional information for these options can be found in the
740
741
  # main documentation.
742
+ #
743
+ # @param [Array<Token>] tokens Tokens to be parsed.
744
+ #
745
+ # @return [Object, Array<Object>] Result or results of parsing the given tokens.
741
746
  def parse(tokens, opts = {})
742
747
  # Get the full options hash.
743
- opts = self.build_parse_opts(opts)
748
+ opts = build_parse_opts(opts)
744
749
  v = opts[:verbose]
745
750
 
746
751
  if opts[:verbose]
@@ -858,7 +863,7 @@ module RLTK # :nodoc:
858
863
  production_proc, pop_size = @procs[action.id]
859
864
 
860
865
  if not production_proc
861
- raise InternalParserError, "No production #{action.id} found."
866
+ raise InternalParserException, "No production #{action.id} found."
862
867
  end
863
868
 
864
869
  args, positions = stack.pop(pop_size)
@@ -895,7 +900,7 @@ module RLTK # :nodoc:
895
900
 
896
901
  stack.push(goto.id, result, @lh_sides[action.id], pos0)
897
902
  else
898
- raise InternalParserError, "No GoTo action found in state #{stack.state} " +
903
+ raise InternalParserException, "No GoTo action found in state #{stack.state} " +
899
904
  "after reducing by production #{action.id}"
900
905
  end
901
906
 
@@ -956,17 +961,24 @@ module RLTK # :nodoc:
956
961
  end
957
962
 
958
963
  # Adds a new production to the parser with a left-hand value of
959
- # _symbol_. If _expression_ is specified it is taken as the
960
- # right-hand side of the production and _action_ is associated
961
- # with the production. If _expression_ is nil then _action_ is
964
+ # *symbol*. If *expression* is specified it is taken as the
965
+ # right-hand side of the production and *action* is associated
966
+ # with the production. If *expression* is nil then *action* is
962
967
  # evaluated and expected to make one or more calls to
963
- # ParserCore.clause. A precedence can be associate with this
964
- # production by setting _precedence_ to a terminal symbol.
968
+ # Parser.clause. A precedence can be associate with this
969
+ # production by setting *precedence* to a terminal symbol.
970
+ #
971
+ # @param [Symbol] symbol Left-hand side of the production.
972
+ # @param [String, nil] expression Right-hand side of the production.
973
+ # @param [Symbol, nil] precedence Symbol representing the precedence of this produciton.
974
+ # @param [Proc] action Action associated with this production.
975
+ #
976
+ # @return [void]
965
977
  def production(symbol, expression = nil, precedence = nil, &action)
966
978
 
967
979
  # Check the symbol.
968
980
  if not (symbol.is_a?(Symbol) or symbol.is_a?(String)) or not CFG::is_nonterminal?(symbol)
969
- riase ParserConstructionError, 'Production symbols must be Strings or Symbols and be in all lowercase.'
981
+ riase ParserConstructionException, 'Production symbols must be Strings or Symbols and be in all lowercase.'
970
982
  end
971
983
 
972
984
  @grammar.curr_lhs = symbol.to_sym
@@ -981,11 +993,15 @@ module RLTK # :nodoc:
981
993
  @grammar.curr_lhs = nil
982
994
  @curr_prec = nil
983
995
  end
984
-
985
996
  alias :p :production
986
997
 
987
998
  # This method uses lookahead sets and precedence information to
988
999
  # resolve conflicts and remove unnecessary reduce actions.
1000
+ #
1001
+ # @param [Boolean] do_lookahead Prune based on lookahead sets or not.
1002
+ # @param [Boolean] do_precedence Prune based on precedence or not.
1003
+ #
1004
+ # @return [void]
989
1005
  def prune(do_lookahead, do_precedence)
990
1006
  terms = @grammar.terms
991
1007
 
@@ -1073,7 +1089,7 @@ module RLTK # :nodoc:
1073
1089
  selected_action = a
1074
1090
 
1075
1091
  elsif prec == max_prec and assoc == :nonassoc
1076
- raise ParserConstructionError, 'Non-associative token found during conflict resolution.'
1092
+ raise ParserConstructionException, 'Non-associative token found during conflict resolution.'
1077
1093
 
1078
1094
  end
1079
1095
  end
@@ -1088,6 +1104,10 @@ module RLTK # :nodoc:
1088
1104
  # This method is used to specify that the symbols in _symbols_
1089
1105
  # are right associative. Subsequent calls to this method will
1090
1106
  # give their arguments higher precedence.
1107
+ #
1108
+ # @param [Array<Symbol>] symbols Symbols that are right-associative.
1109
+ #
1110
+ # @return [void]
1091
1111
  def right(*symbols)
1092
1112
  prec_level = @prec_counts[:right] += 1
1093
1113
 
@@ -1097,19 +1117,110 @@ module RLTK # :nodoc:
1097
1117
  end
1098
1118
 
1099
1119
  # Changes the starting symbol of the parser.
1120
+ #
1121
+ # @param [Symbol] symbol The starting symbol of the grammar.
1122
+ #
1123
+ # @return [void]
1100
1124
  def start(symbol)
1101
1125
  @grammar.start symbol
1102
1126
  end
1103
1127
  end
1104
1128
 
1105
- # The ParseStack class is used by a ParserCore to keep track of state
1129
+ ####################
1130
+ # Instance Methods #
1131
+ ####################
1132
+
1133
+ # Instantiates a new parser and creates an environment to be
1134
+ # used for subsequent calls.
1135
+ def initialize
1136
+ @env = self.class::Environment.new
1137
+ end
1138
+
1139
+ # Parses the given token stream using the encapsulated environment.
1140
+ #
1141
+ # @see .parse
1142
+ def parse(tokens, opts = {})
1143
+ self.class.parse(tokens, {:env => @env}.update(opts))
1144
+ end
1145
+
1146
+ ################################
1147
+
1148
+ # All actions passed to Parser.producation and Parser.clause are
1149
+ # evaluated inside an instance of the Environment class or its
1150
+ # subclass (which must have the same name).
1151
+ class Environment
1152
+ # Indicates if an error was encountered and handled.
1153
+ #
1154
+ # @return [Boolean]
1155
+ attr_accessor :he
1156
+
1157
+ # A list of all objects added using the *error* method.
1158
+ #
1159
+ # @return [Array<Object>]
1160
+ attr_reader :errors
1161
+
1162
+ # Instantiate a new Environment object.
1163
+ def initialize
1164
+ self.reset
1165
+ end
1166
+
1167
+ # Adds an object to the list of errors.
1168
+ #
1169
+ # @return [void]
1170
+ def error(o)
1171
+ @errors << o
1172
+ end
1173
+
1174
+ # Returns a StreamPosition object for the symbol at location n,
1175
+ # indexed from zero.
1176
+ #
1177
+ # @param [Integer] n Index for symbol position.
1178
+ #
1179
+ # @return [StreamPosition] Position of symbol at index n.
1180
+ def pos(n)
1181
+ @positions[n]
1182
+ end
1183
+
1184
+ # Reset any variables that need to be re-initialized between
1185
+ # parse calls.
1186
+ #
1187
+ # @return [void]
1188
+ def reset
1189
+ @errors = Array.new
1190
+ @he = false
1191
+ end
1192
+
1193
+ # Setter for the *positions* array.
1194
+ #
1195
+ # @param [Array<StreamPosition>] positions
1196
+ #
1197
+ # @return [Array<StreamPosition>] The same array of positions.
1198
+ def set_positions(positions)
1199
+ @positions = positions
1200
+ end
1201
+ end
1202
+
1203
+ # The ParseStack class is used by a Parser to keep track of state
1106
1204
  # during parsing.
1107
1205
  class ParseStack
1206
+ # @return [Integer] ID of this parse stack.
1108
1207
  attr_reader :id
1208
+
1209
+ # @return [Array<Object>] Array of objects produced by {Reduce} actions.
1109
1210
  attr_reader :output_stack
1211
+
1212
+ # @return [Array<Integer>] Array of states used when performing {Reduce} actions.
1110
1213
  attr_reader :state_stack
1111
1214
 
1112
1215
  # Instantiate a new ParserStack object.
1216
+ #
1217
+ # @param [Integer] id ID for this parse stack. Used by GLR algorithm.
1218
+ # @param [Array<Object>] ostack Output stack. Holds results of {Reduce} and {Shift} actions.
1219
+ # @param [Array<Integer>] sstack State stack. Holds states that have been shifted due to {Shift} actions.
1220
+ # @param [Array<Integer>] nstack Node stack. Holds dot language IDs for nodes in the parse tree.
1221
+ # @param [Array<Array<Integer>>] connections Integer pairs representing edges in the parse tree.
1222
+ # @param [Array<Symbol>] labels Labels for nodes in the parse tree.
1223
+ # @param [Array<StreamPosition>] positions Position data for symbols that have been shifted.
1113
1224
  def initialize(id, ostack = [], sstack = [0], nstack = [], connections = [], labels = [], positions = [])
1114
1225
  @id = id
1115
1226
 
@@ -1124,12 +1235,16 @@ module RLTK # :nodoc:
1124
1235
 
1125
1236
  # Branch this stack, effectively creating a new copy of its
1126
1237
  # internal state.
1238
+ #
1239
+ # @param [Integer] new_id ID for the new ParseStack.
1240
+ #
1241
+ # @return [ParseStack]
1127
1242
  def branch(new_id)
1128
1243
  ParseStack.new(new_id, @output_stack.clone, @state_stack.clone, @node_stack.clone,
1129
1244
  @connections.clone, @labels.clone, @positions.clone)
1130
1245
  end
1131
1246
 
1132
- # Returns the position of the last symbol on the stack.
1247
+ # @return [StreamPosition] Position data for the last symbol on the stack.
1133
1248
  def position
1134
1249
  if @positions.empty?
1135
1250
  StreamPosition.new
@@ -1139,6 +1254,13 @@ module RLTK # :nodoc:
1139
1254
  end
1140
1255
 
1141
1256
  # Push new state and other information onto the stack.
1257
+ #
1258
+ # @param [Integer] state ID of the shifted state.
1259
+ # @param [Object] o Value of Token that caused the shift.
1260
+ # @param [Symbol] node0 Label for node in parse tree.
1261
+ # @param [StreamPosition] position Position token that got shifted.
1262
+ #
1263
+ # @return [void]
1142
1264
  def push(state, o, node0, position)
1143
1265
  @state_stack << state
1144
1266
  @output_stack << o
@@ -1153,8 +1275,11 @@ module RLTK # :nodoc:
1153
1275
  end
1154
1276
  end
1155
1277
 
1156
- # Pop some number of objects off of the inside stacks, returning
1157
- # the values popped from the output stack.
1278
+ # Pop some number of objects off of the inside stacks.
1279
+ #
1280
+ # @param [Integer] n Number of object to pop off the stack.
1281
+ #
1282
+ # @return [Array<Array<Object, StreamPosition>>] Values popped from the output and positions stacks.
1158
1283
  def pop(n = 1)
1159
1284
  @state_stack.pop(n)
1160
1285
 
@@ -1168,21 +1293,22 @@ module RLTK # :nodoc:
1168
1293
 
1169
1294
  # Fetch the result stored in this ParseStack. If there is more
1170
1295
  # than one object left on the output stack there is an error.
1296
+ #
1297
+ # @return [Object] The end result of this parse stack.
1171
1298
  def result
1172
1299
  if @output_stack.length == 1
1173
1300
  return @output_stack.last
1174
1301
  else
1175
- raise InternalParserError, "The parsing stack should have 1 element on the output stack, not #{@output_stack.length}."
1302
+ raise InternalParserException, "The parsing stack should have 1 element on the output stack, not #{@output_stack.length}."
1176
1303
  end
1177
1304
  end
1178
1305
 
1179
- # Return the current state of this ParseStack.
1306
+ # @return [Integer] Current state of this ParseStack.
1180
1307
  def state
1181
1308
  @state_stack.last
1182
1309
  end
1183
1310
 
1184
- # Return a string representing the parse tree in the DOT
1185
- # language.
1311
+ # @return [String] Representation of the parse tree in the DOT langauge.
1186
1312
  def tree
1187
1313
  tree = "digraph tree#{@id} {\n"
1188
1314
 
@@ -1209,15 +1335,19 @@ module RLTK # :nodoc:
1209
1335
  # The State class is used to represent sets of items and actions to be
1210
1336
  # used during parsing.
1211
1337
  class State
1212
- # The state's ID.
1213
- attr_accessor :id
1214
- # The CFG::Item objects that comprise this state.
1215
- attr_reader :items
1216
- # The Action objects that represent the actions that should be
1217
- # taken when various inputs are observed.
1218
- attr_reader :actions
1338
+ # @return [Integer] State's ID.
1339
+ attr_accessor :id
1340
+
1341
+ # @return [Array<CFG::Item>] Item objects that comprise this state.
1342
+ attr_reader :items
1343
+
1344
+ # @return [Array<Action>] Action objects that represent the actions that should be taken when various inputs are observed.
1345
+ attr_reader :actions
1219
1346
 
1220
1347
  # Instantiate a new State object.
1348
+ #
1349
+ # @param [Array<Token>] tokens Tokens that represent this state.
1350
+ # @param [Array<CFG::Item>] items Items that make up this state.
1221
1351
  def initialize(tokens, items = [])
1222
1352
  @id = nil
1223
1353
  @items = items
@@ -1227,11 +1357,19 @@ module RLTK # :nodoc:
1227
1357
  # Compare one State to another. Two States are equal if they
1228
1358
  # have the same items or, if the items have been cleaned, if
1229
1359
  # the States have the same ID.
1360
+ #
1361
+ # @param [State] other Another State to compare to.
1362
+ #
1363
+ # @return [Boolean]
1230
1364
  def ==(other)
1231
1365
  if self.items and other.items then self.items == other.items else self.id == other.id end
1232
1366
  end
1233
1367
 
1234
1368
  # Add a Reduce action to the state.
1369
+ #
1370
+ # @param [Integer] production_id ID of production to add to this state.
1371
+ #
1372
+ # @return [void]
1235
1373
  def add_reduction(production_id)
1236
1374
  action = Reduce.new(production_id)
1237
1375
 
@@ -1239,19 +1377,24 @@ module RLTK # :nodoc:
1239
1377
  @actions.each { |k, v| if CFG::is_terminal?(k) and k != :ERROR then v << action end }
1240
1378
  end
1241
1379
 
1242
- # Add a new item to this state.
1380
+ # @param [CFG::Item] item Item to add to this state.
1243
1381
  def append(item)
1244
1382
  if item.is_a?(CFG::Item) and not @items.include?(item) then @items << item end
1245
1383
  end
1246
-
1247
1384
  alias :<< :append
1248
1385
 
1249
- # Clean this State by removing the list of Item objects.
1386
+ # Clean this State by removing the list of {CFG::Item} objects.
1387
+ #
1388
+ # @return [void]
1250
1389
  def clean
1251
1390
  @items = nil
1252
1391
  end
1253
1392
 
1254
- # Close this state using _productions_.
1393
+ # Close this state using *productions*.
1394
+ #
1395
+ # @param [Array<CFG::Production>] productions Productions used to close this state.
1396
+ #
1397
+ # @return [vod]
1255
1398
  def close(productions)
1256
1399
  self.each do |item|
1257
1400
  if (next_symbol = item.next_symbol) and CFG::is_nonterminal?(next_symbol)
@@ -1261,9 +1404,13 @@ module RLTK # :nodoc:
1261
1404
  end
1262
1405
 
1263
1406
  # Checks to see if there is a conflict in this state, given a
1264
- # input of _sym_. Returns :SR if a shift/reduce conflict is
1407
+ # input of *sym*. Returns :SR if a shift/reduce conflict is
1265
1408
  # detected and :RR if a reduce/reduce conflict is detected. If
1266
1409
  # no conflict is detected nil is returned.
1410
+ #
1411
+ # @param [Symbol] sym Symbol to check for conflicts on.
1412
+ #
1413
+ # @return [:SR, :RR, nil]
1267
1414
  def conflict_on?(sym)
1268
1415
 
1269
1416
  reductions = 0
@@ -1289,21 +1436,32 @@ module RLTK # :nodoc:
1289
1436
  end
1290
1437
 
1291
1438
  # Iterate over the state's items.
1439
+ #
1440
+ # @return [void]
1292
1441
  def each
1293
1442
  @items.each {|item| yield item}
1294
1443
  end
1295
1444
 
1296
- # Specify an Action to perform when the input token is _symbol_.
1445
+ # Specify an Action to perform when the input token is *symbol*.
1446
+ #
1447
+ # @param [Symbol] symbol Symbol to add action for.
1448
+ # @param [Action] action Action for symbol.
1449
+ #
1450
+ # @return [void]
1297
1451
  def on(symbol, action)
1298
1452
  if @actions.key?(symbol)
1299
1453
  @actions[symbol] << action
1300
1454
  else
1301
- raise ParserConstructionError, "Attempting to set action for token (#{symbol}) not seen in grammar definition."
1455
+ raise ParserConstructionException, "Attempting to set action for token (#{symbol}) not seen in grammar definition."
1302
1456
  end
1303
1457
  end
1304
1458
 
1305
1459
  # Returns that actions that should be taken when the input token
1306
- # is _symbol_.
1460
+ # is *symbol*.
1461
+ #
1462
+ # @param [Symbol] symbol Symbol we want the actions for.
1463
+ #
1464
+ # @return [Array<Action>] Actions that should be taken.
1307
1465
  def on?(symbol)
1308
1466
  @actions[symbol].clone
1309
1467
  end
@@ -1312,8 +1470,10 @@ module RLTK # :nodoc:
1312
1470
  # The Action class is used to indicate what action the parser should
1313
1471
  # take given a current state and input token.
1314
1472
  class Action
1473
+ # @return [Integer] ID of this action.
1315
1474
  attr_reader :id
1316
1475
 
1476
+ # @param [Integer] id ID of this action.
1317
1477
  def initialize(id = nil)
1318
1478
  @id = id
1319
1479
  end
@@ -1322,6 +1482,7 @@ module RLTK # :nodoc:
1322
1482
  # The Accept class indicates to the parser that it should accept the
1323
1483
  # current parse tree.
1324
1484
  class Accept < Action
1485
+ # @return [String] String representation of this action.
1325
1486
  def to_s
1326
1487
  "Accept"
1327
1488
  end
@@ -1330,6 +1491,7 @@ module RLTK # :nodoc:
1330
1491
  # The GoTo class indicates to the parser that it should goto the state
1331
1492
  # specified by GoTo.id.
1332
1493
  class GoTo < Action
1494
+ # @return [String] String representation of this action.
1333
1495
  def to_s
1334
1496
  "GoTo #{self.id}"
1335
1497
  end
@@ -1338,6 +1500,7 @@ module RLTK # :nodoc:
1338
1500
  # The Reduce class indicates to the parser that it should reduce the
1339
1501
  # input stack by the rule specified by Reduce.id.
1340
1502
  class Reduce < Action
1503
+ # @return [String] String representation of this action.
1341
1504
  def to_s
1342
1505
  "Reduce by Production #{self.id}"
1343
1506
  end
@@ -1346,6 +1509,7 @@ module RLTK # :nodoc:
1346
1509
  # The Shift class indicates to the parser that it should shift the
1347
1510
  # current input token.
1348
1511
  class Shift < Action
1512
+ # @return [String] String representation of this action.
1349
1513
  def to_s
1350
1514
  "Shift to State #{self.id}"
1351
1515
  end