text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.createNaN = exports.createLocalOrExportedOrGlobalDeclaration = exports.createHoistableVariableDeclarationStatement = exports.wrapInToStringForConcat = exports.wrapInTable = exports.createUnpackCall = exports.createImmediatelyInvokedFunctionExpression = exports.getNumberLiteralValue = exports.addToNumericExpression = exports.createExportsIdentifier = exports.createSelfIdentifier = exports.unwrapVisitorResult = void 0;
  const ts = require("typescript");
  const CompilerOptions_1 = require("../../CompilerOptions");
  const lua = require("../../LuaAST");
  const utils_1 = require("../../utils");
  const export_1 = require("./export");
  const scope_1 = require("./scope");
  const lualib_1 = require("./lualib");
  const LuaLib_1 = require("../../LuaLib");
  function unwrapVisitorResult(result) {
      return result === undefined ? [] : utils_1.castArray(result);
  }
  exports.unwrapVisitorResult = unwrapVisitorResult;
  function createSelfIdentifier(tsOriginal) {
      return lua.createIdentifier("self", tsOriginal, undefined, "this");
  }
  exports.createSelfIdentifier = createSelfIdentifier;
  function createExportsIdentifier() {
      return lua.createIdentifier("____exports");
  }
  exports.createExportsIdentifier = createExportsIdentifier;
  function addToNumericExpression(expression, change) {
      if (change === 0)
          return expression;
      const literalValue = getNumberLiteralValue(expression);
      if (literalValue !== undefined) {
          const newNode = lua.createNumericLiteral(literalValue + change);
          lua.setNodePosition(newNode, expression);
          return newNode;
      }
      if (lua.isBinaryExpression(expression)) {
          if (lua.isNumericLiteral(expression.right) &&
              ((expression.operator === lua.SyntaxKind.SubtractionOperator && expression.right.value === change) ||
                  (expression.operator === lua.SyntaxKind.AdditionOperator && expression.right.value === -change))) {
              return expression.left;
          }
      }
      return change > 0
          ? lua.createBinaryExpression(expression, lua.createNumericLiteral(change), lua.SyntaxKind.AdditionOperator)
          : lua.createBinaryExpression(expression, lua.createNumericLiteral(-change), lua.SyntaxKind.SubtractionOperator);
  }
  exports.addToNumericExpression = addToNumericExpression;
  function getNumberLiteralValue(expression) {
      if (!expression)
          return undefined;
      if (lua.isNumericLiteral(expression))
          return expression.value;
      if (lua.isUnaryExpression(expression) &&
          expression.operator === lua.SyntaxKind.NegationOperator &&
          lua.isNumericLiteral(expression.operand)) {
          return -expression.operand.value;
      }
      return undefined;
  }
  exports.getNumberLiteralValue = getNumberLiteralValue;
  // Prefer use of transformToImmediatelyInvokedFunctionExpression to maintain correct scope. If you use this directly,
  // ensure you push/pop a function scope appropriately to avoid incorrect vararg optimization.
  function createImmediatelyInvokedFunctionExpression(statements, result, tsOriginal) {
      const body = [...statements, lua.createReturnStatement(utils_1.castArray(result))];
      const flags = statements.length === 0 ? lua.FunctionExpressionFlags.Inline : lua.FunctionExpressionFlags.None;
      const iife = lua.createFunctionExpression(lua.createBlock(body), undefined, undefined, flags);
      return lua.createCallExpression(iife, [], tsOriginal);
  }
  exports.createImmediatelyInvokedFunctionExpression = createImmediatelyInvokedFunctionExpression;
  function createUnpackCall(context, expression, tsOriginal) {
      if (context.luaTarget === CompilerOptions_1.LuaTarget.Universal) {
          return lualib_1.transformLuaLibFunction(context, LuaLib_1.LuaLibFeature.Unpack, tsOriginal, expression);
      }
      const unpack = context.luaTarget === CompilerOptions_1.LuaTarget.Lua51 || context.luaTarget === CompilerOptions_1.LuaTarget.LuaJIT
          ? lua.createIdentifier("unpack")
          : lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("unpack"));
      return lua.createCallExpression(unpack, [expression], tsOriginal);
  }
  exports.createUnpackCall = createUnpackCall;
  function wrapInTable(...expressions) {
      const fields = expressions.map(e => lua.createTableFieldExpression(e));
      return lua.createTableExpression(fields);
  }
  exports.wrapInTable = wrapInTable;
  function wrapInToStringForConcat(expression) {
      if (lua.isStringLiteral(expression) ||
          lua.isNumericLiteral(expression) ||
          (lua.isBinaryExpression(expression) && expression.operator === lua.SyntaxKind.ConcatOperator)) {
          return expression;
      }
      return lua.createCallExpression(lua.createIdentifier("tostring"), [expression]);
  }
  exports.wrapInToStringForConcat = wrapInToStringForConcat;
  function createHoistableVariableDeclarationStatement(context, identifier, initializer, tsOriginal) {
      const declaration = lua.createVariableDeclarationStatement(identifier, initializer, tsOriginal);
      if (identifier.symbolId !== undefined) {
          const scope = scope_1.peekScope(context);
          utils_1.assert(scope.type !== scope_1.ScopeType.Switch);
          if (!scope.variableDeclarations) {
              scope.variableDeclarations = [];
          }
          scope.variableDeclarations.push(declaration);
      }
      return declaration;
  }
  exports.createHoistableVariableDeclarationStatement = createHoistableVariableDeclarationStatement;
  function hasMultipleReferences(scope, identifiers) {
      const scopeSymbols = scope.referencedSymbols;
      if (!scopeSymbols) {
          return false;
      }
      const referenceLists = utils_1.castArray(identifiers).map(i => i.symbolId && scopeSymbols.get(i.symbolId));
      return referenceLists.some(symbolRefs => symbolRefs && symbolRefs.length > 1);
  }
  function createLocalOrExportedOrGlobalDeclaration(context, lhs, rhs, tsOriginal, overrideExportScope) {
      let declaration;
      let assignment;
      const isFunctionDeclaration = tsOriginal !== undefined && ts.isFunctionDeclaration(tsOriginal);
      const identifiers = utils_1.castArray(lhs);
      if (identifiers.length === 0) {
          return [];
      }
      const exportScope = overrideExportScope !== null && overrideExportScope !== void 0 ? overrideExportScope : export_1.getIdentifierExportScope(context, identifiers[0]);
      if (exportScope) {
          // exported
          if (!rhs) {
              return [];
          }
          else {
              assignment = lua.createAssignmentStatement(identifiers.map(identifier => export_1.createExportedIdentifier(context, identifier, exportScope)), rhs, tsOriginal);
          }
      }
      else {
          const scope = scope_1.peekScope(context);
          const isTopLevelVariable = scope.type === scope_1.ScopeType.File;
          if (context.isModule || !isTopLevelVariable) {
              if (scope.type === scope_1.ScopeType.Switch || (!isFunctionDeclaration && hasMultipleReferences(scope, lhs))) {
                  // Split declaration and assignment of identifiers that reference themselves in their declaration
                  declaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal);
                  if (rhs) {
                      assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal);
                  }
              }
              else {
                  declaration = lua.createVariableDeclarationStatement(lhs, rhs, tsOriginal);
              }
              // Remember local variable declarations for hoisting later
              if (!scope.variableDeclarations) {
                  scope.variableDeclarations = [];
              }
              scope.variableDeclarations.push(declaration);
              if (scope.type === scope_1.ScopeType.Switch) {
                  declaration = undefined;
              }
          }
          else if (rhs) {
              // global
              assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal);
          }
          else {
              return [];
          }
      }
      if (isFunctionDeclaration) {
          // Remember function definitions for hoisting later
          const functionSymbolId = lhs.symbolId;
          const scope = scope_1.peekScope(context);
          if (functionSymbolId && scope.functionDefinitions) {
              const definitions = scope.functionDefinitions.get(functionSymbolId);
              if (definitions) {
                  definitions.definition = declaration !== null && declaration !== void 0 ? declaration : assignment;
              }
          }
      }
      if (declaration && assignment) {
          return [declaration, assignment];
      }
      else if (declaration) {
          return [declaration];
      }
      else if (assignment) {
          return [assignment];
      }
      else {
          return [];
      }
  }
  exports.createLocalOrExportedOrGlobalDeclaration = createLocalOrExportedOrGlobalDeclaration;
  const createNaN = (tsOriginal) => lua.createBinaryExpression(lua.createNumericLiteral(0), lua.createNumericLiteral(0), lua.SyntaxKind.DivisionOperator, tsOriginal);
  exports.createNaN = createNaN;
  //# sourceMappingURL=lua-ast.js.map