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.
@@ -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