rltk 2.2.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +12 -12
  3. data/README.md +458 -285
  4. data/Rakefile +99 -92
  5. data/lib/rltk/ast.rb +221 -126
  6. data/lib/rltk/cfg.rb +218 -239
  7. data/lib/rltk/cg/basic_block.rb +1 -1
  8. data/lib/rltk/cg/bindings.rb +9 -26
  9. data/lib/rltk/cg/builder.rb +40 -8
  10. data/lib/rltk/cg/context.rb +1 -1
  11. data/lib/rltk/cg/contractor.rb +51 -0
  12. data/lib/rltk/cg/execution_engine.rb +45 -8
  13. data/lib/rltk/cg/function.rb +12 -2
  14. data/lib/rltk/cg/generated_bindings.rb +2541 -575
  15. data/lib/rltk/cg/generic_value.rb +2 -2
  16. data/lib/rltk/cg/instruction.rb +104 -83
  17. data/lib/rltk/cg/llvm.rb +44 -3
  18. data/lib/rltk/cg/memory_buffer.rb +22 -5
  19. data/lib/rltk/cg/module.rb +85 -36
  20. data/lib/rltk/cg/old_generated_bindings.rb +6152 -0
  21. data/lib/rltk/cg/pass_manager.rb +87 -43
  22. data/lib/rltk/cg/support.rb +2 -4
  23. data/lib/rltk/cg/target.rb +158 -28
  24. data/lib/rltk/cg/triple.rb +8 -8
  25. data/lib/rltk/cg/type.rb +69 -25
  26. data/lib/rltk/cg/value.rb +107 -66
  27. data/lib/rltk/cg.rb +16 -17
  28. data/lib/rltk/lexer.rb +21 -11
  29. data/lib/rltk/lexers/calculator.rb +1 -1
  30. data/lib/rltk/lexers/ebnf.rb +8 -7
  31. data/lib/rltk/parser.rb +300 -247
  32. data/lib/rltk/parsers/infix_calc.rb +1 -1
  33. data/lib/rltk/parsers/postfix_calc.rb +2 -2
  34. data/lib/rltk/parsers/prefix_calc.rb +2 -2
  35. data/lib/rltk/token.rb +1 -2
  36. data/lib/rltk/version.rb +3 -3
  37. data/lib/rltk.rb +6 -6
  38. data/test/cg/tc_basic_block.rb +83 -0
  39. data/test/cg/tc_control_flow.rb +191 -0
  40. data/test/cg/tc_function.rb +54 -0
  41. data/test/cg/tc_generic_value.rb +33 -0
  42. data/test/cg/tc_instruction.rb +256 -0
  43. data/test/cg/tc_llvm.rb +25 -0
  44. data/test/cg/tc_math.rb +88 -0
  45. data/test/cg/tc_module.rb +89 -0
  46. data/test/cg/tc_transforms.rb +68 -0
  47. data/test/cg/tc_type.rb +69 -0
  48. data/test/cg/tc_value.rb +151 -0
  49. data/test/cg/ts_cg.rb +23 -0
  50. data/test/tc_ast.rb +105 -8
  51. data/test/tc_cfg.rb +63 -48
  52. data/test/tc_lexer.rb +84 -96
  53. data/test/tc_parser.rb +224 -52
  54. data/test/tc_token.rb +6 -6
  55. data/test/ts_rltk.rb +12 -15
  56. metadata +149 -75
  57. data/lib/rltk/cg/generated_extended_bindings.rb +0 -287
  58. data/lib/rltk/util/abstract_class.rb +0 -25
  59. data/lib/rltk/util/monkeys.rb +0 -129
data/lib/rltk/cfg.rb CHANGED
@@ -10,6 +10,9 @@
10
10
  # Requires #
11
11
  ############
12
12
 
13
+ # Standard Library
14
+ require 'set'
15
+
13
16
  # Ruby Language Toolkit
14
17
  require 'rltk/lexers/ebnf'
15
18
 
@@ -17,8 +20,7 @@ require 'rltk/lexers/ebnf'
17
20
  # Classes and Modules #
18
21
  #######################
19
22
 
20
- module RLTK # :nodoc:
21
-
23
+ module RLTK
22
24
  # An exception class that represents a problem with a context-free
23
25
  # grammar's definition.
24
26
  class GrammarError < StandardError; end
@@ -71,22 +73,22 @@ module RLTK # :nodoc:
71
73
  #
72
74
  # @param [Proc] callback A Proc object to be called when EBNF operators are expanded.
73
75
  def initialize(&callback)
74
- @curr_lhs = nil
75
- @callback = callback || Proc.new {}
76
- @lexer = Lexers::EBNF.new
77
- @production_counter = -1
78
- @start_symbol = nil
79
- @wrapper_symbol = nil
80
-
81
- @productions_id = Hash.new
82
- @productions_sym = Hash.new { |h, k| h[k] = [] }
83
- @production_buffer = Array.new
84
-
85
- @terms = Hash.new(false).update({:EOS => true})
86
- @nonterms = Hash.new(false)
87
-
88
- @firsts = Hash.new
89
- @follows = Hash.new { |h,k| h[k] = Array.new }
76
+ @curr_lhs = nil
77
+ @callback = callback || Proc.new {}
78
+ @lexer = Lexers::EBNF.new
79
+ @production_counter = -1
80
+ @start_symbol = nil
81
+ @wrapper_symbol = nil
82
+
83
+ @productions_id = Hash.new
84
+ @productions_sym = Hash.new { |h, k| h[k] = [] }
85
+ @production_buffer = Array.new
86
+
87
+ @terms = Set.new([:EOS])
88
+ @nonterms = Set.new
89
+
90
+ @firsts = Hash.new
91
+ @follows = Hash.new { |h,k| h[k] = Array.new }
90
92
  end
91
93
 
92
94
  # Adds *production* to the appropriate internal data structures.
@@ -100,13 +102,136 @@ module RLTK # :nodoc:
100
102
  production
101
103
  end
102
104
 
