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

100% Statements 30/30
100% Branches 10/10
100% Functions 7/7
100% Lines 30/30

    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
    8723x
    23x

    87x

    87x
    87x
    179x
    179x

    179x

    179x

    2x
    2x

    179x

    87x
    84x

    84x
    82x

    2x

    2x

    87x
    196x
    196x
    196x

    196x
    23x

    173x

    173x

    173x
    173x
    3x

    170x

     import Syntax from '../Syntax';
    import walkNode from '../utils/walk-node';

    export default function() {
      return {
        semantics() {
          return {
            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 direclty,
              // proceed with the rest of the parsers
              return next([
                {
                  ...node,
                  meta: { ...node.meta, DEFAULT_ARGUMENTS: defaultArguments },
                },
                context,
              ]);
            },
            Assignment: next => (args, transform) => {
              const [node, context] = args;
              // Not inside arguments
              if (!context.isParsingArguments) {
                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 sence to process
              // assignment anymore. Instead parse the Pair, which is the argument.
              return transform([pair, context]);
            },
            FunctionCall: next => args => {
              const [call, context] = args;
              const { functions } = context;
              const target = functions[call.value];

              // Most likely a built-in funciton
              if (!target) {
                return next(args);
              }

              const expectedArguments =
                target.meta.FUNCTION_METADATA.argumentsCount;
              const count =
                call.params.length > 0 && call.params[0].Type === Syntax.Sequence
                  ? call.params[0].length
                  : call.params.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);
            },
          };
        },
      };
    }