diff --git a/src/apps/state-docs/pd-to-md.ts b/src/apps/state-docs/pd-to-md.ts index e1a6ceac44e82037a389d8d331c2676ae347ab15..dc67d414049378fa8acb28c1148df3864ad12774 100644 --- a/src/apps/state-docs/pd-to-md.ts +++ b/src/apps/state-docs/pd-to-md.ts @@ -30,7 +30,7 @@ function paramInfo(param: PD.Any, offset: number): string { case 'line-graph': return `A list of 2d vectors [xi, yi][]`; case 'object-list': return `Array of\n${paramInfo(PD.Group(param.element), offset + 2)}`; // TODO: support more languages - case 'script-expression': return `An expression in the specified language { language: 'mol-script', expressiong: string }`; + case 'script': return `An expression in the specified language { language: 'mol-script', expressiong: string }`; default: const _: never = param; console.warn(`${_} has no associated UI component`); diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index df9e6ac52703bb34b5b6049a5cfe51e513d8abf3..3acf34f68296cbd645377d09a64f488d095b4e4c 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -289,7 +289,7 @@ export namespace Loci { : element } - export function toScriptExpression(loci: Loci) { + export function toExpression(loci: Loci) { if (Loci.isEmpty(loci)) return MS.struct.generator.empty(); const models = loci.structure.models; diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts index 698fd310610ef1e148927ead004a7e46534ae26b..eb315686ac60abbd1a9e665851524618b146ad64 100644 --- a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts +++ b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts @@ -146,7 +146,7 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W lastLoci = current; const core = MS.struct.modifier.wholeResidues([ - StructureElement.Loci.toScriptExpression(current.loci) + StructureElement.Loci.toExpression(current.loci) ]); const surroundings = MS.struct.modifier.includeSurroundings({ diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index 0735d4f7d69c0a38602f6b85b8b34029709cc23a..7e2535c392c7b52d24e94a6ee37eaeca7b6954ad 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -355,7 +355,7 @@ export const StructureFromSelection = StateAction.build({ const sel = plugin.helpers.structureSelectionManager.get(a.data); if (sel.kind === 'empty-loci') return Task.constant('', void 0); - const expression = StructureElement.Loci.toScriptExpression(sel); + const expression = StructureElement.Loci.toExpression(sel); const root = state.build().to(ref).apply(StructureSelectionFromExpression, { expression, label: params.label }); return state.updateTree(root); }); \ No newline at end of file diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 75a9802fe827b5b1b915d247fd4e81c74e1d0352..6df5fc2fe65abb369cf4b2277eb9cb3fe1c100ba 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -22,11 +22,10 @@ import { stringToWords } from '../../../mol-util/string'; import { PluginStateObject as SO, PluginStateTransform } from '../objects'; import { trajectoryFromGRO } from '../../../mol-model-formats/structure/gro'; import { parseGRO } from '../../../mol-io/reader/gro/parser'; -import { parseMolScript } from '../../../mol-script/language/parser'; -import { transpileMolScript } from '../../../mol-script/script/mol-script/symbols'; import { shapeFromPly } from '../../../mol-model-formats/shape/ply'; import { SymmetryOperator } from '../../../mol-math/geometry'; import { ensureSecondaryStructure } from './helpers'; +import { Script } from '../../../mol-script/script'; export { TrajectoryFromBlob }; export { TrajectoryFromMmCif }; @@ -390,25 +389,22 @@ const StructureSelectionFromScript = PluginStateTransform.BuiltIn({ from: SO.Molecule.Structure, to: SO.Molecule.Structure, params: { - script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' }), + script: PD.Script({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' }), label: PD.Optional(PD.Text('')) } })({ apply({ a, params, cache }) { - const parsed = parseMolScript(params.script.expression); - if (parsed.length === 0) throw new Error('No query'); - const query = transpileMolScript(parsed[0]); - const compiled = compile<Sel>(query); - (cache as { compiled: QueryFn<Sel> }).compiled = compiled; + const query = Script.toQuery(params.script); + (cache as { query: QueryFn<Sel> }).query = query; (cache as { source: Structure }).source = a.data; - const result = compiled(new QueryContext(a.data)); + const result = query(new QueryContext(a.data)); const s = Sel.unionStructure(result); const props = { label: `${params.label || 'Selection'}`, description: structureDesc(s) }; return new SO.Molecule.Structure(s, props); }, update: ({ a, b, oldParams, newParams, cache }) => { - if (oldParams.script.language !== newParams.script.language || oldParams.script.expression !== newParams.script.expression) { + if (Script.areEqual(oldParams.script, newParams.script)) { return StateTransformer.UpdateResult.Recreate; } @@ -417,7 +413,7 @@ const StructureSelectionFromScript = PluginStateTransform.BuiltIn({ } (cache as { source: Structure }).source = a.data; - updateStructureFromQuery((cache as { compiled: QueryFn<Sel> }).compiled, a.data, b, newParams.label); + updateStructureFromQuery((cache as { query: QueryFn<Sel> }).query, a.data, b, newParams.label); return StateTransformer.UpdateResult.Updated; } }); diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 46451b599b9ebc56a083a4e384c33263ca068aec..fb373164cee802fa78321cbea798dd1ea168b029 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -64,7 +64,7 @@ function controlFor(param: PD.Any): ParamControl | undefined { case 'group': return GroupControl; case 'mapped': return MappedControl; case 'line-graph': return LineGraphControl; - case 'script-expression': return ScriptExpressionControl; + case 'script': return ScriptControl; case 'object-list': return ObjectListControl; default: const _: never = param; @@ -746,7 +746,7 @@ export class ConvertedControl extends React.PureComponent<ParamProps<PD.Converte } } -export class ScriptExpressionControl extends SimpleParam<PD.ScriptExpression> { +export class ScriptControl extends SimpleParam<PD.Script> { onChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; if (value !== this.props.value.expression) { diff --git a/src/mol-script/script.ts b/src/mol-script/script.ts new file mode 100644 index 0000000000000000000000000000000000000000..c34672bd16bd32c55dd4abec1648d740d5edc140 --- /dev/null +++ b/src/mol-script/script.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { transpileMolScript } from './script/mol-script/symbols'; +import { parseMolScript } from './language/parser'; +import Expression from './language/expression'; +import { StructureElement, QueryContext, StructureSelection, Structure, QueryFn } from '../mol-model/structure'; +import { compile } from './runtime/query/compiler'; + +export { Script } + +interface Script { expression: string, language: Script.Language } + +function Script(expression: string, language: Script.Language): Script { + return { expression, language } +} + +namespace Script { + export type Language = 'mol-script' + + export function areEqual(a: Script, b: Script) { + return a.language === b.language && a.expression === b.expression + } + + export function toExpression(script: Script): Expression { + switch (script.language) { + case 'mol-script': + const parsed = parseMolScript(script.expression) + if (parsed.length === 0) throw new Error('No query') + return transpileMolScript(parsed[0]) + } + throw new Error('unsupported script language') + } + + export function toQuery(script: Script): QueryFn<StructureSelection> { + const expression = toExpression(script) + return compile<StructureSelection>(expression); + } + + export function toLoci(script: Script, structure: Structure): StructureElement.Loci { + const query = toQuery(script) + const result = query(new QueryContext(structure)) + return StructureSelection.toLoci2(result) + } +} \ No newline at end of file diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 75c92e462b29a056d0329f584194b6c12be2b654..51792e49b8e3dac2721e22fdbdcaec6ffe1f9ac0 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -9,6 +9,7 @@ import { Color as ColorData } from './color'; import { shallowEqual } from './index'; import { Vec2 as Vec2Data, Vec3 as Vec3Data } from '../mol-math/linear-algebra'; import { deepClone } from './object'; +import { Script as ScriptData } from '../mol-script/script'; export namespace ParamDefinition { export interface Info { @@ -233,16 +234,16 @@ export namespace ParamDefinition { return { type: 'conditioned', select: Select<string>(conditionForValue(defaultValue) as string, options), defaultValue, conditionParams, conditionForValue, conditionedValue }; } - export interface ScriptExpression extends Base<{ language: 'mol-script', expression: string }> { - type: 'script-expression' + export interface Script extends Base<ScriptData> { + type: 'script' } - export function ScriptExpression(defaultValue: ScriptExpression['defaultValue'], info?: Info): ScriptExpression { - return setInfo<ScriptExpression>({ type: 'script-expression', defaultValue }, info) + export function Script(defaultValue: Script['defaultValue'], info?: Info): Script { + return setInfo<Script>({ type: 'script', defaultValue }, info) } export type Any = | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph - | ColorList<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | ScriptExpression | ObjectList + | ColorList<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | Script | ObjectList export type Params = { [k: string]: Any } export type Values<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] } @@ -328,8 +329,8 @@ export namespace ParamDefinition { return true; } else if (p.type === 'vec3') { return Vec3Data.equals(a, b); - } else if (p.type === 'script-expression') { - const u = a as ScriptExpression['defaultValue'], v = b as ScriptExpression['defaultValue']; + } else if (p.type === 'script') { + const u = a as Script['defaultValue'], v = b as Script['defaultValue']; return u.language === v.language && u.expression === v.expression; } else if (p.type === 'object-list') { const u = a as ObjectList['defaultValue'], v = b as ObjectList['defaultValue'];