aran
Version:
JavaScript instrumenter
245 lines (232 loc) • 6.65 kB
TypeScript
import type {
Atom,
Node,
Parameter,
Program,
RoutineBlock,
SegmentBlock,
Statement,
Effect,
Expression,
ResolvePartialAtom,
} from "../../lang/syntax.d.ts";
import type { GetDefault, Json } from "../../util/util.d.ts";
import type { ValueOf } from "../../util/util.d.ts";
export type BlockPointcut<atom extends Atom, point extends Json[]> = (
node: SegmentBlock<atom> | RoutineBlock<atom>,
parent: Node<atom>,
root: Program<atom>,
) => undefined | null | point;
export type StatementPointcut<atom extends Atom, point extends Json[]> = (
node: Statement<atom>,
parent: Node<atom>,
root: Program<atom>,
) => undefined | null | point;
export type EffectPointcut<atom extends Atom, point extends Json[]> = (
node: Effect<atom>,
parent: Node<atom>,
root: Program<atom>,
) => undefined | null | point;
export type ExpressionPointcut<atom extends Atom, point extends Json[]> = (
node: Expression<atom>,
parent: Node<atom>,
root: Program<atom>,
) => undefined | null | point;
type Frame<variable extends string, value> = {
[identifier in variable | Parameter]: value;
};
/**
* The flexible weaving API expects each advice function to be a global value.
* It is more complex to use than the standard weaving API but it let the user
* define the static information provided to the advice functions. Unlike the
* standard weaving API, each join point can be cut multiple times. NB: the join
* points `apply@around` and `construct@around` can only be cut once. The join
* points of the flexible weaving API are very similar to the join points of the
* standard API.
*/
export type AspectTyping<
atom extends Atom,
point extends Json[],
runtime extends {
State: unknown;
Value: unknown;
},
> = {
// block //
"block@setup": {
pointcut: BlockPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => runtime["State"];
};
"block@before": {
pointcut: BlockPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
"block@declaration": {
pointcut: BlockPointcut<atom, point>;
advice: (
state: runtime["State"],
frame: Frame<atom["Variable"], runtime["Value"]>,
...point: point
) => void;
};
"block@declaration-overwrite": {
pointcut: BlockPointcut<atom, point>;
advice: (
state: runtime["State"],
frame: Frame<atom["Variable"], runtime["Value"]>,
...point: point
) => Frame<atom["Variable"], runtime["Value"]>;
};
"block@after": {
pointcut: BlockPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
"block@throwing": {
pointcut: BlockPointcut<atom, point>;
advice: (
state: runtime["State"],
error: runtime["Value"],
...point: point
) => runtime["Value"];
};
"block@teardown": {
pointcut: BlockPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
// statement //
"statement@before": {
pointcut: StatementPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
"statement@after": {
pointcut: StatementPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
// effect //
"effect@before": {
pointcut: EffectPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
"effect@after": {
pointcut: EffectPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
// expression //
"expression@before": {
pointcut: ExpressionPointcut<atom, point>;
advice: (state: runtime["State"], ...point: point) => void;
};
"expression@after": {
pointcut: ExpressionPointcut<atom, point>;
advice: (
state: runtime["State"],
result: runtime["Value"],
...point: point
) => runtime["Value"];
};
// apply - construct //
"apply@around": {
pointcut: ExpressionPointcut<atom, point>;
advice: (
state: runtime["State"],
callee: runtime["Value"],
this_: runtime["Value"],
arguments_: runtime["Value"][],
...point: point
) => runtime["Value"];
};
"construct@around": {
pointcut: ExpressionPointcut<atom, point>;
advice: (
state: runtime["State"],
callee: runtime["Value"],
arguments_: runtime["Value"][],
...point: point
) => runtime["Value"];
};
};
export type AspectKind = keyof AspectTyping<never, never, never>;
export type PointcutElement<
kind extends AspectKind,
atom extends Atom,
point extends Json[],
> = AspectTyping<atom, point, { State: never; Value: never }>[kind]["pointcut"];
export type AdviceElement<
kind extends AspectKind,
atom extends { Variable: string },
point extends Json[],
runtime extends { State: unknown; Value: unknown },
> = AspectTyping<
atom & { Label: never; Specifier: never; Source: never; Tag: never },
point,
runtime
>[kind]["advice"];
export type Aspect<
param extends Partial<Atom> & {
AdviceGlobalVariable?: string;
State?: unknown;
Value?: unknown;
Point?: Json[];
} = {},
> = {
[key in param["AdviceGlobalVariable"] & string]: ValueOf<{
[key in AspectKind]: {
kind: key;
pointcut: PointcutElement<
key,
ResolvePartialAtom<param>,
GetDefault<param, "Point", Json[]>
>;
advice: AdviceElement<
key,
{ Variable: GetDefault<param, "Variable", string> },
GetDefault<param, "Point", Json[]>,
{
State: GetDefault<param, "State", null>;
Value: GetDefault<param, "Value", unknown>;
}
>;
};
}>;
};
export type Pointcut<
param extends Partial<Atom> & {
AdviceGlobalVariable?: string;
Point?: Json[];
} = {},
> = {
[key in param["AdviceGlobalVariable"] & string]: ValueOf<{
[key in AspectKind]: {
kind: key;
pointcut: PointcutElement<
key,
ResolvePartialAtom<param>,
GetDefault<param, "Point", Json[]>
>;
};
}>;
};
export type Advice<
param extends {
AdviceGlobalVariable?: string;
State?: unknown;
Value?: unknown;
Variable?: string;
Point?: Json[];
} = {},
> = {
[key in param["AdviceGlobalVariable"] & string]: ValueOf<{
[key in AspectKind]: {
kind: key;
advice: AdviceElement<
key,
{ Variable: GetDefault<param, "Variable", string> },
GetDefault<param, "Point", Json[]>,
{
State: GetDefault<param, "State", null>;
Value: GetDefault<param, "Value", unknown>;
}
>;
};
}>;
};