All files / src/core unary.js

100% Statements 17/17
100% Branches 6/6
100% Functions 3/3
100% Lines 16/16
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          23x     23x           23x               90x   90x 90x 88x     174x 88x     1x 1x         1x 1x       86x 84x                       2x                                          
/**
 * Unary operator plugin.
 *
 * @flow
 */
import Syntax from 'walt-syntax';
import type { SemanticPlugin } from '../flow/types';
 
const shifts = {
  i64: 63,
  f64: 63,
  i32: 31,
  f32: 31,
};
const masks = {
  i64: '0xffffffffffff',
  f64: '0xffffffffffff',
  i32: '0xffffff',
  f32: '0xffffff',
};
// Unary expressions need to be patched so that the LHS type matches the RHS
export default function(): SemanticPlugin {
  return {
    semantics({ stmt }) {
      return {
        [Syntax.UnaryExpression]: _ignore => (args, transform) => {
          const [unaryNode, context] = args;
          // While it's counter-intuitive that an unary operation would have two operands
          // it is simpler to always parse them as pseudo-binary and then simplify them here.
          const [lhs, rhs] = unaryNode.params.map(p => transform([p, context]));
          switch (unaryNode.value) {
            // Transform bang
            case '!':
              const shift = shifts[lhs.type];
              return transform([
                stmt`(((${lhs} >> ${shift}) | ((~${lhs} + 1) >> ${shift})) + 1);`,
                context,
              ]);
            case '~':
              const mask = masks[transform([lhs, context]).type];
              return transform([stmt`(${lhs} ^ ${mask});`, context]);
            case '-':
              // Fold negation into a single opcode (a negative constant).
              // The parser _currently_ generates 0 - <const> node pairs instead
              if (rhs.Type === Syntax.Constant) {
                return {
                  ...rhs,
                  meta: {
                    ...rhs.meta,
                    // Hint for generator
                    SIGN: -1,
                  },
                };
              }
            // fallthrough
            // eslint-disable-next-line
            default:
              return transform([
                {
                  ...unaryNode,
                  type: rhs.type,
                  params: [
                    {
                      ...lhs,
                      type: rhs.type,
                    },
                    rhs,
                  ],
                  Type: Syntax.BinaryExpression,
                },
                context,
              ]);
          }
        },
      };
    },
  };
}