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

This commit is contained in:
Mattia Giambirtone 2022-05-21 12:20:12 +02:00
parent e4c2ba2ce2
commit 7cf69cf0cf
3 changed files with 39 additions and 12 deletions

View File

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

View File

@ -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]

View File

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