function replaceDynamicBits(statements) {
        var isDynamic = false;

        statements.forEach((statement, i) => {
          const childNode = statement.expression;

          if (childNode.type === 'ArrayExpression') {
            let newAst = childNode.elements.map(e => t.expressionStatement(e));
            let _isDynamic = replaceDynamicBits(newAst);
            if (_isDynamic) { isDynamic = true; }
            childNode.elements = newAst.map(e => e.expression);

            return;
          }

          // The nodeName without `toLowerCase()` being called on it.
          const rawNodeName = childNode.properties.filter(property => {
            return property.key.value === 'rawNodeName';
          })[0].value;

          // Extract
          const identifierIsInScope = path.scope.hasBinding(rawNodeName.value);

          const nodeType = childNode.properties.filter(property => {
            return property.key.value === 'nodeType';
          })[0].value.value;

          const nodeValue = childNode.properties.filter(property => {
            return property.key.value === 'nodeValue';
          })[0].value;

          const attributes = childNode.properties.filter(property => {
            return property.key.value === 'attributes';
          })[0].value;

          const childNodes = childNode.properties.filter(property => {
            return property.key.value === 'childNodes';
          })[0].value;

          const args = [];

          // Remaps the supplemental values inline. This will strip out the
          // tokens and inject.
          attributes.properties.forEach((property, i, properties) => {
            const keysMatched = property.key.value.split(tokenEx);
            const valuesMatched = String(property.value.value).split(tokenEx);

            const keys = [];
            const values = [];

            let key;
            let value;

            // Build up the key.
            for (let i = 0; i < keysMatched.length; i++) {
              const match = keysMatched[i];

              if (i % 2 === 1) {
                keys.push(supplemental.attributes[match]);
              }
              else if (match) {
                keys.push(t.stringLiteral(match));
              }
            }

            key = keys.reduce((memo, curr) => {
              if (!memo) {
                return curr;
              }

              return t.binaryExpression('+', memo, curr);
            }, null);

            // Build up the value.
            for (let i = 0; i < valuesMatched.length; i++) {
              const match = valuesMatched[i];

              if (i % 2 === 1) {
                values.push(supplemental.attributes[match]);
              }
              else if (match) {
                values.push(t.stringLiteral(match));
              }
            }

            value = values.reduce((memo, curr) => {
              if (!memo) {
                return curr;
              }

              return t.binaryExpression('+', memo, curr);
            }, null);

            properties[i] = t.objectProperty(
              keys.length ? key : property.key,
              values.length ? value : property.value,
              Boolean(keys.length),
            );
          });

          // Real elements.
          if (nodeType === 1) {
            // Check childNodes.
            let expressions = childNodes.elements.map(
              c => t.expressionStatement(c)
            );

            // Replace the nested structures.
            let _isDynamic = replaceDynamicBits(expressions);

            // Only set if true.
            if (_isDynamic) {
              isDynamic = true;
            }

            const token = rawNodeName.value.match(tokenEx);

            args.push(createTreeNode, [
              identifierIsInScope ? t.identifier(rawNodeName.value) : (token ? supplemental.tags[token[1]] : rawNodeName),
              attributes,
              t.arrayExpression(expressions.map(expr => expr.expression)),
            ]);
          }
          // Text nodes.
          else if (nodeType === 3) {
            let value = nodeValue.value || '';

            if (value.match(tokenEx)) {
              const values = interpolateValues(value, supplemental, createTreeNode);

              if (values.elements.length === 1) {
                args.replacement = values.elements[0];
              }
              else {
                args.push(createTreeNode, [t.stringLiteral('#document-fragment'), values]);
              }

              isDynamic = true;
            }
            else {
              let id = path.scope.generateUidIdentifier('vtree');

              path.scope.parent.push({
                id,
                init: t.callExpression(createTreeNode, [
                  t.stringLiteral('#text'),
                  nodeValue,
                ]),
              });

              args.replacement = id;
            }
          }

          const callExpr = args.replacement || t.callExpression.apply(null, args);

          // TODO This will determine if the Node is embedded in a dynamic call
          // in which case it cannot be hoisted.
          const isTopLevelStatic = false;

          // Is a static node and never changes, so hoist createTree call.
          if (!isDynamic && isTopLevelStatic) {
            let id = path.scope.generateUidIdentifier('vtree');
            path.scope.parent.push({ id, init: callExpr });
            statements[i].expression = id;
          }
          else {
            statements[i].expression = callExpr;
          }
        });

        return isDynamic;
      }
Replace the dynamic parts of the AST with the actual quasi
expressions.

@param statements