| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package code
- // Author: simon
- // Author: ynwdlxm@163.com
- // Date: 2022/10/19 13:12
- // Desc: bytecode
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- )
- type Instructions []byte
- // String MiniDisassembler
- func (ins Instructions) String() string {
- var out bytes.Buffer
- i := 0
- for i < len(ins) {
- def, err := Lookup(ins[i])
- if err != nil {
- _, err := fmt.Fprintf(&out, "ERROR: %s\n", err)
- if err != nil {
- return ""
- }
- continue
- }
- operands, read := ReadOperands(def, ins[i+1:])
- _, err = fmt.Fprintf(&out, "%04d %s\n", i, ins.fmtInstructions(def, operands))
- if err != nil {
- return ""
- }
- i += 1 + read
- }
- return out.String()
- }
- func (ins Instructions) fmtInstructions(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])
- }
- return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name)
- }
- type Opcode byte
- const (
- OpConstant Opcode = iota
- OpAdd // +
- OpSub // -
- OpMul // *
- OpDiv // /
- OpPop
- OpTrue // true
- OpFalse // false
- OpEqual // ==
- OpNotEqual // !=
- OpGreaterThan // >
- OpMinus
- OpBang
- )
- // Definition For debugging and testing purposes
- //
- // it’s handy being able to look up how many operands an opcode has and what its human-readable name is.
- // In order to achieve that, we’ll add proper definitions and some tooling
- //
- // Name helps to make an opcode readable
- // OperandWidths contains the number of bytes each operand takes up
- type Definition struct {
- Name string
- OperandWidths []int
- }
- var definitions = map[Opcode]*Definition{
- OpConstant: {"OpConstant", []int{2}},
- OpAdd: {"OpAdd", []int{}}, // doesn't have any operands
- OpSub: {"OpSub", []int{}},
- OpMul: {"OpMul", []int{}},
- OpDiv: {"OpDiv", []int{}},
- OpPop: {"OpPop", []int{}},
- OpTrue: {"OpTrue", []int{}},
- OpFalse: {"OpFalse", []int{}},
- OpEqual: {"OpEqual", []int{}},
- OpNotEqual: {"OpNotEqual", []int{}},
- OpGreaterThan: {"OpGreaterThan", []int{}},
- OpMinus: {"OpMinus", []int{}},
- OpBang: {"OpBang", []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{}
- }
- instructionLen := 1
- for _, w := range def.OperandWidths {
- instructionLen += w
- }
- instruction := make([]byte, instructionLen) // allocate the byte slice
- 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))
- }
- offset += width
- }
- return instruction
- }
- // ReadOperands reverses everything Make did
- 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:]))
- }
- offset += width
- }
- return operands, offset
- }
- func ReadUint16(ins Instructions) uint16 {
- return binary.BigEndian.Uint16(ins)
- }
|