Initial ground work on generics, some stuff is broken :(

This commit is contained in:
Mattia Giambirtone 2022-10-13 16:52:37 +02:00
parent d4d1034cef
commit 8667cbdceb
3 changed files with 155 additions and 484 deletions

View File

@ -413,6 +413,7 @@ proc patchJump(self: Compiler, offset: int) =
if jump < 0:
self.error("invalid jump size (< 0), did the bytecode size change without fixJumps being called?")
if jump > 16777215:
# TODO: Emit consecutive jumps?
self.error("cannot jump more than 16777215 instructions")
self.setJump(self.jumps[offset].offset, (jump - 4).toTriple())
self.jumps[offset].patched = true
@ -493,8 +494,43 @@ proc resolve(self: Compiler, name: IdentExpr,
self.varDecl(VarDecl(obj.node), obj)
of NameKind.Type:
self.typeDecl(TypeDecl(obj.node), obj)
of NameKind.Function:
if not obj.valueType.isGeneric:
self.funDecl(FunDecl(obj.node), obj)
# Generic functions need to be compiled at
# the call site, but regular functions can
# be precompiled as soon as we resolve them
else:
discard # Functions need to be compiled at the call site
discard
return obj
return nil
proc resolve(self: Compiler, name: string,
depth: int = self.scopeDepth): Name =
## Version of resolve that takes strings instead
## of AST nodes
for obj in reversed(self.names):
if obj.name.token.lexeme == name:
if obj.isPrivate and obj.owner != self.currentModule:
continue # There may be a name in the current module that
# matches, so we skip this
if not obj.resolved:
obj.resolved = true
obj.codePos = self.chunk.code.len()
case obj.kind:
of NameKind.Var:
self.varDecl(VarDecl(obj.node), obj)
of NameKind.Type:
self.typeDecl(TypeDecl(obj.node), obj)
of NameKind.Function:
if not obj.valueType.isGeneric:
self.funDecl(FunDecl(obj.node), obj)
# Generic functions need to be compiled at
# the call site, but regular functions can
# be precompiled as soon as we resolve them
else:
discard
return obj
return nil
@ -509,6 +545,8 @@ proc getStackPos(self: Compiler, name: Name): int =
continue
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
continue
elif not variable.valueType.isNil() and variable.valueType.kind == Generic:
continue
if name == variable:
found = true
break
@ -722,11 +760,18 @@ proc infer(self: Compiler, node: Expression): Type =
case node.kind:
of identExpr:
let node = IdentExpr(node)
let name = self.resolve(node)
var name = self.resolve(node)
if not name.isNil():
result = name.valueType
if result.kind == Generic:
result = self.resolve(newIdentExpr(Token(lexeme: result.name, kind: Identifier))).valueType
if not result.isNil() and result.kind == Generic:
if name.belongsTo.isNil():
name = self.resolve(result.name)
if not name.isNil():
return name.valueType
else:
for arg in name.belongsTo.valueType.args:
if node.token.lexeme == arg.name:
return arg.kind
else:
result = node.name.lexeme.toIntrinsic()
of unaryExpr:
@ -886,7 +931,7 @@ proc check(self: Compiler, term: Expression, kind: Type, allowAny: bool = false)
## otherwise
let k = self.infer(term)
if k.isNil():
if term.kind == identExpr:
if term.kind == identExpr and self.resolve(IdentExpr(term)).isNil():
self.error(&"reference to undeclared name '{term.token.lexeme}'", term)
elif term.kind == callExpr and CallExpr(term).callee.kind == identExpr:
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'", term)
@ -1011,7 +1056,10 @@ proc endScope(self: Compiler) =
for name in self.names:
if name.depth > self.scopeDepth:
names.add(name)
if name.kind in [NameKind.Var, NameKind.Argument]:
if not name.resolved:
# TODO: Emit a warning?
continue
elif name.kind in [NameKind.Var, NameKind.Argument]:
# We don't increase the pop count for some kinds of objects
# because they're not stored the same way as regular variables
# (for types, generics and function declarations)
@ -1211,6 +1259,7 @@ proc emitLoop(self: Compiler, begin: int, line: int) =
## jump offset
let offset = self.chunk.code.high() - begin + 4
if offset > 16777215:
# TODO: Emit consecutive jumps?
self.error("cannot jump more than 16777215 bytecode instructions")
self.emitByte(JumpBackwards, line)
self.emitBytes(offset.toTriple(), line)
@ -1625,31 +1674,40 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
self.patchReturnAddress(pos)
proc shadowGenerics(self: Compiler, fn: Name, args: seq[Expression]) =
proc specializeGeneric(self: Compiler, fn: Name, args: seq[Expression]): Name =
## Specializes a generic function by
## shadowing the names of the generic
## types with their respective concrete
## types
## instantiating a concrete version
## of it
var mapping: TableRef[string, Type] = newTable[string, Type]()
var kind: Type
result = deepCopy(fn)
# This first loop checks if a user tries to reassign a generic's
# name to a different type
for i, (name, typ) in fn.valueType.args:
for i, (name, typ) in result.valueType.args:
if typ.kind != Generic:
continue
kind = self.infer(args[i])
if typ.name in mapping and not self.compare(kind, mapping[typ.name]):
self.error(&"expected generic argument '{typ.name}' to be of type {self.typeToStr(mapping[typ.name])}, got {self.typeToStr(kind)} instead")
mapping[typ.name] = kind
for gen in mapping.keys():
kind = mapping[gen]
result.valueType.args[i].kind = kind
for (argExpr, argName) in zip(args, result.valueType.args):
if self.names.high() > 16777215:
self.error("cannot declare more than 16777215 variables at a time")
self.names.add(Name(depth: self.scopeDepth + 1,
isPrivate: true,
isConst: false,
owner: self.currentModule,
isConst: false,
name: newIdentExpr(Token(lexeme: argName.name)),
valueType: argName.kind,
codePos: 0,
isLet: false,
line: fn.line,
valueType: kind,
name: newIdentExpr(Token(kind: TokenType.Identifier, lexeme: gen))))
belongsTo: result,
kind: NameKind.Argument
))
if result.valueType.returnType.kind == Generic:
result.valueType.returnType = mapping[result.valueType.returnType.name]
proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} =
@ -1663,7 +1721,7 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} =
dec(i)
kind = self.infer(argument)
if kind.isNil():
if argument.kind == identExpr:
if argument.kind == identExpr and self.resolve(IdentExpr(argument)).isNil():
self.error(&"reference to undeclared name '{IdentExpr(argument).name.lexeme}'")
if node.callee.kind != identExpr:
self.error(&"cannot infer the type of argument {i + 1} in call")
@ -1676,9 +1734,13 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} =
# Calls like hi()
result = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args), node)
if result.valueType.isGeneric:
self.shadowGenerics(result, argExpr)
# Here is when the function gets *actually* compiled
self.funDecl(FunDecl(result.node), result)
result = self.specializeGeneric(result, argExpr)
# We can't instantiate a concrete version
# of a generic function without the types
# of its arguments, so we wait until the
# very last moment to compile it, once
# that info is available to us
self.funDecl(FunDecl(result.node), result)
# Now we call it
self.generateCall(result, argExpr, node.token.line)
of NodeKind.callExpr:
@ -1849,6 +1911,44 @@ proc importStmt(self: Compiler, node: ImportStmt) =
self.error(&"""could not import '{filename}': {getCurrentExceptionMsg()} [errno {osLastError()}]""")
proc printRepl(self: Compiler, typ: Type, node: Expression) =
## Emits instruction to print
## peon types in REPL mode
case typ.kind:
of Generic:
discard # TODO
of Int64:
self.emitByte(PrintInt64, node.token.line)
of UInt64:
self.emitByte(PrintUInt64, node.token.line)
of Int32:
self.emitByte(PrintInt32, node.token.line)
of UInt32:
self.emitByte(PrintInt32, node.token.line)
of Int16:
self.emitByte(PrintInt16, node.token.line)
of UInt16:
self.emitByte(PrintUInt16, node.token.line)
of Int8:
self.emitByte(PrintInt8, node.token.line)
of UInt8:
self.emitByte(PrintUInt8, node.token.line)
of Float64:
self.emitByte(PrintFloat64, node.token.line)
of Float32:
self.emitByte(PrintFloat32, node.token.line)
of Bool:
self.emitByte(PrintBool, node.token.line)
of Nan:
self.emitByte(PrintNan, node.token.line)
of Inf:
self.emitByte(PrintInf, node.token.line)
of String:
self.emitByte(PrintString, node.token.line)
else:
self.emitByte(PrintHex, node.token.line)
proc statement(self: Compiler, node: Statement) =
## Compiles all statements
case node.kind:
@ -1861,37 +1961,7 @@ proc statement(self: Compiler, node: Statement) =
# so we don't have to pop anything
discard
elif self.replMode:
case kind.kind:
of Int64:
self.emitByte(PrintInt64, node.token.line)
of UInt64:
self.emitByte(PrintUInt64, node.token.line)
of Int32:
self.emitByte(PrintInt32, node.token.line)
of UInt32:
self.emitByte(PrintInt32, node.token.line)
of Int16:
self.emitByte(PrintInt16, node.token.line)
of UInt16:
self.emitByte(PrintUInt16, node.token.line)
of Int8:
self.emitByte(PrintInt8, node.token.line)
of UInt8:
self.emitByte(PrintUInt8, node.token.line)
of Float64:
self.emitByte(PrintFloat64, node.token.line)
of Float32:
self.emitByte(PrintFloat32, node.token.line)
of Bool:
self.emitByte(PrintBool, node.token.line)
of Nan:
self.emitByte(PrintNan, node.token.line)
of Inf:
self.emitByte(PrintInf, node.token.line)
of String:
self.emitByte(PrintString, node.token.line)
else:
self.emitByte(PrintHex, node.token.line)
self.printRepl(kind, expression)
else:
self.emitByte(Pop, node.token.line)
of NodeKind.ifStmt:
@ -1942,13 +2012,20 @@ proc varDecl(self: Compiler, node: VarDecl, name: Name) =
var name = node.value.token.lexeme
if node.value.kind == callExpr:
name = CallExpr(node.value).callee.token.lexeme
self.error(&"reference to undeclared name '{name}'")
if self.resolve(name).isNil():
self.error(&"reference to undeclared name '{name}'")
self.error(&"'{node.name.token.lexeme}' has no type")
elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway)
self.error(&"invalid type '{self.typeToStr(expected)}' for var")
elif not self.compare(expected, actual):
if not expected.isNil():
self.error(&"expected value of type '{self.typeToStr(expected)}', but '{node.name.token.lexeme}' is of type '{self.typeToStr(actual)}'")
if expected.isNil():
name.valueType = actual
if actual.kind == Generic:
# We matched a generic type, but we
# need the concrete one
name.valueType = expected
self.expression(node.value)
self.emitByte(StoreVar, node.token.line)
self.emitBytes(self.getStackPos(self.names[^1]).toTriple(), node.token.line)

View File

@ -6,6 +6,6 @@ fn sum[T: int | int32](a, b: T): T {
}
# print(sum(1, 2));
# print(sum(1'i32, 2'i32));
print(sum(1'i16, 2'i16)); # Will not work if uncommented!
print(sum(1, 2));
print(sum(1'i32, 2'i32));
# print(sum(1'i16, 2'i16)); # Will not work if uncommented!

View File

@ -8,94 +8,22 @@
# - It makes the implementation easier and more flexible
# TODO: Use generics
operator `+`*(a, b: int): int {
operator `+`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: uint64): uint64 {
#pragma[magic: "Add", pure]
operator `+`(a, b: float): float {
#pragma[magic: "AddFloat64", pure]
}
operator `+`*(a, b: int32): int32 {
#pragma[magic: "Add", pure]
operator `+`(a, b: float32): float32 {
#pragma[magic: "AddFloat32", pure]
}
operator `+`*(a, b: uint32): uint32 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: int16): int16 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: uint16): uint16 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: int8): int8 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: uint8): uint8 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: float64): float64 {
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: float32): float32 {
#pragma[magic: "Add", pure]
}
operator `-`*(a, b: int): int {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: uint64): uint64 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: int32): int32 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: uint32): uint32 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: int16): int16 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: uint16): uint16 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: int8): int8 {
#pragma[magic: "Subtract", pure]
}
operator `-`*(a, b: uint8): uint8 {
operator `-`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "Subtract", pure]
}
@ -110,42 +38,7 @@ operator `-`*(a, b: float32): float32 {
}
operator `*`*(a, b: int): int {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: uint64): uint64 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: int32): int32 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: uint32): uint32 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: int16): int16 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: uint16): uint16 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: int8): int8 {
#pragma[magic: "Multiply", pure]
}
operator `*`*(a, b: uint8): uint8 {
operator `*`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "Multiply", pure]
}
@ -160,42 +53,12 @@ operator `*`*(a, b: float32): float32 {
}
operator `/`*(a, b: int): int {
operator `/`*[T: int | int32 | int16 | int8](a, b: T): T {
#pragma[magic: "SignedDivide", pure]
}
operator `/`*(a, b: uint64): uint64 {
#pragma[magic: "Divide", pure]
}
operator `/`*(a, b: int32): int32 {
#pragma[magic: "SignedDivide", pure]
}
operator `/`*(a, b: uint32): uint32 {
#pragma[magic: "Divide", pure]
}
operator `/`*(a, b: int16): int16 {
#pragma[magic: "SignedDivide", pure]
}
operator `/`*(a, b: uint16): uint16 {
#pragma[magic: "Divide", pure]
}
operator `/`*(a, b: int8): int8 {
#pragma[magic: "SignedDivide", pure]
}
operator `/`*(a, b: uint8): uint8 {
operator `/`*[T: uint64 | uint32 | uint16 | uint8](a, b: T): T {
#pragma[magic: "Divide", pure]
}
@ -210,12 +73,12 @@ operator `/`*(a, b: float32): float32 {
}
operator `**`*(a, b: int64): int64 {
operator `**`*[T: int | int32 | int16 | int8](a, b: T): T {
#pragma[magic: "SignedPow", pure]
}
operator `**`*(a, b: uint64): uint64 {
operator `**`*[T: uint64 | uint32 | uint16 | uint8](a, b: T): T {
#pragma[magic: "Pow", pure]
}
@ -226,301 +89,32 @@ operator `%`*(a, b: int64): int64 {
# Comparison operators
operator `>`*(a, b: int): bool {
operator `>`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: int): bool {
operator `<`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: int): bool {
operator `==`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: int): bool {
operator `!=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: uint64): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: uint64): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: uint64): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: uint64): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: int32): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: int32): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: int32): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: int32): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: uint32): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: uint32): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: uint32): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: uint32): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: int16): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: int16): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: int16): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: int16): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: uint16): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: uint16): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: uint16): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: uint16): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: int8): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: int8): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: int8): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: int8): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: uint8): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: uint8): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: uint8): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: uint8): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: float): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: float): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: float): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: float): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>`*(a, b: float32): bool {
#pragma[magic: "GreaterThan", pure]
}
operator `<`*(a, b: float32): bool {
#pragma[magic: "LessThan", pure]
}
operator `==`*(a, b: float32): bool {
#pragma[magic: "Equal", pure]
}
operator `!=`*(a, b: float32): bool {
#pragma[magic: "NotEqual", pure]
}
operator `>=`*(a, b: int): bool {
operator `>=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: int): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: uint64): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: uint64): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: int32): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: int32): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: uint32): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: uint32): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: int16): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: int16): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: uint16): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: uint16): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: int8): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: int8): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: uint8): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: uint8): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: float): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: float): bool {
#pragma[magic: "LessOrEqual", pure]
}
operator `>=`*(a, b: float32): bool {
#pragma[magic: "GreaterOrEqual", pure]
}
operator `<=`*(a, b: float32): bool {
operator `<=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool {
#pragma[magic: "LessOrEqual", pure]
}
@ -545,34 +139,34 @@ operator `&`*(a, b: int): bool {
}
operator `|`*(a, b: int): int {
operator `|`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): int {
#pragma[magic: "Or", pure]
}
operator `~`*(a: int): int {
operator `~`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a: T): T {
#pragma[magic: "Not", pure]
}
operator `>>`*(a, b: int): int {
operator `>>`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "RShift", pure]
}
operator `<<`*(a, b: int): int {
operator `<<`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "LShift", pure]
}
operator `^`*(a, b: int): int {
operator `^`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T {
#pragma[magic: "Xor", pure]
}
# Assignment operators
operator `=`*[T: all](a: var T, b: T) {
operator `=`*[T: all](a: var T, b: T) { # TODO: This is just a placeholder right now
#pragma[magic: "GenericAssign"]
}