Broken work with closures and chained calls
This commit is contained in:
parent
85de75a50a
commit
c230142378
|
@ -55,7 +55,7 @@ type
|
||||||
cache: array[6, uint64] # Singletons cache
|
cache: array[6, uint64] # 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 offsets
|
closures: seq[uint64] # Stores closure offsets
|
||||||
closedOver: seq[uint64] # Stores variables that do not have stack semantics
|
envs: seq[uint64] # Stores variables that do not have stack semantics
|
||||||
results: seq[uint64] # Stores function's results (return values)
|
results: seq[uint64] # Stores function's results (return values)
|
||||||
gc: PeonGC
|
gc: PeonGC
|
||||||
ObjectKind* = enum
|
ObjectKind* = enum
|
||||||
|
@ -213,7 +213,7 @@ proc markRoots(self: PeonGC): seq[ptr HeapObject] =
|
||||||
for obj in self.vm.operands:
|
for obj in self.vm.operands:
|
||||||
if obj in self.pointers:
|
if obj in self.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
for obj in self.vm.closedOver:
|
for obj in self.vm.envs:
|
||||||
if obj in self.pointers:
|
if obj in self.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
# We preallocate the space on the seq
|
# We preallocate the space on the seq
|
||||||
|
@ -342,7 +342,7 @@ proc newPeonVM*: PeonVM =
|
||||||
result.calls = @[]
|
result.calls = @[]
|
||||||
result.operands = @[]
|
result.operands = @[]
|
||||||
result.results = @[]
|
result.results = @[]
|
||||||
result.closedOver = @[]
|
result.envs = @[]
|
||||||
result.gc.vm = result
|
result.gc.vm = result
|
||||||
|
|
||||||
|
|
||||||
|
@ -383,66 +383,90 @@ proc `!>=`[T](a, b: T): auto {.inline, used.} =
|
||||||
# that go through the getc/setc wrappers is frame-relative,
|
# that go through the getc/setc wrappers is frame-relative,
|
||||||
# meaning that the index is added to the current stack frame's
|
# meaning that the index is added to the current stack frame's
|
||||||
# bottom to obtain an absolute stack index
|
# bottom to obtain an absolute stack index
|
||||||
proc push(self: PeonVM, obj: uint64) =
|
proc push(self: PeonVM, obj: uint64) =
|
||||||
## Pushes a value object onto the
|
## Pushes a value object onto the
|
||||||
## operand stack
|
## operand stack
|
||||||
self.operands.add(obj)
|
self.operands.add(obj)
|
||||||
|
|
||||||
|
|
||||||
proc pop(self: PeonVM): uint64 =
|
proc pop(self: PeonVM): uint64 =
|
||||||
## Pops a value off the
|
## Pops a value off the operand
|
||||||
## operand stack and
|
## stack and returns it
|
||||||
## returns it
|
|
||||||
return self.operands.pop()
|
return self.operands.pop()
|
||||||
|
|
||||||
|
|
||||||
proc peekb(self: PeonVM, distance: BackwardsIndex = ^1): uint64 =
|
proc peekb(self: PeonVM, distance: BackwardsIndex = ^1): uint64 =
|
||||||
## Returns the value at the
|
## Returns the value at the given (backwards)
|
||||||
## given (backwards) distance from the top of
|
## distance from the top of the operand stack
|
||||||
## the operand stack without consuming it
|
## without consuming it
|
||||||
return self.operands[distance]
|
return self.operands[distance]
|
||||||
|
|
||||||
|
|
||||||
proc peek(self: PeonVM, distance: int = 0): uint64 =
|
proc peek(self: PeonVM, distance: int = 0): uint64 =
|
||||||
## Returns the value at the
|
## Returns the value at the given
|
||||||
## given distance from the top of
|
## distance from the top of the
|
||||||
## the operand stack without consuming it
|
## operand stack without consuming it
|
||||||
if distance < 0:
|
if distance < 0:
|
||||||
return self.peekb(^(-int(distance)))
|
return self.peekb(^(-int(distance)))
|
||||||
return self.operands[self.operands.high() + distance]
|
return self.operands[self.operands.high() + distance]
|
||||||
|
|
||||||
|
|
||||||
proc pushc(self: PeonVM, val: uint64) =
|
proc pushc(self: PeonVM, val: uint64) =
|
||||||
## Pushes a value to the
|
## Pushes a value onto the
|
||||||
## call stack
|
## call stack
|
||||||
self.calls.add(val)
|
self.calls.add(val)
|
||||||
|
|
||||||
|
|
||||||
proc popc(self: PeonVM): uint64 =
|
proc popc(self: PeonVM): uint64 =
|
||||||
## Pops a value off the call
|
## Pops a value off the call
|
||||||
## stack and returns it
|
## stack and returns it
|
||||||
return self.calls.pop()
|
return self.calls.pop()
|
||||||
|
|
||||||
|
|
||||||
proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} =
|
proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} =
|
||||||
## Returns the value at the
|
## Returns the value at the given
|
||||||
## given distance from the top of
|
## distance from the top of the
|
||||||
## the call stack without consuming it
|
## call stack without consuming it
|
||||||
return self.calls[self.calls.high() + distance]
|
return self.calls[self.calls.high() + distance]
|
||||||
|
|
||||||
|
|
||||||
proc getc(self: PeonVM, idx: int): uint64 =
|
proc getc(self: PeonVM, idx: int): uint64 =
|
||||||
## Accessor method that abstracts
|
## Getter method that abstracts
|
||||||
## indexing our call stack through stack
|
## indexing our call stack through
|
||||||
## frames
|
## stack frames
|
||||||
return self.calls[idx.uint64 + self.frames[^1]]
|
return self.calls[idx.uint64 + self.frames[^1]]
|
||||||
|
|
||||||
|
|
||||||
proc setc(self: PeonVM, idx: uint, val: uint64) =
|
proc setc(self: PeonVM, idx: int, val: uint64) =
|
||||||
## Setter method that abstracts
|
## Setter method that abstracts
|
||||||
## indexing our call stack through stack
|
## indexing our call stack through
|
||||||
## frames
|
## stack frames
|
||||||
self.calls[idx + 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: 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: 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
|
||||||
|
@ -613,22 +637,29 @@ when debugVM: # So nim shuts up
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]"
|
||||||
if self.frames.len() !> 0:
|
if self.frames.len() !> 0:
|
||||||
stdout.styledWrite(fgCyan, "Current Frame: ", fgMagenta, "[")
|
stdout.styledWrite(fgCyan, "Current Frame: ", fgMagenta, "[")
|
||||||
for i, e in self.calls[self.frames[^1]..self.calls.high()]:
|
for i, e in self.calls[self.frames[^1]..^1]:
|
||||||
stdout.styledWrite(fgYellow, $e)
|
stdout.styledWrite(fgYellow, $e)
|
||||||
if i < self.calls.high():
|
if i < (self.calls.high() - self.frames[^1].int):
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]", fgCyan
|
||||||
stdout.styledWrite(fgRed, "Live stack frames: ", fgMagenta, "[")
|
stdout.styledWrite(fgRed, "Live stack frames: ", fgMagenta, "[")
|
||||||
for i, e in self.frames:
|
for i, e in self.frames:
|
||||||
stdout.styledWrite(fgYellow, $e)
|
stdout.styledWrite(fgYellow, $e)
|
||||||
if i < self.frames.high():
|
if i < self.frames.high():
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]"
|
||||||
if self.closedOver.len() !> 0:
|
if self.envs.len() !> 0:
|
||||||
stdout.styledWrite(fgGreen, "Closure Array: ", fgMagenta, "[")
|
stdout.styledWrite(fgGreen, "Environments: ", fgMagenta, "[")
|
||||||
for i, e in self.closedOver:
|
for i, e in self.envs:
|
||||||
stdout.styledWrite(fgYellow, $e)
|
stdout.styledWrite(fgYellow, $e)
|
||||||
if i < self.closedOver.high():
|
if i < self.envs.high():
|
||||||
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
|
styledEcho fgMagenta, "]"
|
||||||
|
if self.closures.len() !> 0:
|
||||||
|
stdout.styledWrite(fgGreen, "Environment offsets: ", fgMagenta, "[")
|
||||||
|
for i, e in self.closures:
|
||||||
|
stdout.styledWrite(fgYellow, $e)
|
||||||
|
if i < self.closures.high():
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]"
|
||||||
if self.results.len() !> 0:
|
if self.results.len() !> 0:
|
||||||
|
@ -695,13 +726,6 @@ proc dispatch*(self: PeonVM) =
|
||||||
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
||||||
of LoadFloat64:
|
of LoadFloat64:
|
||||||
self.push(cast[uint64](self.constReadFloat64(int(self.readLong()))))
|
self.push(cast[uint64](self.constReadFloat64(int(self.readLong()))))
|
||||||
of LoadFunction:
|
|
||||||
# Loads a function address onto the operand stack
|
|
||||||
self.push(uint64(self.readLong()))
|
|
||||||
of LoadReturnAddress:
|
|
||||||
# Loads a 32-bit unsigned integer onto the operand stack.
|
|
||||||
# Used to load function return addresses
|
|
||||||
self.push(uint64(self.readUInt()))
|
|
||||||
of Call:
|
of Call:
|
||||||
# Calls a peon function. The calling convention here
|
# Calls a peon function. The calling convention here
|
||||||
# is pretty simple: the first value in the frame is
|
# is pretty simple: the first value in the frame is
|
||||||
|
@ -722,7 +746,32 @@ proc dispatch*(self: PeonVM) =
|
||||||
self.results.add(self.getNil())
|
self.results.add(self.getNil())
|
||||||
# Creates a new call frame
|
# Creates a new call frame
|
||||||
self.frames.add(uint64(self.calls.len() - 2))
|
self.frames.add(uint64(self.calls.len() - 2))
|
||||||
self.closures.add(self.closedOver.len().uint64)
|
# 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 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)
|
||||||
# Loads the arguments onto the stack
|
# Loads the arguments onto the stack
|
||||||
for _ in 0..<argc:
|
for _ in 0..<argc:
|
||||||
self.pushc(self.pop())
|
self.pushc(self.pop())
|
||||||
|
@ -750,7 +799,6 @@ proc dispatch*(self: PeonVM) =
|
||||||
discard self.results.pop()
|
discard self.results.pop()
|
||||||
# Discard the topmost stack frame
|
# Discard the topmost stack frame
|
||||||
discard self.frames.pop()
|
discard self.frames.pop()
|
||||||
discard self.closures.pop()
|
|
||||||
if self.frames.len() == 0:
|
if self.frames.len() == 0:
|
||||||
# End of the program!
|
# End of the program!
|
||||||
return
|
return
|
||||||
|
@ -770,28 +818,20 @@ proc dispatch*(self: PeonVM) =
|
||||||
when debugVM:
|
when debugVM:
|
||||||
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
||||||
if idx + self.frames[^1] <= self.calls.high().uint:
|
if idx + self.frames[^1] <= self.calls.high().uint:
|
||||||
self.setc(idx, self.pop())
|
self.setc(idx.int, self.pop())
|
||||||
else:
|
else:
|
||||||
self.pushc(self.pop())
|
self.pushc(self.pop())
|
||||||
of StoreClosure:
|
|
||||||
# Stores/updates the value of a closed-over
|
|
||||||
# variable
|
|
||||||
let idx = self.readLong().int
|
|
||||||
if idx !> self.closedOver.high():
|
|
||||||
# Note: we *peek* the stack, but we
|
|
||||||
# don't pop!
|
|
||||||
self.closedOver.add(self.peek())
|
|
||||||
else:
|
|
||||||
self.closedOver[idx] = self.peek()
|
|
||||||
of LoadClosure:
|
of LoadClosure:
|
||||||
# Loads a closed-over variable onto the
|
# Loads a closed-over variable onto the
|
||||||
# stack
|
# stack
|
||||||
self.push(self.closedOver[self.readLong() + self.closures[^1] - 1])
|
self.push(self.getClosure(self.readLong().int))
|
||||||
of PopClosure:
|
of PopClosure:
|
||||||
self.closedOver.delete(self.readLong())
|
discard self.popClosure(self.readLong().int)
|
||||||
of LiftArgument:
|
of StoreClosure:
|
||||||
# Lifts a function argument onto the stack
|
# Stores/updates the value of a closed-over
|
||||||
self.closedOver.add(self.getc(self.readLong().int))
|
# variable
|
||||||
|
let item = self.getc(self.readLong().int)
|
||||||
|
self.setClosure(self.readLong().int, item)
|
||||||
of LoadVar:
|
of LoadVar:
|
||||||
# Pushes a variable onto the operand
|
# Pushes a variable onto the operand
|
||||||
# stack
|
# stack
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -182,7 +182,6 @@ type
|
||||||
isPure*: bool
|
isPure*: bool
|
||||||
returnType*: Expression
|
returnType*: Expression
|
||||||
hasExplicitReturn*: bool
|
hasExplicitReturn*: bool
|
||||||
freeVars*: seq[IdentExpr]
|
|
||||||
depth*: int
|
depth*: int
|
||||||
|
|
||||||
SliceExpr* = ref object of Expression
|
SliceExpr* = ref object of Expression
|
||||||
|
@ -264,7 +263,6 @@ type
|
||||||
isPure*: bool
|
isPure*: bool
|
||||||
returnType*: Expression
|
returnType*: Expression
|
||||||
hasExplicitReturn*: bool
|
hasExplicitReturn*: bool
|
||||||
freeVars*: seq[IdentExpr]
|
|
||||||
depth*: int
|
depth*: int
|
||||||
|
|
||||||
TypeDecl* = ref object of Declaration
|
TypeDecl* = ref object of Declaration
|
||||||
|
@ -428,7 +426,6 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]
|
||||||
result.isPure = false
|
result.isPure = false
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
result.generics = generics
|
result.generics = generics
|
||||||
result.freeVars = freeVars
|
|
||||||
result.depth = depth
|
result.depth = depth
|
||||||
|
|
||||||
|
|
||||||
|
@ -634,7 +631,6 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
|
||||||
result.returnType = returnType
|
result.returnType = returnType
|
||||||
result.isPure = false
|
result.isPure = false
|
||||||
result.generics = generics
|
result.generics = generics
|
||||||
result.freeVars = freeVars
|
|
||||||
result.depth = depth
|
result.depth = depth
|
||||||
|
|
||||||
|
|
||||||
|
@ -735,13 +731,13 @@ proc `$`*(self: ASTNode): string =
|
||||||
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType}, pragmas={self.pragmas})"
|
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType}, pragmas={self.pragmas})"
|
||||||
of funDecl:
|
of funDecl:
|
||||||
var self = FunDecl(self)
|
var self = FunDecl(self)
|
||||||
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas}, vars=[{self.freeVars.join(", ")}])"""
|
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})"""
|
||||||
of typeDecl:
|
of typeDecl:
|
||||||
var self = TypeDecl(self)
|
var self = TypeDecl(self)
|
||||||
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, pragmas={self.pragmas}, type={self.valueType})"""
|
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, pragmas={self.pragmas}, type={self.valueType})"""
|
||||||
of lambdaExpr:
|
of lambdaExpr:
|
||||||
var self = LambdaExpr(self)
|
var self = LambdaExpr(self)
|
||||||
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync}, pragmas={self.pragmas}, vars=[{self.freeVars.join(", ")}])"""
|
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync}, pragmas={self.pragmas})"""
|
||||||
of deferStmt:
|
of deferStmt:
|
||||||
var self = DeferStmt(self)
|
var self = DeferStmt(self)
|
||||||
result &= &"Defer({self.expression})"
|
result &= &"Defer({self.expression})"
|
||||||
|
|
|
@ -29,13 +29,10 @@ type
|
||||||
## Length Encoding. Instructions are encoded in groups whose structure
|
## Length Encoding. Instructions are encoded in groups whose structure
|
||||||
## follows the following schema:
|
## follows the following schema:
|
||||||
## - The first integer represents the line number
|
## - The first integer represents the line number
|
||||||
## - The second integer represents the count of whatever comes after it
|
## - The second integer represents the number of
|
||||||
## (let's call it c)
|
## instructions on that line
|
||||||
## - After c, a sequence of c integers follows
|
## For example, if lines equals [1, 5], it means that there are 5 instructions
|
||||||
##
|
## at line 1, meaning that all instructions in code[0..4] belong to the same line.
|
||||||
## A visual representation may be easier to understand: [1, 2, 3, 4]
|
|
||||||
## This is to be interpreted as "there are 2 instructions at line 1 whose values
|
|
||||||
## are 3 and 4"
|
|
||||||
## This is more efficient than using the naive approach, which would encode
|
## This is more efficient than using the naive approach, which would encode
|
||||||
## the same line number multiple times and waste considerable amounts of space.
|
## the same line number multiple times and waste considerable amounts of space.
|
||||||
## cfi represents Call Frame Information and encodes the following information:
|
## cfi represents Call Frame Information and encodes the following information:
|
||||||
|
@ -81,8 +78,6 @@ type
|
||||||
LoadFloat64,
|
LoadFloat64,
|
||||||
LoadFloat32,
|
LoadFloat32,
|
||||||
LoadString,
|
LoadString,
|
||||||
LoadFunction,
|
|
||||||
LoadReturnAddress,
|
|
||||||
## Singleton opcodes (each of them pushes a constant singleton on the stack)
|
## Singleton opcodes (each of them pushes a constant singleton on the stack)
|
||||||
LoadNil,
|
LoadNil,
|
||||||
LoadTrue,
|
LoadTrue,
|
||||||
|
@ -164,6 +159,7 @@ 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
|
||||||
|
@ -264,8 +260,6 @@ const stackDoubleInstructions* = {}
|
||||||
# Argument double argument instructions take hardcoded arguments as 16 bit integers
|
# Argument double argument instructions take hardcoded arguments as 16 bit integers
|
||||||
const argumentDoubleInstructions* = {PopN, }
|
const argumentDoubleInstructions* = {PopN, }
|
||||||
|
|
||||||
# Argument double argument instructions take hardcoded arguments as 24 bit integers
|
|
||||||
const argumentTripleInstructions* = {StoreClosure}
|
|
||||||
|
|
||||||
# Jump instructions jump at relative or absolute bytecode offsets
|
# Jump instructions jump at relative or absolute bytecode offsets
|
||||||
const jumpInstructions* = {Jump, JumpIfFalse, JumpIfFalsePop,
|
const jumpInstructions* = {Jump, JumpIfFalse, JumpIfFalsePop,
|
||||||
|
@ -329,6 +323,15 @@ proc getLine*(self: Chunk, idx: int): int =
|
||||||
raise newException(IndexDefect, "index out of range")
|
raise newException(IndexDefect, "index out of range")
|
||||||
|
|
||||||
|
|
||||||
|
proc getIdx*(self: Chunk, line: int): int =
|
||||||
|
## Gets the index into self.lines
|
||||||
|
## where the line counter for the given
|
||||||
|
## line is located
|
||||||
|
for i, v in self.lines:
|
||||||
|
if (i and 1) != 0 and v == line:
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
proc writeConstant*(self: Chunk, data: openarray[uint8]): array[3, uint8] =
|
proc writeConstant*(self: Chunk, data: openarray[uint8]): array[3, uint8] =
|
||||||
## Writes a series of bytes to the chunk's constant
|
## Writes a series of bytes to the chunk's constant
|
||||||
## table and returns the index of the first byte as
|
## table and returns the index of the first byte as
|
||||||
|
|
|
@ -86,6 +86,8 @@ type
|
||||||
currentFunction: Declaration
|
currentFunction: Declaration
|
||||||
# Stores the current scope depth (0 = global, > 0 local)
|
# Stores the current scope depth (0 = global, > 0 local)
|
||||||
scopeDepth: int
|
scopeDepth: int
|
||||||
|
# TODO
|
||||||
|
scopes: seq[Declaration]
|
||||||
operators: OperatorTable
|
operators: OperatorTable
|
||||||
# The AST node
|
# The AST node
|
||||||
tree: seq[Declaration]
|
tree: seq[Declaration]
|
||||||
|
@ -170,7 +172,7 @@ proc getLines*(self: Parser): seq[tuple[start, stop: int]] = self.lines
|
||||||
proc getSource*(self: Parser): string = self.source
|
proc getSource*(self: Parser): string = self.source
|
||||||
proc getRelPos*(self: Parser, line: int): tuple[start, stop: int] = self.lines[line - 1]
|
proc getRelPos*(self: Parser, line: int): tuple[start, stop: int] = self.lines[line - 1]
|
||||||
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
||||||
template endOfLine(msg: string) = self.expect(Semicolon, msg)
|
template endOfLine(msg: string, tok: Token = nil) = self.expect(Semicolon, msg, tok)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,19 +267,19 @@ proc match[T: TokenType or string](self: Parser, kind: openarray[T]): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
|
|
||||||
proc expect[T: TokenType or string](self: Parser, kind: T, message: string = "") =
|
proc expect[T: TokenType or string](self: Parser, kind: T, message: string = "", token: Token = nil) =
|
||||||
## Behaves like self.match(), except that
|
## Behaves like self.match(), except that
|
||||||
## when a token doesn't match, an error
|
## when a token doesn't match, an error
|
||||||
## is raised. If no error message is
|
## is raised. If no error message is
|
||||||
## given, a default one is used
|
## given, a default one is used
|
||||||
if not self.match(kind):
|
if not self.match(kind):
|
||||||
if message.len() == 0:
|
if message.len() == 0:
|
||||||
self.error(&"expecting token of kind {kind}, found {self.peek().kind} instead")
|
self.error(&"expecting token of kind {kind}, found {self.peek().kind} instead", token)
|
||||||
else:
|
else:
|
||||||
self.error(message)
|
self.error(message)
|
||||||
|
|
||||||
|
|
||||||
proc expect[T: TokenType or string](self: Parser, kind: openarray[T], message: string = "") {.used.} =
|
proc expect[T: TokenType or string](self: Parser, kind: openarray[T], message: string = "", token: Token = nil) {.used.} =
|
||||||
## Behaves like self.expect(), except that
|
## Behaves like self.expect(), except that
|
||||||
## an error is raised only if none of the
|
## an error is raised only if none of the
|
||||||
## given token kinds matches
|
## given token kinds matches
|
||||||
|
@ -285,7 +287,7 @@ proc expect[T: TokenType or string](self: Parser, kind: openarray[T], message: s
|
||||||
if self.match(kind):
|
if self.match(kind):
|
||||||
return
|
return
|
||||||
if message.len() == 0:
|
if message.len() == 0:
|
||||||
self.error(&"""expecting any of the following tokens: {kind.join(", ")}, but got {self.peek().kind} instead""")
|
self.error(&"""expecting any of the following tokens: {kind.join(", ")}, but got {self.peek().kind} instead""", token)
|
||||||
|
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
|
@ -320,16 +322,6 @@ proc primary(self: Parser): Expression =
|
||||||
result = newIntExpr(self.step())
|
result = newIntExpr(self.step())
|
||||||
of Identifier:
|
of Identifier:
|
||||||
result = newIdentExpr(self.step(), self.scopeDepth)
|
result = newIdentExpr(self.step(), self.scopeDepth)
|
||||||
if not self.currentFunction.isNil() and self.scopeDepth > 0:
|
|
||||||
case self.currentFunction.kind:
|
|
||||||
of NodeKind.funDecl:
|
|
||||||
if FunDecl(self.currentFunction).depth != self.scopeDepth:
|
|
||||||
FunDecl(self.currentFunction).freeVars.add(IdentExpr(result))
|
|
||||||
of NodeKind.lambdaExpr:
|
|
||||||
if LambdaExpr(self.currentFunction).depth != self.scopeDepth:
|
|
||||||
LambdaExpr(self.currentFunction).freeVars.add(IdentExpr(result))
|
|
||||||
else:
|
|
||||||
discard # Unreachable
|
|
||||||
of LeftParen:
|
of LeftParen:
|
||||||
let tok = self.step()
|
let tok = self.step()
|
||||||
result = newGroupingExpr(self.expression(), tok)
|
result = newGroupingExpr(self.expression(), tok)
|
||||||
|
@ -1036,6 +1028,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
||||||
elif isLambda:
|
elif isLambda:
|
||||||
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok,
|
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok,
|
||||||
returnType=nil, depth=self.scopeDepth)
|
returnType=nil, depth=self.scopeDepth)
|
||||||
|
self.scopes.add(FunDecl(self.currentFunction))
|
||||||
if self.match(":"):
|
if self.match(":"):
|
||||||
# Function has explicit return type
|
# Function has explicit return type
|
||||||
if self.match([Function, Coroutine, Generator]):
|
if self.match([Function, Coroutine, Generator]):
|
||||||
|
@ -1101,11 +1094,12 @@ proc expression(self: Parser): Expression =
|
||||||
result = self.parseArrow() # Highest-level expression
|
result = self.parseArrow() # Highest-level expression
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc expressionStatement(self: Parser): Statement =
|
proc expressionStatement(self: Parser): Statement =
|
||||||
## Parses expression statements, which
|
## Parses expression statements, which
|
||||||
## are expressions followed by a semicolon
|
## are expressions followed by a semicolon
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing expression terminator")
|
endOfLine("missing expression terminator", expression.token)
|
||||||
result = Statement(newExprStmt(expression, expression.token))
|
result = Statement(newExprStmt(expression, expression.token))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1254,6 +1248,7 @@ proc findOperators(self: Parser, tokens: seq[Token]) =
|
||||||
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)", token)
|
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)", token)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[start, stop: int]], source: string, persist: bool = false): seq[Declaration] =
|
proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[start, stop: int]], source: string, persist: bool = false): seq[Declaration] =
|
||||||
## Parses a sequence of tokens into a sequence of AST nodes
|
## Parses a sequence of tokens into a sequence of AST nodes
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
|
|
|
@ -124,7 +124,7 @@ proc argumentDoubleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
self.current += 3
|
self.current += 3
|
||||||
|
|
||||||
|
|
||||||
proc argumentTripleInstruction(self: Debugger, instruction: OpCode) =
|
proc argumentTripleInstruction(self: Debugger, instruction: OpCode) {.used.} =
|
||||||
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
|
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
|
||||||
var slot = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
var slot = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
|
@ -132,6 +132,15 @@ proc argumentTripleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
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()
|
||||||
|
@ -141,23 +150,6 @@ proc callInstruction(self: Debugger, instruction: OpCode) =
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc functionInstruction(self: Debugger, instruction: OpCode) =
|
|
||||||
## Debugs function calls
|
|
||||||
var address = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
|
||||||
printInstruction(instruction)
|
|
||||||
styledEcho fgGreen, &", loads function at address ", fgYellow, $address
|
|
||||||
self.current += 4
|
|
||||||
|
|
||||||
|
|
||||||
proc loadAddressInstruction(self: Debugger, instruction: OpCode) =
|
|
||||||
## Debugs LoadReturnAddress instructions
|
|
||||||
var address = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3],
|
|
||||||
self.chunk.code[self.current + 4]].fromQuad()
|
|
||||||
printInstruction(instruction)
|
|
||||||
styledEcho fgGreen, &", loads address ", fgYellow, $address
|
|
||||||
self.current += 5
|
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -207,16 +199,12 @@ proc disassembleInstruction*(self: Debugger) =
|
||||||
self.stackTripleInstruction(opcode)
|
self.stackTripleInstruction(opcode)
|
||||||
of argumentDoubleInstructions:
|
of argumentDoubleInstructions:
|
||||||
self.argumentDoubleInstruction(opcode)
|
self.argumentDoubleInstruction(opcode)
|
||||||
of argumentTripleInstructions:
|
of StoreClosure:
|
||||||
self.argumentTripleInstruction(opcode)
|
self.storeClosureInstruction(opcode)
|
||||||
of Call:
|
of Call:
|
||||||
self.callInstruction(opcode)
|
self.callInstruction(opcode)
|
||||||
of jumpInstructions:
|
of jumpInstructions:
|
||||||
self.jumpInstruction(opcode)
|
self.jumpInstruction(opcode)
|
||||||
of LoadFunction:
|
|
||||||
self.functionInstruction(opcode)
|
|
||||||
of LoadReturnAddress:
|
|
||||||
self.loadAddressInstruction(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
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
|
|
||||||
fn makeClosure(n: int): fn: int {
|
fn makeClosure(n: int): fn: fn: int {
|
||||||
fn inner: int {
|
fn inner: fn: int {
|
||||||
return n;
|
fn deep: int {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return deep;
|
||||||
}
|
}
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
var closed = makeClosure(1)();
|
|
||||||
print(closed); # 1
|
print(makeClosure(1)()()); # 1
|
||||||
print(makeClosure(2)()); # 2
|
|
||||||
var closure = makeClosure(3);
|
|
||||||
print(closure()); # 3
|
|
||||||
print(closure()); # 3
|
|
||||||
|
|
Loading…
Reference in New Issue