public Unit CallFunction(Unit p_callable, List <Unit> p_args = null) { UnitType this_type = p_callable.Type; if (p_args != null) { for (int i = p_args.Count - 1; i >= 0; i--) { stack.Push(p_args[i]); } } Operand before_call_IP = IP; if (this_type == UnitType.Function) { instructions.PushRET((Operand)(main.Body.Count - 1)); FunctionUnit this_func = (FunctionUnit)(p_callable.heapUnitValue); instructions.PushFunction(this_func, Env, out instructionsCache); IP = 0; } else if (this_type == UnitType.Closure) { instructions.PushRET((Operand)(main.Body.Count - 1)); ClosureUnit this_closure = (ClosureUnit)(p_callable.heapUnitValue); upValues.PushEnv(); foreach (UpValueUnit u in this_closure.UpValues) { upValues.Add(u); } instructions.PushFunction(this_closure.Function, Env, out instructionsCache); IP = 0; } else if (this_type == UnitType.Intrinsic) { IntrinsicUnit this_intrinsic = (IntrinsicUnit)(p_callable.heapUnitValue); Unit intrinsic_result = this_intrinsic.Function(this); stack.top -= this_intrinsic.Arity; return(intrinsic_result); } VMResult result = Run(); IP = before_call_IP; if (result.status == VMResultType.OK) { return(result.value); } return(new Unit(UnitType.Null)); }
private VMResult Run() { Instruction instruction; while (true) { instruction = instructionsCache[IP]; switch (instruction.opCode) { case OpCode.POP: IP++; stack.Pop(); break; case OpCode.DUP: IP++; stack.Push(stack.Peek()); break; case OpCode.LOAD_DATA: IP++; stack.Push(data[instruction.opA]); break; case OpCode.LOAD_VARIABLE: IP++; stack.Push(variables.GetAt(instruction.opA, CalculateEnvShift(instruction.opB))); break; case OpCode.LOAD_GLOBAL: IP++; if (parallelVM == true) { lock (globals.Get(instruction.opA).heapUnitValue) // needed because global may be loaded from parallel functions stack.Push(globals.Get(instruction.opA)); } else { stack.Push(globals.Get(instruction.opA)); } break; case OpCode.LOAD_IMPORTED_GLOBAL: IP++; stack.Push(modules[instruction.opB].GetGlobal(instruction.opA)); break; case OpCode.LOAD_IMPORTED_DATA: IP++; stack.Push(modules[instruction.opB].GetData(instruction.opA)); break; case OpCode.LOAD_UPVALUE: IP++; if (parallelVM == true) { lock (upValues.GetAt(instruction.opA)) // needed because upvalue may be loaded from parallel functions stack.Push(upValues.GetAt(instruction.opA).UpValue); } else { stack.Push(upValues.GetAt(instruction.opA).UpValue); } break; case OpCode.LOAD_NIL: IP++; stack.Push(new Unit(UnitType.Null)); break; case OpCode.LOAD_TRUE: IP++; stack.Push(new Unit(true)); break; case OpCode.LOAD_FALSE: IP++; stack.Push(new Unit(false)); break; case OpCode.LOAD_INTRINSIC: IP++; stack.Push(new Unit(Intrinsics[instruction.opA])); break; case OpCode.DECLARE_VARIABLE: IP++; variables.Add(stack.Pop()); break; case OpCode.DECLARE_GLOBAL: IP++; // lock(globals) // not needed because global declaration can not happen inside parallel functions globals.Add(stack.Pop()); break; case OpCode.DECLARE_FUNCTION: { IP++; Operand env = instruction.opA; Operand is_function_expression = instruction.opB; Operand new_fun_address = instruction.opC; Unit this_callable = data[new_fun_address]; if (this_callable.Type == UnitType.Function) { if (is_function_expression == 0) { if (env == 0) // Global { // lock(globals) // not needed because global declaration can not happen inside parallel functions globals.Add(this_callable); } else { variables.Add(this_callable); } } else { stack.Push(this_callable); } } else { ClosureUnit this_closure = (ClosureUnit)(this_callable.heapUnitValue); // new upvalues List <UpValueUnit> new_upValues = new List <UpValueUnit>(); foreach (UpValueUnit u in this_closure.UpValues) { // here we convert env from shift based to absolute based UpValueUnit new_upvalue = registeredUpValues.Get(u.Address, CalculateEnvShiftUpVal(u.Env)); new_upValues.Add(new_upvalue); } ClosureUnit new_closure = new ClosureUnit(this_closure.Function, new_upValues); Unit new_closure_unit = new Unit(new_closure); if (is_function_expression == 0) { if (env == 0) // yes they exist! { // lock(globals) // not needed because global declaration can not happen inside parallel functions globals.Add(new_closure_unit); } else { variables.Add(new_closure_unit); } } else { stack.Push(new_closure_unit); } } break; } case OpCode.ASSIGN_VARIABLE: { IP++; Unit old_value = variables.GetAt(instruction.opA, CalculateEnvShift(instruction.opB)); Unit result; switch (instruction.opC) { case ASSIGN: variables.SetAt(stack.Peek(), instruction.opA, CalculateEnvShift(instruction.opB)); break; case ADDITION_ASSIGN: result = old_value + stack.Peek(); variables.SetAt(result, instruction.opA, CalculateEnvShift(instruction.opB)); break; case SUBTRACTION_ASSIGN: result = old_value - stack.Peek(); variables.SetAt(result, instruction.opA, CalculateEnvShift(instruction.opB)); break; case MULTIPLICATION_ASSIGN: result = old_value * stack.Peek(); variables.SetAt(result, instruction.opA, CalculateEnvShift(instruction.opB)); break; case DIVISION_ASSIGN: result = old_value / stack.Peek(); variables.SetAt(result, instruction.opA, CalculateEnvShift(instruction.opB)); break; default: throw new Exception("Unknown operator"); } break; } case OpCode.ASSIGN_GLOBAL: { IP++; Operand address = instruction.opA; Operand op = instruction.opB; Unit new_value = stack.Peek(); if (parallelVM == true) { switch (op) { case ASSIGN: lock (globals.Get(address).heapUnitValue) globals.Set(new_value, address); break; case ADDITION_ASSIGN: lock (globals.Get(address).heapUnitValue){ Unit result = globals.Get(address) + new_value; globals.Set(result, address); } break; case SUBTRACTION_ASSIGN: lock (globals.Get(address).heapUnitValue){ Unit result = globals.Get(address) - new_value; globals.Set(result, address); } break; case MULTIPLICATION_ASSIGN: lock (globals.Get(address).heapUnitValue){ Unit result = globals.Get(address) * new_value; globals.Set(result, address); } break; case DIVISION_ASSIGN: lock (globals.Get(address).heapUnitValue){ Unit result = globals.Get(address) / new_value; globals.Set(result, address); } break; default: throw new Exception("Unknown operator"); } } else { Unit result; switch (op) { case ASSIGN: globals.Set(new_value, address); break; case ADDITION_ASSIGN: result = globals.Get(address) + new_value; globals.Set(result, address); break; case SUBTRACTION_ASSIGN: result = globals.Get(address) - new_value; globals.Set(result, address); break; case MULTIPLICATION_ASSIGN: result = globals.Get(address) * new_value; globals.Set(result, address); break; case DIVISION_ASSIGN: result = globals.Get(address) / new_value; globals.Set(result, address); break; default: throw new Exception("Unknown operator"); } } break; } case OpCode.ASSIGN_IMPORTED_GLOBAL: IP++; modules[instruction.opB].SetOpGlobal(stack.Peek(), instruction.opC, instruction.opA); break; case OpCode.ASSIGN_UPVALUE: IP++; if (parallelVM == true) { UpValueUnit this_upValue; this_upValue = upValues.GetAt(instruction.opA); switch (instruction.opB) { case ASSIGN: lock (this_upValue) this_upValue.UpValue = stack.Peek(); break; case ADDITION_ASSIGN: lock (this_upValue) this_upValue.UpValue = upValues.GetAt(instruction.opA).UpValue + stack.Peek(); break; case SUBTRACTION_ASSIGN: lock (this_upValue) this_upValue.UpValue = upValues.GetAt(instruction.opA).UpValue - stack.Peek(); break; case MULTIPLICATION_ASSIGN: lock (this_upValue) this_upValue.UpValue = upValues.GetAt(instruction.opA).UpValue *stack.Peek(); break; case DIVISION_ASSIGN: lock (this_upValue) this_upValue.UpValue = upValues.GetAt(instruction.opA).UpValue / stack.Peek(); break; default: throw new Exception("Unknown operator"); } } else { UpValueUnit this_upValue = upValues.GetAt(instruction.opA); switch (instruction.opB) { case ASSIGN: this_upValue.UpValue = stack.Peek(); break; case ADDITION_ASSIGN: this_upValue.UpValue = this_upValue.UpValue + stack.Peek(); break; case SUBTRACTION_ASSIGN: this_upValue.UpValue = this_upValue.UpValue - stack.Peek(); break; case MULTIPLICATION_ASSIGN: this_upValue.UpValue = this_upValue.UpValue * stack.Peek(); break; case DIVISION_ASSIGN: this_upValue.UpValue = this_upValue.UpValue / stack.Peek(); break; default: throw new Exception("Unknown operator"); } } break; case OpCode.GET: { IP++; Operand indexes_counter = instruction.opA; Unit[] indexes = new Unit[indexes_counter]; for (int i = indexes_counter - 1; i >= 0; i--) { indexes[i] = stack.Pop(); } Unit value = stack.Pop(); foreach (Unit v in indexes) { value = (value.heapUnitValue).Get(v); } stack.Push(value); break; } case OpCode.SET: { IP++; Operand indexes_counter = instruction.opA; Operand op = instruction.opB; Unit[] indexes = new Unit[indexes_counter]; for (int i = indexes_counter - 1; i >= 0; i--) { indexes[i] = stack.Pop(); } Unit this_table = stack.Pop(); for (int i = 0; i < indexes_counter - 1; i++) { Unit v = indexes[i]; this_table = (this_table.heapUnitValue).Get(v); } Unit new_value = stack.Peek(); Unit index = indexes[indexes_counter - 1]; UnitType index_type = indexes[indexes_counter - 1].Type; if (op == ASSIGN) { ((this_table.heapUnitValue)).Set(index, new_value); } else if (parallelVM == true) { Unit old_value; Unit result; switch (op) { case ADDITION_ASSIGN: lock (this_table.heapUnitValue){ old_value = ((TableUnit)(this_table.heapUnitValue)).Get(index); result = old_value + new_value; (this_table.heapUnitValue).Set(index, result); } break; case SUBTRACTION_ASSIGN: lock (this_table.heapUnitValue){ old_value = ((TableUnit)(this_table.heapUnitValue)).Get(index); result = old_value - new_value; (this_table.heapUnitValue).Set(index, result); } break; case MULTIPLICATION_ASSIGN: lock (this_table.heapUnitValue){ old_value = ((TableUnit)(this_table.heapUnitValue)).Get(index); result = old_value * new_value; (this_table.heapUnitValue).Set(index, result); } break; case DIVISION_ASSIGN: lock (this_table.heapUnitValue){ old_value = ((TableUnit)(this_table.heapUnitValue)).Get(index); result = old_value / new_value; (this_table.heapUnitValue).Set(index, result); } break; default: throw new Exception("Unknown operator"); } } else { Unit old_value = (this_table.heapUnitValue).Get(index); Unit result; switch (op) { case ADDITION_ASSIGN: result = old_value + new_value; break; case SUBTRACTION_ASSIGN: result = old_value - new_value; break; case MULTIPLICATION_ASSIGN: result = old_value * new_value; break; case DIVISION_ASSIGN: result = old_value / new_value; break; default: throw new Exception("Unknown operator"); } (this_table.heapUnitValue).Set(index, result); } break; } case OpCode.JUMP: IP += instruction.opA; break; case OpCode.JUMP_IF_NOT_TRUE: if (stack.Pop().ToBool() == false) { IP += instruction.opA; } else { IP++; } break; case OpCode.JUMP_BACK: IP -= instruction.opA; break; case OpCode.OPEN_ENV: IP++; EnvPush(); break; case OpCode.CLOSE_ENV: IP++; EnvPop(); break; case OpCode.RETURN: IP = instructions.PopRET(); break; case OpCode.RETURN_SET: instructions.PushRET((Operand)(instruction.opA + IP)); IP++; break; case OpCode.ADD: { IP++; Unit opB = stack.Pop(); stack.Push(stack.Pop() + opB); break; } case OpCode.APPEND: { IP++; Unit opB = stack.Pop(); string result = stack.Pop().ToString() + opB.ToString(); stack.Push(new Unit(result)); break; } case OpCode.SUBTRACT: { IP++; Unit opB = stack.Pop(); stack.Push(stack.Pop() - opB); break; } case OpCode.MULTIPLY: IP++; stack.Push(stack.Pop() * stack.Pop()); break; case OpCode.DIVIDE: { IP++; Unit opB = stack.Pop(); stack.Push(stack.Pop() / opB); break; } case OpCode.NEGATE: IP++; stack.Push(-stack.Pop()); break; case OpCode.INCREMENT: IP++; stack.Push(stack.Pop() + 1); break; case OpCode.DECREMENT: IP++; stack.Push(stack.Pop() - 1); break; case OpCode.EQUALS: IP++; stack.Push(new Unit(stack.Pop().Equals(stack.Pop()))); break; case OpCode.NOT_EQUALS: IP++; stack.Push(new Unit(!stack.Pop().Equals(stack.Pop()))); break; case OpCode.GREATER_EQUALS: IP++; // the values are popped out of order from stack, so the logic is inverted! stack.Push(new Unit(stack.Pop().floatValue <= stack.Pop().floatValue)); break; case OpCode.LESS_EQUALS: IP++; // the values are popped out of order from stack, so the logic is inverted! stack.Push(new Unit(stack.Pop().floatValue >= stack.Pop().floatValue)); break; case OpCode.GREATER: IP++; // the values are popped out of order from stack, so the logic is inverted! stack.Push(new Unit(stack.Pop().floatValue < stack.Pop().floatValue)); break; case OpCode.LESS: IP++; // the values are popped out of order from stack, so the logic is inverted! stack.Push(new Unit(stack.Pop().floatValue > stack.Pop().floatValue)); break; case OpCode.NOT: IP++; stack.Push(new Unit(!stack.Pop().ToBool())); break; case OpCode.AND: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(stack.Pop().ToBool() && opB.ToBool())); break; } case OpCode.OR: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(stack.Pop().ToBool() || opB.ToBool())); break; } case OpCode.XOR: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(stack.Pop().ToBool() ^ opB.ToBool())); break; } case OpCode.NAND: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(!(stack.Pop().ToBool() && opB.ToBool()))); break; } case OpCode.NOR: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(!(stack.Pop().ToBool() || opB.ToBool()))); break; } case OpCode.XNOR: { IP++; Unit opB = stack.Pop(); stack.Push(new Unit(!(stack.Pop().ToBool() ^ opB.ToBool()))); break; } case OpCode.CLOSE_CLOSURE: upValues.PopEnv(); EnvSet(instructions.TargetEnv); IP = instructions.PopFunction(out instructionsCache); break; case OpCode.CLOSE_FUNCTION: EnvSet(instructions.TargetEnv); IP = instructions.PopFunction(out instructionsCache); break; case OpCode.NEW_TABLE: { IP++; TableUnit new_table = new TableUnit(null); int n_table = instruction.opA; for (int i = 0; i < n_table; i++) { Unit val = stack.Pop(); Unit key = stack.Pop(); new_table.Map.Add(key, val); } stack.Push(new Unit(new_table)); break; } case OpCode.NEW_LIST: { IP++; ListUnit new_list = new ListUnit(null); int n_table = instruction.opA; for (int i = 0; i < n_table; i++) { Unit val = stack.Pop(); new_list.Elements.Add(val); } stack.Push(new Unit(new_list)); break; } case OpCode.CALL: { IP++; Unit this_callable = stack.Pop(); UnitType this_type = this_callable.Type; if (this_type == UnitType.Function) { FunctionUnit this_func = (FunctionUnit)this_callable.heapUnitValue; instructions.PushRET(IP); instructions.PushFunction(this_func, Env, out instructionsCache); IP = 0; } else if (this_type == UnitType.Closure) { ClosureUnit this_closure = (ClosureUnit)this_callable.heapUnitValue; upValues.PushEnv(); foreach (UpValueUnit u in this_closure.UpValues) { upValues.Add(u); } instructions.PushRET(IP); instructions.PushFunction(this_closure.Function, Env, out instructionsCache); IP = 0; } else if (this_type == UnitType.Intrinsic) { IntrinsicUnit this_intrinsic = (IntrinsicUnit)this_callable.heapUnitValue; Unit result = this_intrinsic.Function(this); stack.top -= this_intrinsic.Arity; stack.Push(result); } else { Error("Trying to call a " + this_callable.Type); return(new VMResult(VMResultType.OK, new Unit(UnitType.Null))); } break; } case OpCode.PUSH_STASH: IP++; stack.PushStash(); break; case OpCode.POP_STASH: IP++; stack.PopStash(); break; case OpCode.EXIT: if (stack.top > 0) { return(new VMResult(VMResultType.OK, stack.Pop())); } return(new VMResult(VMResultType.OK, new Unit(UnitType.Null))); default: Error("Unkown OpCode: " + instruction.opCode); return(new VMResult(VMResultType.ERROR, new Unit(UnitType.Null))); } } }