crokus 0.0.2
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.
- checksums.yaml +7 -0
- data/bin/crokus +5 -0
- data/lib/crokus.rb +4 -0
- data/lib/crokus/ast.rb +431 -0
- data/lib/crokus/ast_printer.rb +86 -0
- data/lib/crokus/cfg.rb +81 -0
- data/lib/crokus/cfg_builder.rb +169 -0
- data/lib/crokus/cfg_cleaner.rb +62 -0
- data/lib/crokus/cfg_printer.rb +73 -0
- data/lib/crokus/cleaner.rb +10 -0
- data/lib/crokus/code.rb +46 -0
- data/lib/crokus/compiler.rb +124 -0
- data/lib/crokus/generic_lexer.rb +62 -0
- data/lib/crokus/indent.rb +19 -0
- data/lib/crokus/ir_dumper.rb +48 -0
- data/lib/crokus/lexer.rb +113 -0
- data/lib/crokus/parser.rb +1072 -0
- data/lib/crokus/parser_only.rb +993 -0
- data/lib/crokus/pretty_printer.rb +443 -0
- data/lib/crokus/runner.rb +86 -0
- data/lib/crokus/tac_builder.rb +109 -0
- data/lib/crokus/token.rb +43 -0
- data/lib/crokus/transformer.rb +304 -0
- data/lib/crokus/version.rb +3 -0
- data/lib/crokus/visitor.rb +341 -0
- metadata +70 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'ast'
|
2
|
+
require_relative 'indent'
|
3
|
+
require_relative 'code'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
module Crokus
|
7
|
+
|
8
|
+
class AstPrinter
|
9
|
+
|
10
|
+
include Indent
|
11
|
+
|
12
|
+
attr_accessor :code,:nodes_decl,:nodes_cnx
|
13
|
+
|
14
|
+
def print ast #entry method
|
15
|
+
@verbose=false
|
16
|
+
@nodes_decl=Code.new
|
17
|
+
@nodes_cnx=Code.new
|
18
|
+
@printed_cnx={} #Cosmetic ! to keep track of already printed cnx source->sink
|
19
|
+
@code=Code.new
|
20
|
+
code << "digraph G {"
|
21
|
+
code.indent=2
|
22
|
+
code << "ordering=out;"
|
23
|
+
code << "ranksep=.4;"
|
24
|
+
code << "bgcolor=\"lightgrey\";"
|
25
|
+
code.newline
|
26
|
+
code << "node [shape=box, fixedsize=false, fontsize=12, fontname=\"Helvetica-bold\", fontcolor=\"blue\""
|
27
|
+
code << " width=.25, height=.25, color=\"black\", fillcolor=\"white\", style=\"filled, solid, bold\"];"
|
28
|
+
code << "edge [arrowsize=.5, color=\"black\", style=\"bold\"]"
|
29
|
+
process(ast)
|
30
|
+
code << @nodes_decl
|
31
|
+
code << @nodes_cnx
|
32
|
+
code.indent=0
|
33
|
+
code << "}"
|
34
|
+
clean(code)
|
35
|
+
return code
|
36
|
+
end
|
37
|
+
|
38
|
+
def process node,level=0
|
39
|
+
#puts "processing #{node}"
|
40
|
+
kname=node.class.name.split("::")[1]
|
41
|
+
id=node.object_id
|
42
|
+
(nodes_decl << "#{id} [label=\"#{kname}\"]")
|
43
|
+
|
44
|
+
node.instance_variables.each{|vname|
|
45
|
+
ivar=node.instance_variable_get(vname)
|
46
|
+
vname=vname.to_s[1..-1]
|
47
|
+
if ivar
|
48
|
+
case ivar
|
49
|
+
when Array
|
50
|
+
ivar.each_with_index{|e,idx|
|
51
|
+
sink=process(e,level+2)
|
52
|
+
@printed_cnx[id]||=[]
|
53
|
+
nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}[#{idx}]\"]" if not @printed_cnx[id].include? sink
|
54
|
+
@printed_cnx[id] << sink
|
55
|
+
}
|
56
|
+
when Token
|
57
|
+
val=ivar.val
|
58
|
+
sink="#{ivar.object_id}"
|
59
|
+
nodes_decl << "#{sink} [label=\"#{val}\",color=\"red\"]"
|
60
|
+
@printed_cnx[id]||=[]
|
61
|
+
nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
|
62
|
+
@printed_cnx[id] << sink
|
63
|
+
else
|
64
|
+
sink=process(ivar,level+2)
|
65
|
+
@printed_cnx[id]||=[]
|
66
|
+
nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
|
67
|
+
@printed_cnx[id] << sink
|
68
|
+
end
|
69
|
+
end
|
70
|
+
}
|
71
|
+
return id
|
72
|
+
end
|
73
|
+
|
74
|
+
# suppress syndrom : name=""not correct""
|
75
|
+
def clean code
|
76
|
+
#puts "=> cleaning dot code"
|
77
|
+
code.lines.each_with_index do |line,idx|
|
78
|
+
if line=~/\"\"(.*)\"\"/
|
79
|
+
code.lines[idx].gsub!(/\"\"(.*)\"\"/,"\"#{$1}\"")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end #class
|
86
|
+
end #module
|
data/lib/crokus/cfg.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'ast'
|
2
|
+
require_relative 'cfg_printer'
|
3
|
+
|
4
|
+
|
5
|
+
module Crokus
|
6
|
+
|
7
|
+
class ITE < Ast
|
8
|
+
attr_accessor :cond,:trueBranch,:falseBranch
|
9
|
+
def initialize cond,bb_t,bb_f
|
10
|
+
@cond=cond
|
11
|
+
@trueBranch=bb_t
|
12
|
+
@falseBranch=bb_f
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class CFG
|
17
|
+
attr_accessor :name,:bbs,:starter
|
18
|
+
def initialize name
|
19
|
+
@name=name
|
20
|
+
@bbs=[]
|
21
|
+
@bbs << @starter=BasicBlock.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
@bbs.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def <<(bb)
|
29
|
+
@bbs << bb
|
30
|
+
end
|
31
|
+
|
32
|
+
def print
|
33
|
+
CFGPrinter.new.print(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class BasicBlock
|
38
|
+
@@id=-1
|
39
|
+
attr_accessor :id,:stmts,:succs
|
40
|
+
|
41
|
+
alias :label :id
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@@id+=1
|
45
|
+
@id="L"+@@id.to_s
|
46
|
+
@stmts=[]
|
47
|
+
@succs=[]
|
48
|
+
end
|
49
|
+
|
50
|
+
def <<(e)
|
51
|
+
@stmts << e
|
52
|
+
end
|
53
|
+
|
54
|
+
def to bb
|
55
|
+
@succs << bb
|
56
|
+
end
|
57
|
+
|
58
|
+
def trueBranch
|
59
|
+
unless @succs.size==2
|
60
|
+
raise "request for trueBranch failed because #{@succs.size} branch(es) found. Strange."
|
61
|
+
end
|
62
|
+
return @succs.first
|
63
|
+
end
|
64
|
+
|
65
|
+
def falseBranch
|
66
|
+
unless @succs.size==2
|
67
|
+
raise "request for falseBranch failed because #{@succs.size} branch(es) found. Strange."
|
68
|
+
end
|
69
|
+
return @succs.last
|
70
|
+
end
|
71
|
+
|
72
|
+
def code4dot
|
73
|
+
@ppr||=PrettyPrinter.new
|
74
|
+
@stmts.compact.collect{|stmt| stmt.accept(@ppr)}.join("\n")
|
75
|
+
end
|
76
|
+
|
77
|
+
def size
|
78
|
+
@stmts.size
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative 'cfg'
|
2
|
+
require_relative 'cfg_cleaner'
|
3
|
+
|
4
|
+
require_relative 'visitor'
|
5
|
+
require_relative 'cleaner'
|
6
|
+
|
7
|
+
module Crokus
|
8
|
+
|
9
|
+
class CFGBuilder < Transformer
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@ind=-2
|
13
|
+
@verbose=false
|
14
|
+
end
|
15
|
+
|
16
|
+
def build ast
|
17
|
+
ast.accept(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def visitFunction func,args=nil
|
21
|
+
puts " |--> visitFunction '#{func.name}'" unless $options[:mute]
|
22
|
+
@cfg=CFG.new(func.name)
|
23
|
+
@current=@cfg.starter
|
24
|
+
func.body.accept(self)
|
25
|
+
@cfg.print
|
26
|
+
@cfg=CFGCleaner.new.clean(@cfg)
|
27
|
+
@cfg.name=Ident.new(Token.create "#{@cfg.name}_clean")
|
28
|
+
func.cfg=@cfg
|
29
|
+
puts "\t|--> cfg size for '#{func.name}' : #{@cfg.size}" unless $options[:mute]
|
30
|
+
@cfg.print
|
31
|
+
end
|
32
|
+
|
33
|
+
def visitBody body,args=nil
|
34
|
+
body.each{|stmt| stmt.accept(self,args)}
|
35
|
+
end
|
36
|
+
#...........stmts...............
|
37
|
+
def visitAssign assign,args=nil
|
38
|
+
@current << assign
|
39
|
+
end
|
40
|
+
|
41
|
+
def visitPreFixAccu accu,args=nil
|
42
|
+
@current << accu
|
43
|
+
end
|
44
|
+
|
45
|
+
def visitPostFixAccu accu,args=nil
|
46
|
+
@current << accu
|
47
|
+
end
|
48
|
+
|
49
|
+
def visitSwitch switch,args=nil
|
50
|
+
@cfg << finalBranch=BasicBlock.new
|
51
|
+
@current_break_dest=finalBranch
|
52
|
+
for cas in switch.cases
|
53
|
+
cond=Binary.new(switch.expr,EQUAL,cas.expr)
|
54
|
+
@cfg << trueBranch=BasicBlock.new
|
55
|
+
@cfg << falseBranch=BasicBlock.new
|
56
|
+
|
57
|
+
@current << ITE.new(cond,trueBranch,falseBranch)
|
58
|
+
@current.to trueBranch
|
59
|
+
@current.to falseBranch
|
60
|
+
@current = trueBranch
|
61
|
+
cas.body.accept(self)
|
62
|
+
@current = falseBranch
|
63
|
+
end
|
64
|
+
if switch.default
|
65
|
+
switch.default.accept(self)
|
66
|
+
@current.to finalBranch
|
67
|
+
end
|
68
|
+
@current=finalBranch
|
69
|
+
end
|
70
|
+
|
71
|
+
def visitBreak brk,args=nil
|
72
|
+
@current << brk
|
73
|
+
@current.to @current_break_dest
|
74
|
+
@cfg << unreachable = BasicBlock.new
|
75
|
+
@current = unreachable
|
76
|
+
end
|
77
|
+
|
78
|
+
def visitContinue cont,args=nil
|
79
|
+
@current << cont
|
80
|
+
@current.to @current_continue_dest
|
81
|
+
@cfg << unreachable = BasicBlock.new
|
82
|
+
@current = unreachable
|
83
|
+
end
|
84
|
+
|
85
|
+
def visitIf if_,args=nil
|
86
|
+
cond=if_.cond.accept(self,:as_expr)
|
87
|
+
@cfg << trueBranch =BasicBlock.new
|
88
|
+
@cfg << falseBranch=BasicBlock.new
|
89
|
+
@cfg << mergeBranch=BasicBlock.new
|
90
|
+
@current << ITE.new(cond,trueBranch,falseBranch)
|
91
|
+
|
92
|
+
@current.to trueBranch
|
93
|
+
@current.to falseBranch
|
94
|
+
#-----------
|
95
|
+
@current=trueBranch
|
96
|
+
if_.body.accept(self) #may change @current !
|
97
|
+
@current.to mergeBranch
|
98
|
+
#
|
99
|
+
@current=falseBranch
|
100
|
+
if_.else.accept(self) if if_.else #may change @current !
|
101
|
+
@current.to mergeBranch
|
102
|
+
@current=mergeBranch
|
103
|
+
end
|
104
|
+
|
105
|
+
def visitElse else_,args=nil
|
106
|
+
else_.body.accept(self)
|
107
|
+
end
|
108
|
+
|
109
|
+
def visitWhile while_,args=nil
|
110
|
+
cond=while_.cond.accept(self,:as_expr)
|
111
|
+
@cfg << cond_bb = BasicBlock.new
|
112
|
+
@current_cond = cond_bb # for continue stmt !
|
113
|
+
@cfg << trueBranch = BasicBlock.new
|
114
|
+
@cfg << falseBranch = BasicBlock.new
|
115
|
+
@current.to cond_bb
|
116
|
+
cond_bb << ITE.new(cond,trueBranch,falseBranch)
|
117
|
+
cond_bb.to trueBranch
|
118
|
+
cond_bb.to falseBranch
|
119
|
+
@current = trueBranch
|
120
|
+
while_.body.accept(self) #may modify identity of @current
|
121
|
+
@current.to cond_bb
|
122
|
+
@current=falseBranch
|
123
|
+
end
|
124
|
+
|
125
|
+
def visitFor for_,args=nil
|
126
|
+
for_.init.each{|stmt| stmt.accept(self)}
|
127
|
+
cond=for_.cond.accept(self)
|
128
|
+
@cfg << cond_bb = BasicBlock.new
|
129
|
+
@cfg << trueBranch = BasicBlock.new
|
130
|
+
@cfg << falseBranch = BasicBlock.new
|
131
|
+
@cfg << postBranch = BasicBlock.new
|
132
|
+
@current_continue_dest = postBranch
|
133
|
+
@current_break_dest = falseBranch
|
134
|
+
@current.to cond_bb
|
135
|
+
cond_bb << ITE.new(cond,trueBranch,falseBranch)
|
136
|
+
cond_bb.to trueBranch
|
137
|
+
cond_bb.to falseBranch
|
138
|
+
@current= trueBranch
|
139
|
+
for_.body.accept(self) #may modify @current identity
|
140
|
+
@current.to postBranch
|
141
|
+
@current=postBranch
|
142
|
+
for_.increment.accept(self)
|
143
|
+
@current.to cond_bb
|
144
|
+
@current=falseBranch
|
145
|
+
end
|
146
|
+
|
147
|
+
def visitDoWhile dowhile,args=nil
|
148
|
+
@cfg << cond_bb = BasicBlock.new
|
149
|
+
@current_continue_dest = cond_bb # for continue stmt !
|
150
|
+
@cfg << trueBranch = BasicBlock.new
|
151
|
+
@cfg << falseBranch = BasicBlock.new
|
152
|
+
|
153
|
+
@current.to trueBranch
|
154
|
+
@current = trueBranch
|
155
|
+
dowhile.body.accept(self) # may modify @current
|
156
|
+
|
157
|
+
@current.to cond_bb
|
158
|
+
@current = cond_bb
|
159
|
+
cond=dowhile.cond.accept(self)
|
160
|
+
|
161
|
+
cond_bb << ITE.new(cond,trueBranch,falseBranch)
|
162
|
+
|
163
|
+
cond_bb.to trueBranch
|
164
|
+
cond_bb.to falseBranch
|
165
|
+
@current = falseBranch
|
166
|
+
end
|
167
|
+
|
168
|
+
end #class Visitor
|
169
|
+
end #module
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Crokus
|
2
|
+
|
3
|
+
class CFGCleaner
|
4
|
+
|
5
|
+
def clean cfg
|
6
|
+
puts "\t|--> cleaning '#{cfg.name}'" unless $options[:mute]
|
7
|
+
@cfg=cfg
|
8
|
+
@visited=[]
|
9
|
+
@new_succs={}
|
10
|
+
clean_rec cfg.starter
|
11
|
+
update
|
12
|
+
rename
|
13
|
+
cfg
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def clean_rec bb
|
19
|
+
@visited << bb
|
20
|
+
@new_succs[bb]=[]
|
21
|
+
bb.succs.each_with_index do |succ,idx|
|
22
|
+
cand=find_next_rec(succ)
|
23
|
+
if cand
|
24
|
+
#puts " |--> #{bb.label} next #{idx} is #{cand.label}"
|
25
|
+
@new_succs[bb] << cand
|
26
|
+
unless @visited.include? cand
|
27
|
+
clean_rec cand
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_next_rec bb
|
34
|
+
if bb.size>0
|
35
|
+
return bb
|
36
|
+
else
|
37
|
+
if bb.succs.any?
|
38
|
+
@cfg.bbs.delete(bb)
|
39
|
+
return find_next_rec(bb.succs.first)
|
40
|
+
else
|
41
|
+
return bb #ending
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def update
|
47
|
+
@cfg.bbs.each{|bb| bb.succs=@new_succs[bb]}
|
48
|
+
@cfg.bbs.each{|bb|
|
49
|
+
if (ite=bb.stmts.last).is_a? ITE
|
50
|
+
ite.trueBranch=bb.succs.first
|
51
|
+
ite.falseBranch=bb.succs.last
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def rename
|
57
|
+
@cfg.bbs.each_with_index do |bb,idx|
|
58
|
+
bb.id="L#{idx}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'code'
|
2
|
+
|
3
|
+
module Crokus
|
4
|
+
|
5
|
+
class CFGPrinter
|
6
|
+
|
7
|
+
attr_accessor :code
|
8
|
+
|
9
|
+
def print cfg
|
10
|
+
@code=Code.new
|
11
|
+
@code << header
|
12
|
+
@visited=[]
|
13
|
+
visitRec(cfg.starter)
|
14
|
+
@code << footer
|
15
|
+
dot_name="cfg_#{cfg.name}.dot"
|
16
|
+
@code.save_as dot_name
|
17
|
+
puts "\t|--> graphviz file saved as '#{dot_name}'" unless $options[:mute]
|
18
|
+
end
|
19
|
+
|
20
|
+
def header
|
21
|
+
ret=Code.new
|
22
|
+
ret << "digraph ControlFlowGraph {"
|
23
|
+
ret.indent=2
|
24
|
+
ret << 'forcelabels=true;'
|
25
|
+
ret << 'graph [ label="",'
|
26
|
+
ret << ' bgcolor="white",'
|
27
|
+
ret << ' fontname="Arail",'
|
28
|
+
ret << ' rankdir="TB"]'
|
29
|
+
ret.newline
|
30
|
+
ret << 'node [ fontname="Arial",'
|
31
|
+
ret << ' shape="box",'
|
32
|
+
ret << ' style="filled",'
|
33
|
+
ret << ' fillcolor="AliceBlue"]'
|
34
|
+
ret.newline
|
35
|
+
ret << 'edge [ fontname="Arial",'
|
36
|
+
ret << ' color="Blue",'
|
37
|
+
ret << ' dir="forward"]'
|
38
|
+
ret.newline
|
39
|
+
ret
|
40
|
+
end
|
41
|
+
|
42
|
+
def footer
|
43
|
+
"}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def visitRec bb
|
47
|
+
while !@visited.include?(bb)
|
48
|
+
@visited << bb
|
49
|
+
c_code=bb.code4dot
|
50
|
+
c_code=clean4dot(c_code)
|
51
|
+
code << "#{id(bb)} [label=\"#{c_code}\",shape=rectangle, xlabel=#{bb.label}]"
|
52
|
+
bb.succs.each_with_index do |succ,idx|
|
53
|
+
if bb.succs.size>1
|
54
|
+
label = (idx==0 ? "true" : "false")
|
55
|
+
label = "[label=#{label}]"
|
56
|
+
end
|
57
|
+
code << "#{id(bb)} -> #{id(succ)} #{label}"
|
58
|
+
visitRec(succ)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def id bb
|
64
|
+
"bb_#{bb.id.to_s}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def clean4dot str
|
68
|
+
str=str.gsub(/\\n\"/,'"')
|
69
|
+
str=str.gsub(/"/,'\"')
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|