2022-01-20 21:54:11 +01:00
import strformat
import strutils
2022-01-29 05:38:16 +01:00
import types / value
2022-01-20 21:54:11 +01:00
type
OpCode * = enum
2022-01-29 20:43:13 +01:00
opReturn , opCall , opCheckArity , opFunctionDef , # functions
2022-02-05 12:30:07 +01:00
opClosure , # closures
2022-01-27 03:32:42 +01:00
opPop , opPopSA , opPopA # pop
2022-01-20 21:54:11 +01:00
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
2022-02-03 03:18:11 +01:00
opCreateList , opCreateTable , # collection creation
opLen , opSetIndex , opGetIndex , # collection operators
2022-02-05 02:45:29 +01:00
opPrint , opChr , opInt , opPutchar , # temporary opcodes for the brainfuck interpreter TODO move to native funcs
2022-01-20 21:54:11 +01:00
Chunk * = object
code * : seq [ uint8 ]
2022-01-27 05:37:10 +01:00
constants * : seq [ NdValue ]
2022-01-20 21:54:11 +01:00
lines * : seq [ int ]
2022-01-21 00:18:58 +01:00
name * : string # name of the module/chunk/files
2022-01-20 21:54:11 +01:00
2022-01-21 18:37:46 +01:00
DoubleUint8 * = array [ 2 , uint8 ]
2022-01-27 03:32:42 +01:00
# WARNING! short args can safely assumed to be 1 byte long outside of chunk.nim
const shortArgSize * = 1
2022-01-27 04:41:32 +01:00
const shortArgMax * = 256 - 1
2022-01-27 03:32:42 +01:00
# modules outside chunk.nim should not assume any length of argSize, however code inside chunk.nim can make such assumptions
2022-01-21 18:37:46 +01:00
const argSize * = 2
2022-01-27 04:41:32 +01:00
const argMax * : int = 256 * 256 - 1
2022-01-20 21:54:11 +01:00
2022-01-21 00:18:58 +01:00
proc initChunk * ( name : string ) : Chunk =
Chunk ( code : @ [ ] , name : name , lines : @ [ ] , constants : @ [ ] )
2022-01-20 21:54:11 +01:00
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 )
2022-01-21 18:37:46 +01:00
proc writeChunk * ( ch : var Chunk , code : DoubleUint8 , line : int ) =
2022-01-20 21:54:11 +01:00
for c in code :
ch . code . add ( c )
ch . lines . add ( line )
proc len * ( ch : Chunk ) : int =
ch . code . len
2022-01-21 18:37:46 +01:00
proc toDU8 * ( integ : int ) : DoubleUint8 =
cast [ ptr array [ 2 , uint8 ] ] ( integ . unsafeAddr ) [ ]
2022-01-20 21:54:11 +01:00
2022-01-21 18:37:46 +01:00
proc toInt * ( du8 : DoubleUint8 ) : int =
cast [ uint16 ] ( du8 ) . int
2022-01-20 21:54:11 +01:00
2022-01-22 04:46:53 +01:00
proc DU8ptrToInt * ( du8 : ptr uint8 ) : int =
cast [ ptr uint16 ] ( du8 ) [ ] . int
2022-02-05 05:52:45 +01:00
proc findConstant ( ch : var Chunk , constant : NdValue ) : int =
2022-02-05 06:10:37 +01:00
# 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
2022-02-05 05:52:45 +01:00
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
2022-01-27 05:37:10 +01:00
proc addConstant * ( ch : var Chunk , constant : NdValue ) : int =
2022-02-05 05:52:45 +01:00
let found = ch . findConstant ( constant )
if found = = - 1 :
ch . constants . add ( constant )
ch . constants . high
else :
found
2022-01-20 21:54:11 +01:00
2022-01-27 05:37:10 +01:00
proc writeConstant * ( ch : var Chunk , constant : NdValue , line : int ) : int =
2022-01-20 21:54:11 +01:00
result = ch . addConstant ( constant )
ch . writeChunk ( opConstant , line )
2022-01-21 18:37:46 +01:00
ch . writeChunk ( result . toDU8 , line )
2022-01-20 21:54:11 +01:00
const simpleInstructions = {
2022-01-21 01:51:55 +01:00
opReturn ,
2022-01-20 21:54:11 +01:00
opPop ,
opPrint ,
opNegate , opNot ,
opAdd , opSubtract , opMultiply , opDivide ,
opEqual , opGreater , opLess ,
2022-02-03 03:18:11 +01:00
opTrue , opFalse , opNil ,
opLen , opSetIndex , opGetIndex ,
2022-02-05 02:45:29 +01:00
opChr , opInt , opPutchar ,
2022-01-20 21:54:11 +01:00
}
const constantInstructions = {
opConstant ,
opDefineGlobal , opGetGlobal , opSetGlobal ,
}
2022-01-27 03:32:42 +01:00
const shortArgInstructions = {
opPopSA ,
2022-01-27 04:41:32 +01:00
opCall ,
2022-01-27 05:56:09 +01:00
opCheckArity ,
2022-01-27 03:32:42 +01:00
}
2022-01-20 21:54:11 +01:00
const argInstructions = {
2022-01-27 03:32:42 +01:00
opPopA ,
2022-01-20 21:54:11 +01:00
opGetLocal , opSetLocal ,
opJumpIfFalse , opJump , opLoop , opJumpIfFalsePop ,
2022-02-05 12:30:07 +01:00
opFunctionDef , opClosure ,
2022-02-03 03:18:11 +01:00
opCreateList , opCreateTable ,
2022-01-20 21:54:11 +01:00
}
proc disassembleChunk * ( ch : Chunk ) =
2022-01-21 00:18:58 +01:00
echo & " == Chunk {ch.name} begin == "
2022-01-20 21:54:11 +01:00
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 ]
2022-01-21 18:37:46 +01:00
template double : DoubleUint8 = [ ch . code [ c + 1 ] , ch . code [ c + 2 ] ]
2022-01-27 03:32:42 +01:00
template shortArg : uint8 = ch . code [ c + 1 ]
2022-01-20 21:54:11 +01:00
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 "
2022-01-27 03:32:42 +01:00
of shortArgInstructions :
write stdout , & " {shortArg.toHex(2)} "
c + = 1
2022-01-20 21:54:11 +01:00
of argInstructions :
2022-01-21 18:37:46 +01:00
write stdout , & " {double[0].toHex(2)} {double[1].toHex(2)}) \n "
c + = 2
2022-01-20 21:54:11 +01:00
of constantInstructions :
2022-01-21 18:37:46 +01:00
let i = double . toInt
write stdout , & " {double[0].toHex(2)} {double[1].toHex(2)}) \n "
2022-01-20 21:54:11 +01:00
echo & " points to constant {ch.constants[i]} (i: {i}) "
2022-01-21 18:37:46 +01:00
c + = 2
2022-01-20 21:54:11 +01:00
except :
echo & " [{cFmt}] {lineFmt} Unknown opcode {instruction} "
c . inc
2022-01-21 00:18:58 +01:00
echo & " == Chunk {ch.name} end == "
2022-01-20 21:54:11 +01:00