// @flow

import type { ASTNode } from '../src/parser.js';
import {
  throwEmptyListError,
  throwEmptyApplicationError,
} from './errors';

const firstChildType =
  (node: ASTNode): string => node.children[0].body.type;

const isApplication =
  (node: ASTNode): boolean => firstChildType(node) !== 'VAR_DEC' && firstChildType(node) !== 'LAMBDA_DEC';

export const toJS = (node: ASTNode): ?string => {
  switch (node.body.type) {
  case 'PROGRAM':
    return node.children.map(toJS).join(';\n') + ';';
  case 'VAR_DEC':
    return `const ${node.body.name} = ${node.children.map(toJS).join('')}`;
  case 'LAMBDA_DEC':
    return `(${node.body.input} => ${node.children.map(toJS).join('')})`;
  case 'ATOM':
    return node.body.name;
  case 'LIST':
    if (node.children.length === 0) {
      throwEmptyListError();
    }
    if (isApplication(node)) {
      if (node.children.length < 2) {
        throwEmptyApplicationError();
      }
      const namedLambdaNode = node.children[0];
      const lambdaArgumentNode = node.children[1];
      return `${toJS(namedLambdaNode)}(${toJS(lambdaArgumentNode)})`;
    }
    return node.children.map(toJS).join('');
  }
};

@@ -1156,8 +1156,918 @@
 %0A  %7D%0A%7D;%0A
+%0Aconst varMap = %7B%7D;%0Aexport const toPointFreeJS = (node: ASTNode): ?string =%3E %7B%0A  switch (node.body.type) %7B%0A  case 'PROGRAM':%0A    return node.children.map(toPointFreeJS).join('');%0A  case 'VAR_DEC':%0A    varMap%5Bnode.body.name%5D = node.children.map(toPointFreeJS).join('');%0A    return%0A  case 'LAMBDA_DEC':%0A    return %60($%7Bnode.body.input%7D =%3E $%7Bnode.children.map(toPointFreeJS).join('')%7D)%60;%0A  case 'ATOM':%0A    return varMap%5Bnode.body.name%5D %7C%7C node.body.name;%0A  case 'LIST':%0A    if (node.children.length === 0) %7B%0A      throwEmptyListError();%0A    %7D%0A    if (isApplication(node)) %7B%0A      if (node.children.length %3C 2) %7B%0A        throwEmptyApplicationError();%0A      %7D%0A      const namedLambdaNode = node.children%5B0%5D;%0A      const lambdaArgumentNode = node.children%5B1%5D;%0A      return %60$%7BtoPointFreeJS(namedLambdaNode)%7D($%7BtoPointFreeJS(lambdaArgumentNode)%7D)%60;%0A    %7D%0A    return node.children.map(toPointFreeJS).join('');%0A  %7D%0A%7D;%0A%0A

implement compilers to point free javascript