Compare commits

..

No commits in common. "c8aa3e7c1c1c1fba01a03425a1920ce85569dd95" and "4d7998eb9f642178396853078645ac62c0ade572" have entirely different histories.

5 changed files with 75 additions and 80 deletions

View File

@ -2,37 +2,32 @@ import * as ast from "../ast.ts";
import { FileReporter, Loc } from "../diagnostics.ts"; import { FileReporter, Loc } from "../diagnostics.ts";
import { Ty } from "../ty.ts"; import { Ty } from "../ty.ts";
import { builtins } from "./builtins.ts"; import { builtins } from "./builtins.ts";
import { Syms } from "./resolve.ts"; import { ResolveMap } from "./resolve.ts";
export class Tys { // export class Tys {
// private nodeTys = new Map<number, Ty>();
//
// expr(expr: ast.Node): Ty {}
// }
export class Checker {
private nodeTys = new Map<number, Ty>(); private nodeTys = new Map<number, Ty>();
private checker: Checker;
constructor( constructor(
private syms: Syms, private resols: ResolveMap,
private reporter: FileReporter,
) {
this.checker = new Checker(this, this.syms, this.reporter);
}
expr(node: ast.Node): Ty {
if (this.nodeTys.has(node.id)) {
return this.nodeTys.get(node.id)!;
}
const ty = this.checker.checkExpr(node);
this.nodeTys.set(node.id, ty);
return ty;
}
}
class Checker {
constructor(
private tys: Tys,
private syms: Syms,
private reporter: FileReporter, private reporter: FileReporter,
) {} ) {}
checkExpr(node: ast.Node): Ty { check(node: ast.Node): Ty {
if (this.nodeTys.has(node.id)) {
return this.nodeTys.get(node.id)!;
}
const ty = this.checkNode(node);
this.nodeTys.set(node.id, ty);
return ty;
}
private checkNode(node: ast.Node): Ty {
const k = node.kind; const k = node.kind;
if (node.is("FnStmt")) { if (node.is("FnStmt")) {
@ -40,12 +35,12 @@ class Checker {
} }
if (node.is("Param")) { if (node.is("Param")) {
const sym = this.syms.get(node); const sym = this.resols.get(node);
if (sym.tag === "Let") { if (sym.tag === "Let") {
const exprTy = this.tys.expr(sym.stmt.kind.expr); const exprTy = this.check(sym.stmt.kind.expr);
if (node.kind.ty) { if (node.kind.ty) {
const explicitTy = this.tys.expr(node.kind.ty); const explicitTy = this.check(node.kind.ty);
this.assertCompatible( this.assertCompatible(
exprTy, exprTy,
explicitTy, explicitTy,
@ -59,16 +54,16 @@ class Checker {
this.error(node.loc, `parameter must have a type`); this.error(node.loc, `parameter must have a type`);
this.fail(); this.fail();
} }
return this.tys.expr(node.kind.ty); return this.check(node.kind.ty);
} }
throw new Error(`'${sym.tag}' not handled`); throw new Error(`'${sym.tag}' not handled`);
} }
if (node.is("IdentExpr")) { if (node.is("IdentExpr")) {
const sym = this.syms.get(node); const sym = this.resols.get(node);
if (sym.tag === "Fn") { if (sym.tag === "Fn") {
return this.tys.expr(sym.stmt); return this.check(sym.stmt);
} }
if (sym.tag === "Bool") { if (sym.tag === "Bool") {
return Ty.Bool; return Ty.Bool;
@ -77,10 +72,10 @@ class Checker {
return builtins.find((s) => s.id === sym.id)!.ty; return builtins.find((s) => s.id === sym.id)!.ty;
} }
if (sym.tag === "FnParam") { if (sym.tag === "FnParam") {
return this.tys.expr(sym.param); return this.check(sym.param);
} }
if (sym.tag === "Let") { if (sym.tag === "Let") {
return this.tys.expr(sym.param); return this.check(sym.param);
} }
throw new Error(`'${sym.tag}' not handled`); throw new Error(`'${sym.tag}' not handled`);
} }
@ -92,7 +87,7 @@ class Checker {
if (node.is("ArrayExpr")) { if (node.is("ArrayExpr")) {
let ty: Ty | null = null; let ty: Ty | null = null;
for (const value of node.kind.values) { for (const value of node.kind.values) {
const valueTy = this.tys.expr(value); const valueTy = this.check(value);
if (ty) { if (ty) {
this.assertCompatible(ty, valueTy, value.loc); this.assertCompatible(ty, valueTy, value.loc);
} else { } else {
@ -108,8 +103,8 @@ class Checker {
} }
if (node.is("IndexExpr")) { if (node.is("IndexExpr")) {
const exprTy = this.tys.expr(node.kind.expr); const exprTy = this.check(node.kind.expr);
const argTy = this.tys.expr(node.kind.arg); const argTy = this.check(node.kind.arg);
if ( if (
(exprTy.is("Array") || exprTy.is("Slice")) && (exprTy.is("Array") || exprTy.is("Slice")) &&
argTy.compatibleWith(Ty.Int) argTy.compatibleWith(Ty.Int)
@ -134,7 +129,7 @@ class Checker {
} }
if (node.is("UnaryExpr")) { if (node.is("UnaryExpr")) {
const exprTy = this.tys.expr(node.kind.expr); const exprTy = this.check(node.kind.expr);
if (node.kind.op === "Negate" && exprTy.compatibleWith(Ty.Int)) { if (node.kind.op === "Negate" && exprTy.compatibleWith(Ty.Int)) {
return Ty.Int; return Ty.Int;
} }
@ -160,8 +155,8 @@ class Checker {
} }
if (node.is("BinaryExpr")) { if (node.is("BinaryExpr")) {
const left = this.tys.expr(node.kind.left); const left = this.check(node.kind.left);
const right = this.tys.expr(node.kind.right); const right = this.check(node.kind.right);
const binaryOp = binaryOpPatterns const binaryOp = binaryOpPatterns
.find((pat) => .find((pat) =>
pat.op === node.kind.op && pat.op === node.kind.op &&
@ -180,7 +175,7 @@ class Checker {
if (node.is("RangeExpr")) { if (node.is("RangeExpr")) {
for (const operandExpr of [node.kind.begin, node.kind.end]) { for (const operandExpr of [node.kind.begin, node.kind.end]) {
const operandTy = operandExpr && this.tys.expr(operandExpr); const operandTy = operandExpr && this.check(operandExpr);
if (operandTy && !operandTy.compatibleWith(Ty.Int)) { if (operandTy && !operandTy.compatibleWith(Ty.Int)) {
this.error( this.error(
operandExpr.loc, operandExpr.loc,
@ -206,17 +201,17 @@ class Checker {
} }
if (node.is("PtrTy")) { if (node.is("PtrTy")) {
const ty = this.tys.expr(node.kind.ty); const ty = this.check(node.kind.ty);
return Ty.create("Ptr", { ty }); return Ty.create("Ptr", { ty });
} }
if (node.is("PtrMutTy")) { if (node.is("PtrMutTy")) {
const ty = this.tys.expr(node.kind.ty); const ty = this.check(node.kind.ty);
return Ty.create("PtrMut", { ty }); return Ty.create("PtrMut", { ty });
} }
if (node.is("ArrayTy")) { if (node.is("ArrayTy")) {
const ty = this.tys.expr(node.kind.ty); const ty = this.check(node.kind.ty);
const lengthTy = this.tys.expr(node.kind.length); const lengthTy = this.check(node.kind.length);
if (!lengthTy.compatibleWith(Ty.Int)) { if (!lengthTy.compatibleWith(Ty.Int)) {
this.error( this.error(
node.kind.length.loc, node.kind.length.loc,
@ -236,7 +231,7 @@ class Checker {
} }
if (node.is("SliceTy")) { if (node.is("SliceTy")) {
const ty = this.tys.expr(node.kind.ty); const ty = this.check(node.kind.ty);
return Ty.create("Slice", { ty }); return Ty.create("Slice", { ty });
} }
@ -246,14 +241,14 @@ class Checker {
private checkFnStmt(stmt: ast.NodeWithKind<"FnStmt">): Ty { private checkFnStmt(stmt: ast.NodeWithKind<"FnStmt">): Ty {
const k = stmt.kind; const k = stmt.kind;
const params = k.params.map((param) => this.tys.expr(param)); const params = k.params.map((param) => this.check(param));
const retTy = k.retTy ? this.tys.expr(k.retTy) : Ty.Void; const retTy = k.retTy ? this.check(k.retTy) : Ty.Void;
k.body.visit({ k.body.visit({
visit: (node) => { visit: (node) => {
if (node.is("ReturnStmt")) { if (node.is("ReturnStmt")) {
const ty = node.kind.expr const ty = node.kind.expr
? this.tys.expr(node.kind.expr) ? this.check(node.kind.expr)
: Ty.Void; : Ty.Void;
if (!ty.compatibleWith(retTy)) { if (!ty.compatibleWith(retTy)) {
this.error( this.error(
@ -275,7 +270,7 @@ class Checker {
} }
private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty { private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty {
const calleeTy = this.tys.expr(node.kind.expr); const calleeTy = this.check(node.kind.expr);
const callableTy = calleeTy.is("Fn") const callableTy = calleeTy.is("Fn")
? calleeTy ? calleeTy
@ -292,7 +287,7 @@ class Checker {
} }
const args = node.kind.args const args = node.kind.args
.map((arg) => this.tys.expr(arg)); .map((arg) => this.check(arg));
const params = callableTy.kind.params; const params = callableTy.kind.params;
if (args.length !== params.length) { if (args.length !== params.length) {
this.error( this.error(

View File

@ -2,7 +2,7 @@ import * as ast from "../ast.ts";
import { FileReporter } from "../diagnostics.ts"; import { FileReporter } from "../diagnostics.ts";
import { builtins } from "./builtins.ts"; import { builtins } from "./builtins.ts";
export class Syms { export class ResolveMap {
constructor( constructor(
private resols: Map<number, Sym>, private resols: Map<number, Sym>,
) {} ) {}
@ -35,7 +35,7 @@ export type Sym =
export function resolve( export function resolve(
file: ast.Node, file: ast.Node,
reporter: FileReporter, reporter: FileReporter,
): Syms { ): ResolveMap {
let syms = ResolverSyms.root(); let syms = ResolverSyms.root();
const resols = new Map<number, Sym>(); const resols = new Map<number, Sym>();
@ -88,7 +88,7 @@ export function resolve(
}, },
}); });
return new Syms(resols); return new ResolveMap(resols);
} }
class ResolverSyms { class ResolverSyms {

View File

@ -12,8 +12,8 @@ const text = await Deno.readTextFile(filename);
const fileRep = reporter.ofFile({ filename, text }); const fileRep = reporter.ofFile({ filename, text });
const fileAst = front.parse(text, fileRep); const fileAst = front.parse(text, fileRep);
const syms = front.resolve(fileAst, fileRep); const resols = front.resolve(fileAst, fileRep);
const tys = new front.Tys(syms, fileRep); const checker = new front.Checker(resols, fileRep);
let mainFn: ast.NodeWithKind<"FnStmt"> | null = null; let mainFn: ast.NodeWithKind<"FnStmt"> | null = null;
@ -36,7 +36,7 @@ if (!mainFn) {
Deno.exit(1); Deno.exit(1);
} }
const m = new middle.MiddleLowerer(syms, tys); const m = new middle.MiddleLowerer(resols, checker);
const mainMiddleFn = m.lowerFn(mainFn); const mainMiddleFn = m.lowerFn(mainFn);
if (!Deno.args.includes("--test")) { if (!Deno.args.includes("--test")) {

View File

@ -1,5 +1,5 @@
import * as ast from "./ast.ts"; import * as ast from "./ast.ts";
import { Syms, Tys } from "./front/mod.ts"; import { Checker, ResolveMap } from "./front/mod.ts";
import { Ty } from "./ty.ts"; import { Ty } from "./ty.ts";
import { BasicBlock, BinaryOp, Fn, Inst, InstKind } from "./mir.ts"; import { BasicBlock, BinaryOp, Fn, Inst, InstKind } from "./mir.ts";
@ -7,15 +7,15 @@ export class MiddleLowerer {
private fns = new Map<number, Fn>(); private fns = new Map<number, Fn>();
constructor( constructor(
private resols: Syms, private resols: ResolveMap,
private tys: Tys, private checker: Checker,
) {} ) {}
lowerFn(stmt: ast.FnStmt): Fn { lowerFn(stmt: ast.FnStmt): Fn {
if (this.fns.has(stmt.id)) { if (this.fns.has(stmt.id)) {
return this.fns.get(stmt.id)!; return this.fns.get(stmt.id)!;
} }
const fn = new FnLowerer(this, this.resols, this.tys, stmt).lower(); const fn = new FnLowerer(this, this.resols, this.checker, stmt).lower();
this.fns.set(stmt.id, fn); this.fns.set(stmt.id, fn);
return fn; return fn;
} }
@ -28,13 +28,13 @@ class FnLowerer {
constructor( constructor(
private lowerer: MiddleLowerer, private lowerer: MiddleLowerer,
private resols: Syms, private resols: ResolveMap,
private tys: Tys, private checker: Checker,
private stmt: ast.FnStmt, private stmt: ast.FnStmt,
) {} ) {}
lower(): Fn { lower(): Fn {
const ty = this.tys.expr(this.stmt); const ty = this.checker.check(this.stmt);
this.lowerBlock(this.stmt.kind.body.as("Block")); this.lowerBlock(this.stmt.kind.body.as("Block"));
this.pushInst(Ty.Void, "Return", { source: this.makeVoid() }); this.pushInst(Ty.Void, "Return", { source: this.makeVoid() });
this.bbs[0].insts.unshift(...this.allocs); this.bbs[0].insts.unshift(...this.allocs);
@ -49,7 +49,7 @@ class FnLowerer {
private lowerStmt(stmt: ast.Node) { private lowerStmt(stmt: ast.Node) {
if (stmt.is("LetStmt")) { if (stmt.is("LetStmt")) {
const ty = this.tys.expr(stmt.kind.param); const ty = this.checker.check(stmt.kind.param);
const expr = this.lowerExpr(stmt.kind.expr); const expr = this.lowerExpr(stmt.kind.expr);
const local = new Inst(Ty.create("Ptr", { ty }), { tag: "Alloca" }); const local = new Inst(Ty.create("Ptr", { ty }), { tag: "Alloca" });
this.allocs.push(local); this.allocs.push(local);
@ -136,12 +136,12 @@ class FnLowerer {
} }
if (place.is("IndexExpr")) { if (place.is("IndexExpr")) {
const exprTy = this.tys.expr(place.kind.expr); const exprTy = this.checker.check(place.kind.expr);
if (!exprTy.is("Array") && !exprTy.is("Slice")) { if (!exprTy.is("Array") && !exprTy.is("Slice")) {
throw new Error(exprTy.pretty()); throw new Error(exprTy.pretty());
} }
const arg = place.kind.arg; const arg = place.kind.arg;
const argTy = this.tys.expr(arg); const argTy = this.checker.check(arg);
const exprInst = this.lowerExpr(place.kind.expr); const exprInst = this.lowerExpr(place.kind.expr);
if (argTy.is("Int")) { if (argTy.is("Int")) {
const argInst = this.lowerExpr(arg); const argInst = this.lowerExpr(arg);
@ -183,7 +183,7 @@ class FnLowerer {
return this.pushInst(fn.ty, "Fn", { fn }); return this.pushInst(fn.ty, "Fn", { fn });
} }
if (sym.tag === "FnParam") { if (sym.tag === "FnParam") {
const ty = this.tys.expr(sym.param); const ty = this.checker.check(sym.param);
return this.pushInst(ty, "Param", { idx: sym.idx }); return this.pushInst(ty, "Param", { idx: sym.idx });
} }
if (sym.tag === "Builtin") { if (sym.tag === "Builtin") {
@ -205,19 +205,19 @@ class FnLowerer {
return this.pushInst(Ty.Int, "Int", { value: expr.kind.value }); return this.pushInst(Ty.Int, "Int", { value: expr.kind.value });
} }
if (expr.is("ArrayExpr")) { if (expr.is("ArrayExpr")) {
const ty = this.tys.expr(expr); const ty = this.checker.check(expr);
const values = expr.kind.values const values = expr.kind.values
.map((value) => this.lowerExpr(value)); .map((value) => this.lowerExpr(value));
return this.pushInst(ty, "Array", { values }); return this.pushInst(ty, "Array", { values });
} }
if (expr.is("IndexExpr")) { if (expr.is("IndexExpr")) {
const ty = this.tys.expr(expr.kind.expr); const ty = this.checker.check(expr.kind.expr);
const arg = this.lowerExpr(expr.kind.arg); const arg = this.lowerExpr(expr.kind.arg);
const value = this.lowerExpr(expr.kind.expr); const value = this.lowerExpr(expr.kind.expr);
return this.pushInst(ty, "Index", { value, arg }); return this.pushInst(ty, "Index", { value, arg });
} }
if (expr.is("CallExpr")) { if (expr.is("CallExpr")) {
const ty = this.tys.expr(expr); const ty = this.checker.check(expr);
const args = expr.kind.args const args = expr.kind.args
.map((arg) => this.lowerExpr(arg)); .map((arg) => this.lowerExpr(arg));
@ -239,8 +239,8 @@ class FnLowerer {
return this.pushInst(ty, "Call", { callee, args }); return this.pushInst(ty, "Call", { callee, args });
} }
if (expr.is("UnaryExpr")) { if (expr.is("UnaryExpr")) {
const resultTy = this.tys.expr(expr); const resultTy = this.checker.check(expr);
const operandTy = this.tys.expr(expr.kind.expr); const operandTy = this.checker.check(expr.kind.expr);
if ( if (
expr.kind.op === "Negate" && expr.kind.op === "Negate" &&
operandTy.compatibleWith(Ty.Int) && operandTy.compatibleWith(Ty.Int) &&
@ -273,7 +273,7 @@ class FnLowerer {
); );
} }
if (place.is("IndexExpr")) { if (place.is("IndexExpr")) {
const placeTy = this.tys.expr(place); const placeTy = this.checker.check(place);
const placeInst = this.lowerPlace(place); const placeInst = this.lowerPlace(place);
if (placeTy.is("Slice")) { if (placeTy.is("Slice")) {
return placeInst; return placeInst;
@ -295,9 +295,9 @@ class FnLowerer {
); );
} }
if (expr.is("BinaryExpr")) { if (expr.is("BinaryExpr")) {
const resultTy = this.tys.expr(expr); const resultTy = this.checker.check(expr);
const leftTy = this.tys.expr(expr.kind.left); const leftTy = this.checker.check(expr.kind.left);
const rightTy = this.tys.expr(expr.kind.right); const rightTy = this.checker.check(expr.kind.right);
const binaryOp = binaryOpPatterns const binaryOp = binaryOpPatterns
.find((pat) => .find((pat) =>
expr.kind.op === pat.op && expr.kind.op === pat.op &&

View File

@ -1,12 +1,12 @@
fn main() fn main()
{ {
let array: [int; 3] = [1, 2, 3]; let array: [int; 3] = [1, 2, false];
let a = 4; // let a = 4;
let b = a; // let b = a;
array[0] = a; // array[0] = a;
print_int(array[0]); // print_int(array[0]);
// let elem: int = array[0]; // let elem: int = array[0];
// // e xpect: 1 // // e xpect: 1