Пример #1
0
        // === Functions =============================================================================================
        // ===========================================================================================================

        private void DefineMethod()
        {
            ulong            methodName = (ulong)ReadConstant();
            GearsValue       methodPtr  = Peek();
            GearsObjFunction method     = GetObjectFromPtr <GearsObjFunction>(methodPtr);
            GearsObjClass    objClass   = GetObjectFromPtr <GearsObjClass>(Peek(1));

            objClass.Methods.Set(methodName, methodPtr);
            Pop();
        }
Пример #2
0
        private bool BindLoxMethod(GearsObjClass classObj, ulong name)
        {
            if (!classObj.Methods.TryGet(name, out GearsValue method))
            {
                return(false);
            }
            int objPtr = HeapAddObject(new GearsObjBoundMethod(Peek(), HeapGetObject(method.AsObjPtr) as GearsObjFunction));

            Pop();
            Push(GearsValue.CreateObjPtr(objPtr));
            return(true);
        }
Пример #3
0
        private void CallInvokeSuper()
        {
            int argCount = ReadByte();

            // next instruction will always be OP_GET_UPVALUE (for the super class). we include it here:
            if (!(ReadByte() == (int)OP_GET_UPVALUE))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), "OP_SUPER_INVOKE must be followed by OP_GET_UPVALUE.");
            }
            int             slot       = ReadShort();
            GearsObjUpvalue upvalue    = _OpenFrame.Function.Upvalues[slot];
            GearsObjClass   superclass = GetObjectFromPtr <GearsObjClass>(upvalue.IsClosed ? upvalue.Value : StackGet(upvalue.OriginalSP));
            ulong           methodName = (ulong)ReadConstant();

            InvokeFromClass(argCount, methodName, GearsValue.NilValue, superclass);
        }
Пример #4
0
        private void InvokeFromClass(int argCount, ulong methodName, GearsValue receiverPtr, GearsObjClass objClass)
        {
            if (!objClass.Methods.TryGet(methodName, out GearsValue methodPtr))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{objClass} has no method with name '{BitString.GetBitStr(methodName)}'.");
            }
            if ((!methodPtr.IsObjPtr) || !(HeapGetObject(methodPtr.AsObjPtr) is GearsObjFunction method))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Could not resolve method '{BitString.GetBitStr(methodName)}' in class {objClass}.");
            }
            if (method.Arity != argCount)
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{method} expects {method.Arity} arguments but was passed {argCount}.");
            }
            int ip = method.IP;
            int bp = _SP - (method.Arity + 1);

            if (!receiverPtr.IsNil)
            {
                StackSet(bp, receiverPtr); // todo: this wipes out the method object. Is this bad?
            }
            PushFrame(new GearsCallFrame(method, ip, bp));
        }
