All files / src/syntax-sugar default-arguments.js

100% Statements 26/26
100% Branches 4/4
100% Functions 5/5
100% Lines 26/26
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              23x   23x 23x       90x     90x 90x 173x 173x   173x   173x   2x 2x           173x                                                                   90x 706x 706x 706x   706x     706x 460x       246x 246x 246x 246x 2x                       244x            
/**
 * Default Arguments syntax sugar plugin.
 *
 * Converts FUNCTION CALLS with missing arguments to default values
 *
 * @flow
 */
import Syntax from 'walt-syntax';
// $FlowFixMe
import grammar from './default-arguments.ne';
import walkNode from 'walt-parser-tools/walk-node';
import type { SemanticPlugin, GrammarPlugin } from '../flow/types';
 
export default function(): SemanticPlugin & GrammarPlugin {
  return {
    grammar,
    semantics() {
      return {
        [Syntax.FunctionDeclaration]: next => args => {
          const [node, context] = args;
          const [argumentsNode] = node.params;
 
          const defaultArguments = [];
 
          walkNode({
            Assignment: defaultArg => {
              const [, value] = defaultArg.params;
              defaultArguments.push(value);
            },
          })(argumentsNode);
 
          // Attach any default arguments found to the function node directly,
          // proceed with the rest of the parsers
          return next([
            {
              ...node,
              meta: { ...node.meta, DEFAULT_ARGUMENTS: defaultArguments },
            },
            context,
          ]);
        },
        // There isn't a need to parse out the Assignment expressions as they are
        // not actually compiled/generated into the final binary
        // [Syntax.Assignment]: next => (args, transform) => {
        //   const [node, context] = args;
        //   // Not inside arguments
        //   const currentScope = current(context.scopes);
 
        //   // Arguments have not been set for scope yet and the current scope is
        //   // not global
        //   if (currentScope.arguments == null && context.scopes.length > 1) {
        //     return next(args);
        //   }
 
        //   // Assignment has higher precedence than ":" Pair expressions so the
        //   // children of this node will be [Pair(id:type), Constant(value)]
        //   // remove the constant return the pair.
        //   //
        //   // A helpful visual of a valid default argument syntax:
        //   //
        //   //      function fn(x : i32, y : i32, z : i32 = 0) { ... }
        //   const [pair] = node.params;
 
        //   // Short circuit the parsers since it does not make sense to process
        //   // assignment anymore. Instead parse the Pair, which is the argument.
        //   return transform([pair, context]);
        // },
        [Syntax.FunctionCall]: next => args => {
          const [call, context] = args;
          const { functions } = context;
          const [id, ...fnArgs] = call.params;
 
          const target = functions[id.value];
 
          // Most likely a built-in function
          if (!target) {
            return next(args);
          }
 
          const expectedArguments =
            target.meta.FUNCTION_METADATA.argumentsCount;
          const count = fnArgs.length;
          const difference = expectedArguments - count;
          if (difference > 0) {
            return next([
              {
                ...call,
                params: [
                  ...call.params,
                  ...target.meta.DEFAULT_ARGUMENTS.slice(difference - 1),
                ],
              },
              context,
            ]);
          }
 
          return next(args);
        },
      };
    },
  };
}