David Sehnal 6 jaren geleden
bovenliggende
commit
d1fe97f753

+ 5 - 5
src/mol-script/expression.ts

@@ -11,18 +11,18 @@ type Expression =
 
 namespace Expression {
     export type Literal = string | number | boolean
-    export type Symbol = { kind: 'symbol', name: string }
+    export type Symbol = { name: string }
     export type Arguments = Expression[] | { [name: string]: Expression }
     export interface Apply { readonly head: Expression, readonly args?: Arguments }
 
-    export function Symbol(name: string): Symbol { return { kind: 'symbol', name }; }
+    export function Symbol(name: string): Symbol { return { name }; }
     export function Apply(head: Expression, args?: Arguments): Apply { return args ? { head, args } : { head }; }
 
-    export function isArgumentsArray(e: Arguments): e is Expression[] { return e instanceof Array; }
-    export function isArgumentsMap(e: Arguments): e is { [name: string]: Expression } { return !(e instanceof Array); }
+    export function isArgumentsArray(e?: Arguments): e is Expression[] { return !!e && Array.isArray(e); }
+    export function isArgumentsMap(e?: Arguments): e is { [name: string]: Expression } { return !!e && !Array.isArray(e); }
     export function isLiteral(e: Expression): e is Expression.Literal { return !isApply(e); }
     export function isApply(e: Expression): e is Expression.Apply { return !!e && !!(e as Expression.Apply).head && typeof e === 'object'; }
-    export function isSymbol(e: Expression): e is Expression.Symbol { return !!e && (e as any).kind === 'symbol' }
+    export function isSymbol(e: Expression): e is Expression.Symbol { return !!e && typeof (e as any).name === 'string' }
 }
 
 export default Expression

+ 4 - 2
src/mol-script/runtime/environment.ts

@@ -1,14 +1,16 @@
-/*
+/**
  * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
 import { SymbolRuntimeTable } from './symbol'
+import { Macro } from './macro';
 
 interface Environment<T = any> {
     readonly symbolTable: SymbolRuntimeTable,
-    readonly context: T
+    readonly context: T,
+    readonly macros: Macro.Table
 }
 
 export default Environment

+ 1 - 1
src/mol-script/runtime/expression.ts

@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>

+ 81 - 0
src/mol-script/runtime/macro.ts

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import Expression from '../expression';
+
+interface Macro {
+    readonly argNames: ReadonlyArray<string>,
+    readonly argIndex: { [name: string]: number },
+    readonly expression: Expression
+}
+
+namespace Macro {
+    export type Table = Map<string, Macro>
+
+    function subst(table: Table, expr: Expression, argIndex: { [name: string]: number }, args: ArrayLike<Expression>): Expression {
+        if (Expression.isLiteral(expr)) return expr;
+        if (Expression.isSymbol(expr)) {
+            const idx = argIndex[expr.name];
+            if (typeof idx !== 'undefined') return args[idx];
+
+            if (table.has(expr.name)) {
+                const macro = table.get(expr.name)!;
+                if (macro.argNames.length === 0) return macro.expression;
+            }
+
+            return expr;
+        }
+
+        const head = subst(table, expr.head, argIndex, args);
+        const headChanged = head === expr.head;
+        if (!expr.args) {
+            return headChanged ? Expression.Apply(head) : expr;
+        }
+
+        let argsChanged = false;
+
+        if (Expression.isArgumentsArray(expr.args)) {
+            let newArgs: Expression[] = [];
+            for (let i = 0, _i = expr.args.length; i < _i; i++) {
+                const oldArg = expr.args[i];
+                const newArg = subst(table, oldArg, argIndex, args);
+                if (oldArg !== newArg) argsChanged = true;
+                newArgs[newArgs.length] = newArg;
+            }
+            if (!argsChanged) newArgs = expr.args;
+
+            if (Expression.isSymbol(head) && table.has(head.name)) {
+                const macro = table.get(head.name)!;
+                if (macro.argNames.length === newArgs.length) {
+                    return subst(table, macro.expression, macro.argIndex, newArgs);
+                }
+            }
+
+            if (!headChanged && !argsChanged) return expr;
+            return Expression.Apply(head, newArgs);
+        } else {
+
+            let newArgs: any = {}
+            for (const key of Object.keys(expr.args)) {
+                const oldArg = expr.args[key];
+                const newArg = subst(table, oldArg, argIndex, args);
+                if (oldArg !== newArg) argsChanged = true;
+                newArgs[key] = newArg;
+            }
+            if (!headChanged && !argsChanged) return expr;
+            if (!argsChanged) newArgs = expr.args;
+
+            return Expression.Apply(head, newArgs);
+        }
+    }
+
+    export function substitute(table: Table, macro: Macro, args: ArrayLike<Expression>) {
+        if (args.length === 0) return macro.expression;
+        return subst(table, macro.expression, macro.argIndex, args);
+    }
+}
+
+export { Macro }

+ 2 - 2
src/mol-script/runtime/symbol.ts

@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -20,7 +20,7 @@ namespace SymbolRuntime {
     export interface Attributes { isStatic: boolean }
 }
 
-function SymbolRuntime(symbol: Symbol, attributes: Partial<SymbolRuntime.Attributes> = {}) {
+function SymbolRuntime(attributes: Partial<SymbolRuntime.Attributes> = {}) {
     const { isStatic = false } = attributes;
     return (runtime: SymbolRuntime): SymbolRuntime.Info => {
         return ({ runtime, attributes: { isStatic } });