All files / complex-js/compiler expressions.ts

0% Statements 0/67
0% Branches 0/14
0% Functions 0/27
0% Lines 0/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138                                                                                                                                                                                                                                                                                   
import from from '../functions/from';
import { IComplex, IComplexConstructor } from '../internal/complex';
import { Binary, binaryLookup } from './binary';
import { callLookup } from './call';
import { Unary, unaryLookup } from './unary';
 
export interface IContext<T extends IComplex> {
  [identifierName: string]: T;
}
 
export type Variable<T extends IComplex> = (context: IContext<T>) => T;
 
type Constant<T extends IComplex> = () => T;
 
export type Reviver<T extends IComplex, U extends any[]> = (...args: U) => IContext<T>;
 
function isConstant<T extends IComplex>(param: Variable<T>): param is Constant<T> {
  return param.length === 0;
}
 
function areConstant<T extends IComplex>(params: Variable<T>[]): params is Constant<T>[] {
  return params.every(isConstant);
}
 
export type Expression = <T extends IComplex>(Complex: IComplexConstructor<T>) => Variable<T>;
 
export const literal = (data: unknown[]): Expression => {
  const [numericLiteral] = data;
 
  return <T extends IComplex>(Complex: IComplexConstructor<T>): Constant<T> => {
    const z = from(Complex, Number(numericLiteral));
 
    return (): T => z;
  };
};
 
function isIndex<T>(key: string | number | symbol, lookup: T): key is keyof T {
  const o = Object(lookup);
  return key in o && o[key] !== undefined;
}
 
function throwInvalidIdentifierName(name: string): never {
  throw new ReferenceError(`${name} is not defined`);
}
 
export const identifier = (data: unknown[]): Expression => {
  const [identifierName] = data;
  const { text } = identifierName as { text: string };
 
  return <T extends IComplex>() => (context: IContext<T>): T => {
    if (!isIndex(text, context)) {
      throwInvalidIdentifierName(text);
    }
 
    return context[text];
  };
};
 
function throwInvalidCallExpression(name: string): never {
  throw new ReferenceError(`${name} is not a function`);
}
 
function concat<T, U>(t: T, u: U[]): [T, ...U[]] {
  return [t as T | U].concat(u) as [T, ...U[]];
}
 
export const call = (data: unknown[]): Expression => {
  const [identifierName, , expressions] = data;
  const { text } = identifierName as { text: string };
 
  if (!isIndex(text, callLookup)) {
    throwInvalidCallExpression(text);
  }
 
  return <T extends IComplex>(Complex: IComplexConstructor<T>) => {
    const fn = callLookup[text] as (Complex: IComplexConstructor<T>, ...args: T[]) => T;
    const variables = (expressions as Expression[]).map(expression => expression(Complex));
 
    if (areConstant(variables)) {
      const args = concat(
        Complex,
        variables.map(
          constant => constant()
        )
      );
      const z = fn(...args);
 
      return () => z;
    }
 
    return (context: IContext<T>) => {
      const args = concat(
        Complex,
        variables.map(
          variable => variable(context)
        )
      );
 
      return fn(...args);
    };
  };
};
 
export const unary = (data: unknown[]): Expression => {
  const [punctuator, , expression] = data;
 
  return Complex => {
    const fn = unaryLookup[punctuator as keyof Unary];
    const variable = (expression as Expression)(Complex);
 
    if (isConstant(variable)) {
      const z = fn(Complex, variable());
 
      return () => z;
    }
 
    return context => fn(Complex, variable(context));
  };
};
 
export const binary = (data: unknown[]): Expression => {
  const [lhsExpression, , punctuator, , rhsExpression] = data;
 
  return Complex => {
    const fn = binaryLookup[punctuator as keyof Binary];
    const lhs = (lhsExpression as Expression)(Complex);
    const rhs = (rhsExpression as Expression)(Complex);
 
    if (isConstant(lhs) && isConstant(rhs)) {
      const z = fn(Complex, lhs(), rhs());
 
      return () => z;
    }
 
    return context => fn(Complex, lhs(context), rhs(context));
  };
};