rockit 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README +24 -160
  2. data/TODO +17 -17
  3. data/VERSION +1 -1
  4. data/doc/rockit_paper.pdf +0 -0
  5. data/lib/packrat/grammar.rb +139 -84
  6. data/rakefile +27 -9
  7. data/tests/acceptance/packrat/java/atest_java.rb +37 -0
  8. data/tests/acceptance/packrat/java/java.rb +136 -0
  9. data/tests/acceptance/packrat/java/t.rb +10 -0
  10. data/tests/acceptance/packrat/java/todo +10 -0
  11. data/tests/acceptance/packrat/java/xtc.lang.java/Java.rats +446 -0
  12. data/tests/acceptance/packrat/java/xtc.lang.java/JavaConstant.rats +111 -0
  13. data/tests/acceptance/packrat/java/xtc.lang.java/JavaCore.rats +508 -0
  14. data/tests/acceptance/packrat/java/xtc.lang.java/JavaIdentifier.rats +62 -0
  15. data/tests/acceptance/packrat/java/xtc.lang.java/JavaSymbol.rats +38 -0
  16. data/tests/acceptance/packrat/java/xtc.lang.java/JavaTree.rats +40 -0
  17. data/tests/acceptance/packrat/java/xtc.lang.java/JavaType.rats +61 -0
  18. data/tests/acceptance/packrat/java/xtc.lang.java/Spacing.rats +36 -0
  19. data/tests/acceptance/packrat/java/xtc.lang.java/Symbol.rats +77 -0
  20. data/tests/acceptance/packrat/minibasic/README +13 -0
  21. data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +151 -13
  22. data/tests/acceptance/packrat/minibasic/minibasic.rb +94 -76
  23. data/tests/acceptance/packrat/minibasic/mult3.basic +6 -0
  24. data/tests/acceptance/packrat/minibasic/sumeven.basic +19 -0
  25. data/tests/unit/packrat/test_ast.rb +116 -0
  26. data/tests/unit/packrat/test_interpreting_parser.rb +15 -55
  27. metadata +22 -59
  28. data/lib/rockit/prettyprint/box.rb +0 -60
  29. data/lib/rockit/prettyprint/renderer.rb +0 -41
  30. data/lib/rockit/prettyprint/text_renderer.rb +0 -47
  31. data/lib/rockit/tree/base.rb +0 -223
  32. data/lib/rockit/tree/enter_leave_visitor.rb +0 -12
  33. data/lib/rockit/tree/graphviz.rb +0 -69
  34. data/lib/rockit/tree/visitor.rb +0 -12
  35. data/lib/util/array_alternatives.rb +0 -20
  36. data/lib/util/enter_leave_visitor.rb +0 -69
  37. data/lib/util/graphviz_dot.rb +0 -182
  38. data/lib/util/string_location.rb +0 -42
  39. data/lib/util/visitor.rb +0 -49
  40. data/lib/util/visitor_combinators.rb +0 -14
  41. data/tests/acceptance/rockit/dparser/atest_any_operator.rb +0 -33
  42. data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +0 -30
  43. data/tests/acceptance/rockit/dparser/atest_list_operator.rb +0 -57
  44. data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +0 -60
  45. data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +0 -61
  46. data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +0 -55
  47. data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +0 -14
  48. data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +0 -20
  49. data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +0 -36
  50. data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +0 -34
  51. data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +0 -128
  52. data/tests/acceptance/rockit/dparser/calc_tests_common.rb +0 -103
  53. data/tests/unit/parse/utest_ebnf_grammar.rb +0 -50
  54. data/tests/unit/parse/utest_expand_grammar.rb +0 -23
  55. data/tests/unit/parse/utest_grammar.rb +0 -160
  56. data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +0 -41
  57. data/tests/unit/rockit/assembler/llvm/utest_module.rb +0 -19
  58. data/tests/unit/rockit/prettyprint/utest_box.rb +0 -44
  59. data/tests/unit/rockit/tree/utest_tree_base.rb +0 -301
  60. data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +0 -69
  61. data/tests/unit/rockit/tree/utest_tree_visitor.rb +0 -63
  62. data/tests/unit/rockit/utest_grammar.rb +0 -145
  63. data/tests/unit/rockit/utest_grammar_symbol.rb +0 -11
  64. data/tests/unit/rockit/utest_maybe_operator.rb +0 -12
  65. data/tests/unit/rockit/utest_regexp_terminal.rb +0 -45
  66. data/tests/unit/rockit/utest_repetition_operators.rb +0 -35
  67. data/tests/unit/rockit/utest_rule.rb +0 -23
  68. data/tests/unit/rockit/utest_string_terminal.rb +0 -40
  69. data/tests/unit/util/utest_array_alternatives.rb +0 -23
  70. data/tests/unit/util/utest_enter_leave_visitor.rb +0 -89
  71. data/tests/unit/util/utest_string_location.rb +0 -42
  72. data/tests/unit/util/utest_visitor.rb +0 -92
  73. data/tests/unit/util/utest_visitor_combinators.rb +0 -64
