# All files / src/parser operator.js

100% Statements 55/55
100% Branches 28/28
100% Functions 8/8
100% Lines 55/55

    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
    28x

    28x
    28x

    510x
    510x
    510x

    510x
    510x
    60x
    450x
    19x
    19x
    19x
    19x

    431x
    48x
    383x
    152x
    231x
    9x

    462x

    28x
    27x
    27x
    26x

    1x

    15x
    15x
    15x

    28x
    2x
    2x
    2x
    2x

    2x

    28x
    99x
    232x
    25x

    207x

    232x

    28x
    74x
    74x
    74x
    74x

    28x

    613x
    613x

    4x

    2x

    74x

    15x

    27x

    491x

     // @flow
    import Syntax from "../Syntax";
    import type Context from "./context";
    import closure from "./closure";
    import { subscriptFromNode } from "./array-subscript";
    import type { TokenType, NodeType } from "../flow/types";

    function binary(ctx: Context, op: TokenType, params: NodeType[]) {
      const node: NodeType = { ...params[0] };
      node.value = op.value;
      node.params = params;

      let Type = Syntax.BinaryExpression;
      if (node.value === "=") {
        Type = Syntax.Assignment;
      } else if (node.value === "-=" || node.value === "+=") {
        Type = Syntax.Assignment;
        const value = node.value[0];
        node.value = "=";
        node.params = [
          node.params[0],
          binary(ctx, { ...op, value }, [node.params[0], node.params[1]]),
        ];
      } else if (node.value === "[" || node.value === ".") {
        return subscriptFromNode(ctx, node);
      } else if (node.value === ":") {
        Type = Syntax.Pair;
      } else if (node.value === "||" || node.value === "&&") {
        Type = Syntax.Select;
      }

      return ctx.endNode(node, Type);
    }

    const unary = (ctx: Context, op: TokenType, params: NodeType[]): NodeType => {
      const [target] = params;
      if (op.value === "--") {
        return {
          ...target,
          Type: Syntax.UnaryExpression,
          value: "-",
          meta: {},
          params: [
            {
              ...target,
              value: "0",
              Type: Syntax.Constant,
              params: [],
              meta: {},
            },
            target,
          ],
        };
      }

      return {
        ...op,
        range: [op.start, target.range[1]],
        meta: {},
        Type: Syntax.Spread,
        params: [target],
      };
    };

    function objectLiteral(
      ctx: Context,
      op: TokenType,
      params: NodeType[]
    ): NodeType {
      const node = ctx.startNode(op);
      node.params = params;
      return ctx.endNode(node, Syntax.ObjectLiteral);
    }

    const ternary = (ctx: Context, op: TokenType, params: NodeType[]) => {
      const node = ctx.startNode(params[0]);
      node.params = params;
      node.value = op.value;
      node.type = params[params.length - 1].type;

      return ctx.endNode(node, Syntax.TernaryExpression);
    };

    const flattenSequence = (sequence: NodeType[]): NodeType[] => {
      return sequence.reduce((memo, node) => {
        if (node.Type === Syntax.Sequence) {
          memo.push.apply(memo, flattenSequence(node.params));
        } else {
          memo.push(node);
        }

        return memo;
      }, []);
    };

    // Sequence is a list of comma separated nodes. It's a slighlty special operator
    // in that it unrolls any other sequences into it's own params
    const sequence = (ctx: Context, op: TokenType, params: NodeType[]) => {
      const node = ctx.startNode(params[0]);
      node.value = op.value;
      node.params = flattenSequence(params);
      return ctx.endNode(node, Syntax.Sequence);
    };

    // Abstraction for handling operations
    const operator = (
      ctx: Context,
      operators: TokenType[],
      operands: NodeType[]
    ) => {
      const op = operators.pop();
      switch (op.value) {
        case "=>":
          return closure(ctx, op, operands);
        case "?":
          return ternary(ctx, op, operands.splice(-2));
        case ",":
          return sequence(ctx, op, operands.splice(-2));
        case "{":
          return objectLiteral(ctx, op, operands.splice(-1));
        case "--":
        case "...":
        case "sizeof":
          return unary(ctx, op, operands.splice(-1));
        default:
          return binary(ctx, op, operands.splice(-2));
      }
    };

    export default operator;