All files / src/core array.js

100% Statements 47/47
100% Branches 18/18
100% Functions 13/13
100% Lines 46/46
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          23x 23x 23x 23x 23x 23x     23x 23x     180x 209x       209x 199x     10x             10x       40x           40x           40x       40x       40x   40x     90x     90x 636x 636x 636x 550x         86x   90x 100x 100x   100x 77x     23x 23x 23x   23x         23x   90x 17x 17x 17x 17x   17x 1x     16x               90x    
/**
 * Array Plugin
 *
 * @flow
 */
import Syntax from 'walt-syntax';
import invariant from 'invariant';
import { find } from 'walt-parser-tools/scope';
import { extendNode } from '../utils/extend-node';
import withContext from '../utils/transform-with-context';
import pick from '../utils/pick';
import type { SemanticPlugin } from '../flow/types';
 
const shifts = { i32: 2, f32: 2, i64: 3, f64: 3 };
const NATIVE_ARRAY_TYPE = 'i32';
 
function semantics({ stmt }) {
  const declaration = next => args => {
    const [node, context] = args;
 
    // For every declaration of array types we will strip the declaration type
    // to a core type (i32) and attach the original type reference as metadata
    if (!String(node.type).endsWith('[]')) {
      return next(args);
    }
 
    const decl = extendNode(
      {
        type: NATIVE_ARRAY_TYPE,
        meta: { TYPE_ARRAY: node.type.slice(0, -2) },
      },
      node
    );
    return next([decl, context]);
  };
 
  function arrayOffset(base, offset) {
    const shift = shifts[base.meta.TYPE_ARRAY] || 2;
 
    // if (shift == null) {
    //   return null;
    // }
 
    return offset.Type !== Syntax.Constant || Number(offset.value)
      ? stmt`(${base} + (${offset} << ${shift}));`
      : stmt`(${base});`;
  }
 
  function sanityCheck(subscript) {
    return !(subscript.type == null || subscript.index == null);
  }
 
  function produceSubscript([base, offset]) {
    let type = base.meta.TYPE_ARRAY;
    // if (context.userTypes[type]) {
    //   type = 'i32';
    // }
    const index = arrayOffset(base, offset);
 
    return { type, index, TYPE_ARRAY: base.meta.TYPE_ARRAY };
  }
 
  return {
    [Syntax.Declaration]: declaration,
    [Syntax.ImmutableDeclaration]: declaration,
    [Syntax.Identifier]: next => args => {
      const [node, context] = args;
      const ref = find(context.scopes, node.value);
      if (!(ref && ref.meta.TYPE_ARRAY)) {
        return next(args);
      }
 
      // Before moving on to the core parser all identifiers need to have
      // concrete basic types
      return next([extendNode(pick(['type', 'meta'], ref), node), context]);
    },
    [Syntax.Assignment]: next => (args, t) => {
      const [node, context] = args;
      const [lhs, rhs] = node.params;
 
      if (lhs.Type !== Syntax.ArraySubscript) {
        return next(args);
      }
 
      const transform = withContext(t, context);
      const subscript = produceSubscript(lhs.params.map(transform));
      const { type, index } = subscript;
 
      invariant(
        sanityCheck(subscript),
        `PANIC - Cannot assign to subscript of ${lhs.value}`
      );
 
      return transform(stmt`${type}.store(${index}, ${rhs});`);
    },
    [Syntax.ArraySubscript]: next => (args, t) => {
      const [node, context] = args;
      const transform = withContext(t, context);
      const subscript = produceSubscript(node.params.map(transform));
      const { type, index, TYPE_ARRAY } = subscript;
 
      if (!sanityCheck(subscript)) {
        return next(args);
      }
 
      return extendNode(
        { meta: { TYPE_ARRAY } },
        transform(stmt`${type}.load(${index});`)
      );
    },
  };
}
export default function arrayPlugin(): SemanticPlugin {
  return { semantics };
}