Further work on function calls
This commit is contained in:
parent
f34b71ec0b
commit
db41234ee0
|
@ -70,7 +70,7 @@ type
|
|||
isGenerator*: bool
|
||||
isCoroutine*: bool
|
||||
isAuto*: bool
|
||||
args*: seq[tuple[name: string, kind: Type, default: Expression]]
|
||||
args*: TypeSignature
|
||||
returnType*: Type
|
||||
builtinOp*: string
|
||||
fun*: Declaration
|
||||
|
@ -178,6 +178,8 @@ type
|
|||
# Is this name generated by user code or internally by the compiler?
|
||||
isReal: bool
|
||||
|
||||
TypeSignature = seq[tuple[name: string, kind: Type, default: Expression]]
|
||||
|
||||
## Our typed AST representation
|
||||
|
||||
TypedNode* = ref object of RootObj
|
||||
|
@ -198,7 +200,12 @@ type
|
|||
|
||||
TypedIdentExpr* = ref object of TypedExpr
|
||||
## A typed identifier expression
|
||||
name*: Name
|
||||
name*: Name
|
||||
|
||||
TypedCallExpr* = ref object of TypedExpr
|
||||
## A typed function call expression
|
||||
callee*: Name
|
||||
args*: seq[tuple[name: string, kind: Type, default: TypedExpr]]
|
||||
|
||||
TypedDecl* = ref object of TypedNode
|
||||
## A typed declaration node
|
||||
|
@ -297,7 +304,7 @@ proc stringify*(self: PeonCompiler, typ: TypedNode): string
|
|||
proc inferOrError*(self: PeonCompiler, node: Expression): TypedExpr
|
||||
proc compare(self: PeonCompiler, a, b: Type): bool
|
||||
proc expression(self: PeonCompiler, node: Expression): TypedExpr
|
||||
proc specialize(self: PeonCompiler, typ: Type, args: seq[Expression]): Type {.discardable.}
|
||||
proc specialize(self: PeonCompiler, name: Name, args: seq[Expression]): Type
|
||||
proc funDecl(self: PeonCompiler, node: FunDecl, name: Name = nil): TypedDecl
|
||||
proc getTypeDistance(self: PeonCompiler, a, b: Type): int
|
||||
|
||||
|
@ -358,6 +365,13 @@ proc newTypedBinaryExpr(node: UnaryExpr, kind: Type, a, b: TypedExpr): TypedBina
|
|||
result = TypedBinaryExpr(node: node, a: a, b: b, kind: kind)
|
||||
|
||||
|
||||
proc newTypedCallExpr(node: CallExpr, callee: Name,
|
||||
args: seq[tuple[name: string, kind: Type, default: TypedExpr]]): TypedCallExpr =
|
||||
|
||||
## Initializes a new typed function call expression
|
||||
result = TypedCallExpr(node: node, callee: callee, args: args, kind: callee.valueType.returnType)
|
||||
|
||||
|
||||
proc newTypedBlockStmt(node: BlockStmt, body: seq[TypedNode]): TypedBlockStmt =
|
||||
## Initializes a new typed block statement
|
||||
result = TypedBlockStmt(node: node, body: body)
|
||||
|
@ -982,6 +996,8 @@ proc inferOrError(self: PeonCompiler, node: Expression): TypedExpr =
|
|||
proc stringify*(self: PeonCompiler, typ: Type): string =
|
||||
## Returns the string representation of a
|
||||
## type object
|
||||
if typ.isNil():
|
||||
return ""
|
||||
case typ.kind:
|
||||
of Char, Byte, String, Nil, TypeKind.Nan,
|
||||
Auto, Any:
|
||||
|
@ -1171,7 +1187,7 @@ proc getTypeDistance(self: PeonCompiler, a, b: Type): int =
|
|||
parent = parent.parent
|
||||
|
||||
|
||||
proc calcTypeDistance(self: PeonCompiler, typ: Type, sig: seq[tuple[name: string, kind: Type, default: Expression]]): int =
|
||||
proc calcTypeDistance(self: PeonCompiler, typ: Type, sig: TypeSignature): int =
|
||||
## Computes the cumulative type distance between
|
||||
## the given type and the given type signature. It's
|
||||
## basically just adding up the type distances of each
|
||||
|
@ -1191,7 +1207,7 @@ proc calcTypeDistance(self: PeonCompiler, typ: Type, sig: seq[tuple[name: string
|
|||
self.error(&"cannot compute type distance for object of type {self.stringify(typ)}")
|
||||
|
||||
|
||||
proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: seq[tuple[name: string, kind: Type, default: Expression]]): bool =
|
||||
proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: TypeSignature): bool =
|
||||
## Helper for to check type signatures.
|
||||
## Returns true if the given type matches
|
||||
## the given type signature
|
||||
|
@ -1213,7 +1229,7 @@ proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: seq[tuple[name: stri
|
|||
# third argument has a default value
|
||||
return false
|
||||
# We construct a new signature, without the function's default arguments
|
||||
var args: seq[tuple[name: string, kind: Type, default: Expression]] = @[]
|
||||
var args: TypeSignature = @[]
|
||||
for argument in typ.args:
|
||||
if argument.default.isNil():
|
||||
args.add(argument)
|
||||
|
@ -1237,7 +1253,7 @@ proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: seq[tuple[name: stri
|
|||
self.error(&"cannot check type signature for object of type {self.stringify(typ)}")
|
||||
|
||||
|
||||
proc match(self: PeonCompiler, name: string, sig: seq[tuple[name: string, kind: Type, default: Expression]], node: ASTNode = nil): Name =
|
||||
proc match(self: PeonCompiler, name: string, sig: TypeSignature, node: ASTNode = nil): Name =
|
||||
## Tries to find a matching type for a given (typeName, typeSignature) pair
|
||||
## and returns it. In this context, "type signature" means an ordered list of
|
||||
## tuples (paramName, paramType, paramDefault) that represents the arguments we
|
||||
|
@ -1368,50 +1384,39 @@ proc match(self: PeonCompiler, name: string, sig: seq[tuple[name: string, kind:
|
|||
self.error(msg, node)
|
||||
|
||||
|
||||
proc specialize(self: PeonCompiler, typ: Type, args: seq[Expression]): Type =
|
||||
proc specialize(self: PeonCompiler, name: Name, args: seq[Expression]): Type =
|
||||
## Instantiates a generic type
|
||||
var
|
||||
mapping: TableRef[string, Type] = newTable[string, Type]()
|
||||
kind: Type
|
||||
gen: Type
|
||||
result = deepCopy(typ)
|
||||
case result.kind:
|
||||
of TypeKind.Function:
|
||||
# This loop checks if a user tries to reassign a generic's
|
||||
# name to a different type
|
||||
for i, arg in args:
|
||||
if i > typ.fun.generics.high():
|
||||
gen = typ.genericArgs[typ.fun.generics[^1].name.token.lexeme]
|
||||
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 types: seq[Type] = @[]
|
||||
var map = newTable[string, Type]()
|
||||
for arg in args:
|
||||
types.add(self.inferOrError(arg).kind)
|
||||
if types[^1].kind != Typevar:
|
||||
self.error(&"expecting type name during generic instantiation, got {self.stringify(types[^1])}", arg)
|
||||
for (gen, value) in zip(fun.generics, args):
|
||||
map[gen.name.token.lexeme] = self.inferOrError(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:
|
||||
gen = typ.genericArgs[typ.fun.generics[i].name.token.lexeme]
|
||||
kind = self.inferOrError(arg).kind
|
||||
if gen.name in mapping and not self.compare(kind, mapping[gen.name]):
|
||||
self.error(&"expecting generic argument '{gen.name}' to be of type {self.stringify(mapping[gen.name])}, got {self.stringify(kind)}", args[i])
|
||||
mapping[gen.name] = kind
|
||||
result.args[i].kind = kind
|
||||
for arg in result.args.mitems():
|
||||
# We only replace types we know about. This allows
|
||||
# us to do partial instantiation
|
||||
if arg.kind.kind == Generic and arg.kind.name in mapping:
|
||||
arg.kind = mapping[arg.kind.name]
|
||||
if not result.returnType.isNil() and result.returnType.kind == Generic:
|
||||
if result.returnType.name in mapping:
|
||||
result.returnType = mapping[result.returnType.name]
|
||||
elif mapping.len() == 0:
|
||||
# The function has no generic arguments,
|
||||
# just a generic return type
|
||||
var typ: Type
|
||||
for i, gen in result.fun.generics:
|
||||
if gen.name.token.lexeme == result.returnType.name:
|
||||
typ = result.args[i].kind
|
||||
break
|
||||
if typ.isNil():
|
||||
self.error(&"unknown generic argument name '{result.returnType.name}'", result.fun)
|
||||
result.returnType = typ
|
||||
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 '{result.returnType.name}'", result.fun)
|
||||
self.error(&"unknown generic argument name '{concrete.returnType.name}'", concrete.fun)
|
||||
result = concrete
|
||||
else:
|
||||
discard
|
||||
self.error(&"cannot instantiate concrete type for object of type {self.stringify(name.valueType)}")
|
||||
|
||||
|
||||
proc unpackTypes(self: PeonCompiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, isGeneric: bool = false) =
|
||||
|
@ -1620,7 +1625,7 @@ proc unary(self: PeonCompiler, node: UnaryExpr): TypedUnaryExpr =
|
|||
var name = self.match(node.token.lexeme, fn.args, node)
|
||||
var impl = name.valueType
|
||||
if name.isGeneric:
|
||||
impl = self.specialize(impl, @[node.a])
|
||||
impl = self.specialize(name, @[node.a])
|
||||
result = newTypedUnaryExpr(node, impl.returnType, typeOfA)
|
||||
|
||||
|
||||
|
@ -1636,50 +1641,19 @@ proc binary(self: PeonCompiler, node: BinaryExpr): TypedBinaryExpr =
|
|||
var name = self.match(node.token.lexeme, fn.args, node)
|
||||
var impl = name.valueType
|
||||
if name.isGeneric:
|
||||
impl = self.specialize(impl, @[node.a])
|
||||
impl = self.specialize(name, @[node.a])
|
||||
result = newTypedBinaryExpr(node, impl.returnType, typeOfA, typeOfB)
|
||||
|
||||
|
||||
proc genericExpr(self: PeonCompiler, node: GenericExpr): TypedExpr =
|
||||
## Compiles generic instantiation
|
||||
var name = self.findOrError(node.ident.token.lexeme)
|
||||
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() != node.args.len():
|
||||
self.error(&"wrong number of types supplied for generic instantiation (expected {fun.generics.len()}, got {node.args.len()})")
|
||||
var concrete = deepCopy(name.valueType)
|
||||
var types: seq[Type] = @[]
|
||||
var map = newTable[string, Type]()
|
||||
for arg in node.args:
|
||||
types.add(self.inferOrError(arg).kind)
|
||||
if types[^1].kind != Typevar:
|
||||
self.error(&"expecting type name during generic instantiation, got {self.stringify(types[^1])}", arg)
|
||||
for (gen, value) in zip(fun.generics, node.args):
|
||||
map[gen.name.token.lexeme] = self.inferOrError(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 = newTypedExpr(node, concrete)
|
||||
else:
|
||||
self.error(&"cannot instantiate concrete type for object of type {self.stringify(name.valueType)}")
|
||||
|
||||
result = newTypedExpr(node, self.specialize(self.findOrError(node.ident.token.lexeme), node.args))
|
||||
|
||||
|
||||
proc call(self: PeonCompiler, node: CallExpr): TypedExpr =
|
||||
## Compiles call expressions. This includes
|
||||
## things like object and enum construction
|
||||
var args: seq[tuple[name: string, kind: Type, default: Expression]] = @[]
|
||||
var args: TypeSignature = @[]
|
||||
var argExpr: seq[Expression] = @[]
|
||||
var default: Expression
|
||||
var kind: Type
|
||||
|
@ -1695,11 +1669,25 @@ proc call(self: PeonCompiler, node: CallExpr): TypedExpr =
|
|||
of NodeKind.identExpr:
|
||||
# Calls like hi()
|
||||
var impl = self.match(IdentExpr(node.callee).name.lexeme, args, node)
|
||||
var function = impl.valueType
|
||||
if impl.isGeneric:
|
||||
function = self.specialize(function, argExpr)
|
||||
result = newTypedExpr(node, function.returnType)
|
||||
self.dispatchDelayedPragmas(impl)
|
||||
var typ = impl.valueType
|
||||
if impl.isGeneric:
|
||||
typ = self.specialize(impl, argExpr)
|
||||
case typ.kind:
|
||||
of Enum, CustomType:
|
||||
# TODO
|
||||
result = newTypedExpr(node, typ)
|
||||
of Function:
|
||||
var typedArgs: seq[tuple[name: string, kind: Type, default: TypedExpr]] = @[]
|
||||
for arg in args:
|
||||
if not arg.default.isNil():
|
||||
typedArgs.add((arg.name, arg.kind, self.expression(arg.default)))
|
||||
else:
|
||||
typedArgs.add((arg.name, arg.kind, nil))
|
||||
result = newTypedCallExpr(node, impl, typedArgs)
|
||||
else:
|
||||
# TODO?
|
||||
discard
|
||||
of NodeKind.callExpr:
|
||||
# Calling a call expression, like hello()()
|
||||
# TODO
|
||||
|
|
Loading…
Reference in New Issue