Fixed bug with calling a call

This commit is contained in:
Mattia Giambirtone 2022-07-31 16:09:22 +02:00
parent da2cfefe75
commit 33066d3b9b
8 changed files with 117 additions and 100 deletions

View File

@ -188,7 +188,7 @@ proc varDecl(self: Compiler, node: VarDecl)
proc inferType(self: Compiler, node: LiteralExpr): Type
proc inferType(self: Compiler, node: Expression): Type
proc findByName(self: Compiler, name: string): seq[Name]
proc findByType(self: Compiler, name: string, kind: Type): seq[Name]
proc findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name]
proc compareTypes(self: Compiler, a, b: Type): bool
proc patchReturnAddress(self: Compiler, pos: int)
proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode)
@ -456,7 +456,8 @@ proc detectClosureVariable(self: Compiler, name: var Name, depth: int = self.sco
for i, b in self.closedOver.high().toTriple():
self.chunk.code[name.codePos + i + 1] = b
else:
self.error("it is currently not possible to close over function arguments")
discard
# self.error("it is currently not possible to close over function arguments")
proc compareTypes(self: Compiler, a, b: Type): bool =
@ -639,12 +640,12 @@ proc inferType(self: Compiler, node: Expression): Type =
let resolved = self.resolve(IdentExpr(node.callee))
if not resolved.isNil():
result = resolved.valueType.returnType
if result.isNil():
result = Type(kind: Any)
else:
result = nil
of lambdaExpr:
result = self.inferType(LambdaExpr(node.callee).returnType)
of callExpr:
result = self.inferType(CallExpr(node.callee).callee)
else:
discard # Unreachable
of varExpr:
@ -719,11 +720,12 @@ proc findByName(self: Compiler, name: string): seq[Name] =
result.add(obj)
proc findByType(self: Compiler, name: string, kind: Type): seq[Name] =
proc findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name] =
## Looks for objects that have already been declared
## with the given name and type
## with the given name and type. If depth is not -1,
## it also compares the name's scope depth
for obj in self.findByName(name):
if self.compareTypes(obj.valueType, kind):
if self.compareTypes(obj.valueType, kind) and depth == -1 or depth == obj.depth:
result.add(obj)
#[
@ -751,7 +753,7 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name =
msg &= "s"
msg &= ": "
for name in names:
msg &= &"\n - '{name.name.token.lexeme}' of type '{self.typeToStr(name.valueType)}'"
msg &= &"\n - in module '{name.owner}' at line {name.name.token.line} of type '{self.typeToStr(name.valueType)}'"
if name.valueType.kind != Function:
msg &= ", not a callable"
elif kind.args.len() != name.valueType.args.len():
@ -1004,7 +1006,7 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
self.error(&"unknown built-in: '{fn.valueType.builtinOp}'")
proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) =
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], onStack: bool = false) =
## Small wrapper that abstracts emitting a call instruction
## for a given function
if fn.valueType.isBuiltinFunction:
@ -1013,11 +1015,8 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) =
# them differently
self.handleBuiltinFunction(fn, args)
return
if any(fn.valueType.args, proc (arg: tuple[name: string, kind: Type]): bool = arg[1].kind == Generic):
# The function has generic arguments! We need to compile a version
# of it with the right type data
self.funDecl(nil, fn, args)
self.emitFunction(fn)
if not onStack:
self.emitFunction(fn)
self.emitByte(LoadReturnAddress)
let pos = self.chunk.code.len()
# We initially emit a dummy return
@ -1177,7 +1176,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
self.emitConstant(node, self.inferType(node))
else:
self.detectClosureVariable(s)
if s.valueType.kind == Function:
if s.valueType.kind == Function and s.isFunDecl:
# Functions have no runtime
# representation, so we need
# to create one on the fly
@ -1242,24 +1241,29 @@ proc endScope(self: Compiler) =
self.error("cannot call endScope with scopeDepth < 0 (This is an internal error and most likely a bug)")
dec(self.scopeDepth)
var names: seq[Name] = @[]
var popCount = 0
for name in self.names:
if name.depth > self.scopeDepth and name.valueType.kind notin {Generic, CustomType} and not name.isFunDecl:
if name.depth > self.scopeDepth:
names.add(name)
if len(names) > 1:
if name.valueType.kind notin {Generic, CustomType} and not name.isFunDecl:
# We don't increase the pop count for these kinds of objects
# because they're not stored the same way as regular variables
inc(popCount)
if popCount > 1:
# If we're popping less than 65535 variables, then
# we can emit a PopN instruction. This is true for
# 99.99999% of the use cases of the language (who the
# hell is going to use 65 THOUSAND local variables?), but
# hell is going to use 65 THOUSAND variables?), but
# if you'll ever use more then Peon will emit a PopN instruction
# for the first 65 thousand and change local variables and then
# emit another batch of plain ol' Pop instructions for the rest
self.emitByte(PopN)
self.emitBytes(len(names).toDouble())
if len(names) > uint16.high().int():
for i in countdown(self.names.high(), len(names) - uint16.high().int()):
self.emitBytes(popCount.toDouble())
if popCount > uint16.high().int():
for i in countdown(self.names.high(), popCount - uint16.high().int()):
if self.names[i].depth > self.scopeDepth:
self.emitByte(PopC)
elif len(names) == 1:
elif popCount == 1:
# We only emit PopN if we're popping more than one value
self.emitByte(PopC)
# This seems *really* slow, but
@ -1274,7 +1278,6 @@ proc endScope(self: Compiler) =
inc(idx)
proc blockStmt(self: Compiler, node: BlockStmt) =
## Compiles block statements, which create a new
## local scope
@ -1321,15 +1324,17 @@ proc whileStmt(self: Compiler, node: WhileStmt) =
proc checkCallIsPure(self: Compiler, node: ASTnode): bool =
## Checks if a call has any side effects
## Checks if a call has any side effects. Returns
## true if it doesn't and false otherwise
return true # TODO
proc callExpr(self: Compiler, node: CallExpr) =
proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} =
## Compiles code to call a function
var args: seq[tuple[name: string, kind: Type]] = @[]
var argExpr: seq[Expression] = @[]
var kind: Type
var onStack = false
# TODO: Keyword arguments
for i, argument in node.arguments.positionals:
kind = self.inferType(argument)
@ -1340,6 +1345,7 @@ proc callExpr(self: Compiler, node: CallExpr) =
args.add(("", kind))
argExpr.add(argument)
for argument in node.arguments.keyword:
# TODO
discard
if args.len() >= 16777216:
self.error(&"cannot pass more than 16777215 arguments")
@ -1348,28 +1354,19 @@ proc callExpr(self: Compiler, node: CallExpr) =
of identExpr:
funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args))
of NodeKind.callExpr:
var node = node.callee
while node.kind == callExpr:
self.callExpr(CallExpr(node))
node = CallExpr(node).callee
# funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args))
# TODO: Calling lambdas
funct = self.callExpr(CallExpr(node.callee))
funct = Name(valueType: Type(kind: Function, returnType: Type(kind: Any), args: args))
onStack = true
# TODO: Calling lambdas on-the-fly (i.e. on the same line)
else:
let typ = self.inferType(node)
if typ.isNil():
self.error(&"expression has no type")
else:
self.error(&"object of type '{self.typeToStr(typ)}' is not callable")
if any(funct.valueType.args, proc (arg: tuple[name: string, kind: Type]): bool = arg[1].kind == Generic):
# The function has generic arguments! We need to compile a version
# of it with the right type data
self.funDecl(nil, funct, argExpr)
# TODO: What next?
elif funct.valueType.isBuiltinFunction:
self.handleBuiltinFunction(funct, argExpr)
else:
self.generateCall(funct, argExpr)
if self.scopeDepth > 0 and not self.checkCallIsPure(node.callee):
result = funct
self.generateCall(funct, argExpr, onStack)
if not self.checkCallIsPure(node.callee):
if self.currentFunction.name != "":
self.error(&"cannot make sure that calls to '{self.currentFunction.name}' are side-effect free")
else:
@ -1688,14 +1685,14 @@ proc fixGenericFunc(self: Compiler, name: Name, args: seq[Expression]): Type =
proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression] = @[]) =
## Compiles function declarations
if not node.isNil():
#[if not node.isNil():
if node.generics.len() > 0 and fn.isNil() and args.len() == 0:
# Generic function! We can't compile it right now
self.declareName(node)
self.dispatchPragmas(node)
return
self.declareName(node)
self.dispatchPragmas(node)
return]#
self.declareName(node)
self.dispatchPragmas(node)
var node = node
var fn = if fn.isNil(): self.names[^(node.arguments.len() + 1)] else: fn
if fn.valueType.isBuiltinFunction:
@ -1729,14 +1726,14 @@ proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression
self.error("cannot declare function with empty body")
else:
discard # TODO: Forward declarations
let impl = self.findByType(fn.name.token.lexeme, fn.valueType)
let impl = self.findByType(fn.name.token.lexeme, fn.valueType, self.scopeDepth)
if impl.len() > 1:
# We found more than one (public) implementation of
# the same function with the same name: this is an
# error, as it would raise ambiguity when calling them
var msg = &"multiple matching implementations of '{fn.name.token.lexeme}' found:\n"
for f in reversed(impl):
msg &= &"- '{f.name.token.lexeme}' at line {f.line} of type {self.typeToStr(f.valueType)}\n"
msg &= &"- in module '{f.owner}' at line {f.line} of type {self.typeToStr(f.valueType)}\n"
self.error(msg)
# Since the deferred array is a linear
# sequence of instructions and we want