105
+ # Builds a production representing a (possibly empty) list of tokens.
106
+ # These tokens may optionally be separated by a provided token. This
107
+ # function is used to eliminate the EBNF * operator.
108
+ #
109
+ # @param [Symbol] name The name of the production to add
110
+ # @param [String, Symbol, Array<String>] list_elements Expression(s) that may appear in the list
111
+ # @param [Symbol, String] separator The list separator symbol or symbols
112
+ #
113
+ # @return [void]
114
+ def build_list_production(name, list_elements, separator = '')
115
+ # Add the items for the following productions:
116
+ #
117
+ # name: | name_prime
118
+
119
+ name_prime = "#{name}_prime".to_sym
120
+
121
+ # 1st Production
122
+ production, _ = self.production(name, '')
123
+ @callback.call(:elp, :empty, production)
124
+
125
+ # 2nd Production
126
+ production, _ = self.production(name, name_prime)
127
+ @callback.call(:elp, :nonempty, production)
128
+
129
+ # Add remaining productions via nonempty_list helper.
130
+ self.nonempty_list(name_prime, list_elements, separator)
131
+
132
+ name
133
+ end
134
+ alias :list :build_list_production
135
+
136
+ # Builds a production representing a non-empty list of tokens. These
137
+ # tokens may optionally be separated by a provided token. This
138
+ # function is used to eliminate the EBNF + operator.
139
+ #
140
+ # @param [Symbol] name The name of the production to add
141
+ # @param [String, Symbol, Array<String, Symbol>] list_elements Expression(s) that may appear in the list
142
+ # @param [Symbol, String] separator The list separator symbol or symbols
143
+ #
144
+ # @return [void]
145
+ def build_nonempty_list_production(name, list_elements, separator = '')
146
+ # Add the items for the following productions:
147
+ #
148
+ # If there is only one list element:
149
+ #
150
+ # name: list_element | name separator list_element
151
+ #
152
+ # else
153
+ #
154
+ # name: name_list_elements | name separator name_list_elements
155
+ #
156
+ # name_list_elements: #{list_elements.join('|')}
157
+
158
+ build_elements_productions = false
159
+
160
+ list_element_string =
161
+ if list_elements.is_a?(Array)
162
+ if list_elements.empty?
163
+ raise ArgumentError, 'Parameter list_elements must not be empty.'
164
+
165
+ elsif list_elements.length == 1
166
+ list_elements.first
167
+
168
+ else
169
+ build_elements_productions = true
170
+ "#{name}_list_elements"
171
+ end
172
+ else
173
+ list_elements
174
+ end
175
+
176
+ list_element_selected_string = list_element_string.to_s.split.map { |s| ".#{s}" }.join(' ')
177
+
178
+ # Single Element Production
179
+ production, _ = self.production(name, list_element_string)
180
+ @callback.call(:nelp, :single, production)
181
+
182
+ # Multiple Element Production
183
+ production, selections = self.production(name, ".#{name} #{separator} #{list_element_selected_string}")
184
+ @callback.call(:nelp, :multiple, production, selections)
185
+
186
+ if build_elements_productions
187
+ # List Element Productions
188
+ list_elements.each do |element|
189
+ production, _ = self.production(list_element_string, element)
190
+ @callback.call(:nelp, :elements, production)
191
+ end
192
+ end
193
+
194
+ name
195
+ end
196
+ alias :nonempty_list :build_nonempty_list_production
197
+
198
+ # Build a production for an optional symbol. This is used to
199
+ # eliminate the EBNF ? operator.
200
+ #
201
+ # @param [Symbol] name The name for the new production
202
+ # @param [Symbol] opt_symbol Symbol to expand
203
+ #
204
+ # @return [Symbol] The value of the name argument
205
+ def build_optional_production(name, opt_symbol)
206
+ if not @productions_sym.has_key?(name)
207
+ # Add the items for the following productions:
208
+ #
209
+ # name: | opt_symbol
210
+
211
+ # Empty production.
212
+ production = self.add_production(Production.new(self.next_id, name, []))
213
+ @callback.call(:optional, :empty, production)
214
+
215
+ # Nonempty production
216
+ production = self.add_production(Production.new(self.next_id, name, [opt_symbol]))
217
+ @callback.call(:optional, :nonempty, production)
218
+
219
+ # Add the new symbol to the list of nonterminals.
220
+ @nonterms << name
221
+ end
222
+
223
+ name
224
+ end
225
+
103
226
  # Sets the EBNF callback to *callback*.
104
227
  #
105
228
  # @param [Proc] callback A Proc object to be called when EBNF operators are expanded and list productions are added.
106
229
  #
107
230
  # @return [void]
108
231
  def callback(&callback)
109
- @callback = callback || Proc.new {}
232
+ @callback = callback if callback
233
+
234
+ nil
110
235
  end
111
236
 
112
237
  # This function MUST be called inside a CFG.production block. It will
@@ -114,90 +239,73 @@ module RLTK # :nodoc:
114
239
  # CFG.production call's argument. This is the function that is
115
240
  # responsible for removing EBNF symbols from the grammar.
116
241
  #
117
- # @param [String] expression The right-hand side of a CFG production.
242
+ # @param [String, Symbol] expression The right-hand side of a CFG production.
118
243
  #
119
- # @return [Production]
244
+ # @return [Array(Production, Array<Integer>)]
120
245
  def clause(expression)
121
246
  raise GrammarError, 'CFG#clause called outside of CFG#production block.' if not @curr_lhs
122
247
 
123
- lhs = @curr_lhs.to_sym
124
- rhs = Array.new
125
- tokens = @lexer.lex(expression)
248
+ lhs = @curr_lhs.to_sym
249
+ rhs = Array.new
250
+ tokens = @lexer.lex(expression.to_s)
251
+ selections = Array.new
126
252
 
127
253
  # Set this as the start symbol if there isn't one already
128
254
  # defined.
129
255
  @start_symbol ||= lhs
130
256
 
131
257
  # Remove EBNF tokens and replace them with new productions.
