derete the fil #2
|
@ -33,20 +33,28 @@ export multibyte
|
|||
|
||||
|
||||
type
|
||||
NameKind = enum
|
||||
Function, Type, Var
|
||||
Name = ref object
|
||||
## A compile-time wrapper around
|
||||
## statically resolved names.
|
||||
## Depth indicates to which scope
|
||||
## the variable belongs, zero meaning
|
||||
## the global one
|
||||
name: IdentExpr
|
||||
owner: string
|
||||
depth: int
|
||||
isPrivate: bool
|
||||
isConst: bool
|
||||
isLet: bool
|
||||
valueType: IdentExpr
|
||||
codePos: int
|
||||
kind: NameKind
|
||||
name: IdentExpr # Name of the identifier
|
||||
owner: string # Owner of the identifier
|
||||
depth: int # Scope depth
|
||||
isPrivate: bool # Is this name private?
|
||||
isConst: bool # Is this a constant?
|
||||
isLet: bool # Can this name's value be mutated?
|
||||
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
|
||||
## A "loop object" used
|
||||
|
@ -54,9 +62,9 @@ type
|
|||
## appropriate jump offsets
|
||||
## for continue and break
|
||||
## statements
|
||||
start: int
|
||||
depth: int
|
||||
breakPos: seq[int]
|
||||
start: int # Position in the bytecode where the loop starts
|
||||
depth: int # Scope depth where the loop is located
|
||||
breakPos: seq[int] # List of positions where
|
||||
|
||||
Compiler* = ref object
|
||||
## A wrapper around the compiler's state
|
||||
|
@ -133,7 +141,6 @@ proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >=
|
|||
|
||||
|
||||
## Utility functions
|
||||
|
||||
proc peek(self: Compiler, distance: int = 0): ASTNode =
|
||||
## Peeks at the AST node at the given distance.
|
||||
## 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) =
|
||||
## Patches a previously emitted jump
|
||||
## using emitJump. Since emitJump assumes
|
||||
## Patches a previously emitted relative
|
||||
## jump using emitJump. Since emitJump assumes
|
||||
## a long jump, this also shrinks the jump
|
||||
## offset and changes the bytecode instruction if possible
|
||||
## (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)
|
||||
let jump: int = self.chunk.code.len() - offset
|
||||
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:
|
||||
case OpCode(self.chunk.code[offset]):
|
||||
of LongJumpForwards:
|
||||
|
@ -248,7 +255,7 @@ proc patchJump(self: Compiler, offset: int) =
|
|||
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
||||
else:
|
||||
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!
|
||||
self.chunk.code[offset + 1] = offsetArray[0]
|
||||
self.chunk.code[offset + 2] = offsetArray[1]
|
||||
|
@ -290,9 +297,6 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
self.emitByte(OpCode.Nan)
|
||||
of strExpr:
|
||||
self.emitConstant(node)
|
||||
# The optimizer will emit warning
|
||||
# for overflowing numbers. Here, we
|
||||
# treat them as errors
|
||||
of intExpr:
|
||||
var x: int
|
||||
var y = IntExpr(node)
|
||||
|
@ -301,11 +305,6 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
except ValueError:
|
||||
self.error("integer value out of range")
|
||||
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:
|
||||
var x: int
|
||||
var y = HexExpr(node)
|
||||
|
@ -347,7 +346,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
of listExpr:
|
||||
var y = ListExpr(node)
|
||||
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:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildList)
|
||||
|
@ -355,7 +354,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
of tupleExpr:
|
||||
var y = TupleExpr(node)
|
||||
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:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildTuple)
|
||||
|
@ -363,7 +362,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
of setExpr:
|
||||
var y = SetExpr(node)
|
||||
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:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildSet)
|
||||
|
@ -371,7 +370,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
of dictExpr:
|
||||
var y = DictExpr(node)
|
||||
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):
|
||||
self.expression(key)
|
||||
self.expression(value)
|
||||
|
@ -382,7 +381,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
self.expression(y.expression)
|
||||
self.emitByte(OpCode.Await)
|
||||
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) =
|
||||
|
@ -399,59 +398,84 @@ proc unary(self: Compiler, node: UnaryExpr) =
|
|||
of Tilde:
|
||||
self.emitByte(NoOp)
|
||||
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) =
|
||||
## Compiles all binary expressions
|
||||
|
||||
# These two lines prepare the stack by pushing the
|
||||
# opcode's operands onto it
|
||||
self.expression(node.a)
|
||||
self.expression(node.b)
|
||||
# TODO: Find implementation of
|
||||
# the given operator and call it
|
||||
case node.operator.kind:
|
||||
of Plus:
|
||||
# a + b
|
||||
self.emitByte(NoOp)
|
||||
of Minus:
|
||||
# a - b
|
||||
self.emitByte(NoOp)
|
||||
of Star:
|
||||
# a * b
|
||||
self.emitByte(NoOp)
|
||||
of DoubleStar:
|
||||
# a ** b
|
||||
self.emitByte(NoOp)
|
||||
of Percentage:
|
||||
# a % b
|
||||
self.emitByte(NoOp)
|
||||
of FloorDiv:
|
||||
# a // b
|
||||
self.emitByte(NoOp)
|
||||
of Slash:
|
||||
# a / b
|
||||
self.emitByte(NoOp)
|
||||
of Ampersand:
|
||||
# a & b
|
||||
self.emitByte(NoOp)
|
||||
of Caret:
|
||||
# a ^ b
|
||||
self.emitByte(NoOp)
|
||||
of Pipe:
|
||||
self.emitByte(NoOp)
|
||||
of As:
|
||||
# a | b
|
||||
self.emitByte(NoOp)
|
||||
of Is:
|
||||
# a is b
|
||||
self.emitByte(NoOp)
|
||||
of IsNot:
|
||||
# a isnot b
|
||||
self.emitByte(NoOp)
|
||||
of Of:
|
||||
# a of b
|
||||
self.emitByte(NoOp)
|
||||
of As:
|
||||
# a as b
|
||||
self.emitByte(NoOp)
|
||||
of RightShift:
|
||||
# a >> b
|
||||
self.emitByte(NoOp)
|
||||
of LeftShift:
|
||||
# a << b
|
||||
self.emitByte(NoOp)
|
||||
of LessThan:
|
||||
# a < b
|
||||
self.emitByte(NoOp)
|
||||
of GreaterThan:
|
||||
# a > b
|
||||
self.emitByte(NoOp)
|
||||
of DoubleEqual:
|
||||
# a == b
|
||||
self.emitByte(NoOp)
|
||||
of LessOrEqual:
|
||||
# a <= b
|
||||
self.emitByte(NoOp)
|
||||
of GreaterOrEqual:
|
||||
# a >= b
|
||||
self.emitByte(NoOp)
|
||||
of LogicalAnd:
|
||||
# a and b
|
||||
self.expression(node.a)
|
||||
var jump: int
|
||||
if self.enableOptimizations:
|
||||
|
@ -462,17 +486,16 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
self.expression(node.b)
|
||||
self.patchJump(jump)
|
||||
of LogicalOr:
|
||||
# a or b
|
||||
self.expression(node.a)
|
||||
let jump = self.emitJump(JumpIfTrue)
|
||||
self.expression(node.b)
|
||||
self.patchJump(jump)
|
||||
# TODO: In-place operations
|
||||
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: IdentExpr) =
|
||||
proc declareName(self: Compiler, node: Declaration, kind: Expression) =
|
||||
## Statically declares a name into the current scope
|
||||
case node.kind:
|
||||
of NodeKind.varDecl:
|
||||
|
@ -489,28 +512,26 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
|||
isConst: node.isConst,
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len(),
|
||||
isLet: node.isLet))
|
||||
isLet: node.isLet,
|
||||
kind: Var))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
of funDecl:
|
||||
of NodeKind.funDecl:
|
||||
var node = FunDecl(node)
|
||||
# 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,
|
||||
isPrivate: node.isPrivate,
|
||||
isConst: false,
|
||||
owner: self.currentModule,
|
||||
valueType: node.returnType,
|
||||
codePos: self.chunk.code.len(),
|
||||
codePos: -1,
|
||||
name: node.name,
|
||||
isLet: false))
|
||||
self.emitByte(StoreVar)
|
||||
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)
|
||||
isLet: false,
|
||||
kind: Function))
|
||||
for argument in node.arguments:
|
||||
if self.names.high() > 16777215:
|
||||
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,
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len(),
|
||||
isLet: false))
|
||||
isLet: false,
|
||||
kind: Var))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
self.scopeDepth -= 1
|
||||
# TODO: Default arguments and unpacking
|
||||
else:
|
||||
discard # Unreachable
|
||||
|
@ -608,6 +629,7 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
|||
proc identifier(self: Compiler, node: IdentExpr) =
|
||||
## Compiles access to identifiers
|
||||
let s = self.resolve(node)
|
||||
echo s[]
|
||||
if s == nil:
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
elif s.isConst:
|
||||
|
@ -641,8 +663,7 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
case node.kind:
|
||||
of assignExpr:
|
||||
var node = AssignExpr(node)
|
||||
# TODO: This will explode with slicing!
|
||||
var name = IdentExpr(node.name)
|
||||
let name = IdentExpr(node.name)
|
||||
let r = self.resolve(name)
|
||||
if r == nil:
|
||||
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
|
||||
# of boilerplate code to make closures work, but
|
||||
# that's about it
|
||||
|
||||
self.emitBytes(OpCode.Nil, OpCode.Return)
|
||||
|
||||
# Currently defer is not functional so we
|
||||
|
|
|
@ -184,11 +184,11 @@ type
|
|||
|
||||
LambdaExpr* = ref object of Expression
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
|
||||
defaults*: seq[Expression]
|
||||
isGenerator*: bool
|
||||
isAsync*: bool
|
||||
returnType*: IdentExpr
|
||||
returnType*: Expression
|
||||
|
||||
SliceExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
|
@ -258,17 +258,17 @@ type
|
|||
isConst*: bool
|
||||
isPrivate*: bool
|
||||
isLet*: bool
|
||||
valueType*: IdentExpr
|
||||
valueType*: Expression
|
||||
|
||||
FunDecl* = ref object of Declaration
|
||||
name*: IdentExpr
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
|
||||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
isPrivate*: bool
|
||||
returnType*: IdentExpr
|
||||
returnType*: Expression
|
||||
|
||||
|
||||
proc newASTNode*(kind: NodeKind, token: Token): ASTNode =
|
||||
|
@ -371,8 +371,8 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression], body: Statement,
|
||||
isGenerator: bool, isAsync: bool, token: Token): LambdaExpr =
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression], body: Statement,
|
||||
isGenerator: bool, isAsync: bool, token: Token, returnType: Expression): LambdaExpr =
|
||||
result = LambdaExpr(kind: lambdaExpr)
|
||||
result.body = body
|
||||
result.arguments = arguments
|
||||
|
@ -380,6 +380,7 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
|||
result.isGenerator = isGenerator
|
||||
result.isAsync = isAsync
|
||||
result.token = token
|
||||
result.returnType = returnType
|
||||
|
||||
|
||||
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,
|
||||
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.name = name
|
||||
result.value = value
|
||||
|
@ -590,9 +591,10 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
|||
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,
|
||||
isPrivate: bool, token: Token, pragmas: seq[Token]): FunDecl =
|
||||
isPrivate: bool, token: Token, pragmas: seq[Token],
|
||||
returnType: Expression): FunDecl =
|
||||
result = FunDecl(kind: funDecl)
|
||||
result.name = name
|
||||
result.arguments = arguments
|
||||
|
@ -603,6 +605,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
|
|||
result.isPrivate = isPrivate
|
||||
result.token = token
|
||||
result.pragmas = pragmas
|
||||
result.returnType = returnType
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -50,11 +50,17 @@ type
|
|||
OpCode* {.pure.} = enum
|
||||
## Enum of possible opcodes.
|
||||
|
||||
# Note: x represents the
|
||||
# argument to unary opcodes, while
|
||||
# a and b represent arguments to binary
|
||||
# opcodes. Other variable names (c, d, etc.)
|
||||
# may be used for more complex opcodes
|
||||
# Note: x represents the argument
|
||||
# to unary opcodes, while a and b
|
||||
# represent arguments to binary
|
||||
# opcodes. Other variable names (c, d, ...)
|
||||
# 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
|
||||
## Constant opcodes (each of them pushes a constant singleton on the stack)
|
||||
|
@ -68,19 +74,19 @@ type
|
|||
Push, # Pushes x onto the stack
|
||||
PopN, # Pops x elements off the stack (optimization for exiting scopes and returning from functions)
|
||||
## Name resolution/handling
|
||||
LoadAttribute,
|
||||
LoadVar, # Pushes the object at position x in the stack
|
||||
LoadAttribute, # Pushes the attribute b of object a onto the stack
|
||||
LoadVar, # Pushes the object at position x in the stack onto the stack
|
||||
StoreVar, # Stores the value of b at position a in the stack
|
||||
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
|
||||
## Looping and jumping
|
||||
Jump, # Absolute, unconditional jump into the bytecode at offset x
|
||||
JumpIfFalse, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is falsey
|
||||
JumpIfTrue, # Jumps to an absolute index (x) 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 (x) in the bytecode if the value at the top of the stack is falsey and pops it otherwise
|
||||
JumpForwards, # Relative, unconditional, positive jump of size x in the bytecode
|
||||
JumpBackwards, # Relative, unconditional, negative jump of size x in the bytecode
|
||||
Jump, # Absolute, unconditional jump into the bytecode
|
||||
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||
JumpBackwards, # Relative, unconditional, negative jump in the bytecode
|
||||
JumpIfFalse, # Jumps to an absolute index in the bytecode if x is true
|
||||
JumpIfTrue, # Jumps to an absolute index in the bytecode if x is false
|
||||
JumpIfFalsePop, # Like JumpIfFalse, but also pops off the stack (regardless of truthyness). Optimization for if statements
|
||||
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)
|
||||
LongJump,
|
||||
LongJumpIfFalse,
|
||||
|
@ -90,25 +96,22 @@ type
|
|||
LongJumpForwards,
|
||||
LongJumpBackwards,
|
||||
## Functions
|
||||
Call, # Calls a function
|
||||
Return # Returns from the current function
|
||||
## Exception handling
|
||||
Raise, # Raises exception x or re-raises active exception if x is nil
|
||||
BeginTry, # Initiates an exception handling context
|
||||
FinishTry, # Closes the current exception handling context
|
||||
## Generators
|
||||
Yield,
|
||||
Yield, # Yields control from a generator back to the caller
|
||||
## Coroutines
|
||||
Await,
|
||||
Await, # Calls an asynchronous function
|
||||
## Collection literals
|
||||
BuildList,
|
||||
BuildDict,
|
||||
BuildSet,
|
||||
BuildTuple,
|
||||
## Misc
|
||||
Assert, # Raises an AssertionFailed exception if the value at the top of the stack is falsey
|
||||
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
|
||||
Assert, # Raises an AssertionFailed exception if the x is false
|
||||
NoOp, # Just a no-op
|
||||
|
||||
|
||||
|
@ -120,14 +123,15 @@ const simpleInstructions* = {OpCode.Return, OpCode.Nil,
|
|||
OpCode.Nan, OpCode.Inf,
|
||||
Pop, OpCode.Raise,
|
||||
BeginTry, FinishTry,
|
||||
OpCode.Yield, OpCode.Await}
|
||||
OpCode.Yield, OpCode.Await,
|
||||
OpCode.NoOp}
|
||||
|
||||
# Constant instructions are instructions that operate on the bytecode constant table
|
||||
const constantInstructions* = {LoadConstant, }
|
||||
|
||||
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||
# 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
|
||||
# of 16 bit integers
|
||||
|
|
|
@ -36,10 +36,10 @@ type
|
|||
Var, Let, Const, Is, Return,
|
||||
Coroutine, Generator, Import,
|
||||
IsNot, Raise, Assert, Await,
|
||||
Foreach, Yield, Public, As,
|
||||
Of, Defer, Try, Except, Finally,
|
||||
Type, Operator, Case, Enum, From,
|
||||
Emit
|
||||
Foreach, Yield, Of, Defer,
|
||||
Try, Except, Finally, Type,
|
||||
Operator, Case, Enum, From,
|
||||
Emit, As
|
||||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
|
|
|
@ -445,7 +445,7 @@ proc comparison(self: Parser): Expression =
|
|||
result = self.add()
|
||||
var operator: Token
|
||||
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)
|
||||
right = self.add()
|
||||
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
|
||||
let tok = self.peek(-1)
|
||||
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 returnType: IdentExpr
|
||||
var returnType: Expression
|
||||
if not isLambda and self.check(Identifier):
|
||||
# We do this extra check because we might
|
||||
# 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.currentFunction = newFunDecl(nil, arguments, defaults, newBlockStmt(@[], Token()),
|
||||
isAsync=isAsync, isGenerator=isGenerator, isPrivate=true,
|
||||
token=tok, pragmas=(@[]))
|
||||
token=tok, pragmas=(@[]), returnType=nil)
|
||||
FunDecl(self.currentFunction).name = newIdentExpr(self.peek(-1))
|
||||
if self.match(Star):
|
||||
FunDecl(self.currentFunction).isPrivate = false
|
||||
|
@ -894,26 +894,69 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
self.currentFunction = enclosingFunction
|
||||
return result
|
||||
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:
|
||||
self.error("funDecl: invalid state")
|
||||
if self.match(Colon):
|
||||
# A function without an explicit
|
||||
# return type is the same as a void
|
||||
# function in C (i.e. no return type)
|
||||
self.expect([Identifier, Nil], "expecting function return type after ':'")
|
||||
returnType = newIdentExpr(self.peek(-1))
|
||||
# Function has explicit return type
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
# The function's return type is another
|
||||
# function or callable object
|
||||
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):
|
||||
var parameter: tuple[name: IdentExpr, valueType: IdentExpr]
|
||||
var parameter: tuple[name: IdentExpr, valueType: Expression]
|
||||
self.expect(LeftParen)
|
||||
while not self.check(RightParen):
|
||||
var hasType = false
|
||||
var j = 0
|
||||
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))
|
||||
self.expect(Colon, "expecting ':' after parameter name")
|
||||
self.expect(Identifier, "expecting parameter type")
|
||||
parameter.valueType = 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)
|
||||
|
@ -926,8 +969,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
self.expect(RightParen)
|
||||
if self.match(Colon):
|
||||
# Function's return type
|
||||
self.expect(Identifier, "expecting return type after ':'")
|
||||
returnType = newIdentExpr(self.peek(-1))
|
||||
returnType = self.expression()
|
||||
self.expect(LeftBrace)
|
||||
if self.currentFunction.kind == funDecl:
|
||||
if not self.match(Semicolon):
|
||||
|
|
|
@ -29,7 +29,7 @@ proc getLineEditor: LineEditor
|
|||
|
||||
# Handy dandy compile-time constants
|
||||
const debugLexer = false
|
||||
const debugParser = false
|
||||
const debugParser = true
|
||||
const debugCompiler = true
|
||||
const debugOptimizer = false
|
||||
const debugSerializer = true
|
||||
|
@ -264,7 +264,7 @@ proc fillSymbolTable(tokenizer: Lexer) =
|
|||
# and specialize them later
|
||||
tokenizer.symbols.addKeyword("isnot", IsNot)
|
||||
tokenizer.symbols.addKeyword("is", Is)
|
||||
tokenizer.symbols.addKeyword("as", As)
|
||||
tokenizer.symbols.addKeyword("is", As)
|
||||
tokenizer.symbols.addKeyword("of", Of)
|
||||
tokenizer.symbols.addKeyword("and", TokenType.LogicalAnd)
|
||||
tokenizer.symbols.addKeyword("or", TokenType.LogicalOr)
|
||||
|
|
Loading…
Reference in New Issue