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) var node = FunDecl(node)
let resolved = self.resolve(node.name) let resolved = self.resolve(node.name)
if resolved != nil: if resolved != nil:
echo resolved[]
return resolved.valueType return resolved.valueType
of NodeKind.varDecl: of NodeKind.varDecl:
var node = VarDecl(node) var node = VarDecl(node)
@ -699,9 +700,8 @@ proc literal(self: Compiler, node: ASTNode) =
proc unary(self: Compiler, node: UnaryExpr) = proc unary(self: Compiler, node: UnaryExpr) =
## Compiles unary expressions such as decimal ## Compiles unary expressions such as decimal
## and bitwise negation ## and bitwise negation
self.expression(node.a) # Pushes the operand onto the stack self.expression(node.a) # Pushes the operand onto the stack
# TODO: Find implementation of
# the given operator and call it
proc binary(self: Compiler, node: BinaryExpr) = proc binary(self: Compiler, node: BinaryExpr) =
@ -1055,7 +1055,7 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
let typ = self.inferType(self.currentFunction) let typ = self.inferType(self.currentFunction)
## Having the return type ## Having the return type
if typ.returnType == nil and returnType != nil: 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: elif returnType == nil and typ.returnType != nil:
self.error(&"expected return value of type '{self.typeToStr(typ.returnType)}', but expression has no type") self.error(&"expected return value of type '{self.typeToStr(typ.returnType)}', but expression has no type")
elif not self.compareTypes(returnType, typ.returnType): 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 # are resolved properly). There's a need for a bit
# of boilerplate code to make closures work, but # of boilerplate code to make closures work, but
# that's about it # 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 # Currently defer is not functional so we
# just pop the instructions # just pop the instructions
for i in countup(deferStart, self.deferred.len() - 1, 1): for i in countup(deferStart, self.deferred.len() - 1, 1):

View File

@ -176,6 +176,8 @@ type
isAsync*: bool isAsync*: bool
isPure*: bool isPure*: bool
returnType*: Expression returnType*: Expression
hasExplicitReturn*: bool
SliceExpr* = ref object of Expression SliceExpr* = ref object of Expression
expression*: Expression expression*: Expression
@ -258,6 +260,7 @@ type
isPrivate*: bool isPrivate*: bool
isPure*: bool isPure*: bool
returnType*: Expression returnType*: Expression
hasExplicitReturn*: bool
Pragma* = ref object of Expression Pragma* = ref object of Expression
name*: IdentExpr name*: IdentExpr
args*: seq[LiteralExpr] args*: seq[LiteralExpr]

View File

@ -167,9 +167,13 @@ proc peek(self: Parser, distance: int = 0): Token =
result = endOfFile result = endOfFile
else: else:
result = self.tokens[self.current + distance] 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 ## Returns true if we're at the
## end of the file. Note that the ## end of the file. Note that the
## parser expects an explicit ## 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].} = proc error(self: Parser, message: string) {.raises: [ParseError, ValueError].} =
## Raises a formatted ParseError exception ## Raises a formatted ParseError exception
var lexeme = self.getCurrentToken().lexeme var lexeme = self.peek().lexeme
var errorMessage = &"A fatal error occurred while parsing '{self.file}', line {self.peek().line} at '{lexeme}' -> {message}" 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) raise newException(ParseError, errorMessage)
@ -594,6 +602,11 @@ proc returnStmt(self: Parser): Statement =
value = self.expression() value = self.expression()
endOfLine("missing semicolon after return statement") endOfLine("missing semicolon after return statement")
result = newReturnStmt(value, tok) 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 = 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 # go all the way up to primary(), which will
# call us back with isLambda=true, allowing us # call us back with isLambda=true, allowing us
# to actually parse the function as an expression # to actually parse the function as an expression
dec(self.current) while not self.check(tok.kind):
dec(self.current)
result = Declaration(self.expressionStatement()) result = Declaration(self.expressionStatement())
self.currentFunction = enclosingFunction self.currentFunction = enclosingFunction
return result return result
elif isLambda: elif isLambda:
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok, self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok,
returnType = nil, pragmas = (@[])) returnType = nil, pragmas = (@[]))
elif not isOperator:
self.error("funDecl: invalid state")
if self.match(":"): if self.match(":"):
# Function has explicit return type # Function has explicit return type
if self.match([Function, Coroutine, Generator]): if self.match([Function, Coroutine, Generator]):