import { CheckedExpr, CheckedType } from "./checked.ts"; import { ParsedExpr as AnyParsedExpr } from "./parsed.ts"; import { CompileError, Position } from "./token.ts"; type ParsedExpr< Type extends AnyParsedExpr["exprType"] = AnyParsedExpr["exprType"], > = & AnyParsedExpr & { exprType: Type; }; export type SymbolValue = { id: number; subject: string; valueType: CheckedType; }; export class SymbolTable { private idCounter = 0; private symbols: { [key: string]: SymbolValue } = {}; public define(subject: string, valueType: CheckedType) { if (subject in this.symbols) { throw new Error("redefinition not implemented"); } const id = this.idCounter; this.idCounter++; this.symbols[subject] = { id, subject, valueType }; } } export class Checker { private symbolTable = new SymbolTable(); public errors: CompileError[] = []; public check(statements: ParsedExpr[]): CheckedExpr[] { return statements as CheckedExpr[]; } private searchTopLevelStatements(statements: ParsedExpr[]) { for (const statement of statements) { switch (statement.exprType) { case "fn": this.searchTopLevelFn(statement); break; case "let": break; default: this.errors.push({ pos: statement.pos, message: `statement '${statement.exprType}' not supported in top level`, }); break; } } } private searchTopLevelFn(statement: ParsedExpr<"fn">) { } private checkStatement(expr: ParsedExpr): CheckedExpr { switch (expr.exprType) { case "if": return this.checkIfStatement(expr); case "let": return this.checkLetStatement(expr); case "fn": return this.checkFnStatement(expr); case "return": return this.checkReturnStatement(expr); case "loop": return this.checkLoopStatement(expr); case "while": return this.checkWhileStatement(expr); case "break": return this.checkBreakStatement(expr); case "continue": return this.checkContinueStatement(expr); default: return this.checkExpr(expr); } } private checkIfStatement(expr: ParsedExpr<"if">): CheckedExpr { const condition = this.checkExpr(expr.condition); const truthy = this.checkExpr(expr.truthy); const falsy = expr.falsy ? this.checkExpr(expr.falsy) : null; return { pos: expr.pos, exprType: "if", condition, truthy, falsy, valueType: { pos: expr.pos, typeType: "unit" }, }; } private checkLetStatement(expr: ParsedExpr<"let">): CheckedExpr { throw new Error("not implemented"); } private checkFnStatement(expr: ParsedExpr<"fn">): CheckedExpr { throw new Error("not implemented"); } private checkReturnStatement(expr: ParsedExpr<"return">): CheckedExpr { throw new Error("not implemented"); } private checkLoopStatement(expr: ParsedExpr<"loop">): CheckedExpr { const body = this.checkExpr(expr.body); return { pos: expr.pos, exprType: "loop", body, valueType: { pos: expr.pos, typeType: "unit" }, }; } private checkWhileStatement(expr: ParsedExpr<"while">): CheckedExpr { const condition = this.checkExpr(expr.condition); const body = this.checkExpr(expr.body); return { pos: expr.pos, exprType: "while", condition, body, valueType: { pos: expr.pos, typeType: "unit" }, }; } private checkBreakStatement(expr: ParsedExpr<"break">): CheckedExpr { return { ...expr, valueType: { pos: expr.pos, typeType: "unit" } }; } private checkContinueStatement(expr: ParsedExpr<"continue">): CheckedExpr { return { ...expr, valueType: { pos: expr.pos, typeType: "unit" } }; } private checkExpr(expr: ParsedExpr): CheckedExpr { switch (expr.exprType) { case "error": return this.errorExpr(expr.message, expr.pos); case "unit": return { pos: expr.pos, exprType: "unit", valueType: { pos: expr.pos, typeType: "unit" }, }; case "id": return this.checkId(expr); case "int": return this.checkInt(expr); case "if": return this.checkIf(expr); case "block": return this.checkBlock(expr); case "call": return this.checkCall(expr); case "index": return this.checkIndex(expr); case "increment": return this.checkIncrement(expr as ParsedExpr<"increment">); case "decrement": return this.checkDecrement(expr as ParsedExpr<"decrement">); case "unary": return this.checkUnary(expr); case "binary": return this.checkBinary(expr); case "assign": return this.checkAssign(expr); default: return this.errorExpr( `expected expression, got '${expr.exprType}' statement`, expr.pos, ); } } private checkId(_expr: ParsedExpr<"id">): CheckedExpr { throw new Error("not implemented"); } private checkInt(expr: ParsedExpr<"int">): CheckedExpr { if (expr.value >= 1 << 15) { return this.errorExpr("16-bit literal too large", expr.pos); } return { pos: expr.pos, exprType: "int", value: expr.value, valueType: { pos: expr.pos, typeType: "i16" }, }; } private checkIf(expr: ParsedExpr<"if">): CheckedExpr { const condition = this.checkExpr(expr.condition); const truthy = this.checkExpr(expr.truthy); if (!expr.falsy) { this.errors.push({ pos: expr.pos, message: "expected else-block in if expression", }); return { pos: expr.pos, exprType: "if", condition, truthy, falsy: null, valueType: { pos: expr.pos, typeType: "unit" }, }; } const falsy = this.checkExpr(expr.falsy); const valueType = this.compatibleType( truthy.valueType, falsy.valueType, ); return { pos: expr.pos, exprType: "if", condition, truthy, falsy, valueType, }; } private checkBlock(expr: ParsedExpr<"block">): CheckedExpr { const body = expr.body.map((statement) => this.checkStatement(statement) ); if (expr.value) { const value = this.checkExpr(expr.value); return { pos: expr.pos, exprType: "block", body, value, valueType: value.valueType, }; } else { return { pos: expr.pos, exprType: "block", body, value: null, valueType: { pos: expr.pos, typeType: "unit" }, }; } } private checkCall(expr: ParsedExpr<"call">): CheckedExpr { throw new Error("not implemented"); } private checkIndex(expr: ParsedExpr<"index">): CheckedExpr { throw new Error("not implemented"); } private checkIncrement(expr: ParsedExpr<"increment">): CheckedExpr { throw new Error("not implemented"); } private checkDecrement(expr: ParsedExpr<"decrement">): CheckedExpr { throw new Error("not implemented"); } private checkUnary(expr: ParsedExpr<"unary">): CheckedExpr { throw new Error("not implemented"); } private checkBinary(expr: ParsedExpr<"binary">): CheckedExpr { throw new Error("not implemented"); } private checkAssign(expr: ParsedExpr<"assign">): CheckedExpr { throw new Error("not implemented"); } private compatibleType( a: CheckedType, b: CheckedType, reversed = false, ): CheckedType { if (a.typeType === "error") { return a; } else if (b.typeType === "error") { return b; } else if (a.typeType === "unit" && b.typeType === "unit") { return a; } else if (a.typeType === "u16" && b.typeType === "u16") { return a; } else if (a.typeType === "u16" && b.typeType === "i16") { return a; } else if (!reversed) { return this.compatibleType(b, a, true); } else { throw new Error("not implemented"); } } private errorExpr(message: string, pos: Position): CheckedExpr { this.errors.push({ message, pos }); return { pos, exprType: "error", valueType: { pos, typeType: "unit" }, message, }; } }