mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
crates work
This commit is contained in:
parent
b3b0bc2e6f
commit
c4cfa19fa9
7 changed files with 125 additions and 49 deletions
27
src/ast.ts
27
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<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);
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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, () => {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 }[] = [];
|
||||
|
|
|
|||
32
src/utils.ts
32
src/utils.ts
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue