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