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