258
+ symbol_count = 0
132
259
  tokens.each_index do |i|
133
- ttype0 = tokens[i].type
134
- tvalue0 = tokens[i].value
260
+ ttype0 = tokens[i].type
261
+ tvalue0 = tokens[i].value
135
262
 
136
263
  if ttype0 == :TERM or ttype0 == :NONTERM
137
264
 
138
265
  # Add this symbol to the correct collection.
139
- (ttype0 == :TERM ? @terms : @nonterms)[tvalue0] = true
266
+ (ttype0 == :TERM ? @terms : @nonterms) << tvalue0
140
267
 
141
268
  if i + 1 < tokens.length
142
- ttype1 = tokens[i + 1].type
143
- tvalue1 = tokens[i + 1].value
269
+ ttype1 = tokens[i + 1].type
270
+ tvalue1 = tokens[i + 1].value
144
271
 
145
272
  rhs <<
146
273
  case ttype1
147
- when :'?' then self.get_question(tvalue0)
148
- when :* then self.get_star(tvalue0)
149
- when :+ then self.get_plus(tvalue0)
150
- else tvalue0
274
+ when :QUESTION then self.build_optional_production("#{tvalue0.downcase}_optional".to_sym, tvalue0)
275
+ when :STAR then self.build_list_production("#{tvalue0.downcase}_list".to_sym, tvalue0)
276
+ when :PLUS then self.build_nonempty_list_production("#{tvalue0.downcase}_nonempty_list".to_sym, tvalue0)
277
+ else tvalue0
151
278
  end
152
279
  else
153
280
  rhs << tvalue0
154
281
  end
282
+
283
+ symbol_count += 1
284
+
285
+ elsif ttype0 == :DOT
286
+ selections << symbol_count
155
287
  end
156
288
  end
157
289
 
158
290
  # Make the production.
159
- @production_buffer << (production = Production.new(self.next_id, lhs, rhs))
291
+ @production_buffer << [(production = Production.new(self.next_id, lhs, rhs)), selections]
160
292
 
161
293
  # Make sure the production symbol is collected.
162
- @nonterms[lhs] = true
294
+ @nonterms << lhs
163
295
 
164
296
  # Add the new production to our collections.
165
297
  self.add_production(production)
166
298
 
167
- return production
299
+ return [production, selections]
168
300
  end
169
301
 
170
- # This method adds the necessary productions for empty lists to the
171
- # grammar. These productions are named `symbol`, `symbol + '_prime'`
172
- # and `symbol + '_elements'`
173
- #
174
- # @param [Symbol] symbol The name of the production to add.
175
- # @param [Array<String>] list_elements An array of expressions that may appear in the list.
176
- # @param [Symbol] separator The list separator symbol.
302
+ # This function calculates the *first* set of a series of tokens. It
303
+ # uses the {CFG#first_set} helper function to find the first set of
304
+ # individual symbols.
177
305
  #
178
- # @return [void]
179
- def empty_list_production(symbol, list_elements, separator)
180
- # Add the items for the following productions:
181
- #
182
- # symbol: | symbol_prime
183
-
184
- prime = symbol.to_s + '_prime'
185
-
186
- # 1st Production
187
- production = self.production(symbol, '').first
188
- @callback.call(production, :elp, :first)
189
-
190
- # 2nd Production
191
- production = self.production(symbol, prime.to_s).first
192
- @callback.call(production, :elp, :second)
193
-
194
- self.nonempty_list(prime, list_elements, separator)
195
- end
196
- alias :empty_list :empty_list_production
197
-
198
- # @param [Symbol, Array<Symbol>] sentence Sentence to find the *first set* for.
306
+ # @param [Symbol, Array<Symbol>] sentence Sentence to find the *first set* for.
199
307
  #
200
- # @return [Array<Symbol>] The *first set* for the given sentence.
308
+ # @return [Array<Symbol>] The *first set* for the given sentence.
201
309
  def first_set(sentence)
202
310
  if sentence.is_a?(Symbol)
203
311
  first_set_prime(sentence)
