added type system

This commit is contained in:
2025-08-19 22:40:45 -06:00
parent 90381840af
commit df1083df3b
3 changed files with 166 additions and 20 deletions

View File

@@ -1,29 +1,10 @@
import { Impl, Module, TraitTypeDeclaration, TypeUsage } from "../parse/ast"; import { Impl, Module, TraitTypeDeclaration, TypeUsage } from "../parse/ast";
import { compareTypes } from "./type_system";
interface Context { interface Context {
environment: Record<string, TraitTypeDeclaration>; environment: Record<string, TraitTypeDeclaration>;
} }
const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
if (typeA.typeUsage !== typeB.typeUsage) {
throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`);
}
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeA.name.name !== typeB.name.name) {
throw Error(`Mismatched types: ${typeA.name.name} ${typeB.name.name}`);
}
}
if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") {
if (typeA.arguments.length !== typeB.arguments.length) {
throw Error(`Mismatched arg lengths: ${typeA.arguments.length} ${typeB.arguments.length}`);
}
for (let i = 0; i < typeA.arguments.length; i++) {
compareTypes(typeA.arguments[i], typeB.arguments[i]);
}
compareTypes(typeA.returnType, typeB.returnType);
}
};
export default class TraitChecker { export default class TraitChecker {
withModule = (module: Module) => { withModule = (module: Module) => {
let ctx: Context = { environment: {} }; let ctx: Context = { environment: {} };

View File

@@ -0,0 +1,165 @@
import { TypeUsage } from "../parse/ast";
export const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
if (typeA.typeUsage !== typeB.typeUsage) {
throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`);
}
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeA.name.name !== typeB.name.name) {
throw Error(`Mismatched types: ${typeA.name.name} ${typeB.name.name}`);
}
}
if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") {
if (typeA.arguments.length !== typeB.arguments.length) {
throw Error(`Mismatched arg lengths: ${typeA.arguments.length} ${typeB.arguments.length}`);
}
for (let i = 0; i < typeA.arguments.length; i++) {
compareTypes(typeA.arguments[i], typeB.arguments[i]);
}
compareTypes(typeA.returnType, typeB.returnType);
}
};
interface Equals {
operation: "equals";
}
interface Argument {
operation: "argument";
argNum: number;
}
interface ReturnValue {
operation: "return";
}
interface Comparison {
left: TypeUsage;
operation: Equals | Argument | ReturnValue;
right: TypeUsage;
}
class TypeSystem {
comparisons: Comparison[];
result: Record<string, TypeUsage>;
constructor() {
this.comparisons = [];
this.result = {};
}
compare = (comparison: Comparison) => {
this.comparisons.push(comparison);
};
solve = () => {
let foundUpdate = false;
let containsUnknown = false;
while (true) {
for (const comparison of this.comparisons) {
// if already found, just update
if (comparison.left.typeUsage === "UnknownTypeUsage" && this.result[comparison.left.name]) {
foundUpdate = true;
comparison.left = this.result[comparison.left.name];
}
if (
comparison.right.typeUsage === "UnknownTypeUsage" &&
this.result[comparison.right.name]
) {
foundUpdate = true;
comparison.right = this.result[comparison.right.name];
}
// equals
if (comparison.operation.operation === "equals") {
// solve left
if (
comparison.left.typeUsage === "UnknownTypeUsage" &&
comparison.right.typeUsage !== "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[comparison.left.name] = comparison.right;
comparison.left = comparison.right;
}
// solve right
if (
comparison.left.typeUsage !== "UnknownTypeUsage" &&
comparison.right.typeUsage === "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[comparison.right.name] = comparison.left;
comparison.right = comparison.left;
}
if (
comparison.left.typeUsage !== "UnknownTypeUsage" &&
comparison.right.typeUsage !== "UnknownTypeUsage"
) {
compareTypes(comparison.left, comparison.right);
}
}
// argument
if (comparison.operation.operation === "argument") {
if (comparison.left.typeUsage !== "FunctionTypeUsage") {
throw Error("Argument for something that isn't a function");
}
// solve left
const argument = comparison.left.arguments[comparison.operation.argNum];
if (
argument.typeUsage === "UnknownTypeUsage" &&
comparison.right.typeUsage !== "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[argument.name] = comparison.right;
comparison.left.arguments[comparison.operation.argNum] = comparison.right;
}
// solve right
if (
argument.typeUsage !== "UnknownTypeUsage" &&
comparison.right.typeUsage === "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[comparison.right.name] =
comparison.left.arguments[comparison.operation.argNum];
comparison.right = comparison.left.arguments[comparison.operation.argNum];
}
}
// return type
if (comparison.operation.operation === "return") {
if (comparison.left.typeUsage !== "FunctionTypeUsage") {
throw Error("return type for something that isn't a function");
}
// solve left
if (
comparison.left.returnType.typeUsage === "UnknownTypeUsage" &&
comparison.right.typeUsage !== "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[comparison.left.returnType.name] = comparison.right;
comparison.left.returnType = comparison.right;
}
// solve right
if (
comparison.left.returnType.typeUsage !== "UnknownTypeUsage" &&
comparison.right.typeUsage === "UnknownTypeUsage"
) {
foundUpdate = true;
this.result[comparison.right.name] = comparison.left.returnType;
comparison.right = comparison.left.returnType;
}
}
if (
comparison.left.typeUsage === "UnknownTypeUsage" ||
comparison.right.typeUsage === "UnknownTypeUsage"
) {
containsUnknown = true;
}
}
if (!foundUpdate && containsUnknown) {
throw Error("Type system failed to resolve all unknowns");
}
if (!foundUpdate) {
break;
}
}
return this.result;
};
}