Added Compiler.check(), made type constraints mandatory in generics, reverted '\n' being converted to a semicolon, minor refactoring
This commit is contained in:
parent
2072f34d4c
commit
b40275b52f
|
@ -773,6 +773,22 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name =
|
||||||
return impl[0]
|
return impl[0]
|
||||||
|
|
||||||
|
|
||||||
|
proc check(self: Compiler, term: Expression, kind: Type) =
|
||||||
|
## Checks the type of term against a known type.
|
||||||
|
## Raises an error if appropriate and returns
|
||||||
|
## otherwise
|
||||||
|
let k = self.inferType(term)
|
||||||
|
if k.isNil():
|
||||||
|
if term.kind == identExpr:
|
||||||
|
self.error(&"reference to undeclared name '{term.token.lexeme}'")
|
||||||
|
elif term.kind == callExpr and CallExpr(term).callee.kind == identExpr:
|
||||||
|
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'")
|
||||||
|
self.error(&"expecting value of type '{self.typeToStr(kind)}', but expression has no type")
|
||||||
|
elif not self.compareTypes(k, kind):
|
||||||
|
self.error(&"expecting value of type '{self.typeToStr(k)}', got '{self.typeToStr(k)}' instead")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc emitFunction(self: Compiler, name: Name) =
|
proc emitFunction(self: Compiler, name: Name) =
|
||||||
## Wrapper to emit LoadFunction instructions
|
## Wrapper to emit LoadFunction instructions
|
||||||
if name.isFunDecl:
|
if name.isFunDecl:
|
||||||
|
@ -1271,13 +1287,8 @@ proc blockStmt(self: Compiler, node: BlockStmt) =
|
||||||
proc ifStmt(self: Compiler, node: IfStmt) =
|
proc ifStmt(self: Compiler, node: IfStmt) =
|
||||||
## Compiles if/else statements for conditional
|
## Compiles if/else statements for conditional
|
||||||
## execution of code
|
## execution of code
|
||||||
var cond = self.inferType(node.condition)
|
self.check(node.condition, Type(kind: Bool))
|
||||||
self.expression(node.condition)
|
self.expression(node.condition)
|
||||||
if not self.compareTypes(cond, Type(kind: Bool)):
|
|
||||||
if cond.isNil():
|
|
||||||
self.error(&"expecting value of type 'bool', but expression has no type")
|
|
||||||
else:
|
|
||||||
self.error(&"expecting value of type 'bool', got '{self.typeToStr(cond)}' instead")
|
|
||||||
let jump = self.emitJump(JumpIfFalsePop)
|
let jump = self.emitJump(JumpIfFalsePop)
|
||||||
self.statement(node.thenBranch)
|
self.statement(node.thenBranch)
|
||||||
let jump2 = self.emitJump(JumpForwards)
|
let jump2 = self.emitJump(JumpForwards)
|
||||||
|
@ -1290,7 +1301,7 @@ proc ifStmt(self: Compiler, node: IfStmt) =
|
||||||
proc emitLoop(self: Compiler, begin: int) =
|
proc emitLoop(self: Compiler, begin: int) =
|
||||||
## Emits a JumpBackwards instruction with the correct
|
## Emits a JumpBackwards instruction with the correct
|
||||||
## jump offset
|
## jump offset
|
||||||
var offset = self.chunk.code.len() - begin + 4
|
let offset = self.chunk.code.len() - begin + 4
|
||||||
if offset > 16777215:
|
if offset > 16777215:
|
||||||
self.error("cannot jump more than 16777215 bytecode instructions")
|
self.error("cannot jump more than 16777215 bytecode instructions")
|
||||||
self.emitByte(JumpBackwards)
|
self.emitByte(JumpBackwards)
|
||||||
|
@ -1300,15 +1311,10 @@ proc emitLoop(self: Compiler, begin: int) =
|
||||||
proc whileStmt(self: Compiler, node: WhileStmt) =
|
proc whileStmt(self: Compiler, node: WhileStmt) =
|
||||||
## Compiles C-style while loops and
|
## Compiles C-style while loops and
|
||||||
## desugared C-style for loops
|
## desugared C-style for loops
|
||||||
var cond = self.inferType(node.condition)
|
self.check(node.condition, Type(kind: Bool))
|
||||||
self.expression(node.condition)
|
self.expression(node.condition)
|
||||||
if not self.compareTypes(cond, Type(kind: Bool)):
|
|
||||||
if cond.isNil():
|
|
||||||
self.error(&"expecting value of type 'bool', but expression has no type")
|
|
||||||
else:
|
|
||||||
self.error(&"expecting value of type 'bool', got '{self.typeToStr(cond)}' instead")
|
|
||||||
let start = self.chunk.code.len()
|
let start = self.chunk.code.len()
|
||||||
var jump = self.emitJump(JumpIfFalsePop)
|
let jump = self.emitJump(JumpIfFalsePop)
|
||||||
self.statement(node.body)
|
self.statement(node.body)
|
||||||
self.patchJump(jump)
|
self.patchJump(jump)
|
||||||
self.emitLoop(start)
|
self.emitLoop(start)
|
||||||
|
@ -1329,7 +1335,7 @@ proc callExpr(self: Compiler, node: CallExpr) =
|
||||||
kind = self.inferType(argument)
|
kind = self.inferType(argument)
|
||||||
if kind.isNil():
|
if kind.isNil():
|
||||||
if argument.kind == identExpr:
|
if argument.kind == identExpr:
|
||||||
self.error(&"reference to undeclared identifier '{IdentExpr(argument).name.lexeme}'")
|
self.error(&"reference to undeclared name '{IdentExpr(argument).name.lexeme}'")
|
||||||
self.error(&"cannot infer the type of argument {i + 1} in function call")
|
self.error(&"cannot infer the type of argument {i + 1} in function call")
|
||||||
args.add(("", kind))
|
args.add(("", kind))
|
||||||
argExpr.add(argument)
|
argExpr.add(argument)
|
||||||
|
@ -1459,7 +1465,7 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
if actual.isNil() and not expected.isNil():
|
if actual.isNil() and not expected.isNil():
|
||||||
if not node.value.isNil():
|
if not node.value.isNil():
|
||||||
if node.value.kind == identExpr:
|
if node.value.kind == identExpr:
|
||||||
self.error(&"reference to undeclared identifier '{node.value.token.lexeme}'")
|
self.error(&"reference to undeclared name '{node.value.token.lexeme}'")
|
||||||
elif node.value.kind == callExpr and CallExpr(node.value).callee.kind == identExpr:
|
elif node.value.kind == callExpr and CallExpr(node.value).callee.kind == identExpr:
|
||||||
self.error(&"call to undeclared function '{CallExpr(node.value).callee.token.lexeme}'")
|
self.error(&"call to undeclared function '{CallExpr(node.value).callee.token.lexeme}'")
|
||||||
self.error(&"expected return value of type '{self.typeToStr(expected)}', but expression has no type")
|
self.error(&"expected return value of type '{self.typeToStr(expected)}', but expression has no type")
|
||||||
|
@ -1478,12 +1484,13 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
self.emitByte(0)
|
self.emitByte(0)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Implement this as a custom operator
|
||||||
proc yieldStmt(self: Compiler, node: YieldStmt) =
|
proc yieldStmt(self: Compiler, node: YieldStmt) =
|
||||||
## Compiles yield statements
|
## Compiles yield statements
|
||||||
self.expression(node.expression)
|
self.expression(node.expression)
|
||||||
self.emitByte(OpCode.Yield)
|
self.emitByte(OpCode.Yield)
|
||||||
|
|
||||||
|
# TODO: Implement this as a custom operator
|
||||||
proc raiseStmt(self: Compiler, node: RaiseStmt) =
|
proc raiseStmt(self: Compiler, node: RaiseStmt) =
|
||||||
## Compiles raise statements
|
## Compiles raise statements
|
||||||
self.expression(node.exception)
|
self.expression(node.exception)
|
||||||
|
@ -1551,11 +1558,10 @@ proc statement(self: Compiler, node: Statement) =
|
||||||
# The expression has no type, so we don't have to
|
# The expression has no type, so we don't have to
|
||||||
# pop anything
|
# pop anything
|
||||||
discard
|
discard
|
||||||
|
elif self.replMode:
|
||||||
|
self.emitByte(PopRepl)
|
||||||
else:
|
else:
|
||||||
if self.replMode:
|
self.emitByte(Pop)
|
||||||
self.emitByte(PopRepl)
|
|
||||||
else:
|
|
||||||
self.emitByte(Pop)
|
|
||||||
of NodeKind.ifStmt:
|
of NodeKind.ifStmt:
|
||||||
self.ifStmt(IfStmt(node))
|
self.ifStmt(IfStmt(node))
|
||||||
of NodeKind.assertStmt:
|
of NodeKind.assertStmt:
|
||||||
|
@ -1572,7 +1578,7 @@ proc statement(self: Compiler, node: Statement) =
|
||||||
self.importStmt(ImportStmt(node))
|
self.importStmt(ImportStmt(node))
|
||||||
of NodeKind.whileStmt:
|
of NodeKind.whileStmt:
|
||||||
# Note: Our parser already desugars
|
# Note: Our parser already desugars
|
||||||
# for loops to while loops!
|
# for loops to while loops
|
||||||
let loop = self.currentLoop
|
let loop = self.currentLoop
|
||||||
self.currentLoop = Loop(start: self.chunk.code.len(),
|
self.currentLoop = Loop(start: self.chunk.code.len(),
|
||||||
depth: self.scopeDepth, breakPos: @[])
|
depth: self.scopeDepth, breakPos: @[])
|
||||||
|
@ -1604,7 +1610,7 @@ proc varDecl(self: Compiler, node: VarDecl) =
|
||||||
var name = node.value.token.lexeme
|
var name = node.value.token.lexeme
|
||||||
if node.value.kind == callExpr:
|
if node.value.kind == callExpr:
|
||||||
name = CallExpr(node.value).callee.token.lexeme
|
name = CallExpr(node.value).callee.token.lexeme
|
||||||
self.error(&"reference to undeclared identifier '{name}'")
|
self.error(&"reference to undeclared name '{name}'")
|
||||||
self.error(&"'{node.name.token.lexeme}' has no type")
|
self.error(&"'{node.name.token.lexeme}' has no type")
|
||||||
elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway)
|
elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway)
|
||||||
self.error(&"invalid type '{self.typeToStr(expected)}' for var")
|
self.error(&"invalid type '{self.typeToStr(expected)}' for var")
|
||||||
|
|
|
@ -565,8 +565,9 @@ proc next(self: Lexer) =
|
||||||
elif self.match("\n"):
|
elif self.match("\n"):
|
||||||
# New line
|
# New line
|
||||||
self.incLine()
|
self.incLine()
|
||||||
if not self.getToken("\n").isNil():
|
# TODO: Broken
|
||||||
self.createToken(Semicolon)
|
#[if not self.getToken("\n").isNil():
|
||||||
|
self.createToken(Semicolon)]#
|
||||||
elif self.match("`"):
|
elif self.match("`"):
|
||||||
# Stropped token
|
# Stropped token
|
||||||
self.parseBackticks()
|
self.parseBackticks()
|
||||||
|
|
|
@ -155,11 +155,11 @@ proc getCurrentFunction*(self: Parser): Declaration {.inline.} = self.currentFun
|
||||||
proc getFile*(self: Parser): string {.inline.} = self.file
|
proc getFile*(self: Parser): string {.inline.} = self.file
|
||||||
proc getModule*(self: Parser): string {.inline.} = self.getFile().extractFilename()
|
proc getModule*(self: Parser): string {.inline.} = self.getFile().extractFilename()
|
||||||
|
|
||||||
# Handy templates to make our life easier, thanks nim!
|
|
||||||
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
||||||
template endOfLine(msg: string) = self.expect(Semicolon, msg)
|
template endOfLine(msg: string) = self.expect(Semicolon, msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc peek(self: Parser, distance: int = 0): Token =
|
proc peek(self: Parser, distance: int = 0): Token =
|
||||||
## Peeks at the token at the given distance.
|
## Peeks at the token at the given distance.
|
||||||
## If the distance is out of bounds, an EOF
|
## If the distance is out of bounds, an EOF
|
||||||
|
@ -251,8 +251,7 @@ proc match[T: TokenType or string](self: Parser, kind: openarray[T]): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
|
|
||||||
proc expect[T: TokenType or string](self: Parser, kind: T,
|
proc expect[T: TokenType or string](self: Parser, kind: T, message: string = "") =
|
||||||
message: string = "") =
|
|
||||||
## Behaves like self.match(), except that
|
## Behaves like self.match(), except that
|
||||||
## when a token doesn't match, an error
|
## when a token doesn't match, an error
|
||||||
## is raised. If no error message is
|
## is raised. If no error message is
|
||||||
|
@ -264,8 +263,7 @@ proc expect[T: TokenType or string](self: Parser, kind: T,
|
||||||
self.error(message)
|
self.error(message)
|
||||||
|
|
||||||
|
|
||||||
proc expect[T: TokenType or string](self: Parser, kind: openarray[T],
|
proc expect[T: TokenType or string](self: Parser, kind: openarray[T], message: string = "") =
|
||||||
message: string = "") =
|
|
||||||
## Behaves like self.expect(), except that
|
## Behaves like self.expect(), except that
|
||||||
## an error is raised only if none of the
|
## an error is raised only if none of the
|
||||||
## given token kinds matches
|
## given token kinds matches
|
||||||
|
@ -273,7 +271,7 @@ proc expect[T: TokenType or string](self: Parser, kind: openarray[T],
|
||||||
if self.match(kind):
|
if self.match(kind):
|
||||||
return
|
return
|
||||||
if message.len() == 0:
|
if message.len() == 0:
|
||||||
self.error(&"""expecting any of the following tokens: {kinds.join(", ")}, but got {self.peek().kind} instead""")
|
self.error(&"""expecting any of the following tokens: {kind.join(", ")}, but got {self.peek().kind} instead""")
|
||||||
|
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
|
@ -526,7 +524,7 @@ proc assertStmt(self: Parser): Statement =
|
||||||
## fed into them is falsey
|
## fed into them is falsey
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing semicolon after assert statement")
|
endOfLine("missing statement terminator after 'assert'")
|
||||||
result = newAssertStmt(expression, tok)
|
result = newAssertStmt(expression, tok)
|
||||||
|
|
||||||
|
|
||||||
|
@ -561,7 +559,7 @@ proc breakStmt(self: Parser): Statement =
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
if self.currentLoop != Loop:
|
if self.currentLoop != Loop:
|
||||||
self.error("'break' cannot be used outside loops")
|
self.error("'break' cannot be used outside loops")
|
||||||
endOfLine("missing semicolon after break statement")
|
endOfLine("missing statement terminator after 'break'")
|
||||||
result = newBreakStmt(tok)
|
result = newBreakStmt(tok)
|
||||||
|
|
||||||
|
|
||||||
|
@ -571,7 +569,7 @@ proc deferStmt(self: Parser): Statement =
|
||||||
if self.currentFunction.isNil():
|
if self.currentFunction.isNil():
|
||||||
self.error("'defer' cannot be used outside functions")
|
self.error("'defer' cannot be used outside functions")
|
||||||
result = newDeferStmt(self.expression(), tok)
|
result = newDeferStmt(self.expression(), tok)
|
||||||
endOfLine("missing semicolon after defer statement")
|
endOfLine("missing statement terminator after 'defer'")
|
||||||
|
|
||||||
|
|
||||||
proc continueStmt(self: Parser): Statement =
|
proc continueStmt(self: Parser): Statement =
|
||||||
|
@ -579,7 +577,7 @@ proc continueStmt(self: Parser): Statement =
|
||||||
let tok = self.peek(-1)
|
let tok = self.peek(-1)
|
||||||
if self.currentLoop != Loop:
|
if self.currentLoop != Loop:
|
||||||
self.error("'continue' cannot be used outside loops")
|
self.error("'continue' cannot be used outside loops")
|
||||||
endOfLine("missing semicolon after continue statement")
|
endOfLine("missing statement terminator after 'continue'")
|
||||||
result = newContinueStmt(tok)
|
result = newContinueStmt(tok)
|
||||||
|
|
||||||
|
|
||||||
|
@ -594,7 +592,7 @@ proc returnStmt(self: Parser): Statement =
|
||||||
# we need to check if there's an actual value
|
# we need to check if there's an actual value
|
||||||
# to return or not
|
# to return or not
|
||||||
value = self.expression()
|
value = self.expression()
|
||||||
endOfLine("missing semicolon after return statement")
|
endOfLine("missing statement terminator after 'return'")
|
||||||
result = newReturnStmt(value, tok)
|
result = newReturnStmt(value, tok)
|
||||||
case self.currentFunction.kind:
|
case self.currentFunction.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
|
@ -614,7 +612,7 @@ proc yieldStmt(self: Parser): Statement =
|
||||||
result = newYieldStmt(self.expression(), tok)
|
result = newYieldStmt(self.expression(), tok)
|
||||||
else:
|
else:
|
||||||
result = newYieldStmt(newNilExpr(Token(lexeme: "nil")), tok)
|
result = newYieldStmt(newNilExpr(Token(lexeme: "nil")), tok)
|
||||||
endOfLine("missing semicolon after yield statement")
|
endOfLine("missing statement terminator after 'yield'")
|
||||||
|
|
||||||
|
|
||||||
proc awaitStmt(self: Parser): Statement =
|
proc awaitStmt(self: Parser): Statement =
|
||||||
|
@ -625,7 +623,7 @@ proc awaitStmt(self: Parser): Statement =
|
||||||
if self.currentFunction.token.kind != Coroutine:
|
if self.currentFunction.token.kind != Coroutine:
|
||||||
self.error("'await' can only be used inside coroutines")
|
self.error("'await' can only be used inside coroutines")
|
||||||
result = newAwaitStmt(self.expression(), tok)
|
result = newAwaitStmt(self.expression(), tok)
|
||||||
endOfLine("missing semicolon after await statement")
|
endOfLine("missing statement terminator after 'await'")
|
||||||
|
|
||||||
|
|
||||||
proc raiseStmt(self: Parser): Statement =
|
proc raiseStmt(self: Parser): Statement =
|
||||||
|
@ -636,7 +634,7 @@ proc raiseStmt(self: Parser): Statement =
|
||||||
# Raise can be used on its own, in which
|
# Raise can be used on its own, in which
|
||||||
# case it re-raises the last active exception
|
# case it re-raises the last active exception
|
||||||
exception = self.expression()
|
exception = self.expression()
|
||||||
endOfLine("missing semicolon after raise statement")
|
endOfLine("missing statement terminator after 'raise'")
|
||||||
result = newRaiseStmt(exception, tok)
|
result = newRaiseStmt(exception, tok)
|
||||||
|
|
||||||
|
|
||||||
|
@ -666,7 +664,7 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement =
|
||||||
# TODO: New AST node
|
# TODO: New AST node
|
||||||
self.expect(Identifier, "expecting module name(s) after import statement")
|
self.expect(Identifier, "expecting module name(s) after import statement")
|
||||||
result = newImportStmt(newIdentExpr(self.peek(-1), self.scopeDepth), tok)
|
result = newImportStmt(newIdentExpr(self.peek(-1), self.scopeDepth), tok)
|
||||||
endOfLine("missing semicolon after import statement")
|
endOfLine("missing statement terminator after 'import'")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -932,8 +930,8 @@ proc parseGenerics(self: Parser, decl: Declaration) =
|
||||||
while not self.check(RightBracket) and not self.done():
|
while not self.check(RightBracket) and not self.done():
|
||||||
self.expect(Identifier, "expecting generic type name")
|
self.expect(Identifier, "expecting generic type name")
|
||||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||||
if self.match(":"):
|
self.expect(":", "expecting type constraint after generic name")
|
||||||
gen.cond = self.expression()
|
gen.cond = self.expression()
|
||||||
decl.generics.add(gen)
|
decl.generics.add(gen)
|
||||||
if not self.match(Comma):
|
if not self.match(Comma):
|
||||||
break
|
break
|
||||||
|
@ -1054,7 +1052,7 @@ proc expressionStatement(self: Parser): Statement =
|
||||||
## Parses expression statements, which
|
## Parses expression statements, which
|
||||||
## are expressions followed by a semicolon
|
## are expressions followed by a semicolon
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing semicolon after expression")
|
endOfLine("missing expression terminator")
|
||||||
result = Statement(newExprStmt(expression, expression.token))
|
result = Statement(newExprStmt(expression, expression.token))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -380,8 +380,8 @@ proc fillSymbolTable(tokenizer: Lexer) =
|
||||||
tokenizer.symbols.addSymbol("]", RightBracket)
|
tokenizer.symbols.addSymbol("]", RightBracket)
|
||||||
tokenizer.symbols.addSymbol(".", Dot)
|
tokenizer.symbols.addSymbol(".", Dot)
|
||||||
tokenizer.symbols.addSymbol(",", Comma)
|
tokenizer.symbols.addSymbol(",", Comma)
|
||||||
# tokenizer.symbols.addSymbol(";", Semicolon)
|
tokenizer.symbols.addSymbol(";", Semicolon)
|
||||||
tokenizer.symbols.addSymbol("\n", Semicolon)
|
# tokenizer.symbols.addSymbol("\n", Semicolon) # TODO: Broken
|
||||||
# Keywords
|
# Keywords
|
||||||
tokenizer.symbols.addKeyword("type", TokenType.Type)
|
tokenizer.symbols.addKeyword("type", TokenType.Type)
|
||||||
tokenizer.symbols.addKeyword("enum", Enum)
|
tokenizer.symbols.addKeyword("enum", Enum)
|
||||||
|
|
Loading…
Reference in New Issue