Пример #5
0
        /// <summary>
        /// Runs the script from top to bottom.
        /// </summary>
        private bool RunOne()
        {
            LastReturnValue = GearsValue.NilValue;
            while (true)
            {
                EGearsOpCode instruction = (EGearsOpCode)ReadByte();
                switch (instruction)
                {
                case OP_LOAD_CONSTANT:
                    Push(ReadConstant());
                    break;

                case OP_LOAD_STRING:
                    Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(ReadConstantString()))));
                    break;

                case OP_LOAD_FUNCTION: {
                    int arity                = ReadByte();
                    int address              = ReadShort();
                    int upvalueCount         = ReadByte();
                    GearsObjFunction closure = new GearsObjFunction(Chunk, arity, upvalueCount, address);
                    for (int i = 0; i < upvalueCount; i++)
                    {
                        bool isLocal = ReadByte() == 1;
                        int  index   = ReadByte();
                        if (isLocal)
                        {
                            int location = _OpenFrame.BP + index;
                            closure.Upvalues[i] = CaptureUpvalue(location);
                        }
                        else
                        {
                            closure.Upvalues[i] = _OpenFrame.Function.Upvalues[index];
                        }
                    }
                    Push(GearsValue.CreateObjPtr(HeapAddObject(closure)));
                }
                break;

                case OP_NIL:
                    Push(GearsValue.NilValue);
                    break;

                case OP_TRUE:
                    Push(GearsValue.TrueValue);
                    break;

                case OP_FALSE:
                    Push(GearsValue.FalseValue);
                    break;

                case OP_POP:
                    Pop();
                    break;

                case OP_GET_LOCAL: {
                    int slot = ReadShort();
                    Push(StackGet(slot + _BP));
                }
                break;

                case OP_SET_LOCAL: {
                    int slot = ReadShort();
                    StackSet(slot + _BP, Peek());
                }
                break;

                case OP_GET_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    if (!Globals.TryGet(name, out GearsValue value))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined variable '{BitString.GetBitStr(name)}'.");
                    }
                    Push(value);
                }
                break;

                case OP_DEFINE_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    Globals.Set(name, Peek());
                    Pop();
                }
                break;

                case OP_SET_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    if (!Globals.ContainsKey(name))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined variable '{BitString.GetBitStr(name)}'.");
                    }
                    Globals.Set(name, Peek());
                    break;
                }

                case OP_GET_UPVALUE: {
                    int             slot    = ReadShort();
                    GearsObjUpvalue upvalue = _OpenFrame.Function.Upvalues[slot];
                    if (upvalue.IsClosed)
                    {
                        Push(upvalue.Value);
                    }
                    else
                    {
                        Push(StackGet(upvalue.OriginalSP));
                    }
                }
                break;

                case OP_SET_UPVALUE: {
                    int             slot    = ReadShort();
                    GearsObjUpvalue upvalue = _OpenFrame.Function.Upvalues[slot];
                    if (upvalue.IsClosed)
                    {
                        upvalue.Value = Peek();
                    }
                    else
                    {
                        StackSet(upvalue.OriginalSP, Peek());
                    }
                }
                break;

                case OP_GET_PROPERTY: {
                    GearsObjInstance instance = GetObjectFromPtr <GearsObjInstance>(Peek());
                    ulong            name     = (ulong)ReadConstant(); // property name
                    if (instance.TryGetField(name, out GearsValue value))
                    {
                        Pop();         // instance
                        Push(value);   // property value
                        break;
                    }
                    if (instance is GearsObjInstanceLox loxInstance && BindLoxMethod(loxInstance.Class, name))
                    {
                        break;
                    }
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined property or method '{name}'.");
                }

                case OP_SET_PROPERTY: {
                    GearsObjInstance instance = GetObjectFromPtr <GearsObjInstance>(Peek(1));
                    ulong            name     = (ulong)ReadConstant(); // property name
                    GearsValue       value    = Pop();                 // value
                    instance.SetField(name, value);
                    Pop();                                             // ptr
                    Push(value);                                       // value
                }
                break;

                case OP_GET_SUPER: {
                    ulong         name       = (ulong)ReadConstant(); // method/property name
                    GearsObjClass superclass = GetObjectFromPtr <GearsObjClass>(Pop());
                    if (!BindLoxMethod(superclass, name))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Could not get {name} in superclass {superclass}");
                    }
                }
                break;

                case OP_EQUAL:
                    Push(AreValuesEqual(Pop(), Pop()));
                    break;

                case OP_GREATER: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a > b);
                }
                break;

                case OP_LESS: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a < b);
                }
                break;

                case OP_BITWISE_AND: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of bitwise operators must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push((long)((double)a) & (long)((double)b));
                }
                break;

                case OP_BITWISE_OR: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of bitwise operators must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push((long)((double)a) | (long)((double)b));
                }
                break;

                case OP_ADD: {
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    if (a.IsNumber && b.IsNumber)
                    {
                        Push(a + b);
                    }
                    else if (a.IsObjType <GearsObjString>(this) && b.IsObjType <GearsObjString>(this))
                    {
                        string sa = GetObjectFromPtr <GearsObjString>(a).Value;
                        string sb = GetObjectFromPtr <GearsObjString>(b).Value;
                        Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(sa + sb))));
                    }
                    else
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of add must be numbers or strings.");
                    }
                }
                break;

                case OP_SUBTRACT: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of subtract must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a - b);
                }
                break;

                case OP_MULTIPLY: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of multiply must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a * b);
                }
                break;

                case OP_DIVIDE: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of divide must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a / b);
                }
                break;

                case OP_NOT:
                    Push(IsFalsey(Pop()));
                    break;

                case OP_NEGATE: {
                    if (!Peek(0).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operand of negate must be a number.");
                    }
                    Push(-Pop());
                }
                break;

                case OP_JUMP: {
                    int offset = ReadShort();
                    ModIP(offset);
                }
                break;

                case OP_JUMP_IF_FALSE: {
                    int offset = ReadShort();
                    if (IsFalsey(Peek()))
                    {
                        ModIP(offset);
                    }
                }
                break;

                case OP_LOOP: {
                    int offset = ReadShort();
                    ModIP(-offset);
                }
                break;

                case OP_CALL: {
                    Call(argCount: ReadByte());
                }
                break;

                case OP_INVOKE: {
                    CallInvoke();
                }
                break;

                case OP_SUPER_INVOKE: {
                    CallInvokeSuper();
                }
                break;

                case OP_CLOSE_UPVALUE:
                    CloseUpvalues(_SP - 1);
                    Pop();
                    break;

                case OP_RETURN: {
                    GearsValue result = Pop();
                    CloseUpvalues(_OpenFrame.BP);
                    if (PopFrame())
                    {
                        if (_SP != 0)
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Error after final return: SP is '{_SP}', not '0'.");
                        }
                        LastReturnValue = result;
                        _IP             = Chunk.SizeCode; // code is complete and no longer running.
                        return(true);
                    }
                    Push(result);
                }
                break;

                case OP_CLASS: {
                    Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjClass(ReadConstantString()))));
                }
                break;

                case OP_INHERIT: {
                    if (!Peek(0).IsObjType <GearsObjClass>(this))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Superclass is not a class.");
                    }
                    GearsObjClass super = GetObjectFromPtr <GearsObjClass>(Peek(1));
                    GearsObjClass sub   = GetObjectFromPtr <GearsObjClass>(Peek(0));
                    foreach (ulong key in super.Methods.AllKeys)
                    {
                        if (!super.Methods.TryGet(key, out GearsValue methodPtr))
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), "Could not copy superclass method table.");
                        }
                        sub.Methods.Set(key, methodPtr);
                    }
                    Pop();         // pop subclass
                }
                break;

                case OP_METHOD: {
                    DefineMethod();
                }
                break;

                default:
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Unknown opcode {instruction}");
                }
            }
        }