mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02:00
compiler: match expr first half
This commit is contained in:
parent
5c9e3ce73f
commit
8f3523d4e1
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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":
|
||||||
|
@ -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 = [
|
||||||
"=",
|
"=",
|
||||||
"==",
|
"==",
|
||||||
|
"=>",
|
||||||
"<",
|
"<",
|
||||||
"<=",
|
"<=",
|
||||||
">",
|
">",
|
||||||
|
@ -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 {
|
||||||
|
@ -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 } => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user