Compare commits
2 Commits
7455f9d259
...
26a83080aa
Author | SHA1 | Date | |
---|---|---|---|
|
26a83080aa | ||
|
19efd900fd |
@ -3,9 +3,11 @@ import { Span } from "../diagnostics.ts";
|
|||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
stmts: Stmt[];
|
stmts: Stmt[];
|
||||||
|
file: CtxFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Stmt = {
|
export type Stmt = {
|
||||||
|
id: number;
|
||||||
kind: StmtKind;
|
kind: StmtKind;
|
||||||
span: Span;
|
span: Span;
|
||||||
};
|
};
|
||||||
@ -42,6 +44,7 @@ export type AssignType = "=" | "+=" | "-=";
|
|||||||
export type ExprStmt = { expr: Expr };
|
export type ExprStmt = { expr: Expr };
|
||||||
|
|
||||||
export type Item = {
|
export type Item = {
|
||||||
|
id: number;
|
||||||
kind: ItemKind;
|
kind: ItemKind;
|
||||||
span: Span;
|
span: Span;
|
||||||
ident: Ident;
|
ident: Ident;
|
||||||
@ -117,6 +120,7 @@ export type GenericParam = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Expr = {
|
export type Expr = {
|
||||||
|
id: number;
|
||||||
kind: ExprKind;
|
kind: ExprKind;
|
||||||
span: Span;
|
span: Span;
|
||||||
};
|
};
|
||||||
@ -164,11 +168,11 @@ export type CallExpr = { expr: Expr; args: Expr[] };
|
|||||||
export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
|
export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
|
||||||
export type BinaryExpr = { binaryType: BinaryType; left: Expr; right: Expr };
|
export type BinaryExpr = { binaryType: BinaryType; left: Expr; right: Expr };
|
||||||
export type BlockExpr = { block: Block };
|
export type BlockExpr = { block: Block };
|
||||||
export type IfExpr = { cond: Expr; truthy: Block; falsy?: Expr };
|
export type IfExpr = { cond: Expr; truthy: Expr; falsy?: Expr };
|
||||||
export type LoopExpr = { body: Block };
|
export type LoopExpr = { body: Expr };
|
||||||
export type WhileExpr = { cond: Expr; body: Block };
|
export type WhileExpr = { cond: Expr; body: Expr };
|
||||||
export type ForExpr = { pat: Pat; expr: Expr; body: Block };
|
export type ForExpr = { pat: Pat; expr: Expr; body: Expr };
|
||||||
export type CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Block };
|
export type CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Expr };
|
||||||
|
|
||||||
export type RefType = "ref" | "ptr";
|
export type RefType = "ref" | "ptr";
|
||||||
export type UnaryType = "not" | "-";
|
export type UnaryType = "not" | "-";
|
||||||
@ -199,6 +203,7 @@ export type Block = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Pat = {
|
export type Pat = {
|
||||||
|
id: number;
|
||||||
kind: PatKind;
|
kind: PatKind;
|
||||||
span: Span;
|
span: Span;
|
||||||
};
|
};
|
||||||
@ -212,6 +217,7 @@ export type BindPat = { ident: Ident; mut: boolean };
|
|||||||
export type PathPat = { qty?: Ty; path: Path };
|
export type PathPat = { qty?: Ty; path: Path };
|
||||||
|
|
||||||
export type Ty = {
|
export type Ty = {
|
||||||
|
id: number;
|
||||||
kind: TyKind;
|
kind: TyKind;
|
||||||
span: Span;
|
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 "./ast.ts";
|
||||||
export * from "./visitor.ts";
|
export * from "./visitor.ts";
|
||||||
|
export * from "./cx.ts";
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { exhausted } from "../util.ts";
|
import { exhausted } from "../util.ts";
|
||||||
import { Block, BlockExpr, PathExpr, PathTy } from "./ast.ts";
|
|
||||||
import {
|
import {
|
||||||
AnonStructTy,
|
AnonStructTy,
|
||||||
ArrayExpr,
|
ArrayExpr,
|
||||||
@ -7,6 +6,8 @@ import {
|
|||||||
AssignStmt,
|
AssignStmt,
|
||||||
BinaryExpr,
|
BinaryExpr,
|
||||||
BindPat,
|
BindPat,
|
||||||
|
Block,
|
||||||
|
BlockExpr,
|
||||||
BoolExpr,
|
BoolExpr,
|
||||||
BreakStmt,
|
BreakStmt,
|
||||||
CallExpr,
|
CallExpr,
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
File,
|
File,
|
||||||
FnItem,
|
FnItem,
|
||||||
ForExpr,
|
ForExpr,
|
||||||
|
Generics,
|
||||||
GroupExpr,
|
GroupExpr,
|
||||||
Ident,
|
Ident,
|
||||||
IfExpr,
|
IfExpr,
|
||||||
@ -31,8 +33,12 @@ import {
|
|||||||
LoopExpr,
|
LoopExpr,
|
||||||
ModBlockItem,
|
ModBlockItem,
|
||||||
ModFileItem,
|
ModFileItem,
|
||||||
|
Param,
|
||||||
Pat,
|
Pat,
|
||||||
Path,
|
Path,
|
||||||
|
PathExpr,
|
||||||
|
PathPat,
|
||||||
|
PathTy,
|
||||||
PtrTy,
|
PtrTy,
|
||||||
RefExpr,
|
RefExpr,
|
||||||
RefTy,
|
RefTy,
|
||||||
@ -48,6 +54,8 @@ import {
|
|||||||
TypeAliasItem,
|
TypeAliasItem,
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
UseItem,
|
UseItem,
|
||||||
|
Variant,
|
||||||
|
VariantData,
|
||||||
WhileExpr,
|
WhileExpr,
|
||||||
} from "./ast.ts";
|
} from "./ast.ts";
|
||||||
|
|
||||||
@ -81,6 +89,8 @@ export interface Visitor<
|
|||||||
visitUseItem?(item: Item, kind: UseItem, ...p: P): R;
|
visitUseItem?(item: Item, kind: UseItem, ...p: P): R;
|
||||||
visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R;
|
visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R;
|
||||||
|
|
||||||
|
visitVariant?(variant: Variant, ...p: P): R;
|
||||||
|
|
||||||
visitExpr?(expr: Expr, ...p: P): R;
|
visitExpr?(expr: Expr, ...p: P): R;
|
||||||
visitErrorExpr?(expr: Expr, ...p: P): R;
|
visitErrorExpr?(expr: Expr, ...p: P): R;
|
||||||
visitPathExpr?(expr: Expr, kind: PathExpr, ...p: P): R;
|
visitPathExpr?(expr: Expr, kind: PathExpr, ...p: P): R;
|
||||||
@ -110,6 +120,7 @@ export interface Visitor<
|
|||||||
visitPat?(pat: Pat, ...p: P): R;
|
visitPat?(pat: Pat, ...p: P): R;
|
||||||
visitErrorPat?(pat: Pat, ...p: P): R;
|
visitErrorPat?(pat: Pat, ...p: P): R;
|
||||||
visitBindPat?(pat: Pat, kind: BindPat, ...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;
|
visitTy?(ty: Ty, ...p: P): R;
|
||||||
visitErrorTy?(ty: Ty, ...p: P): R;
|
visitErrorTy?(ty: Ty, ...p: P): R;
|
||||||
@ -187,9 +198,7 @@ export function visitStmt<
|
|||||||
return;
|
return;
|
||||||
case "break":
|
case "break":
|
||||||
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
|
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
if (kind.expr) {
|
kind.expr && visitExpr(v, kind.expr, ...p);
|
||||||
visitExpr(v, kind.expr, ...p);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
case "continue":
|
case "continue":
|
||||||
if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
|
if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
|
||||||
@ -222,29 +231,102 @@ export function visitItem<
|
|||||||
return;
|
return;
|
||||||
case "mod_block":
|
case "mod_block":
|
||||||
if (v.visitModBlockItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitModBlockItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
visitBlock(v, kind.block, ...p);
|
||||||
return;
|
return;
|
||||||
case "mod_file":
|
case "mod_file":
|
||||||
if (v.visitModFileItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitModFileItem?.(item, kind, ...p) === "stop") return;
|
||||||
return;
|
return;
|
||||||
case "enum":
|
case "enum":
|
||||||
if (v.visitEnumItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitEnumItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
for (const variant of kind.variants) {
|
||||||
|
visitVariant(v, variant, ...p);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case "struct":
|
case "struct":
|
||||||
if (v.visitStructItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitStructItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
visitVariantData(v, kind.data, ...p);
|
||||||
return;
|
return;
|
||||||
case "fn":
|
case "fn":
|
||||||
if (v.visitFnItem?.(item, kind, ...p) === "stop") return;
|
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;
|
return;
|
||||||
case "use":
|
case "use":
|
||||||
if (v.visitUseItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitUseItem?.(item, kind, ...p) === "stop") return;
|
||||||
return;
|
return;
|
||||||
case "type_alias":
|
case "type_alias":
|
||||||
if (v.visitTypeAliasItem?.(item, kind, ...p) === "stop") return;
|
if (v.visitTypeAliasItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
visitTy(v, kind.ty, ...p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exhausted(kind);
|
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<
|
export function visitExpr<
|
||||||
P extends PM = [],
|
P extends PM = [],
|
||||||
>(
|
>(
|
||||||
@ -343,25 +425,25 @@ export function visitExpr<
|
|||||||
case "if":
|
case "if":
|
||||||
if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return;
|
if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
visitExpr(v, kind.cond, ...p);
|
visitExpr(v, kind.cond, ...p);
|
||||||
visitBlock(v, kind.truthy, ...p);
|
visitExpr(v, kind.truthy, ...p);
|
||||||
if (kind.falsy) {
|
if (kind.falsy) {
|
||||||
visitExpr(v, kind.falsy, ...p);
|
visitExpr(v, kind.falsy, ...p);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case "loop":
|
case "loop":
|
||||||
if (v.visitLoopExpr?.(expr, kind, ...p) === "stop") return;
|
if (v.visitLoopExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
visitBlock(v, kind.body, ...p);
|
visitExpr(v, kind.body, ...p);
|
||||||
return;
|
return;
|
||||||
case "while":
|
case "while":
|
||||||
if (v.visitWhileExpr?.(expr, kind, ...p) === "stop") return;
|
if (v.visitWhileExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
visitExpr(v, kind.cond, ...p);
|
visitExpr(v, kind.cond, ...p);
|
||||||
visitBlock(v, kind.body, ...p);
|
visitExpr(v, kind.body, ...p);
|
||||||
return;
|
return;
|
||||||
case "for":
|
case "for":
|
||||||
if (v.visitForExpr?.(expr, kind, ...p) === "stop") return;
|
if (v.visitForExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
visitPat(v, kind.pat, ...p);
|
visitPat(v, kind.pat, ...p);
|
||||||
visitExpr(v, kind.expr, ...p);
|
visitExpr(v, kind.expr, ...p);
|
||||||
visitBlock(v, kind.body, ...p);
|
visitExpr(v, kind.body, ...p);
|
||||||
return;
|
return;
|
||||||
case "c_for":
|
case "c_for":
|
||||||
if (v.visitCForExpr?.(expr, kind, ...p) === "stop") return;
|
if (v.visitCForExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
@ -393,6 +475,11 @@ export function visitPat<
|
|||||||
return;
|
return;
|
||||||
case "bind":
|
case "bind":
|
||||||
if (v.visitBindPat?.(pat, kind, ...p) === "stop") return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
exhausted(kind);
|
exhausted(kind);
|
||||||
@ -461,7 +548,7 @@ export function visitBlock<
|
|||||||
block: Block,
|
block: Block,
|
||||||
...p: P
|
...p: P
|
||||||
) {
|
) {
|
||||||
v.visitBlock?.(block, ...p);
|
if (v.visitBlock?.(block, ...p) === "stop") return;
|
||||||
for (const stmt of block.stmts) {
|
for (const stmt of block.stmts) {
|
||||||
visitStmt(v, stmt, ...p);
|
visitStmt(v, stmt, ...p);
|
||||||
}
|
}
|
||||||
@ -477,7 +564,10 @@ export function visitPath<
|
|||||||
path: Path,
|
path: Path,
|
||||||
...p: P
|
...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<
|
export function visitIdent<
|
||||||
|
113
compiler/ctx.ts
113
compiler/ctx.ts
@ -6,8 +6,6 @@ import {
|
|||||||
Report,
|
Report,
|
||||||
Span,
|
Span,
|
||||||
} from "./diagnostics.ts";
|
} from "./diagnostics.ts";
|
||||||
import * as hir from "./middle/hir.ts";
|
|
||||||
import { DefKind, Mod, Res } from "./middle/res.ts";
|
|
||||||
|
|
||||||
export class Ctx {
|
export class Ctx {
|
||||||
private fileIds = new Ids<File>();
|
private fileIds = new Ids<File>();
|
||||||
@ -80,101 +78,6 @@ export class Ctx {
|
|||||||
return this.identIdToString.get(idKey(ident))!;
|
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 {
|
public filePosLineText(file: File, pos: Pos): string {
|
||||||
const fileTextLines = this.fileInfo(file).text.split("\n");
|
const fileTextLines = this.fileInfo(file).text.split("\n");
|
||||||
return fileTextLines[pos.line - 1];
|
return fileTextLines[pos.line - 1];
|
||||||
@ -195,6 +98,12 @@ export class Ctx {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
private defIds = new Ids<DefId>();
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
public report(rep: Report) {
|
public report(rep: Report) {
|
||||||
this.reports.push(rep);
|
this.reports.push(rep);
|
||||||
this.reportImmediately(rep);
|
this.reportImmediately(rep);
|
||||||
@ -231,17 +140,7 @@ export type FileInfo = {
|
|||||||
ast?: ast.File;
|
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 IdentId = IdBase & { readonly _: unique symbol };
|
||||||
export type HirId = IdBase & { readonly _: unique symbol };
|
|
||||||
export type DefId = IdBase & { readonly _: unique symbol };
|
export type DefId = IdBase & { readonly _: unique symbol };
|
||||||
|
|
||||||
export type IdBase = { key: number };
|
export type IdBase = { key: number };
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as path from "jsr:@std/path";
|
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 * as ast from "./ast/mod.ts";
|
||||||
import { Ctx } from "./ctx.ts";
|
import { Ctx } from "./ctx.ts";
|
||||||
import { File } from "./ctx.ts";
|
import { File } from "./ctx.ts";
|
||||||
@ -28,6 +28,7 @@ export class NullEmitter implements PackEmitter {
|
|||||||
|
|
||||||
export class PackCompiler {
|
export class PackCompiler {
|
||||||
private ctx = new Ctx();
|
private ctx = new Ctx();
|
||||||
|
private astCx = new ast.Cx();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private entryFilePath: string,
|
private entryFilePath: string,
|
||||||
@ -36,7 +37,7 @@ export class PackCompiler {
|
|||||||
|
|
||||||
public async compile() {
|
public async compile() {
|
||||||
await FileTreeAstCollector
|
await FileTreeAstCollector
|
||||||
.fromEntryFile(this.ctx, this.entryFilePath)
|
.fromEntryFile(this.ctx, this.astCx, this.entryFilePath)
|
||||||
.collect();
|
.collect();
|
||||||
this.ctx.printAsts();
|
this.ctx.printAsts();
|
||||||
}
|
}
|
||||||
@ -53,6 +54,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
|||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
private ctx: Ctx,
|
private ctx: Ctx,
|
||||||
|
private astCx: ast.Cx,
|
||||||
private superFile: File | undefined,
|
private superFile: File | undefined,
|
||||||
private ident: string,
|
private ident: string,
|
||||||
private absPath: string,
|
private absPath: string,
|
||||||
@ -61,10 +63,12 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
|||||||
|
|
||||||
public static fromEntryFile(
|
public static fromEntryFile(
|
||||||
ctx: Ctx,
|
ctx: Ctx,
|
||||||
|
astCx: ast.Cx,
|
||||||
entryFilePath: string,
|
entryFilePath: string,
|
||||||
): FileTreeAstCollector {
|
): FileTreeAstCollector {
|
||||||
return new FileTreeAstCollector(
|
return new FileTreeAstCollector(
|
||||||
ctx,
|
ctx,
|
||||||
|
astCx,
|
||||||
undefined,
|
undefined,
|
||||||
"root",
|
"root",
|
||||||
entryFilePath,
|
entryFilePath,
|
||||||
@ -81,7 +85,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
|||||||
this.superFile,
|
this.superFile,
|
||||||
text,
|
text,
|
||||||
);
|
);
|
||||||
const fileAst = new Parser(this.ctx, file).parse();
|
const fileAst = new Parser(this.ctx, this.astCx, file).parse();
|
||||||
this.ctx.addFileAst(file, fileAst);
|
this.ctx.addFileAst(file, fileAst);
|
||||||
ast.visitFile(this, fileAst, { file });
|
ast.visitFile(this, fileAst, { file });
|
||||||
await this.subFilePromise;
|
await this.subFilePromise;
|
||||||
@ -109,6 +113,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
|||||||
}
|
}
|
||||||
const modFile = await new FileTreeAstCollector(
|
const modFile = await new FileTreeAstCollector(
|
||||||
this.ctx,
|
this.ctx,
|
||||||
|
this.astCx,
|
||||||
file,
|
file,
|
||||||
ident,
|
ident,
|
||||||
absPath,
|
absPath,
|
||||||
|
@ -1,277 +0,0 @@
|
|||||||
import { HirId, IdentId } from "../ctx.ts";
|
|
||||||
import { Span } from "../diagnostics.ts";
|
|
||||||
import { Mod, Res } from "./res.ts";
|
|
||||||
|
|
||||||
export type Stmt = {
|
|
||||||
id: HirId;
|
|
||||||
kind: StmtKind;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type StmtKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "item" } & ItemStmt
|
|
||||||
| { tag: "let" } & LetStmt
|
|
||||||
| { tag: "return" } & ReturnStmt
|
|
||||||
| { tag: "break" } & BreakStmt
|
|
||||||
| { tag: "continue" }
|
|
||||||
| { tag: "assign" } & AssignStmt
|
|
||||||
| { tag: "expr" } & ExprStmt;
|
|
||||||
|
|
||||||
export type ItemStmt = { item: Item };
|
|
||||||
|
|
||||||
export type LetStmt = {
|
|
||||||
pat: Pat;
|
|
||||||
ty?: Ty;
|
|
||||||
expr?: Expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ReturnStmt = { expr?: Expr };
|
|
||||||
export type BreakStmt = { expr?: Expr };
|
|
||||||
|
|
||||||
export type AssignStmt = {
|
|
||||||
assignType: AssignType;
|
|
||||||
subject: Expr;
|
|
||||||
value: Expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AssignType = "=" | "+=" | "-=";
|
|
||||||
|
|
||||||
export type ExprStmt = { expr: Expr };
|
|
||||||
|
|
||||||
export type Item = {
|
|
||||||
id: HirId;
|
|
||||||
kind: ItemKind;
|
|
||||||
span: Span;
|
|
||||||
ident: Ident;
|
|
||||||
pub: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ItemKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "mod" } & ModItem
|
|
||||||
| { tag: "enum" } & EnumItem
|
|
||||||
| { tag: "struct" } & StructItem
|
|
||||||
| { tag: "fn" } & FnItem
|
|
||||||
| { tag: "use" } & UseItem
|
|
||||||
| { tag: "type_alias" } & TypeAliasItem;
|
|
||||||
|
|
||||||
export type ModItem = { mod: Mod };
|
|
||||||
export type EnumItem = { variants: Variant[] };
|
|
||||||
export type StructItem = { data: VariantData };
|
|
||||||
|
|
||||||
export type FnItem = {
|
|
||||||
generics?: Generics;
|
|
||||||
params: Param[];
|
|
||||||
returnTy?: Ty;
|
|
||||||
body: Block;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UseItem = { _: 0 };
|
|
||||||
export type TypeAliasItem = { ty: Ty };
|
|
||||||
|
|
||||||
export type Variant = {
|
|
||||||
ident: Ident;
|
|
||||||
data: VariantData;
|
|
||||||
pub: boolean;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type VariantData = {
|
|
||||||
kind: VariantDataKind;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type VariantDataKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "unit" }
|
|
||||||
| { tag: "tuple" } & TupleVariantData
|
|
||||||
| { tag: "struct" } & StructVariantData;
|
|
||||||
|
|
||||||
export type TupleVariantData = { elems: VariantData[] };
|
|
||||||
export type StructVariantData = { fields: FieldDef[] };
|
|
||||||
|
|
||||||
export type FieldDef = {
|
|
||||||
ident: Ident;
|
|
||||||
ty: Ty;
|
|
||||||
pub: boolean;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Param = {
|
|
||||||
pat: Pat;
|
|
||||||
ty: Ty;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Generics = {
|
|
||||||
params: GenericParam[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GenericParam = {
|
|
||||||
ident: Ident;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Expr = {
|
|
||||||
id: HirId;
|
|
||||||
kind: ExprKind;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExprKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "path" } & PathExpr
|
|
||||||
| { tag: "null" }
|
|
||||||
| { tag: "int" } & IntExpr
|
|
||||||
| { tag: "bool" } & BoolExpr
|
|
||||||
| { tag: "str" } & StringExpr
|
|
||||||
| { tag: "group" } & GroupExpr
|
|
||||||
| { tag: "array" } & ArrayExpr
|
|
||||||
| { tag: "repeat" } & RepeatExpr
|
|
||||||
| { tag: "struct" } & StructExpr
|
|
||||||
| { tag: "ref" } & RefExpr
|
|
||||||
| { tag: "deref" } & DerefExpr
|
|
||||||
| { tag: "elem" } & ElemExpr
|
|
||||||
| { tag: "field" } & FieldExpr
|
|
||||||
| { tag: "index" } & IndexExpr
|
|
||||||
| { tag: "call" } & CallExpr
|
|
||||||
| { tag: "unary" } & UnaryExpr
|
|
||||||
| { tag: "binary" } & BinaryExpr
|
|
||||||
| { tag: "block" } & BlockExpr
|
|
||||||
| { tag: "if" } & IfExpr
|
|
||||||
| { tag: "loop" } & LoopExpr
|
|
||||||
| { tag: "while" } & WhileExpr
|
|
||||||
| { tag: "for" } & ForExpr
|
|
||||||
| { tag: "c_for" } & CForExpr;
|
|
||||||
|
|
||||||
export type PathExpr = { path: QPath };
|
|
||||||
export type IntExpr = { value: number };
|
|
||||||
export type BoolExpr = { value: boolean };
|
|
||||||
export type StringExpr = { value: string };
|
|
||||||
export type GroupExpr = { expr: Expr };
|
|
||||||
export type ArrayExpr = { exprs: Expr[] };
|
|
||||||
export type RepeatExpr = { expr: Expr; length: Expr };
|
|
||||||
export type StructExpr = { path?: QPath; fields: ExprField[] };
|
|
||||||
export type RefExpr = { expr: Expr; refType: RefType; mut: boolean };
|
|
||||||
export type DerefExpr = { expr: Expr };
|
|
||||||
export type ElemExpr = { expr: Expr; elem: number };
|
|
||||||
export type FieldExpr = { expr: Expr; ident: Ident };
|
|
||||||
export type IndexExpr = { expr: Expr; index: Expr };
|
|
||||||
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 RefType = "ref" | "ptr";
|
|
||||||
export type UnaryType = "not" | "-";
|
|
||||||
export type BinaryType =
|
|
||||||
| "+"
|
|
||||||
| "*"
|
|
||||||
| "=="
|
|
||||||
| "-"
|
|
||||||
| "/"
|
|
||||||
| "!="
|
|
||||||
| "<"
|
|
||||||
| ">"
|
|
||||||
| "<="
|
|
||||||
| ">="
|
|
||||||
| "or"
|
|
||||||
| "and";
|
|
||||||
|
|
||||||
export type ExprField = {
|
|
||||||
ident: Ident;
|
|
||||||
expr: Expr;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Pat = {
|
|
||||||
id: HirId;
|
|
||||||
kind: PatKind;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PatKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "bind" } & BindPat
|
|
||||||
| { tag: "path" } & PathPat;
|
|
||||||
|
|
||||||
export type BindPat = { ident: Ident; mut: boolean };
|
|
||||||
export type PathPat = { path: QPath };
|
|
||||||
|
|
||||||
export type Ty = {
|
|
||||||
id: HirId;
|
|
||||||
kind: TyKind;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TyKind =
|
|
||||||
| { tag: "error" }
|
|
||||||
| { tag: "null" }
|
|
||||||
| { tag: "int" }
|
|
||||||
| { tag: "bool" }
|
|
||||||
| { tag: "str" }
|
|
||||||
| { tag: "path" } & PathTy
|
|
||||||
| { tag: "ref" } & RefTy
|
|
||||||
| { tag: "ptr" } & PtrTy
|
|
||||||
| { tag: "slice" } & SliceTy
|
|
||||||
| { tag: "array" } & ArrayTy
|
|
||||||
| { tag: "anon_struct" } & AnonStructTy;
|
|
||||||
|
|
||||||
export type PathTy = { path: QPath };
|
|
||||||
export type RefTy = { ty: Ty; mut: boolean };
|
|
||||||
export type PtrTy = { ty: Ty; mut: boolean };
|
|
||||||
export type SliceTy = { ty: Ty };
|
|
||||||
export type ArrayTy = { ty: Ty; length: Expr };
|
|
||||||
export type TupleTy = { elems: Ty[] };
|
|
||||||
export type AnonStructTy = { fields: AnonFieldDef[] };
|
|
||||||
|
|
||||||
export type AnonFieldDef = {
|
|
||||||
ident: Ident;
|
|
||||||
ty: Ty;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Block = {
|
|
||||||
id: HirId;
|
|
||||||
stmts: Stmt[];
|
|
||||||
expr?: Expr;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type QPath =
|
|
||||||
| { tag: "resolved"; path: Path }
|
|
||||||
| { tag: "type_relative"; ty: Ty; seg: PathSegment };
|
|
||||||
|
|
||||||
export type Path = {
|
|
||||||
segments: PathSegment[];
|
|
||||||
res: Res;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PathSegment = {
|
|
||||||
ident: Ident;
|
|
||||||
res: Res;
|
|
||||||
genericArgs?: Ty[];
|
|
||||||
inferArgs: boolean;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Ident = {
|
|
||||||
id: IdentId;
|
|
||||||
span: Span;
|
|
||||||
};
|
|
@ -1,80 +0,0 @@
|
|||||||
import { arrayEq, exhausted } from "../util.ts";
|
|
||||||
import { Ident, Item, Stmt, VariantData } from "./hir.ts";
|
|
||||||
|
|
||||||
/// Shallow equality
|
|
||||||
export function stmtEq(a: Stmt, b: Stmt): boolean {
|
|
||||||
const [ak, bk] = [a.kind, b.kind];
|
|
||||||
switch (ak.tag) {
|
|
||||||
case "error":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "item":
|
|
||||||
return ak.tag === bk.tag && ak.item === bk.item;
|
|
||||||
case "let":
|
|
||||||
return ak.tag === bk.tag && ak.pat === bk.pat && ak.ty === bk.ty &&
|
|
||||||
ak.expr === bk.expr;
|
|
||||||
case "return":
|
|
||||||
return ak.tag === bk.tag && ak.expr === bk.expr;
|
|
||||||
case "break":
|
|
||||||
return ak.tag === bk.tag && ak.expr === bk.expr;
|
|
||||||
case "continue":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "assign":
|
|
||||||
return ak.tag === bk.tag && ak.assignType === bk.assignType &&
|
|
||||||
ak.subject === bk.subject && ak.value === bk.value;
|
|
||||||
case "expr":
|
|
||||||
return ak.tag === bk.tag && ak.expr === bk.expr;
|
|
||||||
}
|
|
||||||
exhausted(ak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shallow equality
|
|
||||||
export function itemEq(a: Item, b: Item): boolean {
|
|
||||||
const [ak, bk] = [a.kind, b.kind];
|
|
||||||
switch (ak.tag) {
|
|
||||||
case "error":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "mod_block":
|
|
||||||
return ak.tag === bk.tag && ak.block === bk.block;
|
|
||||||
case "mod_file":
|
|
||||||
return ak.tag === bk.tag && ak.filePath === bk.filePath;
|
|
||||||
case "enum":
|
|
||||||
return ak.tag === bk.tag &&
|
|
||||||
arrayEq(ak.variants, bk.variants);
|
|
||||||
case "struct":
|
|
||||||
return ak.tag === bk.tag && variantDataEq(ak.data, bk.data);
|
|
||||||
case "fn":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "use":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "type_alias":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
}
|
|
||||||
exhausted(ak);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function variantDataEq(a: VariantData, b: VariantData): boolean {
|
|
||||||
const [ak, bk] = [a.kind, b.kind];
|
|
||||||
switch (ak.tag) {
|
|
||||||
case "error":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "unit":
|
|
||||||
return ak.tag === bk.tag;
|
|
||||||
case "tuple":
|
|
||||||
return ak.tag === bk.tag &&
|
|
||||||
arrayEq(ak.elems, bk.elems, variantDataEq);
|
|
||||||
case "struct":
|
|
||||||
return ak.tag === bk.tag &&
|
|
||||||
arrayEq(
|
|
||||||
ak.fields,
|
|
||||||
bk.fields,
|
|
||||||
(a, b) =>
|
|
||||||
identEq(a.ident, b.ident) && a.ty === b.ty &&
|
|
||||||
a.pub === b.pub && a.span === b.span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
exhausted(ak);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function identEq(a: Ident, b: Ident): boolean {
|
|
||||||
return a.id === b.id;
|
|
||||||
}
|
|
@ -1,637 +0,0 @@
|
|||||||
import { Ctx, DefId, File, IdentId, idKey, Mod } from "../ctx.ts";
|
|
||||||
import * as ast from "../ast/ast.ts";
|
|
||||||
import {
|
|
||||||
Block,
|
|
||||||
Expr,
|
|
||||||
Ident,
|
|
||||||
Item,
|
|
||||||
Pat,
|
|
||||||
Path,
|
|
||||||
PathSegment,
|
|
||||||
QPath,
|
|
||||||
Stmt,
|
|
||||||
Ty,
|
|
||||||
} from "./hir.ts";
|
|
||||||
import { exhausted, Res as Result, todo } from "../util.ts";
|
|
||||||
import { Rib, RibKind } from "./rib.ts";
|
|
||||||
import { Res } from "./res.ts";
|
|
||||||
|
|
||||||
export class AstLowerer {
|
|
||||||
private tyRibs: Rib[] = [];
|
|
||||||
private valRibs: Rib[] = [];
|
|
||||||
private currentFile!: File;
|
|
||||||
private currentMod!: Mod;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
private ctx: Ctx,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerFile(file: ast.File) {
|
|
||||||
this.lowerStmts(file.stmts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerStmts(stmts: ast.Stmt[]): Stmt[] {
|
|
||||||
return stmts.map((stmt) => this.lowerStmt(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerStmt(stmt: ast.Stmt): Stmt {
|
|
||||||
const span = stmt.span;
|
|
||||||
const kind = stmt.kind;
|
|
||||||
switch (kind.tag) {
|
|
||||||
case "error":
|
|
||||||
return this.ctx.internStmt(kind, span);
|
|
||||||
case "item":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "item",
|
|
||||||
item: this.lowerItem(kind.item),
|
|
||||||
}, span);
|
|
||||||
case "let":
|
|
||||||
return this.lowerLetStmt(stmt, kind);
|
|
||||||
case "return":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "return",
|
|
||||||
expr: kind.expr && this.lowerExpr(kind.expr),
|
|
||||||
}, span);
|
|
||||||
case "break":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "break",
|
|
||||||
expr: kind.expr && this.lowerExpr(kind.expr),
|
|
||||||
}, span);
|
|
||||||
case "continue":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "continue",
|
|
||||||
}, span);
|
|
||||||
case "assign":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "assign",
|
|
||||||
assignType: kind.assignType,
|
|
||||||
subject: this.lowerExpr(kind.subject),
|
|
||||||
value: this.lowerExpr(kind.value),
|
|
||||||
}, span);
|
|
||||||
case "expr":
|
|
||||||
return this.ctx.internStmt({
|
|
||||||
tag: "expr",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
}, span);
|
|
||||||
}
|
|
||||||
exhausted(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
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({ 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 {
|
|
||||||
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 {
|
|
||||||
const span = expr.span;
|
|
||||||
const kind = expr.kind;
|
|
||||||
switch (kind.tag) {
|
|
||||||
case "error":
|
|
||||||
return this.ctx.internExpr(kind, span);
|
|
||||||
case "path":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "path",
|
|
||||||
path: this.lowerPath(kind.path, kind.qty),
|
|
||||||
}, span);
|
|
||||||
case "null":
|
|
||||||
return this.ctx.internExpr(kind, span);
|
|
||||||
case "int":
|
|
||||||
return this.ctx.internExpr(kind, span);
|
|
||||||
case "bool":
|
|
||||||
return this.ctx.internExpr(kind, span);
|
|
||||||
case "str":
|
|
||||||
return this.ctx.internExpr(kind, span);
|
|
||||||
case "group":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "group",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
}, span);
|
|
||||||
case "array":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "array",
|
|
||||||
exprs: kind.exprs.map((expr) => this.lowerExpr(expr)),
|
|
||||||
}, span);
|
|
||||||
case "repeat":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "repeat",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
length: this.lowerExpr(kind.length),
|
|
||||||
}, span);
|
|
||||||
case "struct":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "struct",
|
|
||||||
path: kind.path && this.lowerPath(kind.path),
|
|
||||||
fields: kind.fields.map(({ ident, expr, span }) => ({
|
|
||||||
ident,
|
|
||||||
expr: this.lowerExpr(expr),
|
|
||||||
span,
|
|
||||||
})),
|
|
||||||
}, span);
|
|
||||||
case "ref":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "ref",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
refType: kind.refType,
|
|
||||||
mut: kind.mut,
|
|
||||||
}, span);
|
|
||||||
case "deref":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "deref",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
}, span);
|
|
||||||
case "elem":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "elem",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
elem: kind.elem,
|
|
||||||
}, span);
|
|
||||||
case "field":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "field",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
ident: kind.ident,
|
|
||||||
}, span);
|
|
||||||
case "index":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "index",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
index: this.lowerExpr(kind.index),
|
|
||||||
}, span);
|
|
||||||
case "call":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "call",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
args: kind.args.map((arg) => this.lowerExpr(arg)),
|
|
||||||
}, span);
|
|
||||||
case "unary":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "unary",
|
|
||||||
expr: this.lowerExpr(kind.expr),
|
|
||||||
unaryType: kind.unaryType,
|
|
||||||
}, span);
|
|
||||||
case "binary":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "binary",
|
|
||||||
left: this.lowerExpr(kind.left),
|
|
||||||
right: this.lowerExpr(kind.right),
|
|
||||||
binaryType: kind.binaryType,
|
|
||||||
}, span);
|
|
||||||
case "block":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "block",
|
|
||||||
block: this.lowerBlock(kind.block),
|
|
||||||
}, span);
|
|
||||||
case "if":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "if",
|
|
||||||
cond: this.lowerExpr(kind.cond),
|
|
||||||
truthy: this.lowerBlock(kind.truthy),
|
|
||||||
falsy: kind.falsy && this.lowerExpr(kind.falsy),
|
|
||||||
}, span);
|
|
||||||
case "loop":
|
|
||||||
return this.ctx.internExpr({
|
|
||||||
tag: "loop",
|
|
||||||
body: this.lowerBlock(kind.body),
|
|
||||||
}, span);
|
|
||||||
case "while":
|
|
||||||
throw new Error("not implemented");
|
|
||||||
case "for":
|
|
||||||
throw new Error("not implemented");
|
|
||||||
case "c_for":
|
|
||||||
throw new Error("not implemented");
|
|
||||||
}
|
|
||||||
exhausted(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerPat(pat: ast.Pat): Pat {
|
|
||||||
const span = pat.span;
|
|
||||||
const kind = pat.kind;
|
|
||||||
switch (kind.tag) {
|
|
||||||
case "error":
|
|
||||||
return this.ctx.internPat(kind, span);
|
|
||||||
case "bind": {
|
|
||||||
const v = this.ctx.internPat(kind, span);
|
|
||||||
this.valRib().bindings.set(idKey(kind.ident.id), {
|
|
||||||
tag: "local",
|
|
||||||
id: v.id,
|
|
||||||
});
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
case "path":
|
|
||||||
return this.ctx.internPat({
|
|
||||||
tag: "path",
|
|
||||||
path: this.lowerPath(kind.path, kind.qty),
|
|
||||||
}, span);
|
|
||||||
}
|
|
||||||
exhausted(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerTy(ty: ast.Ty): Ty {
|
|
||||||
const span = ty.span;
|
|
||||||
const kind = ty.kind;
|
|
||||||
switch (kind.tag) {
|
|
||||||
case "error":
|
|
||||||
return this.ctx.internTy(kind, span);
|
|
||||||
case "null":
|
|
||||||
return this.ctx.internTy(kind, span);
|
|
||||||
case "int":
|
|
||||||
return this.ctx.internTy(kind, span);
|
|
||||||
case "bool":
|
|
||||||
return this.ctx.internTy(kind, span);
|
|
||||||
case "str":
|
|
||||||
return this.ctx.internTy(kind, span);
|
|
||||||
case "path":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "path",
|
|
||||||
path: this.lowerPath(kind.path, kind.qty),
|
|
||||||
}, span);
|
|
||||||
case "ref":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "ref",
|
|
||||||
ty: this.lowerTy(kind.ty),
|
|
||||||
mut: kind.mut,
|
|
||||||
}, span);
|
|
||||||
case "ptr":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "ptr",
|
|
||||||
ty: this.lowerTy(kind.ty),
|
|
||||||
mut: kind.mut,
|
|
||||||
}, span);
|
|
||||||
case "slice":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "slice",
|
|
||||||
ty: this.lowerTy(kind.ty),
|
|
||||||
}, span);
|
|
||||||
case "array":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "array",
|
|
||||||
ty: this.lowerTy(kind.ty),
|
|
||||||
length: this.lowerExpr(kind.length),
|
|
||||||
}, span);
|
|
||||||
case "anon_struct":
|
|
||||||
return this.ctx.internTy({
|
|
||||||
tag: "anon_struct",
|
|
||||||
fields: kind.fields.map(({ ident, ty, span }) => ({
|
|
||||||
ident,
|
|
||||||
ty: this.lowerTy(ty),
|
|
||||||
span,
|
|
||||||
})),
|
|
||||||
}, span);
|
|
||||||
}
|
|
||||||
exhausted(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerBlock(block: ast.Block): Block {
|
|
||||||
const point = this.ribPoint();
|
|
||||||
this.pushRib({ tag: "block" });
|
|
||||||
const stmts = block.stmts.map((stmt) => this.lowerStmt(stmt));
|
|
||||||
const expr = block.expr && this.lowerExpr(block.expr);
|
|
||||||
this.returnToRibPoint(point);
|
|
||||||
return this.ctx.internBlock({ stmts, expr, span: block.span });
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerPath(path: ast.Path, qty?: ast.Ty): QPath {
|
|
||||||
if (qty) {
|
|
||||||
const ty = this.lowerTy(qty);
|
|
||||||
if (path.segments.length !== 1) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
const seg = path.segments[0];
|
|
||||||
return {
|
|
||||||
tag: "type_relative",
|
|
||||||
ty,
|
|
||||||
seg: {
|
|
||||||
ident: seg.ident,
|
|
||||||
res: todo(),
|
|
||||||
genericArgs: seg.genericArgs &&
|
|
||||||
seg.genericArgs.map((ty) => this.lowerTy(ty)),
|
|
||||||
inferArgs: false,
|
|
||||||
span: path.span,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const [res, segments] = this.resolvePathSegs(path.segments);
|
|
||||||
return {
|
|
||||||
tag: "resolved",
|
|
||||||
path: { segments, res, span: path.span },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolvePathSegs(segs: ast.PathSegment[]): [Res, PathSegment[]] {
|
|
||||||
if (segs.length <= 1) {
|
|
||||||
const seg = segs[0];
|
|
||||||
const res = this.resolveTyIdent(seg.ident);
|
|
||||||
return [res, [{
|
|
||||||
ident: seg.ident,
|
|
||||||
res,
|
|
||||||
genericArgs: seg.genericArgs &&
|
|
||||||
seg.genericArgs.map((ty) => this.lowerTy(ty)),
|
|
||||||
inferArgs: false,
|
|
||||||
span: seg.span,
|
|
||||||
}]];
|
|
||||||
}
|
|
||||||
const seg = segs.at(-1)!;
|
|
||||||
const [innerRes, resSegs] = this.resolvePathSegs(
|
|
||||||
segs.slice(0, segs.length - 1),
|
|
||||||
);
|
|
||||||
switch (innerRes.tag) {
|
|
||||||
case "error":
|
|
||||||
return [innerRes, [...resSegs, {
|
|
||||||
ident: seg.ident,
|
|
||||||
res: innerRes,
|
|
||||||
inferArgs: false,
|
|
||||||
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.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 "ty_alias":
|
|
||||||
return todo();
|
|
||||||
case "ty_param":
|
|
||||||
return todo();
|
|
||||||
case "use":
|
|
||||||
return todo();
|
|
||||||
case "fn":
|
|
||||||
case "variant":
|
|
||||||
case "ctor":
|
|
||||||
case "field":
|
|
||||||
this.ctx.report({
|
|
||||||
severity: "error",
|
|
||||||
file: this.currentFile,
|
|
||||||
msg: `${irk.tag} contains zero members`,
|
|
||||||
span: seg.span,
|
|
||||||
});
|
|
||||||
return error();
|
|
||||||
}
|
|
||||||
exhausted(irk);
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
case "local":
|
|
||||||
throw new Error("should not be possible");
|
|
||||||
}
|
|
||||||
exhausted(innerRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 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.tyRibs.pop();
|
|
||||||
this.valRibs.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ribPoint(): number {
|
|
||||||
return this.valRibs.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private returnToRibPoint(point: number) {
|
|
||||||
this.tyRibs = this.tyRibs.slice(0, point);
|
|
||||||
this.valRibs = this.valRibs.slice(0, point);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
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" };
|
|
@ -1,21 +0,0 @@
|
|||||||
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<Key<IdentId>, Res>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/enum.RibKind.html
|
|
||||||
export type RibKind =
|
|
||||||
/// No restriction needs to be applied.
|
|
||||||
| { tag: "normal" }
|
|
||||||
/// We passed through a function, closure or coroutine signature. Disallow labels.
|
|
||||||
| { tag: "fn" }
|
|
||||||
/// We passed through an item scope. Disallow upvars.
|
|
||||||
| { tag: "item"; defKind: DefKind }
|
|
||||||
/// We passed through a module.
|
|
||||||
| { tag: "mod"; mod: Mod }
|
|
||||||
/// We passed through a block (same as module, see Rust).
|
|
||||||
| { tag: "block" };
|
|
@ -25,6 +25,7 @@ import {
|
|||||||
Ty,
|
Ty,
|
||||||
TyKind,
|
TyKind,
|
||||||
} from "../ast/ast.ts";
|
} from "../ast/ast.ts";
|
||||||
|
import { Cx } from "../ast/cx.ts";
|
||||||
import { Ctx, File as CtxFile } from "../ctx.ts";
|
import { Ctx, File as CtxFile } from "../ctx.ts";
|
||||||
import { Pos, Span } from "../diagnostics.ts";
|
import { Pos, Span } from "../diagnostics.ts";
|
||||||
import { Res, todo } from "../util.ts";
|
import { Res, todo } from "../util.ts";
|
||||||
@ -46,6 +47,7 @@ export class Parser {
|
|||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private ctx: Ctx,
|
private ctx: Ctx,
|
||||||
|
private cx: Cx,
|
||||||
private file: CtxFile,
|
private file: CtxFile,
|
||||||
) {
|
) {
|
||||||
this.lexer = new SigFilter(new Lexer(this.ctx, this.file));
|
this.lexer = new SigFilter(new Lexer(this.ctx, this.file));
|
||||||
@ -53,7 +55,7 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public parse(): File {
|
public parse(): File {
|
||||||
return { stmts: this.parseStmts() };
|
return { stmts: this.parseStmts(), file: this.file };
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseStmts(): Stmt[] {
|
private parseStmts(): Stmt[] {
|
||||||
@ -542,11 +544,7 @@ export class Parser {
|
|||||||
this.report("expected '{'");
|
this.report("expected '{'");
|
||||||
return this.expr({ tag: "error" }, pos);
|
return this.expr({ tag: "error" }, pos);
|
||||||
}
|
}
|
||||||
const bodyRes = this.parseBlock();
|
const body = this.parseExpr();
|
||||||
if (!bodyRes.ok) {
|
|
||||||
return this.expr({ tag: "error" }, pos);
|
|
||||||
}
|
|
||||||
const body = bodyRes.val;
|
|
||||||
return this.expr({ tag: "loop", body }, pos);
|
return this.expr({ tag: "loop", body }, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,11 +556,7 @@ export class Parser {
|
|||||||
this.report("expected '{'");
|
this.report("expected '{'");
|
||||||
return this.expr({ tag: "error" }, pos);
|
return this.expr({ tag: "error" }, pos);
|
||||||
}
|
}
|
||||||
const bodyRes = this.parseBlock();
|
const body = this.parseExpr();
|
||||||
if (!bodyRes.ok) {
|
|
||||||
return this.expr({ tag: "error" }, pos);
|
|
||||||
}
|
|
||||||
const body = bodyRes.val;
|
|
||||||
return this.expr({ tag: "while", cond, body }, pos);
|
return this.expr({ tag: "while", cond, body }, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,11 +581,7 @@ export class Parser {
|
|||||||
this.report("expected '{'");
|
this.report("expected '{'");
|
||||||
return this.expr({ tag: "error" }, pos);
|
return this.expr({ tag: "error" }, pos);
|
||||||
}
|
}
|
||||||
const bodyRes = this.parseBlock();
|
const body = this.parseExpr();
|
||||||
if (!bodyRes.ok) {
|
|
||||||
return this.expr({ tag: "error" }, pos);
|
|
||||||
}
|
|
||||||
const body = bodyRes.val;
|
|
||||||
return this.expr({ tag: "for", pat, expr, body }, pos);
|
return this.expr({ tag: "for", pat, expr, body }, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,11 +619,7 @@ export class Parser {
|
|||||||
this.report("expected '{'");
|
this.report("expected '{'");
|
||||||
return this.expr({ tag: "error" }, begin);
|
return this.expr({ tag: "error" }, begin);
|
||||||
}
|
}
|
||||||
const bodyRes = this.parseBlock();
|
const body = this.parseExpr();
|
||||||
if (!bodyRes.ok) {
|
|
||||||
return this.expr({ tag: "error" }, begin);
|
|
||||||
}
|
|
||||||
const body = bodyRes.val;
|
|
||||||
return this.expr(
|
return this.expr(
|
||||||
{ tag: "c_for", decl, cond, incr, body },
|
{ tag: "c_for", decl, cond, incr, body },
|
||||||
Span.fromto(begin, body.span),
|
Span.fromto(begin, body.span),
|
||||||
@ -721,11 +707,7 @@ export class Parser {
|
|||||||
this.report("expected block");
|
this.report("expected block");
|
||||||
return this.expr({ tag: "error" }, pos);
|
return this.expr({ tag: "error" }, pos);
|
||||||
}
|
}
|
||||||
const truthyRes = this.parseBlock();
|
const truthy = this.parseExpr();
|
||||||
if (!truthyRes.ok) {
|
|
||||||
return this.expr({ tag: "error" }, pos);
|
|
||||||
}
|
|
||||||
const truthy = truthyRes.val;
|
|
||||||
if (!this.test("else")) {
|
if (!this.test("else")) {
|
||||||
return this.expr({ tag: "if", cond, truthy }, pos);
|
return this.expr({ tag: "if", cond, truthy }, pos);
|
||||||
}
|
}
|
||||||
@ -1263,7 +1245,7 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private stmt(kind: StmtKind, span: Span): Stmt {
|
private stmt(kind: StmtKind, span: Span): Stmt {
|
||||||
return { kind, span };
|
return this.cx.stmt(kind, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
private item(
|
private item(
|
||||||
@ -1272,18 +1254,18 @@ export class Parser {
|
|||||||
ident: Ident,
|
ident: Ident,
|
||||||
pub: boolean,
|
pub: boolean,
|
||||||
): Item {
|
): Item {
|
||||||
return { kind, span, ident, pub };
|
return this.cx.item(kind, span, ident, pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
private expr(kind: ExprKind, span: Span): Expr {
|
private expr(kind: ExprKind, span: Span): Expr {
|
||||||
return { kind, span };
|
return this.cx.expr(kind, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
private pat(kind: PatKind, span: Span): Pat {
|
private pat(kind: PatKind, span: Span): Pat {
|
||||||
return { kind, span };
|
return this.cx.pat(kind, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ty(kind: TyKind, span: Span): Ty {
|
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