redparse 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+