printing!!!

This commit is contained in:
nora 2023-07-29 21:32:19 +02:00
parent 27a4ecc46b
commit 761f78de0b
6 changed files with 165 additions and 26 deletions

View file

@ -10,15 +10,12 @@ import fs from "fs";
import { exec } from "child_process";
const input = `
function printInt(a: Int): Int = (
if true then (
0;
1;
);
0
function main() = (
print("uwu
");
print("owo
");
);
function main() = ;
`;
function main() {

View file

@ -8,14 +8,30 @@ import {
TyFn,
varUnreachable,
} from "./ast";
import { CompilerError } from "./error";
import { encodeUtf8 } from "./utils";
import * as wasm from "./wasm/defs";
type StringifiedForMap<T> = string;
const USIZE: wasm.ValType = "i32";
// POINTERS ARE JUST INTEGERS
const POINTER: wasm.ValType = USIZE;
const STRING_TYPES: wasm.ValType[] = [POINTER, USIZE];
const STRING_ABI: ArgAbi & RetAbi = {
kind: "aggregate",
types: STRING_TYPES,
};
const WASM_PAGE = 65536;
export type Context = {
mod: wasm.Module;
funcTypes: Map<StringifiedForMap<wasm.FuncType>, wasm.TypeIdx>;
reservedHeapMemoryStart: number;
funcIndices: Map<number, wasm.FuncIdx>;
ast: Ast;
};
function internFuncType(cx: Context, type: wasm.FuncType): wasm.TypeIdx {
@ -30,6 +46,31 @@ function internFuncType(cx: Context, type: wasm.FuncType): wasm.TypeIdx {
return idx;
}
function appendData(cx: Context, newData: Uint8Array): number {
const datas = cx.mod.datas;
if (datas.length === 0) {
datas.push({
init: newData,
mode: {
kind: "active",
memory: 0,
offset: [{ kind: "i32.const", imm: 0 }],
},
_name: "staticdata",
});
return 0;
} else {
const data = datas[0];
const idx = data.init.length;
const init = new Uint8Array(data.init.length + newData.length);
init.set(data.init, 0);
init.set(newData, data.init.length);
data.init = init;
return idx;
}
}
export function lower(ast: Ast): wasm.Module {
const mod: wasm.Module = {
types: [],
@ -43,7 +84,7 @@ export function lower(ast: Ast): wasm.Module {
exports: [],
};
mod.mems.push({ _name: "memory", type: { min: 1024, max: 1024 } });
mod.mems.push({ _name: "memory", type: { min: WASM_PAGE, max: WASM_PAGE } });
mod.exports.push({ name: "memory", desc: { kind: "memory", idx: 0 } });
mod.tables.push({
@ -55,7 +96,13 @@ export function lower(ast: Ast): wasm.Module {
desc: { kind: "table", idx: 0 },
});
const cx: Context = { mod, funcTypes: new Map(), funcIndices: new Map() };
const cx: Context = {
mod,
funcTypes: new Map(),
funcIndices: new Map(),
reservedHeapMemoryStart: 0,
ast,
};
ast.items.forEach((item) => {
switch (item.kind) {
@ -65,6 +112,8 @@ export function lower(ast: Ast): wasm.Module {
}
});
cx.reservedHeapMemoryStart = (mod.datas[0].init.length & ~0x8) + 0x8;
addRt(cx, ast);
return mod;
@ -80,8 +129,14 @@ type FuncContext = {
type Abi = { params: ArgAbi[]; ret: RetAbi };
type ArgAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
type RetAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
type ArgAbi =
| { kind: "scalar"; type: wasm.ValType }
| { kind: "zst" }
| { kind: "aggregate"; types: wasm.ValType[] };
type RetAbi =
| { kind: "scalar"; type: wasm.ValType }
| { kind: "zst" }
| { kind: "aggregate"; types: wasm.ValType[] };
type VarLocation = { kind: "local"; idx: number } | { kind: "zst" };
@ -166,9 +221,16 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
case "literal": {
switch (expr.value.kind) {
case "str":
todo("strings");
const utf8 = encodeUtf8(expr.value.value);
const idx = appendData(fcx.cx, utf8);
instrs.push({ kind: "i32.const", imm: idx });
instrs.push({ kind: "i32.const", imm: utf8.length });
break;
case "int":
instrs.push({ kind: "i64.const", imm: expr.value.value });
break;
}
break;
}
@ -234,6 +296,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
break;
case ">":
kind = "i64.gt_u";
// errs
break;
case "==":
kind = "i64.eq";
@ -301,8 +364,24 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
}
break;
}
case "call":
todo("call");
case "call": {
if (expr.lhs.kind !== "ident") {
todo("non constant calls");
}
if (expr.lhs.value.res!.kind !== "builtin") {
todo("youre not a builtin, fuck you");
}
const printIdx =
fcx.cx.ast.items.filter((item) => item.kind === "function").length +
/*import*/ 1 +
/*_start*/ 1;
expr.args.forEach((arg) => {
lowerExpr(fcx, instrs, arg);
});
instrs.push({ kind: "call", func: printIdx });
break;
}
case "if": {
lowerExpr(fcx, instrs, expr.cond!);
@ -369,7 +448,7 @@ function computeAbi(ty: TyFn): Abi {
function paramAbi(param: Ty): ArgAbi {
switch (param.kind) {
case "string":
todo("string abi");
return STRING_ABI;
case "fn":
todo("fn abi");
case "int":
@ -397,7 +476,8 @@ function computeAbi(ty: TyFn): Abi {
let ret: RetAbi;
switch (ty.returnTy.kind) {
case "string":
todo("string abi");
ret = STRING_ABI;
break;
case "fn":
todo("fn abi");
case "int":
@ -441,7 +521,11 @@ function wasmTypeForAbi(abi: Abi): {
break;
case "zst":
paramLocations.push({ kind: "zst" });
return undefined;
break;
case "aggregate":
paramLocations.push({ kind: "local", idx: params.length });
params.push(...arg.types);
break;
}
});
let returns: wasm.ValType[];
@ -452,6 +536,8 @@ function wasmTypeForAbi(abi: Abi): {
case "zst":
returns = [];
break;
case "aggregate":
returns = abi.ret.types;
}
return { type: { params, returns }, paramLocations };
@ -460,7 +546,7 @@ function wasmTypeForAbi(abi: Abi): {
function wasmTypeForBody(ty: Ty): wasm.ValType[] {
switch (ty.kind) {
case "string":
todo("string types");
return STRING_TYPES;
case "int":
return ["i64"];
case "bool":
@ -498,7 +584,6 @@ function todo(msg: string): never {
// Make the program runnable using wasi-preview-1
function addRt(cx: Context, ast: Ast) {
const { mod } = cx;
const main = cx.funcIndices.get(ast.typeckResults!.main);
if (main === undefined) {
throw new Error(`main function (${main}) was not compiled.`);
@ -508,11 +593,56 @@ function addRt(cx: Context, ast: Ast) {
_name: "_start",
type: internFuncType(cx, { params: [], returns: [] }),
locals: [],
body: [{ kind: "call", func: main }],
body: [{ kind: "call", func: main + 1 }],
};
const startIdx = mod.funcs.length;
mod.funcs.push(start);
const reserveMemory = (amount: number) => {
const start = cx.reservedHeapMemoryStart;
cx.reservedHeapMemoryStart += amount;
return start;
};
const fd_write_type = internFuncType(cx, {
params: ["i32", "i32", "i32", "i32"],
returns: ["i32"],
});
cx.mod.imports.push({
module: "wasi_snapshot_preview1",
name: "fd_write",
desc: { kind: "func", type: fd_write_type },
});
const printReturnValue = reserveMemory(4);
const iovecArray = reserveMemory(8);
const print: wasm.Func = {
_name: "____print",
locals: [],
type: internFuncType(cx, { params: [POINTER, USIZE], returns: [] }),
body: [
// get the pointer and store it in the iovec
{ kind: "i32.const", imm: iovecArray },
{ kind: "local.get", imm: 0 },
{ kind: "i32.store", imm: { offset: 0, align: 4 } },
// get the length and store it in the iovec
{ kind: "i32.const", imm: iovecArray + 4 },
{ kind: "local.get", imm: 1 },
{ kind: "i32.store", imm: { offset: 0, align: 4 } },
// now call stuff
{ kind: "i32.const", imm: /*stdout*/ 1 },
{ kind: "i32.const", imm: iovecArray },
{ kind: "i32.const", imm: /*iovec len*/ 1 },
{ kind: "i32.const", imm: /*out ptr*/ printReturnValue },
{ kind: "call", func: 0 },
{ kind: "drop" },
],
};
cx.mod.funcs.push(print);
mod.exports.push({ name: "_start", desc: { kind: "func", idx: startIdx } });
}

View file

@ -1,4 +1,4 @@
import { TY_INT, TY_STRING, TY_UNIT } from "./ast";
import { TY_INT, TY_STRING, TY_UNIT, Ty } from "./ast";
import { DUMMY_SPAN as SPAN } from "./error";
import { InferContext } from "./typeck";

View file

@ -315,6 +315,9 @@ export class InferContext {
}
public resolveIfPossible(ty: Ty): Ty {
// TODO: dont be shallow resolve
// note that fixing this will cause cycles. fix those cycles instead using
// he fancy occurs check as errs called it.
if (ty.kind === "var") {
return this.tryResolveVar(ty.index) ?? ty;
} else {

3
src/utils.ts Normal file
View file

@ -0,0 +1,3 @@
export function encodeUtf8(s: string): Uint8Array {
return new TextEncoder().encode(s);
}

View file

@ -136,7 +136,13 @@ function printString(s: string, f: Formatter) {
}
function printBinaryString(buf: Uint8Array, f: Formatter) {
f.word(`"${buf.toString()}"`);
const parts: string[] = [];
for (let i = 0; i < buf.length; i++) {
const idx = buf[i];
parts.push(`\\${idx.toString(16).padStart(2, "0")}`);
}
f.word(`"${parts.join("")}"`);
}
function printId(id: string | undefined, f: Formatter) {
@ -200,12 +206,12 @@ function printBlockType(type: Blocktype, f: Formatter) {
}
function printMemarg(arg: MemArg, f: Formatter) {
if (arg.offset /*0->false*/) {
f.word(`offset=${arg.offset}`);
}
if (arg.align !== undefined) {
f.word(`align=${arg.align}`);
}
if (arg.offset /*0->false*/) {
`offset=${arg.offset}`;
}
}
/**