UNPKG

molstar

Version:

A comprehensive macromolecular library.

352 lines (351 loc) 14.3 kB
/** * Copyright (c) 2017-2022 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> * @author Koya Sakuma <koya.sakuma.work@gmail.com> * * Adapted from MolQL project */ import * as P from '../../mol-util/monadic-parser'; import { MolScriptBuilder } from '../../mol-script/language/builder'; var B = MolScriptBuilder; export function escapeRegExp(s) { return String(s).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); } // Takes a parser for the prefix operator, and a parser for the base thing being // parsed, and parses as many occurrences as possible of the prefix operator. // Note that the parser is created using `P.lazy` because it's recursive. It's // valid for there to be zero occurrences of the prefix operator. export function prefix(opParser, nextParser, mapFn) { var parser = P.MonadicParser.lazy(function () { return P.MonadicParser.seq(opParser, parser) .map(function (x) { return mapFn.apply(void 0, 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 // the very first rule. Inside `parser` is to match parser again. Alternatively, // you might think to try `nextParser.or(P.seq(parser, opParser))`, but // that won't work either because in a call to `.or` (aka `P.alt`), Parsimmon // takes the first possible match, even if subsequent matches are longer, so the // parser will never actually look far enough ahead to see the postfix // operators. export function postfix(opParser, nextParser, mapFn) { // Because we can't use recursion like stated above, we just match a flat list // of as many occurrences of the postfix operator as possible, then use // `.reduce` to manually nest the list. // // Example: // // INPUT :: "4!!!" // PARSE :: [4, "factorial", "factorial", "factorial"] // REDUCE :: ["factorial", ["factorial", ["factorial", 4]]] return P.MonadicParser.seqMap(nextParser, opParser.many(), function (x, suffixes) { return suffixes.reduce(function (acc, x) { return mapFn(x, acc); }, x); }); } // Takes a parser for all the operators at this precedence level, and a parser // that parsers everything at the next precedence level, and returns a parser // that parses as many binary operations as possible, associating them to the // right. (e.g. 1^2^3 is 1^(2^3) not (1^2)^3) export function binaryRight(opParser, nextParser, mapFn) { var parser = P.MonadicParser.lazy(function () { return nextParser.chain(function (next) { return P.MonadicParser.seq(opParser, P.MonadicParser.of(next), parser).map(function (x) { return x; }).or(P.MonadicParser.of(next)); }); }); return parser; } // Takes a parser for all the operators at this precedence level, and a parser // that parsers everything at the next precedence level, and returns a parser // that parses as many binary operations as possible, associating them to the // left. (e.g. 1-2-3 is (1-2)-3 not 1-(2-3)) export function binaryLeft(opParser, nextParser, mapFn) { // We run into a similar problem as with the `POSTFIX` parser above where we // can't recurse in the direction we want, so we have to resort to parsing an // entire list of operator chunks and then using `.reduce` to manually nest // them again. // // Example: // // INPUT :: "1+2+3" // PARSE :: [1, ["+", 2], ["+", 3]] // REDUCE :: ["+", ["+", 1, 2], 3] return P.MonadicParser.seqMap(nextParser, P.MonadicParser.seq(opParser, nextParser).many(), function (first, rest) { return rest.reduce(function (acc, ch) { var op = ch[0], another = ch[1]; return mapFn(op, acc, another); }, first); }); } /** * combine operators of decreasing binding strength */ export function combineOperators(opList, rule) { var x = opList.reduce(function (acc, level) { var map = level.isUnsupported ? makeError("operator '".concat(level.name, "' not supported")) : level.map; return level.type(level.rule, acc, map); }, rule); return x; } export function infixOp(re, group) { if (group === void 0) { group = 0; } return P.MonadicParser.optWhitespace.then(P.MonadicParser.regexp(re, group).skip(P.MonadicParser.optWhitespace)); } export function prefixOp(re, group) { if (group === void 0) { group = 0; } return P.MonadicParser.regexp(re, group).skip(P.MonadicParser.optWhitespace); } export function postfixOp(re, group) { if (group === void 0) { group = 0; } return P.MonadicParser.optWhitespace.then(P.MonadicParser.regexp(re, group)); } export function ofOp(name, short) { var op = short ? "".concat(name, "|").concat(escapeRegExp(short)) : name; var re = RegExp("(".concat(op, ")\\s+([-+]?[0-9]*\\.?[0-9]+)\\s+OF"), 'i'); return infixOp(re, 2).map(parseFloat); } export function makeError(msg) { return function () { throw new Error(msg); }; } export function andExpr(selections) { if (selections.length === 1) { return selections[0]; } else if (selections.length > 1) { return B.core.logic.and(selections); } else { return undefined; } } export function orExpr(selections) { if (selections.length === 1) { return selections[0]; } else if (selections.length > 1) { return B.core.logic.or(selections); } else { return undefined; } } export function testExpr(property, args) { if (args && args.op !== undefined && args.val !== undefined) { var opArgs = [property, args.val]; switch (args.op) { case '=': return B.core.rel.eq(opArgs); case '!=': return B.core.rel.neq(opArgs); case '>': return B.core.rel.gr(opArgs); case '<': return B.core.rel.lt(opArgs); case '>=': return B.core.rel.gre(opArgs); case '<=': return B.core.rel.lte(opArgs); default: throw new Error("operator '".concat(args.op, "' not supported")); } } else if (args && args.flags !== undefined) { return B.core.flags.hasAny([property, args.flags]); } else if (args && args.min !== undefined && args.max !== undefined) { return B.core.rel.inRange([property, args.min, args.max]); } else if (!Array.isArray(args)) { return B.core.rel.eq([property, args]); } else if (args.length > 1) { return B.core.set.has([B.core.type.set(args), property]); } else { return B.core.rel.eq([property, args[0]]); } } export function invertExpr(selection) { return B.struct.generator.queryInSelection({ 0: selection, query: B.struct.generator.all(), 'in-complement': true }); } export function strLenSortFn(a, b) { return a.length < b.length ? 1 : -1; } function getNamesRegex(name, abbr) { var names = (abbr ? [name].concat(abbr) : [name]) .sort(strLenSortFn).map(escapeRegExp).join('|'); return RegExp("".concat(names), 'i'); } export function getPropertyRules(properties) { // in keyof typeof properties var propertiesDict = {}; Object.keys(properties).sort(strLenSortFn).forEach(function (name) { var ps = properties[name]; var errorFn = makeError("property '".concat(name, "' not supported")); var rule = P.MonadicParser.regexp(ps.regex).map(function (x) { if (ps.isUnsupported) errorFn(); return testExpr(ps.property, ps.map(x)); }); if (!ps.isNumeric) { propertiesDict[name] = rule; } }); return propertiesDict; } export function getNamedPropertyRules(properties) { var namedPropertiesList = []; Object.keys(properties).sort(strLenSortFn).forEach(function (name) { var ps = properties[name]; var errorFn = makeError("property '".concat(name, "' not supported")); var rule = P.MonadicParser.regexp(ps.regex).map(function (x) { if (ps.isUnsupported) errorFn(); return testExpr(ps.property, ps.map(x)); }); var nameRule = P.MonadicParser.regexp(getNamesRegex(name, ps.abbr)).trim(P.MonadicParser.optWhitespace); var groupMap = function (x) { var _a; return B.struct.generator.atomGroups((_a = {}, _a[ps.level] = x, _a)); }; if (ps.isNumeric) { namedPropertiesList.push(nameRule.then(P.MonadicParser.seq(P.MonadicParser.regexp(/>=|<=|=|!=|>|</).trim(P.MonadicParser.optWhitespace), P.MonadicParser.regexp(ps.regex).map(ps.map))).map(function (x) { if (ps.isUnsupported) errorFn(); return testExpr(ps.property, { op: x[0], val: x[1] }); }).map(groupMap)); } else { namedPropertiesList.push(nameRule.then(rule).map(groupMap)); } }); return namedPropertiesList; } export function getKeywordRules(keywords) { var keywordsList = []; Object.keys(keywords).sort(strLenSortFn).forEach(function (name) { var ks = keywords[name]; var mapFn = ks.map ? ks.map : makeError("keyword '".concat(name, "' not supported")); var rule = P.MonadicParser.regexp(getNamesRegex(name, ks.abbr)).map(mapFn); keywordsList.push(rule); }); return keywordsList; } export function getFunctionRules(functions, argRule) { var functionsList = []; var begRule = P.MonadicParser.regexp(/\(\s*/); var endRule = P.MonadicParser.regexp(/\s*\)/); Object.keys(functions).sort(strLenSortFn).forEach(function (name) { var fs = functions[name]; var mapFn = fs.map ? fs.map : makeError("function '".concat(name, "' not supported")); var rule = P.MonadicParser.regexp(new RegExp(name, 'i')).skip(begRule).then(argRule).skip(endRule).map(mapFn); functionsList.push(rule); }); return functionsList; } export function getPropertyNameRules(properties, lookahead) { var list = []; Object.keys(properties).sort(strLenSortFn).forEach(function (name) { var ps = properties[name]; var errorFn = makeError("property '".concat(name, "' not supported")); var rule = P.MonadicParser.regexp(getNamesRegex(name, ps.abbr)).lookahead(lookahead).map(function () { if (ps.isUnsupported) errorFn(); return ps.property; }); list.push(rule); }); return list; } export function getReservedWords(properties, keywords, operators, functions) { var w = []; for (var name_1 in properties) { w.push(name_1); if (properties[name_1].abbr) w.push.apply(w, properties[name_1].abbr); } for (var name_2 in keywords) { w.push(name_2); if (keywords[name_2].abbr) w.push.apply(w, keywords[name_2].abbr); } operators.forEach(function (o) { w.push(o.name); if (o.abbr) w.push.apply(w, o.abbr); }); return w; } export function atomNameSet(ids) { return B.core.type.set(ids.map(B.atomName)); } export function asAtoms(e) { return B.struct.generator.queryInSelection({ 0: e, query: B.struct.generator.all() }); } export function wrapValue(property, value, sstrucDict) { switch (property.head.name) { case 'structure-query.atom-property.macromolecular.label_atom_id': return B.atomName(value); case 'structure-query.atom-property.core.element-symbol': return B.es(value); case 'structure-query.atom-property.macromolecular.secondary-structure-flags': if (sstrucDict) { value = [sstrucDict[value.toUpperCase()] || 'none']; } return B.struct.type.secondaryStructureFlags([value]); default: return value; } } var propPrefix = 'structure-query.atom-property.macromolecular.'; var entityProps = ['entityKey', 'label_entity_id', 'entityType']; var chainProps = ['chainKey', 'label_asym_id', 'label_entity_id', 'auth_asym_id', 'entityType']; var residueProps = ['residueKey', 'label_comp_id', 'label_seq_id', 'auth_comp_id', 'auth_seq_id', 'pdbx_formal_charge', 'secondaryStructureKey', 'secondaryStructureFlags', 'isModified', 'modifiedParentName']; export function testLevel(property) { if (property.head.name.startsWith(propPrefix)) { var name_3 = property.head.name.substr(propPrefix.length); if (entityProps.includes(name_3)) return 'entity-test'; if (chainProps.includes(name_3)) return 'chain-test'; if (residueProps.includes(name_3)) return 'residue-test'; } return 'atom-test'; } var flagProps = [ 'structure-query.atom-property.macromolecular.secondary-structure-flags' ]; export function valuesTest(property, values) { if (flagProps.includes(property.head.name)) { var name_4 = values[0].head; var flags_1 = []; values.forEach(function (v) { return flags_1.push.apply(flags_1, v.args[0]); }); return B.core.flags.hasAny([property, { head: name_4, args: flags_1 }]); } else { if (values.length === 1) { return B.core.rel.eq([property, values[0]]); } else if (values.length > 1) { return B.core.set.has([B.core.type.set(values), property]); } } } export function resnameExpr(resnameList) { return B.struct.generator.atomGroups({ 'residue-test': B.core.set.has([ B.core.type.set(resnameList), B.ammp('label_comp_id') ]) }); }