示例#1
0
        /// <summary> Converts a set of instructions to a string </summary>
        private string DebugPrint(CodeHandle handle, int indentLevel)
        {
            var           block = Get(handle);
            StringBuilder sb    = new StringBuilder();

            sb.Append('\t', indentLevel);
            //sb.AppendLine($"CODE BLOCK # {block.handle.index} ; {block.debug}");
            sb.AppendLine($"CODE BLOCK ; {block.debug}");

            for (int i = 0, count = block.instructions.Count; i < count; i++)
            {
                Instruction instruction = block.instructions[i];

                // tab out and print current instruction
                int tabs = indentLevel + (instruction.type == Opcode.LABEL ? -1 : 0);
                sb.Append('\t', tabs);
                sb.Append(i);
                sb.Append('\t');
                sb.AppendLine(instruction.DebugPrint());

                //if (instruction.type == Opcode.MAKE_CLOSURE) {
                //    // if function, recurse
                //    Closure closure = instruction.first.AsClosure;
                //    sb.Append(DebugPrint(closure, indentLevel + 1));
                //}
            }
            return(sb.ToString());
        }
示例#2
0
        /// <summary> Retrieves a code block registered for a given handle </summary>
        public CodeBlock Get(CodeHandle handle)
        {
            if (handle.index <= 0 || handle.index >= _blocks.Count)
            {
                throw new LanguageError("Invalid code block handle!");
            }

            return(_blocks[handle.index]);
        }
示例#3
0
        /// <summary> Deregisters the specified code block and replaces it with null. </summary>
        public void RemoveBlock(CodeHandle handle)
        {
            if (handle.index <= 0 || handle.index >= _blocks.Count)
            {
                throw new LanguageError("Invalid code block handle!");
            }

            _blocks[handle.index] = null; // note we just leave a hole, so we don't renumber other ones
        }
示例#4
0
        /// <summary> Registers a new code block and returns its handle </summary>
        public CodeHandle AddBlock(List <Instruction> instructions, string debug)
        {
            var handle = new CodeHandle {
                index = _blocks.Count
            };

            _blocks.Add(new CodeBlock(handle, instructions, debug));
            return(handle);
        }
示例#5
0
        /// <summary> Compiles code to produce a new closure </summary>
        private Closure CompileLambda(Val args, Cons body, Environment env)
        {
            Environment        newEnv       = Environment.Make(MakeTrueList(args), env);
            List <Instruction> instructions = Merge(
                EmitArgs(args, newEnv.DebugPrintSymbols()),
                CompileBegin(new Val(body), newEnv, State.UsedFinal));

            var        debug  = newEnv.DebugPrintSymbols() + " => " + Val.DebugPrint(body);
            CodeHandle handle = _ctx.code.AddBlock(Assemble(instructions), debug);

            return(new Closure(handle, env, args.AsConsOrNull, ""));
        }
