From 33066d3b9bd73509dbdecbc741668df2a5e7c7e2 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sun, 31 Jul 2022 16:09:22 +0200 Subject: [PATCH] Fixed bug with calling a call --- src/frontend/compiler.nim | 91 ++++++++++++++++++------------------- src/frontend/lexer.nim | 11 +++-- src/frontend/meta/ast.nim | 23 ++++++---- src/frontend/meta/token.nim | 2 +- src/frontend/parser.nim | 62 ++++++++++++++----------- src/main.nim | 22 +++++---- src/util/debugger.nim | 2 +- tests/closures.pn | 4 +- 8 files changed, 117 insertions(+), 100 deletions(-) diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 56b03a1..e929e83 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -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 diff --git a/src/frontend/lexer.nim b/src/frontend/lexer.nim index 2201d36..3e02341 100644 --- a/src/frontend/lexer.nim +++ b/src/frontend/lexer.nim @@ -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) diff --git a/src/frontend/meta/ast.nim b/src/frontend/meta/ast.nim index af04657..902384f 100644 --- a/src/frontend/meta/ast.nim +++ b/src/frontend/meta/ast.nim @@ -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})" diff --git a/src/frontend/meta/token.nim b/src/frontend/meta/token.nim index 516b93a..fc4ce8f 100644 --- a/src/frontend/meta/token.nim +++ b/src/frontend/meta/token.nim @@ -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" diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index 5b52fd5..dbe8f10 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -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: diff --git a/src/main.nim b/src/main.nim index df85b72..e23a6a5 100644 --- a/src/main.nim +++ b/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: diff --git a/src/util/debugger.nim b/src/util/debugger.nim index 62aaa97..f64b5fd 100644 --- a/src/util/debugger.nim +++ b/src/util/debugger.nim @@ -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 diff --git a/tests/closures.pn b/tests/closures.pn index edb7612..52166cf 100644 --- a/tests/closures.pn +++ b/tests/closures.pn @@ -7,5 +7,5 @@ fn makeClosure(n: int): fn: int { } -var closure = makeClosure(1); -closure(); +var closure = makeClosure(1)(); +closure; \ No newline at end of file