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