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); }
/// <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); } }
internal void WriteCode(EGearsOpCode value, int line) { WriteCode((byte)value, line); }
/// <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}"); } } }
private int EmitJump(EGearsOpCode instruction) { EmitData(LineOfLastToken, (byte)instruction, 0xff, 0xff); return(_Chunk.SizeCode - 2); }
// === Emit Infrastructure =================================================================================== // =========================================================================================================== private void EmitOpcode(int line, EGearsOpCode opcode) { _Chunk.WriteCode(opcode, line); }