diff --git a/packages/boringlang/src/parse/ast.ts b/packages/boringlang/src/parse/ast.ts index 6fb721b..a8724ed 100644 --- a/packages/boringlang/src/parse/ast.ts +++ b/packages/boringlang/src/parse/ast.ts @@ -4,7 +4,7 @@ export interface Spanned { } export interface Identifier extends Spanned { - name: string; + text: string; } export interface LiteralInt { @@ -113,11 +113,7 @@ export interface AssignmentStatement { expression: Expression; } -export type Statement = - | ReturnStatement - | LetStatement - | AssignmentStatement - | Expression; +export type Statement = ReturnStatement | LetStatement | AssignmentStatement | Expression; export interface Block { statements: Statement[]; @@ -141,6 +137,14 @@ export interface Function { block: Block; } +export const functionToType = (fn: FunctionDeclaration): TypeUsage => { + return { + typeUsage: "FunctionTypeUsage", + arguments: fn.arguments.map((arg) => arg.type), + returnType: fn.returnType, + }; +}; + export interface StructTypeField { name: Identifier; type: TypeUsage; diff --git a/packages/boringlang/src/parse/semantics.ts b/packages/boringlang/src/parse/semantics.ts index e8e8598..79d4a13 100644 --- a/packages/boringlang/src/parse/semantics.ts +++ b/packages/boringlang/src/parse/semantics.ts @@ -50,7 +50,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", - name: { name: "i64", spanStart: 0, spanEnd: 0 }, + name: { text: "i64", spanStart: 0, spanEnd: 0 }, }, }; }, @@ -60,7 +60,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", - name: { name: "f64", spanStart: 0, spanEnd: 0 }, + name: { text: "f64", spanStart: 0, spanEnd: 0 }, }, }; }, @@ -70,7 +70,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", - name: { name: "bool", spanStart: 0, spanEnd: 0 }, + name: { text: "bool", spanStart: 0, spanEnd: 0 }, }, }; }, @@ -80,7 +80,7 @@ semantics.addOperation("toAST", { value: text.sourceString, type: { typeUsage: "NamedTypeUsage", - name: { name: "String", spanStart: 0, spanEnd: 0 }, + name: { text: "String", spanStart: 0, spanEnd: 0 }, }, }; }, @@ -100,7 +100,7 @@ semantics.addOperation("toAST", { }, identifier(_1, _2): Identifier { return { - name: this.sourceString, + text: this.sourceString, spanStart: this.source.startIdx, spanEnd: this.source.endIdx, }; @@ -197,10 +197,7 @@ semantics.addOperation("toAST", { statementType: "LetStatement", variableName: ident.toAST(), expression: expression.toAST(), - type: - tu.length > 0 - ? tu[0] - : { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, + type: tu.length > 0 ? tu[0] : { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }; }, AssignmentStatement(variable, _2, expression, _4): AssignmentStatement { @@ -248,15 +245,7 @@ semantics.addOperation("toAST", { type: typeUsage.toAST(), }; }, - FunctionDeclaration( - _1, - identifier, - _3, - args, - _4, - _5, - returnType, - ): FunctionDeclaration { + FunctionDeclaration(_1, identifier, _3, args, _4, _5, returnType): FunctionDeclaration { return { name: identifier.toAST(), arguments: args.asIteration().children.map((c) => c.toAST()), @@ -276,14 +265,7 @@ semantics.addOperation("toAST", { type: typeUsage.toAST(), }; }, - StructTypeDeclaration( - _1, - identifier, - _3, - _4, - fields, - _6, - ): StructTypeDeclaration { + StructTypeDeclaration(_1, identifier, _3, _4, fields, _6): StructTypeDeclaration { return { moduleItem: "StructTypeDeclaration", typeDeclaration: "StructTypeDeclaration", @@ -294,14 +276,7 @@ semantics.addOperation("toAST", { TraitMethod(declaration, _2): FunctionDeclaration { return declaration.toAST(); }, - TraitTypeDeclaration( - _1, - identifier, - _3, - _4, - methods, - _5, - ): TraitTypeDeclaration { + TraitTypeDeclaration(_1, identifier, _3, _4, methods, _5): TraitTypeDeclaration { return { moduleItem: "TraitTypeDeclaration", typeDeclaration: "TraitTypeDeclaration", diff --git a/packages/boringlang/src/types/type_checker.ts b/packages/boringlang/src/types/type_checker.ts index e69de29..de786ee 100644 --- a/packages/boringlang/src/types/type_checker.ts +++ b/packages/boringlang/src/types/type_checker.ts @@ -0,0 +1,110 @@ +import { functionToType, Module, TypeUsage } from "../parse/ast"; +import { TypeSystem } from "./type_system"; + +interface EnvImpl { + trait: string | null; + functions: Record; +} + +interface NamedType { + namedEntity: "NamedType"; + isA: "Scalar" | "Trait" | "Struct"; + fields: Record; + impls: EnvImpl[]; +} + +interface Variable { + namedEntity: "Variable"; + type: TypeUsage; +} + +type NamedEntity = NamedType | Variable; + +interface Context { + currentFunctionReturn: TypeUsage | null; + environment: Record; +} + +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); + } + } + }; +} diff --git a/packages/boringlang/src/types/type_system.ts b/packages/boringlang/src/types/type_system.ts index 8c160c8..cdf01af 100644 --- a/packages/boringlang/src/types/type_system.ts +++ b/packages/boringlang/src/types/type_system.ts @@ -5,8 +5,8 @@ export const compareTypes = (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.name.text !== typeB.name.text) { + throw Error(`Mismatched types: ${typeA.name.text} ${typeB.name.text}`); } } if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") { @@ -39,7 +39,7 @@ interface Comparison { right: TypeUsage; } -class TypeSystem { +export class TypeSystem { comparisons: Comparison[]; result: Record;