You are implementing a small type system for a custom “Toy Language”. Types can be:
int
,
float
,
str
,
bool
T1
,
T2
[int,[T1,str]]
(param1,param2,...) -> returnType
Implement the following classes.
NodeRepresents a type node.
node_type
is a
string
, the node is a
leaf
representing either a primitive type or a generic.
node_type
is a
list of Node
, the node is a
tuple
whose children are those nodes.
class Node:
def __init__(self, node_type):
...
def to_str(self) -> str:
...
Node.to_str() formatting rules
"int"
,
"T1"
.
[int,T1]
,
[int,[T1,str]]
.
FunctionRepresents a function signature.
class Function:
def __init__(self, parameters: list[Node], output_type: Node):
...
def to_str(self) -> str:
...
Function.to_str() formatting rules
(param1,param2,...) -> returnType
using each node’s
to_str()
.
(int,T1) -> [T1,str]
Implement:
def get_return_type(parameters: list[Node], function: Function) -> Node:
...
Given:
function.parameters
: the expected parameter type patterns (may include generics and nested tuples)
parameters
: the actual argument types supplied at a call site
Return a new Node representing the concrete return type after substituting generics.
len(parameters) != len(function.parameters)
.
T1
), bind it to the actual type node.
T1
is bound more than once in the same call, all bindings must be structurally identical.
T1
cannot be both
int
and
str
).
function.output_type
with their bound concrete types.
[T1, T2, int, T1] -> [T1, T2]
[int, str, int, int]
T1 = int
,
T2 = str
[int, str]
[[T1, float], T2, T3] -> [T3, T1]
[[str, float], [int, str], int]
T1 = str
,
T2 = [int, str]
,
T3 = int
[int, str]
[T1, T1] -> T1
[int, str]
T1
cannot be both
int
and
str
Implement the above behavior with clear recursive logic and appropriate error handling.