Initial work on generics, fixed StoreVar. declareName no longer emits any bytecode (as it doesn't need to)

This commit is contained in:
Mattia Giambirtone 2022-05-23 11:53:34 +02:00
parent 8d1699ff9e
commit 9c14bfae91
4 changed files with 36 additions and 15 deletions

View File

@ -227,6 +227,9 @@ proc dispatch*(self: PeonVM) =
continue
of Pop:
discard self.pop()
of PopN:
for _ in 0..<int(self.readLong()):
discard self.pop()
of Jump:
self.ip = int(self.readShort())
of JumpForwards:

View File

@ -38,7 +38,7 @@ type
Int8, UInt8, Int16, UInt16, Int32,
UInt32, Int64, UInt64, Float32, Float64,
Char, Byte, String, Function, CustomType,
Nil, Nan, Bool, Inf
Nil, Nan, Bool, Inf, Typedesc, Generic
Type* = ref object
## A wrapper around
## compile-time types
@ -489,6 +489,8 @@ proc toIntrinsic(name: string): Type =
return Type(kind: Inf)
elif name == "bool":
return Type(kind: Bool)
elif name == "type":
return Type(kind: Typedesc)
else:
return nil
@ -799,14 +801,9 @@ proc declareName(self: Compiler, node: Declaration) =
node.value).kind, node: node),
codePos: self.chunk.code.len(),
isLet: node.isLet))
self.emitByte(StoreVar)
self.emitBytes(self.names.high().toTriple())
of NodeKind.funDecl:
var node = FunDecl(node)
# Declares the function's name in the
# current scope but no StoreVar is emitted
# because the name is only useful at compile time.
# TODO: Maybe emit some optional debugging
# TODO: Emit some optional debugging
# metadata to let the VM know where a function's
# code begins and ends (similar to what gcc does with
# CFI in object files) to build stack traces
@ -837,6 +834,15 @@ proc declareName(self: Compiler, node: Declaration) =
codePos: self.chunk.code.len(),
isLet: false))
self.names[^1].valueType = self.inferType(argument.valueType)
# We check if the argument's type is a generic
if self.names[^1].valueType == nil and argument.valueType.kind == identExpr:
for gen in node.generics:
if gen.name == IdentExpr(argument.valueType):
self.names[^1].valueType = Type(kind: Generic)
break
# If it's still nil, it's an error!
if self.names[^1].valueType == nil:
self.error(&"cannot determine the type of argument '{self.names[^1].name.token.lexeme}'")
self.names[^1].valueType.node = argument.name
fn.valueType.args.add(self.names[^1].valueType)
else:
@ -1103,10 +1109,10 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
let returnType = self.inferType(node.value)
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 with an explicit return type")
elif returnType == nil and typ.returnType != nil:
if returnType == nil and typ.returnType != nil:
self.error(&"expected return value of type '{self.typeToStr(typ.returnType)}', but expression has no type")
elif typ.returnType == nil and returnType != nil:
self.error("empty return statement is not allowed in non-void functions")
elif not self.compareTypes(returnType, typ.returnType):
self.error(&"expected return value of type '{self.typeToStr(typ.returnType)}', got '{self.typeToStr(returnType)}' instead")
if node.value != nil:

View File

@ -672,7 +672,7 @@ proc `$`*(self: ASTNode): string =
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType})"
of funDecl:
var self = FunDecl(self)
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate})"""
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate})"""
of lambdaExpr:
var self = LambdaExpr(self)
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync})"""

View File

@ -785,8 +785,6 @@ proc ifStmt(self: Parser): Statement =
template checkDecl(self: Parser, isPrivate: bool) =
## Handy utility template that avoids us from copy
## pasting the same checks to all declaration handlers
if not isPrivate and self.currentFunction != nil:
self.error("cannot bind public names inside functions")
if not isPrivate and self.scopeDepth > 0:
self.error("cannot bind public names inside local scopes")
@ -895,6 +893,20 @@ proc parseFunExpr(self: Parser): LambdaExpr =
result.returnType = self.expression()
proc parseGenerics(self: Parser, decl: Declaration) =
## Parses generics in declarations
var gen: tuple[name: IdentExpr, cond: Expression]
while not self.check(RightBracket) and not self.done():
self.expect(Identifier, "expecting generic type name")
gen.name = newIdentExpr(self.peek(-1))
if self.match(":"):
gen.cond = self.expression()
decl.generics.add(gen)
if not self.match(Comma):
break
self.expect(RightBracket)
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
isLambda: bool = false, isOperator: bool = false): Declaration = # Can't use just FunDecl because it can also return LambdaExpr!
## Parses all types of functions, coroutines, generators and operators
@ -922,6 +934,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
if self.match("*"):
FunDecl(self.currentFunction).isPrivate = false
self.checkDecl(FunDecl(self.currentFunction).isPrivate)
if self.match(LeftBracket):
self.parseGenerics(self.currentFunction)
elif not isLambda and (self.check([LeftBrace, LeftParen]) or self.check(":")):
# We do a bit of hacking to pretend we never
# wanted to parse this as a declaration in
@ -981,8 +995,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
if isOperator:
if arguments.len() == 0:
self.error("cannot declare operator without arguments")
elif FunDecl(result).returnType == nil:
self.error("operators must have a return type")
elif isLambda:
self.error("cannot declare anonymous operator")
for argument in arguments: