Bug fixes across the toolchain. Minor fixes and touch ups

This commit is contained in:
Mattia Giambirtone 2024-03-05 21:03:59 +01:00
parent eca8d362d2
commit 40e11c5afe
5 changed files with 86 additions and 41 deletions

View File

@ -388,7 +388,7 @@ proc generateLiteral(self: BytecodeGenerator, literal: TypedExpr) =
of NaN:
self.emitByte(LoadNaN, node.token.line)
else:
self.error(&"Unknown typed node of type {node.kind} at generateLiteral()")
self.error(&"unknown typed node of type {node.kind} at generateLiteral()")
proc generateUnary(self: BytecodeGenerator, expression: TypedExpr) =
@ -413,7 +413,7 @@ proc generateExpression(self: BytecodeGenerator, expression: TypedExpr) =
of binaryExpr:
self.generateBinary(expression)
else:
self.error(&"Unknown typed node of type {node.kind} at generateExpression()")
self.error(&"unknown typed node of type {node.kind} at generateExpression()")
proc beginProgram(self: BytecodeGenerator): int =
@ -458,7 +458,7 @@ proc generate*(self: BytecodeGenerator, compiled: seq[TypedNode], typeChecker: T
self.generateExpression(TypedExprStmt(typedNode).expression)
self.emitByte(Pop, typedNode.node.token.line)
else:
self.error(&"Unknown typed node of type {typedNode.node.kind} at generate()")
self.error(&"unknown typed node of type {typedNode.node.kind} at generate()")
self.currentFile = currentFile
self.endProgram(offset)
result = self.chunk

View File

@ -152,10 +152,14 @@ proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) =
elif name.node.kind == NodeKind.typeDecl:
let typ = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
if pragma.args[0].token.lexeme[1..^2] == "owo":
self.error("Thou hast discovered the forbidden type")
# :3
self.error("Thou hast discovered ze forbidden type. Huzzah! :3")
if typ.isNil():
self.error("'magic' pragma: wrong argument value", pragma.args[0])
name.valueType = typ
elif name.node.kind == NodeKind.funDecl:
name.valueType.isBuiltin = true
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
else:
self.error("'magic' pragma is not valid in this context")
@ -427,6 +431,8 @@ proc isAny(typ: Type): bool =
for condition in typ.types:
if condition.kind.isAny():
return true
of Typevar:
return typ.wrapped.isAny()
else:
return false
@ -747,14 +753,14 @@ proc stringify*(self: TypeChecker, typ: Type): string =
inc(i)
result &= "]"
result &= "("
for i, (argName, argType, argDefault) in typ.parameters:
for i, (argName, argType, argDefault) in typ.signature:
result &= &"{argName}: {self.stringify(argType)}"
if not argDefault.isNil():
result &= &" = {argDefault.kind}"
if i < typ.parameters.len() - 1:
if i < typ.signature.len() - 1:
result &= ", "
result &= ")"
if not self.compare(typ.returnType, "nil".toIntrinsic()):
if not typ.returnType.isNil():
result &= &": {self.stringify(typ.returnType)}"
if typ.pragmas.len() > 0:
result &= " {"
@ -879,7 +885,7 @@ proc calcTypeDistance(self: TypeChecker, typ: Type, sig: TypeSignature): int =
## that the types are already compatible
case typ.kind:
of TypeKind.Function:
for (argA, argB) in zip(typ.parameters, sig):
for (argA, argB) in zip(typ.signature, sig):
result += self.getTypeDistance(argA.kind, argB.kind)
of TypeKind.Structure:
discard
@ -893,7 +899,7 @@ proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool
## given type signature
case typ.kind:
of TypeKind.Function:
if typ.parameters.len() < sig.len():
if typ.signature.len() < sig.len():
# If the function has less arguments than
# we have in our signature, then we surely
# can't match to it. This is different from
@ -909,7 +915,7 @@ proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool
return false
# We construct a new signature, without the default arguments
var args: TypeSignature = @[]
for argument in typ.parameters:
for argument in typ.signature:
if argument.default.isNil():
args.add(argument)
else:
@ -1020,7 +1026,7 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
# Extra checks
case result.valueType.kind:
of Function:
for (a, b) in zip(result.valueType.parameters, sig):
for (a, b) in zip(result.valueType.signature, sig):
if not a.kind.isAny() and b.kind.isAny():
self.error("any is not a valid type in this context", node)
of Structure:
@ -1048,14 +1054,14 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
msg &= &"\n - in {relativePath(name.file, getCurrentDir())}:{name.ident.token.line}:{name.ident.token.relPos.start} -> {self.stringify(name.valueType)}"
if name.valueType.kind notin [Function, Structure]:
msg &= ": not callable"
elif sig.len() != name.valueType.parameters.len():
msg &= &": wrong number of arguments (expected {name.valueType.parameters.len()}, got {sig.len()} instead)"
elif sig.len() != name.valueType.signature.len():
msg &= &": wrong number of arguments (expected {name.valueType.signature.len()}, got {sig.len()} instead)"
else:
for i, arg in sig:
if arg.name != "" and name.valueType.parameters[i].name != "" and arg.name != name.valueType.parameters[i].name:
if arg.name != "" and name.valueType.signature[i].name != "" and arg.name != name.valueType.signature[i].name:
msg &= &": unexpected argument '{arg.name}' at position {i + 1}"
if not self.compare(arg.kind, name.valueType.parameters[i].kind):
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.parameters[i].kind)}, got {self.stringify(arg.kind)} instead)"
if not self.compare(arg.kind, name.valueType.signature[i].kind):
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.signature[i].kind)}, got {self.stringify(arg.kind)} instead)"
break
else:
msg &= " (compile with --showMismatches for more details)"
@ -1127,7 +1133,7 @@ proc expandTypeConstraints(self: TypeChecker, condition: Expression, list: var s
of identExpr, genericExpr:
var typ = self.inferOrError(condition).kind
if self.compare(typ, "auto".toIntrinsic()):
if not typ.isAny() and self.compare(typ, "auto".toIntrinsic()):
self.error("automatic types cannot be used within type constraints", condition)
list.add((accept, typ, condition))
of binaryExpr:
@ -1188,6 +1194,8 @@ proc dispatchDelayedPragmas(self: TypeChecker, name: Name) {.used.} =
proc addName(self: TypeChecker, name: Name) =
## Adds a name to the current lexical scope
if name.file == "":
name.file = self.file
var scope = self.names[self.scopeDepth]
if scope.hasKey(name.ident.token.lexeme):
for obj in scope[name.ident.token.lexeme]:
@ -1216,7 +1224,6 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
ident: node.name,
isPrivate: node.isPrivate,
module: self.currentModule,
file: self.file,
valueType: nil, # Done later in varDecl (for better semantics)
line: node.token.line,
owner: self.currentFunction,
@ -1225,13 +1232,28 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
)
self.addName(name)
of NodeKind.funDecl:
discard
var node = FunDecl(node)
declaredName = node.name.token.lexeme
var kind = Type(kind: Function,
genericTypes: newTable[string, Type](),
genericValues: newTable[string, Type](),
pragmas: newTable[string, Pragma]()
)
name = Name(depth: self.scopeDepth,
module: self.currentModule,
node: node,
ident: node.name,
line: node.name.token.line,
isPrivate: node.isPrivate,
owner: self.currentFunction,
valueType: kind)
self.addName(name)
of NodeKind.importStmt:
discard
of NodeKind.typeDecl:
var node = TypeDecl(node)
declaredName = node.name.token.lexeme
var kind: Type = Type(kind: Structure,
var kind = Type(kind: Structure,
name: declaredName,
genericTypes: newTable[string, Type](),
genericValues: newTable[string, Type](),
@ -1263,14 +1285,19 @@ proc identifier(self: TypeChecker, node: IdentExpr): TypedExpr =
return newTypedIdentExpr(node, self.findOrError(node.name.lexeme, node=node))
proc isGeneric(typ: Type): bool = typ.genericTypes.len() > 0 or typ.genericValues.len() > 0
proc unary(self: TypeChecker, node: UnaryExpr): TypedUnaryExpr =
## Typechecks unary expressions
var
default: TypedExpr
typeOfA = self.infer(node.a)
let fn = Type(kind: Function, returnType: Type(kind: Any), parameters: @[("", typeOfA.kind, default)])
let name = self.match(node.token.lexeme, fn.parameters, node)
let impl = self.concretize(name, @[self.expression(node.a)])
let fn = Type(kind: Function, returnType: Type(kind: Any), signature: @[("", typeOfA.kind, default)])
let name = self.match(node.token.lexeme, fn.signature, node)
var impl = name.valueType
if impl.isGeneric():
impl = self.concretize(name, @[self.expression(node.a)])
result = newTypedUnaryExpr(node, impl.returnType, typeOfA)
@ -1282,9 +1309,11 @@ proc binary(self: TypeChecker, node: BinaryExpr): TypedBinaryExpr =
typeOfB = self.infer(node.b)
let fn = Type(kind: Function,
returnType: Type(kind: Any),
parameters: @[("", typeOfA.kind, default), ("", typeOfB.kind, default)])
let name = self.match(node.token.lexeme, fn.parameters, node)
let impl = self.concretize(name, @[self.expression(node.a)])
signature: @[("", typeOfA.kind, default), ("", typeOfB.kind, default)])
let name = self.match(node.token.lexeme, fn.signature, node)
var impl = name.valueType
if impl.isGeneric():
impl = self.concretize(name, @[self.expression(node.a), self.expression(node.b)])
result = newTypedBinaryExpr(node, impl.returnType, typeOfA, typeOfB)
@ -1519,23 +1548,34 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
# First we declare the function's generics, if it has any
self.declareGenerics(name)
# We now declare and typecheck the function's
# arguments
# arguments and return type
for paramName in node.parameters.keys():
let parameter = node.parameters[paramName]
var typ = self.check(parameter.valueType, "typevar".toIntrinsic()).kind.unwrapType()
if parameter.default.isNil():
name.valueType.signature.add((parameter.ident.token.lexeme, typ, nil))
else:
var default = self.inferOrError(parameter.default)
var typ = self.check(typ, default.kind)
name.valueType.signature.add((parameter.ident.token.lexeme, typ, default))
if not node.returnType.isNil():
# The function needs a return type too!
name.valueType.returnType = self.check(node.returnType, "typevar".toIntrinsic()).kind.unwrapType()
if not name.valueType.isAuto and self.compare(name.valueType.returnType, "auto".toIntrinsic()):
name.valueType.isAuto = true
if name.valueType.isBuiltin:
self.endScope()
return
if node.body.isNil():
# Forward declaration
# TODO
self.endScope()
return
if BlockStmt(node.body).code.len() == 0:
self.error("cannot declare function with empty body")
# We store the current function to restore
# it later
let function = self.currentFunction
self.currentFunction = name
if BlockStmt(node.body).code.len() == 0:
self.error("cannot declare function with empty body")
for decl in BlockStmt(node.body).code:
result.body.body.add(self.validate(decl))
self.endScope()
@ -1660,7 +1700,8 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
result.fields[field.ident.token.lexeme] = newTypedExpr(field.ident, result.parent.valueType.fields[field.ident.token.lexeme])
# Turn the declared type into a typevar so that future references
# to it will be distinct from its instances
name.valueType = name.valueType.wrapType()
if not name.valueType.isTypevar():
name.valueType = name.valueType.wrapType()
# TODO: Check interfaces
self.endScope()
@ -1739,7 +1780,7 @@ proc validate*(self: TypeChecker, tree: ParseTree, file, source: string, showMis
file: self.file,
valueType: Type(kind: Function,
returnType: nil,
parameters: @[],
signature: @[],
),
ident: newIdentExpr(Token(lexeme: "", kind: Identifier)),
line: 1)

View File

@ -45,6 +45,7 @@ type
Const,
Generic
Type* = ref object
## A compile-time type
@ -72,9 +73,11 @@ type
isGenerator*: bool
isCoroutine*: bool
isAuto*: bool
parameters*: TypeSignature
signature*: TypeSignature
returnType*: Type
unsafe*: bool
isBuiltin*: bool
builtinOp*: string
of Typevar:
wrapped*: Type
of Structure:
@ -174,7 +177,7 @@ type
TypedCallExpr* = ref object of TypedExpr
## A typed function call expression
callee*: Name
args*: seq[tuple[name: string, kind: Type, default: TypedExpr]]
args*: TypeSignature
TypedDecl* = ref object of TypedNode
## A typed declaration node
@ -274,8 +277,7 @@ proc newTypedBinaryExpr*(node: UnaryExpr, kind: Type, a, b: TypedExpr): TypedBin
proc newTypedCallExpr*(node: CallExpr, callee: Name,
args: seq[tuple[name: string, kind: Type, default: TypedExpr]]): TypedCallExpr =
args: TypeSignature): TypedCallExpr =
## Initializes a new typed function call expression
result = TypedCallExpr(node: node, callee: callee, args: args, kind: callee.valueType.returnType)

