import strformat import strutils import types/value type OpCode* = enum opReturn, opCall, opCheckArity, opFunctionDef, # functions opClosure, # closures 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 opGetUpvalue, opSetUpvalue, # upvalues 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 findConstant(ch: var Chunk, constant: NdValue): int = # TODO, if there are a lot of constants that are different, this could be possibly slow, so either use a table lookup or only check for the last x constants if ch.constants.len() == 0: return -1 for i in countup(0, ch.constants.high()): let current = ch.constants[i] if current == constant: return i return -1 proc addConstant*(ch: var Chunk, constant: NdValue): int = let found = ch.findConstant(constant) if found == -1: ch.constants.add(constant) ch.constants.high else: found 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, opClosure, opCreateList, opCreateTable, opGetUpvalue, opSetUpvalue, } 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 =="