From c4cfa19fa9c39661acee86ab4b0df5ea04fd1b2a Mon Sep 17 00:00:00 2001
From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com>
Date: Mon, 31 Jul 2023 18:22:59 +0200
Subject: [PATCH] crates work
---
src/ast.ts | 27 +++++++++++++++++++++-----
src/error.ts | 7 +++++--
src/index.ts | 5 +++--
src/parser.ts | 29 ++++++++++++++++++----------
src/resolve.ts | 22 +++++++++++----------
src/typeck.ts | 52 ++++++++++++++++++++++++++++++++++++++++----------
src/utils.ts | 32 +++++++++++++++++++++----------
7 files changed, 125 insertions(+), 49 deletions(-)
diff --git a/src/ast.ts b/src/ast.ts
index cfba0da..fd74c0c 100644
--- a/src/ast.ts
+++ b/src/ast.ts
@@ -1,5 +1,6 @@
import { Span } from "./error";
import { LitIntType } from "./lexer";
+import { ComplexMap } from "./utils";
export type Phase = {
res: unknown;
@@ -52,7 +53,7 @@ export type CrateId = number;
export type Crate
= {
id: CrateId;
rootItems: Item
[];
- itemsById: Map>;
+ itemsById: ComplexMap>;
packageName: string;
} & P["typeckResults"];
@@ -66,7 +67,23 @@ export type IdentWithRes = {
span: Span;
} & P["res"];
-export type ItemId = number;
+export class ItemId {
+ public crateId: number;
+ public itemIdx: number;
+
+ constructor(crateId: number, itemIdx: number) {
+ this.crateId = crateId;
+ this.itemIdx = itemIdx;
+ }
+
+ static dummy(): ItemId {
+ return new ItemId(999999, 999999);
+ }
+
+ toString(): string {
+ return `[${this.crateId}@${this.itemIdx}]`;
+ }
+}
export type ItemKind
=
| {
@@ -495,7 +512,7 @@ export type TypeckResults = {
export type FoldFn = (value: From) => To;
export type Folder = {
- newItemsById: Map>;
+ newItemsById: ComplexMap>;
/**
* This should not be overridden.
*/
@@ -507,7 +524,7 @@ export type Folder = {
};
type ItemFolder = {
- newItemsById: Map>;
+ newItemsById: ComplexMap>;
item: FoldFn- , Item>;
itemInner: FoldFn
- , Item>;
};
@@ -519,7 +536,7 @@ export function mkDefaultFolder<
To extends Phase
>(): ItemFolder {
const folder: ItemFolder = {
- newItemsById: new Map(),
+ newItemsById: new ComplexMap(),
item(item) {
const newItem = this.itemInner(item);
this.newItemsById.set(newItem.id, newItem);
diff --git a/src/error.ts b/src/error.ts
index b499361..677d5e1 100644
--- a/src/error.ts
+++ b/src/error.ts
@@ -23,7 +23,11 @@ export class CompilerError extends Error {
}
}
-export function withErrorPrinter(input: string, filename: string, f: () => void): void {
+export function withErrorPrinter(
+ input: string,
+ filename: string,
+ f: () => void
+): void {
try {
f();
} catch (e) {
@@ -50,7 +54,6 @@ function renderError(input: string, filename: string, e: CompilerError) {
const lineNo = lineIdx + 1;
console.error(`error: ${e.message}`);
console.error(` --> ${filename}:${lineNo}`);
-
console.error(`${lineNo} | ${spanToSnippet(input, line)}`);
const startRelLine =
diff --git a/src/index.ts b/src/index.ts
index 4aae1ea..4a4e871 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -72,7 +72,7 @@ function main() {
console.log(resolvedPrinted);
console.log("-----AST typecked------");
- const typecked: Crate = typeck(resolved);
+ const typecked: Crate = typeck(resolved, crates);
const typeckPrinted = printAst(typecked);
console.log(typeckPrinted);
@@ -135,7 +135,8 @@ function loadCrate(
const tokens = tokenize(input);
const ast = parse(name, tokens, crateId.next());
const [resolved, crates] = resolve(ast, loadCrate);
- const typecked = typeck(resolved);
+
+ const typecked = typeck(resolved, [...existingCrates, ...crates]);
return [typecked, crates];
} catch (e) {
withErrorPrinter(input, filename, () => {
diff --git a/src/parser.ts b/src/parser.ts
index 7f213c0..2783ca0 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -28,10 +28,11 @@ import {
Built,
Parsed,
ExternItem,
+ ItemId,
} from "./ast";
import { CompilerError, Span, spanMerge } from "./error";
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
-import { Ids } from "./utils";
+import { ComplexMap, ComplexSet, Ids } from "./utils";
type Parser = (t: Token[]) => [Token[], T];
@@ -81,7 +82,7 @@ function parseItem(t: Token[]): [Token[], Item] {
node: def,
span: tok.span,
// Assigned later.
- id: 0,
+ id: ItemId.dummy(),
},
];
} else if (tok.kind === "type") {
@@ -116,7 +117,10 @@ function parseItem(t: Token[]): [Token[], Item] {
fields,
};
- return [t, { kind: "type", node: def, span: name.span, id: 0 }];
+ return [
+ t,
+ { kind: "type", node: def, span: name.span, id: ItemId.dummy() },
+ ];
} else if (tok.kind === "import") {
[t] = expectNext(t, "(");
let module;
@@ -136,7 +140,10 @@ function parseItem(t: Token[]): [Token[], Item] {
...sig,
};
- return [t, { kind: "import", node: def, span: tok.span, id: 0 }];
+ return [
+ t,
+ { kind: "import", node: def, span: tok.span, id: ItemId.dummy() },
+ ];
} else if (tok.kind === "extern") {
[t] = expectNext(t, "mod");
let name;
@@ -148,7 +155,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ";");
- return [t, { kind: "extern", node, span: name.span, id: 0 }];
+ return [t, { kind: "extern", node, span: name.span, id: ItemId.dummy() }];
} else if (tok.kind === "mod") {
let name;
[t, name] = expectNext(t, "identifier");
@@ -172,7 +179,7 @@ function parseItem(t: Token[]): [Token[], Item] {
contents,
};
- return [t, { kind: "mod", node, span: name.span, id: 0 }];
+ return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }];
} else {
unexpectedToken(tok, "item");
}
@@ -663,13 +670,15 @@ function unexpectedToken(token: Token, expected: string): never {
}
function validateAst(ast: Crate) {
- const seenItemIds = new Set();
+ const seenItemIds = new ComplexSet();
const validator: Folder = {
...mkDefaultFolder(),
itemInner(item: Item): Item {
if (seenItemIds.has(item.id)) {
- throw new Error(`duplicate item id: ${item.id} for ${item.node.name}`);
+ throw new Error(
+ `duplicate item id: ${item.id.toString()} for ${item.node.name}`
+ );
}
seenItemIds.add(item.id);
return superFoldItem(item, this);
@@ -734,14 +743,14 @@ function buildCrate(
const ast: Crate = {
id: crateId,
rootItems,
- itemsById: new Map(),
+ itemsById: new ComplexMap(),
packageName,
};
const assigner: Folder = {
...mkDefaultFolder(),
itemInner(item: Item): Item {
- const id = itemId.next();
+ const id = new ItemId(crateId, itemId.next());
return { ...superFoldItem(item, this), id };
},
expr(expr: Expr): Expr {
diff --git a/src/resolve.ts b/src/resolve.ts
index b46a9b6..fd47ec8 100644
--- a/src/resolve.ts
+++ b/src/resolve.ts
@@ -20,7 +20,7 @@ import {
Typecked,
} from "./ast";
import { CompilerError, Span, spanMerge } from "./error";
-import { Ids, unwrap } from "./utils";
+import { ComplexMap, Ids, unwrap } from "./utils";
const BUILTIN_SET = new Set(BUILTINS);
@@ -34,8 +34,8 @@ export type CrateLoader = (
type Context = {
ast: Crate;
crates: Crate[];
- modContentsCache: Map>;
- newItemsById: Map>;
+ modContentsCache: ComplexMap>;
+ newItemsById: ComplexMap>;
crateLoader: CrateLoader;
crateId: Ids;
};
@@ -56,15 +56,21 @@ function resolveModItem(
if ("contents" in mod) {
contents = new Map(mod.contents.map((item) => [item.node.name, item.id]));
} else {
+ console.log("resolve...");
+
const [loadedCrate, itsDeps] = cx.crateLoader(
item.node.name,
item.span,
cx.crateId,
cx.crates
);
+ console.log("hmm");
+
cx.crates.push(loadedCrate);
cx.crates.push(...itsDeps);
+ console.log(cx.crates);
+
contents = new Map(
loadedCrate.rootItems.map((item) => [item.node.name, item.id])
);
@@ -83,8 +89,8 @@ export function resolve(
const cx: Context = {
ast,
crates: [],
- modContentsCache: new Map(),
- newItemsById: new Map(),
+ modContentsCache: new ComplexMap(),
+ newItemsById: new ComplexMap(),
crateLoader,
crateId,
};
@@ -106,7 +112,7 @@ function resolveModule(
modName: string[],
contents: Item[]
): Item[] {
- const items = new Map();
+ const items = new Map();
contents.forEach((item) => {
const existing = items.get(item.node.name);
@@ -266,11 +272,7 @@ function resolveModule(
if (res.kind === "item") {
const module = unwrap(cx.ast.itemsById.get(res.id));
- console.log("nested", module.kind, res.id, cx.ast.itemsById);
-
if (module.kind === "mod" || module.kind === "extern") {
- console.log("resolve");
-
if (typeof expr.field.value === "number") {
throw new CompilerError(
"module contents cannot be indexed with a number",
diff --git a/src/typeck.ts b/src/typeck.ts
index 0fb2ca6..2d2dc75 100644
--- a/src/typeck.ts
+++ b/src/typeck.ts
@@ -32,7 +32,7 @@ import {
} from "./ast";
import { CompilerError, Span } from "./error";
import { printTy } from "./printer";
-import { unwrap } from "./utils";
+import { ComplexMap, unwrap } from "./utils";
function mkTyFn(params: Ty[], returnTy: Ty): Ty {
return { kind: "fn", params, returnTy };
@@ -89,7 +89,7 @@ function typeOfBuiltinValue(name: BuiltinName, span: Span): Ty {
function lowerAstTyBase(
type: Type,
lowerIdentTy: (ident: IdentWithRes) => Ty,
- typeOfItem: (index: number, cause: Span) => Ty
+ typeOfItem: (itemId: ItemId, cause: Span) => Ty
): Ty {
switch (type.kind) {
case "ident": {
@@ -115,19 +115,51 @@ function lowerAstTyBase(
}
}
-export function typeck(ast: Crate): Crate {
- const itemTys = new Map();
- function typeOfItem(index: ItemId, cause: Span): Ty {
- const item = unwrap(ast.itemsById.get(index));
+export function typeck(
+ ast: Crate,
+ otherCrates: Crate[]
+): Crate {
+ const itemTys = new ComplexMap();
+ function typeOfItem(itemId: ItemId, cause: Span): Ty {
+ if (itemId.crateId !== ast.id) {
+ console.log(otherCrates);
- const ty = itemTys.get(index);
+ const crate = unwrap(
+ otherCrates.find((crate) => crate.id === itemId.crateId)
+ );
+ const item = unwrap(crate.itemsById.get(itemId));
+ switch (item.kind) {
+ case "function":
+ case "import":
+ case "type":
+ return item.node.ty!;
+ case "mod": {
+ throw new CompilerError(
+ `module ${item.node.name} cannot be used as a type or value`,
+ cause
+ );
+ }
+ case "extern": {
+ throw new CompilerError(
+ `extern declaration ${item.node.name} cannot be used as a type or value`,
+ cause
+ );
+ }
+ }
+ }
+
+ const item = unwrap(ast.itemsById.get(itemId));
+ const ty = itemTys.get(itemId);
if (ty) {
return ty;
}
if (ty === null) {
- throw new CompilerError(`cycle computing type of #G${index}`, item.span);
+ throw new CompilerError(
+ `cycle computing type of #G${itemId.toString()}`,
+ item.span
+ );
}
- itemTys.set(index, null);
+ itemTys.set(itemId, null);
switch (item.kind) {
case "function":
case "import": {
@@ -528,7 +560,7 @@ export class InferContext {
export function checkBody(
body: Expr,
fnTy: TyFn,
- typeOfItem: (index: number, cause: Span) => Ty
+ typeOfItem: (itemId: ItemId, cause: Span) => Ty
): Expr {
const localTys = [...fnTy.params];
const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];
diff --git a/src/utils.ts b/src/utils.ts
index 826e62e..d760905 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -22,20 +22,32 @@ export function unwrap(value: T | undefined): T {
* It uses JSON+string equality instead of refernece equality.
*/
export class ComplexMap {
- inner = new Map();
+ private inner = new Map();
public get(key: K): V | undefined {
- return this.inner.get(this.mangleKey(key));
+ return this.inner.get(mangleKey(key));
}
public set(key: K, value: V): void {
- 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);
+ this.inner.set(mangleKey(key), value);
}
}
+
+export class ComplexSet {
+ private inner = new Set();
+
+ public has(key: K): boolean {
+ return this.inner.has(mangleKey(key));
+ }
+
+ public add(key: K): void {
+ this.inner.add(mangleKey(key));
+ }
+}
+
+function mangleKey(key: K): string | number {
+ if (typeof key === "string" || typeof key === "number") {
+ return key;
+ }
+ return JSON.stringify(key);
+}