base.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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 { Expression } from '../../language/expression';
  7. import { QueryContext, QueryFn, Structure } from '../../../mol-model/structure';
  8. import { MSymbol } from '../../language/symbol';
  9. import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
  10. export class QueryRuntimeTable {
  11. private map = new Map<string, QuerySymbolRuntime>();
  12. removeSymbol(runtime: QuerySymbolRuntime) {
  13. this.map.delete(runtime.symbol.id);
  14. }
  15. addSymbol(runtime: QuerySymbolRuntime) {
  16. if (this.map.has(runtime.symbol.id)) {
  17. console.warn(`Symbol '${runtime.symbol.id}' already added. Call removeSymbol/removeCustomProps re-adding the symbol.`);
  18. }
  19. this.map.set(runtime.symbol.id, runtime);
  20. }
  21. addCustomProp(desc: CustomPropertyDescriptor<any>) {
  22. if (!desc.symbols) return;
  23. for (const k of Object.keys(desc.symbols)) {
  24. this.addSymbol((desc.symbols as any)[k]);
  25. }
  26. }
  27. removeCustomProp(desc: CustomPropertyDescriptor<any>) {
  28. if (!desc.symbols) return;
  29. for (const k of Object.keys(desc.symbols)) {
  30. this.removeSymbol((desc.symbols as any)[k]);
  31. }
  32. }
  33. getRuntime(id: string) {
  34. return this.map.get(id);
  35. }
  36. }
  37. export const DefaultQueryRuntimeTable = new QueryRuntimeTable();
  38. export class QueryCompilerCtx {
  39. constQueryContext: QueryContext = new QueryContext(Structure.Empty);
  40. constructor(public table: QueryRuntimeTable) {
  41. }
  42. }
  43. export type ConstQuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
  44. export type QuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
  45. export type QueryCompiledSymbolRuntime = { kind: 'const', value: any } | { kind: 'dynamic', runtime: QuerySymbolFn }
  46. export type CompiledQueryFn<T = any> = { isConst: boolean, fn: QueryFn }
  47. export namespace QueryCompiledSymbol {
  48. export function Const(value: any): QueryCompiledSymbolRuntime {
  49. return { kind: 'const', value };
  50. }
  51. export function Dynamic(runtime: QuerySymbolFn): QueryCompiledSymbolRuntime {
  52. return { kind: 'dynamic', runtime };
  53. }
  54. }
  55. export namespace CompiledQueryFn {
  56. export function Const(value: any): CompiledQueryFn {
  57. return { isConst: true, fn: function CompiledQueryFn_Const(ctx) { return value; } };
  58. }
  59. export function Dynamic(fn: QueryFn): CompiledQueryFn {
  60. return { isConst: false, fn };
  61. }
  62. }
  63. export interface QuerySymbolRuntime {
  64. symbol: MSymbol,
  65. compile(ctx: QueryCompilerCtx, args?: Expression.Arguments): CompiledQueryFn
  66. }
  67. export type QueryRuntimeArguments<S extends MSymbol> =
  68. { length?: number } & { [P in keyof S['args']['@type']]: QueryFn<S['args']['@type'][P]> }
  69. export namespace QueryRuntimeArguments {
  70. export function forEachEval<S extends MSymbol, Ctx>(xs: QueryRuntimeArguments<S>, queryCtx: QueryContext, f: (arg: any, i: number, ctx: Ctx) => void, ctx: Ctx): Ctx {
  71. if (typeof xs.length === 'number') {
  72. for (let i = 0, _i = xs.length; i < _i; i++) f((xs as any)[i](queryCtx), i, ctx);
  73. } else {
  74. let i = 0;
  75. for (const k of Object.keys(xs)) f((xs as any)[k](queryCtx), i++, ctx);
  76. }
  77. return ctx;
  78. }
  79. }
  80. export namespace QuerySymbolRuntime {
  81. export function Const<S extends MSymbol<any>>(symbol: S, fn: ConstQuerySymbolFn<S>): QuerySymbolRuntime {
  82. return new SymbolRuntimeImpl(symbol, fn, true);
  83. }
  84. export function Dynamic<S extends MSymbol<any>>(symbol: S, fn: QuerySymbolFn<S>): QuerySymbolRuntime {
  85. return new SymbolRuntimeImpl(symbol, fn, false);
  86. }
  87. }
  88. class SymbolRuntimeImpl<S extends MSymbol> implements QuerySymbolRuntime {
  89. compile(ctx: QueryCompilerCtx, inputArgs?: Expression.Arguments): CompiledQueryFn {
  90. let args: any, constArgs = false;
  91. if (!inputArgs) {
  92. args = void 0;
  93. constArgs = true;
  94. } else if (Expression.isArgumentsArray(inputArgs)) {
  95. args = [];
  96. constArgs = true;
  97. for (const arg of inputArgs) {
  98. const compiled = _compile(ctx, arg);
  99. constArgs = constArgs && compiled.isConst;
  100. args.push(compiled.fn);
  101. }
  102. } else {
  103. args = Object.create(null);
  104. constArgs = true;
  105. for (const key of Object.keys(inputArgs)) {
  106. const compiled = _compile(ctx, inputArgs[key]);
  107. constArgs = constArgs && compiled.isConst;
  108. args[key] = compiled.fn;
  109. }
  110. }
  111. if (this.isConst) {
  112. if (this.isConst && constArgs) {
  113. return CompiledQueryFn.Const(this.fn(ctx.constQueryContext, args));
  114. }
  115. return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
  116. }
  117. return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
  118. }
  119. constructor(public symbol: S, private fn: QuerySymbolFn<S>, private isConst: boolean) {
  120. }
  121. }
  122. function createDynamicFn<S extends MSymbol>(fn: QuerySymbolFn<S>, args: any): QueryFn {
  123. return function DynamicFn(ctx) { return fn(ctx, args); };
  124. }
  125. function _compile(ctx: QueryCompilerCtx, expression: Expression): CompiledQueryFn {
  126. if (Expression.isLiteral(expression)) {
  127. return CompiledQueryFn.Const(expression);
  128. }
  129. if (Expression.isSymbol(expression)) {
  130. const runtime = ctx.table.getRuntime(expression.name);
  131. if (!runtime) return CompiledQueryFn.Const(expression.name);
  132. return runtime.compile(ctx);
  133. }
  134. if (!Expression.isSymbol(expression.head)) {
  135. throw new Error('Can only apply symbols.');
  136. }
  137. const compiler = ctx.table.getRuntime(expression.head.name);
  138. if (!compiler) {
  139. throw new Error(`Symbol '${expression.head.name}' is not implemented.`);
  140. }
  141. return compiler.compile(ctx, expression.args);
  142. }
  143. export function compile<T = any>(expression: Expression): QueryFn<T> {
  144. const ctx = new QueryCompilerCtx(DefaultQueryRuntimeTable);
  145. return _compile(ctx, expression).fn;
  146. }