# All files / src/semantics index.js

100% Statements 39/39
100% Branches 6/6
100% Functions 4/4
100% Lines 39/39

    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

    23x
    23x
    23x
    23x
    23x
    23x
    23x
    23x
    23x
    23x
    23x
    23x

    83x
    83x
    83x
    83x
    83x
    83x
    83x
    83x

    83x
    1x

    83x

    132x
    132x

    3x

    129x

    37x
    37x
    37x

    1x
    1x

    48x

    37x

    37x
    37x

    83x
    41x

    83x

     /**
     * Semantic Analysis
     *
     * The semantic analyzer below accepts a Walt AST and maps it, returning a new
     * transformed AST which contains all necessary data to generate the final
     * WebAssembly binary.
     *
     * The transformations may or may not create new nodes or attach metadata to
     * existing nodes.
     *
     * Metadata is information necessary to generate a valid binary, like type info.
     */

    // @flow
    import Syntax from "../Syntax";
    import mapNode from "../utils/map-node";
    import walkNode from "../utils/walk-node";
    import { mapImport } from "./map-import";
    import mapFunctionNode from "./map-function";
    import closureImports from "../closure-plugin/imports";
    import { parseGlobalDeclaration } from "./map-function/declaration";
    import mapStructNode from "./map-struct";
    import mapCharacterLiteral from "./map-char";
    import { mapGeneric } from "./map-generic";
    import hasNode from "../utils/has-node";
    import { AST_METADATA } from "./metadata";
    import type { NodeType } from "../flow/types";

    function semantics(ast: NodeType): NodeType {
      const functions: { [string]: NodeType } = {};
      const globals: { [string]: NodeType } = {};
      const types: { [string]: NodeType } = {};
      const userTypes: { [string]: NodeType } = {};
      const table: { [string]: NodeType } = {};
      const hoist: NodeType[] = [];
      const hoistImports: NodeType[] = [];
      const statics: { [string]: null } = {};

      if (hasNode(Syntax.Closure, ast)) {
        ast = { ...ast, params: [...closureImports(), ...ast.params] };
      }
      // Types have to be pre-parsed before the rest of the program
      const astWithTypes = mapNode({
        [Syntax.Export]: (node, transform) => {
          const [maybeType] = node.params;
          if (
            maybeType != null &&
            [Syntax.Typedef, Syntax.Struct].includes(maybeType.Type)
          ) {
            return transform({
              ...maybeType,
              meta: {
                ...maybeType.meta,
                EXPORTED: true,
              },
            });
          }
          return node;
        },
        [Syntax.Typedef]: (node, _) => {
          let argumentsCount = 0;
          const defaultArgs = [];
          walkNode({
            Assignment(assignment) {
              const defaultValue = assignment.params[1];
              defaultArgs.push(defaultValue);
            },
            Type() {
              argumentsCount += 1;
            },
          })(node);
          const parsed = {
            ...node,
            meta: {
              ...node.meta,
              FUNCTION_METADATA: {
                argumentsCount,
              },
              DEFAULT_ARGUMENTS: defaultArgs,
            },
          };
          types[node.value] = parsed;
          return parsed;
        },
        [Syntax.GenericType]: mapGeneric({ types }),
      })(ast);

      const patched = mapNode({
        [Syntax.Typedef]: (_, __) => _,
        // Read Import node, attach indexes if non-scalar
        [Syntax.Import]: mapImport({ functions, types, globals }),
        [Syntax.Declaration]: parseGlobalDeclaration(false, { globals, types }),
        [Syntax.ImmutableDeclaration]: parseGlobalDeclaration(true, {
          globals,
          types,
        }),
        [Syntax.CharacterLiteral]: mapCharacterLiteral,
        [Syntax.Struct]: mapStructNode({ userTypes }),
        [Syntax.FunctionDeclaration]: mapFunctionNode({
          hoist,
          hoistImports,
          types,
          globals,
          functions,
          userTypes,
          table,
          statics,
        }),
      })(astWithTypes);

      return {
        ...patched,
        meta: {
          ...patched.meta,
          // Attach information collected to the AST
          [AST_METADATA]: { functions, globals, types, userTypes, statics },
        },
        params: [...hoistImports, ...patched.params, ...hoist],
      };
    }

    export default semantics;