also derete this #3

Closed
N00nehere wants to merge 49 commits from (deleted):n00nehere-patch-2 into master
6 changed files with 177 additions and 108 deletions
Showing only changes of commit 8ca5caabb7 - Show all commits

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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):

View File

@ -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)