avoid wasm symbol conflicts

This commit is contained in:
nora 2023-07-31 13:49:43 +02:00
parent 6bdbf14ecb
commit e951cd5ee1
8 changed files with 188 additions and 99 deletions

View file

@ -5,6 +5,7 @@ export type Ast = {
rootItems: Item[]; rootItems: Item[];
typeckResults?: TypeckResults; typeckResults?: TypeckResults;
itemsById: Map<ItemId, Item>; itemsById: Map<ItemId, Item>;
packageName: string;
}; };
export type Identifier = { export type Identifier = {
@ -36,6 +37,7 @@ export type ItemKind =
export type Item = ItemKind & { export type Item = ItemKind & {
span: Span; span: Span;
id: ItemId; id: ItemId;
defPath?: string[];
}; };
export type FunctionDef = { export type FunctionDef = {
@ -493,6 +495,7 @@ export function foldAst(ast: Ast, folder: Folder): Ast {
rootItems: ast.rootItems.map((item) => folder.item(item)), rootItems: ast.rootItems.map((item) => folder.item(item)),
itemsById: ast.itemsById, itemsById: ast.itemsById,
typeckResults: ast.typeckResults, typeckResults: ast.typeckResults,
packageName: ast.packageName,
}; };
} }

View file

@ -1,5 +1,5 @@
import { withErrorHandler } from "./error"; import { withErrorHandler } from "./error";
import { tokenize } from "./lexer"; import { isValidIdent, tokenize } from "./lexer";
import { lower as lowerToWasm } from "./lower"; import { lower as lowerToWasm } from "./lower";
import { parse } from "./parser"; import { parse } from "./parser";
import { printAst } from "./printer"; import { printAst } from "./printer";
@ -7,28 +7,50 @@ import { resolve } from "./resolve";
import { typeck } from "./typeck"; import { typeck } from "./typeck";
import { writeModuleWatToString } from "./wasm/wat"; import { writeModuleWatToString } from "./wasm/wat";
import fs from "fs"; import fs from "fs";
import path from "path";
import { exec } from "child_process"; import { exec } from "child_process";
const INPUT = ` const INPUT = `
function main() = ( function main() = (
owo.uwu.meow(); owo.uwu.main();
owo.owo();
); );
mod owo ( mod owo (
mod uwu ( mod uwu (
function meow() =; function main() =;
); );
function owo() = ;
); );
`; `;
function main() { function main() {
let input: string; let input: string;
let packageName: string;
if (process.argv.length > 2) { if (process.argv.length > 2) {
input = fs.readFileSync(process.argv[2], { encoding: "utf-8" }); const filename = process.argv[2];
if (path.extname(filename) !== ".nil") {
console.error(
`error: filename must have \`.nil\` extension: \`${filename}\``
);
process.exit(1);
}
input = fs.readFileSync(filename, { encoding: "utf-8" });
packageName = path.basename(filename, ".nil");
} else { } else {
input = INPUT; input = INPUT;
packageName = "test";
} }
if (!isValidIdent(packageName)) {
console.error(`error: package name \`${packageName}\` is not a valid identifer`);
process.exit(1);
}
console.log(`package name: '${packageName}'`);
withErrorHandler(input, () => { withErrorHandler(input, () => {
const start = Date.now(); const start = Date.now();
@ -36,7 +58,7 @@ function main() {
console.log("-----TOKENS------------"); console.log("-----TOKENS------------");
console.log(tokens); console.log(tokens);
const ast = parse(tokens); const ast = parse(packageName, tokens);
console.log("-----AST---------------"); console.log("-----AST---------------");
console.dir(ast.rootItems, { depth: 50 }); console.dir(ast.rootItems, { depth: 50 });

View file

@ -272,6 +272,19 @@ export function tokenize(input: string): Token[] {
return tokens; return tokens;
} }
export function isValidIdent(ident: string): boolean {
if (!isIdentStart(ident[0])) {
return false;
}
for (let i = 1; i < ident.length; i++) {
const char = ident[i];
if (!isIdentContinue(char)) {
return false;
}
}
return true;
}
function isIdentStart(char: string): boolean { function isIdentStart(char: string): boolean {
return ( return (
(char <= "Z" && char >= "A") || (char <= "z" && char >= "a") || char === "_" (char <= "Z" && char >= "A") || (char <= "z" && char >= "a") || char === "_"

View file

@ -12,7 +12,7 @@ import {
TyTuple, TyTuple,
varUnreachable, varUnreachable,
} from "./ast"; } from "./ast";
import { encodeUtf8, unwrap } from "./utils"; import { ComplexMap, encodeUtf8, unwrap } from "./utils";
import * as wasm from "./wasm/defs"; import * as wasm from "./wasm/defs";
const USIZE: wasm.ValType = "i32"; const USIZE: wasm.ValType = "i32";
@ -29,43 +29,37 @@ type Relocation = {
instr: wasm.Instr & { func: wasm.FuncIdx }; instr: wasm.Instr & { func: wasm.FuncIdx };
} & { res: Resolution }; } & { res: Resolution };
type StringifiedMap<K, V> = { _map: Map<string, V> };
function setMap<K, V>(map: StringifiedMap<K, V>, key: K, value: V) {
map._map.set(JSON.stringify(key), value);
}
function getMap<K, V>(map: StringifiedMap<K, V>, key: K): V | undefined {
return map._map.get(JSON.stringify(key));
}
type FuncOrImport = type FuncOrImport =
| { kind: "func"; idx: wasm.FuncIdx } | { kind: "func"; idx: wasm.FuncIdx }
| { kind: "import"; idx: number }; | { kind: "import"; idx: number };
export type Context = { export type Context = {
mod: wasm.Module; mod: wasm.Module;
funcTypes: StringifiedMap<wasm.FuncType, wasm.TypeIdx>; funcTypes: ComplexMap<wasm.FuncType, wasm.TypeIdx>;
reservedHeapMemoryStart: number; reservedHeapMemoryStart: number;
funcIndices: StringifiedMap<Resolution, FuncOrImport>; funcIndices: ComplexMap<Resolution, FuncOrImport>;
ast: Ast; ast: Ast;
relocations: Relocation[]; relocations: Relocation[];
}; };
function mangleDefPath(defPath: string[]): string {
return `nil__${defPath.map(escapeIdentName).join("__")}`;
}
function escapeIdentName(name: string): string { function escapeIdentName(name: string): string {
// This allows the implementation to use 2 leading underscores // This allows the implementation to use 2 leading underscores
// for any names and it will not conflict. // to separate ident parts of in a loading position and avoid conflicts.
return name.startsWith("__") ? `_${name}` : name; return name.replace(/__/g, "___");
} }
function internFuncType(cx: Context, type: wasm.FuncType): wasm.TypeIdx { function internFuncType(cx: Context, type: wasm.FuncType): wasm.TypeIdx {
const existing = getMap(cx.funcTypes, type); const existing = cx.funcTypes.get(type);
if (existing !== undefined) { if (existing !== undefined) {
return existing; return existing;
} }
const idx = cx.mod.types.length; const idx = cx.mod.types.length;
cx.mod.types.push(type); cx.mod.types.push(type);
setMap(cx.funcTypes, type, idx); cx.funcTypes.set(type, idx);
return idx; return idx;
} }
@ -121,25 +115,34 @@ export function lower(ast: Ast): wasm.Module {
const cx: Context = { const cx: Context = {
mod, mod,
funcTypes: { _map: new Map() }, funcTypes: new ComplexMap(),
funcIndices: { _map: new Map() }, funcIndices: new ComplexMap(),
reservedHeapMemoryStart: 0, reservedHeapMemoryStart: 0,
ast, ast,
relocations: [], relocations: [],
}; };
ast.rootItems.forEach((item) => { function lowerMod(items: Item[]) {
switch (item.kind) { items.forEach((item) => {
case "function": { switch (item.kind) {
lowerFunc(cx, item, item.node); case "function": {
break; lowerFunc(cx, item, item.node);
break;
}
case "import": {
lowerImport(cx, item, item.node);
break;
}
case "mod": {
if (item.node.modKind.kind === "inline") {
lowerMod(item.node.modKind.contents);
}
}
} }
case "import": { });
lowerImport(cx, item, item.node); }
break;
} lowerMod(ast.rootItems);
}
});
const HEAP_ALIGN = 0x08; const HEAP_ALIGN = 0x08;
cx.reservedHeapMemoryStart = cx.reservedHeapMemoryStart =
@ -154,7 +157,7 @@ export function lower(ast: Ast): wasm.Module {
cx.relocations.forEach((rel) => { cx.relocations.forEach((rel) => {
switch (rel.kind) { switch (rel.kind) {
case "funccall": { case "funccall": {
const idx = getMap<Resolution, FuncOrImport>(cx.funcIndices, rel.res); const idx = cx.funcIndices.get(rel.res);
if (idx === undefined) { if (idx === undefined) {
throw new Error( throw new Error(
`no function found for relocation '${JSON.stringify(rel.res)}'` `no function found for relocation '${JSON.stringify(rel.res)}'`
@ -193,11 +196,7 @@ function lowerImport(cx: Context, item: Item, def: ImportDef) {
}); });
} }
setMap<Resolution, FuncOrImport>( cx.funcIndices.set({ kind: "item", id: item.id }, { kind: "import", idx });
cx.funcIndices,
{ kind: "item", id: item.id },
{ kind: "import", idx }
);
} }
type FuncContext = { type FuncContext = {
@ -223,7 +222,7 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
const type = internFuncType(cx, wasmType); const type = internFuncType(cx, wasmType);
const wasmFunc: wasm.Func = { const wasmFunc: wasm.Func = {
_name: escapeIdentName(func.name), _name: mangleDefPath(item.defPath!),
type, type,
locals: [], locals: [],
body: [], body: [],
@ -244,8 +243,8 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
const idx = fcx.cx.mod.funcs.length; const idx = fcx.cx.mod.funcs.length;
fcx.cx.mod.funcs.push(wasmFunc); fcx.cx.mod.funcs.push(wasmFunc);
setMap<Resolution, FuncOrImport>(
fcx.cx.funcIndices, fcx.cx.funcIndices.set(
{ kind: "item", id: fcx.item.id }, { kind: "item", id: fcx.item.id },
{ kind: "func", idx } { kind: "func", idx }
); );
@ -353,8 +352,10 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
} }
break; break;
} }
case "path":
case "ident": { case "ident": {
const res = expr.value.res!; const res = expr.kind === "ident" ? expr.value.res! : expr.res;
switch (res.kind) { switch (res.kind) {
case "local": { case "local": {
const location = const location =
@ -382,9 +383,6 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
break; break;
} }
case "path": {
todo("path");
}
case "binary": { case "binary": {
// By evaluating the LHS first, the RHS is on top, which // By evaluating the LHS first, the RHS is on top, which
// is correct as it's popped first. Evaluating the LHS first // is correct as it's popped first. Evaluating the LHS first
@ -498,12 +496,15 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
break; break;
} }
case "call": { case "call": {
if (expr.lhs.kind !== "ident") { if (expr.lhs.kind !== "ident" && expr.lhs.kind !== "path") {
todo("non constant calls"); todo("non constant calls");
} }
if (expr.lhs.value.res!.kind === "builtin") { const res =
switch (expr.lhs.value.res!.name) { expr.lhs.kind === "ident" ? expr.lhs.value.res! : expr.lhs.res;
if (res.kind === "builtin") {
switch (res.name) {
case "trap": { case "trap": {
instrs.push({ kind: "unreachable" }); instrs.push({ kind: "unreachable" });
break exprKind; break exprKind;
@ -553,7 +554,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
fcx.cx.relocations.push({ fcx.cx.relocations.push({
kind: "funccall", kind: "funccall",
instr: callInstr, instr: callInstr,
res: expr.lhs.value.res!, res,
}); });
expr.args.forEach((arg) => { expr.args.forEach((arg) => {
@ -893,7 +894,7 @@ function addRt(cx: Context, ast: Ast) {
const iovecArray = reserveMemory(8); const iovecArray = reserveMemory(8);
const print: wasm.Func = { const print: wasm.Func = {
_name: "___print", _name: "nil__print",
locals: [], locals: [],
type: internFuncType(cx, { params: [POINTER, USIZE], returns: [] }), type: internFuncType(cx, { params: [POINTER, USIZE], returns: [] }),
body: [ body: [
@ -917,8 +918,7 @@ function addRt(cx: Context, ast: Ast) {
const printIdx = cx.mod.funcs.length; const printIdx = cx.mod.funcs.length;
cx.mod.funcs.push(print); cx.mod.funcs.push(print);
setMap( cx.funcIndices.set(
cx.funcIndices,
{ kind: "builtin", name: "print" }, { kind: "builtin", name: "print" },
{ kind: "func", idx: printIdx } { kind: "func", idx: printIdx }
); );

View file

@ -32,7 +32,7 @@ import { Ids } from "./utils";
type Parser<T> = (t: Token[]) => [Token[], T]; type Parser<T> = (t: Token[]) => [Token[], T];
export function parse(t: Token[]): Ast { export function parse(packageName: string, t: Token[]): Ast {
const items: Item[] = []; const items: Item[] = [];
while (t.length > 0) { while (t.length > 0) {
@ -41,7 +41,7 @@ export function parse(t: Token[]): Ast {
items.push(item); items.push(item);
} }
const ast = assignIds(items); const ast = buildAst(packageName, items);
validateAst(ast); validateAst(ast);
@ -711,13 +711,14 @@ function validateAst(ast: Ast) {
foldAst(ast, validator); foldAst(ast, validator);
} }
function assignIds(rootItems: Item[]): Ast { function buildAst(packageName: string, rootItems: Item[]): Ast {
const itemId = new Ids(); const itemId = new Ids();
const loopId = new Ids(); const loopId = new Ids();
const ast: Ast = { const ast: Ast = {
rootItems, rootItems,
itemsById: new Map(), itemsById: new Map(),
packageName,
}; };
const assigner: Folder = { const assigner: Folder = {

View file

@ -52,11 +52,15 @@ function resolveModItem(
export function resolve(ast: Ast): Ast { export function resolve(ast: Ast): Ast {
const cx: Context = { ast, modContentsCache: new Map() }; const cx: Context = { ast, modContentsCache: new Map() };
const rootItems = resolveModule(cx, ast.rootItems); const rootItems = resolveModule(cx, [ast.packageName], ast.rootItems);
return { ...ast, rootItems }; return { ...ast, rootItems };
} }
function resolveModule(cx: Context, contents: Item[]): Item[] { function resolveModule(
cx: Context,
modName: string[],
contents: Item[]
): Item[] {
const items = new Map<string, number>(); const items = new Map<string, number>();
contents.forEach((item) => { contents.forEach((item) => {
@ -117,6 +121,8 @@ function resolveModule(cx: Context, contents: Item[]): Item[] {
return cx.ast; return cx.ast;
}, },
itemInner(item) { itemInner(item) {
const defPath = [...modName, item.node.name];
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const params = item.node.params.map(({ name, span, type }) => ({ const params = item.node.params.map(({ name, span, type }) => ({
@ -143,22 +149,28 @@ function resolveModule(cx: Context, contents: Item[]): Item[] {
body, body,
}, },
id: item.id, id: item.id,
defPath,
}; };
} }
case "mod": { case "mod": {
if (item.node.modKind.kind === "inline") { if (item.node.modKind.kind === "inline") {
const contents = resolveModule(cx, item.node.modKind.contents); const contents = resolveModule(
cx,
defPath,
item.node.modKind.contents
);
return { return {
...item, ...item,
kind: "mod", kind: "mod",
node: { ...item.node, modKind: { kind: "inline", contents } }, node: { ...item.node, modKind: { kind: "inline", contents } },
defPath,
}; };
} }
break; break;
} }
} }
return superFoldItem(item, this); return { ...superFoldItem(item, this), defPath };
}, },
expr(expr) { expr(expr) {
switch (expr.kind) { switch (expr.kind) {
@ -195,9 +207,16 @@ function resolveModule(cx: Context, contents: Item[]): Item[] {
}; };
} }
case "fieldAccess": { case "fieldAccess": {
if (expr.lhs.kind === "ident") { // We convert field accesses to paths if the lhs refers to a module.
// If the lhs is a module we need to convert this into a path.
const res = resolveIdent(expr.lhs.value); const lhs = this.expr(expr.lhs);
if (lhs.kind === "ident" || lhs.kind === "path") {
const res =
lhs.kind === "ident" ? resolveIdent(lhs.value) : lhs.res;
const segments =
lhs.kind === "ident" ? [lhs.value.name] : lhs.segments;
if (res.kind === "item") { if (res.kind === "item") {
const module = unwrap(cx.ast.itemsById.get(res.id)); const module = unwrap(cx.ast.itemsById.get(res.id));
if (module.kind === "mod") { if (module.kind === "mod") {
@ -225,9 +244,9 @@ function resolveModule(cx: Context, contents: Item[]): Item[] {
return { return {
kind: "path", kind: "path",
segments: [expr.lhs.value.name, expr.field.value], segments: [...segments, expr.field.value],
res: pathRes, res: pathRes,
span: spanMerge(expr.lhs.span, expr.field.span), span: spanMerge(lhs.span, expr.field.span),
}; };
} }
} }

View file

@ -16,3 +16,26 @@ export function unwrap<T>(value: T | undefined): T {
} }
return value; return value;
} }
/**
* A `Map` that can have arbitrarily complex keys.
* It uses JSON+string equality instead of refernece equality.
*/
export class ComplexMap<K, V> {
inner: Map<string | number, V> = new Map();
public get(key: K): V | undefined {
return this.inner.get(this.mangleKey(key));
}
public set(key: K, value: V) {
this.inner.set(this.mangleKey(key), value);
}
private mangleKey(key: K): string | number {
if (typeof key === "string" || typeof key === "number") {
return key;
}
return JSON.stringify(key);
}
}

View file

@ -26,19 +26,21 @@ import {
const identity = (s: string) => s; const identity = (s: string) => s;
class Formatter { class FmtCtx {
print: (chunk: string) => void; print: (chunk: string) => void;
indentation: number; indentation: number;
wordsInSexpr: number[]; wordsInSexpr: number[];
freshLinebreak: boolean; freshLinebreak: boolean;
color: boolean; color: boolean;
mod: Module;
constructor(print: (chunk: string) => void, color = true) { constructor(print: (chunk: string) => void, mod: Module, color = true) {
this.print = print; this.print = print;
this.indentation = 0; this.indentation = 0;
this.wordsInSexpr = []; this.wordsInSexpr = [];
this.freshLinebreak = false; this.freshLinebreak = false;
this.color = color; this.color = color;
this.mod = mod;
} }
linebreak() { linebreak() {
@ -103,6 +105,10 @@ class Formatter {
this.wordsInSexpr[last]++; this.wordsInSexpr[last]++;
} }
comment(content: string | number) {
this.word(`(;${content};)`, chalk.gray);
}
startSexpr() { startSexpr() {
this.word("("); this.word("(");
this.wordsInSexpr.push(0); this.wordsInSexpr.push(0);
@ -121,21 +127,18 @@ class Formatter {
export function writeModuleWatToString(module: Module, color = false): string { export function writeModuleWatToString(module: Module, color = false): string {
const parts: string[] = []; const parts: string[] = [];
const writer = (s: string) => parts.push(s); const writer = (s: string) => parts.push(s);
printModule(module, new Formatter(writer, color)); printModule(module, new FmtCtx(writer, module, color));
return parts.join(""); return parts.join("");
} }
export function writeModuleWat(module: Module, f: Formatter) {
printModule(module, f);
}
// base // base
function printString(s: string, f: Formatter) { function printString(s: string, f: FmtCtx) {
// TODO: escaping // TODO: escaping
f.word(`"${s}"`); f.word(`"${s}"`);
} }
function printBinaryString(buf: Uint8Array, f: Formatter) { function printBinaryString(buf: Uint8Array, f: FmtCtx) {
const parts: string[] = []; const parts: string[] = [];
for (let i = 0; i < buf.length; i++) { for (let i = 0; i < buf.length; i++) {
const byte = buf[i]; const byte = buf[i];
@ -151,7 +154,7 @@ function printBinaryString(buf: Uint8Array, f: Formatter) {
f.word(`"${parts.join("")}"`); f.word(`"${parts.join("")}"`);
} }
function printId(id: string | undefined, f: Formatter) { function printId(id: string | undefined, f: FmtCtx) {
if (id) { if (id) {
f.word(`$${id}`); f.word(`$${id}`);
} }
@ -159,11 +162,11 @@ function printId(id: string | undefined, f: Formatter) {
// types // types
function printValType(type: ValType, f: Formatter) { function printValType(type: ValType, f: FmtCtx) {
f.type(type); f.type(type);
} }
function printFuncType(type: FuncType, f: Formatter) { function printFuncType(type: FuncType, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("func"); f.keyword("func");
f.sexpr(() => { f.sexpr(() => {
@ -177,17 +180,17 @@ function printFuncType(type: FuncType, f: Formatter) {
}); });
} }
function printLimits(limits: Limits, f: Formatter) { function printLimits(limits: Limits, f: FmtCtx) {
f.word(limits.min); f.word(limits.min);
f.word(limits.max); f.word(limits.max);
} }
function printTableType(type: TableType, f: Formatter) { function printTableType(type: TableType, f: FmtCtx) {
printLimits(type.limits, f); printLimits(type.limits, f);
printValType(type.reftype, f); printValType(type.reftype, f);
} }
function printGlobalType(type: GlobalType, f: Formatter) { function printGlobalType(type: GlobalType, f: FmtCtx) {
if (type.mut === "const") { if (type.mut === "const") {
printValType(type.type, f); printValType(type.type, f);
} else { } else {
@ -200,7 +203,7 @@ function printGlobalType(type: GlobalType, f: Formatter) {
// instrs // instrs
function printBlockType(type: Blocktype, f: Formatter) { function printBlockType(type: Blocktype, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("type"); f.keyword("type");
if (type.kind === "typeidx") { if (type.kind === "typeidx") {
@ -211,7 +214,7 @@ function printBlockType(type: Blocktype, f: Formatter) {
}); });
} }
function printMemarg(arg: MemArg, f: Formatter) { function printMemarg(arg: MemArg, f: FmtCtx) {
if (arg.offset /*0->false*/) { if (arg.offset /*0->false*/) {
f.word(`offset=${arg.offset}`); f.word(`offset=${arg.offset}`);
} }
@ -225,7 +228,7 @@ function printMemarg(arg: MemArg, f: Formatter) {
* Start: indented start of first instr * Start: indented start of first instr
* End: start of next line * End: start of next line
*/ */
function printInstrBlock(instrs: Instr[], f: Formatter) { function printInstrBlock(instrs: Instr[], f: FmtCtx) {
instrs.forEach((nested, i) => { instrs.forEach((nested, i) => {
printInstr(nested, f); printInstr(nested, f);
if (i !== instrs.length - 1) { if (i !== instrs.length - 1) {
@ -235,7 +238,7 @@ function printInstrBlock(instrs: Instr[], f: Formatter) {
f.breakDedent(); f.breakDedent();
} }
function printInstr(instr: Instr, f: Formatter) { function printInstr(instr: Instr, f: FmtCtx) {
switch (instr.kind) { switch (instr.kind) {
case "block": case "block":
case "loop": case "loop":
@ -430,10 +433,15 @@ function printInstr(instr: Instr, f: Formatter) {
instr.labels.forEach((label) => f.word(label)); instr.labels.forEach((label) => f.word(label));
f.word(instr.label); f.word(instr.label);
break; break;
case "call": case "call": {
f.controlFlow(instr.kind); f.controlFlow(instr.kind);
f.word(instr.func); f.word(instr.func);
const name = f.mod.funcs[instr.func]?._name;
if (name) {
f.comment(name);
}
break; break;
}
case "call_indirect": case "call_indirect":
f.controlFlow(instr.kind); f.controlFlow(instr.kind);
f.word(instr.table); f.word(instr.table);
@ -504,14 +512,14 @@ function printInstr(instr: Instr, f: Formatter) {
// modules // modules
function printType(type: FuncType, f: Formatter) { function printType(type: FuncType, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("type"); f.keyword("type");
printFuncType(type, f); printFuncType(type, f);
}); });
} }
function printImport(import_: Import, f: Formatter) { function printImport(import_: Import, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("import"); f.keyword("import");
printString(import_.module, f); printString(import_.module, f);
@ -541,12 +549,12 @@ function printImport(import_: Import, f: Formatter) {
}); });
} }
function printFunction(func: Func, idx: number, f: Formatter) { function printFunction(func: Func, idx: number, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("func"); f.keyword("func");
printId(func._name, f); printId(func._name, f);
f.word(`(;${idx};)`, chalk.gray); f.comment(idx);
f.sexpr(() => { f.sexpr(() => {
f.keyword("type"); f.keyword("type");
@ -571,7 +579,7 @@ function printFunction(func: Func, idx: number, f: Formatter) {
}); });
} }
function printTable(table: Table, f: Formatter) { function printTable(table: Table, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("table"); f.keyword("table");
printId(table._name, f); printId(table._name, f);
@ -579,7 +587,7 @@ function printTable(table: Table, f: Formatter) {
}); });
} }
function printMem(mem: Mem, f: Formatter) { function printMem(mem: Mem, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("memory"); f.keyword("memory");
printId(mem._name, f); printId(mem._name, f);
@ -588,7 +596,7 @@ function printMem(mem: Mem, f: Formatter) {
}); });
} }
function printGlobal(global: Global, f: Formatter) { function printGlobal(global: Global, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("global"); f.keyword("global");
printId(global._name, f); printId(global._name, f);
@ -600,7 +608,7 @@ function printGlobal(global: Global, f: Formatter) {
}); });
} }
function printExport(export_: Export, f: Formatter) { function printExport(export_: Export, f: FmtCtx) {
const desc = export_.desc; const desc = export_.desc;
f.sexpr(() => { f.sexpr(() => {
@ -614,18 +622,18 @@ function printExport(export_: Export, f: Formatter) {
}); });
} }
function printStart(start: Start, f: Formatter) { function printStart(start: Start, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("start"); f.keyword("start");
f.word(start.func); f.word(start.func);
}); });
} }
function printElem(_elem: Elem, f: Formatter) { function printElem(_elem: Elem, f: FmtCtx) {
todo(); todo();
} }
function printData(data: Data, f: Formatter) { function printData(data: Data, f: FmtCtx) {
let mode = data.mode; let mode = data.mode;
f.sexpr(() => { f.sexpr(() => {
@ -656,7 +664,7 @@ function printData(data: Data, f: Formatter) {
}); });
} }
function printModule(module: Module, f: Formatter) { function printModule(module: Module, f: FmtCtx) {
f.sexpr(() => { f.sexpr(() => {
f.keyword("module"); f.keyword("module");
printId(module._name, f); printId(module._name, f);