From 8f4ca01f77c65c4fd42e3c4a682f0dff990e9ef1 Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Mon, 10 Feb 2025 23:01:34 +0100 Subject: [PATCH] compiler: add assignment stuff --- slige/compiler/check/checker.ts | 76 ++++++++++++++++++++++++++-- slige/compiler/middle/ast_lower.ts | 80 ++++++++++++++++++++++-------- slige/compiler/middle/mir.ts | 1 + slige/compiler/parse/parser.ts | 2 +- slige/compiler/program.slg | 11 +--- slige/compiler/stringify/mir.ts | 3 +- 6 files changed, 138 insertions(+), 35 deletions(-) diff --git a/slige/compiler/check/checker.ts b/slige/compiler/check/checker.ts index bc26418..6882396 100644 --- a/slige/compiler/check/checker.ts +++ b/slige/compiler/check/checker.ts @@ -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); } diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts index 5d46b29..0cb7612 100644 --- a/slige/compiler/middle/ast_lower.ts +++ b/slige/compiler/middle/ast_lower.ts @@ -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; } diff --git a/slige/compiler/middle/mir.ts b/slige/compiler/middle/mir.ts index dceea9f..057f20c 100644 --- a/slige/compiler/middle/mir.ts +++ b/slige/compiler/middle/mir.ts @@ -17,6 +17,7 @@ export type LocalId = IdBase & { readonly _: unique symbol }; export type Local = { id: LocalId; ty: Ty; + mut: boolean; ident?: ast.Ident; }; diff --git a/slige/compiler/parse/parser.ts b/slige/compiler/parse/parser.ts index 5569d82..6bcc6df 100644 --- a/slige/compiler/parse/parser.ts +++ b/slige/compiler/parse/parser.ts @@ -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(); diff --git a/slige/compiler/program.slg b/slige/compiler/program.slg index 863a1a4..91607c7 100644 --- a/slige/compiler/program.slg +++ b/slige/compiler/program.slg @@ -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; } diff --git a/slige/compiler/stringify/mir.ts b/slige/compiler/stringify/mir.ts index 4396d98..48b7708 100644 --- a/slige/compiler/stringify/mir.ts +++ b/slige/compiler/stringify/mir.ts @@ -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 {