From 0bf9aed35ec2c70f9739502d2f0361a2a6bf673a Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 30 Jul 2023 20:44:59 +0200 Subject: [PATCH] Add assignments --- src/ast.ts | 19 +++++++++++++++++++ src/index.ts | 32 +++++++------------------------- src/lower.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/parser.ts | 24 +++++++++++++++++------- src/printer.ts | 3 +++ src/typeck.ts | 28 ++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 32 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index 5510fb9..1a7ca3f 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -72,9 +72,18 @@ export type ExprLet = { type?: Type; rhs: Expr; // IMPORTANT: This is (sadly) shared with ExprBlock. + // TODO: Stop this sharing and just store the stack of blocks in typeck. local?: LocalInfo; }; +// A bit like ExprBinary except there are restrictions +// on the LHS and precedence is unrestricted. +export type ExprAssign = { + kind: "assign"; + lhs: Expr; + rhs: Expr; +}; + export type ExprBlock = { kind: "block"; exprs: Expr[]; @@ -128,6 +137,7 @@ export type ExprBreak = { /** * The break target block. * May be any control flow block, labelled from inside out. + * TODO: This is not a good solution at all and pretty broken. */ target?: number; }; @@ -141,6 +151,7 @@ export type ExprStructLiteral = { export type ExprKind = | ExprEmpty | ExprLet + | ExprAssign | ExprBlock | ExprLiteral | ExprIdent @@ -482,6 +493,14 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr { rhs: folder.expr(expr.rhs), }; } + case "assign": { + return { + ...expr, + kind: "assign", + lhs: folder.expr(expr.lhs), + rhs: folder.expr(expr.rhs), + }; + } case "block": { return { ...expr, diff --git a/src/index.ts b/src/index.ts index 760f0d8..393bdea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,32 +10,14 @@ import fs from "fs"; import { exec } from "child_process"; const input = ` -import("wasi_snapshot_preview1" "fd_write") - fd_write(a: I32, b: I32, c: I32, d: I32): I32; - -function coolerPrint(a: String) = ( - let ptr = __string_ptr(a); - let len = __string_len(a); - - let mem = 1024_I32; - - __i32_store(mem + 4_I32, ptr); - __i32_store(mem + 8_I32, 2_I32); - - fd_write( - // stdout - 1_I32, - // iovec - mem + 4_I32, - // len - len, - // return value - mem, - ); -); - function main() = ( - coolerPrint("uwu\\n"); + let i = 0; + loop ( + if i > 10 then break; + + print("uwu\\n"); + i = i + 1; + ); ); `; diff --git a/src/lower.ts b/src/lower.ts index 7cc89be..bea44e4 100644 --- a/src/lower.ts +++ b/src/lower.ts @@ -272,6 +272,36 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { break; } + case "assign": { + lowerExpr(fcx, instrs, expr.rhs); + const { lhs } = expr; + switch (lhs.kind) { + case "ident": { + const res = lhs.value.res!; + + switch (res.kind) { + case "local": { + const location = + fcx.varLocations[fcx.varLocations.length - 1 - res.index]; + storeVariable(instrs, location); + break; + } + case "item": { + throw new Error("cannot store to item"); + } + case "builtin": { + throw new Error("cannot store to builtin"); + } + } + break; + } + default: { + throw new Error("invalid lhs side of assignment"); + } + } + + break; + } case "block": { const prevVarLocationLengths = fcx.varLocations.length; @@ -610,6 +640,15 @@ function loadVariable(instrs: wasm.Instr[], loc: VarLocation) { }); } +function storeVariable(instrs: wasm.Instr[], loc: VarLocation) { + // Stores are just like loads, just the other way around. + const types = loc.types.map((_, i) => i); + types.reverse(); + types.forEach((i) => { + instrs.push({ kind: "local.set", imm: loc.localIdx + i }); + }); +} + function computeAbi(ty: TyFn): FnAbi { function argRetAbi(param: Ty): ArgRetAbi { switch (param.kind) { diff --git a/src/parser.ts b/src/parser.ts index e32f8d0..a2e11d3 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -174,13 +174,15 @@ function parseFunctionSig(t: Token[]): [Token[], FunctionSig] { function parseExpr(t: Token[]): [Token[], Expr] { /* - EXPR = COMPARISON + EXPR = ASSIGNMENT LET = "let" NAME { ":" TYPE } "=" EXPR "in" EXPR IF = "if" EXPR "then" EXPR { "else" EXPR } LOOP = "loop" EXPR BREAK = "break" + ASSIGNMENT = COMPARISON { "=" ASSIGNMENT } + // The precende here is pretty arbitrary since we forbid mixing of operators // with different precedence classes anyways. COMPARISON = LOGICAL { ( ">" | "<" | "==" | "<=" | ">=" | "!=" ) COMPARISON } @@ -199,12 +201,17 @@ function parseExpr(t: Token[]): [Token[], Expr] { STRUCT_INIT = "{" { NAME ":" EXPR } { "," NAME ":" EXPR } { "," } "}" EXPR_LIST = { EXPR { "," EXPR } { "," } } */ - return parseExprComparison(t); + return parseExprAssignment(t); +} + +function mkBinaryExpr(lhs: Expr, rhs: Expr, span: Span, kind: string): Expr { + return { kind: "binary", binaryKind: kind as BinaryKind, lhs, rhs, span }; } function mkParserExprBinary( lower: Parser, - kinds: string[] + kinds: string[], + mkExpr = mkBinaryExpr ): Parser { function parser(t: Token[]): [Token[], Expr] { let lhs; @@ -216,10 +223,7 @@ function mkParserExprBinary( let rhs; [t, rhs] = parser(t); const span = spanMerge(lhs.span, rhs.span); - return [ - t, - { kind: "binary", binaryKind: peek.kind as BinaryKind, lhs, rhs, span }, - ]; + return [t, mkExpr(lhs, rhs, span, peek.kind)]; } return [t, lhs]; @@ -245,6 +249,12 @@ const parseExprComparison = mkParserExprBinary( COMPARISON_KINDS ); +const parseExprAssignment = mkParserExprBinary( + parseExprComparison, + ["="], + (lhs, rhs, span) => ({ kind: "assign", lhs, rhs, span }) +); + function parseExprUnary(t: Token[]): [Token[], Expr] { const [, peak] = next(t); if (peak.kind in UNARY_KINDS) { diff --git a/src/printer.ts b/src/printer.ts index 6256617..001ec1c 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -78,6 +78,9 @@ function printExpr(expr: Expr, indent: number): string { indent + 1 )}`; } + case "assign": { + return `${printExpr(expr.lhs, indent)} = ${printExpr(expr.rhs, indent)}`; + } case "block": { const exprs = expr.exprs.map((expr) => printExpr(expr, indent + 1)); diff --git a/src/typeck.ts b/src/typeck.ts index ac8872f..ff8086e 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -557,6 +557,34 @@ export function checkBody( span: expr.span, }; } + case "assign": { + const lhs = this.expr(expr.lhs); + const rhs = this.expr(expr.rhs); + + infcx.assign(lhs.ty!, rhs.ty!, expr.span); + + switch (lhs.kind) { + case "ident": + if (lhs.value.res!.kind !== "local") { + throw new CompilerError("cannot assign to items", expr.span); + } + break; + default: { + throw new CompilerError( + "invalid left-hand side of assignment", + lhs.span + ); + } + } + + return { + ...expr, + kind: "assign", + lhs, + rhs, + ty: TY_UNIT, + }; + } case "block": { currentNestingDepth++; const prevLocalTysLen = localTys.length;