@@ -217,19 +325,16 @@ module RLTK # :nodoc:
217
325
  end
218
326
 
219
327
  # This function is responsible for calculating the *first* set of
220
- # individual symbols. {CFG#first_set} is a wrapper around this
221
- # function to provide support for calculating the *first* set for
222
- # sentences.
328
+ # individual symbols.
223
329
  #
224
- # @param [Symbol] sym0 The symbol to find the *first set* of.
225
- # @param [Array<Symbol>] seen_lh_sides Previously seen LHS symbols.
330
+ # @param [Symbol] sym0 The symbol to find the *first set* of.
331
+ # @param [Array<Symbol>] seen_lh_sides Previously seen LHS symbols.
226
332
  #
227
333
  # @return [Array<Symbol>]
228
334
  def first_set_prime(sym0, seen_lh_sides = [])
229
335
  if self.symbols.include?(sym0)
230
336
  # Memoize the result for later.
231
337
  @firsts[sym0] ||=
232
-
233
338
  if CFG::is_terminal?(sym0)
234
339
  # If the symbol is a terminal, it is the only symbol in
235
340
  # its follow set.
@@ -238,7 +343,7 @@ module RLTK # :nodoc:
238
343
  set0 = []
239
344
 
240
345
  @productions_sym[sym0].each do |production|
241
- if production.rhs == []
346
+ if production.rhs.empty?
242
347
  # If this is an empty production we should
243
348
  # add the empty string to the First set.
244
349
  set0 << :'ɛ'
@@ -277,8 +382,8 @@ module RLTK # :nodoc:
277
382
  # used to avoid infinite recursion when mutually recursive rules are
278
383
  # encountered.
279
384
  #
280
- # @param [Symbol] sym0 The symbol to find the *follow set* for.
281
- # @param [Array<Symbol>] seen_lh_sides Previously seen LHS symbols.
385
+ # @param [Symbol] sym0 The symbol to find the *follow set* for.
386
+ # @param [Array<Symbol>] seen_lh_sides Previously seen LHS symbols.
282
387
  #
283
388
  # @return [Array<Symbol>]
284
389
  def follow_set(sym0, seen_lh_sides = [])
@@ -286,7 +391,7 @@ module RLTK # :nodoc:
286
391
  # Use the memoized set if possible.
287
392
  return @follows[sym0] if @follows.has_key?(sym0)
288
393
 
289
- if @nonterms[sym0]
394
+ if @nonterms.member? sym0
290
395
  set0 = []
291
396
 
292
397
  # Add EOS to the start symbol's follow set.
@@ -317,146 +422,14 @@ module RLTK # :nodoc:
317
422
  end
318
423
  end
319
424
 
320
- # Builds productions used to eliminate the + EBNF operator.
321
- #
322
- # @param [Symbol] symbol Symbol to expand.
323
- #
324
- # @return [Symbol]
325
- def get_plus(symbol)
326
- new_symbol = (symbol.to_s.downcase + '_plus').to_sym
327
-
328
- if not @productions_sym.has_key?(new_symbol)
329
- # Add the items for the following productions:
330
- #
331
- # token_plus: token | token token_plus
332
-
333
- # 1st production
334
- production = self.add_production(Production.new(self.next_id, new_symbol, [symbol]))
335
- @callback.call(production, :+, :first)
336
-
337
- # 2nd production
338
- production = self.add_production(Production.new(self.next_id, new_symbol, [new_symbol, symbol]))
339
- @callback.call(production, :+, :second)
340
-
341
- # Add the new symbol to the list of nonterminals.
342
- @nonterms[new_symbol] = true
343
- end
344
-
345
- return new_symbol
346
- end
347
-
348
- # Builds productions used to eliminate the ? EBNF operator.
349
- #
350
- # @param [Symbol] symbol Symbol to expand.
351
- #
352
- # @return [Symbol]
353
- def get_question(symbol)
354
- new_symbol = (symbol.to_s.downcase + '_question').to_sym
355
-
356
- if not @productions_sym.has_key?(new_symbol)
357
- # Add the items for the following productions:
358
- #
359
- # nonterm_question: | nonterm
360
-
361
- # 1st (empty) production.
362
- production = self.add_production(Production.new(self.next_id, new_symbol, []))
363
- @callback.call(production, :'?', :first)
364
-
365
- # 2nd production
366
- production = self.add_production(Production.new(self.next_id, new_symbol, [symbol]))
367
- @callback.call(production, :'?', :second)
368
-
369
- # Add the new symbol to the list of nonterminals.
370
- @nonterms[new_symbol] = true
371
- end
372
-
373
- return new_symbol
374
- end
375
-
376
- # Builds productions used to eliminate the * EBNF operator.
377
- #
378
- # @param [Symbol] symbol Symbol to expand.
379
- #
380
- # @return [Symbol]
381
- def get_star(symbol)
382
- new_symbol = (symbol.to_s.downcase + '_star').to_sym
383
-
384
- if not @productions_sym.has_key?(new_symbol)
385
- # Add the items for the following productions:
386
- #
387
- # token_star: | token token_star
388
-
389
- # 1st (empty) production
390
- production = self.add_production(Production.new(self.next_id, new_symbol, []))
391
- @callback.call(production, :*, :first)
392
-
393
- # 2nd production
394
- production = self.add_production(Production.new(self.next_id, new_symbol, [new_symbol, symbol]))
395
- @callback.call(production, :*, :second)
396
-
397
- # Add the new symbol to the list of nonterminals.
398
- @nonterms[new_symbol] = true
399
- end
400
-
401
- return new_symbol
402
- end
403
-
404
- # @return [Integer] ID for the next production to be defined.
425
+ # @return [Integer] ID for the next production to be defined.
405
426
  def next_id
406
427
  @production_counter += 1
407
428
  end
408
429
 
409
- # This method adds the necessary productions for non-empty lists to
410
- # the grammar. These productions are named `symbol` and
411
- # `symbol + '_elements'`
412
- #
413
- # @param [Symbol] symbol The name of the production to add.
414
- # @param [String, Symbol, Array<String>] list_elements Expression(s) that may appear in the list.
415
- # @param [Symbol] separator The list separator symbol.
416
- #
417
- # @return [void]
418
- def nonempty_list_production(symbol, list_elements, separator)
419
- # Add the items for the following productions:
420
- #
421
- # symbol_elements: list_elements.join('|')
422
- #
423
- # symbol: symbol_elements | symbol separator symbol_elements
424
-
425
- if list_elements.is_a?(String) or list_elements.is_a?(Symbol)
426
- list_elements = [list_elements.to_s]
427
-
428
- elsif list_elements.is_a?(Array)
429
- if list_elements.empty?
430
- raise ArgumentError, 'Parameter list_elements must not be empty.'
431
- else
432
- list_elements.map! { |el| el.to_s }
433
- end
434
-
435
- else
436
- raise ArgumentError, 'Parameter list_elements must be a String, Symbol, or array of Strings and Symbols.'
437
- end
438
-
439
- el_symbol = (symbol.to_s + '_elements').to_sym
440
-
441
- # 1st Production
442
- production = self.production(symbol, el_symbol.to_s).first
443
- @callback.call(production, :nelp, :first)
444
-
445
- # 2nd Production
446
- production = self.production(symbol, "#{symbol} #{separator} #{el_symbol}").first
447
- @callback.call(production, :nelp, :second)
448
-
449
- # 3rd Productions
450
- list_elements.each do |el|
451
- production = self.production(el_symbol, el).first
452
- @callback.call(production, :nelp, :third)
453
- end
454
- end
455
- alias :nonempty_list :nonempty_list_production
456
-
457
- # @return [Array<Symbol>] All terminal symbols used in the grammar's definition.
430
+ # @return [Set<Symbol>] All terminal symbols used in the grammar's definition.
458
431
  def nonterms
459
- @nonterms.keys
432
+ @nonterms.clone
460
433
  end
461
434
 
462
435
  # Builds a new production with the left-hand side value of *symbol*.
