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 { private checkBlock(block: ast.Block, expected: Ty): Ty {
this.checkStmts(block.stmts);
return block.expr && return block.expr &&
this.checkExpr(block.expr, expected) || this.checkExpr(block.expr, expected) ||
Ty({ tag: "null" }); Ty({ tag: "null" });
} }
private checkStmts(stmts: ast.Stmt[]) {
}
private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) { private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) {
if (this.stmtChecked.has(stmt.id)) { if (this.stmtChecked.has(stmt.id)) {
return; return;
@ -88,6 +84,78 @@ export class Checker {
exhausted(k); 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 { public fnItemTy(item: ast.Item, kind: ast.FnItem): Ty {
return this.itemTys.get(item.id) ?? this.checkFnItem(item, kind); return this.itemTys.get(item.id) ?? this.checkFnItem(item, kind);
} }

View File

@ -109,8 +109,9 @@ export class FnLowerer {
case "return": case "return":
case "break": case "break":
case "continue": case "continue":
case "assign":
return todo(k.tag); return todo(k.tag);
case "assign":
return this.lowerAssignStmt(stmt, k);
case "expr": { case "expr": {
const rval = this.lowerExpr(k.expr); const rval = this.lowerExpr(k.expr);
// ignore the fuck out of the value // ignore the fuck out of the value
@ -121,7 +122,7 @@ export class FnLowerer {
exhausted(k); 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); const val = kind.expr && this.lowerExpr(kind.expr);
this.allocatePat(kind.pat); this.allocatePat(kind.pat);
if (val) { if (val) {
@ -136,7 +137,9 @@ export class FnLowerer {
return; return;
case "bind": { case "bind": {
const ty = this.ch.patTy(pat); 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); this.reLocals.set(pat.id, local);
return; return;
} }
@ -166,28 +169,59 @@ export class FnLowerer {
exhausted(k); exhausted(k);
} }
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[] = []) { private lowerAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
const k = pat.kind; 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) { switch (k.tag) {
case "error": case "path": {
return; const re = this.re.exprRes(expr.id);
case "bind": { if (re.kind.tag !== "local") {
const patLocal = this.reLocals.get(pat.id)!; 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({ this.addStmt({
tag: "assign", tag: "assign",
place: { local: patLocal, proj: [] }, place: { local, proj: [] },
rval: { rval,
tag: "use",
operand: this.copyOrMoveLocal(
local,
this.locals.get(local)!.ty,
),
},
}); });
return; return;
} }
case "path": case "error":
return todo(); 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); exhausted(k);
} }
@ -454,7 +488,13 @@ export class FnLowerer {
private local(ty: Ty, ident?: ast.Ident): LocalId { private local(ty: Ty, ident?: ast.Ident): LocalId {
const id = this.localIds.nextThenStep(); 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; return id;
} }

View File

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

View File

@ -1004,7 +1004,7 @@ export class Parser {
return this.pat({ tag: "error" }, pos); return this.pat({ tag: "error" }, pos);
} }
const ident = this.parseIdent(); 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.report(`expected pattern, got '${this.current().type}'`, pos);
this.step(); this.step();

View File

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

View File

@ -61,7 +61,8 @@ export class MirFnStringifyer {
private localDef(local: Local): string { private localDef(local: Local): string {
const ident = local.ident && ` // ${local.ident.text}` || ""; 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 { private block(block: Block): string {