aniero-tire_swing 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +6 -0
- data/README.txt +8 -4
- data/examples/simple_assignment.rb +6 -9
- data/lib/tire_swing.rb +1 -1
- data/lib/tire_swing/error.rb +10 -0
- data/lib/tire_swing/node.rb +25 -6
- data/lib/tire_swing/node_creator.rb +13 -5
- data/lib/tire_swing/node_definition.rb +111 -17
- data/lib/tire_swing/parser_extension.rb +33 -0
- data/spec/error_spec.rb +21 -0
- data/spec/grammars/assignments.rb +3 -0
- data/spec/grammars/assignments.treetop +1 -0
- data/spec/grammars/lists.rb +36 -0
- data/spec/grammars/magic.rb +7 -19
- data/spec/integration/lists_spec.rb +30 -0
- data/spec/integration/magic_spec.rb +9 -9
- data/spec/node_creator_spec.rb +8 -3
- data/spec/node_definition_spec.rb +82 -8
- data/spec/node_spec.rb +37 -26
- data/spec/parser_extension_spec.rb +85 -0
- data/spec/spec_helper.rb +2 -2
- data/tire_swing.gemspec +3 -3
- metadata +8 -2
data/Manifest.txt
CHANGED
@@ -4,25 +4,31 @@ README.txt
|
|
4
4
|
Rakefile
|
5
5
|
examples/simple_assignment.rb
|
6
6
|
lib/tire_swing.rb
|
7
|
+
lib/tire_swing/error.rb
|
7
8
|
lib/tire_swing/metaid.rb
|
8
9
|
lib/tire_swing/node.rb
|
9
10
|
lib/tire_swing/node_creator.rb
|
10
11
|
lib/tire_swing/node_definition.rb
|
12
|
+
lib/tire_swing/parser_extension.rb
|
11
13
|
lib/tire_swing/visitor.rb
|
12
14
|
lib/tire_swing/visitor_definition.rb
|
15
|
+
spec/error_spec.rb
|
13
16
|
spec/fixtures/assignments.txt
|
14
17
|
spec/fixtures/ey00-s00348.xen
|
15
18
|
spec/grammars/assignments.rb
|
16
19
|
spec/grammars/assignments.treetop
|
17
20
|
spec/grammars/dot_xen.rb
|
18
21
|
spec/grammars/dot_xen.treetop
|
22
|
+
spec/grammars/lists.rb
|
19
23
|
spec/grammars/magic.rb
|
20
24
|
spec/integration/assignments_spec.rb
|
21
25
|
spec/integration/dot_xen_spec.rb
|
26
|
+
spec/integration/lists_spec.rb
|
22
27
|
spec/integration/magic_spec.rb
|
23
28
|
spec/node_creator_spec.rb
|
24
29
|
spec/node_definition_spec.rb
|
25
30
|
spec/node_spec.rb
|
31
|
+
spec/parser_extension_spec.rb
|
26
32
|
spec/spec_helper.rb
|
27
33
|
spec/tire_swing_spec.rb
|
28
34
|
spec/visitor_definition_spec.rb
|
data/README.txt
CHANGED
@@ -17,10 +17,10 @@ Given a treetop grammar:
|
|
17
17
|
|
18
18
|
grammar SimpleAssignment
|
19
19
|
rule assignment
|
20
|
-
lhs:variable space* "=" space* rhs:variable <
|
20
|
+
lhs:variable space* "=" space* rhs:variable <node(:assignment)>
|
21
21
|
end
|
22
22
|
rule variable
|
23
|
-
[a-z]+ <
|
23
|
+
[a-z]+ <node(:variable)>
|
24
24
|
end
|
25
25
|
rule space
|
26
26
|
[ ]+
|
@@ -36,10 +36,14 @@ You can use TireSwing to define nodes for the grammar:
|
|
36
36
|
node :variable, :value => :text_value
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
And use TireSwing to extend the Treetop-provided parser with a helper method or two:
|
40
|
+
|
41
|
+
TireSwing.parses_grammar(SimpleAssignment)
|
42
|
+
|
43
|
+
When you parse the grammar using the helper, it will return an AST using the nodes you defined, auto-building everything
|
40
44
|
for you:
|
41
45
|
|
42
|
-
ast =
|
46
|
+
ast = SimpleAssignment.ast("foo = bar")
|
43
47
|
|
44
48
|
ast.class #=> SimpleAssignment::Assignment
|
45
49
|
ast.lhs.class #=> SimpleAssignment::Variable
|
@@ -4,10 +4,10 @@ Treetop.load_from_string <<-GRAMMAR
|
|
4
4
|
module SimpleAssignment
|
5
5
|
grammar Grammar
|
6
6
|
rule assignment
|
7
|
-
lhs:variable space* "=" space* rhs:variable <
|
7
|
+
lhs:variable space* "=" space* rhs:variable <node(:assignment)>
|
8
8
|
end
|
9
9
|
rule variable
|
10
|
-
[a-z]+ <
|
10
|
+
[a-z]+ <node(:variable)>
|
11
11
|
end
|
12
12
|
rule space
|
13
13
|
[ ]+
|
@@ -37,17 +37,14 @@ module SimpleAssignment
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
new.parse(io).build
|
44
|
-
end
|
45
|
-
end
|
40
|
+
# Set up the parser helper, pointing to the grammar and the AST
|
41
|
+
# This defines GrammarParser.ast on the Treetop-provided GrammarParser.
|
42
|
+
TireSwing.parses_grammar(Grammar, AST)
|
46
43
|
|
47
44
|
end
|
48
45
|
|
49
46
|
require "pp"
|
50
|
-
ast = SimpleAssignment::
|
47
|
+
ast = SimpleAssignment::GrammarParser.ast("foo=bar")
|
51
48
|
puts "----- AST -----"
|
52
49
|
pp ast
|
53
50
|
puts "\n----- visitor output -----"
|
data/lib/tire_swing.rb
CHANGED
data/lib/tire_swing/node.rb
CHANGED
@@ -51,21 +51,40 @@ module TireSwing
|
|
51
51
|
# Auto-builds this node using the provided parsed node and the defined attributes and mapped attributes.
|
52
52
|
def build_from_parsed_node(parsed_node)
|
53
53
|
attributes.each do |attrib|
|
54
|
-
if
|
55
|
-
if
|
56
|
-
value =
|
54
|
+
if handler = mapping(attrib)
|
55
|
+
if handler.kind_of?(Proc)
|
56
|
+
value = handler.call(parsed_node)
|
57
57
|
else
|
58
|
-
|
58
|
+
if parsed_node.respond_to?(handler)
|
59
|
+
value = parsed_node.send(handler)
|
60
|
+
else
|
61
|
+
value = parsed_node.text_value.send(handler)
|
62
|
+
end
|
59
63
|
end
|
60
64
|
else
|
61
65
|
value = parsed_node.send(attrib)
|
62
66
|
end
|
63
|
-
|
64
|
-
value =
|
67
|
+
|
68
|
+
value = if value.kind_of?(Array)
|
69
|
+
value.map { |val| extract_value(val) }
|
70
|
+
else
|
71
|
+
extract_value(value)
|
72
|
+
end
|
73
|
+
|
65
74
|
send("#{attrib}=", value)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
78
|
+
def extract_value(value)
|
79
|
+
if value.respond_to?(:build)
|
80
|
+
value.build
|
81
|
+
elsif value.kind_of?(Treetop::Runtime::SyntaxNode)
|
82
|
+
value.text_value
|
83
|
+
else
|
84
|
+
value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
69
88
|
def attributes
|
70
89
|
self.class.attributes
|
71
90
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module TireSwing
|
2
2
|
class NodeCreator
|
3
3
|
|
4
|
-
# Creates a node creator for the given node class. This is meant to act as a stand-in for the treetop
|
5
|
-
# syntax node class.
|
4
|
+
# Creates a node creator for the given node type and class. This is meant to act as a stand-in for the treetop
|
5
|
+
# syntax node class. The node_name, although somewhat redundant, is used for filtering later on
|
6
|
+
# (see: array_of)
|
6
7
|
#
|
7
|
-
def initialize(node_class)
|
8
|
-
@node_class = node_class
|
8
|
+
def initialize(node_name, node_class)
|
9
|
+
@node_name, @node_class = node_name, node_class
|
9
10
|
end
|
10
11
|
|
11
12
|
# Returns a new treetop syntax node to act as a standin for a normal syntax node. Before returning the node,
|
@@ -16,11 +17,18 @@ module TireSwing
|
|
16
17
|
def new(*args)
|
17
18
|
parsed_node = Treetop::Runtime::SyntaxNode.new(*args)
|
18
19
|
|
19
|
-
node_class = @node_class # local scope for the block below
|
20
|
+
node_name, node_class = @node_name, @node_class # local scope for the block below
|
21
|
+
|
22
|
+
# so the node knows how to build itself:
|
20
23
|
parsed_node.meta_def :build do
|
21
24
|
node_class.new(self)
|
22
25
|
end
|
23
26
|
|
27
|
+
# so the node can be filtered based on what kind of AST node it will build
|
28
|
+
parsed_node.meta_def :node_to_build do
|
29
|
+
node_name
|
30
|
+
end
|
31
|
+
|
24
32
|
parsed_node
|
25
33
|
end
|
26
34
|
|
@@ -17,11 +17,29 @@ module TireSwing::NodeDefinition
|
|
17
17
|
# You can define the grammar as
|
18
18
|
#
|
19
19
|
# rule variable
|
20
|
-
# [a-z]+ <
|
20
|
+
# [a-z]+ <node(:variable)>
|
21
21
|
# end
|
22
22
|
#
|
23
|
+
# Also note that you can specify alternate namespaces:
|
24
|
+
#
|
25
|
+
# module AST
|
26
|
+
# node :variable
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# <AST.create_node(:variable)>
|
30
|
+
#
|
31
|
+
# When you're using the parser extension:
|
32
|
+
#
|
33
|
+
# TireSwing.parses_grammar(Grammar, AST)
|
34
|
+
#
|
35
|
+
# you can use
|
36
|
+
#
|
37
|
+
# <node(...)>
|
38
|
+
#
|
39
|
+
# Which is an instance method wrapper for the create_node class method.
|
40
|
+
#
|
23
41
|
def create_node(name)
|
24
|
-
TireSwing::NodeCreator.new(const_get(name.to_s.camelize))
|
42
|
+
TireSwing::NodeCreator.new(name, const_get(name.to_s.camelize))
|
25
43
|
end
|
26
44
|
|
27
45
|
# Define a node.
|
@@ -53,18 +71,31 @@ module TireSwing::NodeDefinition
|
|
53
71
|
# The second method takes a treetop syntax node and auto-builds the node using the syntax node as a basis.
|
54
72
|
# The auto-build functionality uses the attribute names and mapped attributes in the following way:
|
55
73
|
#
|
56
|
-
# * Simple attributes: calls the method by that name on the syntax node
|
57
|
-
#
|
58
|
-
#
|
74
|
+
# * Simple attributes: calls the method by that name on the syntax node, e.g.
|
75
|
+
#
|
76
|
+
# node :assignment, :lhs, :rhs
|
77
|
+
#
|
78
|
+
# * Mapped attributes:
|
79
|
+
# * symbol/string - call that method on *either* the node, if it responds, or on its text value (e.g. #to_i)
|
59
80
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
81
|
+
# node :number, :value => :to_i # calls to_i on the number node's text value
|
82
|
+
#
|
83
|
+
# * lambda - yields the parsed node to the lambda
|
84
|
+
#
|
85
|
+
# Whatever the value (or array of values) is returned by the mapped call will then be built. Any item returned
|
86
|
+
# that responds to the build method will have it called. If there's any bare syntax nodes left over, they are
|
87
|
+
# converted into their text value, and anything else will be returned as-is (numbers, strings, etc.)
|
88
|
+
#
|
89
|
+
# Note that you can use the array_of and extract helpers defined below to define lambdas for doing more advanced
|
90
|
+
# filtering on the node and it's children.
|
63
91
|
#
|
64
92
|
# Simple example:
|
65
93
|
#
|
66
94
|
# rule assignment
|
67
|
-
# variable:lhs "=" variable:rhs <
|
95
|
+
# variable:lhs "=" variable:rhs <node(:assignment)>
|
96
|
+
# end
|
97
|
+
# rule variable
|
98
|
+
# [a-z]+
|
68
99
|
# end
|
69
100
|
#
|
70
101
|
# node :assignment, :lhs, :rhs
|
@@ -84,22 +115,85 @@ module TireSwing::NodeDefinition
|
|
84
115
|
klass.class_eval &blk if block_given?
|
85
116
|
end
|
86
117
|
|
118
|
+
# Returns a lambda to select only child nodes of the given kind. This is best used for rules that can return
|
119
|
+
# arrays of different kind of nodes, some of which you want to ignore, e.g.:
|
120
|
+
#
|
121
|
+
# rule assignments
|
122
|
+
# assignment* <node(:assignments)>
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# node :assignments, :assignments => array_of(:assignment)
|
126
|
+
#
|
127
|
+
# When parsed, this will give you a recursive set of nodes:
|
128
|
+
#
|
129
|
+
# [assignment, [assignment, [assignment]]]
|
130
|
+
#
|
131
|
+
# If you specify that the array is recursive, it will retrieve all nested child nodes, no matter how deep, which
|
132
|
+
# provide the kind of node you want.
|
133
|
+
#
|
134
|
+
# If you provide a block, the filtered result will be yielded to the block and returned as the final result.
|
135
|
+
#
|
136
|
+
def array_of(kind, recursive = false, &blk)
|
137
|
+
lambda do |node|
|
138
|
+
result = NodeFilters.filter(node, kind, recursive)
|
139
|
+
blk ? result.map(&blk) : result
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns a lambda which takes a node and calls node_name on each in turn if possible, returning the result.
|
144
|
+
# This is useful for nested rules, such as:
|
145
|
+
#
|
146
|
+
# rule assignments
|
147
|
+
# (assignment [\n])+
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# This is a subtle difference from array_of. The rule given here provides a syntax node with multiple elements,
|
151
|
+
# each one corresponding to the grouping, not to an individual child node -- that is, it's a list of
|
152
|
+
# [[assignment node, newline node], [assignment, newline], ...] instead of being a flattened list of
|
153
|
+
# [assignment, newline, assignment, newline, ...].
|
154
|
+
#
|
155
|
+
# To extract these:
|
156
|
+
#
|
157
|
+
# node :assignments, :assignments => extract(:assignment)
|
158
|
+
#
|
159
|
+
# Which will extract just the assignments out of those "nested children", and then do what you expect from there.
|
160
|
+
#
|
161
|
+
def extract(node_name)
|
162
|
+
lambda do |node|
|
163
|
+
results = []
|
164
|
+
results << node.send(node_name) if node.respond_to?(node_name)
|
165
|
+
|
166
|
+
results.push *node.elements.select { |elem| elem.respond_to?(node_name) }.map { |elem| elem.send(node_name) }
|
167
|
+
|
168
|
+
results
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
87
172
|
end
|
88
173
|
|
89
|
-
|
90
|
-
|
174
|
+
module NodeFilters
|
175
|
+
|
176
|
+
def self.filter(node, kind, recursive)
|
177
|
+
nodes = []
|
178
|
+
children = node.respond_to?(:elements) ? (node.elements || []) : []
|
179
|
+
if recursive
|
180
|
+
nodes << node if node.respond_to?(:node_to_build) && node.node_to_build == kind
|
181
|
+
children.each { |child| nodes.push *filter(child, kind, true) }
|
182
|
+
else
|
183
|
+
nodes = ([node] + children).select { |n| n.respond_to?(:node_to_build) && n.node_to_build == kind }
|
184
|
+
end
|
185
|
+
nodes
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
# Include this module to get access to the node and create_node class methods.
|
91
191
|
#
|
92
192
|
# module AST
|
93
193
|
# include NodeDefinition
|
94
194
|
# node :foo
|
95
195
|
# end
|
96
196
|
#
|
97
|
-
# And then in your grammar:
|
98
|
-
#
|
99
|
-
# rule foo
|
100
|
-
# "foo" <AST.create_node(:foo)>
|
101
|
-
# end
|
102
|
-
#
|
103
197
|
def self.included(base)
|
104
198
|
base.extend ModuleMethods
|
105
199
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module TireSwing
|
2
|
+
|
3
|
+
module ParserExtension
|
4
|
+
|
5
|
+
def ast(io)
|
6
|
+
parser = new
|
7
|
+
result = parser.parse(io)
|
8
|
+
if result
|
9
|
+
result.build
|
10
|
+
else
|
11
|
+
raise TireSwing::ParseError.new(parser.failure_reason, parser)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
# Extends the treetop-provided grammar parser with a .ast class method for simple parsing and building of
|
18
|
+
# an AST defined by TireSwing. Takes the grammar module as an argument.
|
19
|
+
#
|
20
|
+
# Additionally, this defines a #node method on the grammar to delegate to the class or the AST to create
|
21
|
+
# new nodes, e.g. <node(:variable)> instead of <AST.create_node(:variable)>
|
22
|
+
#
|
23
|
+
# You can specify an alternate module which contains the AST if desired.
|
24
|
+
def self.parses_grammar(grammar, ast=nil)
|
25
|
+
parser = (grammar.to_s + "Parser").constantize
|
26
|
+
ast ||= grammar
|
27
|
+
parser.module_eval do
|
28
|
+
extend ParserExtension
|
29
|
+
define_method(:node) { |*args| ast.create_node(*args) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe TireSwing::ParseError do
|
4
|
+
|
5
|
+
describe ".new" do
|
6
|
+
it "takes a message and a parser instance" do
|
7
|
+
TireSwing::ParseError.new("message", "parser").should be_an_instance_of(TireSwing::ParseError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "has a message" do
|
12
|
+
e = TireSwing::ParseError.new("message", "parser")
|
13
|
+
e.message.should == "message"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a parser" do
|
17
|
+
e = TireSwing::ParseError.new("message", "parser")
|
18
|
+
e.parser.should == "parser"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -28,6 +28,9 @@ module AssignmentsLanguage
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
# This uses an external eval method to build the TireSwing AST manually. Don't do this, but it's here for
|
32
|
+
# an example.
|
33
|
+
|
31
34
|
class Parser < ::Treetop::Runtime::CompiledParser
|
32
35
|
include Grammar
|
33
36
|
def self.parse(io)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# More magic: handle recursive rules
|
2
|
+
|
3
|
+
Treetop.load_from_string <<-GRAMMAR
|
4
|
+
|
5
|
+
module Lists
|
6
|
+
grammar Grammar
|
7
|
+
rule lists
|
8
|
+
(list [\n])+ <node(:lists)>
|
9
|
+
end
|
10
|
+
|
11
|
+
rule list
|
12
|
+
"[" whitespace* number ("," whitespace* number)* "]" <node(:list)>
|
13
|
+
end
|
14
|
+
|
15
|
+
rule number
|
16
|
+
[1-9] [0-9]* <node(:number)>
|
17
|
+
end
|
18
|
+
|
19
|
+
rule whitespace
|
20
|
+
[ ]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
GRAMMAR
|
26
|
+
|
27
|
+
module Lists
|
28
|
+
module AST
|
29
|
+
include TireSwing::NodeDefinition
|
30
|
+
node :lists, :elements, :lists => extract(:list)
|
31
|
+
node :list, :elements, :numbers => array_of(:number, true) { |num| num.text_value.to_i }
|
32
|
+
node :number # placeholder
|
33
|
+
end
|
34
|
+
|
35
|
+
TireSwing.parses_grammar(Grammar, AST)
|
36
|
+
end
|
data/spec/grammars/magic.rb
CHANGED
@@ -5,19 +5,19 @@ Treetop.load_from_string <<-GRAMMAR
|
|
5
5
|
module MagicAssignments
|
6
6
|
grammar Grammar
|
7
7
|
rule assignments
|
8
|
-
( blank_line / assignment )* <
|
8
|
+
( blank_line / assignment )* <node(:assignments)>
|
9
9
|
end
|
10
10
|
rule assignment
|
11
|
-
lhs:variable whitespace* "=" whitespace* rhs:variable [\\n] <
|
11
|
+
lhs:variable whitespace* "=" whitespace* rhs:variable [\\n] <node(:assignment)>
|
12
12
|
end
|
13
13
|
rule variable
|
14
|
-
[a-z]+
|
14
|
+
[a-z]+
|
15
15
|
end
|
16
16
|
rule whitespace
|
17
17
|
[ ]
|
18
18
|
end
|
19
19
|
rule blank_line
|
20
|
-
whitespace* [\\n]
|
20
|
+
whitespace* [\\n]
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -28,11 +28,8 @@ module MagicAssignments
|
|
28
28
|
module AST
|
29
29
|
include TireSwing::NodeDefinition
|
30
30
|
|
31
|
-
node :assignments, :assignments => :elements
|
32
31
|
node :assignment, :lhs, :rhs
|
33
|
-
node :
|
34
|
-
node :variable, :value => :text_value
|
35
|
-
|
32
|
+
node :assignments, :assignments => array_of(:assignment)
|
36
33
|
end
|
37
34
|
|
38
35
|
include TireSwing::VisitorDefinition
|
@@ -44,20 +41,11 @@ module MagicAssignments
|
|
44
41
|
hash
|
45
42
|
end
|
46
43
|
visits AST::Assignment do |assignment, hash|
|
47
|
-
hash[
|
48
|
-
end
|
49
|
-
visits AST::BlankLine
|
50
|
-
visits AST::Variable do |variable|
|
51
|
-
variable.value
|
44
|
+
hash[assignment.lhs] = assignment.rhs
|
52
45
|
end
|
53
46
|
end
|
54
47
|
|
55
|
-
|
56
|
-
include Grammar
|
57
|
-
def self.parse(io)
|
58
|
-
new.parse(io).build
|
59
|
-
end
|
60
|
-
end
|
48
|
+
TireSwing.parses_grammar(Grammar, AST)
|
61
49
|
|
62
50
|
end
|
63
51
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
|
3
|
+
require TireSwing.path(%w(spec grammars lists))
|
4
|
+
|
5
|
+
describe Lists::GrammarParser do
|
6
|
+
describe ".ast" do
|
7
|
+
before(:each) do
|
8
|
+
@input = <<-EOD
|
9
|
+
[1, 2, 3]
|
10
|
+
[4, 5]
|
11
|
+
[6, 7,8,9,10]
|
12
|
+
EOD
|
13
|
+
@result = Lists::GrammarParser.ast(@input)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns an AST" do
|
17
|
+
@result.should be_an_instance_of(Lists::AST::Lists)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "has an array of lists" do
|
21
|
+
@result.should have(3).lists
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has lists with numbers" do
|
25
|
+
@result.lists.first.should have(3).numbers
|
26
|
+
@result.lists.first.numbers.should == [1, 2, 3]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -2,25 +2,25 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
2
2
|
|
3
3
|
require TireSwing.path(%w(spec grammars magic))
|
4
4
|
|
5
|
-
describe MagicAssignments::
|
6
|
-
describe ".
|
5
|
+
describe MagicAssignments::GrammarParser do
|
6
|
+
describe ".ast" do
|
7
7
|
before(:each) do
|
8
8
|
@input = File.read(TireSwing.path(%w(spec fixtures assignments.txt)))
|
9
|
-
@result = MagicAssignments::
|
9
|
+
@result = MagicAssignments::GrammarParser.ast(@input)
|
10
10
|
end
|
11
11
|
|
12
12
|
it "returns an AST" do
|
13
13
|
@result.should be_an_instance_of(MagicAssignments::AST::Assignments)
|
14
14
|
end
|
15
15
|
|
16
|
-
it "has the right number of
|
17
|
-
@result.should have(
|
16
|
+
it "has the right number of assignments" do
|
17
|
+
@result.should have(3).assignments
|
18
18
|
end
|
19
19
|
|
20
20
|
it "has an assignment with correct values" do
|
21
21
|
@result.assignments.first.should be_an_instance_of(MagicAssignments::AST::Assignment)
|
22
|
-
@result.assignments.first.lhs.
|
23
|
-
@result.assignments.first.rhs.
|
22
|
+
@result.assignments.first.lhs.should == "foo"
|
23
|
+
@result.assignments.first.rhs.should == "bar"
|
24
24
|
end
|
25
25
|
|
26
26
|
end
|
@@ -29,7 +29,7 @@ end
|
|
29
29
|
describe MagicAssignments::HashVisitor do
|
30
30
|
describe ".visit" do
|
31
31
|
it "returns a hash representation of the assignments" do
|
32
|
-
ast = MagicAssignments::
|
32
|
+
ast = MagicAssignments::GrammarParser.ast(File.read(TireSwing.path(%w(spec fixtures assignments.txt))))
|
33
33
|
MagicAssignments::HashVisitor.visit(ast).should == {
|
34
34
|
"foo" => "bar",
|
35
35
|
"baz" => "blech",
|
@@ -37,4 +37,4 @@ describe MagicAssignments::HashVisitor do
|
|
37
37
|
}
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end
|
data/spec/node_creator_spec.rb
CHANGED
@@ -11,14 +11,14 @@ describe TireSwing::NodeCreator do
|
|
11
11
|
|
12
12
|
describe "#initialize" do
|
13
13
|
|
14
|
-
it "takes a class as
|
15
|
-
nc = TireSwing::NodeCreator.new(SomeNode)
|
14
|
+
it "takes a node name and a node class as arguments" do
|
15
|
+
nc = TireSwing::NodeCreator.new(:some_node, SomeNode)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe "#new" do
|
20
20
|
before(:each) do
|
21
|
-
@nc = TireSwing::NodeCreator.new(SomeNode)
|
21
|
+
@nc = TireSwing::NodeCreator.new(:some_node, SomeNode)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "takes parameters as if it were a treetop syntax node" do
|
@@ -35,6 +35,11 @@ describe TireSwing::NodeCreator do
|
|
35
35
|
node.methods.should include("build")
|
36
36
|
end
|
37
37
|
|
38
|
+
it "defines a node_to_build method on the syntax node that returns the kind of ast node it's going to build" do
|
39
|
+
node = @nc.new("what", 0..3)
|
40
|
+
node.node_to_build.should == :some_node
|
41
|
+
end
|
42
|
+
|
38
43
|
it "creates an instance of the defined class when build is called on the resulting syntax node" do
|
39
44
|
node = @nc.new("what", 0..3)
|
40
45
|
mock_node = mock("node")
|
@@ -5,6 +5,14 @@ describe TireSwing::NodeDefinition do
|
|
5
5
|
include TireSwing::NodeDefinition
|
6
6
|
end
|
7
7
|
|
8
|
+
before(:all) do
|
9
|
+
TestNodes.class_eval { node :foo_node }
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) do
|
13
|
+
TestNodes.send(:remove_const, "FooNode")
|
14
|
+
end
|
15
|
+
|
8
16
|
it "adds a node method when included" do
|
9
17
|
TestNodes.methods.should include("node")
|
10
18
|
end
|
@@ -34,12 +42,6 @@ describe TireSwing::NodeDefinition do
|
|
34
42
|
end
|
35
43
|
|
36
44
|
describe ".create_node" do
|
37
|
-
before(:all) do
|
38
|
-
TestNodes.class_eval { node :foo_node }
|
39
|
-
end
|
40
|
-
after(:all) do
|
41
|
-
TestNodes.send(:remove_const, "FooNode")
|
42
|
-
end
|
43
45
|
|
44
46
|
it "takes a node name as an argument" do
|
45
47
|
TestNodes.create_node(:foo_node)
|
@@ -49,12 +51,84 @@ describe TireSwing::NodeDefinition do
|
|
49
51
|
TestNodes.create_node(:foo_node).should be_an_instance_of(TireSwing::NodeCreator)
|
50
52
|
end
|
51
53
|
|
52
|
-
it "instantiates the node creator
|
54
|
+
it "instantiates the node creator for the given node name" do
|
53
55
|
obj = Object.new
|
54
|
-
TireSwing::NodeCreator.should_receive(:new).with(TestNodes::FooNode).and_return(obj)
|
56
|
+
TireSwing::NodeCreator.should_receive(:new).with(:foo_node, TestNodes::FooNode).and_return(obj)
|
55
57
|
TestNodes.create_node(:foo_node).should == obj
|
56
58
|
end
|
57
59
|
|
58
60
|
end
|
59
61
|
|
62
|
+
describe ".array_of" do
|
63
|
+
|
64
|
+
it "returns a lambda" do
|
65
|
+
TestNodes.array_of(:foo_node).should be_an_instance_of(Proc)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns a lambda that filters a node's elements for nodes that will build the given node type" do
|
69
|
+
a, b = mock_syntax_node("a", :node_to_build => :foo_node), mock_syntax_node("b", :node_to_build => :foo_node)
|
70
|
+
node = mock_syntax_node("node", :elements => [1, 2, a, 3, b])
|
71
|
+
TestNodes.array_of(:foo_node).call(node).should == [a, b]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "will return the node itself if it provides the given node type" do
|
75
|
+
node = mock_syntax_node("node", :node_to_build => :foo)
|
76
|
+
TestNodes.array_of(:foo).call(node).should == [node]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "does not filter recursively by default" do
|
80
|
+
b = mock_syntax_node("b", :elements => ["stuff", "whatever"], :node_to_build => :foo)
|
81
|
+
a = mock_syntax_node("a", :elements => ["jkl", b], :node_to_build => :foo)
|
82
|
+
top = mock_syntax_node("top", :elements => ["asdf", a], :node_to_build => :foo)
|
83
|
+
TestNodes.array_of(:foo).call(top).should == [top, a]
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "with the recursive flag" do
|
87
|
+
it "returns a lambda that filters recursively for the right kind of child" do
|
88
|
+
b = mock_syntax_node("b", :elements => ["stuff", "whatever"], :node_to_build => :foo)
|
89
|
+
a = mock_syntax_node("a", :elements => ["jkl", b], :node_to_build => :foo)
|
90
|
+
top = mock_syntax_node("top", :elements => ["asdf", a], :node_to_build => :foo)
|
91
|
+
TestNodes.array_of(:foo, true).call(top).should == [top, a, b]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "with a block provided" do
|
96
|
+
it "yields the filtered results to the block" do
|
97
|
+
a = mock_syntax_node("a", :node_to_build => :foo_node, :x => 1)
|
98
|
+
b = mock_syntax_node("b", :node_to_build => :foo_node, :x => 2)
|
99
|
+
node = mock_syntax_node("node", :elements => [1, 2, a, 3, b])
|
100
|
+
yielded = []
|
101
|
+
array_of = TestNodes.array_of(:foo_node) { |node| yielded << node; node.x }
|
102
|
+
array_of.call(node).should == [1, 2]
|
103
|
+
yielded.should == [a, b]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
describe ".extract" do
|
110
|
+
|
111
|
+
it "returns a lambda" do
|
112
|
+
TestNodes.extract(:thing).should be_an_instance_of(Proc)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "extracts all nodes by calling the given node name wherever it applies" do
|
116
|
+
q = mock_syntax_node("q")
|
117
|
+
a = mock_syntax_node("a", :value => q)
|
118
|
+
b = mock_syntax_node("b")
|
119
|
+
c = mock_syntax_node("c", :value => "c")
|
120
|
+
node = mock_syntax_node("node", :elements => [a, b, c])
|
121
|
+
TestNodes.extract(:value).call(node).should == [q, "c"]
|
122
|
+
end
|
123
|
+
|
124
|
+
it "will extract the given node as well as the nested children " do
|
125
|
+
a = mock_syntax_node("a", :value => "a")
|
126
|
+
b = mock_syntax_node("b")
|
127
|
+
c = mock_syntax_node("c", :value => "c")
|
128
|
+
node = mock_syntax_node("node", :value => "foo", :elements => [a, b, c])
|
129
|
+
TestNodes.extract(:value).call(node).should == ["foo", "a", "c"]
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
60
134
|
end
|
data/spec/node_spec.rb
CHANGED
@@ -87,44 +87,55 @@ describe TireSwing::Node do
|
|
87
87
|
|
88
88
|
describe "with an instance of Treetop::Runtime::SyntaxNode" do
|
89
89
|
before(:each) do
|
90
|
-
@child = mock_syntax_node
|
91
|
-
@top = mock_syntax_node
|
90
|
+
@child = mock_syntax_node("child")
|
91
|
+
@top = mock_syntax_node("top")
|
92
92
|
end
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
describe "for a node with a single attribute as a named method" do
|
95
|
+
it "retrieves the value for the attribute by calling the named method" do
|
96
|
+
@node = TireSwing::Node.create(:foo => :a_method)
|
97
|
+
@top.should_receive(:a_method).and_return("asdf")
|
98
|
+
@node.new(@top).foo.should == "asdf"
|
99
|
+
end
|
99
100
|
end
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
describe "for a node with an attribute naming a child syntax node" do
|
103
|
+
it "retrieves the value by calling build on the child" do
|
104
|
+
@top.should_receive(:child).and_return(@child)
|
105
|
+
@node = TireSwing::Node.create(:foo => :child)
|
106
|
+
@child.should_receive(:build).and_return("child value")
|
107
|
+
@node.new(@top).foo.should == "child value"
|
105
108
|
end
|
106
|
-
n = @node.new(@top)
|
107
|
-
n.value.should == "foo"
|
108
|
-
end
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
110
|
+
it "calls text_value on the child if the child doesn't have a build method" do
|
111
|
+
@top.should_receive(:child).and_return(@child)
|
112
|
+
@node = TireSwing::Node.create(:foo => :child)
|
113
|
+
@child.should_receive(:text_value).and_return("child value")
|
114
|
+
@node.new(@top).foo.should == "child value"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "retrieves the value of each item in an array if the named attribute returns one" do
|
118
|
+
@child.should_receive(:build).and_return("child")
|
119
|
+
@top.should_receive(:children).and_return(["foo", 1, @child])
|
120
|
+
TireSwing::Node.create(:foo => :children).new(@top).foo.should == ["foo", 1, "child"]
|
121
|
+
end
|
116
122
|
|
117
|
-
it "only calls build on elements of an array if they respond to that method" do
|
118
|
-
@top.should_receive(:child).and_return([@child, "blah"])
|
119
|
-
@node.new(@top).child.should == ["child value", "blah"]
|
120
123
|
end
|
121
124
|
|
122
|
-
it "yields the syntax node instance if a
|
123
|
-
@node = TireSwing::Node.create(:value => lambda { |x| @x = x })
|
124
|
-
@node.new(@top)
|
125
|
+
it "yields the syntax node instance if a named attribute is a lambda" do
|
126
|
+
@node = TireSwing::Node.create(:value => lambda { |x| @x = x; "asdf" })
|
127
|
+
@node.new(@top).value.should == "asdf"
|
125
128
|
@x.should == @top
|
126
129
|
end
|
127
130
|
|
131
|
+
describe "for a node with an attribute as a method to call on the text value" do
|
132
|
+
it "calls that method on the text value, not the parsed node" do
|
133
|
+
node = TireSwing::Node.create(:value => :to_i)
|
134
|
+
@top.should_receive(:text_value).and_return("1234")
|
135
|
+
node.new(@top).value.should == 1234
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
128
139
|
end
|
129
140
|
end
|
130
141
|
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe TireSwing::ParserExtension do
|
4
|
+
|
5
|
+
it "defines a parses_grammar method on TireSwing" do
|
6
|
+
TireSwing.should respond_to(:parses_grammar)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "when given a grammar" do
|
10
|
+
module Foo; end
|
11
|
+
class FooParser; end
|
12
|
+
module AST; end
|
13
|
+
|
14
|
+
it "extends the parser for that grammar with the ParserExtension module, defining an ast method" do
|
15
|
+
TireSwing.parses_grammar(Foo)
|
16
|
+
FooParser.should respond_to(:ast)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "defines a node method on the parser that delegates to the grammar" do
|
20
|
+
TireSwing.parses_grammar(Foo)
|
21
|
+
Foo.should_receive(:create_node).with("a", "b", "c")
|
22
|
+
FooParser.new.node("a", "b", "c")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "defines a create_node method on the parser that delegates to the given AST, if provided" do
|
26
|
+
TireSwing.parses_grammar(Foo, AST)
|
27
|
+
AST.should_receive(:create_node).with("a", "b", "c")
|
28
|
+
FooParser.new.node("a", "b", "c")
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#ast" do
|
34
|
+
before(:all) do
|
35
|
+
|
36
|
+
Treetop.load_from_string <<-GRAMMAR
|
37
|
+
module TestGramma
|
38
|
+
grammar Grammar
|
39
|
+
rule letters
|
40
|
+
letter* <node(:letters)>
|
41
|
+
end
|
42
|
+
rule letter
|
43
|
+
value:[a-z] [\n]? <node(:letter)>
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
GRAMMAR
|
48
|
+
|
49
|
+
module ::TestGramma
|
50
|
+
module AST
|
51
|
+
include TireSwing::NodeDefinition
|
52
|
+
node :letters, :letters => array_of(:letter)
|
53
|
+
node :letter, :value
|
54
|
+
end
|
55
|
+
TireSwing.parses_grammar(Grammar, AST)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
after(:all) do
|
61
|
+
Object.send(:remove_const, "TestGramma")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "takes an AST and returns the built AST" do
|
65
|
+
asdf = TestGramma::GrammarParser.ast("asdf")
|
66
|
+
asdf.should be_an_instance_of ::TestGramma::AST::Letters
|
67
|
+
asdf.should have(4).letters
|
68
|
+
asdf.letters.map { |l| l.value }.should == %w(a s d f)
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "with invalid input" do
|
72
|
+
|
73
|
+
it "raises an exception" do
|
74
|
+
lambda { parse }.should raise_error(TireSwing::ParseError)
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse
|
78
|
+
TestGramma::GrammarParser.ast("as3f")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,8 +11,8 @@ Spec::Runner.configure do |config|
|
|
11
11
|
# config.mock_with :rr
|
12
12
|
|
13
13
|
# borrowed from rspec_on_rails' mock_model
|
14
|
-
def mock_syntax_node(stubs={})
|
15
|
-
m = mock "mock syntax node", stubs
|
14
|
+
def mock_syntax_node(name, stubs={})
|
15
|
+
m = mock "mock syntax node (#{name})", stubs
|
16
16
|
m.send(:__mock_proxy).instance_eval do
|
17
17
|
def @target.is_a?(other)
|
18
18
|
Treetop::Runtime::SyntaxNode.ancestors.include?(other)
|
data/tire_swing.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = %q{tire_swing}
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.3"
|
4
4
|
|
5
5
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
6
|
s.authors = ["Nathan Witmer"]
|
7
|
-
s.date = %q{2008-
|
7
|
+
s.date = %q{2008-08-11}
|
8
8
|
s.description = %q{Simple node and visitor definitions for Treetop grammars.}
|
9
9
|
s.email = %q{nwitmer at gmail dot com}
|
10
10
|
s.extra_rdoc_files = ["History.txt", "README.txt", "spec/fixtures/assignments.txt"]
|
11
|
-
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "examples/simple_assignment.rb", "lib/tire_swing.rb", "lib/tire_swing/metaid.rb", "lib/tire_swing/node.rb", "lib/tire_swing/node_creator.rb", "lib/tire_swing/node_definition.rb", "lib/tire_swing/visitor.rb", "lib/tire_swing/visitor_definition.rb", "spec/fixtures/assignments.txt", "spec/fixtures/ey00-s00348.xen", "spec/grammars/assignments.rb", "spec/grammars/assignments.treetop", "spec/grammars/dot_xen.rb", "spec/grammars/dot_xen.treetop", "spec/grammars/magic.rb", "spec/integration/assignments_spec.rb", "spec/integration/dot_xen_spec.rb", "spec/integration/magic_spec.rb", "spec/node_creator_spec.rb", "spec/node_definition_spec.rb", "spec/node_spec.rb", "spec/spec_helper.rb", "spec/tire_swing_spec.rb", "spec/visitor_definition_spec.rb", "spec/visitor_spec.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/manifest.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "tire_swing.gemspec"]
|
11
|
+
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "examples/simple_assignment.rb", "lib/tire_swing.rb", "lib/tire_swing/error.rb", "lib/tire_swing/metaid.rb", "lib/tire_swing/node.rb", "lib/tire_swing/node_creator.rb", "lib/tire_swing/node_definition.rb", "lib/tire_swing/parser_extension.rb", "lib/tire_swing/visitor.rb", "lib/tire_swing/visitor_definition.rb", "spec/error_spec.rb", "spec/fixtures/assignments.txt", "spec/fixtures/ey00-s00348.xen", "spec/grammars/assignments.rb", "spec/grammars/assignments.treetop", "spec/grammars/dot_xen.rb", "spec/grammars/dot_xen.treetop", "spec/grammars/lists.rb", "spec/grammars/magic.rb", "spec/integration/assignments_spec.rb", "spec/integration/dot_xen_spec.rb", "spec/integration/lists_spec.rb", "spec/integration/magic_spec.rb", "spec/node_creator_spec.rb", "spec/node_definition_spec.rb", "spec/node_spec.rb", "spec/parser_extension_spec.rb", "spec/spec_helper.rb", "spec/tire_swing_spec.rb", "spec/visitor_definition_spec.rb", "spec/visitor_spec.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/manifest.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "tire_swing.gemspec"]
|
12
12
|
s.has_rdoc = true
|
13
13
|
s.homepage = %q{http://github.com/aniero/tire_swing}
|
14
14
|
s.rdoc_options = ["--main", "README.txt"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aniero-tire_swing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Witmer
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-08-11 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -56,25 +56,31 @@ files:
|
|
56
56
|
- Rakefile
|
57
57
|
- examples/simple_assignment.rb
|
58
58
|
- lib/tire_swing.rb
|
59
|
+
- lib/tire_swing/error.rb
|
59
60
|
- lib/tire_swing/metaid.rb
|
60
61
|
- lib/tire_swing/node.rb
|
61
62
|
- lib/tire_swing/node_creator.rb
|
62
63
|
- lib/tire_swing/node_definition.rb
|
64
|
+
- lib/tire_swing/parser_extension.rb
|
63
65
|
- lib/tire_swing/visitor.rb
|
64
66
|
- lib/tire_swing/visitor_definition.rb
|
67
|
+
- spec/error_spec.rb
|
65
68
|
- spec/fixtures/assignments.txt
|
66
69
|
- spec/fixtures/ey00-s00348.xen
|
67
70
|
- spec/grammars/assignments.rb
|
68
71
|
- spec/grammars/assignments.treetop
|
69
72
|
- spec/grammars/dot_xen.rb
|
70
73
|
- spec/grammars/dot_xen.treetop
|
74
|
+
- spec/grammars/lists.rb
|
71
75
|
- spec/grammars/magic.rb
|
72
76
|
- spec/integration/assignments_spec.rb
|
73
77
|
- spec/integration/dot_xen_spec.rb
|
78
|
+
- spec/integration/lists_spec.rb
|
74
79
|
- spec/integration/magic_spec.rb
|
75
80
|
- spec/node_creator_spec.rb
|
76
81
|
- spec/node_definition_spec.rb
|
77
82
|
- spec/node_spec.rb
|
83
|
+
- spec/parser_extension_spec.rb
|
78
84
|
- spec/spec_helper.rb
|
79
85
|
- spec/tire_swing_spec.rb
|
80
86
|
- spec/visitor_definition_spec.rb
|