crates work

This commit is contained in:
nora 2023-07-31 18:22:59 +02:00
parent b3b0bc2e6f
commit c4cfa19fa9
7 changed files with 125 additions and 49 deletions

View file

@ -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<P extends Phase> = {
id: CrateId;
rootItems: Item<P>[];
itemsById: Map<ItemId, Item<P>>;
itemsById: ComplexMap<ItemId, Item<P>>;
packageName: string;
} & P["typeckResults"];
@ -66,7 +67,23 @@ export type IdentWithRes<P extends Phase> = {
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<P extends Phase> =
| {
@ -495,7 +512,7 @@ export type TypeckResults = {
export type FoldFn<From, To> = (value: From) => To;
export type Folder<From extends Phase, To extends Phase> = {
newItemsById: Map<ItemId, Item<To>>;
newItemsById: ComplexMap<ItemId, Item<To>>;
/**
* This should not be overridden.
*/
@ -507,7 +524,7 @@ export type Folder<From extends Phase, To extends Phase> = {
};
type ItemFolder<From extends Phase, To extends Phase> = {
newItemsById: Map<ItemId, Item<To>>;
newItemsById: ComplexMap<ItemId, Item<To>>;
item: FoldFn<Item<From>, Item<To>>;
itemInner: FoldFn<Item<From>, Item<To>>;
};
@ -519,7 +536,7 @@ export function mkDefaultFolder<
To extends Phase
>(): ItemFolder<From, To> {
const folder: ItemFolder<From, To> = {
newItemsById: new Map(),
newItemsById: new ComplexMap(),
item(item) {
const newItem = this.itemInner(item);
this.newItemsById.set(newItem.id, newItem);

View file

@ -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 =

View file

@ -72,7 +72,7 @@ function main() {
console.log(resolvedPrinted);
console.log("-----AST typecked------");
const typecked: Crate<Typecked> = typeck(resolved);
const typecked: Crate<Typecked> = 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, () => {

View file

@ -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> = (t: Token[]) => [Token[], T];
@ -81,7 +82,7 @@ function parseItem(t: Token[]): [Token[], Item<Parsed>] {
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<Parsed>] {
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<Parsed>] {
...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<Parsed>] {
[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<TokenIdent>(t, "identifier");
@ -172,7 +179,7 @@ function parseItem(t: Token[]): [Token[], Item<Parsed>] {
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<Built>) {
const seenItemIds = new Set();
const seenItemIds = new ComplexSet();
const validator: Folder<Built, Built> = {
...mkDefaultFolder(),
itemInner(item: Item<Built>): Item<Built> {
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<Built> = {
id: crateId,
rootItems,
itemsById: new Map(),
itemsById: new ComplexMap(),
packageName,
};
const assigner: Folder<Parsed, Built> = {
...mkDefaultFolder(),
itemInner(item: Item<Parsed>): Item<Built> {
const id = itemId.next();
const id = new ItemId(crateId, itemId.next());
return { ...superFoldItem(item, this), id };
},
expr(expr: Expr<Parsed>): Expr<Built> {

View file

@ -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<string>(BUILTINS);
@ -34,8 +34,8 @@ export type CrateLoader = (
type Context = {
ast: Crate<Built>;
crates: Crate<Typecked>[];
modContentsCache: Map<ItemId, Map<string, ItemId>>;
newItemsById: Map<ItemId, Item<Resolved>>;
modContentsCache: ComplexMap<ItemId, Map<string, ItemId>>;
newItemsById: ComplexMap<ItemId, Item<Resolved>>;
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<Built>[]
): Item<Resolved>[] {
const items = new Map<string, number>();
const items = new Map<string, ItemId>();
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",

View file

@ -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<Resolved>,
lowerIdentTy: (ident: IdentWithRes<Resolved>) => 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<Resolved>): Crate<Typecked> {
const itemTys = new Map<number, Ty | null>();
function typeOfItem(index: ItemId, cause: Span): Ty {
const item = unwrap(ast.itemsById.get(index));
export function typeck(
ast: Crate<Resolved>,
otherCrates: Crate<Typecked>[]
): Crate<Typecked> {
const itemTys = new ComplexMap<ItemId, Ty | null>();
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<Resolved>,
fnTy: TyFn,
typeOfItem: (index: number, cause: Span) => Ty
typeOfItem: (itemId: ItemId, cause: Span) => Ty
): Expr<Typecked> {
const localTys = [...fnTy.params];
const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];

View file

@ -22,20 +22,32 @@ export function unwrap<T>(value: T | undefined): T {
* It uses JSON+string equality instead of refernece equality.
*/
export class ComplexMap<K, V> {
inner = new Map<string | number, V>();
private inner = new Map<string | number, V>();
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<K> {
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<K>(key: K): string | number {
if (typeof key === "string" || typeof key === "number") {
return key;
}
return JSON.stringify(key);
}