mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-16 17:35:02 +01:00
start lowering
This commit is contained in:
parent
87f081a4fe
commit
ccd8008731
9 changed files with 275 additions and 71 deletions
11
package-lock.json
generated
11
package-lock.json
generated
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
18
src/index.ts
18
src/index.ts
|
|
@ -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
175
src/lower.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue