diff --git a/src/mol-script/transpilers/helper.ts b/src/mol-script/transpilers/helper.ts index db368cb90b52a56979786e7d62a39fbd38c28976..f8b1585c71382e7ced8d0ab2179fa3bec7cbb03a 100644 --- a/src/mol-script/transpilers/helper.ts +++ b/src/mol-script/transpilers/helper.ts @@ -29,6 +29,16 @@ export function prefix(opParser: P.MonadicParser<any>, nextParser: P.MonadicPars return parser; } +export function prefixRemoveKet(opParser: P.MonadicParser<any>, nextParser: P.MonadicParser<any>, mapFn: any) { + const parser: P.MonadicParser<any> = P.MonadicParser.lazy(() => { + return P.MonadicParser.seq(opParser, parser.skip(P.MonadicParser.string(")"))) + .map(x => mapFn(...x)) + .or(nextParser); + }); + return parser; +} + + // Ideally this function would be just like `PREFIX` but reordered like // `P.seq(parser, opParser).or(nextParser)`, but that doesn't work. The // reason for that is that Parsimmon will get stuck in infinite recursion, since @@ -129,6 +139,11 @@ export function prefixOp(re: RegExp, group: number = 0) { return P.MonadicParser.regexp(re, group).skip(P.MonadicParser.whitespace); } +export function prefixOpNoWhiteSpace(re: RegExp, group: number = 0) { + // return P.MonadicParser.regexp(re, group).skip(P.MonadicParser.regexp(/\s*/)); + return P.MonadicParser.regexp(re, group).skip(P.MonadicParser.regexp(/\s*/)); +} + export function postfixOp(re: RegExp, group: number = 0) { return P.MonadicParser.whitespace.then(P.MonadicParser.regexp(re, group)); } diff --git a/src/mol-script/transpilers/rasmol/special_properties.ts b/src/mol-script/transpilers/rasmol/macroproperties.ts similarity index 98% rename from src/mol-script/transpilers/rasmol/special_properties.ts rename to src/mol-script/transpilers/rasmol/macroproperties.ts index 5b3f975033c93ab0cb22371a8959db03baa5ee9c..5f70bce0e1157e5e77cea0d9cdfe66c4cbaeab4f 100644 --- a/src/mol-script/transpilers/rasmol/special_properties.ts +++ b/src/mol-script/transpilers/rasmol/macroproperties.ts @@ -1,4 +1,4 @@ -\[/** +/** * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -60,7 +60,7 @@ function elementListMap(x: string) { // }; // } -export const special_properties: PropertyDict = { +export const macroproperties: PropertyDict = { symbol: { '@desc': 'chemical-symbol-list: list of 1- or 2-letter chemical symbols from the periodic table', '@examples': ['symbol O+N'], diff --git a/src/mol-script/transpilers/rasmol/operators.ts b/src/mol-script/transpilers/rasmol/operators.ts index 582678e641b96e57f9731325be8fa66ac1a49296..b6cc6486c35de9ddfb374c021f82915cf865ae09 100644 --- a/src/mol-script/transpilers/rasmol/operators.ts +++ b/src/mol-script/transpilers/rasmol/operators.ts @@ -4,7 +4,7 @@ * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com> * * @author Koya Sakuma - * This module is based on jmol transpiler from MolQL and modified in similar manner as pymol and vmd tranpilers. \ + * This module is based on jmol transpiler from MolQL and modified in similar manner as pymol and vmd tranpilers */ @@ -14,6 +14,10 @@ import { MolScriptBuilder } from '../../../mol-script/language/builder'; const B = MolScriptBuilder; import { OperatorList } from '../types'; import { Expression } from '../../language/expression'; +import { macroproperties } from './macroproperties' + +const propNames = Object.keys(macroproperties).sort(h.strLenSortFn) + .filter(name => !macroproperties[name].isUnsupported).join('|'); export const operators: OperatorList = [ @@ -30,7 +34,8 @@ export const operators: OperatorList = [ '@examples': ['ASP and .CA'], name: 'and', type: h.binaryLeft, - rule: h.infixOp(/AND|&/i), + rule: P.MonadicParser.alt(h.infixOp(/AND|&/i), P.MonadicParser.whitespace), + //rule: h.infixOp(/AND|&/i), map: (op, selection, by) => B.struct.modifier.intersectBy({ 0: selection, by }) }, { @@ -41,15 +46,70 @@ export const operators: OperatorList = [ rule: h.infixOp(/OR|\||\|\|/i), map: (op, s1, s2) => B.struct.combinator.merge([s1, s2]) }, + /* { '@desc': 'Selects atoms within a specified distance of a selection', - '@examples': ['within 5 of name FE'], + '@examples': ['within (5.0, LYS:A.CA)'], + name: 'aaa', + type: h.prefixRemoveKet, + rule: h.prefixOpNoWhiteSpace(/within\s*\(\s*([-+]?[0-9]*\.?[0-9]+)\s*,/, 1).map((x: any) => { + console.log(x) + return parseFloat(x)}), + map: (radius: number, selection: Expression) => { + + return B.struct.modifier.includeSurroundings({ 0: selection, radius }); + } + }, + */ + { + '@desc':'Selects atoms in s1 that are within X Angstroms of any atom in s2.', + '@examples': ['chain A WITHIN 3 OF chain B'], name: 'within', + abbr: ['w2.'], + // type: h.binaryLeft, + type: h.prefixRemoveKet, + rule: h.prefixOpNoWhiteSpace(/within\s*\(\s*([-+]?[0-9]*\.?[0-9]+)\s*,/i, 1).map((x: any) => { + console.log(x) + return parseFloat(x)}), + map: (radius: number, target: Expression) => { + return B.struct.filter.within({ + 0: B.struct.generator.all(), + target, + 'max-radius': radius, + }); + }, + }, + { + '@desc': 'Selects atoms which have the same keyword as the atoms in a given selection', + '@examples': ['same resid as name FE'], + name: 'same', type: h.prefix, - rule: h.prefixOp(/within\s+([-+]?[0-9]*\.?[0-9]+)\s+of/, 1).map((x: any) => parseFloat(x)), - map: (radius: number, selection: Expression) => { - return B.struct.modifier.includeSurroundings({ 0: selection, radius }); + rule: h.prefixOp(new RegExp(`SAME\\s+(${propNames})\\s+AS`, 'i'), 1).map((x: any) => macroproperties[x].property), + map: (property: Expression, source: Expression) => { + return B.struct.filter.withSameAtomProperties({ + '0': B.struct.generator.all(), + source, + property + }); } }, + { + '@desc': + 'Selects atoms in s1 whose identifiers name and resi match atoms in s2.', + '@examples': ['chain A LIKE chain B'], + name: 'like', + type: h.binaryLeft, + rule: h.infixOp(/LIKE|l\./i), + map: (op: string, selection: Expression, source: Expression) => { + return B.struct.filter.withSameAtomProperties({ + 0: selection, + source, + property: B.core.type.compositeKey([ + B.ammp('label_atom_id'), + B.ammp('label_seq_id'), + ]), + }); + }, + }, ]; diff --git a/src/mol-script/transpilers/rasmol/parser.ts b/src/mol-script/transpilers/rasmol/parser.ts index 133cf42a407264f96d098505377939a0dd9baa98..74387650d62a1607010b017fa4685cb5f010b943 100644 --- a/src/mol-script/transpilers/rasmol/parser.ts +++ b/src/mol-script/transpilers/rasmol/parser.ts @@ -12,9 +12,9 @@ import * as h from '../helper'; import { MolScriptBuilder } from '../../../mol-script/language/builder'; const B = MolScriptBuilder; import { properties } from './properties'; -import { special_properties } from './special_properties'; -import { special_keywords } from './special_keywords'; -import { special_operators } from './special_operators'; +import { macroproperties } from './macroproperties'; +//import { macrokeywords } from './macrokeywords'; +//import { macrooperators } from './macrooperators'; import { operators } from './operators'; import { keywords } from './keywords'; import { AtomGroupArgs } from '../types'; @@ -25,7 +25,7 @@ import { Transpiler } from '../transpiler'; // const slash = P.MonadicParser.string('/'); -const propertiesDict = h.getPropertyRules(special_properties); +const propertiesDict = h.getPropertyRules(macroproperties); // const slash = P.MonadicParser.string('/'); const dot = P.MonadicParser.string('.'); @@ -49,7 +49,7 @@ function atomSelectionQuery2(x: any) { const props: { [k: string]: any[] } = {}; for (const k in x) { - const ps = special_properties[k]; + const ps = macroproperties[k]; if (!ps) { throw new Error(`property '${k}' not supported, value '${x[k]}'`); } @@ -65,33 +65,12 @@ function atomSelectionQuery2(x: any) { return B.struct.generator.atomGroups(tests); } -function atomExpressionQuery(x: any[]) { - const resname = x[0]; -// const tests: AtomGroupArgs = {}; - -/* if (chainname) { - // should be configurable, there is an option in Jmol to use auth or label - tests['chain-test'] = B.core.rel.eq([B.ammp('auth_asym_id'), chainname]); - } - - const resProps = []; - if (resno) resProps.push(B.core.rel.eq([B.ammp('auth_seq_id'), resno])); - if (inscode) resProps.push(B.core.rel.eq([B.ammp('pdbx_PDB_ins_code'), inscode])); - if (resProps.length) tests['residue-test'] = h.andExpr(resProps); - - const atomProps = []; - if (atomname) atomProps.push(B.core.rel.eq([B.ammp('auth_atom_id'), atomname])); - if (altloc) atomProps.push(B.core.rel.eq([B.ammp('label_alt_id'), altloc])); - if (atomProps.length) tests['atom-test'] = h.andExpr(atomProps); - - return B.struct.generator.atomGroups(tests); -*/ - if (resname){ - return B.struct.generator.atomGroups({'residue-test': B.core.rel.eq([B.ammp('label_comp_id'), resname])}) - } - -} - +//function atomExpressionQuery(x: any[]) { +// const resname = x[0]; +// if (resname){ +// return B.struct.generator.atomGroups({'residue-test': B.core.rel.eq([B.ammp('label_comp_id'), resname])}) +// } +//} const lang = P.MonadicParser.createLanguage({ @@ -109,11 +88,17 @@ const lang = P.MonadicParser.createLanguage({ r.Keywords, r.NamedAtomProperties, r.AtomSelectionMacro.map(atomSelectionQuery2), -// r.AtomExpression.map(atomExpressionQuery), - r.Object +// r.AtomExparession.map(atomExpressionQuery), + r.Object, + r.Object2, ); }, +// AtomExpression: function (r: any) { +// return P.MonadicParser.seq(r.Resname.or(P.MonadicParser.of(null))); +// }, + + // lys:a.ca -> resn lys and chain A and name ca // lys*a.ca -> resn lys and chain A and name ca @@ -191,7 +176,7 @@ const lang = P.MonadicParser.createLanguage({ ).map(x => { return { resn: x[0], name: x[1] }; }), P.MonadicParser.seq( orNull(propertiesDict.resn).skip(tator), - ).map(x => { return { resn: x[0]}; }) + ).map(x => { return { resn: x[0] }; }) ) ) ) @@ -200,29 +185,32 @@ const lang = P.MonadicParser.createLanguage({ }, ObjectProperty: () => { - const w = h.getReservedWords(special_properties, special_keywords, special_operators) + const w = h.getReservedWords(macroproperties, keywords, operators) .sort(h.strLenSortFn).map(h.escapeRegExp).join('|'); return P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i')); }, + Object: (r: any) => { + return r.ObjectProperty + .map((x: any) => { throw new Error(`property 'object' not supported, value '${x}'`); }); + }, + + ObjectProperty2: () => { const w = h.getReservedWords(properties, keywords, operators) .sort(h.strLenSortFn).map(h.escapeRegExp).join('|'); return P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i')); }, - Object: (r: any) => { + Object2: (r: any) => { return r.ObjectProperty2 .map((x: any) => { throw new Error(`property 'object' not supported, value '${x}'`); }); }, - AtomExpression: function (r: any) { - return P.MonadicParser.seq(r.Resname.or(P.MonadicParser.of(null))); - }, // Resname: () => P.MonadicParser.regexp(s/[a-zA-Z0-9]{1,4}/).desc('resname'), - Resname: () => P.MonadicParser.regexp(/\[[A-Z0-9]{1,4}\]/).desc('resname'), +// Resname: () => P.MonadicParser.regexp(/\[[A-Z0-9]{1,4}\]/).desc('resname'), NamedAtomProperties: function () { return P.MonadicParser.alt(...h.getNamedPropertyRules(properties)); diff --git a/src/mol-script/transpilers/rasmol/special_keywords.ts b/src/mol-script/transpilers/rasmol/special_keywords.ts deleted file mode 100644 index ecdcef51e1662b086d79eadcdb112c9e2c62b6a1..0000000000000000000000000000000000000000 --- a/src/mol-script/transpilers/rasmol/special_keywords.ts +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com> - */ - -import { MolScriptBuilder } from '../../../mol-script/language/builder'; -const B = MolScriptBuilder; -import * as h from '../helper'; -import { KeywordDict } from '../types'; - -const ResDict = { - nucleic: ['A', 'C', 'T', 'G', 'U', 'DA', 'DC', 'DT', 'DG', 'DU'], - protein: ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'CYX', 'GLN', 'GLU', 'GLY', 'HIS', 'HID', 'HIE', 'HIP', 'ILE', 'LEU', 'LYS', 'MET', 'MSE', 'PHE', 'PRO', 'SER', 'THR', 'TRP', 'TYR', 'VAL'], - solvent: ['HOH', 'WAT', 'H20', 'TIP', 'SOL'] -}; - -const Backbone = { - nucleic: ['P', "O3'", "O5'", "C5'", "C4'", "C3'", 'OP1', 'OP2', 'O3*', 'O5*', 'C5*', 'C4*', 'C3*'], - protein: ['C', 'N', 'CA', 'O'] -}; - - -export const special_keywords: KeywordDict = { - all: { - '@desc': 'All atoms currently loaded into PyMOL', - abbr: ['*'], - map: () => B.struct.generator.all() - }, - none: { - '@desc': 'No atoms (empty selection)', - map: () => B.struct.generator.empty() - }, - hydrogens: { - '@desc': 'All hydrogen atoms currently loaded into PyMOL', - abbr: ['hydro', 'h.'], - map: () => B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.eq([ - B.acp('elementSymbol'), - B.es('H') - ]) - }) - }, - hetatm: { - '@desc': 'All atoms loaded from Protein Data Bank HETATM records', - abbr: ['het'], - map: () => B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.eq([B.ammp('isHet'), true]) - }) - }, - visible: { - '@desc': 'All atoms in enabled objects with at least one visible representation', - abbr: ['v.'] - }, - polymer: { - '@desc': 'All atoms on the polymer (not het). Finds atoms with residue identifiers matching a known polymer, such a peptide and DNA.', - abbr: ['pol.'], - map: () => B.struct.generator.atomGroups({ - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.nucleic.concat(ResDict.protein)), - B.ammp('label_comp_id') - ]) - }) - }, - sidechain: { - '@desc': 'Polymer non-backbone atoms (new in PyMOL 1.6.1)', - }, - present: { - '@desc': 'All atoms with defined coordinates in the current state (used in creating movies)', - abbr: ['pr.'] - }, - center: { - '@desc': 'Pseudo-atom at the center of the scene' - }, - origin: { - '@desc': 'Pseudo-atom at the origin of rotation', - }, - enabled: { - '@desc': 'All enabled objects or selections from the object list.', - }, - masked: { - '@desc': 'All masked atoms.', - abbr: ['msk.'] - }, - protected: { - '@desc': 'All protected atoms.', - abbr: ['pr.'] - }, - bonded: { - '@desc': 'All bonded atoms', - map: () => B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.gr([B.struct.atomProperty.core.bondCount({ - flags: B.struct.type.bondFlags(['covalent', 'metallic', 'sulfide']) - }), 0]) - }) - }, - donors: { - '@desc': 'All hydrogen bond donor atoms.', - abbr: ['don.'] - }, - acceptors: { - '@desc': 'All hydrogen bond acceptor atoms.', - abbr: ['acc.'] - }, - fixed: { - '@desc': 'All fixed atoms.', - abbr: ['fxd.'] - }, - restrained: { - '@desc': 'All restrained atoms.', - abbr: ['rst.'] - }, - organic: { - '@desc': 'All atoms in non-polymer organic compounds (e.g. ligands, buffers). Finds carbon-containing molecules that do not match known polymers.', - abbr: ['org.'], - map: () => h.asAtoms(B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union([ - B.struct.generator.queryInSelection({ - '0': B.struct.generator.atomGroups({ - 'residue-test': B.core.logic.not([ - B.core.set.has([ - B.core.type.set(ResDict.nucleic.concat(ResDict.protein)), - B.ammp('label_comp_id') - ]) - ]) - }), - query: B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.eq([ - B.es('C'), - B.acp('elementSymbol') - ]) - }) - }) - ]), - property: B.ammp('residueKey') - })) - }, - inorganic: { - '@desc': 'All non-polymer inorganic atoms/ions. Finds atoms in molecules that do not contain carbon and do not match any known solvent residues.', - abbr: ['ino.'], - map: () => h.asAtoms(B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union([ - B.struct.filter.pick({ - '0': B.struct.generator.atomGroups({ - 'residue-test': B.core.logic.not([ - B.core.set.has([ - B.core.type.set(ResDict.nucleic.concat(ResDict.protein).concat(ResDict.solvent)), - B.ammp('label_comp_id') - ]) - ]), - 'group-by': B.ammp('residueKey') - }), - test: B.core.logic.not([ - B.core.set.has([ - B.struct.atomSet.propertySet([B.acp('elementSymbol')]), - B.es('C') - ]) - ]) - }) - ]), - property: B.ammp('residueKey') - })) - }, - solvent: { - '@desc': 'All water molecules. The hardcoded solvent residue identifiers are currently: HOH, WAT, H20, TIP, SOL.', - abbr: ['sol.'], - map: () => B.struct.generator.atomGroups({ - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.solvent), - B.ammp('label_comp_id') - ]) - }) - }, - guide: { - '@desc': 'All protein CA and nucleic acid C4*/C4', - map: () => B.struct.combinator.merge([ - B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.eq([ - B.atomName('CA'), - B.ammp('label_atom_id') - ]), - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.protein), - B.ammp('label_comp_id') - ]) - }), - B.struct.generator.atomGroups({ - 'atom-test': B.core.set.has([ - h.atomNameSet(['C4*', 'C4']), - B.ammp('label_atom_id') - ]), - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.nucleic), - B.ammp('label_comp_id') - ]) - }) - ]), - }, - metals: { - '@desc': 'All metal atoms (new in PyMOL 1.6.1)' - }, - backbone: { - '@desc': 'the C, N, CA, and O atoms of a protein and the equivalent atoms in a nucleic acid.', - map: () => B.struct.generator.atomGroups({ - 'atom-test': B.core.set.has([ - B.core.type.set(Backbone.protein.concat(ResDict.protein)), - B.ammp('label_atom_id') - ]) - }), - }, - proteinxxxxxx: { - '@desc': 'protein................', - abbr: ['polymer.protein'], - map: () => B.struct.generator.atomGroups({ - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.protein), - B.ammp('label_comp_id') - ]) - }) - }, - nucleicxxxxx: { - '@desc': 'protein................', - abbr: ['polymer.nucleic'], - map: () => B.struct.generator.atomGroups({ - 'residue-test': B.core.set.has([ - B.core.type.set(ResDict.nucleic), - B.ammp('label_comp_id') - ]) - }) - } -}; diff --git a/src/mol-script/transpilers/rasmol/special_operators.ts b/src/mol-script/transpilers/rasmol/special_operators.ts deleted file mode 100644 index 9a53bde352334e70f3d3e43488e40c9a9aab4f85..0000000000000000000000000000000000000000 --- a/src/mol-script/transpilers/rasmol/special_operators.ts +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com> - */ - -import * as P from '../../../mol-util/monadic-parser'; -import * as h from '../helper'; -import { MolScriptBuilder } from '../../../mol-script/language/builder'; -const B = MolScriptBuilder; -import { OperatorList } from '../types'; -import { Expression } from '../../language/expression'; - -export const special_operators: OperatorList = [ - { - '@desc': 'Selects atoms that are not included in s1.', - '@examples': [ - 'NOT resn ALA', - 'not (resi 42 or chain A)', - '!resi 42 or chain A', - ], - name: 'not', - type: h.prefix, - rule: P.MonadicParser.alt( - P.MonadicParser.regexp(/NOT/i).skip(P.MonadicParser.whitespace), - P.MonadicParser.string('!').skip(P.MonadicParser.optWhitespace) - ), - map: (op, selection) => h.invertExpr(selection), - }, - { - '@desc': 'Selects atoms included in both s1 and s2.', - '@examples': ['chain A AND name CA'], - name: 'and', - type: h.binaryLeft, - rule: h.infixOp(/AND|&/i), - map: (op, selection, by) => - B.struct.modifier.intersectBy({ 0: selection, by }), - }, - { - '@desc': 'Selects atoms included in either s1 or s2.', - '@examples': ['chain A OR chain B'], - name: 'or', - type: h.binaryLeft, - rule: h.infixOp(/OR|\|/i), - map: (op: string, s1: Expression, s2: Expression) => B.struct.combinator.merge([s1, s2]), - }, - { - '@desc': - 'Selects atoms in s1 whose identifiers name, resi, resn, chain and segi all match atoms in s2.', - '@examples': ['chain A IN chain B'], - name: 'in', - type: h.binaryLeft, - rule: h.infixOp(/IN/i), - map: (op: string, selection: Expression, source: Expression) => { - return B.struct.filter.withSameAtomProperties({ - 0: selection, - source, - property: B.core.type.compositeKey([ - B.ammp('label_atom_id'), - B.ammp('label_seq_id'), - B.ammp('label_comp_id'), - B.ammp('auth_asym_id'), - B.ammp('label_asym_id'), - ]), - }); - }, - }, - { - '@desc': - 'Selects atoms in s1 whose identifiers name and resi match atoms in s2.', - '@examples': ['chain A LIKE chain B'], - name: 'like', - type: h.binaryLeft, - rule: h.infixOp(/LIKE|l\./i), - map: (op: string, selection: Expression, source: Expression) => { - return B.struct.filter.withSameAtomProperties({ - 0: selection, - source, - property: B.core.type.compositeKey([ - B.ammp('label_atom_id'), - B.ammp('label_seq_id'), - ]), - }); - }, - }, - { - '@desc': - 'Selects all atoms whose van der Waals radii are separated from the van der Waals radii of s1 by a minimum of X Angstroms.', - '@examples': ['solvent GAP 2'], - name: 'gap', - type: h.postfix, - rule: h - .postfixOp(/GAP\s+([-+]?[0-9]*\.?[0-9]+)/i, 1) - .map((x: any) => parseFloat(x)), - map: (distance: number, target: Expression) => { - return B.struct.filter.within({ - '0': B.struct.generator.all(), - target, - 'atom-radius': B.acp('vdw'), - 'max-radius': distance, - invert: true, - }); - }, - }, - { - '@desc': - 'Selects atoms with centers within X Angstroms of the center of any atom in s1.', - '@examples': ['resname LIG AROUND 1'], - name: 'around', - abbr: ['a.'], - type: h.postfix, - rule: h - .postfixOp(/(AROUND|a\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2) - .map((x: any) => parseFloat(x)), - map: (radius: number, target: Expression) => { - return B.struct.modifier.exceptBy({ - '0': B.struct.filter.within({ - '0': B.struct.generator.all(), - target, - 'max-radius': radius, - }), - by: target, - }); - }, - }, - { - '@desc': - 'Expands s1 by all atoms within X Angstroms of the center of any atom in s1.', - '@examples': ['chain A EXPAND 3'], - name: 'expand', - abbr: ['x.'], - type: h.postfix, - rule: h - .postfixOp(/(EXPAND|x\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2) - .map((x: any) => parseFloat(x)), - map: (radius: number, selection: Expression) => { - return B.struct.modifier.includeSurroundings({ 0: selection, radius }); - }, - }, - { - '@desc': - 'Selects atoms in s1 that are within X Angstroms of any atom in s2.', - '@examples': ['chain A WITHIN 3 OF chain B'], - name: 'within', - abbr: ['w.'], - type: h.binaryLeft, - rule: h.ofOp('WITHIN', 'w.'), - map: (radius: number, selection: Expression, target: Expression) => { - return B.struct.filter.within({ - 0: selection, - target, - 'max-radius': radius, - }); - }, - }, - { - '@desc': - 'Same as within, but excludes s2 from the selection (and thus is identical to s1 and s2 around X).', - '@examples': ['chain A NEAR_TO 3 OF chain B'], - name: 'near_to', - abbr: ['nto.'], - type: h.binaryLeft, - rule: h.ofOp('NEAR_TO', 'nto.'), - map: (radius: number, selection: Expression, target: Expression) => { - return B.struct.modifier.exceptBy({ - '0': B.struct.filter.within({ - '0': selection, - target, - 'max-radius': radius, - }), - by: target, - }); - }, - }, - { - '@desc': 'Selects atoms in s1 that are at least X Anstroms away from s2.', - '@examples': ['solvent BEYOND 2 OF chain A'], - name: 'beyond', - abbr: ['be.'], - type: h.binaryLeft, - rule: h.ofOp('BEYOND', 'be.'), - map: (radius: number, selection: Expression, target: Expression) => { - return B.struct.modifier.exceptBy({ - '0': B.struct.filter.within({ - '0': selection, - target, - 'max-radius': radius, - invert: true, - }), - by: target, - }); - }, - }, - { - '@desc': 'Expands selection to complete residues.', - '@examples': ['BYRESIDUE name N'], - name: 'byresidue', - abbr: ['byresi', 'byres', 'br.'], - type: h.prefix, - rule: h.prefixOp(/BYRESIDUE|byresi|byres|br\./i), - map: (op: string, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union({ 0: selection }), - property: B.ammp('residueKey'), - }) - ); - }, - }, - { - '@desc': - 'Completely selects all alpha carbons in all residues covered by a selection.', - '@examples': ['BYCALPHA chain A'], - name: 'bycalpha', - abbr: ['bca.'], - type: h.prefix, - rule: h.prefixOp(/BYCALPHA|bca\./i), - map: (op: string, selection: Expression) => { - return B.struct.generator.queryInSelection({ - '0': B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union({ 0: selection }), - property: B.ammp('residueKey'), - }), - query: B.struct.generator.atomGroups({ - 'atom-test': B.core.rel.eq([ - B.atomName('CA'), - B.ammp('label_atom_id'), - ]), - }), - }); - }, - }, - { - '@desc': 'Expands selection to complete molecules.', - '@examples': ['BYMOLECULE resi 20-30'], - name: 'bymolecule', - isUnsupported: true, // structure-query.atom-property.topology.connected-component-key' is not implemented - abbr: ['bymol', 'bm.'], - type: h.prefix, - rule: h.prefixOp(/BYMOLECULE|bymol|bm\./i), - map: (op: string, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union({ 0: selection }), - property: B.atp('connectedComponentKey'), - }) - ); - }, - }, - { - '@desc': 'Expands selection to complete fragments.', - '@examples': ['BYFRAGMENT resi 10'], - name: 'byfragment', - abbr: ['byfrag', 'bf.'], - isUnsupported: true, - type: h.prefix, - rule: h.prefixOp(/BYFRAGMENT|byfrag|bf\./i), - map: (op: string, selection: Expression) => [op, selection], - }, - { - '@desc': 'Expands selection to complete segments.', - '@examples': ['BYSEGMENT resn CYS'], - name: 'bysegment', - abbr: ['bysegi', 'byseg', 'bs.'], - type: h.prefix, - rule: h.prefixOp(/BYSEGMENT|bysegi|byseg|bs\./i), - map: (op: string, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.expandProperty({ - '0': B.struct.modifier.union({ 0: selection }), - property: B.ammp('chainKey'), - }) - ); - }, - }, - { - '@desc': 'Expands selection to complete objects.', - '@examples': ['BYOBJECT chain A'], - name: 'byobject', - abbr: ['byobj', 'bo.'], - isUnsupported: true, - type: h.prefix, - rule: h.prefixOp(/BYOBJECT|byobj|bo\./i), - map: (op: string, selection: Expression) => [op, selection], - }, - { - '@desc': 'Expands selection to unit cell.', - '@examples': ['BYCELL chain A'], - name: 'bycell', - isUnsupported: true, - type: h.prefix, - rule: h.prefixOp(/BYCELL/i), - map: (op: string, selection: Expression) => [op, selection], - }, - { - '@desc': 'All rings of size ≤ 7 which have at least one atom in s1.', - '@examples': ['BYRING resn HEM'], - name: 'byring', - // isUnsupported: true, // structure-query.atom-set.atom-count' is not implemented. - type: h.prefix, - rule: h.prefixOp(/BYRING/i), - map: (op: string, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.intersectBy({ - '0': B.struct.filter.pick({ - '0': B.struct.generator.rings(), - test: B.core.logic.and([ - B.core.rel.lte([B.struct.atomSet.atomCount(), 7]), - B.core.rel.gr([B.struct.atomSet.countQuery([selection]), 1]), - ]), - }), - by: selection, - }) - ); - }, - }, - { - '@desc': 'Selects atoms directly bonded to s1, excludes s1.', - '@examples': ['NEIGHBOR resn CYS'], - name: 'neighbor', - type: h.prefix, - abbr: ['nbr.'], - rule: h.prefixOp(/NEIGHBOR|nbr\./i), - map: (op: string, selection: Expression) => { - return B.struct.modifier.exceptBy({ - '0': h.asAtoms( - B.struct.modifier.includeConnected({ - '0': B.struct.modifier.union({ 0: selection }), - 'bond-test': true, - }) - ), - by: selection, - }); - }, - }, - { - '@desc': 'Selects atoms directly bonded to s1, may include s1.', - '@examples': ['BOUND_TO name CA'], - name: 'bound_to', - abbr: ['bto.'], - type: h.prefix, - rule: h.prefixOp(/BOUND_TO|bto\./i), - map: (op: string, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.includeConnected({ - '0': B.struct.modifier.union({ 0: selection }), - }) - ); - }, - }, - { - '@desc': 'Extends s1 by X bonds connected to atoms in s1.', - '@examples': ['resname LIG EXTEND 3'], - name: 'extend', - abbr: ['xt.'], - type: h.postfix, - rule: h.postfixOp(/(EXTEND|xt\.)\s+([0-9]+)/i, 2).map((x: any) => parseInt(x)), - map: (count: number, selection: Expression) => { - return h.asAtoms( - B.struct.modifier.includeConnected({ - '0': B.struct.modifier.union({ 0: selection }), - 'bond-test': true, - 'layer-count': count, - }) - ); - }, - }, -]; diff --git a/src/mol-script/transpilers/rasmol/symbols.ts b/src/mol-script/transpilers/rasmol/symbols.ts index 79b9c50feefc7f81a4017b391bd02a642df1976f..f298facbef04484a29550e194729bd4bee8ff395 100644 --- a/src/mol-script/transpilers/rasmol/symbols.ts +++ b/src/mol-script/transpilers/rasmol/symbols.ts @@ -8,6 +8,7 @@ */ import { properties } from './properties'; +import { macroproperties } from './macroproperties'; import { operators } from './operators'; import { keywords } from './keywords'; @@ -17,6 +18,11 @@ for (const name in properties) { Properties.push(name); if (properties[name].abbr) Properties.push(...properties[name].abbr!); } +for (const name in macroproperties) { + if (macroproperties[name].isUnsupported) continue; + Properties.push(name); + if (macroproperties[name].abbr) Properties.push(...macroproperties[name].abbr!); +} export const Operators: string[] = []; operators.forEach(o => {