fix: traversal!

This commit is contained in:
cha0s 2020-06-22 04:53:32 -05:00
parent 7714c87058
commit 89dc685d62
4 changed files with 80 additions and 55 deletions

View File

@ -16,7 +16,7 @@ const App = () => {
const [typeRenderers] = useState(typeRenderMap()); const [typeRenderers] = useState(typeRenderMap());
return ( return (
<TypeRenderersContext.Provider value={typeRenderers}> <TypeRenderersContext.Provider value={typeRenderers}>
<Resource uri="/resources/cha0s/initial/kitty.entity.json" /> <Resource uri="/resources/cha0s/initial/mama-kitty.entity.json" />
</TypeRenderersContext.Provider> </TypeRenderersContext.Provider>
); );
}; };

View File

@ -5,8 +5,7 @@ import React from 'react';
import useTypeRenderers from '~/client/hooks/useTypeRenderers'; import useTypeRenderers from '~/client/hooks/useTypeRenderers';
import propTypes from './prop-types'; import propTypes from './prop-types';
import {contextStepsList} from './steps-lists'; import {stepOptions, typeFromLiteral} from './typing';
import {typeFromLiteral} from './typing';
const decorate = compose( const decorate = compose(
contempo(require('./literal.raw.scss')), contempo(require('./literal.raw.scss')),
@ -20,21 +19,15 @@ const Literal = ({
const typeRenderers = useTypeRenderers(); const typeRenderers = useTypeRenderers();
const type = typeFromLiteral(value.value); const type = typeFromLiteral(value.value);
const Component = typeRenderers[type]; const Component = typeRenderers[type];
const stepsList = contextStepsList(context, type); const options = stepOptions(context, [], 0, type);
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => { options.push('<literal>');
if (!optionSteps[0] || !optionSteps[0].key) {
return r;
}
return {...r, [optionSteps[0].key]: true};
}, {}));
tierOptions.push('<literal>');
return ( return (
<div className="literal"> <div className="literal">
<select readOnly value="<literal>"> <select readOnly value="<literal>">
{ {
tierOptions options
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1)) .sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
.map((tierOption) => <option key={tierOption}>{tierOption}</option>) .map((option) => <option key={option}>{option}</option>)
} }
</select> </select>
{Component ? <Component dispatchers={dispatchers} value={value.value} /> : null} {Component ? <Component dispatchers={dispatchers} value={value.value} /> : null}

View File

@ -5,8 +5,7 @@ import React from 'react';
import Value from '~/client/value'; import Value from '~/client/value';
import propTypes from './prop-types'; import propTypes from './prop-types';
import {contextStepsList} from './steps-lists'; import {stepOptions, typeFromSteps} from './typing';
import {typeFromSteps} from './typing';
const decorate = compose( const decorate = compose(
contempo(require('./traversal.raw.scss')), contempo(require('./traversal.raw.scss')),
@ -15,9 +14,10 @@ const decorate = compose(
const Traversal = (props) => { const Traversal = (props) => {
const { const {
context, context,
dispatchers,
value: {steps, value}, value: {steps, value},
} = props; } = props;
const stepsList = contextStepsList(context, typeFromSteps(context, steps)); const stepsType = typeFromSteps(context, steps);
return ( return (
<div className="traversal"> <div className="traversal">
{steps.map((step, i) => ( {steps.map((step, i) => (
@ -27,27 +27,14 @@ const Traversal = (props) => {
(() => { (() => {
switch (step.type) { switch (step.type) {
case 'key': { case 'key': {
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => { const options = stepOptions(context, steps, i, stepsType);
if (!optionSteps[i] || !optionSteps[i].key) {
return r;
}
for (let j = 0; j < i; ++j) {
if (steps[j].key !== optionSteps[j].key) {
return r;
}
}
return {...r, [optionSteps[i].key]: true};
}, {}));
if (0 === i) {
tierOptions.push('<literal>');
}
return ( return (
<span className="key"> <span className="key" data-step-key={step.key}>
<select readOnly value={steps[i].key}> <select readOnly value={step.key}>
{ {
tierOptions options
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1)) .sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
.map((tierOption) => <option key={tierOption}>{tierOption}</option>) .map((option) => <option key={option}>{option}</option>)
} }
</select> </select>
</span> </span>
@ -61,7 +48,11 @@ const Traversal = (props) => {
<div className="arg" key={JSON.stringify(arg)}> <div className="arg" key={JSON.stringify(arg)}>
<label> <label>
{arg.label} {arg.label}
<Value.Component context={context} value={arg} /> <Value.Component
context={context}
dispatchers={dispatchers}
value={arg}
/>
</label> </label>
</div> </div>
))} ))}
@ -79,7 +70,11 @@ const Traversal = (props) => {
value && ( value && (
<span className="assign"> <span className="assign">
<span className="op">=</span> <span className="op">=</span>
<Value.Component context={context} value={value} /> <Value.Component
context={context}
dispatchers={dispatchers}
value={value}
/>
</span> </span>
) )
} }
@ -92,6 +87,6 @@ Traversal.propTypes = {
}; };
export default { export default {
type: '-traversal', type: 'traversal',
Component: decorate(Traversal), Component: decorate(Traversal),
}; };

View File

@ -1,5 +1,9 @@
import {Context} from '@avocado/behavior'; import {Context} from '@avocado/behavior';
let digraph;
// eslint-disable-next-line no-return-assign
const cachedDigraph = () => (digraph || (digraph = Context.allTypesInvertedDigraph()));
export function isBehaviorItem(valueOrItem) { export function isBehaviorItem(valueOrItem) {
if ('object' !== typeof valueOrItem) { if ('object' !== typeof valueOrItem) {
return false; return false;
@ -42,27 +46,60 @@ export function typeFromValue(v) {
return isBehaviorItem(v) ? v.type : undefined; return isBehaviorItem(v) ? v.type : undefined;
} }
export function typeFromSteps(context, steps) { const stepTo = ([variable, type], step) => {
if (!steps || 0 === steps.length) { const {key} = step;
return 'undefined'; if (key) {
const description = Context.typeDescription(type, variable);
return [
'object' === typeof variable ? variable[key] : undefined,
description[key] ? description[key].type : type,
];
} }
const [, finalType] = steps.slice(1).reduce( return [variable, type];
([v, type], step) => { };
const {key} = step;
if (key) { export function descriptionFromSteps(context, steps) {
const description = Context.typeDescription(type, v); if (!steps || 0 === steps.length) {
return [ return {};
'object' === typeof v ? v[key] : undefined, }
description[key] ? description[key].type : type, const [variable, type] = steps.slice(1).reduce(stepTo, context.get(steps[0].key));
]; return Context.typeDescription(type, variable);
}
return [v, type];
},
context.get(steps[0].key),
);
return finalType;
} }
export function typeFits(reference, candidate) { export function typeFits(reference, candidate) {
return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate); return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate);
} }
export function stepOptions(context, steps, i, type) {
let description;
if (0 === i) {
description = Object.entries(context.all())
.reduce((r, [key, tuple]) => ({...r, [key]: {type: tuple[1]}}), {});
}
else {
description = descriptionFromSteps(context, steps.slice(0, i));
}
let options;
if ('any' !== type) {
const inverted = cachedDigraph();
const candidates = (inverted[type] || []).concat(type);
options = Object.entries(description)
.reduce((r, [key, spec]) => (
candidates.find((candidate) => typeFits(candidate, spec.type))
? r.concat(key)
: r
), []);
}
else {
options = Object.keys(description);
}
return options;
}
export function typeFromSteps(context, steps) {
if (!steps || 0 === steps.length) {
return 'undefined';
}
const [, type] = steps.slice(1).reduce(stepTo, context.get(steps[0].key));
return type;
}