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.
@@ -1,278 +1,9 @@
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
- # this is just so we can distinguish between normal arrays and arrays of matches
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
@@ -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