compiler: match expr first half

This commit is contained in:
sfja 2025-02-24 00:02:16 +01:00
parent 5c9e3ce73f
commit 8f3523d4e1
12 changed files with 349 additions and 29 deletions

View File

@ -148,6 +148,7 @@ export type ExprKind =
| { tag: "binary" } & BinaryExpr | { tag: "binary" } & BinaryExpr
| { tag: "block" } & BlockExpr | { tag: "block" } & BlockExpr
| { tag: "if" } & IfExpr | { tag: "if" } & IfExpr
| { tag: "match" } & MatchExpr
| { tag: "loop" } & LoopExpr | { tag: "loop" } & LoopExpr
| { tag: "while" } & WhileExpr | { tag: "while" } & WhileExpr
| { tag: "for" } & ForExpr | { tag: "for" } & ForExpr
@ -171,11 +172,18 @@ 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: Expr; falsy?: Expr }; export type IfExpr = { cond: Expr; truthy: Expr; falsy?: Expr };
export type MatchExpr = { expr: Expr; arms: MatchArm[] };
export type LoopExpr = { body: Expr }; export type LoopExpr = { body: Expr };
export type WhileExpr = { cond: Expr; body: Expr }; export type WhileExpr = { cond: Expr; body: Expr };
export type ForExpr = { pat: Pat; expr: 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 CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Expr };
export type MatchArm = {
pat: Pat;
expr: Expr;
span: Span;
};
export type RefType = "ref" | "ptr"; export type RefType = "ref" | "ptr";
export type UnaryType = "not" | "-"; export type UnaryType = "not" | "-";
export type BinaryType = export type BinaryType =
@ -213,10 +221,20 @@ export type Pat = {
export type PatKind = export type PatKind =
| { tag: "error" } | { tag: "error" }
| { tag: "bind" } & BindPat | { tag: "bind" } & BindPat
| { tag: "path" } & PathPat; | { tag: "path" } & PathPat
| { tag: "tuple" } & TuplePat
| { tag: "struct" } & StructPat;
export type BindPat = { ident: Ident; mut: boolean }; export type BindPat = { ident: Ident; mut: boolean };
export type PathPat = { qty?: Ty; path: Path }; export type PathPat = { qty?: Ty; path: Path };
export type TuplePat = { path?: Path; elems: Pat[] };
export type StructPat = { path?: Path; fields: PatField[] };
export type PatField = {
ident: Ident;
pat: Pat;
span: Span;
};
export type Ty = { export type Ty = {
id: AstId; id: AstId;

View File

@ -33,6 +33,8 @@ import {
ItemStmt, ItemStmt,
LetStmt, LetStmt,
LoopExpr, LoopExpr,
MatchArm,
MatchExpr,
ModBlockItem, ModBlockItem,
ModFileItem, ModFileItem,
Param, Param,
@ -51,6 +53,8 @@ import {
StringExpr, StringExpr,
StructExpr, StructExpr,
StructItem, StructItem,
StructPat,
TuplePat,
TupleTy, TupleTy,
Ty, Ty,
TypeAliasItem, TypeAliasItem,
@ -116,15 +120,20 @@ export interface Visitor<
visitBinaryExpr?(expr: Expr, kind: BinaryExpr, ...p: P): R; visitBinaryExpr?(expr: Expr, kind: BinaryExpr, ...p: P): R;
visitBlockExpr?(expr: Expr, kind: BlockExpr, ...p: P): R; visitBlockExpr?(expr: Expr, kind: BlockExpr, ...p: P): R;
visitIfExpr?(expr: Expr, kind: IfExpr, ...p: P): R; visitIfExpr?(expr: Expr, kind: IfExpr, ...p: P): R;
visitMatchExpr?(expr: Expr, kind: MatchExpr, ...p: P): R;
visitLoopExpr?(expr: Expr, kind: LoopExpr, ...p: P): R; visitLoopExpr?(expr: Expr, kind: LoopExpr, ...p: P): R;
visitWhileExpr?(expr: Expr, kind: WhileExpr, ...p: P): R; visitWhileExpr?(expr: Expr, kind: WhileExpr, ...p: P): R;
visitForExpr?(expr: Expr, kind: ForExpr, ...p: P): R; visitForExpr?(expr: Expr, kind: ForExpr, ...p: P): R;
visitCForExpr?(expr: Expr, kind: CForExpr, ...p: P): R; visitCForExpr?(expr: Expr, kind: CForExpr, ...p: P): R;
visitMatchArm?(arm: MatchArm, ...p: P): R;
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; visitPathPat?(pat: Pat, kind: PathPat, ...p: P): R;
visitTuplePat?(pat: Pat, kind: TuplePat, ...p: P): R;
visitStructPat?(pat: Pat, kind: StructPat, ...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;
@ -453,6 +462,13 @@ export function visitExpr<
visitExpr(v, kind.falsy, ...p); visitExpr(v, kind.falsy, ...p);
} }
return; return;
case "match":
if (v.visitMatchExpr?.(expr, kind, ...p) === "stop") return;
visitExpr(v, kind.expr, ...p);
for (const arm of kind.arms) {
visitMatchArm(v, arm, ...p);
}
return;
case "loop": case "loop":
if (v.visitLoopExpr?.(expr, kind, ...p) === "stop") return; if (v.visitLoopExpr?.(expr, kind, ...p) === "stop") return;
visitExpr(v, kind.body, ...p); visitExpr(v, kind.body, ...p);
@ -484,6 +500,18 @@ export function visitExpr<
exhausted(kind); exhausted(kind);
} }
export function visitMatchArm<
P extends PM = [],
>(
v: Visitor<P>,
arm: MatchArm,
...p: P
) {
if (v.visitMatchArm?.(arm, ...p) === "stop") return;
visitPat(v, arm.pat, ...p);
visitExpr(v, arm.expr, ...p);
}
export function visitPat< export function visitPat<
P extends PM = [], P extends PM = [],
>( >(
@ -504,6 +532,21 @@ export function visitPat<
if (v.visitPathPat?.(pat, kind, ...p) === "stop") return; if (v.visitPathPat?.(pat, kind, ...p) === "stop") return;
visitPath(v, kind.path, ...p); visitPath(v, kind.path, ...p);
return; return;
case "tuple":
if (v.visitTuplePat?.(pat, kind, ...p) === "stop") return;
kind.path && visitPath(v, kind.path, ...p);
for (const pat of kind.elems) {
visitPat(v, pat, ...p);
}
return;
case "struct":
if (v.visitStructPat?.(pat, kind, ...p) === "stop") return;
kind.path && visitPath(v, kind.path, ...p);
for (const field of kind.fields) {
visitIdent(v, field.ident, ...p);
visitPat(v, field.pat, ...p);
}
return;
} }
exhausted(kind); exhausted(kind);
} }

