compiler.ts 5.0 KB

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