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