diff --git a/code/code.go b/code/code.go index 309e73a..5e83d83 100644 --- a/code/code.go +++ b/code/code.go @@ -22,6 +22,8 @@ const ( OpEqual OpNotEqual OpGreaterThan + OpMinus + OpBang ) type Definition struct { @@ -41,6 +43,8 @@ var definitions = map[Opcode]*Definition{ OpEqual: {"OpEqual", []int{}}, OpNotEqual: {"OpNotEqual", []int{}}, OpGreaterThan: {"OpGreaterThan", []int{}}, + OpMinus: {"OpMinus", []int{}}, + OpBang: {"OpBang", []int{}}, } func Lookup(op byte) (*Definition, error) { diff --git a/compiler/compiler.go b/compiler/compiler.go index 05d7460..0100f97 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -91,6 +91,21 @@ func (c *Compiler) Compile(node ast.Node) error { c.emit(code.OpFalse) } + case *ast.PrefixExpression: + err := c.Compile(node.Right) + if err != nil { + return err + } + + switch node.Operator { + case "!": + c.emit(code.OpBang) + case "-": + c.emit(code.OpMinus) + default: + return fmt.Errorf("unknown operator %s", node.Operator) + } + } return nil diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index a009ed7..8318a99 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -68,6 +68,15 @@ func TestIntegerArithmetic(t *testing.T) { code.Make(code.OpPop), }, }, + { + input: "-1", + expectedConstants: []interface{}{1}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpConstant, 0), + code.Make(code.OpMinus), + code.Make(code.OpPop), + }, + }, } runCompilerTests(t, tests) @@ -151,6 +160,15 @@ func TestBooleanExpressions(t *testing.T) { code.Make(code.OpPop), }, }, + { + input: "!true", + expectedConstants: []interface{}{}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpTrue), + code.Make(code.OpBang), + code.Make(code.OpPop), + }, + }, } runCompilerTests(t, tests) diff --git a/vm/vm.go b/vm/vm.go index 6e2b8cb..ff391c4 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -74,6 +74,19 @@ func (vm *VM) Run() error { if err != nil { return err } + + case code.OpBang: + err := vm.executeBangOperator() + if err != nil { + return err + } + + case code.OpMinus: + err := vm.executeMinusOperator() + if err != nil { + return err + } + } } @@ -167,6 +180,30 @@ func (vm *VM) executeIntegerComparison(op code.Opcode, left, right object.Object } } +func (vm *VM) executeBangOperator() error { + operand := vm.pop() + + switch operand { + case True: + return vm.push(False) + case False: + return vm.push(True) + default: + return vm.push(False) + } +} + +func (vm *VM) executeMinusOperator() error { + operand := vm.pop() + + if operand.Type() != object.INTEGER_OBJ { + return fmt.Errorf("unsupported type for negation: %s", operand.Type()) + } + + value := operand.(*object.Integer).Value + return vm.push(&object.Integer{Value: -value}) +} + func nativeBoolToBooleanObject(input bool) *object.Boolean { if input { return True diff --git a/vm/vm_test.go b/vm/vm_test.go index 612de94..ec3d6ec 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -119,6 +119,10 @@ func TestIntegerArithmetic(t *testing.T) { {"(1 < 2) == false", false}, {"(1 > 2) == true", false}, {"(1 > 2) == false", true}, + {"-5", -5}, + {"-10", -10}, + {"-50 + 100 + -50", 0}, + {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, } runVmTests(t, tests) @@ -128,6 +132,12 @@ func TestBooleanExpressions(t *testing.T) { tests := []vmTestCase{ {"true", true}, {"false", false}, + {"!true", false}, + {"!false", true}, + {"!5", false}, + {"!!true", true}, + {"!!false", false}, + {"!!5", true}, } runVmTests(t, tests)