All files / src/core imports.js

100% Statements 29/29
100% Branches 8/8
100% Functions 6/6
100% Lines 29/29
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          23x 23x           23x 23x       90x 90x 90x 27x 27x   3x   3x   1x       2x   2x                               38x 38x   38x     25x 25x 25x                           25x 25x           13x 2x 2x 2x             11x 11x     13x              
/**
 * Imports Plugin
 *
 * @flow
 */
import Syntax from 'walt-syntax';
import {
  current,
  add,
  index as scopeIndex,
  namespace,
} from 'walt-parser-tools/scope';
import { mapNode } from 'walt-parser-tools/map-node';
import { FUNCTION_INDEX, TYPE_INDEX, TYPE_CONST } from '../semantics/metadata';
import type { SemanticPlugin } from '../flow/types';
 
export default function Imports(): SemanticPlugin {
  return {
    semantics: () => ({
      [Syntax.Import]: _next => args => {
        const [node, context] = args;
        return mapNode({
          [Syntax.BinaryExpression]: (as, transform) => {
            const [maybePair, asIdentifier] = as.params;
            // if the original import is not typed this isn't a valid import and is ignored
            if (maybePair.Type !== Syntax.Pair) {
              // No transform happens here (the transform is what creates the global fn to reference)
              return as;
            }
            // Continue transforming the import as before, the AS metadata will notify
            // the generator to ask for the original import.
            const [original, typeNode] = maybePair.params;
 
            return transform({
              ...maybePair,
              params: [
                {
                  ...asIdentifier,
                  meta: {
                    ...original.meta,
                    // <new-value> AS <original-value>
                    AS: original.value,
                  },
                },
                typeNode,
              ],
            });
          },
          [Syntax.Pair]: (pairNode, __) => {
            const { types, functions } = context;
            const [identifierNode, typeNode] = pairNode.params;
 
            if (types[typeNode.value] != null) {
              // crate a new type
 
              const functionIndex = Object.keys(functions).length;
              const typeIndex = Object.keys(types).indexOf(typeNode.value);
              const functionNode = {
                ...identifierNode,
                id: identifierNode.value,
                type: types[typeNode.value].type,
                meta: {
                  ...identifierNode.meta,
                  [FUNCTION_INDEX]: functionIndex,
                  [TYPE_INDEX]: typeIndex,
                  FUNCTION_METADATA:
                    types[typeNode.value].meta.FUNCTION_METADATA,
                  DEFAULT_ARGUMENTS:
                    types[typeNode.value].meta.DEFAULT_ARGUMENTS,
                },
              };
              functions[identifierNode.value] = functionNode;
              return {
                ...pairNode,
                params: [functionNode, types[typeNode.value]],
              };
            }
 
            if (!['Table', 'Memory'].includes(typeNode.value)) {
              const scope = current(context.scopes);
              const index = scopeIndex(scope, identifierNode.value);
              add(context.scopes, identifierNode.value, {
                ...identifierNode,
                meta: { [scope[namespace]]: index, [TYPE_CONST]: true },
                type: typeNode.type,
              });
            } else {
              const bucket =
                typeNode.value === 'Memory' ? 'memories' : 'tables';
              context[bucket].push(identifierNode);
            }
 
            return pairNode;
          },
        })(node);
      },
    }),
  };
}