diff --git a/frontend/src/domain/visual-proof-editor/components/visual-proof-editor-parameter-identifier-selector.tsx b/frontend/src/domain/visual-proof-editor/components/visual-proof-editor-parameter-identifier-selector.tsx new file mode 100644 index 0000000..cabf40e --- /dev/null +++ b/frontend/src/domain/visual-proof-editor/components/visual-proof-editor-parameter-identifier-selector.tsx @@ -0,0 +1,207 @@ +import { get_free_parameters, Identifier, instantiate_free_parameter_by_index, Prop } from 'alice'; +import { Select, Space } from 'antd'; +import React, { Fragment, ReactElement, useCallback, useEffect, useRef, useState } from 'react'; + +interface VisualProofEditorParameterIdentifierSelectorProps { + prop: Prop; + options: { [name: string]: Identifier[] }, + onAllSelect: (prop: Prop) => void; + getPopupContainer?: () => HTMLElement; +} + +interface RenderPropResult { + node: ReactElement; + newIndex: number; +} + +type RenderProp = (prop: Prop, index: number, boundIdentifiers: string[]) => RenderPropResult; + +export function VisualProofEditorParameterIdentifierSelector(props: VisualProofEditorParameterIdentifierSelectorProps) { + const { + prop, + options, + onAllSelect, + getPopupContainer, + } = props; + + const containerRef = useRef(null); + const [paramMapping, setParamMapping] = useState<{ [idx: number]: number } | null>(null); + + useEffect(() => { + const selectableParamSize = get_free_parameters(prop) + .filter((param) => param.kind === 'Uninstantiated') + .length; + + setParamMapping(Object.assign({}, Array(selectableParamSize).fill(0))); + }, [prop]); + + useEffect(() => { + + if (!paramMapping) { + return; + } + + const params = get_free_parameters(prop); + let instantiatedProp = { ...prop }; + for (const [paramIndex, identifierIndex] of Object.entries(paramMapping)) { + const param = params[parseInt(paramIndex)]; + const paramName = param.kind === 'Instantiated' ? param.value.name : param.value; + + instantiatedProp = instantiate_free_parameter_by_index( + instantiatedProp, + parseInt(paramIndex), + options[paramName][identifierIndex], + ); + } + + onAllSelect(instantiatedProp); + }, [paramMapping]); + + const renderProp: RenderProp = (prop: Prop, index: number, boundIdentifiers: string[]) => { + + const handlePrimitive = useCallback((symbol: string, index: number) => { + return { + node: ({symbol}), + newIndex: index, + }; + }, []); + + const handleBinaryConnective = useCallback((symbol: string, prop: Prop & { kind: 'And' | 'Or' | 'Impl' }, index: number, boundIdentifiers: string[]) => { + const fst = renderProp(prop, index, [...boundIdentifiers]); + const snd = renderProp(prop, fst.newIndex, boundIdentifiers); + + const node = ( + <> + {fst.node} + + {symbol} + + {snd.node} + + ); + + return { + node, + newIndex: snd.newIndex, + }; + }, []); + + const handleQuantifier = useCallback((symbol: string, prop: Prop & { kind: 'ForAll' | 'Exists' }, index: number, boundIdentifiers: string[]) => { + const body = renderProp( + prop.value.body, + index, + [...boundIdentifiers, prop.value.object_ident], + ); + + const node = ( + <> + {symbol} + {prop.value.object_ident} + : + {prop.value.object_type_ident} + . + {body.node} + + ); + + return { + node, + newIndex: body.newIndex, + }; + }, []); + + const handleAtom = useCallback((prop: Prop & { kind: 'Atom' }, index: number, boundIdentifiers: string[]) => { + const params = prop.value[1]; + + const paramNodes = []; + for (const param of params) { + const paramIndex = index; + const paramName = param.kind === 'Instantiated' ? param.value.name : param.value; + + if (param.kind === 'Instantiated' || boundIdentifiers.includes(paramName)) { + paramNodes.push({paramName}); + + index++; + continue; + } + + const identOptions = options[paramName]; + + if (!identOptions || identOptions.length === 0) { + throw new Error(`No options for name ${paramName}.`); + } + + const selectionOptions = identOptions + .map((option, i) => ({ + label: `${option.name} - ${option.unique_id}`, + value: i, + })); + + const handleSelect = (idx: number) => { + setParamMapping({ + ...paramMapping, + [paramIndex]: idx, + }); + }; + + const node = ( + +