diff --git a/compiler/ast/ast.ts b/compiler/ast/ast.ts index 2f6edfe..df9cd32 100644 --- a/compiler/ast/ast.ts +++ b/compiler/ast/ast.ts @@ -1,4 +1,4 @@ -import { IdentId } from "../ctx.ts"; +import { File as CtxFile, IdentId } from "../ctx.ts"; import { Span } from "../diagnostics.ts"; export type File = { @@ -59,7 +59,7 @@ export type ItemKind = | { tag: "type_alias" } & TypeAliasItem; export type ModBlockItem = { block: Block }; -export type ModFileItem = { filePath: string }; +export type ModFileItem = { filePath: string; file?: CtxFile }; export type EnumItem = { variants: Variant[] }; export type StructItem = { data: VariantData }; @@ -67,7 +67,7 @@ export type FnItem = { generics?: Generics; params: Param[]; returnTy?: Ty; - body: Block; + body?: Block; }; export type UseItem = { _: 0 }; diff --git a/compiler/ctx.ts b/compiler/ctx.ts index 2d0fff8..c14d929 100644 --- a/compiler/ctx.ts +++ b/compiler/ctx.ts @@ -7,7 +7,7 @@ import { Span, } from "./diagnostics.ts"; import * as hir from "./middle/hir.ts"; -import { Mod } from "./middle/res.ts"; +import { DefKind, Mod, Res } from "./middle/res.ts"; export class Ctx { private fileIds = new Ids(); @@ -94,6 +94,16 @@ export class Ctx { return v; } + private items = new Map, hir.Item>(); + + /// don't intern the same thing twice + public internItem(item: Omit): hir.Item { + const id = this.hirIds.nextThenStep(); + const v: hir.Item = { id, ...item }; + this.items.set(idKey(id), v); + return v; + } + private exprs = new Map, hir.Expr>(); /// don't intern the same thing twice @@ -150,15 +160,17 @@ export class Ctx { return this.defs.get(idKey(id))!; } - private modDefs = new Map, Mod>(); - private modItemMaps = new Map, Map, DefId>>(); + private mods = new Map, Mod>(); - public modDef(id: DefId): Mod { - return this.modDefs.get(idKey(id))!; + public getMod(id: DefId): Mod { + return this.mods.get(idKey(id))!; } - public mod(id: DefId, ident: IdentId): DefId | undefined { - return this.modItemMaps.get(idKey(id))!.get(idKey(ident)); + public internMod(mod: Omit): Mod { + const id = this.defIds.nextThenStep(); + const v: Mod = { id, ...mod }; + this.mods.set(idKey(id), mod); + return v; } // @@ -219,6 +231,15 @@ export type FileInfo = { ast?: ast.File; }; +export type Mod = { + id: DefId; + parent?: Mod; + defKind: DefKind; + ident: IdentId; + items: Set>; + defs: Map, Res>; +}; + export type IdentId = IdBase & { readonly _: unique symbol }; export type HirId = IdBase & { readonly _: unique symbol }; export type DefId = IdBase & { readonly _: unique symbol }; diff --git a/compiler/main.ts b/compiler/main.ts index ef1121b..c99ce8a 100644 --- a/compiler/main.ts +++ b/compiler/main.ts @@ -72,7 +72,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> { ); } - public async collect(): Promise { + public async collect(): Promise { const text = await Deno.readTextFile(this.absPath); const file = this.ctx.addFile( this.ident, @@ -85,6 +85,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> { this.ctx.addFileAst(file, fileAst); ast.visitFile(this, fileAst, { file }); await this.subFilePromise; + return file; } visitModFileItem( @@ -96,7 +97,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> { const { filePath: relPath } = kind; const absPath = path.join(path.dirname(this.absPath), relPath); this.subFilePromise = this.subFilePromise - .then(() => { + .then(async () => { if (this.ctx.fileHasChildWithIdent(file, ident)) { this.ctx.report({ severity: "fatal", @@ -106,7 +107,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> { }); Deno.exit(1); } - return new FileTreeAstCollector( + const modFile = await new FileTreeAstCollector( this.ctx, file, ident, @@ -114,6 +115,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> { relPath, ) .collect(); + kind.file = modFile; }); return "stop"; } diff --git a/compiler/middle/hir.ts b/compiler/middle/hir.ts index 49af72c..4a7fae4 100644 --- a/compiler/middle/hir.ts +++ b/compiler/middle/hir.ts @@ -1,6 +1,6 @@ import { HirId, IdentId } from "../ctx.ts"; import { Span } from "../diagnostics.ts"; -import { Res } from "./res.ts"; +import { Mod, Res } from "./res.ts"; export type Stmt = { id: HirId; @@ -40,6 +40,7 @@ export type AssignType = "=" | "+=" | "-="; export type ExprStmt = { expr: Expr }; export type Item = { + id: HirId; kind: ItemKind; span: Span; ident: Ident; @@ -48,16 +49,14 @@ export type Item = { export type ItemKind = | { tag: "error" } - | { tag: "mod_block" } & ModBlockItem - | { tag: "mod_file" } & ModFileItem + | { tag: "mod" } & ModItem | { tag: "enum" } & EnumItem | { tag: "struct" } & StructItem | { tag: "fn" } & FnItem | { tag: "use" } & UseItem | { tag: "type_alias" } & TypeAliasItem; -export type ModBlockItem = { block: Block }; -export type ModFileItem = { filePath: string }; +export type ModItem = { mod: Mod }; export type EnumItem = { variants: Variant[] }; export type StructItem = { data: VariantData }; diff --git a/compiler/middle/lower_ast.ts b/compiler/middle/lower_ast.ts index 4b71d06..5e992a1 100644 --- a/compiler/middle/lower_ast.ts +++ b/compiler/middle/lower_ast.ts @@ -1,4 +1,4 @@ -import { Ctx, DefId, IdentId } from "../ctx.ts"; +import { Ctx, DefId, File, IdentId, idKey, Mod } from "../ctx.ts"; import * as ast from "../ast/ast.ts"; import { Block, @@ -13,11 +13,14 @@ import { Ty, } from "./hir.ts"; import { exhausted, Res as Result, todo } from "../util.ts"; -import { Rib } from "./rib.ts"; +import { Rib, RibKind } from "./rib.ts"; import { Res } from "./res.ts"; export class AstLowerer { - private ribs: Rib[] = []; + private tyRibs: Rib[] = []; + private valRibs: Rib[] = []; + private currentFile!: File; + private currentMod!: Mod; public constructor( private ctx: Ctx, @@ -25,6 +28,13 @@ export class AstLowerer { public lower() { const file = this.ctx.entryFile(); + this.currentFile = file; + this.currentMod = this.ctx.internMod({ + defKind: { tag: "mod" }, + ident: this.ctx.internIdent("root"), + items: new Set(), + defs: new Map(), + }); const ast = this.ctx.fileInfo(file).ast!; this.lowerFile(ast); } @@ -83,13 +93,87 @@ export class AstLowerer { private lowerLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): Stmt { const expr = kind.expr && this.lowerExpr(kind.expr); const ty = kind.ty && this.lowerTy(kind.ty); - this.pushRib({ kind: { tag: "normal" }, bindings: new Map() }); + this.pushRib({ tag: "normal" }); const pat = this.lowerPat(kind.pat); return this.ctx.internStmt({ tag: "let", pat, ty, expr }, stmt.span); } private lowerItem(item: ast.Item): Item { - return todo(); + const { ident, kind, pub, span } = item; + switch (kind.tag) { + case "error": + return this.ctx.internItem({ ident, kind, pub, span }); + case "mod_block": { + const parent = this.currentMod; + const mod = this.currentMod = this.ctx.internMod({ + parent, + ident: ident.id, + defKind: { tag: "mod" }, + items: new Set(), + defs: new Map(), + }); + const point = this.ribPoint(); + this.pushRib({ tag: "mod", mod }); + const _block = this.lowerBlock(kind.block); + this.returnToRibPoint(point); + this.currentMod = parent; + return this.ctx.internItem({ + ident, + kind: { tag: "mod", mod }, + pub, + span, + }); + } + case "mod_file": { + const parent = this.currentMod; + const mod = this.currentMod = this.ctx.internMod({ + parent, + ident: ident.id, + defKind: { tag: "mod" }, + items: new Set(), + defs: new Map(), + }); + const point = this.ribPoint(); + this.pushRib({ tag: "mod", mod }); + const parentFile = this.currentFile; + const fileInfo = this.ctx.fileInfo(kind.file!); + this.lowerFile(fileInfo.ast!); + this.returnToRibPoint(point); + this.currentFile = parentFile; + this.currentMod = parent; + if (this.tyRib().bindings.has(idKey(ident.id))) { + throw new Error(); + } + this.tyRib().bindings.set(idKey(ident.id), { + tag: "def", + id: mod.id, + kind: { tag: "mod" }, + }); + return this.ctx.internItem({ + ident, + kind: { tag: "mod", mod }, + pub, + span, + }); + } + case "enum": + return todo(); + case "struct": + return todo(); + case "fn": { + return this.ctx.internItem({ + ident, + kind: { ...todo() }, + pub, + span, + }); + } + case "use": + return todo(); + case "type_alias": + return todo(); + } + exhausted(kind); } private lowerExpr(expr: ast.Expr): Expr { @@ -221,7 +305,7 @@ export class AstLowerer { return this.ctx.internPat(kind, span); case "bind": { const v = this.ctx.internPat(kind, span); - this.rib().bindings.set(kind.ident.id, { + this.valRib().bindings.set(idKey(kind.ident.id), { tag: "local", id: v.id, }); @@ -293,10 +377,7 @@ export class AstLowerer { private lowerBlock(block: ast.Block): Block { const point = this.ribPoint(); - this.pushRib({ - kind: { tag: "mod", mod: { kind: { tag: "block" } } }, - bindings: new Map(), - }); + this.pushRib({ tag: "block" }); const stmts = block.stmts.map((stmt) => this.lowerStmt(stmt)); const expr = block.expr && this.lowerExpr(block.expr); this.returnToRibPoint(point); @@ -356,27 +437,60 @@ export class AstLowerer { span: seg.span, }]]; case "def": { + const error = (): [ + Res, + PathSegment[], + ] => [{ tag: "error" }, [...resSegs, { + ident: seg.ident, + res: innerRes, + inferArgs: false, + span: seg.span, + }]]; + const irk = innerRes.kind; switch (irk.tag) { case "mod": { - const mod = this.ctx.modDef(innerRes.id); - const def = this.ctx.modItem(); - return todo(); + const mod = this.ctx.getMod(innerRes.id); + const res = mod.defs.get(seg.ident.id); + if (!res) { + this.ctx.report({ + severity: "error", + file: this.currentFile, + msg: `module does not contain definition for '${ + this.ctx.identText(seg.ident.id) + }'`, + span: seg.span, + }); + return error(); + } + return [res, [...resSegs, { + ident: seg.ident, + res: innerRes, + inferArgs: false, + span: seg.span, + }]]; } case "struct": return todo(); case "enum": return todo(); - case "variant": + case "ty_alias": return todo(); - case "fn": - return todo(); - case "ctor": + case "ty_param": return todo(); case "use": return todo(); + case "fn": + case "variant": + case "ctor": case "field": - return todo(); + this.ctx.report({ + severity: "error", + file: this.currentFile, + msg: `${irk.tag} contains zero members`, + span: seg.span, + }); + return error(); } exhausted(irk); throw new Error(); @@ -388,28 +502,136 @@ export class AstLowerer { } private resolveTyIdent(ident: Ident): Res { + return this.findTyRibIdent(this.tyRibs.length - 1, ident); + } + + private findTyRibIdent(ribIdx: number, ident: Ident): Res { + const rib = this.tyRibs[ribIdx]; + if (rib.bindings.has(idKey(ident.id))) { + return rib.bindings.get(idKey(ident.id))!; + } + if (ribIdx === 0) { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `no type with name '${text}' in module`, + }); + return { tag: "error" }; + } + const res = this.findTyRibIdent(ribIdx - 1, ident); + const kind = rib.kind; + switch (kind.tag) { + case "normal": + return res; + case "fn": + return res; + case "item": + if (res.tag === "local") { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `cannot use local '${text}' here`, + }); + return { tag: "error" }; + } + return res; + case "mod": { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `no type with name '${text}' in module`, + }); + return { tag: "error" }; + } + case "block": + return res; + } + exhausted(kind); } private resolveValIdent(ident: Ident): Res { + return this.findValRibIdent(this.valRibs.length - 1, ident); } - private rib(): Rib { - return this.ribs.at(-1)!; + private findValRibIdent(ribIdx: number, ident: Ident): Res { + const rib = this.valRibs[ribIdx]; + if (rib.bindings.has(idKey(ident.id))) { + return rib.bindings.get(idKey(ident.id))!; + } + if (ribIdx === 0) { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `no value with name '${text}' in module`, + }); + return { tag: "error" }; + } + const res = this.findValRibIdent(ribIdx - 1, ident); + const kind = rib.kind; + switch (kind.tag) { + case "normal": + return res; + case "fn": + return res; + case "item": + if (res.tag === "local") { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `cannot use local '${text}' here`, + }); + return { tag: "error" }; + } + return res; + case "mod": { + const text = this.ctx.identText(ident.id); + this.ctx.report({ + severity: "error", + file: this.currentFile, + span: ident.span, + msg: `no value with name '${text}' in module`, + }); + return { tag: "error" }; + } + case "block": + return res; + } + exhausted(kind); + } + private tyRib(): Rib { + return this.tyRibs.at(-1)!; } - private pushRib(rib: Rib) { - this.ribs.push(rib); + private valRib(): Rib { + return this.valRibs.at(-1)!; + } + + private pushRib(kind: RibKind) { + this.tyRibs.push({ kind, bindings: new Map() }); + this.valRibs.push({ kind, bindings: new Map() }); } private popRib() { - this.ribs.pop(); + this.tyRibs.pop(); + this.valRibs.pop(); } private ribPoint(): number { - return this.ribs.length; + return this.valRibs.length; } private returnToRibPoint(point: number) { - this.ribs = this.ribs.slice(0, point); + this.tyRibs = this.tyRibs.slice(0, point); + this.valRibs = this.valRibs.slice(0, point); } } diff --git a/compiler/middle/res.ts b/compiler/middle/res.ts new file mode 100644 index 0000000..2b9a7e4 --- /dev/null +++ b/compiler/middle/res.ts @@ -0,0 +1,20 @@ +import { DefId, HirId } from "../ctx.ts"; + +// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/enum.RibKind.html +export type Res = + | { tag: "error" } + | { tag: "def"; kind: DefKind; id: DefId } + | { tag: "local"; id: HirId }; + +// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def/enum.DefKind.html +export type DefKind = + | { tag: "mod" } + | { tag: "struct" } + | { tag: "enum" } + | { tag: "variant" } + | { tag: "ty_alias" } + | { tag: "ty_param" } + | { tag: "fn" } + | { tag: "ctor" } + | { tag: "use" } + | { tag: "field" }; diff --git a/compiler/middle/rib.ts b/compiler/middle/rib.ts index 87059da..eb97fb6 100644 --- a/compiler/middle/rib.ts +++ b/compiler/middle/rib.ts @@ -1,10 +1,10 @@ -import { IdentId } from "../ctx.ts"; -import { DefKind, Mod, Res } from "./res.ts"; +import { IdentId, Key, Mod } from "../ctx.ts"; +import { DefKind, Res } from "./res.ts"; // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html export type Rib = { kind: RibKind; - bindings: Map; + bindings: Map, Res>; }; // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/enum.RibKind.html