public void ChunkReturnsConstantIndices() { var chunk = new Chunk(); Assert.Equal(0, chunk.AddConstant(Values.Bool(true))); Assert.Equal(1, chunk.AddConstant(Values.Number(0m))); }
public void ValueEqualMemberAndExtensionAreEquivalent() { Assert.Equal(Values.Bool(true).Equals(Values.Bool(true)), Values.Bool(true).EqualsValue(Values.Bool(true))); Assert.Equal(Values.Number(123).Equals(Values.Number(123)), Values.Number(123).EqualsValue(Values.Number(123))); Assert.Equal(Values.Number(123).Equals(Values.Bool(false)), Values.Number(123).EqualsValue(Values.Bool(false))); Assert.Equal(Values.Number(123).Equals((object)Values.Bool(false)), Values.Number(123).EqualsValue(Values.Bool(false))); Assert.Equal(Values.Bool(true).Equals((object)Values.Bool(true)), Values.Bool(true).EqualsValue(Values.Bool(true))); }
public void ChunkWritesConstants() { var chunk = new Chunk(); chunk.AddConstant(Values.String("test")); chunk.AddConstant(Values.Bool(true)); Assert.Equal(Values.String("test"), chunk.Constants[0]); Assert.Equal(Values.Bool(true), chunk.Constants[1]); }
public void ValuesDoNotEqualUnwrappedValues() { Assert.False(Values.Bool(true).Equals(null)); Assert.False(Values.Bool(true).Equals(true)); Assert.False(Values.String("test").Equals("test")); var func = new Function("test", 0); Assert.False(Values.Function(func).Equals(func)); }
public void PeekBeyondStackBorderThrows() { var vm = new VirtualMachine(); vm.Push(Values.Bool(true)); vm.Push(Values.Bool(false)); vm.Peek(0); vm.Peek(1); Assert.Throws <RuntimeExecutionException>(() => vm.Peek(2)); Assert.Throws <RuntimeExecutionException>(() => vm.Peek(3)); Assert.Throws <RuntimeExecutionException>(() => vm.Peek(1_000_000_000)); }
public void ValuesAreTheirOwnType() { Assert.True(Values.Bool(true).IsBool()); Assert.True(Values.Bool(false).IsBool()); Assert.True(Values.Number(1m).IsNumber()); Assert.True(Values.String("").IsString()); Assert.True(Values.Function(new Function("testfunc", 0)).IsFunction()); Assert.True(Values.NativeFunction(args => Values.Bool(true), 0).IsNativeFunction()); }
public void PushAndPopAndPeekWorkAsStack() { var vm = new VirtualMachine(); var firstVal = Values.Bool(false); var secondVal = Values.Number(decimal.MinusOne); vm.Push(firstVal); vm.Push(secondVal); Assert.Equal(vm.Peek(0), secondVal); Assert.Equal(vm.Peek(1), firstVal); Assert.Equal(vm.Pop(), secondVal); Assert.Equal(vm.Pop(), firstVal); }
public void ValuesRetainTheValueTheyAreGiven() { Assert.True(Values.Bool(true).AsBool()); Assert.False(Values.Bool(false).AsBool()); Assert.Equal(0m, Values.Number(0m).AsNumber()); Assert.Equal(1m, Values.Number(1m).AsNumber()); Assert.Equal(1000.500m, Values.Number(1000.500m).AsNumber()); Assert.Equal("", Values.String("").AsString()); Assert.Equal("test", Values.String("test").AsString()); var func = new Function("test", 0); Assert.Equal(func, Values.Function(func).AsFunction());
public void ValuesAreNotSomeOtherType() { var boolVal = Values.Bool(true); Assert.False(boolVal.IsNumber()); Assert.False(boolVal.IsString()); Assert.False(boolVal.IsFunction()); Assert.False(boolVal.IsNativeFunction()); var numberVal = Values.Number(0m); Assert.False(numberVal.IsBool()); Assert.False(numberVal.IsString()); Assert.False(numberVal.IsFunction()); Assert.False(numberVal.IsNativeFunction()); var stringVal = Values.String(""); Assert.False(stringVal.IsBool()); Assert.False(stringVal.IsNumber()); Assert.False(stringVal.IsFunction()); Assert.False(stringVal.IsNativeFunction()); var funcVal = Values.Function(new Function("myFunction", 2)); Assert.False(funcVal.IsBool()); Assert.False(funcVal.IsString()); Assert.False(funcVal.IsNumber()); Assert.False(funcVal.IsNativeFunction()); var nativeFuncVal = Values.NativeFunction(args => Values.Bool(true), 0); Assert.False(nativeFuncVal.IsBool()); Assert.False(nativeFuncVal.IsString()); Assert.False(nativeFuncVal.IsNumber()); Assert.False(nativeFuncVal.IsFunction()); }
InterpretResult Run() { Frame = frames[FrameCount - 1]; while (true) { #if TRACE System.Diagnostics.Debug.WriteLine(CurrentChunk.DisassembleInstruction(Frame.IP)); #endif InstructionCounter++; var instruction = (OpCode)ReadByte(); switch (instruction) { case OpCode.Nop: break; case OpCode.Constant: { var constant = ReadConstant(); Push(constant); } break; case OpCode.True: Push(Values.Bool(true)); break; case OpCode.False: Push(Values.Bool(false)); break; case OpCode.Pop: Pop(); break; case OpCode.PopN: Pop(ReadByte()); break; case OpCode.LoadGlobal: { var name = ReadConstant().AsString(); if (!globals.TryGetValue(name, out var value)) { throw new RuntimeExecutionException($"Undefined variable '{name}'", CreateStackTrace()); } Push(value); } break; case OpCode.LoadLocal: { var slot = ReadByte(); Push(stack[Frame.StackBase + slot]); } break; case OpCode.AssignGlobal: { var name = ReadConstant().AsString(); if (!globals.ContainsKey(name)) { throw new RuntimeExecutionException($"Undefined variable '{name}'", CreateStackTrace()); } globals[name] = Peek(); } break; case OpCode.AssignLocal: { var slot = ReadByte(); stack[Frame.StackBase + slot] = Peek(); } break; case OpCode.DefineGlobal: { var globalName = ReadConstant().AsString(); globals[globalName] = Peek(); Pop(); } break; case OpCode.Equal: { var a = Pop(); var b = Pop(); var equals = a.EqualsValue(b); Push(Values.Bool(equals)); } break; case OpCode.Greater: BinaryOp((a, b) => Values.Bool(a > b)); break; case OpCode.Less: BinaryOp((a, b) => Values.Bool(a < b)); break; case OpCode.Add: BinaryOp((a, b) => Values.Number(a + b)); break; case OpCode.Subtract: BinaryOp((a, b) => Values.Number(a - b)); break; case OpCode.Multiply: BinaryOp((a, b) => Values.Number(a * b)); break; case OpCode.Divide: BinaryOp((a, b) => Values.Number(a / b)); break; case OpCode.Exp: BinaryOp((a, b) => Values.Number((decimal)Math.Pow((double)a, (double)b))); break; case OpCode.Modulo: BinaryOp((a, b) => Values.Number(decimal.Remainder(a, b))); break; case OpCode.Not: { var falsey = Pop().IsFalsey(); Push(Values.Bool(falsey)); } break; case OpCode.Negate: { if (!Peek(0).IsNumber()) { throw new RuntimeExecutionException("Operand must be a number", CreateStackTrace()); } Push(Values.Number(-Pop().AsNumber())); } break; case OpCode.Print: StdOut.WriteLine(Pop().ToString()); break; case OpCode.Jump: { var offset = ReadShort(); Frame.IP += offset; } break; case OpCode.JumpIfFalse: { var offset = ReadShort(); if (Peek().IsFalsey()) { Frame.IP += offset; } } break; case OpCode.Loop: { var offset = ReadShort(); Frame.IP -= offset; } break; case OpCode.Call: { var argCount = ReadByte(); CallValue(Peek(argCount), argCount); Frame = frames[FrameCount - 1]; } break; case OpCode.Return: { var result = Pop(); FrameCount--; if (FrameCount == 0) { Pop(); return(InterpretResult.Success); } StackPointer = Frame.StackBase; Push(result); Frame = frames[FrameCount - 1]; } break; default: throw new RuntimeExecutionException($"Unknown instruction value '{instruction}' at ip offset {Frame.IP,4:D4}", CreateStackTrace()); } } }
static Value nativeFunc(Value[] args) => Values.Bool(true);