import strformat import strutils import types/value type OpCode* = enum opReturn, opCall, opCheckArity, opFunctionDef, # functions opPop, opPopSA, opPopA # pop opNegate, opNot # unary opAdd, opSubtract, opMultiply, opDivide, # math opEqual, opGreater, opLess, # comparison opTrue, opFalse, opNil, # literal opConstant, # constant opDefineGlobal, opGetGlobal, opSetGlobal, # globals (uses constants) opGetLocal, opSetLocal, # locals opJumpIfFalse, opJump, opLoop, opJumpIfFalsePop, # jumps opCreateList, opCreateTable, # collection creation opLen, opSetIndex, opGetIndex, # collection operators opPrint, opChr, opInt, opPutchar, # temporary opcodes for the brainfuck interpreter TODO move to native funcs Chunk* = object code*: seq[uint8] constants*: seq[NdValue] lines*: seq[int] name*: string # name of the module/chunk/files DoubleUint8* = array[2, uint8] # WARNING! short args can safely assumed to be 1 byte long outside of chunk.nim const shortArgSize* = 1 const shortArgMax* = 256 - 1 # modules outside chunk.nim should not assume any length of argSize, however code inside chunk.nim can make such assumptions const argSize* = 2 const argMax*: int = 256*256 - 1 proc initChunk*(name: string): Chunk = Chunk(code: @[], name: name, lines: @[], constants: @[]) proc writeChunk*(ch: var Chunk, code: uint8, line: int) = ch.code.add(code) ch.lines.add(line) proc writeChunk*(ch: var Chunk, code: OpCode, line: int) = ch.code.add(code.uint8) ch.lines.add(line) proc writeChunk*(ch: var Chunk, code: DoubleUint8, line: int) = for c in code: ch.code.add(c) ch.lines.add(line) proc len*(ch: Chunk): int = ch.code.len proc toDU8*(integ: int): DoubleUint8 = cast[ptr array[2, uint8]](integ.unsafeAddr)[] proc toInt*(du8: DoubleUint8): int = cast[uint16](du8).int proc DU8ptrToInt*(du8: ptr uint8): int = cast[ptr uint16](du8)[].int proc addConstant*(ch: var Chunk, constant: NdValue): int = ch.constants.add(constant) ch.constants.high proc writeConstant*(ch: var Chunk, constant: NdValue, line: int): int = result = ch.addConstant(constant) ch.writeChunk(opConstant, line) ch.writeChunk(result.toDU8, line) const simpleInstructions = { opReturn, opPop, opPrint, opNegate, opNot, opAdd, opSubtract, opMultiply, opDivide, opEqual, opGreater, opLess, opTrue, opFalse, opNil, opLen, opSetIndex, opGetIndex, opChr, opInt, opPutchar, } const constantInstructions = { opConstant, opDefineGlobal, opGetGlobal, opSetGlobal, } const shortArgInstructions = { opPopSA, opCall, opCheckArity, } const argInstructions = { opPopA, opGetLocal, opSetLocal, opJumpIfFalse, opJump, opLoop, opJumpIfFalsePop, opFunctionDef, opCreateList, opCreateTable, } proc disassembleChunk*(ch: Chunk) = echo &"== Chunk {ch.name} begin ==" echo "index line instruction" var c: int = 0 var lastLine = -1 while c < ch.code.len: template instruction: uint8 = ch.code[c] template line: int = ch.lines[c] template double: DoubleUint8 = [ch.code[c+1], ch.code[c+2]] template shortArg: uint8 = ch.code[c+1] let cFmt = &"{c:04}" let lineFmt = if lastLine == line: " | " else: &"{line:04}" try: write stdout, &"[{cFmt}] {lineFmt} {instruction.OpCode} ({instruction.toHex(2)}" case instruction.OpCode: of simpleInstructions: write stdout, ")\n" of shortArgInstructions: write stdout, &" {shortArg.toHex(2)}" c += 1 of argInstructions: write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" c += 2 of constantInstructions: let i = double.toInt write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" echo &" points to constant {ch.constants[i]} (i: {i})" c += 2 except: echo &"[{cFmt}] {lineFmt} Unknown opcode {instruction}" c.inc echo &"== Chunk {ch.name} end =="