rockit 0.7.1 → 0.7.2

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 (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