peon/src/util/debugger.nim

196 lines
6.8 KiB
Nim

# Copyright 2022 Mattia Giambirtone & All Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ../frontend/meta/bytecode
import ../frontend/meta/ast
import multibyte
import strformat
import strutils
import terminal
proc nl = stdout.write("\n")
proc printDebug(s: string, newline: bool = false) =
stdout.write(&"DEBUG - Disassembler -> {s}")
if newline:
nl()
proc printName(name: string, newline: bool = false) =
setForegroundColor(fgRed)
stdout.write(name)
setForegroundColor(fgGreen)
if newline:
nl()
proc printInstruction(instruction: OpCode, newline: bool = false) =
printDebug("Instruction: ")
printName($instruction)
if newline:
nl()
proc simpleInstruction(instruction: OpCode, offset: int): int =
printInstruction(instruction)
nl()
return offset + 1
proc stackTripleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
var slot = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple()
printInstruction(instruction)
stdout.write(&", points to index ")
setForegroundColor(fgYellow)
stdout.write(&"{slot}")
nl()
return offset + 4
proc stackDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs instructions that operate on a single value on the stack using a 16-bit operand
var slot = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble()
printInstruction(instruction)
stdout.write(&", points to index ")
setForegroundColor(fgYellow)
stdout.write(&"{slot}")
nl()
return offset + 3
proc argumentDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs instructions that operate on a hardcoded value value on the stack using a 16-bit operand
var slot = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble()
printInstruction(instruction)
stdout.write(&", has argument ")
setForegroundColor(fgYellow)
stdout.write(&"{slot}")
nl()
return offset + 3
proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs instructions that operate on the constant table
var constant = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple()
printInstruction(instruction)
stdout.write(&", points to constant at position ")
setForegroundColor(fgYellow)
stdout.write(&"{constant}")
nl()
let obj = chunk.consts[constant]
setForegroundColor(fgGreen)
printDebug("Operand: ")
setForegroundColor(fgYellow)
stdout.write(&"{obj}\n")
setForegroundColor(fgGreen)
printDebug("Value kind: ")
setForegroundColor(fgYellow)
stdout.write(&"{obj.kind}\n")
return offset + 4
proc jumpInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs jumps
var jump: int
case instruction:
of JumpIfFalse, JumpIfTrue, JumpIfFalsePop, JumpForwards, JumpBackwards:
jump = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble().int()
of LongJumpIfFalse, LongJumpIfTrue, LongJumpIfFalsePop, LongJumpForwards, LongJumpBackwards:
jump = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple().int()
else:
discard # Unreachable
printInstruction(instruction, true)
printDebug("Jump size: ")
setForegroundColor(fgYellow)
stdout.write($jump)
nl()
return offset + 3
proc collectionInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
## Debugs instructions that push collection types on the stack
var elemCount = int([chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple())
printInstruction(instruction, true)
case instruction:
of BuildList, BuildTuple, BuildSet:
var elements: seq[ASTNode] = @[]
for n in countup(0, elemCount - 1):
elements.add(chunk.consts[n])
printDebug("Elements: ")
setForegroundColor(fgYellow)
stdout.write(&"""[{elements.join(", ")}]""")
setForegroundColor(fgGreen)
of BuildDict:
var elements: seq[tuple[key: ASTNode, value: ASTNode]] = @[]
for n in countup(0, (elemCount - 1) * 2, 2):
elements.add((key: chunk.consts[n], value: chunk.consts[n + 1]))
printDebug("Elements: ")
setForegroundColor(fgYellow)
stdout.write(&"""[{elements.join(", ")}]""")
setForegroundColor(fgGreen)
else:
discard # Unreachable
echo ""
return offset + 4
proc disassembleInstruction*(chunk: Chunk, offset: int): int =
## Takes one bytecode instruction and prints it
setForegroundColor(fgGreen)
printDebug("Offset: ")
setForegroundColor(fgYellow)
echo offset
setForegroundColor(fgGreen)
printDebug("Line: ")
setForegroundColor(fgYellow)
stdout.write(&"{chunk.getLine(offset)}\n")
setForegroundColor(fgGreen)
var opcode = OpCode(chunk.code[offset])
case opcode:
of simpleInstructions:
result = simpleInstruction(opcode, offset)
of constantInstructions:
result = constantInstruction(opcode, chunk, offset)
of stackDoubleInstructions:
result = stackDoubleInstruction(opcode, chunk, offset)
of stackTripleInstructions:
result = stackTripleInstruction(opcode, chunk, offset)
of argumentDoubleInstructions:
result = argumentDoubleInstruction(opcode, chunk, offset)
of jumpInstructions:
result = jumpInstruction(opcode, chunk, offset)
of collectionInstructions:
result = collectionInstruction(opcode, chunk, offset)
else:
echo &"DEBUG - Unknown opcode {opcode} at index {offset}"
result = offset + 1
proc disassembleChunk*(chunk: Chunk, name: string) =
## Takes a chunk of bytecode, and prints it
echo &"==== JAPL Bytecode Debugger - Chunk '{name}' ====\n"
var index = 0
while index < chunk.code.len:
index = disassembleInstruction(chunk, index)
echo ""
setForegroundColor(fgDefault)
echo &"==== Debug session ended - Chunk '{name}' ===="