From 38dc2d6e2669b9ecc40ca3b7e161ba37034a1c53 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Mon, 12 Aug 2019 16:00:53 +0200 Subject: [PATCH] mol-script: macro support --- src/mol-script/script/mol-script/macro.ts | 68 ++++++++-------- src/mol-script/script/mol-script/symbols.ts | 89 ++++++++++++--------- 2 files changed, 86 insertions(+), 71 deletions(-) diff --git a/src/mol-script/script/mol-script/macro.ts b/src/mol-script/script/mol-script/macro.ts index 436e06bc6..e42b79b6f 100644 --- a/src/mol-script/script/mol-script/macro.ts +++ b/src/mol-script/script/mol-script/macro.ts @@ -1,39 +1,39 @@ -// /** -// * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info. -// * -// * @author David Sehnal <david.sehnal@gmail.com> -// * @author Alexander Rose <alexander.rose@weirdbyte.de> -// */ +/** + * Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ -// import B from '../../language/builder' +import { MolScriptBuilder as B } from '../../language/builder'; -// export function getPositionalArgs(args: any) { -// return Object.keys(args) -// .filter(k => !isNaN(k as any)) -// .map(k => +k) -// .sort((a, b) => a - b) -// .map(k => args[k]); -// } +export function getPositionalArgs(args: any) { + return Object.keys(args) + .filter(k => !isNaN(k as any)) + .map(k => +k) + .sort((a, b) => a - b) + .map(k => args[k]); +} -// export function tryGetArg(args: any, name: string | number, defaultValue?: any) { -// return (args && args[name] !== void 0) ? args[name] : defaultValue; -// } +export function tryGetArg(args: any, name: string | number, defaultValue?: any) { + return (args && args[name] !== void 0) ? args[name] : defaultValue; +} -// export function pickArgs(args: any, ...names: string[]) { -// const ret = Object.create(null); -// let count = 0; -// for (let k of Object.keys(args)) { -// if (names.indexOf(k) >= 0) { -// ret[k] = args[k]; -// count++; -// } -// } -// return count ? ret : void 0; -// } +export function pickArgs(args: any, ...names: string[]) { + const ret = Object.create(null); + let count = 0; + for (let k of Object.keys(args)) { + if (names.indexOf(k) >= 0) { + ret[k] = args[k]; + count++; + } + } + return count ? ret : void 0; +} -// export function aggregate(property: any, fn: any, initial?: any){ -// return B.struct.atomSet.reduce({ -// initial: initial !== void 0 ? initial : property, -// value: fn([ B.struct.slot.elementSetReduce(), property ]) -// }); -// } \ No newline at end of file +export function aggregate(property: any, fn: any, initial?: any) { + return B.struct.atomSet.reduce({ + initial: initial !== void 0 ? initial : property, + value: fn([ B.struct.slot.elementSetReduce(), property ]) + }); +} \ No newline at end of file diff --git a/src/mol-script/script/mol-script/symbols.ts b/src/mol-script/script/mol-script/symbols.ts index 9e17f8844..42218fb8d 100644 --- a/src/mol-script/script/mol-script/symbols.ts +++ b/src/mol-script/script/mol-script/symbols.ts @@ -6,20 +6,23 @@ import { UniqueArray } from '../../../mol-data/generic'; import Expression from '../../language/expression'; -import { Argument, MSymbol } from '../../language/symbol'; +import { Argument, MSymbol, Arguments } from '../../language/symbol'; import { MolScriptSymbolTable as MolScript } from '../../language/symbol-table'; import Type from '../../language/type'; +import { Types as StructureQueryTypes } from '../../language/symbol-table/structure-query'; +import { MolScriptBuilder as B } from '../../language/builder'; +import { getPositionalArgs, tryGetArg } from './macro'; export type MolScriptSymbol = | { kind: 'alias', aliases: string[], symbol: MSymbol } | { kind: 'macro', aliases: string[], symbol: MSymbol, translate: (args: any) => Expression } function Alias(symbol: MSymbol<any>, ...aliases: string[]): MolScriptSymbol { return { kind: 'alias', aliases, symbol }; } -// function Macro(symbol: MSymbol<any>, translate: (args: any) => Expression, ...aliases: string[]): MolScriptSymbol { -// symbol.info.namespace = 'molscript-macro'; -// symbol.id = `molscript-macro.${symbol.info.name}`; -// return { kind: 'macro', symbol, translate, aliases: [symbol.info.name, ...aliases] }; -// } +function Macro(symbol: MSymbol<any>, translate: (args: any) => Expression, ...aliases: string[]): MolScriptSymbol { + symbol.info.namespace = 'molscript-macro'; + symbol.id = `molscript-macro.${symbol.info.name}`; + return { kind: 'macro', symbol, translate, aliases: [symbol.info.name, ...aliases] }; +} export function isMolScriptSymbol(x: any): x is MolScriptSymbol { return x.kind === 'alias' || x.kind === 'macro'; @@ -104,26 +107,26 @@ export const SymbolTable = [ Alias(MolScript.structureQuery.generator.empty, 'sel.atom.empty'), Alias(MolScript.structureQuery.generator.all, 'sel.atom.all'), - // Macro(MSymbol('sel.atom.atoms', Arguments.Dictionary({ - // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to each atom.' }) - // }), Struct.Types.ElementSelection, 'A selection of singleton atom sets.'), - // args => B.struct.generator.atomGroups({ 'atom-test': M.tryGetArg(args, 0, true) })), - - // Macro(MSymbol('sel.atom.res', Arguments.Dictionary({ - // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each residue.' }) - // }), Struct.Types.ElementSelection, 'A selection of atom sets grouped by residue.'), - // args => B.struct.generator.atomGroups({ - // 'residue-test': M.tryGetArg(args, 0, true), - // 'group-by': B.ammp('residueKey') - // })), - - // Macro(MSymbol('sel.atom.chains', Arguments.Dictionary({ - // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each chain.' }) - // }), Struct.Types.ElementSelection, 'A selection of atom sets grouped by chain.'), - // args => B.struct.generator.atomGroups({ - // 'chain-test': M.tryGetArg(args, 0, true), - // 'group-by': B.ammp('chainKey') - // })), + Macro(MSymbol('sel.atom.atoms', Arguments.Dictionary({ + 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to each atom.' }) + }), StructureQueryTypes.ElementSelection, 'A selection of singleton atom sets.'), + args => B.struct.generator.atomGroups({ 'atom-test': tryGetArg(args, 0, true) })), + + Macro(MSymbol('sel.atom.res', Arguments.Dictionary({ + 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each residue.' }) + }), StructureQueryTypes.ElementSelection, 'A selection of atom sets grouped by residue.'), + args => B.struct.generator.atomGroups({ + 'residue-test': tryGetArg(args, 0, true), + 'group-by': B.ammp('residueKey') + })), + + Macro(MSymbol('sel.atom.chains', Arguments.Dictionary({ + 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each chain.' }) + }), StructureQueryTypes.ElementSelection, 'A selection of atom sets grouped by chain.'), + args => B.struct.generator.atomGroups({ + 'chain-test': tryGetArg(args, 0, true), + 'group-by': B.ammp('chainKey') + })), ], [ 'Modifiers', @@ -244,9 +247,9 @@ export const SymbolTable = [ [ 'Link Properties', Alias(MolScript.structureQuery.linkProperty.order, 'link.order'), - // Macro(MSymbol('bond.is', Arguments.List(Struct.Types.LinkFlag), Type.Bool, - // `Test if the current bond has at least one (or all if partial = false) of the specified flags: ${Type.oneOfValues(Struct.Types.LinkFlag).join(', ')}`), - // args => B.core.flags.hasAny([B.struct.bondProperty.flags(), B.struct.type.linkFlags(M.getPositionalArgs(args))])), + Macro(MSymbol('link.is', Arguments.List(StructureQueryTypes.LinkFlag), Type.Bool, + `Test if the current link has at least one (or all if partial = false) of the specified flags: ${Type.oneOfValues(StructureQueryTypes.LinkFlag).join(', ')}`), + args => B.core.flags.hasAny([B.struct.linkProperty.flags(), B.struct.type.linkFlags(getPositionalArgs(args))])), ] ] ]; @@ -305,19 +308,25 @@ function substSymbols(expr: Expression): Expression { } if (Expression.isSymbol(expr)) { if (!SymbolMap[expr.name]) return expr; - return Expression.Symbol(SymbolMap[expr.name]!.symbol.id); + const s = SymbolMap[expr.name]!; + if (s.kind === 'alias') return Expression.Symbol(SymbolMap[expr.name]!.symbol.id); + throw s.translate([]); } - const head = substSymbols(expr.head); + const isMacro = Expression.isSymbol(expr.head) && !!SymbolMap[expr.head.name] && SymbolMap[expr.head.name]!.kind === 'macro'; + + const head = isMacro ? expr.head : substSymbols(expr.head); const headChanged = head !== expr.head; if (!expr.args) { + if (isMacro) return substSymbols(expr.head); // TODO: is this correct? return headChanged ? Expression.Apply(head) : expr; } let argsChanged = false; + let newArgs: any; if (Expression.isArgumentsArray(expr.args)) { - let newArgs: Expression[] = []; + newArgs = []; for (let i = 0, _i = expr.args.length; i < _i; i++) { const oldArg = expr.args[i]; const newArg = substSymbols(oldArg); @@ -325,21 +334,27 @@ function substSymbols(expr: Expression): Expression { newArgs[newArgs.length] = newArg; } if (!argsChanged) newArgs = expr.args; - if (!headChanged && !argsChanged) return expr; - return Expression.Apply(head, newArgs); + if (!isMacro && !headChanged && !argsChanged) return expr; } else { - let newArgs: any = {} + newArgs = {}; for (const key of Object.keys(expr.args)) { const oldArg = expr.args[key]; const newArg = substSymbols(oldArg); if (oldArg !== newArg) argsChanged = true; newArgs[key] = newArg; } - if (!headChanged && !argsChanged) return expr; + if (!isMacro && !headChanged && !argsChanged) return expr; if (!argsChanged) newArgs = expr.args; + } - return Expression.Apply(head, newArgs); + if (isMacro) { + const macro = SymbolMap[(expr.head as Expression.Symbol).name]!; + if (macro.kind !== 'macro') return Expression.Apply(head, newArgs); + const ret = macro.translate(newArgs); + return ret; } + + return Expression.Apply(head, newArgs); } export function transpileMolScript(expr: Expression) { -- GitLab