View File

@ -88,6 +88,10 @@ export class Checker {
return Ok(undefined); return Ok(undefined);
case "path": case "path":
return todo(); return todo();
case "tuple":
return todo();
case "struct":
return todo();
} }
exhausted(k); exhausted(k);
} }
@ -230,6 +234,7 @@ export class Checker {
case "binary": case "binary":
case "block": case "block":
case "if": case "if":
case "match":
case "loop": case "loop":
case "while": case "while":
case "for": case "for":
@ -358,6 +363,8 @@ export class Checker {
return this.checkBlock(k.block, expected); return this.checkBlock(k.block, expected);
case "if": case "if":
return this.checkIfExpr(expr, k, expected); return this.checkIfExpr(expr, k, expected);
case "match":
return this.checkMatchExpr(expr, k, expected);
case "loop": case "loop":
return this.checkLoopExpr(expr, k, expected); return this.checkLoopExpr(expr, k, expected);
case "while": case "while":
@ -760,6 +767,36 @@ export class Checker {
return bothRes.val; return bothRes.val;
} }
private checkMatchExpr(
expr: ast.Expr,
kind: ast.MatchExpr,
expected: Ty,
): Ty {
const ty = this.exprTy(kind.expr);
for (const arm of kind.arms) {
const res = this.assignPatTy(arm.pat, ty);
if (!res.ok) {
this.report(res.val, arm.pat.span);
continue;
}
}
const tyRes = kind.arms
.reduce<Res<Ty, string>>((earlier, arm) => {
if (!earlier.ok) {
return earlier;
}
const exprTy = this.exprTy(arm.expr);
return this.resolveTys(exprTy, earlier.val);
}, Res.Ok(Ty({ tag: "null" })));
if (!tyRes.ok) {
this.report(tyRes.val, expr.span);
this.exprTys.set(expr.id, Ty({ tag: "error" }));
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, tyRes.val);
return todo();
}
private checkLoopExpr( private checkLoopExpr(
expr: ast.Expr, expr: ast.Expr,
kind: ast.LoopExpr, kind: ast.LoopExpr,
@ -911,15 +948,23 @@ export class Checker {
} }
case "let": { case "let": {
this.checkLetStmt(patRes.kind.stmt, patRes.kind.kind); this.checkLetStmt(patRes.kind.stmt, patRes.kind.kind);
const ty = this.patTy(pat); return this.patTy(pat);
this.patTys.set(pat.id, ty); }
return ty; case "match": {
this.checkMatchExpr(
patRes.kind.expr,
patRes.kind.kind,
Ty({ tag: "unknown" }),
);
return this.patTy(pat);
} }
} }
exhausted(patRes.kind); exhausted(patRes.kind);
return todo(); return todo();
} }
case "path": case "path":
case "tuple":
case "struct":
return todo(); return todo();
} }
exhausted(k); exhausted(k);

View File

@ -119,9 +119,15 @@ export class Ctx {
return this.maxSeverity >= severityCode.error; return this.maxSeverity >= severityCode.error;
} }
private amountReportedImmediately = 0;
public enableReportImmediately = false; public enableReportImmediately = false;
public enableStacktrace = false; public enableStacktrace = false;
private reportImmediately(rep: Report) { private reportImmediately(rep: Report) {
if (this.amountReportedImmediately >= 2) {
return;
}
this.amountReportedImmediately += 1;
if (this.enableReportImmediately) { if (this.enableReportImmediately) {
prettyPrintReport(this, rep); prettyPrintReport(this, rep);
if (this.enableStacktrace) { if (this.enableStacktrace) {

View File

@ -79,7 +79,7 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
"color: cyan", "color: cyan",
); );
console.error( console.error(
`${rep.span.begin.line.toString().padStart(4, " ")}%c| %c${ `%c${rep.span.begin.line.toString().padStart(4, " ")}| %c${
spanLines[0] spanLines[0]
}`, }`,
"color: cyan", "color: cyan",
@ -92,6 +92,7 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
"color: cyan", "color: cyan",
`color: ${sevColor}; font-weight: bold`, `color: ${sevColor}; font-weight: bold`,
); );
console.error(""); // empty newline
return; return;
} }
for (let i = 0; i < spanLines.length; i++) { for (let i = 0; i < spanLines.length; i++) {
@ -125,6 +126,7 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
); );
} }
} }
console.error(""); // empty newline
} else if (rep.pos) { } else if (rep.pos) {
console.error( console.error(
` %c|`, ` %c|`,
@ -142,6 +144,7 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
"color: cyan", "color: cyan",
`color: ${sevColor}; font-weight: bold`, `color: ${sevColor}; font-weight: bold`,
); );
console.error(""); // empty newline
} }
} }

View File

