redparse 0.8.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.
@@ -0,0 +1,137 @@
1
+ require 'pp'
2
+ require 'rubygems'
3
+ require 'rubylexer'
4
+ require 'reg'
5
+
6
+
7
+
8
+
9
+ class RedParse
10
+ module Nodes
11
+ #import token classes from rubylexer
12
+ tokenclass=(::Token rescue RubyLexer::Token)
13
+ ObjectSpace.each_object(Class){|k|
14
+ k<=tokenclass and self.const_set k.name[/[^:]+$/], k
15
+ }
16
+
17
+ module SimpleToLisp
18
+ def to_lisp; to_s end
19
+ end
20
+ [NumberToken, SymbolToken, VarNameToken, MethNameToken].each{|tokclass|
21
+ tokclass.send :include, SimpleToLisp
22
+ }
23
+
24
+ class Token
25
+ def to_parsetree; [parsetree] end
26
+ end
27
+
28
+ class NumberToken
29
+ def parsetree
30
+ [:lit, ident]
31
+ end
32
+ end
33
+
34
+ class KeywordToken
35
+ def not_real!
36
+ @not_real=true
37
+ end
38
+
39
+ def not_real?
40
+ @not_real if defined? @not_real
41
+ end
42
+ end
43
+
44
+ class SymbolToken
45
+ def parsetree
46
+ [:lit, ident[1..-1].to_sym]
47
+ end
48
+ end
49
+
50
+ class VarNameToken
51
+ def parsetree;
52
+ type=case ident[0]
53
+ when ?$: :gvar
54
+ when ?@: ident[1]==?@ ? :cvar : :ivar
55
+ when ?A..?Z: :const
56
+ else :lvar
57
+ end
58
+ [type,ident.to_sym]
59
+ end
60
+ end
61
+
62
+ class ExprNode
63
+ def initialize(*data)
64
+ @data=data
65
+ end
66
+
67
+ attr :data
68
+ alias unwrap data
69
+
70
+ def to_parsetree; [parsetree] end
71
+ def parsetree
72
+ "wrong(#{inspect})"
73
+ end
74
+
75
+ def parsetrees list
76
+ !list.empty? and list.map{|node| node.parsetree}
77
+ end
78
+ end
79
+ class OpNode<ExprNode
80
+ def initialize(expr1,op,expr2)
81
+ super
82
+ end
83
+
84
+ def to_lisp
85
+ "(#{@data[1]} #{@data[0].to_lisp} #{@data[2].to_lisp})"
86
+ end
87
+
88
+ def parsetree
89
+ [:call,
90
+ @data[0].parsetree,
91
+ @data[1].ident.to_sym,
92
+ [:array, @data[2].parsetree]
93
+ ]
94
+ end
95
+ end
96
+
97
+ class UnOpNode<ExprNode
98
+ def initialize(op,val)
99
+ @ident=op.ident
100
+ super(op,val)
101
+ end
102
+
103
+ attr :ident
104
+
105
+ def to_lisp
106
+ "(#{@data[0]} #{@data[1].to_lisp})"
107
+ end
108
+
109
+ def parsetree
110
+ case @data[0].ident
111
+ when /^[*&]/: "huh"
112
+ when "!": [:not, @data[1].parsetree]
113
+ when "defined?": [:defined, @data[1].parsetree]
114
+ else
115
+ [:call, @data[1].parsetree, @data[0].ident.to_sym]
116
+ end
117
+ end
118
+ end
119
+
120
+ class ParenedNode<ExprNode
121
+ def initialize(*args)
122
+ @data=[args[1]]
123
+ end
124
+
125
+ def to_lisp
126
+ @data.first.to_lisp
127
+ end
128
+
129
+ def parsetree
130
+ @data.first.parsetree
131
+ end
132
+ end
133
+
134
+ end
135
+ end
136
+
137
+
@@ -0,0 +1,276 @@
1
+ require 'pp'
2
+ require 'rubygems'
3
+ require 'rubylexer'
4
+ require 'reg'
5
+
6
+ require "babynodes"
7
+
8
+
9
+
10
+ class RedParse
11
+ include Nodes
12
+
13
+ unless defined? ::Reg::Transform
14
+ #hack, until support for this syntax makes it into the release of reg
15
+ module ::Reg
16
+ class Transform
17
+ def initialize(left,right)
18
+ @left,@right=left,right
19
+ end
20
+ attr_reader :left,:right
21
+ end
22
+ module Reg
23
+ def >>(rep)
24
+ Transform.new(self,rep)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ #see pickaxe, 1st ed, page 221
31
+ RIGHT_ASSOCIATIVE={
32
+ "**"=>118,
33
+
34
+ "="=>105, "%="=>105, "/="=>105, "-="=>105, "+="=>105,
35
+ "|="=>105, "&="=>105, ">>="=>105, "<<="=>105, "*="=>105,
36
+ "&&="=>105, "||="=>105, "**="=>105, "^="=>105,
37
+ }
38
+ PRECEDENCE={
39
+ "::"=>120, "."=>120,
40
+
41
+ "["=>119, "("=>119, "{"=>119, #[] []=
42
+
43
+ "**"=>118,
44
+
45
+ "+@"=>117, "-@"=>117, "!"=>117, "~"=>117,
46
+
47
+ "*"=>116, "/"=>116, "%"=>116,
48
+
49
+ "+"=>115, "-"=>115,
50
+
51
+ "<<"=>114, ">>"=>114,
52
+
53
+ "&"=>113,
54
+
55
+ "^"=>112, "|"=>112,
56
+
57
+ "<="=>111, ">="=>111, "<"=>111, ">"=>111,
58
+
59
+ "<=>"=>110, "=="=>110, "==="=>110,
60
+ "!="=>110, "=~"=>110, "!~"=>110,
61
+
62
+ "&&"=>109,
63
+
64
+ "||"=>108,
65
+
66
+ ".."=>107, "..."=>107,
67
+
68
+ "?"=>106, # ":"=>106, #not sure what to do with ":"
69
+
70
+ "="=>105, "%="=>105, "/="=>105, "-="=>105, "+="=>105,
71
+ "|="=>105, "&="=>105, ">>="=>105, "<<="=>105, "*="=>105,
72
+ "&&="=>105, "||="=>105, "**="=>105, "^="=>105,
73
+
74
+ "*@"=>104, "&@"=>104, #unary * and & operators
75
+
76
+ "defined?"=>103,
77
+
78
+ ":"=>102.5,
79
+
80
+ "not"=>102,
81
+ "=>"=>101,
82
+ ","=>100,
83
+
84
+ #"unary" prefix function names seen has operators have this precedence
85
+ #but, rubylexer handles precedence of these and outputs fake parens
86
+ #to tell us how its parsed
87
+
88
+ "or"=>99, "and"=>99,
89
+
90
+ "if"=>98, "unless"=>98, "while"=>98, "until"=>98,
91
+
92
+ ";"=>97,
93
+ }
94
+
95
+ Expr=NumberToken|SymbolToken|
96
+ VarNameToken|MethNameToken|
97
+ HerePlaceholderToken|
98
+ ExprNode
99
+
100
+ UNOP=(OperatorToken|KeywordToken)&-{ #sppflt! KeywordToken here is a hack too
101
+ :ident=>%r[^([*&+-]@|[~!]|not|defined\?)$],
102
+ }
103
+
104
+ #these ought to be regular operators, fer gosh sake
105
+ BINOP_KEYWORDS=
106
+ %w[
107
+ if unless while until and or rescue
108
+ && || :: => !~ . .. ...
109
+ : , != == >= <=
110
+ = %= /= -= \+=
111
+ \|= &= >>= <<= \*=
112
+ &&= \|\|= \*\*= \^=
113
+ ]
114
+ KeywordOp=
115
+ KeywordToken & -{
116
+ :ident=>/^(#{BINOP_KEYWORDS.join '|'}|[+*?])$/
117
+ }
118
+
119
+ Op=OperatorToken|KeywordOp
120
+
121
+ LowerOp=proc{|list,op2|
122
+ op=list[-2]
123
+ if Op===op2 or /^[\[({]$/===op2.ident
124
+ rightprec=PRECEDENCE[op2.to_s] or fail "unrecognized right operator: #{op2.inspect}"
125
+ rightprec+=0.001 if RIGHT_ASSOCIATIVE[op2.to_s]
126
+ PRECEDENCE[op.to_s]>=rightprec
127
+ else true
128
+ end
129
+ }
130
+
131
+ #rule format:
132
+ # syntax pattern_matchers.+, lookahead.-, node type
133
+ RULES=[
134
+ -[UNOP, Expr, LowerOp]>>UnOpNode,
135
+ -[Expr, Op, Expr, LowerOp]>>OpNode,
136
+ -['(', Expr, ')']>>ParenedNode,
137
+ ]
138
+
139
+
140
+
141
+
142
+ def initialize(input,name="(eval)")
143
+ @lexer=RubyLexer.new(name,input)
144
+ end
145
+
146
+ def get_token
147
+ begin
148
+ case result=@lexer.get1token
149
+ when OperatorToken:
150
+ /^[*&+-]$/===result.ident and !(Expr===@last) and result.ident<<"@"
151
+ when NewlineToken:
152
+ result= KeywordToken.new(';',result.offset)
153
+ when EoiToken:
154
+ when IgnoreToken:
155
+ redo
156
+ end
157
+ end while false
158
+
159
+ p result if ENV['PRINT_TOKENS']
160
+
161
+ return @last=result
162
+ end
163
+
164
+ def evaluate rule,stack
165
+ #dissect the rule
166
+ Reg::Transform===rule or fail
167
+ node_type= rule.right
168
+ rule=rule.left.subregs.dup
169
+ lookahead_processor=(rule.pop if Proc===rule.last)
170
+
171
+ #index of data at which to start matching
172
+ i=stack.size-2 #-1 because last element of stack is always lookahead
173
+
174
+ compiled_rule=
175
+ rule.map{|pattern|
176
+ String|Regexp===pattern ?
177
+ -{:class=>+KeywordToken, :ident=>pattern} :
178
+ pattern
179
+ }
180
+
181
+ #what's the minimum stack size this rule could match?
182
+ i>=compiled_rule.size-1 or return false
183
+
184
+ matching=[]
185
+
186
+ #actually try to match rule elements against each stack element in turn
187
+ compiled_rule.reverse_each{|matcher|
188
+ return false unless matcher===stack[i] #try match
189
+
190
+ matching.unshift stack[i]
191
+ i-=1
192
+ }
193
+
194
+
195
+ #give lookahead matcher (if any) a chance to fail the match
196
+ if lookahead_processor
197
+ return false unless lookahead_processor[matching,stack.last]
198
+ end
199
+
200
+ #replace matching elements in stack with node type found
201
+ matchrange= i+1...-1 #what elems in stack were matched?
202
+ if Proc===node_type
203
+ node_type[stack]
204
+ else
205
+ stack[matchrange]=node_type.new(*matching)
206
+ end
207
+
208
+ return true #let caller know we found a match
209
+
210
+
211
+ rescue Exception
212
+ puts "error while executing rule: #{rule.inspect}"
213
+ raise
214
+ end
215
+
216
+ def parse
217
+ stack=[get_token]
218
+ #last token on stack is always implicitly the lookahead
219
+ loop {
220
+ #try all possible reductions
221
+ RULES.reverse_each{|rule|
222
+ evaluate(rule,stack) and break
223
+ } or next
224
+
225
+ #no rule can match current stack, get another token
226
+ tok=get_token
227
+
228
+ #are we done yet?
229
+ tok.nil? or EoiToken===tok && EoiToken===stack.last and break
230
+
231
+ #shift our token onto the stack
232
+ stack.push tok
233
+ }
234
+
235
+ #unless the stack is 2 tokens,
236
+ #with the last an Eoi
237
+ #there was a parse error
238
+ unless stack.size==2
239
+ pp stack[-[15,stack.size].min..-1]
240
+ fail "parse error"
241
+ end
242
+ EoiToken===stack.last or fail
243
+
244
+ return stack.first
245
+ end
246
+ end
247
+
248
+
249
+ if __FILE__==$0
250
+ output=:pp
251
+ while /^-/===ARGV.first
252
+ case opt=ARGV.shift
253
+ when "--": break
254
+ when "--pp": output=:pp
255
+ when "--lisp": output=:lisp
256
+ when "--parsetree": output=:parsetree
257
+ when "-e": input=ARGV.join(" "); name="-e"; break
258
+ else fail "unknown option: #{opt}"
259
+ end
260
+ end
261
+
262
+
263
+ unless input
264
+ input=open(ARGV.first) rescue STDIN
265
+ name=ARGV.first || "-"
266
+ end
267
+ tree=RedParse.new(input,name).parse
268
+ case output
269
+ when :pp
270
+ pp tree
271
+ when :lisp
272
+ puts tree.to_lisp
273
+ when :parsetree
274
+ pp tree.to_parsetree
275
+ end
276
+ end
@@ -0,0 +1,372 @@
1
+ =begin
2
+ redparse - a ruby parser written in ruby
3
+ Copyright (C) 2008 Caleb Clausen
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'rubygems'
20
+ require 'rubylexer'
21
+ require 'reg'
22
+ require 'tempfile'
23
+ require 'pp'
24
+
25
+
26
+
27
+
28
+ class Class
29
+ class FlattenedHierarchy
30
+ def self.child_relations_among(*classes)
31
+ classes.unshift Object
32
+ result={}
33
+ classes.each{|klass| result[klass]=[] }
34
+
35
+ classes.each{|klass| klass.ancestors.each{|anc|
36
+ if anc=result[anc]
37
+ anc << klass
38
+ break
39
+ end
40
+ }}
41
+
42
+ return result
43
+ end
44
+ alias child_relationships_among child_relations_among
45
+
46
+ def self.sort_by_inheiritance(*classes)
47
+ children_of=child_relations_among(*classes)
48
+ result=[]
49
+ class_eater=proc{|klass|
50
+ result << klass
51
+ children_of[klass].each(&class_eater)
52
+ }
53
+ class_eater[::Object]
54
+ #classes.each(&class_eater)
55
+
56
+ return result
57
+ end
58
+
59
+ def initialize(*classes)
60
+ @sorted=FlattenedHierarchy.sort_by_inheiritance(*classes)
61
+ @ranges=create_member_ranges
62
+ end
63
+
64
+ def range klass
65
+ @ranges[klass]
66
+ end
67
+
68
+ attr :ranges
69
+
70
+ def overlapping_class_range_list(classes)
71
+ classes=@sorted&classes
72
+ return classes,classes.map{|k| @ranges[k] }
73
+ end
74
+
75
+ def nonoverlapping_class_range_list(classes)
76
+ classes,ranges=overlapping_class_range_list(classes)
77
+
78
+ myclasses=[];myranges=[]
79
+ classes_index_stack=[]
80
+ classes.each_with_index{|k,i|
81
+ x=ranges[i].first
82
+ if i>0 and ranges[i-1]===x #if overlaps previous range
83
+ classes_index_stack.push i-1 #add to the stack of saved-up ranges
84
+ else
85
+ #pop off old superclasses that no longer apply
86
+ #to this k, adding trailing fragments for the
87
+ #ranges of those superclasses as we go along
88
+ until classes_index_stack.empty?
89
+ current_range=ranges[classes_index_stack.last]
90
+ break if current_range===x #stop if this superclass still applies
91
+
92
+ ending=classes_index_stack.pop
93
+
94
+ #trailing fragment
95
+ done_thru=myranges.last.last
96
+ current_end=current_range.last
97
+ unless done_thru==current_end
98
+ myranges << (done_thru+1..current_end)
99
+ myclasses << classes[ending]
100
+ end
101
+ end
102
+ end
103
+
104
+ #if a gap between (sub-?)classes, emit a fragment for the appropriate super (or default to nil)
105
+ next_expected=myranges.last.last+1
106
+ if next_expected!=x #was: (ranges[i].huh)
107
+ myclasses<< (classes[classes_index_stack.last] unless classes_index_stack.empty?)
108
+ myranges<<(next_expected..x-1)
109
+ end
110
+
111
+ #emit initial fragment for current class
112
+ myclasses << k
113
+ myranges << (x..[ranges[i+1].first-1,ranges[i].last].min)
114
+ }
115
+
116
+ return myclasses, myranges
117
+ end
118
+
119
+ def nonoverlapping_results_range_list(class2results)
120
+ classes=class2results.keys
121
+ classes,ranges=nonoverlapping_class_range_list(classes)
122
+ return classes.map{|k| class2results[k] }, ranges
123
+ end
124
+
125
+ private
126
+ def last_member_num(klass,i)
127
+ @sorted[i]==klass or fail
128
+ low=i
129
+ high=@sorted.size
130
+ until low==high
131
+ mid=(low+high)/2
132
+ if classes[mid]<=klass
133
+ low=mid
134
+ else
135
+ high=mid
136
+ end
137
+ end
138
+
139
+ return high
140
+ end
141
+
142
+ def create_member_ranges
143
+ result={}
144
+ @sorted.each_with_index{|klass,i|
145
+ result[klass]=i..last_member_num(klass,i)
146
+ }
147
+ return result
148
+ end
149
+ end
150
+
151
+ =begin
152
+ attr_accessor :member_range
153
+ def member_num; member_range.first end
154
+
155
+ =end
156
+
157
+ class DecisionTable<Array
158
+ def initialize(fh,class2result)
159
+ @hierarchy=fh
160
+ classes,ranges=fh.nonoverlapping_results_range_list(class2result)
161
+ newme=[]
162
+ classes.each_with_index{|k,i| newme.push k,ranges[i]}
163
+ replace newme
164
+ end
165
+ class<<self; alias [] new; end
166
+
167
+ def inspect
168
+ "Class::DecisionTable"+super
169
+ end
170
+ def pretty_print(q)
171
+ q.group(1, 'Class::DecisionTable[', ']') {
172
+ q.seplist(self) {|v| q.pp v }
173
+ }
174
+ end
175
+
176
+ def decide(klass,member_ranges)
177
+ decide_from_classid(member_ranges[klass])
178
+ end
179
+ def decide_from_classid(x)
180
+ huh broken
181
+ if x.send op, val
182
+ use=iftrue
183
+ else
184
+ use=iffalse
185
+ end
186
+ return use.decide_from_classid(x) if DecisionTree===use
187
+ return use
188
+ end
189
+
190
+ =begin
191
+ def to_ruby
192
+ "
193
+ if (x#{op}#{val}) #{DecisionTree===iftrue ? iftrue.to_ruby : ref_to iftrue};
194
+ else #{DecisionTree===iffalse ? iffalse.to_ruby : ref_to iffalse};
195
+ end
196
+ "
197
+ end
198
+
199
+ def to_c
200
+ "
201
+ if (x#{op}#{val}) #{DecisionTree===iftrue ? iftrue.to_c : c_ref_to iftrue};
202
+ else #{DecisionTree===iffalse ? iffalse.to_c : c_ref_to iffalse};
203
+ "
204
+ end
205
+ =end
206
+
207
+ =begin
208
+ def self.overlapping_class_range_list(classes,member_ranges)
209
+ classes=sort_by_inheiritance(*classes)
210
+ return classes,classes.map{|k| member_ranges[k] }
211
+ end
212
+
213
+ def self.nonoverlapping_class_range_list(classes,member_ranges)
214
+ classes,ranges=overlapping_class_range_list(classes,member_ranges)
215
+
216
+ myclasses=[];myranges=[]
217
+ classes_index_stack=[]
218
+ classes.each_with_index{|k,i|
219
+ x=ranges[i].first
220
+ if i>0 and ranges[i-1]===x #if overlaps previous range
221
+ classes_index_stack.push i-1 #add to the stack of saved-up ranges
222
+ else
223
+ #pop off old superclasses that no longer apply to k,
224
+ #adding regions for their last fragment as we go along
225
+ until classes_index_stack.empty?
226
+ current_range=ranges[classes_index_stack.last]
227
+ break if current_range===x
228
+ ending=classes_index_stack.pop
229
+ done_thru=myranges.last.last
230
+ current_end=current_range.last
231
+ unless done_thru==current_end
232
+ myranges<<(done_thru+1..current_end)
233
+ myclasses<<classes[ending]
234
+ end
235
+ end
236
+ end
237
+
238
+ #if a gap between (sub-?)classes, emit a fragment for the appropriate super (or default to nil)
239
+ next_expected=myranges.last.last+1
240
+ if next_expected!=x #was: (ranges[i].huh)
241
+ myclasses<< (classes[classes_index_stack.last] unless classes_index_stack.empty?)
242
+ myranges<<(next_expected..x-1)
243
+ end
244
+
245
+ #emit initial fragment for current class
246
+ myclasses<<k
247
+ myranges<<(x..[ranges[i+1].first-1,ranges[i].last].min)
248
+ }
249
+
250
+ return myclasses, myranges
251
+ end
252
+
253
+ def self.nonoverlapping_results_range_list(class2results,member_ranges)
254
+ classes=class2results.keys
255
+ classes,ranges=nonoverlapping_class_range_list(classes,member_ranges)
256
+ return classes.map{|k| class2results[k] }, ranges
257
+ end
258
+
259
+
260
+ =end
261
+ def to_ruby(low=0,high=results.size-1)
262
+ #if downto a list of just 1 possibility
263
+ #then return the corresponding result
264
+ return ref_to self[1+2*low] if high==low
265
+ low<high or fail
266
+
267
+ mid=((high+low+0.5)/2).to_i #midpoint of remaining list
268
+ mid_class_id=self[2*mid].first
269
+ "
270
+ if (x<#{mid_class_id})
271
+ #{to_ruby(low,mid-1)};
272
+ else
273
+ #{to_ruby(mid,high)};
274
+ end
275
+ "
276
+ end
277
+
278
+ def to_c(low=0,high=self.size-1)
279
+ #if downto a list of just 1 possibility
280
+ #then return the corresponding result
281
+ return "return "+c_ref_to(self[1+2*low]) if high==low
282
+ low<high or fail
283
+
284
+ mid=((high+low+0.5)/2).to_i #midpoint of remaining list
285
+ mid_class_id=self[2*mid].first
286
+ "
287
+ if (x<#{mid_class_id})
288
+ #{to_c(low,mid-1)};
289
+ else
290
+ #{to_c(mid,high)};
291
+ "
292
+ end
293
+
294
+ def ref_to obj
295
+ @ruby_refs= defined?(@ruby_refs) ? @ruby_refs+1 : 1
296
+ result="@ruby_ref_#{@ruby_refs}"
297
+ instance_variable_set result, obj
298
+ return result
299
+ end
300
+
301
+ def c_ref_to(obj)
302
+ @dont_delete_yet||=[]
303
+ @dont_delete_yet << obj #keep a real ref around so that weak ref in inline c code
304
+ #keeps pointing to the right object.
305
+ return "(VALUE)0x#{obj.object_id.to_s(16)}L"
306
+ end
307
+
308
+ def compile_to_ruby
309
+ eval " def self.decide_from_classid(x)\n#{to_ruby}\n end"
310
+ end
311
+
312
+ def compile_to_c
313
+ huh "this prolly won't work cause inline c code has to be in an actual class"
314
+ huh 'so,, need to rewrite as temporary class (module?) for the method to live in... then delegate to that'
315
+
316
+ begin
317
+ require 'rubygems'
318
+ rescue Exception
319
+ end
320
+ require 'inline'
321
+
322
+ class << self
323
+ inline{|write| write.c %{
324
+ static VALUE decide_from_classid(unsigned x){
325
+ #{to_c}
326
+ }
327
+ } }
328
+ end
329
+ end
330
+
331
+ def compile
332
+ begin
333
+ compile_to_c
334
+ rescue Exception
335
+ compile_to_ruby
336
+ end
337
+ end
338
+ end
339
+
340
+
341
+
342
+ =begin
343
+ def Class.create_decision_tree(class2result,member_ranges,sorted_list,default=class2result.default)
344
+ sorted_list=sorted_list&class2result.keys
345
+ _create_decision_tree(class2result,member_ranges,sorted_list,default)
346
+ end
347
+
348
+ def Class._create_decision_tree(class2result,member_ranges,sorted_list,default,limits=0..sorted_list.size-1)
349
+ case sorted_list.size
350
+ when 0: default #is this right???
351
+ when 1
352
+ klass=sorted_list.first
353
+ x=member_ranges[klass].last
354
+ if (huh member_ranges[sorted_list[limits.first]].first..member_ranges[sorted_lists[limits.last]].last)===x
355
+ DecisionTree[:<=, x, class2result[klass], default]
356
+ else huh
357
+ end
358
+ else
359
+ mid=sorted_list.size/2
360
+ klass=sorted_list[mid]
361
+ DecisionTree[
362
+ :<, member_ranges[klass].first,
363
+ _create_decision_tree(class2result,member_ranges,sorted_list[0...mid],default,limits.first..mid-1),
364
+ _create_decision_tree(class2result,member_ranges,sorted_list[mid..-1],default,mid..limits.last)
365
+ ]
366
+ end
367
+ end
368
+ =end
369
+
370
+ end
371
+
372
+