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