@@ -464,23 +437,29 @@ module RLTK # :nodoc:
464
437
  # production. If *expression* is nil then *block* is evaluated, and
465
438
  # expected to make one or more calls to {CFG#clause}.
466
439
  #
467
- # @param [Symbol] symbol The right-hand side of a production.
468
- # @param [String] expression The left-hand side of a production.
469
- # @param [Proc] block Optional block for defining production clauses.
440
+ # @param [Symbol] symbol The left-hand side of a production
441
+ # @param [String, Symbol] expression The right-hand side of a production
442
+ # @param [Proc] block Optional block for defining production clauses
470
443
  #
471
- # @return [Array<Production>]
444
+ # @return [Production, Array<Production>] A single production if called with an expression;
445
+ # an array of productions otherwise
472
446
  def production(symbol, expression = nil, &block)
473
447
  @production_buffer = Array.new
448
+
449
+ prev_lhs = @curr_lhs
474
450
  @curr_lhs = symbol
475
451
 
452
+ ret_val =
476
453
  if expression
477
454
  self.clause(expression)
478
455
  else
479
456
  self.instance_exec(&block)
457
+
458
+ @production_buffer.clone
480
459
  end
481
460
 
482
- @curr_lhs = nil
483
- return @production_buffer.clone
461
+ @curr_lhs = prev_lhs
462
+ return ret_val
484
463
  end
485
464
 
486
465
  # If *by* is :sym, returns a hash of the grammar's productions, using
@@ -488,7 +467,7 @@ module RLTK # :nodoc:
488
467
  # an array of productions is returned in the order of their
489
468
  # definition.
490
469
  #
491
- # @param [:sym, :id] by The way in which productions should be returned.
470
+ # @param [:sym, :id] by The way in which productions should be returned.
492
471
  #
493
472
  # @return [Array<Production>, Hash{Symbol => Production}]
494
473
  def productions(by = :sym)
@@ -503,7 +482,7 @@ module RLTK # :nodoc:
503
482
 
504
483
  # Sets the start symbol for this grammar.
505
484
  #
506
- # @param [Symbol] symbol The new start symbol.
485
+ # @param [Symbol] symbol The new start symbol.
507
486
  #
508
487
  # @return [Symbol]
509
488
  def start(symbol)
@@ -514,34 +493,34 @@ module RLTK # :nodoc:
514
493
  @start_symbol = symbol
515
494
  end
516
495
 
517
- # @return [Array<Symbol>] All symbols used in the grammar's definition.
496
+ # @return [Array<Symbol>] All symbols used in the grammar's definition.
518
497
  def symbols
519
498
  self.terms + self.nonterms
520
499
  end
521
500
 
522
- # @return [Array<Symbol>] All terminal symbols used in the grammar's definition.
501
+ # @return [Set<Symbol>] All terminal symbols used in the grammar's definition.
523
502
  def terms
524
- @terms.keys
503
+ @terms.clone
525
504
  end
526
505
 
527
506
  # Oddly enough, the Production class represents a production in a
528
507
  # context-free grammar.
529
508
  class Production
530
- # @return [Integer] ID of this production.
509
+ # @return [Integer] ID of this production.
531
510
  attr_reader :id
532
511
 
533
- # @return [Symbol] Left-hand side of this production.
512
+ # @return [Symbol] Left-hand side of this production.
534
513
  attr_reader :lhs
535
514
 
536
- # @return [Array<Symbol>] Right-hand side of this production.
515
+ # @return [Array<Symbol>] Right-hand side of this production.
537
516
  attr_reader :rhs
538
517
 
539
518
  # Instantiates a new Production object with the specified ID,
540
519
  # and left- and right-hand sides.
541
520
  #
542
- # @param [Integer] id ID number of this production.
543
- # @param [Symbol] lhs Left-hand side of the production.
544
- # @param [Array<Symbol>] rhs Right-hand side of the production.
521
+ # @param [Integer] id ID number of this production.
522
+ # @param [Symbol] lhs Left-hand side of the production.
523
+ # @param [Array<Symbol>] rhs Right-hand side of the production.
545
524
  def initialize(id, lhs, rhs)
