started on type checker
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user