private int DisassembleConstant(string name, GearsChunk chunk, int offset, EGearsOpCode constantType, Action <string> writeLine)
        {
            int constantIndex = (chunk.ReadCode(ref offset) << 8) + chunk.ReadCode(ref offset);

            switch (constantType)
            {
            case OP_LOAD_CONSTANT: {
                GearsValue value = chunk.ReadConstantValue(constantIndex);
                writeLine($"{name} const[{constantIndex}] ({value})");
            }
            break;

            case OP_LOAD_STRING: {
                string value = chunk.Strings.ReadStringConstant(constantIndex);
                writeLine($"{name} string[{constantIndex}] ({value})");
            }
            break;

            case OP_LOAD_FUNCTION: {
                string value = chunk.ReadConstantValueAsBitStr(constantIndex);
                writeLine($"{name} bitstr[{constantIndex}] ({value})");
            }
            break;
            }
            return(offset);
        }
Exemple #2
0
        /// <summary>
        /// arguments → expression ( "," expression )* ;
        ///
        /// Requires one or more argument expressions, followed by zero or more expressions each preceded by a comma.
        /// To handle zero-argument calls, the call rule itself considers the entire arguments production optional.
        /// </summary>
        private void FinishCall(EGearsOpCode opCode)
        {
            int argumentCount = 0;

            if (!Tokens.Check(RIGHT_PAREN))
            {
                do
                {
                    if (argumentCount >= MAX_PARAMS)
                    {
                        throw new CompilerException(Tokens.Peek(), $"Cannot have more than {MAX_PARAMS} arguments.");
                    }
                    Expression();
                    argumentCount += 1;
                } while (Tokens.Match(COMMA));
            }
            Tokens.Consume(RIGHT_PAREN, "Expect ')' ending call operator parens (following any arguments).");
            EmitOpcode(LineOfLastToken, opCode);
            EmitData(LineOfLastToken, (byte)argumentCount);
        }
        private int Disassemble(GearsChunk chunk, int offset, Action <string> write, Action <string> writeLine)
        {
            write($"{chunk._Lines[offset]:D4}  {offset:D4}  ");
            EGearsOpCode instruction = (EGearsOpCode)chunk.ReadCode(ref offset);

            switch (instruction)
            {
            case OP_LOAD_CONSTANT:
                return(DisassembleConstant("OP_LOAD_CONSTANT", chunk, offset, OP_LOAD_CONSTANT, writeLine));

            case OP_LOAD_STRING:
                return(DisassembleConstant("OP_LOAD_STRING", chunk, offset, OP_LOAD_STRING, writeLine));

            case OP_LOAD_FUNCTION:
                return(DisassembleFunction("OP_LOAD_FUNCTION", chunk, offset, writeLine));

            case OP_NIL:
                return(DisassembleSimple("OP_NIL", chunk, offset, writeLine));

            case OP_TRUE:
                return(DisassembleSimple("OP_TRUE", chunk, offset, writeLine));

            case OP_FALSE:
                return(DisassembleSimple("OP_FALSE", chunk, offset, writeLine));

            case OP_POP:
                return(DisassembleSimple("OP_POP", chunk, offset, writeLine));

            case OP_GET_LOCAL:
                return(DisassembleTwoParams("OP_GET_LOCAL", chunk, offset, writeLine));

            case OP_SET_LOCAL:
                return(DisassembleTwoParams("OP_SET_LOCAL", chunk, offset, writeLine));

            case OP_DEFINE_GLOBAL:
                return(DisassembleConstant("OP_DEF_GLOBAL", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_GET_GLOBAL:
                return(DisassembleConstant("OP_GET_GLOBAL", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_SET_GLOBAL:
                return(DisassembleConstant("OP_SET_GLOBAL", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_GET_UPVALUE:
                return(DisassembleTwoParams("OP_GET_UPVALUE", chunk, offset, writeLine));

            case OP_SET_UPVALUE:
                return(DisassembleTwoParams("OP_SET_UPVALUE", chunk, offset, writeLine));

            case OP_GET_PROPERTY:
                return(DisassembleConstant("OP_GET_PROPERTY", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_SET_PROPERTY:
                return(DisassembleConstant("OP_SET_PROPERTY", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_GET_SUPER:
                return(DisassembleConstant("OP_GET_SUPER", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_EQUAL:
                return(DisassembleSimple("OP_EQUAL", chunk, offset, writeLine));

            case OP_GREATER:
                return(DisassembleSimple("OP_GREATER", chunk, offset, writeLine));

            case OP_LESS:
                return(DisassembleSimple("OP_LESS", chunk, offset, writeLine));

            case OP_ADD:
                return(DisassembleSimple("OP_ADD", chunk, offset, writeLine));

            case OP_SUBTRACT:
                return(DisassembleSimple("OP_SUBTRACT", chunk, offset, writeLine));

            case OP_MULTIPLY:
                return(DisassembleSimple("OP_MULTIPLY", chunk, offset, writeLine));

            case OP_DIVIDE:
                return(DisassembleSimple("OP_DIVIDE", chunk, offset, writeLine));

            case OP_NOT:
                return(DisassembleSimple("OP_NOT", chunk, offset, writeLine));

            case OP_NEGATE:
                return(DisassembleSimple("OP_NEGATE", chunk, offset, writeLine));

            case OP_JUMP:
                return(DisassembleTwoParams("OP_JUMP", chunk, offset, writeLine));

            case OP_JUMP_IF_FALSE:
                return(DisassembleTwoParams("OP_JUMP_IF_FALSE", chunk, offset, writeLine));

            case OP_LOOP:
                return(DisassembleTwoParams("OP_LOOP", chunk, offset, writeLine));

            case OP_CALL:
                return(DisassembleOneParam("OP_CALL", chunk, offset, writeLine));

            case OP_INVOKE:
                return(DisassembleInvoke("OP_INVOKE", chunk, offset, writeLine));

            case OP_SUPER_INVOKE:
                return(DisassembleInvoke("OP_SUPER_INVOKE", chunk, offset, writeLine));

            case OP_CLOSE_UPVALUE:
                return(DisassembleSimple("OP_CLOSE_UPVALUE", chunk, offset, writeLine));

            case OP_RETURN:
                return(DisassembleSimple("OP_RETURN", chunk, offset, writeLine));

            case OP_CLASS:
                return(DisassembleConstant("OP_CLASS", chunk, offset, OP_LOAD_FUNCTION, writeLine));

            case OP_INHERIT:
                return(DisassembleSimple("OP_INHERIT", chunk, offset, writeLine));

            case OP_METHOD:
                return(DisassembleSimple("OP_METHOD", chunk, offset, writeLine));

            default:
                writeLine($"Unknown opcode {instruction}");
                return(offset);
            }
        }
Exemple #4
0
 internal void WriteCode(EGearsOpCode value, int line)
 {
     WriteCode((byte)value, line);
 }
Exemple #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}");
                }
            }
        }
Exemple #6
0
 private int EmitJump(EGearsOpCode instruction)
 {
     EmitData(LineOfLastToken, (byte)instruction, 0xff, 0xff);
     return(_Chunk.SizeCode - 2);
 }
Exemple #7
0
        // === Emit Infrastructure ===================================================================================
        // ===========================================================================================================

        private void EmitOpcode(int line, EGearsOpCode opcode)
        {
            _Chunk.WriteCode(opcode, line);
        }