start lowering

This commit is contained in:
nora 2023-07-26 19:36:02 +02:00
parent 87f081a4fe
commit ccd8008731
9 changed files with 275 additions and 71 deletions

11
package-lock.json generated
View file

@ -8,6 +8,9 @@
"name": "tscript", "name": "tscript",
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": {
"chalk": "^4.0.0"
},
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.3", "@types/jest": "^29.5.3",
"jest": "^29.6.1", "jest": "^29.6.1",
@ -1183,7 +1186,6 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
}, },
@ -1442,7 +1444,6 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
@ -1518,7 +1519,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": { "dependencies": {
"color-name": "~1.1.4" "color-name": "~1.1.4"
}, },
@ -1529,8 +1529,7 @@
"node_modules/color-name": { "node_modules/color-name": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"dev": true
}, },
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
@ -1931,7 +1930,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -3530,7 +3528,6 @@
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },

View file

@ -19,5 +19,8 @@
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.1.6" "typescript": "^5.1.6"
},
"dependencies": {
"chalk": "^4.0.0"
} }
} }

View file

@ -8,6 +8,8 @@ export type Identifier = {
res?: Resolution; res?: Resolution;
}; };
export type ItemId = number;
export type ItemKind = { export type ItemKind = {
kind: "function"; kind: "function";
node: FunctionDef; node: FunctionDef;
@ -15,7 +17,7 @@ export type ItemKind = {
export type Item = ItemKind & { export type Item = ItemKind & {
span: Span; span: Span;
id: number; id: ItemId;
}; };
export type FunctionDef = { export type FunctionDef = {

View file

@ -1,29 +1,29 @@
import { withErrorHandler } from "./error"; import { withErrorHandler } from "./error";
import { tokenize } from "./lexer"; import { tokenize } from "./lexer";
import { lower as lowerToWasm } from "./lower";
import { parse } from "./parser"; import { parse } from "./parser";
import { printAst } from "./printer"; import { printAst } from "./printer";
import { resolve } from "./resolve"; import { resolve } from "./resolve";
import { typeck } from "./typeck"; import { typeck } from "./typeck";
import { writeModuleWatToString } from "./wasm/wat";
const input = ` const input = `
function main() = ( function main(i: Int): Int = 0;
let a = 0 in 0;
);
`; `;
function main() { function main() {
withErrorHandler(input, () => { withErrorHandler(input, () => {
const tokens = tokenize(input); const tokens = tokenize(input);
console.log("-----TOKENS---"); console.log("-----TOKENS------------");
console.log(tokens); console.log(tokens);
const ast = parse(tokens); const ast = parse(tokens);
console.log("-----AST------"); console.log("-----AST---------------");
console.dir(ast, { depth: 50 }); console.dir(ast, { depth: 50 });
const printed = printAst(ast); const printed = printAst(ast);
console.log("-----AST pretty------"); console.log("-----AST pretty--------");
console.log(printed); console.log(printed);
const resolved = resolve(ast); const resolved = resolve(ast);
@ -34,7 +34,11 @@ function main() {
console.log("-----AST typecked------"); console.log("-----AST typecked------");
const typecked = typeck(resolved); const typecked = typeck(resolved);
console.dir(typecked, { depth: 10 });
console.log("-----wasm--------------");
const wasmModule = lowerToWasm(typecked);
const moduleString = writeModuleWatToString(wasmModule);
console.log(moduleString);
}); });
} }

175
src/lower.ts Normal file
View file

@ -0,0 +1,175 @@
import { Ast, FunctionDef, Item, Ty, TyFn, varUnreachable } from "./ast";
import * as wasm from "./wasm/defs";
type StringifiedForMap<T> = string;
type Context = {
mod: wasm.Module;
funcTypes: Map<StringifiedForMap<wasm.FuncType>, wasm.TypeIdx>;
funcIndices: Map<number, wasm.FuncIdx>;
};
function internFuncType(cx: Context, type: wasm.FuncType): wasm.TypeIdx {
const s = JSON.stringify(type);
const existing = cx.funcTypes.get(s);
if (existing !== undefined) {
return existing;
}
const idx = cx.mod.types.length;
cx.mod.types.push(type);
cx.funcTypes.set(s, idx);
return idx;
}
export function lower(ast: Ast): wasm.Module {
const mod: wasm.Module = {
types: [],
funcs: [],
tables: [],
mems: [],
globals: [],
elems: [],
datas: [],
imports: [],
exports: [],
};
const cx: Context = { mod, funcTypes: new Map(), funcIndices: new Map() };
ast.forEach((item) => {
switch (item.kind) {
case "function": {
const fcx: FuncContext = {
cx,
item,
func: item.node,
};
lowerFunc(fcx);
}
}
});
return mod;
}
type FuncContext = {
cx: Context;
item: Item;
func: FunctionDef;
};
type Abi = { params: ArgAbi[]; ret: RetAbi };
type ArgAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
type RetAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
function lowerFunc(fcx: FuncContext) {
const abi = computeAbi(fcx.func.ty!);
const wasmType = wasmTypeForAbi(abi);
const type = internFuncType(fcx.cx, wasmType);
const wasmFunc: wasm.Func = {
type,
locals: [],
body: [],
};
const idx = fcx.cx.mod.funcs.length;
fcx.cx.mod.funcs.push(wasmFunc);
fcx.cx.funcIndices.set(fcx.item.id, idx);
}
function computeAbi(ty: TyFn): Abi {
const scalar = (type: wasm.ValType): ArgAbi & RetAbi =>
({ kind: "scalar", type } as const);
const zst: ArgAbi & RetAbi = { kind: "zst" };
function paramAbi(param: Ty): ArgAbi {
switch (param.kind) {
case "string":
todo("string abi");
case "fn":
todo("fn abi");
case "int":
return scalar("i64");
case "bool":
return scalar("i32");
case "list":
todo("list abi");
case "tuple":
if (param.elems.length === 0) {
return zst;
} else if (param.elems.length === 1) {
return paramAbi(param.elems[0]);
}
todo("complex tuple abi");
case "var":
varUnreachable();
}
}
const params = ty.params.map(paramAbi);
let ret: RetAbi;
switch (ty.returnTy.kind) {
case "string":
todo("string abi");
case "fn":
todo("fn abi");
case "int":
ret = scalar("i64");
break;
case "bool":
ret = scalar("i32");
break;
case "list":
todo("list abi");
case "tuple":
if (ty.returnTy.elems.length === 0) {
ret = zst;
break;
} else if (ty.returnTy.elems.length === 1) {
ret = paramAbi(ty.returnTy.elems[0]);
break;
}
todo("complex tuple abi");
case "var":
varUnreachable();
}
return { params, ret };
}
function wasmTypeForAbi(abi: Abi): wasm.FuncType {
const params = abi.params
.map((arg) => {
switch (arg.kind) {
case "scalar":
return arg.type;
case "zst":
return undefined;
}
})
.filter(exists);
let returns: wasm.ValType[];
switch (abi.ret.kind) {
case "scalar":
returns = [abi.ret.type];
break;
case "zst":
returns = [];
break;
}
return { params, returns };
}
function todo(msg: string): never {
throw new Error(`TODO: ${msg}`);
}
function exists<T>(val: T | undefined): val is T {
return val !== undefined;
}

View file

@ -10,6 +10,7 @@ import {
foldAst, foldAst,
Folder, Folder,
Identifier, Identifier,
ItemId,
LOGICAL_KINDS, LOGICAL_KINDS,
Resolution, Resolution,
Ty, Ty,
@ -29,7 +30,7 @@ function builtinAsTy(name: string, span: Span): Ty {
return TY_STRING; return TY_STRING;
} }
case "Int": { case "Int": {
return TY_BOOL; return TY_INT;
} }
case "Bool": { case "Bool": {
return TY_BOOL; return TY_BOOL;
@ -92,7 +93,7 @@ function lowerAstTyBase(
export function typeck(ast: Ast): Ast { export function typeck(ast: Ast): Ast {
const itemTys = new Map<number, Ty | null>(); const itemTys = new Map<number, Ty | null>();
function typeOfItem(index: number): Ty { function typeOfItem(index: ItemId): Ty {
const ty = itemTys.get(index); const ty = itemTys.get(index);
if (ty) { if (ty) {
return ty; return ty;
@ -147,7 +148,6 @@ export function typeck(ast: Ast): Ast {
...item.node.returnType, ...item.node.returnType,
ty: fnTy.returnTy, ty: fnTy.returnTy,
}; };
return { return {
kind: "function", kind: "function",
node: { node: {
@ -158,10 +158,10 @@ export function typeck(ast: Ast): Ast {
})), })),
body, body,
returnType, returnType,
ty: fnTy,
}, },
span: item.span, span: item.span,
id: item.id, id: item.id,
ty: fnTy,
}; };
} }
} }

View file

@ -3,7 +3,6 @@
// Base types. // Base types.
export type Vec<T> = T[];
export type u32 = number; export type u32 = number;
export type u64 = number; export type u64 = number;
export type f32 = number; export type f32 = number;
@ -21,7 +20,7 @@ export type Reftype = "funcref" | "externref";
export type ValType = Numtype | Vectype | Reftype; export type ValType = Numtype | Vectype | Reftype;
export type ResultType = Vec<ValType>; export type ResultType = ValType[];
export type FuncType = { export type FuncType = {
params: ResultType; params: ResultType;
@ -279,16 +278,16 @@ export type Expr = Instr[];
// Modules // Modules
export type Module = { export type Module = {
types: Vec<FuncType>; types: FuncType[];
funcs: Vec<Func>; funcs: Func[];
tables: Vec<Table>; tables: Table[];
mems: Vec<Mem>; mems: Mem[];
globals: Vec<Global>; globals: Global[];
elems: Vec<Elem>; elems: Elem[];
datas: Vec<Data>; datas: Data[];
start?: Start; start?: Start;
imports: Vec<Import>; imports: Import[];
exports: Vec<Export>; exports: Export[];
_name?: string; _name?: string;
}; };
@ -304,7 +303,7 @@ export type LabelIdx = u32;
export type Func = { export type Func = {
type: TypeIdx; type: TypeIdx;
locals: Vec<ValType>; locals: ValType[];
body: Expr; body: Expr;
_name?: string; _name?: string;
}; };

View file

@ -79,7 +79,7 @@ const EXAMPLE_MODULE: Module = {
}; };
it("should print a Wasm module with the correct formatting", () => { it("should print a Wasm module with the correct formatting", () => {
const wat = writeModuleWatToString(EXAMPLE_MODULE); const wat = writeModuleWatToString(EXAMPLE_MODULE, false);
expect(wat).toMatchInlineSnapshot(` expect(wat).toMatchInlineSnapshot(`
"(module $example "(module $example

View file

@ -1,6 +1,7 @@
// This module converts the Wasm definitions to the WAT // This module converts the Wasm definitions to the WAT
// WebAssembly text format for easier debugging and inspection. // WebAssembly text format for easier debugging and inspection.
import chalk from "chalk";
import { import {
Blocktype, Blocktype,
Data, Data,
@ -23,17 +24,21 @@ import {
ValType, ValType,
} from "./defs"; } from "./defs";
const identity = (s: string) => s;
class Formatter { class Formatter {
print: (chunk: string) => void; print: (chunk: string) => void;
indentation: number; indentation: number;
wordsInSexpr: number[]; wordsInSexpr: number[];
freshLinebreak: boolean; freshLinebreak: boolean;
color: boolean;
constructor(print: (chunk: string) => void) { constructor(print: (chunk: string) => void, 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;
} }
linebreak() { linebreak() {
@ -47,6 +52,11 @@ class Formatter {
} }
breakDedent() { breakDedent() {
this.indentation--; this.indentation--;
if (this.indentation < 0) {
throw new Error(
"Cannot dedent from 0 indents, there are more dedents than indents"
);
}
this.linebreak(); this.linebreak();
} }
@ -56,14 +66,26 @@ class Formatter {
this.endSexpr(); this.endSexpr();
} }
word(word: string | number) { keyword(word: string) {
this.word(word, chalk.blue);
}
type(word: string | number) {
this.word(word, chalk.green);
}
word(word: string | number, color: (s: string) => string = identity) {
const last = this.wordsInSexpr.length - 1; const last = this.wordsInSexpr.length - 1;
if (this.wordsInSexpr[last] > 0 && !this.freshLinebreak) { if (this.wordsInSexpr[last] > 0 && !this.freshLinebreak) {
// The first word hugs the left parenthesis. // The first word hugs the left parenthesis.
this.print(" "); this.print(" ");
} }
this.freshLinebreak = false; this.freshLinebreak = false;
if (this.color) {
this.print(color(String(word)));
} else {
this.print(String(word)); this.print(String(word));
}
this.wordsInSexpr[last]++; this.wordsInSexpr[last]++;
} }
@ -78,10 +100,10 @@ class Formatter {
} }
} }
export function writeModuleWatToString(module: Module): string { export function writeModuleWatToString(module: Module, color = true): 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)); printModule(module, new Formatter(writer, color));
return parts.join(""); return parts.join("");
} }
@ -108,19 +130,19 @@ function printId(id: string | undefined, f: Formatter) {
// types // types
function printValType(type: ValType, f: Formatter) { function printValType(type: ValType, f: Formatter) {
f.word(type); f.type(type);
} }
function printFuncType(type: FuncType, f: Formatter) { function printFuncType(type: FuncType, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("func"); f.keyword("func");
f.sexpr(() => { f.sexpr(() => {
f.word("param"); f.keyword("param");
type.params.forEach(f.word.bind(f)); type.params.forEach((param) => printValType(param, f));
}); });
f.sexpr(() => { f.sexpr(() => {
f.word("result"); f.keyword("result");
type.returns.forEach(f.word.bind(f)); type.returns.forEach((type) => printValType(type, f));
}); });
}); });
} }
@ -132,7 +154,7 @@ function printLimits(limits: Limits, f: Formatter) {
function printTableType(type: TableType, f: Formatter) { function printTableType(type: TableType, f: Formatter) {
printLimits(type.limits, f); printLimits(type.limits, f);
f.word(type.reftype); printValType(type.reftype, f);
} }
function printGlobalType(type: GlobalType, f: Formatter) { function printGlobalType(type: GlobalType, f: Formatter) {
@ -140,7 +162,7 @@ function printGlobalType(type: GlobalType, f: Formatter) {
printValType(type.type, f); printValType(type.type, f);
} else { } else {
f.sexpr(() => { f.sexpr(() => {
f.word("mut"); f.keyword("mut");
printValType(type.type, f); printValType(type.type, f);
}); });
} }
@ -150,9 +172,9 @@ function printGlobalType(type: GlobalType, f: Formatter) {
function printBlockType(type: Blocktype, f: Formatter) { function printBlockType(type: Blocktype, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("type"); f.keyword("type");
if (type.kind === "typeidx") { if (type.kind === "typeidx") {
f.word(type.idx); f.type(type.idx);
} else if (type.type !== undefined) { } else if (type.type !== undefined) {
printValType(type.type, f); printValType(type.type, f);
} }
@ -375,7 +397,7 @@ function printInstr(instr: Instr, f: Formatter) {
break; break;
case "br_table": case "br_table":
f.word(instr.kind); f.word(instr.kind);
instr.labels.forEach(f.word.bind(f)); instr.labels.forEach((label) => f.word(label));
f.word(instr.label); f.word(instr.label);
break; break;
case "call": case "call":
@ -454,14 +476,14 @@ function printInstr(instr: Instr, f: Formatter) {
function printType(type: FuncType, f: Formatter) { function printType(type: FuncType, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("type"); f.keyword("type");
printFuncType(type, f); printFuncType(type, f);
}); });
} }
function printImport(import_: Import, f: Formatter) { function printImport(import_: Import, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("import"); f.keyword("import");
printString(import_.module, f); printString(import_.module, f);
printString(import_.name, f); printString(import_.name, f);
@ -471,8 +493,8 @@ function printImport(import_: Import, f: Formatter) {
switch (desc.kind) { switch (desc.kind) {
case "func": case "func":
f.sexpr(() => { f.sexpr(() => {
f.word("type"); f.keyword("type");
f.word(desc.type); f.type(desc.type);
}); });
break; break;
case "table": case "table":
@ -491,33 +513,35 @@ function printImport(import_: Import, f: Formatter) {
function printFunction(func: Func, f: Formatter) { function printFunction(func: Func, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("func"); f.keyword("func");
printId(func._name, f); printId(func._name, f);
f.sexpr(() => { f.sexpr(() => {
f.word("type"); f.keyword("type");
f.word(func.type); f.type(func.type);
}); });
if (func.locals.length > 0 || func.body.length > 0) {
f.breakIndent(); f.breakIndent();
}
if (func.locals.length > 0) { if (func.locals.length > 0) {
f.sexpr(() => { f.sexpr(() => {
f.word("local"); f.keyword("local");
func.locals.forEach((local) => printValType(local, f)); func.locals.forEach((local) => printValType(local, f));
}); });
}
f.linebreak(); f.linebreak();
}
if (func.body.length > 0) {
printInstrBlock(func.body, f); printInstrBlock(func.body, f);
}
}); });
} }
function printTable(table: Table, f: Formatter) { function printTable(table: Table, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("table"); f.keyword("table");
printId(table._name, f); printId(table._name, f);
printTableType(table.type, f); printTableType(table.type, f);
}); });
@ -525,7 +549,7 @@ function printTable(table: Table, f: Formatter) {
function printMem(mem: Mem, f: Formatter) { function printMem(mem: Mem, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("memory"); f.keyword("memory");
printId(mem._name, f); printId(mem._name, f);
printLimits(mem.type, f); printLimits(mem.type, f);
@ -534,7 +558,7 @@ function printMem(mem: Mem, f: Formatter) {
function printGlobal(global: Global, f: Formatter) { function printGlobal(global: Global, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("global"); f.keyword("global");
printId(global._name, f); printId(global._name, f);
printGlobalType(global.type, f); printGlobalType(global.type, f);
@ -548,7 +572,7 @@ function printExport(export_: Export, f: Formatter) {
const desc = export_.desc; const desc = export_.desc;
f.sexpr(() => { f.sexpr(() => {
f.word("export"); f.keyword("export");
printString(export_.name, f); printString(export_.name, f);
f.sexpr(() => { f.sexpr(() => {
@ -560,7 +584,7 @@ function printExport(export_: Export, f: Formatter) {
function printStart(start: Start, f: Formatter) { function printStart(start: Start, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("start"); f.keyword("start");
f.word(start.func); f.word(start.func);
}); });
} }
@ -573,14 +597,14 @@ function printData(data: Data, f: Formatter) {
let mode = data.mode; let mode = data.mode;
f.sexpr(() => { f.sexpr(() => {
f.word("data"); f.keyword("data");
printId(data._name, f); printId(data._name, f);
if (mode.kind === "active") { if (mode.kind === "active") {
const active: DatamodeActive = mode; const active: DatamodeActive = mode;
if (active.memory !== 0) { if (active.memory !== 0) {
f.sexpr(() => { f.sexpr(() => {
f.word("memory"); f.keyword("memory");
f.word(active.memory); f.word(active.memory);
}); });
} }
@ -589,7 +613,7 @@ function printData(data: Data, f: Formatter) {
if (active.offset.length === 1) { if (active.offset.length === 1) {
printInstr(active.offset[0], f); printInstr(active.offset[0], f);
} else { } else {
f.word("offset"); f.keyword("offset");
f.linebreak(); f.linebreak();
printInstrBlock(active.offset, f); printInstrBlock(active.offset, f);
} }
@ -602,7 +626,7 @@ function printData(data: Data, f: Formatter) {
function printModule(module: Module, f: Formatter) { function printModule(module: Module, f: Formatter) {
f.sexpr(() => { f.sexpr(() => {
f.word("module"); f.keyword("module");
printId(module._name, f); printId(module._name, f);
f.breakIndent(); f.breakIndent();