Various fixes to stack frame alignment and added incremental compilation to REPL
This commit is contained in:
parent
0887dab246
commit
df105125c4
|
@ -21,6 +21,8 @@ import ../frontend/meta/bytecode
|
|||
import ../util/multibyte
|
||||
|
||||
|
||||
export types
|
||||
|
||||
type
|
||||
PeonVM* = ref object
|
||||
## The Peon Virtual Machine
|
||||
|
@ -268,7 +270,7 @@ proc dispatch*(self: PeonVM) =
|
|||
of Pop:
|
||||
self.lastPop = self.pop()
|
||||
of PopN:
|
||||
for _ in 0..<int(self.readLong()):
|
||||
for _ in 0..<int(self.readShort()):
|
||||
discard self.pop()
|
||||
of Jump:
|
||||
self.ip = int(self.readShort())
|
||||
|
@ -319,4 +321,5 @@ proc run*(self: PeonVM, chunk: Chunk) =
|
|||
self.frames = @[0]
|
||||
self.stack = @[]
|
||||
self.ip = 0
|
||||
self.lastPop = self.getNil()
|
||||
self.dispatch()
|
||||
|
|
|
@ -27,7 +27,7 @@ when len(PEON_COMMIT_HASH) != 40:
|
|||
const PEON_BRANCH* = "master"
|
||||
when len(PEON_BRANCH) > 255:
|
||||
{.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_ALLOCATION* = false # Traces memory allocation/deallocation
|
||||
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
||||
|
|
|
@ -403,7 +403,7 @@ proc detectClosureVariable(self: Compiler, name: Name,
|
|||
## unpredictably or crash
|
||||
if name == nil:
|
||||
return
|
||||
if name.depth < depth:
|
||||
if name.depth > 0 and name.depth < depth:
|
||||
# Ding! The given name is closed over: we need to
|
||||
# change the NoOp instructions that self.declareName
|
||||
# put in place for us into a StoreHeap. We don't need to change
|
||||
|
@ -595,7 +595,11 @@ proc inferType(self: Compiler, node: Expression): Type =
|
|||
var node = CallExpr(node)
|
||||
case node.callee.kind:
|
||||
of identExpr:
|
||||
result = self.resolve(IdentExpr(node.callee)).valueType.returnType
|
||||
let resolved = self.resolve(IdentExpr(node.callee))
|
||||
if resolved != nil:
|
||||
result = resolved.valueType.returnType
|
||||
else:
|
||||
result = nil
|
||||
of lambdaExpr:
|
||||
result = self.inferType(LambdaExpr(node.callee).returnType)
|
||||
else:
|
||||
|
@ -962,7 +966,9 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
|||
if not t.closedOver:
|
||||
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
self.emitByte(LoadVar)
|
||||
self.emitBytes((index - self.frames[self.scopeDepth]).toTriple())
|
||||
if self.frames.len() > 1:
|
||||
inc(index, 2)
|
||||
self.emitBytes((index - self.frames[^1]).toTriple())
|
||||
else:
|
||||
# Heap-allocated closure variable. Stored in a separate "closure array" in the VM that does not have stack semantics.
|
||||
# This makes closures work as expected and is not much slower than indexing our stack (since they're both
|
||||
|
@ -1019,7 +1025,7 @@ proc endScope(self: Compiler) =
|
|||
var names: seq[Name] = @[]
|
||||
for name in self.names:
|
||||
if name.depth > self.scopeDepth:
|
||||
if name.valueType.kind != Function and OpCode(self.chunk.code[name.codePos]) == NoOp:
|
||||
if name.valueType.kind != Function and OpCode(self.chunk.code[name.codePos]) == NoOp:
|
||||
for _ in countup(0, 3):
|
||||
# Since by deleting it the size of the
|
||||
# sequence decreases, we don't need to
|
||||
|
@ -1369,10 +1375,11 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
|||
## Compiles function declarations
|
||||
# A function's code is just compiled linearly
|
||||
# and then jumped over
|
||||
self.emitByte(LoadNil) # Aligns the stack
|
||||
let jmp = self.emitJump(JumpForwards)
|
||||
var function = self.currentFunction
|
||||
self.declareName(node)
|
||||
self.frames.add(self.names.high())
|
||||
self.frames.add(self.names.len())
|
||||
# TODO: Forward declarations
|
||||
if node.body != nil:
|
||||
if BlockStmt(node.body).code.len() == 0:
|
||||
|
@ -1384,7 +1391,7 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
|||
# the same function with the same name! Error!
|
||||
var msg = &"multiple matching implementations of '{node.name.token.lexeme}' found:\n"
|
||||
for fn in reversed(impl):
|
||||
msg &= &"- '{fn.name}' at line {fn.line} of type {self.typeToStr(fn.valueType)}\n"
|
||||
msg &= &"- '{fn.name.token.lexeme}' at line {fn.line} of type {self.typeToStr(fn.valueType)}\n"
|
||||
self.error(msg)
|
||||
# We store the current function
|
||||
self.currentFunction = node
|
||||
|
@ -1490,8 +1497,9 @@ proc compile*(self: Compiler, ast: seq[Declaration], file: string): Chunk =
|
|||
self.declaration(Declaration(self.step()))
|
||||
if self.ast.len() > 0:
|
||||
# *Technically* an empty program is a valid program
|
||||
self.endScope()
|
||||
self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope
|
||||
result = self.chunk
|
||||
if self.ast.len() > 0 and self.scopeDepth != 0:
|
||||
self.error(&"invalid state: invalid scopeDepth value (expected 0, got {self.scopeDepth}), did you forget to call endScope/beginScope?")
|
||||
self.endScope()
|
||||
if self.ast.len() > 0 and self.scopeDepth != -1:
|
||||
self.error(&"invalid state: invalid scopeDepth value (expected -1, got {self.scopeDepth}), did you forget to call endScope/beginScope?")
|
||||
echo self.chunk.code
|
48
src/main.nim
48
src/main.nim
|
@ -53,20 +53,33 @@ proc repl =
|
|||
vm = newPeonVM()
|
||||
editor = getLineEditor()
|
||||
input: string
|
||||
current: string
|
||||
tokenizer.fillSymbolTable()
|
||||
editor.bindEvent(jeQuit):
|
||||
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
||||
editor.prompt = ""
|
||||
keep = false
|
||||
input = ""
|
||||
editor.bindKey("ctrl+a"):
|
||||
editor.content.home()
|
||||
editor.bindKey("ctrl+e"):
|
||||
editor.content.`end`()
|
||||
while keep:
|
||||
try:
|
||||
input = editor.read()
|
||||
if input.len() == 0:
|
||||
# We incrementally add content to the input
|
||||
# so that you can, for example, define a function
|
||||
# then press enter and use it at the next iteration
|
||||
# of the read loop
|
||||
current = editor.read()
|
||||
if current.len() == 0:
|
||||
continue
|
||||
elif current == "#clearInput":
|
||||
input = ""
|
||||
continue
|
||||
elif current == "#clear":
|
||||
stdout.write("\x1Bc")
|
||||
continue
|
||||
input &= &"\n{current}"
|
||||
tokens = tokenizer.lex(input, "stdin")
|
||||
if tokens.len() == 0:
|
||||
continue
|
||||
|
@ -123,8 +136,35 @@ proc repl =
|
|||
when debugRuntime:
|
||||
styledEcho fgCyan, "\n\nExecution step: "
|
||||
vm.run(serialized.chunk)
|
||||
echo vm.lastPop
|
||||
var popped = vm.lastPop
|
||||
case popped.kind:
|
||||
of Int64:
|
||||
echo &"{popped.long}'i64"
|
||||
of UInt64:
|
||||
echo &"{popped.uLong}'u64"
|
||||
of Int32:
|
||||
echo &"{popped.`int`}'i32"
|
||||
of UInt32:
|
||||
echo &"{popped.uInt}'u32"
|
||||
of Int16:
|
||||
echo &"{popped.short}'i16"
|
||||
of UInt16:
|
||||
echo &"{popped.uShort}'u16"
|
||||
of Int8:
|
||||
echo &"{popped.tiny}'i8"
|
||||
of UInt8:
|
||||
echo &"{popped.uTiny}'u8"
|
||||
of ObjectKind.Inf:
|
||||
if popped.positive:
|
||||
echo "inf"
|
||||
else:
|
||||
echo "-inf"
|
||||
of ObjectKind.Nan, Nil:
|
||||
echo ($popped.kind).toLowerAscii()
|
||||
else:
|
||||
discard
|
||||
except LexingError:
|
||||
input = ""
|
||||
let exc = LexingError(getCurrentException())
|
||||
let relPos = tokenizer.getRelPos(exc.line)
|
||||
let line = tokenizer.getSource().splitLines()[exc.line - 1].strip()
|
||||
|
@ -134,6 +174,7 @@ proc repl =
|
|||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
||||
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
||||
except ParseError:
|
||||
input = ""
|
||||
let exc = ParseError(getCurrentException())
|
||||
let lexeme = exc.token.lexeme
|
||||
let lineNo = exc.token.line
|
||||
|
@ -149,6 +190,7 @@ proc repl =
|
|||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
||||
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
||||
except CompileError:
|
||||
input = ""
|
||||
let exc = CompileError(getCurrentException())
|
||||
let lexeme = exc.node.token.lexeme
|
||||
let lineNo = exc.node.token.line
|
||||
|
|
|
@ -63,7 +63,8 @@ proc printInstruction(instruction: OpCode, newline: bool = false) =
|
|||
|
||||
|
||||
proc checkFrameStart(self: Debugger, n: int) =
|
||||
return
|
||||
## Todo: Checking frame end offsets needs
|
||||
## fixes
|
||||
for i, e in self.cfiData:
|
||||
if n == e.start and not (e.started or e.stopped):
|
||||
e.started = true
|
||||
|
@ -131,7 +132,7 @@ proc callInstruction(self: Debugger, instruction: OpCode) =
|
|||
printInstruction(instruction)
|
||||
stdout.styledWrite(fgGreen, &", jumps to address ", fgYellow, $slot, fgGreen, " with ", fgYellow, $args, fgGreen, " argument")
|
||||
if args > 1:
|
||||
stdout.styledWrite(fgYellow, "s")
|
||||
stdout.styledWrite(fgGreen, "s")
|
||||
nl()
|
||||
self.current += 7
|
||||
|
||||
|
|
Loading…
Reference in New Issue