Skip to content

Commit

Permalink
feat: use the interpreter to evaluate top-level definitions
Browse files Browse the repository at this point in the history
Signed-off-by: Drew Hess <[email protected]>
  • Loading branch information
dhess committed Apr 20, 2024
1 parent 71e2e25 commit d9b92b8
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 37 deletions.
2 changes: 1 addition & 1 deletion orval.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { defineConfig } from "orval";
// This lists the operation IDs for such requests.
// We configure Orval to generate simple `useQuery`-based code, as it would for a GET request,
// rather than its default `useMutation` approach for POSTs.
const getStylePostRequests = ["getAvailableActions", "eval-full"];
const getStylePostRequests = ["getAvailableActions", "eval-full", "eval-bounded-interp"];
const useQueryPost: {
[key: string]: OperationOptions;
} = Object.assign(
Expand Down
8 changes: 4 additions & 4 deletions src/components/Edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
useGetActionOptions,
useCreateDefinition,
useCreateTypeDef,
useEvalFull,
useEvalBoundedInterp,
Level,
useUndo,
useRedo,
Expand Down Expand Up @@ -250,10 +250,10 @@ const AppNoError = ({

const [evalTarget, setEvalTarget] = useState<string | undefined>();
const evalResult = useInvalidateOnChange(
useEvalFull(
useEvalBoundedInterp(
p.sessionId,
{ qualifiedModule: p.module.modname, baseName: evalTarget || "" },
{ stepLimit: 300 },
{ timeoutMicroseconds: 100000 },
{ query: { enabled: !!evalTarget } }
),
[p.module]
Expand Down Expand Up @@ -344,7 +344,7 @@ const AppNoError = ({
// Note: these offsets are rather arbitrary.
initialPosition={{ x: 10, y: 10 }}
moduleName={p.module.modname}
evalFull={{
evalBoundedInterp={{
request: setEvalTarget,
...(evalResult.isSuccess ? { result: evalResult.data } : {}),
}}
Expand Down
33 changes: 33 additions & 0 deletions src/components/EvalBoundedInterp/EvalBoundedInterp.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Meta, StoryObj } from "@storybook/react";
import { tree3 } from "../examples/trees";

import { EvalBoundedInterp } from "./";

const meta: Meta<typeof EvalBoundedInterp> = {
title: "Application/Component Library/EvalBoundedInterp",
component: EvalBoundedInterp,
decorators: [
(Story) => (
<div className="h-screen">
<Story />
</div>
),
],
};

export default meta;
type Story = StoryObj<typeof EvalBoundedInterp>;

export const Default: Story = {
args: {
moduleName: ["Primer", "Examples"],
evalBoundedInterp: {
request: () => {
return;
},
result: { tag: "EvalBoundedInterpRespNormal", contents: tree3 },
},
level: "Expert",
defs: ["footballGame", "whatsopposite"],
},
};
105 changes: 105 additions & 0 deletions src/components/EvalBoundedInterp/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useState } from "react";
import { NodeChange, ReactFlowProvider, useReactFlow } from "reactflow";
import { EvalBoundedInterpResp, GlobalName, Level } from "@/primer-api";
import { SelectMenu, TreeReactFlowOne } from "@/components";
import {
TreeReactFlowOneProps,
defaultTreeReactFlowProps,
} from "../TreeReactFlow";

export type EvalBoundedInterpProps = {
moduleName: string[];
evalBoundedInterp: {
request: (baseName: string | undefined) => void;
result?: EvalBoundedInterpResp;
};
level: Level;
defs: string[];
initialEvalDef: string | undefined;
extraTreeProps: Partial<TreeReactFlowOneProps>;
};

const Evaluated = (p: {
defName: GlobalName;
evaluated?: EvalBoundedInterpResp;
level: Level;
extraTreeProps: Partial<TreeReactFlowOneProps>;
}) => {
const padding = 1.0;
const { fitView } = useReactFlow();
const onNodesChange = (_: NodeChange[]) => {
fitView({ padding });
};
const resultTree = () => {
switch (p?.evaluated?.tag) {
case "EvalBoundedInterpRespNormal":
return { tree: p.evaluated.contents };
default:
// This should be some indication of an error having occurred,
// but our UI is a bit too limited for that at the moment.
return {};
}
};

return (
<TreeReactFlowOne
{...defaultTreeReactFlowProps}
{...resultTree()}
level={p.level}
zoomBarProps={{ padding }}
onNodesChange={onNodesChange}
fitViewOptions={{ padding }}
{...p.extraTreeProps}
/>
);
};

const disableEval = "<disabled>";

// We only offer to evaluate the definitions in the "main" module
export const EvalBoundedInterp = ({
defs,
evalBoundedInterp,
moduleName,
level,
initialEvalDef,
extraTreeProps,
}: EvalBoundedInterpProps): JSX.Element => {
const [evalDef, setEvalDef0] = useState(initialEvalDef ?? disableEval);
const setEvalDef = (e: string) => {
setEvalDef0(e);
evalBoundedInterp.request(e === disableEval ? undefined : e);
};
return (
<div className="flex h-full flex-col overflow-auto">
<div className="mx-2">
<SelectMenu
label="Definition"
selected={evalDef}
options={[disableEval].concat(defs)}
optionType="code"
onChange={(selection: string) => setEvalDef(selection)}
/>
</div>
{evalDef !== disableEval && (
<>
<div className="grow">
<ReactFlowProvider>
<Evaluated
key={evalDef}
defName={{ qualifiedModule: moduleName, baseName: evalDef }}
{...(evalBoundedInterp.result
? { evaluated: evalBoundedInterp.result }
: {})}
level={level}
extraTreeProps={extraTreeProps}
/>
</ReactFlowProvider>
</div>
</>
)}
</div>
);
};

export default EvalBoundedInterp;
8 changes: 4 additions & 4 deletions src/components/PictureInPicture/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import classNames from "classnames";
import { useRef, useState } from "react";
import { useDraggable, DragOptions } from "@neodrag/react";
import { Tab } from "@headlessui/react";
import type { EvalFullProps } from "@/components/EvalFull";
import type { EvalBoundedInterpProps } from "@/components/EvalBoundedInterp";
import type { SelectionInfoProps } from "@/components/SelectionInfo";
import { EvalFull, SelectionInfo } from "@/components";
import { EvalBoundedInterp, SelectionInfo } from "@/components";

export type PictureInPictureTab = "Eval" | "Info";

export type PictureInPictureProps = {
initialTab: PictureInPictureTab;
initialPosition: { x: number; y: number };
} & EvalFullProps &
} & EvalBoundedInterpProps &
SelectionInfoProps;

const tabClasses = (selected: boolean, extra?: string) =>
Expand Down Expand Up @@ -96,7 +96,7 @@ export const PictureInPicture = (p: PictureInPictureProps): JSX.Element => {
<Tab.Panels>
<Tab.Panel>
<div className="h-60">
<EvalFull {...p} />
<EvalBoundedInterp {...p} />
</div>
</Tab.Panel>
<Tab.Panel>
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { default as CreateDefModal } from "./CreateDefModal";
export { default as DefsToolbar } from "./DefsToolbar";
export { default as Edit } from "./Edit";
export { default as Error } from "./Error";
export { default as EvalBoundedInterp } from "./EvalBoundedInterp";
export { default as EvalFull } from "./EvalFull";
export { default as GroupedButtonList } from "./GroupedButtonList";
export { default as NoMatch } from "./NoMatch";
Expand Down
67 changes: 39 additions & 28 deletions src/primer-api/primer-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ export const useEvalFull = <TData = Awaited<ReturnType<ReturnType<typeof useEval
/**
* @summary Using the interpreter, evaluate the named definition to normal form (or time out)
*/
export const useEvalBoundedInterpHook = () => {
const useEvalBoundedInterpHook = () => {
const evalBoundedInterp = useCustomInstance<EvalBoundedInterpResp>();

return (
Expand All @@ -714,47 +714,58 @@ export const useEvalBoundedInterpHook = () => {
}


export const getEvalBoundedInterpQueryKey = (sessionId: string,
globalName: GlobalName,
params?: EvalBoundedInterpParams,) => {
return [`/openapi/sessions/${sessionId}/eval-bounded-interp`, ...(params ? [params]: []), globalName] as const;
}

export const useEvalBoundedInterpMutationOptions = <TError = ErrorType<void>,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext>, }
): UseMutationOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext> => {
const {mutation: mutationOptions} = options ?? {};

export const useEvalBoundedInterpQueryOptions = <TData = Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError = ErrorType<void>>(sessionId: string,
globalName: GlobalName,
params?: EvalBoundedInterpParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError, TData>>, }
) => {

const evalBoundedInterp = useEvalBoundedInterpHook()
const {query: queryOptions} = options ?? {};

const queryKey = queryOptions?.queryKey ?? getEvalBoundedInterpQueryKey(sessionId,globalName,params);

const mutationFn: MutationFunction<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, {sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}> = (props) => {
const {sessionId,data,params} = props ?? {};
const evalBoundedInterp = useEvalBoundedInterpHook();

return evalBoundedInterp(sessionId,data,params,)
}
const queryFn: QueryFunction<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>> = () => evalBoundedInterp(sessionId,globalName,params, );





return { mutationFn, ...mutationOptions }}
return { queryKey, queryFn, enabled: !!(sessionId), ...queryOptions} as UseQueryOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError, TData> & { queryKey: QueryKey }
}

export type EvalBoundedInterpMutationResult = NonNullable<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>>
export type EvalBoundedInterpMutationBody = GlobalName
export type EvalBoundedInterpMutationError = ErrorType<void>
export type EvalBoundedInterpQueryResult = NonNullable<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>>
export type EvalBoundedInterpQueryError = ErrorType<void>

/**
/**
* @summary Using the interpreter, evaluate the named definition to normal form (or time out)
*/
export const useEvalBoundedInterp = <TError = ErrorType<void>,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext>, }
): UseMutationResult<
Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>,
TError,
{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams},
TContext
> => {
export const useEvalBoundedInterp = <TData = Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError = ErrorType<void>>(
sessionId: string,
globalName: GlobalName,
params?: EvalBoundedInterpParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<ReturnType<typeof useEvalBoundedInterpHook>>>, TError, TData>>, }

): UseQueryResult<TData, TError> & { queryKey: QueryKey } => {

const queryOptions = useEvalBoundedInterpQueryOptions(sessionId,globalName,params,options)

const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };

query.queryKey = queryOptions.queryKey ;

return query;
}



const mutationOptions = useEvalBoundedInterpMutationOptions(options);

return useMutation(mutationOptions);
}

/**
* @summary Get the specified session's name
*/
Expand Down

0 comments on commit d9b92b8

Please sign in to comment.