Fixed bug with calling a call
This commit is contained in:
parent
da2cfefe75
commit
33066d3b9b
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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})"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
22
src/main.nim
22
src/main.nim
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@ fn makeClosure(n: int): fn: int {
|
|||
}
|
||||
|
||||
|
||||
var closure = makeClosure(1);
|
||||
closure();
|
||||
var closure = makeClosure(1)();
|
||||
closure;
|
Loading…
Reference in New Issue