rockit 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +24 -160
- data/TODO +17 -17
- data/VERSION +1 -1
- data/doc/rockit_paper.pdf +0 -0
- data/lib/packrat/grammar.rb +139 -84
- data/rakefile +27 -9
- data/tests/acceptance/packrat/java/atest_java.rb +37 -0
- data/tests/acceptance/packrat/java/java.rb +136 -0
- data/tests/acceptance/packrat/java/t.rb +10 -0
- data/tests/acceptance/packrat/java/todo +10 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/Java.rats +446 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaConstant.rats +111 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaCore.rats +508 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaIdentifier.rats +62 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaSymbol.rats +38 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaTree.rats +40 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/JavaType.rats +61 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/Spacing.rats +36 -0
- data/tests/acceptance/packrat/java/xtc.lang.java/Symbol.rats +77 -0
- data/tests/acceptance/packrat/minibasic/README +13 -0
- data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +151 -13
- data/tests/acceptance/packrat/minibasic/minibasic.rb +94 -76
- data/tests/acceptance/packrat/minibasic/mult3.basic +6 -0
- data/tests/acceptance/packrat/minibasic/sumeven.basic +19 -0
- data/tests/unit/packrat/test_ast.rb +116 -0
- data/tests/unit/packrat/test_interpreting_parser.rb +15 -55
- metadata +22 -59
- data/lib/rockit/prettyprint/box.rb +0 -60
- data/lib/rockit/prettyprint/renderer.rb +0 -41
- data/lib/rockit/prettyprint/text_renderer.rb +0 -47
- data/lib/rockit/tree/base.rb +0 -223
- data/lib/rockit/tree/enter_leave_visitor.rb +0 -12
- data/lib/rockit/tree/graphviz.rb +0 -69
- data/lib/rockit/tree/visitor.rb +0 -12
- data/lib/util/array_alternatives.rb +0 -20
- data/lib/util/enter_leave_visitor.rb +0 -69
- data/lib/util/graphviz_dot.rb +0 -182
- data/lib/util/string_location.rb +0 -42
- data/lib/util/visitor.rb +0 -49
- data/lib/util/visitor_combinators.rb +0 -14
- data/tests/acceptance/rockit/dparser/atest_any_operator.rb +0 -33
- data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +0 -30
- data/tests/acceptance/rockit/dparser/atest_list_operator.rb +0 -57
- data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +0 -60
- data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +0 -61
- data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +0 -55
- data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +0 -14
- data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +0 -20
- data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +0 -36
- data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +0 -34
- data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +0 -128
- data/tests/acceptance/rockit/dparser/calc_tests_common.rb +0 -103
- data/tests/unit/parse/utest_ebnf_grammar.rb +0 -50
- data/tests/unit/parse/utest_expand_grammar.rb +0 -23
- data/tests/unit/parse/utest_grammar.rb +0 -160
- data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +0 -41
- data/tests/unit/rockit/assembler/llvm/utest_module.rb +0 -19
- data/tests/unit/rockit/prettyprint/utest_box.rb +0 -44
- data/tests/unit/rockit/tree/utest_tree_base.rb +0 -301
- data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +0 -69
- data/tests/unit/rockit/tree/utest_tree_visitor.rb +0 -63
- data/tests/unit/rockit/utest_grammar.rb +0 -145
- data/tests/unit/rockit/utest_grammar_symbol.rb +0 -11
- data/tests/unit/rockit/utest_maybe_operator.rb +0 -12
- data/tests/unit/rockit/utest_regexp_terminal.rb +0 -45
- data/tests/unit/rockit/utest_repetition_operators.rb +0 -35
- data/tests/unit/rockit/utest_rule.rb +0 -23
- data/tests/unit/rockit/utest_string_terminal.rb +0 -40
- data/tests/unit/util/utest_array_alternatives.rb +0 -23
- data/tests/unit/util/utest_enter_leave_visitor.rb +0 -89
- data/tests/unit/util/utest_string_location.rb +0 -42
- data/tests/unit/util/utest_visitor.rb +0 -92
- data/tests/unit/util/utest_visitor_combinators.rb +0 -64
data/README
CHANGED
@@ -1,172 +1,36 @@
|
|
1
|
-
|
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
|
-
|
4
|
-
toolKIT in/for Ruby.
|
6
|
+
Lots of stuff that will be in 0.8.0 is not in this release:
|
5
7
|
|
6
|
-
|
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
|
-
|
17
|
+
so *BE WARNED*... ;)
|
9
18
|
|
10
|
-
|
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
|
-
|
21
|
+
If you still want to check this out I suggest you start with the example in
|
22
22
|
|
23
|
-
|
23
|
+
tests/acceptance/packrat/minibasic
|
24
24
|
|
25
|
-
|
25
|
+
which is a grammar and interpreter for a mini version of Basic.
|
26
26
|
|
27
|
-
|
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
|
-
|
30
|
+
----------------------------------------------------------
|
30
31
|
|
31
|
-
|
32
|
+
If you have comments I'd appreciate if you send them to me at:
|
32
33
|
|
33
|
-
|
34
|
+
robert.feldt@gmail.com
|
34
35
|
|
35
|
-
|
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
|
-
*
|
5
|
-
|
6
|
-
|
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
|
-
*
|
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
|
-
*
|
17
|
+
* Separate things out into different logical files depending on purpose.
|
11
18
|
|
12
|
-
*
|
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
|
-
*
|
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
|
-
*
|
23
|
+
* Add the simple memoization (always memoize on non-transient prod level)
|
24
24
|
|
25
|
-
* Then we
|
25
|
+
* Then we start adding the adaptive optimizations.
|
26
26
|
|
27
|
-
* Then we
|
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
|
+
0.7.2
|
Binary file
|
data/lib/packrat/grammar.rb
CHANGED
@@ -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
|
-
@
|
63
|
+
@string = str
|
63
64
|
end
|
64
|
-
def inspect; @
|
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, :
|
103
|
+
attr_reader :name, :rhs, :result_producer
|
103
104
|
def initialize(name, rhs)
|
104
105
|
@name, @rhs = name, rhs
|
105
|
-
if Packrat::
|
106
|
-
@
|
106
|
+
if Packrat::ResultProducer === rhs.last
|
107
|
+
@result_producer = @rhs.pop
|
107
108
|
else
|
108
|
-
# Default
|
109
|
+
# Default producer is to create a Sexpr with the production name
|
109
110
|
# as the head of the returned array.
|
110
|
-
@
|
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
|
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
|
-
|
168
|
+
each_prod {|p| p.finalize!}
|
164
169
|
end
|
165
170
|
def postprocess_set_grammar_on_rules
|
166
|
-
|
171
|
+
each_prod {|r| r.grammar = self}
|
167
172
|
end
|
168
|
-
def
|
169
|
-
|
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
|
-
|
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
|
-
|
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
|
241
|
-
# the result to be returned by the prod
|
242
|
-
class Packrat::
|
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::
|
255
|
+
class Packrat::SexprProducer < Packrat::ResultProducer
|
248
256
|
def initialize(name)
|
249
257
|
@name = name
|
250
258
|
end
|
251
|
-
def
|
252
|
-
|
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
|
-
|
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
|
265
|
-
|
266
|
-
@
|
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::
|
272
|
-
def lift(index, &b); Packrat::
|
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
|
-
|
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
|
317
|
-
|
318
|
-
|
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
|
-
|
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
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
e
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
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
|
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
|
-
|
402
|
-
|
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.
|
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
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
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 @
|
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
|
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
|