slige/compiler/vtype.ts

140 lines
3.8 KiB
TypeScript
Raw Normal View History

2024-12-06 13:17:52 +00:00
export type VType =
| { type: "error" }
| { type: "unknown" }
| { type: "null" }
| { type: "int" }
| { type: "string" }
| { type: "bool" }
| { type: "array"; inner: VType }
| { type: "struct"; fields: VTypeParam[] }
| {
type: "fn";
genericParams?: VTypeGenericParam[];
params: VTypeParam[];
returnType: VType;
2024-12-26 00:51:05 +00:00
stmtId: number;
}
2024-12-26 00:51:05 +00:00
| { type: "generic"; param: VTypeGenericParam }
| {
type: "generic_spec";
subject: VType;
2024-12-26 00:51:05 +00:00
genericArgs: GenericArgsMap;
};
2024-12-06 13:17:52 +00:00
export type VTypeParam = {
ident: string;
vtype: VType;
};
export type VTypeGenericParam = {
2024-12-26 00:51:05 +00:00
id: number;
ident: string;
};
2024-12-26 00:51:05 +00:00
export type GenericArgsMap = { [id: number]: VType };
export function vtypesEqual(
a: VType,
b: VType,
generics?: GenericArgsMap,
): boolean {
2024-12-06 13:17:52 +00:00
if (
2024-12-09 09:52:05 +00:00
["error", "unknown", "null", "int", "string", "bool"]
2024-12-26 00:51:05 +00:00
.includes(a.type) && a.type === b.type
2024-12-06 13:17:52 +00:00
) {
return true;
}
if (a.type === "array" && b.type === "array") {
2024-12-26 00:51:05 +00:00
return vtypesEqual(a.inner, b.inner, generics);
2024-12-06 13:17:52 +00:00
}
2024-12-31 02:38:38 +00:00
if (a.type === "struct" && b.type === "struct") {
if (a.fields.length !== b.fields.length) {
return false;
}
const match = a.fields
.map((af) => ({
ident: af.ident,
af,
bf: b.fields.find((bf) => bf.ident === af.ident),
}));
if (match.some((m) => m.bf === undefined)) {
return false;
}
if (match.some((m) => !vtypesEqual(m.af.vtype, m.bf!.vtype))) {
return false;
}
return true;
}
2024-12-06 13:17:52 +00:00
if (a.type === "fn" && b.type === "fn") {
if (a.params.length !== b.params.length) {
return false;
}
for (let i = 0; i < a.params.length; ++i) {
if (!vtypesEqual(a.params[i].vtype, b.params[i].vtype)) {
return false;
}
}
2024-12-26 00:51:05 +00:00
return vtypesEqual(a.returnType, b.returnType, generics);
}
if (a.type === "generic" && b.type === "generic") {
return a.param.id === b.param.id;
}
if (
(a.type === "generic" || b.type === "generic") &&
generics !== undefined
) {
if (generics === undefined) {
throw new Error();
}
const generic = a.type === "generic" ? a : b;
const concrete = a.type === "generic" ? b : a;
const genericType = extractGenericType(generic, generics);
return vtypesEqual(genericType, concrete, generics);
2024-12-06 13:17:52 +00:00
}
return false;
}
2024-12-26 00:51:05 +00:00
export function extractGenericType(
generic: VType,
generics: GenericArgsMap,
): VType {
if (generic.type !== "generic") {
return generic;
}
if (!(generic.param.id in generics)) {
throw new Error("generic not found (not supposed to happen)");
}
return generics[generic.param.id];
}
2024-12-06 13:17:52 +00:00
export function vtypeToString(vtype: VType): string {
if (
2024-12-09 09:52:05 +00:00
["error", "unknown", "null", "int", "string", "bool"]
2024-12-06 13:17:52 +00:00
.includes(vtype.type)
) {
return vtype.type;
}
if (vtype.type === "array") {
return `[${vtypeToString(vtype.inner)}]`;
}
2024-12-31 02:38:38 +00:00
if (vtype.type === "struct") {
const fields = vtype.fields
.map((field) => `${field.ident}: ${vtypeToString(field.vtype)}`)
.join(", ");
return `struct { ${fields} }`;
}
2024-12-06 13:17:52 +00:00
if (vtype.type === "fn") {
const paramString = vtype.params.map((param) =>
`${param.ident}: ${vtypeToString(param.vtype)}`
)
.join(", ");
2025-01-02 08:14:24 +00:00
return `fn(${paramString}) -> ${vtypeToString(vtype.returnType)}`;
2024-12-06 13:17:52 +00:00
}
if (vtype.type === "generic") {
return `generic`;
}
2024-12-06 13:17:52 +00:00
throw new Error(`unhandled vtype '${vtype.type}'`);
}