Files
monkey/code/code.go
Chuck Smith 7463b3af39
Some checks failed
Build / build (push) Failing after 1m30s
Test / build (push) Failing after 12m1s
fixed mutability
2024-03-15 15:50:11 -04:00

195 lines
4.0 KiB
Go

package code
import (
"bytes"
"encoding/binary"
"fmt"
)
type Instructions []byte
type Opcode byte
const (
OpConstant Opcode = iota
OpAdd
OpPop
OpSub
OpMul
OpDiv
OpTrue
OpFalse
OpEqual
OpNotEqual
OpGreaterThan
OpMinus
OpBang
OpJumpNotTruthy
OpJump
OpNull
OpAssignGlobal
OpAssignLocal
OpGetGlobal
OpSetGlobal
OpArray
OpHash
OpIndex
OpCall
OpReturnValue
OpReturn
OpGetLocal
OpSetLocal
OpGetBuiltin
OpClosure
OpGetFree
OpCurrentClosure
OpNoop
)
type Definition struct {
Name string
OperandWidths []int
}
var definitions = map[Opcode]*Definition{
OpConstant: {"OpConstant", []int{2}},
OpAssignGlobal: {"OpAssignGlobal", []int{2}},
OpAssignLocal: {"OpAssignLocal", []int{1}},
OpAdd: {"OpAdd", []int{}},
OpPop: {"OpPop", []int{}},
OpSub: {"OpSub", []int{}},
OpMul: {"OpMul", []int{}},
OpDiv: {"OpDiv", []int{}},
OpTrue: {"OpTrue", []int{}},
OpFalse: {"OpFalse", []int{}},
OpEqual: {"OpEqual", []int{}},
OpNotEqual: {"OpNotEqual", []int{}},
OpGreaterThan: {"OpGreaterThan", []int{}},
OpMinus: {"OpMinus", []int{}},
OpBang: {"OpBang", []int{}},
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
OpJump: {"OpJump", []int{2}},
OpNull: {"OpNull", []int{}},
OpGetGlobal: {"OpGetGlobal", []int{2}},
OpSetGlobal: {"OpSetGlobal", []int{2}},
OpArray: {"OpArray", []int{2}},
OpHash: {"OpHash", []int{2}},
OpIndex: {"OpIndex", []int{}},
OpCall: {"OpCall", []int{1}},
OpReturnValue: {"OpReturnValue", []int{}},
OpReturn: {"OpReturn", []int{}},
OpGetLocal: {"OpGetLocal", []int{1}},
OpSetLocal: {"OpSetLocal", []int{1}},
OpGetBuiltin: {"OpGetBuiltin", []int{1}},
OpClosure: {"OpClosure", []int{2, 1}},
OpGetFree: {"OpGetFree", []int{1}},
OpCurrentClosure: {"OpCurrentClosure", []int{}},
OpNoop: {"OpNoop", []int{}},
}
func Lookup(op byte) (*Definition, error) {
def, ok := definitions[Opcode(op)]
if !ok {
return nil, fmt.Errorf("opcode %d undefined", op)
}
return def, nil
}
func Make(op Opcode, operands ...int) []byte {
def, ok := definitions[op]
if !ok {
return []byte{}
}
instructions := 1
for _, w := range def.OperandWidths {
instructions += w
}
instruction := make([]byte, instructions)
instruction[0] = byte(op)
offset := 1
for i, o := range operands {
width := def.OperandWidths[i]
switch width {
case 2:
binary.BigEndian.PutUint16(instruction[offset:], uint16(o))
case 1:
instruction[offset] = byte(o)
}
offset += width
}
return instruction
}
func (ins Instructions) String() string {
var out bytes.Buffer
i := 0
for i < len(ins) {
def, err := Lookup(ins[i])
if err != nil {
fmt.Fprintf(&out, "ERROR: %s\n", err)
continue
}
operands, read := ReadOperands(def, ins[i+1:])
fmt.Fprintf(&out, "%04d %s\n", i, ins.fmtInstruction(def, operands))
i += 1 + read
}
return out.String()
}
func (ins Instructions) fmtInstruction(def *Definition, operands []int) string {
operandCount := len(def.OperandWidths)
if len(operands) != operandCount {
return fmt.Sprintf("ERROR: operand len %d does not match defined %d\n", len(operands), operandCount)
}
switch operandCount {
case 0:
return def.Name
case 1:
return fmt.Sprintf("%s %d", def.Name, operands[0])
case 2:
return fmt.Sprintf("%s %d %d", def.Name, operands[0], operands[1])
}
return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name)
}
func ReadOperands(def *Definition, ins Instructions) ([]int, int) {
operands := make([]int, len(def.OperandWidths))
offset := 0
for i, width := range def.OperandWidths {
switch width {
case 2:
operands[i] = int(ReadUint16(ins[offset:]))
case 1:
operands[i] = int(ReadUint8(ins[offset:]))
}
offset += width
}
return operands, offset
}
func ReadUint16(ins Instructions) uint16 {
return binary.BigEndian.Uint16(ins)
}
func ReadUint8(ins Instructions) uint8 {
return ins[0]
}