Removed broken closure implementation, changed local resolution mechanism, many bug fixes to namespaces and tests
This commit is contained in:
parent
142e575497
commit
21738b9382
|
@ -92,8 +92,6 @@ type
|
||||||
operands: seq[uint64] # The operand stack
|
operands: seq[uint64] # The operand stack
|
||||||
cache: array[6, uint64] # The singletons cache
|
cache: array[6, uint64] # The singletons cache
|
||||||
frames: seq[uint64] # Stores the bottom of stack frames
|
frames: seq[uint64] # Stores the bottom of stack frames
|
||||||
closures: seq[uint64] # Stores closure environment offsets
|
|
||||||
envs: seq[uint64] # Stores closure variables
|
|
||||||
results: seq[uint64] # Stores function return values
|
results: seq[uint64] # Stores function return values
|
||||||
gc: PeonGC # A reference to the VM's garbage collector
|
gc: PeonGC # A reference to the VM's garbage collector
|
||||||
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
||||||
|
@ -231,9 +229,6 @@ proc markRoots(self: var PeonVM): seq[ptr HeapObject] =
|
||||||
for obj in self.operands:
|
for obj in self.operands:
|
||||||
if obj in self.gc.pointers:
|
if obj in self.gc.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
for obj in self.envs:
|
|
||||||
if obj in self.gc.pointers:
|
|
||||||
live.incl(obj)
|
|
||||||
# We preallocate the space on the seq
|
# We preallocate the space on the seq
|
||||||
result = newSeqOfCap[ptr HeapObject](len(live))
|
result = newSeqOfCap[ptr HeapObject](len(live))
|
||||||
var obj: ptr HeapObject
|
var obj: ptr HeapObject
|
||||||
|
@ -368,7 +363,6 @@ proc newPeonVM*: PeonVM =
|
||||||
result.frames = @[]
|
result.frames = @[]
|
||||||
result.operands = @[]
|
result.operands = @[]
|
||||||
result.results = @[]
|
result.results = @[]
|
||||||
result.envs = @[]
|
|
||||||
result.calls = @[]
|
result.calls = @[]
|
||||||
|
|
||||||
|
|
||||||
|
@ -470,30 +464,6 @@ proc setc(self: var PeonVM, idx: int, val: uint64) =
|
||||||
self.calls[idx.uint + self.frames[^1]] = val
|
self.calls[idx.uint + self.frames[^1]] = val
|
||||||
|
|
||||||
|
|
||||||
proc getClosure(self: PeonVM, idx: int): uint64 =
|
|
||||||
## Getter method that abstracts
|
|
||||||
## indexing closure environments
|
|
||||||
return self.envs[idx.uint + self.closures[^1]]
|
|
||||||
|
|
||||||
|
|
||||||
proc setClosure(self: var PeonVM, idx: int, val: uint64) =
|
|
||||||
## Setter method that abstracts
|
|
||||||
## indexing closure environments
|
|
||||||
if idx == self.envs.len():
|
|
||||||
self.envs.add(val)
|
|
||||||
else:
|
|
||||||
self.envs[idx.uint + self.closures[^1]] = val
|
|
||||||
|
|
||||||
|
|
||||||
proc popClosure(self: var PeonVM, idx: int): uint64 =
|
|
||||||
## Pop method that abstracts
|
|
||||||
## popping values off closure
|
|
||||||
## environments
|
|
||||||
var idx = idx.uint + self.closures[^1]
|
|
||||||
result = self.envs[idx]
|
|
||||||
self.envs.delete(idx)
|
|
||||||
|
|
||||||
|
|
||||||
# Byte-level primitives to read and decode
|
# Byte-level primitives to read and decode
|
||||||
# bytecode
|
# bytecode
|
||||||
|
|
||||||
|
@ -700,20 +670,6 @@ when debugVM: # So nim shuts up
|
||||||
if i < self.frames.high():
|
if i < self.frames.high():
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]"
|
||||||
of "cl", "closures":
|
|
||||||
stdout.styledWrite(fgBlue, "Closure offsets: ", fgMagenta, "[")
|
|
||||||
for i, e in self.closures:
|
|
||||||
stdout.styledWrite(fgYellow, $e)
|
|
||||||
if i < self.closures.high():
|
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
|
||||||
styledEcho fgMagenta, "]"
|
|
||||||
of "e", "env", "environments":
|
|
||||||
stdout.styledWrite(fgGreen, "Environments: ", fgMagenta, "[")
|
|
||||||
for i, e in self.envs:
|
|
||||||
stdout.styledWrite(fgYellow, $e)
|
|
||||||
if i < self.envs.high():
|
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
|
||||||
styledEcho fgMagenta, "]"
|
|
||||||
of "r", "results":
|
of "r", "results":
|
||||||
stdout.styledWrite(fgYellow, "Function Results: ", fgMagenta, "[")
|
stdout.styledWrite(fgYellow, "Function Results: ", fgMagenta, "[")
|
||||||
for i, e in self.results:
|
for i, e in self.results:
|
||||||
|
@ -769,9 +725,6 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of LoadString:
|
of LoadString:
|
||||||
# Loads the string's pointer onto the stack
|
# Loads the string's pointer onto the stack
|
||||||
self.push(cast[uint64](self.constReadString(int(self.readLong()), int(self.readLong()))))
|
self.push(cast[uint64](self.constReadString(int(self.readLong()), int(self.readLong()))))
|
||||||
# We cast instead of converting because, unlike with integers,
|
|
||||||
# we don't want nim to touch any of the bits of the underlying
|
|
||||||
# value!
|
|
||||||
of LoadFloat32:
|
of LoadFloat32:
|
||||||
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
||||||
of LoadFloat64:
|
of LoadFloat64:
|
||||||
|
@ -804,32 +757,6 @@ proc dispatch*(self: var PeonVM) =
|
||||||
# not needed there anymore
|
# not needed there anymore
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
of CallClosure:
|
|
||||||
# Calls a peon closure. The code here is
|
|
||||||
# mostly identical to the one for Call,
|
|
||||||
# but we also create a new environment
|
|
||||||
# containing the function's closed-over variables
|
|
||||||
let argc = self.readLong().int
|
|
||||||
let offset = self.readLong().uint64
|
|
||||||
let retAddr = self.peek(-argc - 1) # Return address
|
|
||||||
let jmpAddr = self.peek(-argc - 2) # Function address
|
|
||||||
self.ip = jmpAddr
|
|
||||||
self.pushc(jmpAddr)
|
|
||||||
self.pushc(retAddr)
|
|
||||||
# Creates a new result slot for the
|
|
||||||
# function's return value
|
|
||||||
self.results.add(self.getNil())
|
|
||||||
# Creates a new call frame
|
|
||||||
self.frames.add(uint64(self.calls.len() - 2))
|
|
||||||
self.closures.add(offset - 1)
|
|
||||||
# Loads the arguments onto the stack
|
|
||||||
for _ in 0..<argc:
|
|
||||||
self.pushc(self.pop())
|
|
||||||
# Pops the function and return address
|
|
||||||
# off the operand stack since they're
|
|
||||||
# not needed there anymore
|
|
||||||
discard self.pop()
|
|
||||||
discard self.pop()
|
|
||||||
of Return:
|
of Return:
|
||||||
# Returns from a function.
|
# Returns from a function.
|
||||||
# Every peon program is wrapped
|
# Every peon program is wrapped
|
||||||
|
@ -890,25 +817,6 @@ proc dispatch*(self: var PeonVM) =
|
||||||
# condition in the VM's bytecode dispatch loop (which is
|
# condition in the VM's bytecode dispatch loop (which is
|
||||||
# not a great idea)
|
# not a great idea)
|
||||||
self.pushc(self.pop())
|
self.pushc(self.pop())
|
||||||
of LoadClosure:
|
|
||||||
# Loads a closed-over variable from the current
|
|
||||||
# environment onto the operand stack
|
|
||||||
self.push(self.getClosure(self.readLong().int))
|
|
||||||
of StoreClosure:
|
|
||||||
# Updates the value of a closed-over
|
|
||||||
# variable
|
|
||||||
let item = self.getc(self.readLong().int)
|
|
||||||
self.setClosure(self.readLong().int, item)
|
|
||||||
of LoadTos:
|
|
||||||
# Copies the top of the call stack
|
|
||||||
# (TOS: Top of Stack) onto the
|
|
||||||
# operand stack
|
|
||||||
self.push(self.peekc())
|
|
||||||
of AddClosure:
|
|
||||||
# Stores the value at the top of the
|
|
||||||
# operand stack in the topmost closure
|
|
||||||
# environment
|
|
||||||
self.envs.add(self.pop())
|
|
||||||
of LoadVar:
|
of LoadVar:
|
||||||
# Pushes a variable from the call stack
|
# Pushes a variable from the call stack
|
||||||
# onto the operand stack
|
# onto the operand stack
|
||||||
|
@ -987,7 +895,7 @@ proc dispatch*(self: var PeonVM) =
|
||||||
# types, we don't need specialized instructions
|
# types, we don't need specialized instructions
|
||||||
# to operate on them
|
# to operate on them
|
||||||
of Negate:
|
of Negate:
|
||||||
self.push(cast[uint64](-int64(self.pop())))
|
self.push(cast[uint64](-(cast[int64](self.pop()))))
|
||||||
of NegateFloat64:
|
of NegateFloat64:
|
||||||
self.push(cast[uint64](-cast[float](self.pop())))
|
self.push(cast[uint64](-cast[float](self.pop())))
|
||||||
of NegateFloat32:
|
of NegateFloat32:
|
||||||
|
@ -1001,7 +909,7 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of Divide:
|
of Divide:
|
||||||
self.push(self.pop() div self.pop())
|
self.push(self.pop() div self.pop())
|
||||||
of SignedDivide:
|
of SignedDivide:
|
||||||
self.push(uint64(int64(self.pop()) div int64(self.pop())))
|
self.push(uint64(cast[int64](self.pop()) div cast[int64](self.pop())))
|
||||||
of AddFloat64:
|
of AddFloat64:
|
||||||
self.push(cast[uint64](cast[float](self.pop()) + cast[float](self.pop())))
|
self.push(cast[uint64](cast[float](self.pop()) + cast[float](self.pop())))
|
||||||
of SubtractFloat64:
|
of SubtractFloat64:
|
||||||
|
@ -1021,7 +929,7 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of Pow:
|
of Pow:
|
||||||
self.push(uint64(self.pop() ^ self.pop()))
|
self.push(uint64(self.pop() ^ self.pop()))
|
||||||
of SignedPow:
|
of SignedPow:
|
||||||
self.push(uint64(int64(self.pop()) ^ int64(self.pop())))
|
self.push(uint64(cast[int64](self.pop()) ^ cast[int64](self.pop())))
|
||||||
of PowFloat64:
|
of PowFloat64:
|
||||||
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
||||||
of PowFloat32:
|
of PowFloat32:
|
||||||
|
@ -1029,7 +937,7 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of Mod:
|
of Mod:
|
||||||
self.push(uint64(self.pop() mod self.pop()))
|
self.push(uint64(self.pop() mod self.pop()))
|
||||||
of SignedMod:
|
of SignedMod:
|
||||||
self.push(uint64(int64(self.pop()) mod int64(self.pop())))
|
self.push(uint64(cast[int64](self.pop()) mod cast[int64](self.pop())))
|
||||||
of ModFloat64:
|
of ModFloat64:
|
||||||
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
||||||
of ModFloat32:
|
of ModFloat32:
|
||||||
|
@ -1056,7 +964,31 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of GreaterOrEqual:
|
of GreaterOrEqual:
|
||||||
self.push(self.getBool(self.pop() !>= self.pop()))
|
self.push(self.getBool(self.pop() !>= self.pop()))
|
||||||
of LessOrEqual:
|
of LessOrEqual:
|
||||||
self.push(self.getBool(self.pop() <= self.pop()))
|
self.push(self.getBool(cast[int64](self.pop()) <= cast[int64](self.pop())))
|
||||||
|
of SignedGreaterThan:
|
||||||
|
self.push(self.getBool(cast[int64](self.pop()) !> cast[int64](self.pop())))
|
||||||
|
of SignedLessThan:
|
||||||
|
self.push(self.getBool(cast[int64](self.pop()) < cast[int64](self.pop())))
|
||||||
|
of SignedGreaterOrEqual:
|
||||||
|
self.push(self.getBool(cast[int64](self.pop()) !>= cast[int64](self.pop())))
|
||||||
|
of SignedLessOrEqual:
|
||||||
|
self.push(self.getBool(cast[int64](self.pop()) <= cast[int64](self.pop())))
|
||||||
|
of Float64GreaterThan:
|
||||||
|
self.push(self.getBool(cast[float64](self.pop()) !> cast[float64](self.pop())))
|
||||||
|
of Float64LessThan:
|
||||||
|
self.push(self.getBool(cast[float64](self.pop()) < cast[float64](self.pop())))
|
||||||
|
of Float64GreaterOrEqual:
|
||||||
|
self.push(self.getBool(cast[float64](self.pop()) !>= cast[float64](self.pop())))
|
||||||
|
of Float64LessOrEqual:
|
||||||
|
self.push(self.getBool(cast[float64](self.pop()) <= cast[float64](self.pop())))
|
||||||
|
of Float32GreaterThan:
|
||||||
|
self.push(self.getBool(cast[float32](self.pop()) !> cast[float32](self.pop())))
|
||||||
|
of Float32LessThan:
|
||||||
|
self.push(self.getBool(cast[float32](self.pop()) < cast[float32](self.pop())))
|
||||||
|
of Float32GreaterOrEqual:
|
||||||
|
self.push(self.getBool(cast[float32](self.pop()) !>= cast[float32](self.pop())))
|
||||||
|
of Float32LessOrEqual:
|
||||||
|
self.push(self.getBool(cast[float32](self.pop()) <= cast[float32](self.pop())))
|
||||||
# Print opcodes
|
# Print opcodes
|
||||||
of PrintInt64:
|
of PrintInt64:
|
||||||
echo cast[int64](self.pop())
|
echo cast[int64](self.pop())
|
||||||
|
|
|
@ -62,6 +62,7 @@ Options
|
||||||
-d, --disassemble Disassemble the given bytecode file instead of executing it
|
-d, --disassemble Disassemble the given bytecode file instead of executing it
|
||||||
-m, --mode Set the compilation mode. Acceptable values are 'debug' and
|
-m, --mode Set the compilation mode. Acceptable values are 'debug' and
|
||||||
'release'
|
'release'
|
||||||
|
-c, --compile Compile the code, but do not execute it
|
||||||
--warnings Turn warnings on/off (default: on). Acceptable values are
|
--warnings Turn warnings on/off (default: on). Acceptable values are
|
||||||
yes/on and no/off
|
yes/on and no/off
|
||||||
--noWarn Disable a specific warning (for example, --noWarn unusedVariable)
|
--noWarn Disable a specific warning (for example, --noWarn unusedVariable)
|
||||||
|
|
|
@ -59,10 +59,6 @@ type
|
||||||
returnType: Type
|
returnType: Type
|
||||||
builtinOp: string
|
builtinOp: string
|
||||||
fun: Declaration
|
fun: Declaration
|
||||||
isClosure: bool
|
|
||||||
envLen: int
|
|
||||||
children: seq[Type]
|
|
||||||
parent: Type
|
|
||||||
retJumps: seq[int]
|
retJumps: seq[int]
|
||||||
forwarded: bool
|
forwarded: bool
|
||||||
location: int
|
location: int
|
||||||
|
@ -90,7 +86,8 @@ export bytecode
|
||||||
type
|
type
|
||||||
WarningKind* {.pure.} = enum
|
WarningKind* {.pure.} = enum
|
||||||
## A warning enumeration type
|
## A warning enumeration type
|
||||||
UnreachableCode, UnusedName, ShadowOuterScope
|
UnreachableCode, UnusedName, ShadowOuterScope,
|
||||||
|
MutateOuterScope
|
||||||
CompileMode* {.pure.} = enum
|
CompileMode* {.pure.} = enum
|
||||||
## A compilation mode enumeration
|
## A compilation mode enumeration
|
||||||
Debug, Release
|
Debug, Release
|
||||||
|
@ -134,8 +131,6 @@ type
|
||||||
belongsTo: Name
|
belongsTo: Name
|
||||||
# Where is this node declared in its file?
|
# Where is this node declared in its file?
|
||||||
line: int
|
line: int
|
||||||
# Has this name been closed over?
|
|
||||||
isClosedOver: bool
|
|
||||||
# Has this name been referenced at least once?
|
# Has this name been referenced at least once?
|
||||||
resolved: bool
|
resolved: bool
|
||||||
# The AST node associated with this node. This
|
# The AST node associated with this node. This
|
||||||
|
@ -151,6 +146,12 @@ type
|
||||||
isReal: bool
|
isReal: bool
|
||||||
# Is this name a builtin?
|
# Is this name a builtin?
|
||||||
isBuiltin: bool
|
isBuiltin: bool
|
||||||
|
# The location of this name on the stack.
|
||||||
|
# Only makes sense for names that actually
|
||||||
|
# materialize on the call stack at runtime
|
||||||
|
# (except for functions, where we use it to
|
||||||
|
# signal where the function's frame starts)
|
||||||
|
position: int
|
||||||
|
|
||||||
Loop = object
|
Loop = object
|
||||||
## A "loop object" used
|
## A "loop object" used
|
||||||
|
@ -243,6 +244,9 @@ type
|
||||||
showMismatches: bool
|
showMismatches: bool
|
||||||
# Are we compiling in debug mode?
|
# Are we compiling in debug mode?
|
||||||
mode: CompileMode
|
mode: CompileMode
|
||||||
|
# The topmost occupied stack slot
|
||||||
|
# in the current frame (0-indexed)
|
||||||
|
stackIndex: int
|
||||||
PragmaKind = enum
|
PragmaKind = enum
|
||||||
## An enumeration of pragma types
|
## An enumeration of pragma types
|
||||||
Immediate,
|
Immediate,
|
||||||
|
@ -253,8 +257,8 @@ type
|
||||||
kind: PragmaKind
|
kind: PragmaKind
|
||||||
handler: proc (self: Compiler, pragma: Pragma, name: Name)
|
handler: proc (self: Compiler, pragma: Pragma, name: Name)
|
||||||
CompileError* = ref object of PeonException
|
CompileError* = ref object of PeonException
|
||||||
compiler*: Compiler
|
|
||||||
node*: ASTNode
|
node*: ASTNode
|
||||||
|
function*: Declaration
|
||||||
|
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
|
@ -268,8 +272,9 @@ proc peek(self: Compiler, distance: int = 0): ASTNode
|
||||||
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Type {.discardable.}
|
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Type {.discardable.}
|
||||||
proc varDecl(self: Compiler, node: VarDecl)
|
proc varDecl(self: Compiler, node: VarDecl)
|
||||||
proc match(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
proc match(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
||||||
|
proc specialize(self: Compiler, typ: Type, args: seq[Expression]): Type {.discardable.}
|
||||||
proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable.}
|
proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.}
|
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.}
|
||||||
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable.}
|
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.}
|
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc infer(self: Compiler, node: LiteralExpr): Type
|
proc infer(self: Compiler, node: LiteralExpr): Type
|
||||||
|
@ -319,6 +324,7 @@ proc newCompiler*(replMode: bool = false): Compiler =
|
||||||
result.forwarded = @[]
|
result.forwarded = @[]
|
||||||
result.disabledWarnings = @[]
|
result.disabledWarnings = @[]
|
||||||
result.functions = @[]
|
result.functions = @[]
|
||||||
|
result.stackIndex = 1
|
||||||
|
|
||||||
|
|
||||||
## Public getters for nicer error formatting
|
## Public getters for nicer error formatting
|
||||||
|
@ -330,8 +336,8 @@ proc getSource*(self: Compiler): string = self.source
|
||||||
|
|
||||||
## Utility functions
|
## Utility functions
|
||||||
|
|
||||||
proc `$`*(self: Name): string = $self[]
|
proc `$`*(self: Name): string = $(self[])
|
||||||
#proc `$`(self: Type): string = $self[]
|
proc `$`(self: Type): string = $(self[])
|
||||||
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,10 +359,10 @@ proc done(self: Compiler): bool {.inline.} =
|
||||||
result = self.current > self.ast.high()
|
result = self.current > self.ast.high()
|
||||||
|
|
||||||
|
|
||||||
proc error(self: Compiler, message: string, node: ASTNode = nil) {.raises: [CompileError], inline.} =
|
proc error(self: Compiler, message: string, node: ASTNode = nil) {.inline.} =
|
||||||
## Raises a CompileError exception
|
## Raises a CompileError exception
|
||||||
let node = if node.isNil(): self.getCurrentNode() else: node
|
let node = if node.isNil(): self.getCurrentNode() else: node
|
||||||
raise CompileError(msg: message, node: node, line: node.token.line, file: self.file, compiler: self)
|
raise CompileError(msg: message, node: node, line: node.token.line, file: node.file)
|
||||||
|
|
||||||
|
|
||||||
proc warning(self: Compiler, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
proc warning(self: Compiler, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
||||||
|
@ -427,6 +433,43 @@ proc emitBytes(self: Compiler, bytarr: openarray[OpCode | uint8], line: int) {.i
|
||||||
self.emitByte(b, line)
|
self.emitByte(b, line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc printRepl(self: Compiler, typ: Type, node: Expression) =
|
||||||
|
## Emits instruction to print
|
||||||
|
## peon types in REPL mode
|
||||||
|
case typ.kind:
|
||||||
|
of Int64:
|
||||||
|
self.emitByte(PrintInt64, node.token.line)
|
||||||
|
of UInt64:
|
||||||
|
self.emitByte(PrintUInt64, node.token.line)
|
||||||
|
of Int32:
|
||||||
|
self.emitByte(PrintInt32, node.token.line)
|
||||||
|
of UInt32:
|
||||||
|
self.emitByte(PrintInt32, node.token.line)
|
||||||
|
of Int16:
|
||||||
|
self.emitByte(PrintInt16, node.token.line)
|
||||||
|
of UInt16:
|
||||||
|
self.emitByte(PrintUInt16, node.token.line)
|
||||||
|
of Int8:
|
||||||
|
self.emitByte(PrintInt8, node.token.line)
|
||||||
|
of UInt8:
|
||||||
|
self.emitByte(PrintUInt8, node.token.line)
|
||||||
|
of Float64:
|
||||||
|
self.emitByte(PrintFloat64, node.token.line)
|
||||||
|
of Float32:
|
||||||
|
self.emitByte(PrintFloat32, node.token.line)
|
||||||
|
of Bool:
|
||||||
|
self.emitByte(PrintBool, node.token.line)
|
||||||
|
of Nan:
|
||||||
|
self.emitByte(PrintNan, node.token.line)
|
||||||
|
of Inf:
|
||||||
|
self.emitByte(PrintInf, node.token.line)
|
||||||
|
of String:
|
||||||
|
self.emitByte(PrintString, node.token.line)
|
||||||
|
else:
|
||||||
|
self.emitByte(PrintHex, node.token.line)
|
||||||
|
|
||||||
|
|
||||||
proc makeConstant(self: Compiler, val: Expression, typ: Type): array[3, uint8] =
|
proc makeConstant(self: Compiler, val: Expression, typ: Type): array[3, uint8] =
|
||||||
## Adds a constant to the current chunk's constant table
|
## Adds a constant to the current chunk's constant table
|
||||||
## and returns its index as a 3-byte array of uint8s
|
## and returns its index as a 3-byte array of uint8s
|
||||||
|
@ -520,18 +563,17 @@ proc patchJump(self: Compiler, offset: int) =
|
||||||
## jump using emitJump
|
## jump using emitJump
|
||||||
var jump: int = self.chunk.code.len() - self.jumps[offset].offset
|
var jump: int = self.chunk.code.len() - self.jumps[offset].offset
|
||||||
if jump < 0:
|
if jump < 0:
|
||||||
self.error("invalid jump size (< 0), did the bytecode size change without fixJumps being called?")
|
self.error("jump size cannot be negative (This is an internal error and most likely a bug)")
|
||||||
if jump > 16777215:
|
if jump > 16777215:
|
||||||
# TODO: Emit consecutive jumps?
|
# TODO: Emit consecutive jumps using insertAt
|
||||||
self.error("cannot jump more than 16777215 instructions")
|
self.error("cannot jump more than 16777215 instructions")
|
||||||
if jump > 0:
|
if jump > 0:
|
||||||
self.setJump(self.jumps[offset].offset, (jump - 4).toTriple())
|
self.setJump(self.jumps[offset].offset, (jump - 4).toTriple())
|
||||||
self.jumps[offset].patched = true
|
self.jumps[offset].patched = true
|
||||||
else:
|
else:
|
||||||
# TODO: Discard jump of size 0 and update
|
# TODO: Discard jump of size 0
|
||||||
# bytecode metadata
|
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc emitJump(self: Compiler, opcode: OpCode, line: int): int =
|
proc emitJump(self: Compiler, opcode: OpCode, line: int): int =
|
||||||
## Emits a dummy jump offset to be patched later
|
## Emits a dummy jump offset to be patched later
|
||||||
|
@ -622,7 +664,9 @@ proc fixNames(self: Compiler, where, oldLen: int) =
|
||||||
proc insertAt(self: Compiler, where: int, opcode: OpCode, data: openarray[uint8]): int =
|
proc insertAt(self: Compiler, where: int, opcode: OpCode, data: openarray[uint8]): int =
|
||||||
## Inserts the given instruction into the
|
## Inserts the given instruction into the
|
||||||
## chunk's code segment and updates internal
|
## chunk's code segment and updates internal
|
||||||
## metadata to reflect this change
|
## metadata to reflect this change. Returns
|
||||||
|
## the new location where the code was added
|
||||||
|
## plus one (useful for consecutive calls)
|
||||||
result = where
|
result = where
|
||||||
let oldLen = self.chunk.code.len()
|
let oldLen = self.chunk.code.len()
|
||||||
self.chunk.code.insert(uint8(opcode), where)
|
self.chunk.code.insert(uint8(opcode), where)
|
||||||
|
@ -639,31 +683,6 @@ proc insertAt(self: Compiler, where: int, opcode: OpCode, data: openarray[uint8]
|
||||||
self.fixFunctionOffsets(oldLen, where)
|
self.fixFunctionOffsets(oldLen, where)
|
||||||
|
|
||||||
|
|
||||||
proc compileDecl(self: Compiler, name: Name) =
|
|
||||||
## Internal resolve() helper
|
|
||||||
|
|
||||||
# There's no reason to compile a declaration
|
|
||||||
# unless it is used at least once: this way
|
|
||||||
# not only do we save space if a name is declared
|
|
||||||
# but never used, it also makes it easier to
|
|
||||||
# implement generics and lets us emit warnings for
|
|
||||||
# unused names once they go out of scope. Yay!
|
|
||||||
if name.resolved:
|
|
||||||
return
|
|
||||||
name.resolved = true
|
|
||||||
if name.isGeneric:
|
|
||||||
# We typecheck generics at declaration time,
|
|
||||||
# so they're already compiled
|
|
||||||
return
|
|
||||||
# Now we just dispatch to one of our functions to
|
|
||||||
# compile the declaration
|
|
||||||
case name.kind:
|
|
||||||
of NameKind.Function:
|
|
||||||
self.funDecl(FunDecl(name.node), name)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
|
|
||||||
proc resolve(self: Compiler, name: string): Name =
|
proc resolve(self: Compiler, name: string): Name =
|
||||||
## Traverses all existing namespaces and returns
|
## Traverses all existing namespaces and returns
|
||||||
## the first object with the given name. Returns
|
## the first object with the given name. Returns
|
||||||
|
@ -694,10 +713,11 @@ proc resolve(self: Compiler, name: string): Name =
|
||||||
# might not want to also have access to C's and D's
|
# might not want to also have access to C's and D's
|
||||||
# names as they might clash with its own stuff)
|
# names as they might clash with its own stuff)
|
||||||
continue
|
continue
|
||||||
|
if obj.kind == Argument and obj.belongsTo != self.currentFunction:
|
||||||
|
continue
|
||||||
result = obj
|
result = obj
|
||||||
|
result.resolved = true
|
||||||
break
|
break
|
||||||
if not result.isNil():
|
|
||||||
self.compileDecl(result)
|
|
||||||
|
|
||||||
|
|
||||||
proc resolve(self: Compiler, name: IdentExpr): Name =
|
proc resolve(self: Compiler, name: IdentExpr): Name =
|
||||||
|
@ -717,63 +737,6 @@ proc resolveOrError[T: IdentExpr | string](self: Compiler, name: T): Name =
|
||||||
self.error(&"reference to undefined name '{name}'")
|
self.error(&"reference to undefined name '{name}'")
|
||||||
|
|
||||||
|
|
||||||
proc getStackPos(self: Compiler, name: Name): int =
|
|
||||||
## Returns the predicted call stack position
|
|
||||||
## of a given name, relative to the current
|
|
||||||
## stack frame
|
|
||||||
var found = false
|
|
||||||
result = 2 # Locals start at frame offset 2
|
|
||||||
for variable in self.names:
|
|
||||||
# Only variables and arguments are actually located on
|
|
||||||
# the stack, so we skip everything else
|
|
||||||
if variable.kind notin [NameKind.Var, NameKind.Argument]:
|
|
||||||
continue
|
|
||||||
# Variable is in a scope above us, so not in frame. Skip it!
|
|
||||||
elif variable.depth < name.depth:
|
|
||||||
continue
|
|
||||||
# Arguments to builtin functions are optimized away to stack
|
|
||||||
# temporaries. There is no stack frame for builtins, so we skip
|
|
||||||
# these names too
|
|
||||||
elif variable.kind == Argument:
|
|
||||||
if variable.belongsTo.isBuiltin:
|
|
||||||
continue
|
|
||||||
elif not variable.belongsTo.resolved:
|
|
||||||
continue
|
|
||||||
# This variable isn't in declared in our module,
|
|
||||||
# but we may still have access to it
|
|
||||||
elif variable.owner != name.owner:
|
|
||||||
# Variable is private in its owner module
|
|
||||||
# or it wasn't exported to us explicitly,
|
|
||||||
# so we just move on
|
|
||||||
if variable.isPrivate or name.owner notin variable.exportedTo:
|
|
||||||
inc(result)
|
|
||||||
continue
|
|
||||||
# Note: this MUST be an if, NOT an elif!
|
|
||||||
if name.ident == variable.ident and variable.depth == name.depth and name.owner == variable.owner:
|
|
||||||
if not name.belongsTo.isNil() and not variable.belongsTo.isNil():
|
|
||||||
if name.belongsTo != variable.belongsTo and variable.belongsTo.depth > name.belongsTo.depth:
|
|
||||||
# Argument with the same name but in a different function: ignore it
|
|
||||||
dec(result)
|
|
||||||
continue
|
|
||||||
found = true
|
|
||||||
variable.resolved = true
|
|
||||||
break
|
|
||||||
inc(result)
|
|
||||||
if not found:
|
|
||||||
result = -1
|
|
||||||
|
|
||||||
|
|
||||||
proc getClosurePos(self: Compiler, name: Name): int =
|
|
||||||
## Returns the position of a name in a closure's
|
|
||||||
## environment
|
|
||||||
if not self.currentFunction.valueType.isClosure:
|
|
||||||
return -1
|
|
||||||
for i, e in self.closures:
|
|
||||||
if e == name:
|
|
||||||
return i
|
|
||||||
return -1
|
|
||||||
|
|
||||||
|
|
||||||
proc compareUnions(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]): bool =
|
proc compareUnions(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]): bool =
|
||||||
## Compares type unions between each other
|
## Compares type unions between each other
|
||||||
var
|
var
|
||||||
|
@ -958,26 +921,22 @@ proc infer(self: Compiler, node: LiteralExpr): Type =
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of intExpr, binExpr, octExpr, hexExpr:
|
of intExpr, binExpr, octExpr, hexExpr:
|
||||||
let size = node.token.lexeme.split("'")
|
let size = node.token.lexeme.split("'")
|
||||||
if len(size) notin 1..2:
|
|
||||||
self.error("invalid state: infer -> invalid size specifier (This is an internal error and most likely a bug!)")
|
|
||||||
if size.len() == 1:
|
if size.len() == 1:
|
||||||
return Type(kind: Int64)
|
return Type(kind: Int64)
|
||||||
let typ = size[1].toIntrinsic()
|
let typ = size[1].toIntrinsic()
|
||||||
if not self.compare(typ, nil):
|
if not self.compare(typ, nil):
|
||||||
return typ
|
return typ
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid type specifier '{size[1]}' for int")
|
self.error(&"invalid type specifier '{size[1]}' for int", node)
|
||||||
of floatExpr:
|
of floatExpr:
|
||||||
let size = node.token.lexeme.split("'")
|
let size = node.token.lexeme.split("'")
|
||||||
if len(size) notin 1..2:
|
if size.len() == 1:
|
||||||
self.error("invalid state: infer -> invalid size specifier (This is an internal error and most likely a bug!)")
|
|
||||||
if size.len() == 1 or size[1] == "f64":
|
|
||||||
return Type(kind: Float64)
|
return Type(kind: Float64)
|
||||||
let typ = size[1].toIntrinsic()
|
let typ = size[1].toIntrinsic()
|
||||||
if not typ.isNil():
|
if not typ.isNil():
|
||||||
return typ
|
return typ
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid type specifier '{size[1]}' for float")
|
self.error(&"invalid type specifier '{size[1]}' for float", node)
|
||||||
of trueExpr:
|
of trueExpr:
|
||||||
return Type(kind: Bool)
|
return Type(kind: Bool)
|
||||||
of falseExpr:
|
of falseExpr:
|
||||||
|
@ -985,7 +944,7 @@ proc infer(self: Compiler, node: LiteralExpr): Type =
|
||||||
of strExpr:
|
of strExpr:
|
||||||
return Type(kind: String)
|
return Type(kind: String)
|
||||||
else:
|
else:
|
||||||
discard # TODO
|
discard # Unreachable
|
||||||
|
|
||||||
|
|
||||||
proc infer(self: Compiler, node: Expression): Type =
|
proc infer(self: Compiler, node: Expression): Type =
|
||||||
|
@ -1104,12 +1063,17 @@ proc findInModule(self: Compiler, name: string, module: Name): seq[Name] =
|
||||||
## Looks for objects that have been already declared as
|
## Looks for objects that have been already declared as
|
||||||
## public within the given module with the given name.
|
## public within the given module with the given name.
|
||||||
## Returns all objects that apply. If the name is an
|
## Returns all objects that apply. If the name is an
|
||||||
## empty string, returns all objects within the given moule
|
## empty string, returns all objects within the given
|
||||||
for obj in reversed(self.names):
|
## module, regardless of whether they are exported to
|
||||||
if name != "" and obj.ident.token.lexeme != name:
|
## the current one or not
|
||||||
continue
|
if name == "":
|
||||||
if not obj.isPrivate and obj.owner == module:
|
for obj in reversed(self.names):
|
||||||
result.add(obj)
|
if not obj.isPrivate and obj.owner == module:
|
||||||
|
result.add(obj)
|
||||||
|
else:
|
||||||
|
for obj in self.findInModule("", module):
|
||||||
|
if obj.ident.token.lexeme == name and self.currentModule in obj.exportedTo:
|
||||||
|
result.add(obj)
|
||||||
|
|
||||||
|
|
||||||
proc findByType(self: Compiler, name: string, kind: Type): seq[Name] =
|
proc findByType(self: Compiler, name: string, kind: Type): seq[Name] =
|
||||||
|
@ -1207,12 +1171,11 @@ proc match(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowF
|
||||||
msg &= " (compile with --showMismatches for more details)"
|
msg &= " (compile with --showMismatches for more details)"
|
||||||
self.error(msg, node)
|
self.error(msg, node)
|
||||||
if impl[0].valueType.forwarded and not allowFwd:
|
if impl[0].valueType.forwarded and not allowFwd:
|
||||||
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
|
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
|
||||||
result = impl[0]
|
result = impl[0]
|
||||||
for (a, b) in zip(result.valueType.args, kind.args):
|
for (a, b) in zip(result.valueType.args, kind.args):
|
||||||
if not a.kind.isAny() and b.kind.isAny():
|
if not a.kind.isAny() and b.kind.isAny():
|
||||||
self.error("any is not a valid type in this context", node)
|
self.error("any is not a valid type in this context", node)
|
||||||
self.compileDecl(result)
|
|
||||||
|
|
||||||
|
|
||||||
proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line: int) =
|
proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line: int) =
|
||||||
|
@ -1260,13 +1223,25 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line
|
||||||
"GreaterThan": GreaterThan,
|
"GreaterThan": GreaterThan,
|
||||||
"LessOrEqual": LessOrEqual,
|
"LessOrEqual": LessOrEqual,
|
||||||
"GreaterOrEqual": GreaterOrEqual,
|
"GreaterOrEqual": GreaterOrEqual,
|
||||||
|
"SignedLessThan": SignedLessThan,
|
||||||
|
"SignedGreaterThan": SignedGreaterThan,
|
||||||
|
"SignedLessOrEqual": SignedLessOrEqual,
|
||||||
|
"SignedGreaterOrEqual": SignedGreaterOrEqual,
|
||||||
|
"Float32LessThan": Float32LessThan,
|
||||||
|
"Float32GreaterThan": Float32GreaterThan,
|
||||||
|
"Float32LessOrEqual": Float32LessOrEqual,
|
||||||
|
"Float32GreaterOrEqual": Float32GreaterOrEqual,
|
||||||
|
"Float64LessThan": Float64LessThan,
|
||||||
|
"Float64GreaterThan": Float64GreaterThan,
|
||||||
|
"Float64LessOrEqual": Float64LessOrEqual,
|
||||||
|
"Float64GreaterOrEqual": Float64GreaterOrEqual,
|
||||||
"PrintString": PrintString,
|
"PrintString": PrintString,
|
||||||
"SysClock64": SysClock64,
|
"SysClock64": SysClock64,
|
||||||
"LogicalNot": LogicalNot,
|
"LogicalNot": LogicalNot,
|
||||||
"NegInf": LoadNInf
|
"NegInf": LoadNInf
|
||||||
}.to_table()
|
}.to_table()
|
||||||
if fn.builtinOp == "print":
|
if fn.builtinOp == "print":
|
||||||
var typ = self.expression(args[0], compile=false)
|
let typ = self.inferOrError(args[0])
|
||||||
case typ.kind:
|
case typ.kind:
|
||||||
of Int64:
|
of Int64:
|
||||||
self.emitByte(PrintInt64, line)
|
self.emitByte(PrintInt64, line)
|
||||||
|
@ -1297,9 +1272,20 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line
|
||||||
of Inf:
|
of Inf:
|
||||||
self.emitByte(PrintInf, line)
|
self.emitByte(PrintInf, line)
|
||||||
of Function:
|
of Function:
|
||||||
self.emitByte(PrintHex, line)
|
self.emitByte(LoadString, line)
|
||||||
|
var loc: string = typ.location.toHex()
|
||||||
|
while loc[0] == '0' and loc.len() > 1:
|
||||||
|
loc = loc[1..^1]
|
||||||
|
var str: string
|
||||||
|
if typ.isLambda:
|
||||||
|
str = &"anonymous function at 0x{loc}"
|
||||||
|
else:
|
||||||
|
str = &"function '{FunDecl(typ.fun).name.token.lexeme}' at 0x{loc}"
|
||||||
|
self.emitBytes(str.len().toTriple(), line)
|
||||||
|
self.emitBytes(self.chunk.writeConstant(str.toBytes()), line)
|
||||||
|
self.emitByte(PrintString, line)
|
||||||
else:
|
else:
|
||||||
self.error("invalid type for built-in 'print'", args[0])
|
self.error(&"invalid type {self.stringify(typ)} for built-in 'print'", args[0])
|
||||||
return
|
return
|
||||||
if fn.builtinOp in codes:
|
if fn.builtinOp in codes:
|
||||||
self.emitByte(codes[fn.builtinOp], line)
|
self.emitByte(codes[fn.builtinOp], line)
|
||||||
|
@ -1327,16 +1313,6 @@ proc beginScope(self: Compiler) =
|
||||||
inc(self.depth)
|
inc(self.depth)
|
||||||
|
|
||||||
|
|
||||||
# Flattens our weird function tree into a linear
|
|
||||||
# list
|
|
||||||
proc flattenImpl(self: Type, to: var seq[Type]) =
|
|
||||||
to.add(self)
|
|
||||||
for child in self.children:
|
|
||||||
flattenImpl(child, to)
|
|
||||||
|
|
||||||
|
|
||||||
proc flatten(self: Type): seq[Type] = flattenImpl(self, result)
|
|
||||||
|
|
||||||
|
|
||||||
proc patchForwardDeclarations(self: Compiler) =
|
proc patchForwardDeclarations(self: Compiler) =
|
||||||
## Patches forward declarations and looks
|
## Patches forward declarations and looks
|
||||||
|
@ -1386,7 +1362,7 @@ proc endScope(self: Compiler) =
|
||||||
# be referenced anymore, of course)
|
# be referenced anymore, of course)
|
||||||
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
||||||
continue
|
continue
|
||||||
elif name.kind == NameKind.Argument:
|
elif name.kind == NameKind.Argument and not name.belongsTo.isNil():
|
||||||
if name.belongsTo.isBuiltin:
|
if name.belongsTo.isBuiltin:
|
||||||
# Arguments to builtin functions become temporaries on the
|
# Arguments to builtin functions become temporaries on the
|
||||||
# stack and are popped automatically
|
# stack and are popped automatically
|
||||||
|
@ -1397,6 +1373,7 @@ proc endScope(self: Compiler) =
|
||||||
# (it may need them later)
|
# (it may need them later)
|
||||||
names.delete(names.high())
|
names.delete(names.high())
|
||||||
continue
|
continue
|
||||||
|
inc(popCount)
|
||||||
if not name.resolved:
|
if not name.resolved:
|
||||||
case name.kind:
|
case name.kind:
|
||||||
of NameKind.Var:
|
of NameKind.Var:
|
||||||
|
@ -1404,7 +1381,7 @@ proc endScope(self: Compiler) =
|
||||||
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
||||||
of NameKind.Argument:
|
of NameKind.Argument:
|
||||||
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
||||||
if not name.belongsTo.isBuiltin and name.belongsTo.isReal:
|
if not name.belongsTo.isNil() and not name.belongsTo.isBuiltin and name.belongsTo.isReal:
|
||||||
# Builtin functions never use their arguments. We also don't emit this
|
# Builtin functions never use their arguments. We also don't emit this
|
||||||
# warning if the function was generated internally by the compiler (for
|
# warning if the function was generated internally by the compiler (for
|
||||||
# example as a result of generic specialization) because such objects do
|
# example as a result of generic specialization) because such objects do
|
||||||
|
@ -1412,7 +1389,7 @@ proc endScope(self: Compiler) =
|
||||||
self.warning(UnusedName, &"argument '{name.ident.token.lexeme}' is unused (add '_' prefix to silence warning)", name)
|
self.warning(UnusedName, &"argument '{name.ident.token.lexeme}' is unused (add '_' prefix to silence warning)", name)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
inc(popCount)
|
dec(self.stackIndex, popCount)
|
||||||
if popCount > 1:
|
if popCount > 1:
|
||||||
# If we're popping more than one variable,
|
# If we're popping more than one variable,
|
||||||
# we emit a bunch of PopN instructions until
|
# we emit a bunch of PopN instructions until
|
||||||
|
@ -1529,7 +1506,6 @@ proc declare(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
returnType: nil, # We check it later
|
returnType: nil, # We check it later
|
||||||
args: @[],
|
args: @[],
|
||||||
fun: node,
|
fun: node,
|
||||||
children: @[],
|
|
||||||
forwarded: node.body.isNil()),
|
forwarded: node.body.isNil()),
|
||||||
ident: node.name,
|
ident: node.name,
|
||||||
node: node,
|
node: node,
|
||||||
|
@ -1578,6 +1554,7 @@ proc declare(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
n = self.names[^1]
|
n = self.names[^1]
|
||||||
|
declaredName = node.name.token.lexeme
|
||||||
if node.value.isNil():
|
if node.value.isNil():
|
||||||
discard # TODO: Fields
|
discard # TODO: Fields
|
||||||
else:
|
else:
|
||||||
|
@ -1603,18 +1580,20 @@ proc declare(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
if name == n:
|
if name == n:
|
||||||
continue
|
continue
|
||||||
# We don't check for name clashes with functions because self.match() does that
|
# We don't check for name clashes with functions because self.match() does that
|
||||||
elif name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum] and name.depth == n.depth and name.owner == n.owner:
|
||||||
if name.owner != self.currentModule:
|
self.error(&"re-declaration of {declaredName} is not allowed (previously declared in {name.owner.ident.token.lexeme}:{name.ident.token.line}:{name.ident.token.relPos.start})")
|
||||||
if name.isPrivate:
|
for name in self.names:
|
||||||
continue
|
if name == n:
|
||||||
elif self.currentModule notin name.exportedTo:
|
break
|
||||||
continue
|
if name.ident.token.lexeme != declaredName:
|
||||||
elif name.depth == 0:
|
continue
|
||||||
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer module ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})")
|
if name.owner != n.owner and (name.isPrivate or n.owner notin name.exportedTo):
|
||||||
elif name.depth == n.depth:
|
continue
|
||||||
self.error(&"re-declaration of {declaredName} is not allowed (previously declared in {name.owner.ident.token.lexeme}:{name.ident.token.line}:{name.ident.token.relPos.start})")
|
if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
elif name.depth < self.depth:
|
if name.depth < n.depth:
|
||||||
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer scope")
|
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer scope ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n)
|
||||||
|
elif name.owner != n.owner:
|
||||||
|
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer module ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
@ -1907,7 +1886,10 @@ proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discar
|
||||||
returnType: Type(kind: Any),
|
returnType: Type(kind: Any),
|
||||||
args: @[("", self.inferOrError(node.a), default)])
|
args: @[("", self.inferOrError(node.a), default)])
|
||||||
let impl = self.match(node.token.lexeme, fn, node)
|
let impl = self.match(node.token.lexeme, fn, node)
|
||||||
result = impl.valueType.returnType
|
result = impl.valueType
|
||||||
|
if impl.isGeneric:
|
||||||
|
result = self.specialize(result, @[node.a])
|
||||||
|
result = result.returnType
|
||||||
if compile:
|
if compile:
|
||||||
self.generateCall(impl, @[node.a], impl.line)
|
self.generateCall(impl, @[node.a], impl.line)
|
||||||
|
|
||||||
|
@ -1926,16 +1908,15 @@ proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool
|
||||||
## Compiles access to identifiers
|
## Compiles access to identifiers
|
||||||
var s = name
|
var s = name
|
||||||
if s.isNil():
|
if s.isNil():
|
||||||
s = self.resolveOrError(node)
|
s = self.resolveOrError(node)
|
||||||
var t = self.findByType(s.ident.token.lexeme, Type(kind: All))
|
|
||||||
s = t[0] # Shadowing!
|
|
||||||
result = s.valueType
|
result = s.valueType
|
||||||
if not compile:
|
if not compile:
|
||||||
return
|
return
|
||||||
|
var node = s.ident
|
||||||
if s.isConst:
|
if s.isConst:
|
||||||
# Constants are always emitted as Load* instructions
|
# Constants are always emitted as Load* instructions
|
||||||
# no matter the scope depth
|
# no matter the scope depth
|
||||||
self.emitConstant(node, self.infer(node))
|
self.emitConstant(VarDecl(s.node).value, self.infer(node))
|
||||||
elif s.kind == NameKind.Function:
|
elif s.kind == NameKind.Function:
|
||||||
# Functions have no runtime representation, they're just
|
# Functions have no runtime representation, they're just
|
||||||
# a location to jump to, but we pretend they aren't and
|
# a location to jump to, but we pretend they aren't and
|
||||||
|
@ -1953,45 +1934,14 @@ proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool
|
||||||
self.emitByte(LoadInf, node.token.line)
|
self.emitByte(LoadInf, node.token.line)
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
elif s.depth > 0 and not s.belongsTo.isNil() and s.belongsTo != self.currentFunction and self.currentFunction.valueType in s.belongsTo.valueType.children:
|
|
||||||
# Loads a closure variable from a closure environment
|
|
||||||
if not s.isClosedOver:
|
|
||||||
var fn = self.currentFunction.valueType
|
|
||||||
while true:
|
|
||||||
fn.isClosure = true
|
|
||||||
fn.envLen += 1
|
|
||||||
if fn.parent.isNil():
|
|
||||||
break
|
|
||||||
fn = fn.parent
|
|
||||||
s.isClosedOver = true
|
|
||||||
self.closures.add(s)
|
|
||||||
# The best place to save this name for later use into
|
|
||||||
# the closure is right when the name is declared: the
|
|
||||||
# problem with this approach is that we have no way of
|
|
||||||
# knowing which names are closed over at the time they
|
|
||||||
# are first declared. What we do, then, is this: once we
|
|
||||||
# do know that a given name is closed over, we modify the
|
|
||||||
# code segment so that it stores the name's value into the
|
|
||||||
# topmost closure environment once it is declared
|
|
||||||
var location: int
|
|
||||||
if s.kind == Argument:
|
|
||||||
location = self.insertAt(s.belongsTo.codePos, LoadVar, self.getStackPos(s).toTriple())
|
|
||||||
else:
|
|
||||||
location = self.insertAt(s.codePos, LoadTos, [])
|
|
||||||
discard self.insertAt(location, AddClosure, [])
|
|
||||||
let pos = self.getClosurePos(s)
|
|
||||||
if pos == -1:
|
|
||||||
self.error(&"cannot compute closure offset for '{s.ident.token.lexeme}'", s.ident)
|
|
||||||
self.emitByte(LoadClosure, s.ident.token.line)
|
|
||||||
self.emitBytes(pos.toTriple(), s.ident.token.line)
|
|
||||||
else:
|
else:
|
||||||
# Loads a regular variable from the current frame
|
# Loads a regular variable from the current frame
|
||||||
self.emitByte(LoadVar, s.ident.token.line)
|
self.emitByte(LoadVar, s.ident.token.line)
|
||||||
# No need to check for -1 here: we already did a nil check above!
|
# No need to check for -1 here: we already did a nil check above!
|
||||||
self.emitBytes(self.getStackPos(s).toTriple(), s.ident.token.line)
|
self.emitBytes(s.position.toTriple(), s.ident.token.line)
|
||||||
|
|
||||||
|
|
||||||
proc assignment(self: Compiler, node: ASTNode, compile: bool = true): Name {.discardable.} =
|
proc assignment(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles assignment expressions
|
## Compiles assignment expressions
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of assignExpr:
|
of assignExpr:
|
||||||
|
@ -2004,14 +1954,14 @@ proc assignment(self: Compiler, node: ASTNode, compile: bool = true): Name {.dis
|
||||||
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
||||||
self.check(node.value, r.valueType)
|
self.check(node.value, r.valueType)
|
||||||
self.expression(node.value)
|
self.expression(node.value)
|
||||||
|
var position = r.position
|
||||||
|
if r.depth < self.depth:
|
||||||
|
self.warning(WarningKind.MutateOuterScope, &"mutation of '{r.ident.token.lexeme}' declared in outer scope ({r.owner.file}.pn:{r.ident.token.line}:{r.ident.token.relPos.start})", nil, node)
|
||||||
|
result = r.valueType
|
||||||
if not compile:
|
if not compile:
|
||||||
return
|
return
|
||||||
if not r.isClosedOver:
|
self.emitByte(StoreVar, node.token.line)
|
||||||
self.emitByte(StoreVar, node.token.line)
|
self.emitBytes(position.toTriple(), node.token.line)
|
||||||
self.emitBytes(self.getStackPos(r).toTriple(), node.token.line)
|
|
||||||
else:
|
|
||||||
self.emitByte(StoreClosure, node.token.line)
|
|
||||||
self.emitBytes(self.getClosurePos(r).toTriple(), node.token.line)
|
|
||||||
of setItemExpr:
|
of setItemExpr:
|
||||||
let node = SetItemExpr(node)
|
let node = SetItemExpr(node)
|
||||||
let name = IdentExpr(node.name)
|
let name = IdentExpr(node.name)
|
||||||
|
@ -2086,13 +2036,8 @@ proc generateCall(self: Compiler, fn: Type, args: seq[Expression], line: int) {.
|
||||||
# Creates a new call frame and jumps
|
# Creates a new call frame and jumps
|
||||||
# to the function's first instruction
|
# to the function's first instruction
|
||||||
# in the code
|
# in the code
|
||||||
if not fn.isClosure:
|
self.emitByte(Call, line)
|
||||||
self.emitByte(Call, line)
|
|
||||||
else:
|
|
||||||
self.emitByte(CallClosure,line)
|
|
||||||
self.emitBytes(args.len().toTriple(), line)
|
self.emitBytes(args.len().toTriple(), line)
|
||||||
if fn.isClosure:
|
|
||||||
self.emitBytes(fn.envLen.toTriple(), line)
|
|
||||||
self.patchReturnAddress(pos)
|
self.patchReturnAddress(pos)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2119,12 +2064,15 @@ proc prepareFunction(self: Compiler, fn: Name) =
|
||||||
constraints = @[]
|
constraints = @[]
|
||||||
# We now declare and typecheck the function's
|
# We now declare and typecheck the function's
|
||||||
# arguments
|
# arguments
|
||||||
|
let idx = self.stackIndex
|
||||||
|
self.stackIndex = 1
|
||||||
var default: Expression
|
var default: Expression
|
||||||
var i = 0
|
var i = 0
|
||||||
var node = FunDecl(fn.node)
|
var node = FunDecl(fn.node)
|
||||||
for argument in node.arguments:
|
for argument in node.arguments:
|
||||||
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")
|
||||||
|
inc(self.stackIndex)
|
||||||
self.names.add(Name(depth: fn.depth + 1,
|
self.names.add(Name(depth: fn.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
owner: fn.owner,
|
owner: fn.owner,
|
||||||
|
@ -2137,7 +2085,8 @@ proc prepareFunction(self: Compiler, fn: Name) =
|
||||||
line: argument.name.token.line,
|
line: argument.name.token.line,
|
||||||
belongsTo: fn,
|
belongsTo: fn,
|
||||||
kind: NameKind.Argument,
|
kind: NameKind.Argument,
|
||||||
node: argument.name
|
node: argument.name,
|
||||||
|
position: self.stackIndex
|
||||||
))
|
))
|
||||||
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
||||||
# There's a default argument!
|
# There's a default argument!
|
||||||
|
@ -2149,6 +2098,8 @@ proc prepareFunction(self: Compiler, fn: Name) =
|
||||||
# The function needs a return type too!
|
# The function needs a return type too!
|
||||||
if not FunDecl(fn.node).returnType.isNil():
|
if not FunDecl(fn.node).returnType.isNil():
|
||||||
fn.valueType.returnType = self.inferOrError(FunDecl(fn.node).returnType)
|
fn.valueType.returnType = self.inferOrError(FunDecl(fn.node).returnType)
|
||||||
|
fn.position = self.stackIndex
|
||||||
|
self.stackIndex = idx
|
||||||
|
|
||||||
|
|
||||||
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
|
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
|
||||||
|
@ -2176,13 +2127,8 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
|
||||||
# Creates a new call frame and jumps
|
# Creates a new call frame and jumps
|
||||||
# to the function's first instruction
|
# to the function's first instruction
|
||||||
# in the code
|
# in the code
|
||||||
if not fn.valueType.isClosure:
|
self.emitByte(Call, line)
|
||||||
self.emitByte(Call, line)
|
|
||||||
else:
|
|
||||||
self.emitByte(CallClosure, line)
|
|
||||||
self.emitBytes(args.len().toTriple(), line)
|
self.emitBytes(args.len().toTriple(), line)
|
||||||
if fn.valueType.isClosure:
|
|
||||||
self.emitBytes(fn.valueType.envLen.toTriple(), line)
|
|
||||||
self.patchReturnAddress(pos)
|
self.patchReturnAddress(pos)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2224,7 +2170,7 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
kind = self.infer(argument) # We don't use inferOrError so that we can raise a more appropriate error message
|
kind = self.infer(argument) # We don't use inferOrError so that we can raise a more appropriate error message
|
||||||
if kind.isNil():
|
if kind.isNil():
|
||||||
if argument.kind == NodeKind.identExpr:
|
if argument.kind == NodeKind.identExpr:
|
||||||
self.error(&"reference to undeclared name '{argument.token.lexeme}'", argument)
|
self.error(&"reference to undefined name '{argument.token.lexeme}'", argument)
|
||||||
self.error(&"positional argument {i + 1} in function call has no type", argument)
|
self.error(&"positional argument {i + 1} in function call has no type", argument)
|
||||||
args.add(("", kind, default))
|
args.add(("", kind, default))
|
||||||
argExpr.add(argument)
|
argExpr.add(argument)
|
||||||
|
@ -2232,12 +2178,12 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
kind = self.infer(argument.value)
|
kind = self.infer(argument.value)
|
||||||
if kind.isNil():
|
if kind.isNil():
|
||||||
if argument.value.kind == NodeKind.identExpr:
|
if argument.value.kind == NodeKind.identExpr:
|
||||||
self.error(&"reference to undeclared name '{argument.value.token.lexeme}'", argument.value)
|
self.error(&"reference to undefined name '{argument.value.token.lexeme}'", argument.value)
|
||||||
self.error(&"keyword argument '{argument.name.token.lexeme}' in function call has no type", argument.value)
|
self.error(&"keyword argument '{argument.name.token.lexeme}' in function call has no type", argument.value)
|
||||||
args.add((argument.name.token.lexeme, kind, default))
|
args.add((argument.name.token.lexeme, kind, default))
|
||||||
argExpr.add(argument.value)
|
argExpr.add(argument.value)
|
||||||
case node.callee.kind:
|
case node.callee.kind:
|
||||||
of identExpr:
|
of NodeKind.identExpr:
|
||||||
# Calls like hi()
|
# Calls like hi()
|
||||||
let impl = self.match(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: All), args: args), node)
|
let impl = self.match(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: All), args: args), node)
|
||||||
result = impl.valueType
|
result = impl.valueType
|
||||||
|
@ -2261,11 +2207,19 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
# one and work our way to the innermost call
|
# one and work our way to the innermost call
|
||||||
for exp in all:
|
for exp in all:
|
||||||
result = self.call(exp, compile)
|
result = self.call(exp, compile)
|
||||||
#echo result
|
|
||||||
#result = result.returnType
|
|
||||||
if compile and result.kind == Function:
|
if compile and result.kind == Function:
|
||||||
self.generateCall(result, argExpr, node.token.line)
|
self.generateCall(result, argExpr, node.token.line)
|
||||||
result = result.returnType
|
result = result.returnType
|
||||||
|
of NodeKind.getItemExpr:
|
||||||
|
var node = GetItemExpr(node.callee)
|
||||||
|
let impl = self.match(node.name.token.lexeme,
|
||||||
|
self.getItemExpr(node, compile=false, matching=Type(kind: Function, args: args, returnType: Type(kind: All))), node)
|
||||||
|
result = impl.valueType
|
||||||
|
if impl.isGeneric:
|
||||||
|
result = self.specialize(result, argExpr)
|
||||||
|
result = result.returnType
|
||||||
|
if compile:
|
||||||
|
self.generateCall(impl, argExpr, node.token.line)
|
||||||
# TODO: Calling lambdas on-the-fly (i.e. on the same line)
|
# TODO: Calling lambdas on-the-fly (i.e. on the same line)
|
||||||
else:
|
else:
|
||||||
let typ = self.infer(node)
|
let typ = self.infer(node)
|
||||||
|
@ -2275,7 +2229,7 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||||
|
|
||||||
|
|
||||||
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.} =
|
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
||||||
## Compiles accessing to fields of a type or
|
## Compiles accessing to fields of a type or
|
||||||
## module namespace. If the compile flag is set
|
## module namespace. If the compile flag is set
|
||||||
## to false, no code is generated for resolving
|
## to false, no code is generated for resolving
|
||||||
|
@ -2286,12 +2240,22 @@ proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type
|
||||||
let name = self.resolveOrError(IdentExpr(node.obj))
|
let name = self.resolveOrError(IdentExpr(node.obj))
|
||||||
case name.kind:
|
case name.kind:
|
||||||
of NameKind.Module:
|
of NameKind.Module:
|
||||||
let value = self.findInModule(node.name.token.lexeme, name)
|
var values = self.findInModule(node.name.token.lexeme, name)
|
||||||
if len(value) == 0 or self.currentModule notin value[0].exportedTo:
|
if len(values) == 0:
|
||||||
self.error(&"reference to undefined name '{node.name.token.lexeme}' in module '{name.ident.token.lexeme}'")
|
self.error(&"reference to undefined name '{node.name.token.lexeme}' in module '{name.ident.token.lexeme}'")
|
||||||
|
elif len(values) > 1 and matching.isNil():
|
||||||
|
self.error(&"ambiguous reference for '{node.name.token.lexeme}' in module '{name.ident.token.lexeme}'")
|
||||||
|
if not matching.isNil():
|
||||||
|
for name in values:
|
||||||
|
if self.compare(name.valueType, matching):
|
||||||
|
result = name.valueType
|
||||||
|
return
|
||||||
|
if len(values) == 1:
|
||||||
|
result = values[0].valueType
|
||||||
|
else:
|
||||||
|
self.error(&"ambiguous reference for '{node.name.token.lexeme}' in module '{name.ident.token.lexeme}'")
|
||||||
if compile:
|
if compile:
|
||||||
self.identifier(nil, value[0])
|
self.identifier(nil, values[0])
|
||||||
result = value[0].valueType
|
|
||||||
else:
|
else:
|
||||||
self.error("invalid syntax", node.obj)
|
self.error("invalid syntax", node.obj)
|
||||||
else:
|
else:
|
||||||
|
@ -2300,7 +2264,7 @@ proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type
|
||||||
|
|
||||||
proc lambdaExpr(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
|
proc lambdaExpr(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles lambda functions as expressions
|
## Compiles lambda functions as expressions
|
||||||
result = Type(kind: Function, isLambda: true, fun: node)
|
result = Type(kind: Function, isLambda: true, fun: node, location: self.chunk.code.high())
|
||||||
self.beginScope()
|
self.beginScope()
|
||||||
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||||
for gen in node.generics:
|
for gen in node.generics:
|
||||||
|
@ -2345,10 +2309,10 @@ proc lambdaExpr(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.
|
||||||
# The function needs a return type too!
|
# The function needs a return type too!
|
||||||
if not node.returnType.isNil():
|
if not node.returnType.isNil():
|
||||||
result.returnType = self.inferOrError(node.returnType)
|
result.returnType = self.inferOrError(node.returnType)
|
||||||
|
self.endScope()
|
||||||
if not compile:
|
if not compile:
|
||||||
return
|
return
|
||||||
# TODO
|
# TODO
|
||||||
self.endScope()
|
|
||||||
|
|
||||||
|
|
||||||
proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
||||||
|
@ -2365,7 +2329,7 @@ proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.
|
||||||
# would be lost in the call anyway. The differentiation
|
# would be lost in the call anyway. The differentiation
|
||||||
# happens in self.assignment()
|
# happens in self.assignment()
|
||||||
of NodeKind.setItemExpr, NodeKind.assignExpr:
|
of NodeKind.setItemExpr, NodeKind.assignExpr:
|
||||||
return self.assignment(node, compile).valueType
|
return self.assignment(node, compile)
|
||||||
of NodeKind.identExpr:
|
of NodeKind.identExpr:
|
||||||
return self.identifier(IdentExpr(node), compile=compile)
|
return self.identifier(IdentExpr(node), compile=compile)
|
||||||
of NodeKind.unaryExpr:
|
of NodeKind.unaryExpr:
|
||||||
|
@ -2501,42 +2465,6 @@ proc exportStmt(self: Compiler, node: ExportStmt) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc printRepl(self: Compiler, typ: Type, node: Expression) =
|
|
||||||
## Emits instruction to print
|
|
||||||
## peon types in REPL mode
|
|
||||||
case typ.kind:
|
|
||||||
of Int64:
|
|
||||||
self.emitByte(PrintInt64, node.token.line)
|
|
||||||
of UInt64:
|
|
||||||
self.emitByte(PrintUInt64, node.token.line)
|
|
||||||
of Int32:
|
|
||||||
self.emitByte(PrintInt32, node.token.line)
|
|
||||||
of UInt32:
|
|
||||||
self.emitByte(PrintInt32, node.token.line)
|
|
||||||
of Int16:
|
|
||||||
self.emitByte(PrintInt16, node.token.line)
|
|
||||||
of UInt16:
|
|
||||||
self.emitByte(PrintUInt16, node.token.line)
|
|
||||||
of Int8:
|
|
||||||
self.emitByte(PrintInt8, node.token.line)
|
|
||||||
of UInt8:
|
|
||||||
self.emitByte(PrintUInt8, node.token.line)
|
|
||||||
of Float64:
|
|
||||||
self.emitByte(PrintFloat64, node.token.line)
|
|
||||||
of Float32:
|
|
||||||
self.emitByte(PrintFloat32, node.token.line)
|
|
||||||
of Bool:
|
|
||||||
self.emitByte(PrintBool, node.token.line)
|
|
||||||
of Nan:
|
|
||||||
self.emitByte(PrintNan, node.token.line)
|
|
||||||
of Inf:
|
|
||||||
self.emitByte(PrintInf, node.token.line)
|
|
||||||
of String:
|
|
||||||
self.emitByte(PrintString, node.token.line)
|
|
||||||
else:
|
|
||||||
self.emitByte(PrintHex, node.token.line)
|
|
||||||
|
|
||||||
|
|
||||||
proc statement(self: Compiler, node: Statement) =
|
proc statement(self: Compiler, node: Statement) =
|
||||||
## Compiles all statements
|
## Compiles all statements
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
@ -2619,8 +2547,9 @@ proc varDecl(self: Compiler, node: VarDecl) =
|
||||||
self.emitByte(AddVar, node.token.line)
|
self.emitByte(AddVar, node.token.line)
|
||||||
self.declare(node)
|
self.declare(node)
|
||||||
var name = self.names[^1]
|
var name = self.names[^1]
|
||||||
|
inc(self.stackIndex)
|
||||||
|
name.position = self.stackIndex
|
||||||
name.valueType = typ
|
name.valueType = typ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
|
@ -2630,32 +2559,27 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
var node = node
|
var node = node
|
||||||
var jmp: int
|
var jmp: int
|
||||||
# We store the current function
|
# We store the current function
|
||||||
var function = self.currentFunction
|
let function = self.currentFunction
|
||||||
if node.body.isNil():
|
if node.body.isNil():
|
||||||
# We ignore forward declarations
|
# We ignore forward declarations
|
||||||
self.forwarded.add((name, 0))
|
self.forwarded.add((name, 0))
|
||||||
name.valueType.forwarded = true
|
name.valueType.forwarded = true
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
return
|
return
|
||||||
if not function.isNil() and self.depth > 1:
|
|
||||||
# Calling a function immediately compiles it, so if
|
|
||||||
# we didn't check for the scope depth we'd mistakenly
|
|
||||||
# mark functions as being nested when they actually
|
|
||||||
# aren't
|
|
||||||
function.valueType.children.add(name.valueType)
|
|
||||||
name.valueType.parent = function.valueType
|
|
||||||
self.currentFunction = name
|
self.currentFunction = name
|
||||||
if self.currentFunction.isBuiltin:
|
if self.currentFunction.isBuiltin:
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
return
|
return
|
||||||
|
let stackIdx = self.stackIndex
|
||||||
|
self.stackIndex = name.position
|
||||||
# A function's code is just compiled linearly
|
# A function's code is just compiled linearly
|
||||||
# and then jumped over
|
# and then jumped over
|
||||||
jmp = self.emitJump(JumpForwards, node.token.line)
|
jmp = self.emitJump(JumpForwards, node.token.line)
|
||||||
name.codePos = self.chunk.code.len()
|
name.codePos = self.chunk.code.len()
|
||||||
name.valueType.location = name.codePos
|
name.valueType.location = name.codePos
|
||||||
# We let our debugger know this function's boundaries
|
# We let our debugger know this function's boundaries
|
||||||
self.chunk.functions.add(self.chunk.code.high().toTriple())
|
self.chunk.functions.add(self.chunk.code.len().toTriple())
|
||||||
self.functions.add((start: self.chunk.code.high(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name))
|
self.functions.add((start: self.chunk.code.len(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name))
|
||||||
var offset = self.functions[^1]
|
var offset = self.functions[^1]
|
||||||
let idx = self.chunk.functions.len()
|
let idx = self.chunk.functions.len()
|
||||||
self.chunk.functions.add(0.toTriple()) # Patched it later
|
self.chunk.functions.add(0.toTriple()) # Patched it later
|
||||||
|
@ -2728,19 +2652,17 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
# Restores the enclosing function (if any).
|
# Restores the enclosing function (if any).
|
||||||
# Makes nested calls work (including recursion)
|
# Makes nested calls work (including recursion)
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
|
self.stackIndex = stackIdx
|
||||||
|
|
||||||
|
|
||||||
proc declaration(self: Compiler, node: Declaration) =
|
proc declaration(self: Compiler, node: Declaration) =
|
||||||
## Handles all declarations. They are not compiled
|
## Compiles declarations, statements and expressions
|
||||||
## right away, but rather only when they're referenced
|
## recursively
|
||||||
## the first time
|
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
var name = self.declare(node)
|
var name = self.declare(node)
|
||||||
|
self.funDecl(FunDecl(node), name)
|
||||||
if name.isGeneric:
|
if name.isGeneric:
|
||||||
# We typecheck generics immediately
|
|
||||||
name.resolved = true
|
|
||||||
self.funDecl(FunDecl(node), name)
|
|
||||||
# After we're done compiling a generic
|
# After we're done compiling a generic
|
||||||
# function, we pull a magic trick: since,
|
# function, we pull a magic trick: since,
|
||||||
# from here on, the user will be able to
|
# from here on, the user will be able to
|
||||||
|
@ -2757,7 +2679,7 @@ proc declaration(self: Compiler, node: Declaration) =
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
argument.kind.asUnion = true
|
argument.kind.asUnion = true
|
||||||
if not name.valueType.returnType.isNil() and name.valueType.returnType.isNil():
|
if not name.valueType.returnType.isNil() and name.valueType.returnType.kind == Generic:
|
||||||
name.valueType.returnType.asUnion = true
|
name.valueType.returnType.asUnion = true
|
||||||
of NodeKind.typeDecl:
|
of NodeKind.typeDecl:
|
||||||
self.declare(node)
|
self.declare(node)
|
||||||
|
@ -2790,6 +2712,7 @@ proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tu
|
||||||
self.disabledWarnings = disabledWarnings
|
self.disabledWarnings = disabledWarnings
|
||||||
self.showMismatches = showMismatches
|
self.showMismatches = showMismatches
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
self.stackIndex = 1
|
||||||
if not incremental:
|
if not incremental:
|
||||||
self.jumps = @[]
|
self.jumps = @[]
|
||||||
let pos = self.beginProgram()
|
let pos = self.beginProgram()
|
||||||
|
|
|
@ -93,6 +93,7 @@ type
|
||||||
# This is not shown when the node is printed, but makes it a heck of a lot easier to report
|
# This is not shown when the node is printed, but makes it a heck of a lot easier to report
|
||||||
# errors accurately even deep in the compilation pipeline
|
# errors accurately even deep in the compilation pipeline
|
||||||
token*: Token
|
token*: Token
|
||||||
|
file*: string
|
||||||
# This weird inheritance chain is needed for the parser to
|
# This weird inheritance chain is needed for the parser to
|
||||||
# work properly
|
# work properly
|
||||||
Declaration* = ref object of ASTNode
|
Declaration* = ref object of ASTNode
|
||||||
|
|
|
@ -122,6 +122,18 @@ type
|
||||||
LessThan,
|
LessThan,
|
||||||
GreaterOrEqual,
|
GreaterOrEqual,
|
||||||
LessOrEqual,
|
LessOrEqual,
|
||||||
|
SignedGreaterThan,
|
||||||
|
SignedLessThan,
|
||||||
|
SignedGreaterOrEqual,
|
||||||
|
SignedLessOrEqual,
|
||||||
|
Float64GreaterThan,
|
||||||
|
Float64LessThan,
|
||||||
|
Float64GreaterOrEqual,
|
||||||
|
Float64LessOrEqual,
|
||||||
|
Float32GreaterThan,
|
||||||
|
Float32LessThan,
|
||||||
|
Float32GreaterOrEqual,
|
||||||
|
Float32LessOrEqual,
|
||||||
LogicalNot,
|
LogicalNot,
|
||||||
## Print opcodes
|
## Print opcodes
|
||||||
PrintInt64,
|
PrintInt64,
|
||||||
|
@ -148,9 +160,6 @@ type
|
||||||
LoadVar, # Pushes the object at position x in the stack onto the stack
|
LoadVar, # Pushes the object at position x in the stack onto the stack
|
||||||
StoreVar, # Stores the value of b at position a in the stack
|
StoreVar, # Stores the value of b at position a in the stack
|
||||||
AddVar, # An optimization for StoreVar (used when the variable is first declared)
|
AddVar, # An optimization for StoreVar (used when the variable is first declared)
|
||||||
LoadClosure, # Pushes the object position x in the closure array onto the stack
|
|
||||||
StoreClosure, # Stores the value of b at position a in the closure array
|
|
||||||
AddClosure, # This is the same optimization of AddVar, but applied to StoreClosure instead
|
|
||||||
## Looping and jumping
|
## Looping and jumping
|
||||||
Jump, # Absolute, unconditional jump into the bytecode
|
Jump, # Absolute, unconditional jump into the bytecode
|
||||||
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||||
|
@ -161,7 +170,6 @@ type
|
||||||
JumpIfFalseOrPop, # Jumps to an absolute index in the bytecode if x is false and pops otherwise (used for logical and)
|
JumpIfFalseOrPop, # Jumps to an absolute index in the bytecode if x is false and pops otherwise (used for logical and)
|
||||||
## Functions
|
## Functions
|
||||||
Call, # Calls a function and initiates a new stack frame
|
Call, # Calls a function and initiates a new stack frame
|
||||||
CallClosure, # Calls a closure
|
|
||||||
Return, # Terminates the current function
|
Return, # Terminates the current function
|
||||||
SetResult, # Sets the result of the current function
|
SetResult, # Sets the result of the current function
|
||||||
## Exception handling
|
## Exception handling
|
||||||
|
@ -178,7 +186,7 @@ type
|
||||||
PopC, # Pop off the call stack onto the operand stack
|
PopC, # Pop off the call stack onto the operand stack
|
||||||
PushC, # Pop off the operand stack onto the call stack
|
PushC, # Pop off the operand stack onto the call stack
|
||||||
SysClock64, # Pushes the output of a monotonic clock on the stack
|
SysClock64, # Pushes the output of a monotonic clock on the stack
|
||||||
LoadTos # Pushes the top of the call stack onto the operand stack
|
LoadTOS # Pushes the top of the call stack onto the operand stack
|
||||||
|
|
||||||
|
|
||||||
# We group instructions by their operation/operand types for easier handling when debugging
|
# We group instructions by their operation/operand types for easier handling when debugging
|
||||||
|
@ -244,8 +252,19 @@ const simpleInstructions* = {Return, LoadNil,
|
||||||
PrintString,
|
PrintString,
|
||||||
LogicalNot,
|
LogicalNot,
|
||||||
AddVar,
|
AddVar,
|
||||||
AddClosure,
|
LoadTOS,
|
||||||
LoadTos
|
SignedGreaterThan,
|
||||||
|
SignedLessThan,
|
||||||
|
SignedGreaterOrEqual,
|
||||||
|
SignedLessOrEqual,
|
||||||
|
Float64GreaterThan,
|
||||||
|
Float64LessThan,
|
||||||
|
Float64GreaterOrEqual,
|
||||||
|
Float64LessOrEqual,
|
||||||
|
Float32GreaterThan,
|
||||||
|
Float32LessThan,
|
||||||
|
Float32GreaterOrEqual,
|
||||||
|
Float32LessOrEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Constant instructions are instructions that operate on the bytecode constant table
|
# Constant instructions are instructions that operate on the bytecode constant table
|
||||||
|
@ -258,7 +277,7 @@ const constantInstructions* = {LoadInt64, LoadUInt64,
|
||||||
|
|
||||||
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||||
# of 24 bit integers
|
# of 24 bit integers
|
||||||
const stackTripleInstructions* = {StoreVar, LoadVar, LoadCLosure, }
|
const stackTripleInstructions* = {StoreVar, LoadVar, }
|
||||||
|
|
||||||
# Stack double instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
# Stack double instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||||
# of 16 bit integers
|
# of 16 bit integers
|
||||||
|
|
|
@ -38,7 +38,7 @@ type
|
||||||
Yield, Defer, Try, Except,
|
Yield, Defer, Try, Except,
|
||||||
Finally, Type, Operator, Case,
|
Finally, Type, Operator, Case,
|
||||||
Enum, From, Ptr, Ref, Object,
|
Enum, From, Ptr, Ref, Object,
|
||||||
Export,
|
Export, Block
|
||||||
|
|
||||||
# Literal types
|
# Literal types
|
||||||
Integer, Float, String, Identifier,
|
Integer, Float, String, Identifier,
|
||||||
|
@ -52,12 +52,12 @@ type
|
||||||
LeftBracket, RightBracket, # []
|
LeftBracket, RightBracket, # []
|
||||||
Dot, Semicolon, Comma, # . ; ,
|
Dot, Semicolon, Comma, # . ; ,
|
||||||
|
|
||||||
# Miscellaneous
|
# Miscellaneous
|
||||||
|
|
||||||
EndOfFile, # Marks the end of the token stream
|
EndOfFile, # Marks the end of the token stream
|
||||||
NoMatch, # Used internally by the symbol table
|
NoMatch, # Used internally by the symbol table
|
||||||
Comment, # Useful for documentation comments, pragmas, etc.
|
Comment, # Useful for documentation comments, pragmas, etc.
|
||||||
Symbol, # A generic symbol
|
Symbol, # A generic symbol
|
||||||
Pragma,
|
Pragma,
|
||||||
|
|
||||||
Token* = ref object
|
Token* = ref object
|
||||||
|
|
|
@ -315,17 +315,23 @@ proc primary(self: Parser): Expression =
|
||||||
case self.peek().kind:
|
case self.peek().kind:
|
||||||
of True:
|
of True:
|
||||||
result = newTrueExpr(self.step())
|
result = newTrueExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of False:
|
of False:
|
||||||
result = newFalseExpr(self.step())
|
result = newFalseExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Float:
|
of Float:
|
||||||
result = newFloatExpr(self.step())
|
result = newFloatExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Integer:
|
of Integer:
|
||||||
result = newIntExpr(self.step())
|
result = newIntExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Identifier:
|
of Identifier:
|
||||||
result = newIdentExpr(self.step(), self.scopeDepth)
|
result = newIdentExpr(self.step(), self.scopeDepth)
|
||||||
|
result.file = self.file
|
||||||
of LeftParen:
|
of LeftParen:
|
||||||
let tok = self.step()
|
let tok = self.step()
|
||||||
result = newGroupingExpr(self.expression(), tok)
|
result = newGroupingExpr(self.expression(), tok)
|
||||||
|
result.file = self.file
|
||||||
self.expect(RightParen, "unterminated parenthesized expression")
|
self.expect(RightParen, "unterminated parenthesized expression")
|
||||||
of Yield:
|
of Yield:
|
||||||
let tok = self.step()
|
let tok = self.step()
|
||||||
|
@ -340,6 +346,7 @@ proc primary(self: Parser): Expression =
|
||||||
else:
|
else:
|
||||||
# Empty yield
|
# Empty yield
|
||||||
result = newYieldExpr(nil, tok)
|
result = newYieldExpr(nil, tok)
|
||||||
|
result.file = self.file
|
||||||
of Await:
|
of Await:
|
||||||
let tok = self.step()
|
let tok = self.step()
|
||||||
if self.currentFunction.isNil():
|
if self.currentFunction.isNil():
|
||||||
|
@ -347,6 +354,7 @@ proc primary(self: Parser): Expression =
|
||||||
if self.currentFunction.token.kind != Coroutine:
|
if self.currentFunction.token.kind != Coroutine:
|
||||||
self.error("'await' can only be used inside coroutines", tok)
|
self.error("'await' can only be used inside coroutines", tok)
|
||||||
result = newAwaitExpr(self.expression(), tok)
|
result = newAwaitExpr(self.expression(), tok)
|
||||||
|
result.file = self.file
|
||||||
of RightParen, RightBracket, RightBrace:
|
of RightParen, RightBracket, RightBrace:
|
||||||
# This is *technically* unnecessary: the parser would
|
# This is *technically* unnecessary: the parser would
|
||||||
# throw an error regardless, but it's a little bit nicer
|
# throw an error regardless, but it's a little bit nicer
|
||||||
|
@ -354,27 +362,36 @@ proc primary(self: Parser): Expression =
|
||||||
self.error(&"unmatched '{self.peek().lexeme}'")
|
self.error(&"unmatched '{self.peek().lexeme}'")
|
||||||
of Hex:
|
of Hex:
|
||||||
result = newHexExpr(self.step())
|
result = newHexExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Octal:
|
of Octal:
|
||||||
result = newOctExpr(self.step())
|
result = newOctExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Binary:
|
of Binary:
|
||||||
result = newBinExpr(self.step())
|
result = newBinExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of String:
|
of String:
|
||||||
result = newStrExpr(self.step())
|
result = newStrExpr(self.step())
|
||||||
|
result.file = self.file
|
||||||
of Function:
|
of Function:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = Expression(self.funDecl(isLambda=true))
|
result = Expression(self.funDecl(isLambda=true))
|
||||||
|
result.file = self.file
|
||||||
of Coroutine:
|
of Coroutine:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = Expression(self.funDecl(isAsync=true, isLambda=true))
|
result = Expression(self.funDecl(isAsync=true, isLambda=true))
|
||||||
|
result.file = self.file
|
||||||
of Generator:
|
of Generator:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = Expression(self.funDecl(isGenerator=true, isLambda=true))
|
result = Expression(self.funDecl(isGenerator=true, isLambda=true))
|
||||||
|
result.file = self.file
|
||||||
of TokenType.Ref:
|
of TokenType.Ref:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = newRefExpr(self.expression(), self.peek(-1))
|
result = newRefExpr(self.expression(), self.peek(-1))
|
||||||
|
result.file = self.file
|
||||||
of TokenType.Ptr:
|
of TokenType.Ptr:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = newPtrExpr(self.expression(), self.peek(-1))
|
result = newPtrExpr(self.expression(), self.peek(-1))
|
||||||
|
result.file = self.file
|
||||||
else:
|
else:
|
||||||
self.error("invalid syntax")
|
self.error("invalid syntax")
|
||||||
|
|
||||||
|
@ -409,6 +426,7 @@ proc makeCall(self: Parser, callee: Expression): CallExpr =
|
||||||
argCount += 1
|
argCount += 1
|
||||||
self.expect(RightParen)
|
self.expect(RightParen)
|
||||||
result = newCallExpr(callee, arguments, tok)
|
result = newCallExpr(callee, arguments, tok)
|
||||||
|
result.file = self.file
|
||||||
result.closeParen = self.peek(-1)
|
result.closeParen = self.peek(-1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -428,6 +446,7 @@ proc call(self: Parser): Expression =
|
||||||
elif self.match(Dot):
|
elif self.match(Dot):
|
||||||
self.expect(Identifier, "expecting attribute name after '.'")
|
self.expect(Identifier, "expecting attribute name after '.'")
|
||||||
result = newGetItemExpr(result, newIdentExpr(self.peek(-1), self.scopeDepth), self.peek(-1))
|
result = newGetItemExpr(result, newIdentExpr(self.peek(-1), self.scopeDepth), self.peek(-1))
|
||||||
|
result.file = self.file
|
||||||
elif self.match(LeftBracket):
|
elif self.match(LeftBracket):
|
||||||
self.parseGenericArgs() # TODO
|
self.parseGenericArgs() # TODO
|
||||||
result = self.makeCall(result)
|
result = self.makeCall(result)
|
||||||
|
@ -440,6 +459,7 @@ proc unary(self: Parser): Expression =
|
||||||
## Parses unary expressions
|
## Parses unary expressions
|
||||||
if self.check([Identifier, Symbol]) and self.peek().lexeme in self.operators.tokens:
|
if self.check([Identifier, Symbol]) and self.peek().lexeme in self.operators.tokens:
|
||||||
result = newUnaryExpr(self.step(), self.unary())
|
result = newUnaryExpr(self.step(), self.unary())
|
||||||
|
result.file = self.file
|
||||||
else:
|
else:
|
||||||
result = self.call()
|
result = self.call()
|
||||||
|
|
||||||
|
@ -453,6 +473,7 @@ proc parsePow(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.unary()
|
right = self.unary()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseMul(self: Parser): Expression =
|
proc parseMul(self: Parser): Expression =
|
||||||
|
@ -465,6 +486,7 @@ proc parseMul(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parsePow()
|
right = self.parsePow()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseAdd(self: Parser): Expression =
|
proc parseAdd(self: Parser): Expression =
|
||||||
|
@ -477,6 +499,7 @@ proc parseAdd(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseMul()
|
right = self.parseMul()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseBitwise(self: Parser): Expression =
|
proc parseBitwise(self: Parser): Expression =
|
||||||
|
@ -488,6 +511,7 @@ proc parseBitwise(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseAdd()
|
right = self.parseAdd()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseCmp(self: Parser): Expression =
|
proc parseCmp(self: Parser): Expression =
|
||||||
|
@ -499,6 +523,7 @@ proc parseCmp(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseAdd()
|
right = self.parseAdd()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseAnd(self: Parser): Expression =
|
proc parseAnd(self: Parser): Expression =
|
||||||
|
@ -510,6 +535,7 @@ proc parseAnd(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseCmp()
|
right = self.parseCmp()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseOr(self: Parser): Expression =
|
proc parseOr(self: Parser): Expression =
|
||||||
|
@ -521,6 +547,7 @@ proc parseOr(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseAnd()
|
right = self.parseAnd()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseAssign(self: Parser): Expression =
|
proc parseAssign(self: Parser): Expression =
|
||||||
|
@ -532,8 +559,10 @@ proc parseAssign(self: Parser): Expression =
|
||||||
case result.kind:
|
case result.kind:
|
||||||
of identExpr, sliceExpr:
|
of identExpr, sliceExpr:
|
||||||
result = newAssignExpr(result, value, tok)
|
result = newAssignExpr(result, value, tok)
|
||||||
|
result.file = self.file
|
||||||
of getItemExpr:
|
of getItemExpr:
|
||||||
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
|
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
|
||||||
|
result.file = self.file
|
||||||
else:
|
else:
|
||||||
self.error("invalid assignment target", tok)
|
self.error("invalid assignment target", tok)
|
||||||
|
|
||||||
|
@ -547,6 +576,7 @@ proc parseArrow(self: Parser): Expression =
|
||||||
operator = self.step()
|
operator = self.step()
|
||||||
right = self.parseAssign()
|
right = self.parseAssign()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
## End of operator parsing handlers
|
## End of operator parsing handlers
|
||||||
|
@ -560,6 +590,7 @@ proc assertStmt(self: Parser): Statement =
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing semicolon after 'assert'")
|
endOfLine("missing semicolon after 'assert'")
|
||||||
result = newAssertStmt(expression, tok)
|
result = newAssertStmt(expression, tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc beginScope(self: Parser) =
|
proc beginScope(self: Parser) =
|
||||||
|
@ -585,6 +616,7 @@ proc blockStmt(self: Parser): Statement =
|
||||||
code.delete(code.high())
|
code.delete(code.high())
|
||||||
self.expect(RightBrace, "expecting '}'")
|
self.expect(RightBrace, "expecting '}'")
|
||||||
result = newBlockStmt(code, tok)
|
result = newBlockStmt(code, tok)
|
||||||
|
result.file = self.file
|
||||||
self.endScope()
|
self.endScope()
|
||||||
|
|
||||||
|
|
||||||
|
@ -595,6 +627,7 @@ proc breakStmt(self: Parser): Statement =
|
||||||
self.error("'break' cannot be used outside loops")
|
self.error("'break' cannot be used outside loops")
|
||||||
endOfLine("missing semicolon after 'break'")
|
endOfLine("missing semicolon after 'break'")
|
||||||
result = newBreakStmt(tok)
|
result = newBreakStmt(tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc deferStmt(self: Parser): Statement =
|
proc deferStmt(self: Parser): Statement =
|
||||||
|
@ -604,6 +637,7 @@ proc deferStmt(self: Parser): Statement =
|
||||||
self.error("'defer' cannot be used outside functions")
|
self.error("'defer' cannot be used outside functions")
|
||||||
endOfLine("missing semicolon after 'defer'")
|
endOfLine("missing semicolon after 'defer'")
|
||||||
result = newDeferStmt(self.expression(), tok)
|
result = newDeferStmt(self.expression(), tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc continueStmt(self: Parser): Statement =
|
proc continueStmt(self: Parser): Statement =
|
||||||
|
@ -613,6 +647,7 @@ proc continueStmt(self: Parser): Statement =
|
||||||
self.error("'continue' cannot be used outside loops")
|
self.error("'continue' cannot be used outside loops")
|
||||||
endOfLine("missing semicolon after 'continue'")
|
endOfLine("missing semicolon after 'continue'")
|
||||||
result = newContinueStmt(tok)
|
result = newContinueStmt(tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc returnStmt(self: Parser): Statement =
|
proc returnStmt(self: Parser): Statement =
|
||||||
|
@ -628,6 +663,7 @@ proc returnStmt(self: Parser): Statement =
|
||||||
value = self.expression()
|
value = self.expression()
|
||||||
endOfLine("missing semicolon after 'return'")
|
endOfLine("missing semicolon after 'return'")
|
||||||
result = newReturnStmt(value, tok)
|
result = newReturnStmt(value, tok)
|
||||||
|
result.file = self.file
|
||||||
case self.currentFunction.kind:
|
case self.currentFunction.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
FunDecl(self.currentFunction).hasExplicitReturn = true
|
FunDecl(self.currentFunction).hasExplicitReturn = true
|
||||||
|
@ -646,6 +682,7 @@ proc yieldStmt(self: Parser): Statement =
|
||||||
result = newYieldStmt(self.expression(), tok)
|
result = newYieldStmt(self.expression(), tok)
|
||||||
else:
|
else:
|
||||||
result = newYieldStmt(nil, tok)
|
result = newYieldStmt(nil, tok)
|
||||||
|
result.file = self.file
|
||||||
endOfLine("missing semicolon after 'yield'")
|
endOfLine("missing semicolon after 'yield'")
|
||||||
|
|
||||||
|
|
||||||
|
@ -658,6 +695,7 @@ proc awaitStmt(self: Parser): Statement =
|
||||||
self.error("'await' can only be used inside coroutines")
|
self.error("'await' can only be used inside coroutines")
|
||||||
endOfLine("missing semicolon after 'await'")
|
endOfLine("missing semicolon after 'await'")
|
||||||
result = newAwaitStmt(self.expression(), tok)
|
result = newAwaitStmt(self.expression(), tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc raiseStmt(self: Parser): Statement =
|
proc raiseStmt(self: Parser): Statement =
|
||||||
|
@ -670,6 +708,7 @@ proc raiseStmt(self: Parser): Statement =
|
||||||
exception = self.expression()
|
exception = self.expression()
|
||||||
endOfLine("missing semicolon after 'raise'")
|
endOfLine("missing semicolon after 'raise'")
|
||||||
result = newRaiseStmt(exception, tok)
|
result = newRaiseStmt(exception, tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc forEachStmt(self: Parser): Statement =
|
proc forEachStmt(self: Parser): Statement =
|
||||||
|
@ -683,6 +722,7 @@ proc forEachStmt(self: Parser): Statement =
|
||||||
let expression = self.expression()
|
let expression = self.expression()
|
||||||
self.expect(LeftBrace)
|
self.expect(LeftBrace)
|
||||||
result = newForEachStmt(identifier, expression, self.blockStmt(), tok)
|
result = newForEachStmt(identifier, expression, self.blockStmt(), tok)
|
||||||
|
result.file = self.file
|
||||||
self.currentLoop = enclosingLoop
|
self.currentLoop = enclosingLoop
|
||||||
|
|
||||||
|
|
||||||
|
@ -719,6 +759,7 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement =
|
||||||
pos: (tok.pos.stop + 1, (tok.pos.stop + 1) + len(moduleName)),
|
pos: (tok.pos.stop + 1, (tok.pos.stop + 1) + len(moduleName)),
|
||||||
relPos: (tok.relPos.stop + 1, (tok.relPos.stop + 1) + len(moduleName))),
|
relPos: (tok.relPos.stop + 1, (tok.relPos.stop + 1) + len(moduleName))),
|
||||||
self.scopeDepth), tok)
|
self.scopeDepth), tok)
|
||||||
|
result.file = self.file
|
||||||
moduleName &= ".pn"
|
moduleName &= ".pn"
|
||||||
var lexer = newLexer()
|
var lexer = newLexer()
|
||||||
lexer.fillSymbolTable()
|
lexer.fillSymbolTable()
|
||||||
|
@ -778,6 +819,7 @@ proc tryStmt(self: Parser): Statement =
|
||||||
if handler.exc.isNil() and i != handlers.high():
|
if handler.exc.isNil() and i != handlers.high():
|
||||||
self.error("catch-all exception handler with bare 'except' must come last in try statement", handler.exc.token)
|
self.error("catch-all exception handler with bare 'except' must come last in try statement", handler.exc.token)
|
||||||
result = newTryStmt(body, handlers, finallyClause, elseClause, tok)
|
result = newTryStmt(body, handlers, finallyClause, elseClause, tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc whileStmt(self: Parser): Statement =
|
proc whileStmt(self: Parser): Statement =
|
||||||
|
@ -789,6 +831,7 @@ proc whileStmt(self: Parser): Statement =
|
||||||
self.expect(LeftBrace)
|
self.expect(LeftBrace)
|
||||||
self.currentLoop = Loop
|
self.currentLoop = Loop
|
||||||
result = newWhileStmt(condition, self.blockStmt(), tok)
|
result = newWhileStmt(condition, self.blockStmt(), tok)
|
||||||
|
result.file = self.file
|
||||||
self.currentLoop = enclosingLoop
|
self.currentLoop = enclosingLoop
|
||||||
self.endScope()
|
self.endScope()
|
||||||
|
|
||||||
|
@ -807,6 +850,7 @@ proc ifStmt(self: Parser): Statement =
|
||||||
self.expect(LeftBrace, "expecting 'if' or block statement")
|
self.expect(LeftBrace, "expecting 'if' or block statement")
|
||||||
elseBranch = self.blockStmt()
|
elseBranch = self.blockStmt()
|
||||||
result = newIfStmt(condition, thenBranch, elseBranch, tok)
|
result = newIfStmt(condition, thenBranch, elseBranch, tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc exportStmt(self: Parser): Statement =
|
proc exportStmt(self: Parser): Statement =
|
||||||
|
@ -818,6 +862,7 @@ proc exportStmt(self: Parser): Statement =
|
||||||
exported = newIdentExpr(self.peek(-1))
|
exported = newIdentExpr(self.peek(-1))
|
||||||
endOfLine("missing semicolon after 'raise'")
|
endOfLine("missing semicolon after 'raise'")
|
||||||
result = newExportStmt(exported, tok)
|
result = newExportStmt(exported, tok)
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
template checkDecl(self: Parser, isPrivate: bool) =
|
template checkDecl(self: Parser, isPrivate: bool) =
|
||||||
|
@ -841,6 +886,7 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
||||||
self.error("duplicate pragmas are not allowed")
|
self.error("duplicate pragmas are not allowed")
|
||||||
names.add(self.peek(-1).lexeme)
|
names.add(self.peek(-1).lexeme)
|
||||||
name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||||
|
name.file = self.file
|
||||||
if not self.match(":"):
|
if not self.match(":"):
|
||||||
if self.match("]"):
|
if self.match("]"):
|
||||||
result.add(newPragma(name, @[]))
|
result.add(newPragma(name, @[]))
|
||||||
|
@ -862,6 +908,7 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
||||||
self.error("pragma arguments can only be literals", exp.token)
|
self.error("pragma arguments can only be literals", exp.token)
|
||||||
args.add(LiteralExpr(exp))
|
args.add(LiteralExpr(exp))
|
||||||
result.add(newPragma(name, args))
|
result.add(newPragma(name, args))
|
||||||
|
result[^1].file = self.file
|
||||||
if self.match(","):
|
if self.match(","):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -913,6 +960,7 @@ proc varDecl(self: Parser, isLet: bool = false,
|
||||||
if not hasInit and VarDecl(result).valueType.isNil():
|
if not hasInit and VarDecl(result).valueType.isNil():
|
||||||
self.error("expecting initializer or explicit type annotation, but neither was found", result.token)
|
self.error("expecting initializer or explicit type annotation, but neither was found", result.token)
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression]],
|
proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression]],
|
||||||
|
@ -924,6 +972,7 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr,
|
||||||
self.error("cannot have more than 255 arguments in function declaration", self.peek(-1))
|
self.error("cannot have more than 255 arguments in function declaration", self.peek(-1))
|
||||||
self.expect(Identifier, "expecting parameter name")
|
self.expect(Identifier, "expecting parameter name")
|
||||||
parameter.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
parameter.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||||
|
parameter.name.file = self.file
|
||||||
if self.match(":"):
|
if self.match(":"):
|
||||||
parameter.valueType = self.expression()
|
parameter.valueType = self.expression()
|
||||||
for i in countdown(arguments.high(), 0):
|
for i in countdown(arguments.high(), 0):
|
||||||
|
@ -966,6 +1015,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
|
||||||
result.returnType = self.expression()
|
result.returnType = self.expression()
|
||||||
result.arguments = arguments
|
result.arguments = arguments
|
||||||
result.defaults = defaults
|
result.defaults = defaults
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc parseGenericConstraint(self: Parser): Expression =
|
proc parseGenericConstraint(self: Parser): Expression =
|
||||||
|
@ -976,8 +1026,10 @@ proc parseGenericConstraint(self: Parser): Expression =
|
||||||
case self.peek().lexeme:
|
case self.peek().lexeme:
|
||||||
of "|":
|
of "|":
|
||||||
result = newBinaryExpr(result, self.step(), self.parseGenericConstraint())
|
result = newBinaryExpr(result, self.step(), self.parseGenericConstraint())
|
||||||
|
result.file = self.file
|
||||||
of "~":
|
of "~":
|
||||||
result = newUnaryExpr(self.step(), result)
|
result = newUnaryExpr(self.step(), result)
|
||||||
|
result.file = self.file
|
||||||
of ",":
|
of ",":
|
||||||
discard # Comma is handled in parseGenerics()
|
discard # Comma is handled in parseGenerics()
|
||||||
else:
|
else:
|
||||||
|
@ -990,6 +1042,7 @@ proc parseGenerics(self: Parser, decl: Declaration) =
|
||||||
while not self.check(RightBracket) and not self.done():
|
while not self.check(RightBracket) and not self.done():
|
||||||
self.expect(Identifier, "expecting generic type name")
|
self.expect(Identifier, "expecting generic type name")
|
||||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||||
|
gen.name.file = self.file
|
||||||
self.expect(":", "expecting type constraint after generic name")
|
self.expect(":", "expecting type constraint after generic name")
|
||||||
gen.cond = self.parseGenericConstraint()
|
gen.cond = self.parseGenericConstraint()
|
||||||
decl.generics.add(gen)
|
decl.generics.add(gen)
|
||||||
|
@ -1101,11 +1154,13 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
||||||
self.error(&"missing type declaration for '{argument.name.token.lexeme}' in function declaration")
|
self.error(&"missing type declaration for '{argument.name.token.lexeme}' in function declaration")
|
||||||
self.currentFunction = enclosingFunction
|
self.currentFunction = enclosingFunction
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc expression(self: Parser): Expression =
|
proc expression(self: Parser): Expression =
|
||||||
## Parses expressions
|
## Parses expressions
|
||||||
result = self.parseArrow() # Highest-level expression
|
result = self.parseArrow() # Highest-level expression
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc expressionStatement(self: Parser): Statement =
|
proc expressionStatement(self: Parser): Statement =
|
||||||
|
@ -1114,6 +1169,7 @@ proc expressionStatement(self: Parser): Statement =
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing semicolon at end of expression", expression.token)
|
endOfLine("missing semicolon at end of expression", expression.token)
|
||||||
result = Statement(newExprStmt(expression, expression.token))
|
result = Statement(newExprStmt(expression, expression.token))
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc statement(self: Parser): Statement =
|
proc statement(self: Parser): Statement =
|
||||||
|
@ -1171,6 +1227,7 @@ proc statement(self: Parser): Statement =
|
||||||
result = self.tryStmt()
|
result = self.tryStmt()
|
||||||
else:
|
else:
|
||||||
result = self.expressionStatement()
|
result = self.expressionStatement()
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc typeDecl(self: Parser): TypeDecl =
|
proc typeDecl(self: Parser): TypeDecl =
|
||||||
|
@ -1241,6 +1298,7 @@ proc typeDecl(self: Parser): TypeDecl =
|
||||||
if not self.check(RightBrace):
|
if not self.check(RightBrace):
|
||||||
self.expect(",", "expecting comma after enum field declaration")
|
self.expect(",", "expecting comma after enum field declaration")
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
|
result.file = self.file
|
||||||
|
|
||||||
|
|
||||||
proc declaration(self: Parser): Declaration =
|
proc declaration(self: Parser): Declaration =
|
||||||
|
|
12
src/main.nim
12
src/main.nim
|
@ -154,7 +154,7 @@ proc repl =
|
||||||
|
|
||||||
|
|
||||||
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], dis: bool = false,
|
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], dis: bool = false,
|
||||||
warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug) =
|
warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, run: bool = true) =
|
||||||
var
|
var
|
||||||
tokens: seq[Token] = @[]
|
tokens: seq[Token] = @[]
|
||||||
tree: seq[Declaration] = @[]
|
tree: seq[Declaration] = @[]
|
||||||
|
@ -237,7 +237,8 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
|
||||||
styledEcho fgGreen, "OK"
|
styledEcho fgGreen, "OK"
|
||||||
else:
|
else:
|
||||||
styledEcho fgRed, "Corrupted"
|
styledEcho fgRed, "Corrupted"
|
||||||
vm.run(serialized.chunk, breakpoints)
|
if run:
|
||||||
|
vm.run(serialized.chunk, breakpoints)
|
||||||
except LexingError:
|
except LexingError:
|
||||||
print(LexingError(getCurrentException()))
|
print(LexingError(getCurrentException()))
|
||||||
except ParseError:
|
except ParseError:
|
||||||
|
@ -264,6 +265,7 @@ when isMainModule:
|
||||||
var dis: bool = false
|
var dis: bool = false
|
||||||
var mismatches: bool = false
|
var mismatches: bool = false
|
||||||
var mode: CompileMode = CompileMode.Debug
|
var mode: CompileMode = CompileMode.Debug
|
||||||
|
var run: bool = true
|
||||||
for kind, key, value in optParser.getopt():
|
for kind, key, value in optParser.getopt():
|
||||||
case kind:
|
case kind:
|
||||||
of cmdArgument:
|
of cmdArgument:
|
||||||
|
@ -323,6 +325,8 @@ when isMainModule:
|
||||||
quit()
|
quit()
|
||||||
of "disassemble":
|
of "disassemble":
|
||||||
dis = true
|
dis = true
|
||||||
|
of "compile":
|
||||||
|
run = false
|
||||||
else:
|
else:
|
||||||
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, &"error: unkown option '{key}'")
|
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, &"error: unkown option '{key}'")
|
||||||
quit()
|
quit()
|
||||||
|
@ -349,6 +353,8 @@ when isMainModule:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, &"error: invalid breakpoint value '{point}'")
|
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, &"error: invalid breakpoint value '{point}'")
|
||||||
quit()
|
quit()
|
||||||
|
of "c":
|
||||||
|
run = false
|
||||||
of "d":
|
of "d":
|
||||||
dis = true
|
dis = true
|
||||||
else:
|
else:
|
||||||
|
@ -360,7 +366,7 @@ when isMainModule:
|
||||||
if file == "":
|
if file == "":
|
||||||
repl()
|
repl()
|
||||||
else:
|
else:
|
||||||
runFile(file, fromString, dump, breaks, dis, warnings, mismatches, mode)
|
runFile(file, fromString, dump, breaks, dis, warnings, mismatches, mode, run)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
import values;
|
import values;
|
||||||
|
|
||||||
|
|
||||||
operator `>`*[T: Number](a, b: T): bool {
|
operator `>`*[T: UnsignedInteger](a, b: T): bool {
|
||||||
#pragma[magic: "GreaterThan", pure]
|
#pragma[magic: "GreaterThan", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operator `<`*[T: Number](a, b: T): bool {
|
operator `<`*[T: UnsignedInteger](a, b: T): bool {
|
||||||
#pragma[magic: "LessThan", pure]
|
#pragma[magic: "LessThan", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,72 @@ operator `!=`*[T: Number](a, b: T): bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operator `>=`*[T: Number](a, b: T): bool {
|
operator `>=`*[T: UnsignedInteger](a, b: T): bool {
|
||||||
#pragma[magic: "GreaterOrEqual", pure]
|
#pragma[magic: "GreaterOrEqual", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operator `<=`*[T: Number](a, b: T): bool {
|
operator `<=`*[T: UnsignedInteger](a, b: T): bool {
|
||||||
#pragma[magic: "LessOrEqual", pure]
|
#pragma[magic: "LessOrEqual", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>`*[T: SignedInteger](a, b: T): bool {
|
||||||
|
#pragma[magic: "SignedGreaterThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<`*[T: SignedInteger](a, b: T): bool {
|
||||||
|
#pragma[magic: "SignedLessThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>=`*[T: SignedInteger](a, b: T): bool {
|
||||||
|
#pragma[magic: "SignedGreaterOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<=`*[T: SignedInteger](a, b: T): bool {
|
||||||
|
#pragma[magic: "SignedLessOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>`*(a, b: float64): bool {
|
||||||
|
#pragma[magic: "Float64GreaterThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<`*(a, b: float64): bool {
|
||||||
|
#pragma[magic: "Float64LessThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>=`*(a, b: float64): bool {
|
||||||
|
#pragma[magic: "Float64GreaterOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<=`*(a, b: float64): bool {
|
||||||
|
#pragma[magic: "Float64LessOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>`*(a, b: float32): bool {
|
||||||
|
#pragma[magic: "Float32GreaterThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<`*(a, b: float32): bool {
|
||||||
|
#pragma[magic: "Float32LessThan", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `>=`*(a, b: float32): bool {
|
||||||
|
#pragma[magic: "Float32GreaterOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
operator `<=`*(a, b: float32): bool {
|
||||||
|
#pragma[magic: "Float32LessOrEqual", pure]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
import values;
|
import values;
|
||||||
|
|
||||||
|
|
||||||
# Some useful builtins
|
|
||||||
|
|
||||||
fn clock*: float {
|
fn clock*: float {
|
||||||
#pragma[magic: "SysClock64"]
|
#pragma[magic: "SysClock64"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,31 @@ const e* = 2.718281828459045;
|
||||||
const tau* = 6.283185307179586;
|
const tau* = 6.283185307179586;
|
||||||
|
|
||||||
|
|
||||||
fn abs*[T: int64 | int32 | int16 | int8](n: T): T {
|
fn abs*[T: SignedInteger](n: T): T {
|
||||||
## Returns the absolute value of the given number
|
## Returns the absolute value of
|
||||||
|
## the given number
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return -n;
|
return -n;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn abs*(f: float64): float64 {
|
||||||
|
## Returns the absolute value of
|
||||||
|
## the given number
|
||||||
|
if f < 0.0 {
|
||||||
|
return -f;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn abs*(f: float32): float32 {
|
||||||
|
## Returns the absolute value of
|
||||||
|
## the given number
|
||||||
|
if f < 0.0'f32 {
|
||||||
|
return -f;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ export logical;
|
||||||
export misc;
|
export misc;
|
||||||
export comparisons;
|
export comparisons;
|
||||||
|
|
||||||
|
|
||||||
var version* = 1;
|
var version* = 1;
|
||||||
var _private = 5; # Invisible outside the module (underscore is to silence warning)
|
var _private = 5; # Invisible outside the module (underscore is to silence warning)
|
||||||
var test* = 0x60;
|
var test* = 0x60;
|
|
@ -132,15 +132,6 @@ proc argumentTripleInstruction(self: Debugger, instruction: OpCode) {.used.} =
|
||||||
self.current += 4
|
self.current += 4
|
||||||
|
|
||||||
|
|
||||||
proc storeClosureInstruction(self: Debugger, instruction: OpCode) =
|
|
||||||
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
|
|
||||||
var idx = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
|
||||||
var idx2 = [self.chunk.code[self.current + 4], self.chunk.code[self.current + 5], self.chunk.code[self.current + 6]].fromTriple()
|
|
||||||
printInstruction(instruction)
|
|
||||||
stdout.styledWriteLine(fgGreen, ", stores element at position ", fgYellow, $idx, fgGreen, " into position ", fgYellow, $idx2)
|
|
||||||
self.current += 7
|
|
||||||
|
|
||||||
|
|
||||||
proc callInstruction(self: Debugger, instruction: OpCode) =
|
proc callInstruction(self: Debugger, instruction: OpCode) =
|
||||||
## Debugs function calls
|
## Debugs function calls
|
||||||
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
||||||
|
@ -150,17 +141,6 @@ proc callInstruction(self: Debugger, instruction: OpCode) =
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc callClosureInstruction(self: Debugger, instruction: OpCode) =
|
|
||||||
## Debugs closure calls
|
|
||||||
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
|
||||||
self.current += 3
|
|
||||||
var envSize = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
|
||||||
self.current += 3
|
|
||||||
printInstruction(instruction)
|
|
||||||
styledEcho fgGreen, &", creates frame of size ", fgYellow, $(size + 2), fgGreen, " with environment of size ", fgYellow, $envSize, fgGreen
|
|
||||||
self.current += 1
|
|
||||||
|
|
||||||
|
|
||||||
proc constantInstruction(self: Debugger, instruction: OpCode) =
|
proc constantInstruction(self: Debugger, instruction: OpCode) =
|
||||||
## Debugs instructions that operate on the constant table
|
## Debugs instructions that operate on the constant table
|
||||||
var size: uint
|
var size: uint
|
||||||
|
@ -190,7 +170,7 @@ proc jumpInstruction(self: Debugger, instruction: OpCode) =
|
||||||
inc(self.current)
|
inc(self.current)
|
||||||
for i in countup(orig, self.current + 1):
|
for i in countup(orig, self.current + 1):
|
||||||
self.checkFunctionStart(i)
|
self.checkFunctionStart(i)
|
||||||
|
|
||||||
|
|
||||||
proc disassembleInstruction*(self: Debugger) =
|
proc disassembleInstruction*(self: Debugger) =
|
||||||
## Takes one bytecode instruction and prints it
|
## Takes one bytecode instruction and prints it
|
||||||
|
@ -210,18 +190,14 @@ proc disassembleInstruction*(self: Debugger) =
|
||||||
self.stackTripleInstruction(opcode)
|
self.stackTripleInstruction(opcode)
|
||||||
of argumentDoubleInstructions:
|
of argumentDoubleInstructions:
|
||||||
self.argumentDoubleInstruction(opcode)
|
self.argumentDoubleInstruction(opcode)
|
||||||
of StoreClosure:
|
|
||||||
self.storeClosureInstruction(opcode)
|
|
||||||
of Call:
|
of Call:
|
||||||
self.callInstruction(opcode)
|
self.callInstruction(opcode)
|
||||||
of CallClosure:
|
|
||||||
self.callClosureInstruction(opcode)
|
|
||||||
of jumpInstructions:
|
of jumpInstructions:
|
||||||
self.jumpInstruction(opcode)
|
self.jumpInstruction(opcode)
|
||||||
else:
|
else:
|
||||||
echo &"DEBUG - Unknown opcode {opcode} at index {self.current}"
|
echo &"DEBUG - Unknown opcode {opcode} at index {self.current}"
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc parseFunctions(self: Debugger) =
|
proc parseFunctions(self: Debugger) =
|
||||||
## Parses function information in the chunk
|
## Parses function information in the chunk
|
||||||
|
|
|
@ -44,8 +44,8 @@ proc print*(exc: CompileError) =
|
||||||
var file = exc.file
|
var file = exc.file
|
||||||
if file notin ["<string>", ""]:
|
if file notin ["<string>", ""]:
|
||||||
file = relativePath(exc.file, getCurrentDir())
|
file = relativePath(exc.file, getCurrentDir())
|
||||||
printError(file, exc.compiler.getSource().splitLines()[exc.line - 1].strip(chars={'\n'}),
|
printError(file, readFile(file).splitLines()[exc.line - 1].strip(chars={'\n'}),
|
||||||
exc.line, exc.node.getRelativeBoundaries(), exc.compiler.getCurrentFunction(),
|
exc.line, exc.node.getRelativeBoundaries(), exc.function,
|
||||||
exc.msg)
|
exc.msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import std;
|
||||||
|
import math;
|
||||||
|
|
||||||
|
|
||||||
|
print(math.abs(-math.pi) == math.pi);
|
||||||
|
print(math.abs(5) == 5);
|
||||||
|
print(math.abs(-1) == 1);
|
|
@ -1,16 +0,0 @@
|
||||||
# Tests closures
|
|
||||||
import std;
|
|
||||||
|
|
||||||
|
|
||||||
fn makeClosure(x: int): fn: int {
|
|
||||||
fn inner: int {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
return inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
print(makeClosure(38)() == 38); # true;
|
|
||||||
var closure = makeClosure(42);
|
|
||||||
print(closure);
|
|
||||||
#closure(); # TODO: Fix
|
|
|
@ -1,12 +0,0 @@
|
||||||
import std;
|
|
||||||
|
|
||||||
|
|
||||||
fn makeAdder(x: int): fn (n: int): int {
|
|
||||||
fn adder(n: int): int {
|
|
||||||
return x + n;
|
|
||||||
}
|
|
||||||
return adder;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
print(makeAdder(5)(2) == 7); # true
|
|
|
@ -1,10 +0,0 @@
|
||||||
# Tests operator dispatching
|
|
||||||
|
|
||||||
|
|
||||||
operator `+`(a: int): int {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
+1; # Works: defined for int64
|
|
||||||
# +1'i32; # Will not work
|
|
|
@ -5,7 +5,7 @@ import std;
|
||||||
fn foo: int;
|
fn foo: int;
|
||||||
|
|
||||||
|
|
||||||
print(foo());
|
print(foo() == 42);
|
||||||
|
|
||||||
|
|
||||||
fn foo: int {return 42;} # Commenting this line will cause an error
|
fn foo: int {return 42;} # Commenting this line will cause an error
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Another test for generic functions
|
# Another test for generic functions. This should fail to compile
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import std;
|
|
||||||
|
|
||||||
|
|
||||||
var a = 5;
|
|
||||||
fn f {
|
|
||||||
a = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
print(a);
|
|
||||||
f();
|
|
||||||
#print(a);
|
|
|
@ -1,6 +0,0 @@
|
||||||
import std;
|
|
||||||
import math;
|
|
||||||
|
|
||||||
|
|
||||||
print("Testing math module");
|
|
||||||
print(math.abs(-5) == 5);
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Tests var parameters. TODO: They don't actually exist yet, they're just checked statically
|
|
||||||
|
|
||||||
import std;
|
|
||||||
|
|
||||||
|
|
||||||
operator `+=`(a: var int, b: int) {
|
|
||||||
a = a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn plusOne(x: var int): int {
|
|
||||||
x += 1;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var x = 5;
|
|
||||||
print(plusOne(x));
|
|
||||||
# plusOne(38); # If you uncomment this, the compiler errors out!
|
|
|
@ -7,8 +7,8 @@ var x = 5;
|
||||||
var x = 55;
|
var x = 55;
|
||||||
{
|
{
|
||||||
var x = 22;
|
var x = 22;
|
||||||
print(x);
|
print(x == 22);
|
||||||
}
|
}
|
||||||
print(x);
|
print(x == 55);
|
||||||
}
|
}
|
||||||
print(x);
|
print(x == 5);
|
||||||
|
|
Loading…
Reference in New Issue