more resolve
This commit is contained in:
parent
19efd900fd
commit
26a83080aa
@ -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;
|
||||
};
|
||||
|
52
compiler/ast/cx.ts
Normal file
52
compiler/ast/cx.ts
Normal file
@ -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 };
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
export * from "./ast.ts";
|
||||
export * from "./visitor.ts";
|
||||
export * from "./cx.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<P>,
|
||||
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<P>,
|
||||
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<P>,
|
||||
generics: Generics,
|
||||
...p: P
|
||||
) {
|
||||
for (const param of generics.params) {
|
||||
visitIdent(v, param.ident, ...p);
|
||||
}
|
||||
}
|
||||
|
||||
export function visitParam<
|
||||
P extends PM = [],
|
||||
>(
|
||||
v: Visitor<P>,
|
||||
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<
|
||||
|
113
compiler/ctx.ts
113
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<File>();
|
||||
@ -80,101 +78,6 @@ export class Ctx {
|
||||
return this.identIdToString.get(idKey(ident))!;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
private hirIds = new Ids<HirId>();
|
||||
|
||||
private stmts = new Map<Key<HirId>, 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<Key<HirId>, hir.Item>();
|
||||
|
||||
/// don't intern the same thing twice
|
||||
public internItem(item: Omit<hir.Item, "id">): 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<Key<HirId>, 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<Key<HirId>, 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<Key<HirId>, 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<Key<HirId>, hir.Block>();
|
||||
|
||||
/// don't intern the same thing twice
|
||||
public internBlock(block: Omit<hir.Block, "id">): 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<DefId>();
|
||||
|
||||
private defs = new Map<Key<DefId>, 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<Key<DefId>, Mod>();
|
||||
|
||||
public getMod(id: DefId): Mod {
|
||||
return this.mods.get(idKey(id))!;
|
||||
}
|
||||
|
||||
public internMod(mod: Omit<Mod, "id">): 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<DefId>();
|
||||
|
||||
//
|
||||
|
||||
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<Key<DefId>>;
|
||||
defs: Map<Key<IdentId>, 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 };
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
91
compiler/resolve/cx.ts
Normal file
91
compiler/resolve/cx.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { IdentId, idKey, Key } from "../ctx.ts";
|
||||
|
||||
type Ident = Key<IdentId>;
|
||||
|
||||
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<Ident, Res>;
|
||||
};
|
||||
|
||||
export type Rib = {
|
||||
bindings: Map<Ident, Res>;
|
||||
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";
|
123
compiler/resolve/resolver.ts
Normal file
123
compiler/resolve/resolver.ts
Normal file
@ -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<number, [ast.Item, Mod]>();
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user