Various fixes to module system
This commit is contained in:
parent
1c54243d43
commit
9573769868
|
@ -16,9 +16,8 @@
|
||||||
import std/math
|
import std/math
|
||||||
import std/segfaults
|
import std/segfaults
|
||||||
import std/strutils
|
import std/strutils
|
||||||
import std/sequtils
|
|
||||||
import std/sets
|
import std/sets
|
||||||
import std/times
|
import std/monotimes
|
||||||
|
|
||||||
|
|
||||||
import ../config
|
import ../config
|
||||||
|
@ -28,6 +27,7 @@ import ../util/multibyte
|
||||||
|
|
||||||
when debugVM or debugMem or debugGC:
|
when debugVM or debugMem or debugGC:
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
import std/sequtils
|
||||||
import std/terminal
|
import std/terminal
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ type
|
||||||
envs: seq[uint64] # Stores variables that do not have stack semantics
|
envs: seq[uint64] # Stores variables that do not have stack semantics
|
||||||
results: seq[uint64] # Stores function's results (return values)
|
results: seq[uint64] # Stores function's results (return values)
|
||||||
gc: PeonGC # Our memory manager
|
gc: PeonGC # Our memory manager
|
||||||
|
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
||||||
ObjectKind* = enum
|
ObjectKind* = enum
|
||||||
## A tag for heap-allocated
|
## A tag for heap-allocated
|
||||||
## peon objects
|
## peon objects
|
||||||
|
@ -500,7 +501,7 @@ proc readLong(self: PeonVM): uint32 =
|
||||||
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
||||||
|
|
||||||
|
|
||||||
proc readUInt(self: PeonVM): uint32 =
|
proc readUInt(self: PeonVM): uint32 {.used.} =
|
||||||
## Reads three bytes from the
|
## Reads three bytes from the
|
||||||
## bytecode and returns them
|
## bytecode and returns them
|
||||||
## as an unsigned 32 bit
|
## as an unsigned 32 bit
|
||||||
|
@ -681,7 +682,8 @@ proc dispatch*(self: PeonVM) =
|
||||||
while true:
|
while true:
|
||||||
{.computedgoto.} # https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
{.computedgoto.} # https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
||||||
when debugVM:
|
when debugVM:
|
||||||
self.debug()
|
if self.ip in self.breakpoints or self.breakpoints.len() == 0:
|
||||||
|
self.debug()
|
||||||
instruction = OpCode(self.readByte())
|
instruction = OpCode(self.readByte())
|
||||||
case instruction:
|
case instruction:
|
||||||
# Constant loading instructions
|
# Constant loading instructions
|
||||||
|
@ -1020,19 +1022,20 @@ proc dispatch*(self: PeonVM) =
|
||||||
stdout.write(s.str[i])
|
stdout.write(s.str[i])
|
||||||
stdout.write("\n")
|
stdout.write("\n")
|
||||||
of SysClock64:
|
of SysClock64:
|
||||||
self.push(cast[uint64](cpuTime()))
|
self.push(cast[uint64](getMonoTime().ticks.float() / 1_000_000_000))
|
||||||
of LogicalNot:
|
of LogicalNot:
|
||||||
self.push(uint64(not self.pop().bool))
|
self.push(uint64(not self.pop().bool))
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc run*(self: PeonVM, chunk: Chunk) =
|
proc run*(self: PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[]) =
|
||||||
## Executes a piece of Peon bytecode
|
## Executes a piece of Peon bytecode
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.frames = @[]
|
self.frames = @[]
|
||||||
self.calls = @[]
|
self.calls = @[]
|
||||||
self.operands = @[]
|
self.operands = @[]
|
||||||
|
self.breakpoints = breakpoints
|
||||||
self.results = @[]
|
self.results = @[]
|
||||||
self.ip = 0
|
self.ip = 0
|
||||||
# Sorry, but there only is enough space
|
# Sorry, but there only is enough space
|
||||||
|
|
|
@ -51,6 +51,8 @@ Basic usage
|
||||||
|
|
||||||
$ peon Opens an interactive session (REPL)
|
$ peon Opens an interactive session (REPL)
|
||||||
$ peon file.pn Runs the given Peon source file
|
$ peon file.pn Runs the given Peon source file
|
||||||
|
$ peon file.pbc Runs the given Peon bytecode file
|
||||||
|
|
||||||
|
|
||||||
Command-line options
|
Command-line options
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -58,7 +60,7 @@ Command-line options
|
||||||
-h, --help Shows this help text and exits
|
-h, --help Shows this help text and exits
|
||||||
-v, --version Prints the peon version number and exits
|
-v, --version Prints the peon version number and exits
|
||||||
-s, --string Executes the passed string as if it was a file
|
-s, --string Executes the passed string as if it was a file
|
||||||
-i, --interactive Enables interactive mode, which opens a REPL session after execution of a file or source string
|
-i, --interactive Enables interactive mode: a REPL session is opened after execution
|
||||||
-c, --nocache Disables dumping the result of bytecode compilation to files for caching
|
-d, --nodump Disables dumping the result of bytecode compilation to files
|
||||||
-d, --cache-delay Configures the bytecode cache invalidation threshold, in minutes (defaults to 60)
|
-b, --breakpoints Pauses execution of the peon virtual machine and runs the debugger at specific bytecode offsets
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -558,7 +558,9 @@ proc getStackPos(self: Compiler, name: Name): int =
|
||||||
var found = false
|
var found = false
|
||||||
result = 2
|
result = 2
|
||||||
for variable in self.names:
|
for variable in self.names:
|
||||||
if variable.kind notin [NameKind.Var, NameKind.Argument]:
|
if variable.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum, NameKind.Function]:
|
||||||
|
continue
|
||||||
|
elif variable.kind == NameKind.Argument and variable.depth > self.scopeDepth:
|
||||||
continue
|
continue
|
||||||
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
|
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
|
||||||
continue
|
continue
|
||||||
|
@ -566,12 +568,11 @@ proc getStackPos(self: Compiler, name: Name): int =
|
||||||
continue
|
continue
|
||||||
elif variable.owner != self.currentModule:
|
elif variable.owner != self.currentModule:
|
||||||
continue
|
continue
|
||||||
if variable.depth > self.scopeDepth:
|
|
||||||
continue
|
|
||||||
if name.ident == variable.ident:
|
if name.ident == variable.ident:
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
inc(result)
|
inc(result)
|
||||||
|
echo variable
|
||||||
if not found:
|
if not found:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
@ -1223,7 +1224,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
))
|
))
|
||||||
if mutable:
|
if mutable:
|
||||||
self.names[^1].valueType.mutable = true
|
self.names[^1].valueType.mutable = true
|
||||||
return self.names[^1]
|
result = self.names[^1]
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
var node = FunDecl(node)
|
var node = FunDecl(node)
|
||||||
var generics: seq[Name] = @[]
|
var generics: seq[Name] = @[]
|
||||||
|
@ -1266,12 +1267,12 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
# wait, no LoadVar? Yes! That's because when calling functions,
|
# wait, no LoadVar? Yes! That's because when calling functions,
|
||||||
# arguments will already be on the stack, so there's no need to
|
# arguments will already be on the stack, so there's no need to
|
||||||
# load them here
|
# load them here
|
||||||
name = Name(depth: self.scopeDepth + 1,
|
name = Name(depth: fn.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
ident: argument.name,
|
ident: argument.name,
|
||||||
valueType: nil,
|
valueType: self.infer(argument.valueType),
|
||||||
codePos: 0,
|
codePos: 0,
|
||||||
isLet: false,
|
isLet: false,
|
||||||
line: argument.name.token.line,
|
line: argument.name.token.line,
|
||||||
|
@ -1279,14 +1280,13 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
kind: NameKind.Argument
|
kind: NameKind.Argument
|
||||||
)
|
)
|
||||||
self.names.add(name)
|
self.names.add(name)
|
||||||
name.valueType = self.infer(argument.valueType)
|
# If it's nil, it's an error!
|
||||||
# If it's still nil, it's an error!
|
|
||||||
if name.valueType.isNil():
|
if name.valueType.isNil():
|
||||||
self.error(&"cannot determine the type of argument '{argument.name.token.lexeme}'", argument.name)
|
self.error(&"cannot determine the type of argument '{argument.name.token.lexeme}'", argument.name)
|
||||||
fn.valueType.args.add((argument.name.token.lexeme, name.valueType))
|
fn.valueType.args.add((argument.name.token.lexeme, name.valueType))
|
||||||
if generics.len() > 0:
|
if generics.len() > 0:
|
||||||
fn.valueType.isGeneric = true
|
fn.valueType.isGeneric = true
|
||||||
return fn
|
result = fn
|
||||||
of NodeKind.importStmt:
|
of NodeKind.importStmt:
|
||||||
var node = ImportStmt(node)
|
var node = ImportStmt(node)
|
||||||
var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "")
|
var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "")
|
||||||
|
@ -1301,12 +1301,14 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
kind: NameKind.Module,
|
kind: NameKind.Module,
|
||||||
isPrivate: false
|
isPrivate: false
|
||||||
))
|
))
|
||||||
return self.names[^1]
|
result = self.names[^1]
|
||||||
else:
|
else:
|
||||||
discard # TODO: Types, enums
|
discard # TODO: Types, enums
|
||||||
for name in self.findByName(declaredName):
|
for name in self.findByName(declaredName):
|
||||||
if (name.kind == NameKind.Var and name.depth == self.scopeDepth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
if name == result:
|
||||||
self.error(&"attempt to redeclare '{name}', which was previously defined in '{name.owner}' at line {name.line}")
|
continue
|
||||||
|
elif (name.kind == NameKind.Var and name.depth == self.scopeDepth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
|
self.error(&"attempt to redeclare '{name.ident.token.lexeme}', which was previously defined in '{name.owner}' at line {name.line}")
|
||||||
|
|
||||||
|
|
||||||
proc emitLoop(self: Compiler, begin: int, line: int) =
|
proc emitLoop(self: Compiler, begin: int, line: int) =
|
||||||
|
@ -1746,7 +1748,7 @@ proc specialize(self: Compiler, name: Name, args: seq[Expression]): Name =
|
||||||
for (argExpr, argName) in zip(args, result.valueType.args):
|
for (argExpr, argName) in zip(args, result.valueType.args):
|
||||||
if self.names.high() > 16777215:
|
if self.names.high() > 16777215:
|
||||||
self.error("cannot declare more than 16777215 variables at a time")
|
self.error("cannot declare more than 16777215 variables at a time")
|
||||||
self.names.add(Name(depth: self.scopeDepth + 1,
|
self.names.add(Name(depth: name.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
|
|
41
src/main.nim
41
src/main.nim
|
@ -191,7 +191,7 @@ proc repl =
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
proc runFile(f: string, interactive: bool = false, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[]) =
|
||||||
var
|
var
|
||||||
tokens: seq[Token] = @[]
|
tokens: seq[Token] = @[]
|
||||||
tree: seq[Declaration] = @[]
|
tree: seq[Declaration] = @[]
|
||||||
|
@ -242,10 +242,11 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
||||||
if path.len() > 0:
|
if path.len() > 0:
|
||||||
path &= "/"
|
path &= "/"
|
||||||
path &= splitFile(f).name & ".pbc"
|
path &= splitFile(f).name & ".pbc"
|
||||||
serializer.dumpFile(compiled, f, path)
|
if dump or not fromString:
|
||||||
serialized = serializer.loadFile(path)
|
serializer.dumpFile(compiled, f, path)
|
||||||
if fromString:
|
serialized = serializer.loadFile(path)
|
||||||
discard tryRemoveFile("<string>.pbc")
|
else:
|
||||||
|
serialized = serializer.loadBytes(serializer.dumpBytes(compiled, f))
|
||||||
when debugSerializer:
|
when debugSerializer:
|
||||||
styledEcho fgCyan, "Serialization step: "
|
styledEcho fgCyan, "Serialization step: "
|
||||||
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
|
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
|
||||||
|
@ -270,7 +271,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
||||||
styledEcho fgGreen, "OK"
|
styledEcho fgGreen, "OK"
|
||||||
else:
|
else:
|
||||||
styledEcho fgRed, "Corrupted"
|
styledEcho fgRed, "Corrupted"
|
||||||
vm.run(serialized.chunk)
|
vm.run(serialized.chunk, breakpoints)
|
||||||
except LexingError:
|
except LexingError:
|
||||||
var exc = LexingError(getCurrentException())
|
var exc = LexingError(getCurrentException())
|
||||||
let relPos = exc.lexer.getRelPos(exc.line)
|
let relPos = exc.lexer.getRelPos(exc.line)
|
||||||
|
@ -326,6 +327,8 @@ when isMainModule:
|
||||||
var file: string = ""
|
var file: string = ""
|
||||||
var fromString: bool = false
|
var fromString: bool = false
|
||||||
var interactive: bool = false
|
var interactive: bool = false
|
||||||
|
var dump: bool = true
|
||||||
|
var breaks: seq[uint64] = @[]
|
||||||
for kind, key, value in optParser.getopt():
|
for kind, key, value in optParser.getopt():
|
||||||
case kind:
|
case kind:
|
||||||
of cmdArgument:
|
of cmdArgument:
|
||||||
|
@ -343,6 +346,18 @@ when isMainModule:
|
||||||
fromString = true
|
fromString = true
|
||||||
of "interactive":
|
of "interactive":
|
||||||
interactive = true
|
interactive = true
|
||||||
|
of "no-dump":
|
||||||
|
dump = false
|
||||||
|
of "breakpoints":
|
||||||
|
when not debugVM:
|
||||||
|
echo "error: cannot set breakpoints in release mode"
|
||||||
|
quit()
|
||||||
|
for point in value.strip(chars={' '}).split(","):
|
||||||
|
try:
|
||||||
|
breaks.add(parseBiggestUInt(point))
|
||||||
|
except ValueError:
|
||||||
|
echo &"error: invalid breakpoint value '{point}'"
|
||||||
|
quit()
|
||||||
else:
|
else:
|
||||||
echo &"error: unkown option '{key}'"
|
echo &"error: unkown option '{key}'"
|
||||||
quit()
|
quit()
|
||||||
|
@ -359,6 +374,18 @@ when isMainModule:
|
||||||
fromString = true
|
fromString = true
|
||||||
of "i":
|
of "i":
|
||||||
interactive = true
|
interactive = true
|
||||||
|
of "d":
|
||||||
|
dump = false
|
||||||
|
of "b":
|
||||||
|
when not debugVM:
|
||||||
|
echo "error: cannot set breakpoints in release mode"
|
||||||
|
quit()
|
||||||
|
for point in value.strip(chars={' '}).split(","):
|
||||||
|
try:
|
||||||
|
breaks.add(parseBiggestUInt(point))
|
||||||
|
except ValueError:
|
||||||
|
echo &"error: invalid breakpoint value '{point}'"
|
||||||
|
quit()
|
||||||
else:
|
else:
|
||||||
echo &"error: unkown option '{key}'"
|
echo &"error: unkown option '{key}'"
|
||||||
quit()
|
quit()
|
||||||
|
@ -369,7 +396,7 @@ when isMainModule:
|
||||||
if file == "":
|
if file == "":
|
||||||
repl()
|
repl()
|
||||||
else:
|
else:
|
||||||
runFile(file, interactive, fromString)
|
runFile(file, interactive, fromString, dump, breaks)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
# Assignment operators
|
# Various miscellaneous utilities
|
||||||
|
|
||||||
|
# Assignment operator
|
||||||
|
|
||||||
operator `=`*[T: all](a: var T, b: T) { # TODO: This is just a placeholder right now
|
operator `=`*[T: all](a: var T, b: T) { # TODO: This is just a placeholder right now
|
||||||
#pragma[magic: "GenericAssign"]
|
#pragma[magic: "GenericAssign"]
|
||||||
|
|
|
@ -11,6 +11,6 @@ fn fib(n: int): int {
|
||||||
|
|
||||||
print("Computing the value of fib(37)");
|
print("Computing the value of fib(37)");
|
||||||
var x = clock();
|
var x = clock();
|
||||||
print(fib(37));
|
print(fib(33));
|
||||||
print(clock() - x);
|
print(clock() - x);
|
||||||
print("Done!");
|
print("Done!");
|
||||||
|
|
|
@ -6,6 +6,6 @@ fn sum[T: int | int32](a, b: T): T {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print(sum(1, 2));
|
print(sum(1, 2)); # Prints 3
|
||||||
print(sum(1'i32, 2'i32));
|
print(sum(1'i32, 2'i32)); # Also prints 3!
|
||||||
# print(sum(1'i16, 2'i16)); # Will not work if uncommented!
|
# print(sum(1'i16, 2'i16)); # Will not work if uncommented!
|
||||||
|
|
Loading…
Reference in New Issue