derete the fil #2
|
@ -15,6 +15,7 @@ import meta/token
|
|||
import meta/ast
|
||||
import meta/errors
|
||||
import meta/bytecode
|
||||
import meta/typing
|
||||
import ../config
|
||||
import ../util/multibyte
|
||||
|
||||
|
@ -22,7 +23,6 @@ import ../util/multibyte
|
|||
import strformat
|
||||
import algorithm
|
||||
import parseutils
|
||||
import sequtils
|
||||
import strutils
|
||||
|
||||
|
||||
|
@ -33,28 +33,6 @@ export multibyte
|
|||
|
||||
|
||||
type
|
||||
TypeKind = enum
|
||||
## An enumeration of compile-time
|
||||
## types
|
||||
Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Function, CustomType,
|
||||
Dict, List, Tuple, Set, Nil, Nan, Bool,
|
||||
Inf
|
||||
Type = ref object
|
||||
## A wrapper around
|
||||
## compile-time types
|
||||
node: ASTNode
|
||||
case kind: TypeKind:
|
||||
of Function:
|
||||
returnType: Type
|
||||
of List, Tuple, Set:
|
||||
memberType: Type
|
||||
of Dict:
|
||||
keyType: Type
|
||||
valueType: Type
|
||||
else:
|
||||
discard
|
||||
Name = ref object
|
||||
## A compile-time wrapper around
|
||||
## statically resolved names.
|
||||
|
@ -348,19 +326,13 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
|||
## than the given one and modifies the code emitted for it
|
||||
## to store it as a closure variable if it is. Does nothing if the name
|
||||
## hasn't been declared yet or is unreachable (for example if it's
|
||||
## declared as private in another module), if the name itself is a
|
||||
## global variable and if either the current or the outer scope are
|
||||
## the global (outermost) one. This function must be called each
|
||||
## time a name is referenced in order for closed-over variables
|
||||
## declared as private in another module). This function must be called
|
||||
## each time a name is referenced in order for closed-over variables
|
||||
## to be emitted properly, otherwise the runtime may behave
|
||||
## unpredictably or crash
|
||||
if depth == 0 or depth - 1 == 0:
|
||||
return
|
||||
let entry = self.resolve(name)
|
||||
if entry == nil:
|
||||
return
|
||||
if entry.depth == 0:
|
||||
return
|
||||
if entry.depth < depth:
|
||||
# Ding! The given name is closed over: we need to
|
||||
# change the StoreVar instruction that created this
|
||||
|
@ -370,7 +342,7 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
|||
# whether or not this function is called
|
||||
self.closedOver.add(entry.name)
|
||||
if self.closedOver.len() >= 16777216:
|
||||
self.error("too many consecutive closure-over variables (max is 16777216)")
|
||||
self.error("too many consecutive closed-over variables (max is 16777216)")
|
||||
let idx = self.closedOver.high().toTriple()
|
||||
self.chunk.code[entry.codePos] = StoreHeap.uint8
|
||||
self.chunk.code[entry.codePos + 1] = idx[0]
|
||||
|
@ -378,70 +350,10 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
|||
self.chunk.code[entry.codePos + 3] = idx[2]
|
||||
|
||||
|
||||
proc toIntrinsic(name: string): Type =
|
||||
## Converts a string to an intrinsic
|
||||
## type if it is valid and returns nil
|
||||
## otherwise
|
||||
if name in ["int", "int64", "i64"]:
|
||||
return Type(kind: Int64)
|
||||
elif name in ["uint64", "u64"]:
|
||||
return Type(kind: UInt64)
|
||||
elif name in ["int32", "i32"]:
|
||||
return Type(kind: Int32)
|
||||
elif name in ["uint32", "u32"]:
|
||||
return Type(kind: UInt32)
|
||||
elif name in ["int16", "i16"]:
|
||||
return Type(kind: Int16)
|
||||
elif name in ["uint16", "u16"]:
|
||||
return Type(kind: UInt16)
|
||||
elif name in ["int8", "i8"]:
|
||||
return Type(kind: Int8)
|
||||
elif name in ["uint8", "u8"]:
|
||||
return Type(kind: UInt8)
|
||||
elif name in ["f64", "float", "float64"]:
|
||||
return Type(kind: Float64)
|
||||
elif name in ["f32", "float32"]:
|
||||
return Type(kind: Float32)
|
||||
elif name == "byte":
|
||||
return Type(kind: Byte)
|
||||
elif name == "char":
|
||||
return Type(kind: Char)
|
||||
elif name == "nan":
|
||||
return Type(kind: Nan)
|
||||
elif name == "nil":
|
||||
return Type(kind: Nil)
|
||||
elif name == "inf":
|
||||
return Type(kind: Inf)
|
||||
elif name == "bool":
|
||||
return Type(kind: Bool)
|
||||
else:
|
||||
return nil
|
||||
|
||||
|
||||
proc toIntrinsic(typ: Expression): Type =
|
||||
## Gets an expression's
|
||||
## intrinsic type, if possible
|
||||
if typ == nil:
|
||||
return nil
|
||||
case typ.kind:
|
||||
of identExpr:
|
||||
return typ.token.lexeme.toIntrinsic()
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
proc inferValueType(self: Compiler, node: ASTNode): Type =
|
||||
## Infers the type of a given literal expression
|
||||
case node.kind:
|
||||
of listExpr:
|
||||
return Type(kind: List, memberType: self.inferExprType(ListExpr(node).valueType))
|
||||
of tupleExpr:
|
||||
return Type(kind: Tuple, memberType: self.inferExprType(TupleExpr(node).valueType))
|
||||
of setExpr:
|
||||
return Type(kind: Set, memberType: self.inferExprType(SetExpr(node).valueType))
|
||||
of dictExpr:
|
||||
let node = DictExpr(node)
|
||||
return Type(kind: Dict, keyType: self.inferExprType(node.valueType), valueType: self.inferExprType(node.valueType))
|
||||
of intExpr, binExpr, octExpr, hexExpr:
|
||||
let node = LiteralExpr(node)
|
||||
let size = node.token.lexeme.split("'")
|
||||
|
@ -473,9 +385,9 @@ proc inferValueType(self: Compiler, node: ASTNode): Type =
|
|||
of falseExpr:
|
||||
return Type(kind: Bool)
|
||||
of nanExpr:
|
||||
return Type(kind: Nan)
|
||||
return Type(kind: TypeKind.Nan)
|
||||
of infExpr:
|
||||
return Type(kind: Inf)
|
||||
return Type(kind: TypeKind.Inf)
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
|
@ -499,8 +411,7 @@ proc inferExprType(self: Compiler, node: ASTNode): Type =
|
|||
return a
|
||||
of {intExpr, hexExpr, binExpr, octExpr,
|
||||
strExpr, falseExpr, trueExpr, infExpr,
|
||||
nanExpr, floatExpr, nilExpr, listExpr,
|
||||
dictExpr, setExpr, tupleExpr
|
||||
nanExpr, floatExpr, nilExpr
|
||||
}:
|
||||
return self.inferValueType(node)
|
||||
else:
|
||||
|
@ -528,10 +439,13 @@ proc inferDeclType(self: Compiler, node: Declaration): Type =
|
|||
|
||||
|
||||
proc typeToStr(self: Compiler, typ: Type): string =
|
||||
## Returns the string representation of a
|
||||
## type object
|
||||
case typ.kind:
|
||||
of {Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, Nan, Bool, Inf}:
|
||||
of Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||
TypeKind.Inf:
|
||||
return ($typ.kind).toLowerAscii()
|
||||
of Function:
|
||||
result = "function ("
|
||||
|
@ -552,35 +466,27 @@ proc typeToStr(self: Compiler, typ: Type): string =
|
|||
result &= ")"
|
||||
else:
|
||||
discard # Unreachable
|
||||
of List, Tuple, Set:
|
||||
result &= &"{($typ.kind).toLowerAscii()}["
|
||||
of Dict:
|
||||
result &= &"{($typ.kind).toLowerAscii()}[]"
|
||||
else:
|
||||
discard
|
||||
|
||||
proc `==`(self, other: Type): bool =
|
||||
if system.`==`(self, nil):
|
||||
return system.`==`(other, nil)
|
||||
elif system.`==`(other, nil):
|
||||
return system.`==`(self, nil)
|
||||
if self.kind != other.kind:
|
||||
return false
|
||||
case self.kind:
|
||||
of {Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, Nan, Bool, Inf}:
|
||||
return true
|
||||
of Function:
|
||||
discard # TODO
|
||||
of List, Tuple, Set:
|
||||
return self.memberType == other.memberType
|
||||
of Dict:
|
||||
return self.keyType == other.keyType and self.valueType == other.valueType
|
||||
|
||||
proc toIntrinsic(self: Compiler, typ: Expression): Type =
|
||||
## Gets an expression's
|
||||
## intrinsic type, if possible
|
||||
if typ == nil:
|
||||
return nil
|
||||
case typ.kind:
|
||||
of trueExpr, falseExpr, intExpr, floatExpr:
|
||||
return typ.token.lexeme.toIntrinsic()
|
||||
of identExpr:
|
||||
let inferred = self.inferExprType(typ)
|
||||
if inferred != nil:
|
||||
return
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
|
||||
## End of utility functions
|
||||
|
||||
proc literal(self: Compiler, node: ASTNode) =
|
||||
|
@ -646,39 +552,6 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
except ValueError:
|
||||
self.error("floating point value out of range")
|
||||
self.emitConstant(y)
|
||||
of listExpr:
|
||||
var y = ListExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("list literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildList)
|
||||
self.emitBytes(y.members.len().toTriple()) # 24-bit integer, meaning collection literals can have up to 2^24 elements
|
||||
of tupleExpr:
|
||||
var y = TupleExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("tuple literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildTuple)
|
||||
self.emitBytes(y.members.len().toTriple())
|
||||
of setExpr:
|
||||
var y = SetExpr(node)
|
||||
if y.members.len() > 16777216:
|
||||
self.error("set literals can't have more than 16777216 elements")
|
||||
for member in y.members:
|
||||
self.expression(member)
|
||||
self.emitByte(BuildSet)
|
||||
self.emitBytes(y.members.len().toTriple())
|
||||
of dictExpr:
|
||||
var y = DictExpr(node)
|
||||
if y.keys.len() > 16777216:
|
||||
self.error("dict literals can't have more than 16777216 elements")
|
||||
for (key, value) in zip(y.keys, y.values):
|
||||
self.expression(key)
|
||||
self.expression(value)
|
||||
self.emitByte(BuildDict)
|
||||
self.emitBytes(y.keys.len().toTriple())
|
||||
of awaitExpr:
|
||||
var y = AwaitExpr(node)
|
||||
self.expression(y.expression)
|
||||
|
@ -1077,8 +950,7 @@ proc expression(self: Compiler, node: ASTNode) =
|
|||
# Binary expressions such as 2 ^ 5 and 0.66 * 3.14
|
||||
self.binary(BinaryExpr(node))
|
||||
of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr,
|
||||
infExpr, nanExpr, floatExpr, nilExpr, tupleExpr, setExpr, listExpr,
|
||||
dictExpr:
|
||||
infExpr, nanExpr, floatExpr, nilExpr:
|
||||
# Since all of these AST nodes mostly share
|
||||
# the same overall structure, and the kind
|
||||
# discriminant is enough to tell one
|
||||
|
@ -1233,7 +1105,7 @@ proc statement(self: Compiler, node: ASTNode) =
|
|||
|
||||
proc varDecl(self: Compiler, node: VarDecl) =
|
||||
## Compiles variable declarations
|
||||
let kind = node.valueType.toIntrinsic()
|
||||
let kind = self.toIntrinsic(node.valueType)
|
||||
let typ = self.inferExprType(node.value)
|
||||
if kind == nil and typ == nil:
|
||||
self.error(&"cannot determine the type of '{node.name.token.lexeme}'")
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
import strformat
|
||||
import strutils
|
||||
import sequtils
|
||||
|
||||
|
||||
import token
|
||||
|
@ -65,10 +64,6 @@ type
|
|||
# Primary expressions
|
||||
groupingExpr, # Parenthesized expressions such as (true) and (3 + 4)
|
||||
trueExpr,
|
||||
listExpr,
|
||||
tupleExpr,
|
||||
dictExpr,
|
||||
setExpr,
|
||||
falseExpr,
|
||||
strExpr,
|
||||
charExpr,
|
||||
|
@ -132,20 +127,6 @@ type
|
|||
NanExpr* = ref object of LiteralExpr
|
||||
InfExpr* = ref object of LiteralExpr
|
||||
|
||||
ListExpr* = ref object of LiteralExpr
|
||||
members*: seq[Expression]
|
||||
valueType*: IdentExpr
|
||||
|
||||
SetExpr* = ref object of ListExpr
|
||||
|
||||
TupleExpr* = ref object of ListExpr
|
||||
|
||||
DictExpr* = ref object of Expression
|
||||
keys*: seq[Expression]
|
||||
values*: seq[Expression]
|
||||
keyType*: IdentExpr
|
||||
valueType*: IdentExpr
|
||||
|
||||
IdentExpr* = ref object of LiteralExpr
|
||||
name*: Token
|
||||
|
||||
|
@ -184,7 +165,7 @@ type
|
|||
|
||||
LambdaExpr* = ref object of Expression
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]]
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]]
|
||||
defaults*: seq[Expression]
|
||||
isGenerator*: bool
|
||||
isAsync*: bool
|
||||
|
@ -263,7 +244,7 @@ type
|
|||
FunDecl* = ref object of Declaration
|
||||
name*: IdentExpr
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]]
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]]
|
||||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
|
@ -276,21 +257,10 @@ proc isConst*(self: ASTNode): bool =
|
|||
## AST node represents a value
|
||||
## of constant type. All integers,
|
||||
## strings and singletons count as
|
||||
## constants, as well as collections
|
||||
## comprised only of those types
|
||||
## constants
|
||||
case self.kind:
|
||||
of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr, nilExpr:
|
||||
return true
|
||||
of tupleExpr, setExpr, listExpr:
|
||||
for item in ListExpr(self).members:
|
||||
if not item.isConst():
|
||||
return false
|
||||
return true
|
||||
of dictExpr:
|
||||
for (key, value) in zip(DictExpr(self).keys, DictExpr(self).values):
|
||||
if not key.isConst() or not value.isConst():
|
||||
return false
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
|
@ -299,8 +269,7 @@ proc isLiteral*(self: ASTNode): bool {.inline.} =
|
|||
## Returns if the AST node represents a literal
|
||||
self.kind in {intExpr, hexExpr, binExpr, octExpr,
|
||||
strExpr, falseExpr, trueExpr, infExpr,
|
||||
nanExpr, floatExpr, nilExpr, listExpr,
|
||||
dictExpr, setExpr, tupleExpr
|
||||
nanExpr, floatExpr, nilExpr
|
||||
}
|
||||
|
||||
## AST node constructors
|
||||
|
@ -373,7 +342,7 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]], defaults: seq[Expression], body: Statement,
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], body: Statement,
|
||||
isGenerator: bool, isAsync: bool, token: Token, returnType: Expression): LambdaExpr =
|
||||
result = LambdaExpr(kind: lambdaExpr)
|
||||
result.body = body
|
||||
|
@ -392,34 +361,6 @@ proc newGetItemExpr*(obj: Expression, name: IdentExpr, token: Token): GetItemExp
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newListExpr*(members: seq[Expression], token: Token): ListExpr =
|
||||
result = ListExpr(kind: listExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newSetExpr*(members: seq[Expression], token: Token): SetExpr =
|
||||
result = SetExpr(kind: setExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newTupleExpr*(members: seq[Expression], token: Token): TupleExpr =
|
||||
result = TupleExpr(kind: tupleExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newDictExpr*(keys, values: seq[Expression], token: Token): DictExpr =
|
||||
result = DictExpr(kind: dictExpr)
|
||||
result.keys = keys
|
||||
result.values = values
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newSetItemExpr*(obj: Expression, name: IdentExpr, value: Expression, token: Token): SetItemExpr =
|
||||
result = SetItemExpr(kind: setItemExpr)
|
||||
result.obj = obj
|
||||
|
@ -593,7 +534,7 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
|||
result.pragmas = pragmas
|
||||
|
||||
|
||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]], defaults: seq[Expression],
|
||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression],
|
||||
body: Statement, isAsync, isGenerator: bool,
|
||||
isPrivate: bool, token: Token, pragmas: seq[Token],
|
||||
returnType: Expression): FunDecl =
|
||||
|
@ -695,18 +636,6 @@ proc `$`*(self: ASTNode): string =
|
|||
of funDecl:
|
||||
var self = FunDecl(self)
|
||||
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate})"""
|
||||
of tupleExpr:
|
||||
var self = TupleExpr(self)
|
||||
result &= &"""Tuple([{self.members.join(", ")}])"""
|
||||
of setExpr:
|
||||
var self = SetExpr(self)
|
||||
result &= &"""Set([{self.members.join(", ")}])"""
|
||||
of listExpr:
|
||||
var self = ListExpr(self)
|
||||
result &= &"""List([{self.members.join(", ")}])"""
|
||||
of dictExpr:
|
||||
var self = DictExpr(self)
|
||||
result &= &"""Dict(keys=[{self.keys.join(", ")}], values=[{self.values.join(", ")}])"""
|
||||
of lambdaExpr:
|
||||
var self = LambdaExpr(self)
|
||||
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync})"""
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
## Low level bytecode implementation details
|
||||
import ast
|
||||
import typing
|
||||
import ../../util/multibyte
|
||||
import errors
|
||||
|
||||
|
@ -27,9 +28,11 @@ export ast
|
|||
type
|
||||
Chunk* = ref object
|
||||
## A piece of bytecode.
|
||||
## Consts represents the constants table the code is referring to.
|
||||
## Code is the linear sequence of compiled bytecode instructions.
|
||||
## Lines maps bytecode instructions to line numbers using Run
|
||||
## consts represents the constants table the code is referring to.
|
||||
## byteConsts represents the actual encoding of the constants table
|
||||
## used when serializing to/from a bytecode stream.
|
||||
## code is the linear sequence of compiled bytecode instructions.
|
||||
## lines maps bytecode instructions to line numbers using Run
|
||||
## Length Encoding. Instructions are encoded in groups whose structure
|
||||
## follows the following schema:
|
||||
## - The first integer represents the line number
|
||||
|
@ -43,12 +46,13 @@ type
|
|||
## This is more efficient than using the naive approach, which would encode
|
||||
## the same line number multiple times and waste considerable amounts of space.
|
||||
consts*: seq[LiteralExpr]
|
||||
byteConsts*: seq[uint8]
|
||||
code*: seq[uint8]
|
||||
lines*: seq[int]
|
||||
reuseConsts*: bool
|
||||
|
||||
OpCode* {.pure.} = enum
|
||||
## Enum of possible opcodes.
|
||||
## Enum of Peon's bytecode opcodes
|
||||
|
||||
# Note: x represents the argument
|
||||
# to unary opcodes, while a and b
|
||||
|
@ -60,7 +64,7 @@ type
|
|||
# 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.
|
||||
# at compilation time into the bytecode
|
||||
|
||||
# These push a constant onto the stack
|
||||
LoadInt64 = 0u8,
|
||||
|
@ -107,6 +111,7 @@ 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
|
||||
|
@ -116,13 +121,8 @@ type
|
|||
Yield, # Yields control from a generator back to the caller
|
||||
## Coroutines
|
||||
Await, # Calls an asynchronous function
|
||||
## Collection literals
|
||||
BuildList,
|
||||
BuildDict,
|
||||
BuildSet,
|
||||
BuildTuple,
|
||||
## Misc
|
||||
Assert, # Raises an AssertionFailed exception if the x is false
|
||||
Assert, # Raises an AssertionFailed exception if x is false
|
||||
NoOp, # Just a no-op
|
||||
|
||||
|
||||
|
@ -163,9 +163,6 @@ const jumpInstructions* = {JumpIfFalse, JumpIfFalsePop,
|
|||
LongJumpForwards, LongJumpBackwards,
|
||||
JumpIfTrue, LongJumpIfTrue}
|
||||
|
||||
# Collection instructions push a built-in collection type onto the stack
|
||||
const collectionInstructions* = {BuildList, BuildDict, BuildSet, BuildTuple}
|
||||
|
||||
|
||||
proc newChunk*(reuseConsts: bool = true): Chunk =
|
||||
## Initializes a new, empty chunk
|
||||
|
@ -235,7 +232,6 @@ proc findOrAddConstant(self: Chunk, constant: LiteralExpr): int =
|
|||
if c.kind != constant.kind:
|
||||
continue
|
||||
if constant.isConst():
|
||||
var constant = constant
|
||||
if c.literal.lexeme == constant.literal.lexeme:
|
||||
# This wouldn't work for stuff like 2e3 and 2000.0, but those
|
||||
# forms are collapsed in the compiler before being written
|
||||
|
@ -256,10 +252,10 @@ proc addConstant*(self: Chunk, constant: LiteralExpr): array[3, uint8] =
|
|||
## Writes a constant to a chunk. Returns its index casted to a 3-byte
|
||||
## sequence (array). Constant indexes are reused if a constant is used
|
||||
## more than once and self.reuseConsts equals true
|
||||
if self.consts.len() == 16777215:
|
||||
if self.consts.high() == 16777215:
|
||||
# The constant index is a 24 bit unsigned integer, so that's as far
|
||||
# as we can index into the constant table (the same applies
|
||||
# to our stack by the way). Not that anyone's ever gonna hit this
|
||||
# limit in the real world, but you know, just in case
|
||||
raise newException(CompileError, "cannot encode more than 16777215 constants")
|
||||
raise newException(CompileError, "cannot encode more than 16777216 constants")
|
||||
result = self.findOrAddConstant(constant).toTriple()
|
||||
|
|
|
@ -39,7 +39,7 @@ type
|
|||
Foreach, Yield, Of, Defer,
|
||||
Try, Except, Finally, Type,
|
||||
Operator, Case, Enum, From,
|
||||
Emit, As
|
||||
Emit, As, Ptr, Ref
|
||||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
|
|
|
@ -224,65 +224,8 @@ proc primary(self: Parser): Expression =
|
|||
result = newIdentExpr(self.step())
|
||||
of LeftParen:
|
||||
let tok = self.step()
|
||||
if self.match(RightParen):
|
||||
# This yields an empty tuple
|
||||
result = newTupleExpr(@[], tok)
|
||||
else:
|
||||
result = self.expression()
|
||||
if self.match(Comma):
|
||||
var tupleObject = newTupleExpr(@[result], tok)
|
||||
while not self.check(RightParen):
|
||||
tupleObject.members.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
result = tupleObject
|
||||
self.expect(RightParen, "unterminated tuple literal")
|
||||
else:
|
||||
self.expect(RightParen, "unterminated parenthesized expression")
|
||||
result = newGroupingExpr(result, tok)
|
||||
of LeftBracket:
|
||||
let tok = self.step()
|
||||
if self.match(RightBracket):
|
||||
# This yields an empty list
|
||||
result = newListExpr(@[], tok)
|
||||
else:
|
||||
var listObject = newListExpr(@[], tok)
|
||||
while not self.check(RightBracket):
|
||||
listObject.members.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
result = listObject
|
||||
self.expect(RightBracket, "unterminated list literal")
|
||||
of LeftBrace:
|
||||
let tok = self.step()
|
||||
if self.match(RightBrace):
|
||||
# This yields an empty dictionary, not an empty set!
|
||||
# For empty sets, there will be a builtin set() type
|
||||
# that can be instantiated with no arguments
|
||||
result = newDictExpr(@[], @[], tok)
|
||||
else:
|
||||
result = self.expression()
|
||||
if self.match(Comma) or self.check(RightBrace):
|
||||
var setObject = newSetExpr(@[result], tok)
|
||||
while not self.check(RightBrace):
|
||||
setObject.members.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
result = setObject
|
||||
self.expect(RightBrace, "unterminated set literal")
|
||||
elif self.match(Colon):
|
||||
var dictObject = newDictExpr(@[result], @[self.expression()], tok)
|
||||
if self.match(RightBrace):
|
||||
return dictObject
|
||||
if self.match(Comma):
|
||||
while not self.check(RightBrace):
|
||||
dictObject.keys.add(self.expression())
|
||||
self.expect(Colon)
|
||||
dictObject.values.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBrace, "unterminated dict literal")
|
||||
result = dictObject
|
||||
result = newGroupingExpr(self.expression(), tok)
|
||||
self.expect(RightParen, "unterminated parenthesized expression")
|
||||
of Yield:
|
||||
let tok = self.step()
|
||||
if self.currentFunction == nil:
|
||||
|
@ -704,19 +647,6 @@ proc tryStmt(self: Parser): Statement =
|
|||
elif BinaryExpr(excName).a.kind != identExpr:
|
||||
self.error("expecting exception name")
|
||||
excName = BinaryExpr(excName).a
|
||||
# Note how we don't use elif here: when the if above sets excName to As'
|
||||
# first operand, that might be a tuple, which we unpack below
|
||||
if excName.kind == tupleExpr:
|
||||
# This allows to do except (a, b, c) as SomeError {...}
|
||||
# TODO: Consider adding the ability to make exc a sequence
|
||||
# instead of adding the same body with different exception
|
||||
# types each time
|
||||
handlerBody = self.statement()
|
||||
for element in TupleExpr(excName).members:
|
||||
if element.kind != identExpr:
|
||||
self.error("expecting exception name")
|
||||
handlers.add((body: handlerBody, exc: IdentExpr(element), name: asName))
|
||||
continue
|
||||
else:
|
||||
excName = nil
|
||||
if self.match(Else):
|
||||
|
@ -859,8 +789,8 @@ proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declarat
|
|||
discard # Unreachable
|
||||
|
||||
|
||||
proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]],
|
||||
parameter: var tuple[name: IdentExpr, valueType: Expression, mutable: bool],
|
||||
proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]],
|
||||
parameter: var tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool],
|
||||
defaults: var seq[Expression]) =
|
||||
while not self.check(RightParen):
|
||||
if arguments.len > 255:
|
||||
|
@ -871,6 +801,10 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr,
|
|||
parameter.mutable = false
|
||||
if self.match(Var):
|
||||
parameter.mutable = true
|
||||
elif self.match(Ptr):
|
||||
parameter.isPtr = true
|
||||
elif self.match(Ref):
|
||||
parameter.isRef = true
|
||||
parameter.valueType = self.expression()
|
||||
for i in countdown(arguments.high(), 0):
|
||||
if arguments[i].valueType != nil:
|
||||
|
@ -898,7 +832,7 @@ 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: Expression, mutable: bool]] = @[]
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]] = @[]
|
||||
var defaults: seq[Expression] = @[]
|
||||
var returnType: Expression
|
||||
if not isLambda and self.check(Identifier):
|
||||
|
@ -941,12 +875,12 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
# the type declaration for a function lacks
|
||||
# the braces that would qualify it as an
|
||||
# expression
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]] = @[]
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]] = @[]
|
||||
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, mutable: bool]
|
||||
var parameter: tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]
|
||||
if self.match(LeftParen):
|
||||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(Colon):
|
||||
|
@ -955,17 +889,17 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
returnType = self.expression()
|
||||
if not self.match(LeftBrace):
|
||||
self.expect(LeftParen)
|
||||
var parameter: tuple[name: IdentExpr, valueType: Expression, mutable: bool]
|
||||
var parameter: tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]
|
||||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(Colon):
|
||||
# Function's return type
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool]] = @[]
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]] = @[]
|
||||
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, mutable: bool]
|
||||
var parameter: tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]
|
||||
if self.match(LeftParen):
|
||||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(Colon):
|
||||
|
|
|
@ -244,6 +244,8 @@ proc fillSymbolTable(tokenizer: Lexer) =
|
|||
tokenizer.symbols.addKeyword("and", TokenType.LogicalAnd)
|
||||
tokenizer.symbols.addKeyword("or", TokenType.LogicalOr)
|
||||
tokenizer.symbols.addKeyword("not", TokenType.LogicalNot)
|
||||
tokenizer.symbols.addKeyword("ref", Ref)
|
||||
tokenizer.symbols.addKeyword("ptr", Ptr)
|
||||
|
||||
# P.S.: There's no reason for the order of addition of
|
||||
# symbols to be ascending in length (the symbol table uses
|
||||
|
|
|
@ -75,7 +75,7 @@ proc stackDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int
|
|||
|
||||
|
||||
proc argumentDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||
## Debugs instructions that operate on a hardcoded value value on the stack using a 16-bit operand
|
||||
## Debugs instructions that operate on a hardcoded value on the stack using a 16-bit operand
|
||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble()
|
||||
printInstruction(instruction)
|
||||
stdout.write(&", has argument ")
|
||||
|
@ -123,33 +123,6 @@ proc jumpInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
|||
return offset + 3
|
||||
|
||||
|
||||
proc collectionInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||
## Debugs instructions that push collection types on the stack
|
||||
var elemCount = int([chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple())
|
||||
printInstruction(instruction, true)
|
||||
case instruction:
|
||||
of BuildList, BuildTuple, BuildSet:
|
||||
var elements: seq[ASTNode] = @[]
|
||||
for n in countup(0, elemCount - 1):
|
||||
elements.add(chunk.consts[n])
|
||||
printDebug("Elements: ")
|
||||
setForegroundColor(fgYellow)
|
||||
stdout.write(&"""[{elements.join(", ")}]""")
|
||||
setForegroundColor(fgGreen)
|
||||
of BuildDict:
|
||||
var elements: seq[tuple[key: ASTNode, value: ASTNode]] = @[]
|
||||
for n in countup(0, (elemCount - 1) * 2, 2):
|
||||
elements.add((key: chunk.consts[n], value: chunk.consts[n + 1]))
|
||||
printDebug("Elements: ")
|
||||
setForegroundColor(fgYellow)
|
||||
stdout.write(&"""[{elements.join(", ")}]""")
|
||||
setForegroundColor(fgGreen)
|
||||
else:
|
||||
discard # Unreachable
|
||||
echo ""
|
||||
return offset + 4
|
||||
|
||||
|
||||
proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
||||
## Takes one bytecode instruction and prints it
|
||||
setForegroundColor(fgGreen)
|
||||
|
@ -175,8 +148,6 @@ proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
|||
result = argumentDoubleInstruction(opcode, chunk, offset)
|
||||
of jumpInstructions:
|
||||
result = jumpInstruction(opcode, chunk, offset)
|
||||
of collectionInstructions:
|
||||
result = collectionInstruction(opcode, chunk, offset)
|
||||
else:
|
||||
echo &"DEBUG - Unknown opcode {opcode} at index {offset}"
|
||||
result = offset + 1
|
||||
|
|
|
@ -151,10 +151,9 @@ proc writeConstants(self: Serializer, stream: var seq[byte]) =
|
|||
|
||||
proc readConstants(self: Serializer, stream: seq[byte]): int =
|
||||
## Reads the constant table from the given stream and
|
||||
## adds each constant to the chunk object (note: most compile-time
|
||||
## information such as the original token objects and line info is lost when
|
||||
## serializing the data, so those fields are set to nil or some default
|
||||
## value). Returns the number of bytes that were processed in the stream
|
||||
## adds each constant to the chunk object.
|
||||
## Returns the number of bytes that were processed in
|
||||
## the stream
|
||||
var stream = stream
|
||||
var count: int = 0
|
||||
while true:
|
||||
|
|
Loading…
Reference in New Issue