Bug fixes across the toolchain. Minor fixes and touch ups
This commit is contained in:
parent
eca8d362d2
commit
40e11c5afe
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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"):
|
||||
|
|
Loading…
Reference in New Issue