Collections are no longer a compiler intrinsic. Added parser support for ptr/ref arguments

This commit is contained in:
Mattia Giambirtone 2022-05-04 14:01:38 +02:00
parent f189b0214e
commit a1c5430773
8 changed files with 69 additions and 366 deletions

View File

@ -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}'")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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