示例#6
0
        /// <summary> Runs the given piece of code, and returns the value left at the top of the stack. </summary>
        public Val Execute(Closure fn, params Val[] args)
        {
            State              st           = new State(fn, args);
            CodeHandle         code         = default;
            List <Instruction> instructions = null;

            if (_logger.EnableInstructionLogging)
            {
                _logger.Log("Executing: ", fn.name);
                _logger.Log(_ctx.code.DebugPrint(fn));
            }

            while (!st.done)
            {
                if (!code.Equals(st.fn.code))
                {
                    code         = st.fn.code;
                    instructions = _ctx.code.Get(code).instructions;
                }

                if (st.pc >= instructions.Count)
                {
                    throw new LanguageError("Runaway opcodes!");
                }

                // fetch instruction
                Instruction instr = instructions[st.pc++];

                if (_logger.EnableStackLogging)
                {
                    _logger.Log("                                    " + State.PrintStack(st));
                    _logger.Log(string.Format("[{0,2}] {1,3} : {2}", st.stack.Count, st.pc - 1, instr.DebugPrint()));
                }

                // and now a big old switch statement. not handler functions - this is much faster.

                switch (instr.type)
                {
                case Opcode.LABEL:
                    // no op :)
                    break;

                case Opcode.PUSH_CONST: {
                    st.Push(instr.first);
                }
                break;

                case Opcode.LOCAL_GET: {
                    VarPos pos   = new VarPos(instr.first, instr.second);
                    Val    value = Environment.GetValueAt(pos, st.env);
                    st.Push(value);
                }
                break;

                case Opcode.LOCAL_SET: {
                    VarPos pos   = new VarPos(instr.first, instr.second);
                    Val    value = st.Peek();
                    Environment.SetValueAt(pos, value, st.env);
                }
                break;

                case Opcode.GLOBAL_GET: {
                    Symbol symbol = instr.first.AsSymbol;
                    Val    value  = symbol.pkg.GetValue(symbol);
                    st.Push(value);
                }
                break;

                case Opcode.GLOBAL_SET: {
                    Symbol symbol = instr.first.AsSymbol;
                    Val    value  = st.Peek();
                    symbol.pkg.SetValue(symbol, value);
                }
                break;

                case Opcode.STACK_POP:
                    st.Pop();
                    break;

                case Opcode.JMP_IF_TRUE: {
                    Val value = st.Pop();
                    if (value.CastToBool)
                    {
                        st.pc = GetLabelPosition(instr);
                    }
                }
                break;

                case Opcode.JMP_IF_FALSE: {
                    Val value = st.Pop();
                    if (!value.CastToBool)
                    {
                        st.pc = GetLabelPosition(instr);
                    }
                }
                break;

                case Opcode.JMP_TO_LABEL: {
                    st.pc = GetLabelPosition(instr);
                }
                break;

                case Opcode.MAKE_ENV: {
                    int argcount = instr.first.AsInt;
                    if (st.argcount != argcount)
                    {
                        throw new LanguageError($"Argument count error, expected {argcount}, got {st.argcount}");
                    }

                    // make an environment for the given number of named args
                    st.env = new Environment(st.argcount, st.env);

                    // move named arguments onto the stack frame
                    for (int i = argcount - 1; i >= 0; i--)
                    {
                        st.env.SetValue(i, st.Pop());
                    }
                }
                break;

                case Opcode.MAKE_ENVDOT: {
                    int argcount = instr.first.AsInt;
                    if (st.argcount < argcount)
                    {
                        throw new LanguageError($"Argument count error, expected {argcount} or more, got {st.argcount}");
                    }

                    // make an environment for all named args, +1 for the list of remaining varargs
                    int dotted = st.argcount - argcount;
                    st.env = new Environment(argcount + 1, st.env);

                    // cons up dotted values from the stack
                    for (int dd = dotted - 1; dd >= 0; dd--)
                    {
                        Val arg = st.Pop();
                        st.env.SetValue(argcount, new Val(new Cons(arg, st.env.GetValue(argcount))));
                    }

                    // and move the named ones onto the environment stack frame
                    for (int i = argcount - 1; i >= 0; i--)
                    {
                        st.env.SetValue(i, st.Pop());
                    }
                }
                break;

                case Opcode.DUPLICATE: {
                    if (st.stack.Count == 0)
                    {
                        throw new LanguageError("Cannot duplicate on an empty stack!");
                    }
                    st.Push(st.Peek());
                }
                break;

                case Opcode.JMP_CLOSURE: {
                    st.env = st.env.parent;         // discard the top environment frame
                    Val     top     = st.Pop();
                    Closure closure = top.AsClosureOrNull;

                    // set vm state to the beginning of the closure
                    st.fn       = closure ?? throw new LanguageError($"Unknown function during function call around: {DebugRecentInstructions(st, instructions)}");
                    st.env      = closure.env;
                    st.pc       = 0;
                    st.argcount = instr.first.AsInt;
                }
                break;

                case Opcode.SAVE_RETURN: {
                    // save current vm state to a return value
                    st.Push(new Val(new ReturnAddress(st.fn, GetLabelPosition(instr), st.env, instr.first.AsStringOrNull)));
                }
                break;

                case Opcode.RETURN_VAL:
                    if (st.stack.Count > 1)
                    {
                        // preserve return value on top of the stack
                        Val           retval  = st.Pop();
                        ReturnAddress retaddr = st.Pop().AsReturnAddress;
                        st.Push(retval);

                        // restore vm state from the return value
                        st.fn  = retaddr.fn;
                        st.env = retaddr.env;
                        st.pc  = retaddr.pc;
                    }
                    else
                    {
                        st.done = true;     // this will force the virtual machine to finish up
                    }
                    break;

                case Opcode.MAKE_CLOSURE: {
                    var cl = instr.first.AsClosure;
                    st.Push(new Closure(cl.code, st.env, null, cl.name));
                }
                break;

                case Opcode.CALL_PRIMOP: {
                    string name = instr.first.AsString;
                    int    argn = (instr.second.IsInt) ? instr.second.AsInt : st.argcount;

                    Primitive prim = Primitives.FindNary(name, argn);
                    if (prim == null)
                    {
                        throw new LanguageError($"Invalid argument count to primitive {name}, count of {argn}");
                    }

                    Val result = prim.Call(_ctx, argn, st);
                    st.Push(result);
                }
                break;

                default:
                    throw new LanguageError("Unknown instruction type: " + instr.type);
                }
            }

            // return whatever's on the top of the stack
            if (st.stack.Count == 0)
            {
                throw new LanguageError("Stack underflow!");
            }

            return(st.Peek());
        }
示例#7
0
 public CodeBlock(CodeHandle handle, List <Instruction> instructions, string debug)
 {
     this.handle       = handle;
     this.instructions = instructions;
     this.debug        = debug;
 }