546
525
  @id = id
547
526
  @lhs = lhs
@@ -551,35 +530,35 @@ module RLTK # :nodoc:
551
530
  # Comparese on production to another. Returns true only if the
552
531
  # left- and right- hand sides match.
553
532
  #
554
- # @param [Production] other Another production to compare to.
533
+ # @param [Production] other Another production to compare to.
555
534
  #
556
535
  # @return [Boolean]
557
536
  def ==(other)
558
537
  self.lhs == other.lhs and self.rhs == other.rhs
559
538
  end
560
539
 
561
- # @return [Production] A new copy of this production.
540
+ # @return [Production] A new copy of this production.
562
541
  def copy
563
542
  Production.new(@id, @lhs, @rhs.clone)
564
543
  end
565
544
 
566
- # @return [Symbol] The last terminal in the right-hand side of the production.
545
+ # @return [Symbol] The last terminal in the right-hand side of the production.
567
546
  def last_terminal
568
547
  @rhs.inject(nil) { |m, sym| if CFG::is_terminal?(sym) then sym else m end }
569
548
  end
570
549
 
571
- # @return [Item] An Item based on this production.
550
+ # @return [Item] An Item based on this production.
572
551
  def to_item
573
552
  Item.new(0, @id, @lhs, @rhs)
574
553
  end
575
554
 
576
555
  # Returns a string representation of this production.
577
556
  #
578
- # @param [Integer] padding The ammount of padding spaces to add to the beginning of the string.
557
+ # @param [Integer] padding The ammount of padding spaces to add to the beginning of the string.
579
558
  #
580
559
  # @return [String]
581
560
  def to_s(padding = 0)
582
- "#{format("%-#{padding}s", @lhs)} -> #{@rhs.map { |s| s.to_s }.join(' ')}"
561
+ "#{format("%-#{padding}s", @lhs)} -> #{@rhs.empty? ? 'ɛ' : @rhs.map { |s| s.to_s }.join(' ')}"
583
562
  end
584
563
  end
585
564
 
@@ -593,8 +572,8 @@ module RLTK # :nodoc:
593
572
  # arguments (*args*) should be as specified by
594
573
  # {Production#initialize}.
595
574
  #
596
- # @param [Integer] dot Location of the dot in this Item.
597
- # @param [Array<Object>] args (see {Production#initialize})
575
+ # @param [Integer] dot Location of the dot in this Item.
576
+ # @param [Array<Object>] args (see {Production#initialize})
598
577
  def initialize(dot, *args)
599
578
  super(*args)
600
579
 
@@ -604,7 +583,7 @@ module RLTK # :nodoc:
604
583
 
605
584
  # Compares two items.
606
585
  #
607
- # @param [Item] other Another item to compare to.
586
+ # @param [Item] other Another item to compare to.
608
587
  #
609
588
  # @return [Boolean]
610
589
  def ==(other)
@@ -628,21 +607,21 @@ module RLTK # :nodoc:
628
607
  @dot == @rhs.length
629
608
  end
630
609
 
631
- # @return [Item] A new copy of this item.
610
+ # @return [Item] A new copy of this item.
632
611
  def copy
633
612
  Item.new(@dot, @id, @lhs, @rhs.clone)
634
613
  end
635
614
 
636
615
  # Returns the symbol located after the dot.
637
616
  #
638
- # @return [Symbol] Symbol located after the dot (at the index indicated by the {#dot} attribute).
617
+ # @return [Symbol] Symbol located after the dot (at the index indicated by the {#dot} attribute).
639
618
  def next_symbol
640
619
  @rhs[@dot]
641
620
  end
642
621
 
643
622
  # Returns a string representation of this item.
644
623
  #
645
- # @param [Integer] padding The ammount of padding spaces to add to the beginning of the string.
624
+ # @param [Integer] padding The ammount of padding spaces to add to the beginning of the string.
646
625
  #
647
626
  # @return [String]
648
627
  def to_s(padding = 0)