symbols.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /**
  2. * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { UniqueArray } from '../../../mol-data/generic';
  7. import Expression from '../../language/expression';
  8. import { Argument, MSymbol } from '../../language/symbol';
  9. import { MolScriptSymbolTable as MolScript } from '../../language/symbol-table';
  10. import Type from '../../language/type';
  11. export type MolScriptSymbol =
  12. | { kind: 'alias', aliases: string[], symbol: MSymbol }
  13. | { kind: 'macro', aliases: string[], symbol: MSymbol, translate: (args: any) => Expression }
  14. function Alias(symbol: MSymbol<any>, ...aliases: string[]): MolScriptSymbol { return { kind: 'alias', aliases, symbol }; }
  15. // function Macro(symbol: MSymbol<any>, translate: (args: any) => Expression, ...aliases: string[]): MolScriptSymbol {
  16. // symbol.info.namespace = 'molscript-macro';
  17. // symbol.id = `molscript-macro.${symbol.info.name}`;
  18. // return { kind: 'macro', symbol, translate, aliases: [symbol.info.name, ...aliases] };
  19. // }
  20. export function isMolScriptSymbol(x: any): x is MolScriptSymbol {
  21. return x.kind === 'alias' || x.kind === 'macro';
  22. }
  23. export const SymbolTable = [
  24. [
  25. 'Core symbols',
  26. Alias(MolScript.core.type.bool, 'bool'),
  27. Alias(MolScript.core.type.num, 'num'),
  28. Alias(MolScript.core.type.str, 'str'),
  29. Alias(MolScript.core.type.regex, 'regex'),
  30. Alias(MolScript.core.type.list, 'list'),
  31. Alias(MolScript.core.type.set, 'set'),
  32. Alias(MolScript.core.type.compositeKey, 'composite-key'),
  33. Alias(MolScript.core.logic.not, 'not'),
  34. Alias(MolScript.core.logic.and, 'and'),
  35. Alias(MolScript.core.logic.or, 'or'),
  36. Alias(MolScript.core.ctrl.if, 'if'),
  37. Alias(MolScript.core.ctrl.fn, 'fn'),
  38. Alias(MolScript.core.ctrl.eval, 'eval'),
  39. Alias(MolScript.core.math.add, 'add', '+'),
  40. Alias(MolScript.core.math.sub, 'sub', '-'),
  41. Alias(MolScript.core.math.mult, 'mult', '*'),
  42. Alias(MolScript.core.math.div, 'div', '/'),
  43. Alias(MolScript.core.math.pow, 'pow', '**'),
  44. Alias(MolScript.core.math.mod, 'mod'),
  45. Alias(MolScript.core.math.min, 'min'),
  46. Alias(MolScript.core.math.max, 'max'),
  47. Alias(MolScript.core.math.floor, 'floor'),
  48. Alias(MolScript.core.math.ceil, 'ceil'),
  49. Alias(MolScript.core.math.roundInt, 'round'),
  50. Alias(MolScript.core.math.abs, 'abs'),
  51. Alias(MolScript.core.math.sqrt, 'sqrt'),
  52. Alias(MolScript.core.math.sin, 'sin'),
  53. Alias(MolScript.core.math.cos, 'cos'),
  54. Alias(MolScript.core.math.tan, 'tan'),
  55. Alias(MolScript.core.math.asin, 'asin'),
  56. Alias(MolScript.core.math.acos, 'acos'),
  57. Alias(MolScript.core.math.atan, 'atan'),
  58. Alias(MolScript.core.math.sinh, 'sinh'),
  59. Alias(MolScript.core.math.cosh, 'cosh'),
  60. Alias(MolScript.core.math.tanh, 'tanh'),
  61. Alias(MolScript.core.math.exp, 'exp'),
  62. Alias(MolScript.core.math.log, 'log'),
  63. Alias(MolScript.core.math.log10, 'log10'),
  64. Alias(MolScript.core.math.atan2, 'atan2'),
  65. Alias(MolScript.core.rel.eq, 'eq', '='),
  66. Alias(MolScript.core.rel.neq, 'neq', '!='),
  67. Alias(MolScript.core.rel.lt, 'lt', '<'),
  68. Alias(MolScript.core.rel.lte, 'lte', '<='),
  69. Alias(MolScript.core.rel.gr, 'gr', '>'),
  70. Alias(MolScript.core.rel.gre, 'gre', '>='),
  71. Alias(MolScript.core.rel.inRange, 'in-range'),
  72. Alias(MolScript.core.str.concat, 'concat'),
  73. Alias(MolScript.core.str.match, 'regex.match'),
  74. Alias(MolScript.core.list.getAt, 'list.get'),
  75. Alias(MolScript.core.set.has, 'set.has'),
  76. Alias(MolScript.core.set.isSubset, 'set.subset'),
  77. ],
  78. [
  79. 'Structure',
  80. [
  81. 'Types',
  82. Alias(MolScript.structureQuery.type.entityType, 'ent-type'),
  83. Alias(MolScript.structureQuery.type.authResidueId, 'auth-resid'),
  84. Alias(MolScript.structureQuery.type.labelResidueId, 'label-resid'),
  85. Alias(MolScript.structureQuery.type.ringFingerprint, 'ringfp'),
  86. Alias(MolScript.structureQuery.type.bondFlags, 'bond-flags'),
  87. ],
  88. [
  89. 'Slots',
  90. Alias(MolScript.structureQuery.slot.elementSetReduce, 'atom.set.reduce.value'),
  91. ],
  92. [
  93. 'Generators',
  94. Alias(MolScript.structureQuery.generator.atomGroups, 'sel.atom.atom-groups'),
  95. Alias(MolScript.structureQuery.generator.queryInSelection, 'sel.atom.query-in-selection'),
  96. Alias(MolScript.structureQuery.generator.rings, 'sel.atom.rings'),
  97. Alias(MolScript.structureQuery.generator.empty, 'sel.atom.empty'),
  98. Alias(MolScript.structureQuery.generator.all, 'sel.atom.all'),
  99. // Macro(MSymbol('sel.atom.atoms', Arguments.Dictionary({
  100. // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to each atom.' })
  101. // }), Struct.Types.ElementSelection, 'A selection of singleton atom sets.'),
  102. // args => B.struct.generator.atomGroups({ 'atom-test': M.tryGetArg(args, 0, true) })),
  103. // Macro(MSymbol('sel.atom.res', Arguments.Dictionary({
  104. // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each residue.' })
  105. // }), Struct.Types.ElementSelection, 'A selection of atom sets grouped by residue.'),
  106. // args => B.struct.generator.atomGroups({
  107. // 'residue-test': M.tryGetArg(args, 0, true),
  108. // 'group-by': B.ammp('residueKey')
  109. // })),
  110. // Macro(MSymbol('sel.atom.chains', Arguments.Dictionary({
  111. // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each chain.' })
  112. // }), Struct.Types.ElementSelection, 'A selection of atom sets grouped by chain.'),
  113. // args => B.struct.generator.atomGroups({
  114. // 'chain-test': M.tryGetArg(args, 0, true),
  115. // 'group-by': B.ammp('chainKey')
  116. // })),
  117. ],
  118. [
  119. 'Modifiers',
  120. Alias(MolScript.structureQuery.modifier.queryEach, 'sel.atom.query-each'),
  121. Alias(MolScript.structureQuery.modifier.intersectBy, 'sel.atom.intersect-by'),
  122. Alias(MolScript.structureQuery.modifier.exceptBy, 'sel.atom.except-by'),
  123. Alias(MolScript.structureQuery.modifier.unionBy, 'sel.atom.union-by'),
  124. Alias(MolScript.structureQuery.modifier.union, 'sel.atom.union'),
  125. Alias(MolScript.structureQuery.modifier.cluster, 'sel.atom.cluster'),
  126. Alias(MolScript.structureQuery.modifier.includeSurroundings, 'sel.atom.include-surroundings'),
  127. Alias(MolScript.structureQuery.modifier.includeConnected, 'sel.atom.include-connected'),
  128. Alias(MolScript.structureQuery.modifier.expandProperty, 'sel.atom.expand-property'),
  129. // Macro(MSymbol('sel.atom.around', Arguments.Dictionary({
  130. // 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to the 1st atom of each chain.' })
  131. // }), Struct.Types.ElementSelection, 'A selection of singleton atom sets with centers within "radius" of the center of any atom in the given selection.'),
  132. // args => B.struct.modifier.exceptBy({
  133. // '0': B.struct.filter.within({
  134. // '0': B.struct.generator.atomGroups(), target: M.tryGetArg(args, 0), 'max-radius': M.tryGetArg(args, 'radius')
  135. // }),
  136. // by: M.tryGetArg(args, 0)
  137. // }))
  138. ],
  139. [
  140. 'Filters',
  141. Alias(MolScript.structureQuery.filter.pick, 'sel.atom.pick'),
  142. Alias(MolScript.structureQuery.filter.first, 'sel.atom.first'),
  143. Alias(MolScript.structureQuery.filter.withSameAtomProperties, 'sel.atom.with-same-atom-properties'),
  144. Alias(MolScript.structureQuery.filter.intersectedBy, 'sel.atom.intersected-by'),
  145. Alias(MolScript.structureQuery.filter.within, 'sel.atom.within'),
  146. Alias(MolScript.structureQuery.filter.isConnectedTo, 'sel.atom.is-connected-to'),
  147. ],
  148. [
  149. 'Combinators',
  150. Alias(MolScript.structureQuery.combinator.intersect, 'sel.atom.intersect'),
  151. Alias(MolScript.structureQuery.combinator.merge, 'sel.atom.merge'),
  152. Alias(MolScript.structureQuery.combinator.distanceCluster, 'sel.atom.dist-cluster'),
  153. ],
  154. [
  155. 'Atom Set Properties',
  156. Alias(MolScript.structureQuery.atomSet.atomCount, 'atom.set.atom-count'),
  157. Alias(MolScript.structureQuery.atomSet.countQuery, 'atom.set.count-query'),
  158. Alias(MolScript.structureQuery.atomSet.reduce, 'atom.set.reduce'),
  159. Alias(MolScript.structureQuery.atomSet.propertySet, 'atom.set.property'),
  160. // Macro(MSymbol('atom.set.max', Arguments.Dictionary({
  161. // 0: Argument(Type.Num, { description: 'Numeric atom property.'})
  162. // }), Type.Num, 'Maximum of the given property in the current atom set.'),
  163. // args => M.aggregate(M.tryGetArg(args, 0), B.core.math.max)),
  164. // Macro(MSymbol('atom.set.sum', Arguments.Dictionary({
  165. // 0: Argument(Type.Num, { description: 'Numeric atom property.'})
  166. // }), Type.Num, 'Sum of the given property in the current atom set.'),
  167. // args => M.aggregate(M.tryGetArg(args, 0), B.core.math.add, 0)),
  168. // Macro(MSymbol('atom.set.avg', Arguments.Dictionary({
  169. // 0: Argument(Type.Num, { description: 'Numeric atom property.'})
  170. // }), Type.Num, 'Average of the given property in the current atom set.'),
  171. // args => B.core.math.div([ M.aggregate(M.tryGetArg(args, 0), B.core.math.add, 0), B.struct.atomSet.atomCount() ])),
  172. // Macro(MSymbol('atom.set.min', Arguments.Dictionary({
  173. // 0: Argument(Type.Num, { description: 'Numeric atom property.'})
  174. // }), Type.Num, 'Minimum of the given property in the current atom set.'),
  175. // args => M.aggregate(M.tryGetArg(args, 0), B.core.math.min))
  176. ],
  177. [
  178. 'Atom Properties',
  179. Alias(MolScript.structureQuery.atomProperty.core.elementSymbol, 'atom.el'),
  180. Alias(MolScript.structureQuery.atomProperty.core.vdw, 'atom.vdw'),
  181. Alias(MolScript.structureQuery.atomProperty.core.mass, 'atom.mass'),
  182. Alias(MolScript.structureQuery.atomProperty.core.atomicNumber, 'atom.atomic-number'),
  183. Alias(MolScript.structureQuery.atomProperty.core.x, 'atom.x'),
  184. Alias(MolScript.structureQuery.atomProperty.core.y, 'atom.y'),
  185. Alias(MolScript.structureQuery.atomProperty.core.z, 'atom.z'),
  186. Alias(MolScript.structureQuery.atomProperty.core.sourceIndex, 'atom.src-index'),
  187. Alias(MolScript.structureQuery.atomProperty.core.operatorName, 'atom.op-name'),
  188. Alias(MolScript.structureQuery.atomProperty.core.modelIndex, 'atom.model-index'),
  189. Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'),
  190. Alias(MolScript.structureQuery.atomProperty.core.atomKey, 'atom.key'),
  191. Alias(MolScript.structureQuery.atomProperty.core.bondCount, 'atom.bond-count'),
  192. Alias(MolScript.structureQuery.atomProperty.topology.connectedComponentKey, 'atom.key.molecule'),
  193. Alias(MolScript.structureQuery.atomProperty.macromolecular.authResidueId, 'atom.auth-resid'),
  194. Alias(MolScript.structureQuery.atomProperty.macromolecular.labelResidueId, 'atom.label-resid'),
  195. Alias(MolScript.structureQuery.atomProperty.macromolecular.residueKey, 'atom.key.res'),
  196. Alias(MolScript.structureQuery.atomProperty.macromolecular.chainKey, 'atom.key.chain'),
  197. Alias(MolScript.structureQuery.atomProperty.macromolecular.entityKey, 'atom.key.entity'),
  198. Alias(MolScript.structureQuery.atomProperty.macromolecular.isHet, 'atom.is-het'),
  199. Alias(MolScript.structureQuery.atomProperty.macromolecular.id, 'atom.id'),
  200. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_atom_id, 'atom.label_atom_id'),
  201. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_alt_id, 'atom.label_alt_id', 'atom.altloc'),
  202. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_comp_id, 'atom.label_comp_id'),
  203. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_asym_id, 'atom.label_asym_id'),
  204. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_entity_id, 'atom.label_entity_id'),
  205. Alias(MolScript.structureQuery.atomProperty.macromolecular.label_seq_id, 'atom.label_seq_id'),
  206. Alias(MolScript.structureQuery.atomProperty.macromolecular.auth_atom_id, 'atom.auth_atom_id', 'atom.name'),
  207. Alias(MolScript.structureQuery.atomProperty.macromolecular.auth_comp_id, 'atom.auth_comp_id', 'atom.resname'),
  208. Alias(MolScript.structureQuery.atomProperty.macromolecular.auth_asym_id, 'atom.auth_asym_id', 'atom.chain'),
  209. Alias(MolScript.structureQuery.atomProperty.macromolecular.auth_seq_id, 'atom.auth_seq_id', 'atom.resno'),
  210. Alias(MolScript.structureQuery.atomProperty.macromolecular.pdbx_PDB_ins_code, 'atom.pdbx_PDB_ins_code', 'atom.inscode'),
  211. Alias(MolScript.structureQuery.atomProperty.macromolecular.pdbx_formal_charge, 'atom.pdbx_formal_charge'),
  212. Alias(MolScript.structureQuery.atomProperty.macromolecular.occupancy, 'atom.occupancy'),
  213. Alias(MolScript.structureQuery.atomProperty.macromolecular.B_iso_or_equiv, 'atom.B_iso_or_equiv', 'atom.bfactor'),
  214. Alias(MolScript.structureQuery.atomProperty.macromolecular.entityType, 'atom.entity-type'),
  215. Alias(MolScript.structureQuery.atomProperty.macromolecular.secondaryStructureKey, 'atom.key.sec-struct'),
  216. Alias(MolScript.structureQuery.atomProperty.macromolecular.isModified, 'atom.is-modified'),
  217. Alias(MolScript.structureQuery.atomProperty.macromolecular.modifiedParentName, 'atom.modified-parent'),
  218. // Macro(MSymbol('atom.sec-struct.is', Arguments.List(Struct.Types.SecondaryStructureFlag), Type.Bool,
  219. // `Test if the current atom is part of an secondary structure. Optionally specify allowed sec. struct. types: ${Type.oneOfValues(Struct.Types.SecondaryStructureFlag).join(', ')}`),
  220. // args => B.core.flags.hasAny([B.struct.atomProperty.macromolecular.secondaryStructureFlags(), B.struct.type.secondaryStructureFlags(args)])),
  221. ],
  222. [
  223. 'Bond Properties',
  224. Alias(MolScript.structureQuery.bondProperty.order, 'bond.order'),
  225. // Macro(MSymbol('bond.is', Arguments.List(Struct.Types.BondFlag), Type.Bool,
  226. // `Test if the current bond has at least one (or all if partial = false) of the specified flags: ${Type.oneOfValues(Struct.Types.BondFlag).join(', ')}`),
  227. // args => B.core.flags.hasAny([B.struct.bondProperty.flags(), B.struct.type.bondFlags(M.getPositionalArgs(args))])),
  228. ]
  229. ]
  230. ];
  231. const list: MolScriptSymbol[] = [];
  232. function makeList(xs: any[]) {
  233. for (const x of xs) {
  234. if (isMolScriptSymbol(x)) list.push(x);
  235. else if (x instanceof Array) makeList(x);
  236. }
  237. }
  238. makeList(SymbolTable);
  239. const normalized = (function () {
  240. const symbolList: [string, MolScriptSymbol][] = [];
  241. const symbolMap: { [id: string]: MolScriptSymbol | undefined } = Object.create(null);
  242. const namedArgs = UniqueArray.create<string, string>();
  243. const constants = UniqueArray.create<string, string>();
  244. for (const s of list) {
  245. for (const a of s.aliases) {
  246. symbolList.push([a, s]);
  247. if (symbolMap[a]) throw new Error(`Alias '${a}' already in use.`);
  248. symbolMap[a] = s;
  249. }
  250. const args = s.symbol.args;
  251. if (args.kind !== 'dictionary') {
  252. if (args.type.kind === 'oneof') {
  253. Type.oneOfValues(args.type).forEach(v => UniqueArray.add(constants, v, v));
  254. }
  255. continue;
  256. }
  257. for (const a of Object.keys(args.map)) {
  258. if (isNaN(a as any)) UniqueArray.add(namedArgs, a, a);
  259. const arg = ((args.map as any)[a]) as Argument;
  260. if (arg.type.kind === 'oneof') {
  261. Type.oneOfValues(arg.type).forEach(v => UniqueArray.add(constants, v, v));
  262. }
  263. }
  264. }
  265. return { symbolList, symbolMap, namedArgs: namedArgs.array, constants: constants.array }
  266. })();
  267. export const MolScriptSymbols = list;
  268. export const Constants = normalized.constants;
  269. export const NamedArgs = normalized.namedArgs;
  270. export const SymbolMap = normalized.symbolMap;
  271. export const SymbolList = normalized.symbolList;
  272. function substSymbols(expr: Expression): Expression {
  273. if (Expression.isLiteral(expr)) {
  274. return expr;
  275. }
  276. if (Expression.isSymbol(expr)) {
  277. if (!SymbolMap[expr.name]) return expr;
  278. return Expression.Symbol(SymbolMap[expr.name]!.symbol.id);
  279. }
  280. const head = substSymbols(expr.head);
  281. const headChanged = head !== expr.head;
  282. if (!expr.args) {
  283. return headChanged ? Expression.Apply(head) : expr;
  284. }
  285. let argsChanged = false;
  286. if (Expression.isArgumentsArray(expr.args)) {
  287. let newArgs: Expression[] = [];
  288. for (let i = 0, _i = expr.args.length; i < _i; i++) {
  289. const oldArg = expr.args[i];
  290. const newArg = substSymbols(oldArg);
  291. if (oldArg !== newArg) argsChanged = true;
  292. newArgs[newArgs.length] = newArg;
  293. }
  294. if (!argsChanged) newArgs = expr.args;
  295. if (!headChanged && !argsChanged) return expr;
  296. return Expression.Apply(head, newArgs);
  297. } else {
  298. let newArgs: any = {}
  299. for (const key of Object.keys(expr.args)) {
  300. const oldArg = expr.args[key];
  301. const newArg = substSymbols(oldArg);
  302. if (oldArg !== newArg) argsChanged = true;
  303. newArgs[key] = newArg;
  304. }
  305. if (!headChanged && !argsChanged) return expr;
  306. if (!argsChanged) newArgs = expr.args;
  307. return Expression.Apply(head, newArgs);
  308. }
  309. }
  310. export function transpileMolScript(expr: Expression) {
  311. return substSymbols(expr);
  312. }
  313. // const sortedSymbols = SymbolList.map(s => s[0]).sort((a, b) => {
  314. // if (a.length === b.length) return (a < b) as any;
  315. // return a.length - b.length;
  316. // });
  317. // export default [...sortedSymbols, ...NamedArgs.map(a => ':' + a), ...Constants];