// 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); }
public BfOp(BfOpKind kind_param, int argument_param) { this.kind = kind_param; this.argument = argument_param; }