started on type checker

This commit is contained in:
2025-08-20 21:42:42 -06:00
parent 982603aa54
commit 68e51cf8aa
4 changed files with 132 additions and 43 deletions

View File

@@ -4,7 +4,7 @@ export interface Spanned {
} }
export interface Identifier extends Spanned { export interface Identifier extends Spanned {
name: string; text: string;
} }
export interface LiteralInt { export interface LiteralInt {
@@ -113,11 +113,7 @@ export interface AssignmentStatement {
expression: Expression; expression: Expression;
} }
export type Statement = export type Statement = ReturnStatement | LetStatement | AssignmentStatement | Expression;
| ReturnStatement
| LetStatement
| AssignmentStatement
| Expression;
export interface Block { export interface Block {
statements: Statement[]; statements: Statement[];
@@ -141,6 +137,14 @@ export interface Function {
block: Block; block: Block;
} }
export const functionToType = (fn: FunctionDeclaration): TypeUsage => {
return {
typeUsage: "FunctionTypeUsage",
arguments: fn.arguments.map((arg) => arg.type),
returnType: fn.returnType,
};
};
export interface StructTypeField { export interface StructTypeField {
name: Identifier; name: Identifier;
type: TypeUsage; type: TypeUsage;

View File

@@ -50,7 +50,7 @@ semantics.addOperation<any>("toAST", {
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { name: "i64", spanStart: 0, spanEnd: 0 }, name: { text: "i64", spanStart: 0, spanEnd: 0 },
}, },
}; };
}, },
@@ -60,7 +60,7 @@ semantics.addOperation<any>("toAST", {
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { name: "f64", spanStart: 0, spanEnd: 0 }, name: { text: "f64", spanStart: 0, spanEnd: 0 },
}, },
}; };
}, },
@@ -70,7 +70,7 @@ semantics.addOperation<any>("toAST", {
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { name: "bool", spanStart: 0, spanEnd: 0 }, name: { text: "bool", spanStart: 0, spanEnd: 0 },
}, },
}; };
}, },
@@ -80,7 +80,7 @@ semantics.addOperation<any>("toAST", {
value: text.sourceString, value: text.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { name: "String", spanStart: 0, spanEnd: 0 }, name: { text: "String", spanStart: 0, spanEnd: 0 },
}, },
}; };
}, },
@@ -100,7 +100,7 @@ semantics.addOperation<any>("toAST", {
}, },
identifier(_1, _2): Identifier { identifier(_1, _2): Identifier {
return { return {
name: this.sourceString, text: this.sourceString,
spanStart: this.source.startIdx, spanStart: this.source.startIdx,
spanEnd: this.source.endIdx, spanEnd: this.source.endIdx,
}; };
@@ -197,10 +197,7 @@ semantics.addOperation<any>("toAST", {
statementType: "LetStatement", statementType: "LetStatement",
variableName: ident.toAST(), variableName: ident.toAST(),
expression: expression.toAST(), expression: expression.toAST(),
type: type: tu.length > 0 ? tu[0] : { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
tu.length > 0
? tu[0]
: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
AssignmentStatement(variable, _2, expression, _4): AssignmentStatement { AssignmentStatement(variable, _2, expression, _4): AssignmentStatement {
@@ -248,15 +245,7 @@ semantics.addOperation<any>("toAST", {
type: typeUsage.toAST(), type: typeUsage.toAST(),
}; };
}, },
FunctionDeclaration( FunctionDeclaration(_1, identifier, _3, args, _4, _5, returnType): FunctionDeclaration {
_1,
identifier,
_3,
args,
_4,
_5,
returnType,
): FunctionDeclaration {
return { return {
name: identifier.toAST(), name: identifier.toAST(),
arguments: args.asIteration().children.map((c) => c.toAST()), arguments: args.asIteration().children.map((c) => c.toAST()),
@@ -276,14 +265,7 @@ semantics.addOperation<any>("toAST", {
type: typeUsage.toAST(), type: typeUsage.toAST(),
}; };
}, },
StructTypeDeclaration( StructTypeDeclaration(_1, identifier, _3, _4, fields, _6): StructTypeDeclaration {
_1,
identifier,
_3,
_4,
fields,
_6,
): StructTypeDeclaration {
return { return {
moduleItem: "StructTypeDeclaration", moduleItem: "StructTypeDeclaration",
typeDeclaration: "StructTypeDeclaration", typeDeclaration: "StructTypeDeclaration",
@@ -294,14 +276,7 @@ semantics.addOperation<any>("toAST", {
TraitMethod(declaration, _2): FunctionDeclaration { TraitMethod(declaration, _2): FunctionDeclaration {
return declaration.toAST(); return declaration.toAST();
}, },
TraitTypeDeclaration( TraitTypeDeclaration(_1, identifier, _3, _4, methods, _5): TraitTypeDeclaration {
_1,
identifier,
_3,
_4,
methods,
_5,
): TraitTypeDeclaration {
return { return {
moduleItem: "TraitTypeDeclaration", moduleItem: "TraitTypeDeclaration",
typeDeclaration: "TraitTypeDeclaration", typeDeclaration: "TraitTypeDeclaration",

View File

@@ -0,0 +1,110 @@
import { functionToType, Module, TypeUsage } from "../parse/ast";
import { TypeSystem } from "./type_system";
interface EnvImpl {
trait: string | null;
functions: Record<string, TypeUsage>;
}
interface NamedType {
namedEntity: "NamedType";
isA: "Scalar" | "Trait" | "Struct";
fields: Record<string, TypeUsage>;
impls: EnvImpl[];
}
interface Variable {
namedEntity: "Variable";
type: TypeUsage;
}
type NamedEntity = NamedType | Variable;
interface Context {
currentFunctionReturn: TypeUsage | null;
environment: Record<string, NamedEntity>;
}
class TypeChecker {
withModule = (module: Module, typeSystem: TypeSystem) => {
const ctx: Context = {
currentFunctionReturn: null,
environment: {},
};
// add functions, structs, and traits to the context
for (const item of module.items) {
if (item.moduleItem === "StructTypeDeclaration") {
if (ctx.environment[item.name.text]) {
throw Error("Duplicate name of struct");
}
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Struct",
fields: Object.fromEntries(item.fields.map((field) => [field.name, field.type])),
impls: [],
};
}
if (item.moduleItem === "TraitTypeDeclaration") {
if (ctx.environment[item.name.text]) {
throw Error("Duplicate name of trait");
}
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Trait",
fields: {},
impls: [
{
trait: item.name.text,
functions: Object.fromEntries(
item.functions.map((fn) => {
return [fn.name, functionToType(fn)];
}),
),
},
],
};
}
if (item.moduleItem === "Function") {
if (ctx.environment[item.declaration.name.text]) {
throw Error("Duplicate name of function");
}
ctx.environment[item.declaration.name.text] = {
namedEntity: "Variable",
type: functionToType(item.declaration),
};
}
}
// now that structs and traits are added, add impls
for (const item of module.items) {
if (item.moduleItem === "Impl") {
const struct = ctx.environment[item.struct.name.text];
if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") {
throw Error("Impl for non-struct");
}
struct.impls.push({
trait: item.trait?.name.text ?? null,
functions: Object.fromEntries(
item.functions.map((fn) => {
return [fn.declaration.name, functionToType(fn.declaration)];
}),
),
});
}
}
// environment set up, actually recurse.
for (const item of module.items) {
if (item.moduleItem === "Function") {
// this.withFunction(item, typeSystem);
}
if (item.moduleItem === "Impl") {
// this.withImpl(item, typeSystem);
}
if (item.moduleItem === "StructTypeDeclaration") {
// this.withImpl(item, typeSystem);
}
if (item.moduleItem === "TraitTypeDeclaration") {
// this.withTrait(item, typeSystem);
}
}
};
}

View File

@@ -5,8 +5,8 @@ export const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`); throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`);
} }
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") { if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeA.name.name !== typeB.name.name) { if (typeA.name.text !== typeB.name.text) {
throw Error(`Mismatched types: ${typeA.name.name} ${typeB.name.name}`); throw Error(`Mismatched types: ${typeA.name.text} ${typeB.name.text}`);
} }
} }
if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") { if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") {
@@ -39,7 +39,7 @@ interface Comparison {
right: TypeUsage; right: TypeUsage;
} }
class TypeSystem { export class TypeSystem {
comparisons: Comparison[]; comparisons: Comparison[];
result: Record<string, TypeUsage>; result: Record<string, TypeUsage>;