Arithmetic

This commit is contained in:
Chuck Smith
2024-01-26 12:13:23 -05:00
parent e34991c081
commit b4cc771baa
6 changed files with 111 additions and 14 deletions

View File

@@ -13,6 +13,10 @@ type Opcode byte
const ( const (
OpConstant Opcode = iota OpConstant Opcode = iota
OpAdd OpAdd
OpPop
OpSub
OpMul
OpDiv
) )
type Definition struct { type Definition struct {
@@ -23,6 +27,10 @@ type Definition struct {
var definitions = map[Opcode]*Definition{ var definitions = map[Opcode]*Definition{
OpConstant: {"OpConstant", []int{2}}, OpConstant: {"OpConstant", []int{2}},
OpAdd: {"OpAdd", []int{}}, OpAdd: {"OpAdd", []int{}},
OpPop: {"OpPop", []int{}},
OpSub: {"OpSub", []int{}},
OpMul: {"OpMul", []int{}},
OpDiv: {"OpDiv", []int{}},
} }
func Lookup(op byte) (*Definition, error) { func Lookup(op byte) (*Definition, error) {

View File

@@ -34,6 +34,7 @@ func (c *Compiler) Compile(node ast.Node) error {
if err != nil { if err != nil {
return err return err
} }
c.emit(code.OpPop)
case *ast.InfixExpression: case *ast.InfixExpression:
err := c.Compile(node.Left) err := c.Compile(node.Left)
@@ -49,6 +50,12 @@ func (c *Compiler) Compile(node ast.Node) error {
switch node.Operator { switch node.Operator {
case "+": case "+":
c.emit(code.OpAdd) c.emit(code.OpAdd)
case "-":
c.emit(code.OpSub)
case "*":
c.emit(code.OpMul)
case "/":
c.emit(code.OpDiv)
default: default:
return fmt.Errorf("unknown operator %s", node.Operator) return fmt.Errorf("unknown operator %s", node.Operator)
} }

View File

@@ -25,6 +25,47 @@ func TestIntegerArithmetic(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpPop),
},
},
{
input: "1; 2",
expectedConstants: []interface{}{1, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpPop),
code.Make(code.OpConstant, 1),
code.Make(code.OpPop),
},
},
{
input: "1 - 2",
expectedConstants: []interface{}{1, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpSub),
code.Make(code.OpPop),
},
},
{
input: "1 * 2",
expectedConstants: []interface{}{1, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpMul),
code.Make(code.OpPop),
},
},
{
input: "2 / 1",
expectedConstants: []interface{}{2, 1},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpDiv),
code.Make(code.OpPop),
}, },
}, },
} }

View File

@@ -46,7 +46,7 @@ func Start(in io.Reader, out io.Writer) {
continue continue
} }
stackTop := machine.StackTop() stackTop := machine.LastPoppedStackElem()
io.WriteString(out, stackTop.Inspect()) io.WriteString(out, stackTop.Inspect())
io.WriteString(out, "\n") io.WriteString(out, "\n")
} }

View File

@@ -27,11 +27,8 @@ func New(bytecode *compiler.Bytecode) *VM {
} }
} }
func (vm *VM) StackTop() object.Object { func (vm *VM) LastPoppedStackElem() object.Object {
if vm.sp == 0 { return vm.stack[vm.sp]
return nil
}
return vm.stack[vm.sp-1]
} }
func (vm *VM) Run() error { func (vm *VM) Run() error {
@@ -48,14 +45,14 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpAdd: case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv:
right := vm.pop() err := vm.executeBinaryOperation(op)
left := vm.pop() if err != nil {
leftValue := left.(*object.Integer).Value return err
rightValue := right.(*object.Integer).Value }
result := leftValue + rightValue case code.OpPop:
vm.push(&object.Integer{Value: result}) vm.pop()
} }
} }
@@ -78,3 +75,39 @@ func (vm *VM) pop() object.Object {
vm.sp-- vm.sp--
return o return o
} }
func (vm *VM) executeBinaryOperation(op code.Opcode) error {
right := vm.pop()
left := vm.pop()
leftType := left.Type()
rightRight := right.Type()
if leftType == object.INTEGER_OBJ && rightRight == object.INTEGER_OBJ {
return vm.executeBinaryIntegerOperation(op, left, right)
}
return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightRight)
}
func (vm *VM) executeBinaryIntegerOperation(op code.Opcode, left, right object.Object) error {
leftValue := left.(*object.Integer).Value
rightValue := right.(*object.Integer).Value
var result int64
switch op {
case code.OpAdd:
result = leftValue + rightValue
case code.OpSub:
result = leftValue - rightValue
case code.OpMul:
result = leftValue * rightValue
case code.OpDiv:
result = leftValue / rightValue
default:
return fmt.Errorf("unknown integer operator: %d", op)
}
return vm.push(&object.Integer{Value: result})
}

View File

@@ -33,7 +33,7 @@ func runVmTests(t *testing.T, tests []vmTestCase) {
t.Fatalf("vm error: %s", err) t.Fatalf("vm error: %s", err)
} }
stackElem := vm.StackTop() stackElem := vm.LastPoppedStackElem()
testExpectedObject(t, tt.expected, stackElem) testExpectedObject(t, tt.expected, stackElem)
} }
@@ -75,6 +75,14 @@ func TestIntegerArithmetic(t *testing.T) {
{"1", 1}, {"1", 1},
{"2", 2}, {"2", 2},
{"1 + 2", 3}, {"1 + 2", 3},
{"1 * 2", 2},
{"4 / 2", 2},
{"50 / 2 * 2 + 10 - 5", 55},
{"5 + 5 + 5 + 5 - 10", 10},
{"2 * 2 * 2 * 2 * 2", 32},
{"5 * 2 + 10", 20},
{"5 + 2 * 10", 25},
{"5 * (2 + 10)", 60},
} }
runVmTests(t, tests) runVmTests(t, tests)