code.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package code
  2. // Author: simon
  3. // Author: ynwdlxm@163.com
  4. // Date: 2022/10/19 13:12
  5. // Desc: bytecode
  6. import (
  7. "bytes"
  8. "encoding/binary"
  9. "fmt"
  10. )
  11. type Instructions []byte
  12. // String MiniDisassembler
  13. func (ins Instructions) String() string {
  14. var out bytes.Buffer
  15. i := 0
  16. for i < len(ins) {
  17. def, err := Lookup(ins[i])
  18. if err != nil {
  19. _, err := fmt.Fprintf(&out, "ERROR: %s\n", err)
  20. if err != nil {
  21. return ""
  22. }
  23. continue
  24. }
  25. operands, read := ReadOperands(def, ins[i+1:])
  26. _, err = fmt.Fprintf(&out, "%04d %s\n", i, ins.fmtInstructions(def, operands))
  27. if err != nil {
  28. return ""
  29. }
  30. i += 1 + read
  31. }
  32. return out.String()
  33. }
  34. func (ins Instructions) fmtInstructions(def *Definition, operands []int) string {
  35. operandCount := len(def.OperandWidths)
  36. if len(operands) != operandCount {
  37. return fmt.Sprintf("ERROR: operand len %d does not match defined %d\n", len(operands), operandCount)
  38. }
  39. switch operandCount {
  40. case 0:
  41. return def.Name
  42. case 1:
  43. return fmt.Sprintf("%s %d", def.Name, operands[0])
  44. }
  45. return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name)
  46. }
  47. type Opcode byte
  48. const (
  49. OpConstant Opcode = iota
  50. OpAdd // +
  51. OpSub // -
  52. OpMul // *
  53. OpDiv // /
  54. OpPop
  55. OpTrue // true
  56. OpFalse // false
  57. OpEqual // ==
  58. OpNotEqual // !=
  59. OpGreaterThan // >
  60. OpMinus
  61. OpBang
  62. OpJumpNotTruthy
  63. OpJump
  64. OpNull
  65. OpGetGlobal
  66. OpSetGlobal
  67. )
  68. // Definition For debugging and testing purposes
  69. //
  70. // it’s handy being able to look up how many operands an opcode has and what its human-readable name is.
  71. // In order to achieve that, we’ll add proper definitions and some tooling
  72. //
  73. // Name helps to make an opcode readable
  74. // OperandWidths contains the number of bytes each operand takes up
  75. type Definition struct {
  76. Name string
  77. OperandWidths []int
  78. }
  79. var definitions = map[Opcode]*Definition{
  80. OpConstant: {"OpConstant", []int{2}},
  81. OpAdd: {"OpAdd", []int{}}, // doesn't have any operands
  82. OpSub: {"OpSub", []int{}},
  83. OpMul: {"OpMul", []int{}},
  84. OpDiv: {"OpDiv", []int{}},
  85. OpPop: {"OpPop", []int{}},
  86. OpTrue: {"OpTrue", []int{}},
  87. OpFalse: {"OpFalse", []int{}},
  88. OpEqual: {"OpEqual", []int{}},
  89. OpNotEqual: {"OpNotEqual", []int{}},
  90. OpGreaterThan: {"OpGreaterThan", []int{}},
  91. OpMinus: {"OpMinus", []int{}},
  92. OpBang: {"OpBang", []int{}},
  93. OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
  94. OpJump: {"OpJump", []int{2}},
  95. OpNull: {"OpNull", []int{}},
  96. OpGetGlobal: {"OpGetGlobal", []int{2}},
  97. OpSetGlobal: {"OpSetGlobal", []int{2}},
  98. }
  99. func Lookup(op byte) (*Definition, error) {
  100. def, ok := definitions[Opcode(op)]
  101. if !ok {
  102. return nil, fmt.Errorf("opcode %d undefined", op)
  103. }
  104. return def, nil
  105. }
  106. func Make(op Opcode, operands ...int) []byte {
  107. def, ok := definitions[op]
  108. if !ok {
  109. return []byte{}
  110. }
  111. instructionLen := 1
  112. for _, w := range def.OperandWidths {
  113. instructionLen += w
  114. }
  115. instruction := make([]byte, instructionLen) // allocate the byte slice
  116. instruction[0] = byte(op)
  117. offset := 1
  118. for i, o := range operands {
  119. width := def.OperandWidths[i]
  120. switch width {
  121. case 2:
  122. binary.BigEndian.PutUint16(instruction[offset:], uint16(o))
  123. }
  124. offset += width
  125. }
  126. return instruction
  127. }
  128. // ReadOperands reverses everything Make did
  129. func ReadOperands(def *Definition, ins Instructions) ([]int, int) {
  130. operands := make([]int, len(def.OperandWidths))
  131. offset := 0
  132. for i, width := range def.OperandWidths {
  133. switch width {
  134. case 2:
  135. operands[i] = int(ReadUint16(ins[offset:]))
  136. }
  137. offset += width
  138. }
  139. return operands, offset
  140. }
  141. func ReadUint16(ins Instructions) uint16 {
  142. return binary.BigEndian.Uint16(ins)
  143. }