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); +}