also derete this #3
|
@ -151,7 +151,7 @@ proc done(self: Compiler): bool =
|
||||||
result = self.current > self.ast.high()
|
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
|
## Raises a formatted CompileError exception
|
||||||
var tok = self.getCurrentNode().token
|
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}")
|
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()
|
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
self.chunk.code.delete(offset + 1) # Discards the 24 bit integer
|
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
|
||||||
let offsetArray = jump.toDouble()
|
let offsetArray = (jump - 1).toDouble() # -1 since we got rid of 1 byte!
|
||||||
self.chunk.code[offset + 1] = offsetArray[0]
|
self.chunk.code[offset + 1] = offsetArray[0]
|
||||||
self.chunk.code[offset + 2] = offsetArray[1]
|
self.chunk.code[offset + 2] = offsetArray[1]
|
||||||
else:
|
else:
|
||||||
|
@ -562,7 +562,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
let t = self.getStaticIndex(node)
|
let t = self.getStaticIndex(node)
|
||||||
let index = t.pos
|
let index = t.pos
|
||||||
if index != -1:
|
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.emitByte(LoadVar) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||||
self.emitBytes(index.toTriple())
|
self.emitBytes(index.toTriple())
|
||||||
else:
|
else:
|
||||||
|
@ -765,11 +765,13 @@ proc inferValueType(self: Compiler, node: ASTNode): ASTNode =
|
||||||
size[1] = &"int{size[1]}"
|
size[1] = &"int{size[1]}"
|
||||||
elif size[1].startsWith("f"):
|
elif size[1].startsWith("f"):
|
||||||
size[1] = size[1].strip(true, false, {'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]))
|
return newIdentExpr(Token(lexeme: size[1]))
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid type specifier '{size[1]}' for '{size[0]}'")
|
self.error(&"invalid type specifier '{size[1]}' for '{size[0]}'")
|
||||||
return newIdentExpr(Token(lexeme: "int"))
|
return newIdentExpr(Token(lexeme: "int"))
|
||||||
|
of nilExpr:
|
||||||
|
return newIdentExpr(Token(lexeme: "nil"))
|
||||||
else:
|
else:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
|
|
||||||
|
@ -887,10 +889,14 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
## Compiles return statements. An empty return
|
## Compiles return statements. An empty return
|
||||||
## implicitly returns nil
|
## implicitly returns nil
|
||||||
let returnType = self.inferExprType(node.value)
|
let returnType = self.inferExprType(node.value)
|
||||||
if returnType == nil:
|
if returnType == nil and self.currentFunction.returnType != nil:
|
||||||
self.error("expression has no type")
|
self.error(&"expected return value of type '{self.currentFunction.returnType.token.lexeme}', but expression has no type")
|
||||||
elif returnType.token.lexeme != self.currentFunction.returnType.token.lexeme:
|
elif self.currentFunction.returnType == nil:
|
||||||
self.error(&"expected value of type '{self.currentFunction.returnType.token.lexeme}', got '{returnType.token.lexeme}'")
|
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.expression(node.value)
|
||||||
self.emitByte(OpCode.Return)
|
self.emitByte(OpCode.Return)
|
||||||
|
|
||||||
|
@ -997,16 +1003,16 @@ proc statement(self: Compiler, node: ASTNode) =
|
||||||
|
|
||||||
proc varDecl(self: Compiler, node: VarDecl) =
|
proc varDecl(self: Compiler, node: VarDecl) =
|
||||||
## Compiles variable declarations
|
## 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.error(&"Cannot determine the type of '{node.name.token.lexeme}'")
|
||||||
self.expression(node.value)
|
self.expression(node.value)
|
||||||
self.declareName(node, IdentExpr(node.valueType))
|
self.declareName(node, IdentExpr(kind))
|
||||||
|
|
||||||
|
|
||||||
proc funDecl(self: Compiler, node: FunDecl) =
|
proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
## Compiles function declarations
|
## 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
|
# We store the current function
|
||||||
var function = self.currentFunction
|
var function = self.currentFunction
|
||||||
self.currentFunction = node
|
self.currentFunction = node
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
## A recursive-descent top-down parser implementation
|
## A recursive-descent top-down parser implementation
|
||||||
|
|
||||||
import strformat
|
import strformat
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
|
||||||
import meta/token
|
import meta/token
|
||||||
|
@ -120,7 +121,7 @@ proc step(self: Parser, n: int = 1): Token =
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc error(self: Parser, message: string) =
|
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.getCurrentToken().lexeme
|
||||||
var errorMessage = &"A fatal error occurred while parsing '{self.file}', line {self.peek().line} at '{lexeme}' -> {message}"
|
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)
|
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
|
# Forward declarations
|
||||||
proc expression(self: Parser): ASTNode
|
proc expression(self: Parser): ASTNode
|
||||||
proc expressionStatement(self: Parser): ASTNode
|
proc expressionStatement(self: Parser): ASTNode
|
||||||
|
@ -590,7 +602,7 @@ proc returnStmt(self: Parser): ASTNode =
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
if self.currentFunction == nil:
|
if self.currentFunction == nil:
|
||||||
self.error("'return' cannot be used outside functions")
|
self.error("'return' cannot be used outside functions")
|
||||||
var value: ASTNode
|
var value: ASTNode = newNilExpr(Token(lexeme: "nil"))
|
||||||
if not self.check(Semicolon):
|
if not self.check(Semicolon):
|
||||||
# Since return can be used on its own too
|
# Since return can be used on its own too
|
||||||
# (in which case it implicitly returns nil),
|
# (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
|
# A function without an explicit
|
||||||
# return type is the same as a void
|
# return type is the same as a void
|
||||||
# function in C (i.e. no return type)
|
# 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))
|
returnType = newIdentExpr(self.peek(-1))
|
||||||
if not self.match(LeftBrace):
|
if not self.match(LeftBrace):
|
||||||
# Argument-less function
|
# Argument-less function
|
||||||
|
@ -1031,7 +1043,7 @@ proc declaration(self: Parser): ASTNode =
|
||||||
of Operator:
|
of Operator:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = self.funDecl(isOperator=true)
|
result = self.funDecl(isOperator=true)
|
||||||
of Type, Comment, Whitespace, Tab:
|
of Type, Comment, TokenType.Whitespace, TokenType.Tab:
|
||||||
discard self.step() # TODO
|
discard self.step() # TODO
|
||||||
else:
|
else:
|
||||||
result = self.statement()
|
result = self.statement()
|
||||||
|
|
Loading…
Reference in New Issue