1 module dfuck;
2 
3 import optimize;
4 import brainfuck;
5 import input_stream;
6 import parse;
7 
8 import darg;
9 
10 import std.stdio;
11 import core.stdc.stdlib;
12 import std.file;
13 import std.range;
14 import std.conv;
15 import std.traits;
16 import std.regex;
17 import std..string;
18 import std.conv;
19 import std.process;
20 
21 struct Options {
22     @Option("help", "h")
23     @Help("Prints this help.")
24     OptionFlag help;
25 
26     @Option("only-compile", "oc")
27     @Help("Compile a brainfuck file and generate an executable.")
28     OptionFlag only_compile;
29 
30     @Option("compile-run", "cr")
31     @Help("Compile and run a brainfuck file.")
32     OptionFlag compile_run;
33     
34     @Option("interpret", "i")
35     @Help("Interpret a brainfuck file.")
36     OptionFlag interpret;
37 
38     @Option("intermediate", "ir")
39     @Help("Specifies a file to ouput IR code to.")
40     string intermediate;
41 
42     @Option("compiler", "c")
43     @Help("The compiler to use. Must be one of: gcc. Defaults to gcc.")
44     string compiler = "gcc";
45 
46     @Argument("source")
47     @Help("The brainfuck file to be run.")
48     string source;
49 }
50 
51 int main(string[] args) {
52     immutable help = helpString!Options;
53     immutable usage = usageString!Options("dfuck");
54     Options options;
55 
56     try {
57         options = parseArgs!Options(args[1 .. $]);
58     } catch (ArgParseError e) {
59         writeln(e.msg);
60         writeln(usage);
61         return 1;
62     } catch (ArgParseHelp e) {
63         writeln(usage);
64         write(help);
65         return 0;
66     }
67 
68     auto code = readText(options.source);
69 
70     writeln("Sanitizing code...");
71     auto re = regex(r"[^(\[|\]|\+|\-|>|<|\.|,)]", "g");
72     auto sanitized_code = replaceAll(code, re, "");
73     writeln("done");
74 
75     writeln("Parsing instructions...");
76     auto insts = parse_brainfuck(sanitized_code);
77     writeln("done");
78     writeln("Clear optimization...");
79     insts = clear_opt(insts);
80     writeln("done");
81     writeln("BalancedLoop optimization...");
82     insts = balanced_opt(insts);
83     writeln("done");
84 
85     stdout.flush();
86 
87     char[] file_out;
88 
89     if(options.intermediate != "") {
90         foreach(inst; insts) {
91             file_out ~= inst.to_string() ~ "\n";
92         }
93         std.file.write(options.intermediate, file_out);
94     }
95 
96     if(options.compile_run == OptionFlag.yes || options.only_compile == OptionFlag.yes) {
97         file_out.length = 0;
98         file_out ~=
99 "#include <stdio.h>
100 char t[30000];
101 char* p = t;
102 int main(int argc, const char* argv[]) {\n";
103         
104         foreach(inst; insts) {
105             file_out ~= inst.compile(1) ~ "\n";
106         }
107         
108         file_out ~= "}";
109         std.file.write("dfuck_temp.c", file_out);
110 
111         switch(options.compiler) {
112             case "gcc":
113                 auto com = "gcc -O3 dfuck_temp.c -o dfuck_temp.exe";
114                 writefln("Running %s", com);
115                 stdout.flush();
116                 executeShell(com);
117                 break;
118             default:
119                 writefln("Compiler %s is not supported.", options.compiler);
120                 return -1;
121         }
122 
123         if(options.compile_run == OptionFlag.yes) {
124             auto pid = spawnProcess("./dfuck_temp.exe");
125             wait(pid);
126             executeShell("rm dfuck_temp.c && rm dfuck_temp.exe");
127         }
128     }
129 
130     if(options.interpret == OptionFlag.yes) {
131         auto vm = new VM();
132 
133         foreach(inst; insts) {
134             inst.run(vm);
135         }
136     }
137 
138     return 0;
139 }