also derete this #3
|
@ -33,20 +33,28 @@ export multibyte
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
NameKind = enum
|
||||||
|
Function, Type, Var
|
||||||
Name = ref object
|
Name = ref object
|
||||||
## A compile-time wrapper around
|
## A compile-time wrapper around
|
||||||
## statically resolved names.
|
## statically resolved names.
|
||||||
## Depth indicates to which scope
|
## Depth indicates to which scope
|
||||||
## the variable belongs, zero meaning
|
## the variable belongs, zero meaning
|
||||||
## the global one
|
## the global one
|
||||||
name: IdentExpr
|
kind: NameKind
|
||||||
owner: string
|
name: IdentExpr # Name of the identifier
|
||||||
depth: int
|
owner: string # Owner of the identifier
|
||||||
isPrivate: bool
|
depth: int # Scope depth
|
||||||
isConst: bool
|
isPrivate: bool # Is this name private?
|
||||||
isLet: bool
|
isConst: bool # Is this a constant?
|
||||||
valueType: IdentExpr
|
isLet: bool # Can this name's value be mutated?
|
||||||
codePos: int
|
valueType: Expression # Name's type
|
||||||
|
codePos: int # The position in the bytecode
|
||||||
|
# where this name's StoreVar
|
||||||
|
# instruction was emitted. This
|
||||||
|
# is kept so that once we detect
|
||||||
|
# this name as a closed-over variable
|
||||||
|
# we can change the StoreVar into a StoreHeap
|
||||||
|
|
||||||
Loop = object
|
Loop = object
|
||||||
## A "loop object" used
|
## A "loop object" used
|
||||||
|
@ -54,9 +62,9 @@ type
|
||||||
## appropriate jump offsets
|
## appropriate jump offsets
|
||||||
## for continue and break
|
## for continue and break
|
||||||
## statements
|
## statements
|
||||||
start: int
|
start: int # Position in the bytecode where the loop starts
|
||||||
depth: int
|
depth: int # Scope depth where the loop is located
|
||||||
breakPos: seq[int]
|
breakPos: seq[int] # List of positions where
|
||||||
|
|
||||||
Compiler* = ref object
|
Compiler* = ref object
|
||||||
## A wrapper around the compiler's state
|
## A wrapper around the compiler's state
|
||||||
|
@ -133,7 +141,6 @@ proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >=
|
||||||
|
|
||||||
|
|
||||||
## Utility functions
|
## Utility functions
|
||||||
|
|
||||||
proc peek(self: Compiler, distance: int = 0): ASTNode =
|
proc peek(self: Compiler, distance: int = 0): ASTNode =
|
||||||
## Peeks at the AST node at the given distance.
|
## Peeks at the AST node at the given distance.
|
||||||
## If the distance is out of bounds, the last
|
## If the distance is out of bounds, the last
|
||||||
|
@ -225,15 +232,15 @@ proc emitJump(self: Compiler, opcode: OpCode): int =
|
||||||
|
|
||||||
|
|
||||||
proc patchJump(self: Compiler, offset: int) =
|
proc patchJump(self: Compiler, offset: int) =
|
||||||
## Patches a previously emitted jump
|
## Patches a previously emitted relative
|
||||||
## using emitJump. Since emitJump assumes
|
## jump using emitJump. Since emitJump assumes
|
||||||
## a long jump, this also shrinks the jump
|
## a long jump, this also shrinks the jump
|
||||||
## offset and changes the bytecode instruction if possible
|
## offset and changes the bytecode instruction if possible
|
||||||
## (i.e. jump is in 16 bit range), but the converse is also
|
## (i.e. jump is in 16 bit range), but the converse is also
|
||||||
## true (i.e. it might change a regular jump into a long one)
|
## true (i.e. it might change a regular jump into a long one)
|
||||||
let jump: int = self.chunk.code.len() - offset
|
let jump: int = self.chunk.code.len() - offset
|
||||||
if jump > 16777215:
|
if jump > 16777215:
|
||||||
self.error("cannot jump more than 16777215 bytecode instructions")
|
self.error("cannot jump more than 16777216 bytecode instructions")
|
||||||
if jump < uint16.high().int:
|
if jump < uint16.high().int:
|
||||||
case OpCode(self.chunk.code[offset]):
|
case OpCode(self.chunk.code[offset]):
|
||||||
of LongJumpForwards:
|
of LongJumpForwards:
|
||||||
|
@ -248,7 +255,7 @@ proc patchJump(self: Compiler, offset: int) =
|
||||||
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
|
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
|
||||||
let offsetArray = (jump - 1).toDouble() # -1 since we got rid of 1 byte!
|
let offsetArray = (jump - 1).toDouble() # -1 since we got rid of 1 byte!
|
||||||
self.chunk.code[offset + 1] = offsetArray[0]
|
self.chunk.code[offset + 1] = offsetArray[0]
|
||||||
self.chunk.code[offset + 2] = offsetArray[1]
|
self.chunk.code[offset + 2] = offsetArray[1]
|
||||||
|
@ -290,9 +297,6 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
self.emitByte(OpCode.Nan)
|
self.emitByte(OpCode.Nan)
|
||||||
of strExpr:
|
of strExpr:
|
||||||
self.emitConstant(node)
|
self.emitConstant(node)
|
||||||
# The optimizer will emit warning
|
|
||||||
# for overflowing numbers. Here, we
|
|
||||||
# treat them as errors
|
|
||||||
of intExpr:
|
of intExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = IntExpr(node)
|
var y = IntExpr(node)
|
||||||
|
@ -301,11 +305,6 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.error("integer value out of range")
|
self.error("integer value out of range")
|
||||||
self.emitConstant(y)
|
self.emitConstant(y)
|
||||||
# Even though most likely the optimizer
|
|
||||||
# will collapse all these other literals
|
|
||||||
# to nodes of kind intExpr, that can be
|
|
||||||
# disabled. This also allows us to catch
|
|
||||||
# basic overflow errors before running any code
|
|
||||||
of hexExpr:
|
of hexExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = HexExpr(node)
|
var y = HexExpr(node)
|
||||||
|
@ -347,7 +346,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
of listExpr:
|
of listExpr:
|
||||||
var y = ListExpr(node)
|
var y = ListExpr(node)
|
||||||
if y.members.len() > 16777216:
|
if y.members.len() > 16777216:
|
||||||
self.error("collection literals can't have more than 16777216 elements")
|
self.error("list literals can't have more than 16777216 elements")
|
||||||
for member in y.members:
|
for member in y.members:
|
||||||
self.expression(member)
|
self.expression(member)
|
||||||
self.emitByte(BuildList)
|
self.emitByte(BuildList)
|
||||||
|
@ -355,7 +354,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
of tupleExpr:
|
of tupleExpr:
|
||||||
var y = TupleExpr(node)
|
var y = TupleExpr(node)
|
||||||
if y.members.len() > 16777216:
|
if y.members.len() > 16777216:
|
||||||
self.error("collection literals can't have more than 16777216 elements")
|
self.error("tuple literals can't have more than 16777216 elements")
|
||||||
for member in y.members:
|
for member in y.members:
|
||||||
self.expression(member)
|
self.expression(member)
|
||||||
self.emitByte(BuildTuple)
|
self.emitByte(BuildTuple)
|
||||||
|
@ -363,7 +362,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
of setExpr:
|
of setExpr:
|
||||||
var y = SetExpr(node)
|
var y = SetExpr(node)
|
||||||
if y.members.len() > 16777216:
|
if y.members.len() > 16777216:
|
||||||
self.error("collection literals can't have more than 16777216 elements")
|
self.error("set literals can't have more than 16777216 elements")
|
||||||
for member in y.members:
|
for member in y.members:
|
||||||
self.expression(member)
|
self.expression(member)
|
||||||
self.emitByte(BuildSet)
|
self.emitByte(BuildSet)
|
||||||
|
@ -371,7 +370,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
of dictExpr:
|
of dictExpr:
|
||||||
var y = DictExpr(node)
|
var y = DictExpr(node)
|
||||||
if y.keys.len() > 16777216:
|
if y.keys.len() > 16777216:
|
||||||
self.error("collection literals can't have more than 16777216 elements")
|
self.error("dict literals can't have more than 16777216 elements")
|
||||||
for (key, value) in zip(y.keys, y.values):
|
for (key, value) in zip(y.keys, y.values):
|
||||||
self.expression(key)
|
self.expression(key)
|
||||||
self.expression(value)
|
self.expression(value)
|
||||||
|
@ -382,7 +381,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
self.expression(y.expression)
|
self.expression(y.expression)
|
||||||
self.emitByte(OpCode.Await)
|
self.emitByte(OpCode.Await)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
|
||||||
|
|
||||||
|
|
||||||
proc unary(self: Compiler, node: UnaryExpr) =
|
proc unary(self: Compiler, node: UnaryExpr) =
|
||||||
|
@ -399,59 +398,84 @@ proc unary(self: Compiler, node: UnaryExpr) =
|
||||||
of Tilde:
|
of Tilde:
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at unary(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at unary(): {node} (This is an internal error and most likely a bug!)")
|
||||||
|
|
||||||
|
|
||||||
proc binary(self: Compiler, node: BinaryExpr) =
|
proc binary(self: Compiler, node: BinaryExpr) =
|
||||||
## Compiles all binary expressions
|
## Compiles all binary expressions
|
||||||
|
|
||||||
# These two lines prepare the stack by pushing the
|
# These two lines prepare the stack by pushing the
|
||||||
# opcode's operands onto it
|
# opcode's operands onto it
|
||||||
self.expression(node.a)
|
self.expression(node.a)
|
||||||
self.expression(node.b)
|
self.expression(node.b)
|
||||||
|
# TODO: Find implementation of
|
||||||
|
# the given operator and call it
|
||||||
case node.operator.kind:
|
case node.operator.kind:
|
||||||
of Plus:
|
of Plus:
|
||||||
|
# a + b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Minus:
|
of Minus:
|
||||||
|
# a - b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Star:
|
of Star:
|
||||||
|
# a * b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of DoubleStar:
|
of DoubleStar:
|
||||||
|
# a ** b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Percentage:
|
of Percentage:
|
||||||
|
# a % b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of FloorDiv:
|
of FloorDiv:
|
||||||
|
# a // b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Slash:
|
of Slash:
|
||||||
|
# a / b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Ampersand:
|
of Ampersand:
|
||||||
|
# a & b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Caret:
|
of Caret:
|
||||||
|
# a ^ b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Pipe:
|
of Pipe:
|
||||||
self.emitByte(NoOp)
|
# a | b
|
||||||
of As:
|
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Is:
|
of Is:
|
||||||
|
# a is b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of IsNot:
|
of IsNot:
|
||||||
|
# a isnot b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of Of:
|
of Of:
|
||||||
|
# a of b
|
||||||
|
self.emitByte(NoOp)
|
||||||
|
of As:
|
||||||
|
# a as b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of RightShift:
|
of RightShift:
|
||||||
|
# a >> b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of LeftShift:
|
of LeftShift:
|
||||||
|
# a << b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of LessThan:
|
of LessThan:
|
||||||
|
# a < b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of GreaterThan:
|
of GreaterThan:
|
||||||
|
# a > b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of DoubleEqual:
|
of DoubleEqual:
|
||||||
|
# a == b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of LessOrEqual:
|
of LessOrEqual:
|
||||||
|
# a <= b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of GreaterOrEqual:
|
of GreaterOrEqual:
|
||||||
|
# a >= b
|
||||||
self.emitByte(NoOp)
|
self.emitByte(NoOp)
|
||||||
of LogicalAnd:
|
of LogicalAnd:
|
||||||
|
# a and b
|
||||||
self.expression(node.a)
|
self.expression(node.a)
|
||||||
var jump: int
|
var jump: int
|
||||||
if self.enableOptimizations:
|
if self.enableOptimizations:
|
||||||
|
@ -462,17 +486,16 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
||||||
self.expression(node.b)
|
self.expression(node.b)
|
||||||
self.patchJump(jump)
|
self.patchJump(jump)
|
||||||
of LogicalOr:
|
of LogicalOr:
|
||||||
|
# a or b
|
||||||
self.expression(node.a)
|
self.expression(node.a)
|
||||||
let jump = self.emitJump(JumpIfTrue)
|
let jump = self.emitJump(JumpIfTrue)
|
||||||
self.expression(node.b)
|
self.expression(node.b)
|
||||||
self.patchJump(jump)
|
self.patchJump(jump)
|
||||||
# TODO: In-place operations
|
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug!)")
|
||||||
|
|
||||||
|
|
||||||
|
proc declareName(self: Compiler, node: Declaration, kind: Expression) =
|
||||||
proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
|
||||||
## Statically declares a name into the current scope
|
## Statically declares a name into the current scope
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of NodeKind.varDecl:
|
of NodeKind.varDecl:
|
||||||
|
@ -489,28 +512,26 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
||||||
isConst: node.isConst,
|
isConst: node.isConst,
|
||||||
valueType: kind,
|
valueType: kind,
|
||||||
codePos: self.chunk.code.len(),
|
codePos: self.chunk.code.len(),
|
||||||
isLet: node.isLet))
|
isLet: node.isLet,
|
||||||
|
kind: Var))
|
||||||
self.emitByte(StoreVar)
|
self.emitByte(StoreVar)
|
||||||
self.emitBytes(self.names.high().toTriple())
|
self.emitBytes(self.names.high().toTriple())
|
||||||
of funDecl:
|
of NodeKind.funDecl:
|
||||||
var node = FunDecl(node)
|
var node = FunDecl(node)
|
||||||
# Declares the function's name in the
|
# Declares the function's name in the
|
||||||
# current (outer) scope...
|
# current scope but no StoreVar is emitted
|
||||||
|
# because a function's name is only useful
|
||||||
|
# at compile time and has no runtime meaning
|
||||||
|
# given that we implement functions with jumps
|
||||||
self.names.add(Name(depth: self.scopeDepth,
|
self.names.add(Name(depth: self.scopeDepth,
|
||||||
isPrivate: node.isPrivate,
|
isPrivate: node.isPrivate,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
valueType: node.returnType,
|
valueType: node.returnType,
|
||||||
codePos: self.chunk.code.len(),
|
codePos: -1,
|
||||||
name: node.name,
|
name: node.name,
|
||||||
isLet: false))
|
isLet: false,
|
||||||
self.emitByte(StoreVar)
|
kind: Function))
|
||||||
self.emitBytes(self.names.high().toTriple())
|
|
||||||
# ... but its arguments in an inner one!
|
|
||||||
self.scopeDepth += 1
|
|
||||||
# (this ugly part is needed because
|
|
||||||
# self.blockStmt() already increments
|
|
||||||
# and decrements the scope depth)
|
|
||||||
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 16777216 variables at a time")
|
self.error("cannot declare more than 16777216 variables at a time")
|
||||||
|
@ -521,10 +542,10 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
||||||
name: argument.name,
|
name: argument.name,
|
||||||
valueType: kind,
|
valueType: kind,
|
||||||
codePos: self.chunk.code.len(),
|
codePos: self.chunk.code.len(),
|
||||||
isLet: false))
|
isLet: false,
|
||||||
|
kind: Var))
|
||||||
self.emitByte(StoreVar)
|
self.emitByte(StoreVar)
|
||||||
self.emitBytes(self.names.high().toTriple())
|
self.emitBytes(self.names.high().toTriple())
|
||||||
self.scopeDepth -= 1
|
|
||||||
# TODO: Default arguments and unpacking
|
# TODO: Default arguments and unpacking
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
@ -608,6 +629,7 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
||||||
proc identifier(self: Compiler, node: IdentExpr) =
|
proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
## Compiles access to identifiers
|
## Compiles access to identifiers
|
||||||
let s = self.resolve(node)
|
let s = self.resolve(node)
|
||||||
|
echo s[]
|
||||||
if s == nil:
|
if s == nil:
|
||||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||||
elif s.isConst:
|
elif s.isConst:
|
||||||
|
@ -641,8 +663,7 @@ proc assignment(self: Compiler, node: ASTNode) =
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of assignExpr:
|
of assignExpr:
|
||||||
var node = AssignExpr(node)
|
var node = AssignExpr(node)
|
||||||
# TODO: This will explode with slicing!
|
let name = IdentExpr(node.name)
|
||||||
var name = IdentExpr(node.name)
|
|
||||||
let r = self.resolve(name)
|
let r = self.resolve(name)
|
||||||
if r == nil:
|
if r == nil:
|
||||||
self.error(&"assignment to undeclared name '{node.name}'")
|
self.error(&"assignment to undeclared name '{node.name}'")
|
||||||
|
@ -1116,7 +1137,6 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
# are resolved properly). There's a need for a bit
|
# are resolved properly). There's a need for a bit
|
||||||
# of boilerplate code to make closures work, but
|
# of boilerplate code to make closures work, but
|
||||||
# that's about it
|
# that's about it
|
||||||
|
|
||||||
self.emitBytes(OpCode.Nil, OpCode.Return)
|
self.emitBytes(OpCode.Nil, OpCode.Return)
|
||||||
|
|
||||||
# Currently defer is not functional so we
|
# Currently defer is not functional so we
|
||||||
|
|
|
@ -184,11 +184,11 @@ type
|
||||||
|
|
||||||
LambdaExpr* = ref object of Expression
|
LambdaExpr* = ref object of Expression
|
||||||
body*: Statement
|
body*: Statement
|
||||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
|
||||||
defaults*: seq[Expression]
|
defaults*: seq[Expression]
|
||||||
isGenerator*: bool
|
isGenerator*: bool
|
||||||
isAsync*: bool
|
isAsync*: bool
|
||||||
returnType*: IdentExpr
|
returnType*: Expression
|
||||||
|
|
||||||
SliceExpr* = ref object of Expression
|
SliceExpr* = ref object of Expression
|
||||||
expression*: Expression
|
expression*: Expression
|
||||||
|
@ -258,17 +258,17 @@ type
|
||||||
isConst*: bool
|
isConst*: bool
|
||||||
isPrivate*: bool
|
isPrivate*: bool
|
||||||
isLet*: bool
|
isLet*: bool
|
||||||
valueType*: IdentExpr
|
valueType*: Expression
|
||||||
|
|
||||||
FunDecl* = ref object of Declaration
|
FunDecl* = ref object of Declaration
|
||||||
name*: IdentExpr
|
name*: IdentExpr
|
||||||
body*: Statement
|
body*: Statement
|
||||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
|
||||||
defaults*: seq[Expression]
|
defaults*: seq[Expression]
|
||||||
isAsync*: bool
|
isAsync*: bool
|
||||||
isGenerator*: bool
|
isGenerator*: bool
|
||||||
isPrivate*: bool
|
isPrivate*: bool
|
||||||
returnType*: IdentExpr
|
returnType*: Expression
|
||||||
|
|
||||||
|
|
||||||
proc newASTNode*(kind: NodeKind, token: Token): ASTNode =
|
proc newASTNode*(kind: NodeKind, token: Token): ASTNode =
|
||||||
|
@ -371,8 +371,8 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
|
||||||
result.token = token
|
result.token = token
|
||||||
|
|
||||||
|
|
||||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression], body: Statement,
|
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression], body: Statement,
|
||||||
isGenerator: bool, isAsync: bool, token: Token): LambdaExpr =
|
isGenerator: bool, isAsync: bool, token: Token, returnType: Expression): LambdaExpr =
|
||||||
result = LambdaExpr(kind: lambdaExpr)
|
result = LambdaExpr(kind: lambdaExpr)
|
||||||
result.body = body
|
result.body = body
|
||||||
result.arguments = arguments
|
result.arguments = arguments
|
||||||
|
@ -380,6 +380,7 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
||||||
result.isGenerator = isGenerator
|
result.isGenerator = isGenerator
|
||||||
result.isAsync = isAsync
|
result.isAsync = isAsync
|
||||||
result.token = token
|
result.token = token
|
||||||
|
result.returnType = returnType
|
||||||
|
|
||||||
|
|
||||||
proc newGetItemExpr*(obj: Expression, name: IdentExpr, token: Token): GetItemExpr =
|
proc newGetItemExpr*(obj: Expression, name: IdentExpr, token: Token): GetItemExpr =
|
||||||
|
@ -578,7 +579,7 @@ proc newIfStmt*(condition: Expression, thenBranch, elseBranch: Statement,
|
||||||
|
|
||||||
proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
||||||
isPrivate: bool = true, token: Token, isLet: bool = false,
|
isPrivate: bool = true, token: Token, isLet: bool = false,
|
||||||
valueType: IdentExpr, pragmas: seq[Token]): VarDecl =
|
valueType: Expression, pragmas: seq[Token]): VarDecl =
|
||||||
result = VarDecl(kind: varDecl)
|
result = VarDecl(kind: varDecl)
|
||||||
result.name = name
|
result.name = name
|
||||||
result.value = value
|
result.value = value
|
||||||
|
@ -590,9 +591,10 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
|
|
||||||
|
|
||||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression],
|
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression],
|
||||||
body: Statement, isAsync, isGenerator: bool,
|
body: Statement, isAsync, isGenerator: bool,
|
||||||
isPrivate: bool, token: Token, pragmas: seq[Token]): FunDecl =
|
isPrivate: bool, token: Token, pragmas: seq[Token],
|
||||||
|
returnType: Expression): FunDecl =
|
||||||
result = FunDecl(kind: funDecl)
|
result = FunDecl(kind: funDecl)
|
||||||
result.name = name
|
result.name = name
|
||||||
result.arguments = arguments
|
result.arguments = arguments
|
||||||
|
@ -603,6 +605,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
|
||||||
result.isPrivate = isPrivate
|
result.isPrivate = isPrivate
|
||||||
result.token = token
|
result.token = token
|
||||||
result.pragmas = pragmas
|
result.pragmas = pragmas
|
||||||
|
result.returnType = returnType
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,17 @@ type
|
||||||
OpCode* {.pure.} = enum
|
OpCode* {.pure.} = enum
|
||||||
## Enum of possible opcodes.
|
## Enum of possible opcodes.
|
||||||
|
|
||||||
# Note: x represents the
|
# Note: x represents the argument
|
||||||
# argument to unary opcodes, while
|
# to unary opcodes, while a and b
|
||||||
# a and b represent arguments to binary
|
# represent arguments to binary
|
||||||
# opcodes. Other variable names (c, d, etc.)
|
# opcodes. Other variable names (c, d, ...)
|
||||||
# may be used for more complex opcodes
|
# may be used for more complex opcodes. If
|
||||||
|
# an opcode takes any arguments at runtime,
|
||||||
|
# they come from either the stack or the VM's
|
||||||
|
# closure array. Some other opcodes (e.g.
|
||||||
|
# jumps), take arguments in the form of 16
|
||||||
|
# or 24 bit numbers that are defined statically
|
||||||
|
# at compilation time into the bytecode.
|
||||||
|
|
||||||
LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack
|
LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack
|
||||||
## Constant opcodes (each of them pushes a constant singleton on the stack)
|
## Constant opcodes (each of them pushes a constant singleton on the stack)
|
||||||
|
@ -68,19 +74,19 @@ type
|
||||||
Push, # Pushes x onto the stack
|
Push, # Pushes x onto the stack
|
||||||
PopN, # Pops x elements off the stack (optimization for exiting scopes and returning from functions)
|
PopN, # Pops x elements off the stack (optimization for exiting scopes and returning from functions)
|
||||||
## Name resolution/handling
|
## Name resolution/handling
|
||||||
LoadAttribute,
|
LoadAttribute, # Pushes the attribute b of object a onto the stack
|
||||||
LoadVar, # Pushes the object at position x in 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
|
||||||
LoadHeap, # Pushes the object position x in the closure array
|
LoadHeap, # Pushes the object position x in the closure array onto the stack
|
||||||
StoreHeap, # Stores the value of b at position a in the closure array
|
StoreHeap, # Stores the value of b at position a in the closure array
|
||||||
## Looping and jumping
|
## Looping and jumping
|
||||||
Jump, # Absolute, unconditional jump into the bytecode at offset x
|
Jump, # Absolute, unconditional jump into the bytecode
|
||||||
JumpIfFalse, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is falsey
|
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||||
JumpIfTrue, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is truthy
|
JumpBackwards, # Relative, unconditional, negative jump in the bytecode
|
||||||
JumpIfFalsePop, # Like JumpIfFalse, but it also pops off the stack (regardless of truthyness). Optimization for if statements
|
JumpIfFalse, # Jumps to an absolute index in the bytecode if x is true
|
||||||
JumpIfFalseOrPop, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is falsey and pops it otherwise
|
JumpIfTrue, # Jumps to an absolute index in the bytecode if x is false
|
||||||
JumpForwards, # Relative, unconditional, positive jump of size x in the bytecode
|
JumpIfFalsePop, # Like JumpIfFalse, but also pops off the stack (regardless of truthyness). Optimization for if statements
|
||||||
JumpBackwards, # Relative, unconditional, negative jump of size x in the bytecode
|
JumpIfFalseOrPop, # Jumps to an absolute index in the bytecode if x is false and pops it otherwise (used for logical and)
|
||||||
## Long variants of jumps (they use a 24-bit operand instead of a 16-bit one)
|
## Long variants of jumps (they use a 24-bit operand instead of a 16-bit one)
|
||||||
LongJump,
|
LongJump,
|
||||||
LongJumpIfFalse,
|
LongJumpIfFalse,
|
||||||
|
@ -90,25 +96,22 @@ type
|
||||||
LongJumpForwards,
|
LongJumpForwards,
|
||||||
LongJumpBackwards,
|
LongJumpBackwards,
|
||||||
## Functions
|
## Functions
|
||||||
Call, # Calls a function
|
|
||||||
Return # Returns from the current function
|
Return # Returns from the current function
|
||||||
## Exception handling
|
## Exception handling
|
||||||
Raise, # Raises exception x or re-raises active exception if x is nil
|
Raise, # Raises exception x or re-raises active exception if x is nil
|
||||||
BeginTry, # Initiates an exception handling context
|
BeginTry, # Initiates an exception handling context
|
||||||
FinishTry, # Closes the current exception handling context
|
FinishTry, # Closes the current exception handling context
|
||||||
## Generators
|
## Generators
|
||||||
Yield,
|
Yield, # Yields control from a generator back to the caller
|
||||||
## Coroutines
|
## Coroutines
|
||||||
Await,
|
Await, # Calls an asynchronous function
|
||||||
## Collection literals
|
## Collection literals
|
||||||
BuildList,
|
BuildList,
|
||||||
BuildDict,
|
BuildDict,
|
||||||
BuildSet,
|
BuildSet,
|
||||||
BuildTuple,
|
BuildTuple,
|
||||||
## Misc
|
## Misc
|
||||||
Assert, # Raises an AssertionFailed exception if the value at the top of the stack is falsey
|
Assert, # Raises an AssertionFailed exception if the x is false
|
||||||
Slice, # Slices an object (takes 3 arguments: start, stop, step). Pushes the result of a.subscript(b, c, d) onto the stack
|
|
||||||
GetItem, # Pushes the result of a.getItem(b) onto the stack
|
|
||||||
NoOp, # Just a no-op
|
NoOp, # Just a no-op
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,14 +123,15 @@ const simpleInstructions* = {OpCode.Return, OpCode.Nil,
|
||||||
OpCode.Nan, OpCode.Inf,
|
OpCode.Nan, OpCode.Inf,
|
||||||
Pop, OpCode.Raise,
|
Pop, OpCode.Raise,
|
||||||
BeginTry, FinishTry,
|
BeginTry, FinishTry,
|
||||||
OpCode.Yield, OpCode.Await}
|
OpCode.Yield, OpCode.Await,
|
||||||
|
OpCode.NoOp}
|
||||||
|
|
||||||
# Constant instructions are instructions that operate on the bytecode constant table
|
# Constant instructions are instructions that operate on the bytecode constant table
|
||||||
const constantInstructions* = {LoadConstant, }
|
const constantInstructions* = {LoadConstant, }
|
||||||
|
|
||||||
# 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* = {Call, StoreVar, LoadVar, LoadHeap, StoreHeap}
|
const stackTripleInstructions* = {StoreVar, LoadVar, LoadHeap, StoreHeap}
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -36,10 +36,10 @@ type
|
||||||
Var, Let, Const, Is, Return,
|
Var, Let, Const, Is, Return,
|
||||||
Coroutine, Generator, Import,
|
Coroutine, Generator, Import,
|
||||||
IsNot, Raise, Assert, Await,
|
IsNot, Raise, Assert, Await,
|
||||||
Foreach, Yield, Public, As,
|
Foreach, Yield, Of, Defer,
|
||||||
Of, Defer, Try, Except, Finally,
|
Try, Except, Finally, Type,
|
||||||
Type, Operator, Case, Enum, From,
|
Operator, Case, Enum, From,
|
||||||
Emit
|
Emit, As
|
||||||
|
|
||||||
# Literal types
|
# Literal types
|
||||||
Integer, Float, String, Identifier,
|
Integer, Float, String, Identifier,
|
||||||
|
|
|
@ -445,7 +445,7 @@ proc comparison(self: Parser): Expression =
|
||||||
result = self.add()
|
result = self.add()
|
||||||
var operator: Token
|
var operator: Token
|
||||||
var right: Expression
|
var right: Expression
|
||||||
while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Is, As, Of, IsNot]):
|
while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Is, Of, IsNot]):
|
||||||
operator = self.peek(-1)
|
operator = self.peek(-1)
|
||||||
right = self.add()
|
right = self.add()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
@ -863,9 +863,9 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
||||||
## Parses functions, coroutines, generators, anonymous functions and custom operators
|
## Parses functions, coroutines, generators, anonymous functions and custom operators
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
var enclosingFunction = self.currentFunction
|
var enclosingFunction = self.currentFunction
|
||||||
var arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]] = @[]
|
var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[]
|
||||||
var defaults: seq[Expression] = @[]
|
var defaults: seq[Expression] = @[]
|
||||||
var returnType: IdentExpr
|
var returnType: Expression
|
||||||
if not isLambda and self.check(Identifier):
|
if not isLambda and self.check(Identifier):
|
||||||
# We do this extra check because we might
|
# We do this extra check because we might
|
||||||
# be called from a context where it's
|
# be called from a context where it's
|
||||||
|
@ -877,7 +877,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
||||||
self.checkDecl(not self.check(Star))
|
self.checkDecl(not self.check(Star))
|
||||||
self.currentFunction = newFunDecl(nil, arguments, defaults, newBlockStmt(@[], Token()),
|
self.currentFunction = newFunDecl(nil, arguments, defaults, newBlockStmt(@[], Token()),
|
||||||
isAsync=isAsync, isGenerator=isGenerator, isPrivate=true,
|
isAsync=isAsync, isGenerator=isGenerator, isPrivate=true,
|
||||||
token=tok, pragmas=(@[]))
|
token=tok, pragmas=(@[]), returnType=nil)
|
||||||
FunDecl(self.currentFunction).name = newIdentExpr(self.peek(-1))
|
FunDecl(self.currentFunction).name = newIdentExpr(self.peek(-1))
|
||||||
if self.match(Star):
|
if self.match(Star):
|
||||||
FunDecl(self.currentFunction).isPrivate = false
|
FunDecl(self.currentFunction).isPrivate = false
|
||||||
|
@ -894,26 +894,69 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
||||||
self.currentFunction = enclosingFunction
|
self.currentFunction = enclosingFunction
|
||||||
return result
|
return result
|
||||||
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)
|
||||||
elif not isOperator:
|
elif not isOperator:
|
||||||
self.error("funDecl: invalid state")
|
self.error("funDecl: invalid state")
|
||||||
if self.match(Colon):
|
if self.match(Colon):
|
||||||
# A function without an explicit
|
# Function has explicit return type
|
||||||
# return type is the same as a void
|
if self.match([Function, Coroutine, Generator]):
|
||||||
# function in C (i.e. no return type)
|
# The function's return type is another
|
||||||
self.expect([Identifier, Nil], "expecting function return type after ':'")
|
# function or callable object
|
||||||
returnType = newIdentExpr(self.peek(-1))
|
var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[]
|
||||||
|
var defaults: seq[Expression] = @[]
|
||||||
|
returnType = newLambdaExpr(arguments, defaults, nil, isGenerator=self.peek(-1).kind == Generator,
|
||||||
|
isAsync=self.peek(-1).kind == Coroutine,
|
||||||
|
token=self.peek(-1), returnType=nil)
|
||||||
|
var parameter: tuple[name: IdentExpr, valueType: Expression]
|
||||||
|
if self.match(LeftParen):
|
||||||
|
var hasType = false
|
||||||
|
var j = 0
|
||||||
|
while not self.check(RightParen):
|
||||||
|
if arguments.len > 255:
|
||||||
|
self.error("cannot have more than 255 arguments in function declaration")
|
||||||
|
self.expect(Identifier, "expecting parameter name")
|
||||||
|
parameter.name = newIdentExpr(self.peek(-1))
|
||||||
|
if self.match(Colon):
|
||||||
|
hasType = true
|
||||||
|
parameter.valueType = self.expression()
|
||||||
|
for i in j..arguments.high():
|
||||||
|
arguments[i].valueType = parameter.valueType
|
||||||
|
else:
|
||||||
|
hasType = false
|
||||||
|
j = arguments.high()
|
||||||
|
if parameter in arguments:
|
||||||
|
self.error("duplicate parameter name in function declaration")
|
||||||
|
arguments.add(parameter)
|
||||||
|
if self.match(Equal):
|
||||||
|
defaults.add(self.expression())
|
||||||
|
elif defaults.len() > 0:
|
||||||
|
self.error("positional argument cannot follow default argument in function declaration")
|
||||||
|
if not self.match(Comma):
|
||||||
|
break
|
||||||
|
self.expect(RightParen)
|
||||||
|
if not hasType:
|
||||||
|
self.error("cannot omit type declaration for function parameters")
|
||||||
|
if self.match(Colon):
|
||||||
|
LambdaExpr(returnType).returnType = self.expression()
|
||||||
if not self.match(LeftBrace):
|
if not self.match(LeftBrace):
|
||||||
var parameter: tuple[name: IdentExpr, valueType: IdentExpr]
|
var parameter: tuple[name: IdentExpr, valueType: Expression]
|
||||||
self.expect(LeftParen)
|
self.expect(LeftParen)
|
||||||
while not self.check(RightParen):
|
while not self.check(RightParen):
|
||||||
|
var hasType = false
|
||||||
|
var j = 0
|
||||||
if arguments.len > 255:
|
if arguments.len > 255:
|
||||||
self.error("cannot have more than 255 arguments in function declaration")
|
self.error("cannot have more than 255 arguments in function declaration")
|
||||||
self.expect(Identifier, "expecting parameter name")
|
self.expect(Identifier, "expecting parameter name")
|
||||||
parameter.name = newIdentExpr(self.peek(-1))
|
parameter.name = newIdentExpr(self.peek(-1))
|
||||||
self.expect(Colon, "expecting ':' after parameter name")
|
if self.match(Colon):
|
||||||
self.expect(Identifier, "expecting parameter type")
|
hasType = true
|
||||||
parameter.valueType = newIdentExpr(self.peek(-1))
|
parameter.valueType = self.expression()
|
||||||
|
for i in j..arguments.high():
|
||||||
|
arguments[i].valueType = parameter.valueType
|
||||||
|
else:
|
||||||
|
hasType = false
|
||||||
|
j = arguments.high()
|
||||||
if parameter in arguments:
|
if parameter in arguments:
|
||||||
self.error("duplicate parameter name in function declaration")
|
self.error("duplicate parameter name in function declaration")
|
||||||
arguments.add(parameter)
|
arguments.add(parameter)
|
||||||
|
@ -926,8 +969,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
||||||
self.expect(RightParen)
|
self.expect(RightParen)
|
||||||
if self.match(Colon):
|
if self.match(Colon):
|
||||||
# Function's return type
|
# Function's return type
|
||||||
self.expect(Identifier, "expecting return type after ':'")
|
returnType = self.expression()
|
||||||
returnType = newIdentExpr(self.peek(-1))
|
|
||||||
self.expect(LeftBrace)
|
self.expect(LeftBrace)
|
||||||
if self.currentFunction.kind == funDecl:
|
if self.currentFunction.kind == funDecl:
|
||||||
if not self.match(Semicolon):
|
if not self.match(Semicolon):
|
||||||
|
|
|
@ -29,7 +29,7 @@ proc getLineEditor: LineEditor
|
||||||
|
|
||||||
# Handy dandy compile-time constants
|
# Handy dandy compile-time constants
|
||||||
const debugLexer = false
|
const debugLexer = false
|
||||||
const debugParser = false
|
const debugParser = true
|
||||||
const debugCompiler = true
|
const debugCompiler = true
|
||||||
const debugOptimizer = false
|
const debugOptimizer = false
|
||||||
const debugSerializer = true
|
const debugSerializer = true
|
||||||
|
@ -264,7 +264,7 @@ proc fillSymbolTable(tokenizer: Lexer) =
|
||||||
# and specialize them later
|
# and specialize them later
|
||||||
tokenizer.symbols.addKeyword("isnot", IsNot)
|
tokenizer.symbols.addKeyword("isnot", IsNot)
|
||||||
tokenizer.symbols.addKeyword("is", Is)
|
tokenizer.symbols.addKeyword("is", Is)
|
||||||
tokenizer.symbols.addKeyword("as", As)
|
tokenizer.symbols.addKeyword("is", As)
|
||||||
tokenizer.symbols.addKeyword("of", Of)
|
tokenizer.symbols.addKeyword("of", Of)
|
||||||
tokenizer.symbols.addKeyword("and", TokenType.LogicalAnd)
|
tokenizer.symbols.addKeyword("and", TokenType.LogicalAnd)
|
||||||
tokenizer.symbols.addKeyword("or", TokenType.LogicalOr)
|
tokenizer.symbols.addKeyword("or", TokenType.LogicalOr)
|
||||||
|
|
Loading…
Reference in New Issue