Initial work on calls (with runtime support too)
This commit is contained in:
parent
50b0f98f6a
commit
5f43ea9490
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
## The Peon runtime environment
|
## The Peon runtime environment
|
||||||
import types
|
import types
|
||||||
|
import strformat
|
||||||
import ../config
|
import ../config
|
||||||
import ../frontend/meta/bytecode
|
import ../frontend/meta/bytecode
|
||||||
import ../util/multibyte
|
import ../util/multibyte
|
||||||
|
@ -24,9 +25,9 @@ type
|
||||||
## The Peon Virtual Machine
|
## The Peon Virtual Machine
|
||||||
stack: seq[PeonObject]
|
stack: seq[PeonObject]
|
||||||
ip: int # Instruction pointer
|
ip: int # Instruction pointer
|
||||||
sp: int # Stack pointer
|
|
||||||
cache: array[6, PeonObject] # Singletons cache
|
cache: array[6, PeonObject] # Singletons cache
|
||||||
chunk: Chunk # Piece of bytecode to execute
|
chunk: Chunk # Piece of bytecode to execute
|
||||||
|
depth: int
|
||||||
|
|
||||||
|
|
||||||
proc initCache*(self: PeonVM) =
|
proc initCache*(self: PeonVM) =
|
||||||
|
@ -45,11 +46,10 @@ proc newPeonVM*: PeonVM =
|
||||||
## for executing Peon bytecode
|
## for executing Peon bytecode
|
||||||
new(result)
|
new(result)
|
||||||
result.ip = 0
|
result.ip = 0
|
||||||
result.sp = 0
|
result.depth = 0
|
||||||
result.stack = newSeqOfCap[PeonObject](INITIAL_STACK_SIZE)
|
result.stack = newSeq[PeonObject]()
|
||||||
result.initCache()
|
result.initCache()
|
||||||
for _ in 0..<INITIAL_STACK_SIZE:
|
|
||||||
result.stack.add(result.cache[0])
|
|
||||||
|
|
||||||
## Getters for singleton types (they are cached!)
|
## Getters for singleton types (they are cached!)
|
||||||
|
|
||||||
|
@ -72,26 +72,21 @@ proc getNan*(self: PeonVM): PeonObject = self.cache[5]
|
||||||
proc push(self: PeonVM, obj: PeonObject) =
|
proc push(self: PeonVM, obj: PeonObject) =
|
||||||
## Pushes a Peon object onto the
|
## Pushes a Peon object onto the
|
||||||
## stack
|
## stack
|
||||||
if self.sp >= self.stack.high():
|
self.stack.add(obj)
|
||||||
for _ in 0..self.stack.len():
|
|
||||||
self.stack.add(self.getNil())
|
|
||||||
self.stack[self.sp] = obj
|
|
||||||
inc(self.sp)
|
|
||||||
|
|
||||||
|
|
||||||
proc pop(self: PeonVM): PeonObject =
|
proc pop(self: PeonVM): PeonObject =
|
||||||
## Pops a Peon object off the
|
## Pops a Peon object off the
|
||||||
## stack, decreasing the stack
|
## stack, decreasing the stack
|
||||||
## pointer. The object is returned
|
## pointer. The object is returned
|
||||||
dec(self.sp)
|
return self.stack.pop()
|
||||||
return self.stack[self.sp]
|
|
||||||
|
|
||||||
|
|
||||||
proc peek(self: PeonVM): PeonObject =
|
proc peek(self: PeonVM): PeonObject =
|
||||||
## Returns the element at the top
|
## Returns the element at the top
|
||||||
## of the stack without consuming
|
## of the stack without consuming
|
||||||
## it
|
## it
|
||||||
return self.stack[self.sp]
|
return self.stack[^1]
|
||||||
|
|
||||||
|
|
||||||
proc readByte(self: PeonVM): uint8 =
|
proc readByte(self: PeonVM): uint8 =
|
||||||
|
@ -126,7 +121,10 @@ proc readInt64(self: PeonVM, idx: int): PeonObject =
|
||||||
## returns a Peon object. Assumes
|
## returns a Peon object. Assumes
|
||||||
## the constant is an Int64
|
## the constant is an Int64
|
||||||
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||||
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
|
||||||
|
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
|
||||||
|
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
|
||||||
|
]
|
||||||
result = PeonObject(kind: Int64)
|
result = PeonObject(kind: Int64)
|
||||||
copyMem(result.long.addr, arr.addr, sizeof(arr))
|
copyMem(result.long.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
@ -137,16 +135,36 @@ proc readUInt64(self: PeonVM, idx: int): PeonObject =
|
||||||
## returns a Peon object. Assumes
|
## returns a Peon object. Assumes
|
||||||
## the constant is an UInt64
|
## the constant is an UInt64
|
||||||
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||||
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
|
||||||
|
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
|
||||||
|
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
|
||||||
|
]
|
||||||
result = PeonObject(kind: UInt64)
|
result = PeonObject(kind: UInt64)
|
||||||
copyMem(result.uLong.addr, arr.addr, sizeof(arr))
|
copyMem(result.uLong.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
|
proc readUInt32(self: PeonVM, idx: int): PeonObject =
|
||||||
|
## Reads a constant from the
|
||||||
|
## chunk's constant table and
|
||||||
|
## returns a Peon object. Assumes
|
||||||
|
## the constant is an UInt32
|
||||||
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||||
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
||||||
|
result = PeonObject(kind: UInt32)
|
||||||
|
copyMem(result.uInt.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc dispatch*(self: PeonVM) =
|
proc dispatch*(self: PeonVM) =
|
||||||
## Main bytecode dispatch loop
|
## Main bytecode dispatch loop
|
||||||
var instruction: OpCode
|
var instruction: OpCode
|
||||||
while true:
|
while true:
|
||||||
instruction = OpCode(self.readByte())
|
instruction = OpCode(self.readByte())
|
||||||
|
when DEBUG_TRACE_VM:
|
||||||
|
echo &"Stack: {self.stack}"
|
||||||
|
echo &"PC: {self.ip}"
|
||||||
|
echo &"SP: {self.stack.high()}"
|
||||||
|
echo &"Instruction: {instruction}"
|
||||||
|
discard readLine stdin
|
||||||
case instruction:
|
case instruction:
|
||||||
of LoadTrue:
|
of LoadTrue:
|
||||||
self.push(self.getBool(true))
|
self.push(self.getBool(true))
|
||||||
|
@ -162,15 +180,25 @@ proc dispatch*(self: PeonVM) =
|
||||||
self.push(self.readInt64(int(self.readLong())))
|
self.push(self.readInt64(int(self.readLong())))
|
||||||
of LoadUInt64:
|
of LoadUInt64:
|
||||||
self.push(self.readUInt64(int(self.readLong())))
|
self.push(self.readUInt64(int(self.readLong())))
|
||||||
|
of LoadUInt32:
|
||||||
|
self.push(self.readUInt32(int(self.readLong())))
|
||||||
of OpCode.Return:
|
of OpCode.Return:
|
||||||
# TODO
|
if self.depth == 0:
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
self.ip = int(self.pop().uInt)
|
||||||
|
of ReturnPop:
|
||||||
|
var retVal = self.pop()
|
||||||
|
self.ip = int(self.pop().uInt)
|
||||||
|
self.push(retVal)
|
||||||
of OpCode.LoadVar:
|
of OpCode.LoadVar:
|
||||||
self.push(self.stack[self.readLong()])
|
self.push(self.stack[self.readLong()])
|
||||||
of NoOp:
|
of NoOp:
|
||||||
continue
|
continue
|
||||||
of Pop:
|
of Pop:
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
|
of Call:
|
||||||
|
self.ip = int(self.readLong())
|
||||||
of Jump:
|
of Jump:
|
||||||
self.ip = int(self.readShort())
|
self.ip = int(self.readShort())
|
||||||
of JumpForwards:
|
of JumpForwards:
|
||||||
|
@ -217,6 +245,5 @@ proc dispatch*(self: PeonVM) =
|
||||||
proc run*(self: PeonVM, chunk: Chunk) =
|
proc run*(self: PeonVM, chunk: Chunk) =
|
||||||
## Executes a piece of Peon bytecode.
|
## Executes a piece of Peon bytecode.
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.sp = 0
|
|
||||||
self.ip = 0
|
self.ip = 0
|
||||||
self.dispatch()
|
self.dispatch()
|
||||||
|
|
|
@ -27,11 +27,10 @@ when len(PEON_COMMIT_HASH) != 40:
|
||||||
const PEON_BRANCH* = "master"
|
const PEON_BRANCH* = "master"
|
||||||
when len(PEON_BRANCH) > 255:
|
when len(PEON_BRANCH) > 255:
|
||||||
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
|
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
|
||||||
const DEBUG_TRACE_VM* = false # Traces VM execution
|
const DEBUG_TRACE_VM* = true # Traces VM execution
|
||||||
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
|
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
|
||||||
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation
|
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation
|
||||||
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
||||||
const INITIAL_STACK_SIZE* = 0
|
|
||||||
const PEON_VERSION_STRING* = &"Peon {PEON_VERSION.major}.{PEON_VERSION.minor}.{PEON_VERSION.patch} {PEON_RELEASE} ({PEON_BRANCH}, {CompileDate}, {CompileTime}, {PEON_COMMIT_HASH[0..8]}) [Nim {NimVersion}] on {hostOS} ({hostCPU})"
|
const PEON_VERSION_STRING* = &"Peon {PEON_VERSION.major}.{PEON_VERSION.minor}.{PEON_VERSION.patch} {PEON_RELEASE} ({PEON_BRANCH}, {CompileDate}, {CompileTime}, {PEON_COMMIT_HASH[0..8]}) [Nim {NimVersion}] on {hostOS} ({hostCPU})"
|
||||||
const HELP_MESSAGE* = """The peon programming language, Copyright (C) 2022 Mattia Giambirtone & All Contributors
|
const HELP_MESSAGE* = """The peon programming language, Copyright (C) 2022 Mattia Giambirtone & All Contributors
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,7 @@ type
|
||||||
closedOver: seq[IdentExpr]
|
closedOver: seq[IdentExpr]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc newCompiler*(enableOptimizations: bool = true): Compiler =
|
proc newCompiler*(enableOptimizations: bool = true): Compiler =
|
||||||
## Initializes a new Compiler object
|
## Initializes a new Compiler object
|
||||||
new(result)
|
new(result)
|
||||||
|
@ -165,7 +166,7 @@ proc inferType(self: Compiler, node: Expression): Type
|
||||||
proc findByName(self: Compiler, name: string): seq[Name]
|
proc findByName(self: Compiler, name: string): seq[Name]
|
||||||
proc findByType(self: Compiler, name: string, kind: Type): seq[Name]
|
proc findByType(self: Compiler, name: string, kind: Type): seq[Name]
|
||||||
proc compareTypes(self: Compiler, a, b: Type): bool
|
proc compareTypes(self: Compiler, a, b: Type): bool
|
||||||
|
proc patchReturnAddress(self: Compiler, retAddr: int)
|
||||||
## End of forward declarations
|
## End of forward declarations
|
||||||
|
|
||||||
## Public getter for nicer error formatting
|
## Public getter for nicer error formatting
|
||||||
|
@ -724,6 +725,22 @@ proc unary(self: Compiler, node: UnaryExpr) =
|
||||||
let impl = self.findByType(node.token.lexeme, Type(kind: Function, returnType: valueType, node: nil, args: @[valueType]))
|
let impl = self.findByType(node.token.lexeme, Type(kind: Function, returnType: valueType, node: nil, args: @[valueType]))
|
||||||
if impl.len() == 0:
|
if impl.len() == 0:
|
||||||
self.error(&"cannot find a suitable implementation for '{node.token.lexeme}'")
|
self.error(&"cannot find a suitable implementation for '{node.token.lexeme}'")
|
||||||
|
elif impl.len() > 2:
|
||||||
|
var msg = &"multiple matching implementations of '{node.token.lexeme}' found:\n"
|
||||||
|
for fn in reversed(impl):
|
||||||
|
var node = FunDecl(fn.valueType.node)
|
||||||
|
discard self.typeToStr(fn.valueType)
|
||||||
|
msg &= &"- '{node.name.token.lexeme}' at line {node.token.line} of type {self.typeToStr(fn.valueType)}\n"
|
||||||
|
self.error(msg)
|
||||||
|
else:
|
||||||
|
# Pushes the return address
|
||||||
|
self.emitByte(LoadUInt32)
|
||||||
|
# We patch it later!
|
||||||
|
let idx = self.chunk.consts.len()
|
||||||
|
self.emitBytes(self.chunk.writeConstant((0xffffffff'u32).toQuad()))
|
||||||
|
self.emitByte(Call)
|
||||||
|
self.emitBytes(impl[0].codePos.toTriple())
|
||||||
|
self.patchReturnAddress(idx)
|
||||||
|
|
||||||
|
|
||||||
proc binary(self: Compiler, node: BinaryExpr) =
|
proc binary(self: Compiler, node: BinaryExpr) =
|
||||||
|
@ -795,7 +812,7 @@ proc declareName(self: Compiler, node: Declaration) =
|
||||||
returnType: self.inferType(
|
returnType: self.inferType(
|
||||||
node.returnType),
|
node.returnType),
|
||||||
args: @[]),
|
args: @[]),
|
||||||
codePos: self.chunk.code.len(),
|
codePos: self.chunk.code.high(),
|
||||||
name: node.name,
|
name: node.name,
|
||||||
isLet: false))
|
isLet: false))
|
||||||
let fn = self.names[^1]
|
let fn = self.names[^1]
|
||||||
|
@ -813,8 +830,6 @@ proc declareName(self: Compiler, node: Declaration) =
|
||||||
self.names[^1].valueType = self.inferType(argument.valueType)
|
self.names[^1].valueType = self.inferType(argument.valueType)
|
||||||
self.names[^1].valueType.node = argument.name
|
self.names[^1].valueType.node = argument.name
|
||||||
fn.valueType.args.add(self.names[^1].valueType)
|
fn.valueType.args.add(self.names[^1].valueType)
|
||||||
self.emitByte(LoadVar)
|
|
||||||
self.emitBytes(self.names.high().toTriple())
|
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
|
||||||
|
@ -839,7 +854,11 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
if not t.closedOver:
|
if not t.closedOver:
|
||||||
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||||
self.emitByte(LoadVar)
|
self.emitByte(LoadVar)
|
||||||
self.emitBytes(index.toTriple())
|
if self.scopeDepth > 0:
|
||||||
|
# Skips function's return address
|
||||||
|
self.emitBytes((index - 1).toTriple())
|
||||||
|
else:
|
||||||
|
self.emitBytes(index.toTriple())
|
||||||
else:
|
else:
|
||||||
if self.closedOver.len() == 0:
|
if self.closedOver.len() == 0:
|
||||||
self.error("error: closure variable array is empty but LoadHeap would be emitted (this is an internal error and most likely a bug)")
|
self.error("error: closure variable array is empty but LoadHeap would be emitted (this is an internal error and most likely a bug)")
|
||||||
|
@ -1277,6 +1296,16 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
|
|
||||||
|
|
||||||
|
proc patchReturnAddress(self: Compiler, retAddr: int) =
|
||||||
|
## Patches the return address of a function
|
||||||
|
## call. This is called at each iteration of
|
||||||
|
## the compiler's loop
|
||||||
|
let address = self.chunk.code.len().toQuad()
|
||||||
|
self.chunk.consts[retAddr] = address[0]
|
||||||
|
self.chunk.consts[retAddr + 1] = address[1]
|
||||||
|
self.chunk.consts[retAddr + 2] = address[2]
|
||||||
|
self.chunk.consts[retAddr + 3] = address[3]
|
||||||
|
|
||||||
|
|
||||||
proc declaration(self: Compiler, node: Declaration) =
|
proc declaration(self: Compiler, node: Declaration) =
|
||||||
## Compiles all declarations
|
## Compiles all declarations
|
||||||
|
|
|
@ -147,9 +147,11 @@ const stackTripleInstructions* = {StoreVar, LoadVar, LoadHeap, StoreHeap}
|
||||||
# of 16 bit integers
|
# of 16 bit integers
|
||||||
const stackDoubleInstructions* = {}
|
const stackDoubleInstructions* = {}
|
||||||
|
|
||||||
# Argument double argument instructions take hardcoded arguments on the stack as 16 bit integers
|
# Argument double argument instructions take hardcoded arguments as 16 bit integers
|
||||||
const argumentDoubleInstructions* = {PopN, }
|
const argumentDoubleInstructions* = {PopN, }
|
||||||
|
|
||||||
|
# Argument double argument instructions take hardcoded arguments as 24 bit integers
|
||||||
|
const argumentTripleInstructions* = {Call, }
|
||||||
# Jump instructions jump at relative or absolute bytecode offsets
|
# Jump instructions jump at relative or absolute bytecode offsets
|
||||||
const jumpInstructions* = {Jump, LongJump, JumpIfFalse, JumpIfFalsePop,
|
const jumpInstructions* = {Jump, LongJump, JumpIfFalse, JumpIfFalsePop,
|
||||||
JumpForwards, JumpBackwards,
|
JumpForwards, JumpBackwards,
|
||||||
|
|
66
src/main.nim
66
src/main.nim
|
@ -67,7 +67,6 @@ proc repl =
|
||||||
input = editor.read()
|
input = editor.read()
|
||||||
if input.len() == 0:
|
if input.len() == 0:
|
||||||
continue
|
continue
|
||||||
# Currently the parser doesn't handle these tokens well
|
|
||||||
tokens = tokenizer.lex(input, "stdin")
|
tokens = tokenizer.lex(input, "stdin")
|
||||||
if tokens.len() == 0:
|
if tokens.len() == 0:
|
||||||
continue
|
continue
|
||||||
|
@ -168,13 +167,72 @@ proc repl =
|
||||||
|
|
||||||
proc runFile(f: string) =
|
proc runFile(f: string) =
|
||||||
var
|
var
|
||||||
|
tokens: seq[Token] = @[]
|
||||||
|
tree: seq[Declaration] = @[]
|
||||||
|
compiled: Chunk
|
||||||
|
serialized: Serialized
|
||||||
tokenizer = newLexer()
|
tokenizer = newLexer()
|
||||||
parser = newParser()
|
parser = newParser()
|
||||||
compiler = newCompiler()
|
compiler = newCompiler()
|
||||||
|
serializer = newSerializer()
|
||||||
vm = newPeonVM()
|
vm = newPeonVM()
|
||||||
|
input: string
|
||||||
tokenizer.fillSymbolTable()
|
tokenizer.fillSymbolTable()
|
||||||
try:
|
try:
|
||||||
vm.run(compiler.compile(parser.parse(tokenizer.lex(readFile(f), f), f), f))
|
input = readFile(f)
|
||||||
|
tokens = tokenizer.lex(input, f)
|
||||||
|
if tokens.len() == 0:
|
||||||
|
return
|
||||||
|
when debugLexer:
|
||||||
|
styledEcho fgCyan, "Tokenization step:"
|
||||||
|
for i, token in tokens:
|
||||||
|
if i == tokens.high():
|
||||||
|
# Who cares about EOF?
|
||||||
|
break
|
||||||
|
styledEcho fgGreen, "\t", $token
|
||||||
|
echo ""
|
||||||
|
tree = parser.parse(tokens, f)
|
||||||
|
if tree.len() == 0:
|
||||||
|
return
|
||||||
|
when debugParser:
|
||||||
|
styledEcho fgCyan, "Parsing step:"
|
||||||
|
for node in tree:
|
||||||
|
styledEcho fgGreen, "\t", $node
|
||||||
|
echo ""
|
||||||
|
compiled = compiler.compile(tree, f)
|
||||||
|
when debugCompiler:
|
||||||
|
styledEcho fgCyan, "Compilation step:"
|
||||||
|
stdout.styledWrite(fgCyan, "\tRaw byte stream: ", fgGreen, "[", fgYellow, compiled.code.join(", "), fgGreen, "]")
|
||||||
|
styledEcho fgCyan, "\n\nBytecode disassembler output below:\n"
|
||||||
|
disassembleChunk(compiled, f)
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
serializer.dumpFile(compiled, input, f, splitFile(f).name & ".pbc")
|
||||||
|
serialized = serializer.loadFile(splitFile(f).name & ".pbc")
|
||||||
|
when debugSerializer:
|
||||||
|
var hashMatches = computeSHA256(input).toHex().toLowerAscii() == serialized.fileHash
|
||||||
|
styledEcho fgCyan, "Serialization step: "
|
||||||
|
styledEcho fgBlue, &"\t- File hash: ", fgYellow, serialized.fileHash, fgBlue, " (", if hashMatches: fgGreen else: fgRed, if hashMatches: "OK" else: "Fail", fgBlue, ")"
|
||||||
|
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.peonVer.major}.{serialized.peonVer.minor}.{serialized.peonVer.patch}", fgBlue, " (commit ", fgYellow, serialized.commitHash[0..8], fgBlue, ") on branch ", fgYellow, serialized.peonBranch
|
||||||
|
stdout.styledWriteLine(fgBlue, "\t- Compilation date & time: ", fgYellow, fromUnix(serialized.compileDate).format("d/M/yyyy HH:mm:ss"))
|
||||||
|
stdout.styledWrite(fgBlue, &"\t- Constants segment: ")
|
||||||
|
if serialized.chunk.consts == compiled.consts:
|
||||||
|
styledEcho fgGreen, "OK"
|
||||||
|
else:
|
||||||
|
styledEcho fgRed, "Corrupted"
|
||||||
|
stdout.styledWrite(fgBlue, &"\t- Code segment: ")
|
||||||
|
if serialized.chunk.code == compiled.code:
|
||||||
|
styledEcho fgGreen, "OK"
|
||||||
|
else:
|
||||||
|
styledEcho fgRed, "Corrupted"
|
||||||
|
stdout.styledWrite(fgBlue, "\t- Line info segment: ")
|
||||||
|
if serialized.chunk.lines == compiled.lines:
|
||||||
|
styledEcho fgGreen, "OK"
|
||||||
|
else:
|
||||||
|
styledEcho fgRed, "Corrupted"
|
||||||
|
when debugRuntime:
|
||||||
|
styledEcho fgCyan, "\n\nExecution step: "
|
||||||
|
vm.run(serialized.chunk)
|
||||||
except LexingError:
|
except LexingError:
|
||||||
let exc = LexingError(getCurrentException())
|
let exc = LexingError(getCurrentException())
|
||||||
let relPos = tokenizer.getRelPos(exc.line)
|
let relPos = tokenizer.getRelPos(exc.line)
|
||||||
|
@ -218,9 +276,9 @@ proc runFile(f: string) =
|
||||||
let exc = SerializationError(getCurrentException())
|
let exc = SerializationError(getCurrentException())
|
||||||
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
||||||
except IOError:
|
except IOError:
|
||||||
stderr.styledWriteLine("An error occurred while trying to read ", fgYellow, &"'{f}'", fgRed, &": {getCurrentExceptionMsg()}")
|
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {getCurrentExceptionMsg()}")
|
||||||
except OSError:
|
except OSError:
|
||||||
stderr.styledWriteLine("An error occurred while trying to read ", fgYellow, &"'{f}'", fgRed, &": {osErrorMsg(osLastError())} [errno {osLastError()}]")
|
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {osErrorMsg(osLastError())} [errno {osLastError()}]")
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
|
@ -3,4 +3,3 @@ operator `+`(a: int): int {
|
||||||
}
|
}
|
||||||
|
|
||||||
+1; # Works: defined for int64
|
+1; # Works: defined for int64
|
||||||
+1'u8; # No definition for int8: error!
|
|
||||||
|
|
|
@ -85,6 +85,17 @@ proc argumentDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int):
|
||||||
return offset + 3
|
return offset + 3
|
||||||
|
|
||||||
|
|
||||||
|
proc argumentTripleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
|
## Debugs instructions that operate on a hardcoded value on the stack using a 16-bit operand
|
||||||
|
var slot = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple()
|
||||||
|
printInstruction(instruction)
|
||||||
|
stdout.styledWrite(fgGreen, &", has argument ")
|
||||||
|
setForegroundColor(fgYellow)
|
||||||
|
stdout.write(&"{slot}")
|
||||||
|
nl()
|
||||||
|
return offset + 4
|
||||||
|
|
||||||
|
|
||||||
proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
## Debugs instructions that operate on the constant table
|
## Debugs instructions that operate on the constant table
|
||||||
var constant = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[
|
var constant = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[
|
||||||
|
@ -94,9 +105,8 @@ proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
setForegroundColor(fgYellow)
|
setForegroundColor(fgYellow)
|
||||||
stdout.write(&"{constant}")
|
stdout.write(&"{constant}")
|
||||||
nl()
|
nl()
|
||||||
let obj = chunk.consts[constant]
|
|
||||||
printDebug("Operand: ")
|
printDebug("Operand: ")
|
||||||
stdout.styledWrite(fgYellow, &"{obj}\n")
|
stdout.styledWriteLine(fgYellow, &"{chunk.consts[constant]}")
|
||||||
return offset + 4
|
return offset + 4
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,6 +148,8 @@ proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
||||||
result = stackTripleInstruction(opcode, chunk, offset)
|
result = stackTripleInstruction(opcode, chunk, offset)
|
||||||
of argumentDoubleInstructions:
|
of argumentDoubleInstructions:
|
||||||
result = argumentDoubleInstruction(opcode, chunk, offset)
|
result = argumentDoubleInstruction(opcode, chunk, offset)
|
||||||
|
of argumentTripleInstructions:
|
||||||
|
result = argumentTripleInstruction(opcode, chunk, offset)
|
||||||
of jumpInstructions:
|
of jumpInstructions:
|
||||||
result = jumpInstruction(opcode, chunk, offset)
|
result = jumpInstruction(opcode, chunk, offset)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue