preparation for type system overhaul (please send help)
This commit is contained in:
parent
83051d67f8
commit
838fc3d5a1
|
@ -52,7 +52,7 @@ proc newBytecodeGenerator*: BytecodeGenerator =
|
|||
proc generateExpression(self: BytecodeGenerator, expression: TypedExpr)
|
||||
|
||||
|
||||
proc error(self: BytecodeGenerator, msg: string, typedNode: TypedNode = nil) {.raises: [CodeGenError].} =
|
||||
proc error(self: BytecodeGenerator, msg: string, typedNode: TypedNode = nil) =
|
||||
## Raises a generic peon exception
|
||||
var typedNode = typedNode
|
||||
var file = self.currentFile
|
||||
|
@ -208,7 +208,11 @@ proc patchJump(self: BytecodeGenerator, offset: int) =
|
|||
proc handleBuiltinFunction(self: BytecodeGenerator, fn: FunctionWrapper, args: seq[TypedExpr], line: int) =
|
||||
## Emits instructions for builtin functions
|
||||
## such as addition or subtraction
|
||||
if fn.decl.name.valueType.builtinOp notin ["LogicalOr", "LogicalAnd"]:
|
||||
var builtinOp: string
|
||||
for pragma in FunDecl(fn.decl.node).pragmas:
|
||||
if pragma.name.token.lexeme == "magic":
|
||||
builtinOp = pragma.args[0].token.lexeme
|
||||
if builtinOp notin ["LogicalOr", "LogicalAnd"]:
|
||||
if len(args) == 2:
|
||||
self.generateExpression(args[1])
|
||||
self.generateExpression(args[0])
|
||||
|
@ -268,7 +272,7 @@ proc handleBuiltinFunction(self: BytecodeGenerator, fn: FunctionWrapper, args: s
|
|||
"NegInf": LoadNInf,
|
||||
"Identity": Identity
|
||||
}.toTable()
|
||||
if fn.decl.name.valueType.builtinOp == "print":
|
||||
if builtinOp == "print":
|
||||
let typ = args[0].kind
|
||||
case typ.kind:
|
||||
of Integer:
|
||||
|
@ -316,19 +320,19 @@ proc handleBuiltinFunction(self: BytecodeGenerator, fn: FunctionWrapper, args: s
|
|||
if typ.isLambda:
|
||||
str = &"anonymous function at 0x{loc}"
|
||||
else:
|
||||
str = &"function '{FunDecl(typ.fun).name.token.lexeme}' at 0x{loc}"
|
||||
str = &"function '{FunDecl(fn.decl.node).name.token.lexeme}' at 0x{loc}"
|
||||
self.emitBytes(str.len().toTriple(), line)
|
||||
self.emitBytes(self.chunk.writeConstant(str.toBytes()), line)
|
||||
self.emitByte(PrintString, line)
|
||||
else:
|
||||
self.error(&"invalid type {self.typechecker.stringify(typ)} for built-in 'print'", args[0])
|
||||
return
|
||||
if fn.decl.name.valueType.builtinOp in codes:
|
||||
self.emitByte(codes[fn.decl.name.valueType.builtinOp], line)
|
||||
if builtinOp in codes:
|
||||
self.emitByte(codes[builtinOp], line)
|
||||
return
|
||||
# Some builtin operations are slightly more complex
|
||||
# so we handle them separately
|
||||
case fn.decl.name.valueType.builtinOp:
|
||||
case builtinOp:
|
||||
of "LogicalOr":
|
||||
self.generateExpression(args[0])
|
||||
let jump = self.emitJump(JumpIfTrue, line)
|
||||
|
@ -347,7 +351,7 @@ proc handleBuiltinFunction(self: BytecodeGenerator, fn: FunctionWrapper, args: s
|
|||
# make it so that the peon stub can have no body
|
||||
discard
|
||||
else:
|
||||
self.error(&"unknown built-in: '{fn.decl.name.valueType.builtinOp}'")
|
||||
self.error(&"unknown built-in: '{builtinOp}'")
|
||||
|
||||
|
||||
proc patchReturnAddress(self: BytecodeGenerator, pos: int) =
|
||||
|
@ -364,25 +368,46 @@ proc patchReturnAddress(self: BytecodeGenerator, pos: int) =
|
|||
self.chunk.consts[pos + 7] = address[7]
|
||||
|
||||
|
||||
proc generateExpression(self: BytecodeGenerator, expression: TypedExpr) =
|
||||
## Emits code for expressions
|
||||
proc generateLiteral(self: BytecodeGenerator, literal: TypedExpr) =
|
||||
## Emits code for literals
|
||||
let
|
||||
typ = expression.kind
|
||||
node = expression.node
|
||||
typ = literal.kind
|
||||
node = literal.node
|
||||
case typ.kind:
|
||||
of Integer, Float:
|
||||
# No need to do any input validation here: the typechecker
|
||||
# has graciously done all the work for us! :)
|
||||
self.emitConstant(expression)
|
||||
self.emitConstant(literal)
|
||||
of Infinity:
|
||||
if typ.positive:
|
||||
self.emitByte(LoadInf, node.token.line)
|
||||
else:
|
||||
self.emitByte(LoadNInf, node.token.line)
|
||||
of NaN:
|
||||
self.emitByte(LoadNaN, node.token.line)
|
||||
self.emitByte(LoadNaN, node.token.line)
|
||||
else:
|
||||
discard # TODO
|
||||
self.error(&"Unknown typed node of type {node.kind} at generateLiteral()")
|
||||
|
||||
|
||||
proc generateUnary(self: BytecodeGenerator, expression: TypedExpr) =
|
||||
## Emits code for unary expressions
|
||||
echo expression[]
|
||||
|
||||
|
||||
proc generateExpression(self: BytecodeGenerator, expression: TypedExpr) =
|
||||
## Emits code for expressions
|
||||
if expression.node.isConst():
|
||||
self.generateLiteral(expression)
|
||||
else:
|
||||
let node = expression.node
|
||||
case node.kind:
|
||||
of binaryExpr:
|
||||
self.generateUnary(expression)
|
||||
of unaryExpr:
|
||||
discard
|
||||
else:
|
||||
self.error(&"Unknown typed node of type {node.kind} at generateExpression()")
|
||||
|
||||
|
||||
|
||||
proc beginProgram(self: BytecodeGenerator): int =
|
||||
|
@ -420,13 +445,13 @@ proc generate*(self: BytecodeGenerator, compiled: seq[TypedNode], typeChecker: T
|
|||
self.currentNode = typedNode
|
||||
let currentFile = self.currentFile
|
||||
if self.currentNode.node.isDecl():
|
||||
self.currentFile = TypedDecl(typedNode).name.ident.token.lexeme
|
||||
self.currentFile = TypedDecl(typedNode).name.owner.ident.token.lexeme
|
||||
case typedNode.node.kind:
|
||||
of exprStmt:
|
||||
self.generateExpression(TypedExprStmt(typedNode).expression)
|
||||
self.emitByte(Pop, typedNode.node.token.line)
|
||||
else:
|
||||
discard # TODO
|
||||
self.error(&"Unknown typed node of type {typedNode.node.kind} at generate()")
|
||||
self.currentFile = currentFile
|
||||
self.endProgram(offset)
|
||||
result = self.chunk
|
||||
|
|
|
@ -49,7 +49,7 @@ type
|
|||
## peon objects
|
||||
String, List,
|
||||
Dict, Tuple,
|
||||
CustomType,
|
||||
Structure,
|
||||
HeapObject* = object
|
||||
## A tagged box for a heap-allocated
|
||||
## peon object
|
||||
|
|
|
@ -144,19 +144,13 @@ proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|||
## Handles the "magic" pragma. Assumes the given name is already
|
||||
## declared
|
||||
if pragma.args.len() != 1:
|
||||
self.error(&"'magic' pragma: wrong number of arguments (expected 1, got {len(pragma.args)})")
|
||||
self.error(&"'magic' pragma: wrong number of arguments (expected 1, got {len(pragma.args)} instead)")
|
||||
elif pragma.args[0].kind != strExpr:
|
||||
self.error(&"'magic' pragma: wrong argument type (constant string expected, got {self.stringify(self.inferOrError(pragma.args[0]))})")
|
||||
elif name.node.kind == NodeKind.funDecl:
|
||||
name.valueType.intrinsic = true
|
||||
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
||||
self.error(&"'magic' pragma: wrong argument type (constant string expected, got {self.stringify(self.inferOrError(pragma.args[0]))} instead)")
|
||||
elif name.node.kind == NodeKind.typeDecl:
|
||||
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
||||
if name.valueType.isNil():
|
||||
let typ = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
||||
if typ.isNil():
|
||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||
if TypeDecl(name.node).isRef:
|
||||
name.valueType = name.valueType.toRef()
|
||||
name.valueType = name.valueType.wrapType()
|
||||
name.valueType.intrinsic = true
|
||||
else:
|
||||
self.error("'magic' pragma is not valid in this context")
|
||||
|
@ -186,10 +180,8 @@ proc handleWarnPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|||
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "pure" pragma
|
||||
case name.node.kind:
|
||||
of NodeKind.funDecl:
|
||||
FunDecl(name.node).isPure = true
|
||||
of NodeKind.lambdaExpr:
|
||||
LambdaExpr(name.node).isPure = true
|
||||
of NodeKind.funDecl, lambdaExpr:
|
||||
discard # TODO
|
||||
else:
|
||||
self.error("'pure' pragma is not valid in this context")
|
||||
|
||||
|
@ -244,19 +236,18 @@ proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name =
|
|||
|
||||
|
||||
proc wrapType(self: Type): Type {.inline.} =
|
||||
## Wraps a type in a typevar if it's not already
|
||||
## wrapped
|
||||
if self.kind != Typevar:
|
||||
return Type(kind: Typevar, wrapped: self)
|
||||
return self
|
||||
## Wraps a type in a typevar
|
||||
result = Type(kind: Typevar, wrapped: self)
|
||||
|
||||
|
||||
proc unwrapType(self: Type): Type {.inline.} =
|
||||
## Unwraps a typevar if it's not already
|
||||
## unwrapped
|
||||
if self.kind == Typevar:
|
||||
return self.wrapped
|
||||
return self
|
||||
case self.kind:
|
||||
of Typevar:
|
||||
result = self.wrapped
|
||||
else:
|
||||
result = self
|
||||
|
||||
|
||||
proc toRef(self: Type): Type {.inline.} =
|
||||
|
@ -285,54 +276,55 @@ proc toIntrinsic(name: string): Type =
|
|||
## Converts a string to an intrinsic
|
||||
## type if it is valid and returns nil
|
||||
## otherwise
|
||||
if name == "any":
|
||||
return Type(kind: Any, intrinsic: true)
|
||||
elif name == "auto":
|
||||
return Type(kind: Auto, intrinsic: true)
|
||||
elif name in ["int", "int64", "i64"]:
|
||||
return Type(kind: Integer, size: LongLong, signed: true, intrinsic: true)
|
||||
elif name in ["uint64", "u64", "uint"]:
|
||||
return Type(kind: Integer, size: LongLong, signed: false, intrinsic: true)
|
||||
elif name in ["int32", "i32"]:
|
||||
return Type(kind: Integer, size: Long, signed: true, intrinsic: true)
|
||||
elif name in ["uint32", "u32"]:
|
||||
return Type(kind: Integer, size: Long, signed: false, intrinsic: true)
|
||||
elif name in ["int16", "i16", "short"]:
|
||||
return Type(kind: Integer, size: Short, signed: true, intrinsic: true)
|
||||
elif name in ["uint16", "u16"]:
|
||||
return Type(kind: Integer, size: Short, signed: false, intrinsic: true)
|
||||
elif name in ["int8", "i8"]:
|
||||
return Type(kind: Integer, size: Tiny, signed: true, intrinsic: true)
|
||||
elif name in ["uint8", "u8"]:
|
||||
return Type(kind: Integer, size: Tiny, signed: false, intrinsic: true)
|
||||
elif name in ["f64", "float", "float64"]:
|
||||
return Type(kind: Float, width: Full, intrinsic: true)
|
||||
elif name in ["f32", "float32"]:
|
||||
return Type(kind: Float, width: Half, intrinsic: true)
|
||||
elif name in ["byte", "b"]:
|
||||
return Type(kind: Byte, intrinsic: true)
|
||||
elif name in ["char", "c"]:
|
||||
return Type(kind: Char, intrinsic: true)
|
||||
elif name == "NaN":
|
||||
return Type(kind: TypeKind.Nan, intrinsic: true)
|
||||
elif name == "Inf":
|
||||
return Type(kind: Infinity, intrinsic: true, positive: true)
|
||||
elif name == "NegInf":
|
||||
return Type(kind: Infinity, intrinsic: true)
|
||||
elif name == "bool":
|
||||
return Type(kind: Boolean, intrinsic: true)
|
||||
elif name == "typevar":
|
||||
return Type(kind: Typevar, intrinsic: true, wrapped: "any".toIntrinsic())
|
||||
elif name == "string":
|
||||
return Type(kind: String, intrinsic: true)
|
||||
elif name == "pointer":
|
||||
return Type(kind: Pointer, intrinsic: true, value: "any".toIntrinsic())
|
||||
elif name == "lent":
|
||||
return Type(kind: TypeKind.Lent, intrinsic: true, value: "any".toIntrinsic())
|
||||
elif name == "const":
|
||||
return Type(kind: TypeKind.Const, intrinsic: true, value: "any".toIntrinsic())
|
||||
elif name == "ref":
|
||||
return Type(kind: Reference, intrinsic: true, value: "any".toIntrinsic())
|
||||
case name:
|
||||
of "any":
|
||||
return Type(kind: Any)
|
||||
of "auto":
|
||||
return Type(kind: Auto)
|
||||
of "int64":
|
||||
return Type(kind: Integer, size: LongLong, signed: true)
|
||||
of "uint64":
|
||||
return Type(kind: Integer, size: LongLong, signed: false)
|
||||
of "int32":
|
||||
return Type(kind: Integer, size: Long, signed: true)
|
||||
of "uint32":
|
||||
return Type(kind: Integer, size: Long, signed: false)
|
||||
of "int16":
|
||||
return Type(kind: Integer, size: Short, signed: true)
|
||||
of "uint16":
|
||||
return Type(kind: Integer, size: Short, signed: false)
|
||||
of "int8":
|
||||
return Type(kind: Integer, size: Tiny, signed: true)
|
||||
of "uint8":
|
||||
return Type(kind: Integer, size: Tiny, signed: false)
|
||||
of "float64":
|
||||
return Type(kind: Float, width: Full)
|
||||
of "float32":
|
||||
return Type(kind: Float, width: Half)
|
||||
of "byte":
|
||||
return Type(kind: Byte)
|
||||
of "char":
|
||||
return Type(kind: Char)
|
||||
of "NaN":
|
||||
return Type(kind: TypeKind.Nan)
|
||||
of "Inf":
|
||||
return Type(kind: Infinity, positive: true)
|
||||
of "NegInf":
|
||||
return Type(kind: Infinity)
|
||||
of "bool":
|
||||
return Type(kind: Boolean)
|
||||
of "typevar":
|
||||
return Type(kind: Typevar, wrapped: "any".toIntrinsic())
|
||||
of "string":
|
||||
return Type(kind: String)
|
||||
of "pointer":
|
||||
return Type(kind: Pointer, value: "any".toIntrinsic())
|
||||
of "lent":
|
||||
return Type(kind: TypeKind.Lent, value: "any".toIntrinsic())
|
||||
of "const":
|
||||
return Type(kind: TypeKind.Const, value: "any".toIntrinsic())
|
||||
of "ref":
|
||||
return Type(kind: Reference, value: "any".toIntrinsic())
|
||||
|
||||
|
||||
|
||||
|
@ -407,6 +399,9 @@ proc matchGeneric(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Ty
|
|||
return true
|
||||
|
||||
|
||||
proc isType(typ: Type): bool =
|
||||
return typ.kind in [Structure, Typevar]
|
||||
|
||||
|
||||
proc isAny(typ: Type): bool =
|
||||
## Returns true if the given type is
|
||||
|
@ -415,10 +410,6 @@ proc isAny(typ: Type): bool =
|
|||
case typ.kind:
|
||||
of Any:
|
||||
return true
|
||||
of Generic:
|
||||
for condition in typ.cond:
|
||||
if condition.kind.isAny():
|
||||
return true
|
||||
of Union:
|
||||
for condition in typ.types:
|
||||
if condition.kind.isAny():
|
||||
|
@ -428,45 +419,16 @@ proc isAny(typ: Type): bool =
|
|||
|
||||
|
||||
proc compare(self: TypeChecker, a, b: Type): bool =
|
||||
if a.isNil() or b.isNil():
|
||||
raise newException(NilAccessDefect, "what the fuck are you doing")
|
||||
if a.isAny() or b.isAny():
|
||||
return true
|
||||
if a.kind == b.kind:
|
||||
case a.kind:
|
||||
# TODO: Take interfaces into account
|
||||
of CustomType:
|
||||
# First, compare their names (only if both types have names)
|
||||
if a.typeName != "" and a.typeName != b.typeName:
|
||||
return false
|
||||
# Then, compare the number of fields
|
||||
if a.fields.len() != b.fields.len():
|
||||
return false
|
||||
# a has a parent and b doesn't
|
||||
if b.parent.isNil() and not a.parent.isNil():
|
||||
return false
|
||||
# b has a parent and a doesn't
|
||||
if a.parent.isNil() and not b.parent.isNil():
|
||||
return false
|
||||
if not a.parent.isNil() and not b.parent.isNil():
|
||||
if not self.compare(a.parent, b.parent):
|
||||
# Different parent
|
||||
return false
|
||||
# Then the field names
|
||||
var fields: HashSet[string] = initHashSet[string]()
|
||||
for fieldName in a.fields.keys():
|
||||
fields.incl(fieldName)
|
||||
for fieldName in b.fields.keys():
|
||||
if not fields.contains(fieldName):
|
||||
return false
|
||||
# From here on out, we know that both a and b have
|
||||
# the same number of fields and that they share field
|
||||
# names. All we gotta now do is compare the types of the
|
||||
# fields
|
||||
for fieldName in a.fields.keys():
|
||||
if not self.compare(a.fields[fieldName], b.fields[fieldName]):
|
||||
return false
|
||||
# All checks passed, types are equal as
|
||||
# far as the type system is concerned
|
||||
return true
|
||||
of Structure:
|
||||
# TODO
|
||||
return false
|
||||
of Boolean, Infinity, Any,
|
||||
Auto, Char, Byte, String:
|
||||
return true
|
||||
|
@ -481,27 +443,8 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
of Typevar:
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
of Function:
|
||||
# Are arities the same?
|
||||
if a.args.len() != b.args.len():
|
||||
return false
|
||||
# Is it a coroutine/generator?
|
||||
if a.isCoroutine != b.isCoroutine or a.isGenerator != b.isGenerator:
|
||||
return false
|
||||
# Do they have compatible return types?
|
||||
if not self.compare(b.returnType, a.returnType):
|
||||
return false
|
||||
# Do they have compatible arguments?
|
||||
var i = 0
|
||||
for (argA, argB) in zip(a.args, b.args):
|
||||
if not self.compare(argA.kind, argB.kind):
|
||||
return false
|
||||
if argA.name == "":
|
||||
continue
|
||||
if argB.name == "":
|
||||
continue
|
||||
if argA.name != argB.name:
|
||||
return false
|
||||
return true
|
||||
# TODO
|
||||
return false
|
||||
else:
|
||||
# TODO
|
||||
return false
|
||||
|
@ -509,16 +452,6 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
return self.matchUnion(b, a.types)
|
||||
if b.kind == Union:
|
||||
return self.matchUnion(a, b.types)
|
||||
if a.kind == Generic:
|
||||
if a.asUnion:
|
||||
return self.matchUnion(b, a.cond)
|
||||
else:
|
||||
return self.matchGeneric(b, a.cond)
|
||||
if b.kind == Generic:
|
||||
if b.asUnion:
|
||||
return self.matchUnion(a, b.cond)
|
||||
else:
|
||||
return self.matchGeneric(a, b.cond)
|
||||
return false
|
||||
|
||||
|
||||
|
@ -637,6 +570,8 @@ proc find(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): Na
|
|||
if self.compare(obj.valueType, kind):
|
||||
result = obj
|
||||
break
|
||||
echo "DIOSTRONZO"
|
||||
|
||||
if not result.isNil():
|
||||
break
|
||||
dec(depth)
|
||||
|
@ -695,14 +630,23 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
of Char, Byte, String, TypeKind.Nan,
|
||||
Auto, Any:
|
||||
result &= ($typ.kind).toLowerAscii()
|
||||
of CustomType:
|
||||
result &= typ.typeName
|
||||
if typ.generics.len() > 0:
|
||||
of Structure:
|
||||
result &= typ.name
|
||||
if typ.genericTypes.len() > 0:
|
||||
result &= "<"
|
||||
var i = 0
|
||||
for gen in typ.genericTypes.keys():
|
||||
result &= &"{gen}: {self.stringify(typ.genericTypes[gen])}"
|
||||
if i < typ.genericTypes.len() - 1:
|
||||
result &= ", "
|
||||
inc(i)
|
||||
result &= ">"
|
||||
if typ.genericValues.len() > 0:
|
||||
result &= "["
|
||||
var i = 0
|
||||
for gen in typ.generics.keys():
|
||||
result &= &"{gen}: {self.stringify(typ.generics[gen])}"
|
||||
if i < typ.generics.len() - 1:
|
||||
for gen in typ.genericValues.keys():
|
||||
result &= &"{gen}: {self.stringify(typ.genericValues[gen])}"
|
||||
if i < typ.genericValues.len() - 1:
|
||||
result &= ", "
|
||||
inc(i)
|
||||
result &= "]"
|
||||
|
@ -731,15 +675,17 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
result &= &"const {self.stringify(typ.value)}"
|
||||
of Function:
|
||||
result &= "fn "
|
||||
if typ.fun.generics.len() > 0:
|
||||
result &= "["
|
||||
for i, gen in typ.fun.generics:
|
||||
result &= &"{gen.name.token.lexeme}: {self.stringify(typ.genericArgs[gen.name.token.lexeme])}"
|
||||
if i < typ.fun.generics.len() - 1:
|
||||
if typ.genericTypes.len() > 0:
|
||||
result &= "<"
|
||||
var i = 0
|
||||
for gen in typ.genericTypes.keys():
|
||||
result &= &"{gen}: {self.stringify(typ.genericTypes[gen])}"
|
||||
if i < typ.genericTypes.len() - 1:
|
||||
result &= ", "
|
||||
inc(i)
|
||||
result &= "]"
|
||||
result &= "("
|
||||
for i, (argName, argType, argDefault) in typ.args:
|
||||
for i, (argName, argType, argDefault) in typ.parameters:
|
||||
result &= &"{argName}: "
|
||||
if argType.kind == Generic:
|
||||
result &= argType.name
|
||||
|
@ -747,25 +693,27 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
result &= self.stringify(argType)
|
||||
if not argDefault.isNil():
|
||||
result &= &" = {argDefault.kind}"
|
||||
if i < typ.args.len() - 1:
|
||||
if i < typ.parameters.len() - 1:
|
||||
result &= ", "
|
||||
result &= ")"
|
||||
if not self.compare(typ.returnType, "nil".toIntrinsic()):
|
||||
result &= &": {self.stringify(typ.returnType)}"
|
||||
if typ.fun.pragmas.len() > 0:
|
||||
if typ.pragmas.len() > 0:
|
||||
result &= " {"
|
||||
for i, pragma in typ.fun.pragmas:
|
||||
result &= &"{pragma.name.token.lexeme}"
|
||||
var i = 0
|
||||
for name, pragma in typ.pragmas:
|
||||
result &= &"{name}"
|
||||
if pragma.args.len() > 0:
|
||||
result &= ": "
|
||||
for j, arg in pragma.args:
|
||||
result &= arg.token.lexeme
|
||||
if j < pragma.args.high():
|
||||
result &= ", "
|
||||
if i < typ.fun.pragmas.high():
|
||||
if i < typ.pragmas.len() - 1:
|
||||
result &= ", "
|
||||
else:
|
||||
result &= "}"
|
||||
inc(i)
|
||||
of TypeKind.Lent:
|
||||
result &= &"lent {self.stringify(typ.value)}"
|
||||
of Union:
|
||||
|
@ -775,13 +723,6 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
if not condition.match:
|
||||
result &= "~"
|
||||
result &= self.stringify(condition.kind)
|
||||
of Generic:
|
||||
for i, condition in typ.cond:
|
||||
if i > 0:
|
||||
result &= " | "
|
||||
if not condition.match:
|
||||
result &= "~"
|
||||
result &= self.stringify(condition.kind)
|
||||
else:
|
||||
discard # TODO(?)
|
||||
|
||||
|
@ -814,32 +755,32 @@ proc endScope(self: TypeChecker) =
|
|||
assert self.scopeDepth == self.names.high()
|
||||
|
||||
|
||||
proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
|
||||
## Like the other check(), but works with two type objects.
|
||||
## The node is passed in to error() in case of a failure
|
||||
result = term
|
||||
if not self.compare(result, expected):
|
||||
self.error(&"expecting expression of type {self.stringify(expected)}, got {self.stringify(result)} instead", node=node)
|
||||
if result.isAny() and not expected.isAny():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
|
||||
|
||||
proc check(self: TypeChecker, term: Expression, expected: Type): TypedExpr {.inline, discardable.} =
|
||||
## Checks the type of the given expression against a known one.
|
||||
## Raises an error if appropriate and returns the typed expression
|
||||
## otherwise
|
||||
result = self.inferOrError(term)
|
||||
if not self.compare(result.kind, expected):
|
||||
self.error(&"expecting value of type {self.stringify(expected)}, got {self.stringify(result)}", term)
|
||||
if not result.kind.isAny() and expected.isAny():
|
||||
self.error(&"expecting expression of type {self.stringify(expected)}, got {self.stringify(result)} instead", term)
|
||||
if result.kind.isAny() and not expected.isAny():
|
||||
self.error("any is not a valid type in this context", term)
|
||||
|
||||
|
||||
proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
|
||||
## Like the other check(), but works with two type objects.
|
||||
## The node is passed in to error() in case of a failure
|
||||
result = term
|
||||
if not self.compare(result, expected):
|
||||
self.error(&"expecting value of type {self.stringify(expected)}, got {self.stringify(result)}", node=node)
|
||||
if not result.isAny() and expected.isAny():
|
||||
self.error("any is not a valid type in this context", node=node)
|
||||
|
||||
|
||||
proc getTypeDistance(self: TypeChecker, a, b: Type): int =
|
||||
## Gets the type distance of two Peon types. Assumes
|
||||
## a and b are already compatible (i.e. compare(a, b)
|
||||
## returns true). For more info, check out self.match()
|
||||
if a.kind != CustomType or b.kind != CustomType:
|
||||
if a.kind != Structure or b.kind != Structure:
|
||||
# TODO: Test
|
||||
return 0
|
||||
var parent = b.parent
|
||||
|
@ -869,9 +810,9 @@ 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.args, sig):
|
||||
for (argA, argB) in zip(typ.parameters, sig):
|
||||
result += self.getTypeDistance(argA.kind, argB.kind)
|
||||
of TypeKind.CustomType:
|
||||
of TypeKind.Structure:
|
||||
discard
|
||||
else:
|
||||
self.error(&"cannot compute type distance for object of type {self.stringify(typ)}")
|
||||
|
@ -883,7 +824,7 @@ proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool
|
|||
## the given type signature
|
||||
case typ.kind:
|
||||
of TypeKind.Function:
|
||||
if typ.args.len() < sig.len():
|
||||
if typ.parameters.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
|
||||
|
@ -900,7 +841,7 @@ proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool
|
|||
return false
|
||||
# We construct a new signature, without the function's default arguments
|
||||
var args: TypeSignature = @[]
|
||||
for argument in typ.args:
|
||||
for argument in typ.parameters:
|
||||
if argument.default.isNil():
|
||||
args.add(argument)
|
||||
else:
|
||||
|
@ -915,7 +856,7 @@ proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool
|
|||
elif argA.name != argB.name:
|
||||
return false
|
||||
return true
|
||||
of TypeKind.CustomType:
|
||||
of TypeKind.Structure:
|
||||
if sig.len() != typ.fields.len():
|
||||
# For now, we require that all fields of an object
|
||||
# be explicitly initialized
|
||||
|
@ -944,7 +885,7 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
|
|||
# Find all matching implementations and record their
|
||||
# type distance relative to our input type signature,
|
||||
# as well as the smallest one found so far
|
||||
for n in filterIt(self.findAll(name), it.valueType.kind in [TypeKind.Function, TypeKind.CustomType]):
|
||||
for n in filterIt(self.findAll(name), it.valueType.kind in [TypeKind.Function, TypeKind.Structure]):
|
||||
if self.checkTypeSignature(n.valueType, sig):
|
||||
impl.add(n)
|
||||
dst = self.calcTypeDistance(n.valueType, sig)
|
||||
|
@ -1010,10 +951,10 @@ 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.args, sig):
|
||||
for (a, b) in zip(result.valueType.parameters, sig):
|
||||
if not a.kind.isAny() and b.kind.isAny():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
of CustomType:
|
||||
of Structure:
|
||||
for (a, b) in zip(result.valueType.fields.values().toSeq(), sig):
|
||||
if not a.isAny() and b.kind.isAny():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
|
@ -1036,16 +977,16 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
|
|||
msg &= ":"
|
||||
for name in names:
|
||||
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, CustomType]:
|
||||
if name.valueType.kind notin [Function, Structure]:
|
||||
msg &= ": not callable"
|
||||
elif sig.len() != name.valueType.args.len():
|
||||
msg &= &": wrong number of arguments (expected {name.valueType.args.len()}, got {sig.len()})"
|
||||
elif sig.len() != name.valueType.parameters.len():
|
||||
msg &= &": wrong number of arguments (expected {name.valueType.parameters.len()}, got {sig.len()} instead)"
|
||||
else:
|
||||
for i, arg in sig:
|
||||
if arg.name != "" and name.valueType.args[i].name != "" and arg.name != name.valueType.args[i].name:
|
||||
if arg.name != "" and name.valueType.parameters[i].name != "" and arg.name != name.valueType.parameters[i].name:
|
||||
msg &= &": unexpected argument '{arg.name}' at position {i + 1}"
|
||||
if not self.compare(arg.kind, name.valueType.args[i].kind):
|
||||
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.args[i].kind)}, got {self.stringify(arg.kind)})"
|
||||
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)"
|
||||
break
|
||||
else:
|
||||
msg &= " (compile with --showMismatches for more details)"
|
||||
|
@ -1064,46 +1005,25 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
|
|||
|
||||
|
||||
proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr]): Type =
|
||||
## Instantiates a generic type
|
||||
if not name.isGeneric:
|
||||
self.error(&"cannot instantiate concrete type from {self.stringify(name.valueType)}: a generic is required")
|
||||
case name.node.kind:
|
||||
of NodeKind.funDecl:
|
||||
var fun = FunDecl(name.node)
|
||||
if fun.generics.len() != args.len():
|
||||
self.error(&"wrong number of types supplied for generic instantiation (expected {fun.generics.len()}, got {args.len()})")
|
||||
var concrete = deepCopy(name.valueType)
|
||||
var map = newTable[string, Type]()
|
||||
for arg in args:
|
||||
if arg.kind.kind != Typevar:
|
||||
self.error(&"expecting type name during generic instantiation, got {self.stringify(arg.kind)}", arg.node)
|
||||
for (gen, value) in zip(fun.generics, args):
|
||||
map[gen.name.token.lexeme] = value.kind
|
||||
for i, argument in concrete.args:
|
||||
if argument.kind.kind != Generic:
|
||||
continue
|
||||
elif argument.name in map:
|
||||
concrete.args[i].kind = map[argument.name]
|
||||
else:
|
||||
self.error(&"unknown generic argument name '{argument.name}'", concrete.fun)
|
||||
if not concrete.returnType.isNil() and concrete.returnType.kind == Generic:
|
||||
if concrete.returnType.name in map:
|
||||
concrete.returnType = map[concrete.returnType.name]
|
||||
else:
|
||||
self.error(&"unknown generic argument name '{concrete.returnType.name}'", concrete.fun)
|
||||
result = concrete
|
||||
## Instantiates a generic type
|
||||
let typ = name.valueType
|
||||
case typ.kind:
|
||||
of TypeKind.Structure:
|
||||
discard
|
||||
else:
|
||||
self.error(&"cannot instantiate concrete type for object of type {self.stringify(name.valueType)}")
|
||||
|
||||
|
||||
proc unpackTypes(self: TypeChecker, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, isGeneric: bool = false) =
|
||||
proc unpackTypes(self: TypeChecker, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, expectTypes: bool = false) =
|
||||
## Recursively unpacks a type constraint
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
of identExpr, genericExpr:
|
||||
var typ = self.inferOrError(condition).kind
|
||||
if not isGeneric and not self.compare(typ, "typevar".toIntrinsic()):
|
||||
self.error(&"expecting a type name, got value of type {self.stringify(typ)}", condition)
|
||||
typ = typ.unwrapType()
|
||||
if expectTypes:
|
||||
if not typ.isType():
|
||||
self.error(&"expecting a type, got a value of type {self.stringify(typ)} instead", condition)
|
||||
typ = typ.unwrapType()
|
||||
|
||||
if self.compare(typ, "auto".toIntrinsic()):
|
||||
self.error("automatic types cannot be used within type constraints", condition)
|
||||
list.add((accept, typ, condition))
|
||||
|
@ -1170,10 +1090,10 @@ proc addName(self: TypeChecker, name: Name) =
|
|||
var scope = self.names[self.scopeDepth]
|
||||
if scope.hasKey(name.ident.token.lexeme):
|
||||
for obj in scope[name.ident.token.lexeme]:
|
||||
if name.kind == NameKind.Function:
|
||||
if name.valueType.kind == TypeKind.Function:
|
||||
# We don't check for name clashes for functions because self.match() does that
|
||||
continue
|
||||
if obj.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum, NameKind.Function] and name.owner == obj.owner:
|
||||
if (obj.kind in [NameKind.Var, NameKind.Module] or obj.valueType.kind in [TypeKind.Structure, TypeKind.EnumEntry]) and name.owner == obj.owner:
|
||||
self.error(&"re-declaration of '{obj.ident.token.lexeme}' is not allowed (previously declared in {obj.owner.ident.token.lexeme}:{obj.ident.token.line}:{obj.ident.token.relPos.start})", name.node)
|
||||
else:
|
||||
scope[name.ident.token.lexeme] = @[]
|
||||
|
@ -1195,70 +1115,30 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
|
|||
isPrivate: node.isPrivate,
|
||||
owner: self.currentModule,
|
||||
file: self.file,
|
||||
valueType: "nil".toIntrinsic(), # Done later in varDecl (for better semantics)
|
||||
valueType: nil, # Done later in varDecl (for better semantics)
|
||||
line: node.token.line,
|
||||
belongsTo: self.currentFunction,
|
||||
kind: NameKind.Var,
|
||||
node: node,
|
||||
isReal: true
|
||||
)
|
||||
self.addName(name)
|
||||
of NodeKind.funDecl:
|
||||
var node = FunDecl(node)
|
||||
declaredName = node.name.token.lexeme
|
||||
name = Name(depth: self.scopeDepth,
|
||||
isPrivate: node.isPrivate,
|
||||
owner: self.currentModule,
|
||||
file: self.file,
|
||||
valueType: Type(kind: Function,
|
||||
returnType: "nil".toIntrinsic(), # We check it later
|
||||
args: @[],
|
||||
fun: node,
|
||||
forwarded: node.body.isNil(),
|
||||
isAuto: false,
|
||||
genericArgs: newTable[string, Type]()),
|
||||
ident: node.name,
|
||||
node: node,
|
||||
line: node.token.line,
|
||||
kind: NameKind.Function,
|
||||
belongsTo: self.currentFunction,
|
||||
isReal: true,
|
||||
)
|
||||
if node.generics.len() > 0:
|
||||
name.isGeneric = true
|
||||
self.addName(name)
|
||||
discard
|
||||
of NodeKind.importStmt:
|
||||
var node = ImportStmt(node)
|
||||
# We change the name of the module internally so that
|
||||
# if you import /path/to/mod, then doing mod.f() will
|
||||
# still work without any extra work on our end. Note how
|
||||
# we don't change the metadata about the identifier's
|
||||
# position so that error messages still highlight the
|
||||
# full path
|
||||
let path = node.moduleName.token.lexeme
|
||||
node.moduleName.token.lexeme = node.moduleName.token.lexeme.extractFilename()
|
||||
self.addName(Name(depth: self.scopeDepth,
|
||||
owner: self.currentModule,
|
||||
file: "", # The file of the module isn't known until it's compiled!
|
||||
path: path,
|
||||
ident: node.moduleName,
|
||||
line: node.moduleName.token.line,
|
||||
kind: NameKind.Module,
|
||||
isPrivate: false,
|
||||
isReal: true
|
||||
))
|
||||
name = scope[node.moduleName.token.lexeme][^1]
|
||||
declaredName = name.ident.token.lexeme
|
||||
discard
|
||||
of NodeKind.typeDecl:
|
||||
var node = TypeDecl(node)
|
||||
declaredName = node.name.token.lexeme
|
||||
var kind: Type = Type(kind: CustomType, typeName: declaredName, generics: newTable[string, Type](), fields: newTable[string, Type](),
|
||||
interfaces: @[], isEnum: node.isEnum)
|
||||
var kind: Type = Type(kind: Structure,
|
||||
name: declaredName,
|
||||
genericTypes: newTable[string, Type](),
|
||||
genericValues: newTable[string, Type](),
|
||||
fields: newTable[string, Type](),
|
||||
interfaces: @[],
|
||||
isEnum: node.isEnum)
|
||||
if node.isRef:
|
||||
kind = kind.toRef()
|
||||
kind = kind.wrapType()
|
||||
self.addName(Name(kind: NameKind.CustomType,
|
||||
depth: self.scopeDepth,
|
||||
self.addName(Name(depth: self.scopeDepth,
|
||||
owner: self.currentModule,
|
||||
node: node,
|
||||
ident: node.name,
|
||||
|
@ -1266,7 +1146,6 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
|
|||
isPrivate: node.isPrivate,
|
||||
belongsTo: self.currentFunction,
|
||||
valueType: kind,
|
||||
isReal: true,
|
||||
))
|
||||
name = scope[declaredName][^1]
|
||||
else:
|
||||
|
@ -1277,41 +1156,37 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
|
|||
|
||||
|
||||
proc identifier(self: TypeChecker, node: IdentExpr): TypedExpr =
|
||||
## Compiles name resolution
|
||||
## Typechecks name resolution
|
||||
return newTypedIdentExpr(node, self.findOrError(node.name.lexeme, node=node))
|
||||
|
||||
|
||||
proc unary(self: TypeChecker, node: UnaryExpr): TypedUnaryExpr =
|
||||
## Compiles unary expressions
|
||||
## Typechecks unary expressions
|
||||
var
|
||||
default: TypedExpr
|
||||
typeOfA = self.infer(node.a)
|
||||
let fn = Type(kind: Function, returnType: Type(kind: Any), args: @[("", typeOfA.kind, default)])
|
||||
var name = self.match(node.token.lexeme, fn.args, node)
|
||||
var impl = name.valueType
|
||||
if name.isGeneric:
|
||||
impl = self.specialize(name, @[self.expression(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.specialize(name, @[self.expression(node.a)])
|
||||
result = newTypedUnaryExpr(node, impl.returnType, typeOfA)
|
||||
|
||||
|
||||
proc binary(self: TypeChecker, node: BinaryExpr): TypedBinaryExpr =
|
||||
## Compiles unary expressions
|
||||
## Typechecks binary expressions
|
||||
var
|
||||
default: TypedExpr
|
||||
typeOfA = self.infer(node.a)
|
||||
typeOfB = self.infer(node.b)
|
||||
let fn = Type(kind: Function,
|
||||
returnType: Type(kind: Any),
|
||||
args: @[("", typeOfA.kind, default), ("", typeOfB.kind, default)])
|
||||
var name = self.match(node.token.lexeme, fn.args, node)
|
||||
var impl = name.valueType
|
||||
if name.isGeneric:
|
||||
impl = self.specialize(name, @[self.expression(node.a)])
|
||||
parameters: @[("", typeOfA.kind, default), ("", typeOfB.kind, default)])
|
||||
let name = self.match(node.token.lexeme, fn.parameters, node)
|
||||
let impl = self.specialize(name, @[self.expression(node.a)])
|
||||
result = newTypedBinaryExpr(node, impl.returnType, typeOfA, typeOfB)
|
||||
|
||||
|
||||
proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
|
||||
## Compiles generic instantiation
|
||||
## Typechecks generic instantiation
|
||||
var args: seq[TypedExpr] = @[]
|
||||
for arg in node.args:
|
||||
args.add(self.expression(arg))
|
||||
|
@ -1319,7 +1194,7 @@ proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
|
|||
|
||||
|
||||
proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
||||
## Compiles call expressions. This includes
|
||||
## Typechecks call expressions. This includes
|
||||
## things like object and enum construction
|
||||
var args: TypeSignature = @[]
|
||||
var argExpr: seq[TypedExpr] = @[]
|
||||
|
@ -1338,11 +1213,9 @@ proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
|||
# Calls like hi()
|
||||
var impl = self.match(IdentExpr(node.callee).name.lexeme, args, node)
|
||||
self.dispatchDelayedPragmas(impl)
|
||||
var typ = impl.valueType
|
||||
if impl.isGeneric:
|
||||
typ = self.specialize(impl, argExpr)
|
||||
var typ = self.specialize(impl, argExpr)
|
||||
case typ.kind:
|
||||
of CustomType:
|
||||
of Structure:
|
||||
# TODO
|
||||
result = newTypedExpr(node, typ)
|
||||
of Function:
|
||||
|
@ -1451,7 +1324,7 @@ method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true):
|
|||
self.error(&"cannot assign to '{name.token.lexeme}' (value is a constant)", name)
|
||||
elif r.mutable:
|
||||
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
||||
if r.valueType.kind != CustomType:
|
||||
if r.valueType.kind != Structure:
|
||||
self.error("only types have fields", node)
|
||||
else:
|
||||
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
||||
|
@ -1500,27 +1373,27 @@ proc blockStmt(self: TypeChecker, node: BlockStmt): TypedBlockStmt =
|
|||
|
||||
|
||||
proc ifStmt(self: TypeChecker, node: IfStmt): TypedNode =
|
||||
## Typechecks if/else statements for conditional
|
||||
## execution of code
|
||||
## Typechecks if/else statements
|
||||
|
||||
# Check that the condition is a boolean and record it
|
||||
# Check the condition
|
||||
let condition = self.check(node.condition, "bool".toIntrinsic())
|
||||
# Compile the "then" part of "if-then-else"
|
||||
# Check the "then" part of "if-then-else"
|
||||
let then = TypedBlockStmt(self.validate(node.thenBranch))
|
||||
# Compile the "else" part
|
||||
# Check the "else" part
|
||||
let otherwise = TypedBlockStmt(self.validate(node.elseBranch))
|
||||
# Note: Peon enforces the body of loops and conditionals to
|
||||
# always be a block statement (for a variety of very good reasons),
|
||||
# so the conversion here is safe
|
||||
# always be a block statement (for a variety of very good reasons,
|
||||
# as it avoids mistakes like the infamous GOTO fail), so the
|
||||
# conversion here is safe
|
||||
return newTypedIfStmt(node, then, otherwise, condition)
|
||||
|
||||
|
||||
proc whileStmt(self: TypeChecker, node: WhileStmt): TypedNode =
|
||||
## Typechecks C-style while loops
|
||||
|
||||
# Compile and check the condition
|
||||
|
||||
# Check the condition
|
||||
let condition = self.check(node.condition, "bool".toIntrinsic())
|
||||
# Compile the body
|
||||
# Check the body
|
||||
return newTypedWhileStmt(node, TypedBlockStmt(self.validate(node.body)), condition)
|
||||
|
||||
|
||||
|
@ -1539,19 +1412,19 @@ proc varDecl(self: TypeChecker, node: VarDecl): TypedVarDecl =
|
|||
if node.constant and not node.value.isConst():
|
||||
self.error("constant initializer is not a constant", node.value)
|
||||
init = TypedExpr(self.validate(node.value))
|
||||
typ = init.kind.wrapType()
|
||||
typ = init.kind
|
||||
if not node.valueType.isNil():
|
||||
# Explicit type declaration always takes over
|
||||
typ = self.inferOrError(node.valueType).kind
|
||||
|
||||
# Check that the inferred expression represents a type
|
||||
# and not a value. This is to guard against things
|
||||
# like "var x: 1 = 1;". We unwrap it immediately
|
||||
# because we don't want to assign a typevar to the
|
||||
# valueType field of the variable-- it would just
|
||||
# be redundant
|
||||
typ = self.check(typ, "typevar".toIntrinsic(), node.valueType)
|
||||
if typ.isNil():
|
||||
self.error("expecting either a type declaration or an initializer value, but neither was found", node)
|
||||
# Check that the inferred expression represents a type
|
||||
# and not a value. This is to guard against things
|
||||
# like "var x: 1 = 1;". We unwrap it immediately
|
||||
# because we don't want to assign a typevar to the
|
||||
# valueType field of the variable-- it would just
|
||||
# be redundant
|
||||
typ = self.check(typ, "typevar".toIntrinsic(), node.valueType).unwrapType()
|
||||
# Now check that the type of the initializer, if it exists,
|
||||
# matches the type of the variable
|
||||
if not init.isNil():
|
||||
|
@ -1586,11 +1459,7 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|||
name.valueType.isAuto = true
|
||||
if node.body.isNil():
|
||||
# Forward declaration
|
||||
name.valueType.forwarded = true
|
||||
self.endScope()
|
||||
return
|
||||
if name.valueType.intrinsic:
|
||||
# Builtins are handled at call time
|
||||
# TODO
|
||||
self.endScope()
|
||||
return
|
||||
# We store the current function to restore
|
||||
|
@ -1610,27 +1479,17 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|||
proc declareGenerics(self: TypeChecker, name: Name) =
|
||||
## Helper to declare the generic arguments of the
|
||||
## given name, if it has any
|
||||
if name.valueType.kind != TypeKind.Structure:
|
||||
return
|
||||
var
|
||||
constraints: seq[tuple[match: bool, kind: Type, value: Expression]] = @[]
|
||||
value: Expression
|
||||
for gen in name.node.generics:
|
||||
for gen in name.node.genericTypes:
|
||||
if gen.cond.isNil():
|
||||
constraints = @[(match: true, kind: "any".toIntrinsic(), value: value)]
|
||||
else:
|
||||
self.unpackTypes(gen.cond, constraints)
|
||||
self.addName(Name(depth: self.scopeDepth,
|
||||
isPrivate: true,
|
||||
valueType: Type(kind: Generic, name: gen.name.token.lexeme, cond: constraints),
|
||||
line: name.node.token.line,
|
||||
belongsTo: name,
|
||||
ident: gen.name,
|
||||
owner: self.currentModule,
|
||||
file: self.file,
|
||||
isReal: true))
|
||||
if name.valueType.wrapped.generics.hasKey(gen.name.token.lexeme):
|
||||
self.error(&"cannot re-declare generic argument '{gen.name.token.lexeme}'", node=gen.name)
|
||||
name.valueType.wrapped.generics[gen.name.token.lexeme] = self.names[self.scopeDepth][gen.name.token.lexeme][^1].valueType
|
||||
constraints = @[]
|
||||
self.unpackTypes(gen.cond, constraints, expectTypes=true)
|
||||
# TODO
|
||||
|
||||
|
||||
proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDecl =
|
||||
|
@ -1651,7 +1510,7 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|||
for field in node.fields:
|
||||
fieldType = self.infer(field.valueType)
|
||||
if not node.isRef:
|
||||
# Check for self-recursion of non-ref (aka managed pointer) types (which would require
|
||||
# Check for self-recursion of non-ref types (which would require
|
||||
# infinite memory)
|
||||
n = fieldType.getName()
|
||||
if n.isNil():
|
||||
|
@ -1660,6 +1519,7 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|||
if name == n:
|
||||
self.error(&"illegal self-recursion in member '{field.name.token.lexeme}' for non-ref type '{name.ident.token.lexeme}'", fieldType.node)
|
||||
result.fields[field.name.token.lexeme] = fieldType
|
||||
name.valueType.fields[field.name.token.lexeme] = fieldType.kind
|
||||
else:
|
||||
# Type is a variant type (aka enum). We'll only declare a single
|
||||
# object (the enum type itself) so that we don't pollute the
|
||||
|
@ -1706,7 +1566,6 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|||
self.endScope()
|
||||
|
||||
|
||||
|
||||
proc pragmaExpr(self: TypeChecker, pragma: Pragma) =
|
||||
## Validates pragma expressions (not bound to a name)
|
||||
if pragma.name.token.lexeme notin self.pragmas:
|
||||
|
@ -1714,7 +1573,6 @@ proc pragmaExpr(self: TypeChecker, pragma: Pragma) =
|
|||
self.pragmas[pragma.name.token.lexeme].handler(self, pragma, nil)
|
||||
|
||||
|
||||
|
||||
proc validate(self: TypeChecker, node: ASTNode): TypedNode =
|
||||
## Dispatches typeless AST nodes to typecheck them and turn
|
||||
## them into typed ones
|
||||
|
@ -1725,8 +1583,7 @@ proc validate(self: TypeChecker, node: ASTNode): TypedNode =
|
|||
result = self.expression(Expression(node))
|
||||
of exprStmt:
|
||||
let statement = ExprStmt(node)
|
||||
result = self.expression(statement.expression)
|
||||
result = TypedExprStmt(node: statement, expression: TypedExpr(result))
|
||||
result = TypedExprStmt(node: statement, expression: TypedExpr(self.validate(statement.expression)))
|
||||
of NodeKind.whileStmt:
|
||||
result = self.whileStmt(WhileStmt(node))
|
||||
of NodeKind.blockStmt:
|
||||
|
@ -1781,10 +1638,9 @@ proc validate*(self: TypeChecker, tree: ParseTree, file, source: string, showMis
|
|||
file: self.file,
|
||||
valueType: Type(kind: Function,
|
||||
returnType: "nil".toIntrinsic(),
|
||||
args: @[],
|
||||
parameters: @[],
|
||||
),
|
||||
ident: newIdentExpr(Token(lexeme: "", kind: Identifier)),
|
||||
kind: NameKind.Function,
|
||||
line: 1)
|
||||
self.addName(main)
|
||||
while not self.done():
|
||||
|
|
|
@ -35,7 +35,7 @@ type
|
|||
Auto,
|
||||
Byte,
|
||||
Char,
|
||||
CustomType,
|
||||
Structure,
|
||||
EnumEntry,
|
||||
Reference,
|
||||
Pointer,
|
||||
|
@ -48,12 +48,17 @@ type
|
|||
Type* = ref object
|
||||
## A compile-time type
|
||||
|
||||
# Is this type a compiler intrinsic?
|
||||
intrinsic*: bool
|
||||
# Is this a type constant?
|
||||
constant*: bool
|
||||
# Can this value be mutated?
|
||||
# Can it be mutated?
|
||||
mutable*: bool
|
||||
# Is it a compiler intrinsic?
|
||||
intrinsic*: bool
|
||||
# Mapping of generic names to types
|
||||
genericTypes*: TableRef[string, Type]
|
||||
genericValues*: TableRef[string, Type]
|
||||
# Type pragmas
|
||||
pragmas*: TableRef[string, Pragma]
|
||||
case kind*: TypeKind
|
||||
of Integer:
|
||||
signed*: bool
|
||||
|
@ -67,31 +72,19 @@ type
|
|||
isGenerator*: bool
|
||||
isCoroutine*: bool
|
||||
isAuto*: bool
|
||||
args*: TypeSignature
|
||||
parameters*: TypeSignature
|
||||
returnType*: Type
|
||||
builtinOp*: string
|
||||
fun*: Declaration
|
||||
forwarded*: bool
|
||||
genericArgs*: TableRef[string, Type]
|
||||
safe*: bool
|
||||
of Typevar:
|
||||
wrapped*: Type
|
||||
of CustomType:
|
||||
typeName*: string
|
||||
generics*: TableRef[string, Type]
|
||||
of Structure:
|
||||
name*: string
|
||||
fields*: TableRef[string, Type]
|
||||
parent*: Type
|
||||
interfaces*: seq[Type]
|
||||
isEnum*: bool
|
||||
of Reference, Pointer, Lent, Const:
|
||||
value*: Type
|
||||
of Generic:
|
||||
# cond represents a type constraint. For
|
||||
# example, fn foo[T*: int & ~uint](...) {...}
|
||||
# would map to [(true, int), (false, uint)]
|
||||
cond*: seq[tuple[match: bool, kind: Type, value: Expression]]
|
||||
asUnion*: bool # If this is true, the constraint is treated like a type union
|
||||
name*: string
|
||||
of Union:
|
||||
types*: seq[tuple[match: bool, kind: Type, value: Expression]]
|
||||
else:
|
||||
|
@ -103,7 +96,7 @@ type
|
|||
|
||||
NameKind* {.pure.} = enum
|
||||
## A name enumeration type
|
||||
None, Module, Argument, Var, Function, CustomType, Enum
|
||||
Default, Var, Module
|
||||
|
||||
Name* = ref object
|
||||
## A generic name object
|
||||
|
@ -138,8 +131,6 @@ type
|
|||
depth*: int
|
||||
# Is this name private?
|
||||
isPrivate*: bool
|
||||
# Is this name a generic type?
|
||||
isGeneric*: bool
|
||||
# The type of the name's associated
|
||||
# value
|
||||
valueType*: Type
|
||||
|
@ -155,8 +146,6 @@ type
|
|||
# Who is this name exported to? (Only makes sense if isPrivate
|
||||
# equals false)
|
||||
exportedTo*: seq[Name]
|
||||
# Is this name generated by user code or internally by the type checker?
|
||||
isReal*: bool
|
||||
|
||||
TypeSignature* = seq[tuple[name: string, kind: Type, default: TypedExpr]]
|
||||
|
||||
|
|
|
@ -108,7 +108,9 @@ type
|
|||
## A declaration
|
||||
isPrivate*: bool
|
||||
pragmas*: seq[Pragma]
|
||||
generics*: seq[tuple[name: IdentExpr, cond: Expression]]
|
||||
genericTypes*: seq[tuple[name: IdentExpr, cond: Expression]]
|
||||
genericValues*: seq[tuple[name: IdentExpr, cond: Expression]]
|
||||
|
||||
|
||||
Statement* = ref object of Declaration
|
||||
## A statement
|
||||
|
@ -176,7 +178,6 @@ type
|
|||
defaults*: seq[Expression]
|
||||
isGenerator*: bool
|
||||
isAsync*: bool
|
||||
isPure*: bool
|
||||
returnType*: Expression
|
||||
depth*: int
|
||||
|
||||
|
@ -257,9 +258,7 @@ type
|
|||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
isPure*: bool
|
||||
returnType*: Expression
|
||||
depth*: int
|
||||
|
||||
TypeDecl* = ref object of Declaration
|
||||
name*: IdentExpr
|
||||
|
@ -434,7 +433,8 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
|
|||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression],
|
||||
body: Statement, isAsync, isGenerator: bool,
|
||||
token: Token, depth: int, pragmas: seq[Pragma] = @[],
|
||||
returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[]): LambdaExpr =
|
||||
returnType: Expression, genericTypes: seq[tuple[name: IdentExpr, cond: Expression]] = @[],
|
||||
genericValues: seq[tuple[name: IdentExpr, cond: Expression]] = @[]): LambdaExpr =
|
||||
result = LambdaExpr(kind: lambdaExpr)
|
||||
result.body = body
|
||||
result.arguments = arguments
|
||||
|
@ -443,9 +443,9 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]
|
|||
result.isAsync = isAsync
|
||||
result.token = token
|
||||
result.returnType = returnType
|
||||
result.isPure = false
|
||||
result.pragmas = pragmas
|
||||
result.generics = generics
|
||||
result.genericTypes = genericTypes
|
||||
result.genericValues = genericValues
|
||||
result.depth = depth
|
||||
|
||||
|
||||
|
@ -640,10 +640,9 @@ proc newVarDecl*(name: IdentExpr, valueType, value: Expression, token: Token,
|
|||
|
||||
|
||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression],
|
||||
body: Statement, isAsync, isGenerator: bool,
|
||||
isPrivate: bool, token: Token, depth: int,
|
||||
pragmas: seq[Pragma] = @[], returnType: Expression,
|
||||
generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[]): FunDecl =
|
||||
body: Statement, isAsync, isGenerator: bool, isPrivate: bool, token: Token, pragmas: seq[Pragma] = @[],
|
||||
returnType: Expression, genericTypes: seq[tuple[name: IdentExpr, cond: Expression]] = @[],
|
||||
genericValues: seq[tuple[name: IdentExpr, cond: Expression]] = @[]): FunDecl =
|
||||
result = FunDecl(kind: funDecl)
|
||||
result.name = name
|
||||
result.arguments = arguments
|
||||
|
@ -655,21 +654,23 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
|
|||
result.token = token
|
||||
result.pragmas = pragmas
|
||||
result.returnType = returnType
|
||||
result.isPure = false
|
||||
result.generics = generics
|
||||
result.depth = depth
|
||||
result.genericTypes = genericTypes
|
||||
result.genericValues = genericValues
|
||||
|
||||
|
||||
proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool, default: Expression]],
|
||||
defaults: seq[Expression], isPrivate: bool, token: Token, pragmas: seq[Pragma],
|
||||
generics: seq[tuple[name: IdentExpr, cond: Expression]], parent: IdentExpr, isEnum: bool, isRef: bool): TypeDecl =
|
||||
defaults: seq[Expression], isPrivate: bool, token: Token, pragmas: seq[Pragma],
|
||||
genericTypes: seq[tuple[name: IdentExpr, cond: Expression]],
|
||||
genericValues: seq[tuple[name: IdentExpr, cond: Expression]],
|
||||
parent: IdentExpr, isEnum: bool, isRef: bool): TypeDecl =
|
||||
result = TypeDecl(kind: typeDecl)
|
||||
result.name = name
|
||||
result.fields = fields
|
||||
result.isPrivate = isPrivate
|
||||
result.token = token
|
||||
result.pragmas = pragmas
|
||||
result.generics = generics
|
||||
result.genericTypes = genericTypes
|
||||
result.genericValues = genericValues
|
||||
result.parent = parent
|
||||
result.isEnum = isEnum
|
||||
result.isRef = isRef
|
||||
|
@ -766,10 +767,10 @@ proc `$`*(self: ASTNode): string =
|
|||
result &= &"Var(name={self.name}, type={self.valueType}, value={self.value}, mutable={self.mutable}, constant={self.constant}, private={self.isPrivate}, pragmas={self.pragmas})"
|
||||
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(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})"""
|
||||
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], genericTypes={self.genericTypes}, genericValues={self.genericValues}, async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})"""
|
||||
of typeDecl:
|
||||
var self = TypeDecl(self)
|
||||
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, members={self.members}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, parent={self.parent}, ref={self.isRef}, enum={self.isEnum}, value={self.value})"""
|
||||
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, members={self.members}, private={self.isPrivate}, pragmas={self.pragmas}, genericTypes={self.genericTypes}, genericValues={self.genericValues}, parent={self.parent}, ref={self.isRef}, enum={self.isEnum}, value={self.value})"""
|
||||
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}, pragmas={self.pragmas})"""
|
||||
|
|
|
@ -451,8 +451,7 @@ proc parseGenericArgs(self: Parser): Expression =
|
|||
var item = newIdentExpr(self.peek(-2), self.scopeDepth)
|
||||
var types: seq[Expression] = @[]
|
||||
while not self.check(RightBracket) and not self.done():
|
||||
self.expect(Identifier)
|
||||
types.add(newIdentExpr(self.peek(-1), self.scopeDepth))
|
||||
types.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBracket)
|
||||
|
@ -890,10 +889,11 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
|||
names.add(self.peek(-1).lexeme)
|
||||
name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
name.file = self.file
|
||||
if not self.match(":"):
|
||||
if self.match("]"):
|
||||
result.add(newPragma(name, @[]))
|
||||
break
|
||||
if self.match("]"):
|
||||
result.add(newPragma(name, @[]))
|
||||
break
|
||||
# Pragma takes more than one argument, so they need
|
||||
# to be parenthesized to avoid ambiguity
|
||||
elif self.match("("):
|
||||
while not self.match(")") and not self.done():
|
||||
exp = self.primary()
|
||||
|
@ -903,7 +903,7 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
|||
if not self.match(","):
|
||||
break
|
||||
self.expect(LeftParen, "unterminated parenthesis in pragma arguments")
|
||||
else:
|
||||
elif self.match(":"):
|
||||
exp = self.primary()
|
||||
if not exp.isConst():
|
||||
self.error("pragma arguments can only be literals", exp.token)
|
||||
|
@ -1004,14 +1004,14 @@ proc parseFunExpr(self: Parser): LambdaExpr =
|
|||
result.file = self.file
|
||||
|
||||
|
||||
proc parseGenericConstraint(self: Parser): Expression =
|
||||
proc parseGenericConstraint(self: Parser, endToken: TokenType or string): Expression =
|
||||
## Recursively parses a generic constraint
|
||||
## and returns it as an expression
|
||||
result = self.expression()
|
||||
if not self.check(RightBracket):
|
||||
if not self.check(endToken):
|
||||
case self.peek().lexeme:
|
||||
of "|":
|
||||
result = newBinaryExpr(result, self.step(), self.parseGenericConstraint())
|
||||
result = newBinaryExpr(result, self.step(), self.parseGenericConstraint(endToken))
|
||||
result.file = self.file
|
||||
of "~":
|
||||
result = newUnaryExpr(self.step(), result)
|
||||
|
@ -1025,18 +1025,31 @@ proc parseGenericConstraint(self: Parser): 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():
|
||||
while not self.check(">") and not self.done():
|
||||
self.expect(Identifier, "expecting generic type name")
|
||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
gen.name.file = self.file
|
||||
if self.match(":"):
|
||||
gen.cond = self.parseGenericConstraint()
|
||||
gen.cond = self.parseGenericConstraint(">")
|
||||
else:
|
||||
gen.cond = nil
|
||||
decl.generics.add(gen)
|
||||
decl.genericTypes.add(gen)
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBracket, "unterminated generic declaration")
|
||||
self.expect(">", "unterminated generic declaration")
|
||||
if self.match(LeftBracket):
|
||||
while not self.check(RightBracket) and not self.done():
|
||||
self.expect(Identifier, "expecting generic type name")
|
||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
gen.name.file = self.file
|
||||
if self.match(":"):
|
||||
gen.cond = self.parseGenericConstraint(RightBracket)
|
||||
else:
|
||||
gen.cond = nil
|
||||
decl.genericValues.add(gen)
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBracket, "unterminated generic declaration")
|
||||
|
||||
|
||||
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
||||
|
@ -1061,12 +1074,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|||
isGenerator=isGenerator,
|
||||
isPrivate=true,
|
||||
token=tok,
|
||||
returnType=nil,
|
||||
depth=self.scopeDepth)
|
||||
returnType=nil)
|
||||
if self.match("*"):
|
||||
FunDecl(self.currentFunction).isPrivate = false
|
||||
self.checkDecl(FunDecl(self.currentFunction).isPrivate)
|
||||
if self.match(LeftBracket):
|
||||
if self.match("<"):
|
||||
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
|
||||
|
@ -1262,12 +1274,12 @@ proc typeDecl(self: Parser): TypeDecl =
|
|||
let token = self.peek(-1)
|
||||
self.expect(Identifier, "expecting type name after 'type'")
|
||||
var name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
let isPrivate = not self.match("*")
|
||||
self.checkDecl(isPrivate)
|
||||
result = newTypeDecl(name, @[], @[], isPrivate, token, @[], @[], nil, false, false)
|
||||
result = newTypeDecl(name, @[], @[], true, token, @[], @[], @[], nil, false, false)
|
||||
result.file = self.file
|
||||
if self.match(LeftBracket):
|
||||
if self.match("<"):
|
||||
self.parseGenerics(result)
|
||||
result.isPrivate = not self.match("*")
|
||||
self.checkDecl(result.isPrivate)
|
||||
self.expect("=", "expecting '=' after type name")
|
||||
var hasNone = false
|
||||
case self.peek().kind:
|
||||
|
@ -1305,7 +1317,7 @@ proc typeDecl(self: Parser): TypeDecl =
|
|||
else:
|
||||
var variant: TypeDecl
|
||||
while not self.done():
|
||||
variant = newTypeDecl(nil, @[], @[], true, nil, @[], @[], nil, false, false)
|
||||
variant = newTypeDecl(nil, @[], @[], true, nil, @[], @[], @[], nil, false, false)
|
||||
self.expect(Identifier, "expecting variant name")
|
||||
variant.name = newIdentExpr(self.peek(-1))
|
||||
variant.token = variant.name.token
|
||||
|
|
|
@ -86,6 +86,8 @@ proc runFile(filename: string, fromString: bool = false, dump: bool = true, brea
|
|||
if fromString:
|
||||
input = filename
|
||||
filename = "<string>"
|
||||
else:
|
||||
input = readFile(filename)
|
||||
try:
|
||||
if not isBinary:
|
||||
tokens = tokenizer.lex(input, filename)
|
||||
|
@ -182,6 +184,11 @@ proc runFile(filename: string, fromString: bool = false, dump: bool = true, brea
|
|||
print(exc)
|
||||
except TypeCheckError as exc:
|
||||
print(exc)
|
||||
except CodeGenError as exc:
|
||||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
file = relativePath(file, getCurrentDir())
|
||||
stderr.styledWriteLine(fgRed, styleBright, "Error while generating code for ", fgYellow, file, fgDefault, &": {exc.msg}")
|
||||
except SerializationError as exc:
|
||||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
|
|
Loading…
Reference in New Issue