mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
avoid wasm symbol conflicts
This commit is contained in:
parent
6bdbf14ecb
commit
e951cd5ee1
8 changed files with 188 additions and 99 deletions
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
32
src/index.ts
32
src/index.ts
|
|
@ -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 });
|
||||||
|
|
|
||||||
13
src/lexer.ts
13
src/lexer.ts
|
|
@ -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 === "_"
|
||||||
|
|
|
||||||
100
src/lower.ts
100
src/lower.ts
|
|
@ -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 }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
src/utils.ts
23
src/utils.ts
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue