Skip to content

Commit 4ecf064

Browse files
committed
refactor: matcher and usage example
1 parent fb9b2e1 commit 4ecf064

File tree

2 files changed

+123
-45
lines changed

2 files changed

+123
-45
lines changed

src/interpreter.ts

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import {
7373
} from "./types/types";
7474
import { sha256_sync } from "@ton/crypto";
7575
import { enabledMasterchain } from "./config/features";
76+
import { match } from "./utils/tricks";
7677

7778
// TVM integers are signed 257-bit integers
7879
const minTvmInt: bigint = -(2n ** 256n);
@@ -765,36 +766,28 @@ export class Interpreter {
765766
);
766767
}
767768

768-
public interpretExpression(ast: AstExpression): Value {
769-
switch (ast.kind) {
770-
case "id":
771-
return this.interpretName(ast);
772-
case "method_call":
773-
return this.interpretMethodCall(ast);
774-
case "init_of":
775-
return this.interpretInitOf(ast);
776-
case "null":
777-
return this.interpretNull(ast);
778-
case "boolean":
779-
return this.interpretBoolean(ast);
780-
case "number":
781-
return this.interpretNumber(ast);
782-
case "string":
783-
return this.interpretString(ast);
784-
case "op_unary":
785-
return this.interpretUnaryOp(ast);
786-
case "op_binary":
787-
return this.interpretBinaryOp(ast);
788-
case "conditional":
789-
return this.interpretConditional(ast);
790-
case "struct_instance":
791-
return this.interpretStructInstance(ast);
792-
case "field_access":
793-
return this.interpretFieldAccess(ast);
794-
case "static_call":
795-
return this.interpretStaticCall(ast);
796-
}
797-
}
769+
public interpretExpression = (ast: AstExpression): Value =>
770+
match(ast)
771+
.on({ kind: "id" })((ast) => this.interpretName(ast))
772+
.on({ kind: "method_call" })((ast) => this.interpretMethodCall(ast))
773+
.on({ kind: "init_of" })((ast) => this.interpretInitOf(ast))
774+
.on({ kind: "null" })((ast) => this.interpretNull(ast))
775+
.on({ kind: "boolean" })((ast) => this.interpretBoolean(ast))
776+
.on({ kind: "number" })((ast) => this.interpretNumber(ast))
777+
.on({ kind: "string" })((ast) => this.interpretString(ast))
778+
.on({ kind: "op_unary" })((ast) => this.interpretUnaryOp(ast))
779+
.on({ kind: "op_binary" })((ast) => this.interpretBinaryOp(ast))
780+
.on({ kind: "conditional" })((ast) =>
781+
this.interpretConditional(ast),
782+
)
783+
.on({ kind: "struct_instance" })((ast) =>
784+
this.interpretStructInstance(ast),
785+
)
786+
.on({ kind: "field_access" })((ast) =>
787+
this.interpretFieldAccess(ast),
788+
)
789+
.on({ kind: "static_call" })((ast) => this.interpretStaticCall(ast))
790+
.end();
798791

799792
public interpretName(ast: AstId): Value {
800793
if (hasStaticConstant(this.context, idText(ast))) {

src/utils/tricks.ts

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
12
import { throwInternalCompilerError } from "../errors";
23

34
/**
@@ -44,19 +45,103 @@ type Handlers<I, O> = Unwrap<Intersect<Inputs<I>>> & Outputs<O>;
4445
*/
4546
export const makeVisitor =
4647
<I>() =>
47-
<O>(handlers: Handlers<I, O>) => {
48-
return (input: Extract<I, { kind: string }>): O[keyof O] => {
49-
const handler = (
50-
handlers as Record<string, (input: I) => O[keyof O]>
51-
)[input.kind];
52-
53-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
54-
if (handler) {
55-
return handler(input);
56-
} else {
57-
throwInternalCompilerError(
58-
`Reached impossible case: ${input.kind}`,
59-
);
60-
}
61-
};
48+
<O>(handlers: Handlers<I, O>) =>
49+
(input: Extract<I, { kind: string }>): O[keyof O] => {
50+
const handler = (handlers as Record<string, (input: I) => O[keyof O]>)[
51+
input.kind
52+
];
53+
54+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
55+
if (handler) {
56+
return handler(input);
57+
} else {
58+
throwInternalCompilerError(
59+
`Reached impossible case: ${input.kind}`,
60+
);
61+
}
6262
};
63+
64+
type Extend<T extends any[], H> = H extends infer A ? [...T, A] : never;
65+
type Flat<TS extends any[], R extends any[] = []> = TS extends [
66+
infer H,
67+
...infer T,
68+
]
69+
? Flat<T, Extend<R, H>>
70+
: R;
71+
72+
declare const NoSuchCase: unique symbol;
73+
interface NoSuchCaseBug<L> extends Array<never> {
74+
[NoSuchCase]: L;
75+
}
76+
type On<V, I extends any[], O> = {
77+
on: <const DI extends any[]>(
78+
...key: I extends Flat<DI> ? DI : NoSuchCaseBug<DI>
79+
) => <const DO>(
80+
handler: (...args: Extract<I, Flat<DI>>) => DO,
81+
) => MV<V, Exclude<I, Flat<DI>>, O | DO>;
82+
};
83+
84+
declare const CasesAreNotExhaustive: unique symbol;
85+
interface NonExhaustiveBug<L> {
86+
[CasesAreNotExhaustive]: L;
87+
}
88+
type End<I, O> = [I] extends [never]
89+
? EndInternal<O>
90+
: {
91+
end: NonExhaustiveBug<I>;
92+
};
93+
type MV<V, I extends any[], O> = End<I, O> & On<V, I, O>;
94+
95+
type OnInternal<V, I extends any[], O> = {
96+
on: <const DI extends any[]>(
97+
...key: DI
98+
) => <const DO>(
99+
handler: (...args: Extract<I, Flat<DI>>) => DO,
100+
) => MVInternal<V, Exclude<I, Flat<DI>>, O | DO>;
101+
};
102+
type EndInternal<O> = {
103+
end: () => O;
104+
};
105+
type MVInternal<V, I extends any[], O> = EndInternal<O> & OnInternal<V, I, O>;
106+
107+
const deepMatch = (a: unknown, b: unknown): boolean => {
108+
if (
109+
a === b &&
110+
["number", "string", "boolean", "bigint"].includes(typeof a) &&
111+
typeof a === typeof b
112+
) {
113+
return true;
114+
}
115+
if (a === null || b === null) {
116+
return a === b;
117+
}
118+
if (typeof a === "object" && typeof b === "object") {
119+
if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
120+
return a.every((a, i) => deepMatch(a, b[i]));
121+
} else {
122+
return Object.entries(b).every(([k, b]) =>
123+
deepMatch(k in a ? (a as any)[k] : undefined, b),
124+
);
125+
}
126+
}
127+
return false;
128+
};
129+
130+
export const match = <I extends any[]>(...args: I): MV<I, Flat<I>, never> => {
131+
const rec = <V, I extends any[], O>(end: () => O): MVInternal<V, I, O> => ({
132+
end,
133+
on:
134+
<const DI extends any[]>(...match: DI) =>
135+
<const DO>(handler: (...args: Extract<I, Flat<DI>>) => DO) =>
136+
rec<V, Exclude<I, Flat<DI>>, O | DO>(() =>
137+
deepMatch(args, match)
138+
? handler(
139+
...(args as unknown as Extract<I, Flat<DI, []>>),
140+
)
141+
: end(),
142+
),
143+
});
144+
return rec<I, Flat<I>, never>(() => {
145+
throw new Error("Not exhaustive");
146+
}) as MV<I, Flat<I>, never>;
147+
};

0 commit comments

Comments
 (0)