babel_bridge 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|