compiler: add assignment stuff

This commit is contained in:
SimonFJ20 2025-02-10 23:01:34 +01:00
parent 245ed06a3b
commit 8f4ca01f77
6 changed files with 138 additions and 35 deletions

View File

@ -32,15 +32,11 @@ export class Checker {
}
private checkBlock(block: ast.Block, expected: Ty): Ty {
this.checkStmts(block.stmts);
return block.expr &&
this.checkExpr(block.expr, expected) ||
Ty({ tag: "null" });
}
private checkStmts(stmts: ast.Stmt[]) {
}
private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) {
if (this.stmtChecked.has(stmt.id)) {
return;
@ -88,6 +84,78 @@ export class Checker {
exhausted(k);
}
public checkAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
if (this.stmtChecked.has(stmt.id)) {
return;
}
this.stmtChecked.add(stmt.id);
switch (kind.assignType) {
case "=": {
const valTy = this.exprTy(kind.value);
this.checkAssignableExpr(
kind.subject,
valTy,
kind.subject.span,
);
return;
}
case "+=":
case "-=": {
const re = this.re.exprRes(kind.subject.id);
if (re.kind.tag !== "local") {
this.report(
"cannot assign to expression",
kind.subject.span,
);
this.exprTys.set(kind.subject.id, Ty({ tag: "error" }));
return;
}
const patRe = this.re.patRes(re.kind.id);
const patTy = this.patTy(patRe.pat);
const tyRes = this.resolveTys(patTy, Ty({ tag: "int" }));
if (!tyRes.ok) {
this.report(
"cannot increment/decrement non-integer",
kind.subject.span,
);
this.exprTys.set(kind.subject.id, Ty({ tag: "error" }));
return;
}
const valTy = this.exprTy(kind.value);
const valTyRes = this.resolveTys(valTy, Ty({ tag: "int" }));
if (!valTyRes.ok) {
if (valTy.kind.tag !== "error") {
this.report(
"cannot increment/decrement with non-integer",
kind.value.span,
);
}
this.exprTys.set(kind.subject.id, Ty({ tag: "error" }));
return;
}
this.exprTys.set(kind.subject.id, valTyRes.val);
return;
}
}
exhausted(kind.assignType);
}
private checkAssignableExpr(expr: ast.Expr, ty: Ty, valSpan: Span) {
todo();
switch (ty.kind.tag) {
case "error":
this.exprTys.set(expr.id, ty);
return;
case "unknown":
this.exprTys.set(expr.id, ty);
return;
case "null":
case "int":
case "bool":
case "fn":
}
}
public fnItemTy(item: ast.Item, kind: ast.FnItem): Ty {
return this.itemTys.get(item.id) ?? this.checkFnItem(item, kind);
}

View File

@ -109,8 +109,9 @@ export class FnLowerer {
case "return":
case "break":
case "continue":
case "assign":
return todo(k.tag);
case "assign":
return this.lowerAssignStmt(stmt, k);
case "expr": {
const rval = this.lowerExpr(k.expr);
// ignore the fuck out of the value
@ -121,7 +122,7 @@ export class FnLowerer {
exhausted(k);
}
private lowerLetStmt(_stmt: ast.Stmt, kind: ast.LetStmt) {
private lowerLetStmt(stmt: ast.Stmt, kind: ast.LetStmt) {
const val = kind.expr && this.lowerExpr(kind.expr);
this.allocatePat(kind.pat);
if (val) {
@ -136,7 +137,9 @@ export class FnLowerer {
return;
case "bind": {
const ty = this.ch.patTy(pat);
const local = this.local(ty, k.ident);
const local = k.mut
? this.localMut(ty, k.ident)
: this.local(ty, k.ident);
this.reLocals.set(pat.id, local);
return;
}
@ -166,28 +169,59 @@ export class FnLowerer {
exhausted(k);
}
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[] = []) {
const k = pat.kind;
private lowerAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
this.ch.checkAssignStmt(stmt, kind);
const rval = this.lowerExpr(kind.value);
switch (kind.assignType) {
case "=":
return this.assignToExpr(kind.subject, rval, []);
case "+=":
case "-=":
todo();
}
}
private assignToExpr(expr: ast.Expr, rval: RVal, proj: ProjElem[]) {
const k = expr.kind;
switch (k.tag) {
case "error":
return;
case "bind": {
const patLocal = this.reLocals.get(pat.id)!;
case "path": {
const re = this.re.exprRes(expr.id);
if (re.kind.tag !== "local") {
throw new Error("cannot assign. checker should catch");
}
const patRe = this.re.patRes(re.kind.id);
const local = this.reLocals.get(patRe.pat.id)!;
this.addStmt({
tag: "assign",
place: { local: patLocal, proj: [] },
rval: {
tag: "use",
operand: this.copyOrMoveLocal(
local,
this.locals.get(local)!.ty,
),
},
place: { local, proj: [] },
rval,
});
return;
}
case "path":
return todo();
case "error":
case "null":
case "int":
case "bool":
case "str":
case "group":
case "array":
case "repeat":
case "struct":
case "ref":
case "deref":
case "elem":
case "field":
case "index":
case "call":
case "unary":
case "binary":
case "block":
case "if":
case "loop":
case "while":
case "for":
case "c_for":
throw new Error("not assignable. checker should catch");
}
exhausted(k);
}
@ -454,7 +488,13 @@ export class FnLowerer {
private local(ty: Ty, ident?: ast.Ident): LocalId {
const id = this.localIds.nextThenStep();
this.locals.set(id, { id, ty, ident });
this.locals.set(id, { id, ty, mut: false, ident });
return id;
}
private localMut(ty: Ty, ident?: ast.Ident): LocalId {
const id = this.localIds.nextThenStep();
this.locals.set(id, { id, ty, mut: true, ident });
return id;
}

View File

@ -17,6 +17,7 @@ export type LocalId = IdBase & { readonly _: unique symbol };
export type Local = {
id: LocalId;
ty: Ty;
mut: boolean;
ident?: ast.Ident;
};

View File

@ -1004,7 +1004,7 @@ export class Parser {
return this.pat({ tag: "error" }, pos);
}
const ident = this.parseIdent();
return this.pat({ tag: "bind", ident, mut: false }, pos);
return this.pat({ tag: "bind", ident, mut: true }, pos);
}
this.report(`expected pattern, got '${this.current().type}'`, pos);
this.step();

View File

@ -1,15 +1,8 @@
fn add(lhs: int, rhs: int) -> int {
lhs + rhs
}
fn main() {
let foo = 5;
let bar = 7 + foo;
let a = 5;
if foo == 5 {} else {}
let c = add(foo, bar);
a = 10;
}

View File

@ -61,7 +61,8 @@ export class MirFnStringifyer {
private localDef(local: Local): string {
const ident = local.ident && ` // ${local.ident.text}` || "";
return `${this.local(local.id)}: ${this.ty(local.ty)}${ident}`;
const mut = local.mut ? "mut " : "";
return `${mut}${this.local(local.id)}: ${this.ty(local.ty)}${ident}`;
}
private block(block: Block): string {