Files
monkey/code/code.go
Chuck Smith 110152a139
Some checks failed
Build / build (push) Successful in 10m22s
Test / build (push) Failing after 15m54s
module support
2024-03-26 16:49:38 -04:00

223 lines
4.8 KiB
Go

package code
import (
"bytes"
"encoding/binary"
"fmt"
)
type Instructions []byte
type Opcode byte
func (o Opcode) String() string {
def, err := Lookup(byte(o))
if err != nil {
return ""
}
return def.Name
}
const (
OpConstant Opcode = iota
OpAdd
OpPop
OpSub
OpMul
OpDiv
OpMod
OpOr
OpAnd
OpNot
OpBitwiseOR
OpBitwiseXOR
OpBitwiseAND
OpBitwiseNOT
OpLeftShift
OpRightShift
OpTrue
OpFalse
OpEqual
OpNotEqual
OpGreaterThan
OpGreaterThanEqual
OpMinus
OpJumpNotTruthy
OpJump
OpNull
OpAssignGlobal
OpAssignLocal
OpGetGlobal
OpSetGlobal
OpArray
OpHash
OpGetItem
OpSetItem
OpCall
OpReturn
OpGetLocal
OpSetLocal
OpGetBuiltin
OpClosure
OpGetFree
OpCurrentClosure
OpLoadModule
)
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{}},
OpMod: {"OpMod", []int{}},
OpOr: {"OpOr", []int{}},
OpAnd: {"OpAnd", []int{}},
OpNot: {"OpNot", []int{}},
OpBitwiseOR: {"OpBitwiseOR", []int{}},
OpBitwiseXOR: {"OpBitwiseXOR", []int{}},
OpBitwiseAND: {"OpBitwiseAND", []int{}},
OpBitwiseNOT: {"OpBitwiseNOT", []int{}},
OpLeftShift: {"OpLeftShift", []int{}},
OpRightShift: {"OpRightShift", []int{}},
OpTrue: {"OpTrue", []int{}},
OpFalse: {"OpFalse", []int{}},
OpEqual: {"OpEqual", []int{}},
OpNotEqual: {"OpNotEqual", []int{}},
OpGreaterThan: {"OpGreaterThan", []int{}},
OpGreaterThanEqual: {"OpGreaterThanEqual", []int{}},
OpMinus: {"OpMinus", []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}},
OpGetItem: {"OpGetItem", []int{}},
OpSetItem: {"OpSetItem", []int{}},
OpCall: {"OpCall", []int{1}},
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{}},
OpLoadModule: {"OpLoadModule", []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]
}