public static void Main(string[] args) { if (args.Length < 1) { BfUtil.DIE("argv < 1"); } string bfCode = BfUtil.LoadProgram(args[0]); byte[] memory = new byte[30000]; Run(memory, bfCode); }
public static void Main(string[] args) { if (args.Length < 1) { BfUtil.DIE("argv < 1"); } string bfCode = BfUtil.LoadProgram(args[0]); //Console.WriteLine(bfCode); var gen = new BfGen(); var program = gen.Gen(bfCode); byte[] memory = new byte[30000]; program.Invoke(memory); }
public static void Main(string[] args) { if (args.Length < 1) { BfUtil.DIE("argv < 1"); } string bfCode = BfUtil.LoadProgram(args[0]); List <BfOp> optimized = translate_program(bfCode); //foreach (BfOp op in optimized) { // Console.WriteLine($"op={op.kind}, argument={op.argument}"); //} var gen = new BfGen(); var program = gen.Gen(optimized); byte[] memory = new byte[30000]; program.Invoke(memory); }
public static void Run(byte[] memory, string instructions) { int pc = 0; int dataptr = 0; while (pc < instructions.Length) { char instruction = instructions[pc]; switch (instruction) { case '>': dataptr++; break; case '<': dataptr--; break; case '+': memory[dataptr]++; break; case '-': memory[dataptr]--; break; case '.': BfUtil.PutChar((char)memory[dataptr]); break; case ',': memory[dataptr] = (byte)BfUtil.GetChar(); break; case '[': if (memory[dataptr] == 0) { int bracket_nesting = 1; int saved_pc = pc; while (bracket_nesting > 0 && ++pc < instructions.Length) { if (instructions[pc] == ']') { bracket_nesting--; } else if (instructions[pc] == '[') { bracket_nesting++; } } if (bracket_nesting == 0) { break; } else { BfUtil.DIE($"unmatched '[' at pc={saved_pc}"); } } break; case ']': if (memory[dataptr] != 0) { int bracket_nesting = 1; int saved_pc = pc; while (bracket_nesting > 0 && pc > 0) { pc--; if (instructions[pc] == '[') { bracket_nesting--; } else if (instructions[pc] == ']') { bracket_nesting++; } } if (bracket_nesting == 0) { break; } else { BfUtil.DIE($"unmatched ']' at pc={saved_pc}"); } } break; default: BfUtil.DIE($"bad char '{instruction}' at pc={pc}"); break; } pc++; } }
private MethodBuilder DefineInvokeMethod(TypeBuilder myType, List <BfOp> ops) { MethodBuilder simpleMethod = myType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), // returnType new Type[] { typeof(byte[]) }); // ;paramTypes ILGenerator generator = simpleMethod.GetILGenerator(); MethodInfo putcharMI = typeof(BfUtil).GetMethod("PutChar", new Type[] { typeof(char) }); MethodInfo getcharMI = typeof(BfUtil).GetMethod("GetChar", new Type[] {}); Stack <BracketLabels> openBracketStack = new Stack <BracketLabels>(); LocalBuilder dataptr = generator.DeclareLocal(typeof(int)); // local0: pc LocalBuilder tempptr = generator.DeclareLocal(typeof(int)); // local1: temporary generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Stloc, dataptr); // dataptr = 0 for (int pc = 0; pc < ops.Count; ++pc) { BfOp op = ops[pc]; switch (op.kind) { case BfOpKind.INC_PTR: generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> break; case BfOpKind.DEC_PTR: generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> break; case BfOpKind.INC_DATA: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] += <op.argument> break; case BfOpKind.DEC_DATA: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] -= <op.argument> break; case BfOpKind.WRITE_STDOUT: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] for (int i = 0; i < op.argument; ++i) { if (i < op.argument - 1) { generator.Emit(OpCodes.Dup); } generator.EmitCall(OpCodes.Call, putcharMI, null); // putchar(memory[dataptr]) } break; case BfOpKind.READ_STDIN: for (int i = 0; i < op.argument; ++i) { generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.EmitCall(OpCodes.Call, getcharMI, null); // getchar() generator.Emit(OpCodes.Stelem_I4); // memory[pc] = getchar() } break; case BfOpKind.LOOP_SET_TO_ZERO: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = 0 break; case BfOpKind.LOOP_MOVE_PTR: { Label loop = generator.DefineLabel(); Label endloop = generator.DefineLabel(); // Emit a loop that moves the pointer in jumps of op.argument; it's // important to do an equivalent of while(...) rather than do...while(...) // here so that we don't do the first pointer change if already pointing // to a zero. // // loop: // cmpb 0(%r13), 0 // jz endloop // %r13 += argument // jmp loop // endloop: generator.MarkLabel(loop); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, endloop); // if memory[dataptr] == 0 goto endloop generator.Emit(OpCodes.Ldloc, dataptr); // dataptr if (op.argument < 0) { generator.Emit(OpCodes.Ldc_I4, -op.argument); generator.Emit(OpCodes.Sub); } else { generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); } generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> generator.Emit(OpCodes.Br, loop); generator.MarkLabel(endloop); } break; case BfOpKind.LOOP_MOVE_DATA: { // Only move if the current data isn't 0: // // cmpb 0(%r13), 0 // jz skip_move // <...> move data // skip_move: Label skip_move = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, skip_move); generator.Emit(OpCodes.Ldloc, dataptr); // dataptr if (op.argument < 0) { generator.Emit(OpCodes.Ldc_I4, -op.argument); generator.Emit(OpCodes.Sub); } else { generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); } generator.Emit(OpCodes.Stloc, tempptr); // temp = dataptr + <op.argument> // Use rax as a temporary holding the value of at the original pointer; // then use al to add it to the new location, so that only the target // location is affected: addb %al, 0(%r13) generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, tempptr); // temp generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, tempptr); // temp generator.Emit(OpCodes.Ldelem_I4); // memory[temp] generator.Emit(OpCodes.Add); // memory[dataptr] + memory[temp] generator.Emit(OpCodes.Stelem_I4); // memory[temp] = memory[dataptr] + memory[temp] generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = 0 generator.MarkLabel(skip_move); } break; case BfOpKind.JUMP_IF_DATA_ZERO: { Label openLabel = generator.DefineLabel(); Label closeLabel = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, closeLabel); // if memory[dataptr] == 0 goto closeLabel generator.MarkLabel(openLabel); openBracketStack.Push(new BracketLabels(openLabel, closeLabel)); } break; case BfOpKind.JUMP_IF_DATA_NOT_ZERO: { if (openBracketStack.Count == 0) { BfUtil.DIE($"Unmatched closing ']' at pc={pc}"); } BracketLabels labels = openBracketStack.Pop(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Bne_Un, labels.openLabel); // if memory[dataptr] != 0 goto openLabel generator.MarkLabel(labels.closeLabel); } break; default: BfUtil.DIE($"INVALID_OP encountered on pc={pc}"); break; } } generator.Emit(OpCodes.Ret); return(simpleMethod); }
// Translates the given program into a vector of BfOps that can be used for fast // interpretation. private static List <BfOp> translate_program(string instructions) { int pc = 0; int program_size = instructions.Length; List <BfOp> ops = new List <BfOp>(); // Throughout the translation loop, this stack contains offsets (in the ops // vector) of open brackets (JUMP_IF_DATA_ZERO ops) waiting for a closing // bracket. Since brackets nest, these naturally form a stack. The // JUMP_IF_DATA_ZERO ops get added to ops with their argument set to 0 until a // matching closing bracket is encountered, at which point the argument can be // back-patched. Stack <int> open_bracket_stack = new Stack <int>(); while (pc < program_size) { char instruction = instructions[pc]; if (instruction == '[') { // Place a jump op with a placeholder 0 offset. It will be patched-up to // the right offset when the matching ']' is found. open_bracket_stack.Push(ops.Count); ops.Add(new BfOp(BfOpKind.JUMP_IF_DATA_ZERO, 0)); pc++; } else if (instruction == ']') { if (open_bracket_stack.Count == 0) { BfUtil.DIE($"unmatched closing ']' at pc={pc}"); } int open_bracket_offset = open_bracket_stack.Pop(); // Try to optimize this loop; if optimize_loop succeeds, it returns a // non-empty vector which we can splice into ops in place of the loop. // If the returned vector is empty, we proceed as usual. List <BfOp> optimized_loop = optimize_loop(ops, open_bracket_offset); if (optimized_loop.Count == 0) { // Loop wasn't optimized, so proceed emitting the back-jump to ops. We // have the offset of the matching '['. We can use it to create a new // jump op for the ']' we're handling, as well as patch up the offset of // the matching '['. ops[open_bracket_offset].argument = ops.Count; ops.Add(new BfOp(BfOpKind.JUMP_IF_DATA_NOT_ZERO, open_bracket_offset)); } else { // Replace this whole loop with optimized_loop. ops.RemoveRange(open_bracket_offset, ops.Count - open_bracket_offset); ops.AddRange(optimized_loop); } pc++; } else { // Not a jump; all the other ops can be repeated, so find where the repeat // ends. int start = pc++; while (pc < program_size && instructions[pc] == instruction) { pc++; } // Here pc points to the first new instruction encountered, or to the end // of the program. int num_repeats = pc - start; // Figure out which op kind the instruction represents and add it to the // ops. BfOpKind kind = BfOpKind.INVALID_OP; switch (instruction) { case '>': kind = BfOpKind.INC_PTR; break; case '<': kind = BfOpKind.DEC_PTR; break; case '+': kind = BfOpKind.INC_DATA; break; case '-': kind = BfOpKind.DEC_DATA; break; case ',': kind = BfOpKind.READ_STDIN; break; case '.': kind = BfOpKind.WRITE_STDOUT; break; default: BfUtil.DIE($"bad char '{instruction}' at pc={start}"); break; } ops.Add(new BfOp(kind, num_repeats)); } } return(ops); }
private MethodBuilder DefineInvokeMethod(TypeBuilder myType, string instructions) { MethodBuilder simpleMethod = myType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), // returnType new Type[] { typeof(byte[]) }); // ;paramTypes ILGenerator generator = simpleMethod.GetILGenerator(); MethodInfo putcharMI = typeof(BfUtil).GetMethod("PutChar", new Type[] { typeof(char) }); MethodInfo getcharMI = typeof(BfUtil).GetMethod("GetChar", new Type[] {}); Stack <BracketLabels> openBracketStack = new Stack <BracketLabels>(); LocalBuilder dataptr = generator.DeclareLocal(typeof(int)); // local0: pc generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Stloc, dataptr); // dataptr = 0 for (int pc = 0; pc < instructions.Length; ++pc) { char c = instructions[pc]; switch (c) { case '>': generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stloc, dataptr); // ++dataptr break; case '<': generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stloc, dataptr); // --dataptr break; case '+': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] += 1 break; case '-': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stelem_I4); // memory[pc] -= 1 break; case '.': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.EmitCall(OpCodes.Call, putcharMI, null); // putchar(memory[dataptr]) break; case ',': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.EmitCall(OpCodes.Call, getcharMI, null); // getchar() generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = getchar() break; case '[': { Label openLabel = generator.DefineLabel(); Label closeLabel = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, closeLabel); // if memory[pc] == 0 goto closeLabel generator.MarkLabel(openLabel); openBracketStack.Push(new BracketLabels(openLabel, closeLabel)); } break; case ']': { if (openBracketStack.Count == 0) { BfUtil.DIE($"Unmatched closing ']' at pc={pc}"); } BracketLabels labels = openBracketStack.Pop(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Bne_Un, labels.openLabel); // if memory[pc] != 0 goto openLabel generator.MarkLabel(labels.closeLabel); } break; default: Console.WriteLine("Unhandled instruction: " + c); break; } } generator.Emit(OpCodes.Ret); return(simpleMethod); }