2023-04-27 01:32:06 +01:00
|
|
|
import { CheckedExpr, CheckedType } from "./checked.ts";
|
2023-05-01 12:36:06 +01:00
|
|
|
import { ParsedExpr as AnyParsedExpr } from "./parsed.ts";
|
2023-04-27 13:48:08 +01:00
|
|
|
import { CompileError, Position } from "./token.ts";
|
2023-04-27 01:32:06 +01:00
|
|
|
|
2023-05-01 12:36:06 +01:00
|
|
|
type ParsedExpr<
|
|
|
|
Type extends AnyParsedExpr["exprType"] = AnyParsedExpr["exprType"],
|
|
|
|
> =
|
|
|
|
& AnyParsedExpr
|
|
|
|
& {
|
|
|
|
exprType: Type;
|
|
|
|
};
|
|
|
|
|
2023-04-27 01:32:06 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-01 12:36:06 +01:00
|
|
|
private searchTopLevelFn(statement: ParsedExpr<"fn">) {
|
2023-04-27 01:32:06 +01:00
|
|
|
}
|
|
|
|
|
2023-04-27 13:48:08 +01:00
|
|
|
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" };
|
|
|
|
case "id":
|
|
|
|
throw new Error("not implemented");
|
|
|
|
case "int":
|
2023-05-01 12:36:06 +01:00
|
|
|
return this.checkInt(expr);
|
2023-04-27 13:48:08 +01:00
|
|
|
case "if":
|
|
|
|
case "block":
|
|
|
|
case "call":
|
|
|
|
case "index":
|
|
|
|
case "increment":
|
|
|
|
case "decrement":
|
|
|
|
case "unary":
|
|
|
|
case "binary":
|
|
|
|
case "assign":
|
|
|
|
default:
|
|
|
|
return this.errorExpr(
|
|
|
|
`expected expression, got '${expr.exprType}' statement`,
|
|
|
|
expr.pos,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-01 12:36:06 +01:00
|
|
|
private checkInt(expr: ParsedExpr<"int">): CheckedExpr {
|
|
|
|
return {
|
|
|
|
pos: expr.pos,
|
|
|
|
exprType: "int",
|
|
|
|
value: expr.value,
|
|
|
|
valueType: { pos: expr.pos, typeType: "i16" },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-04-27 13:48:08 +01:00
|
|
|
private errorExpr(message: string, pos: Position): CheckedExpr {
|
|
|
|
this.errors.push({ message, pos });
|
|
|
|
return {
|
|
|
|
pos,
|
|
|
|
exprType: "error",
|
|
|
|
message,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|