data/README CHANGED
@@ -1,172 +1,36 @@
1
- = Rockit -- Ruby Object-oriented Compiler construction toolKIT
1
+ This is a preview release of the upcoming Rockit 0.8.0. It has
2
+ *VERY LITTLE* in the form of documentation and examples so it is mainly for
3
+ *VERY INTERESTED* inidividuals who want to check out what is coming. You are
4
+ also encouraged to give feedback on the "pure-Ruby" way of specing grammars.
2
5
 
3
- This package contains Rockit an object-oriented compiler construction
4
- toolKIT in/for Ruby.
6
+ Lots of stuff that will be in 0.8.0 is not in this release:
5
7
 
6
- NOTE! This README is incomplete and will be updated for the 0.7 release!!
8
+ * Proper memoization la packrat)
9
+ * Optimization
10
+ * Error reporting
11
+ * Java grammar
12
+ * Ruby grammar
13
+ * many of the good stuff from older Rockit versions (tree pattern matching, fully automated AST generation etc)
14
+ * GPL only on things related to generation, MIT/BSD on everything needed for
15
+ using the generated parsers etc (as much as possible)
7
16
 
8
- Rockit has the following features:
17
+ so *BE WARNED*... ;)
9
18
 
10
- * Scannerless Generalized LR Parsing with a fast C backend
11
- * Means: Can parse any context-free grammar and is still pretty fast
12
- * Grammars are specified in Ruby so no special Grammar file format
13
- * Means: You can use your ordinary Ruby skills when writing grammars (=>
14
- shorter, more easily understandable, and powerful grammars)
15
- * The actions taken during parsing can be easily changed by specifying Handler
16
- objects.
17
- * Rockit can directly build the AST (Abstract Syntax Tree) for you
18
- * AST classes can be pattern matched
19
- * Rockit has a library for pretty-printing AST's
19
+ ----------------------------------------------------------
20
20
 
21
- Svn id: $Id: README 131 2004-09-03 12:11:29Z robert_feldt $
21
+ If you still want to check this out I suggest you start with the example in
22
22
 
23
- == Download
23
+ tests/acceptance/packrat/minibasic
24
24
 
25
- The latest version of rockit can be found at
25
+ which is a grammar and interpreter for a mini version of Basic.
26
26
 
27
- * http://www.pronovomundo.com/projects/rockit
27
+ You might also get some background by checking out the starts of a paper
28
+ describing Rockit (in file rockit_paper.pdf).
28
29
 
29
- Documentation can be found at
30
+ ----------------------------------------------------------
30
31
 
31
- * http://www.pronovomundo.com/projects/rockit/svn/doc
32
+ If you have comments I'd appreciate if you send them to me at:
32
33
 
33
- == Installation
34
+ robert.feldt@gmail.com
34
35
 
