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.
- data/COPYING.LGPL +165 -0
- data/Manifest.txt +40 -0
- data/README.txt +461 -0
- data/Rakefile +26 -0
- data/lib/redparse.rb +1083 -0
- data/lib/redparse/babynodes.rb +137 -0
- data/lib/redparse/babyparser.rb +276 -0
- data/lib/redparse/decisiontree.rb +372 -0
- data/lib/redparse/node.rb +3808 -0
- data/lib/redparse/problemfiles.rb +84 -0
- data/lib/redparse/reg_more_sugar.rb +99 -0
- data/nurli/test_control.nurli +261 -0
- data/redparse.vpj +92 -0
- data/redparse.vpw +8 -0
- data/test/data/__end.rb +5 -0
- data/test/data/__f.rb +2 -0
- data/test/data/be.rb +3 -0
- data/test/data/be2.rb +6 -0
- data/test/data/bqhd.rb +3 -0
- data/test/data/bqhd2.rb +3 -0
- data/test/data/case.rb +8 -0
- data/test/data/datetime.rb +66 -0
- data/test/data/defd.rb +9 -0
- data/test/data/hd-def.rb +8 -0
- data/test/data/hd.rb +3 -0
- data/test/data/hd2.rb +3 -0
- data/test/data/hd3.rb +3 -0
- data/test/data/hd4.rb +75 -0
- data/test/data/hd5.rb +4 -0
- data/test/data/hdcat.rb +4 -0
- data/test/data/hdx.rb +3 -0
- data/test/data/heredoc.rb +3 -0
- data/test/data/if.rb +7 -0
- data/test/data/jbridge.rb +779 -0
- data/test/data/mod.rb +3 -0
- data/test/data/nl_as_strdelim.rb +7 -0
- data/test/data/pw.rb +2 -0
- data/test/data/wvt.rb +2 -0
- data/test/rp-locatetest.rb +344 -0
- data/test/test_redparse.rb +3319 -0
- metadata +113 -0
@@ -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
|
+
|