Added implicit nil return from functions, JumpIfFalseOrPop opcode, error handling for overflowing collection literals, getStaticIndex now returns negative stack index for closed-over variables and variable() emits LoadHeap instructions for them
This commit is contained in:
parent
43f5cea905
commit
8e94891d37
|
@ -246,6 +246,8 @@ proc patchJump(self: Compiler, offset: int) =
|
|||
self.chunk.code[offset] = JumpIfFalse.uint8()
|
||||
of LongJumpIfFalsePop:
|
||||
self.chunk.code[offset] = JumpIfFalsePop.uint8()
|
||||
of LongJumpIfFalseOrPop:
|
||||
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
||||
else:
|
||||
discard
|
||||
self.chunk.code.delete(offset + 1) # Discards the 24 bit integer
|
||||
|
@ -262,6 +264,8 @@ proc patchJump(self: Compiler, offset: int) =
|
|||
self.chunk.code[offset] = LongJumpIfFalse.uint8()
|
||||
of JumpIfFalsePop:
|
||||
self.chunk.code[offset] = LongJumpIfFalsePop.uint8()
|
||||
of JumpIfFalseOrPop:
|
||||
self.chunk.code[offset] = LongJumpIfFalseOrPop.uint8()
|
||||
else:
|
||||
discard
|
||||
let offsetArray = jump.toTriple()
|
||||
|
@ -344,24 +348,32 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
self.emitConstant(y)
|
||||
of listExpr:
|
||||
var y = ListExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("collection literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildList)
|
||||
self.emitBytes(y.members.len().toTriple()) # 24-bit integer, meaning list literals can have up to 2^24 elements
|
||||
self.emitBytes(y.members.len().toTriple()) # 24-bit integer, meaning collection literals can have up to 2^24 elements
|
||||
of tupleExpr:
|
||||
var y = TupleExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("collection literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildTuple)
|
||||
self.emitBytes(y.members.len().toTriple())
|
||||
of setExpr:
|
||||
var y = SetExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("collection literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildSet)
|
||||
self.emitBytes(y.members.len().toTriple())
|
||||
of dictExpr:
|
||||
var y = DictExpr(node)
|
||||
if y.keys.len() > 16777216:
|
||||
self.error("collection literals can't have more than 16777216 elements")
|
||||
for (key, value) in zip(y.keys, y.values):
|
||||
self.expression(key)
|
||||
self.expression(value)
|
||||
|
@ -376,14 +388,14 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
|
||||
|
||||
proc unary(self: Compiler, node: UnaryExpr) =
|
||||
## Compiles unary expressions such as negation or
|
||||
## bitwise inversion
|
||||
## Compiles unary expressions such as decimal or
|
||||
## bitwise negation
|
||||
self.expression(node.a) # Pushes the operand onto the stack
|
||||
case node.operator.kind:
|
||||
of Minus:
|
||||
self.emitByte(UnaryNegate)
|
||||
of Plus:
|
||||
discard # Unary + does nothing
|
||||
discard # Unary + does nothing, but we allow it for consistency
|
||||
of TokenType.LogicalNot:
|
||||
self.emitByte(OpCode.LogicalNot)
|
||||
of Tilde:
|
||||
|
@ -443,8 +455,12 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
self.emitByte(OpCode.GreaterOrEqual)
|
||||
of TokenType.LogicalAnd:
|
||||
self.expression(node.a)
|
||||
let jump = self.emitJump(JumpIfFalse)
|
||||
self.emitByte(Pop)
|
||||
var jump: int
|
||||
if self.enableOptimizations:
|
||||
jump = self.emitJump(JumpIfFalseOrPop)
|
||||
else:
|
||||
jump = self.emitJump(JumpIfFalse)
|
||||
self.emitByte(Pop)
|
||||
self.expression(node.b)
|
||||
self.patchJump(jump)
|
||||
of TokenType.LogicalOr:
|
||||
|
@ -511,7 +527,7 @@ proc deleteStatic(self: Compiler, name: IdentExpr,
|
|||
self.names.del(i)
|
||||
|
||||
|
||||
proc getStaticIndex(self: Compiler, name: IdentExpr): int =
|
||||
proc getStaticIndex(self: Compiler, name: IdentExpr, depth: int = self.scopeDepth): int =
|
||||
## Gets the predicted stack position of the given variable
|
||||
## if it is static, returns -1 if it is to be bound dynamically
|
||||
## or it does not exist at all and returns -pos if the variable
|
||||
|
@ -519,7 +535,12 @@ proc getStaticIndex(self: Compiler, name: IdentExpr): int =
|
|||
var i: int = self.names.high()
|
||||
for variable in reversed(self.names):
|
||||
if name.name.lexeme == variable.name.name.lexeme:
|
||||
return i
|
||||
if variable.depth == depth:
|
||||
return i
|
||||
else:
|
||||
# This tells self.identifier() that this is
|
||||
# a closed-over variable
|
||||
return -i
|
||||
dec(i)
|
||||
return -1
|
||||
|
||||
|
@ -540,8 +561,10 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
|||
self.emitByte(LoadFast) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
self.emitBytes(index.toTriple())
|
||||
else:
|
||||
self.emitByte(LoadHeap)
|
||||
self.emitBytes(self.identifierConstant(node)) # Closed-over variable!
|
||||
# TODO: find the code that emitted the StoreFast instruction and
|
||||
# change it to StoreHeap
|
||||
self.emitByte(LoadHeap) # Heap-allocated closure variable. Faster than LoadName, but slower than LoadFast
|
||||
self.emitBytes(self.identifierConstant(node))
|
||||
else:
|
||||
self.emitByte(LoadName) # Resolves by name, at runtime, in a global hashmap. Slower
|
||||
self.emitBytes(self.identifierConstant(node))
|
||||
|
@ -926,8 +949,12 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
|||
# so recursion & co. works) and then just compile
|
||||
# their body as a block statement (which takes
|
||||
# care of incrementing self.scopeDepth so locals
|
||||
# are resolved properly). Yay!
|
||||
# are resolved properly). There's a need for a bit
|
||||
# of boilerplate code to make closures work, but
|
||||
# that's about it
|
||||
|
||||
# All functions implicitly return nil
|
||||
self.emitBytes(OpCode.Nil, OpCode.Return)
|
||||
# Currently defer is not functional so we
|
||||
# just pop the instructions
|
||||
for i in countup(deferStart, self.deferred.len(), 1):
|
||||
|
|
|
@ -115,18 +115,20 @@ type
|
|||
LoadHeap, # Loads a closed-over variable
|
||||
StoreHeap, # Stores a closed-over variable
|
||||
## Looping and jumping
|
||||
Jump, # Absolute and unconditional jump into the bytecode
|
||||
Jump, # Absolute, unconditional jump into the bytecode
|
||||
JumpIfFalse, # Jumps to an absolute index in the bytecode if the value at the top of the stack is falsey
|
||||
JumpIfTrue, # Jumps to an absolute index in the bytecode if the value at the top of the stack is truthy
|
||||
JumpIfFalsePop, # Like JumpIfFalse, but it also pops off the stack (regardless of truthyness). Optimization for if statements
|
||||
JumpIfFalseOrPop, # Jumps to an absolute index in the bytecode if the value at the top of the stack is falsey and pops it otherwise
|
||||
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||
JumpBackwards, # Relative, unconditional, negative jump into the bytecode
|
||||
Break, # Temporary opcode used to signal exiting out of loop
|
||||
Break, # Temporary opcode used to signal exiting out of loops
|
||||
## Long variants of jumps (they use a 24-bit operand instead of a 16-bit one)
|
||||
LongJump,
|
||||
LongJumpIfFalse,
|
||||
LongJumpIfTrue,
|
||||
LongJumpIfFalsePop,
|
||||
LongJumpIfFalseOrPop,
|
||||
LongJumpForwards,
|
||||
LongJumpBackwards,
|
||||
## Functions
|
||||
|
@ -148,7 +150,9 @@ type
|
|||
BuildTuple,
|
||||
## Misc
|
||||
Assert,
|
||||
MakeClass
|
||||
MakeClass,
|
||||
Slice, # Slices an object (takes 3 arguments: start, stop, step) and pushes the result of a.subscript(b, c, d) onto the stack
|
||||
GetItem, # Pushes the result of a.getItem(b)
|
||||
|
||||
|
||||
# We group instructions by their operation/operand types for easier handling when debugging
|
||||
|
|
|
@ -356,8 +356,9 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
|
|||
|
||||
|
||||
proc call(self: Parser): ASTNode =
|
||||
## Parses call expressions and object
|
||||
## field accessing ("dot syntax")
|
||||
## Parses calls, object field
|
||||
## field accessing and slicing
|
||||
## expressions
|
||||
result = self.primary()
|
||||
while true:
|
||||
if self.match(LeftParen):
|
||||
|
|
Loading…
Reference in New Issue