1 module brainfuck;
2 
3 import std.conv;
4 import std.array;
5 import std.stdio;
6 import std.format;
7 
8 class VM {
9     uint pointer = 0;
10     ubyte[] tape;
11 
12     this() {
13         tape.length = 30000;
14     }
15 }
16 
17 class BrainfuckInstruction {
18     abstract void run(VM);
19     abstract string compile(uint);
20     abstract string to_string(uint);
21 
22     string compile() {
23         return compile(0);
24     }
25 
26     string to_string() {
27         return to_string(0);
28     }
29 }
30 
31 class Modify : BrainfuckInstruction {
32     int amt;
33 
34     this(int amt) {
35         this.amt = amt;
36     }
37 
38     override void run(VM vm) {
39         vm.tape[vm.pointer] += amt;
40     }
41 
42     override string compile(uint depth) {
43         return "%s*p += %s;".format("    ".replicate(depth), amt);
44     }
45 
46     override string to_string(uint depth) {
47         return "%sModify(%s)".format("    ".replicate(depth), amt.to!string);
48     }
49 }
50 
51 class Select : BrainfuckInstruction {
52     int amt;
53 
54     this(int amt) {
55         this.amt = amt;
56     }
57 
58     override void run(VM vm) {
59         vm.pointer += amt;
60     }
61 
62     override string compile(uint depth) {
63         return "%sp += %s;".format("    ".replicate(depth), amt);
64     }
65 
66     override string to_string(uint depth) {
67         return "%sSelect(%s)".format("    ".replicate(depth), amt.to!string);
68     }
69 }
70 
71 class Loop : BrainfuckInstruction {
72     BrainfuckInstruction[] insts;
73 
74     this(BrainfuckInstruction[] insts) {
75         this.insts = insts;
76     }
77 
78     override void run(VM vm) {
79         while(vm.tape[vm.pointer] != 0) {
80             foreach(inst; insts) {
81                 inst.run(vm);
82             }
83         }
84     }
85 
86     override string compile(uint depth) {
87         char[] s;
88         s ~= "%swhile(*p) {\n".format("    ".replicate(depth));
89 
90         foreach(inst; insts) {
91             s ~= "%s\n".format(inst.compile(depth + 1));
92         }
93 
94         s ~= "%s}".format("    ".replicate(depth));
95         return cast(string) s;
96     }
97 
98     override string to_string(uint depth) {
99         char[] s;
100         s ~= "%sLoop {\n".format("    ".replicate(depth));
101 
102         foreach(inst; insts) {
103             s ~= "%s\n".format(inst.to_string(depth + 1));
104         }
105 
106         s ~= "%s}".format("    ".replicate(depth));
107         return cast(string) s;
108     }
109 }
110 
111 class Input : BrainfuckInstruction {
112     override void run(VM vm) {
113         char c;
114         readf("%s", &c);
115         vm.tape[vm.pointer] = c;
116     }
117 
118     override string compile(uint depth) {
119         return "%s*p = getchar();".format("    ".replicate(depth));
120     }
121 
122     override string to_string(uint depth) {
123         return "%sInput".format("    ".replicate(depth));
124     }
125 }
126 
127 class Output : BrainfuckInstruction {
128     override void run(VM vm) {
129         write(cast(char) vm.tape[vm.pointer]);
130         stdout.flush;
131     }
132 
133     override string compile(uint depth) {
134         return "%sputchar(*p); fflush(stdout);".format("    ".replicate(depth));
135     }
136 
137     override string to_string(uint depth) {
138         return "%sOutput".format("    ".replicate(depth));
139     }
140 }
141 
142 class Clear : BrainfuckInstruction {
143     override void run(VM vm) {
144         vm.tape[vm.pointer] = 0;
145     }
146 
147     override string compile(uint depth) {
148         return "%s*p = 0;".format("    ".replicate(depth));
149     }
150 
151     override string to_string(uint depth) {
152         return "%sClear".format("    ".replicate(depth));
153     }
154 }
155 
156 class MoveLoop : BrainfuckInstruction {
157     int[int] modifications;
158 
159     this(int[int] modifications) {
160         this.modifications = modifications;
161     }
162 
163     override void run(VM vm) {
164         auto src = vm.tape[vm.pointer];
165         if(src == 0) return;
166 
167         foreach(pointer, modify; modifications) {
168             vm.tape[vm.pointer + pointer] += modify * src;
169         }
170 
171         vm.tape[vm.pointer] = 0;
172     }
173 
174     override string compile(uint depth) {
175         char[] s;
176         s ~= "%sif(*p) {\n".format("    ".replicate(depth));
177 
178         foreach(pointer, modify; modifications) {
179             s ~= "%s*(p + %s) += %s * (*p);\n".format("    ".replicate(depth + 1), pointer, modify);
180         }
181 
182         s ~= "%s*p = 0;\n".format("    ".replicate(depth + 1));
183         s ~= "%s}".format("    ".replicate(depth));
184         return cast(string) s;
185     }
186 
187     override string to_string(uint depth) {
188         return "%sMoveLoop %s".format("    ".replicate(depth), modifications.to!string);
189     }
190 }
191 
192 class If : BrainfuckInstruction {
193     BrainfuckInstruction[] insts;
194 
195     this(BrainfuckInstruction[] insts) {
196         this.insts = insts;
197     }
198 
199     override void run(VM vm) {
200         if(vm.tape[vm.pointer] != 0) {
201             foreach(inst; insts) {
202                 inst.run(vm);
203             }
204         }
205     }
206 
207     override string compile(uint depth) {
208         char[] s;
209         s ~= "%sif(*p) {\n".format("    ".replicate(depth));
210 
211         foreach(inst; insts) {
212             s ~= "%s\n".format(inst.compile(depth + 1));
213         }
214 
215         s ~= "%s}".format("    ".replicate(depth));
216         return cast(string) s;
217     }
218 
219     override string to_string(uint depth) {
220         char[] s;
221         s ~= "%sIf {\n".format("    ".replicate(depth));
222 
223         foreach(inst; insts) {
224             s ~= "%s\n".format(inst.to_string(depth + 1));
225         }
226 
227         s ~= "%s}".format("    ".replicate(depth));
228         return cast(string) s;
229     }
230 }
231 
232 class UnrolledLoop : BrainfuckInstruction {
233     uint count;
234     BrainfuckInstruction[] insts;
235 
236     this(uint count, BrainfuckInstruction[] insts) {
237         this.count = count;
238         this.insts = insts;
239     }
240 
241     override void run(VM vm) {
242         for(uint i = 0; i < count; i++) {
243             foreach(inst; insts) {
244                 inst.run(vm);
245             }
246         }
247     }
248 
249     override string compile(uint depth) {
250         char[] s;
251 
252         for(uint i = 0; i < count; i++) {
253             foreach(inst; insts) {
254                 s ~= "%s\n".format(inst.compile(depth));
255             }
256         }
257 
258         return cast(string) s;
259     }
260 
261     override string to_string(uint depth) {
262         char[] s;
263         s ~= "%sUnrolledLoop(%s) {\n".format("    ".replicate(depth), count);
264 
265         foreach(inst; insts) {
266             s ~= "%s\n".format(inst.to_string(depth + 1));
267         }
268 
269         s ~= "%s}".format("    ".replicate(depth));
270         return cast(string) s;
271     }
272 }