ethos/src/ty.ts
sfja 696bfc55f4
All checks were successful
Check / Explore-Gitea-Actions (push) Successful in 13s
remove str
2026-04-13 12:33:43 +02:00

203 lines
6.0 KiB
TypeScript

import * as ast from "./ast.ts";
export class Ty {
private static idCounter = 0;
private static internedTys = new Map<string, Ty>();
static create<Tag extends TyKind["tag"]>(
tag: Tag,
kind: Omit<TyKind & { tag: Tag }, "tag">,
): Ty {
const ty = new Ty(
this.idCounter,
{ tag, ...kind } as TyKind & { tag: Tag },
);
const hash = ty.internHash();
if (this.internedTys.has(hash)) {
return this.internedTys.get(hash)!;
}
this.internedTys.set(hash, ty);
this.idCounter += 1;
return ty;
}
static Error = Ty.create("Error", {});
static Void = Ty.create("Void", {});
static IntLiteral = Ty.create("IntLiteral", {});
static I8 = Ty.create("Int", { intTy: "i8" });
static I16 = Ty.create("Int", { intTy: "i16" });
static I32 = Ty.create("Int", { intTy: "i32" });
static I64 = Ty.create("Int", { intTy: "i64" });
static ISize = Ty.create("Int", { intTy: "isize" });
static U8 = Ty.create("Int", { intTy: "u8" });
static U16 = Ty.create("Int", { intTy: "u16" });
static U32 = Ty.create("Int", { intTy: "u32" });
static U64 = Ty.create("Int", { intTy: "u64" });
static USize = Ty.create("Int", { intTy: "usize" });
static Bool = Ty.create("Bool", {});
private internHash(): string {
return JSON.stringify(this.kind);
}
private constructor(
public id: number,
public kind: TyKind,
) {}
is<
Tag extends TyKind["tag"],
>(tag: Tag): this is Ty & { kind: { tag: Tag } } {
return this.kind.tag === tag;
}
compatibleWith(other: Ty): boolean {
// types are interned--we can just do this
if (this === other) {
return true;
}
if (this.is("Error")) {
return false;
}
if (this.is("Void")) {
return other.is("Void");
}
if (this.is("IntLiteral")) {
// return other.is("Int") || other.is("UInt");
return false;
}
if (this.is("Int")) {
return other.is("Int") && this.kind.intTy == other.kind.intTy;
}
if (this.is("Bool")) {
return other.is("Bool");
}
if (this.is("Ptr")) {
if (!other.is("Ptr")) {
return false;
}
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
return false;
}
return true;
}
if (this.is("Array")) {
if (!other.is("Array")) {
return false;
}
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
return false;
}
if (this.kind.length !== other.kind.length) {
return false;
}
return true;
}
if (this.is("Slice")) {
if (!other.is("Slice")) {
return false;
}
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
return false;
}
return true;
}
if (this.is("Range")) {
return other.is("Range");
}
if (this.is("Fn")) {
if (!other.is("Fn")) {
return false;
}
for (const i of this.kind.params.keys()) {
if (!this.kind.params[i].compatibleWith(other.kind.params[i])) {
return false;
}
}
if (!this.kind.retTy.compatibleWith(other.kind.retTy)) {
return false;
}
return true;
}
if (this.is("FnStmt")) {
if (!other.is("FnStmt")) {
return false;
}
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
return false;
}
// redundant; sanity check
if (this.kind.stmt.id !== other.kind.stmt.id) {
throw new Error();
}
return true;
}
throw new Error(`'${this.kind.tag}' not handled`);
}
isSized(): boolean {
if (this.is("Slice")) {
return false;
}
return true;
}
pretty(): string {
switch (this.kind.tag) {
case "Error":
return "<error>";
case "Void":
return "void";
case "IntLiteral":
return "{integer}";
case "Int":
return `${this.kind.intTy}`;
case "Bool":
return "bool";
case "Ptr":
return `*${this.kind.ty.pretty()}`;
case "PtrMut":
return `*mut ${this.kind.ty.pretty()}`;
case "Array":
return `[${this.kind.ty.pretty()}; ${this.kind.length}]`;
case "Slice":
return `[${this.kind.ty.pretty()}]`;
case "Range":
return `Range`;
case "Fn":
return `fn (${
this.kind.params.map((param) => param.pretty()).join(", ")
}) -> ${this.kind.retTy.pretty()}`;
case "FnStmt":
if (!this.kind.ty.is("Fn")) {
throw new Error();
}
return `fn ${this.kind.stmt.kind.ident}(${
this.kind.ty.kind.params.map((param) => param.pretty())
.join(
", ",
)
}) -> ${this.kind.ty.kind.retTy.pretty()}`;
default:
this.kind satisfies never;
}
throw new Error("unhandled");
}
}
export type TyKind =
| { tag: "Error" }
| { tag: "Void" }
| { tag: "IntLiteral" }
| { tag: "Int"; intTy: ast.IntTy }
| { tag: "Bool" }
| { tag: "Ptr"; ty: Ty }
| { tag: "PtrMut"; ty: Ty }
| { tag: "Array"; ty: Ty; length: number }
| { tag: "Slice"; ty: Ty }
| { tag: "Range" }
| { tag: "Fn"; params: Ty[]; retTy: Ty }
| { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> };