From 7cf69cf0cf5ef8d9b62d1eacfe489489f8d1d8c3 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 21 May 2022 12:20:12 +0200 Subject: [PATCH] Added hasExplicitReturn field to function and lambda declarations to fix some errors with compiling them. Fixed minor issues with function and lambda parsing, the parser is now able to skip whitespace/tab tokens trasparently, made parser.done() inline, removed invalid state error from funDecl --- src/frontend/compiler.nim | 24 ++++++++++++++++++------ src/frontend/meta/ast.nim | 3 +++ src/frontend/parser.nim | 24 ++++++++++++++++++------ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 850b0d5..0e0be40 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -601,6 +601,7 @@ proc inferType(self: Compiler, node: Declaration): Type = var node = FunDecl(node) let resolved = self.resolve(node.name) if resolved != nil: + echo resolved[] return resolved.valueType of NodeKind.varDecl: var node = VarDecl(node) @@ -699,9 +700,8 @@ proc literal(self: Compiler, node: ASTNode) = proc unary(self: Compiler, node: UnaryExpr) = ## Compiles unary expressions such as decimal ## and bitwise negation - self.expression(node.a) # Pushes the operand onto the stack - # TODO: Find implementation of - # the given operator and call it + self.expression(node.a) # Pushes the operand onto the stack + proc binary(self: Compiler, node: BinaryExpr) = @@ -1055,7 +1055,7 @@ proc returnStmt(self: Compiler, node: ReturnStmt) = let typ = self.inferType(self.currentFunction) ## Having the return type if typ.returnType == nil and returnType != nil: - self.error("non-empty return statement is not allowed in functions without an explicit return type") + self.error("non-empty return statement is not allowed in functions with an explicit return type") elif returnType == nil and typ.returnType != nil: self.error(&"expected return value of type '{self.typeToStr(typ.returnType)}', but expression has no type") elif not self.compareTypes(returnType, typ.returnType): @@ -1228,8 +1228,20 @@ proc funDecl(self: Compiler, node: FunDecl) = # are resolved properly). There's a need for a bit # of boilerplate code to make closures work, but # that's about it - self.emitBytes(LoadNil, OpCode.Return) - + case self.currentFunction.kind: + of NodeKind.funDecl: + if not self.currentFunction.hasExplicitReturn: + let typ = self.inferType(self.currentFunction) + if self.currentFunction.returnType == nil and typ != nil: + self.error("non-empty return statement is not allowed in functions without an explicit return type") + if self.currentFunction.returnType != nil: + self.error("function has an explicit return type, but no explicit return statement was found") + self.emitByte(OpCode.Return) + of NodeKind.lambdaExpr: + if not LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn: + self.emitByte(OpCode.Return) + else: + discard # Unreachable # Currently defer is not functional so we # just pop the instructions for i in countup(deferStart, self.deferred.len() - 1, 1): diff --git a/src/frontend/meta/ast.nim b/src/frontend/meta/ast.nim index bf3f03b..52ee498 100644 --- a/src/frontend/meta/ast.nim +++ b/src/frontend/meta/ast.nim @@ -176,6 +176,8 @@ type isAsync*: bool isPure*: bool returnType*: Expression + hasExplicitReturn*: bool + SliceExpr* = ref object of Expression expression*: Expression @@ -258,6 +260,7 @@ type isPrivate*: bool isPure*: bool returnType*: Expression + hasExplicitReturn*: bool Pragma* = ref object of Expression name*: IdentExpr args*: seq[LiteralExpr] diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index ec64d87..e164a9d 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -167,9 +167,13 @@ proc peek(self: Parser, distance: int = 0): Token = result = endOfFile else: result = self.tokens[self.current + distance] + ## Hack to ignore whitespace/tab + if result.kind in {TokenType.Whitespace, Tab}: + # self.current += 1 + result = self.peek(distance + 1) -proc done(self: Parser): bool = +proc done(self: Parser): bool {.inline.} = ## Returns true if we're at the ## end of the file. Note that the ## parser expects an explicit @@ -190,8 +194,12 @@ proc step(self: Parser, n: int = 1): Token = proc error(self: Parser, message: string) {.raises: [ParseError, ValueError].} = ## Raises a formatted ParseError exception - var lexeme = self.getCurrentToken().lexeme - var errorMessage = &"A fatal error occurred while parsing '{self.file}', line {self.peek().line} at '{lexeme}' -> {message}" + var lexeme = self.peek().lexeme + var fn = "" + if self.currentFunction != nil: + if self.currentFunction.kind == NodeKind.funDecl: + fn = &"inside function '{FunDecl(self.currentFunction).name.token.lexeme}'" + var errorMessage = &"A fatal error occurred while parsing '{self.file}', {fn} line {self.peek().line} at '{lexeme}' -> {message}" raise newException(ParseError, errorMessage) @@ -594,6 +602,11 @@ proc returnStmt(self: Parser): Statement = value = self.expression() endOfLine("missing semicolon after return statement") result = newReturnStmt(value, tok) + case self.currentFunction.kind: + of NodeKind.funDecl: + FunDecl(self.currentFunction).hasExplicitReturn = true + else: + LambdaExpr(self.currentFunction).hasExplicitReturn = true proc yieldStmt(self: Parser): Statement = @@ -903,15 +916,14 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, # go all the way up to primary(), which will # call us back with isLambda=true, allowing us # to actually parse the function as an expression - dec(self.current) + while not self.check(tok.kind): + dec(self.current) result = Declaration(self.expressionStatement()) self.currentFunction = enclosingFunction return result elif isLambda: self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok, returnType = nil, pragmas = (@[])) - elif not isOperator: - self.error("funDecl: invalid state") if self.match(":"): # Function has explicit return type if self.match([Function, Coroutine, Generator]):