35
- === Normal Installation
36
-
37
- You can install rake with the following command.
38
-
39
- % rake install
40
-
41
- from the top of its distribution directory. For this to work you need to have
42
- rake installed and a C compiler (that Ruby knows of). If you don't have the
43
- latter you can install from prebuilt binaries, see below.
44
-
45
- === Installation from prebuilt binaries
46
-
47
- To simplify installation rockit comes with prebuilt extensions so that you
48
- can use it even if you have no C compiler installed. Rockit currently
49
- comes with prebuilt extensions for cygwin and mingw32. Since the mswin32
50
- platform uses the same format as the mingw32 platform you can use the
51
- mingw32 files with the Ruby one-click installer.
52
-
53
- To install from the prebuilt binaries for your platform and ruby version:
54
-
55
- % rake install_prebuilt
56
- % rake install_lib
57
-
58
- Be sure to test your installation and report any problems since these
59
- prebuilt installations have not had extensive testing.
60
-
61
- === Test the installation
62
-
63
- You can test the installed rockit with the following command:
64
-
65
- % rake test
66
-
67
- from the top of its distribution directory. For this to work you need to have
68
- rake installed.
69
-
70
- == Roadmap
71
-
72
- * If you want to see how to invoke rockit to generate your own parsers, read on.
73
- * If you want to see the format of a Rockit grammar file
74
- doc/rockitfile.rdoc[http://rockit.rubyforge.org/files/doc/rockitfile_rdoc.html].
75
- * If you want to see a glossary of terms, see
76
- doc/glossary.rdoc[http://rockit.rubyforge.org/files/doc/glossary_rdoc.html].
77
-
78
- == Simple Example
79
-
80
- Once installed, you can use rockit as follows:
81
-
82
- == Other Parser generators...
83
-
84
- There are a large number of parser generators available for Ruby.
85
- Here are links to other projects with similar (and not so similar) goals.
86
-
87
- * Racc
88
-
89
- == Credits
90
-
91
- [<b>asdsad</b>] For the .
92
-
93
- == License
94
-
95
- Rockit is available under the GPL2 license.
96
-
97
- :include: LICENSE
98
-
99
- == Support
100
-
101
- The Rockit homepage is http://.
102
-
103
- For other information, feel free to ask on the ruby-talk mailing list
104
- (which is mirrored to comp.lang.ruby) or contact
105
- mailto:robert.feldt@pronovomundo.com.
106
-
107
- ----
108
-
109
- = Usage
110
-
111
- Rockit is invoked from the command line using:
112
-
113
- % rockit <options> [targets...]
114
-
115
- Options are:
116
-
117
- [<tt>--help</tt> (-H)]
118
- Display some help text and exit.
119
-
120
- [<tt>--quiet</tt> (-q)]
121
- Do not echo Sys commands.
122
-
123
- [<tt>--trace</tt> (-t)]
124
- Turn on invoke/execute tracing.
125
-
126
- [<tt>--usage</tt> (-h)]
127
- Display a usage message and exit.
128
-
129
- [<tt>--verbose</tt> (-v)]
130
- Echo the Sys commands to standard output.
131
-
132
- [<tt>--version</tt> (-V)]
133
- Display the program version and exit.
134
-
135
- ---
136
-
137
- = Rockit grammar Format
138
-
139
- See doc/grammar.rdoc[http://www.pronovomundo.com/projects/rockit/doc/grammar_rdoc.html]
140
- for details on the Rockit grammar format.
141
-
142
- ---
143
-
144
- = Other stuff
145
-
146
- Author:: Robert Feldt <robert.feldt@pronovomundo.com>
147
- Address:: Robert Feldt c/o ProNovoMundo
148
- Brolyckan 1
149
- 433 69 S�vedalen
150
- Requires:: Ruby 1.8.2 or later (might work but not tested with earlier version)
151
- License:: Copyright (C) 2001-2004 by Robert Feldt
152
- Released under GNU General Public License 2. See the LICENSE file
153
- included in this distribution.
154
-
155
- == License and Warranty
156
-
157
- Rockit - Ruby Object-oriented Compiler construction toolKIT
158
- Copyright (C) 2001-2004 by Robert Feldt
159
-
160
- This program is free software; you can redistribute it and/or modify
161
- it under the terms of the GNU General Public License as published by
162
- the Free Software Foundation; either version 2 of the License, or
163
- (at your option) any later version.
164
-
165
- This program is distributed in the hope that it will be useful,
166
- but WITHOUT ANY WARRANTY; without even the implied warranty of
167
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
168
- GNU General Public License for more details.
169
-
170
- You should have received a copy of the GNU General Public License
171
- along with this program; if not, write to the Free Software
172
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36
+ Thank you for your interest!
data/TODO CHANGED
@@ -1,30 +1,30 @@
1
1
  ------------------------------------------------------------------------------
2
2
  New Interpretive Packrat Engine
3
3
  ------------------------------------------------------------------------------
4
- * Change ast so that the constant grammar elements need not be given
5
- when creating and ast instance. This should simplify the testing
6
- considerably.
4
+ * Simplify the grammar-in-ruby specing.
5
+ * Need not both rule and prod methods only rule.
6
+ * Short hand "r" instead of "rule" like in old code
7
+ * Specify grammars as subclasses to Grammar instead of in a block. This
8
+ makes constants live in the proper namespace and is cleaner and more
9
+ semantically closer to Ruby.
7
10
 
8
- * Add more atest_minibasic tests.
11
+ * While porting Java grammar:
12
+ * What happens if ast trees are only partially specified? Should it mix
13
+ sexprs with asts? Or should it create anonymous parse trees (ie the
14
+ class has no fixed name but there is a generic class on which you can
15
+ specify the name)?
9
16
 
10
- * Add tests of the MiniBasic interpreter.
17
+ * Separate things out into different logical files depending on purpose.
11
18
 
12
- * Fix the MiniBasic interpreter.
13
-
14
- * Add tests for running the MB interpreter from the command line.
15
-
16
- * Add ability to run MiniBasic interpreter from command line.
17
-
18
- * Start porting the next sample: Java grammar in xtc.lang
19
+ * Then we go on to parse Ruby.
19
20
 
20
- * When we can parse Java, we simplify Rockit so that only this new IPE is
21
- used and packed up in a gem.
21
+ * Then we release Rockit with MB and Java as a gem and rockit_ruby as a separate gem (with the ruby-related stuff). Possibly also ruby_parser.rb as a separate standalone file for people who just wants a parser (or generate multiple stand-alonoe files from rockit_ruby which includes different parts of the ruby-specific stuff (parser, pretty-printer, analyzer).
22
22
 
23
- * Start porting C grammar in xtc.
23
+ * Add the simple memoization (always memoize on non-transient prod level)
24
24
 
25
- * Then we go on to parse Ruby.
25
+ * Then we start adding the adaptive optimizations.
26
26
 
27
- * Then we release as a gem. Check if we have a RubyForge account for Rockit.
27
+ * Then we add the automatic creation of pretty-printers
28
28
 
29
29
  ------------------------------------------------------------------------------
30
30
  OLD
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.1
1
+ 0.7.2
Binary file
@@ -57,11 +57,12 @@ end
57
57
  # a special class for it is so that we can inspect it in a more natural way
58
58
  # (as a string instead of a Regexp).
59
59
  class Packrat::StringLiteral < Packrat::RegexpLiteral
60
+ attr_reader :string
60
61
  def initialize(str)
61
62
  super(Regexp.new(Regexp.escape(str)))
62
- @str = str
63
+ @string = str
63
64
  end
64
- def inspect; @str.inspect; end
65
+ def inspect; @string.inspect; end
65
66
  end
66
67
 
67
68
  class Packrat::RuleRef < Packrat::GrammarElement
@@ -99,20 +100,23 @@ end
99
100
  # symbol should be parsed.
100
101
  class Packrat::Production
101
102
  attr_accessor :grammar
102
- attr_reader :name, :rhs, :result_modifier
103
+ attr_reader :name, :rhs, :result_producer
103
104
  def initialize(name, rhs)
104
105
  @name, @rhs = name, rhs
105
- if Packrat::ResultModifier === rhs.last
106
- @result_modifier = @rhs.pop
106
+ if Packrat::ResultProducer === rhs.last
107
+ @result_producer = @rhs.pop
107
108
  else
108
- # Default modifier is to create a Sexpr with the production name
109
+ # Default producer is to create a Sexpr with the production name
109
110
  # as the head of the returned array.
110
- @result_modifier = Packrat::SexprModifier.new(@name)
111
+ @result_producer = Packrat::SexprProducer.new(@name)
111
112
  end
112
113
  @rhs.map! {|e| e.to_packrat_grammar_element}
113
114
  end
115
+ def finalize!
116
+ @result_producer.production = self
117
+ end
114
118
  def inspect(withLhs = true)
115
- rhs = @rhs.map {|e| e.inspect}.join(' ')
119
+ rhs = @rhs.map {|e| e.hidden ? nil : e.inspect}.compact.join(' ')
116
120
  withLhs ? "#{name.to_s} -> " + rhs : rhs
117
121
  end
118
122
  end
@@ -124,15 +128,16 @@ class Packrat::ErrorReporter < Packrat::GrammarElement
124
128
  end
125
129
  def parse(parser)
126
130
  res = @sub.parse(parser)
127
- if res == false
131
+ if false == res
128
132
  lputs "\t\t\t FAIL #{@sub.inspect}"
129
133
  puts ""
130
134
  else
131
- lputs " Match #{@sub.inspect}"
135
+ lputs " #{parser.pos}: Match #{@sub.inspect}"
132
136
  puts ""
133
137
  end
134
138
  res
135
139
  end
140
+ def inspect; @sub.inspect; end
136
141
  def method_missing(method, *args)
137
142
  @sub.send(method, *args)
138
143
  end
@@ -142,7 +147,7 @@ module Packrat::GrammarBuild
142
147
  attr_reader :start
143
148
  def start_symbol(name); @start = name; end
144
149
  def rules; @rules ||= (Hash.new {|h,k| h[k] = Packrat::Rule.new(k)}); end
145
- def rule(name, rhss)
150
+ def rule(name, *rhss)
146
151
  rhss.each {|rhs| prod(name, rhs)}
147
152
  end
148
153
  def prod(name, rhs)
@@ -160,20 +165,16 @@ module Packrat::GrammarBuild
160
165
  # Finalize the building of the grammar by conducting postprocessing.
161
166
  def finalize!
162
167
  postprocess_set_grammar_on_rules
163
- postprocess_create_ast_classes
168
+ each_prod {|p| p.finalize!}
164
169
  end
165
170
  def postprocess_set_grammar_on_rules
166
- rules.values.each {|r| r.grammar = self}
171
+ each_prod {|r| r.grammar = self}
167
172
  end
168
- def postprocess_create_ast_classes
169
- each_prod do |p|
170
- if Packrat::ASTBuilder === p.result_modifier
171
- ast_class(p.result_modifier.name, p)
172
- end
173
- end
173
+ def each_rule
174
+ rules.values.each {|r| yield(r)}
174
175
  end
175
176
  def each_prod
176
- rules.values.each {|r| r.prods.each {|p| yield(p)}}
177
+ each_rule {|r| r.prods.each {|p| yield(p)}}
177
178
  end
178
179
  end
179
180
 
@@ -225,8 +226,7 @@ class Packrat::Maybe < Packrat::GrammarElement
225
226
  end
226
227
  def parse(parser)
227
228
  res = @sub.parse(parser)
228
- return res if res
229
- return nil
229
+ false == res ? nil : res
230
230
  end
231
231
  def inspect
232
232
  "(#{@sub.inspect})?"
@@ -237,39 +237,49 @@ module Packrat::GrammarBuild
237
237
  def maybe(element); Packrat::Maybe.new(element); end
238
238
  end
239
239
 
240
- # The last element of a prod can be a result modifier that modifies
241
- # the result to be returned by the prod when parsing.
242
- class Packrat::ResultModifier
240
+ # The last element of a prod can be a result producer that produces
241
+ # the result to be returned by the prod in case of a successfull parse.
242
+ class Packrat::ResultProducer
243
+ # Before any results are produced we need to know the prod we are in
244
+ def production=(prod); @prod = prod; end
245
+
246
+ # A ResultProducer returns a result which it then updates. This is needed
247
+ # since multiple results can be in production at the same time.
248
+ def new_result; end
249
+ def update_result(res, subres, elem, index); end
250
+ def finalize_result(res); res; end
243
251
  end
244
252
 
245
253
  # Create a Sexpr based on the name of the matched production and the
246
254
  # result-array.
247
- class Packrat::SexprModifier < Packrat::ResultModifier
255
+ class Packrat::SexprProducer < Packrat::ResultProducer
248
256
  def initialize(name)
249
257
  @name = name
250
258
  end
251
- def modify_result(prod, result)
252
- # Add the production name in the front
253
- result.unshift @name
254
- result
255
- end
259
+ def new_result; [@name]; end
260
+ def update_result(res, subres, elem, index, nhi); res << subres; end
256
261
  end
257
262
 
258
263
  # Lift one of the sub-results as the result from parsing a production.
259
- class Packrat::LiftModifier < Packrat::ResultModifier
264
+ # Optionally a block can be given. If so the block will get called with
265
+ # the lifted result and can modify it.
266
+ class Packrat::LiftOneResultProducer < Packrat::ResultProducer
260
267
  def initialize(valueIndex, &block)
261
268
  @value_index = valueIndex
262
269
  @block = block
263
270
  end
264
- def modify_result(prod, result)
265
- extracted_result = result[@value_index]
266
- @block? @block.call(extracted_result) : extracted_result
271
+ def new_result; nil; end
272
+ def update_result(res, subres, elem, index, nonhiddenIndex)
273
+ index == @value_index ? subres : res
274
+ end
275
+ def finalize_result(res)
276
+ @block ? @block.call(res) : res
267
277
  end
268
278
  end
269
279
 
270
280
  module Packrat::GrammarBuild
271
- def sexpr(name); Packrat::SexprModifier.new(name); end
272
- def lift(index, &b); Packrat::LiftModifier.new(index, &b); end
281
+ def sexpr(name); Packrat::SexprProducer.new(name); end
282
+ def lift(index, &b); Packrat::LiftOneResultProducer.new(index, &b); end
273
283
  end
274
284
 
275
285
  module Packrat::GrammarBuild
@@ -282,7 +292,7 @@ module Packrat::GrammarBuild
282
292
  Packrat::RegexpLiteral.new(Regexp.new(re_string))
283
293
  else
284
294
  name = internal_rule_name()
285
- rule(name, subs.map {|s| [s, lift(0)]})
295
+ rule(name, *subs.map {|s| [s, lift(0)]})
286
296
  Packrat::RuleRef.new(name)
287
297
  end
288
298
  end
@@ -308,30 +318,40 @@ module Packrat::GrammarBuild
308
318
  def eos(); hidden(Packrat::EOS.new); end
309
319
  end
310
320
 
311
- class Packrat::ASTBuilder < Packrat::ResultModifier
321
+ # Build AST tree as result of parsing a Production.
322
+ class Packrat::ASTBuilder < Packrat::ResultProducer
312
323
  attr_reader :name
313
- def initialize(nodeName)
314
- @name = nodeName
324
+ def initialize(nodeName, nameMap = {})
325
+ @name, @name_map = nodeName, nameMap
315
326
  end
316
- def modify_result(prod, result)
317
- astklass = prod.grammar.ast_class(@name, prod)
318
- astklass.new(*result)
327
+ def production=(prod)
328
+ super
329
+ @ast_class = prod.grammar.ast_class(@name, prod, @name_map)
330
+ end
331
+
332
+ def new_result; Array.new; end
333
+ def update_result(res, subres, elem, index, nhIndex)
334
+ res << subres unless @ast_class.constant_elem_at?(nhIndex)
335
+ res
336
+ end
337
+ def finalize_result(res)
338
+ @ast_class.new(res, {:only_nonconstant => true})
319
339
  end
320
340
  end
321
341
 
322
342
  module Packrat::GrammarBuild
323
- def ast(name)
324
- Packrat::ASTBuilder.new(name)
343
+ def ast(name, options = {})
344
+ Packrat::ASTBuilder.new(name, options)
325
345
  end
326
346
 
327
347
  # Return the ast class with the given <nodeName> for the given <production>.
328
348
  # If not previously created we create it and add it to the Tree module.
329
- def ast_class(name, prod)
349
+ def ast_class(name, prod, nameMap)
330
350
  acn = ast_class_name(name)
331
351
  begin
332
352
  const_get("ASTs").const_get(acn)
333
353
  rescue
334
- const_get("ASTs").const_set(acn, make_ast_class(acn, prod))
354
+ const_get("ASTs").const_set(acn, make_ast_class(acn, prod, nameMap))
335
355
  end
336
356
  end
337
357
 
@@ -340,8 +360,8 @@ module Packrat::GrammarBuild
340
360
  s[0,1].upcase + s[1..-1]
341
361
  end
342
362
 
343
- def make_ast_class(klassName, production)
344
- Packrat::AST.new_subclass(klassName, production)
363
+ def make_ast_class(klassName, production, nameMap)
364
+ Packrat::AST.new_subclass(klassName, production, nameMap)
345
365
  end
346
366
  end
347
367
 
@@ -349,9 +369,13 @@ end
349
369
  class Packrat::AST
350
370
  class <<self
351
371
  attr_accessor :sig
352
- def new_subclass(name, production)
372
+ # Create a new AST subclass. The <nameMap> hash can specify names
373
+ # for certain element indices (such explicitly specified names
374
+ # will override the default naming scheme which is to use a downcase
375
+ # version of the production name).
376
+ def new_subclass(name, production, nameMap = {})
353
377
  klass = Class.new(self)
354
- klass.sig = extract_sig(production)
378
+ klass.sig = extract_sig(production, nameMap)
355
379
  # Add accessor methods for all symbols in the sig
356
380
  num_strings = 0
357
381
  klass.sig.each_with_index do |sn, i|
@@ -374,32 +398,59 @@ class Packrat::AST
374
398
  # Return a sig for the given <production>. The sig has strings in the
375
399
  # positions where the production rhs has a String or StringLiteral,
376
400
  # has symbols in the positions where a rhs element refer to another
377
- # production, and has nil in other positions.
378
- def extract_sig(production)
379
- production.rhs.map do |e|
380
- case e
381
- when String
382
- e
383
- when Packrat::StringLiteral
384
- e.inspect # gives us the string itself
385
- when Packrat::RuleRef
386
- sub_element_name(e.rule_name)
387
- else
388
- nil # Expand this so that names are lifted out of Maybe, and "s" is added when plus and mult etc
401
+ # production, and has nil in other positions. The <nameMap> can contain
402
+ # explicit names for certing indices (indices as key and name as symbol
403
+ # value).
404
+ def extract_sig(production, nameMap = {})
405
+ sig = []
406
+ production.rhs.each_with_index do |e, i|
407
+ unless e.hidden
408
+ case e
409
+ when String
410
+ sig << e
411
+ when Packrat::StringLiteral
412
+ sig << e.string
413
+ when Packrat::RuleRef
414
+ sig << sub_element_name(e.rule_name)
415
+ else
416
+ sig << nil # Expand this so that names are lifted out of Maybe, and "s" is added when plus and mult etc
417
+ end
389
418
  end
390
419
  end
420
+ number_multioccurences(sig).map {|n| nameMap[n] || n}
391
421
  end
392
-
422
+
423
+ def number_multioccurences(sig)
424
+ num_sigs = sig.inject(Hash.new(0)) {|h, s| h[s] += 1 if Symbol === s; h}
425
+ counters = Hash.new(0)
426
+ sig.map do |s|
427
+ (num_sigs[s] > 1) ? (s.to_s + (counters[s] += 1).to_s).intern : s
428
+ end
429
+ end
430
+
393
431
  def sub_element_name(name)
394
432
  parts = name.to_s.split(/([A-Z][a-z0-9]*)/).select {|e| e.length > 0}
395
433
  parts.map {|p| p.downcase}.join("_").intern
396
434
  end
397
435
 
398
- def [](*args); new(*args); end
436
+ def constant_elem_at?(index)
437
+ self.sig[index].kind_of?(String)
438
+ end
439
+
440
+ def [](*args); new(args); end
399
441
  end
400
-
401
- def initialize(*children)
402
- @children = children
442
+
443
+ DefaultOptions = {:only_nonconstant => true}
444
+
445
+ def initialize(children, options = {})
446
+ options = DefaultOptions.clone.update(options)
447
+ if options[:only_nonconstant]
448
+ @children = self.class.sig.map do |n|
449
+ n.kind_of?(String) ? n : children.shift
450
+ end
451
+ else
452
+ @children = children
453
+ end
403
454
  end
404
455
  attr_reader :children
405
456
 
@@ -408,7 +459,7 @@ class Packrat::AST
408
459
  def ==(other)
409
460
  self.class == other.class && @children == other.children
410
461
  end
411
-
462
+
412
463
  def inspect
413
464
  self.class.inspect.split("::").last + "[" +
414
465
  @children.map {|c| c.inspect}.join(", ") + "]"
@@ -423,10 +474,10 @@ class Packrat::InterpretingParser
423
474
  klass.grammar = grammar
424
475
  klass
425
476
  end
426
- def parse_string(str)
477
+ def parse_string(str, startSymbol = nil)
427
478
  # We always add a whitespace since StringScanner cannot match /\s*/
428
479
  # (typically used as whitespace) at EOS
429
- new(str + " ").parse_string
480
+ new(str + " ").parse_string(startSymbol)
430
481
  end
431
482
  end
432
483
 
@@ -438,8 +489,9 @@ class Packrat::InterpretingParser
438
489
  @grammar = self.class.grammar
439
490
  end
440
491
 
441
- def parse_string
442
- @grammar.start_rule.parse(self)
492
+ def parse_string(startSymbol = nil)
493
+ startSymbol ||= @grammar.start
494
+ @grammar[startSymbol].parse(self)
443
495
  end
444
496
 
445
497
  # Get and Set current position in string.
@@ -484,25 +536,27 @@ end
484
536
 
485
537
  class Packrat::Production
486
538
  def parse(parser)
487
- result = []
488
- @rhs.each do |e|
489
- res = e.parse(parser)
490
- if res == false
491
- return false
492
- else
493
- result << res unless e.hidden
539
+ res = @result_producer.new_result
540
+ nonhidden_index = 0
541
+ @rhs.each_with_index do |e, i|
542
+ subres = e.parse(parser)
543
+ return false if false == subres
544
+ unless e.hidden
545
+ res = @result_producer.update_result(res, subres, e,
546
+ i, nonhidden_index)
547
+ nonhidden_index += 1
494
548
  end
495
549
  end
496
- return @result_modifier.modify_result(self, result)
550
+ return @result_producer.finalize_result(res)
497
551
  end
498
552
  end
499
553
 
500
554
  class Packrat::Rule
501
555
  def parse(parser)
556
+ oldpos = parser.pos
502
557
  prods.each do |prod|
503
- oldpos = parser.pos
504
558
  res = prod.parse(parser)
505
- return res if res
559
+ return res unless false == res
506
560
  parser.pos = oldpos
507
561
  end
508
562
  return false
@@ -519,6 +573,7 @@ class Packrat::Repeat
519
573
  def parse(parser)
520
574
  result_list = []
521
575
  oldpos = parser.pos
576
+ # XXX: Should we take only amx number of results here if max != false?
522
577
  while (res = @sub.parse(parser))
523
578
  result_list << res
524
579
  end