From 1d0e3a3affd24e01d527438fd760fc47b0018e2c Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 26 Jul 2023 21:35:31 +0200 Subject: [PATCH] make it a wasi executable --- src/ast.ts | 11 +++++++++-- src/index.ts | 3 ++- src/lower.ts | 43 +++++++++++++++++++++++++++++++++++++++---- src/parser.ts | 2 +- src/printer.ts | 2 +- src/resolve.ts | 4 ++-- src/typeck.ts | 35 +++++++++++++++++++++++++++++++++-- 7 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index a53ee68..5ba6189 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,6 +1,6 @@ import { Span } from "./error"; -export type Ast = Item[]; +export type Ast = { items: Item[]; typeckResults?: TypeckResults }; export type Identifier = { name: string; @@ -273,6 +273,10 @@ export const TY_STRING: Ty = { kind: "string" }; export const TY_BOOL: Ty = { kind: "bool" }; export const TY_INT: Ty = { kind: "int", signed: false }; +export type TypeckResults = { + main: ItemId; +}; + // folders export type FoldFn = (value: T) => T; @@ -300,7 +304,10 @@ export const DEFAULT_FOLDER: Folder = { }; export function foldAst(ast: Ast, folder: Folder): Ast { - return ast.map((item) => folder.item(item)); + return { + items: ast.items.map((item) => folder.item(item)), + typeckResults: ast.typeckResults, + }; } export function superFoldItem(item: Item, folder: Folder): Item { diff --git a/src/index.ts b/src/index.ts index 72c8b1e..5db9908 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,8 @@ import fs from "fs"; import { exec } from "child_process"; const input = ` -function main(i: Int, j: Int): Bool = false == (i == 0); +function main() = (); +function test(i: Int, j: Int): Bool = false == (i == 0); `; function main() { diff --git a/src/lower.ts b/src/lower.ts index b138c83..f2fcdde 100644 --- a/src/lower.ts +++ b/src/lower.ts @@ -1,9 +1,10 @@ +import { AST } from "prettier"; import { Ast, Expr, FunctionDef, Item, Ty, TyFn, varUnreachable } from "./ast"; import * as wasm from "./wasm/defs"; type StringifiedForMap = string; -type Context = { +export type Context = { mod: wasm.Module; funcTypes: Map, wasm.TypeIdx>; funcIndices: Map; @@ -34,9 +35,21 @@ export function lower(ast: Ast): wasm.Module { exports: [], }; + mod.mems.push({ _name: "memory", type: { min: 1024, max: 1024 } }); + mod.exports.push({ name: "memory", desc: { kind: "memory", idx: 0 } }); + + mod.tables.push({ + _name: "__indirect_function_table", + type: { limits: { min: 0, max: 0 }, reftype: "funcref" }, + }); + mod.exports.push({ + name: "__indirect_function_table", + desc: { kind: "table", idx: 0 }, + }); + const cx: Context = { mod, funcTypes: new Map(), funcIndices: new Map() }; - ast.forEach((item) => { + ast.items.forEach((item) => { switch (item.kind) { case "function": { lowerFunc(cx, item, item.node); @@ -44,6 +57,8 @@ export function lower(ast: Ast): wasm.Module { } }); + addRt(cx, ast); + return mod; } @@ -384,6 +399,26 @@ function todo(msg: string): never { throw new Error(`TODO: ${msg}`); } -function exists(val: T | undefined): val is T { - return val !== undefined; +// Make the program runnable using wasi-preview-1 +function addRt(cx: Context, ast: Ast) { + const { mod } = cx; + + console.log(cx.funcIndices); + + const main = cx.funcIndices.get(ast.typeckResults!.main); + if (main === undefined) { + throw new Error(`main function (${main}) was not compiled.`); + } + + const start: wasm.Func = { + _name: "_start", + type: internFuncType(cx, { params: [], returns: [] }), + locals: [], + body: [{ kind: "call", func: main }], + }; + + const startIdx = mod.funcs.length; + mod.funcs.push(start); + + mod.exports.push({ name: "_start", desc: { kind: "func", idx: startIdx } }); } diff --git a/src/parser.ts b/src/parser.ts index 2fcaa5c..c8c4fbd 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -29,7 +29,7 @@ export function parse(t: Token[]): Ast { const withIds = items.map((item, i) => ({ ...item, id: i })); - return withIds; + return { items: withIds }; } function parseItem(t: Token[]): [Token[], Item] { diff --git a/src/printer.ts b/src/printer.ts index 2a9d803..8e34be4 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -11,7 +11,7 @@ import { } from "./ast"; export function printAst(ast: Ast): string { - return ast.map(printItem).join("\n"); + return ast.items.map(printItem).join("\n"); } function printItem(item: Item): string { diff --git a/src/resolve.ts b/src/resolve.ts index 8fbf00a..d01b53a 100644 --- a/src/resolve.ts +++ b/src/resolve.ts @@ -17,8 +17,8 @@ const BUILTIN_SET = new Set(BUILTINS); export function resolve(ast: Ast): Ast { const items = new Map(); - for (let i = 0; i < ast.length; i++) { - const item = ast[i]; + for (let i = 0; i < ast.items.length; i++) { + const item = ast.items[i]; const existing = items.get(item.node.name); if (existing !== undefined) { throw new CompilerError( diff --git a/src/typeck.ts b/src/typeck.ts index 1a131bb..f08b7f5 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -102,7 +102,7 @@ export function typeck(ast: Ast): Ast { throw Error(`cycle computing type of #G${index}`); } itemTys.set(index, null); - const item = ast[index]; + const item = ast.items[index]; switch (item.kind) { case "function": { const args = item.node.params.map((arg) => lowerAstTy(arg.type)); @@ -168,7 +168,38 @@ export function typeck(ast: Ast): Ast { }, }; - return foldAst(ast, checker); + const typecked = foldAst(ast, checker); + + const main = typecked.items.find((item) => { + if (item.kind === "function" && item.node.name === "main") { + const func = item.node; + if (func.returnType !== undefined) { + const ty = func.returnType.ty!; + if (ty.kind !== "tuple" || ty.elems.length !== 0) { + throw new CompilerError( + `\`main\` has an invalid signature. main takes no arguments and returns nothing`, + item.span + ); + } + } + + return true; + } + return false; + }); + + if (!main) { + throw new CompilerError(`\`main\` function not found`, { + start: 0, + end: 1, + }); + } + + typecked.typeckResults = { + main: main.id, + }; + + return typecked; } type TyVarRes =