refactorings

This commit is contained in:
nora 2023-08-02 19:18:52 +02:00
parent 7eeaf548d0
commit 2f1f4a9798
12 changed files with 85 additions and 100 deletions

View file

@ -1,6 +1,6 @@
import { DUMMY_SPAN, LoadedFile, Span } from "./error";
import { LoadedFile, Span } from "./error";
import { LitIntType } from "./lexer";
import { ComplexMap, unwrap } from "./utils";
import { ComplexMap } from "./utils";
export type Phase = {
res: unknown;
@ -57,7 +57,7 @@ export type Crate<P extends Phase> = {
rootItems: Item<P>[];
itemsById: ComplexMap<ItemId, Item<P>>;
packageName: string;
rootFile: LoadedFile,
rootFile: LoadedFile;
} & P["typeckResults"];
export type DepCrate = Crate<Final>;
@ -85,6 +85,10 @@ export class ItemId {
return new ItemId(999999, 999999);
}
static crateRoot(crate: CrateId): ItemId {
return new ItemId(crate, 0);
}
toString(): string {
if (this.crateId === 0) {
return `${this.itemIdx}`;
@ -535,29 +539,6 @@ export type TypeckResults = {
main: Resolution | undefined;
};
export function findCrateItem<P extends Phase>(
crate: Crate<P>,
id: ItemId
): Item<P> {
if (id.crateId !== crate.id) {
throw new Error("trying to get item from the wrong crate");
}
if (id.itemIdx === 0) {
// Return a synthetic module representing the crate root.
return {
kind: "mod",
node: {
contents: crate.rootItems,
name: crate.packageName,
},
span: DUMMY_SPAN,
id,
};
}
return unwrap(crate.itemsById.get(id));
}
// folders
export type FoldFn<From, To> = (value: From) => To;

View file

@ -1,5 +1,5 @@
import { Crate, DepCrate, Final, Item, ItemId, Phase } from "./ast";
import { DUMMY_SPAN, Span } from "./error";
import { Span } from "./error";
import { Ids, unwrap } from "./utils";
import fs from "fs";
import path from "path";
@ -20,17 +20,19 @@ export type CrateLoader = (
* dependencies (which also use the same context) do not care about that.
*/
export class GlobalContext {
public depCrates: Crate<Final>[] = [];
public finalizedCrates: Crate<Final>[] = [];
public crateId: Ids = new Ids();
constructor(public opts: Options, public crateLoader: CrateLoader) {}
public findItem<P extends Phase>(
id: ItemId,
localCrate: Crate<P>
localCrate?: Crate<P>
): Item<P | Final> {
const crate = unwrap(
[localCrate, ...this.depCrates].find((crate) => crate.id === id.crateId)
[...(localCrate ? [localCrate] : []), ...this.finalizedCrates].find(
(crate) => crate.id === id.crateId
)
);
if (id.itemIdx === 0) {
@ -41,7 +43,7 @@ export class GlobalContext {
contents: crate.rootItems,
name: crate.packageName,
},
span: DUMMY_SPAN,
span: Span.startOfFile(crate.rootFile),
id,
};
}

View file

@ -3,30 +3,35 @@ export type LoadedFile = {
content: string;
};
export type Span = {
start: number;
end: number;
file: LoadedFile;
};
export class Span {
constructor(
public start: number,
public end: number,
public file: LoadedFile
) {}
export function spanMerge(a: Span, b: Span): Span {
if (a.file !== b.file) {
throw new Error("cannot merge spans from different files");
public merge(b: Span): Span {
if (this.file !== b.file) {
throw new Error("cannot merge spans from different files");
}
return new Span(
Math.min(this.start, b.start),
Math.max(this.end, b.end),
this.file
);
}
return {
start: Math.min(a.start, b.start),
end: Math.max(a.end, b.end),
file: a.file,
};
}
public static eof(file: LoadedFile): Span {
return new Span(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, file);
}
export const DUMMY_SPAN: Span = { start: 0, end: 0, file: { content: "" } };
export const eofSpan = (file: LoadedFile): Span => ({
start: Number.MAX_SAFE_INTEGER,
end: Number.MAX_SAFE_INTEGER,
file,
});
public static startOfFile(file: LoadedFile): Span {
return new Span(0, 1, file);
}
public static DUMMY: Span = new Span(0, 0, { content: "" });
}
export class CompilerError extends Error {
msg: string;
@ -98,11 +103,11 @@ function spanToSnippet(input: string, span: Span): string {
}
export function lines(file: LoadedFile): Span[] {
const lines: Span[] = [{ start: 0, end: 0, file }];
const lines: Span[] = [new Span(0, 0, file)];
for (let i = 0; i < file.content.length; i++) {
if (file.content[i] === "\n") {
lines.push({ start: i + 1, end: i + 1, file });
lines.push(new Span(i + 1, i + 1, file));
} else {
lines[lines.length - 1].end++;
}

View file

@ -1,4 +1,4 @@
import { LoadedFile, withErrorPrinter } from "./error";
import { LoadedFile, Span, withErrorPrinter } from "./error";
import { isValidIdent, tokenize } from "./lexer";
import { lower as lowerToWasm } from "./lower";
import { ParseState, parse } from "./parser";
@ -13,8 +13,6 @@ import { GlobalContext, parseArgs } from "./context";
import { loadCrate } from "./loader";
const INPUT = `
extern mod std;
type A = { a: Int };
function main() = (
@ -49,6 +47,8 @@ function main() {
const gcx = new GlobalContext(opts, loadCrate);
const mainCrate = gcx.crateId.next();
gcx.crateLoader(gcx, "std", Span.startOfFile(file));
withErrorPrinter(
() => {
const start = Date.now();
@ -93,7 +93,9 @@ function main() {
if (debug.has("wat")) {
console.log("-----wasm--------------");
}
const wasmModule = lowerToWasm([typecked, ...gcx.depCrates]);
gcx.finalizedCrates.push(typecked);
const wasmModule = lowerToWasm(gcx);
const moduleStringColor = writeModuleWatToString(wasmModule, true);
const moduleString = writeModuleWatToString(wasmModule);

View file

@ -3,7 +3,7 @@ import { tokenize } from "./lexer";
it("should tokenize an emtpy function", () => {
const input = `function hello() = ;`;
const tokens = tokenize(input);
const tokens = tokenize({ content: input });
expect(tokens).toMatchSnapshot();
});
@ -11,7 +11,7 @@ it("should tokenize an emtpy function", () => {
it("should tokenize hello world", () => {
const input = `print("hello world")`;
const tokens = tokenize(input);
const tokens = tokenize({ content: input });
expect(tokens).toMatchSnapshot();
});

View file

@ -92,7 +92,7 @@ export function tokenize(file: LoadedFile): Token[] {
finish: while (i < input.length) {
const next = input[i];
const span: Span = { start: i, end: i + 1, file };
const span: Span = new Span(i, i + 1, file);
if (next === "/" && input[i + 1] === "/") {
while (input[i] !== "\n") {
@ -206,7 +206,7 @@ export function tokenize(file: LoadedFile): Token[] {
default:
throw new CompilerError(
`invalid escape character: ${input[i]}`,
{ start: span.end - 1, end: span.end, file }
new Span(span.end - 1, span.end, file)
);
}
continue;

View file

@ -45,7 +45,7 @@ export const loadCrate: CrateLoader = (
// We really, really want a good algorithm for finding crates.
// But right now we just look for files in the CWD.
const existing = gcx.depCrates.find((crate) => crate.packageName === name);
const existing = gcx.finalizedCrates.find((crate) => crate.packageName === name);
if (existing) {
return existing;
}
@ -64,7 +64,7 @@ export const loadCrate: CrateLoader = (
const typecked = typeck(gcx, resolved);
gcx.depCrates.push(typecked);
gcx.finalizedCrates.push(typecked);
return typecked;
},
() => {

View file

@ -2,7 +2,6 @@ import {
Crate,
Expr,
ExprBlock,
Final,
Folder,
FunctionDef,
GlobalItem,
@ -16,12 +15,12 @@ import {
TyStruct,
TyTuple,
Typecked,
findCrateItem,
mkDefaultFolder,
superFoldExpr,
superFoldItem,
varUnreachable,
} from "./ast";
import { GlobalContext } from "./context";
import { printTy } from "./printer";
import { ComplexMap, encodeUtf8, unwrap } from "./utils";
import * as wasm from "./wasm/defs";
@ -61,7 +60,7 @@ export type Context = {
reservedHeapMemoryStart: number;
funcIndices: ComplexMap<Resolution, FuncOrImport>;
globalIndices: ComplexMap<Resolution, wasm.GlobalIdx>;
crates: Crate<Final>[];
gcx: GlobalContext;
relocations: Relocation[];
knownDefPaths: ComplexMap<string[], ItemId>;
};
@ -112,13 +111,6 @@ function appendData(cx: Context, newData: Uint8Array): number {
}
}
function findItem(cx: Context, id: ItemId): Item<Typecked> {
return findCrateItem(
unwrap(cx.crates.find((crate) => crate.id === id.crateId)),
id
);
}
const KNOWN_DEF_PATHS = [ALLOCATE_ITEM];
function getKnownDefPaths(
@ -155,8 +147,8 @@ function getKnownDefPaths(
return knows;
}
export function lower(crates: Crate<Final>[]): wasm.Module {
const knownDefPaths = getKnownDefPaths(crates);
export function lower(gcx: GlobalContext): wasm.Module {
const knownDefPaths = getKnownDefPaths(gcx.finalizedCrates);
const mod: wasm.Module = {
types: [],
@ -183,12 +175,12 @@ export function lower(crates: Crate<Final>[]): wasm.Module {
});
const cx: Context = {
gcx,
mod,
funcTypes: new ComplexMap(),
funcIndices: new ComplexMap(),
globalIndices: new ComplexMap(),
reservedHeapMemoryStart: 0,
crates,
relocations: [],
knownDefPaths,
};
@ -221,7 +213,7 @@ export function lower(crates: Crate<Final>[]): wasm.Module {
}
});
}
crates.forEach((ast) => lowerMod(ast.rootItems));
gcx.finalizedCrates.forEach((ast) => lowerMod(ast.rootItems));
const HEAP_ALIGN = 0x08;
cx.reservedHeapMemoryStart =
@ -229,7 +221,7 @@ export function lower(crates: Crate<Final>[]): wasm.Module {
? (mod.datas[0].init.length + (HEAP_ALIGN - 1)) & ~(HEAP_ALIGN - 1)
: 0;
addRt(cx, crates);
addRt(cx, gcx.finalizedCrates);
// THE LINKER
const offset = cx.mod.imports.length;
@ -447,7 +439,7 @@ function lowerExpr(
break;
}
case "item": {
const item = findItem(fcx.cx, res.id);
const item = fcx.cx.gcx.findItem(res.id);
if (item.kind !== "global") {
throw new Error("cannot store to non-global item");
}
@ -529,7 +521,7 @@ function lowerExpr(
break;
}
case "item": {
const item = findItem(fcx.cx, res.id);
const item = fcx.cx.gcx.findItem(res.id);
switch (item.kind) {
case "global": {
const instr: wasm.Instr = { kind: "global.get", imm: DUMMY_IDX };

View file

@ -32,7 +32,7 @@ import {
GlobalItem,
StructLiteralField,
} from "./ast";
import { CompilerError, eofSpan, LoadedFile, Span, spanMerge } from "./error";
import { CompilerError, LoadedFile, Span } from "./error";
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
import { ComplexMap, ComplexSet, Ids } from "./utils";
@ -297,7 +297,7 @@ function mkParserExprBinary(
[t, tok] = next(t);
let rhs;
[t, rhs] = parser(t);
const span = spanMerge(lhs.span, rhs.span);
const span = lhs.span.merge(rhs.span);
return [t, mkExpr(lhs, rhs, span, tok.kind)];
}
@ -380,7 +380,7 @@ function parseExprCall(t: State): [State, Expr<Parsed>] {
kind: "fieldAccess",
lhs,
field: { span: access.span, value },
span: spanMerge(lhs.span, access.span),
span: lhs.span.merge(access.span),
};
}
}
@ -676,7 +676,7 @@ function expectNext<T extends BaseToken>(
if (!tok) {
throw new CompilerError(
`expected \`${kind}\`, found end of file`,
eofSpan(t.file)
Span.eof(t.file)
);
}
if (tok.kind !== kind) {
@ -691,7 +691,7 @@ function expectNext<T extends BaseToken>(
function next(t: State): [State, Token] {
const [rest, next] = maybeNextT(t);
if (!next) {
throw new CompilerError("unexpected end of file", eofSpan(t.file));
throw new CompilerError("unexpected end of file", Span.eof(t.file));
}
return [rest, next];
}

View file

@ -19,7 +19,7 @@ import {
ExternItem,
} from "./ast";
import { GlobalContext } from "./context";
import { CompilerError, spanMerge } from "./error";
import { CompilerError } from "./error";
import { ComplexMap } from "./utils";
const BUILTIN_SET = new Set<string>(BUILTINS);
@ -128,11 +128,14 @@ function resolveModule(
};
}
if (ident.name === cx.ast.packageName) {
return {
kind: "item",
id: new ItemId(cx.ast.id, 0),
};
// All loaded crates are in scope.
for (const crate of [cx.ast, ...cx.gcx.finalizedCrates]) {
if (ident.name === crate.packageName) {
return {
kind: "item",
id: ItemId.crateRoot(crate.id),
};
}
}
if (BUILTIN_SET.has(ident.name)) {
@ -278,7 +281,7 @@ function resolveModule(
kind: "path",
segments: [...segments, expr.field.value],
res: pathRes,
span: spanMerge(lhs.span, expr.field.span),
span: lhs.span.merge(expr.field.span),
};
}
}

View file

@ -1,7 +1,9 @@
import { TY_INT, TY_STRING, TY_UNIT } from "./ast";
import { DUMMY_SPAN as SPAN } from "./error";
import { Span } from "./error";
import { InferContext } from "./typeck";
const SPAN: Span = Span.startOfFile({content: ""});
it("should infer types across assignments", () => {
const infcx = new InferContext();

View file

@ -28,7 +28,6 @@ import {
Typecked,
TyStruct,
Item,
findCrateItem,
StructLiteralField,
} from "./ast";
import { GlobalContext } from "./context";
@ -154,7 +153,7 @@ export function typeck(
}
}
const item = findCrateItem(ast, itemId);
const item = gcx.findItem(itemId, ast);
const ty = itemTys.get(itemId);
if (ty) {
return ty;
@ -420,11 +419,10 @@ export function typeck(
if (ast.id === 0) {
// Only the final id=0 crate needs and cares about main.
if (!main) {
throw new CompilerError(`\`main\` function not found`, {
start: 0,
end: 1,
file: ast.rootFile,
});
throw new CompilerError(
`\`main\` function not found`,
Span.startOfFile(ast.rootFile)
);
}
typecked.typeckResults = {