View File

@ -1045,8 +1045,7 @@ proc funDecl(self: Parser, isOperator: bool = false): FunDecl =
var
parameters: Parameters = newTable[string, Parameter]()
returnType: Expression
pragmas: seq[Pragma] = @[]
function = newFunDecl(newIdentExpr(name), parameters, nil, true, name, pragmas, returnType)
function = newFunDecl(newIdentExpr(name), parameters, nil, true, name, @[], returnType)
function.file = self.file
if self.match("*"):
function.isPrivate = true
@ -1073,7 +1072,7 @@ proc funDecl(self: Parser, isOperator: bool = false): FunDecl =
self.expect(LeftBrace)
if self.match(TokenType.Pragma):
for pragma in self.parsePragmas():
pragmas.add(pragma)
function.pragmas.add(pragma)
function.body = self.blockStmt()
else:
# This is a forward declaration, so we keep the
@ -1081,7 +1080,7 @@ proc funDecl(self: Parser, isOperator: bool = false): FunDecl =
# it as such
if self.match(TokenType.Pragma):
for pragma in self.parsePragmas():
pragmas.add(pragma)
function.pragmas.add(pragma)
function.returnType = returnType
result = function
if isOperator:
@ -1338,6 +1337,6 @@ proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[sta
node = self.dispatch()
if not node.isNil():
# This only happens because we haven't implemented
# all of our grammar yet. Will be removed soon
# all of our grammar yet. Will be removed soon(TM)
self.tree.add(node)
result = self.tree

View File

@ -81,6 +81,9 @@ proc runFile(filename: string, fromString: bool = false, dump: bool = true, gene
filename = filename
isBinary = false
output = output
if output == "":
output = filename.replace(".pn", "")
echo output
tokenizer.fillSymbolTable()
try:
if not fromString and filename.endsWith(".pbc"):