# All files / src/semantics index.js

100% Statements 41/41
100% Branches 6/6
100% Functions 5/5
100% Lines 41/41

    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

    27x
    27x
    27x
    27x
    27x
    27x
    27x
    27x
    27x
    27x
    27x
    27x

    90x
    90x
    90x
    90x
    90x
    90x
    90x
    90x

    90x
    1x

    90x

    166x
    166x

    5x

    161x

    55x
    55x
    55x

    3x
    3x

    66x

    55x

    55x
    55x

    90x
    59x

    90x

    27x
    1x

     /**
     * 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 const async = (): Promise<(ast: NodeType) => NodeType> => {
      return Promise.resolve(semantics);
    };

    export default semantics;