View File

@ -55,6 +55,7 @@ type
proc newSymbolTable: SymbolTable =
## Initializes a new symbol table
new(result)
result.keywords = newTable[string, TokenType]()
result.symbols = newTable[string, TokenType]()
@ -144,6 +145,7 @@ proc isAlphaNumeric(s: string): bool =
return false
return true
# Forward declaration
proc incLine(self: Lexer)
# Simple public getters used for error
@ -213,7 +215,8 @@ proc peek(self: Lexer, distance: int = 0, length: int = 1): string =
## previously consumed tokens. If the
## distance and/or the length are beyond
## EOF (even partially), the resulting string
## will be shorter than length bytes
## will be shorter than length bytes. The string
## may be empty
var i = distance
while len(result) < length:
if self.done() or self.current + i > self.source.high() or
@ -225,8 +228,8 @@ proc peek(self: Lexer, distance: int = 0, length: int = 1): string =
proc error(self: Lexer, message: string) =
## Raises a lexing error with a formatted
## error message
## Raises a lexing error with info
## for error messages
raise LexingError(msg: message, line: self.line, file: self.file, lexeme: self.peek())
@ -299,7 +302,7 @@ proc parseEscape(self: Lexer) =
# likely be soon. Another notable limitation is that
# \xhhh and \nnn are limited to the size of a char
# (i.e. uint8, or 256 values)
case self.peek()[0]: # We use a char instead of a string because of how case statements handle ranges with strings
case self.peek()[0]: # We use a char instead of a string because of how case statements handle ranges with strings
# (i.e. not well, given they crash the C code generator)
of 'a':
self.source[self.current] = cast[char](0x07)