@ -152,6 +152,8 @@ export class FnLowerer {
return; return;
} }
case "path": case "path":
case "tuple":
case "struct":
return todo(); return todo();
} }
exhausted(k); exhausted(k);
@ -172,6 +174,8 @@ export class FnLowerer {
return; return;
} }
case "path": case "path":
case "tuple":
case "struct":
return todo(); return todo();
} }
exhausted(k); exhausted(k);
@ -246,6 +250,7 @@ export class FnLowerer {
case "binary": case "binary":
case "block": case "block":
case "if": case "if":
case "match":
case "loop": case "loop":
case "while": case "while":
case "for": case "for":
@ -292,6 +297,8 @@ export class FnLowerer {
return this.lowerBlock(k.block); return this.lowerBlock(k.block);
case "if": case "if":
return this.lowerIfExpr(expr, k); return this.lowerIfExpr(expr, k);
case "match":
return this.lowerMatchExpr(expr, k);
case "loop": case "loop":
return this.lowerLoopExpr(expr, k); return this.lowerLoopExpr(expr, k);
case "while": case "while":
@ -507,6 +514,10 @@ export class FnLowerer {
} }
} }
private lowerMatchExpr(expr: ast.Expr, kind: ast.MatchExpr): RVal {
return todo();
}
private lowerLoopExpr(expr: ast.Expr, kind: ast.LoopExpr): RVal { private lowerLoopExpr(expr: ast.Expr, kind: ast.LoopExpr): RVal {
const entryBlock = this.currentBlock!; const entryBlock = this.currentBlock!;
const loopBlock = this.pushBlock(); const loopBlock = this.pushBlock();
@ -681,6 +692,7 @@ export class FnLowerer {
case "binary": case "binary":
case "block": case "block":
case "if": case "if":
case "match":
case "loop": case "loop":
case "while": case "while":
case "for": case "for":

View File

@ -291,6 +291,7 @@ const keywords = new Set([
"while", "while",
"for", "for",
"in", "in",
"match",
"mod", "mod",
"pub", "pub",
"use", "use",
@ -300,6 +301,7 @@ const keywords = new Set([
const staticTokens = [ const staticTokens = [
"=", "=",
"==", "==",
"=>",
"<", "<",
"<=", "<=",
">", ">",

View File

@ -14,8 +14,10 @@ import {
Ident, Ident,
Item, Item,
ItemKind, ItemKind,
MatchArm,
Param, Param,
Pat, Pat,
PatField,
Path, Path,
PathSegment, PathSegment,
PatKind, PatKind,
@ -81,7 +83,9 @@ export class Parser {
this.eatSemicolon(); this.eatSemicolon();
return expr; return expr;
} else if ( } else if (
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt)) ["{", "if", "match", "loop", "while", "for"].some((tt) =>
this.test(tt)
)
) { ) {
const expr = this.parseMultiLineBlockExpr(); const expr = this.parseMultiLineBlockExpr();
return (this.stmt({ tag: "expr", expr }, expr.span)); return (this.stmt({ tag: "expr", expr }, expr.span));
@ -139,6 +143,9 @@ export class Parser {
if (this.test("if")) { if (this.test("if")) {
return this.parseIf(); return this.parseIf();
} }
if (this.test("match")) {
return this.parseMatch();
}
if (this.test("loop")) { if (this.test("loop")) {
return this.parseLoop(); return this.parseLoop();
} }
@ -204,7 +211,9 @@ export class Parser {
stmts.push(this.parseSingleLineBlockStmt()); stmts.push(this.parseSingleLineBlockStmt());
this.eatSemicolon(); this.eatSemicolon();
} else if ( } else if (
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt)) ["{", "if", "match", "loop", "while", "for"].some((tt) =>
this.test(tt)
)
) { ) {
const expr = this.parseMultiLineBlockExpr(); const expr = this.parseMultiLineBlockExpr();
const span = expr.span; const span = expr.span;
@ -847,6 +856,53 @@ export class Parser {
return this.expr({ tag: "if", cond, truthy, falsy }, pos); return this.expr({ tag: "if", cond, truthy, falsy }, pos);
} }
private parseMatch(): Expr {
const begin = this.span();
let end = begin;
this.step();
const expr = this.parseExpr(ExprRestricts.NoStructs);
if (!this.test("{")) {
this.report("expected '{'");
return this.expr({ tag: "error" }, begin);
}
this.step();
const arms: MatchArm[] = [];
if (!this.done() && !this.test("}")) {
let needsComma = false;
while (!this.done() && !this.test("}")) {
if (this.test(",")) {
this.step();
} else if (needsComma) {
this.report("expected ','");
}
if (this.done() || this.test("}")) {
break;
}
const pat = this.parsePat();
if (!this.test("=>")) {
this.report("expected '=>'");
return this.expr({ tag: "error" }, begin);
}
this.step();
needsComma = !["{", "if", "match", "loop", "while", "for"]
.some((tt) => this.test(tt));
const expr = this.parseExpr();
arms.push({
pat,
expr,
span: Span.fromto(pat.span, expr.span),
});
}
}
if (!this.test("}")) {
this.report("expected '}'");
return this.expr({ tag: "error" }, begin);
}
end = this.span();
this.step();
return this.expr({ tag: "match", expr, arms }, Span.fromto(begin, end));
}
private parseBinary(rs: ExprRestricts): Expr { private parseBinary(rs: ExprRestricts): Expr {
return this.parseOr(rs); return this.parseOr(rs);
} }
@ -1117,23 +1173,79 @@ export class Parser {
} }
private parsePat(): Pat { private parsePat(): Pat {
const pos = this.span(); const begin = this.span();
if (this.test("ident")) { if (this.test("ident")) {
const ident = this.parseIdent(); const pathRes = this.parsePath();
return this.pat({ tag: "bind", ident, mut: false }, ident.span); if (!pathRes.ok) {
return this.pat({ tag: "error" }, begin);
}
const path = pathRes.val;
if (this.test("(")) {
const end: [Span] = [begin];
const elems = this.parseDelimitedList(
this.parsePatRes,
")",
",",
end,
);
return this.pat(
{ tag: "tuple", path, elems },
Span.fromto(begin, end[0]),
);
}
if (this.test("{")) {
const fields = this.parseDelimitedList(
this.parsePatField,
"}",
",",
);
return this.pat(
{ tag: "struct", path: pathRes.val, fields },
pathRes.val.span,
);
}
if (path.segments.length === 1) {
const ident = path.segments[0].ident;
return this.pat({ tag: "bind", ident, mut: false }, ident.span);
} else {
return this.pat({ tag: "path", path }, path.span);
}
} }
if (this.test("mut")) { if (this.test("mut")) {
this.step(); this.step();
if (!this.test("ident")) { if (!this.test("ident")) {
this.report("expected 'ident'"); this.report("expected 'ident'");
return this.pat({ tag: "error" }, pos); return this.pat({ tag: "error" }, begin);
} }
const ident = this.parseIdent(); const ident = this.parseIdent();
return this.pat({ tag: "bind", ident, mut: true }, pos); return this.pat({ tag: "bind", ident, mut: true }, begin);
} }
this.report(`expected pattern, got '${this.current().type}'`, pos); this.report(`expected pattern, got '${this.current().type}'`, begin);
this.step(); this.step();
return this.pat({ tag: "error" }, pos); return this.pat({ tag: "error" }, begin);
}
private parsePatRes(): ParseRes<Pat> {
return Res.Ok(this.parsePat());
}
private parsePatField(): ParseRes<PatField> {
if (!this.test("ident")) {
this.report("expected 'ident'");
return Res.Err(undefined);
}
const ident = this.parseIdent();
if (!this.test(":")) {
this.report("expected ':'");
return Res.Err(undefined);
}
this.step();
const pat = this.parsePat();
return Res.Ok({
ident,
pat,
span: Span.fromto(ident.span, pat.span),
});
} }
private parseTy(): Ty { private parseTy(): Ty {

View File

@ -1,11 +1,15 @@
enum S { enum S {
A, A(int),
B, B { v: int },
} }
fn main() { fn main() {
let v = S::A; let s = S::A(123);
match s {
S::A(v) => {},
S::B { v: v } => {},
}
} }

View File

@ -36,7 +36,8 @@ export type PatResolve = {
export type PatResolveKind = export type PatResolveKind =
| { tag: "fn_param"; item: ast.Item; kind: ast.FnItem; paramIdx: number } | { tag: "fn_param"; item: ast.Item; kind: ast.FnItem; paramIdx: number }
| { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt }; | { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt }
| { tag: "match"; expr: ast.Expr; kind: ast.MatchExpr };
export const ResolveError = (ident: ast.Ident): Resolve => ({ export const ResolveError = (ident: ast.Ident): Resolve => ({
ident, ident,

View File

@ -121,9 +121,12 @@ export class Resolver implements ast.Visitor {
visitBlock(block: ast.Block): ast.VisitRes { visitBlock(block: ast.Block): ast.VisitRes {
this.fnBodiesToCheck.push([]); this.fnBodiesToCheck.push([]);
const outerSyms = this.syms;
this.syms = new LocalSyms(this.syms);
ast.visitStmts(this, block.stmts); ast.visitStmts(this, block.stmts);
this.popAndVisitFnBodies(); this.popAndVisitFnBodies();
block.expr && ast.visitExpr(this, block.expr); block.expr && ast.visitExpr(this, block.expr);
this.syms = outerSyms;
return "stop"; return "stop";
} }
@ -289,6 +292,25 @@ export class Resolver implements ast.Visitor {
return "stop"; return "stop";
} }
visitMatchExpr(expr: ast.Expr, kind: ast.MatchExpr): ast.VisitRes {
ast.visitExpr(this, kind.expr);
this.patResolveStack.push({ tag: "match", expr, kind });
for (const arm of kind.arms) {
ast.visitMatchArm(this, arm);
}
this.patResolveStack.pop();
return "stop";
}
visitMatchArm(arm: ast.MatchArm): ast.VisitRes {
const outerSyms = this.syms;
this.syms = new LocalSyms(this.syms);
ast.visitPat(this, arm.pat);
ast.visitExpr(this, arm.expr);
this.syms = outerSyms;
return "stop";
}
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes { visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! }); this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! });
const res = this.syms.defVal(kind.ident, { const res = this.syms.defVal(kind.ident, {
@ -303,7 +325,30 @@ export class Resolver implements ast.Visitor {
} }
visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes { visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes {
todo(pat, kind); this.resolveTyPath(kind.path);
return "stop";
}
visitTuplePat(pat: ast.Pat, kind: ast.TuplePat): ast.VisitRes {
if (!kind.path) {
return todo();
}
this.resolveTyPath(kind.path);
for (const elem of kind.elems) {
ast.visitPat(this, elem);
}
return "stop";
}
visitStructPat(pat: ast.Pat, kind: ast.StructPat): ast.VisitRes {
if (!kind.path) {
return todo();
}
this.resolveTyPath(kind.path);
for (const field of kind.fields) {
ast.visitPat(this, field.pat);
}
return "stop";
} }
visitPathTy(ty: ast.Ty, kind: ast.PathTy): ast.VisitRes { visitPathTy(ty: ast.Ty, kind: ast.PathTy): ast.VisitRes {

View File

@ -23,7 +23,7 @@ export class HirStringifyer {
case "item": case "item":
return this.item(k.item); return this.item(k.item);
case "let": { case "let": {
return `let ${this.pat(k.pat)}${ return `let ${this.pat(k.pat, d)}${
k.expr && ` = ${this.expr(k.expr, d)}` || "" k.expr && ` = ${this.expr(k.expr, d)}` || ""
};`; };`;
} }
@ -43,7 +43,7 @@ export class HirStringifyer {
exhausted(k); exhausted(k);
} }
public item(item: ast.Item, depth = 0): string { public item(item: ast.Item, d = 0): string {
const ident = item.ident.text; const ident = item.ident.text;
const pub = item.pub ? "pub " : ""; const pub = item.pub ? "pub " : "";
const k = item.kind; const k = item.kind;
@ -51,11 +51,9 @@ export class HirStringifyer {
case "error": case "error":
return "<error>;"; return "<error>;";
case "mod_block": case "mod_block":
return `${pub}mod ${ident} ${this.block(k.block, depth)}`; return `${pub}mod ${ident} ${this.block(k.block, d)}`;
case "mod_file": case "mod_file":
return `${pub}mod ${ident} {\n${ return `${pub}mod ${ident} {\n${this.file(k.ast!, d + 1)}\n}`;
this.file(k.ast!, depth + 1)
}\n}`;
case "enum": case "enum":
return `enum ${ident}: ${ return `enum ${ident}: ${
this.ty(this.ch.enumItemTy(item, k)) this.ty(this.ch.enumItemTy(item, k))
@ -70,11 +68,11 @@ export class HirStringifyer {
throw new Error(); throw new Error();
} }
const params = k.params const params = k.params
.map((param) => this.pat(param.pat)) .map((param) => this.pat(param.pat, d))
.join(", "); .join(", ");
return `${pub}fn ${ident}(${params}) -> ${ return `${pub}fn ${ident}(${params}) -> ${
this.ty(ty.kind.returnTy) this.ty(ty.kind.returnTy)
} ${this.block(k.body!, depth)}`; } ${this.block(k.body!, d)}`;
} }
case "use": case "use":
return todo(); return todo();
@ -137,6 +135,17 @@ export class HirStringifyer {
return `if ${this.expr(k.cond, d)} ${this.expr(k.truthy, d)}${ return `if ${this.expr(k.cond, d)} ${this.expr(k.truthy, d)}${
k.falsy && ` else ${this.expr(k.falsy, d)}` || "" k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
}`; }`;
case "match":
return `match ${this.expr(k.expr, d)} ${
k.arms.length === 0
? "{}"
: `{${
k.arms.map((arm) => this.matchArm(arm, d + 1)).map(
(s) =>
`\n${s},`,
)
}\n${indent(d)}}`
}`;
case "loop": case "loop":
return `loop ${this.expr(k.body, d)}`; return `loop ${this.expr(k.body, d)}`;
case "while": case "while":
@ -153,7 +162,11 @@ export class HirStringifyer {
exhausted(k); exhausted(k);
} }
public pat(pat: ast.Pat): string { public matchArm(arm: ast.MatchArm, d: number): string {
return `${this.pat(arm.pat, d)} => ${this.expr(arm.expr, d + 1)}`;
}
public pat(pat: ast.Pat, d: number): string {
const k = pat.kind; const k = pat.kind;
switch (k.tag) { switch (k.tag) {
case "error": case "error":
@ -163,7 +176,23 @@ export class HirStringifyer {
this.ty(this.ch.patTy(pat)) this.ty(this.ch.patTy(pat))
}`; }`;
case "path": case "path":
return todo(); return this.path(k.path);
case "tuple":
return `${k.path && this.path(k.path) || ""}(${
k.elems.map((pat) => this.pat(pat, d)).join(", ")
})`;
case "struct":
return `${k.path ? `${this.path(k.path)}` : "struct "} {${
[
k.fields
.map((field) =>
`${indent(d + 1)}${field.ident.text}: ${
this.pat(field.pat, d + 1)
},`
)
.join("\n"),
].map((s) => `\n${s}\n${indent(d)}`)
}}`;
} }
exhausted(k); exhausted(k);
} }