babel_bridge 0.2.0 → 0.3.0
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/examples/indention_grouping.rb +68 -0
- data/examples/indention_grouping_test.txt +10 -0
- data/examples/turing/test.rb +28 -0
- data/examples/turing/turing.rb +71 -0
- data/lib/babel_bridge.rb +13 -344
- data/lib/nodes.rb +9 -278
- data/lib/nodes/empty_node.rb +17 -0
- data/lib/nodes/many_node.rb +62 -0
- data/lib/nodes/node.rb +94 -0
- data/lib/nodes/non_terminal_node.rb +117 -0
- data/lib/nodes/terminal_node.rb +38 -0
- data/lib/parser.rb +285 -0
- data/lib/pattern_element.rb +152 -151
- data/lib/rule.rb +62 -0
- data/lib/rule_variant.rb +45 -0
- data/lib/shell.rb +36 -0
- data/lib/string.rb +26 -0
- data/lib/tools.rb +90 -0
- data/lib/version.rb +3 -0
- data/test/test_bb.rb +39 -3
- metadata +19 -3
data/lib/nodes.rb
CHANGED
@@ -1,278 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# - since a match can be an Array in the case of Poly-matches
|
11
|
-
class MultiMatchesArray < Array
|
12
|
-
end
|
13
|
-
|
14
|
-
# base class for all parse-tree nodes
|
15
|
-
class Node
|
16
|
-
attr_accessor :src,:offset,:match_length,:parent,:parser
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
text
|
20
|
-
end
|
21
|
-
|
22
|
-
def node_init(parent_or_parser)
|
23
|
-
self.match_length=0
|
24
|
-
case parent_or_parser
|
25
|
-
when Parser then
|
26
|
-
self.parser=parent_or_parser
|
27
|
-
self.offset=0
|
28
|
-
self.src=parser.src
|
29
|
-
when Node then
|
30
|
-
self.parent=parent_or_parser
|
31
|
-
self.parser=parent.parser
|
32
|
-
self.offset=parent.next
|
33
|
-
self.src=parent.src
|
34
|
-
raise "parent node does not have parser set" unless parser
|
35
|
-
else
|
36
|
-
raise "parent_or_parser(#{parent_or_parser.class}) must be a Node or a Parser"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(parent)
|
41
|
-
node_init(parent)
|
42
|
-
end
|
43
|
-
|
44
|
-
#********************
|
45
|
-
# info methods
|
46
|
-
#********************
|
47
|
-
def next; offset+match_length end # index of first character after match
|
48
|
-
def text; src[offset,match_length] end # the substring in src matched
|
49
|
-
|
50
|
-
# length returns the number of sub-nodes
|
51
|
-
def length
|
52
|
-
0
|
53
|
-
end
|
54
|
-
|
55
|
-
def parent_list
|
56
|
-
return parent ? parent.parent_list+[parent] : []
|
57
|
-
end
|
58
|
-
|
59
|
-
def node_path
|
60
|
-
"#{parent && (parent.node_path+' > ')}#{self.class}(#{offset})"
|
61
|
-
end
|
62
|
-
|
63
|
-
#*****************************
|
64
|
-
# Array interface implementation
|
65
|
-
#*****************************
|
66
|
-
def matches # override this with function that returns array of matches to be used for Array indexing and iteration
|
67
|
-
[]
|
68
|
-
end
|
69
|
-
|
70
|
-
include Enumerable
|
71
|
-
def length
|
72
|
-
matches.length
|
73
|
-
end
|
74
|
-
|
75
|
-
def <<(node)
|
76
|
-
matches<<node
|
77
|
-
end
|
78
|
-
|
79
|
-
def add_delimiter(node)
|
80
|
-
delimiter_matches<<node
|
81
|
-
end
|
82
|
-
|
83
|
-
def [](i)
|
84
|
-
matches[i]
|
85
|
-
end
|
86
|
-
|
87
|
-
def each(&block)
|
88
|
-
matches.each(&block)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
class RootNode < Node
|
93
|
-
end
|
94
|
-
|
95
|
-
# non-terminal node
|
96
|
-
# subclassed automatically by parser.rule for each unique non-terminal
|
97
|
-
class NonTerminalNode < Node
|
98
|
-
attr_accessor :matches,:match_names
|
99
|
-
|
100
|
-
def match_names
|
101
|
-
@match_names ||= []
|
102
|
-
end
|
103
|
-
def matches
|
104
|
-
@matches ||= []
|
105
|
-
end
|
106
|
-
|
107
|
-
# length returns the number of sub-nodes
|
108
|
-
def length
|
109
|
-
matches.length
|
110
|
-
end
|
111
|
-
|
112
|
-
def matches_by_name
|
113
|
-
@matches_by_name||= begin
|
114
|
-
raise "matches.length #{matches.length} != match_names.length #{match_names.length}" unless matches.length==match_names.length
|
115
|
-
mbn={}
|
116
|
-
mn=match_names
|
117
|
-
matches.each_with_index do |match,i|
|
118
|
-
name=mn[i]
|
119
|
-
next unless name
|
120
|
-
if current=mbn[name] # name already used
|
121
|
-
# convert to MultiMatchesArray if not already
|
122
|
-
mbn[name]=MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
|
123
|
-
# add to array
|
124
|
-
mbn[name]<<match
|
125
|
-
else
|
126
|
-
mbn[name]=match
|
127
|
-
end
|
128
|
-
end
|
129
|
-
mbn
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def inspect(options={})
|
134
|
-
return "#{self.class}" if matches.length==0
|
135
|
-
matches_inspected=matches.collect{|a|a.inspect(options)}.compact
|
136
|
-
if matches_inspected.length==0 then nil
|
137
|
-
elsif matches_inspected.length==1
|
138
|
-
m=matches_inspected[0]
|
139
|
-
ret="#{self.class} > "+matches_inspected[0]
|
140
|
-
if options[:simple]
|
141
|
-
ret=if m["\n"] then m
|
142
|
-
else
|
143
|
-
# just show the first and last nodes in the chain
|
144
|
-
ret.gsub(/( > [A-Z][a-zA-Z0-9:]+ > (\.\.\. > )?)/," > ... > ")
|
145
|
-
end
|
146
|
-
end
|
147
|
-
ret
|
148
|
-
else
|
149
|
-
(["#{self.class}"]+matches_inspected).join("\n").gsub("\n","\n ")
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
#********************
|
154
|
-
# alter methods
|
155
|
-
#********************
|
156
|
-
def reset_matches_by_name
|
157
|
-
@matches_by_name=nil
|
158
|
-
end
|
159
|
-
|
160
|
-
# defines where to forward missing methods to; override for custom behavior
|
161
|
-
def forward_to
|
162
|
-
matches[0]
|
163
|
-
end
|
164
|
-
|
165
|
-
def method_missing(method_name, *args) #method_name is a symbol
|
166
|
-
unless matches_by_name.has_key? method_name
|
167
|
-
if f=forward_to
|
168
|
-
return f.send(method_name,*args)
|
169
|
-
end
|
170
|
-
raise "#{self.class}: missing method #{method_name.inspect} / doesn't match named pattern element: #{matches_by_name.keys.inspect}"
|
171
|
-
end
|
172
|
-
case ret=matches_by_name[method_name]
|
173
|
-
when EmptyNode then nil
|
174
|
-
else ret
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
# adds a match with name (optional)
|
179
|
-
# returns self so you can chain add_match or concat methods
|
180
|
-
def add_match(match,name=nil)
|
181
|
-
reset_matches_by_name
|
182
|
-
matches<<match
|
183
|
-
match_names<<name
|
184
|
-
|
185
|
-
self.match_length=match.next - offset
|
186
|
-
self
|
187
|
-
end
|
188
|
-
|
189
|
-
# concatinate all matches from another node
|
190
|
-
# returns self so you can chain add_match or concat methods
|
191
|
-
def concat(node)
|
192
|
-
names=node.match_names
|
193
|
-
node.matches.each_with_index { |match,i| add_match(match,names[i])}
|
194
|
-
self
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# generated by a :poly PatternElement
|
199
|
-
# Not subclassed
|
200
|
-
class ManyNode < Node
|
201
|
-
attr_accessor :matches,:delimiter_matches
|
202
|
-
def initialize(parent)
|
203
|
-
node_init(parent)
|
204
|
-
self.matches=[]
|
205
|
-
self.delimiter_matches=[]
|
206
|
-
end
|
207
|
-
|
208
|
-
def match_length; self.next-offset end
|
209
|
-
|
210
|
-
def next
|
211
|
-
if m=matches[-1]
|
212
|
-
m_next=m.next
|
213
|
-
if d=delimiter_matches[-1]
|
214
|
-
d_next=d.next
|
215
|
-
m_next > d_next ? m_next : d_next
|
216
|
-
else
|
217
|
-
m_next
|
218
|
-
end
|
219
|
-
else
|
220
|
-
parent.next
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def inspect_helper(list,options)
|
225
|
-
simple=options[:simple]
|
226
|
-
ret=list.collect {|a|a.inspect(options)}.compact
|
227
|
-
ret= if ret.length==0 then simple ? nil : "[]"
|
228
|
-
elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
|
229
|
-
else (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
|
230
|
-
end
|
231
|
-
ret
|
232
|
-
end
|
233
|
-
|
234
|
-
def inspect(options={})
|
235
|
-
if options[:simple]
|
236
|
-
c=[]
|
237
|
-
matches.each_with_index {|n,i| c<<n;c<<delimiter_matches[i]}
|
238
|
-
c=c.compact
|
239
|
-
inspect_helper(c,options)
|
240
|
-
else
|
241
|
-
ret=inspect_helper(matches,options)
|
242
|
-
ret+=" delimiters="+inspect_helper(delimiter_matches,options) if delimiter_matches.length>0
|
243
|
-
ret
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def method_missing(method_name, *args) #method_name is a symbol
|
248
|
-
self.map {|match| match.send(method_name,*args)}
|
249
|
-
end
|
250
|
-
|
251
|
-
end
|
252
|
-
|
253
|
-
# used for String and Regexp PatternElements
|
254
|
-
# not subclassed
|
255
|
-
class TerminalNode < Node
|
256
|
-
attr_accessor :pattern
|
257
|
-
def initialize(parent,match_length,pattern)
|
258
|
-
node_init(parent)
|
259
|
-
self.match_length=match_length
|
260
|
-
self.pattern=pattern
|
261
|
-
end
|
262
|
-
|
263
|
-
def inspect(options={})
|
264
|
-
"#{text.inspect}" unless options[:simple] && text[/^\s*$/] # if simple && node only matched white-space, return nil
|
265
|
-
end
|
266
|
-
|
267
|
-
def matches; [self]; end
|
268
|
-
end
|
269
|
-
|
270
|
-
# used when a PatternElement matchs the empty string
|
271
|
-
# Example: when the PatternElement is optional and doesn't match
|
272
|
-
# not subclassed
|
273
|
-
class EmptyNode < Node
|
274
|
-
def inspect(options={})
|
275
|
-
"EmptyNode" unless options[:simple]
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
1
|
+
%w{
|
2
|
+
node
|
3
|
+
empty_node
|
4
|
+
terminal_node
|
5
|
+
non_terminal_node
|
6
|
+
many_node
|
7
|
+
}.each do |file|
|
8
|
+
require File.join(File.dirname(__FILE__),"nodes",file)
|
9
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2011 Shane Brinkman-Davis
|
3
|
+
See README for licence information.
|
4
|
+
http://babel-bridge.rubyforge.org/
|
5
|
+
=end
|
6
|
+
|
7
|
+
module BabelBridge
|
8
|
+
|
9
|
+
# used when a PatternElement matchs the empty string
|
10
|
+
# Example: when the PatternElement is optional and doesn't match
|
11
|
+
# not subclassed
|
12
|
+
class EmptyNode < Node
|
13
|
+
def inspect(options={})
|
14
|
+
"EmptyNode" unless options[:simple]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2011 Shane Brinkman-Davis
|
3
|
+
See README for licence information.
|
4
|
+
http://babel-bridge.rubyforge.org/
|
5
|
+
=end
|
6
|
+
|
7
|
+
module BabelBridge
|
8
|
+
# generated by a :poly PatternElement
|
9
|
+
# Not subclassed
|
10
|
+
class ManyNode < Node
|
11
|
+
attr_accessor :matches,:delimiter_matches
|
12
|
+
def initialize(parent)
|
13
|
+
node_init(parent)
|
14
|
+
self.matches=[]
|
15
|
+
self.delimiter_matches=[]
|
16
|
+
end
|
17
|
+
|
18
|
+
def match_length; self.next-offset end
|
19
|
+
|
20
|
+
def next
|
21
|
+
if m=matches[-1]
|
22
|
+
m_next=m.next
|
23
|
+
if d=delimiter_matches[-1]
|
24
|
+
d_next=d.next
|
25
|
+
m_next > d_next ? m_next : d_next
|
26
|
+
else
|
27
|
+
m_next
|
28
|
+
end
|
29
|
+
else
|
30
|
+
parent.next
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect_helper(list,options)
|
35
|
+
simple=options[:simple]
|
36
|
+
ret=list.collect {|a|a.inspect(options)}.compact
|
37
|
+
ret= if ret.length==0 then simple ? nil : "[]"
|
38
|
+
elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
|
39
|
+
else (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
|
40
|
+
end
|
41
|
+
ret
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect(options={})
|
45
|
+
if options[:simple]
|
46
|
+
c=[]
|
47
|
+
matches.each_with_index {|n,i| c<<n;c<<delimiter_matches[i]}
|
48
|
+
c=c.compact
|
49
|
+
inspect_helper(c,options)
|
50
|
+
else
|
51
|
+
ret=inspect_helper(matches,options)
|
52
|
+
ret+=" delimiters="+inspect_helper(delimiter_matches,options) if delimiter_matches.length>0
|
53
|
+
ret
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(method_name, *args) #method_name is a symbol
|
58
|
+
self.map {|match| match.send(method_name,*args)}
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/nodes/node.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module BabelBridge
|
2
|
+
|
3
|
+
# this is just so we can distinguish between normal arrays and arrays of matches
|
4
|
+
# - since a match can be an Array in the case of Poly-matches
|
5
|
+
class MultiMatchesArray < Array
|
6
|
+
end
|
7
|
+
|
8
|
+
# base class for all parse-tree nodes
|
9
|
+
class Node
|
10
|
+
attr_accessor :src,:offset,:match_length,:parent,:parser
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
text
|
14
|
+
end
|
15
|
+
|
16
|
+
def node_init(parent_or_parser)
|
17
|
+
self.match_length=0
|
18
|
+
case parent_or_parser
|
19
|
+
when Parser then
|
20
|
+
self.parser=parent_or_parser
|
21
|
+
self.offset=0
|
22
|
+
self.src=parser.src
|
23
|
+
when Node then
|
24
|
+
self.parent=parent_or_parser
|
25
|
+
self.parser=parent.parser
|
26
|
+
self.offset=parent.next
|
27
|
+
self.src=parent.src
|
28
|
+
raise "parent node does not have parser set" unless parser
|
29
|
+
else
|
30
|
+
raise "parent_or_parser(#{parent_or_parser.class}) must be a Node or a Parser"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(parent)
|
35
|
+
node_init(parent)
|
36
|
+
end
|
37
|
+
|
38
|
+
# after a node has been matched, the node will get this called on itself
|
39
|
+
# It can then rewrite itself however it wishes
|
40
|
+
def post_match
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
#********************
|
45
|
+
# info methods
|
46
|
+
#********************
|
47
|
+
def next; offset+match_length end # index of first character after match
|
48
|
+
def text; src[offset,match_length] end # the substring in src matched
|
49
|
+
|
50
|
+
# length returns the number of sub-nodes
|
51
|
+
def length
|
52
|
+
0
|
53
|
+
end
|
54
|
+
|
55
|
+
def parent_list
|
56
|
+
return parent ? parent.parent_list+[parent] : []
|
57
|
+
end
|
58
|
+
|
59
|
+
def node_path
|
60
|
+
"#{parent && (parent.node_path+' > ')}#{self.class}(#{offset})"
|
61
|
+
end
|
62
|
+
|
63
|
+
#*****************************
|
64
|
+
# Array interface implementation
|
65
|
+
#*****************************
|
66
|
+
def matches # override this with function that returns array of matches to be used for Array indexing and iteration
|
67
|
+
[]
|
68
|
+
end
|
69
|
+
|
70
|
+
include Enumerable
|
71
|
+
def length
|
72
|
+
matches.length
|
73
|
+
end
|
74
|
+
|
75
|
+
def <<(node)
|
76
|
+
matches<<node
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_delimiter(node)
|
80
|
+
delimiter_matches<<node
|
81
|
+
end
|
82
|
+
|
83
|
+
def [](i)
|
84
|
+
matches[i]
|
85
|
+
end
|
86
|
+
|
87
|
+
def each(&block)
|
88
|
+
matches.each(&block)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class RootNode < Node
|
93
|
+
end
|
94
|
+
end
|