diff --git a/compiler/ast/ast.ts b/compiler/ast/ast.ts
index df9cd32..9863039 100644
--- a/compiler/ast/ast.ts
+++ b/compiler/ast/ast.ts
@@ -3,9 +3,11 @@ import { Span } from "../diagnostics.ts";
export type File = {
stmts: Stmt[];
+ file: CtxFile;
};
export type Stmt = {
+ id: number;
kind: StmtKind;
span: Span;
};
@@ -42,6 +44,7 @@ export type AssignType = "=" | "+=" | "-=";
export type ExprStmt = { expr: Expr };
export type Item = {
+ id: number;
kind: ItemKind;
span: Span;
ident: Ident;
@@ -117,6 +120,7 @@ export type GenericParam = {
};
export type Expr = {
+ id: number;
kind: ExprKind;
span: Span;
};
@@ -164,11 +168,11 @@ export type CallExpr = { expr: Expr; args: Expr[] };
export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
export type BinaryExpr = { binaryType: BinaryType; left: Expr; right: Expr };
export type BlockExpr = { block: Block };
-export type IfExpr = { cond: Expr; truthy: Block; falsy?: Expr };
-export type LoopExpr = { body: Block };
-export type WhileExpr = { cond: Expr; body: Block };
-export type ForExpr = { pat: Pat; expr: Expr; body: Block };
-export type CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Block };
+export type IfExpr = { cond: Expr; truthy: Expr; falsy?: Expr };
+export type LoopExpr = { body: Expr };
+export type WhileExpr = { cond: Expr; body: Expr };
+export type ForExpr = { pat: Pat; expr: Expr; body: Expr };
+export type CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Expr };
export type RefType = "ref" | "ptr";
export type UnaryType = "not" | "-";
@@ -199,6 +203,7 @@ export type Block = {
};
export type Pat = {
+ id: number;
kind: PatKind;
span: Span;
};
@@ -212,6 +217,7 @@ export type BindPat = { ident: Ident; mut: boolean };
export type PathPat = { qty?: Ty; path: Path };
export type Ty = {
+ id: number;
kind: TyKind;
span: Span;
};
diff --git a/compiler/ast/cx.ts b/compiler/ast/cx.ts
new file mode 100644
index 0000000..cd6a24b
--- /dev/null
+++ b/compiler/ast/cx.ts
@@ -0,0 +1,52 @@
+import { Span } from "../diagnostics.ts";
+import {
+ Expr,
+ ExprKind,
+ Ident,
+ Item,
+ ItemKind,
+ Pat,
+ PatKind,
+ Stmt,
+ StmtKind,
+ Ty,
+ TyKind,
+} from "./ast.ts";
+
+export class Cx {
+ private idCounter = 0;
+
+ private id(): number {
+ return this.idCounter++;
+ }
+
+ public stmt(kind: StmtKind, span: Span): Stmt {
+ const id = this.id();
+ return { id, kind, span };
+ }
+
+ public item(
+ kind: ItemKind,
+ span: Span,
+ ident: Ident,
+ pub: boolean,
+ ): Item {
+ const id = this.id();
+ return { id, kind, span, ident, pub };
+ }
+
+ public expr(kind: ExprKind, span: Span): Expr {
+ const id = this.id();
+ return { id, kind, span };
+ }
+
+ public pat(kind: PatKind, span: Span): Pat {
+ const id = this.id();
+ return { id, kind, span };
+ }
+
+ public ty(kind: TyKind, span: Span): Ty {
+ const id = this.id();
+ return { id, kind, span };
+ }
+}
diff --git a/compiler/ast/mod.ts b/compiler/ast/mod.ts
index 7627d46..980154c 100644
--- a/compiler/ast/mod.ts
+++ b/compiler/ast/mod.ts
@@ -1,2 +1,3 @@
export * from "./ast.ts";
export * from "./visitor.ts";
+export * from "./cx.ts";
diff --git a/compiler/ast/visitor.ts b/compiler/ast/visitor.ts
index 9237cb0..c345eea 100644
--- a/compiler/ast/visitor.ts
+++ b/compiler/ast/visitor.ts
@@ -1,5 +1,4 @@
import { exhausted } from "../util.ts";
-import { Block, BlockExpr, PathExpr, PathTy } from "./ast.ts";
import {
AnonStructTy,
ArrayExpr,
@@ -7,6 +6,8 @@ import {
AssignStmt,
BinaryExpr,
BindPat,
+ Block,
+ BlockExpr,
BoolExpr,
BreakStmt,
CallExpr,
@@ -20,6 +21,7 @@ import {
File,
FnItem,
ForExpr,
+ Generics,
GroupExpr,
Ident,
IfExpr,
@@ -31,8 +33,12 @@ import {
LoopExpr,
ModBlockItem,
ModFileItem,
+ Param,
Pat,
Path,
+ PathExpr,
+ PathPat,
+ PathTy,
PtrTy,
RefExpr,
RefTy,
@@ -48,6 +54,8 @@ import {
TypeAliasItem,
UnaryExpr,
UseItem,
+ Variant,
+ VariantData,
WhileExpr,
} from "./ast.ts";
@@ -81,6 +89,8 @@ export interface Visitor<
visitUseItem?(item: Item, kind: UseItem, ...p: P): R;
visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R;
+ visitVariant?(variant: Variant, ...p: P): R;
+
visitExpr?(expr: Expr, ...p: P): R;
visitErrorExpr?(expr: Expr, ...p: P): R;
visitPathExpr?(expr: Expr, kind: PathExpr, ...p: P): R;
@@ -110,6 +120,7 @@ export interface Visitor<
visitPat?(pat: Pat, ...p: P): R;
visitErrorPat?(pat: Pat, ...p: P): R;
visitBindPat?(pat: Pat, kind: BindPat, ...p: P): R;
+ visitPathPat?(pat: Pat, kind: PathPat, ...p: P): R;
visitTy?(ty: Ty, ...p: P): R;
visitErrorTy?(ty: Ty, ...p: P): R;
@@ -187,9 +198,7 @@ export function visitStmt<
return;
case "break":
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
- if (kind.expr) {
- visitExpr(v, kind.expr, ...p);
- }
+ kind.expr && visitExpr(v, kind.expr, ...p);
return;
case "continue":
if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
@@ -222,29 +231,102 @@ export function visitItem<
return;
case "mod_block":
if (v.visitModBlockItem?.(item, kind, ...p) === "stop") return;
+ visitBlock(v, kind.block, ...p);
return;
case "mod_file":
if (v.visitModFileItem?.(item, kind, ...p) === "stop") return;
return;
case "enum":
if (v.visitEnumItem?.(item, kind, ...p) === "stop") return;
+ for (const variant of kind.variants) {
+ visitVariant(v, variant, ...p);
+ }
return;
case "struct":
if (v.visitStructItem?.(item, kind, ...p) === "stop") return;
+ visitVariantData(v, kind.data, ...p);
return;
case "fn":
if (v.visitFnItem?.(item, kind, ...p) === "stop") return;
+ for (const param of kind.params) {
+ visitParam(v, param, ...p);
+ }
+ kind.returnTy && visitTy(v, kind.returnTy, ...p);
return;
case "use":
if (v.visitUseItem?.(item, kind, ...p) === "stop") return;
return;
case "type_alias":
if (v.visitTypeAliasItem?.(item, kind, ...p) === "stop") return;
+ visitTy(v, kind.ty, ...p);
return;
}
exhausted(kind);
}
+export function visitVariant<
+ P extends PM = [],
+>(
+ v: Visitor
,
+ variant: Variant,
+ ...p: P
+) {
+ if (v.visitVariant?.(variant, ...p) === "stop") return;
+ visitIdent(v, variant.ident, ...p);
+ visitVariantData(v, variant.data, ...p);
+}
+
+export function visitVariantData<
+ P extends PM = [],
+>(
+ v: Visitor
,
+ data: VariantData,
+ ...p: P
+) {
+ const dk = data.kind;
+ switch (dk.tag) {
+ case "error":
+ return;
+ case "unit":
+ return;
+ case "tuple":
+ for (const elem of dk.elems) {
+ visitVariantData(v, elem, ...p);
+ }
+ return;
+ case "struct":
+ for (const field of dk.fields) {
+ visitIdent(v, field.ident, ...p);
+ visitTy(v, field.ty, ...p);
+ }
+ return;
+ }
+ exhausted(dk);
+}
+
+export function visitGenerics<
+ P extends PM = [],
+>(
+ v: Visitor
,
+ generics: Generics,
+ ...p: P
+) {
+ for (const param of generics.params) {
+ visitIdent(v, param.ident, ...p);
+ }
+}
+
+export function visitParam<
+ P extends PM = [],
+>(
+ v: Visitor
,
+ param: Param,
+ ...p: P
+) {
+ visitPat(v, param.pat, ...p);
+ visitTy(v, param.ty, ...p);
+}
+
export function visitExpr<
P extends PM = [],
>(
@@ -343,25 +425,25 @@ export function visitExpr<
case "if":
if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return;
visitExpr(v, kind.cond, ...p);
- visitBlock(v, kind.truthy, ...p);
+ visitExpr(v, kind.truthy, ...p);
if (kind.falsy) {
visitExpr(v, kind.falsy, ...p);
}
return;
case "loop":
if (v.visitLoopExpr?.(expr, kind, ...p) === "stop") return;
- visitBlock(v, kind.body, ...p);
+ visitExpr(v, kind.body, ...p);
return;
case "while":
if (v.visitWhileExpr?.(expr, kind, ...p) === "stop") return;
visitExpr(v, kind.cond, ...p);
- visitBlock(v, kind.body, ...p);
+ visitExpr(v, kind.body, ...p);
return;
case "for":
if (v.visitForExpr?.(expr, kind, ...p) === "stop") return;
visitPat(v, kind.pat, ...p);
visitExpr(v, kind.expr, ...p);
- visitBlock(v, kind.body, ...p);
+ visitExpr(v, kind.body, ...p);
return;
case "c_for":
if (v.visitCForExpr?.(expr, kind, ...p) === "stop") return;
@@ -393,6 +475,11 @@ export function visitPat<
return;
case "bind":
if (v.visitBindPat?.(pat, kind, ...p) === "stop") return;
+ visitIdent(v, kind.ident, ...p);
+ return;
+ case "path":
+ if (v.visitPathPat?.(pat, kind, ...p) === "stop") return;
+ visitPath(v, kind.path, ...p);
return;
}
exhausted(kind);
@@ -461,7 +548,7 @@ export function visitBlock<
block: Block,
...p: P
) {
- v.visitBlock?.(block, ...p);
+ if (v.visitBlock?.(block, ...p) === "stop") return;
for (const stmt of block.stmts) {
visitStmt(v, stmt, ...p);
}
@@ -477,7 +564,10 @@ export function visitPath<
path: Path,
...p: P
) {
- v.visitPath?.(path, ...p);
+ if (v.visitPath?.(path, ...p) === "stop") return;
+ for (const seg of path.segments) {
+ visitIdent(v, seg.ident, ...p);
+ }
}
export function visitIdent<
diff --git a/compiler/ctx.ts b/compiler/ctx.ts
index c14d929..814c07b 100644
--- a/compiler/ctx.ts
+++ b/compiler/ctx.ts
@@ -6,8 +6,6 @@ import {
Report,
Span,
} from "./diagnostics.ts";
-import * as hir from "./middle/hir.ts";
-import { DefKind, Mod, Res } from "./middle/res.ts";
export class Ctx {
private fileIds = new Ids();
@@ -80,101 +78,6 @@ export class Ctx {
return this.identIdToString.get(idKey(ident))!;
}
- //
-
- private hirIds = new Ids();
-
- private stmts = new Map, hir.Stmt>();
-
- /// don't intern the same thing twice
- public internStmt(kind: hir.StmtKind, span: Span): hir.Stmt {
- const id = this.hirIds.nextThenStep();
- const v: hir.Stmt = { id, kind, span };
- this.stmts.set(idKey(id), v);
- 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
- public internExpr(kind: hir.ExprKind, span: Span): hir.Expr {
- const id = this.hirIds.nextThenStep();
- const v: hir.Expr = { id, kind, span };
- this.exprs.set(idKey(id), v);
- return v;
- }
-
- private pats = new Map, hir.Pat>();
-
- /// don't intern the same thing twice
- public internPat(kind: hir.PatKind, span: Span): hir.Pat {
- const id = this.hirIds.nextThenStep();
- const v: hir.Pat = { id, kind, span };
- this.pats.set(idKey(id), v);
- return v;
- }
-
- private tys = new Map, hir.Ty>();
-
- /// don't intern the same thing twice
- public internTy(kind: hir.TyKind, span: Span): hir.Ty {
- const id = this.hirIds.nextThenStep();
- const v: hir.Ty = { id, kind, span };
- this.tys.set(idKey(id), v);
- return v;
- }
-
- private blocks = new Map, hir.Block>();
-
- /// don't intern the same thing twice
- public internBlock(block: Omit): hir.Block {
- const id = this.hirIds.nextThenStep();
- const v: hir.Block = { id, ...block };
- this.blocks.set(idKey(id), v);
- return v;
- }
-
- //
-
- private defIds = new Ids();
-
- private defs = new Map, HirId>();
-
- public addHirIdDef(hirId: HirId): DefId {
- const id = this.defIds.nextThenStep();
- this.defs.set(idKey(id), hirId);
- return id;
- }
-
- public defHirId(id: DefId): HirId {
- return this.defs.get(idKey(id))!;
- }
-
- private mods = new Map, Mod>();
-
- public getMod(id: DefId): Mod {
- return this.mods.get(idKey(id))!;
- }
-
- public internMod(mod: Omit): Mod {
- const id = this.defIds.nextThenStep();
- const v: Mod = { id, ...mod };
- this.mods.set(idKey(id), mod);
- return v;
- }
-
- //
-
public filePosLineText(file: File, pos: Pos): string {
const fileTextLines = this.fileInfo(file).text.split("\n");
return fileTextLines[pos.line - 1];
@@ -195,6 +98,12 @@ export class Ctx {
return result;
}
+ //
+
+ private defIds = new Ids();
+
+ //
+
public report(rep: Report) {
this.reports.push(rep);
this.reportImmediately(rep);
@@ -231,17 +140,7 @@ 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 };
export type IdBase = { key: number };
diff --git a/compiler/main.ts b/compiler/main.ts
index c99ce8a..c8ed4af 100644
--- a/compiler/main.ts
+++ b/compiler/main.ts
@@ -1,5 +1,5 @@
import * as path from "jsr:@std/path";
-import { Parser } from "./parser/parser.ts";
+import { Parser } from "./parse/parser.ts";
import * as ast from "./ast/mod.ts";
import { Ctx } from "./ctx.ts";
import { File } from "./ctx.ts";
@@ -28,6 +28,7 @@ export class NullEmitter implements PackEmitter {
export class PackCompiler {
private ctx = new Ctx();
+ private astCx = new ast.Cx();
public constructor(
private entryFilePath: string,
@@ -36,7 +37,7 @@ export class PackCompiler {
public async compile() {
await FileTreeAstCollector
- .fromEntryFile(this.ctx, this.entryFilePath)
+ .fromEntryFile(this.ctx, this.astCx, this.entryFilePath)
.collect();
this.ctx.printAsts();
}
@@ -53,6 +54,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
private constructor(
private ctx: Ctx,
+ private astCx: ast.Cx,
private superFile: File | undefined,
private ident: string,
private absPath: string,
@@ -61,10 +63,12 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
public static fromEntryFile(
ctx: Ctx,
+ astCx: ast.Cx,
entryFilePath: string,
): FileTreeAstCollector {
return new FileTreeAstCollector(
ctx,
+ astCx,
undefined,
"root",
entryFilePath,
@@ -81,7 +85,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
this.superFile,
text,
);
- const fileAst = new Parser(this.ctx, file).parse();
+ const fileAst = new Parser(this.ctx, this.astCx, file).parse();
this.ctx.addFileAst(file, fileAst);
ast.visitFile(this, fileAst, { file });
await this.subFilePromise;
@@ -109,6 +113,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
}
const modFile = await new FileTreeAstCollector(
this.ctx,
+ this.astCx,
file,
ident,
absPath,
diff --git a/compiler/parser/lexer.ts b/compiler/parse/lexer.ts
similarity index 100%
rename from compiler/parser/lexer.ts
rename to compiler/parse/lexer.ts
diff --git a/compiler/parser/parser.ts b/compiler/parse/parser.ts
similarity index 97%
rename from compiler/parser/parser.ts
rename to compiler/parse/parser.ts
index 7895177..5211eb3 100644
--- a/compiler/parser/parser.ts
+++ b/compiler/parse/parser.ts
@@ -25,6 +25,7 @@ import {
Ty,
TyKind,
} from "../ast/ast.ts";
+import { Cx } from "../ast/cx.ts";
import { Ctx, File as CtxFile } from "../ctx.ts";
import { Pos, Span } from "../diagnostics.ts";
import { Res, todo } from "../util.ts";
@@ -46,6 +47,7 @@ export class Parser {
public constructor(
private ctx: Ctx,
+ private cx: Cx,
private file: CtxFile,
) {
this.lexer = new SigFilter(new Lexer(this.ctx, this.file));
@@ -53,7 +55,7 @@ export class Parser {
}
public parse(): File {
- return { stmts: this.parseStmts() };
+ return { stmts: this.parseStmts(), file: this.file };
}
private parseStmts(): Stmt[] {
@@ -542,11 +544,7 @@ export class Parser {
this.report("expected '{'");
return this.expr({ tag: "error" }, pos);
}
- const bodyRes = this.parseBlock();
- if (!bodyRes.ok) {
- return this.expr({ tag: "error" }, pos);
- }
- const body = bodyRes.val;
+ const body = this.parseExpr();
return this.expr({ tag: "loop", body }, pos);
}
@@ -558,11 +556,7 @@ export class Parser {
this.report("expected '{'");
return this.expr({ tag: "error" }, pos);
}
- const bodyRes = this.parseBlock();
- if (!bodyRes.ok) {
- return this.expr({ tag: "error" }, pos);
- }
- const body = bodyRes.val;
+ const body = this.parseExpr();
return this.expr({ tag: "while", cond, body }, pos);
}
@@ -587,11 +581,7 @@ export class Parser {
this.report("expected '{'");
return this.expr({ tag: "error" }, pos);
}
- const bodyRes = this.parseBlock();
- if (!bodyRes.ok) {
- return this.expr({ tag: "error" }, pos);
- }
- const body = bodyRes.val;
+ const body = this.parseExpr();
return this.expr({ tag: "for", pat, expr, body }, pos);
}
@@ -629,11 +619,7 @@ export class Parser {
this.report("expected '{'");
return this.expr({ tag: "error" }, begin);
}
- const bodyRes = this.parseBlock();
- if (!bodyRes.ok) {
- return this.expr({ tag: "error" }, begin);
- }
- const body = bodyRes.val;
+ const body = this.parseExpr();
return this.expr(
{ tag: "c_for", decl, cond, incr, body },
Span.fromto(begin, body.span),
@@ -721,11 +707,7 @@ export class Parser {
this.report("expected block");
return this.expr({ tag: "error" }, pos);
}
- const truthyRes = this.parseBlock();
- if (!truthyRes.ok) {
- return this.expr({ tag: "error" }, pos);
- }
- const truthy = truthyRes.val;
+ const truthy = this.parseExpr();
if (!this.test("else")) {
return this.expr({ tag: "if", cond, truthy }, pos);
}
@@ -1263,7 +1245,7 @@ export class Parser {
}
private stmt(kind: StmtKind, span: Span): Stmt {
- return { kind, span };
+ return this.cx.stmt(kind, span);
}
private item(
@@ -1272,18 +1254,18 @@ export class Parser {
ident: Ident,
pub: boolean,
): Item {
- return { kind, span, ident, pub };
+ return this.cx.item(kind, span, ident, pub);
}
private expr(kind: ExprKind, span: Span): Expr {
- return { kind, span };
+ return this.cx.expr(kind, span);
}
private pat(kind: PatKind, span: Span): Pat {
- return { kind, span };
+ return this.cx.pat(kind, span);
}
private ty(kind: TyKind, span: Span): Ty {
- return { kind, span };
+ return this.cx.ty(kind, span);
}
}
diff --git a/compiler/parser/token.ts b/compiler/parse/token.ts
similarity index 100%
rename from compiler/parser/token.ts
rename to compiler/parse/token.ts
diff --git a/compiler/resolve/cx.ts b/compiler/resolve/cx.ts
new file mode 100644
index 0000000..3bb2da5
--- /dev/null
+++ b/compiler/resolve/cx.ts
@@ -0,0 +1,91 @@
+import { IdentId, idKey, Key } from "../ctx.ts";
+
+type Ident = Key;
+
+export class Ribs {
+ private tyRibs: Rib[] = [];
+ private valRibs: Rib[] = [];
+
+ private constructor() {}
+
+ public static withRootMod(): Ribs {
+ const ribs = new Ribs();
+ ribs.pushRib({ tag: "mod", mod: { items: new Map() } });
+ return ribs;
+ }
+
+ public pushRib(kind: RibKind) {
+ this.tyRibs.push({ bindings: new Map(), kind });
+ this.valRibs.push({ bindings: new Map(), kind });
+ }
+
+ public hasTy(ident: IdentId): boolean {
+ return this.tyRibs.at(-1)!.bindings.has(idKey(ident));
+ }
+
+ public defTy(ident: IdentId, res: Res) {
+ this.tyRibs.at(-1)!.bindings.set(idKey(ident), res);
+ }
+
+ public hasVal(ident: IdentId): boolean {
+ return this.valRibs.at(-1)!.bindings.has(idKey(ident));
+ }
+
+ public defVal(ident: IdentId, res: Res) {
+ this.valRibs.at(-1)!.bindings.set(idKey(ident), res);
+ }
+
+ public checkpoint(): number {
+ return this.tyRibs.length;
+ }
+
+ public returnToCheckpoint(checkpoint: number) {
+ this.tyRibs = this.tyRibs.slice(checkpoint, this.tyRibs.length);
+ this.valRibs = this.valRibs.slice(checkpoint, this.valRibs.length);
+ }
+
+ public nearestMod(): Mod {
+ return [
+ this.tyRibs
+ .toReversed()
+ .find((r) => r.kind.tag === "mod")!,
+ ]
+ .map((r) => (r.kind.tag === "mod" && r.kind.mod) as Mod)[0];
+ }
+}
+
+export type Mod = {
+ parent?: Mod;
+ items: Map;
+};
+
+export type Rib = {
+ bindings: Map;
+ kind: RibKind;
+};
+
+export type RibKind =
+ | { tag: "normal" }
+ | { tag: "fn" }
+ | { tag: "item" }
+ | { tag: "mod"; mod: Mod };
+
+export type Res =
+ | { tag: "def"; def: Def }
+ | { tag: "local"; id: number };
+
+export type Def = {
+ type: DefType;
+ id: number;
+};
+
+export type DefType =
+ | "mod"
+ | "enum"
+ | "struct"
+ | "variant"
+ | "ty_alias"
+ | "ty_param"
+ | "fn"
+ | "use"
+ | "field";
diff --git a/compiler/resolve/resolver.ts b/compiler/resolve/resolver.ts
new file mode 100644
index 0000000..31d5bbf
--- /dev/null
+++ b/compiler/resolve/resolver.ts
@@ -0,0 +1,123 @@
+import * as ast from "../ast/mod.ts";
+import { Ctx, File, IdentId, idKey, Key } from "../ctx.ts";
+import { todo } from "../util.ts";
+import { Def, DefType, Mod, Res, Ribs } from "./cx.ts";
+
+export class Resolver implements ast.Visitor {
+ private ribs = Ribs.withRootMod();
+ private currentFile!: File;
+
+ public constructor(
+ private ctx: Ctx,
+ private entryFileAst: ast.File,
+ ) {
+ ast.visitFile(this, this.entryFileAst);
+ }
+
+ visitFile(file: ast.File): ast.VisitRes {
+ this.currentFile = this.entryFileAst.file;
+ ast.visitStmts(this, file.stmts);
+ }
+
+ visitLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): ast.VisitRes {
+ kind.expr && ast.visitExpr(this, kind.expr);
+ kind.ty && ast.visitTy(this, kind.ty);
+ this.ribs.pushRib({ tag: "normal" });
+ ast.visitPat(this, kind.pat);
+ return "stop";
+ }
+
+ visitModBlockItem(item: ast.Item, kind: ast.ModBlockItem): ast.VisitRes {
+ const mod: Mod = {
+ parent: this.ribs.nearestMod(),
+ items: new Map(),
+ };
+ const ribPoint = this.ribs.checkpoint();
+ ast.visitBlock(this, kind.block);
+ this.ribs.pushRib({ tag: "mod", mod });
+ this.ribs.returnToCheckpoint(ribPoint);
+ this.defineTy(item.ident, this.defMod(item.ident.id, item, mod));
+ return "stop";
+ }
+
+ visitModFileItem(item: ast.Item, kind: ast.ModFileItem): ast.VisitRes {
+ const mod: Mod = {
+ parent: this.ribs.nearestMod(),
+ items: new Map(),
+ };
+ const ribPoint = this.ribs.checkpoint();
+ const fileAst = this.ctx.fileInfo(kind.file!).ast!;
+ ast.visitFile(this, fileAst);
+ this.ribs.pushRib({ tag: "mod", mod });
+ this.ribs.returnToCheckpoint(ribPoint);
+ this.defineTy(item.ident, this.defMod(item.ident.id, item, mod));
+ return "stop";
+ }
+
+ visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
+ todo();
+ }
+
+ visitStructItem(item: ast.Item, kind: ast.StructItem): ast.VisitRes {
+ todo();
+ }
+
+ visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
+ todo();
+ }
+
+ visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes {
+ todo();
+ }
+
+ visitTypeAliasItem(item: ast.Item, kind: ast.TypeAliasItem): ast.VisitRes {
+ todo();
+ }
+
+ visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
+ this.ribs.defVal(kind.ident.id, { tag: "local", id: pat.id });
+ return "stop";
+ }
+
+ visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes {
+ return "stop";
+ }
+
+ private defIdCounter = 0;
+
+ private modDefs = new Map();
+ private modDef(id: number): [ast.Item, Mod] {
+ return this.modDefs.get(id)!;
+ }
+ private defMod(_ident: IdentId, item: ast.Item, mod: Mod): Res {
+ const id = this.defIdCounter++;
+ this.modDefs.set(id, [item, mod]);
+ return { tag: "def", def: { id, type: "mod" } };
+ }
+
+ private defineTy(ident: ast.Ident, res: Res) {
+ if (this.ribs.hasTy(ident.id)) {
+ const text = this.ctx.identText(ident.id);
+ this.ctx.report({
+ severity: "error",
+ file: this.currentFile,
+ span: ident.span,
+ msg: `redefinition of type '${text}'`,
+ });
+ }
+ this.ribs.defTy(ident.id, res);
+ }
+
+ private defineVal(ident: ast.Ident, res: Res) {
+ if (this.ribs.hasVal(ident.id)) {
+ const text = this.ctx.identText(ident.id);
+ this.ctx.report({
+ severity: "error",
+ file: this.currentFile,
+ span: ident.span,
+ msg: `redefinition of value '${text}'`,
+ });
+ }
+ this.ribs.defVal(ident.id, res);
+ }
+}