Fixed various bugs with parsing and compiling functions as well as with jumping

This commit is contained in:
Nocturn9x 2022-04-26 13:04:40 +02:00
parent ff7dc450ae
commit ed79385e2a
2 changed files with 35 additions and 17 deletions

View File

@ -151,7 +151,7 @@ proc done(self: Compiler): bool =
result = self.current > self.ast.high()
proc error(self: Compiler, message: string) =
proc error(self: Compiler, message: string) {.raises: [CompileError, ValueError].} =
## Raises a formatted CompileError exception
var tok = self.getCurrentNode().token
raise newException(CompileError, &"A fatal error occurred while compiling '{self.file}', module '{self.currentModule}' line {tok.line} at '{tok.lexeme}' -> {message}")
@ -246,8 +246,8 @@ proc patchJump(self: Compiler, offset: int) =
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
else:
discard
self.chunk.code.delete(offset + 1) # Discards the 24 bit integer
let offsetArray = jump.toDouble()
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
let offsetArray = (jump - 1).toDouble() # -1 since we got rid of 1 byte!
self.chunk.code[offset + 1] = offsetArray[0]
self.chunk.code[offset + 2] = offsetArray[1]
else:
@ -562,7 +562,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
let t = self.getStaticIndex(node)
let index = t.pos
if index != -1:
if t.closedOver:
if not t.closedOver:
self.emitByte(LoadVar) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
self.emitBytes(index.toTriple())
else:
@ -765,11 +765,13 @@ proc inferValueType(self: Compiler, node: ASTNode): ASTNode =
size[1] = &"int{size[1]}"
elif size[1].startsWith("f"):
size[1] = size[1].strip(true, false, {'f'})
size[1] = &"float{size[1]}"
size[1] = &"float{size[1]}"
return newIdentExpr(Token(lexeme: size[1]))
else:
self.error(&"invalid type specifier '{size[1]}' for '{size[0]}'")
return newIdentExpr(Token(lexeme: "int"))
of nilExpr:
return newIdentExpr(Token(lexeme: "nil"))
else:
discard # TODO
@ -887,10 +889,14 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
## Compiles return statements. An empty return
## implicitly returns nil
let returnType = self.inferExprType(node.value)
if returnType == nil:
self.error("expression has no type")
elif returnType.token.lexeme != self.currentFunction.returnType.token.lexeme:
self.error(&"expected value of type '{self.currentFunction.returnType.token.lexeme}', got '{returnType.token.lexeme}'")
if returnType == nil and self.currentFunction.returnType != nil:
self.error(&"expected return value of type '{self.currentFunction.returnType.token.lexeme}', but expression has no type")
elif self.currentFunction.returnType == nil:
if node.value.kind != nilExpr:
self.error("non-nil return value is not allowed in functions without an explicit return type")
else:
if returnType.token.lexeme != self.currentFunction.returnType.token.lexeme:
self.error(&"expected return value of type '{self.currentFunction.returnType.token.lexeme}', got '{returnType.token.lexeme}' instead")
self.expression(node.value)
self.emitByte(OpCode.Return)
@ -997,16 +1003,16 @@ proc statement(self: Compiler, node: ASTNode) =
proc varDecl(self: Compiler, node: VarDecl) =
## Compiles variable declarations
if self.inferDeclType(node) == nil:
let kind = self.inferDeclType(node)
if kind == nil:
self.error(&"Cannot determine the type of '{node.name.token.lexeme}'")
self.expression(node.value)
self.declareName(node, IdentExpr(node.valueType))
self.declareName(node, IdentExpr(kind))
proc funDecl(self: Compiler, node: FunDecl) =
## Compiles function declarations
if self.inferDeclType(node) == nil:
self.error(&"Cannot determine the return type of '{node.name.token.lexeme}'")
# We store the current function
var function = self.currentFunction
self.currentFunction = node

View File

@ -15,6 +15,7 @@
## A recursive-descent top-down parser implementation
import strformat
import strutils
import meta/token
@ -120,7 +121,7 @@ proc step(self: Parser, n: int = 1): Token =
self.current += 1
proc error(self: Parser, message: string) =
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}"
@ -181,6 +182,17 @@ proc expect(self: Parser, kind: TokenType, message: string = "") =
self.error(message)
proc expect(self: Parser, kinds: openarray[TokenType], message: string = "") =
## Behaves like self.expect(), except that
## an error is raised only if none of the
## given token kinds matches
for kind in kinds:
if self.match(kind):
return
if message.len() == 0:
self.error(&"""expecting any of the following tokens: {kinds.join(", ")}, but got {self.peek().kind} instead""")
# Forward declarations
proc expression(self: Parser): ASTNode
proc expressionStatement(self: Parser): ASTNode
@ -590,7 +602,7 @@ proc returnStmt(self: Parser): ASTNode =
let tok = self.peek(-1)
if self.currentFunction == nil:
self.error("'return' cannot be used outside functions")
var value: ASTNode
var value: ASTNode = newNilExpr(Token(lexeme: "nil"))
if not self.check(Semicolon):
# Since return can be used on its own too
# (in which case it implicitly returns nil),
@ -884,7 +896,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
# A function without an explicit
# return type is the same as a void
# function in C (i.e. no return type)
self.expect(Identifier, "expecting function return type after ':'")
self.expect([Identifier, Nil], "expecting function return type after ':'")
returnType = newIdentExpr(self.peek(-1))
if not self.match(LeftBrace):
# Argument-less function
@ -1031,7 +1043,7 @@ proc declaration(self: Parser): ASTNode =
of Operator:
discard self.step()
result = self.funDecl(isOperator=true)
of Type, Comment, Whitespace, Tab:
of Type, Comment, TokenType.Whitespace, TokenType.Tab:
discard self.step() # TODO
else:
result = self.statement()