1 module cfg;
2 
3 import brainfuck;
4 
5 import std.algorithm.iteration;
6 import std.array;
7 import std.format;
8 
9 abstract class CFG {
10     CFG[] incoming;
11 
12     abstract CFG run(VM);
13     abstract string compile();
14 }
15 
16 class BasicBlockCFG : CFG {
17     CFG outgoing;
18     BrainfuckInstruction[] insts;
19 
20     this(BrainfuckInstruction[] insts) {
21         this.insts = insts;
22     }
23 
24     override CFG run(VM vm) {
25         foreach(inst; insts) {
26             inst.run(vm);
27         }
28 
29         return outgoing;
30     }
31 
32     override string compile() {
33         char[] s;
34         s ~= "    _%s:\n".format(cast(void*) this);
35 
36         foreach(inst; insts) {
37             s ~= "    %s\n".format(inst.compile);
38         }
39 
40         s ~= "    goto _%s;\n".format(cast(void*) outgoing);
41         return cast(string) s;
42     }
43 }
44 
45 enum IfType { Loop, If, Move }
46 
47 class IfCFG : CFG {
48     IfType type;
49     CFG outgoing_true;
50     CFG outgoing_false;
51 
52     this(IfType type) {
53         this.type = type;
54     }
55 
56     override CFG run(VM vm) {
57         if(vm.tape[vm.pointer] != 0) {
58             return outgoing_true;
59         } else {
60             return outgoing_false;
61         }
62     }
63 
64     override string compile() {
65         char[] s;
66         s ~= "    _%s:\n".format(cast(void*) this);
67         s ~= "    if(*p) goto _%s;\n".format(cast(void*) outgoing_true);
68         s ~= "    goto _%s;\n".format(cast(void*) outgoing_false);
69         return cast(string) s;
70     }
71 }
72 
73 class MoveCFG : CFG {
74     CFG outgoing;
75     MoveLoop inst;
76 
77     this(MoveLoop inst) {
78         this.inst = inst;
79     }
80 
81     override CFG run(VM vm) {
82         inst.run(vm);
83         return outgoing;
84     }
85 
86     override string compile() {
87         char[] s;
88         s ~= "    _%s:\n".format(cast(void*) this);
89         
90         foreach(pointer, modify; inst.modifications) {
91             s ~= "    *(p + %s) += %s * (*p);\n".format(pointer, modify);
92         }
93 
94         s ~= "    *p = 0;\n";
95         s ~= "    goto _%s;\n".format(cast(void*) outgoing);
96         return cast(string) s;
97     }
98 }
99 
100 class IRParser {
101     BrainfuckInstruction[][] insts;
102     uint[] index;
103     size_t depth;
104 
105     this(BrainfuckInstruction[] insts) {
106         index ~= 0;
107         this.insts ~= insts;
108     }
109 
110     void seek(int d) {
111         index[depth] += d;
112     }
113 
114     void next() {
115         seek(1);
116     }
117 
118     void prev() {
119         seek(-1);
120     }
121 
122     void dive(BrainfuckInstruction[] i) {
123         depth++;
124         insts ~= i;
125         index ~= 0;
126     }
127 
128     void rise() {
129         depth--;
130         index.popBack;
131         insts.popBack;
132     }
133 
134     BrainfuckInstruction instruction() {
135         auto i = index[depth];
136 
137         if(i >= 0 && i < insts[depth].length) {
138             return insts[depth][index[depth]];
139         }
140 
141         return null;
142     }
143 
144     bool is_basic(BrainfuckInstruction inst) {
145         return cast(Select) inst
146             || cast(Modify) inst
147             || cast(Clear) inst
148             || cast(Input) inst
149             || cast(Output) inst
150             || cast(UnrolledLoop) inst;
151     }
152 
153     CFG parse() {
154         auto inst = instruction;
155 
156         if(inst !is null) {
157             if(is_basic(inst)) {
158                 return parse_basic_block;
159             } else {
160                 return parse_if;
161             }
162         }
163 
164         return null;
165     }
166 
167     BasicBlockCFG parse_basic_block() {
168         auto inst = instruction;
169         BrainfuckInstruction[] insts;
170 
171         while(is_basic(inst)) {
172             if(auto unroll = cast(UnrolledLoop) inst) {
173                 for(uint i = 0; i < unroll.count; i++) {
174                     foreach(inner_inst; unroll.insts) {
175                         insts ~= inner_inst;
176                     }
177                 }
178             } else {
179                insts ~= inst;
180             }
181 
182             next;
183             inst = instruction;
184         }
185 
186         auto block = new BasicBlockCFG(insts);
187         block.outgoing = parse;
188         if(block.outgoing !is null) block.outgoing.incoming ~= block;
189         return block;
190     }
191 
192     IfCFG parse_if() {
193         auto inst = instruction;
194 
195         if(auto loop = cast(Loop) inst) {
196             auto if_cfg = new IfCFG(IfType.Loop);
197             next;
198             if_cfg.outgoing_false = parse;
199             if(if_cfg.outgoing_false !is null) if_cfg.outgoing_false.incoming ~= if_cfg;
200             dive(loop.insts);
201             if_cfg.outgoing_true = parse;
202             if(if_cfg.outgoing_true !is null) if_cfg.outgoing_true.incoming ~= if_cfg;
203 
204             CFG current = if_cfg.outgoing_true;
205             while(true) {
206                 if(auto basic_cfg = cast(BasicBlockCFG) current) {
207                     current = basic_cfg.outgoing;
208                     if(current is null) {
209                         basic_cfg.outgoing = if_cfg;
210                         if_cfg.incoming ~= basic_cfg;
211                         break;
212                     }
213                 } else if(auto inner_if_cfg = cast(IfCFG) current) {
214                     current = inner_if_cfg.outgoing_false;
215                     if(current is null) {
216                         inner_if_cfg.outgoing_false = if_cfg;
217                         if_cfg.incoming ~= inner_if_cfg;
218                         break;
219                     }
220                 } else if(auto move_cfg = cast(MoveCFG) current) {
221                     current = move_cfg.outgoing;
222                 }
223             }
224 
225             rise;
226             return if_cfg;
227         } else if(auto move = cast(MoveLoop) inst) {
228             auto move_cfg = new MoveCFG(move);
229             auto if_cfg = new IfCFG(IfType.Move);
230             if_cfg.outgoing_true = move_cfg;
231             move_cfg.incoming ~= if_cfg;
232             next;
233 
234             auto next_instruction = parse;
235             if_cfg.outgoing_false = next_instruction;
236             if(next_instruction !is null) next_instruction.incoming ~= if_cfg;
237             move_cfg.outgoing = if_cfg;
238             if_cfg.incoming ~= move_cfg;
239             return if_cfg;
240         } else if(auto if_bf = cast(If) inst) {
241             auto if_cfg = new IfCFG(IfType.If);
242             dive(if_bf.insts);
243             if_cfg.outgoing_true = parse;
244             if(if_cfg !is null) if_cfg.outgoing_true.incoming ~= if_cfg;
245             rise;
246 
247             next;
248             auto next_instruction = parse;
249             if_cfg.outgoing_false = next_instruction;
250             if(next_instruction !is null) next_instruction.incoming ~= if_cfg.outgoing_false;
251 
252             CFG current = if_cfg.outgoing_true;
253             while(true) {
254                 if(auto basic_cfg = cast(BasicBlockCFG) current) {
255                     current = basic_cfg.outgoing;
256                     if(current is null) {
257                         basic_cfg.outgoing = if_cfg;
258                         if_cfg.incoming ~= basic_cfg;
259                         break;
260                     }
261                 } else if(auto inner_if_cfg = cast(IfCFG) current) {
262                     current = inner_if_cfg.outgoing_false;
263                     if(current is null) {
264                         inner_if_cfg.outgoing_false = if_cfg;
265                         if_cfg.incoming ~= inner_if_cfg;
266                         break;
267                     }
268                 } else if(auto move_cfg = cast(MoveCFG) current) {
269                     current = move_cfg.outgoing;
270                 }
271             }
272 
273             return if_cfg;
274         }
275 
276         return null;
277     }
278 }