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 }