View File

@ -183,7 +183,7 @@ type
returnType*: Expression
hasExplicitReturn*: bool
freeVars*: seq[IdentExpr]
depth*: int
SliceExpr* = ref object of Expression
expression*: Expression
@ -265,6 +265,7 @@ type
returnType*: Expression
hasExplicitReturn*: bool
freeVars*: seq[IdentExpr]
depth*: int
TypeDecl* = ref object of Declaration
name*: IdentExpr
@ -411,10 +412,11 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
result.token = token
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]],
defaults: seq[Expression], body: Statement, isGenerator: bool,
isAsync: bool, token: Token, returnType: Expression, pragmas: seq[Pragma],
generics: seq[tuple[name: IdentExpr, cond: Expression]], freeVars: seq[IdentExpr] = @[]): LambdaExpr =
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression],
body: Statement, isAsync, isGenerator: bool,
token: Token, depth: int, pragmas: seq[Pragma] = @[],
returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[],
freeVars: seq[IdentExpr] = @[]): LambdaExpr =
result = LambdaExpr(kind: lambdaExpr)
result.body = body
result.arguments = arguments
@ -427,6 +429,7 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]
result.pragmas = pragmas
result.generics = generics
result.freeVars = freeVars
result.depth = depth
proc newGetItemExpr*(obj: Expression, name: IdentExpr,
@ -615,8 +618,9 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
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[Pragma],
returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]], freeVars: seq[IdentExpr] = @[]): FunDecl =
isPrivate: bool, token: Token, depth: int,
pragmas: seq[Pragma] = @[], returnType: Expression,
generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[], freeVars: seq[IdentExpr] = @[]): FunDecl =
result = FunDecl(kind: funDecl)
result.name = name
result.arguments = arguments
@ -631,6 +635,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
result.isPure = false
result.generics = generics
result.freeVars = freeVars
result.depth = depth
proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]],
@ -730,13 +735,13 @@ proc `$`*(self: ASTNode): string =
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType}, pragmas={self.pragmas})"
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(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})"""
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas}, vars=[{self.freeVars.join(", ")}])"""
of typeDecl:
var self = TypeDecl(self)
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, pragmas={self.pragmas}, type={self.valueType})"""
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}, pragmas={self.pragmas})"""
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync}, pragmas={self.pragmas}, vars=[{self.freeVars.join(", ")}])"""
of deferStmt:
var self = DeferStmt(self)
result &= &"Defer({self.expression})"

View File

@ -79,7 +79,7 @@ type
proc `$`*(self: Token): string =
## Strinfifies
if self != nil:
result = &"Token(kind={self.kind}, lexeme={($self.lexeme).escape()}, line={self.line}, pos=({self.pos.start}, {self.pos.stop}), spaces={self.spaces})"
result = &"Token(kind={self.kind}, lexeme={self.lexeme.escape()}, line={self.line}, pos=({self.pos.start}, {self.pos.stop}), spaces={self.spaces})"
else:
result = "nil"

View File

@ -192,9 +192,9 @@ proc step(self: Parser, n: int = 1): Token =
self.current += 1
proc error(self: Parser, message: string) {.raises: [ParseError].} =
proc error(self: Parser, message: string, token: Token = nil) {.raises: [ParseError].} =
## Raises a ParseError exception
raise ParseError(msg: message, token: self.getCurrentToken(), file: self.file, module: self.getModule())
raise ParseError(msg: message, token: if token.isNil(): self.getCurrentToken() else: token, file: self.file, module: self.getModule())
# Why do we allow strings or enum members of TokenType? Well, it's simple:
@ -306,6 +306,16 @@ proc primary(self: Parser): Expression =
result = newIntExpr(self.step())
of Identifier:
result = newIdentExpr(self.step(), self.scopeDepth)
if not self.currentFunction.isNil() and self.scopeDepth > 0:
case self.currentFunction.kind:
of NodeKind.funDecl:
if FunDecl(self.currentFunction).depth != self.scopeDepth:
FunDecl(self.currentFunction).freeVars.add(IdentExpr(result))
of NodeKind.lambdaExpr:
if LambdaExpr(self.currentFunction).depth != self.scopeDepth:
LambdaExpr(self.currentFunction).freeVars.add(IdentExpr(result))
else:
discard # Unreachable
of LeftParen:
let tok = self.step()
result = newGroupingExpr(self.expression(), tok)
@ -313,10 +323,10 @@ proc primary(self: Parser): Expression =
of Yield:
let tok = self.step()
if self.currentFunction.isNil():
self.error("'yield' cannot be used outside functions")
self.error("'yield' cannot be used outside functions", tok)
elif self.currentFunction.token.kind != Generator:
# It's easier than doing conversions for lambda/funDecl
self.error("'yield' cannot be used outside generators")
self.error("'yield' cannot be used outside generators", tok)
if not self.check([RightBrace, RightBracket, RightParen, Comma, Semicolon]):
# Expression delimiters
result = newYieldExpr(self.expression(), tok)
@ -326,9 +336,9 @@ proc primary(self: Parser): Expression =
of Await:
let tok = self.step()
if self.currentFunction.isNil():
self.error("'await' cannot be used outside functions")
self.error("'await' cannot be used outside functions", tok)
if self.currentFunction.token.kind != Coroutine:
self.error("'await' can only be used inside coroutines")
self.error("'await' can only be used inside coroutines", tok)
result = newAwaitExpr(self.expression(), tok)
of RightParen, RightBracket, RightBrace:
# This is *technically* unnecessary: the parser would
@ -502,7 +512,7 @@ proc parseAssign(self: Parser): Expression =
of getItemExpr:
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
else:
self.error("invalid assignment target")
self.error("invalid assignment target", tok)
proc parseArrow(self: Parser): Expression =
@ -568,8 +578,8 @@ proc deferStmt(self: Parser): Statement =
let tok = self.peek(-1)
if self.currentFunction.isNil():
self.error("'defer' cannot be used outside functions")
result = newDeferStmt(self.expression(), tok)
endOfLine("missing statement terminator after 'defer'")
result = newDeferStmt(self.expression(), tok)
proc continueStmt(self: Parser): Statement =
@ -622,8 +632,8 @@ proc awaitStmt(self: Parser): Statement =
self.error("'await' cannot be used outside functions")
if self.currentFunction.token.kind != Coroutine:
self.error("'await' can only be used inside coroutines")
result = newAwaitStmt(self.expression(), tok)
endOfLine("missing statement terminator after 'await'")
result = newAwaitStmt(self.expression(), tok)
proc raiseStmt(self: Parser): Statement =
@ -661,8 +671,8 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement =
tok = self.peek(-1)
# TODO: New AST node
self.expect(Identifier, "expecting module name(s) after import statement")
result = newImportStmt(newIdentExpr(self.peek(-1), self.scopeDepth), tok)
endOfLine("missing statement terminator after 'import'")
result = newImportStmt(newIdentExpr(self.peek(-1), self.scopeDepth), tok)
@ -687,10 +697,10 @@ proc tryStmt(self: Parser): Statement =
if self.match(Finally):
finallyClause = self.statement()
if handlers.len() == 0 and elseClause.isNil() and finallyClause.isNil():
self.error("expecting 'except', 'finally' or 'else' statement after 'try' block")
self.error("expecting 'except', 'finally' or 'else' statement after 'try' block", tok)
for i, handler in handlers:
if handler.exc.isNil() and i != handlers.high():
self.error("catch-all exception handler with bare 'except' must come last in try statement")
self.error("catch-all exception handler with bare 'except' must come last in try statement", handler.exc.token)
result = newTryStmt(body, handlers, finallyClause, elseClause, tok)
@ -807,15 +817,15 @@ proc parsePragmas(self: Parser): seq[Pragma] =
while not self.match(")") and not self.done():
exp = self.primary()
if not exp.isLiteral():
self.error("pragma arguments can only be literals")
self.error("pragma arguments can only be literals", exp.token)
args.add(LiteralExpr(exp))
if not self.match(","):
break
self.expect(")", "unterminated parenthesis in pragma arguments")
self.expect(LeftParen, "unterminated parenthesis in pragma arguments")
else:
exp = self.primary()
if not exp.isLiteral():
self.error("pragma arguments can only be literals")
self.error("pragma arguments can only be literals", exp.token)
args.add(LiteralExpr(exp))
result.add(newPragma(name, args))
if self.match(","):
@ -866,7 +876,7 @@ proc varDecl(self: Parser, isLet: bool = false,
else:
discard # Unreachable
if not hasInit and VarDecl(result).valueType.isNil():
self.error("expecting initializer or explicit type declaration, but neither was found")
self.error("expecting initializer or explicit type annotation, but neither was found", result.token)
result.pragmas = pragmas
@ -876,7 +886,7 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr,
## Helper to parse declaration arguments and avoid code duplication
while not self.check(RightParen):
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.peek(-1))
self.expect(Identifier, "expecting parameter name")
parameter.name = newIdentExpr(self.peek(-1), self.scopeDepth)
if self.match(":"):
@ -888,12 +898,12 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr,
else:
parameter.valueType = nil
if parameter in arguments:
self.error("duplicate parameter name in function declaration")
self.error("duplicate parameter name in function declaration", parameter.name.token)
arguments.add(parameter)
if self.match("="):
defaults.add(self.expression())
elif defaults.len() > 0:
self.error("positional argument cannot follow default argument in function declaration")
self.error("positional argument cannot follow default argument in function declaration", parameter.name.token)
if not self.match(Comma):
break
self.expect(RightParen)
@ -910,7 +920,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
var defaults: seq[Expression] = @[]
result = newLambdaExpr(arguments, defaults, nil, isGenerator=self.peek(-1).kind == Generator,
isAsync=self.peek(-1).kind == Coroutine, token=self.peek(-1),
returnType=nil, pragmas=(@[]), generics=(@[]))
returnType=nil, depth=self.scopeDepth)
var parameter: tuple[name: IdentExpr, valueType: Expression]
if self.match(LeftParen):
self.parseDeclArguments(arguments, parameter, defaults)
@ -956,9 +966,9 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
isAsync=isAsync,
isGenerator=isGenerator,
isPrivate=true,
token=tok, pragmas=(@[]),
token=tok,
returnType=nil,
generics=(@[]))
depth=self.scopeDepth)
if self.match("*"):
FunDecl(self.currentFunction).isPrivate = false
self.checkDecl(FunDecl(self.currentFunction).isPrivate)
@ -978,8 +988,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
self.currentFunction = enclosingFunction
return result
elif isLambda:
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok,
returnType = nil, pragmas = (@[]), generics=(@[]))
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok,
returnType=nil, depth=self.scopeDepth)
if self.match(":"):
# Function has explicit return type
if self.match([Function, Coroutine, Generator]):
@ -1198,12 +1208,12 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[Declaration] =
# with an EOF token
if token.kind == Operator:
if i == self.tokens.high():
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)")
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)", token)
self.operators.addOperator(self.tokens[i + 1].lexeme)
if i == self.tokens.high() and token.kind != EndOfFile:
# Since we're iterating this list anyway might as
# well perform some extra checks
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)")
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)", token)
while not self.done():
self.tree.add(self.declaration())
if self.tree[^1] == nil:

View File

@ -142,9 +142,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
input = ""
let exc = LexingError(getCurrentException())
let relPos = tokenizer.getRelPos(exc.line)
let line = tokenizer.getSource().splitLines()[exc.line - 1].strip()
let line = tokenizer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme}'",
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
styledEcho fgBlue, "Source line: " , fgDefault, line
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
@ -155,7 +155,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
let lineNo = exc.token.line
let relPos = tokenizer.getRelPos(lineNo)
let fn = parser.getCurrentFunction()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fnMsg = ""
if fn != nil and fn.kind == funDecl:
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
@ -169,7 +169,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
let lexeme = exc.node.token.lexeme
let lineNo = exc.node.token.line
let relPos = tokenizer.getRelPos(lineNo)
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fn = compiler.getCurrentFunction()
var fnMsg = ""
if fn != nil and fn.kind == funDecl:
@ -265,21 +265,23 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
styledEcho fgCyan, "\n\nExecution step: "
vm.run(serialized.chunk)
except LexingError:
input = ""
let exc = LexingError(getCurrentException())
let relPos = tokenizer.getRelPos(exc.line)
let line = tokenizer.getSource().splitLines()[exc.line - 1].strip()
let line = tokenizer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
fgYellow, &"'{exc.file}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
styledEcho fgBlue, "Source line: " , fgDefault, line
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
except ParseError:
input = ""
let exc = ParseError(getCurrentException())
let lexeme = exc.token.lexeme.escape()
let lexeme = exc.token.lexeme
let lineNo = exc.token.line
let relPos = tokenizer.getRelPos(lineNo)
let fn = parser.getCurrentFunction()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fnMsg = ""
if fn != nil and fn.kind == funDecl:
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
@ -290,10 +292,10 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
except CompileError:
let exc = CompileError(getCurrentException())
let lexeme = exc.node.token.lexeme.escape()
let lexeme = exc.node.token.lexeme
let lineNo = exc.node.token.line
let relPos = tokenizer.getRelPos(lineNo)
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fn = compiler.getCurrentFunction()
var fnMsg = ""
if fn != nil and fn.kind == funDecl:

View File

@ -138,7 +138,7 @@ proc callInstruction(self: Debugger, instruction: OpCode) =
## Debugs function calls
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
printInstruction(instruction)
styledEcho fgGreen, &", creates frame of size ", fgYellow, $size
styledEcho fgGreen, &", creates frame of size ", fgYellow, $(size + 2)
self.current += 4

View File

@ -7,5 +7,5 @@ fn makeClosure(n: int): fn: int {
}
var closure = makeClosure(1);
closure();
var closure = makeClosure(1)();
closure;