[
  {
    "path": ".editorconfig",
    "content": "[*.lua]\nindent_style = tab\nindent_size = 4\nend_of_line = lf\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.lua text=auto\n*.lua eol=lf\n"
  },
  {
    "path": ".gitbook.yaml",
    "content": "root: ./doc/\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n\nIf your problem is a non-working obfuscated file, please also include a minimal source code example, your config file as well as the output file that you got.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/workflows/Build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches:\n      main\n\njobs:\n  build:\n    runs-on: windows-latest # gh-actions-lua doesn't work on windows\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@master\n      - name: Download srlua-mingw\n        run: curl -o srlua.zip \"https://raw.githubusercontent.com/joedf/LuaBuilds/gh-pages/hdata/srlua-5.1.5_Win32_bin.zip\"\n      - name: Unzip srlua-mingw\n        run: |\n          tar -xf srlua.zip\n          Rename-Item -Path srglue.exe -NewName glue.exe\n      - name: Download Lua53\n        run: curl -o Lua53.zip \"https://raw.githubusercontent.com/joedf/LuaBuilds/gh-pages/hdata/lua-5.3.5_Win64_bin.zip\"\n      - name: Unzip Lua53\n        run: |\n          tar -xf Lua53.zip\n      - run: dir\n      - name: Build the project\n        run: |\n          ./build.bat\n        shell: bash\n      - name: Zip the build\n        uses: papeloto/action-zip@v1\n        with:\n          files: build/\n          dest: build.zip\n      - name: Load version and name\n        run: | # I have no idea why but 5.1 just won't work for some reason\n          echo \"prometheus_full_version=$(./lua.exe ./src/config.lua --FullVersion)\" >> $GITHUB_ENV\n          echo \"prometheus_version=$(./lua.exe ./src/config.lua --Version)\" >> $GITHUB_ENV\n        shell: bash\n      - name: Upload binaries to release\n        uses: svenstaro/upload-release-action@v2\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: build.zip\n          asset_name: ${{ env.prometheus_full_version }}.zip\n          tag: release-${{ github.ref }}-${{ env.prometheus_version }}\n          release_name: ${{ env.prometheus_version }}\n          overwrite: true\n          body: ${{ env.prometheus_full_version }}  ${{ github.event.commits[0].message }}"
  },
  {
    "path": ".github/workflows/Test.yml",
    "content": "name: Test\non:  \n  push:\n  pull_request:\n    branches:\n      - master\n\njobs:\n  test-linux:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@master\n      - name: Install Lua\n        uses: leafo/gh-actions-lua@master\n        with:\n          luaVersion: 5.1\n      - name: Run test case\n        run: lua ./tests.lua --Linux --CI\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore Vscode Folder\n.vscode\n\n# Ignore Lua Implementation and srlua\nlua51.dll\nluajit.exe\nsrlua\nluajit\nbuildnotes.txt\nsrlua.exe\nglue.exe\nbuild\n\n# Ignore Local Test Files\ntest.lua\ntest.obfuscated.lua\nconfig.lua"
  },
  {
    "path": "LICENSE",
    "content": "PROMETHEUS LICENSE\nCopyright (c) 2025 Elias Oelschner\nhttps://github.com/prometheus-lua/Prometheus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\n  1. Retained Notice.\n     The above copyright notice and this permission notice shall be included\n     in all copies or substantial portions of the Software that are distributed\n     in source or object form, unless explicitly stated otherwise in this\n     license.\n\n  2. Attribution Requirement.\n     Any Product or Service that (a) includes, links to, wraps, depends on,\n     is derived from, uses, or otherwise incorporates the Software (including\n     via modification, obfuscation, binary linking, packaging, or embedding),\n     whether distributed, provided for use, or made available over a network\n     (collectively, \"Using Products/Services\"), must provide clear,\n     reasonably prominent attribution to Prometheus as follows:\n\n       \"Based on Prometheus by Elias Oelschner, https://github.com/prometheus-lua/Prometheus\"\n\n     This attribution must appear in at least one of the following locations,\n     as applicable and reasonably prominent for the type of Using Product/Service:\n\n       a) In-user interface: a visible \"About\", \"Credits\", or equivalent screen\n          within the product's primary user interface (graphical or web UI).\n       b) On the product web site: a visible place such as a footer, About page,\n          or documentation landing page that is accessible to the product's users.\n       c) In the tool itself: the program's --version, --help output, or similar\n          command-line/displayed metadata for CLI tools.\n       d) Documentation: in user or developer documentation, README, or product\n          manual that accompanies the product.\n\n     For Software provided as a Network Service or SaaS (i.e., made available\n     for use over a network rather than distributed as a copy), the attribution\n     must be visible on the public-facing web UI (for example on the login\n     screen, footer, About page, or a help/credits page accessible without an\n     account), and must include the exact phrase above and the URL.\n\n  3. Derivative Statement.\n     Any derivative work, modification, fork, or reimplementation of the\n     Software that is publicly distributed, publicly hosted, or provided as a\n     service must include a prominent statement in its README (or equivalent\n     public-facing documentation) that it is based on Prometheus and include the\n     exact attribution text in section 2.\n\n  4. Generated Output Exception.\n     Files or artifacts produced by running or using the Software (for example,\n     obfuscated output files, compiled artifacts, transformed source files,\n     or other generated outputs) are NOT required by this license to carry\n     copyright notices or license text. The attribution obligations in section 2\n     continue to apply to the Using Product/Service that produces, distributes,\n     or serves those outputs.\n\n  5. No Trademark License.\n     This license does not grant any rights to use the trade names, trademarks,\n     service marks, or product names of the Licensor except as required for the\n     textual attribution specified in section 2.\n\n  6. Compliance and Termination.\n     Any failure to comply with the obligations in sections 2 or 3 will\n     terminate the rights granted under this license for the non-compliant\n     party with respect to the Software. Rights may be reinstated if the\n     non-compliant party cures the breach within thirty (30) days after\n     receiving written notice from the Licensor and provides reasonable proof\n     of cure.\n\n  7. Sublicensing.\n     You may sublicense the Software, provided that any sublicensee is bound\n     by the terms of this license and the attribution obligations described\n     herein.\n\n  8. Warranty Disclaimer.\n     THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n  9. Limitation of Liability.\n     IN NO EVENT WILL THE COPYRIGHT HOLDER BE LIABLE FOR ANY INDIRECT,\n     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY\n     WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY\n     OF SUCH DAMAGE.\n\n  10. Governing Law.\n     This license shall be governed by and construed in accordance with the\n     laws of the jurisdiction chosen by the Licensor. This clause does not\n     limit any mandatory consumer protections that may apply in certain\n     jurisdictions.\n\nBy exercising any rights granted to you under this License, you accept and\nagree to be bound by the terms and conditions of this License.\n"
  },
  {
    "path": "benchmark.lua",
    "content": "print(\"PROMETHEUS Benchmark\")\nprint(\"Based On IronBrew Benchmark\")\nlocal Iterations = 100000\nprint(\"Iterations: \" .. tostring(Iterations))\n\nprint(\"CLOSURE testing.\")\nlocal Start = os.clock()\nlocal TStart = Start\nfor _ = 1, Iterations do\n\t(function()\n\t\tif not true then\n\t\t\tprint(\"Hey gamer.\")\n\t\tend\n\tend)()\nend\nprint(\"Time:\", os.clock() - Start .. \"s\")\n\nprint(\"SETTABLE testing.\")\nStart = os.clock()\nlocal T = {}\nfor Idx = 1, Iterations do\n\tT[tostring(Idx)] = \"EPIC GAMER \" .. tostring(Idx)\nend\n\nprint(\"Time:\", os.clock() - Start .. \"s\")\n\nprint(\"GETTABLE testing.\")\nStart = os.clock()\nfor Idx = 1, Iterations do\n\tT[1] = T[tostring(Idx)]\nend\n\nprint(\"Time:\", os.clock() - Start .. \"s\")\nprint(\"Total Time:\", os.clock() - TStart .. \"s\")\n"
  },
  {
    "path": "cli.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- cli.lua\n--\n-- This Script contains the Code for the Prometheus CLI\n\n-- Configure package.path for requiring Prometheus\nlocal function script_path()\n\tlocal str = debug.getinfo(2, \"S\").source:sub(2)\n\treturn str:match(\"(.*[/%\\\\])\") or \"\";\nend\npackage.path = script_path() .. \"?.lua;\" .. package.path;\nrequire(\"src.cli\");"
  },
  {
    "path": "doc/README.md",
    "content": "---\ndescription: Prometheus is an Lua Obfuscator, that is written in pure Lua.\n---\n\n# Prometheus\n\nPrometheus can obfuscate Lua51 as well as Roblox's LuaU, which is an optionally typed superset of Lua51.\n\nView Prometheus on [github](https://github.com/levno-710/Prometheus).\n\nThis Documentation only applies to the newest version of Prometheus.\n"
  },
  {
    "path": "doc/SUMMARY.md",
    "content": "# Table of contents\n\n* [Prometheus](README.md)\n\n## Getting Started\n\n* [Installation](getting-started/installation.md)\n* [Obfuscating your first script](getting-started/obfuscating-your-first-script.md)\n* [Command Line Options](getting-started/command-line-options.md)\n* [Presets](getting-started/presets.md)\n* [Writing a custom Config File](getting-started/writing-a-custom-config-file.md)\n* [The Config Object](getting-started/the-config-object.md)\n\n## Steps\n\n* [WrapInFunction](steps/wrapinfunction.md)\n* [Vmify](steps/vmify.md)\n* [SplitStrings](steps/splitstrings.md)\n* [ProxifyLocals](steps/proxifylocals.md)\n* [EncryptStrings](steps/encryptstrings.md)\n* [ConstantArray](steps/constantarray.md)\n* [AntiTamper](steps/anti-tamper.md)\n\n\n## advanced\n\n* [Using Prometheus in your Lua Application](advanced/using-prometheus-in-your-lua-application.md)\n"
  },
  {
    "path": "doc/advanced/using-prometheus-in-your-lua-application.md",
    "content": "# Using Prometheus in your Lua Application\n\nPrometheus can also be used as a library for your custom Lua Applications instead of using its cli tool.&#x20;\n\nIn order to do that you'll first need to clone the github repo:\n\n```batch\ngit clone \"https://github.com/levno-710/Prometheus.git\"\n```\n\nAfter that, you'll need to copy everything within the src folder to your project. Let's say you created a folder named `prometheus`, where all the Prometheus files are located. You can the use the following code to obfuscate a string:\n\n{% code title=\"use_prometheus.lua\" %}\n```lua\nlocal Prometheus = require(\"prometheus.prometheus\")\n\n-- If you don't want console output\nPrometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Error\n\n-- Your code\nlocal code = 'print(\"Hello, World!\")'\n\n-- Create a Pipeline using the Strong preset\nlocal pipeline = Prometheus.Pipeline:fromConfig(Prometheus.Presets.Strong)\n\n-- Apply the obfuscation and print the result\nprint(pipeline:apply(code));\n```\n{% endcode %}\n\nInstead of passing the Strong preset you could also pass a custom [Config Object](../getting-started/the-config-object.md).\n"
  },
  {
    "path": "doc/getting-started/command-line-options.md",
    "content": "# Command Line Options\n\nThe following table provides a brief overview over the command line options:\n\n| Option                        | Usage                                                       |\n| ----------------------------- | ----------------------------------------------------------- |\n| --preset \\[name]; --p \\[name] | Specify the config preset to be used; [Details](presets.md) |\n| --config \\[path]; --c \\[path] | Specify the path to a custom config file                    |\n| --out \\[path]; --o \\[path]    | Specify the path of the output file                         |\n| --nocolors                    | Disable ansi colors escape sequences                        |\n| --Lua51                       | Handle input as Lua 5.1                                     |\n| --LuaU                        | Handle input as LuaU                                        |\n| --pretty                      | Pretty print the output                                     |\n"
  },
  {
    "path": "doc/getting-started/installation.md",
    "content": "# Installation\n\nTo install Prometheus, simply clone the Github Repository using:\n\n```batch\ngit clone \"https://github.com/levno-710/Prometheus.git\"\n```\n\nAlternatively you can download the Sources [here](https://github.com/levno-710/Prometheus/archive/refs/heads/master.zip).\n\nPrometheus also Requires LuaJIT or Lua51 in order to work. The Lua51 binaries can be downloaded [here](https://sourceforge.net/projects/luabinaries/files/5.1.5/Tools%20Executables/).\n"
  },
  {
    "path": "doc/getting-started/obfuscating-your-first-script.md",
    "content": "# Obfuscating your first script\n\nNow that you have downloaded and Prometheus, you probably wonder how to use it. In this quick tutorial you are going to learn how to obfuscate your first file.\n\nNote that in the following command examples `lua` should be replaced by your lua implementation.\n\nCreate the following file within the Prometheus main directory that you just downloaded:\n\n{% code title=\"hello_world.lua\" %}\n```lua\nprint(\"Hello, World\")\n```\n{% endcode %}\n\nNow run the following command inside of the Prometheus directory:\n\n```batch\nlua ./cli.lua ./hello_world.lua\n```\n\nYou may notice, that the console output looks weird. If that is the case, your terminal does not support ansi color escape sequences. You should add the `--nocolors` option:\n\n```batch\nlua ./cli.lua --nocolors ./hello_world.lua\n```\n\nThis should create the following file:\n\n{% code title=\"hello_world.obfuscated.lua\" %}\n```lua\nprint(\"Hello, World\")\n```\n{% endcode %}\n\nAs you can see, the file hasn't changed at all. That is because by default prometheus is just a minifier and the code we gave it was already as small as possible. To actually obfuscate the file, prometheus must be told which obfuscation steps it should apply in which order. In order to do this, the cli provides the `--preset` option which allows you to specify the name of a predefined configuration. There are currently the following presets:\n\n* Minify\n* Weak\n* Medium\n* Strong\n\nIn order to perform the obfuscation, you need to specify that Prometheus should use the Strong preset:\n\n```batch\nlua ./cli.lua --preset Medium ./hello_world.lua\n```\n\nThe `hello_world.obfuscated.lua` should now contain the obfuscated code that should still print \"Hello World\".\n\nNote that using the \"Strong\" preset is not recommended for large projects.\n"
  },
  {
    "path": "doc/getting-started/presets.md",
    "content": "# Presets\n\nThe following table provides an overview over the presets\n\n| name   | size   | speed   |\n| ------ | ------ | ------- |\n| Minify | tiny   | fastest |\n| Weak   | small  | fast    |\n| Medium | medium | medium  |\n| Strong | huge   | slowest |\n"
  },
  {
    "path": "doc/getting-started/the-config-object.md",
    "content": "# The Config Object\n\nPrometheus takes a configuration object. In this object there can be many properties applied.   \\\nThe following table provides an overview:\n\n| Property      | type    | possible values                              | default           |\n| ------------- | ------- | -------------------------------------------- | ----------------- |\n| LuaVersion    | string  | \"Lua51\", \"LuaU\"                              | \"Lua51\"           |\n| PrettyPrint   | boolean | true, false                                  | false             |\n| VarNamePrefix | string  | any                                          | \"\"                |\n| NameGenerator | string  | \"Mangled\", \"MangledShuffled\", \"Il\", \"Number\" | \"MangledShuffled\" |\n| Seed          | number  | any                                          | 0                 |\n| Steps         | table   | StepConfig\\[]                                | {}                |\n\nAs this table shows, all properties in the config object are optional as they have a default value.\n\nAs an example, here is the code for the minify preset:\n\n```lua\n{\n    -- The default LuaVersion is Lua51\n    LuaVersion = \"Lua51\";\n    -- For minifying no VarNamePrefix is applied\n    VarNamePrefix = \"\";\n    -- Name Generator for Variables\n    NameGenerator = \"MangledShuffled\";\n    -- No pretty printing\n    PrettyPrint = false;\n    -- Seed is generated based on current time\n    Seed = 0;\n    -- No obfuscation steps\n    Steps = {}\n};\n```\n\n### Steps\n\nThe most important property is the Steps property. This property must be a table of so called Step Configs. A Step in Prometheus describes a single transformation applied to your script by the Prometheus obfuscation pipeline. A StepConfiguration consists of the Name of the Step as well as settings for the step. All Steps will later be applied in the order they are defined. A single Step can be defined twice and will then be applied twice.\n\n```lua\n-- Obfuscation steps\nSteps = {\n    {\n        -- This obfuscation step puts all constants into an array at the beginning of the code\n        Name = \"ConstantArray\";\n        Settings = {\n            -- Apply to Strings only\n            StringsOnly = true;\n            -- Apply to all Constants, 0.5 would only affect 50% of strings\n            Threshold = 1;\n        }\n    },\n}\n```\n\nUnder [Steps](broken-reference), you can find all current Steps, their names as well as the possible options.\n"
  },
  {
    "path": "doc/getting-started/writing-a-custom-config-file.md",
    "content": "# Writing a custom Config File\n\nConfiguration Files for Prometheus are just lua modules, that return a single object, which contains the configuration. Let's say we have the following config file:\n\n{% code title=\"config.lua\" %}\n```lua\nreturn {\n        -- The default LuaVersion is Lua51\n        LuaVersion = \"Lua51\"; -- or \"LuaU\"\n        -- All Variables will start with this prefix\n        VarNamePrefix = \"\";\n        -- Name Generator for Variables that look like this: b, a, c, D, t, G\n        NameGenerator = \"MangledShuffled\";\n        -- No pretty printing\n        PrettyPrint = false;\n        -- Seed is generated based on current time\n        -- When specifying a seed that is not 0, you will get the same output every time\n        Seed = 0;\n        -- Obfuscation steps\n        Steps = {\n            {\n                -- This obfuscation step puts all constants into an array at the beginning of the code\n                Name = \"ConstantArray\";\n                Settings = {\n                    -- Apply to Strings only\n                    StringsOnly = true;\n                    -- Apply to all Constants, 0.5 would only affect 50% of strings\n                    Threshold = 1;\n                }\n            },\n        }\n    }\n```\n{% endcode %}\n\nOne can now obfuscate a script using this configuration by running:\n\n```batch\nlua ./cli.lua --config config.lua hello_world.lua\n```\n\nYou should get the following output:\n\n{% code title=\"hello_world.obfuscated.lua\" %}\n```lua\nlocal N={\"Hello, World!\"}local function k(k)return N[k+40058]end print(k(-40057))\n```\n{% endcode %}\n\nAs you can see, the only transformation that was applied to our Hello World example was putting all strings (in this case only `\"Hello, World!\"` ) into an array and creating a wrapper function for retrieving the value.\n\n### How does the Config File work?\n\nThe config file is simply a lua file, that returns the configuration object. Please note that this lua file is sandboxed by Prometheus when loading the configuration, meaning that you can't use any predefined functions like `tostring` or libraries like `math`.\n\nSee [The Config Object](the-config-object.md) to learn what this configuration object consists of.\n"
  },
  {
    "path": "doc/steps/anti-tamper.md",
    "content": "---\ndescription: This step provides an obfuscation step, that breaks the script, when someone tries to tamper with it.\n---\n\n# Anti Tamper\n\n### Settings\n\n| Name        | type | description                                 | values                                  |\n| ----------- | ---- | ------------------------------------------- | --------------------------------------- |\n| UseDebug | boolean | Uses the debug library in lua. Disable this if you don't have access to debug library | \"true\",\"false\" |\n"
  },
  {
    "path": "doc/steps/constantarray.md",
    "content": "---\ndescription: >-\n  This Step will Extract all Constants and put them into an Array at the\n  beginning of the script\n---\n\n# ConstantArray\n\n### Settings\n\n| Name                 | type    | description                                                                                                  |\n| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |\n| Threshold             | number  | The relative amount of nodes that will be affected\"                                                          |\n| StringsOnly          | boolean | Whether to only Extract Strings                                                                               |\n| Shuffle              | boolean | Whether to shuffle the order of Elements in the Array                                                         |\n| Rotate               | boolean | Whether to rotate the String Array by a specific (random) amount. This will be undone on runtime.             |\n| LocalWrapperThreshold | number  | The relative amount of nodes functions, that will get local wrappers                                         |\n| LocalWrapperCount    | number  | The number of Local wrapper Functions per scope. This only applies if LocalWrapperThreshold is greater than 0 |\n| LocalWrapperArgCount | number  | The number of Arguments to the Local wrapper Functions                                                       |\n| MaxWrapperOffset     | number  | The Max Offset for the Wrapper Functions                                                                     |\n\n### Example\n\n{% code title=\"in.lua\" %}\n```lua\nprint(\"1\")\nprint(\"2\")\nprint(\"3\")\nprint(\"4\")\n```\n{% endcode %}\n\n{% code title=\"out.lua\" %}\n```lua\n-- LocalWrapperCount = 3\n-- LocalWrapperArgCount = 5\nlocal F = {\"4\", \"3\", \"2\", \"1\"}\ndo\n    local y, G = 1, 4\n    while y < G do\n        F[y], F[G] = F[G], F[y]\n        y, G = y + 1, G - 1\n    end\n    y, G = 1, 3\n    while y < G do\n        F[y], F[G] = F[G], F[y]\n        y, G = y + 1, G - 1\n    end\n    y, G = 4, 4\n    while y < G do\n        F[y], F[G] = F[G], F[y]\n        y, G = y + 1, G - 1\n    end\nend\nlocal function y(y)\n    return F[y + 440]\nend\nlocal G = {cb = function(F, G, R, p, b)\n        return y(G - 2277)\n    end, n = function(F, G, R, p, b)\n        return y(p + 47178)\n    end, B = function(F, G, R, p, b)\n        return y(F + 31775)\n    end}\nprint(G.cb(1575, 1840, 2367, 1293, 1280))\nprint(G.B(-32213, -31781, -31538, -32780, -32728))\nprint(G.B(-32214, -33004, -31973, -32125, -31855))\nprint(G.B(-32211, -31884, -31217, -32222, -31210))\n\n```\n{% endcode %}\n"
  },
  {
    "path": "doc/steps/encryptstrings.md",
    "content": "---\ndescription: This Step will encrypt all String constants in your code\n---\n\n# EncryptStrings\n\n## Settings\n\nNone\n\n## Example\n\n{% code title=\"in.lua\" %}\n```lua\nprint(\"Hello, World!\")\n```\n{% endcode %}\n\n{% code title=\"out.lua\" %}\n```lua\n-- Settings: None\nlocal x, F\ndo\n    local k = math.floor\n    local I = math.random\n    local Y = table.remove\n    local i = string.char\n    local K = 0\n    local J = 2\n    local Q = {}\n    local W = {}\n    local q = 0\n    local R = {}\n    for F = 1, 256, 1 do\n        R[F] = F\n    end\n    repeat\n        local F = I(1, #R)\n        local x = Y(R, F)\n        W[x] = i(x - 1)\n    until #R == 0\n    local j = {}\n    local function B()\n        if #j == 0 then\n            K = (K * 173 + 8408159861491) % 35184372088832\n            repeat\n                J = (J * 160) % 257\n            until J ~= 1\n            local F = J % 32\n            local x = (k(K / 2 ^ (13 - (J - F) / 32)) % 4294967296) / 2 ^ F\n            local I = k((x % 1) * 4294967296) + k(x)\n            local Y = I % 65536\n            local i = (I - Y) / 65536\n            local Q = Y % 256\n            local W = (Y - Q) / 256\n            local q = i % 256\n            local R = (i - q) / 256\n            j = {Q, W, q, R}\n        end\n        return table.remove(j)\n    end\n    local d = {}\n    x = setmetatable({}, {__index = d, __metatable = nil})\n    function F(x, k)\n        local I = d\n        if I[k] then\n        else\n            j = {}\n            local F = W\n            K = k % 35184372088832\n            J = k % 255 + 2\n            local Y = string.len(x)\n            I[k] = \"\"\n            local i = 198\n            for Y = 1, Y, 1 do\n                i = ((string.byte(x, Y) + B()) + i) % 256\n                I[k] = I[k] .. F[i + 1]\n            end\n        end\n        return k\n    end\nend\nprint(x[F(\"\\219\\018Q%~Y\\225\\128u\\128\\208&\\155\", 6909832146399)])\n\n```\n{% endcode %}\n"
  },
  {
    "path": "doc/steps/proxifylocals.md",
    "content": "---\ndescription: This Step wraps all locals into Proxy Objects\n---\n\n# ProxifyLocals\n\n### Settings\n\n| Name        | type | description                                 | values                                  |\n| ----------- | ---- | ------------------------------------------- | --------------------------------------- |\n| LiteralType | enum | The type of the randomly generated literals | \"dictionary\", \"number\", \"string\", \"any\" |\n\n### Example\n\n{% code title=\"in.lua\" %}\n```lua\nlocal x = \"Hello, World!\"\nprint(x)\n```\n{% endcode %}\n\n{% code title=\"out.lua\" %}\n```lua\n-- LiteralType = \"dictionary\"\nlocal n = setmetatable\nlocal D =\n    n(\n    {Wz = function()\n        end},\n    {__div = function(R, n)\n            R.Wz = n\n        end, __concat = function(R, n)\n            return R.Wz\n        end}\n)\nlocal R =\n    n(\n    {Js = \"Hello, World!\"},\n    {__add = function(R, n)\n            R.Js = n\n        end, __index = function(R, n)\n            return rawget(R, \"Js\")\n        end}\n)\nprint(R.Muirgen)\n```\n{% endcode %}\n"
  },
  {
    "path": "doc/steps/splitstrings.md",
    "content": "---\ndescription: This Step splits Strings to a specific or random length\n---\n\n# SplitStrings\n\n### Settings\n\n| Name                      | type   | description                                                                                                                                                                            | Values                      |\n| ------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |\n| Threshold                  | number | The relative amount of nodes that will be affected                                                                                                                                     | 0 <= x <= 1                 |\n| MinLength                 | number | The minimal length for the chunks in that the Strings are splitted                                                                                                                     | x > 0                       |\n| MaxLength                 | number | The maximal length for the chunks in that the Strings are splitted                                                                                                                     | x >= MinLength              |\n| ConcatenationType         | enum   | The Functions used for Concatenation. Note that when using custom, the String Array will also be Shuffled                                                                              | \"strcat\", \"table\", \"custom\" |\n| CustomFunctionType        | enum   | <p>The Type of Function code injection This Option only applies when custom Concatenation is selected.<br>Note that when choosing inline, the code size may increase significantly!</p> | \"global\", \"local\", \"inline\" |\n| CustomLocalFunctionsCount | number | The number of local functions per scope. This option only applies when CustomFunctionType = local                                                                                      | x > 0                       |\n\n### Example\n\n{% code title=\"in.lua\" %}\n```lua\nprint(\"Hello, World!\")\n```\n{% endcode %}\n\n{% code title=\"out.lua\" %}\n```lua\n-- MinLength = 1\n-- MaxLength = 1\nlocal f = function(f)\n    local k, C = f[#f], \"\"\n    for j = 1, #k, 1 do\n        C = C .. k[f[j]]\n    end\n    return C\nend\nprint(f({13, 11, 4, 12, 1, 6, 8, 10, 9, 7, 3, 2, 5, {\"o\", \"d\", \"l\", \"l\", \"!\", \",\", \"r\", \" \", \"o\", \"W\", \"e\", \"l\", \"H\"}}))\n\n```\n{% endcode %}\n"
  },
  {
    "path": "doc/steps/vmify.md",
    "content": "---\ndescription: This Step will Compile your script and run it within a VM.\n---\n\n# Vmify\n\n### Settings\n\nNone\n"
  },
  {
    "path": "doc/steps/wrapinfunction.md",
    "content": "---\ndescription: This Step Wraps the Entire Script into a Function\n---\n\n# WrapInFunction\n\n### Settings\n\n| Name       | type   | description              |\n| ---------- | ------ | ------------------------ |\n| Iterations | number | The Number Of Iterations |\n\n### Example\n\n{% code title=\"in.lua\" %}\n```lua\nprint(\"Hello, World!\")\n```\n{% endcode %}\n\n{% code title=\"out.lua\" %}\n```lua\n-- Iterations = 1\nreturn (function()\n    print(\"Hello, World!\")\nend)()\n\n```\n{% endcode %}\n"
  },
  {
    "path": "prometheus-main.lua",
    "content": "require(\"cli\")"
  },
  {
    "path": "readme.md",
    "content": "# :fire: Prometheus Lua Obfuscator\n[![Test](https://github.com/prometheus-lua/Prometheus/actions/workflows/Test.yml/badge.svg)](https://github.com/prometheus-lua/Prometheus/actions/workflows/Test.yml)\n\nPrometheus is a Lua obfuscator written in pure Lua.\nIt uses several AST-based transformations including Control-Flow Flattening, Constant Encryption and more.\n\nThis project was inspired by the amazing [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator).  \nIt can currently obfuscate Lua51 and Roblox's LuaU, however LuaU support is not finished yet.\n\nYou can find the full Documentation including a getting started guide [here](https://levno-710.gitbook.io/prometheus/).\n\nPrometheus has an official [Discord server](https://discord.gg/U8h4d4Rf64).\n\n<p align=\"center\">\n  <img src=\"assets/readme/obfuscation-preview.gif\" alt=\"Prometheus obfuscation process preview\" width=\"900\" />\n</p>\n\n## Installation\nTo install Prometheus, simply clone the GitHub repository using:\n\n```batch\ngit clone https://github.com/prometheus-lua/Prometheus.git\n```\n\nAlternatively you can download the sources [here](https://github.com/prometheus-lua/Prometheus/archive/refs/heads/master.zip).\n\nPrometheus also Requires LuaJIT or Lua51 in order to work. The Lua51 binaries can be downloaded [here](https://sourceforge.net/projects/luabinaries/files/5.1.5/Tools%20Executables/).\n\n## Usage\nTo quickly obfuscate a script:\n```batch\nlua ./cli.lua --preset Medium ./your_file.lua\n```\n\n### Example output\n```lua\n-- input.lua\nprint(\"Hello, World!\");\n```\n\n```lua\n-- input.obfuscated.lua\nreturn(function(...)local L={\"afT6mf1V\",\"/7mJXsuvmE1c/fT3\";\"tn1ZSn6=\",\"37ghSJM=\";\"WqermfWAWuuZpb3XX7M=\",\"tqXGSJ3u\",\"XQXpL9x21dxAWJa//p==\",\"SrM=\";\"3q+5SJM=\",\"/D==\";\"t7XUt0p=\";\"mIeOmIx9\";\"LdgrBfWdWuNABsb+KJxj\",\"SJWJ4dahKsebW7t+KQv=\",\"/cDu3AvP/D==\";\"Llv7uD==\",\"tJWhFfTE\";\"TQ43ctIuy9HIop==\",\"mEu93p==\";\"WJax1sXEXEaxWuxGt6==\",\"t0gPSEp=\",...\n-- remaining obfuscated output omitted\n```\n\nFor more advanced use cases see the [Documentation](https://levno-710.gitbook.io/prometheus/).\n## Tests\nTo perform the Prometheus Tests, just run\n```batch\nlua ./tests.lua [--Linux]\n```\n\n## License and Commercial Use\n\nPrometheus is licensed under the Prometheus License, a modified MIT-style license.\nYou are free to use, modify, and distribute this software, including for commercial purposes, under the following conditions:\n - Any commercial product, wrapper, or service (including SaaS or hosted solutions) that uses or integrates Prometheus must include clear attribution to:\n```\nBased on Prometheus by Elias Oelschner, https://github.com/prometheus-lua/Prometheus\n```\n - The attribution must be visible in the product’s UI, documentation, and public website.\n - The obfuscated output files generated by Prometheus do not need to include any license or copyright notice.\n - Derivative works and public forks must also include a statement in their README noting that they are based on Prometheus.\n\nFull license text: [Prometheus License](https://github.com/levno-710/Prometheus/blob/master/LICENSE)\n"
  },
  {
    "path": "src/cli.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- cli.lua\n--\n-- This Script contains the Code for the Prometheus CLI.\n\n-- Configure package.path for requiring Prometheus.\nlocal function script_path()\n\tlocal str = debug.getinfo(2, \"S\").source:sub(2)\n\treturn str:match(\"(.*[/%\\\\])\")\nend\npackage.path = script_path() .. \"?.lua;\" .. package.path\n---@diagnostic disable-next-line: different-requires\nlocal Prometheus = require(\"prometheus\")\nPrometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Info\n\n-- Check if the file exists\nlocal function file_exists(file)\n\tlocal f = io.open(file, \"rb\")\n\tif f then\n\t\tf:close()\n\tend\n\treturn f ~= nil\nend\n\nstring.split = function(str, sep)\n\tlocal fields = {}\n\tlocal pattern = string.format(\"([^%s]+)\", sep)\n\tstr:gsub(pattern, function(c)\n\t\tfields[#fields + 1] = c\n\tend)\n\treturn fields\nend\n\n-- get all lines from a file, returns an empty\n-- list/table if the file does not exist\nlocal function lines_from(file)\n\tif not file_exists(file) then\n\t\treturn {}\n\tend\n\tlocal lines = {}\n\tfor line in io.lines(file) do\n\t\tlines[#lines + 1] = line\n\tend\n\treturn lines\nend\n\nlocal function load_chunk(content, chunkName, environment)\n\tif type(loadstring) == \"function\" then\n\t\tlocal func, err = loadstring(content, chunkName)\n\t\tif not func then\n\t\t\treturn nil, err\n\t\tend\n\t\tif environment and type(setfenv) == \"function\" then\n\t\t\tsetfenv(func, environment)\n\t\telseif environment and type(load) == \"function\" then\n\t\t\treturn load(content, chunkName, \"t\", environment)\n\t\tend\n\t\treturn func\n\tend\n\n\tif type(load) ~= \"function\" then\n\t\treturn nil, \"No load function available\"\n\tend\n\n\treturn load(content, chunkName, \"t\", environment)\nend\n\n-- CLI\nlocal config, sourceFile, outFile, luaVersion, prettyPrint\n\nPrometheus.colors.enabled = true\n\n-- Parse Arguments\nlocal i = 1\nwhile i <= #arg do\n\tlocal curr = arg[i]\n\tif curr:sub(1, 2) == \"--\" then\n\t\tif curr == \"--preset\" or curr == \"--p\" then\n\t\t\tif config then\n\t\t\t\tPrometheus.Logger:warn(\"The config was set multiple times\")\n\t\t\tend\n\n\t\t\ti = i + 1\n\t\t\tlocal preset = Prometheus.Presets[arg[i]]\n\t\t\tif not preset then\n\t\t\t\tPrometheus.Logger:error(string.format('A Preset with the name \"%s\" was not found!', tostring(arg[i])))\n\t\t\tend\n\n\t\t\tconfig = preset\n\t\telseif curr == \"--config\" or curr == \"--c\" then\n\t\t\ti = i + 1\n\t\t\tlocal filename = tostring(arg[i])\n\t\t\tif not file_exists(filename) then\n\t\t\t\tPrometheus.Logger:error(string.format('The config file \"%s\" was not found!', filename))\n\t\t\tend\n\n\t\t\tlocal content = table.concat(lines_from(filename), \"\\n\")\n\t\t\t-- Load Config from File\n\t\t\tlocal func, err = load_chunk(content, \"@\" .. filename, {})\n\t\t\tif not func then\n\t\t\t\tPrometheus.Logger:error(string.format('Failed to parse config file \"%s\": %s', filename, tostring(err)))\n\t\t\tend\n\t\t\tconfig = func()\n\t\telseif curr == \"--out\" or curr == \"--o\" then\n\t\t\ti = i + 1\n\t\t\tif outFile then\n\t\t\t\tPrometheus.Logger:warn(\"The output file was specified multiple times!\")\n\t\t\tend\n\t\t\toutFile = arg[i]\n\t\telseif curr == \"--nocolors\" then\n\t\t\tPrometheus.colors.enabled = false\n\t\telseif curr == \"--Lua51\" then\n\t\t\tluaVersion = \"Lua51\"\n\t\telseif curr == \"--LuaU\" then\n\t\t\tluaVersion = \"LuaU\"\n\t\telseif curr == \"--pretty\" then\n\t\t\tprettyPrint = true\n\t\telseif curr == \"--saveerrors\" then\n\t\t\t-- Override error callback\n\t\t\tPrometheus.Logger.errorCallback = function(...)\n\t\t\t\tprint(Prometheus.colors(Prometheus.Config.NameUpper .. \": \" .. ..., \"red\"))\n\n\t\t\t\tlocal args = { ... }\n\t\t\t\tlocal message = table.concat(args, \" \")\n\n\t\t\t\tlocal fileName = sourceFile:sub(-4) == \".lua\" and sourceFile:sub(0, -5) .. \".error.txt\"\n\t\t\t\t\tor sourceFile .. \".error.txt\"\n\t\t\t\tlocal handle = io.open(fileName, \"w\")\n\t\t\t\thandle:write(message)\n\t\t\t\thandle:close()\n\n\t\t\t\tos.exit(1)\n\t\t\tend\n\t\telse\n\t\t\tPrometheus.Logger:warn(string.format('The option \"%s\" is not valid and therefore ignored', curr))\n\t\tend\n\telse\n\t\tif sourceFile then\n\t\t\tPrometheus.Logger:error(string.format('Unexpected argument \"%s\"', arg[i]))\n\t\tend\n\t\tsourceFile = tostring(arg[i])\n\tend\n\ti = i + 1\nend\n\nif not sourceFile then\n\tPrometheus.Logger:error(\"No input file was specified!\")\nend\n\nif not config then\n\tPrometheus.Logger:warn(\"No config was specified, falling back to Minify preset\")\n\tconfig = Prometheus.Presets.Minify\nend\n\n-- Add Option to override Lua Version\nconfig.LuaVersion = luaVersion or config.LuaVersion\nconfig.PrettyPrint = prettyPrint ~= nil and prettyPrint or config.PrettyPrint\n\nif not file_exists(sourceFile) then\n\tPrometheus.Logger:error(string.format('The File \"%s\" was not found!', sourceFile))\nend\n\nif not outFile then\n\tif sourceFile:sub(-4) == \".lua\" then\n\t\toutFile = sourceFile:sub(0, -5) .. \".obfuscated.lua\"\n\telse\n\t\toutFile = sourceFile .. \".obfuscated.lua\"\n\tend\nend\n\nlocal source = table.concat(lines_from(sourceFile), \"\\n\")\nlocal pipeline = Prometheus.Pipeline:fromConfig(config)\nlocal out = pipeline:apply(source, sourceFile)\nPrometheus.Logger:info(string.format('Writing output to \"%s\"', outFile))\n\n-- Write Output\nlocal handle = io.open(outFile, \"w\")\nhandle:write(out)\nhandle:close()\n"
  },
  {
    "path": "src/colors.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- colors.lua\n--\n-- This Script provides a simple method for syntax highlighting of Lua code\n\nlocal keys = {\n\treset = 0,\n\tbright = 1,\n\tdim = 2,\n\tunderline = 4,\n\tblink = 5,\n\treverse = 7,\n\thidden = 8,\n\tblack = 30,\n\tpink = 91,\n\tred = 31,\n\tgreen = 32,\n\tyellow = 33,\n\tblue = 34,\n\tmagenta = 35,\n\tcyan = 36,\n\tgrey = 37,\n\tgray = 37,\n\twhite = 97,\n\tblackbg = 40,\n\tredbg = 41,\n\tgreenbg = 42,\n\tyellowbg = 43,\n\tbluebg = 44,\n\tmagentabg = 45,\n\tcyanbg = 46,\n\tgreybg = 47,\n\tgraybg = 47,\n\twhitebg = 107,\n}\n\nlocal escapeString = string.char(27) .. \"[%dm\"\nlocal function escapeNumber(number)\n\treturn escapeString:format(number)\nend\n\nlocal settings = {\n\tenabled = true,\n}\n\nlocal function colors(str, ...)\n\tif not settings.enabled then\n\t\treturn str\n\tend\n\tstr = tostring(str or \"\")\n\n\tlocal escapes = {}\n\tfor _, name in ipairs({ ... }) do\n\t\ttable.insert(escapes, escapeNumber(keys[name]))\n\tend\n\n\treturn escapeNumber(keys.reset) .. table.concat(escapes) .. str .. escapeNumber(keys.reset)\nend\n\nreturn setmetatable(settings, {\n\t__call = function(_, ...)\n\t\treturn colors(...)\n\tend,\n})\n"
  },
  {
    "path": "src/highlightlua.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- highlightlua.lua\n--\n-- This Script provides a simple Method for Syntax Highlighting of Lua code\n\nlocal Tokenizer = require(\"prometheus.tokenizer\");\nlocal colors = require(\"colors\");\nlocal TokenKind = Tokenizer.TokenKind;\nlocal lookupify = require(\"prometheus.util\").lookupify;\n\nreturn function(code, luaVersion)\n    local out = \"\";\n    local tokenizer = Tokenizer:new({\n        LuaVersion = luaVersion,\n    });\n\n    tokenizer:append(code);\n    local tokens = tokenizer:scanAll();\n\n    local nonColorSymbols = lookupify{\n        \",\", \";\", \"(\", \")\", \"{\", \"}\", \".\", \":\", \"[\", \"]\"\n    }\n\n    local defaultGlobals = lookupify{\n        \"string\", \"table\", \"bit32\", \"bit\"\n    }\n\n    local currentPos = 1;\n    for _, token in ipairs(tokens) do\n        if token.startPos >= currentPos then\n            out = out .. string.sub(code, currentPos, token.startPos);\n        end\n        if token.kind == TokenKind.Ident then\n            if defaultGlobals[token.source] then\n                out = out .. colors(token.source, \"red\");\n            else\n                out = out .. token.source;\n            end\n        elseif token.kind == TokenKind.Keyword then\n            if token.source == \"nil\" then\n                out = out .. colors(token.source, \"yellow\");\n            else\n                out = out .. colors(token.source, \"yellow\");\n            end\n        elseif token.kind == TokenKind.Symbol then\n            if nonColorSymbols[token.source] then\n                out = out .. token.source;\n            else\n                out = out .. colors(token.source, \"yellow\");\n            end\n        elseif token.kind == TokenKind.String then\n            out = out .. colors(token.source, \"green\")\n        elseif token.kind == TokenKind.Number then\n            out = out .. colors(token.source, \"red\")\n        else\n            out = out .. token.source;\n        end\n\n        currentPos = token.endPos + 1;\n    end\n    return out;\nend"
  },
  {
    "path": "src/logger.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- logger.lua\n--\n-- This Script provides a Logger for Prometheus.\n\nlocal logger = {}\nlocal config = require(\"config\");\nlocal colors = require(\"colors\");\n\nlogger.LogLevel = {\n\tError = 0,\n\tWarn = 1,\n\tLog = 2,\n\tInfo = 2,\n\tDebug = 3,\n}\n\nlogger.logLevel = logger.LogLevel.Log;\n\nlogger.debugCallback = function(...)\n\tprint(colors(config.NameUpper .. \": \" ..  ..., \"grey\"));\nend;\nfunction logger:debug(...)\n\tif self.logLevel >= self.LogLevel.Debug then\n\t\tself.debugCallback(...);\n\tend\nend\n\nlogger.logCallback = function(...)\n\tprint(colors(config.NameUpper .. \": \", \"magenta\") .. ...);\nend;\nfunction logger:log(...)\n\tif self.logLevel >= self.LogLevel.Log then\n\t\tself.logCallback(...);\n\tend\nend\n\nfunction logger:info(...)\n\tif self.logLevel >= self.LogLevel.Log then\n\t\tself.logCallback(...);\n\tend\nend\n\nlogger.warnCallback = function(...)\n\tprint(colors(config.NameUpper .. \": \" .. ..., \"yellow\"));\nend;\nfunction logger:warn(...)\n\tif self.logLevel >= self.LogLevel.Warn then\n\t\tself.warnCallback(...);\n\tend\nend\n\nlogger.errorCallback = function(...)\n\tprint(colors(config.NameUpper .. \": \" .. ..., \"red\"))\n\terror(...);\nend;\nfunction logger:error(...)\n\tself.errorCallback(...);\n\terror(config.NameUpper .. \": logger.errorCallback did not throw an Error!\");\nend\n\n\nreturn logger;"
  },
  {
    "path": "src/presets.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- presets.lua\n--\n-- This Script provides the predefined obfuscation presets for Prometheus\n\nreturn {\n\t-- Minifies your code. Does not obfuscate it. No performance loss.\n\t[\"Minify\"] = {\n\t\tLuaVersion = \"Lua51\",\n\t\tVarNamePrefix = \"\",\n\t\tNameGenerator = \"MangledShuffled\",\n\t\tPrettyPrint = false,\n\t\tSeed = 0,\n\t\tSteps = {},\n\t},\n\n\t-- Weak obfuscation. Very readable, low performance loss.\n\t[\"Weak\"] = {\n\t\tLuaVersion = \"Lua51\",\n\t\tVarNamePrefix = \"\",\n\t\tNameGenerator = \"MangledShuffled\",\n\t\tPrettyPrint = false,\n\t\tSeed = 0,\n\t\tSteps = {\n\t\t\t{ Name = \"Vmify\", Settings = {} },\n\t\t\t{\n\t\t\t\tName = \"ConstantArray\",\n\t\t\t\tSettings = {\n\t\t\t\t\tThreshold = 1,\n\t\t\t\t\tStringsOnly = true\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ Name = \"WrapInFunction\", Settings = {} },\n\t\t},\n\t},\n\n\t-- This is here for the tests.lua file.\n\t-- It helps isolate any problems with the Vmify step.\n\t-- It is not recommended to use this preset for obfuscation.\n\t-- Use the Weak, Medium, or Strong for obfuscation instead.\n\t[\"Vmify\"] = {\n\t\tLuaVersion = \"Lua51\",\n\t\tVarNamePrefix = \"\",\n\t\tNameGenerator = \"MangledShuffled\",\n\t\tPrettyPrint = false,\n\t\tSeed = 0,\n\t\tSteps = {\n\t\t\t{ Name = \"Vmify\", Settings = {} },\n\t\t},\n\t},\n\n\t-- Medium obfuscation. Moderate obfuscation, moderate performance loss.\n\t[\"Medium\"] = {\n\t\tLuaVersion = \"Lua51\",\n\t\tVarNamePrefix = \"\",\n\t\tNameGenerator = \"MangledShuffled\",\n\t\tPrettyPrint = false,\n\t\tSeed = 0,\n\t\tSteps = {\n\t\t\t{ Name = \"EncryptStrings\", Settings = {} },\n\t\t\t{\n\t\t\t\tName = \"AntiTamper\",\n\t\t\t\tSettings = {\n\t\t\t\t\tUseDebug = false,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ Name = \"Vmify\", Settings = {} },\n\t\t\t{\n\t\t\t\tName = \"ConstantArray\",\n\t\t\t\tSettings = {\n\t\t\t\t\tThreshold = 1,\n\t\t\t\t\tStringsOnly = true,\n\t\t\t\t\tShuffle = true,\n\t\t\t\t\tRotate = true,\n\t\t\t\t\tLocalWrapperThreshold = 0,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ Name = \"NumbersToExpressions\", Settings = {} },\n\t\t\t{ Name = \"WrapInFunction\", Settings = {} },\n\t\t},\n\t},\n\n\t-- Strong obfuscation, high performance loss.\n\t[\"Strong\"] = {\n\t\tLuaVersion = \"Lua51\",\n\t\tVarNamePrefix = \"\",\n\t\tNameGenerator = \"MangledShuffled\",\n\t\tPrettyPrint = false,\n\t\tSeed = 0,\n\t\tSteps = {\n\t\t\t{ Name = \"Vmify\", Settings = {} },\n\t\t\t{ Name = \"EncryptStrings\", Settings = {} },\n\t\t\t{\n\t\t\t\tName = \"AntiTamper\",\n\t\t\t\tSettings = {\n\t\t\t\t\tUseDebug = false,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ Name = \"Vmify\", Settings = {} },\n\t\t\t{\n\t\t\t\tName = \"ConstantArray\",\n\t\t\t\tSettings = {\n\t\t\t\t\tThreshold = 1,\n\t\t\t\t\tStringsOnly = true,\n\t\t\t\t\tShuffle = true,\n\t\t\t\t\tRotate = true,\n\t\t\t\t\tLocalWrapperThreshold = 0\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName = \"NumbersToExpressions\",\n\t\t\t\tSettings = {\n\t\t\t\t\tNumberRepresentationMutaton = true\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ Name = \"WrapInFunction\", Settings = {} },\n\t\t},\n\t},\n}\n"
  },
  {
    "path": "src/prometheus/ast.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- ast.lua\n--\n-- This Script provides the Abstract Syntax Tree (AST) for Prometheus.\n\nlocal Ast = {}\n\nlocal AstKind = {\n\t-- Misc\n\tTopNode = \"TopNode\";\n\tBlock = \"Block\";\n\n\t-- Statements\n\tContinueStatement = \"ContinueStatement\";\n\tBreakStatement = \"BreakStatement\";\n\tDoStatement = \"DoStatement\";\n\tWhileStatement = \"WhileStatement\";\n\tReturnStatement = \"ReturnStatement\";\n\tRepeatStatement = \"RepeatStatement\";\n\tForInStatement = \"ForInStatement\";\n\tForStatement = \"ForStatement\";\n\tIfStatement = \"IfStatement\";\n\tFunctionDeclaration = \"FunctionDeclaration\";\n\tLocalFunctionDeclaration = \"LocalFunctionDeclaration\";\n\tLocalVariableDeclaration = \"LocalVariableDeclaration\";\n\tFunctionCallStatement = \"FunctionCallStatement\";\n\tPassSelfFunctionCallStatement = \"PassSelfFunctionCallStatement\";\n\tAssignmentStatement = \"AssignmentStatement\";\n\n\t-- LuaU Compound Statements\n\tCompoundAddStatement = \"CompoundAddStatement\";\n\tCompoundSubStatement = \"CompoundSubStatement\";\n\tCompoundMulStatement = \"CompoundMulStatement\";\n\tCompoundDivStatement = \"CompoundDivStatement\";\n\tCompoundModStatement = \"CompoundModStatement\";\n\tCompoundPowStatement = \"CompoundPowStatement\";\n\tCompoundConcatStatement = \"CompoundConcatStatement\";\n\n\t-- Assignment Index\n\tAssignmentIndexing = \"AssignmentIndexing\";\n\tAssignmentVariable = \"AssignmentVariable\";\n\n\t-- Expression Nodes\n\tBooleanExpression = \"BooleanExpression\";\n\tNumberExpression = \"NumberExpression\";\n\tStringExpression = \"StringExpression\";\n\tNilExpression = \"NilExpression\";\n\tVarargExpression = \"VarargExpression\";\n\tOrExpression = \"OrExpression\";\n\tAndExpression = \"AndExpression\";\n\tLessThanExpression = \"LessThanExpression\";\n\tGreaterThanExpression = \"GreaterThanExpression\";\n\tLessThanOrEqualsExpression = \"LessThanOrEqualsExpression\";\n\tGreaterThanOrEqualsExpression = \"GreaterThanOrEqualsExpression\";\n\tNotEqualsExpression = \"NotEqualsExpression\";\n\tEqualsExpression = \"EqualsExpression\";\n\tStrCatExpression = \"StrCatExpression\";\n\tAddExpression = \"AddExpression\";\n\tSubExpression = \"SubExpression\";\n\tMulExpression = \"MulExpression\";\n\tDivExpression = \"DivExpression\";\n\tModExpression = \"ModExpression\";\n\tNotExpression = \"NotExpression\";\n\tLenExpression = \"LenExpression\";\n\tNegateExpression = \"NegateExpression\";\n\tPowExpression = \"PowExpression\";\n\tIndexExpression = \"IndexExpression\";\n\tFunctionCallExpression = \"FunctionCallExpression\";\n\tPassSelfFunctionCallExpression = \"PassSelfFunctionCallExpression\";\n\tVariableExpression = \"VariableExpression\";\n\tFunctionLiteralExpression = \"FunctionLiteralExpression\";\n\tTableConstructorExpression = \"TableConstructorExpression\";\n\n\t-- Table Entry\n\tTableEntry = \"TableEntry\";\n\tKeyedTableEntry = \"KeyedTableEntry\";\n\n\t-- Misc\n\tNopStatement = \"NopStatement\";\n\n\tIfElseExpression = \"IfElseExpression\";\n}\n\nlocal astKindExpressionLookup = {\n\t[AstKind.BooleanExpression] = 0;\n\t[AstKind.NumberExpression] = 0;\n\t[AstKind.StringExpression] = 0;\n\t[AstKind.NilExpression] = 0;\n\t[AstKind.VarargExpression] = 0;\n\t[AstKind.OrExpression] = 12;\n\t[AstKind.AndExpression] = 11;\n\t[AstKind.LessThanExpression] = 10;\n\t[AstKind.GreaterThanExpression] = 10;\n\t[AstKind.LessThanOrEqualsExpression] = 10;\n\t[AstKind.GreaterThanOrEqualsExpression] = 10;\n\t[AstKind.NotEqualsExpression] = 10;\n\t[AstKind.EqualsExpression] = 10;\n\t[AstKind.StrCatExpression] = 9;\n\t[AstKind.AddExpression] = 8;\n\t[AstKind.SubExpression] = 8;\n\t[AstKind.MulExpression] = 7;\n\t[AstKind.DivExpression] = 7;\n\t[AstKind.ModExpression] = 7;\n\t[AstKind.NotExpression] = 5;\n\t[AstKind.LenExpression] = 5;\n\t[AstKind.NegateExpression] = 5;\n\t[AstKind.PowExpression] = 4;\n\t[AstKind.IndexExpression] = 1;\n\t[AstKind.AssignmentIndexing] = 1;\n\t[AstKind.FunctionCallExpression] = 2;\n\t[AstKind.PassSelfFunctionCallExpression] = 2;\n\t[AstKind.VariableExpression] = 0;\n\t[AstKind.AssignmentVariable] = 0;\n\t[AstKind.FunctionLiteralExpression] = 3;\n\t[AstKind.TableConstructorExpression] = 3;\n}\n\nAst.AstKind = AstKind;\n\nfunction Ast.astKindExpressionToNumber(kind)\n\treturn astKindExpressionLookup[kind] or 100;\nend\n\nfunction Ast.ConstantNode(val)\n\tif type(val) == \"nil\" then\n\t\treturn Ast.NilExpression();\n\tend\n\n\tif type(val) == \"string\" then\n\t\treturn Ast.StringExpression(val);\n\tend\n\n\tif type(val) == \"number\" then\n\t\treturn Ast.NumberExpression(val);\n\tend\n\n\tif type(val) == \"boolean\" then\n\t\treturn Ast.BooleanExpression(val);\n\tend\nend\n\n\n\nfunction Ast.NopStatement()\n\treturn {\n\t\tkind = AstKind.NopStatement;\n\t}\nend\n\nfunction Ast.IfElseExpression(condition, true_value, false_value)\n\treturn {\n\t\tkind = AstKind.IfElseExpression,\n\t\tcondition = condition,\n\t\ttrue_value = true_value,\n\t\tfalse_value = false_value\n\t}\nend\n\n-- Create Ast Top Node\nfunction Ast.TopNode(body, globalScope)\n\treturn {\n\t\tkind = AstKind.TopNode,\n\t\tbody = body,\n\t\tglobalScope = globalScope,\n\n\t}\nend\n\nfunction Ast.TableEntry(value)\n\treturn {\n\t\tkind = AstKind.TableEntry,\n\t\tvalue = value,\n\n\t}\nend\n\nfunction Ast.KeyedTableEntry(key, value)\n\treturn {\n\t\tkind = AstKind.KeyedTableEntry,\n\t\tkey = key,\n\t\tvalue = value,\n\n\t}\nend\n\nfunction Ast.TableConstructorExpression(entries)\n\treturn {\n\t\tkind = AstKind.TableConstructorExpression,\n\t\tentries = entries,\n\t};\nend\n\n-- Create Statement Block\nfunction Ast.Block(statements, scope)\n\treturn {\n\t\tkind = AstKind.Block,\n\t\tstatements = statements,\n\t\tscope = scope,\n\t}\nend\n\n-- Create Break Statement\nfunction Ast.BreakStatement(loop, scope)\n\treturn {\n\t\tkind = AstKind.BreakStatement,\n\t\tloop = loop,\n\t\tscope = scope,\n\t}\nend\n\n-- Create Continue Statement\nfunction Ast.ContinueStatement(loop, scope)\n\treturn {\n\t\tkind = AstKind.ContinueStatement,\n\t\tloop = loop,\n\t\tscope = scope,\n\t}\nend\n\nfunction Ast.PassSelfFunctionCallStatement(base, passSelfFunctionName, args)\n\treturn {\n\t\tkind = AstKind.PassSelfFunctionCallStatement,\n\t\tbase = base,\n\t\tpassSelfFunctionName = passSelfFunctionName,\n\t\targs = args,\n\t}\nend\n\nfunction Ast.AssignmentStatement(lhs, rhs)\n\tif(#lhs < 1) then\n\t\tprint(debug.traceback());\n\t\terror(\"Something went wrong!\");\n\tend\n\treturn {\n\t\tkind = AstKind.AssignmentStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundAddStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundAddStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundSubStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundSubStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundMulStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundMulStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundDivStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundDivStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundPowStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundPowStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundModStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundModStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.CompoundConcatStatement(lhs, rhs)\n\treturn {\n\t\tkind = AstKind.CompoundConcatStatement,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t}\nend\n\nfunction Ast.FunctionCallStatement(base, args)\n\treturn {\n\t\tkind = AstKind.FunctionCallStatement,\n\t\tbase = base,\n\t\targs = args,\n\t}\nend\n\nfunction Ast.ReturnStatement(args)\n\treturn {\n\t\tkind = AstKind.ReturnStatement,\n\t\targs = args,\n\t}\nend\n\nfunction Ast.DoStatement(body)\n\treturn {\n\t\tkind = AstKind.DoStatement,\n\t\tbody = body,\n\t}\nend\n\nfunction Ast.WhileStatement(body, condition, parentScope)\n\treturn {\n\t\tkind = AstKind.WhileStatement,\n\t\tbody = body,\n\t\tcondition = condition,\n\t\tparentScope = parentScope,\n\t}\nend\n\nfunction Ast.ForInStatement(scope, vars, expressions, body, parentScope)\n\treturn {\n\t\tkind = AstKind.ForInStatement,\n\t\tscope = scope,\n\t\tids = vars,\n\t\tvars = vars,\n\t\texpressions = expressions,\n\t\tbody = body,\n\t\tparentScope = parentScope,\n\t}\nend\n\nfunction Ast.ForStatement(scope, id, initialValue, finalValue, incrementBy, body, parentScope)\n\treturn {\n\t\tkind = AstKind.ForStatement,\n\t\tscope = scope,\n\t\tid = id,\n\t\tinitialValue = initialValue,\n\t\tfinalValue = finalValue,\n\t\tincrementBy = incrementBy,\n\t\tbody = body,\n\t\tparentScope = parentScope,\n\t}\nend\n\nfunction Ast.RepeatStatement(condition, body, parentScope)\n\treturn {\n\t\tkind = AstKind.RepeatStatement,\n\t\tbody = body,\n\t\tcondition = condition,\n\t\tparentScope = parentScope,\n\t}\nend\n\nfunction Ast.IfStatement(condition, body, elseifs, elsebody)\n\treturn {\n\t\tkind = AstKind.IfStatement,\n\t\tcondition = condition,\n\t\tbody = body,\n\t\telseifs = elseifs,\n\t\telsebody = elsebody,\n\t}\nend\n\nfunction Ast.FunctionDeclaration(scope, id, indices, args, body)\n\treturn {\n\t\tkind = AstKind.FunctionDeclaration,\n\t\tscope = scope,\n\t\tbaseScope = scope,\n\t\tid = id,\n\t\tbaseId = id,\n\t\tindices = indices,\n\t\targs = args,\n\t\tbody = body,\n\t\tgetName = function(self)\n\t\t\treturn self.scope:getVariableName(self.id);\n\t\tend,\n\t}\nend\n\nfunction Ast.LocalFunctionDeclaration(scope, id, args, body)\n\treturn {\n\t\tkind = AstKind.LocalFunctionDeclaration,\n\t\tscope = scope,\n\t\tid = id,\n\t\targs = args,\n\t\tbody = body,\n\t\tgetName = function(self)\n\t\t\treturn self.scope:getVariableName(self.id);\n\t\tend,\n\t}\nend\n\nfunction Ast.LocalVariableDeclaration(scope, ids, expressions)\n\treturn {\n\t\tkind = AstKind.LocalVariableDeclaration,\n\t\tscope = scope,\n\t\tids = ids,\n\t\texpressions = expressions,\n\t}\nend\n\nfunction Ast.VarargExpression()\n\treturn {\n\t\tkind = AstKind.VarargExpression;\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.BooleanExpression(value)\n\treturn {\n\t\tkind = AstKind.BooleanExpression,\n\t\tisConstant = true,\n\t\tvalue = value,\n\t}\nend\n\nfunction Ast.NilExpression()\n\treturn {\n\t\tkind = AstKind.NilExpression,\n\t\tisConstant = true,\n\t\tvalue = nil,\n\t}\nend\n\nfunction Ast.NumberExpression(value)\n\treturn {\n\t\tkind = AstKind.NumberExpression,\n\t\tisConstant = true,\n\t\tvalue = value,\n\t}\nend\n\nfunction Ast.StringExpression(value)\n\treturn {\n\t\tkind = AstKind.StringExpression,\n\t\tisConstant = true,\n\t\tvalue = value,\n\t}\nend\n\nfunction Ast.OrExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value or rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.OrExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.AndExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value and rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.AndExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.LessThanExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value < rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.LessThanExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.GreaterThanExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value > rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.GreaterThanExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.LessThanOrEqualsExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value <= rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.LessThanOrEqualsExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.GreaterThanOrEqualsExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value >= rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.GreaterThanOrEqualsExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.NotEqualsExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value ~= rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.NotEqualsExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.EqualsExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value == rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.EqualsExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.StrCatExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value .. rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.StrCatExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.AddExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value + rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.AddExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.SubExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value - rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.SubExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.MulExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value * rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.MulExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.DivExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant and rhs.value ~= 0) then\n\t\tlocal success, val = pcall(function() return lhs.value / rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.DivExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.ModExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value % rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.ModExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.NotExpression(rhs, simplify)\n\tif(simplify and rhs.isConstant) then\n\t\tlocal success, val = pcall(function() return not rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.NotExpression,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.NegateExpression(rhs, simplify)\n\tif(simplify and rhs.isConstant) then\n\t\tlocal success, val = pcall(function() return -rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.NegateExpression,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.LenExpression(rhs, simplify)\n\tif(simplify and rhs.isConstant) then\n\t\tlocal success, val = pcall(function() return #rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.LenExpression,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.PowExpression(lhs, rhs, simplify)\n\tif(simplify and rhs.isConstant and lhs.isConstant) then\n\t\tlocal success, val = pcall(function() return lhs.value ^ rhs.value end);\n\t\tif success then\n\t\t\treturn Ast.ConstantNode(val);\n\t\tend\n\tend\n\n\treturn {\n\t\tkind = AstKind.PowExpression,\n\t\tlhs = lhs,\n\t\trhs = rhs,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.IndexExpression(base, index)\n\treturn {\n\t\tkind = AstKind.IndexExpression,\n\t\tbase = base,\n\t\tindex = index,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.AssignmentIndexing(base, index)\n\treturn {\n\t\tkind = AstKind.AssignmentIndexing,\n\t\tbase = base,\n\t\tindex = index,\n\t\tisConstant = false,\n\t}\nend\n\nfunction Ast.PassSelfFunctionCallExpression(base, passSelfFunctionName, args)\n\treturn {\n\t\tkind = AstKind.PassSelfFunctionCallExpression,\n\t\tbase = base,\n\t\tpassSelfFunctionName = passSelfFunctionName,\n\t\targs = args,\n\n\t}\nend\n\nfunction Ast.FunctionCallExpression(base, args)\n\treturn {\n\t\tkind = AstKind.FunctionCallExpression,\n\t\tbase = base,\n\t\targs = args,\n\t}\nend\n\nfunction Ast.VariableExpression(scope, id)\n\tscope:addReference(id);\n\treturn {\n\t\tkind = AstKind.VariableExpression,\n\t\tscope = scope,\n\t\tid = id,\n\t\tgetName = function(self)\n\t\t\treturn self.scope.getVariableName(self.id);\n\t\tend,\n\t}\nend\n\nfunction Ast.AssignmentVariable(scope, id)\n\tscope:addReference(id);\n\treturn {\n\t\tkind = AstKind.AssignmentVariable,\n\t\tscope = scope,\n\t\tid = id,\n\t\tgetName = function(self)\n\t\t\treturn self.scope.getVariableName(self.id);\n\t\tend,\n\t}\nend\n\nfunction Ast.FunctionLiteralExpression(args, body)\n\treturn {\n\t\tkind = AstKind.FunctionLiteralExpression,\n\t\targs = args,\n\t\tbody = body,\n\t}\nend\n\n\n\nreturn Ast;\n"
  },
  {
    "path": "src/prometheus/compiler/block.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- block.lua\n--\n-- Block management for the compiler\n\nlocal Scope = require(\"prometheus.scope\");\nlocal util = require(\"prometheus.util\");\n\nlocal lookupify = util.lookupify;\n\nreturn function(Compiler)\n    function Compiler:createBlock()\n        local id;\n        repeat\n            id = math.random(0, 2^24)\n        until not self.usedBlockIds[id];\n        self.usedBlockIds[id] = true;\n\n        local scope = Scope:new(self.containerFuncScope);\n        local block = {\n            id = id;\n            statements = {};\n            scope = scope;\n            advanceToNextBlock = true;\n        };\n        table.insert(self.blocks, block);\n        return block;\n    end\n\n    function Compiler:setActiveBlock(block)\n        self.activeBlock = block;\n    end\n\n    function Compiler:addStatement(statement, writes, reads, usesUpvals)\n        if(self.activeBlock.advanceToNextBlock) then\n            table.insert(self.activeBlock.statements, {\n                statement = statement,\n                writes = lookupify(writes),\n                reads = lookupify(reads),\n                usesUpvals = usesUpvals or false,\n            });\n        end\n    end\nend\n\n"
  },
  {
    "path": "src/prometheus/compiler/compile_core.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- compile_core.lua\n-- This Script contains the core compilation functions: compileTopNode, compileFunction, compileBlock,\n-- compileStatement, and compileExpression\n\nlocal compileTop = require(\"prometheus.compiler.compile_top\");\nlocal statementHandlers = require(\"prometheus.compiler.statements\");\nlocal expressionHandlers = require(\"prometheus.compiler.expressions\");\nlocal Ast = require(\"prometheus.ast\");\nlocal logger = require(\"logger\");\n\nreturn function(Compiler)\n    compileTop(Compiler);\n\n    function Compiler:compileStatement(statement, funcDepth)\n        local handler = statementHandlers[statement.kind];\n        if handler then\n            handler(self, statement, funcDepth);\n            return;\n        end\n        logger:error(string.format(\"%s is not a compileable statement!\", statement.kind));\n    end\n\n    function Compiler:compileExpression(expression, funcDepth, numReturns)\n        local handler = expressionHandlers[expression.kind];\n        if handler then\n            return handler(self, expression, funcDepth, numReturns);\n        end\n        logger:error(string.format(\"%s is not an compliable expression!\", expression.kind));\n    end\nend\n"
  },
  {
    "path": "src/prometheus/compiler/compile_top.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- compile_top.lua\n--\n-- This Script contains the compilation of the top node, function, and block.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal util = require(\"prometheus.util\");\nlocal visitast = require(\"prometheus.visitast\");\n\nlocal lookupify = util.lookupify;\nlocal AstKind = Ast.AstKind;\n\nreturn function(Compiler)\n    function Compiler:compileTopNode(node)\n        local startBlock = self:createBlock();\n        local scope = startBlock.scope;\n        self.startBlockId = startBlock.id;\n        self:setActiveBlock(startBlock);\n\n        local varAccessLookup = lookupify{\n            AstKind.AssignmentVariable,\n            AstKind.VariableExpression,\n            AstKind.FunctionDeclaration,\n            AstKind.LocalFunctionDeclaration,\n        }\n\n        local functionLookup = lookupify{\n            AstKind.FunctionDeclaration,\n            AstKind.LocalFunctionDeclaration,\n            AstKind.FunctionLiteralExpression,\n            AstKind.TopNode,\n        }\n        visitast(node, function(node, data)\n            if node.kind == AstKind.Block then\n                node.scope.__depth = data.functionData.depth;\n            end\n\n            if varAccessLookup[node.kind] then\n                if not node.scope.isGlobal then\n                    if node.scope.__depth < data.functionData.depth then\n                        if not self:isUpvalue(node.scope, node.id) then\n                            self:makeUpvalue(node.scope, node.id);\n                        end\n                    end\n                end\n            end\n        end, nil, nil)\n\n        self.varargReg = self:allocRegister(true);\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);\n        scope:addReferenceToHigherScope(self.scope, self.selectVar);\n        scope:addReferenceToHigherScope(self.scope, self.unpackVar);\n        self:addStatement(self:setRegister(scope, self.varargReg, Ast.VariableExpression(self.containerFuncScope, self.argsVar)), {self.varargReg}, {}, false);\n\n        self:compileBlock(node.body, 0);\n        if(self.activeBlock.advanceToNextBlock) then\n            self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);\n            self:addStatement(self:setReturn(self.activeBlock.scope, Ast.TableConstructorExpression({})), {self.RETURN_REGISTER}, {}, false)\n            self.activeBlock.advanceToNextBlock = false;\n        end\n\n        self:resetRegisters();\n    end\n\n    function Compiler:compileFunction(node, funcDepth)\n        funcDepth = funcDepth + 1;\n        local oldActiveBlock = self.activeBlock;\n\n        local upperVarargReg = self.varargReg;\n        self.varargReg = nil;\n\n        local upvalueExpressions = {};\n        local upvalueIds = {};\n        local usedRegs = {};\n\n        local oldGetUpvalueId = self.getUpvalueId;\n        self.getUpvalueId = function(self, scope, id)\n            if(not upvalueIds[scope]) then\n                upvalueIds[scope] = {};\n            end\n            if(upvalueIds[scope][id]) then\n                return upvalueIds[scope][id];\n            end\n            local scopeFuncDepth = self.scopeFunctionDepths[scope];\n            local expression;\n            if(scopeFuncDepth == funcDepth) then\n                oldActiveBlock.scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n                expression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {});\n            elseif(scopeFuncDepth == funcDepth - 1) then\n                local varReg = self:getVarRegister(scope, id, scopeFuncDepth, nil);\n                expression = self:register(oldActiveBlock.scope, varReg);\n                table.insert(usedRegs, varReg);\n            else\n                local higherId = oldGetUpvalueId(self, scope, id);\n                oldActiveBlock.scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n                expression = Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(higherId));\n            end\n            table.insert(upvalueExpressions, Ast.TableEntry(expression));\n            local uid = #upvalueExpressions;\n            upvalueIds[scope][id] = uid;\n            return uid;\n        end\n\n        local block = self:createBlock();\n        self:setActiveBlock(block);\n        local scope = self.activeBlock.scope;\n        self:pushRegisterUsageInfo();\n        for i, arg in ipairs(node.args) do\n            if(arg.kind == AstKind.VariableExpression) then\n                if(self:isUpvalue(arg.scope, arg.id)) then\n                    scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n                    local argReg = self:getVarRegister(arg.scope, arg.id, funcDepth, nil);\n                    self:addStatement(self:setRegister(scope, argReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {argReg}, {}, false);\n                    self:addStatement(self:setUpvalueMember(scope, self:register(scope, argReg), Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.argsVar), Ast.NumberExpression(i))), {}, {argReg}, true);\n                else\n                    local argReg = self:getVarRegister(arg.scope, arg.id, funcDepth, nil);\n                    scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);\n                    self:addStatement(self:setRegister(scope, argReg, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.argsVar), Ast.NumberExpression(i))), {argReg}, {}, false);\n                end\n            else\n                self.varargReg = self:allocRegister(true);\n                scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);\n                scope:addReferenceToHigherScope(self.scope, self.selectVar);\n                scope:addReferenceToHigherScope(self.scope, self.unpackVar);\n                self:addStatement(self:setRegister(scope, self.varargReg, Ast.TableConstructorExpression({\n                    Ast.TableEntry(Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.selectVar), {\n                        Ast.NumberExpression(i);\n                        Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {\n                            Ast.VariableExpression(self.containerFuncScope, self.argsVar),\n                        });\n                    })),\n                })), {self.varargReg}, {}, false);\n            end\n        end\n\n        self:compileBlock(node.body, funcDepth);\n        if(self.activeBlock.advanceToNextBlock) then\n            self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);\n            self:addStatement(self:setReturn(self.activeBlock.scope, Ast.TableConstructorExpression({})), {self.RETURN_REGISTER}, {}, false);\n            self.activeBlock.advanceToNextBlock = false;\n        end\n\n        if(self.varargReg) then\n            self:freeRegister(self.varargReg, true);\n        end\n        self.varargReg = upperVarargReg;\n        self.getUpvalueId = oldGetUpvalueId;\n\n        self:popRegisterUsageInfo();\n        self:setActiveBlock(oldActiveBlock);\n\n        local scope = self.activeBlock.scope;\n\n        local retReg = self:allocRegister(false);\n\n        local isVarargFunction = #node.args > 0 and node.args[#node.args].kind == AstKind.VarargExpression;\n\n        local retrieveExpression\n        if isVarargFunction then\n            scope:addReferenceToHigherScope(self.scope, self.createVarargClosureVar);\n            retrieveExpression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.createVarargClosureVar), {\n                Ast.NumberExpression(block.id),\n                Ast.TableConstructorExpression(upvalueExpressions)\n            });\n        else\n            local varScope, var = self:getCreateClosureVar(#node.args + math.random(0, 5));\n            scope:addReferenceToHigherScope(varScope, var);\n            retrieveExpression = Ast.FunctionCallExpression(Ast.VariableExpression(varScope, var), {\n                Ast.NumberExpression(block.id),\n                Ast.TableConstructorExpression(upvalueExpressions)\n            });\n        end\n\n        self:addStatement(self:setRegister(scope, retReg, retrieveExpression), {retReg}, usedRegs, false);\n        return retReg;\n    end\n\n    function Compiler:compileBlock(block, funcDepth)\n        for i, stat in ipairs(block.statements) do\n            self:compileStatement(stat, funcDepth);\n        end\n\n        local scope = self.activeBlock.scope;\n        for id, name in ipairs(block.scope.variables) do\n            local varReg = self:getVarRegister(block.scope, id, funcDepth, nil);\n            if self:isUpvalue(block.scope, id) then\n                scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);\n                self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {\n                    self:register(scope, varReg)\n                })), {varReg}, {varReg}, false);\n            else\n                self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);\n            end\n            self:freeRegister(varReg, true);\n        end\n    end\nend\n\n"
  },
  {
    "path": "src/prometheus/compiler/compiler.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- compiler.lua\n--\n-- This Script is the main compiler module.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal util = require(\"prometheus.util\");\n\nlocal lookupify = util.lookupify;\nlocal AstKind = Ast.AstKind;\n\nlocal unpack = unpack or table.unpack;\n\nlocal blockModule = require(\"prometheus.compiler.block\");\nlocal registerModule = require(\"prometheus.compiler.register\");\nlocal upvalueModule = require(\"prometheus.compiler.upvalue\");\nlocal emitModule = require(\"prometheus.compiler.emit\");\nlocal compileCoreModule = require(\"prometheus.compiler.compile_core\");\n\nlocal Compiler = {};\n\nfunction Compiler:new()\n    local compiler = {\n        blocks = {};\n        registers = {};\n        activeBlock = nil;\n        registersForVar = {};\n        usedRegisters = 0;\n        maxUsedRegister = 0;\n        registerVars = {};\n\n        VAR_REGISTER = newproxy(false);\n        RETURN_ALL = newproxy(false);\n        POS_REGISTER = newproxy(false);\n        RETURN_REGISTER = newproxy(false);\n        UPVALUE = newproxy(false);\n\n        BIN_OPS = lookupify{\n            AstKind.LessThanExpression,\n            AstKind.GreaterThanExpression,\n            AstKind.LessThanOrEqualsExpression,\n            AstKind.GreaterThanOrEqualsExpression,\n            AstKind.NotEqualsExpression,\n            AstKind.EqualsExpression,\n            AstKind.StrCatExpression,\n            AstKind.AddExpression,\n            AstKind.SubExpression,\n            AstKind.MulExpression,\n            AstKind.DivExpression,\n            AstKind.ModExpression,\n            AstKind.PowExpression,\n        };\n    };\n\n    setmetatable(compiler, self);\n    self.__index = self;\n\n    return compiler;\nend\n\nblockModule(Compiler);\nregisterModule(Compiler);\nupvalueModule(Compiler);\nemitModule(Compiler);\ncompileCoreModule(Compiler);\n\nfunction Compiler:pushRegisterUsageInfo()\n    table.insert(self.registerUsageStack, {\n        usedRegisters = self.usedRegisters;\n        registers = self.registers;\n    });\n    self.usedRegisters = 0;\n    self.registers = {};\nend\n\nfunction Compiler:popRegisterUsageInfo()\n    local info = table.remove(self.registerUsageStack);\n    self.usedRegisters = info.usedRegisters;\n    self.registers = info.registers;\nend\n\nfunction Compiler:compile(ast)\n    self.blocks = {};\n    self.registers = {};\n    self.activeBlock = nil;\n    self.registersForVar = {};\n    self.scopeFunctionDepths = {};\n    self.maxUsedRegister = 0;\n    self.usedRegisters = 0;\n    self.registerVars = {};\n    self.usedBlockIds = {};\n\n    self.upvalVars = {};\n    self.registerUsageStack = {};\n\n    self.upvalsProxyLenReturn = math.random(-2^22, 2^22);\n\n    local newGlobalScope = Scope:newGlobal();\n    local psc = Scope:new(newGlobalScope, nil);\n\n    local _, getfenvVar = newGlobalScope:resolve(\"getfenv\");\n    local _, tableVar = newGlobalScope:resolve(\"table\");\n    local _, unpackVar = newGlobalScope:resolve(\"unpack\");\n    local _, envVar = newGlobalScope:resolve(\"_ENV\");\n    local _, newproxyVar = newGlobalScope:resolve(\"newproxy\");\n    local _, setmetatableVar = newGlobalScope:resolve(\"setmetatable\");\n    local _, getmetatableVar = newGlobalScope:resolve(\"getmetatable\");\n    local _, selectVar = newGlobalScope:resolve(\"select\");\n\n    psc:addReferenceToHigherScope(newGlobalScope, getfenvVar, 2);\n    psc:addReferenceToHigherScope(newGlobalScope, tableVar);\n    psc:addReferenceToHigherScope(newGlobalScope, unpackVar);\n    psc:addReferenceToHigherScope(newGlobalScope, envVar);\n    psc:addReferenceToHigherScope(newGlobalScope, newproxyVar);\n    psc:addReferenceToHigherScope(newGlobalScope, setmetatableVar);\n    psc:addReferenceToHigherScope(newGlobalScope, getmetatableVar);\n\n    self.scope = Scope:new(psc);\n    self.envVar = self.scope:addVariable();\n    self.containerFuncVar = self.scope:addVariable();\n    self.unpackVar = self.scope:addVariable();\n    self.newproxyVar = self.scope:addVariable();\n    self.setmetatableVar = self.scope:addVariable();\n    self.getmetatableVar = self.scope:addVariable();\n    self.selectVar = self.scope:addVariable();\n\n    local argVar = self.scope:addVariable();\n\n    self.containerFuncScope = Scope:new(self.scope);\n    self.whileScope = Scope:new(self.containerFuncScope);\n\n    self.posVar = self.containerFuncScope:addVariable();\n    self.argsVar = self.containerFuncScope:addVariable();\n    self.currentUpvaluesVar = self.containerFuncScope:addVariable();\n    self.detectGcCollectVar = self.containerFuncScope:addVariable();\n    self.returnVar = self.containerFuncScope:addVariable();\n\n    self.upvaluesTable = self.scope:addVariable();\n    self.upvaluesReferenceCountsTable = self.scope:addVariable();\n    self.allocUpvalFunction = self.scope:addVariable();\n    self.currentUpvalId = self.scope:addVariable();\n\n    self.upvaluesProxyFunctionVar = self.scope:addVariable();\n    self.upvaluesGcFunctionVar = self.scope:addVariable();\n    self.freeUpvalueFunc = self.scope:addVariable();\n\n    self.createClosureVars = {};\n    self.createVarargClosureVar = self.scope:addVariable();\n    local createClosureScope = Scope:new(self.scope);\n    local createClosurePosArg = createClosureScope:addVariable();\n    local createClosureUpvalsArg = createClosureScope:addVariable();\n    local createClosureProxyObject = createClosureScope:addVariable();\n    local createClosureFuncVar = createClosureScope:addVariable();\n\n    local createClosureSubScope = Scope:new(createClosureScope);\n\n    local upvalEntries = {};\n    local upvalueIds = {};\n    self.getUpvalueId = function(self, scope, id)\n        local expression;\n        local scopeFuncDepth = self.scopeFunctionDepths[scope];\n        if(scopeFuncDepth == 0) then\n            if upvalueIds[id] then\n                return upvalueIds[id];\n            end\n            expression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {});\n        else\n            require(\"logger\"):error(\"Unresolved Upvalue, this error should not occur!\");\n        end\n        table.insert(upvalEntries, Ast.TableEntry(expression));\n        local uid = #upvalEntries;\n        upvalueIds[id] = uid;\n        return uid;\n    end\n\n    createClosureSubScope:addReferenceToHigherScope(self.scope, self.containerFuncVar);\n    createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosurePosArg)\n    createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureUpvalsArg, 1)\n    createClosureScope:addReferenceToHigherScope(self.scope, self.upvaluesProxyFunctionVar)\n    createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureProxyObject);\n\n    self:compileTopNode(ast);\n\n    local functionNodeAssignments = {\n        {\n            var = Ast.AssignmentVariable(self.scope, self.containerFuncVar),\n            val = Ast.FunctionLiteralExpression({\n                Ast.VariableExpression(self.containerFuncScope, self.posVar),\n                Ast.VariableExpression(self.containerFuncScope, self.argsVar),\n                Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar),\n                Ast.VariableExpression(self.containerFuncScope, self.detectGcCollectVar)\n            }, self:emitContainerFuncBody());\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.createVarargClosureVar),\n            val = Ast.FunctionLiteralExpression({\n                    Ast.VariableExpression(createClosureScope, createClosurePosArg),\n                    Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),\n                },\n                Ast.Block({\n                    Ast.LocalVariableDeclaration(createClosureScope, {\n                        createClosureProxyObject\n                    }, {\n                        Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar), {\n                            Ast.VariableExpression(createClosureScope, createClosureUpvalsArg)\n                        })\n                    }),\n                    Ast.LocalVariableDeclaration(createClosureScope, {createClosureFuncVar},{\n                        Ast.FunctionLiteralExpression({\n                            Ast.VarargExpression();\n                        },\n                        Ast.Block({\n                            Ast.ReturnStatement{\n                                Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.containerFuncVar), {\n                                    Ast.VariableExpression(createClosureScope, createClosurePosArg),\n                                    Ast.TableConstructorExpression({Ast.TableEntry(Ast.VarargExpression())}),\n                                    Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),\n                                    Ast.VariableExpression(createClosureScope, createClosureProxyObject)\n                                })\n                            }\n                        }, createClosureSubScope)\n                        );\n                    });\n                    Ast.ReturnStatement{Ast.VariableExpression(createClosureScope, createClosureFuncVar)};\n                }, createClosureScope)\n            );\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.upvaluesTable),\n            val = Ast.TableConstructorExpression({}),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.upvaluesReferenceCountsTable),\n            val = Ast.TableConstructorExpression({}),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.allocUpvalFunction),\n            val = self:createAllocUpvalFunction(),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.currentUpvalId),\n            val = Ast.NumberExpression(0),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.upvaluesProxyFunctionVar),\n            val = self:createUpvaluesProxyFunc(),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.upvaluesGcFunctionVar),\n            val = self:createUpvaluesGcFunc(),\n        }, {\n            var = Ast.AssignmentVariable(self.scope, self.freeUpvalueFunc),\n            val = self:createFreeUpvalueFunc(),\n        },\n    }\n\n    local tbl = {\n        Ast.VariableExpression(self.scope, self.containerFuncVar),\n        Ast.VariableExpression(self.scope, self.createVarargClosureVar),\n        Ast.VariableExpression(self.scope, self.upvaluesTable),\n        Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable),\n        Ast.VariableExpression(self.scope, self.allocUpvalFunction),\n        Ast.VariableExpression(self.scope, self.currentUpvalId),\n        Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar),\n        Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar),\n        Ast.VariableExpression(self.scope, self.freeUpvalueFunc),\n    };\n    for i, entry in pairs(self.createClosureVars) do\n        table.insert(functionNodeAssignments, entry);\n        table.insert(tbl, Ast.VariableExpression(entry.var.scope, entry.var.id));\n    end\n\n    util.shuffle(functionNodeAssignments);\n    local assignmentStatLhs, assignmentStatRhs = {}, {};\n    for i, v in ipairs(functionNodeAssignments) do\n        assignmentStatLhs[i] = v.var;\n        assignmentStatRhs[i] = v.val;\n    end\n\n\n    -- NEW: Position Shuffler\n    local ids = util.shuffle({1, 2, 3, 4, 5, 6, 7});\n\n    local items = {\n        Ast.VariableExpression(self.scope, self.envVar),\n        Ast.VariableExpression(self.scope, self.unpackVar),\n        Ast.VariableExpression(self.scope, self.newproxyVar),\n        Ast.VariableExpression(self.scope, self.setmetatableVar),\n        Ast.VariableExpression(self.scope, self.getmetatableVar),\n        Ast.VariableExpression(self.scope, self.selectVar),\n        Ast.VariableExpression(self.scope, argVar),\n    }\n\n    local astItems = {\n        Ast.OrExpression(Ast.AndExpression(Ast.VariableExpression(newGlobalScope, getfenvVar), Ast.FunctionCallExpression(Ast.VariableExpression(newGlobalScope, getfenvVar), {})), Ast.VariableExpression(newGlobalScope, envVar));\n        Ast.OrExpression(Ast.VariableExpression(newGlobalScope, unpackVar), Ast.IndexExpression(Ast.VariableExpression(newGlobalScope, tableVar), Ast.StringExpression(\"unpack\")));\n        Ast.VariableExpression(newGlobalScope, newproxyVar);\n        Ast.VariableExpression(newGlobalScope, setmetatableVar);\n        Ast.VariableExpression(newGlobalScope, getmetatableVar);\n        Ast.VariableExpression(newGlobalScope, selectVar);\n        Ast.TableConstructorExpression({\n            Ast.TableEntry(Ast.VarargExpression());\n        })\n    }\n\n    local functionNode = Ast.FunctionLiteralExpression({\n      items[ids[1]], items[ids[2]], items[ids[3]], items[ids[4]],\n      items[ids[5]], items[ids[6]], items[ids[7]],\n      unpack(util.shuffle(tbl))\n    }, Ast.Block({\n        Ast.AssignmentStatement(assignmentStatLhs, assignmentStatRhs);\n        Ast.ReturnStatement{\n            Ast.FunctionCallExpression(Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.createVarargClosureVar), {\n                    Ast.NumberExpression(self.startBlockId);\n                    Ast.TableConstructorExpression(upvalEntries);\n                }), {Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {Ast.VariableExpression(self.scope, argVar)})});\n        }\n    }, self.scope));\n\n    return Ast.TopNode(Ast.Block({\n        Ast.ReturnStatement{Ast.FunctionCallExpression(functionNode, {\n            astItems[ids[1]], astItems[ids[2]], astItems[ids[3]], astItems[ids[4]],\n            astItems[ids[5]], astItems[ids[6]], astItems[ids[7]],\n        })};\n    }, psc), newGlobalScope);\nend\n\nfunction Compiler:getCreateClosureVar(argCount)\n    if not self.createClosureVars[argCount] then\n        local var = Ast.AssignmentVariable(self.scope, self.scope:addVariable());\n        local createClosureScope = Scope:new(self.scope);\n        local createClosureSubScope = Scope:new(createClosureScope);\n\n        local createClosurePosArg = createClosureScope:addVariable();\n        local createClosureUpvalsArg = createClosureScope:addVariable();\n        local createClosureProxyObject = createClosureScope:addVariable();\n        local createClosureFuncVar = createClosureScope:addVariable();\n\n        createClosureSubScope:addReferenceToHigherScope(self.scope, self.containerFuncVar);\n        createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosurePosArg)\n        createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureUpvalsArg, 1)\n        createClosureScope:addReferenceToHigherScope(self.scope, self.upvaluesProxyFunctionVar)\n        createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureProxyObject);\n\n        local  argsTb, argsTb2 = {}, {};\n        for i = 1, argCount do\n            local arg = createClosureSubScope:addVariable()\n            argsTb[i] = Ast.VariableExpression(createClosureSubScope, arg);\n            argsTb2[i] = Ast.TableEntry(Ast.VariableExpression(createClosureSubScope, arg));\n        end\n\n        local val = Ast.FunctionLiteralExpression({\n            Ast.VariableExpression(createClosureScope, createClosurePosArg),\n            Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),\n        }, Ast.Block({\n                Ast.LocalVariableDeclaration(createClosureScope, {\n                    createClosureProxyObject\n                }, {\n                    Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar), {\n                        Ast.VariableExpression(createClosureScope, createClosureUpvalsArg)\n                    })\n                }),\n                Ast.LocalVariableDeclaration(createClosureScope, {createClosureFuncVar},{\n                    Ast.FunctionLiteralExpression(argsTb,\n                    Ast.Block({\n                        Ast.ReturnStatement{\n                            Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.containerFuncVar), {\n                                Ast.VariableExpression(createClosureScope, createClosurePosArg),\n                                Ast.TableConstructorExpression(argsTb2),\n                                Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),\n                                Ast.VariableExpression(createClosureScope, createClosureProxyObject)\n                            })\n                        }\n                    }, createClosureSubScope)\n                    );\n                });\n                Ast.ReturnStatement{Ast.VariableExpression(createClosureScope, createClosureFuncVar)}\n            }, createClosureScope)\n        );\n        self.createClosureVars[argCount] = {\n            var = var,\n            val = val,\n        }\n    end\n\n\n    local var = self.createClosureVars[argCount].var;\n    return var.scope, var.id;\nend\n\nreturn Compiler;\n"
  },
  {
    "path": "src/prometheus/compiler/constants.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- constants.lua\n--\n-- This Script contains the compiler constants shared across modules.\n\nreturn {\n    MAX_REGS = 100,\n    MAX_REGS_MUL = 0,\n}\n\n"
  },
  {
    "path": "src/prometheus/compiler/emit.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- emit.lua\n--\n-- This Script contains the container function body emission for the compiler.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal util = require(\"prometheus.util\");\nlocal constants = require(\"prometheus.compiler.constants\");\nlocal AstKind = Ast.AstKind;\n\nlocal MAX_REGS = constants.MAX_REGS;\n\nreturn function(Compiler)\n    local function hasAnyEntries(tbl)\n        return type(tbl) == \"table\" and next(tbl) ~= nil;\n    end\n\n    local function unionLookupTables(a, b)\n        local out = {};\n        for k, v in pairs(a or {}) do\n            out[k] = v;\n        end\n        for k, v in pairs(b or {}) do\n            out[k] = v;\n        end\n        return out;\n    end\n\n    local function canMergeParallelAssignmentStatements(statA, statB)\n        if type(statA) ~= \"table\" or type(statB) ~= \"table\" then\n            return false;\n        end\n\n        if statA.usesUpvals or statB.usesUpvals then\n            return false;\n        end\n\n        local a = statA.statement;\n        local b = statB.statement;\n        if type(a) ~= \"table\" or type(b) ~= \"table\" then\n            return false;\n        end\n        if a.kind ~= AstKind.AssignmentStatement or b.kind ~= AstKind.AssignmentStatement then\n            return false;\n        end\n\n        if type(a.lhs) ~= \"table\" or type(a.rhs) ~= \"table\" or type(b.lhs) ~= \"table\" or type(b.rhs) ~= \"table\" then\n            return false;\n        end\n\n        if #a.lhs ~= #a.rhs or #b.lhs ~= #b.rhs then\n            return false;\n        end\n\n        -- Avoid merging vararg/call assignments because they can affect multi-return behavior.\n        local function hasUnsafeRhs(rhsList)\n            for _, rhsExpr in ipairs(rhsList) do\n                if type(rhsExpr) ~= \"table\" then\n                    return true;\n                end\n                local kind = rhsExpr.kind;\n                if kind == AstKind.FunctionCallExpression or kind == AstKind.PassSelfFunctionCallExpression or kind == AstKind.VarargExpression then\n                    return true;\n                end\n            end\n            return false;\n        end\n        if hasUnsafeRhs(a.rhs) or hasUnsafeRhs(b.rhs) then\n            return false;\n        end\n\n        local aReads = type(statA.reads) == \"table\" and statA.reads or {};\n        local aWrites = type(statA.writes) == \"table\" and statA.writes or {};\n        local bReads = type(statB.reads) == \"table\" and statB.reads or {};\n        local bWrites = type(statB.writes) == \"table\" and statB.writes or {};\n\n        -- Allow merging even if one statement has no writes (e.g., x = o(x) style assignments)\n        -- Only require that at least one of them has writes\n        if not hasAnyEntries(aWrites) and not hasAnyEntries(bWrites) then\n            return false;\n        end\n\n        for r in pairs(aReads) do\n            if bWrites[r] then\n                return false;\n            end\n        end\n\n        for r, b in pairs(aWrites) do\n            if bWrites[r] or bReads[r] then\n                return false;\n            end\n        end\n\n        return true;\n    end\n\n    local function mergeParallelAssignmentStatements(statA, statB)\n        local lhs = {};\n        local rhs = {};\n        local aLhs, bLhs = statA.statement.lhs, statB.statement.lhs;\n        local aRhs, bRhs = statA.statement.rhs, statB.statement.rhs;\n        for i = 1, #aLhs do lhs[i] = aLhs[i]; end\n        for i = 1, #bLhs do lhs[#aLhs + i] = bLhs[i]; end\n        for i = 1, #aRhs do rhs[i] = aRhs[i]; end\n        for i = 1, #bRhs do rhs[#aRhs + i] = bRhs[i]; end\n\n        return {\n            statement = Ast.AssignmentStatement(lhs, rhs),\n            writes = unionLookupTables(statA.writes, statB.writes),\n            reads = unionLookupTables(statA.reads, statB.reads),\n            usesUpvals = statA.usesUpvals or statB.usesUpvals,\n        };\n    end\n\n    local function mergeAdjacentParallelAssignments(blockstats)\n        local merged = {};\n        local i = 1;\n        while i <= #blockstats do\n            local stat = blockstats[i];\n            i = i + 1;\n\n            while i <= #blockstats and canMergeParallelAssignmentStatements(stat, blockstats[i]) do\n                stat = mergeParallelAssignmentStatements(stat, blockstats[i]);\n                i = i + 1;\n            end\n\n            table.insert(merged, stat);\n        end\n        return merged;\n    end\n\n    function Compiler:emitContainerFuncBody()\n        local blocks = {};\n\n        util.shuffle(self.blocks);\n\n        for i, block in ipairs(self.blocks) do\n            local id = block.id;\n            local blockstats = block.statements;\n\n            for i = 2, #blockstats do\n                local stat = blockstats[i];\n                local reads = stat.reads;\n                local writes = stat.writes;\n                local maxShift = 0;\n                local usesUpvals = stat.usesUpvals;\n                for shift = 1, i - 1 do\n                    local stat2 = blockstats[i - shift];\n\n                    if stat2.usesUpvals and usesUpvals then\n                        break;\n                    end\n\n                    local reads2 = stat2.reads;\n                    local writes2 = stat2.writes;\n                    local f = true;\n\n                    for r, b in pairs(reads2) do\n                        if(writes[r]) then\n                            f = false;\n                            break;\n                        end\n                    end\n\n                    if f then\n                        for r, b in pairs(writes2) do\n                            if(writes[r]) then\n                                f = false;\n                                break;\n                            end\n                            if(reads[r]) then\n                                f = false;\n                                break;\n                            end\n                        end\n                    end\n\n                    if not f then\n                        break\n                    end\n\n                    maxShift = shift;\n                end\n\n                local shift = math.random(0, maxShift);\n                for j = 1, shift do\n                    blockstats[i - j], blockstats[i - j + 1] = blockstats[i - j + 1], blockstats[i - j];\n                end\n            end\n\n            local mergedBlockStats = mergeAdjacentParallelAssignments(blockstats);\n            for _=1, 7 do\n                mergedBlockStats = mergeAdjacentParallelAssignments(mergedBlockStats);\n            end\n\n            blockstats = {};\n            for _, stat in ipairs(mergedBlockStats) do\n                table.insert(blockstats, stat.statement);\n            end\n\n            local block = { id = id, index = i, block = Ast.Block(blockstats, block.scope) }\n            table.insert(blocks, block);\n            blocks[id] = block;\n        end\n\n        table.sort(blocks, function(a, b) return a.id < b.id end);\n\n        -- Build a strict threshold condition between adjacent block IDs.\n        -- Using a midpoint avoids exact-id comparisons while preserving dispatch.\n        local function buildBlockThresholdCondition(scope, leftId, rightId, useAndOr)\n            local bound = math.floor((leftId + rightId) / 2);\n            local posExpr = self:pos(scope);\n            local boundExpr = Ast.NumberExpression(bound);\n\n            if useAndOr then\n                -- Kept for compatibility with caller variations.\n                return Ast.LessThanExpression(posExpr, boundExpr);\n            else\n                local variant = math.random(1, 2);\n                if variant == 1 then\n                    return Ast.LessThanExpression(posExpr, boundExpr);\n                else\n                    return Ast.GreaterThanExpression(boundExpr, posExpr);\n                end\n            end\n        end\n\n        -- Build an elseif chain for a range of blocks\n        local function buildElseifChain(tb, l, r, pScope)\n            -- Handle invalid range by returning an empty block\n            if r < l then\n                local emptyScope = Scope:new(pScope);\n                return Ast.Block({}, emptyScope);\n            end\n\n            local len = r - l + 1;\n\n            -- For single block\n            if len == 1 then\n                tb[l].block.scope:setParent(pScope);\n                return tb[l].block;\n            end\n\n            -- For small ranges, use elseif chain\n            if len <= 4 then\n                local ifScope = Scope:new(pScope);\n                local elseifs = {};\n\n                -- First block uses the first midpoint threshold\n                tb[l].block.scope:setParent(ifScope);\n                local firstCondition = buildBlockThresholdCondition(ifScope, tb[l].id, tb[l + 1].id, false);\n                local firstBlock = tb[l].block;\n\n                -- Middle blocks use their upper midpoint threshold\n                for i = l + 1, r - 1 do\n                    tb[i].block.scope:setParent(ifScope);\n                    local condition = buildBlockThresholdCondition(ifScope, tb[i].id, tb[i + 1].id, false);\n                    table.insert(elseifs, {\n                        condition = condition,\n                        body = tb[i].block\n                    });\n                end\n\n                -- Last block becomes else\n                tb[r].block.scope:setParent(ifScope);\n                local elseBlock = tb[r].block;\n\n                return Ast.Block({\n                    Ast.IfStatement(firstCondition, firstBlock, elseifs, elseBlock);\n                }, ifScope);\n            end\n\n            -- For larger ranges, use binary split with and/or chaining\n            local mid = l + math.ceil(len / 2);\n            local leftMaxId = tb[mid - 1].id;\n            local rightMinId = tb[mid].id;\n            -- Float-safe split: any bound strictly between adjacent IDs works.\n            -- Midpoint avoids integer-only math.random(min, max) behavior.\n            local bound = math.floor((leftMaxId + rightMinId) / 2);\n            local ifScope = Scope:new(pScope);\n\n            local lBlock = buildElseifChain(tb, l, mid - 1, ifScope);\n            local rBlock = buildElseifChain(tb, mid, r, ifScope);\n\n            -- Randomly choose between different condition styles\n            local condStyle = math.random(1, 3);\n            local condition;\n            local trueBlock, falseBlock;\n\n            if condStyle == 1 then\n                -- pos < bound\n                condition = Ast.LessThanExpression(self:pos(ifScope), Ast.NumberExpression(bound));\n                trueBlock, falseBlock = lBlock, rBlock;\n            elseif condStyle == 2 then\n                -- bound > pos\n                condition = Ast.GreaterThanExpression(Ast.NumberExpression(bound), self:pos(ifScope));\n                trueBlock, falseBlock = lBlock, rBlock;\n            else\n                -- Equivalent split using strict > with branches reversed.\n                condition = Ast.GreaterThanExpression(self:pos(ifScope), Ast.NumberExpression(bound));\n                trueBlock, falseBlock = rBlock, lBlock;\n            end\n\n            return Ast.Block({\n                Ast.IfStatement(condition, trueBlock, {}, falseBlock);\n            }, ifScope);\n        end\n\n        local whileBody = buildElseifChain(blocks, 1, #blocks, self.containerFuncScope);\n        if self.whileScope then\n            -- Ensure whileScope is properly connected\n            self.whileScope:setParent(self.containerFuncScope);\n        end\n\n        self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar, 1);\n        self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n\n        self.containerFuncScope:addReferenceToHigherScope(self.scope, self.unpackVar);\n\n        local declarations = {\n            self.returnVar,\n        }\n\n        for i, var in pairs(self.registerVars) do\n            if(i ~= MAX_REGS) then\n                table.insert(declarations, var);\n            end\n        end\n\n        local stats = {}\n\n        if self.maxUsedRegister >= MAX_REGS then\n            table.insert(stats, Ast.LocalVariableDeclaration(self.containerFuncScope, {self.registerVars[MAX_REGS]}, {Ast.TableConstructorExpression({})}));\n        end\n\n        table.insert(stats, Ast.LocalVariableDeclaration(self.containerFuncScope, util.shuffle(declarations), {}));\n\n        table.insert(stats, Ast.WhileStatement(whileBody, Ast.VariableExpression(self.containerFuncScope, self.posVar)));\n\n\n        table.insert(stats, Ast.AssignmentStatement({\n            Ast.AssignmentVariable(self.containerFuncScope, self.posVar)\n        }, {\n            Ast.LenExpression(Ast.VariableExpression(self.containerFuncScope, self.detectGcCollectVar))\n        }));\n\n        table.insert(stats, Ast.ReturnStatement{\n            Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {\n                Ast.VariableExpression(self.containerFuncScope, self.returnVar)\n            });\n        });\n\n        return Ast.Block(stats, self.containerFuncScope);\n    end\nend\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/and.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- and.lua\n--\n-- This Script contains the expression handler for the AndExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local posState = self.registers[self.POS_REGISTER];\n    self.registers[self.POS_REGISTER] = self.VAR_REGISTER;\n\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i ~= 1 then\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n\n    local resReg = regs[1];\n    local tmpReg;\n\n    if posState then\n        tmpReg = self:allocRegister(false);\n        self:addStatement(self:copyRegisters(scope, {tmpReg}, {self.POS_REGISTER}), {tmpReg}, {self.POS_REGISTER}, false);\n    end\n\n    local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];\n    if expression.rhs.isConstant then\n        local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n        self:addStatement(self:setRegister(scope, resReg, Ast.AndExpression(self:register(scope, lhsReg), self:register(scope, rhsReg))), {resReg}, {lhsReg, rhsReg}, false);\n        if tmpReg then\n            self:freeRegister(tmpReg, false);\n        end\n        self:freeRegister(lhsReg, false);\n        self:freeRegister(rhsReg, false);\n        return regs;\n    end\n\n    local block1, block2 = self:createBlock(), self:createBlock();\n    self:addStatement(self:copyRegisters(scope, {resReg}, {lhsReg}), {resReg}, {lhsReg}, false);\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, lhsReg), Ast.NumberExpression(block1.id)), Ast.NumberExpression(block2.id))), {self.POS_REGISTER}, {lhsReg}, false);\n    self:freeRegister(lhsReg, false);\n    do\n        self:setActiveBlock(block1);\n        scope = block1.scope;\n        local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n        self:addStatement(self:copyRegisters(scope, {resReg}, {rhsReg}), {resReg}, {rhsReg}, false);\n        self:freeRegister(rhsReg, false);\n\n        self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(block2.id)), {self.POS_REGISTER}, {}, false);\n    end\n\n    self.registers[self.POS_REGISTER] = posState;\n\n    self:setActiveBlock(block2);\n    scope = block2.scope;\n\n    if tmpReg then\n        self:addStatement(self:copyRegisters(scope, {self.POS_REGISTER}, {tmpReg}), {self.POS_REGISTER}, {tmpReg}, false);\n        self:freeRegister(tmpReg, false);\n    end\n\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/binary.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- binary.lua\n--\n-- This Script contains the expression handler for the Binary operations (Add, Sub, Mul, Div, etc.).\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];\n            local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n\n            local binaryExpr = Ast[expression.kind](self:register(scope, lhsReg), self:register(scope, rhsReg));\n            self:addStatement(self:setRegister(scope, regs[i], binaryExpr), {regs[i]}, {lhsReg, rhsReg}, true);\n            self:freeRegister(rhsReg, false);\n            self:freeRegister(lhsReg, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/boolean.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- boolean.lua\n--\n-- This Script contains the expression handler for the BooleanExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nlocal expressionEvaluators = {\n    [Ast.GreaterThanExpression] = function(left, right)\n        return left > right\n    end,\n    [Ast.LessThanExpression] = function(left, right)\n        return left < right\n    end,\n    [Ast.GreaterThanOrEqualsExpression] = function(left, right)\n        return left >= right\n    end,\n    [Ast.LessThanOrEqualsExpression] = function(left, right)\n        return left <= right\n    end,\n    [Ast.NotEqualsExpression] = function(left, right)\n        return left ~= right\n    end,\n}\n\nlocal function createRandomASTCFlowExpression(resultBool)\n    local expTB = {\n        Ast.GreaterThanExpression,\n        Ast.LessThanExpression,\n        Ast.GreaterThanOrEqualsExpression,\n        Ast.LessThanOrEqualsExpression,\n        Ast.NotEqualsExpression\n    }\n\n    local leftInt, rightInt, boolResult, randomExp\n    repeat\n        randomExp = expTB[math.random(1, #expTB)]\n        leftInt = Ast.NumberExpression(math.random(1, 2^24))\n        rightInt = Ast.NumberExpression(math.random(1, 2^24))\n        boolResult = expressionEvaluators[randomExp](leftInt.value, rightInt.value)\n    until boolResult == resultBool\n\n    return randomExp(leftInt, rightInt, false)\nend\n\nreturn function(self, expression, _, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            self:addStatement(self:setRegister(scope, regs[i], createRandomASTCFlowExpression(expression.value)), {regs[i]}, {}, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/function_call.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- function_call.lua\n--\n-- This Script contains the expression handler for the FunctionCallExpression.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];\n\n    local retRegs = {};\n    local returnAll = numReturns == self.RETURN_ALL;\n    if returnAll then\n        retRegs[1] = self:allocRegister(false);\n    else\n        for i = 1, numReturns do\n            retRegs[i] = self:allocRegister(false);\n        end\n    end\n\n    local regs = {};\n    local args = {};\n    for i, expr in ipairs(expression.args) do\n        if i == #expression.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then\n            local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];\n            table.insert(args, Ast.FunctionCallExpression(\n                self:unpack(scope),\n                {self:register(scope, reg)}));\n            table.insert(regs, reg);\n        else\n            local reg = self:compileExpression(expr, funcDepth, 1)[1];\n            table.insert(args, self:register(scope, reg));\n            table.insert(regs, reg);\n        end\n    end\n\n    if returnAll then\n        self:addStatement(self:setRegister(scope, retRegs[1], Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, baseReg), args))}), {retRegs[1]}, {baseReg, unpack(regs)}, true);\n    else\n        if numReturns > 1 then\n            local tmpReg = self:allocRegister(false);\n\n            self:addStatement(self:setRegister(scope, tmpReg, Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, baseReg), args))}), {tmpReg}, {baseReg, unpack(regs)}, true);\n\n\n            for i, reg in ipairs(retRegs) do\n                self:addStatement(self:setRegister(scope, reg, Ast.IndexExpression(self:register(scope, tmpReg), Ast.NumberExpression(i))), {reg}, {tmpReg}, false);\n            end\n\n            self:freeRegister(tmpReg, false);\n        else\n            self:addStatement(self:setRegister(scope, retRegs[1], Ast.FunctionCallExpression(self:register(scope, baseReg), args)), {retRegs[1]}, {baseReg, unpack(regs)}, true);\n        end\n    end\n\n    self:freeRegister(baseReg, false);\n    for i, reg in ipairs(regs) do\n        self:freeRegister(reg, false);\n    end\n\n    return retRegs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/function_literal.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- function_literal.lua\n--\n-- This Script contains the expression handler for the FunctionLiteralExpression\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        if i == 1 then\n            regs[i] = self:compileFunction(expression, funcDepth);\n        else\n            regs[i] = self:allocRegister();\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/index.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- index.lua\n--\n-- This Script contains the expression handler for the IndexExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];\n            local indexReg = self:compileExpression(expression.index, funcDepth, 1)[1];\n\n\n\n            self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, indexReg))), {regs[i]}, {baseReg, indexReg}, true);\n\n            self:freeRegister(baseReg, false);\n            self:freeRegister(indexReg, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/len.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- len.lua\n--\n-- This Script contains the expression handler for the LenExpression\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n\n            self:addStatement(self:setRegister(scope, regs[i], Ast.LenExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, true);\n            self:freeRegister(rhsReg, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/negate.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- negate.lua\n--\n-- This Script contains the expression handler for the NegateExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NegateExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, true);\n            self:freeRegister(rhsReg, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/nil.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- nil.lua\n--\n-- This Script contains the expression handler for the NilExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/not.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- not.lua\n--\n-- This Script contains the expression handler for the NotExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NotExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, false);\n            self:freeRegister(rhsReg, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/number.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- number.lua\n--\n-- This Script contains the expression handler for the NumberExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NumberExpression(expression.value)), {regs[i]}, {}, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/or.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- or.lua\n--\n-- This Script contains the expression handler for the OrExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local posState = self.registers[self.POS_REGISTER];\n    self.registers[self.POS_REGISTER] = self.VAR_REGISTER;\n\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i ~= 1 then\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n\n    local resReg = regs[1];\n    local tmpReg;\n\n    if posState then\n        tmpReg = self:allocRegister(false);\n        self:addStatement(self:copyRegisters(scope, {tmpReg}, {self.POS_REGISTER}), {tmpReg}, {self.POS_REGISTER}, false);\n    end\n\n    local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];\n    if expression.rhs.isConstant then\n        local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n        self:addStatement(self:setRegister(scope, resReg, Ast.OrExpression(self:register(scope, lhsReg), self:register(scope, rhsReg))), {resReg}, {lhsReg, rhsReg}, false);\n        if tmpReg then\n            self:freeRegister(tmpReg, false);\n        end\n        self:freeRegister(lhsReg, false);\n        self:freeRegister(rhsReg, false);\n        return regs;\n    end\n\n    local block1, block2 = self:createBlock(), self:createBlock();\n    self:addStatement(self:copyRegisters(scope, {resReg}, {lhsReg}), {resReg}, {lhsReg}, false);\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, lhsReg), Ast.NumberExpression(block2.id)), Ast.NumberExpression(block1.id))), {self.POS_REGISTER}, {lhsReg}, false);\n    self:freeRegister(lhsReg, false);\n\n    do\n        self:setActiveBlock(block1);\n        local scope = block1.scope;\n        local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];\n        self:addStatement(self:copyRegisters(scope, {resReg}, {rhsReg}), {resReg}, {rhsReg}, false);\n        self:freeRegister(rhsReg, false);\n\n\n        self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(block2.id)), {self.POS_REGISTER}, {}, false);\n    end\n\n    self.registers[self.POS_REGISTER] = posState;\n\n    self:setActiveBlock(block2);\n    scope = block2.scope;\n\n    if tmpReg then\n        self:addStatement(self:copyRegisters(scope, {self.POS_REGISTER}, {tmpReg}), {self.POS_REGISTER}, {tmpReg}, false);\n        self:freeRegister(tmpReg, false);\n    end\n\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/pass_self_function_call.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- pass_self_function_call.lua\n--\n-- This Script contains the expression handler for the PassSelfFunctionCallExpression.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];\n    local retRegs = {};\n    local returnAll = numReturns == self.RETURN_ALL;\n    if returnAll then\n        retRegs[1] = self:allocRegister(false);\n    else\n        for i = 1, numReturns do\n            retRegs[i] = self:allocRegister(false);\n        end\n    end\n\n    local args = { self:register(scope, baseReg) };\n    local regs = { baseReg };\n\n    for i, expr in ipairs(expression.args) do\n        if i == #expression.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then\n            local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];\n            table.insert(args, Ast.FunctionCallExpression(\n                self:unpack(scope),\n                {self:register(scope, reg)}));\n            table.insert(regs, reg);\n        else\n            local reg = self:compileExpression(expr, funcDepth, 1)[1];\n            table.insert(args, self:register(scope, reg));\n            table.insert(regs, reg);\n        end\n    end\n\n    if returnAll or numReturns > 1 then\n        local tmpReg = self:allocRegister(false);\n\n        self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.passSelfFunctionName)), {tmpReg}, {}, false);\n        self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {baseReg, tmpReg}, false);\n\n        if returnAll then\n            self:addStatement(self:setRegister(scope, retRegs[1], Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, tmpReg), args))}), {retRegs[1]}, {tmpReg, unpack(regs)}, true);\n        else\n            self:addStatement(self:setRegister(scope, tmpReg, Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, tmpReg), args))}), {tmpReg}, {tmpReg, unpack(regs)}, true);\n\n            for i, reg in ipairs(retRegs) do\n                self:addStatement(self:setRegister(scope, reg, Ast.IndexExpression(self:register(scope, tmpReg), Ast.NumberExpression(i))), {reg}, {tmpReg}, false);\n            end\n        end\n\n        self:freeRegister(tmpReg, false);\n    else\n        local tmpReg = retRegs[1] or self:allocRegister(false);\n\n        self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.passSelfFunctionName)), {tmpReg}, {}, false);\n        self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {baseReg, tmpReg}, false);\n        self:addStatement(self:setRegister(scope, retRegs[1], Ast.FunctionCallExpression(self:register(scope, tmpReg), args)), {retRegs[1]}, {baseReg, unpack(regs)}, true);\n    end\n\n    for i, reg in ipairs(regs) do\n        self:freeRegister(reg, false);\n    end\n\n    return retRegs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/string.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- string.lua\n--\n-- This Script contains the expression handler for the StringExpression.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns, 1 do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            self:addStatement(self:setRegister(scope, regs[i], Ast.StringExpression(expression.value)), {regs[i]}, {}, false);\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/table_constructor.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- table_constructor.lua\n--\n-- This Script contains the expression handler for the TableConstructorExpression.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister();\n        if i == 1 then\n            local entries = {};\n            local entryRegs = {};\n            for i, entry in ipairs(expression.entries) do\n                if entry.kind == AstKind.TableEntry then\n                    local value = entry.value;\n                    if i == #expression.entries and (value.kind == AstKind.FunctionCallExpression or value.kind == AstKind.PassSelfFunctionCallExpression or value.kind == AstKind.VarargExpression) then\n                        local reg = self:compileExpression(entry.value, funcDepth, self.RETURN_ALL)[1];\n                        table.insert(entries, Ast.TableEntry(Ast.FunctionCallExpression(\n                            self:unpack(scope),\n                            {self:register(scope, reg)})));\n                        table.insert(entryRegs, reg);\n                    else\n                        local reg = self:compileExpression(entry.value, funcDepth, 1)[1];\n                        table.insert(entries, Ast.TableEntry(self:register(scope, reg)));\n                        table.insert(entryRegs, reg);\n                    end\n                else\n                    local keyReg = self:compileExpression(entry.key, funcDepth, 1)[1];\n                    local valReg = self:compileExpression(entry.value, funcDepth, 1)[1];\n                    table.insert(entries, Ast.KeyedTableEntry(self:register(scope, keyReg), self:register(scope, valReg)));\n                    table.insert(entryRegs, valReg);\n                    table.insert(entryRegs, keyReg);\n                end\n            end\n            self:addStatement(self:setRegister(scope, regs[i], Ast.TableConstructorExpression(entries)), {regs[i]}, entryRegs, false);\n            for i, reg in ipairs(entryRegs) do\n                self:freeRegister(reg, false);\n            end\n        else\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/vararg.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- vararg.lua\n--\n-- This Script contains the expression handler for the VarargExpression\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    if numReturns == self.RETURN_ALL then\n        return {self.varargReg};\n    end\n    local regs = {};\n    for i = 1, numReturns do\n        regs[i] = self:allocRegister(false);\n        self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:register(scope, self.varargReg), Ast.NumberExpression(i))), {regs[i]}, {self.varargReg}, false);\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions/variable.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- variable.lua\n--\n-- This Script contains the expression handler for the VariableExpression\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, expression, funcDepth, numReturns)\n    local scope = self.activeBlock.scope;\n    local regs = {};\n    for i = 1, numReturns do\n        if i == 1 then\n            if expression.scope.isGlobal then\n                regs[i] = self:allocRegister(false);\n                local tmpReg = self:allocRegister(false);\n                self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.scope:getVariableName(expression.id))), {tmpReg}, {}, false);\n                self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:env(scope), self:register(scope, tmpReg))), {regs[i]}, {tmpReg}, true);\n                self:freeRegister(tmpReg, false);\n            else\n                if self.scopeFunctionDepths[expression.scope] == funcDepth then\n                    if self:isUpvalue(expression.scope, expression.id) then\n                        local reg = self:allocRegister(false);\n                        local varReg = self:getVarRegister(expression.scope, expression.id, funcDepth, nil);\n                        self:addStatement(self:setRegister(scope, reg, self:getUpvalueMember(scope, self:register(scope, varReg))), {reg}, {varReg}, true);\n                        regs[i] = reg;\n                    else\n                        regs[i] = self:getVarRegister(expression.scope, expression.id, funcDepth, nil);\n                    end\n                else\n                    local reg = self:allocRegister(false);\n                    local upvalId = self:getUpvalueId(expression.scope, expression.id);\n                    scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n                    self:addStatement(self:setRegister(scope, reg, self:getUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)))), {reg}, {}, true);\n                    regs[i] = reg;\n                end\n            end\n        else\n            regs[i] = self:allocRegister();\n            self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);\n        end\n    end\n    return regs;\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/expressions.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- expressions.lua\n--\n-- This Script contains the expression handlers: exports handler table keyed by AstKind.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nlocal handlers = {};\nlocal expressions = \"prometheus.compiler.expressions.\";\nlocal function requireExpression(name)\n    return require(expressions .. name);\nend\nhandlers[AstKind.StringExpression] = requireExpression(\"string\");\nhandlers[AstKind.NumberExpression] = requireExpression(\"number\");\nhandlers[AstKind.BooleanExpression] = requireExpression(\"boolean\");\nhandlers[AstKind.NilExpression] = requireExpression(\"nil\");\nhandlers[AstKind.VariableExpression] = requireExpression(\"variable\");\nhandlers[AstKind.FunctionCallExpression] = requireExpression(\"function_call\");\nhandlers[AstKind.PassSelfFunctionCallExpression] = requireExpression(\"pass_self_function_call\");\nhandlers[AstKind.IndexExpression] = requireExpression(\"index\");\nhandlers[AstKind.NotExpression] = requireExpression(\"not\");\nhandlers[AstKind.NegateExpression] = requireExpression(\"negate\");\nhandlers[AstKind.LenExpression] = requireExpression(\"len\");\nhandlers[AstKind.OrExpression] = requireExpression(\"or\");\nhandlers[AstKind.AndExpression] = requireExpression(\"and\");\nhandlers[AstKind.TableConstructorExpression] = requireExpression(\"table_constructor\");\nhandlers[AstKind.FunctionLiteralExpression] = requireExpression(\"function_literal\");\nhandlers[AstKind.VarargExpression] = requireExpression(\"vararg\");\n\n-- Binary ops share one handler\nlocal binaryHandler = requireExpression(\"binary\");\nhandlers[AstKind.LessThanExpression] = binaryHandler;\nhandlers[AstKind.GreaterThanExpression] = binaryHandler;\nhandlers[AstKind.LessThanOrEqualsExpression] = binaryHandler;\nhandlers[AstKind.GreaterThanOrEqualsExpression] = binaryHandler;\nhandlers[AstKind.NotEqualsExpression] = binaryHandler;\nhandlers[AstKind.EqualsExpression] = binaryHandler;\nhandlers[AstKind.StrCatExpression] = binaryHandler;\nhandlers[AstKind.AddExpression] = binaryHandler;\nhandlers[AstKind.SubExpression] = binaryHandler;\nhandlers[AstKind.MulExpression] = binaryHandler;\nhandlers[AstKind.DivExpression] = binaryHandler;\nhandlers[AstKind.ModExpression] = binaryHandler;\nhandlers[AstKind.PowExpression] = binaryHandler;\n\nreturn handlers;\n\n"
  },
  {
    "path": "src/prometheus/compiler/register.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- register.lua\n--\n-- This Script contains the register management for the compiler.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal constants = require(\"prometheus.compiler.constants\");\nlocal randomStrings = require(\"prometheus.randomStrings\");\n\nlocal MAX_REGS = constants.MAX_REGS;\n\nreturn function(Compiler)\n    function Compiler:freeRegister(id, force)\n        if force or not (self.registers[id] == self.VAR_REGISTER) then\n            self.usedRegisters = self.usedRegisters - 1;\n            self.registers[id] = false\n        end\n    end\n\n    function Compiler:isVarRegister(id)\n        return self.registers[id] == self.VAR_REGISTER;\n    end\n\n    function Compiler:allocRegister(isVar)\n        self.usedRegisters = self.usedRegisters + 1;\n\n        if not isVar then\n            if not self.registers[self.POS_REGISTER] then\n                self.registers[self.POS_REGISTER] = true;\n                return self.POS_REGISTER;\n            end\n\n            if not self.registers[self.RETURN_REGISTER] then\n                self.registers[self.RETURN_REGISTER] = true;\n                return self.RETURN_REGISTER;\n            end\n        end\n\n        local id = 0;\n        if self.usedRegisters < MAX_REGS * constants.MAX_REGS_MUL then\n            repeat\n                id = math.random(1, MAX_REGS - 1);\n            until not self.registers[id];\n        else\n            repeat\n                id = id + 1;\n            until not self.registers[id];\n        end\n\n        if id > self.maxUsedRegister then\n            self.maxUsedRegister = id;\n        end\n\n        if(isVar) then\n            self.registers[id] = self.VAR_REGISTER;\n        else\n            self.registers[id] = true\n        end\n        return id;\n    end\n\n    function Compiler:isUpvalue(scope, id)\n        return self.upvalVars[scope] and self.upvalVars[scope][id];\n    end\n\n    function Compiler:makeUpvalue(scope, id)\n        if(not self.upvalVars[scope]) then\n            self.upvalVars[scope] = {}\n        end\n        self.upvalVars[scope][id] = true;\n    end\n\n    function Compiler:getVarRegister(scope, id, functionDepth, potentialId)\n        if(not self.registersForVar[scope]) then\n            self.registersForVar[scope] = {};\n            self.scopeFunctionDepths[scope] = functionDepth;\n        end\n\n        local reg = self.registersForVar[scope][id];\n        if not reg then\n            if potentialId and self.registers[potentialId] ~= self.VAR_REGISTER and potentialId ~= self.POS_REGISTER and potentialId ~= self.RETURN_REGISTER then\n                self.registers[potentialId] = self.VAR_REGISTER;\n                reg = potentialId;\n            else\n                reg = self:allocRegister(true);\n            end\n            self.registersForVar[scope][id] = reg;\n        end\n        return reg;\n    end\n\n    function Compiler:getRegisterVarId(id)\n        local varId = self.registerVars[id];\n        if not varId then\n            varId = self.containerFuncScope:addVariable();\n            self.registerVars[id] = varId;\n        end\n        return varId;\n    end\n\n    function Compiler:register(scope, id)\n        if id == self.POS_REGISTER then\n            return self:pos(scope);\n        end\n\n        if id == self.RETURN_REGISTER then\n            return self:getReturn(scope);\n        end\n\n        if id < MAX_REGS then\n            local vid = self:getRegisterVarId(id);\n            scope:addReferenceToHigherScope(self.containerFuncScope, vid);\n            return Ast.VariableExpression(self.containerFuncScope, vid);\n        end\n\n        local vid = self:getRegisterVarId(MAX_REGS);\n        scope:addReferenceToHigherScope(self.containerFuncScope, vid);\n        return Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, vid), Ast.NumberExpression((id - MAX_REGS) + 1));\n    end\n\n    function Compiler:registerList(scope, ids)\n        local l = {};\n        for i, id in ipairs(ids) do\n            table.insert(l, self:register(scope, id));\n        end\n        return l;\n    end\n\n    function Compiler:registerAssignment(scope, id)\n        if id == self.POS_REGISTER then\n            return self:posAssignment(scope);\n        end\n        if id == self.RETURN_REGISTER then\n            return self:returnAssignment(scope);\n        end\n\n        if id < MAX_REGS then\n            local vid = self:getRegisterVarId(id);\n            scope:addReferenceToHigherScope(self.containerFuncScope, vid);\n            return Ast.AssignmentVariable(self.containerFuncScope, vid);\n        end\n\n        local vid = self:getRegisterVarId(MAX_REGS);\n        scope:addReferenceToHigherScope(self.containerFuncScope, vid);\n        return Ast.AssignmentIndexing(Ast.VariableExpression(self.containerFuncScope, vid), Ast.NumberExpression((id - MAX_REGS) + 1));\n    end\n\n    function Compiler:setRegister(scope, id, val, compundArg)\n        if(compundArg) then\n            return compundArg(self:registerAssignment(scope, id), val);\n        end\n        return Ast.AssignmentStatement({\n            self:registerAssignment(scope, id)\n        }, {\n            val\n        });\n    end\n\n    function Compiler:setRegisters(scope, ids, vals)\n        local idStats = {};\n        for i, id in ipairs(ids) do\n            table.insert(idStats, self:registerAssignment(scope, id));\n        end\n\n        return Ast.AssignmentStatement(idStats, vals);\n    end\n\n    function Compiler:copyRegisters(scope, to, from)\n        local idStats = {};\n        local vals = {};\n        for i, id in ipairs(to) do\n            local fromId = from[i];\n            if(fromId ~= id) then\n                table.insert(idStats, self:registerAssignment(scope, id));\n                table.insert(vals, self:register(scope, fromId));\n            end\n        end\n\n        if(#idStats > 0 and #vals > 0) then\n            return Ast.AssignmentStatement(idStats, vals);\n        end\n    end\n\n    function Compiler:resetRegisters()\n        self.registers = {};\n    end\n\n    function Compiler:pos(scope)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n        return Ast.VariableExpression(self.containerFuncScope, self.posVar);\n    end\n\n    function Compiler:posAssignment(scope)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n        return Ast.AssignmentVariable(self.containerFuncScope, self.posVar);\n    end\n\n    function Compiler:args(scope)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);\n        return Ast.VariableExpression(self.containerFuncScope, self.argsVar);\n    end\n\n    function Compiler:unpack(scope)\n        scope:addReferenceToHigherScope(self.scope, self.unpackVar);\n        return Ast.VariableExpression(self.scope, self.unpackVar);\n    end\n\n    function Compiler:env(scope)\n        scope:addReferenceToHigherScope(self.scope, self.envVar);\n        return Ast.VariableExpression(self.scope, self.envVar);\n    end\n\n    function Compiler:jmp(scope, to)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n        return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)},{to});\n    end\n\n    function Compiler:setPos(scope, val)\n        if not val then\n            local v = Ast.IndexExpression(self:env(scope), randomStrings.randomStringNode(math.random(12, 14)));\n            scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n            return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)}, {v});\n        end\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);\n        return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)}, {Ast.NumberExpression(val) or Ast.NilExpression()});\n    end\n\n    function Compiler:setReturn(scope, val)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);\n        return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.returnVar)}, {val});\n    end\n\n    function Compiler:getReturn(scope)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);\n        return Ast.VariableExpression(self.containerFuncScope, self.returnVar);\n    end\n\n    function Compiler:returnAssignment(scope)\n        scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);\n        return Ast.AssignmentVariable(self.containerFuncScope, self.returnVar);\n    end\n\n    function Compiler:setUpvalueMember(scope, idExpr, valExpr, compoundConstructor)\n        scope:addReferenceToHigherScope(self.scope, self.upvaluesTable);\n        if compoundConstructor then\n            return compoundConstructor(Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr), valExpr);\n        end\n        return Ast.AssignmentStatement({Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr)}, {valExpr});\n    end\n\n    function Compiler:getUpvalueMember(scope, idExpr)\n        scope:addReferenceToHigherScope(self.scope, self.upvaluesTable);\n        return Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr);\n    end\nend\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/assignment.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- assignment.lua\n--\n-- This Script contains the statement handler for the AssignmentStatement.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local exprregs = {};\n    local assignmentIndexingRegs = {};\n    for i, primaryExpr in ipairs(statement.lhs) do\n        if(primaryExpr.kind == AstKind.AssignmentIndexing) then\n            assignmentIndexingRegs [i] = {\n                base = self:compileExpression(primaryExpr.base, funcDepth, 1)[1],\n                index = self:compileExpression(primaryExpr.index, funcDepth, 1)[1],\n            };\n        end\n    end\n\n    for i, expr in ipairs(statement.rhs) do\n        if(i == #statement.rhs and #statement.lhs > #statement.rhs) then\n            local regs = self:compileExpression(expr, funcDepth, #statement.lhs - #statement.rhs + 1);\n\n            for i, reg in ipairs(regs) do\n                if(self:isVarRegister(reg)) then\n                    local ro = reg;\n                    reg = self:allocRegister(false);\n                    self:addStatement(self:copyRegisters(scope, {reg}, {ro}), {reg}, {ro}, false);\n                end\n                table.insert(exprregs, reg);\n            end\n        else\n            if statement.lhs[i] or expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression then\n                local reg = self:compileExpression(expr, funcDepth, 1)[1];\n                if(self:isVarRegister(reg)) then\n                    local ro = reg;\n                    reg = self:allocRegister(false);\n                    self:addStatement(self:copyRegisters(scope, {reg}, {ro}), {reg}, {ro}, false);\n                end\n                table.insert(exprregs, reg);\n            end\n        end\n    end\n\n    for i, primaryExpr in ipairs(statement.lhs) do\n        if primaryExpr.kind == AstKind.AssignmentVariable then\n            if primaryExpr.scope.isGlobal then\n                local tmpReg = self:allocRegister(false);\n                self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(primaryExpr.scope:getVariableName(primaryExpr.id))), {tmpReg}, {}, false);\n                self:addStatement(Ast.AssignmentStatement({Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg))},\n                 {self:register(scope, exprregs[i])}), {}, {tmpReg, exprregs[i]}, true);\n                self:freeRegister(tmpReg, false);\n            else\n                if self.scopeFunctionDepths[primaryExpr.scope] == funcDepth then\n                    if self:isUpvalue(primaryExpr.scope, primaryExpr.id) then\n                        local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth);\n                        self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, exprregs[i])), {}, {reg, exprregs[i]}, true);\n                    else\n                        local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth, exprregs[i]);\n                        if reg ~= exprregs[i] then\n                            self:addStatement(self:setRegister(scope, reg, self:register(scope, exprregs[i])), {reg}, {exprregs[i]}, false);\n                        end\n                    end\n                else\n                    local upvalId = self:getUpvalueId(primaryExpr.scope, primaryExpr.id);\n                    scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n                    self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, exprregs[i])), {}, {exprregs[i]}, true);\n                end\n            end\n        elseif primaryExpr.kind == AstKind.AssignmentIndexing then\n            local baseReg = assignmentIndexingRegs[i].base;\n            local indexReg = assignmentIndexingRegs[i].index;\n            self:addStatement(Ast.AssignmentStatement({\n                Ast.AssignmentIndexing(self:register(scope, baseReg), self:register(scope, indexReg))\n            }, {\n                self:register(scope, exprregs[i])\n            }), {}, {exprregs[i], baseReg, indexReg}, true);\n            self:freeRegister(exprregs[i], false);\n            self:freeRegister(baseReg, false);\n            self:freeRegister(indexReg, false);\n        else\n            error(string.format(\"Invalid Assignment lhs: %s\", statement.lhs));\n        end\n    end\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/break_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- break_statement.lua\n--\n-- This Script contains the statement handler for the BreakStatement.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local toFreeVars = {};\n    local statScope;\n    repeat\n        statScope = statScope and statScope.parentScope or statement.scope;\n        for id, name in ipairs(statScope.variables) do\n            table.insert(toFreeVars, {\n                scope = statScope,\n                id = id;\n            });\n        end\n    until statScope == statement.loop.body.scope;\n\n    for i, var in pairs(toFreeVars) do\n        local varScope, id = var.scope, var.id;\n        local varReg = self:getVarRegister(varScope, id, nil, nil);\n        if self:isUpvalue(varScope, id) then\n            scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);\n            self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {\n                self:register(scope, varReg)\n            })), {varReg}, {varReg}, false);\n        else\n            self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);\n        end\n    end\n\n\n    self:addStatement(self:setPos(scope, statement.loop.__final_block.id), {self.POS_REGISTER}, {}, false);\n    self.activeBlock.advanceToNextBlock = false;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/compound.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- compound.lua\n--\n-- This Script contains the statement handler for the Compound statements (compound add, sub, mul, etc.)\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nlocal compoundConstructors = {\n    [AstKind.CompoundAddStatement] = Ast.CompoundAddStatement,\n    [AstKind.CompoundSubStatement] = Ast.CompoundSubStatement,\n    [AstKind.CompoundMulStatement] = Ast.CompoundMulStatement,\n    [AstKind.CompoundDivStatement] = Ast.CompoundDivStatement,\n    [AstKind.CompoundModStatement] = Ast.CompoundModStatement,\n    [AstKind.CompoundPowStatement] = Ast.CompoundPowStatement,\n    [AstKind.CompoundConcatStatement] = Ast.CompoundConcatStatement,\n};\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local compoundConstructor = compoundConstructors[statement.kind];\n    if statement.lhs.kind == AstKind.AssignmentIndexing then\n        local indexing = statement.lhs;\n        local baseReg = self:compileExpression(indexing.base, funcDepth, 1)[1];\n        local indexReg = self:compileExpression(indexing.index, funcDepth, 1)[1];\n        local valueReg = self:compileExpression(statement.rhs, funcDepth, 1)[1];\n\n        self:addStatement(compoundConstructor(Ast.AssignmentIndexing(self:register(scope, baseReg), self:register(scope, indexReg)), self:register(scope, valueReg)), {}, {baseReg, indexReg, valueReg}, true);\n        self:freeRegister(baseReg, false);\n        self:freeRegister(indexReg, false);\n        self:freeRegister(valueReg, false);\n    else\n        local valueReg = self:compileExpression(statement.rhs, funcDepth, 1)[1];\n        local primaryExpr = statement.lhs;\n        if primaryExpr.scope.isGlobal then\n            local tmpReg = self:allocRegister(false);\n            self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(primaryExpr.scope:getVariableName(primaryExpr.id))), {tmpReg}, {}, false);\n            self:addStatement(compoundConstructor(Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg)),\n             self:register(scope, valueReg)), {}, {tmpReg, valueReg}, true);\n            self:freeRegister(tmpReg, false);\n            self:freeRegister(valueReg, false);\n        else\n            if self.scopeFunctionDepths[primaryExpr.scope] == funcDepth then\n                if self:isUpvalue(primaryExpr.scope, primaryExpr.id) then\n                    local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth);\n                    self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, valueReg), compoundConstructor), {}, {reg, valueReg}, true);\n                else\n                    local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth, valueReg);\n                    if reg ~= valueReg then\n                        self:addStatement(self:setRegister(scope, reg, self:register(scope, valueReg), compoundConstructor), {reg}, {valueReg}, false);\n                    end\n                end\n            else\n                local upvalId = self:getUpvalueId(primaryExpr.scope, primaryExpr.id);\n                scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n                self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, valueReg), compoundConstructor), {}, {valueReg}, true);\n            end\n            self:freeRegister(valueReg, false);\n        end\n    end\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/continue_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- continue_statement.lua\n--\n-- This Script contains the statement handler for the ContinueStatement.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local toFreeVars = {};\n    local statScope;\n    repeat\n        statScope = statScope and statScope.parentScope or statement.scope;\n        for id, _ in pairs(statScope.variables) do\n            table.insert(toFreeVars, {\n                scope = statScope,\n                id = id;\n            });\n        end\n    until statScope == statement.loop.body.scope;\n\n    for _, var in ipairs(toFreeVars) do\n        local varScope, id = var.scope, var.id;\n        local varReg = self:getVarRegister(varScope, id, nil, nil);\n        if self:isUpvalue(varScope, id) then\n            scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);\n            self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {\n                self:register(scope, varReg)\n            })), {varReg}, {varReg}, false);\n        else\n            self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);\n        end\n    end\n\n\n    self:addStatement(self:setPos(scope, statement.loop.__start_block.id), {self.POS_REGISTER}, {}, false);\n    self.activeBlock.advanceToNextBlock = false;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/do_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- do_statement.lua\n--\n-- This Script contains the statement handler for the DoStatement.\n\nreturn function(self, statement, funcDepth)\n    self:compileBlock(statement.body, funcDepth);\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/for_in_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- for_in_statement.lua\n--\n-- This Script contains the statement handler for the ForInStatement\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local expressionsLength = #statement.expressions;\n    local exprregs = {};\n    for i, expr in ipairs(statement.expressions) do\n        if(i == expressionsLength and expressionsLength < 3) then\n            local regs = self:compileExpression(expr, funcDepth, 4 - expressionsLength);\n            for i = 1, 4 - expressionsLength do\n                table.insert(exprregs, regs[i]);\n            end\n        else\n            if i <= 3 then\n                table.insert(exprregs, self:compileExpression(expr, funcDepth, 1)[1])\n            else\n                self:freeRegister(self:compileExpression(expr, funcDepth, 1)[1], false);\n            end\n        end\n    end\n\n    for i, reg in ipairs(exprregs) do\n        if reg and self.registers[reg] ~= self.VAR_REGISTER and reg ~= self.POS_REGISTER and reg ~= self.RETURN_REGISTER then\n            self.registers[reg] = self.VAR_REGISTER;\n        else\n            exprregs[i] = self:allocRegister(true);\n            self:addStatement(self:copyRegisters(scope, {exprregs[i]}, {reg}), {exprregs[i]}, {reg}, false);\n        end\n    end\n\n    local checkBlock = self:createBlock();\n    local bodyBlock = self:createBlock();\n    local finalBlock = self:createBlock();\n\n    statement.__start_block = checkBlock;\n    statement.__final_block = finalBlock;\n\n    self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);\n\n    self:setActiveBlock(checkBlock);\n    local scope = self.activeBlock.scope;\n\n    local varRegs = {};\n    for i, id in ipairs(statement.ids) do\n        varRegs[i] = self:getVarRegister(statement.scope, id, funcDepth)\n    end\n\n\n\n    self:addStatement(Ast.AssignmentStatement({\n        self:registerAssignment(scope, exprregs[3]),\n        varRegs[2] and self:registerAssignment(scope, varRegs[2]),\n    }, {\n        Ast.FunctionCallExpression(self:register(scope, exprregs[1]), {\n            self:register(scope, exprregs[2]),\n            self:register(scope, exprregs[3]),\n        })\n    }), {exprregs[3], varRegs[2]}, {exprregs[1], exprregs[2], exprregs[3]}, true);\n\n\n\n    self:addStatement(Ast.AssignmentStatement({\n        self:posAssignment(scope)\n    }, {\n        Ast.OrExpression(Ast.AndExpression(self:register(scope, exprregs[3]), Ast.NumberExpression(bodyBlock.id)), Ast.NumberExpression(finalBlock.id))\n    }), {self.POS_REGISTER}, {exprregs[3]}, false);\n\n    self:setActiveBlock(bodyBlock);\n    local scope = self.activeBlock.scope;\n\n    self:addStatement(self:copyRegisters(scope, {varRegs[1]}, {exprregs[3]}), {varRegs[1]}, {exprregs[3]}, false);\n\n    for i=3, #varRegs do\n        self:addStatement(self:setRegister(scope, varRegs[i], Ast.NilExpression()), {varRegs[i]}, {}, false);\n    end\n\n    for i, id in ipairs(statement.ids) do\n        if(self:isUpvalue(statement.scope, id)) then\n            local varreg = varRegs[i];\n            local tmpReg = self:allocRegister(false);\n            scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n            self:addStatement(self:setRegister(scope, tmpReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {tmpReg}, {}, false);\n            self:addStatement(self:setUpvalueMember(scope, self:register(scope, tmpReg), self:register(scope, varreg)), {}, {tmpReg, varreg}, true);\n            self:addStatement(self:copyRegisters(scope, {varreg}, {tmpReg}), {varreg}, {tmpReg}, false);\n            self:freeRegister(tmpReg, false);\n        end\n    end\n\n    self:compileBlock(statement.body, funcDepth);\n    self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);\n    self:setActiveBlock(finalBlock);\n\n    for i, _ in ipairs(exprregs) do\n        self:freeRegister(exprregs[i], true)\n    end\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/for_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- for_statement.lua\n--\n-- This Script contains the statement handler for the ForStatement\n\nlocal Ast = require(\"prometheus.ast\");\nlocal util = require(\"prometheus.util\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local checkBlock = self:createBlock();\n    local innerBlock = self:createBlock();\n    local finalBlock = self:createBlock();\n\n    statement.__start_block = checkBlock;\n    statement.__final_block = finalBlock;\n\n    local posState = self.registers[self.POS_REGISTER];\n    self.registers[self.POS_REGISTER] = self.VAR_REGISTER;\n\n    local initialReg = self:compileExpression(statement.initialValue, funcDepth, 1)[1];\n\n    local finalExprReg = self:compileExpression(statement.finalValue, funcDepth, 1)[1];\n    local finalReg = self:allocRegister(false);\n    self:addStatement(self:copyRegisters(scope, {finalReg}, {finalExprReg}), {finalReg}, {finalExprReg}, false);\n    self:freeRegister(finalExprReg);\n\n    local incrementExprReg = self:compileExpression(statement.incrementBy, funcDepth, 1)[1];\n    local incrementReg = self:allocRegister(false);\n    self:addStatement(self:copyRegisters(scope, {incrementReg}, {incrementExprReg}), {incrementReg}, {incrementExprReg}, false);\n    self:freeRegister(incrementExprReg);\n\n    local tmpReg = self:allocRegister(false);\n    self:addStatement(self:setRegister(scope, tmpReg, Ast.NumberExpression(0)), {tmpReg}, {}, false);\n    local incrementIsNegReg = self:allocRegister(false);\n\n    local shouldSwap3 = math.random(1, 2) == 2;\n    local shuffledRegs4 = shouldSwap3 and {incrementReg, tmpReg} or {tmpReg, incrementReg};\n    self:addStatement(self:setRegister(scope, incrementIsNegReg, Ast[shouldSwap3 and \"LessThanExpression\" or \"GreaterThanExpression\"](self:register(scope, shuffledRegs4[1]), self:register(scope, shuffledRegs4[2]))), {incrementIsNegReg}, {shuffledRegs4[1], shuffledRegs4[2]}, false);\n\n    self:freeRegister(tmpReg);\n\n    local currentReg = self:allocRegister(true);\n    self:addStatement(self:setRegister(scope, currentReg, Ast.SubExpression(self:register(scope, initialReg), self:register(scope, incrementReg))), {currentReg}, {initialReg, incrementReg}, false);\n    self:freeRegister(initialReg);\n\n    self:addStatement(self:jmp(scope, Ast.NumberExpression(checkBlock.id)), {self.POS_REGISTER}, {}, false);\n\n    self:setActiveBlock(checkBlock);\n\n    scope = checkBlock.scope;\n\n    -- x = x + y or x = y + x instead of just x = x + y.\n    --> NEW: In an attempt to thwart deobfuscations, despite this being a simple comparison... Shuffling these causes problems for decompilers.\n    --> NOTE: This isn't unstable code, I've tested it multiple times.\n\n    local shuffledRegs = util.shuffle({currentReg, incrementReg});\n    self:addStatement(self:setRegister(scope, currentReg, Ast.AddExpression(self:register(scope, shuffledRegs[1]), self:register(scope, shuffledRegs[2]))), {currentReg}, {shuffledRegs[1], shuffledRegs[2]}, false);\n    local tmpReg1 = self:allocRegister(false);\n    local tmpReg2 = self:allocRegister(false);\n    self:addStatement(self:setRegister(scope, tmpReg2, Ast.NotExpression(self:register(scope, incrementIsNegReg))), {tmpReg2}, {incrementIsNegReg}, false);\n\n    local shouldSwap = math.random(1, 2) == 2;\n    local shuffledRegs2 = shouldSwap and {currentReg, finalReg} or {finalReg, currentReg};\n    self:addStatement(self:setRegister(scope, tmpReg1, Ast[shouldSwap and \"LessThanOrEqualsExpression\" or \"GreaterThanOrEqualsExpression\"](self:register(scope, shuffledRegs2[1]), self:register(scope, shuffledRegs2[2]))), {tmpReg1}, {shuffledRegs2[1], shuffledRegs2[2]}, false);\n    self:addStatement(self:setRegister(scope, tmpReg1, Ast.AndExpression(self:register(scope, tmpReg2), self:register(scope, tmpReg1))), {tmpReg1}, {tmpReg1, tmpReg2}, false);\n\n    local shouldSwap2 = math.random(1, 2) == 2;\n    local shuffledRegs3 = shouldSwap2 and {currentReg, finalReg} or {finalReg, currentReg};\n    self:addStatement(self:setRegister(scope, tmpReg2, Ast[shouldSwap2 and \"GreaterThanOrEqualsExpression\" or \"LessThanOrEqualsExpression\"](self:register(scope, shuffledRegs3[1]), self:register(scope, shuffledRegs3[2]))), {tmpReg2}, {shuffledRegs3[1], shuffledRegs3[2]}, false);\n\n    self:addStatement(self:setRegister(scope, tmpReg2, Ast.AndExpression(self:register(scope, incrementIsNegReg), self:register(scope, tmpReg2))), {tmpReg2}, {tmpReg2, incrementIsNegReg}, false);\n    self:addStatement(self:setRegister(scope, tmpReg1, Ast.OrExpression(self:register(scope, tmpReg2), self:register(scope, tmpReg1))), {tmpReg1}, {tmpReg1, tmpReg2}, false);\n    self:freeRegister(tmpReg2);\n    tmpReg2 = self:compileExpression(Ast.NumberExpression(innerBlock.id), funcDepth, 1)[1];\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.AndExpression(self:register(scope, tmpReg1), self:register(scope, tmpReg2))), {self.POS_REGISTER}, {tmpReg1, tmpReg2}, false);\n    self:freeRegister(tmpReg2);\n    self:freeRegister(tmpReg1);\n    tmpReg2 = self:compileExpression(Ast.NumberExpression(finalBlock.id), funcDepth, 1)[1];\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(self:register(scope, self.POS_REGISTER), self:register(scope, tmpReg2))), {self.POS_REGISTER}, {self.POS_REGISTER, tmpReg2}, false);\n    self:freeRegister(tmpReg2);\n\n    self:setActiveBlock(innerBlock);\n    scope = innerBlock.scope;\n    self.registers[self.POS_REGISTER] = posState;\n\n    local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, nil);\n\n    if(self:isUpvalue(statement.scope, statement.id)) then\n        scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n        self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);\n        self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, currentReg)), {}, {varReg, currentReg}, true);\n    else\n        self:addStatement(self:setRegister(scope, varReg, self:register(scope, currentReg)), {varReg}, {currentReg}, false);\n    end\n\n\n    self:compileBlock(statement.body, funcDepth);\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(checkBlock.id)), {self.POS_REGISTER}, {}, false);\n\n    self.registers[self.POS_REGISTER] = self.VAR_REGISTER;\n    self:freeRegister(finalReg);\n    self:freeRegister(incrementIsNegReg);\n    self:freeRegister(incrementReg);\n    self:freeRegister(currentReg, true);\n\n    self.registers[self.POS_REGISTER] = posState;\n    self:setActiveBlock(finalBlock);\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/function_call.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- function_call.lua\n--\n-- This Script contains the statement handler for the FunctionCallStatement.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local baseReg = self:compileExpression(statement.base, funcDepth, 1)[1];\n    local retReg = self:allocRegister(false);\n    local regs = {};\n    local args = {};\n\n    for i, expr in ipairs(statement.args) do\n        if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then\n            local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];\n            table.insert(args, Ast.FunctionCallExpression(\n                self:unpack(scope),\n                {self:register(scope, reg)}));\n            table.insert(regs, reg);\n        else\n            local reg = self:compileExpression(expr, funcDepth, 1)[1];\n            local argExpr = self:register(scope, reg);\n            table.insert(args, argExpr);\n            table.insert(regs, reg);\n        end\n    end\n\n    self:addStatement(self:setRegister(scope, retReg, Ast.FunctionCallExpression(self:register(scope, baseReg), args)), {retReg}, {baseReg, unpack(regs)}, true);\n    self:freeRegister(baseReg, false);\n    self:freeRegister(retReg, false);\n    for _, reg in ipairs(regs) do\n        self:freeRegister(reg, false);\n    end\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/function_declaration.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- function_declaration.lua\n--\n-- This Script contains the statement handler for the FunctionDeclaration.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local retReg = self:compileFunction(statement, funcDepth);\n    if(#statement.indices > 0) then\n        local tblReg;\n        if statement.scope.isGlobal then\n            tblReg = self:allocRegister(false);\n            self:addStatement(self:setRegister(scope, tblReg, Ast.StringExpression(statement.scope:getVariableName(statement.id))), {tblReg}, {}, false);\n            self:addStatement(self:setRegister(scope, tblReg, Ast.IndexExpression(self:env(scope), self:register(scope, tblReg))), {tblReg}, {tblReg}, true);\n        else\n            if self.scopeFunctionDepths[statement.scope] == funcDepth then\n                if self:isUpvalue(statement.scope, statement.id) then\n                    tblReg = self:allocRegister(false);\n                    local reg = self:getVarRegister(statement.scope, statement.id, funcDepth);\n                    self:addStatement(self:setRegister(scope, tblReg, self:getUpvalueMember(scope, self:register(scope, reg))), {tblReg}, {reg}, true);\n                else\n                    tblReg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);\n                end\n            else\n                tblReg = self:allocRegister(false);\n                local upvalId = self:getUpvalueId(statement.scope, statement.id);\n                scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n                self:addStatement(self:setRegister(scope, tblReg, self:getUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)))), {tblReg}, {}, true);\n            end\n        end\n\n        for i = 1, #statement.indices - 1 do\n            local index = statement.indices[i];\n            local indexReg = self:compileExpression(Ast.StringExpression(index), funcDepth, 1)[1];\n            local tblRegOld = tblReg;\n            tblReg = self:allocRegister(false);\n            self:addStatement(self:setRegister(scope, tblReg, Ast.IndexExpression(self:register(scope, tblRegOld), self:register(scope, indexReg))), {tblReg}, {tblReg, indexReg}, false);\n            self:freeRegister(tblRegOld, false);\n            self:freeRegister(indexReg, false);\n        end\n\n        local index = statement.indices[#statement.indices];\n        local indexReg = self:compileExpression(Ast.StringExpression(index), funcDepth, 1)[1];\n        self:addStatement(Ast.AssignmentStatement({\n            Ast.AssignmentIndexing(self:register(scope, tblReg), self:register(scope, indexReg)),\n        }, {\n            self:register(scope, retReg),\n        }), {}, {tblReg, indexReg, retReg}, true);\n        self:freeRegister(indexReg, false);\n        self:freeRegister(tblReg, false);\n        self:freeRegister(retReg, false);\n\n        return;\n    end\n    if statement.scope.isGlobal then\n        local tmpReg = self:allocRegister(false);\n        self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(statement.scope:getVariableName(statement.id))), {tmpReg}, {}, false);\n        self:addStatement(Ast.AssignmentStatement({Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg))},\n         {self:register(scope, retReg)}), {}, {tmpReg, retReg}, true);\n        self:freeRegister(tmpReg, false);\n    else\n        if self.scopeFunctionDepths[statement.scope] == funcDepth then\n            if self:isUpvalue(statement.scope, statement.id) then\n                local reg = self:getVarRegister(statement.scope, statement.id, funcDepth);\n                self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, retReg)), {}, {reg, retReg}, true);\n            else\n                local reg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);\n                if reg ~= retReg then\n                    self:addStatement(self:setRegister(scope, reg, self:register(scope, retReg)), {reg}, {retReg}, false);\n                end\n            end\n        else\n            local upvalId = self:getUpvalueId(statement.scope, statement.id);\n            scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);\n            self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, retReg)), {}, {retReg}, true);\n        end\n    end\n    self:freeRegister(retReg, false);\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/if_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- if_statement.lua\n--\n-- This Script contains the statement handler for the IfStatement.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1];\n    local finalBlock = self:createBlock();\n\n    local nextBlock\n    if statement.elsebody or #statement.elseifs > 0 then\n        nextBlock = self:createBlock();\n    else\n        nextBlock = finalBlock;\n    end\n    local innerBlock = self:createBlock();\n\n\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);\n\n    self:freeRegister(conditionReg, false);\n\n    self:setActiveBlock(innerBlock);\n    scope = innerBlock.scope\n    self:compileBlock(statement.body, funcDepth);\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);\n\n    for i, eif in ipairs(statement.elseifs) do\n        self:setActiveBlock(nextBlock);\n        conditionReg = self:compileExpression(eif.condition, funcDepth, 1)[1];\n        local innerBlock = self:createBlock();\n        if statement.elsebody or i < #statement.elseifs then\n            nextBlock = self:createBlock();\n        else\n            nextBlock = finalBlock;\n        end\n        local scope = self.activeBlock.scope;\n\n        self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);\n\n\n        self:freeRegister(conditionReg, false);\n\n        self:setActiveBlock(innerBlock);\n        scope = innerBlock.scope;\n        self:compileBlock(eif.body, funcDepth);\n        self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);\n    end\n\n    if statement.elsebody then\n        self:setActiveBlock(nextBlock);\n        self:compileBlock(statement.elsebody, funcDepth);\n        self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);\n    end\n\n    self:setActiveBlock(finalBlock);\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/local_function_declaration.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- local_function_declaration.lua\n--\n-- This Script contains the statement handler for the LocalFunctionDeclaration\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n\n    if(self:isUpvalue(statement.scope, statement.id)) then\n        local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, nil);\n        scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n        self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);\n        local retReg = self:compileFunction(statement, funcDepth);\n        self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, retReg)), {}, {varReg, retReg}, true);\n        self:freeRegister(retReg, false);\n    else\n        local retReg = self:compileFunction(statement, funcDepth);\n        local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);\n\n        self:addStatement(self:copyRegisters(scope, {varReg}, {retReg}), {varReg}, {retReg}, false);\n        self:freeRegister(retReg, false);\n    end\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/local_variable_declaration.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- local_variable_declaration.lua\n--\n-- This Script contains the statement handler for the LocalVariableDeclaration\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local exprregs = {};\n    for i, expr in ipairs(statement.expressions) do\n        if(i == #statement.expressions and #statement.ids > #statement.expressions) then\n            local regs = self:compileExpression(expr, funcDepth, #statement.ids - #statement.expressions + 1);\n            for i, reg in ipairs(regs) do\n                table.insert(exprregs, reg);\n            end\n        else\n            if statement.ids[i] or expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression then\n                local reg = self:compileExpression(expr, funcDepth, 1)[1];\n                table.insert(exprregs, reg);\n            end\n        end\n    end\n\n    if #exprregs == 0 then\n        for _=1, #statement.ids do\n            table.insert(exprregs, self:compileExpression(Ast.NilExpression(), funcDepth, 1)[1]);\n        end\n    end\n\n    for i, id in ipairs(statement.ids) do\n        if(exprregs[i]) then\n            if(self:isUpvalue(statement.scope, id)) then\n                local varreg = self:getVarRegister(statement.scope, id, funcDepth);\n                local varReg = self:getVarRegister(statement.scope, id, funcDepth, nil);\n                scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);\n                self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);\n                self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, exprregs[i])), {}, {varReg, exprregs[i]}, true);\n                self:freeRegister(exprregs[i], false);\n            else\n                local varreg = self:getVarRegister(statement.scope, id, funcDepth, exprregs[i]);\n                self:addStatement(self:copyRegisters(scope, {varreg}, {exprregs[i]}), {varreg}, {exprregs[i]}, false);\n                self:freeRegister(exprregs[i], false);\n            end\n        end\n    end\n\n    if not self.scopeFunctionDepths[statement.scope] then\n        self.scopeFunctionDepths[statement.scope] = funcDepth;\n    end\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/pass_self_function_call.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- pass_self_function_call.lua\n--\n-- This Script contains the statement handler for the PassSelfFunctionCallStatement.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local baseReg = self:compileExpression(statement.base, funcDepth, 1)[1];\n    local tmpReg = self:allocRegister(false);\n    local args = { self:register(scope, baseReg) };\n    local regs = { baseReg };\n\n    for i, expr in ipairs(statement.args) do\n        if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then\n            local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];\n            table.insert(args, Ast.FunctionCallExpression(\n                self:unpack(scope),\n                {self:register(scope, reg)}));\n            table.insert(regs, reg);\n        else\n            local reg = self:compileExpression(expr, funcDepth, 1)[1];\n            table.insert(args, self:register(scope, reg));\n            table.insert(regs, reg);\n        end\n    end\n    self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(statement.passSelfFunctionName)), {tmpReg}, {}, false);\n    self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {tmpReg, baseReg}, false);\n\n    self:addStatement(self:setRegister(scope, tmpReg, Ast.FunctionCallExpression(self:register(scope, tmpReg), args)), {tmpReg}, {tmpReg, unpack(regs)}, true);\n\n    self:freeRegister(tmpReg, false);\n    for _, reg in ipairs(regs) do\n        self:freeRegister(reg, false);\n    end\nend;\n\n"
  },
  {
    "path": "src/prometheus/compiler/statements/repeat_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- repeat_statement.lua\n--\n-- This Script contains the statement handler for the RepeatStatement.\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local innerBlock = self:createBlock();\n    local finalBlock = self:createBlock();\n    statement.__start_block = innerBlock;\n    statement.__final_block = finalBlock;\n\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(innerBlock.id)), {self.POS_REGISTER}, {}, false);\n    self:setActiveBlock(innerBlock);\n\n    for _, stat in ipairs(statement.body.statements) do\n        self:compileStatement(stat, funcDepth);\n    end;\n\n    local scope = self.activeBlock.scope;\n    local conditionReg = (self:compileExpression(statement.condition, funcDepth, 1))[1];\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(finalBlock.id)), Ast.NumberExpression(innerBlock.id))), { self.POS_REGISTER }, { conditionReg }, false);\n    self:freeRegister(conditionReg, false);\n\n    for id, _ in ipairs(statement.body.scope.variables) do\n        local varReg = self:getVarRegister(statement.body.scope, id, funcDepth, nil);\n        if self:isUpvalue(statement.body.scope, id) then\n            scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);\n            self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), { self:register(scope, varReg) })), { varReg }, { varReg }, false);\n        else\n            self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), { varReg }, {}, false);\n        end;\n        self:freeRegister(varReg, true);\n    end;\n\n    self:setActiveBlock(finalBlock);\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/return.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- return.lua\n--\n-- This Script contains the statement handler for the ReturnStatement.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local entries = {};\n    local regs = {};\n\n    for i, expr in ipairs(statement.args) do\n        if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then\n            local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];\n            table.insert(entries, Ast.TableEntry(Ast.FunctionCallExpression(\n                self:unpack(scope),\n                {self:register(scope, reg)})));\n            table.insert(regs, reg);\n        else\n            local reg = self:compileExpression(expr, funcDepth, 1)[1];\n            table.insert(entries, Ast.TableEntry(self:register(scope, reg)));\n            table.insert(regs, reg);\n        end\n    end\n\n    for _, reg in ipairs(regs) do\n        self:freeRegister(reg, false);\n    end\n\n    self:addStatement(self:setReturn(scope, Ast.TableConstructorExpression(entries)), {self.RETURN_REGISTER}, regs, false);\n    self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);\n    self.activeBlock.advanceToNextBlock = false;\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements/while_statement.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- while_statement.lua\n--\n-- This Script contains the statement handler for the WhileStatement\n\nlocal Ast = require(\"prometheus.ast\");\n\nreturn function(self, statement, funcDepth)\n    local scope = self.activeBlock.scope;\n    local innerBlock = self:createBlock();\n    local finalBlock = self:createBlock();\n    local checkBlock = self:createBlock();\n\n    statement.__start_block = checkBlock;\n    statement.__final_block = finalBlock;\n\n    self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);\n\n    self:setActiveBlock(checkBlock);\n    scope = self.activeBlock.scope;\n    local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1];\n    self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(finalBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);\n    self:freeRegister(conditionReg, false);\n\n    self:setActiveBlock(innerBlock);\n    local scope = self.activeBlock.scope;\n    self:compileBlock(statement.body, funcDepth);\n    self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);\n    self:setActiveBlock(finalBlock);\nend;\n"
  },
  {
    "path": "src/prometheus/compiler/statements.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- statements.lua\n--\n-- This Script contains the statement handlers: exports handler table keyed by AstKind.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal AstKind = Ast.AstKind;\n\nlocal handlers = {};\nlocal statements = \"prometheus.compiler.statements.\";\nlocal function requireStatement(name)\n    return require(statements .. name);\nend\n\nhandlers[AstKind.ReturnStatement] = requireStatement(\"return\");\nhandlers[AstKind.LocalVariableDeclaration] = requireStatement(\"local_variable_declaration\");\nhandlers[AstKind.FunctionCallStatement] = requireStatement(\"function_call\");\nhandlers[AstKind.PassSelfFunctionCallStatement] = requireStatement(\"pass_self_function_call\");\nhandlers[AstKind.LocalFunctionDeclaration] = requireStatement(\"local_function_declaration\");\nhandlers[AstKind.FunctionDeclaration] = requireStatement(\"function_declaration\");\nhandlers[AstKind.AssignmentStatement] = requireStatement(\"assignment\");\nhandlers[AstKind.IfStatement] = requireStatement(\"if_statement\");\nhandlers[AstKind.DoStatement] = requireStatement(\"do_statement\");\nhandlers[AstKind.WhileStatement] = requireStatement(\"while_statement\");\nhandlers[AstKind.RepeatStatement] = requireStatement(\"repeat_statement\");\nhandlers[AstKind.ForStatement] = requireStatement(\"for_statement\");\nhandlers[AstKind.ForInStatement] = requireStatement(\"for_in_statement\");\nhandlers[AstKind.BreakStatement] = requireStatement(\"break_statement\");\nhandlers[AstKind.ContinueStatement] = requireStatement(\"continue_statement\");\n\n-- Compound statements share one handler\nlocal compoundHandler = requireStatement(\"compound\");\nhandlers[AstKind.CompoundAddStatement] = compoundHandler;\nhandlers[AstKind.CompoundSubStatement] = compoundHandler;\nhandlers[AstKind.CompoundMulStatement] = compoundHandler;\nhandlers[AstKind.CompoundDivStatement] = compoundHandler;\nhandlers[AstKind.CompoundModStatement] = compoundHandler;\nhandlers[AstKind.CompoundPowStatement] = compoundHandler;\nhandlers[AstKind.CompoundConcatStatement] = compoundHandler;\n\nreturn handlers;\n\n"
  },
  {
    "path": "src/prometheus/compiler/upvalue.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- upvalue.lua\n--\n-- This Script contains the upvalue and GC management for the compiler.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal util = require(\"prometheus.util\");\n\nlocal unpack = unpack or table.unpack;\n\nreturn function(Compiler)\n    function Compiler:createUpvaluesGcFunc()\n        local scope = Scope:new(self.scope);\n        local selfVar = scope:addVariable();\n\n        local iteratorVar = scope:addVariable();\n        local valueVar = scope:addVariable();\n\n        local whileScope = Scope:new(scope);\n        whileScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 3);\n        whileScope:addReferenceToHigherScope(scope, valueVar, 3);\n        whileScope:addReferenceToHigherScope(scope, iteratorVar, 3);\n\n        local ifScope = Scope:new(whileScope);\n        ifScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 1);\n        ifScope:addReferenceToHigherScope(self.scope, self.upvaluesTable, 1);\n\n        return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, selfVar)}, Ast.Block({\n            Ast.LocalVariableDeclaration(scope, {iteratorVar, valueVar}, {Ast.NumberExpression(1), Ast.IndexExpression(Ast.VariableExpression(scope, selfVar), Ast.NumberExpression(1))}),\n            Ast.WhileStatement(Ast.Block({\n                Ast.AssignmentStatement({\n                    Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)),\n                    Ast.AssignmentVariable(scope, iteratorVar),\n                }, {\n                    Ast.SubExpression(Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)), Ast.NumberExpression(1)),\n                    Ast.AddExpression(unpack(util.shuffle{Ast.VariableExpression(scope, iteratorVar), Ast.NumberExpression(1)})),\n                }),\n                Ast.IfStatement(Ast.EqualsExpression(unpack(util.shuffle{Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)), Ast.NumberExpression(0)})), Ast.Block({\n                    Ast.AssignmentStatement({\n                        Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)),\n                        Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), Ast.VariableExpression(scope, valueVar)),\n                    }, {\n                        Ast.NilExpression(),\n                        Ast.NilExpression(),\n                    })\n                }, ifScope), {}, nil),\n                Ast.AssignmentStatement({\n                    Ast.AssignmentVariable(scope, valueVar),\n                }, {\n                    Ast.IndexExpression(Ast.VariableExpression(scope, selfVar), Ast.VariableExpression(scope, iteratorVar)),\n                }),\n            }, whileScope), Ast.VariableExpression(scope, valueVar), scope);\n        }, scope));\n    end\n\n    function Compiler:createFreeUpvalueFunc()\n        local scope = Scope:new(self.scope);\n        local argVar = scope:addVariable();\n        local ifScope = Scope:new(scope);\n        ifScope:addReferenceToHigherScope(scope, argVar, 3);\n        scope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 2);\n        return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, argVar)}, Ast.Block({\n            Ast.AssignmentStatement({\n                Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar))\n            }, {\n                Ast.SubExpression(Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)), Ast.NumberExpression(1));\n            }),\n            Ast.IfStatement(Ast.EqualsExpression(unpack(util.shuffle{Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)), Ast.NumberExpression(0)})), Ast.Block({\n                Ast.AssignmentStatement({\n                    Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)),\n                    Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), Ast.VariableExpression(scope, argVar)),\n                }, {\n                    Ast.NilExpression(),\n                    Ast.NilExpression(),\n                })\n            }, ifScope), {}, nil)\n        }, scope))\n    end\n\n    function Compiler:createUpvaluesProxyFunc()\n        local scope = Scope:new(self.scope);\n        scope:addReferenceToHigherScope(self.scope, self.newproxyVar);\n\n        local entriesVar = scope:addVariable();\n\n        local ifScope = Scope:new(scope);\n        local proxyVar = ifScope:addVariable();\n        local metatableVar = ifScope:addVariable();\n        local elseScope = Scope:new(scope);\n        ifScope:addReferenceToHigherScope(self.scope, self.newproxyVar);\n        ifScope:addReferenceToHigherScope(self.scope, self.getmetatableVar);\n        ifScope:addReferenceToHigherScope(self.scope, self.upvaluesGcFunctionVar);\n        ifScope:addReferenceToHigherScope(scope, entriesVar);\n        elseScope:addReferenceToHigherScope(self.scope, self.setmetatableVar);\n        elseScope:addReferenceToHigherScope(scope, entriesVar);\n        elseScope:addReferenceToHigherScope(self.scope, self.upvaluesGcFunctionVar);\n\n        local forScope = Scope:new(scope);\n        local forArg = forScope:addVariable();\n        forScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 2);\n        forScope:addReferenceToHigherScope(scope, entriesVar, 2);\n\n        return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, entriesVar)}, Ast.Block({\n            Ast.ForStatement(forScope, forArg, Ast.NumberExpression(1), Ast.LenExpression(Ast.VariableExpression(scope, entriesVar)), Ast.NumberExpression(1), Ast.Block({\n                Ast.AssignmentStatement({\n                    Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.IndexExpression(Ast.VariableExpression(scope, entriesVar), Ast.VariableExpression(forScope, forArg)))\n                }, {\n                    Ast.AddExpression(unpack(util.shuffle{\n                        Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.IndexExpression(Ast.VariableExpression(scope, entriesVar), Ast.VariableExpression(forScope, forArg))),\n                        Ast.NumberExpression(1),\n                    }))\n                })\n            }, forScope), scope);\n            Ast.IfStatement(Ast.VariableExpression(self.scope, self.newproxyVar), Ast.Block({\n                Ast.LocalVariableDeclaration(ifScope, {proxyVar}, {\n                    Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.newproxyVar), {\n                        Ast.BooleanExpression(true)\n                    });\n                });\n                Ast.LocalVariableDeclaration(ifScope, {metatableVar}, {\n                    Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.getmetatableVar), {\n                        Ast.VariableExpression(ifScope, proxyVar);\n                    });\n                });\n                Ast.AssignmentStatement({\n                    Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression(\"__index\")),\n                    Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression(\"__gc\")),\n                    Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression(\"__len\")),\n                }, {\n                    Ast.VariableExpression(scope, entriesVar),\n                    Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar),\n                    Ast.FunctionLiteralExpression({}, Ast.Block({\n                        Ast.ReturnStatement({Ast.NumberExpression(self.upvalsProxyLenReturn)})\n                    }, Scope:new(ifScope)));\n                });\n                Ast.ReturnStatement({\n                    Ast.VariableExpression(ifScope, proxyVar)\n                })\n            }, ifScope), {}, Ast.Block({\n                Ast.ReturnStatement({Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.setmetatableVar), {\n                    Ast.TableConstructorExpression({}),\n                    Ast.TableConstructorExpression({\n                        Ast.KeyedTableEntry(Ast.StringExpression(\"__gc\"), Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar)),\n                        Ast.KeyedTableEntry(Ast.StringExpression(\"__index\"), Ast.VariableExpression(scope, entriesVar)),\n                        Ast.KeyedTableEntry(Ast.StringExpression(\"__len\"), Ast.FunctionLiteralExpression({}, Ast.Block({\n                            Ast.ReturnStatement({Ast.NumberExpression(self.upvalsProxyLenReturn)})\n                        }, Scope:new(ifScope)))),\n                    })\n                })})\n            }, elseScope));\n        }, scope));\n    end\n\n    function Compiler:createAllocUpvalFunction()\n        local scope = Scope:new(self.scope);\n        scope:addReferenceToHigherScope(self.scope, self.currentUpvalId, 4);\n        scope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 1);\n\n        return Ast.FunctionLiteralExpression({}, Ast.Block({\n            Ast.AssignmentStatement({\n                    Ast.AssignmentVariable(self.scope, self.currentUpvalId),\n                },{\n                    Ast.AddExpression(unpack(util.shuffle({\n                        Ast.VariableExpression(self.scope, self.currentUpvalId),\n                        Ast.NumberExpression(1),\n                    }))),\n                }\n            ),\n            Ast.AssignmentStatement({\n                Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(self.scope, self.currentUpvalId)),\n            }, {\n                Ast.NumberExpression(1),\n            }),\n            Ast.ReturnStatement({\n                Ast.VariableExpression(self.scope, self.currentUpvalId),\n            })\n        }, scope));\n    end\nend\n\n"
  },
  {
    "path": "src/prometheus/enums.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- enums.lua\n--\n-- This Script provides some enums used by the Obfuscator.\n\nlocal Enums = {};\n\nlocal chararray = require(\"prometheus.util\").chararray;\n\nEnums.LuaVersion = {\n\tLuaU = \"LuaU\" ,\n\tLua51 = \"Lua51\",\n}\n\nEnums.Conventions = {\n\t[Enums.LuaVersion.Lua51] = {\n\t\tKeywords = {\n\t\t\t\"and\", \"break\", \"do\", \"else\", \"elseif\",\n\t\t\t\"end\", \"false\", \"for\", \"function\", \"if\",\n\t\t\t\"in\", \"local\", \"nil\", \"not\", \"or\",\n\t\t\t\"repeat\", \"return\", \"then\", \"true\", \"until\", \"while\"\n\t\t},\n\n\t\tSymbolChars = chararray(\"+-*/%^#=~<>(){}[];:,.\"),\n\t\tMaxSymbolLength = 3,\n\t\tSymbols = {\n\t\t\t\"+\", \"-\", \"*\", \"/\", \"%\", \"^\", \"#\",\n\t\t\t\"==\", \"~=\", \"<=\", \">=\", \"<\", \">\", \"=\",\n\t\t\t\"(\", \")\", \"{\", \"}\", \"[\", \"]\",\n\t\t\t\";\", \":\", \",\", \".\", \"..\", \"...\",\n\t\t},\n\n\t\tIdentChars = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789\"),\n\t\tNumberChars = chararray(\"0123456789\"),\n\t\tHexNumberChars = chararray(\"0123456789abcdefABCDEF\"),\n\t\tBinaryNumberChars = {\"0\", \"1\"},\n\t\tDecimalExponent = {\"e\", \"E\"},\n\t\tHexadecimalNums = {\"x\", \"X\"},\n\t\tBinaryNums = {\"b\", \"B\"},\n\t\tDecimalSeperators = false,\n\n\t\tEscapeSequences = {\n\t\t\t[\"a\"] = \"\\a\";\n\t\t\t[\"b\"] = \"\\b\";\n\t\t\t[\"f\"] = \"\\f\";\n\t\t\t[\"n\"] = \"\\n\";\n\t\t\t[\"r\"] = \"\\r\";\n\t\t\t[\"t\"] = \"\\t\";\n\t\t\t[\"v\"] = \"\\v\";\n\t\t\t[\"\\\\\"] = \"\\\\\";\n\t\t\t[\"\\\"\"] = \"\\\"\";\n\t\t\t[\"\\'\"] = \"\\'\";\n\t\t},\n\t\tNumericalEscapes = true,\n\t\tEscapeZIgnoreNextWhitespace = true,\n\t\tHexEscapes = true,\n\t\tUnicodeEscapes = true,\n\t},\n\t[Enums.LuaVersion.LuaU] = {\n\t\tKeywords = {\n\t\t\t\"and\", \"break\", \"do\", \"else\", \"elseif\", \"continue\",\n\t\t\t\"end\", \"false\", \"for\", \"function\", \"if\",\n\t\t\t\"in\", \"local\", \"nil\", \"not\", \"or\",\n\t\t\t\"repeat\", \"return\", \"then\", \"true\", \"until\", \"while\"\n\t\t},\n\n\t\tSymbolChars = chararray(\"+-*/%^#=~<>(){}[];:,.\"),\n\t\tMaxSymbolLength = 3,\n\t\tSymbols = {\n\t\t\t\"+\", \"-\", \"*\", \"/\", \"%\", \"^\", \"#\",\n\t\t\t\"==\", \"~=\", \"<=\", \">=\", \"<\", \">\", \"=\",\n\t\t\t\"+=\", \"-=\", \"/=\", \"%=\", \"^=\", \"..=\", \"*=\",\n\t\t\t\"(\", \")\", \"{\", \"}\", \"[\", \"]\",\n\t\t\t\";\", \":\", \",\", \".\", \"..\", \"...\",\n\t\t\t\"::\", \"->\", \"?\", \"|\", \"&\",\n\t\t},\n\n\t\tIdentChars = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789\"),\n\t\tNumberChars = chararray(\"0123456789\"),\n\t\tHexNumberChars = chararray(\"0123456789abcdefABCDEF\"),\n\t\tBinaryNumberChars = {\"0\", \"1\"},\n\t\tDecimalExponent = {\"e\", \"E\"},\n\t\tHexadecimalNums = {\"x\", \"X\"},\n\t\tBinaryNums = {\"b\", \"B\"},\n\t\tDecimalSeperators = {\"_\"},\n\n\t\tEscapeSequences = {\n\t\t\t[\"a\"] = \"\\a\";\n\t\t\t[\"b\"] = \"\\b\";\n\t\t\t[\"f\"] = \"\\f\";\n\t\t\t[\"n\"] = \"\\n\";\n\t\t\t[\"r\"] = \"\\r\";\n\t\t\t[\"t\"] = \"\\t\";\n\t\t\t[\"v\"] = \"\\v\";\n\t\t\t[\"\\\\\"] = \"\\\\\";\n\t\t\t[\"\\\"\"] = \"\\\"\";\n\t\t\t[\"\\'\"] = \"\\'\";\n\t\t},\n\t\tNumericalEscapes = true,\n\t\tEscapeZIgnoreNextWhitespace = true,\n\t\tHexEscapes = true,\n\t\tUnicodeEscapes = true,\n\t},\n}\n\nreturn Enums;\n"
  },
  {
    "path": "src/prometheus/namegenerators/Il.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators/il.lua\n--\n-- This Script provides a function for generation of weird names consisting of I, l and 1\n\nlocal MIN_CHARACTERS = 5;\nlocal MAX_INITIAL_CHARACTERS = 10;\n\n\nlocal util = require(\"prometheus.util\");\nlocal chararray = util.chararray;\n\nlocal offset = 0;\nlocal VarDigits = chararray(\"Il1\");\nlocal VarStartDigits = chararray(\"Il\");\n\nlocal function generateName(id, _)\n\tlocal name = ''\n\tid = id + offset;\n\tlocal d = id % #VarStartDigits\n\tid = (id - d) / #VarStartDigits\n\tname = name..VarStartDigits[d+1]\n\twhile id > 0 do\n\t\tlocal e = id % #VarDigits\n\t\tid = (id - e) / #VarDigits\n\t\tname = name..VarDigits[e+1]\n\tend\n\treturn name\nend\n\nlocal function prepare(_)\n\tutil.shuffle(VarDigits);\n\tutil.shuffle(VarStartDigits);\n\toffset = math.random(3 ^ MIN_CHARACTERS, 3 ^ MAX_INITIAL_CHARACTERS);\nend\n\nreturn {\n\tgenerateName = generateName,\n\tprepare = prepare\n};\n"
  },
  {
    "path": "src/prometheus/namegenerators/confuse.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators/confuse.lua\n--\n-- This Script provides a function for generation of confusing variable names\n\nlocal util = require(\"prometheus.util\");\n\nlocal varNames = {\n    \"index\",\n    \"iterator\",\n    \"length\",\n    \"size\",\n    \"key\",\n    \"value\",\n    \"data\",\n    \"count\",\n    \"increment\",\n    \"include\",\n    \"string\",\n    \"number\",\n    \"type\",\n    \"void\",\n    \"int\",\n    \"float\",\n    \"bool\",\n    \"char\",\n    \"double\",\n    \"long\",\n    \"short\",\n    \"unsigned\",\n    \"signed\",\n    \"program\",\n    \"factory\",\n    \"Factory\",\n    \"new\",\n    \"delete\",\n    \"table\",\n    \"array\",\n    \"object\",\n    \"class\",\n    \"arr\",\n    \"obj\",\n    \"cls\",\n    \"dir\",\n    \"directory\",\n    \"isWindows\",\n    \"isLinux\",\n    \"game\",\n    \"roblox\",\n    \"gmod\",\n    \"gsub\",\n    \"gmatch\",\n    \"gfind\",\n    \"onload\",\n    \"load\",\n    \"loadstring\",\n    \"loadfile\",\n    \"dofile\",\n    \"require\",\n    \"parse\",\n    \"byte\",\n    \"code\",\n    \"bytecode\",\n    \"idx\",\n    \"const\",\n    \"loader\",\n    \"loaders\",\n    \"module\",\n    \"export\",\n    \"exports\",\n    \"import\",\n    \"imports\",\n    \"package\",\n    \"packages\",\n    \"_G\",\n    \"math\",\n    \"os\",\n    \"io\",\n    \"write\",\n    \"print\",\n    \"read\",\n    \"readline\",\n    \"readlines\",\n    \"close\",\n    \"flush\",\n    \"open\",\n    \"popen\",\n    \"tmpfile\",\n    \"tmpname\",\n    \"rename\",\n    \"remove\",\n    \"seek\",\n    \"setvbuf\",\n    \"lines\",\n    \"call\",\n    \"apply\",\n    \"raise\",\n    \"pcall\",\n    \"xpcall\",\n    \"coroutine\",\n    \"create\",\n    \"resume\",\n    \"status\",\n    \"wrap\",\n    \"yield\",\n    \"debug\",\n    \"traceback\",\n    \"getinfo\",\n    \"getlocal\",\n    \"setlocal\",\n    \"getupvalue\",\n    \"setupvalue\",\n    \"getuservalue\",\n    \"setuservalue\",\n    \"upvalueid\",\n    \"upvaluejoin\",\n    \"sethook\",\n    \"gethook\",\n    \"hookfunction\",\n    \"hooks\",\n    \"error\",\n    \"setmetatable\",\n    \"getmetatable\",\n    \"rand\",\n    \"randomseed\",\n    \"next\",\n    \"ipairs\",\n    \"hasnext\",\n    \"loadlib\",\n    \"searchpath\",\n    \"oldpath\",\n    \"newpath\",\n    \"path\",\n    \"rawequal\",\n    \"rawset\",\n    \"rawget\",\n    \"rawnew\",\n    \"rawlen\",\n    \"select\",\n    \"tonumber\",\n    \"tostring\",\n    \"assert\",\n    \"collectgarbage\",\n    \"a\", \"b\", \"c\", \"i\", \"j\", \"m\",\n}\n\nlocal function generateName(id, _)\n    local name = {};\n    local d = id % #varNames\n\tid = (id - d) / #varNames\n\ttable.insert(name, varNames[d + 1]);\n\twhile id > 0 do\n\t\tlocal e = id % #varNames\n\t\tid = (id - e) / #varNames\n\t\ttable.insert(name, varNames[e + 1]);\n\tend\n\treturn table.concat(name, \"_\");\nend\n\nlocal function prepare(_)\n    util.shuffle(varNames);\nend\n\nreturn {\n\tgenerateName = generateName,\n\tprepare = prepare\n};\n"
  },
  {
    "path": "src/prometheus/namegenerators/mangled.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators/mangled.lua\n--\n-- This Script provides a function for generation of mangled names\n\n\nlocal util = require(\"prometheus.util\");\nlocal chararray = util.chararray;\n\nlocal VarDigits = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_\");\nlocal VarStartDigits = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n\nreturn function(id, _)\n\tlocal name = ''\n\tlocal d = id % #VarStartDigits\n\tid = (id - d) / #VarStartDigits\n\tname = name..VarStartDigits[d+1]\n\twhile id > 0 do\n\t\tlocal e = id % #VarDigits\n\t\tid = (id - e) / #VarDigits\n\t\tname = name..VarDigits[e+1]\n\tend\n\treturn name\nend"
  },
  {
    "path": "src/prometheus/namegenerators/mangled_shuffled.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators/mangled_shuffled.lua\n--\n-- This Script provides a function for generation of mangled names with shuffled character order\n\n\nlocal util = require(\"prometheus.util\");\nlocal chararray = util.chararray;\n\nlocal VarDigits = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_\");\nlocal VarStartDigits = chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n\nlocal function generateName(id, _)\n\tlocal name = ''\n\tlocal d = id % #VarStartDigits\n\tid = (id - d) / #VarStartDigits\n\tname = name..VarStartDigits[d+1]\n\twhile id > 0 do\n\t\tlocal e = id % #VarDigits\n\t\tid = (id - e) / #VarDigits\n\t\tname = name..VarDigits[e+1]\n\tend\n\treturn name\nend\n\nlocal function prepare(_)\n\tutil.shuffle(VarDigits);\n\tutil.shuffle(VarStartDigits);\nend\n\nreturn {\n\tgenerateName = generateName,\n\tprepare = prepare\n};"
  },
  {
    "path": "src/prometheus/namegenerators/number.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators/number.lua\n--\n-- This Script provides a function for generation of simple up counting names but with hex numbers\n\nlocal PREFIX = \"_\";\n\nreturn function(id, _)\n\treturn PREFIX .. tostring(id);\nend\n"
  },
  {
    "path": "src/prometheus/namegenerators.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- namegenerators.lua\n--\n-- This Script provides a collection of name generators for Prometheus.\n\nreturn {\n\tMangled = require(\"prometheus.namegenerators.mangled\");\n\tMangledShuffled = require(\"prometheus.namegenerators.mangled_shuffled\");\n\tIl = require(\"prometheus.namegenerators.Il\");\n\tNumber = require(\"prometheus.namegenerators.number\");\n\tConfuse = require(\"prometheus.namegenerators.confuse\");\n}"
  },
  {
    "path": "src/prometheus/parser.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- parser.lua\n-- Overview:\n-- This Script provides a class for parsing of lua code.\n-- This Parser is Capable of parsing LuaU and Lua5.1\n--\n-- Note that when parsing LuaU \"continue\" is treated as a Keyword, so no variable may be named \"continue\" even though this would be valid in LuaU\n--\n-- Settings Object:\n-- luaVersion : The LuaVersion of the Script - Currently Supported : Lua51 and LuaU\n--\n\nlocal Tokenizer = require(\"prometheus.tokenizer\");\nlocal Enums = require(\"prometheus.enums\");\nlocal util = require(\"prometheus.util\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal logger = require(\"logger\");\n\nlocal AstKind = Ast.AstKind;\n\nlocal LuaVersion = Enums.LuaVersion;\nlocal lookupify = util.lookupify;\n\nlocal TokenKind = Tokenizer.TokenKind;\n\nlocal Parser = {};\n\nlocal ASSIGNMENT_NO_WARN_LOOKUP = lookupify{\n\tAstKind.NilExpression,\n\tAstKind.FunctionCallExpression,\n\tAstKind.PassSelfFunctionCallExpression,\n\tAstKind.VarargExpression\n};\n\nlocal CALLABLE_PREFIX_EXPRESSION_LOOKUP = lookupify{\n\tAstKind.VariableExpression,\n\tAstKind.IndexExpression,\n\tAstKind.FunctionCallExpression,\n\tAstKind.PassSelfFunctionCallExpression\n};\n\nlocal function generateError(self, message)\n\tlocal token;\n\tif(self.index > self.length) then\n\t\ttoken = self.tokens[self.length];\n\telseif(self.index < 1) then\n\t\treturn \"Parsing Error at Position 0:0, \" .. message;\n\telse\n\t\ttoken = self.tokens[self.index];\n\tend\n\n\treturn \"Parsing Error at Position \" .. tostring(token.line) .. \":\" .. tostring(token.linePos) .. \", \" .. message;\nend\n\nlocal function generateWarning(token, message)\n\treturn \"Warning at Position \" .. tostring(token.line) .. \":\" .. tostring(token.linePos) .. \", \" .. message;\nend\n\nfunction Parser:new(settings)\n\tlocal luaVersion = (settings and (settings.luaVersion or settings.LuaVersion)) or LuaVersion.LuaU;\n\tlocal parser = {\n\t\tluaVersion = luaVersion,\n\t\ttokenizer = Tokenizer:new({\n\t\t\tluaVersion = luaVersion\n\t\t}),\n\t\ttokens = {};\n\t\tlength = 0;\n\t\tindex = 0;\n\t};\n\n\tsetmetatable(parser, self);\n\tself.__index = self;\n\n\treturn parser;\nend\n\n-- Function to peek the n'th token\nlocal function peek(self, n)\n\tn = n or 0;\n\tlocal i = self.index + n + 1;\n\tif i > self.length then\n\t\treturn Tokenizer.EOF_TOKEN;\n\tend\n\treturn self.tokens[i];\nend\n\n-- Function to get the next Token\nlocal function get(self)\n\tlocal i = self.index + 1;\n\tif i > self.length then\n\t\terror(generateError(self, \"Unexpected end of Input\"));\n\tend\n\tself.index = self.index + 1;\n\tlocal tk = self.tokens[i];\n\n\treturn tk;\nend\n\nlocal function is(self, kind, sourceOrN, n)\n\tlocal token = peek(self, n);\n\n\tlocal source = nil;\n\tif(type(sourceOrN) == \"string\") then\n\t\tsource = sourceOrN;\n\telse\n\t\tn = sourceOrN;\n\tend\n\tn = n or 0;\n\n\tif(token.kind == kind) then\n\t\tif(source == nil or token.source == source) then\n\t\t\treturn true;\n\t\tend\n\tend\n\n\treturn false;\nend\n\nlocal function consume(self, kind, source)\n\tif(is(self, kind, source)) then\n\t\tself.index = self.index + 1;\n\t\treturn true;\n\tend\n\treturn false;\nend\n\nlocal function expect(self, kind, source)\n\tif(is(self, kind, source, 0)) then\n\t\treturn get(self);\n\tend\n\n\tlocal token = peek(self);\n\tif self.disableLog then error() end\n\tif(source) then\n\t\tlogger:error(generateError(self, string.format(\"unexpected token <%s> \\\"%s\\\", expected <%s> \\\"%s\\\"\", token.kind, token.source, kind, source)));\n\telse\n\t\tlogger:error(generateError(self, string.format(\"unexpected token <%s> \\\"%s\\\", expected <%s>\", token.kind, token.source, kind)));\n\tend\nend\n\n-- Parse the given code to an Abstract Syntax Tree\nfunction Parser:parse(code)\n\tself.tokenizer:append(code);\n\tself.tokens = self.tokenizer:scanAll();\n\tself.length = #self.tokens;\n\n\t-- Create Global Variable Scope\n\tlocal globalScope = Scope:newGlobal();\n\n\tlocal ast = Ast.TopNode(self:block(globalScope, false), globalScope);\n\t-- File Must be Over when Top Node is Fully Parsed\n\texpect(self, TokenKind.Eof);\n\n\tlogger:debug(\"Cleaning up Parser for next Use ...\")\n\t-- Clean Up\n\tself.tokenizer:reset();\n\tself.tokens = {};\n\tself.index = 0;\n\tself.length = 0;\n\n\tlogger:debug(\"Cleanup Done\")\n\n\treturn ast;\nend\n\n-- Parse a Code Block\nfunction Parser:block(parentScope, currentLoop, scope)\n\tscope = scope or Scope:new(parentScope);\n\tlocal statements = {};\n\n\trepeat\n\t\tlocal statement, isTerminatingStatement = self:statement(scope, currentLoop);\n\t\ttable.insert(statements, statement);\n\tuntil isTerminatingStatement or not statement\n\n\t-- Consume Eventual Semicolon after terminating return, break or continue\n\tconsume(self, TokenKind.Symbol, \";\");\n\n\treturn Ast.Block(statements, scope);\nend\n\nfunction Parser:statement(scope, currentLoop)\n\t-- Skip all semicolons before next real statement\n\t-- NOP statements are therefore ignored\n\twhile(consume(self, TokenKind.Symbol, \";\")) do\n\n\tend\n\n\t-- Break Statement - only valid inside of Loops\n\tif(consume(self, TokenKind.Keyword, \"break\")) then\n\t\tif(not currentLoop) then\n\t\t\tif self.disableLog then error() end;\n\t\t\tlogger:error(generateError(self, \"the break Statement is only valid inside of loops\"));\n\t\tend\n\t\t-- Return true as Second value because break must be the last Statement in a block\n\t\treturn Ast.BreakStatement(currentLoop, scope), true;\n\tend\n\n\t-- Continue Statement - only valid inside of Loops - only valid in LuaU\n\tif(self.luaVersion == LuaVersion.LuaU and consume(self, TokenKind.Keyword, \"continue\")) then\n\t\tif(not currentLoop) then\n\t\t\tif self.disableLog then error() end;\n\t\t\tlogger:error(generateError(self, \"the continue Statement is only valid inside of loops\"));\n\t\tend\n\t\t-- Return true as Second value because continue must be the last Statement in a block\n\t\treturn Ast.ContinueStatement(currentLoop, scope), true;\n\tend\n\n\t-- do ... end Statement\n\tif(consume(self, TokenKind.Keyword, \"do\")) then\n\t\tlocal body = self:block(scope, currentLoop);\n\t\texpect(self, TokenKind.Keyword, \"end\");\n\t\treturn Ast.DoStatement(body);\n\tend\n\n\t-- While Statement\n\tif(consume(self, TokenKind.Keyword, \"while\")) then\n\t\tlocal condition = self:expression(scope);\n\t\texpect(self, TokenKind.Keyword, \"do\");\n\t\tlocal stat = Ast.WhileStatement(nil, condition, scope);\n\t\tstat.body = self:block(scope, stat);\n\t\texpect(self, TokenKind.Keyword, \"end\");\n\t\treturn stat;\n\tend\n\n\t-- Repeat Statement\n\tif(consume(self, TokenKind.Keyword, \"repeat\")) then\n\t\tlocal repeatScope = Scope:new(scope);\n\t\tlocal stat = Ast.RepeatStatement(nil, nil, scope);\n\t\tstat.body = self:block(nil, stat, repeatScope);\n\t\texpect(self, TokenKind.Keyword, \"until\");\n\t\tstat.condition = self:expression(repeatScope);\n\t\treturn stat;\n\tend\n\n\t-- Return Statement\n\tif(consume(self, TokenKind.Keyword, \"return\")) then\n\t\tlocal args = {};\n\t\tif(not is(self, TokenKind.Keyword, \"end\") and not is(self, TokenKind.Keyword, \"elseif\") and not is(self, TokenKind.Keyword, \"else\") and not is(self, TokenKind.Symbol, \";\") and not is(self, TokenKind.Eof)) then\n\t\t\targs = self:exprList(scope);\n\t\tend\n\t\t-- Return true as Second value because return must be the last Statement in a block\n\t\treturn Ast.ReturnStatement(args), true;\n\tend\n\n\t-- If Statement\n\tif(consume(self, TokenKind.Keyword, \"if\")) then\n\t\tlocal condition = self:expression(scope);\n\t\texpect(self, TokenKind.Keyword, \"then\");\n\t\tlocal body = self:block(scope, currentLoop);\n\n\t\tlocal elseifs = {};\n\t\t-- Elseifs\n\t\twhile(consume(self, TokenKind.Keyword, \"elseif\")) do\n\t\t\tlocal condition = self:expression(scope);\n\t\t\texpect(self, TokenKind.Keyword, \"then\");\n\t\t\tlocal body = self:block(scope, currentLoop);\n\n\t\t\ttable.insert(elseifs, {\n\t\t\t\tcondition = condition,\n\t\t\t\tbody = body,\n\t\t\t});\n\t\tend\n\n\t\tlocal elsebody = nil;\n\t\t-- Else\n\t\tif(consume(self, TokenKind.Keyword, \"else\")) then\n\t\t\telsebody = self:block(scope, currentLoop);\n\t\tend\n\n\t\texpect(self, TokenKind.Keyword, \"end\");\n\n\t\treturn Ast.IfStatement(condition, body, elseifs, elsebody);\n\tend\n\n\t-- Function Declaration\n\tif(consume(self, TokenKind.Keyword, \"function\")) then\n\t\t-- TODO: Parse Function Declaration Name\n\t\tlocal obj = self:funcName(scope);\n\t\tlocal baseScope = obj.scope;\n\t\tlocal baseId = obj.id;\n\t\tlocal indices = obj.indices;\n\n\t\tlocal funcScope = Scope:new(scope);\n\n\t\texpect(self, TokenKind.Symbol, \"(\");\n\t\tlocal args = self:functionArgList(funcScope);\n\t\texpect(self, TokenKind.Symbol, \")\");\n\n\t\tif(obj.passSelf) then\n\t\t\tlocal id = funcScope:addVariable(\"self\", obj.token);\n\t\t\ttable.insert(args, 1, Ast.VariableExpression(funcScope, id));\n\t\tend\n\n\t\tlocal body = self:block(nil, false, funcScope);\n\t\texpect(self, TokenKind.Keyword, \"end\");\n\n\t\treturn Ast.FunctionDeclaration(baseScope, baseId, indices, args, body);\n\tend\n\n\t-- Local Function or Variable Declaration\n\tif(consume(self, TokenKind.Keyword, \"local\")) then\n\t\t-- Local Function Declaration\n\t\tif(consume(self, TokenKind.Keyword, \"function\")) then\n\t\t\tlocal ident = expect(self, TokenKind.Ident);\n\t\t\tlocal name = ident.value;\n\n\t\t\tlocal id = scope:addVariable(name, ident);\n\t\t\tlocal funcScope = Scope:new(scope);\n\n\t\t\texpect(self, TokenKind.Symbol, \"(\");\n\t\t\tlocal args = self:functionArgList(funcScope);\n\t\t\texpect(self, TokenKind.Symbol, \")\");\n\n\t\t\tlocal body = self:block(nil, false, funcScope);\n\t\t\texpect(self, TokenKind.Keyword, \"end\");\n\n\t\t\treturn Ast.LocalFunctionDeclaration(scope, id, args, body);\n\t\tend\n\n\t\t-- Local Variable Declaration\n\t\tlocal ids = self:nameList(scope);\n\t\tlocal expressions = {};\n\t\tif(consume(self, TokenKind.Symbol, \"=\")) then\n\t\t\texpressions = self:exprList(scope);\n\t\tend\n\n\t\t-- Variables can only be reffered to in the next statement, so the id's are enabled after the expressions have been parsed\n\t\tself:enableNameList(scope, ids);\n\n\t\tif(#expressions > #ids) then\n\t\t\tlogger:warn(generateWarning(peek(self, -1), string.format(\"assigning %d values to %d variable\" .. ((#ids > 1 and \"s\") or \"\"), #expressions, #ids)));\n\t\telseif(#ids > #expressions and #expressions > 0 and not ASSIGNMENT_NO_WARN_LOOKUP[expressions[#expressions].kind]) then\n\t\t\tlogger:warn(generateWarning(peek(self, -1), string.format(\"assigning %d value\" .. ((#expressions > 1 and \"s\") or \"\") ..\n\t\t\t\t\" to %d variables initializes extra variables with nil, add a nil value to silence\", #expressions, #ids)));\n\t\tend\n\t\treturn Ast.LocalVariableDeclaration(scope, ids, expressions);\n\tend\n\n\t-- For Statement\n\tif(consume(self, TokenKind.Keyword, \"for\")) then\n\t\t-- Normal for Statement\n\t\tif(is(self, TokenKind.Symbol, \"=\", 1)) then\n\t\t\tlocal forScope = Scope:new(scope);\n\n\t\t\tlocal ident = expect(self, TokenKind.Ident);\n\t\t\tlocal varId = forScope:addDisabledVariable(ident.value, ident);\n\n\t\t\texpect(self, TokenKind.Symbol, \"=\");\n\t\t\tlocal initialValue = self:expression(scope);\n\n\t\t\texpect(self, TokenKind.Symbol, \",\");\n\t\t\tlocal finalValue = self:expression(scope);\n\t\t\tlocal incrementBy = Ast.NumberExpression(1);\n\t\t\tif(consume(self, TokenKind.Symbol, \",\")) then\n\t\t\t\tincrementBy = self:expression(scope);\n\t\t\tend\n\n\t\t\tlocal stat = Ast.ForStatement(forScope, varId, initialValue, finalValue, incrementBy, nil, scope);\n\t\t\tforScope:enableVariable(varId);\n\t\t\texpect(self, TokenKind.Keyword, \"do\");\n\t\t\tstat.body = self:block(nil, stat, forScope);\n\t\t\texpect(self, TokenKind.Keyword, \"end\");\n\t\t\treturn stat;\n\t\tend\n\n\t\t-- For ... in ... statement\n\t\tlocal forScope = Scope:new(scope);\n\n\t\tlocal ids = self:nameList(forScope);\n\t\texpect(self, TokenKind.Keyword, \"in\");\n\t\tlocal expressions = self:exprList(scope);\n\t\t-- Enable Ids after Expression Parsing so that code like this works:\n\t\t--\tlocal z = {10,20}\n\t\t--\tfor y,z in ipairs(z) do\n\t\t--\t\tprint(y, z);\n\t\t-- \tend\n\t\tself:enableNameList(forScope, ids);\n\t\texpect(self, TokenKind.Keyword, \"do\");\n\t\tlocal stat = Ast.ForInStatement(forScope, ids, expressions, nil, scope);\n\t\tstat.body = self:block(nil, stat, forScope);\n\t\texpect(self, TokenKind.Keyword, \"end\");\n\n\t\treturn stat;\n\tend\n\n\tlocal expr = self:primaryExpression(scope);\n\t-- Variable Assignment or Function Call\n\tif expr then\n\t\t-- Function Call Statement\n\t\tif(expr.kind == AstKind.FunctionCallExpression) then\n\t\t\treturn Ast.FunctionCallStatement(expr.base, expr.args);\n\t\tend\n\n\t\t-- Function Call Statement passing self\n\t\tif(expr.kind == AstKind.PassSelfFunctionCallExpression) then\n\t\t\treturn Ast.PassSelfFunctionCallStatement(expr.base, expr.passSelfFunctionName, expr.args);\n\t\tend\n\n\t\t-- Variable Assignment\n\t\tif(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then\n\t\t\tif(expr.kind == AstKind.IndexExpression) then\n\t\t\t\texpr.kind = AstKind.AssignmentIndexing\n\t\t\tend\n\t\t\tif(expr.kind == AstKind.VariableExpression) then\n\t\t\t\texpr.kind = AstKind.AssignmentVariable\n\t\t\tend\n\n\t\t\tif(self.luaVersion == LuaVersion.LuaU) then\n\t\t\t\t-- LuaU Compound Assignment\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"+=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundAddStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"-=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundSubStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"*=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundMulStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"/=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundDivStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"%=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundModStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"^=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundPowStatement(expr, rhs);\n\t\t\t\tend\n\n\t\t\t\tif(consume(self, TokenKind.Symbol, \"..=\")) then\n\t\t\t\t\tlocal rhs = self:expression(scope);\n\t\t\t\t\treturn Ast.CompoundConcatStatement(expr, rhs);\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal lhs = {\n\t\t\t\texpr\n\t\t\t}\n\n\t\t\twhile consume(self, TokenKind.Symbol, \",\") do\n\t\t\t\texpr = self:primaryExpression(scope);\n\n\t\t\t\tif(not expr) then\n\t\t\t\t\tif self.disableLog then error() end;\n\t\t\t\t\tlogger:error(generateError(self, string.format(\"expected a valid assignment statement lhs part but got nil\")));\n\t\t\t\tend\n\n\t\t\t\tif(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then\n\t\t\t\t\tif(expr.kind == AstKind.IndexExpression) then\n\t\t\t\t\t\texpr.kind = AstKind.AssignmentIndexing\n\t\t\t\t\tend\n\t\t\t\t\tif(expr.kind == AstKind.VariableExpression) then\n\t\t\t\t\t\texpr.kind = AstKind.AssignmentVariable\n\t\t\t\t\tend\n\t\t\t\t\ttable.insert(lhs, expr);\n\t\t\t\telse\n\t\t\t\t\tif self.disableLog then error() end;\n\t\t\t\t\tlogger:error(generateError(self, string.format(\"expected a valid assignment statement lhs part but got <%s>\", expr.kind)));\n\t\t\t\tend\n\t\t\tend\n\n\t\t\texpect(self, TokenKind.Symbol, \"=\");\n\n\t\t\tlocal rhs = self:exprList(scope);\n\n\t\t\treturn Ast.AssignmentStatement(lhs, rhs);\n\t\tend\n\n\t\tif self.disableLog then error() end;\n\t\tlogger:error(generateError(self, \"expressions are not valid statements!\"));\n\tend\n\n\treturn nil;\nend\n\nfunction Parser:primaryExpression(scope)\n\tlocal i = self.index;\n\tlocal s = self;\n\tself.disableLog = true;\n\tlocal status, val = pcall(self.expressionFunctionCall, self, scope);\n\tself.disableLog = false;\n\tif(status) then\n\t\treturn val;\n\telse\n\t\tself.index = i;\n\t\treturn nil;\n\tend\nend\n\n-- List of expressions Seperated by a comma\nfunction Parser:exprList(scope)\n\tlocal expressions = {\n\t\tself:expression(scope)\n\t};\n\twhile(consume(self, TokenKind.Symbol, \",\")) do\n\t\ttable.insert(expressions, self:expression(scope));\n\tend\n\treturn expressions;\nend\n\n-- list of local variable names\nfunction Parser:nameList(scope)\n\tlocal ids = {};\n\n\tlocal ident = expect(self, TokenKind.Ident);\n\tlocal id = scope:addDisabledVariable(ident.value, ident);\n\ttable.insert(ids, id);\n\n\twhile(consume(self, TokenKind.Symbol, \",\")) do\n\t\tident = expect(self, TokenKind.Ident);\n\t\tid = scope:addDisabledVariable(ident.value, ident);\n\t\ttable.insert(ids, id);\n\tend\n\n\treturn ids;\nend\n\nfunction Parser:enableNameList(scope, list)\n\tfor i, id in ipairs(list) do\n\t\tscope:enableVariable(id);\n\tend\nend\n\n\n-- function name\nfunction Parser:funcName(scope)\n\tlocal ident = expect(self, TokenKind.Ident);\n\tlocal baseName = ident.value;\n\n\tlocal baseScope, baseId = scope:resolve(baseName);\n\n\tlocal indices = {};\n\tlocal passSelf = false;\n\twhile(consume(self, TokenKind.Symbol, \".\")) do\n\t\ttable.insert(indices, expect(self, TokenKind.Ident).value);\n\tend\n\n\tif(consume(self, TokenKind.Symbol, \":\")) then\n\t\ttable.insert(indices, expect(self, TokenKind.Ident).value);\n\t\tpassSelf = true;\n\tend\n\n\treturn {\n\t\tscope = baseScope,\n\t\tid = baseId,\n\t\tindices = indices,\n\t\tpassSelf = passSelf,\n\t\ttoken = ident,\n\t};\nend\n\n-- Expression\nfunction Parser:expression(scope)\n\treturn self:expressionOr(scope);\nend\n\nfunction Parser:expressionOr(scope)\n\tlocal lhs = self:expressionAnd(scope);\n\n\tif(consume(self, TokenKind.Keyword, \"or\")) then\n\t\tlocal rhs = self:expressionOr(scope);\n\t\treturn Ast.OrExpression(lhs, rhs, true);\n\tend\n\n\treturn lhs;\nend\n\nfunction Parser:expressionAnd(scope)\n\tlocal lhs = self:expressionComparision(scope);\n\n\tif(consume(self, TokenKind.Keyword, \"and\")) then\n\t\tlocal rhs = self:expressionAnd(scope);\n\t\treturn Ast.AndExpression(lhs, rhs, true);\n\tend\n\n\treturn lhs;\nend\n\nfunction Parser:expressionComparision(scope)\n\tlocal curr = self:expressionStrCat(scope);\n\trepeat\n\t\tlocal found = false;\n\t\tif(consume(self, TokenKind.Symbol, \"<\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.LessThanExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \">\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.GreaterThanExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"<=\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.LessThanOrEqualsExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \">=\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.GreaterThanOrEqualsExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"~=\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.NotEqualsExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"==\")) then\n\t\t\tlocal rhs = self:expressionStrCat(scope);\n\t\t\tcurr = Ast.EqualsExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\tuntil not found;\n\n\treturn curr;\nend\n\nfunction Parser:expressionStrCat(scope)\n\tlocal lhs = self:expressionAddSub(scope);\n\n\tif(consume(self, TokenKind.Symbol, \"..\")) then\n\t\tlocal rhs = self:expressionStrCat(scope);\n\t\treturn Ast.StrCatExpression(lhs, rhs, true);\n\tend\n\n\treturn lhs;\nend\n\nfunction Parser:expressionAddSub(scope)\n\tlocal curr = self:expressionMulDivMod(scope);\n\n\trepeat\n\t\tlocal found = false;\n\t\tif(consume(self, TokenKind.Symbol, \"+\")) then\n\t\t\tlocal rhs = self:expressionMulDivMod(scope);\n\t\t\tcurr = Ast.AddExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"-\")) then\n\t\t\tlocal rhs = self:expressionMulDivMod(scope);\n\t\t\tcurr = Ast.SubExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\tuntil not found;\n\n\n\treturn curr;\nend\n\nfunction Parser:expressionMulDivMod(scope)\n\tlocal curr = self:expressionUnary(scope);\n\n\trepeat\n\t\tlocal found = false;\n\t\tif(consume(self, TokenKind.Symbol, \"*\")) then\n\t\t\tlocal rhs = self:expressionUnary(scope);\n\t\t\tcurr = Ast.MulExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"/\")) then\n\t\t\tlocal rhs = self:expressionUnary(scope);\n\t\t\tcurr = Ast.DivExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\n\t\tif(consume(self, TokenKind.Symbol, \"%\")) then\n\t\t\tlocal rhs = self:expressionUnary(scope);\n\t\t\tcurr = Ast.ModExpression(curr, rhs, true);\n\t\t\tfound = true;\n\t\tend\n\tuntil not found;\n\n\treturn curr;\nend\n\nfunction Parser:expressionUnary(scope)\n\tif(consume(self, TokenKind.Keyword, \"not\")) then\n\t\tlocal rhs = self:expressionUnary(scope);\n\t\treturn Ast.NotExpression(rhs, true);\n\tend\n\n\tif(consume(self, TokenKind.Symbol, \"#\")) then\n\t\tlocal rhs = self:expressionUnary(scope);\n\t\treturn Ast.LenExpression(rhs, true);\n\tend\n\n\tif(consume(self, TokenKind.Symbol, \"-\")) then\n\t\tlocal rhs = self:expressionUnary(scope);\n\t\treturn Ast.NegateExpression(rhs, true);\n\tend\n\n\treturn self:expressionPow(scope);\nend\n\nfunction Parser:expressionPow(scope)\n\tlocal lhs = self:tableOrFunctionLiteral(scope);\n\n\tif(consume(self, TokenKind.Symbol, \"^\")) then\n\t\t-- Allow unary operators on the rhs (e.g. 2 ^ #x, 2 ^ -x) while preserving right-associativity. ~ SpinnySpiwal\n\t\tlocal rhs = self:expressionUnary(scope);\n\t\treturn Ast.PowExpression(lhs, rhs, true);\n\tend\n\n\treturn lhs;\nend\n\n-- Table Literals and Function Literals cannot directly be called or indexed\nfunction Parser:tableOrFunctionLiteral(scope)\n\n\tif(is(self, TokenKind.Symbol, \"{\")) then\n\t\treturn self:tableConstructor(scope);\n\tend\n\n\tif(is(self, TokenKind.Keyword, \"function\")) then\n\t\treturn self:expressionFunctionLiteral(scope);\n\tend\n\n\treturn self:expressionFunctionCall(scope);\nend\n\nfunction Parser:expressionFunctionLiteral(parentScope)\n\tlocal scope = Scope:new(parentScope);\n\n\texpect(self, TokenKind.Keyword, \"function\");\n\n\texpect(self, TokenKind.Symbol, \"(\");\n\tlocal args = self:functionArgList(scope);\n\texpect(self, TokenKind.Symbol, \")\");\n\n\tlocal body = self:block(nil, false, scope);\n\texpect(self, TokenKind.Keyword, \"end\");\n\n\treturn Ast.FunctionLiteralExpression(args, body);\nend\n\nfunction Parser:functionArgList(scope)\n\tlocal args = {};\n\tif(consume(self, TokenKind.Symbol, \"...\")) then\n\t\ttable.insert(args, Ast.VarargExpression());\n\t\treturn args;\n\tend\n\n\tif(is(self, TokenKind.Ident)) then\n\t\tlocal ident = get(self);\n\t\tlocal name = ident.value;\n\n\t\tlocal id = scope:addVariable(name, ident);\n\t\ttable.insert(args, Ast.VariableExpression(scope, id));\n\n\t\twhile(consume(self, TokenKind.Symbol, \",\")) do\n\t\t\tif(consume(self, TokenKind.Symbol, \"...\")) then\n\t\t\t\ttable.insert(args, Ast.VarargExpression());\n\t\t\t\treturn args;\n\t\t\tend\n\n\t\t\tident = get(self);\n\t\t\tname = ident.value;\n\n\t\t\tid = scope:addVariable(name, ident);\n\t\t\ttable.insert(args, Ast.VariableExpression(scope, id));\n\t\tend\n\tend\n\n\treturn args;\nend\n\nfunction Parser:expressionFunctionCall(scope, base)\n\tbase = base or self:expressionIndex(scope);\n\n\tif(not (base and (CALLABLE_PREFIX_EXPRESSION_LOOKUP[base.kind] or base.isParenthesizedExpression))) then\n\t\treturn base;\n\tend\n\n\t-- Normal Function Call\n\tlocal args = {};\n\tif(is(self, TokenKind.String)) then\n\t\targs = {\n\t\t\tAst.StringExpression(get(self).value),\n\t\t};\n\telseif(is(self, TokenKind.Symbol, \"{\")) then\n\t\targs = {\n\t\t\tself:tableConstructor(scope),\n\t\t};\n\telseif(consume(self, TokenKind.Symbol, \"(\")) then\n\t\tif(not is(self, TokenKind.Symbol, \")\")) then\n\t\t\targs = self:exprList(scope);\n\t\tend\n\t\texpect(self, TokenKind.Symbol, \")\");\n\telse\n\t\treturn base;\n\tend\n\n\tlocal node = Ast.FunctionCallExpression(base, args);\n\n\t-- the result of a function call can be indexed\n\tif(is(self, TokenKind.Symbol, \".\") or is(self, TokenKind.Symbol, \"[\") or is(self, TokenKind.Symbol, \":\")) then\n\t\treturn self:expressionIndex(scope, node);\n\tend\n\n\t-- The result of a function call can be a function that is again called\n\tif(is(self, TokenKind.Symbol, \"(\") or is(self, TokenKind.Symbol, \"{\") or is(self, TokenKind.String)) then\n\t\treturn self:expressionFunctionCall(scope, node);\n\tend\n\n\treturn node;\nend\n\nfunction Parser:expressionIndex(scope, base)\n\tbase = base or self:expressionLiteral(scope);\n\n\t-- Parse Indexing Expressions\n\twhile(consume(self, TokenKind.Symbol, \"[\")) do\n\t\tlocal expr = self:expression(scope);\n\t\texpect(self, TokenKind.Symbol, \"]\");\n\t\tbase = Ast.IndexExpression(base, expr);\n\tend\n\n\t-- Parse Indexing Expressions\n\twhile consume(self, TokenKind.Symbol, \".\") do\n\t\tlocal ident = expect(self, TokenKind.Ident);\n\t\tbase = Ast.IndexExpression(base, Ast.StringExpression(ident.value));\n\n\t\twhile(consume(self, TokenKind.Symbol, \"[\")) do\n\t\t\tlocal expr = self:expression(scope);\n\t\t\texpect(self, TokenKind.Symbol, \"]\");\n\t\t\tbase = Ast.IndexExpression(base, expr);\n\t\tend\n\tend\n\n\t-- Function Passing self\n\tif(consume(self, TokenKind.Symbol, \":\")) then\n\t\tlocal passSelfFunctionName = expect(self, TokenKind.Ident).value;\n\t\tlocal args = {};\n\t\tif(is(self, TokenKind.String)) then\n\t\t\targs = {\n\t\t\t\tAst.StringExpression(get(self).value),\n\t\t\t};\n\t\telseif(is(self, TokenKind.Symbol, \"{\")) then\n\t\t\targs = {\n\t\t\t\tself:tableConstructor(scope),\n\t\t\t};\n\t\telse\n\t\t\texpect(self, TokenKind.Symbol, \"(\");\n\t\t\tif(not is(self, TokenKind.Symbol, \")\")) then\n\t\t\t\targs = self:exprList(scope);\n\t\t\tend\n\t\t\texpect(self, TokenKind.Symbol, \")\");\n\t\tend\n\n\t\tlocal node = Ast.PassSelfFunctionCallExpression(base, passSelfFunctionName, args);\n\n\t\t-- the result of a function call can be indexed\n\t\tif(is(self, TokenKind.Symbol, \".\") or is(self, TokenKind.Symbol, \"[\") or is(self, TokenKind.Symbol, \":\")) then\n\t\t\treturn self:expressionIndex(scope, node);\n\t\tend\n\n\t\t-- The result of a function call can be a function that is again called\n\t\tif(is(self, TokenKind.Symbol, \"(\") or is(self, TokenKind.Symbol, \"{\") or is(self, TokenKind.String)) then\n\t\t\treturn self:expressionFunctionCall(scope, node);\n\t\tend\n\n\t\treturn node\n\tend\n\n\t-- The result of a function call can be a function that is again called\n\tif(is(self, TokenKind.Symbol, \"(\") or is(self, TokenKind.Symbol, \"{\") or is(self, TokenKind.String)) then\n\t\treturn self:expressionFunctionCall(scope, base);\n\tend\n\n\treturn base;\nend\n\nfunction Parser:expressionLiteral(scope)\n\t-- () expression\n\tif(consume(self, TokenKind.Symbol, \"(\")) then\n\t\tlocal expr = self:expression(scope);\n\t\texpect(self, TokenKind.Symbol, \")\");\n\t\tif expr then\n\t\t\texpr.isParenthesizedExpression = true;\n\t\tend\n\t\treturn expr;\n\tend\n\n\t-- String Literal\n\tif(is(self, TokenKind.String)) then\n\t\treturn Ast.StringExpression(get(self).value);\n\tend\n\n\t-- Number Literal\n\tif(is(self, TokenKind.Number)) then\n\t\treturn Ast.NumberExpression(get(self).value);\n\tend\n\n\t-- True Literal\n\tif(consume(self, TokenKind.Keyword, \"true\")) then\n\t\treturn Ast.BooleanExpression(true);\n\tend\n\n\t-- False Literal\n\tif(consume(self, TokenKind.Keyword, \"false\")) then\n\t\treturn Ast.BooleanExpression(false);\n\tend\n\n\t-- Nil Literal\n\tif(consume(self, TokenKind.Keyword, \"nil\")) then\n\t\treturn Ast.NilExpression();\n\tend\n\n\t-- Vararg Literal\n\tif(consume(self, TokenKind.Symbol, \"...\")) then\n\t\treturn Ast.VarargExpression();\n\tend\n\n\t-- Variable\n\tif(is(self, TokenKind.Ident)) then\n\t\tlocal ident = get(self);\n\t\tlocal name = ident.value;\n\n\t\tlocal scope, id = scope:resolve(name);\n\t\treturn Ast.VariableExpression(scope, id);\n\tend\n\n\t-- IfElse\n\tif(LuaVersion.LuaU) then\n\t\tif(consume(self, TokenKind.Keyword, \"if\")) then\n\t\t\tlocal condition = self:expression(scope);\n\t\t\texpect(self, TokenKind.Keyword, \"then\");\n\t\t\tlocal true_value = self:expression(scope);\n\t\t\texpect(self, TokenKind.Keyword, \"else\");\n\t\t\tlocal false_value = self:expression(scope);\n\n\t\t\treturn Ast.IfElseExpression(condition, true_value, false_value);\n\t\tend\n\tend\n\n\tif(self.disableLog) then error() end\n\tlogger:error(generateError(self, \"Unexpected Token \\\"\" .. peek(self).source .. \"\\\". Expected a Expression!\"))\nend\n\nfunction Parser:tableConstructor(scope)\n\t-- TODO: Parse Table Literals\n\tlocal entries = {};\n\n\texpect(self, TokenKind.Symbol, \"{\");\n\n\twhile (not consume(self, TokenKind.Symbol, \"}\")) do\n\t\tif(consume(self, TokenKind.Symbol, \"[\")) then\n\t\t\tlocal key = self:expression(scope);\n\t\t\texpect(self, TokenKind.Symbol, \"]\");\n\t\t\texpect(self, TokenKind.Symbol, \"=\");\n\t\t\tlocal value = self:expression(scope);\n\t\t\ttable.insert(entries, Ast.KeyedTableEntry(key, value));\n\t\telseif(is(self, TokenKind.Ident, 0) and is(self, TokenKind.Symbol, \"=\", 1)) then\n\t\t\tlocal key = Ast.StringExpression(get(self).value);\n\t\t\texpect(self, TokenKind.Symbol, \"=\");\n\t\t\tlocal value = self:expression(scope);\n\t\t\ttable.insert(entries, Ast.KeyedTableEntry(key, value));\n\t\telse\n\t\t\tlocal value = self:expression(scope);\n\t\t\ttable.insert(entries, Ast.TableEntry(value));\n\t\tend\n\n\n\t\tif (not consume(self, TokenKind.Symbol, \";\") and not consume(self, TokenKind.Symbol, \",\") and not is(self, TokenKind.Symbol, \"}\")) then\n\t\t\tif self.disableLog then error() end\n\t\t\tlogger:error(generateError(self, \"expected a \\\";\\\" or a \\\",\\\"\"));\n\t\tend\n\tend\n\n\treturn Ast.TableConstructorExpression(entries);\nend\n\nreturn Parser\n"
  },
  {
    "path": "src/prometheus/pipeline.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- pipeline.lua\n--\n-- This Script provides a configurable obfuscation pipeline that can obfuscate code using different modules\n-- These modules can simply be added to the pipeline.\n\nlocal Enums = require(\"prometheus.enums\");\nlocal util = require(\"prometheus.util\");\nlocal Parser = require(\"prometheus.parser\");\nlocal Unparser = require(\"prometheus.unparser\");\nlocal logger = require(\"logger\");\n\nlocal NameGenerators = require(\"prometheus.namegenerators\");\n\nlocal Steps = require(\"prometheus.steps\");\nlocal LuaVersion = Enums.LuaVersion;\n\n-- On Windows, os.clock can be used. On other systems, os.time must be used for benchmarking.\nlocal isWindows = package and package.config and type(package.config) == \"string\" and package.config:sub(1,1) == \"\\\\\";\nlocal function gettime()\n\tif isWindows then\n\t\treturn os.clock();\n\telse\n\t\treturn os.time();\n\tend\nend\n\nlocal Pipeline = {\n\tNameGenerators = NameGenerators;\n\tSteps = Steps;\n\tDefaultSettings = {\n\t\tLuaVersion = LuaVersion.LuaU; -- The Lua Version to use for the Tokenizer, Parser and Unparser\n\t\tPrettyPrint = false; -- Note that Pretty Print is currently not producing Pretty results\n\t\tSeed = 0; -- The Seed. 0 or below uses the current time as a seed\n\t\tVarNamePrefix = \"\"; -- The Prefix that every variable will start with\n\t}\n}\n\n\nfunction Pipeline:new(settings)\n\tlocal luaVersion = settings.luaVersion or settings.LuaVersion or Pipeline.DefaultSettings.LuaVersion;\n\tlocal conventions = Enums.Conventions[luaVersion];\n\tif(not conventions) then\n\t\tlogger:error(\"The Lua Version \\\"\" .. luaVersion\n\t\t\t.. \"\\\" is not recognized by the Tokenizer! Please use one of the following: \\\"\" .. table.concat(util.keys(Enums.Conventions), \"\\\",\\\"\") .. \"\\\"\");\n\tend\n\n\tlocal prettyPrint = settings.PrettyPrint or Pipeline.DefaultSettings.PrettyPrint;\n\tlocal prefix = settings.VarNamePrefix or Pipeline.DefaultSettings.VarNamePrefix;\n\tlocal seed = settings.Seed or 0;\n\n\tlocal pipeline = {\n\t\tLuaVersion = luaVersion;\n\t\tPrettyPrint = prettyPrint;\n\t\tVarNamePrefix = prefix;\n\t\tSeed = seed;\n\t\tparser = Parser:new({\n\t\t\tLuaVersion = luaVersion;\n\t\t});\n\t\tunparser = Unparser:new({\n\t\t\tLuaVersion = luaVersion;\n\t\t\tPrettyPrint = prettyPrint;\n\t\t\tHighlight = settings.Highlight;\n\t\t});\n\t\tnamegenerator = Pipeline.NameGenerators.MangledShuffled;\n\t\tconventions = conventions;\n\t\tsteps = {};\n\t}\n\n\tsetmetatable(pipeline, self);\n\tself.__index = self;\n\n\treturn pipeline;\nend\n\nfunction Pipeline:fromConfig(config)\n\tconfig = config or {};\n\tlocal pipeline = Pipeline:new({\n\t\tLuaVersion = config.LuaVersion or LuaVersion.Lua51;\n\t\tPrettyPrint = config.PrettyPrint or false;\n\t\tVarNamePrefix = config.VarNamePrefix or \"\";\n\t\tSeed = config.Seed or 0;\n\t});\n\n\tpipeline:setNameGenerator(config.NameGenerator or \"MangledShuffled\")\n\n\t-- Add all Steps defined in Config\n\tlocal steps = config.Steps or {};\n\tfor i, step in ipairs(steps) do\n\t\tif type(step.Name) ~= \"string\" then\n\t\t\tlogger:error(\"Step.Name must be a String\");\n\t\tend\n\t\tlocal constructor = pipeline.Steps[step.Name];\n\t\tif not constructor then\n\t\t\tlogger:error(string.format(\"The Step \\\"%s\\\" was not found!\", step.Name));\n\t\tend\n\t\tpipeline:addStep(constructor:new(step.Settings or {}));\n\tend\n\n\treturn pipeline;\nend\n\nfunction Pipeline:addStep(step)\n\ttable.insert(self.steps, step);\nend\n\nfunction Pipeline:resetSteps(_)\n\tself.steps = {};\nend\n\nfunction Pipeline:getSteps()\n\treturn self.steps;\nend\n\nfunction Pipeline:setOption(name, _)\n\tassert(false, \"TODO\");\n\tif(Pipeline.DefaultSettings[name] ~= nil) then\n\n\telse\n\t\tlogger:error(string.format(\"\\\"%s\\\" is not a valid setting\"));\n\tend\nend\n\nfunction Pipeline:setLuaVersion(luaVersion)\n\tlocal conventions = Enums.Conventions[luaVersion];\n\tif(not conventions) then\n\t\tlogger:error(\"The Lua Version \\\"\" .. luaVersion\n\t\t\t.. \"\\\" is not recognized by the Tokenizer! Please use one of the following: \\\"\" .. table.concat(util.keys(Enums.Conventions), \"\\\",\\\"\") .. \"\\\"\");\n\tend\n\n\tself.parser = Parser:new({\n\t\tluaVersion = luaVersion;\n\t});\n\tself.unparser = Unparser:new({\n\t\tluaVersion = luaVersion;\n\t});\n\tself.conventions = conventions;\nend\n\nfunction Pipeline:getLuaVersion()\n\treturn self.luaVersion;\nend\n\nfunction Pipeline:setNameGenerator(nameGenerator)\n\tif(type(nameGenerator) == \"string\") then\n\t\tnameGenerator = Pipeline.NameGenerators[nameGenerator];\n\tend\n\n\tif(type(nameGenerator) == \"function\" or type(nameGenerator) == \"table\") then\n\t\tself.namegenerator = nameGenerator;\n\t\treturn;\n\telse\n\t\tlogger:error(\"The Argument to Pipeline:setNameGenerator must be a valid NameGenerator function or function name e.g: \\\"mangled\\\"\")\n\tend\nend\n\nfunction Pipeline:apply(code, filename)\n\tlocal startTime = gettime();\n\tfilename = filename or \"Anonymous Script\";\n\tlogger:info(string.format(\"Applying Obfuscation Pipeline to %s ...\", filename));\n\n\t-- Seed the Random Generator\n\tif(self.Seed > 0) then\n\t\tmath.randomseed(self.Seed);\n\telse\n\t\t--> use secure random number generator\n\t\tlocal success, seed = pcall(function()\n\t\t\tlocal seedStr =  io.popen(\"openssl rand -hex 12\"):read(\"*a\"):gsub(\"\\n\", \"\")..\"\"\n\t\t\tlocal seedNum = 0;\n\n\t\t\t--> NOTE: tonumber caps at 1.844674407371e+19. So we use this instead.\n\t\t\tfor i = 1, #seedStr do\n\t\t\t\tlocal char = seedStr:sub(i, i):lower()\n\t\t\t\tlocal digit = char:match(\"%d\") and (char:byte() - 48) or (char:byte() - 87)\n\t\t\t\tseedNum = seedNum * 16 + digit\n\t\t\tend\n\n\t\t\t--> Random Number Generator in Lua 5.1 is limited to 9.007199254741e+15.\n\t\t\tif _VERSION == \"Lua 5.1\" and not jit then\n\t\t\t\tseedNum = seedNum % 9.007199254741e+15\n\t\t\tend\n\n\t\t\treturn seedNum\n\t\tend)\n\n\t\tif success then\n\t\t\tmath.randomseed(seed)\n\t\telse\n\t\t\tlogger:warn(\"OpenSSL is unavailable. Falling back to unix time.\");\n\t\t\tmath.randomseed(os.time())\n\t\tend\n\tend\n\n\tlogger:info(\"Parsing ...\");\n\tlocal parserStartTime = gettime();\n\n\tlocal sourceLen = string.len(code);\n\tlocal ast = self.parser:parse(code);\n\n\tlocal parserTimeDiff = gettime() - parserStartTime;\n\tlogger:info(string.format(\"Parsing Done in %.2f seconds\", parserTimeDiff));\n\n\t-- User Defined Steps\n\tfor i, step in ipairs(self.steps) do\n\t\tlocal stepStartTime = gettime();\n\t\tlogger:info(string.format(\"Applying Step \\\"%s\\\" ...\", step.Name or \"Unnamed\"));\n\t\tlocal newAst = step:apply(ast, self);\n\t\tif type(newAst) == \"table\" then\n\t\t\tast = newAst;\n\t\tend\n\t\tlogger:info(string.format(\"Step \\\"%s\\\" Done in %.2f seconds\", step.Name or \"Unnamed\", gettime() - stepStartTime));\n\tend\n\n\t-- Rename Variables Step\n\tself:renameVariables(ast);\n\n\tcode = self:unparse(ast);\n\n\tlocal timeDiff = gettime() - startTime;\n\tlogger:info(string.format(\"Obfuscation Done in %.2f seconds\", timeDiff));\n\n\tlogger:info(string.format(\"Generated Code size is %.2f%% of the Source Code size\", (string.len(code) / sourceLen)*100))\n\n\treturn code;\nend\n\nfunction Pipeline:unparse(ast)\n\tlocal startTime = gettime();\n\tlogger:info(\"Generating Code ...\");\n\n\tlocal unparsed = self.unparser:unparse(ast);\n\n\tlocal timeDiff = gettime() - startTime;\n\tlogger:info(string.format(\"Code Generation Done in %.2f seconds\", timeDiff));\n\n\treturn unparsed;\nend\n\nfunction Pipeline:renameVariables(ast)\n\tlocal startTime = gettime();\n\tlogger:info(\"Renaming Variables ...\");\n\n\n\tlocal generatorFunction = self.namegenerator or Pipeline.NameGenerators.mangled;\n\tif(type(generatorFunction) == \"table\") then\n\t\tif (type(generatorFunction.prepare) == \"function\") then\n\t\t\tgeneratorFunction.prepare(ast);\n\t\tend\n\t\tgeneratorFunction = generatorFunction.generateName;\n\tend\n\n\tif not self.unparser:isValidIdentifier(self.VarNamePrefix) and #self.VarNamePrefix ~= 0 then\n\t\tlogger:error(string.format(\"The Prefix \\\"%s\\\" is not a valid Identifier in %s\", self.VarNamePrefix, self.LuaVersion));\n\tend\n\n\tlocal globalScope = ast.globalScope;\n\tglobalScope:renameVariables({\n\t\tKeywords = self.conventions.Keywords;\n\t\tgenerateName = generatorFunction;\n\t\tprefix = self.VarNamePrefix;\n\t});\n\n\tlocal timeDiff = gettime() - startTime;\n\tlogger:info(string.format(\"Renaming Done in %.2f seconds\", timeDiff));\nend\n\nreturn Pipeline;\n"
  },
  {
    "path": "src/prometheus/randomLiterals.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- randomLiterals.lua\n--\n-- This Script provides a library for creating random literals\n\nlocal Ast = require(\"prometheus.ast\");\nlocal RandomStrings = require(\"prometheus.randomStrings\");\n\nlocal RandomLiterals = {};\n\nlocal function callNameGenerator(generatorFunction, ...)\n\tif(type(generatorFunction) == \"table\") then\n\t\tgeneratorFunction = generatorFunction.generateName;\n\tend\n\treturn generatorFunction(...);\nend\n\nfunction RandomLiterals.String(pipeline)\n    return Ast.StringExpression(callNameGenerator(pipeline.namegenerator, math.random(1, 4096)));\nend\n\nfunction RandomLiterals.Dictionary()\n    return RandomStrings.randomStringNode(true);\nend\n\nfunction RandomLiterals.Number()\n    return Ast.NumberExpression(math.random(-8388608, 8388607));\nend\n\nfunction RandomLiterals.Any(pipeline)\n    local type = math.random(1, 3);\n    if type == 1 then\n        return RandomLiterals.String(pipeline);\n    elseif type == 2 then\n        return RandomLiterals.Number();\n    elseif type == 3 then\n        return RandomLiterals.Dictionary();\n    end\nend\n\n\nreturn RandomLiterals;"
  },
  {
    "path": "src/prometheus/randomStrings.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- randomStrings.lua\n--\n-- This Script provides a library for generating random strings\n\nlocal Ast = require(\"prometheus.ast\")\nlocal utils = require(\"prometheus.util\")\nlocal charset = utils.chararray(\"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890\")\n\nlocal function randomString(wordsOrLen)\n\tif type(wordsOrLen) == \"table\" then\n\t\treturn wordsOrLen[math.random(1, #wordsOrLen)];\n\tend\n\n\twordsOrLen = wordsOrLen or math.random(2, 15);\n\tif wordsOrLen > 0 then\n\t\treturn randomString(wordsOrLen - 1) .. charset[math.random(1, #charset)]\n\telse\n\t\treturn \"\"\n\tend\nend\n\nlocal function randomStringNode(wordsOrLen)\n\treturn Ast.StringExpression(randomString(wordsOrLen))\nend\n\nreturn {\n\trandomString = randomString,\n\trandomStringNode = randomStringNode,\n}\n"
  },
  {
    "path": "src/prometheus/scope.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- scope.lua\n--\n-- This Script provides a class for the Scope of a Lua Script\n\nlocal logger = require(\"logger\");\nlocal config = require(\"config\");\n\nlocal Scope = {};\n\nlocal scopeI = 0;\nlocal function nextName()\n\tscopeI = scopeI + 1;\n\treturn \"local_scope_\" .. tostring(scopeI);\nend\n\nlocal function generateWarning(token, message)\n\treturn \"Warning at Position \" .. tostring(token.line) .. \":\" .. tostring(token.linePos) .. \", \" .. message;\nend\n\n-- Create a new Local Scope\nfunction Scope:new(parentScope, name)\n\tlocal scope = {\n\t\tisGlobal = false,\n\t\tparentScope = parentScope,\n\t\tvariables = {},\n\t\treferenceCounts = {};\n\t\tvariablesLookup = {},\n\t\tvariablesFromHigherScopes = {},\n\t\tskipIdLookup = {};\n\t\tname = name or nextName(),\n\t\tchildren = {},\n\t\tlevel = parentScope.level and (parentScope.level + 1) or 1;\n\t}\n\n\tsetmetatable(scope, self);\n\tself.__index = self;\n\tparentScope:addChild(scope);\n\treturn scope;\nend\n\n-- Create a new Global Scope\nfunction Scope:newGlobal()\n\tlocal scope = {\n\t\tisGlobal = true,\n\t\tparentScope = nil,\n\t\tvariables = {},\n\t\tvariablesLookup = {};\n\t\treferenceCounts = {};\n\t\tskipIdLookup = {};\n\t\tname = \"global_scope\",\n\t\tchildren = {},\n\t\tlevel = 0,\n\t};\n\n\tsetmetatable(scope, self);\n\tself.__index = self;\n\n\treturn scope;\nend\n\n-- Returns the Parent Scope\nfunction Scope:getParent(parentScope)\n\treturn self.parentScope;\nend\n\nfunction Scope:setParent(parentScope)\n\tself.parentScope:removeChild(self);\n\tparentScope:addChild(self);\n\tself.parentScope = parentScope;\n\tself.level = parentScope.level + 1;\nend\n\nlocal next_name_i = 1;\n-- Adds a Variable to the scope and returns the variable id, if no name is passed then a name is generated\nfunction Scope:addVariable(name, token)\n\tif (not name) then\n\t\tname = string.format(\"%s%i\", config.IdentPrefix, next_name_i);\n\t\tnext_name_i = next_name_i + 1;\n\tend\n\n\tif self.variablesLookup[name] ~= nil then\n\t\tif(token) then\n\t\t\tlogger:warn(generateWarning(token, \"the variable \\\"\" .. name .. \"\\\" is already defined in that scope\"));\n\t\telse\n\t\t\tlogger:error(string.format(\"A variable with the name \\\"%s\\\" was already defined, you should have no variables starting with \\\"%s\\\"\", name, config.IdentPrefix));\n\t\tend\n\n\t\t--return self.variablesLookup[name];\n\tend\n\n\ttable.insert(self.variables, name);\n\tlocal id = #self.variables;\n\tself.variablesLookup[name] = id;\n\treturn id;\nend\n\nfunction Scope:enableVariable(id)\n\tlocal name = self.variables[id];\n\tself.variablesLookup[name] = id;\nend\n\nfunction Scope:addDisabledVariable(name, token)\n\tif (not name) then\n\t\tname = string.format(\"%s%i\", config.IdentPrefix, next_name_i);\n\t\tnext_name_i = next_name_i + 1;\n\tend\n\n\tif self.variablesLookup[name] ~= nil then\n\t\tif(token) then\n\t\t\tlogger:warn(generateWarning(token, \"the variable \\\"\" .. name .. \"\\\" is already defined in that scope\"));\n\t\telse\n\t\t\tlogger:warn(string.format(\"a variable with the name \\\"%s\\\" was already defined\", name));\n\t\tend\n\n\t\t--return self.variablesLookup[name];\n\tend\n\n\ttable.insert(self.variables, name);\n\tlocal id = #self.variables;\n\treturn id;\nend\n\nfunction Scope:addIfNotExists(id)\n\tif(not self.variables[id]) then\n\t\tlocal name = string.format(\"%s%i\", config.IdentPrefix, next_name_i);\n\t\tnext_name_i = next_name_i + 1;\n\t\tself.variables[id] = name;\n\t\tself.variablesLookup[name] = id;\n\tend\n\treturn id;\nend\n\n-- Returns wether the variable is defined in this Scope\nfunction Scope:hasVariable(name)\n\tif(self.isGlobal) then\n\t\tif self.variablesLookup[name] == nil then\n\t\t\tself:addVariable(name);\n\t\tend\n\t\treturn true;\n\tend\n\treturn self.variablesLookup[name] ~= nil;\nend\n\n-- Get List of all Variables defined in this Scope\nfunction Scope:getVariables()\n\treturn self.variables;\nend\n\nfunction Scope:resetReferences(id)\n\tself.referenceCounts[id] = 0;\nend\n\nfunction Scope:getReferences(id)\n\treturn self.referenceCounts[id] or 0;\nend\n\nfunction Scope:removeReference(id)\n\tself.referenceCounts[id] = (self.referenceCounts[id] or 0) - 1;\nend\n\nfunction Scope:addReference(id)\n\tself.referenceCounts[id] = (self.referenceCounts[id] or 0) + 1;\nend\n\n-- Resolve the scope of a variable by name\nfunction Scope:resolve(name)\n\tif(self:hasVariable(name)) then\n\t\treturn self, self.variablesLookup[name];\n\tend\n\tassert(self.parentScope, \"No Global Variable Scope was Created! This should not be Possible!\");\n\tlocal scope, id = self.parentScope:resolve(name);\n\tself:addReferenceToHigherScope(scope, id, nil, true);\n\treturn scope, id;\nend\n\nfunction Scope:resolveGlobal(name)\n\tif(self.isGlobal and self:hasVariable(name)) then\n\t\treturn self, self.variablesLookup[name];\n\tend\n\tassert(self.parentScope, \"No Global Variable Scope was Created! This should not be Possible!\");\n\tlocal scope, id = self.parentScope:resolveGlobal(name);\n\tself:addReferenceToHigherScope(scope, id, nil, true);\n\treturn scope, id;\nend\n\n-- Returns the name of an Variable by id - this is used for unparsing\nfunction Scope:getVariableName(id)\n\treturn self.variables[id];\nend\n\n-- Remove A Variable from this Scope\nfunction Scope:removeVariable(id)\n\tlocal name = self.variables[id];\n\tself.variables[id] = nil;\n\tself.variablesLookup[name] = nil;\n\tself.skipIdLookup[id] = true;\nend\n\n-- Add a Children Scope\nfunction Scope:addChild(scope)\n\t-- This will add all References from that Scope to higher Scopes. Note that the higher scopes may only be global\n\tfor scope, ids in pairs(scope.variablesFromHigherScopes) do\n\t\tfor id, count in pairs(ids) do\n\t\t\tif count and count > 0 then\n\t\t\t\tself:addReferenceToHigherScope(scope, id, count);\n\t\t\tend\n\t\tend\n\tend\n\ttable.insert(self.children, scope);\nend\n\nfunction Scope:clearReferences()\n\tself.referenceCounts = {};\n\tself.variablesFromHigherScopes = {};\nend\n\nfunction Scope:removeChild(child)\n\tfor i, v in ipairs(self.children) do\n\t\tif(v == child) then\n\t\t\t-- This will add all References from that Scope to higher Scopes. Note that the higher scopes may only be global\n\t\t\tfor scope, ids in pairs(v.variablesFromHigherScopes) do\n\t\t\t\tfor id, count in pairs(ids) do\n\t\t\t\t\tif count and count > 0 then\n\t\t\t\t\t\tself:removeReferenceToHigherScope(scope, id, count);\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\treturn table.remove(self.children, i);\n\t\tend\n\tend\nend\n\nfunction Scope:getMaxId()\n\treturn #self.variables;\nend\n\nfunction Scope:addReferenceToHigherScope(scope, id, n, b)\n\tn = n or 1;\n\tif self.isGlobal then\n\t\tif not scope.isGlobal then\n\t\t\tlogger:error(string.format(\"Could not resolve Scope \\\"%s\\\"\", scope.name))\n\t\tend\n\t\treturn\n\tend\n\tif scope == self then\n\t\tself.referenceCounts[id] = (self.referenceCounts[id] or 0) + n;\n\t\treturn\n\tend\n\tif not self.variablesFromHigherScopes[scope] then\n\t\tself.variablesFromHigherScopes[scope] = {};\n\tend\n\tlocal scopeReferences = self.variablesFromHigherScopes[scope];\n\tif scopeReferences[id] then\n\t\tscopeReferences[id] = scopeReferences[id] + n;\n\telse\n\t\tscopeReferences[id] = n;\n\tend\n\tif not b then\n\t\tself.parentScope:addReferenceToHigherScope(scope, id, n);\n\tend\nend\n\nfunction Scope:removeReferenceToHigherScope(scope, id, n, b)\n\tn = n or 1;\n\tif self.isGlobal then\n\t\treturn\n\tend\n\tif scope == self then\n\t\tself.referenceCounts[id] = (self.referenceCounts[id] or 0) - n;\n\t\treturn\n\tend\n\tif not self.variablesFromHigherScopes[scope] then\n\t\tself.variablesFromHigherScopes[scope] = {};\n\tend\n\tlocal scopeReferences = self.variablesFromHigherScopes[scope];\n\tif scopeReferences[id] then\n\t\tscopeReferences[id] = scopeReferences[id] - n;\n\telse\n\t\tscopeReferences[id] = 0;\n\tend\n\tif not b then\n\t\tself.parentScope:removeReferenceToHigherScope(scope, id, n);\n\tend\nend\n\n-- Rename Variables from that scope downwards\n-- this function needs a settings object with the following properties\n-- Keywords => forbidden Variable Names\n-- generateName(id, scope, originalName) => function to generate unique variable name based on the id and scope\nfunction Scope:renameVariables(settings)\n\tif(not self.isGlobal) then\n\t\tlocal prefix = settings.prefix or \"\";\n\t\tlocal forbiddenNamesLookup = {};\n\t\tfor _, keyword in pairs(settings.Keywords) do\n\t\t\tforbiddenNamesLookup[keyword] = true;\n\t\tend\n\n\t\tfor scope, ids in pairs(self.variablesFromHigherScopes) do\n\t\t\tfor id, count in pairs(ids) do\n\t\t\t\tif count and count > 0 then\n\t\t\t\t\tlocal name = scope:getVariableName(id);\n\t\t\t\t\tforbiddenNamesLookup[name] = true;\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tself.variablesLookup = {};\n\n\t\tlocal i = 0;\n\t\tfor id, originalName in pairs(self.variables) do\n\t\t\tif(not self.skipIdLookup[id] and (self.referenceCounts[id] or 0) >= 0) then\n\t\t\t\tlocal name;\n\t\t\t\trepeat\n\t\t\t\t\tname = prefix .. settings.generateName(i, self, originalName);\n\t\t\t\t\tif name == nil then\n\t\t\t\t\t\tname = originalName;\n\t\t\t\t\tend\n\t\t\t\t\ti = i + 1;\n\t\t\t\tuntil not forbiddenNamesLookup[name];\n\n\t\t\t\tself.variables[id] = name;\n\t\t\t\tself.variablesLookup[name] = id;\n\t\t\tend\n\t\tend\n\tend\n\n\tfor _, scope in pairs(self.children) do\n\t\tscope:renameVariables(settings);\n\tend\nend\n\nreturn Scope;"
  },
  {
    "path": "src/prometheus/step.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- step.lua\n--\n-- This Script provides the base class for Obfuscation Steps\n\nlocal logger = require(\"logger\");\nlocal util = require(\"prometheus.util\");\n\nlocal lookupify = util.lookupify;\n\nlocal Step = {};\n\nStep.SettingsDescriptor = {}\n\nfunction Step:new(settings)\n\tlocal instance = {};\n\tsetmetatable(instance, self);\n\tself.__index = self;\n\n\tif type(settings) ~= \"table\" then\n\t\tsettings = {};\n\tend\n\n\tfor key, data in pairs(self.SettingsDescriptor) do\n\t\tif settings[key] == nil then\n\t\t\tif data.default == nil then\n\t\t\t\tlogger:error(string.format(\"The Setting \\\"%s\\\" was not provided for the Step \\\"%s\\\"\", key, self.Name));\n\t\t\tend\n\t\t\tinstance[key] = data.default;\n\t\telseif(data.type == \"enum\") then\n\t\t\tlocal lookup = lookupify(data.values);\n\t\t\tif not lookup[settings[key]] then\n\t\t\t\tlogger:error(string.format(\"Invalid value for the Setting \\\"%s\\\" of the Step \\\"%s\\\". It must be one of the following: %s\", key, self.Name, table.concat(data, \", \")));\n\t\t\tend\n\t\t\tinstance[key] = settings[key];\n\t\telseif(type(settings[key]) ~= data.type) then\n\t\t\tlogger:error(string.format(\"Invalid value for the Setting \\\"%s\\\" of the Step \\\"%s\\\". It must be a %s\", key, self.Name, data.type));\n\t\telse\n\t\t\tif data.min then\n\t\t\t\tif  settings[key] < data.min then\n\t\t\t\t\tlogger:error(string.format(\"Invalid value for the Setting \\\"%s\\\" of the Step \\\"%s\\\". It must be at least %d\", key, self.Name, data.min));\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif data.max then\n\t\t\t\tif  settings[key] > data.max then\n\t\t\t\t\tlogger:error(string.format(\"Invalid value for the Setting \\\"%s\\\" of the Step \\\"%s\\\". The biggest allowed value is %d\", key, self.Name, data.min));\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tinstance[key] = settings[key];\n\t\tend\n\tend\n\n\tinstance:init();\n\n\treturn instance;\nend\n\nfunction Step:init()\n\tlogger:error(\"Abstract Steps cannot be Created\");\nend\n\nfunction Step:extend()\n\tlocal ext = {};\n\tsetmetatable(ext, self);\n\tself.__index = self;\n\treturn ext;\nend\n\nfunction Step:apply(ast, pipeline)\n\tlogger:error(\"Abstract Steps cannot be Applied\")\nend\n\nStep.Name = \"Abstract Step\";\nStep.Description = \"Abstract Step\";\n\nreturn Step;\n"
  },
  {
    "path": "src/prometheus/steps/AddVararg.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- AddVararg.lua\n--\n-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal visitast = require(\"prometheus.visitast\");\nlocal AstKind = Ast.AstKind;\n\nlocal AddVararg = Step:extend();\nAddVararg.Description = \"This Step Adds Vararg to all Functions\";\nAddVararg.Name = \"Add Vararg\";\n\nAddVararg.SettingsDescriptor = {}\n\nfunction AddVararg:init(_) end\n\nfunction AddVararg:apply(ast)\n\tvisitast(ast, nil, function(node)\n        if node.kind == AstKind.FunctionDeclaration or node.kind == AstKind.LocalFunctionDeclaration or node.kind == AstKind.FunctionLiteralExpression then\n            if #node.args < 1 or node.args[#node.args].kind ~= AstKind.VarargExpression then\n                node.args[#node.args + 1] = Ast.VarargExpression();\n            end\n        end\n    end)\nend\n\nreturn AddVararg;"
  },
  {
    "path": "src/prometheus/steps/AntiTamper.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- AntiTamper.lua\n--\n-- This Script provides an Obfuscation Step, that breaks the script, when someone tries to tamper with it.\n\nlocal Step = require(\"prometheus.step\")\nlocal RandomStrings = require(\"prometheus.randomStrings\")\nlocal Parser = require(\"prometheus.parser\")\nlocal Enums = require(\"prometheus.enums\")\nlocal logger = require(\"logger\")\n\nlocal AntiTamper = Step:extend()\nAntiTamper.Description = \"This Step Breaks your Script when it is modified. This is only effective when using the new VM.\"\nAntiTamper.Name = \"Anti Tamper\"\n\nAntiTamper.SettingsDescriptor = {\n\tUseDebug = {\n\t\ttype = \"boolean\",\n\t\tdefault = true,\n\t\tdescription = \"Use debug library. (Recommended, however scripts will not work without debug library.)\",\n\t},\n}\n\nlocal function generateSanityCheck()\n\tlocal sanityCheckAnswers = {}\n\tlocal sanityPasses = math.random(1, 10)\n\tfor i = 1, sanityPasses do\n\t\tsanityCheckAnswers[i] = (math.random(1, 2 ^ 24) % 2 == 1)\n\tend\n\tlocal primaryCheck = RandomStrings.randomString()\n\tlocal codeParts = {}\n\tlocal function addCode(fmt, ...)\n\t\ttable.insert(codeParts, string.format(fmt, ...))\n\tend\n\n\tlocal function generateAssignment(idx)\n\t\tlocal index = math.min(idx, sanityPasses)\n\t\taddCode(\"            valid = %s;\\n\", tostring(sanityCheckAnswers[index]))\n\tend\n\tlocal function generateValidation(idx)\n\t\tlocal index = math.min(idx - 1, sanityPasses)\n\t\taddCode(\"            if valid == %s then\\n\", tostring(sanityCheckAnswers[index]))\n\t\taddCode(\"            else\\n\")\n\t\taddCode(\"                while true do end\\n\")\n\t\taddCode(\"            end\\n\")\n\tend\n\n\taddCode(\"do local valid = '%s';\", primaryCheck)\n\taddCode(\"for i = 0, %d do\\n\", sanityPasses)\n\tfor i = 0, sanityPasses do\n\t\tif i == 0 then\n\t\t\taddCode(\"        if i == 0 then\\n\")\n\t\t\taddCode(\"            if valid ~= '%s' then\\n\", primaryCheck)\n\t\t\taddCode(\"                while true do end\\n\")\n\t\t\taddCode(\"            end\\n\")\n\t\t\taddCode(\"            valid = %s;\\n\", tostring(sanityCheckAnswers[1]))\n\t\telseif i == 1 then\n\t\t\taddCode(\"        elseif i == 1 then\\n\")\n\t\t\taddCode(\"            if valid == %s then\\n\", tostring(sanityCheckAnswers[1]))\n\t\t\taddCode(\"            end\\n\")\n\t\telse\n\t\t\taddCode(\"        elseif i == %d then\\n\", i)\n\n\t\t\t--[[\n                Basically, even iterations are used to assign a new sanity check value,\n                and odd iterations are used to validate the previous sanity check value.\n            ]]\n\t\t\tif i % 2 == 0 then\n\t\t\t\tgenerateAssignment(i)\n\t\t\telse\n\t\t\t\tgenerateValidation(i)\n\t\t\tend\n\t\tend\n\tend\n\taddCode(\"        end\\n\")\n\taddCode(\"    end\\n\")\n\taddCode(\"do valid = true end\\n\")\n\treturn table.concat(codeParts)\nend\n\nfunction AntiTamper:init(settings) end\n\nfunction AntiTamper:apply(ast, pipeline)\n\tif pipeline.PrettyPrint then\n\t\tlogger:warn(string.format('\"%s\" cannot be used with PrettyPrint, ignoring \"%s\"', self.Name, self.Name))\n\t\treturn ast\n\tend\n\tlocal code = generateSanityCheck()\n\tif self.UseDebug then\n\t\tlocal string = RandomStrings.randomString()\n\t\tcode = code\n\t\t\t.. [[\n            -- Anti Beautify\n\t\t\tlocal sethook = debug and debug.sethook or function() end;\n\t\t\tlocal allowedLine = nil;\n\t\t\tlocal called = 0;\n\t\t\tsethook(function(s, line)\n\t\t\t\tif not line then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\t\tcalled = called + 1;\n\t\t\t\tif allowedLine then\n\t\t\t\t\tif allowedLine ~= line then\n\t\t\t\t\t\tsethook(error, \"l\", 5);\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tallowedLine = line;\n\t\t\t\tend\n\t\t\tend, \"l\", 5);\n\t\t\t(function() end)();\n\t\t\t(function() end)();\n\t\t\tsethook();\n\t\t\tif called < 2 then\n\t\t\t\tvalid = false;\n\t\t\tend\n            if called < 2 then\n                valid = false;\n            end\n\n            -- Anti Function Hook\n            local funcs = {pcall, string.char, debug.getinfo, string.dump}\n            for i = 1, #funcs do\n                if debug.getinfo(funcs[i]).what ~= \"C\" then\n                    valid = false;\n                end\n\n                if debug.getupvalue(funcs[i], 1) then\n                    valid = false;\n                end\n\n                if pcall(string.dump, funcs[i]) then\n                    valid = false;\n                end\n            end\n\n            -- Anti Beautify\n            local function getTraceback()\n                local str = (function(arg)\n                    return debug.traceback(arg)\n                end)(\"]] .. string .. [[\");\n                return str;\n            end\n\n            local traceback = getTraceback();\n            valid = valid and traceback:sub(1, traceback:find(\"\\n\") - 1) == \"]] .. string .. [[\";\n            local iter = traceback:gmatch(\":(%d*):\");\n            local v, c = iter(), 1;\n            for i in iter do\n                valid = valid and i == v;\n                c = c + 1;\n            end\n            valid = valid and c >= 2;\n        ]]\n    end\n    code = code .. [[\n    local gmatch = string.gmatch;\n    local err = function() error(\"Tamper Detected!\") end;\n\n    local pcallIntact2 = false;\n    local pcallIntact = pcall(function()\n        pcallIntact2 = true;\n    end) and pcallIntact2;\n\n    local random = math.random;\n    local tblconcat = table.concat;\n    local unpkg = table and table.unpack or unpack;\n    local n = random(3, 65);\n    local acc1 = 0;\n    local acc2 = 0;\n    local pcallRet = {pcall(function() local a = ]] .. tostring(math.random(1, 2^24)) .. [[ - \"]] .. RandomStrings.randomString() .. [[\" ^ ]] .. tostring(math.random(1, 2^24)) .. [[ return \"]] .. RandomStrings.randomString() .. [[\" / a; end)};\n    local origMsg = pcallRet[2];\n    local line = tonumber(gmatch(tostring(origMsg), ':(%d*):')());\n    for i = 1, n do\n        local len = math.random(1, 100);\n        local n2 = random(0, 255);\n        local pos = random(1, len);\n        local shouldErr = random(1, 2) == 1;\n        local msg = origMsg:gsub(':(%d*):', ':' .. tostring(random(0, 10000)) .. ':');\n        local arr = {pcall(function()\n            if random(1, 2) == 1 or i == n then\n                local line2 = tonumber(gmatch(tostring(({pcall(function() local a = ]] .. tostring(math.random(1, 2^24)) .. [[ - \"]] .. RandomStrings.randomString() .. [[\" ^ ]] .. tostring(math.random(1, 2^24)) .. [[ return \"]] .. RandomStrings.randomString() .. [[\" / a; end)})[2]), ':(%d*):')());\n                valid = valid and line == line2;\n            end\n            if shouldErr then\n                error(msg, 0);\n            end\n            local arr = {};\n            for i = 1, len do\n                arr[i] = random(0, 255);\n            end\n            arr[pos] = n2;\n            return unpkg(arr);\n        end)};\n        if shouldErr then\n            valid = valid and arr[1] == false and arr[2] == msg;\n        else\n            valid = valid and arr[1];\n            acc1 = (acc1 + arr[pos + 1]) % 256;\n            acc2 = (acc2 + n2) % 256;\n        end\n    end\n    valid = valid and acc1 == acc2;\n\n    if valid then else\n        repeat\n            return (function()\n                while true do\n                    l1, l2 = l2, l1;\n                    err();\n                end\n            end)();\n        until true;\n        while true do\n            l2 = random(1, 6);\n            if l2 > 2 then\n                l2 = tostring(l1);\n            else\n                l1 = l2;\n            end\n        end\n        return;\n    end\nend\n\n    -- Anti Function Arg Hook\n    local obj = setmetatable({}, {\n        __tostring = err,\n    });\n    obj[math.random(1, 100)] = obj;\n    (function() end)(obj);\n\n    repeat until valid;\n    ]]\n\n    local parsed = Parser:new({LuaVersion = Enums.LuaVersion.Lua51}):parse(code);\n    local doStat = parsed.body.statements[1];\n    doStat.body.scope:setParent(ast.body.scope);\n    table.insert(ast.body.statements, 1, doStat);\n\n    return ast;\nend\n\nreturn AntiTamper;\n"
  },
  {
    "path": "src/prometheus/steps/ConstantArray.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- ConstantArray.lua\n--\n-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function\n\n-- TODO: Wrapper Functions\n-- TODO: Proxy Object for indexing: e.g: ARR[X] becomes ARR + X\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal visitast = require(\"prometheus.visitast\");\nlocal util = require(\"prometheus.util\")\nlocal Parser = require(\"prometheus.parser\");\nlocal enums = require(\"prometheus.enums\")\n\nlocal LuaVersion = enums.LuaVersion;\nlocal AstKind = Ast.AstKind;\n\nlocal ConstantArray = Step:extend();\nConstantArray.Description = \"This Step will Extract all Constants and put them into an Array at the beginning of the script\";\nConstantArray.Name = \"Constant Array\";\n\nConstantArray.SettingsDescriptor = {\n\tTreshold = {\n\t\tname = \"Treshold\",\n\t\tdescription = \"The relative amount of nodes that will be affected\",\n\t\ttype = \"number\",\n\t\tdefault = 1,\n\t\tmin = 0,\n\t\tmax = 1,\n\t},\n\tStringsOnly = {\n\t\tname = \"StringsOnly\",\n\t\tdescription = \"Wether to only Extract Strings\",\n\t\ttype = \"boolean\",\n\t\tdefault = false,\n\t},\n\tShuffle = {\n\t\tname = \"Shuffle\",\n\t\tdescription = \"Wether to shuffle the order of Elements in the Array\",\n\t\ttype = \"boolean\",\n\t\tdefault = true,\n\t},\n\tRotate = {\n\t\tname = \"Rotate\",\n\t\tdescription = \"Wether to rotate the String Array by a specific (random) amount. This will be undone on runtime.\",\n\t\ttype = \"boolean\",\n\t\tdefault = true,\n\t},\n\tLocalWrapperTreshold = {\n\t\tname = \"LocalWrapperTreshold\",\n\t\tdescription = \"The relative amount of nodes functions, that will get local wrappers\",\n\t\ttype = \"number\",\n\t\tdefault = 1,\n\t\tmin = 0,\n\t\tmax = 1,\n\t},\n\tLocalWrapperCount = {\n\t\tname = \"LocalWrapperCount\",\n\t\tdescription = \"The number of Local wrapper Functions per scope. This only applies if LocalWrapperTreshold is greater than 0\",\n\t\ttype = \"number\",\n\t\tmin = 0,\n\t\tmax = 512,\n\t\tdefault = 0,\n\t},\n\tLocalWrapperArgCount = {\n\t\tname = \"LocalWrapperArgCount\",\n\t\tdescription = \"The number of Arguments to the Local wrapper Functions\",\n\t\ttype = \"number\",\n\t\tmin = 1,\n\t\tdefault = 10,\n\t\tmax = 200,\n\t};\n\tMaxWrapperOffset = {\n\t\tname = \"MaxWrapperOffset\",\n\t\tdescription = \"The Max Offset for the Wrapper Functions\",\n\t\ttype = \"number\",\n\t\tmin = 0,\n\t\tdefault = 65535,\n\t};\n\tEncoding = {\n\t\tname = \"Encoding\",\n\t\tdescription = \"The Encoding to use for the Strings\",\n\t\ttype = \"enum\",\n\t\tdefault = \"mixed\",\n\t\tvalues = {\n\t\t\t\"none\",\n\t\t\t\"base64\",\n\t\t\t\"base85\",\n\t\t\t\"mixed\",\n\t\t},\n\t}\n}\n\nlocal prefix_0, prefix_1;\nlocal function initPrefixes()\n\tlocal charset = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@£$%^&*()_+-=[]{}|:;<>,./?\";\n\trepeat\n\t\tlocal a, b = math.random(1, #charset), math.random(1, #charset);\n\t\tprefix_0 = charset:sub(a, a);\n\t\tprefix_1 = charset:sub(b, b);\n\tuntil prefix_0 ~= prefix_1\nend\n\nlocal function callNameGenerator(generatorFunction, ...)\n\tif(type(generatorFunction) == \"table\") then\n\t\tgeneratorFunction = generatorFunction.generateName;\n\tend\n\treturn generatorFunction(...);\nend\n\nfunction ConstantArray:init(_) end\n\nfunction ConstantArray:createArray()\n\tlocal entries = {};\n\tfor i, v in ipairs(self.constants) do\n\t\tif type(v) == \"string\" then\n\t\t\tv = self:encode(v);\n\t\tend\n\t\tentries[i] = Ast.TableEntry(Ast.ConstantNode(v));\n\tend\n\treturn Ast.TableConstructorExpression(entries);\nend\n\nfunction ConstantArray:indexing(index, data)\n\tif self.LocalWrapperCount > 0 and data.functionData.local_wrappers then\n\t\tlocal wrappers = data.functionData.local_wrappers;\n\t\tlocal wrapper = wrappers[math.random(#wrappers)];\n\n\t\tlocal args = {};\n\t\tlocal ofs = index - self.wrapperOffset - wrapper.offset;\n\t\tfor i = 1, self.LocalWrapperArgCount, 1 do\n\t\t\tif i == wrapper.arg then\n\t\t\t\targs[i] = Ast.NumberExpression(ofs);\n\t\t\telse\n\t\t\t\targs[i] = Ast.NumberExpression(math.random(ofs - 1024, ofs + 1024));\n\t\t\tend\n\t\tend\n\n\t\tdata.scope:addReferenceToHigherScope(wrappers.scope, wrappers.id);\n\t\treturn Ast.FunctionCallExpression(Ast.IndexExpression(\n\t\t\tAst.VariableExpression(wrappers.scope, wrappers.id),\n\t\t\tAst.StringExpression(wrapper.index)\n\t\t), args);\n\telse\n\t\tdata.scope:addReferenceToHigherScope(self.rootScope, self.wrapperId);\n\t\treturn Ast.FunctionCallExpression(Ast.VariableExpression(self.rootScope, self.wrapperId), {\n\t\t\tAst.NumberExpression(index - self.wrapperOffset);\n\t\t});\n\tend\nend\n\nfunction ConstantArray:getConstant(value, data)\n\tif(self.lookup[value]) then\n\t\treturn self:indexing(self.lookup[value], data)\n\tend\n\tlocal idx = #self.constants + 1;\n\tself.constants[idx] = value;\n\tself.lookup[value] = idx;\n\treturn self:indexing(idx, data);\nend\n\nfunction ConstantArray:addConstant(value)\n\tif(self.lookup[value]) then\n\t\treturn\n\tend\n\tlocal idx = #self.constants + 1;\n\tself.constants[idx] = value;\n\tself.lookup[value] = idx;\nend\n\nlocal function reverse(t, i, j)\n\twhile i < j do\n\t  t[i], t[j] = t[j], t[i]\n\t  i, j = i+1, j-1\n\tend\nend\n\nlocal function rotate(t, d, n)\n\tn = n or #t\n\td = (d or 1) % n\n\treverse(t, 1, n)\n\treverse(t, 1, d)\n\treverse(t, d+1, n)\nend\n\nlocal rotateCode = [=[\n\tfor i, v in ipairs({{1, LEN}, {1, SHIFT}, {SHIFT + 1, LEN}}) do\n\t\twhile v[1] < v[2] do\n\t\t\tARR[v[1]], ARR[v[2]], v[1], v[2] = ARR[v[2]], ARR[v[1]], v[1] + 1, v[2] - 1\n\t\tend\n\tend\n]=];\n\nfunction ConstantArray:addRotateCode(ast, shift)\n\tlocal parser = Parser:new({\n\t\tLuaVersion = LuaVersion.Lua51;\n\t});\n\n\tlocal newAst = parser:parse(string.gsub(string.gsub(rotateCode, \"SHIFT\", tostring(shift)), \"LEN\", tostring(#self.constants)));\n\tlocal forStat = newAst.body.statements[1];\n\tforStat.body.scope:setParent(ast.body.scope);\n\tvisitast(newAst, nil, function(node, data)\n\t\tif(node.kind == AstKind.VariableExpression) then\n\t\t\tif(node.scope:getVariableName(node.id) == \"ARR\") then\n\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\tdata.scope:addReferenceToHigherScope(self.rootScope, self.arrId);\n\t\t\t\tnode.scope = self.rootScope;\n\t\t\t\tnode.id = self.arrId;\n\t\t\tend\n\t\tend\n\tend)\n\n\ttable.insert(ast.body.statements, 1, forStat);\nend\n\nfunction ConstantArray:addDecodeCode(ast)\n\tif self.Encoding == \"base64\" then\n\t\tlocal base64DecodeCode = [[\n\tdo ]] .. table.concat(util.shuffle{\n\t\t\"local lookup = LOOKUP_TABLE;\",\n\t\t\"local len = string.len;\",\n\t\t\"local sub = string.sub;\",\n\t\t\"local floor = math.floor;\",\n\t\t\"local strchar = string.char;\",\n\t\t\"local insert = table.insert;\",\n\t\t\"local concat = table.concat;\",\n\t\t\"local type = type;\",\n\t\t\"local arr = ARR;\",\n\t}) .. [[\n\t\tfor i = 1, #arr do\n\t\t\tlocal data = arr[i];\n\t\t\tif type(data) == \"string\" then\n\t\t\t\tlocal length = len(data)\n\t\t\t\tlocal parts = {}\n\t\t\t\tlocal index = 1\n\t\t\t\tlocal value = 0\n\t\t\t\tlocal count = 0\n\t\t\t\twhile index <= length do\n\t\t\t\t\tlocal char = sub(data, index, index)\n\t\t\t\t\tlocal code = lookup[char]\n\t\t\t\t\tif code then\n\t\t\t\t\t\tvalue = value + code * (64 ^ (3 - count))\n\t\t\t\t\t\tcount = count + 1\n\t\t\t\t\t\tif count == 4 then\n\t\t\t\t\t\t\tcount = 0\n\t\t\t\t\t\t\tlocal c1 = floor(value / 65536)\n\t\t\t\t\t\t\tlocal c2 = floor(value % 65536 / 256)\n\t\t\t\t\t\t\tlocal c3 = value % 256\n\t\t\t\t\t\t\tinsert(parts, strchar(c1, c2, c3))\n\t\t\t\t\t\t\tvalue = 0\n\t\t\t\t\t\tend\n\t\t\t\t\telseif char == \"=\" then\n\t\t\t\t\t\tinsert(parts, strchar(floor(value / 65536)));\n\t\t\t\t\t\tif index >= length or sub(data, index + 1, index + 1) ~= \"=\" then\n\t\t\t\t\t\t\tinsert(parts, strchar(floor(value % 65536 / 256)));\n\t\t\t\t\t\tend\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\t\tindex = index + 1\n\t\t\t\tend\n\t\t\t\tarr[i] = concat(parts)\n\t\t\tend\n\t\tend\n\tend\n]];\n\n\t\tlocal parser = Parser:new({\n\t\t\tLuaVersion = LuaVersion.Lua51;\n\t\t});\n\n\t\tlocal newAst = parser:parse(base64DecodeCode);\n\t\tlocal forStat = newAst.body.statements[1];\n\t\tforStat.body.scope:setParent(ast.body.scope);\n\n\t\tvisitast(newAst, nil, function(node, data)\n\t\t\tif(node.kind == AstKind.VariableExpression) then\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"ARR\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\tdata.scope:addReferenceToHigherScope(self.rootScope, self.arrId);\n\t\t\t\t\tnode.scope = self.rootScope;\n\t\t\t\t\tnode.id = self.arrId;\n\t\t\t\tend\n\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"LOOKUP_TABLE\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\treturn self:createBase64Lookup();\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\ttable.insert(ast.body.statements, 1, forStat);\n\telseif self.Encoding == \"base85\" then\n\t\tlocal base85DecodeCode = [[\n\tdo ]] .. table.concat(util.shuffle{\n\t\t\"local lookup = LOOKUP_TABLE;\",\n\t\t\"local len = string.len;\",\n\t\t\"local sub = string.sub;\",\n\t\t\"local floor = math.floor;\",\n\t\t\"local strchar = string.char;\",\n\t\t\"local insert = table.insert;\",\n\t\t\"local concat = table.concat;\",\n\t\t\"local type = type;\",\n\t\t\"local arr = ARR;\",\n\t}) .. [[\n\t\tfor i = 1, #arr do\n\t\t\tlocal data = arr[i];\n\t\t\tif type(data) == \"string\" then\n\t\t\t\tlocal length = len(data)\n\t\t\t\tlocal parts = {}\n\t\t\t\tlocal index = 1\n\t\t\t\twhile index <= length do\n\t\t\t\t\tlocal remain = length - index + 1\n\t\t\t\t\tlocal count = remain >= 5 and 5 or remain\n\t\t\t\t\tlocal value = 0\n\t\t\t\t\tlocal valid = count > 1\n\n\t\t\t\t\tfor j = 0, 4 do\n\t\t\t\t\t\tlocal code\n\t\t\t\t\t\tif j < count then\n\t\t\t\t\t\t\tlocal ch = sub(data, index + j, index + j)\n\t\t\t\t\t\t\tcode = lookup[ch]\n\t\t\t\t\t\t\tif not code then\n\t\t\t\t\t\t\t\tvalid = false\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcode = 84\n\t\t\t\t\t\tend\n\t\t\t\t\t\tvalue = value * 85 + code\n\t\t\t\t\tend\n\n\t\t\t\t\tif valid then\n\t\t\t\t\t\tlocal b1 = floor(value / 16777216) % 256\n\t\t\t\t\t\tlocal b2 = floor(value / 65536) % 256\n\t\t\t\t\t\tlocal b3 = floor(value / 256) % 256\n\t\t\t\t\t\tlocal b4 = value % 256\n\t\t\t\t\t\tif count == 5 then\n\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2, b3, b4))\n\t\t\t\t\t\telseif count == 4 then\n\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2, b3))\n\t\t\t\t\t\telseif count == 3 then\n\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2))\n\t\t\t\t\t\telseif count == 2 then\n\t\t\t\t\t\t\tinsert(parts, strchar(b1))\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tindex = index + count\n\t\t\t\tend\n\t\t\t\tarr[i] = concat(parts)\n\t\t\tend\n\t\tend\n\tend\n]];\n\n\t\tlocal parser = Parser:new({\n\t\t\tLuaVersion = LuaVersion.Lua51;\n\t\t});\n\n\t\tlocal newAst = parser:parse(base85DecodeCode);\n\t\tlocal forStat = newAst.body.statements[1];\n\t\tforStat.body.scope:setParent(ast.body.scope);\n\n\t\tvisitast(newAst, nil, function(node, data)\n\t\t\tif(node.kind == AstKind.VariableExpression) then\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"ARR\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\tdata.scope:addReferenceToHigherScope(self.rootScope, self.arrId);\n\t\t\t\t\tnode.scope = self.rootScope;\n\t\t\t\t\tnode.id = self.arrId;\n\t\t\t\tend\n\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"LOOKUP_TABLE\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\treturn self:createBase85Lookup();\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\ttable.insert(ast.body.statements, 1, forStat);\n\telseif self.Encoding == \"mixed\" then\n\t\tlocal mixedDecodeCode = [[\n\tdo ]] .. table.concat(util.shuffle{\n\t\t\"local lookup64 = LOOKUP_TABLE_64;\",\n\t\t\"local lookup85 = LOOKUP_TABLE_85;\",\n\t\t\"local len = string.len;\",\n\t\t\"local sub = string.sub;\",\n\t\t\"local floor = math.floor;\",\n\t\t\"local strchar = string.char;\",\n\t\t\"local insert = table.insert;\",\n\t\t\"local concat = table.concat;\",\n\t\t\"local type = type;\",\n\t\t\"local arr = ARR;\",\n\t}) .. [[\n\t\tfor i = 1, #arr do\n\t\t\tlocal data = arr[i];\n\t\t\tif type(data) == \"string\" then\n\t\t\t\tlocal first = sub(data, 1, 1)\n\t\t\t\tif first == \"]]..prefix_0..[[\" then\n\t\t\t\t\tdata = sub(data, 2)\n\t\t\t\t\tlocal length = len(data)\n\t\t\t\t\tlocal parts = {}\n\t\t\t\t\tlocal index = 1\n\t\t\t\t\tlocal value = 0\n\t\t\t\t\tlocal count = 0\n\t\t\t\t\twhile index <= length do\n\t\t\t\t\t\tlocal char = sub(data, index, index)\n\t\t\t\t\t\tlocal code = lookup64[char]\n\t\t\t\t\t\tif code then\n\t\t\t\t\t\t\tvalue = value + code * (64 ^ (3 - count))\n\t\t\t\t\t\t\tcount = count + 1\n\t\t\t\t\t\t\tif count == 4 then\n\t\t\t\t\t\t\t\tcount = 0\n\t\t\t\t\t\t\t\tlocal c1 = floor(value / 65536)\n\t\t\t\t\t\t\t\tlocal c2 = floor(value % 65536 / 256)\n\t\t\t\t\t\t\t\tlocal c3 = value % 256\n\t\t\t\t\t\t\t\tinsert(parts, strchar(c1, c2, c3))\n\t\t\t\t\t\t\t\tvalue = 0\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif char == \"=\" then\n\t\t\t\t\t\t\tinsert(parts, strchar(floor(value / 65536)));\n\t\t\t\t\t\t\tif index >= length or sub(data, index + 1, index + 1) ~= \"=\" then\n\t\t\t\t\t\t\t\tinsert(parts, strchar(floor(value % 65536 / 256)));\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\t\tindex = index + 1\n\t\t\t\t\tend\n\t\t\t\t\tarr[i] = concat(parts)\n\t\t\t\telseif first == \"]]..prefix_1..[[\" then\n\t\t\t\t\tdata = sub(data, 2)\n\t\t\t\t\tlocal length = len(data)\n\t\t\t\t\tlocal parts = {}\n\t\t\t\t\tlocal idx = 1\n\t\t\t\t\twhile idx <= length do\n\t\t\t\t\t\tlocal remain = length - idx + 1\n\t\t\t\t\t\tlocal count = remain >= 5 and 5 or remain\n\t\t\t\t\t\tlocal value = 0\n\t\t\t\t\t\tlocal valid = count > 1\n\n\t\t\t\t\t\tfor j = 0, 4 do\n\t\t\t\t\t\t\tlocal code\n\t\t\t\t\t\t\tif j < count then\n\t\t\t\t\t\t\t\tlocal ch = sub(data, idx + j, idx + j)\n\t\t\t\t\t\t\t\tcode = lookup85[ch]\n\t\t\t\t\t\t\t\tif not code then\n\t\t\t\t\t\t\t\t\tvalid = false\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcode = 84\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tvalue = value * 85 + code\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif valid then\n\t\t\t\t\t\t\tlocal b1 = floor(value / 16777216) % 256\n\t\t\t\t\t\t\tlocal b2 = floor(value / 65536) % 256\n\t\t\t\t\t\t\tlocal b3 = floor(value / 256) % 256\n\t\t\t\t\t\t\tlocal b4 = value % 256\n\t\t\t\t\t\t\tif count == 5 then\n\t\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2, b3, b4))\n\t\t\t\t\t\t\telseif count == 4 then\n\t\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2, b3))\n\t\t\t\t\t\t\telseif count == 3 then\n\t\t\t\t\t\t\t\tinsert(parts, strchar(b1, b2))\n\t\t\t\t\t\t\telseif count == 2 then\n\t\t\t\t\t\t\t\tinsert(parts, strchar(b1))\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tidx = idx + count\n\t\t\t\t\tend\n\t\t\t\t\tarr[i] = concat(parts)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n]];\n\n\t\tlocal parser = Parser:new({\n\t\t\tLuaVersion = LuaVersion.Lua51;\n\t\t});\n\n\t\tlocal newAst = parser:parse(mixedDecodeCode);\n\t\tlocal forStat = newAst.body.statements[1];\n\t\tforStat.body.scope:setParent(ast.body.scope);\n\n\t\tvisitast(newAst, nil, function(node, data)\n\t\t\tif(node.kind == AstKind.VariableExpression) then\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"ARR\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\tdata.scope:addReferenceToHigherScope(self.rootScope, self.arrId);\n\t\t\t\t\tnode.scope = self.rootScope;\n\t\t\t\t\tnode.id = self.arrId;\n\t\t\t\tend\n\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"LOOKUP_TABLE_64\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\treturn self:createBase64Lookup();\n\t\t\t\tend\n\n\t\t\t\tif(node.scope:getVariableName(node.id) == \"LOOKUP_TABLE_85\") then\n\t\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\t\treturn self:createBase85Lookup();\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\ttable.insert(ast.body.statements, 1, forStat);\n\tend\nend\n\nfunction ConstantArray:createBase64Lookup()\n\tlocal entries = {};\n\tlocal i = 0;\n\tfor char in string.gmatch(self.base64chars, \".\") do\n\t\ttable.insert(entries, Ast.KeyedTableEntry(Ast.StringExpression(char), Ast.NumberExpression(i)));\n\t\ti = i + 1;\n\tend\n\tutil.shuffle(entries);\n\treturn Ast.TableConstructorExpression(entries);\nend\n\nfunction ConstantArray:createBase85Lookup()\n\tlocal entries = {};\n\tlocal i = 0;\n\tfor char in string.gmatch(self.base85chars, \".\") do\n\t\ttable.insert(entries, Ast.KeyedTableEntry(Ast.StringExpression(char), Ast.NumberExpression(i)));\n\t\ti = i + 1;\n\tend\n\tutil.shuffle(entries);\n\treturn Ast.TableConstructorExpression(entries);\nend\n\nfunction ConstantArray:encode(str)\n\tif self.Encoding == \"base64\" then\n\t\treturn ((str:gsub('.', function(x)\n\t\t\tlocal r,b='',x:byte()\n\t\t\tfor i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end\n\t\t\treturn r;\n\t\tend)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)\n\t\t\tif (#x < 6) then return '' end\n\t\t\tlocal c=0\n\t\t\tfor i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end\n\t\t\treturn self.base64chars:sub(c+1,c+1)\n\t\tend)..({ '', '==', '=' })[#str%3+1]);\n\telseif self.Encoding == \"base85\" then\n\t\tlocal result = {};\n\t\tlocal len = #str;\n\t\tlocal pos = 1;\n\n\t\twhile pos <= len do\n\t\t\tlocal rem = len - pos + 1;\n\t\t\tlocal count = rem >= 4 and 4 or rem;\n\t\t\tlocal b1, b2, b3, b4 = string.byte(str, pos, pos + count - 1);\n\t\t\tb1, b2, b3, b4 = b1 or 0, b2 or 0, b3 or 0, b4 or 0;\n\n\t\t\tlocal value = ((b1 * 256 + b2) * 256 + b3) * 256 + b4;\n\t\t\tlocal chars = {};\n\t\t\tfor i = 5, 1, -1 do\n\t\t\t\tlocal code = (value % 85) + 1;\n\t\t\t\tchars[i] = self.base85chars:sub(code, code);\n\t\t\t\tvalue = math.floor(value / 85);\n\t\t\tend\n\n\t\t\tresult[#result + 1] = table.concat(chars, \"\", 1, count + 1);\n\t\t\tpos = pos + count;\n\t\tend\n\n\t\treturn table.concat(result);\n\telseif self.Encoding == \"mixed\" then\n\t\tif math.random() < 0.5 then\n\t\t\tlocal encoded = ((str:gsub('.', function(x)\n\t\t\t\tlocal r,b='',x:byte()\n\t\t\t\tfor i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end\n\t\t\t\treturn r;\n\t\t\tend)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)\n\t\t\t\tif (#x < 6) then return '' end\n\t\t\t\tlocal c=0\n\t\t\t\tfor i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end\n\t\t\t\treturn self.base64chars:sub(c+1,c+1)\n\t\t\tend)..({ '', '==', '=' })[#str%3+1]);\n\t\t\treturn prefix_0 .. encoded;\n\t\telse\n\t\t\tlocal result = {};\n\t\t\tlocal len = #str;\n\t\t\tlocal pos = 1;\n\n\t\t\twhile pos <= len do\n\t\t\t\tlocal rem = len - pos + 1;\n\t\t\t\tlocal count = rem >= 4 and 4 or rem;\n\t\t\t\tlocal b1, b2, b3, b4 = string.byte(str, pos, pos + count - 1);\n\t\t\t\tb1 = b1 or 0;\n\t\t\t\tb2 = b2 or 0;\n\t\t\t\tb3 = b3 or 0;\n\t\t\t\tb4 = b4 or 0;\n\n\t\t\t\tlocal value = ((b1 * 256 + b2) * 256 + b3) * 256 + b4;\n\t\t\t\tlocal chars = {};\n\t\t\t\tfor i = 5, 1, -1 do\n\t\t\t\t\tlocal code = (value % 85) + 1;\n\t\t\t\t\tchars[i] = self.base85chars:sub(code, code);\n\t\t\t\t\tvalue = math.floor(value / 85);\n\t\t\t\tend\n\n\t\t\t\tresult[#result + 1] = table.concat(chars, \"\", 1, count + 1);\n\t\t\t\tpos = pos + count;\n\t\t\tend\n\n\t\t\treturn prefix_1 .. table.concat(result);\n\t\tend\n\tend\nend\n\nfunction ConstantArray:apply(ast, pipeline)\n\tinitPrefixes();\n\tself.rootScope = ast.body.scope;\n\tself.arrId = self.rootScope:addVariable();\n\n\tself.base64chars = table.concat(util.shuffle{\n\t\t\"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\",\n\t\t\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\",\n\t\t\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\",\n\t\t\"+\", \"/\",\n\t});\n\n\tself.base85chars = table.concat(util.shuffle{\n\t\t\"!\", \"\\\"\", \"#\", \"$\", \"%\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \"-\", \".\", \"/\",\n\t\t\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\",\n\t\t\":\", \";\", \"<\", \"=\", \">\", \"?\", \"@\",\n\t\t\"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\",\n\t\t\"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\",\n\t\t\"[\", \"\\\\\", \"]\", \"^\", \"_\", \"`\",\n\t\t\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\",\n\t\t\"p\", \"q\", \"r\", \"s\", \"t\", \"u\",\n\t});\n\n\tself.constants = {};\n\tself.lookup = {};\n\n\t-- Extract Constants\n\tvisitast(ast, nil, function(node, data)\n\t\t-- Apply only to some nodes\n\t\tif math.random() <= self.Treshold then\n\t\t\tnode.__apply_constant_array = true;\n\t\t\tif node.kind == AstKind.StringExpression then\n\t\t\t\tself:addConstant(node.value);\n\t\t\telseif not self.StringsOnly then\n\t\t\t\tif node.isConstant then\n\t\t\t\t\tif node.value ~= nil then\n\t\t\t\t\t\tself:addConstant(node.value);\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend);\n\n\t-- Shuffle Array\n\tif self.Shuffle then\n\t\tself.constants = util.shuffle(self.constants);\n\t\tself.lookup = {};\n\t\tfor i, v in ipairs(self.constants) do\n\t\t\tself.lookup[v] = i;\n\t\tend\n\tend\n\n\t-- Set Wrapper Function Offset\n\tself.wrapperOffset = math.random(-self.MaxWrapperOffset, self.MaxWrapperOffset);\n\tself.wrapperId = self.rootScope:addVariable();\n\n\tvisitast(ast, function(node, data)\n\t\t-- Add Local Wrapper Functions\n\t\tif self.LocalWrapperCount > 0 and node.kind == AstKind.Block and node.isFunctionBlock and math.random() <= self.LocalWrapperTreshold then\n\t\t\tlocal id = node.scope:addVariable()\n\t\t\tdata.functionData.local_wrappers = {\n\t\t\t\tid = id;\n\t\t\t\tscope = node.scope,\n\t\t\t};\n\t\t\tlocal nameLookup = {};\n\t\t\tfor i = 1, self.LocalWrapperCount, 1 do\n\t\t\t\tlocal name;\n\t\t\t\trepeat\n\t\t\t\t\tname = callNameGenerator(pipeline.namegenerator, math.random(1, self.LocalWrapperArgCount * 16));\n\t\t\t\tuntil not nameLookup[name];\n\t\t\t\tnameLookup[name] = true;\n\n\t\t\t\tlocal offset = math.random(-self.MaxWrapperOffset, self.MaxWrapperOffset);\n\t\t\t\tlocal argPos = math.random(1, self.LocalWrapperArgCount);\n\n\t\t\t\tdata.functionData.local_wrappers[i] = {\n\t\t\t\t\targ = argPos,\n\t\t\t\t\tindex = name,\n\t\t\t\t\toffset =  offset,\n\t\t\t\t};\n\t\t\t\tdata.functionData.__used = false;\n\t\t\tend\n\t\tend\n\t\tif node.__apply_constant_array then\n\t\t\tdata.functionData.__used = true;\n\t\tend\n\tend, function(node, data)\n\t\t-- Actually insert Statements to get the Constant Values\n\t\tif node.__apply_constant_array then\n\t\t\tif node.kind == AstKind.StringExpression then\n\t\t\t\treturn self:getConstant(node.value, data);\n\t\t\telseif not self.StringsOnly then\n\t\t\t\tif node.isConstant then\n\t\t\t\t\treturn node.value ~= nil and self:getConstant(node.value, data);\n\t\t\t\tend\n\t\t\tend\n\t\t\tnode.__apply_constant_array = nil;\n\t\tend\n\n\t\t-- Insert Local Wrapper Declarations\n\t\tif self.LocalWrapperCount > 0 and node.kind == AstKind.Block and node.isFunctionBlock and data.functionData.local_wrappers and data.functionData.__used then\n\t\t\tdata.functionData.__used = nil;\n\t\t\tlocal elems = {};\n\t\t\tlocal wrappers = data.functionData.local_wrappers;\n\t\t\tfor i = 1, self.LocalWrapperCount, 1 do\n\t\t\t\tlocal wrapper = wrappers[i];\n\t\t\t\tlocal argPos = wrapper.arg;\n\t\t\t\tlocal offset = wrapper.offset;\n\t\t\t\tlocal name = wrapper.index;\n\n\t\t\t\tlocal funcScope = Scope:new(node.scope);\n\n\t\t\t\tlocal arg = nil;\n\t\t\t\tlocal args = {};\n\n\t\t\t\tfor i = 1, self.LocalWrapperArgCount, 1 do\n\t\t\t\t\targs[i] = funcScope:addVariable();\n\t\t\t\t\tif i == argPos then\n\t\t\t\t\t\targ = args[i];\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal addSubArg;\n\n\t\t\t\t-- Create add and Subtract code\n\t\t\t\tif offset < 0 then\n\t\t\t\t\taddSubArg = Ast.SubExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(-offset));\n\t\t\t\telse\n\t\t\t\t\taddSubArg = Ast.AddExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(offset));\n\t\t\t\tend\n\n\t\t\t\tfuncScope:addReferenceToHigherScope(self.rootScope, self.wrapperId);\n\t\t\t\tlocal callArg = Ast.FunctionCallExpression(Ast.VariableExpression(self.rootScope, self.wrapperId), {\n\t\t\t\t\taddSubArg\n\t\t\t\t});\n\n\t\t\t\tlocal fargs = {};\n\t\t\t\tfor i, v in ipairs(args) do\n\t\t\t\t\tfargs[i] = Ast.VariableExpression(funcScope, v);\n\t\t\t\tend\n\n\t\t\t\telems[i] = Ast.KeyedTableEntry(\n\t\t\t\t\tAst.StringExpression(name),\n\t\t\t\t\tAst.FunctionLiteralExpression(fargs, Ast.Block({\n\t\t\t\t\t\tAst.ReturnStatement({\n\t\t\t\t\t\t\tcallArg\n\t\t\t\t\t\t});\n\t\t\t\t\t}, funcScope))\n\t\t\t\t)\n\t\t\tend\n\t\t\ttable.insert(node.statements, 1, Ast.LocalVariableDeclaration(node.scope, {\n\t\t\t\twrappers.id\n\t\t\t}, {\n\t\t\t\tAst.TableConstructorExpression(elems)\n\t\t\t}));\n\t\tend\n\tend);\n\n\tself:addDecodeCode(ast);\n\n\tlocal steps = util.shuffle({\n\t\t-- Add Wrapper Function Code\n\t\tfunction()\n\t\t\tlocal funcScope = Scope:new(self.rootScope);\n\t\t\t-- Add Reference to Array\n\t\t\tfuncScope:addReferenceToHigherScope(self.rootScope, self.arrId);\n\n\t\t\tlocal arg = funcScope:addVariable();\n\t\t\tlocal addSubArg;\n\n\t\t\t-- Create add and Subtract code\n\t\t\tif self.wrapperOffset < 0 then\n\t\t\t\taddSubArg = Ast.SubExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(-self.wrapperOffset));\n\t\t\telse\n\t\t\t\taddSubArg = Ast.AddExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(self.wrapperOffset));\n\t\t\tend\n\n\t\t\t-- Create and Add the Function Declaration\n\t\t\ttable.insert(ast.body.statements, 1, Ast.LocalFunctionDeclaration(self.rootScope, self.wrapperId, {\n\t\t\t\tAst.VariableExpression(funcScope, arg)\n\t\t\t}, Ast.Block({\n\t\t\t\tAst.ReturnStatement({\n\t\t\t\t\tAst.IndexExpression(\n\t\t\t\t\t\tAst.VariableExpression(self.rootScope, self.arrId),\n\t\t\t\t\t\taddSubArg\n\t\t\t\t\t)\n\t\t\t\t});\n\t\t\t}, funcScope)));\n\n\t\t\t-- Resulting Code:\n\t\t\t-- function xy(a)\n\t\t\t-- \t\treturn ARR[a - 10]\n\t\t\t-- end\n\t\tend,\n\t\t-- Rotate Array and Add unrotate code\n\t\tfunction()\n\t\t\tif self.Rotate and #self.constants > 1 then\n\t\t\t\tlocal shift = math.random(1, #self.constants - 1);\n\n\t\t\t\trotate(self.constants, -shift);\n\t\t\t\tself:addRotateCode(ast, shift);\n\t\t\tend\n\t\tend,\n\t});\n\n\tfor i, f in ipairs(steps) do\n\t\tf();\n\tend\n\n\t-- Add the Array Declaration\n\ttable.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.rootScope, {self.arrId}, {self:createArray()}));\n\n\tself.rootScope = nil;\n\tself.arrId = nil;\n\n\tself.constants = nil;\n\tself.lookup = nil;\nend\n\nreturn ConstantArray;"
  },
  {
    "path": "src/prometheus/steps/EncryptStrings.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- EncryptStrings.lua\n--\n-- This Script provides a Simple Obfuscation Step that encrypts strings\n\nlocal Step = require(\"prometheus.step\")\nlocal Ast = require(\"prometheus.ast\")\nlocal Parser = require(\"prometheus.parser\")\nlocal Enums = require(\"prometheus.enums\")\nlocal visitast = require(\"prometheus.visitast\");\nlocal util = require(\"prometheus.util\")\nlocal AstKind = Ast.AstKind;\n\nlocal EncryptStrings = Step:extend()\nEncryptStrings.Description = \"This Step will encrypt strings within your Program.\"\nEncryptStrings.Name = \"Encrypt Strings\"\n\nEncryptStrings.SettingsDescriptor = {}\n\nfunction EncryptStrings:init(_) end\n\n\nfunction EncryptStrings:CreateEncryptionService()\n\tlocal usedSeeds = {};\n\n\tlocal secret_key_6 = math.random(0, 63) -- 6-bit  arbitrary integer (0..63)\n\tlocal secret_key_7 = math.random(0, 127) -- 7-bit  arbitrary integer (0..127)\n\tlocal secret_key_44 = math.random(0, 17592186044415) -- 44-bit arbitrary integer (0..17592186044415)\n\tlocal secret_key_8 = math.random(0, 255); -- 8-bit  arbitrary integer (0..255)\n\n\tlocal floor = math.floor\n\n\tlocal function primitive_root_257(idx)\n\t\tlocal g, m, d = 1, 128, 2 * idx + 1\n\t\trepeat\n\t\t\tg, m, d = g * g * (d >= m and 3 or 1) % 257, m / 2, d % m\n\t\tuntil m < 1\n\t\treturn g\n\tend\n\n\tlocal param_mul_8 = primitive_root_257(secret_key_7)\n\tlocal param_mul_45 = secret_key_6 * 4 + 1\n\tlocal param_add_45 = secret_key_44 * 2 + 1\n\n\tlocal state_45 = 0\n\tlocal state_8 = 2\n\n\tlocal prev_values = {}\n\tlocal function set_seed(seed_53)\n\t\tstate_45 = seed_53 % 35184372088832\n\t\tstate_8 = seed_53 % 255 + 2\n\t\tprev_values = {}\n\tend\n\n\tlocal function gen_seed()\n\t\tlocal seed;\n\t\trepeat\n\t\t\tseed = math.random(0, 35184372088832);\n\t\tuntil not usedSeeds[seed];\n\t\tusedSeeds[seed] = true;\n\t\treturn seed;\n\tend\n\n\tlocal function get_random_32()\n\t\tstate_45 = (state_45 * param_mul_45 + param_add_45) % 35184372088832\n\t\trepeat\n\t\t\tstate_8 = state_8 * param_mul_8 % 257\n\t\tuntil state_8 ~= 1\n\t\tlocal r = state_8 % 32\n\t\tlocal n = floor(state_45 / 2 ^ (13 - (state_8 - r) / 32)) % 2 ^ 32 / 2 ^ r\n\t\treturn floor(n % 1 * 2 ^ 32) + floor(n)\n\tend\n\n\tlocal function get_next_pseudo_random_byte()\n\t\tif #prev_values == 0 then\n\t\t\tlocal rnd = get_random_32() -- value 0..4294967295\n\t\t\tlocal low_16 = rnd % 65536\n\t\t\tlocal high_16 = (rnd - low_16) / 65536\n\t\t\tlocal b1 = low_16 % 256\n\t\t\tlocal b2 = (low_16 - b1) / 256\n\t\t\tlocal b3 = high_16 % 256\n\t\t\tlocal b4 = (high_16 - b3) / 256\n\t\t\tprev_values = { b1, b2, b3, b4 }\n\t\tend\n\t\t--print(unpack(prev_values))\n\t\treturn table.remove(prev_values)\n\tend\n\n\tlocal function encrypt(str)\n\t\tlocal seed = gen_seed();\n\t\tset_seed(seed)\n\t\tlocal len = string.len(str)\n\t\tlocal out = {}\n\t\tlocal prevVal = secret_key_8;\n\t\tfor i = 1, len do\n\t\t\tlocal byte = string.byte(str, i);\n\t\t\tout[i] = string.char((byte - (get_next_pseudo_random_byte() + prevVal)) % 256);\n\t\t\tprevVal = byte;\n\t\tend\n\t\treturn table.concat(out), seed;\n\tend\n\n    local function genCode()\n        local code = [[\ndo\n\t]] .. table.concat(util.shuffle{\n\t\t\"local floor = math.floor\",\n\t\t\"local random = math.random\",\n\t\t\"local remove = table.remove\",\n\t\t\"local char = string.char\",\n\t\t\"local state_45 = 0\",\n\t\t\"local state_8 = 2\",\n\t\t\"local charmap = {}\",\n\t\t\"local nums = {}\"\n\t}, \"\\n\") .. [[\n\tfor i = 1, 256 do\n\t\tnums[i] = i;\n\tend\n\n\trepeat\n\t\tlocal idx = random(1, #nums);\n\t\tlocal n = remove(nums, idx);\n\t\tcharmap[n] = char(n - 1);\n\tuntil #nums == 0;\n\n\tlocal prev_values = {}\n\tlocal function get_next_pseudo_random_byte()\n\t\tif #prev_values == 0 then\n\t\t\tstate_45 = (state_45 * ]] .. tostring(param_mul_45) .. [[ + ]] .. tostring(param_add_45) .. [[) % 35184372088832\n\t\t\trepeat\n\t\t\t\tstate_8 = state_8 * ]] .. tostring(param_mul_8) .. [[ % 257\n\t\t\tuntil state_8 ~= 1\n\t\t\tlocal r = state_8 % 32\n\t\t\tlocal shift = 13 - (state_8 - r) / 32\n\t\t\tlocal n = floor(state_45 / 2 ^ shift) % 4294967296 / 2 ^ r\n\t\t\tlocal rnd = floor(n % 1 * 4294967296) + floor(n)\n\t\t\tlocal low_16 = rnd % 65536\n\t\t\tlocal high_16 = (rnd - low_16) / 65536\n\t\t\tprev_values = { low_16 % 256, (low_16 - low_16 % 256) / 256, high_16 % 256, (high_16 - high_16 % 256) / 256 }\n\t\tend\n\n\n\t\tlocal prevValuesLen = #prev_values;\n\t\tlocal removed = prev_values[prevValuesLen];\n\t\tprev_values[prevValuesLen] = nil;\n\t\treturn removed;\n\tend\n\n\tlocal realStrings = {};\n\tSTRINGS = setmetatable({}, {\n\t\t__index = realStrings;\n\t\t__metatable = nil;\n\t});\n  \tfunction DECRYPT(str, seed)\n\t\tlocal realStringsLocal = realStrings;\n\t\tif(realStringsLocal[seed]) then return seed; else\n\t\t\tprev_values = {};\n\t\t\tlocal chars = charmap;\n\t\t\tstate_45 = seed % 35184372088832\n\t\t\tstate_8 = seed % 255 + 2\n\t\t\tlocal len = #str;\n\t\t\trealStringsLocal[seed] = \"\";\n\t\t\tlocal prevVal = ]] .. tostring(secret_key_8) .. [[;\n\t\t\tlocal s = \"\";\n\t\t\tfor i=1, len, 1 do\n\t\t\t\tprevVal = (string.byte(str, i) + get_next_pseudo_random_byte() + prevVal) % 256\n\t\t\t\ts = s .. chars[prevVal + 1];\n\t\t\tend\n\t\t\trealStringsLocal[seed] = s;\n\t\tend\n\t\treturn seed;\n\tend\nend]]\n\n\t\treturn code;\n    end\n\n    return {\n        encrypt = encrypt,\n        param_mul_45 = param_mul_45,\n        param_mul_8 = param_mul_8,\n        param_add_45 = param_add_45,\n\t\tsecret_key_8 = secret_key_8,\n        genCode = genCode,\n    }\nend\n\nfunction EncryptStrings:apply(ast, _)\n    local Encryptor = self:CreateEncryptionService();\n\n\tlocal code = Encryptor.genCode();\n\tlocal newAst = Parser:new({ LuaVersion = Enums.LuaVersion.Lua51 }):parse(code);\n\tlocal doStat = newAst.body.statements[1];\n\n\tlocal scope = ast.body.scope;\n\tlocal decryptVar = scope:addVariable();\n\tlocal stringsVar = scope:addVariable();\n\n\tdoStat.body.scope:setParent(ast.body.scope);\n\n\tvisitast(newAst, nil, function(node, data)\n\t\tif(node.kind == AstKind.FunctionDeclaration) then\n\t\t\tif(node.scope:getVariableName(node.id) == \"DECRYPT\") then\n\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\tdata.scope:addReferenceToHigherScope(scope, decryptVar);\n\t\t\t\tnode.scope = scope;\n\t\t\t\tnode.id = decryptVar;\n\t\t\tend\n\t\tend\n\t\tif(node.kind == AstKind.AssignmentVariable or node.kind == AstKind.VariableExpression) then\n\t\t\tif(node.scope:getVariableName(node.id) == \"STRINGS\") then\n\t\t\t\tdata.scope:removeReferenceToHigherScope(node.scope, node.id);\n\t\t\t\tdata.scope:addReferenceToHigherScope(scope, stringsVar);\n\t\t\t\tnode.scope = scope;\n\t\t\t\tnode.id = stringsVar;\n\t\t\tend\n\t\tend\n\tend)\n\n\tvisitast(ast, nil, function(node, data)\n\t\tif(node.kind == AstKind.StringExpression) then\n\t\t\tdata.scope:addReferenceToHigherScope(scope, stringsVar);\n\t\t\tdata.scope:addReferenceToHigherScope(scope, decryptVar);\n\t\t\tlocal encrypted, seed = Encryptor.encrypt(node.value);\n\t\t\treturn Ast.IndexExpression(Ast.VariableExpression(scope, stringsVar), Ast.FunctionCallExpression(Ast.VariableExpression(scope, decryptVar), {\n\t\t\t\tAst.StringExpression(encrypted), Ast.NumberExpression(seed),\n\t\t\t}));\n\t\tend\n\tend)\n\n\n\t-- Insert to Main Ast\n\ttable.insert(ast.body.statements, 1, doStat);\n\ttable.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(scope, util.shuffle{ decryptVar, stringsVar }, {}));\n\treturn ast\nend\n\nreturn EncryptStrings\n"
  },
  {
    "path": "src/prometheus/steps/NumbersToExpressions.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- NumbersToExpressions.lua\n--\n-- This Script provides an Obfuscation Step, that converts Number Literals to expressions.\n-- This step can now also convert numbers to different representations!\n-- Supported representations: hex, binary, scientific, normal. Please note that binary is only supported in Lua 5.2 and above.\n\nunpack = unpack or table.unpack\n\nlocal Step = require(\"prometheus.step\")\nlocal Ast = require(\"prometheus.ast\")\nlocal visitast = require(\"prometheus.visitast\")\nlocal util = require(\"prometheus.util\")\nlocal logger = require(\"logger\")\nlocal AstKind = Ast.AstKind\n\nlocal NumbersToExpressions = Step:extend()\nNumbersToExpressions.Description = \"This Step Converts number Literals to Expressions\"\nNumbersToExpressions.Name = \"Numbers To Expressions\"\n\nNumbersToExpressions.SettingsDescriptor = {\n\tThreshold = {\n\t\ttype = \"number\",\n\t\tdefault = 1,\n\t\tmin = 0,\n\t\tmax = 1,\n\t},\n\n\tInternalThreshold = {\n\t\ttype = \"number\",\n\t\tdefault = 0.2,\n\t\tmin = 0,\n\t\tmax = 0.8,\n\t},\n\n\tNumberRepresentationMutaton = {\n\t\ttype = \"boolean\",\n\t\tdefault = false,\n\t},\n\n\tAllowedNumberRepresentations = {\n\t\ttype = \"table\",\n\t\tdefault = {\"hex\", \"scientific\", \"normal\"},\n\t\tvalues = {\"hex\", \"binary\", \"scientific\", \"normal\"},\n\t},\n}\n\nlocal function generateModuloExpression(n)\n\tlocal rhs = n + math.random(1, 2^24)\n\tlocal multiplier = math.random(1, 2^8)\n\tlocal lhs = n + (multiplier * rhs)\n\treturn lhs, rhs\nend\n\nlocal function contains(table, value)\n\tfor _, v in ipairs(table) do\n\t\tif v == value then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nfunction NumbersToExpressions:init(_)\n\tself.ExpressionGenerators = {\n\t\tfunction(val, depth) -- Addition\n\t\t\tlocal val2 = math.random(-2 ^ 20, 2 ^ 20)\n\t\t\tlocal diff = val - val2\n\t\t\tif tonumber(tostring(diff)) + tonumber(tostring(val2)) ~= val then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\treturn Ast.AddExpression(\n\t\t\t\tself:CreateNumberExpression(val2, depth),\n\t\t\t\tself:CreateNumberExpression(diff, depth),\n\t\t\t\tfalse\n\t\t\t)\n\t\tend,\n\n\t\tfunction(val, depth) -- Subtraction\n\t\t\tlocal val2 = math.random(-2 ^ 20, 2 ^ 20)\n\t\t\tlocal diff = val + val2\n\t\t\tif tonumber(tostring(diff)) - tonumber(tostring(val2)) ~= val then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\treturn Ast.SubExpression(\n\t\t\t\tself:CreateNumberExpression(diff, depth),\n\t\t\t\tself:CreateNumberExpression(val2, depth),\n\t\t\t\tfalse\n\t\t\t)\n\t\tend,\n\n\t\tfunction(val, depth) -- Modulo\n\t\t\tlocal lhs, rhs = generateModuloExpression(val)\n\t\t\tif tonumber(tostring(lhs)) % tonumber(tostring(rhs)) ~= val then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\treturn Ast.ModExpression(\n\t\t\t\tself:CreateNumberExpression(lhs, depth),\n\t\t\t\tself:CreateNumberExpression(rhs, depth),\n\t\t\t\tfalse\n\t\t\t)\n\t\tend,\n\t}\nend\n\nfunction NumbersToExpressions:CreateNumberExpression(val, depth)\n\tif depth > 0 and math.random() >= self.InternalThreshold or depth > 15 then\n\t\tlocal format = self.AllowedNumberRepresentations[math.random(1, #self.AllowedNumberRepresentations)]\n\t\tif not self.NumberRepresentationMutaton then\n\t\t\treturn Ast.NumberExpression(val)\n\t\tend\n\n\t\tif format == \"hex\" then\n\t\t\tif val ~= math.floor(val) or val < 0 then\n\t\t\t\treturn Ast.NumberExpression(val)\n\t\t\tend\n\t\t\tlocal hexStr = string.format(\"0x%X\", val)\n\t\t\tlocal result = \"\"\n\t\t\tfor i = 1, #hexStr do\n\t\t\t\tlocal c = hexStr:sub(i, i)\n\t\t\t\tif math.random() > 0.5 then\n\t\t\t\t\tresult = result .. c:upper()\n\t\t\t\telse\n\t\t\t\t\tresult = result .. c:lower()\n\t\t\t\tend\n\t\t\tend\n\t\t\treturn Ast.NumberExpression(result)\n\t\tend\n\n\t\tif format == \"binary\" then\n\t\t\tif val ~= math.floor(val) or val < 0 then\n\t\t\t\treturn Ast.NumberExpression(val)\n\t\t\tend\n\t\t\tlocal binary = \"\"\n\t\t\tlocal n = val\n\t\t\tif n == 0 then\n\t\t\t\tbinary = \"0\"\n\t\t\telse\n\t\t\t\twhile n > 0 do\n\t\t\t\t\tbinary = (n % 2) .. binary\n\t\t\t\t\tn = math.floor(n / 2)\n\t\t\t\tend\n\t\t\tend\n\t\t\treturn Ast.NumberExpression(\"0b\" .. binary)\n\t\tend\n\n\t\tif format == \"scientific\" then\n\t\t\tif val == 0 then\n\t\t\t\treturn Ast.NumberExpression(val)\n\t\t\tend\n\n\t\t\tlocal exp = math.floor(math.log10(math.abs(val)))\n\t\t\tlocal mantissa = val / (10 ^ exp)\n\t\t\treturn Ast.NumberExpression(string.format(\"%.15ge%d\", mantissa, exp))\n\t\tend\n\n\t\tif format == \"normal\" then\n\t\t\treturn Ast.NumberExpression(val)\n\t\tend\n\tend\n\n\tlocal generators = util.shuffle({ unpack(self.ExpressionGenerators) })\n\tfor _, generator in ipairs(generators) do\n\t\tlocal node = generator(val, depth + 1)\n\t\tif node then\n\t\t\treturn node\n\t\tend\n\tend\n\treturn Ast.NumberExpression(val)\nend\n\nfunction NumbersToExpressions:apply(ast)\n\tif contains(self.AllowedNumberRepresentations, \"binary\") then\n\t\tlogger:warn(\"Warning: Binary representation is only supported in Lua 5.2 and above!\")\n\tend\n\n\tvisitast(ast, nil, function(node, _)\n\t\tif node.kind == AstKind.NumberExpression then\n\t\t\tif math.random() <= self.Threshold then\n\t\t\t\treturn self:CreateNumberExpression(node.value, 0)\n\t\t\tend\n\t\tend\n\tend)\nend\n\nreturn NumbersToExpressions\n"
  },
  {
    "path": "src/prometheus/steps/ProxifyLocals.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- ProxifyLocals.lua\n--\n-- This Script provides a Obfuscation Step for putting all Locals into Proxy Objects\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal visitast = require(\"prometheus.visitast\");\nlocal RandomLiterals = require(\"prometheus.randomLiterals\")\n\nlocal AstKind = Ast.AstKind;\n\nlocal ProxifyLocals = Step:extend();\nProxifyLocals.Description = \"This Step wraps all locals into Proxy Objects\";\nProxifyLocals.Name = \"Proxify Locals\";\n\nProxifyLocals.SettingsDescriptor = {\n\tLiteralType = {\n\t\tname = \"LiteralType\",\n\t\tdescription = \"The type of the randomly generated literals\",\n\t\ttype = \"enum\",\n\t\tvalues = {\n\t\t\t\"dictionary\",\n\t\t\t\"number\",\n\t\t\t\"string\",\n            \"any\",\n\t\t},\n\t\tdefault = \"string\",\n\t},\n}\n\nlocal function shallowcopy(orig)\n    local orig_type = type(orig)\n    local copy\n    if orig_type == 'table' then\n        copy = {}\n        for orig_key, orig_value in pairs(orig) do\n            copy[orig_key] = orig_value\n        end\n    else -- number, string, boolean, etc\n        copy = orig\n    end\n    return copy\nend\n\nlocal function callNameGenerator(generatorFunction, ...)\n\tif(type(generatorFunction) == \"table\") then\n\t\tgeneratorFunction = generatorFunction.generateName;\n\tend\n\treturn generatorFunction(...);\nend\n\nlocal MetatableExpressions = {\n    {\n        constructor = Ast.AddExpression,\n        key = \"__add\";\n    },\n    {\n        constructor = Ast.SubExpression,\n        key = \"__sub\";\n    },\n    {\n        constructor = Ast.IndexExpression,\n        key = \"__index\";\n    },\n    {\n        constructor = Ast.MulExpression,\n        key = \"__mul\";\n    },\n    {\n        constructor = Ast.DivExpression,\n        key = \"__div\";\n    },\n    {\n        constructor = Ast.PowExpression,\n        key = \"__pow\";\n    },\n    {\n        constructor = Ast.StrCatExpression,\n        key = \"__concat\";\n    }\n}\n\nfunction ProxifyLocals:init(_) end\n\nlocal function generateLocalMetatableInfo(pipeline)\n    local usedOps = {};\n    local info = {};\n    for i, v in ipairs({\"setValue\", \"getValue\", \"index\"}) do\n        local rop;\n        repeat\n            rop = MetatableExpressions[math.random(#MetatableExpressions)];\n        until not usedOps[rop];\n        usedOps[rop] = true;\n        info[v] = rop;\n    end\n\n    info.valueName = callNameGenerator(pipeline.namegenerator, math.random(1, 4096));\n\n    return info;\nend\n\nfunction ProxifyLocals:CreateAssignmentExpression(info, expr, parentScope)\n    local metatableVals = {};\n\n    -- Setvalue Entry\n    local setValueFunctionScope = Scope:new(parentScope);\n    local setValueSelf = setValueFunctionScope:addVariable();\n    local setValueArg = setValueFunctionScope:addVariable();\n    local setvalueFunctionLiteral = Ast.FunctionLiteralExpression(\n        {\n            Ast.VariableExpression(setValueFunctionScope, setValueSelf), -- Argument 1\n            Ast.VariableExpression(setValueFunctionScope, setValueArg), -- Argument 2\n        },\n        Ast.Block({ -- Create Function Body\n            Ast.AssignmentStatement({\n                Ast.AssignmentIndexing(Ast.VariableExpression(setValueFunctionScope, setValueSelf), Ast.StringExpression(info.valueName));\n            }, {\n                Ast.VariableExpression(setValueFunctionScope, setValueArg)\n            })\n        }, setValueFunctionScope)\n    );\n    table.insert(metatableVals, Ast.KeyedTableEntry(Ast.StringExpression(info.setValue.key), setvalueFunctionLiteral));\n\n    -- Getvalue Entry\n    local getValueFunctionScope = Scope:new(parentScope);\n    local getValueSelf = getValueFunctionScope:addVariable();\n    local getValueArg = getValueFunctionScope:addVariable();\n    local getValueIdxExpr;\n    if(info.getValue.key == \"__index\" or info.setValue.key == \"__index\") then\n        getValueIdxExpr = Ast.FunctionCallExpression(Ast.VariableExpression(getValueFunctionScope:resolveGlobal(\"rawget\")), {\n            Ast.VariableExpression(getValueFunctionScope, getValueSelf),\n            Ast.StringExpression(info.valueName),\n        });\n    else\n        getValueIdxExpr = Ast.IndexExpression(Ast.VariableExpression(getValueFunctionScope, getValueSelf), Ast.StringExpression(info.valueName));\n    end\n    local getvalueFunctionLiteral = Ast.FunctionLiteralExpression(\n        {\n            Ast.VariableExpression(getValueFunctionScope, getValueSelf), -- Argument 1\n            Ast.VariableExpression(getValueFunctionScope, getValueArg), -- Argument 2\n        },\n        Ast.Block({ -- Create Function Body\n            Ast.ReturnStatement({\n                getValueIdxExpr;\n            });\n        }, getValueFunctionScope)\n    );\n    table.insert(metatableVals, Ast.KeyedTableEntry(Ast.StringExpression(info.getValue.key), getvalueFunctionLiteral));\n\n    parentScope:addReferenceToHigherScope(self.setMetatableVarScope, self.setMetatableVarId);\n    return Ast.FunctionCallExpression(\n        Ast.VariableExpression(self.setMetatableVarScope, self.setMetatableVarId),\n        {\n            Ast.TableConstructorExpression({\n                Ast.KeyedTableEntry(Ast.StringExpression(info.valueName), expr)\n            }),\n            Ast.TableConstructorExpression(metatableVals)\n        }\n    );\nend\n\nfunction ProxifyLocals:apply(ast, pipeline)\n    local localMetatableInfos = {};\n    local function getLocalMetatableInfo(scope, id)\n        -- Global Variables should not be transformed\n        if(scope.isGlobal) then return nil end;\n\n        localMetatableInfos[scope] = localMetatableInfos[scope] or {};\n        if localMetatableInfos[scope][id] then\n            -- If locked, return no Metatable\n            if localMetatableInfos[scope][id].locked then\n                return nil\n            end\n            return localMetatableInfos[scope][id];\n        end\n        local localMetatableInfo = generateLocalMetatableInfo(pipeline);\n        localMetatableInfos[scope][id] = localMetatableInfo;\n        return localMetatableInfo;\n    end\n\n    local function disableMetatableInfo(scope, id)\n        -- Global Variables should not be transformed\n        if(scope.isGlobal) then return nil end;\n\n        localMetatableInfos[scope] = localMetatableInfos[scope] or {};\n        localMetatableInfos[scope][id] = {locked = true}\n    end\n\n    -- Create Setmetatable Variable\n    self.setMetatableVarScope = ast.body.scope;\n    self.setMetatableVarId = ast.body.scope:addVariable();\n\n    -- Create Empty Function Variable\n    self.emptyFunctionScope = ast.body.scope;\n    self.emptyFunctionId = ast.body.scope:addVariable();\n    self.emptyFunctionUsed = false;\n\n    -- Add Empty Function Declaration\n    table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.emptyFunctionScope, {self.emptyFunctionId}, {\n        Ast.FunctionLiteralExpression({}, Ast.Block({}, Scope:new(ast.body.scope)));\n    }));\n\n\n    visitast(ast, function(node, data)\n        -- Lock for loop variables\n        if(node.kind == AstKind.ForStatement) then\n            disableMetatableInfo(node.scope, node.id)\n        end\n        if(node.kind == AstKind.ForInStatement) then\n            for i, id in ipairs(node.ids) do\n                disableMetatableInfo(node.scope, id);\n            end\n        end\n\n        -- Lock Function Arguments\n        if(node.kind == AstKind.FunctionDeclaration or node.kind == AstKind.LocalFunctionDeclaration or node.kind == AstKind.FunctionLiteralExpression) then\n            for i, expr in ipairs(node.args) do\n                if expr.kind == AstKind.VariableExpression then\n                    disableMetatableInfo(expr.scope, expr.id);\n                end\n            end\n        end\n\n        -- Assignment Statements may be Obfuscated Differently\n        if(node.kind == AstKind.AssignmentStatement) then\n            if(#node.lhs == 1 and node.lhs[1].kind == AstKind.AssignmentVariable) then\n                local variable = node.lhs[1];\n                local localMetatableInfo = getLocalMetatableInfo(variable.scope, variable.id);\n                if localMetatableInfo then\n                    local args = shallowcopy(node.rhs);\n                    local vexp = Ast.VariableExpression(variable.scope, variable.id);\n                    vexp.__ignoreProxifyLocals = true;\n                    args[1] = localMetatableInfo.setValue.constructor(vexp, args[1]);\n                    self.emptyFunctionUsed = true;\n                    data.scope:addReferenceToHigherScope(self.emptyFunctionScope, self.emptyFunctionId);\n                    return Ast.FunctionCallStatement(Ast.VariableExpression(self.emptyFunctionScope, self.emptyFunctionId), args);\n                end\n            end\n        end\n    end, function(node, data)\n        -- Local Variable Declaration\n        if(node.kind == AstKind.LocalVariableDeclaration) then\n            for i, id in ipairs(node.ids) do\n                local expr = node.expressions[i] or Ast.NilExpression();\n                local localMetatableInfo = getLocalMetatableInfo(node.scope, id);\n                -- Apply Only to Some Variables if Threshold is non 1\n                if localMetatableInfo then\n                    local newExpr = self:CreateAssignmentExpression(localMetatableInfo, expr, node.scope);\n                    node.expressions[i] = newExpr;\n                end\n            end\n        end\n\n        -- Variable Expression\n        if(node.kind == AstKind.VariableExpression and not node.__ignoreProxifyLocals) then\n            local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);\n            -- Apply Only to Some Variables if Threshold is non 1\n            if localMetatableInfo then\n                local literal;\n                if self.LiteralType == \"dictionary\" then\n                    literal = RandomLiterals.Dictionary();\n                elseif self.LiteralType == \"number\" then\n                    literal = RandomLiterals.Number();\n                elseif self.LiteralType == \"string\" then\n                    literal = RandomLiterals.String(pipeline);\n                else\n                    literal = RandomLiterals.Any(pipeline);\n                end\n                return localMetatableInfo.getValue.constructor(node, literal);\n            end\n        end\n\n        -- Assignment Variable for Assignment Statement\n        if(node.kind == AstKind.AssignmentVariable) then\n            local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);\n            -- Apply Only to Some Variables if Threshold is non 1\n            if localMetatableInfo then\n                return Ast.AssignmentIndexing(node, Ast.StringExpression(localMetatableInfo.valueName));\n            end\n        end\n\n        -- Local Function Declaration\n        if(node.kind == AstKind.LocalFunctionDeclaration) then\n            local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);\n            -- Apply Only to Some Variables if Threshold is non 1\n            if localMetatableInfo then\n                local funcLiteral = Ast.FunctionLiteralExpression(node.args, node.body);\n                local newExpr = self:CreateAssignmentExpression(localMetatableInfo, funcLiteral, node.scope);\n                return Ast.LocalVariableDeclaration(node.scope, {node.id}, {newExpr});\n            end\n        end\n\n        -- Function Declaration\n        if(node.kind == AstKind.FunctionDeclaration) then\n            local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);\n            if(localMetatableInfo) then\n                table.insert(node.indices, 1, localMetatableInfo.valueName);\n            end\n        end\n    end)\n\n    -- Add Setmetatable Variable Declaration\n    table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.setMetatableVarScope, {self.setMetatableVarId}, {\n        Ast.VariableExpression(self.setMetatableVarScope:resolveGlobal(\"setmetatable\"))\n    }));\nend\n\nreturn ProxifyLocals;"
  },
  {
    "path": "src/prometheus/steps/SplitStrings.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- SplitStrings.lua\n--\n-- This Script provides a Simple Obfuscation Step for splitting Strings\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal visitAst = require(\"prometheus.visitast\");\nlocal Parser = require(\"prometheus.parser\");\nlocal util = require(\"prometheus.util\");\nlocal enums = require(\"prometheus.enums\")\n\nlocal LuaVersion = enums.LuaVersion;\n\nlocal SplitStrings = Step:extend();\nSplitStrings.Description = \"This Step splits Strings to a specific or random length\";\nSplitStrings.Name = \"Split Strings\";\n\nSplitStrings.SettingsDescriptor = {\n\tThreshold = {\n\t\tname = \"Threshold\",\n\t\tdescription = \"The relative amount of nodes that will be affected\",\n\t\ttype = \"number\",\n\t\tdefault = 1,\n\t\tmin = 0,\n\t\tmax = 1,\n\t},\n\tMinLength = {\n\t\tname = \"MinLength\",\n\t\tdescription = \"The minimal length for the chunks in that the Strings are splitted\",\n\t\ttype = \"number\",\n\t\tdefault = 5,\n\t\tmin = 1,\n\t\tmax = nil,\n\t},\n\tMaxLength = {\n\t\tname = \"MaxLength\",\n\t\tdescription = \"The maximal length for the chunks in that the Strings are splitted\",\n\t\ttype = \"number\",\n\t\tdefault = 5,\n\t\tmin = 1,\n\t\tmax = nil,\n\t},\n\tConcatenationType = {\n\t\tname = \"ConcatenationType\",\n\t\tdescription = \"The Functions used for Concatenation. Note that when using custom, the String Array will also be Shuffled\",\n\t\ttype = \"enum\",\n\t\tvalues = {\n\t\t\t\"strcat\",\n\t\t\t\"table\",\n\t\t\t\"custom\",\n\t\t},\n\t\tdefault = \"custom\",\n\t},\n\tCustomFunctionType = {\n\t\tname = \"CustomFunctionType\",\n\t\tdescription = \"The Type of Function code injection This Option only applies when custom Concatenation is selected.\\\nNote that when choosing inline, the code size may increase significantly!\",\n\t\ttype = \"enum\",\n\t\tvalues = {\n\t\t\t\"global\",\n\t\t\t\"local\",\n\t\t\t\"inline\",\n\t\t},\n\t\tdefault = \"global\",\n\t},\n\tCustomLocalFunctionsCount = {\n\t\tname = \"CustomLocalFunctionsCount\",\n\t\tdescription = \"The number of local functions per scope. This option only applies when CustomFunctionType = local\",\n\t\ttype = \"number\",\n\t\tdefault = 2,\n\t\tmin = 1,\n\t}\n}\n\nfunction SplitStrings:init(settings) end\n\nlocal function generateTableConcatNode(chunks, data)\n\tlocal chunkNodes = {};\n\tfor i, chunk in ipairs(chunks) do\n\t\ttable.insert(chunkNodes, Ast.TableEntry(Ast.StringExpression(chunk)));\n\tend\n\tlocal tb = Ast.TableConstructorExpression(chunkNodes);\n\tdata.scope:addReferenceToHigherScope(data.tableConcatScope, data.tableConcatId);\n\treturn Ast.FunctionCallExpression(Ast.VariableExpression(data.tableConcatScope, data.tableConcatId), {tb});\nend\n\nlocal function generateStrCatNode(chunks)\n\t-- Put Together Expression for Concatenating String\n\tlocal generatedNode = nil;\n\tfor i, chunk in ipairs(chunks) do\n\t\tif generatedNode then\n\t\t\tgeneratedNode = Ast.StrCatExpression(generatedNode, Ast.StringExpression(chunk));\n\t\telse\n\t\t\tgeneratedNode = Ast.StringExpression(chunk);\n\t\tend\n\tend\n\treturn generatedNode\nend\n\nlocal customVariants = 2;\nlocal custom1Code = [=[\nfunction custom(table)\n    local stringTable, str = table[#table], \"\";\n    for i=1,#stringTable, 1 do\n        str = str .. stringTable[table[i]];\n\tend\n\treturn str\nend\n]=];\n\nlocal custom2Code = [=[\nfunction custom(tb)\n\tlocal str = \"\";\n\tfor i=1, #tb / 2, 1 do\n\t\tstr = str .. tb[#tb / 2 + tb[i]];\n\tend\n\treturn str\nend\n]=];\n\nlocal function generateCustomNodeArgs(chunks, data, variant)\n\tlocal shuffled = {};\n\tlocal shuffledIndices = {};\n\tfor i = 1, #chunks, 1 do\n\t\tshuffledIndices[i] = i;\n\tend\n\tutil.shuffle(shuffledIndices);\n\n\tfor i, v in ipairs(shuffledIndices) do\n\t\tshuffled[v] = chunks[i];\n\tend\n\n\t-- Custom Function Type 1\n\tif variant == 1 then\n\t\tlocal args = {};\n\t\tlocal tbNodes = {};\n\n\t\tfor i, v in ipairs(shuffledIndices) do\n\t\t\ttable.insert(args, Ast.TableEntry(Ast.NumberExpression(v)));\n\t\tend\n\n\t\tfor i, chunk in ipairs(shuffled) do\n\t\t\ttable.insert(tbNodes, Ast.TableEntry(Ast.StringExpression(chunk)));\n\t\tend\n\n\t\tlocal tb = Ast.TableConstructorExpression(tbNodes);\n\n\t\ttable.insert(args, Ast.TableEntry(tb));\n\t\treturn {Ast.TableConstructorExpression(args)};\n\n\t-- Custom Function Type 2\n\telse\n\n\t\tlocal args = {};\n\t\tfor i, v in ipairs(shuffledIndices) do\n\t\t\ttable.insert(args, Ast.TableEntry(Ast.NumberExpression(v)));\n\t\tend\n\t\tfor i, chunk in ipairs(shuffled) do\n\t\t\ttable.insert(args, Ast.TableEntry(Ast.StringExpression(chunk)));\n\t\tend\n\t\treturn {Ast.TableConstructorExpression(args)};\n\tend\n\nend\n\nlocal function generateCustomFunctionLiteral(parentScope, variant)\n\tlocal parser = Parser:new({\n\t\tLuaVersion = LuaVersion.Lua52;\n\t});\n\n\t-- Custom Function Type 1\n\tif variant == 1 then\n\t\tlocal funcDeclNode = parser:parse(custom1Code).body.statements[1];\n\t\tlocal funcBody = funcDeclNode.body;\n\t\tlocal funcArgs = funcDeclNode.args;\n\t\tfuncBody.scope:setParent(parentScope);\n\t\treturn Ast.FunctionLiteralExpression(funcArgs, funcBody);\n\n\t\t-- Custom Function Type 2\n\telse\n\t\tlocal funcDeclNode = parser:parse(custom2Code).body.statements[1];\n\t\tlocal funcBody = funcDeclNode.body;\n\t\tlocal funcArgs = funcDeclNode.args;\n\t\tfuncBody.scope:setParent(parentScope);\n\t\treturn Ast.FunctionLiteralExpression(funcArgs, funcBody);\n\tend\nend\n\nlocal function generateGlobalCustomFunctionDeclaration(ast, data)\n\tlocal parser = Parser:new({\n\t\tLuaVersion = LuaVersion.Lua52;\n\t});\n\n\t-- Custom Function Type 1\n\tif data.customFunctionVariant == 1 then\n\t\tlocal astScope = ast.body.scope;\n\t\tlocal funcDeclNode = parser:parse(custom1Code).body.statements[1];\n\t\tlocal funcBody = funcDeclNode.body;\n\t\tlocal funcArgs = funcDeclNode.args;\n\t\tfuncBody.scope:setParent(astScope);\n\t\treturn Ast.LocalVariableDeclaration(astScope, {data.customFuncId},\n\t\t{Ast.FunctionLiteralExpression(funcArgs, funcBody)});\n\t-- Custom Function Type 2\n\telse\n\t\tlocal astScope = ast.body.scope;\n\t\tlocal funcDeclNode = parser:parse(custom2Code).body.statements[1];\n\t\tlocal funcBody = funcDeclNode.body;\n\t\tlocal funcArgs = funcDeclNode.args;\n\t\tfuncBody.scope:setParent(astScope);\n\t\treturn Ast.LocalVariableDeclaration(data.customFuncScope, {data.customFuncId},\n\t\t{Ast.FunctionLiteralExpression(funcArgs, funcBody)});\n\tend\nend\n\nfunction SplitStrings:variant()\n\treturn math.random(1, customVariants);\nend\n\nfunction SplitStrings:apply(ast, pipeline)\n\tlocal data = {};\n\n\n\tif(self.ConcatenationType == \"table\") then\n\t\tlocal scope = ast.body.scope;\n\t\tlocal id = scope:addVariable();\n\t\tdata.tableConcatScope = scope;\n\t\tdata.tableConcatId = id;\n\telseif(self.ConcatenationType == \"custom\") then\n\t\tdata.customFunctionType = self.CustomFunctionType;\n\t\tif data.customFunctionType == \"global\" then\n\t\t\tlocal scope = ast.body.scope;\n\t\t\tlocal id = scope:addVariable();\n\t\t\tdata.customFuncScope = scope;\n\t\t\tdata.customFuncId = id;\n\t\t\tdata.customFunctionVariant = self:variant();\n\t\tend\n\tend\n\n\n\tlocal customLocalFunctionsCount = self.CustomLocalFunctionsCount;\n\tlocal self2 = self;\n\n\tvisitAst(ast, function(node, data)\n\t\t-- Previsit Function\n\n\t\t-- Create Local Function declarations\n\t\tif(self.ConcatenationType == \"custom\" and data.customFunctionType == \"local\" and node.kind == Ast.AstKind.Block and node.isFunctionBlock) then\n\t\t\tdata.functionData.localFunctions = {};\n\t\t\tfor i = 1, customLocalFunctionsCount, 1 do\n\t\t\t\tlocal scope = data.scope;\n\t\t\t\tlocal id = scope:addVariable();\n\t\t\t\tlocal variant = self:variant();\n\t\t\t\ttable.insert(data.functionData.localFunctions, {\n\t\t\t\t\tscope = scope,\n\t\t\t\t\tid = id,\n\t\t\t\t\tvariant = variant,\n\t\t\t\t\tused = false,\n\t\t\t\t});\n\t\t\tend\n\t\tend\n\n\tend, function(node, data)\n\t\t-- PostVisit Function\n\n\t\t-- Create actual function literals for local customFunctionType\n\t\tif(self.ConcatenationType == \"custom\" and data.customFunctionType == \"local\" and node.kind == Ast.AstKind.Block and node.isFunctionBlock) then\n\t\t\tfor i, func in ipairs(data.functionData.localFunctions) do\n\t\t\t\tif func.used then\n\t\t\t\t\tlocal literal = generateCustomFunctionLiteral(func.scope, func.variant);\n\t\t\t\t\ttable.insert(node.statements, 1, Ast.LocalVariableDeclaration(func.scope, {func.id}, {literal}));\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\n\t\t-- Apply Only to String nodes\n\t\tif(node.kind == Ast.AstKind.StringExpression) then\n\t\t\tlocal str = node.value;\n\t\t\tlocal chunks = {};\n\t\t\tlocal i = 1;\n\n\t\t\t-- Split String into Parts of length between MinLength and MaxLength\n\t\t\twhile i <= string.len(str) do\n\t\t\t\tlocal len = math.random(self.MinLength, self.MaxLength);\n\t\t\t\ttable.insert(chunks, string.sub(str, i, i + len - 1));\n\t\t\t\ti = i + len;\n\t\t\tend\n\n\t\t\tif(#chunks > 1) then\n\t\t\t\tif math.random() < self.Threshold then\n\t\t\t\t\tif self.ConcatenationType == \"strcat\" then\n\t\t\t\t\t\tnode = generateStrCatNode(chunks);\n\t\t\t\t\telseif self.ConcatenationType == \"table\" then\n\t\t\t\t\t\tnode = generateTableConcatNode(chunks, data);\n\t\t\t\t\telseif self.ConcatenationType == \"custom\" then\n\t\t\t\t\t\tif self.CustomFunctionType == \"global\" then\n\t\t\t\t\t\t\tlocal args = generateCustomNodeArgs(chunks, data, data.customFunctionVariant);\n\t\t\t\t\t\t\t-- Add Reference for Variable Renaming\n\t\t\t\t\t\t\tdata.scope:addReferenceToHigherScope(data.customFuncScope, data.customFuncId);\n\t\t\t\t\t\t\tnode = Ast.FunctionCallExpression(Ast.VariableExpression(data.customFuncScope, data.customFuncId), args);\n\t\t\t\t\t\telseif self.CustomFunctionType == \"local\" then\n\t\t\t\t\t\t\tlocal lfuncs = data.functionData.localFunctions;\n\t\t\t\t\t\t\tlocal idx = math.random(1, #lfuncs);\n\t\t\t\t\t\t\tlocal func = lfuncs[idx];\n\t\t\t\t\t\t\tlocal args = generateCustomNodeArgs(chunks, data, func.variant);\n\t\t\t\t\t\t\tfunc.used = true;\n\t\t\t\t\t\t\t-- Add Reference for Variable Renaming\n\t\t\t\t\t\t\tdata.scope:addReferenceToHigherScope(func.scope, func.id);\n\t\t\t\t\t\t\tnode = Ast.FunctionCallExpression(Ast.VariableExpression(func.scope, func.id), args);\n\t\t\t\t\t\telseif self.CustomFunctionType == \"inline\" then\n\t\t\t\t\t\t\tlocal variant = self:variant();\n\t\t\t\t\t\t\tlocal args = generateCustomNodeArgs(chunks, data, variant);\n\t\t\t\t\t\t\tlocal literal = generateCustomFunctionLiteral(data.scope, variant);\n\t\t\t\t\t\t\tnode = Ast.FunctionCallExpression(literal, args);\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn node, true;\n\t\tend\n\tend, data)\n\n\n\tif(self.ConcatenationType == \"table\") then\n\t\tlocal globalScope = data.globalScope;\n\t\tlocal tableScope, tableId = globalScope:resolve(\"table\")\n\t\tast.body.scope:addReferenceToHigherScope(globalScope, tableId);\n\t\ttable.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(data.tableConcatScope, {data.tableConcatId},\n\t\t{Ast.IndexExpression(Ast.VariableExpression(tableScope, tableId), Ast.StringExpression(\"concat\"))}));\n\telseif(self.ConcatenationType == \"custom\" and self.CustomFunctionType == \"global\") then\n\t\ttable.insert(ast.body.statements, 1, generateGlobalCustomFunctionDeclaration(ast, data));\n\tend\nend\n\nreturn SplitStrings;"
  },
  {
    "path": "src/prometheus/steps/Vmify.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- Vmify.lua\n--\n-- This Script provides a Complex Obfuscation Step that will compile the entire Script to  a fully custom bytecode that does not share it's instructions\n-- with lua, making it much harder to crack than other lua obfuscators\n\nlocal Step = require(\"prometheus.step\");\nlocal Compiler = require(\"prometheus.compiler.compiler\");\n\nlocal Vmify = Step:extend();\nVmify.Description = \"This Step will Compile your script into a fully-custom (not a half custom like other lua obfuscators) Bytecode Format and emit a vm for executing it.\";\nVmify.Name = \"Vmify\";\n\nVmify.SettingsDescriptor = {}\n\nfunction Vmify:init(_) end\n\nfunction Vmify:apply(ast)\n    -- Create Compiler\n\tlocal compiler = Compiler:new();\n\n    -- Compile the Script into a bytecode vm\n    return compiler:compile(ast);\nend\n\nreturn Vmify;"
  },
  {
    "path": "src/prometheus/steps/Watermark.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- Watermark.lua\n--\n-- This Script provides a Step that will add a watermark to the script\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\n\nlocal Watermark = Step:extend();\nWatermark.Description = \"This Step will add a watermark to the script\";\nWatermark.Name = \"Watermark\";\n\nWatermark.SettingsDescriptor = {\n  Content = {\n    name = \"Content\",\n    description = \"The Content of the Watermark\",\n    type = \"string\",\n    default = \"This Script is Part of the Prometheus Obfuscator by Levno_710\",\n  },\n  CustomVariable = {\n    name = \"Custom Variable\",\n    description = \"The Variable that will be used for the Watermark\",\n    type = \"string\",\n    default = \"_WATERMARK\",\n  }\n}\n\nfunction Watermark:init(settings)\n\t\nend\n\nfunction Watermark:apply(ast)\n  local body = ast.body;\n  if string.len(self.Content) > 0 then\n    local scope, variable = ast.globalScope:resolve(self.CustomVariable);\n    local watermark = Ast.AssignmentVariable(ast.globalScope, variable);\n\n    local functionScope = Scope:new(body.scope);\n    functionScope:addReferenceToHigherScope(ast.globalScope, variable);\n    \n    local arg = functionScope:addVariable();\n    local statement = Ast.PassSelfFunctionCallStatement(Ast.StringExpression(self.Content), \"gsub\", {\n      Ast.StringExpression(\".+\"),\n      Ast.FunctionLiteralExpression({\n        Ast.VariableExpression(functionScope, arg)\n      }, Ast.Block({\n        Ast.AssignmentStatement({\n          watermark\n        }, {\n          Ast.VariableExpression(functionScope, arg)\n        })\n      }, functionScope))\n    });\n\n    table.insert(ast.body.statements, 1, statement)\n  end\nend\n\nreturn Watermark;"
  },
  {
    "path": "src/prometheus/steps/WatermarkCheck.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- WatermarkCheck.lua\n--\n-- This Script provides a Step that will add a watermark to the script\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\nlocal Watermark = require(\"prometheus.steps.Watermark\");\n\nlocal WatermarkCheck = Step:extend();\nWatermarkCheck.Description = \"This Step will add a watermark to the script\";\nWatermarkCheck.Name = \"WatermarkCheck\";\n\nWatermarkCheck.SettingsDescriptor = {\n  Content = {\n    name = \"Content\",\n    description = \"The Content of the WatermarkCheck\",\n    type = \"string\",\n    default = \"This Script is Part of the Prometheus Obfuscator by Levno_710\",\n  },\n}\n\nlocal function callNameGenerator(generatorFunction, ...)\n\tif(type(generatorFunction) == \"table\") then\n\t\tgeneratorFunction = generatorFunction.generateName;\n\tend\n\treturn generatorFunction(...);\nend\n\nfunction WatermarkCheck:init(_) end\n\nfunction WatermarkCheck:apply(ast, pipeline)\n  self.CustomVariable = \"_\" .. callNameGenerator(pipeline.namegenerator, math.random(10000000000, 100000000000));\n  pipeline:addStep(Watermark:new(self));\n\n  local body = ast.body;\n  local watermarkExpression = Ast.StringExpression(self.Content);\n  local scope, variable = ast.globalScope:resolve(self.CustomVariable);\n  local watermark = Ast.VariableExpression(ast.globalScope, variable);\n  local notEqualsExpression = Ast.NotEqualsExpression(watermark, watermarkExpression);\n  local ifBody = Ast.Block({Ast.ReturnStatement({})}, Scope:new(ast.body.scope));\n\n  table.insert(body.statements, 1, Ast.IfStatement(notEqualsExpression, ifBody, {}, nil));\nend\n\nreturn WatermarkCheck;"
  },
  {
    "path": "src/prometheus/steps/WrapInFunction.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- WrapInFunction.lua\n--\n-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function\n\nlocal Step = require(\"prometheus.step\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Scope = require(\"prometheus.scope\");\n\nlocal WrapInFunction = Step:extend();\nWrapInFunction.Description = \"This Step Wraps the Entire Script into a Function\";\nWrapInFunction.Name = \"Wrap in Function\";\n\nWrapInFunction.SettingsDescriptor = {\n\tIterations = {\n\t\tname = \"Iterations\",\n\t\tdescription = \"The Number Of Iterations\",\n\t\ttype = \"number\",\n\t\tdefault = 1,\n\t\tmin = 1,\n\t\tmax = nil,\n\t}\n}\n\nfunction WrapInFunction:init(_) end\n\nfunction WrapInFunction:apply(ast)\n\tfor i = 1, self.Iterations, 1 do\n\t\tlocal body = ast.body;\n\n\t\tlocal scope = Scope:new(ast.globalScope);\n\t\tbody.scope:setParent(scope);\n\n\t\tast.body = Ast.Block({\n\t\t\tAst.ReturnStatement({\n\t\t\t\tAst.FunctionCallExpression(Ast.FunctionLiteralExpression({Ast.VarargExpression()}, body), {Ast.VarargExpression()})\n\t\t\t});\n\t\t}, scope);\n\tend\nend\n\nreturn WrapInFunction;"
  },
  {
    "path": "src/prometheus/steps.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- steps.lua\n--\n-- This Script provides a collection of obfuscation steps.\n\nreturn {\n\tWrapInFunction = require(\"prometheus.steps.WrapInFunction\"),\n\tSplitStrings = require(\"prometheus.steps.SplitStrings\"),\n\tVmify = require(\"prometheus.steps.Vmify\"),\n\tConstantArray = require(\"prometheus.steps.ConstantArray\"),\n\tProxifyLocals = require(\"prometheus.steps.ProxifyLocals\"),\n\tAntiTamper = require(\"prometheus.steps.AntiTamper\"),\n\tEncryptStrings = require(\"prometheus.steps.EncryptStrings\"),\n\tNumbersToExpressions = require(\"prometheus.steps.NumbersToExpressions\"),\n\tAddVararg = require(\"prometheus.steps.AddVararg\"),\n\tWatermarkCheck = require(\"prometheus.steps.WatermarkCheck\"),\n}"
  },
  {
    "path": "src/prometheus/tokenizer.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- tokenizer.lua\n-- Overview:\n-- This Script provides a class for lexical Analysis of lua code.\n-- This Tokenizer is Capable of tokenizing LuaU and Lua5.1\nlocal Enums = require(\"prometheus.enums\");\nlocal util = require(\"prometheus.util\");\nlocal logger = require(\"logger\");\nlocal config = require(\"config\");\n\nlocal LuaVersion = Enums.LuaVersion;\nlocal lookupify = util.lookupify;\nlocal unlookupify = util.unlookupify;\nlocal escape = util.escape;\nlocal chararray = util.chararray;\nlocal keys = util.keys;\nlocal Tokenizer = {};\n\nTokenizer.EOF_CHAR = \"<EOF>\";\nTokenizer.WHITESPACE_CHARS = lookupify{\n\t\" \", \"\\t\", \"\\n\", \"\\r\",\n}\n\nTokenizer.ANNOTATION_CHARS = lookupify(chararray(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_\"))\nTokenizer.ANNOTATION_START_CHARS = lookupify(chararray(\"!@\"))\n\nTokenizer.Conventions = Enums.Conventions;\n\nTokenizer.TokenKind = {\n\tEof = \"Eof\",\n\tKeyword = \"Keyword\",\n\tSymbol = \"Symbol\",\n\tIdent = \"Identifier\",\n\tNumber = \"Number\",\n\tString = \"String\",\n}\n\nTokenizer.EOF_TOKEN = {\n\tkind = Tokenizer.TokenKind.Eof,\n\tvalue = \"<EOF>\",\n\tstartPos = -1,\n\tendPos = -1,\n\tsource = \"<EOF>\",\n}\n\nlocal function token(self, startPos, kind, value)\n\tlocal line, linePos = self:getPosition(self.index);\n\tlocal annotations = self.annotations\n\tself.annotations = {};\n\treturn {\n\t\tkind = kind,\n\t\tvalue = value,\n\t\tstartPos = startPos,\n\t\tendPos = self.index,\n\t\tsource = self.source:sub(startPos + 1, self.index),\n\t\tline = line,\n\t\tlinePos = linePos,\n\t\tannotations = annotations,\n\t}\nend\n\nlocal function generateError(self, message)\n\tlocal line, linePos = self:getPosition(self.index);\n\treturn \"Lexing Error at Position \" .. tostring(line) .. \":\" .. tostring(linePos) .. \", \" .. message;\nend\n\nlocal function generateWarning(token, message)\n\treturn \"Warning at Position \" .. tostring(token.line) .. \":\" .. tostring(token.linePos) .. \", \" .. message;\nend\n\nfunction Tokenizer:getPosition(i)\n\tlocal column = self.columnMap[i]\n\n\tif not column then --// `i` is bigger than self.length, this shouldn't happen, but it did. (Theres probably some error in the tokenizer, cant find it.)\n\t\tcolumn = self.columnMap[#self.columnMap]\n\tend\n\n\treturn column.id, column.charMap[i]\nend\n\n--// Prepare columnMap for getPosition\nfunction Tokenizer:prepareGetPosition()\n\tlocal columnMap, column = {}, { charMap = {}, id = 1, length = 0 }\n\n\tfor index = 1, self.length do\n\t\tlocal character = string.sub(self.source, index, index) -- NOTE_1: this could use table.clone to reduce amount of NEWTABLE (if that causes any performance issues)\n\n\t\tlocal columnLength = column.length + 1\n\t\tcolumn.length = columnLength\n\t\tcolumn.charMap[index] = columnLength\n\n\t\tif character == \"\\n\" then\n\t\t\tcolumn = { charMap = {}, id = column.id + 1, length = 0 } -- NOTE_1\n\t\tend\n\n\t\tcolumnMap[index] = column\n\tend\n\n\tself.columnMap = columnMap\nend\n\n-- Constructor for Tokenizer\nfunction Tokenizer:new(settings)\n\tlocal luaVersion = (settings and (settings.luaVersion or settings.LuaVersion)) or LuaVersion.LuaU;\n\tlocal conventions = Tokenizer.Conventions[luaVersion];\n\n\tif(conventions == nil) then\n\t\tlogger:error(\"The Lua Version \\\"\" .. luaVersion .. \"\\\" is not recognized by the Tokenizer! Please use one of the following: \\\"\" .. table.concat(keys(Tokenizer.Conventions), \"\\\",\\\"\") .. \"\\\"\");\n\tend\n\n\tlocal tokenizer = {\n\t\tindex = 0, -- Index where the current char is read\n\t\tlength = 0,\n\t\tsource = \"\", -- Source to Tokenize\n\t\tluaVersion = luaVersion, -- LuaVersion to be used while Tokenizing\n\t\tconventions = conventions;\n\n\t\tNumberChars = conventions.NumberChars,\n\t\tNumberCharsLookup = lookupify(conventions.NumberChars),\n\t\tKeywords = conventions.Keywords,\n\t\tKeywordsLookup = lookupify(conventions.Keywords),\n\t\tBinaryNumberChars = conventions.BinaryNumberChars,\n\t\tBinaryNumberCharsLookup = lookupify(conventions.BinaryNumberChars);\n\t\tBinaryNums = conventions.BinaryNums,\n\t\tHexadecimalNums = conventions.HexadecimalNums,\n\t\tHexNumberChars = conventions.HexNumberChars,\n\t\tHexNumberCharsLookup = lookupify(conventions.HexNumberChars),\n\t\tDecimalExponent = conventions.DecimalExponent,\n\t\tDecimalSeperators = conventions.DecimalSeperators,\n\t\tIdentChars = conventions.IdentChars,\n\t\tIdentCharsLookup = lookupify(conventions.IdentChars),\n\n\t\tEscapeSequences = conventions.EscapeSequences,\n\t\tNumericalEscapes = conventions.NumericalEscapes,\n\t\tEscapeZIgnoreNextWhitespace = conventions.EscapeZIgnoreNextWhitespace,\n\t\tHexEscapes = conventions.HexEscapes,\n\t\tUnicodeEscapes = conventions.UnicodeEscapes,\n\n\t\tSymbolChars = conventions.SymbolChars,\n\t\tSymbolCharsLookup = lookupify(conventions.SymbolChars),\n\t\tMaxSymbolLength = conventions.MaxSymbolLength,\n\t\tSymbols = conventions.Symbols,\n\t\tSymbolsLookup = lookupify(conventions.Symbols),\n\n\t\tStringStartLookup = lookupify({\"\\\"\", \"\\'\"}),\n\t\tannotations = {},\n\t};\n\n\tsetmetatable(tokenizer, self);\n\tself.__index = self;\n\n\treturn tokenizer;\nend\n\n-- Reset State of Tokenizer to Tokenize another File\nfunction Tokenizer:reset()\n\tself.index = 0;\n\tself.length = 0;\n\tself.source = \"\";\n\tself.annotations = {};\n\tself.columnMap = {};\nend\n\n-- Append String to this Tokenizer\nfunction Tokenizer:append(code)\n\tself.source = self.source .. code\n\tself.length = self.length + code:len();\n\tself:prepareGetPosition();\nend\n\n-- Function to peek the n'th char in the source of the tokenizer\nlocal function peek(self, n)\n\tn = n or 0;\n\tlocal i = self.index + n + 1;\n\tif i > self.length then\n\t\treturn Tokenizer.EOF_CHAR\n\tend\n\treturn self.source:sub(i, i);\nend\n\n-- Function to get the next char in the source\nlocal function get(self)\n\tlocal i = self.index + 1;\n\tif i > self.length then\n\t\tlogger:error(generateError(self, \"Unexpected end of Input\"));\n\tend\n\tself.index = self.index + 1;\n\treturn self.source:sub(i, i);\nend\n\n-- The same as get except it throws an Error if the char is not contained in charOrLookup\nlocal function expect(self, charOrLookup)\n\tif(type(charOrLookup) == \"string\") then\n\t\tcharOrLookup = {[charOrLookup] = true};\n\tend\n\n\tlocal char = peek(self);\n\tif charOrLookup[char] ~= true then\n\t\tlocal etb = unlookupify(charOrLookup);\n\t\tfor i, v in ipairs(etb) do\n\t\t\tetb[i] = escape(v);\n\t\tend\n\t\tlocal errorMessage = \"Unexpected char \\\"\" .. escape(char) .. \"\\\"! Expected one of \\\"\" .. table.concat(etb, \"\\\",\\\"\") .. \"\\\"\";\n\t\tlogger:error(generateError(self, errorMessage));\n\tend\n\n\tself.index = self.index + 1;\n\treturn char;\nend\n\n-- Returns wether the n'th char is in the lookup\nlocal function is(self, charOrLookup, n)\n\tlocal char = peek(self, n);\n\tif(type(charOrLookup) == \"string\") then\n\t\treturn char == charOrLookup;\n\tend\n\treturn charOrLookup[char];\nend\n\nfunction Tokenizer:parseAnnotation()\n\tif is(self, Tokenizer.ANNOTATION_START_CHARS) then\n\t\tself.index = self.index + 1;\n\t\tlocal source, length = {}, 0;\n\t\twhile(is(self, Tokenizer.ANNOTATION_CHARS)) do\n\t\t\tsource[length + 1] = get(self)\n\t\t\tlength = #source\n\t\tend\n\t\tif length > 0 then\n\t\t\tself.annotations[string.lower(table.concat(source))] = true;\n\t\tend\n\t\treturn nil;\n\tend\n\treturn get(self);\nend\n\n-- skip one or 0 Comments and return wether one was found\nfunction Tokenizer:skipComment()\n\tif(is(self, \"-\", 0) and is(self, \"-\", 1)) then\n\t\tself.index = self.index + 2;\n\t\tif(is(self, \"[\")) then\n\t\t\tself.index = self.index + 1;\n\t\t\tlocal eqCount = 0;\n\t\t\twhile(is(self, \"=\")) do\n\t\t\t\tself.index = self.index + 1;\n\t\t\t\teqCount = eqCount + 1;\n\t\t\tend\n\t\t\tif(is(self, \"[\")) then\n\t\t\t\t-- Multiline Comment\n\t\t\t\t-- Get all Chars to Closing bracket but also consider that the count of equal signs must be the same\n\t\t\t\twhile true do\n\t\t\t\t\tif(self:parseAnnotation() == ']') then\n\t\t\t\t\t\tlocal eqCount2 = 0;\n\t\t\t\t\t\twhile(is(self, \"=\")) do\n\t\t\t\t\t\t\tself.index = self.index + 1;\n\t\t\t\t\t\t\teqCount2 = eqCount2 + 1;\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif(is(self, \"]\")) then\n\t\t\t\t\t\t\tif(eqCount2 == eqCount) then\n\t\t\t\t\t\t\t\tself.index = self.index + 1;\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t-- Single Line Comment\n\t\t-- Get all Chars to next Newline\n\t\twhile(self.index < self.length and self:parseAnnotation() ~= \"\\n\") do end\n\t\treturn true;\n\tend\n\treturn false;\nend\n\n-- skip All Whitespace and Comments to next Token\nfunction Tokenizer:skipWhitespaceAndComments()\n\twhile self:skipComment() do end\n\twhile is(self, Tokenizer.WHITESPACE_CHARS) do\n\t\tself.index = self.index + 1;\n\t\twhile self:skipComment() do end\n\tend\nend\n\nlocal function int(self, chars, seperators)\n\tlocal buffer = {};\n\twhile true do\n\t\tif (is(self, chars)) then\n\t\t\tbuffer[#buffer + 1] = get(self)\n\t\telseif (is(self, seperators)) then\n\t\t\tself.index = self.index + 1;\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\treturn table.concat(buffer);\nend\n\n-- Lex the next token as a Number\nfunction Tokenizer:number()\n\tlocal startPos = self.index;\n\tlocal source = expect(self, setmetatable({[\".\"] = true}, {__index = self.NumberCharsLookup}));\n\n\tif source == \"0\" then\n\t\tif self.BinaryNums and is(self, lookupify(self.BinaryNums)) then\n\t\t\tself.index = self.index + 1;\n\t\t\tsource = int(self, self.BinaryNumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\t\t\tlocal value = tonumber(source, 2);\n\t\t\treturn token(self, startPos, Tokenizer.TokenKind.Number, value);\n\t\tend\n\n\t\tif self.HexadecimalNums and is(self, lookupify(self.HexadecimalNums)) then\n\t\t\tself.index = self.index + 1;\n\t\t\tsource = int(self, self.HexNumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\t\t\tlocal value = tonumber(source, 16);\n\t\t\treturn token(self, startPos, Tokenizer.TokenKind.Number, value);\n\t\tend\n\tend\n\n\tif source == \".\" then\n\t\tsource = source .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\telse\n\t\tsource = source .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\t\tif(is(self, \".\")) then\n\t\t\tsource = source .. get(self) .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\t\tend\n\tend\n\n\tif(self.DecimalExponent and is(self, lookupify(self.DecimalExponent))) then\n\t\tsource = source .. get(self);\n\t\tif(is(self, lookupify({\"+\",\"-\"}))) then\n\t\t\tsource = source .. get(self);\n\t\tend\n\t\tlocal v = int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));\n\t\tif(v:len() < 1) then\n\t\t\tlogger:error(generateError(self, \"Expected a Valid Exponent!\"));\n\t\tend\n\t\tsource = source .. v;\n\tend\n\n\tlocal value = tonumber(source);\n\treturn token(self, startPos, Tokenizer.TokenKind.Number, value);\nend\n\n-- Lex the Next Token as Identifier or Keyword\nfunction Tokenizer:ident()\n\tlocal startPos = self.index;\n\tlocal source = expect(self, self.IdentCharsLookup)\n\tlocal sourceAddContent = {source}\n\twhile(is(self, self.IdentCharsLookup)) do\n\t\t-- source = source .. get(self);\n\t\ttable.insert(sourceAddContent, get(self))\n\tend\n\tsource = table.concat(sourceAddContent)\n\tif(self.KeywordsLookup[source]) then\n\t\treturn token(self, startPos, Tokenizer.TokenKind.Keyword, source);\n\tend\n\n\tlocal tk = token(self, startPos, Tokenizer.TokenKind.Ident, source);\n\n\tif(string.sub(source, 1, string.len(config.IdentPrefix)) == config.IdentPrefix) then\n\t\tlogger:warn(generateWarning(tk, string.format(\"identifiers should not start with \\\"%s\\\" as this may break the program\", config.IdentPrefix)));\n\tend\n\n\treturn tk;\nend\n\nfunction Tokenizer:singleLineString()\n\tlocal startPos = self.index;\n\tlocal startChar = expect(self, self.StringStartLookup);\n\tlocal buffer = {};\n\n\twhile (not is(self, startChar)) do\n\t\tlocal char = get(self);\n\n\t\t-- Single Line String may not contain Linebreaks except when they are escaped by \\\n\t\tif(char == '\\n') then\n\t\t\tself.index = self.index - 1;\n\t\t\tlogger:error(generateError(self, \"Unterminated String\"));\n\t\tend\n\n\n\t\tif(char == \"\\\\\") then\n\t\t\tchar = get(self);\n\n\t\t\tlocal escape = self.EscapeSequences[char];\n\t\t\tif(type(escape) == \"string\") then\n\t\t\t\tchar = escape;\n\n\t\t\telseif(self.NumericalEscapes and self.NumberCharsLookup[char]) then\n\t\t\t\tlocal numstr = char;\n\n\t\t\t\tif(is(self, self.NumberCharsLookup)) then\n\t\t\t\t\tchar = get(self);\n\t\t\t\t\tnumstr = numstr .. char;\n\t\t\t\tend\n\n\t\t\t\tif(is(self, self.NumberCharsLookup)) then\n\t\t\t\t\tchar = get(self);\n\t\t\t\t\tnumstr = numstr .. char;\n\t\t\t\tend\n\n\t\t\t\tchar = string.char(tonumber(numstr));\n\n\t\t\telseif(self.UnicodeEscapes and char == \"u\") then\n\t\t\t\texpect(self, \"{\");\n\t\t\t\tlocal num = \"\";\n\t\t\t\twhile (is(self, self.HexNumberCharsLookup)) do\n\t\t\t\t\tnum = num .. get(self);\n\t\t\t\tend\n\t\t\t\texpect(self, \"}\");\n\t\t\t\tchar = util.utf8char(tonumber(num, 16));\n\t\t\telseif(self.HexEscapes and char == \"x\") then\n\t\t\t\tlocal hex = expect(self, self.HexNumberCharsLookup) .. expect(self, self.HexNumberCharsLookup);\n\t\t\t\tchar = string.char(tonumber(hex, 16));\n\t\t\telseif(self.EscapeZIgnoreNextWhitespace and char == \"z\") then\n\t\t\t\tchar = \"\";\n\t\t\t\twhile(is(self, Tokenizer.WHITESPACE_CHARS)) do\n\t\t\t\t\tself.index = self.index + 1;\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t--// since table.insert is slower in lua51\n\t\tbuffer[#buffer + 1] = char\n\tend\n\n\texpect(self, startChar);\n\n\treturn token(self, startPos, Tokenizer.TokenKind.String, table.concat(buffer))\nend\n\nfunction Tokenizer:multiLineString()\n\tlocal startPos = self.index;\n\tif(is(self, \"[\")) then\n\t\tself.index = self.index + 1;\n\t\tlocal eqCount = 0;\n\t\twhile(is(self, \"=\")) do\n\t\t\tself.index = self.index + 1;\n\t\t\teqCount = eqCount + 1;\n\t\tend\n\t\tif(is(self, \"[\")) then\n\t\t\t-- Multiline String\n\t\t\t-- Parse String to Closing bracket but also consider that the count of equal signs must be the same\n\n\t\t\t-- Skip Leading newline if existing\n\t\t\tself.index = self.index + 1;\n\t\t\tif(is(self, \"\\n\")) then\n\t\t\t\tself.index = self.index + 1;\n\t\t\tend\n\n\t\t\tlocal value = \"\";\n\t\t\twhile true do\n\t\t\t\tlocal char = get(self);\n\t\t\t\tif(char == ']') then\n\t\t\t\t\tlocal eqCount2 = 0;\n\t\t\t\t\twhile(is(self, \"=\")) do\n\t\t\t\t\t\tchar = char .. get(self);\n\t\t\t\t\t\teqCount2 = eqCount2 + 1;\n\t\t\t\t\tend\n\t\t\t\t\tif(is(self, \"]\")) then\n\t\t\t\t\t\tif(eqCount2 == eqCount) then\n\t\t\t\t\t\t\tself.index = self.index + 1;\n\t\t\t\t\t\t\treturn token(self, startPos, Tokenizer.TokenKind.String, value), true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tvalue = value .. char;\n\t\t\tend\n\t\tend\n\tend\n\tself.index = startPos;\n\treturn nil, false -- There was not an actual multiline string at the given Position\nend\n\nfunction Tokenizer:symbol()\n\tlocal startPos = self.index;\n\tfor len = self.MaxSymbolLength, 1, -1 do\n\t\tlocal str = self.source:sub(self.index + 1, self.index + len);\n\t\tif self.SymbolsLookup[str] then\n\t\t\tself.index = self.index + len;\n\t\t\treturn token(self, startPos, Tokenizer.TokenKind.Symbol, str);\n\t\tend\n\tend\n\tlogger:error(generateError(self, \"Unknown Symbol\"));\nend\n\n\n-- get the Next token\nfunction Tokenizer:next()\n\t-- Skip All Whitespace before the token\n\tself:skipWhitespaceAndComments();\n\n\tlocal startPos = self.index;\n\tif startPos >= self.length then\n\t\treturn token(self, startPos, Tokenizer.TokenKind.Eof);\n\tend\n\n\t-- Numbers\n\tif(is(self, self.NumberCharsLookup)) then\n\t\treturn self:number();\n\tend\n\n\t-- Identifiers and Keywords\n\tif(is(self, self.IdentCharsLookup)) then\n\t\treturn self:ident();\n\tend\n\n\t-- Singleline String Literals\n\tif(is(self, self.StringStartLookup)) then\n\t\treturn self:singleLineString();\n\tend\n\n\t-- Multiline String Literals\n\tif(is(self, \"[\", 0)) then\n\t\t-- The isString variable is due to the fact that \"[\" could also be a symbol for indexing\n\t\tlocal value, isString = self:multiLineString();\n\t\tif isString then\n\t\t\treturn value;\n\t\tend\n\tend\n\n\t-- Number starting with dot\n\tif(is(self, \".\") and is(self, self.NumberCharsLookup, 1)) then\n\t\treturn self:number();\n\tend\n\n\t-- Symbols\n\tif(is(self, self.SymbolCharsLookup)) then\n\t\treturn self:symbol();\n\tend\n\n\n\tlogger:error(generateError(self, \"Unexpected char \\\"\" .. escape(peek(self)) .. \"\\\"!\"));\nend\n\nfunction Tokenizer:scanAll()\n\tlocal tb = {};\n\trepeat\n\t\tlocal token = self:next();\n\t\ttable.insert(tb, token);\n\tuntil token.kind == Tokenizer.TokenKind.Eof\n\treturn tb\nend\n\nreturn Tokenizer\n"
  },
  {
    "path": "src/prometheus/unparser.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- unparser.lua\n-- Overview:\n-- This Script provides a class for lua code generation from an ast\n-- This UnParser is Capable of generating LuaU and Lua5.1\n--\n-- Note that a LuaU ast can only be unparsed as LuaU if it contains any continue statements\n--\n-- Settings Object:\n-- luaVersion : The LuaVersion of the Script\n\nlocal config = require(\"config\");\nlocal Ast = require(\"prometheus.ast\");\nlocal Enums = require(\"prometheus.enums\");\nlocal util = require(\"prometheus.util\");\nlocal logger = require(\"logger\");\n\nlocal lookupify = util.lookupify;\nlocal LuaVersion = Enums.LuaVersion;\nlocal AstKind = Ast.AstKind;\n\nlocal Unparser = {}\n\nUnparser.SPACE = config.SPACE;\nUnparser.TAB = config.TAB;\n\nlocal function escapeString(str)\n\tstr = util.escape(str)\n\treturn str;\nend\n\nfunction Unparser:new(settings)\n\tlocal luaVersion = settings.LuaVersion or LuaVersion.LuaU;\n\tlocal conventions = Enums.Conventions[luaVersion];\n\tlocal unparser = {\n\t\tluaVersion = luaVersion;\n\t\tconventions = conventions;\n\t\tidentCharsLookup = lookupify(conventions.IdentChars);\n\t\tnumberCharsLookup = lookupify(conventions.NumberChars);\n\t\tprettyPrint = settings and settings.PrettyPrint or false;\n\t\tnotIdentPattern = \"[^\" .. table.concat(conventions.IdentChars, \"\") .. \"]\";\n\t\tnumberPattern = \"^[\" .. table.concat(conventions.NumberChars, \"\") .. \"]\";\n\t\thighlight = settings and settings.Highlight or false;\n\t\tkeywordsLookup = lookupify(conventions.Keywords);\n\t}\n\n\tsetmetatable(unparser, self);\n\tself.__index = self;\n\n\treturn unparser;\nend\n\nfunction Unparser:isValidIdentifier(source)\n\tif(string.find(source, self.notIdentPattern)) then\n\t\treturn false;\n\tend\n\tif(string.find(source, self.numberPattern)) then\n\t\treturn false;\n\tend\n\tif self.keywordsLookup[source] then\n\t\treturn false;\n\tend\n\treturn #source > 0;\nend\n\nfunction Unparser:setPrettyPrint(prettyPrint)\n\tself.prettyPrint = prettyPrint;\nend\n\nfunction Unparser:getPrettyPrint()\n\treturn self.prettyPrint;\nend\n\nfunction Unparser:tabs(i, ws_needed)\n\treturn self.prettyPrint and string.rep(self.TAB, i) or ws_needed and self.SPACE or \"\";\nend\n\nfunction Unparser:newline(ws_needed)\n\treturn self.prettyPrint and \"\\n\" or ws_needed and self.SPACE or \"\";\nend\n\nfunction Unparser:whitespaceIfNeeded(following, ws)\n\tif(self.prettyPrint or self.identCharsLookup[string.sub(following, 1, 1)]) then\n\t\treturn ws or self.SPACE;\n\tend\n\treturn \"\";\nend\n\nfunction Unparser:whitespaceIfNeeded2(leading, ws)\n\tif(self.prettyPrint or self.identCharsLookup[string.sub(leading, #leading, #leading)]) then\n\t\treturn ws or self.SPACE;\n\tend\n\treturn \"\";\nend\n\nfunction Unparser:optionalWhitespace(ws)\n\treturn self.prettyPrint and (ws or self.SPACE) or \"\";\nend\n\nfunction Unparser:whitespace(ws)\n\treturn self.SPACE or ws;\nend\n\nfunction Unparser:unparse(ast)\n\tif(ast.kind ~= AstKind.TopNode) then\n\t\tlogger:error(\"Unparser:unparse expects a TopNode as first argument\")\n\tend\n\n\treturn self:unparseBlock(ast.body);\nend\n\n-- Helper to join parts table (optimized string building)\nlocal function joinParts(parts)\n\treturn table.concat(parts)\nend\n\nfunction Unparser:unparseBlock(block, tabbing)\n\tif(#block.statements < 1) then\n\t\treturn self:whitespace();\n\tend\n\n\tlocal parts = {}\n\n\tfor i, statement in ipairs(block.statements) do\n\t\tif(statement.kind ~= AstKind.NopStatement) then\n\t\t\tlocal statementCode = self:unparseStatement(statement, tabbing);\n\t\t\tif(not self.prettyPrint and #parts > 0 and string.sub(statementCode, 1, 1) == \"(\") then\n\t\t\t\t-- This is so that the following works:\n\t\t\t\t-- print(\"Test\");(function() print(\"Test2\") end)();\n\t\t\t\tstatementCode = \";\" .. statementCode;\n\t\t\tend\n\t\t\tlocal ws = self:whitespaceIfNeeded2(#parts > 0 and parts[#parts] or \"\", self:whitespaceIfNeeded(statementCode, self:newline(true)));\n\t\t\tif i ~= 1 then\n\t\t\t\tparts[#parts + 1] = ws;\n\t\t\tend\n\t\t\tif(self.prettyPrint) then\n\t\t\t\tstatementCode = statementCode .. \";\"\n\t\t\tend\n\t\t\tparts[#parts + 1] = statementCode;\n\t\tend\n\tend\n\n\treturn joinParts(parts);\nend\n\nfunction Unparser:unparseStatement(statement, tabbing)\n\ttabbing = tabbing and tabbing + 1 or 0;\n\tlocal parts = {};\n\tlocal function push(...) -- Helper to add multiple strings efficiently\n\t\tfor i = 1, select('#', ...) do\n\t\t\tparts[#parts + 1] = select(i, ...)\n\t\tend\n\tend\n\n\tif(statement.kind == AstKind.ContinueStatement) then\n\t\tpush(\"continue\");\n\n\t-- Break Statement\n\telseif(statement.kind == AstKind.BreakStatement) then\n\t\tpush(\"break\");\n\n\t-- Do Statement\n\telseif(statement.kind == AstKind.DoStatement) then\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(\"do\", self:whitespaceIfNeeded(bodyCode, self:newline(true)),\n\t\t\tbodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- While Statement\n\telseif(statement.kind == AstKind.WhileStatement) then\n\t\tlocal expressionCode = self:unparseExpression(statement.condition, tabbing);\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(\"while\", self:whitespaceIfNeeded(expressionCode), expressionCode, self:whitespaceIfNeeded2(expressionCode),\n\t\t\t\"do\", self:whitespaceIfNeeded(bodyCode, self:newline(true)),\n\t\t\tbodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- Repeat Until Statement\n\telseif(statement.kind == AstKind.RepeatStatement) then\n\t\tlocal expressionCode = self:unparseExpression(statement.condition, tabbing);\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(\"repeat\", self:whitespaceIfNeeded(bodyCode, self:newline(true)),\n\t\t\tbodyCode,\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:newline() .. self:tabs(tabbing, true)), \"until\",\n\t\t\tself:whitespaceIfNeeded(expressionCode), expressionCode);\n\n\t-- For Statement\n\telseif(statement.kind == AstKind.ForStatement) then\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(\"for\", self:whitespace(), statement.scope:getVariableName(statement.id), self:optionalWhitespace(), \"=\");\n\t\tpush(self:optionalWhitespace(), self:unparseExpression(statement.initialValue, tabbing), \",\");\n\t\tpush(self:optionalWhitespace(), self:unparseExpression(statement.finalValue, tabbing), \",\");\n\t\tlocal incrementByCode = statement.incrementBy and self:unparseExpression(statement.incrementBy, tabbing) or \"1\";\n\t\tpush(self:optionalWhitespace(), incrementByCode, self:whitespaceIfNeeded2(incrementByCode), \"do\",\n\t\t\tself:whitespaceIfNeeded(bodyCode, self:newline(true)),\n\t\t\tbodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- For In Statement\n\telseif(statement.kind == AstKind.ForInStatement) then\n\t\tpush(\"for\", self:whitespace());\n\t\tfor i, id in ipairs(statement.ids) do\n\t\t\tif(i ~= 1) then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(statement.scope:getVariableName(id));\n\t\tend\n\t\tpush(self:whitespace(), \"in\");\n\t\tlocal exprcode = self:unparseExpression(statement.expressions[1], tabbing);\n\t\tpush(self:whitespaceIfNeeded(exprcode), exprcode);\n\t\tfor i = 2, #statement.expressions, 1 do\n\t\t\texprcode = self:unparseExpression(statement.expressions[i], tabbing);\n\t\t\tpush(\",\", self:optionalWhitespace(), exprcode);\n\t\tend\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(self:whitespaceIfNeeded2(#parts > 0 and parts[#parts] or \"\"), \"do\", self:whitespaceIfNeeded(bodyCode, self:newline(true)),\n\t\t\tbodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- If Statement\n\telseif(statement.kind == AstKind.IfStatement) then\n\t\tlocal exprcode = self:unparseExpression(statement.condition, tabbing);\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(\"if\", self:whitespaceIfNeeded(exprcode), exprcode, self:whitespaceIfNeeded2(exprcode), \"then\",\n\t\t\tself:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);\n\n\t\tfor i, eif in ipairs(statement.elseifs) do\n\t\t\texprcode = self:unparseExpression(eif.condition, tabbing);\n\t\t\tbodyCode = self:unparseBlock(eif.body, tabbing);\n\t\t\tlocal lastPart = #parts > 0 and parts[#parts] or \"\";\n\t\t\tpush(self:newline(false), self:whitespaceIfNeeded2(lastPart, self:tabs(tabbing, true)),\n\t\t\t\t\"elseif\", self:whitespaceIfNeeded(exprcode), exprcode, self:whitespaceIfNeeded2(exprcode),\n\t\t\t\t\"then\", self:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);\n\t\tend\n\n\t\tif(statement.elsebody) then\n\t\t\tbodyCode = self:unparseBlock(statement.elsebody, tabbing);\n\t\t\tlocal lastPart = #parts > 0 and parts[#parts] or \"\";\n\t\t\tpush(self:newline(false), self:whitespaceIfNeeded2(lastPart, self:tabs(tabbing, true)),\n\t\t\t\t\"else\", self:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);\n\t\tend\n\n\t\tpush(self:newline(false), self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- Function Declaration\n\telseif(statement.kind == AstKind.FunctionDeclaration) then\n\t\tlocal funcname = statement.scope:getVariableName(statement.id);\n\t\tfor _, index in ipairs(statement.indices) do\n\t\t\tfuncname = funcname .. \".\" .. index;\n\t\tend\n\t\tpush(\"function\", self:whitespace(), funcname, \"(\");\n\t\tfor i, arg in ipairs(statement.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tif(arg.kind == AstKind.VarargExpression) then\n\t\t\t\tpush(\"...\");\n\t\t\telse\n\t\t\t\tpush(arg.scope:getVariableName(arg.id));\n\t\t\tend\n\t\tend\n\t\tpush(\")\");\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(self:newline(false), bodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- Local Function Declaration\n\telseif(statement.kind == AstKind.LocalFunctionDeclaration) then\n\t\tlocal funcname = statement.scope:getVariableName(statement.id);\n\t\tpush(\"local\", self:whitespace(), \"function\", self:whitespace(), funcname, \"(\");\n\t\tfor i, arg in ipairs(statement.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tif(arg.kind == AstKind.VarargExpression) then\n\t\t\t\tpush(\"...\");\n\t\t\telse\n\t\t\t\tpush(arg.scope:getVariableName(arg.id));\n\t\t\tend\n\t\tend\n\t\tpush(\")\");\n\t\tlocal bodyCode = self:unparseBlock(statement.body, tabbing);\n\t\tpush(self:newline(false), bodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\n\t-- Local Variable Declaration\n\telseif(statement.kind == AstKind.LocalVariableDeclaration) then\n\t\tpush(\"local\", self:whitespace());\n\t\tfor i, id in ipairs(statement.ids) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(statement.scope:getVariableName(id));\n\t\tend\n\t\tif(#statement.expressions > 0) then\n\t\t\tpush(self:optionalWhitespace(), \"=\", self:optionalWhitespace());\n\t\t\tfor i, expr in ipairs(statement.expressions) do\n\t\t\t\tif i > 1 then\n\t\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\t\tend\n\t\t\t\tpush(self:unparseExpression(expr, tabbing + 1));\n\t\t\tend\n\t\tend\n\n\t-- Function Call Statement\n\telseif(statement.kind == AstKind.FunctionCallStatement) then\n\t\tif not (statement.base.kind == AstKind.IndexExpression or statement.base.kind == AstKind.VariableExpression) then\n\t\t\tpush(\"(\", self:unparseExpression(statement.base, tabbing), \")\");\n\t\telse\n\t\t\tpush(self:unparseExpression(statement.base, tabbing));\n\t\tend\n\t\tpush(\"(\");\n\t\tfor i, arg in ipairs(statement.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(arg, tabbing));\n\t\tend\n\t\tpush(\")\");\n\n\t-- Pass Self Function Call Statement\n\telseif(statement.kind == AstKind.PassSelfFunctionCallStatement) then\n\t\tif not (statement.base.kind == AstKind.IndexExpression or statement.base.kind == AstKind.VariableExpression) then\n\t\t\tpush(\"(\", self:unparseExpression(statement.base, tabbing), \")\");\n\t\telse\n\t\t\tpush(self:unparseExpression(statement.base, tabbing));\n\t\tend\n\t\tpush(\":\", statement.passSelfFunctionName, \"(\");\n\t\tfor i, arg in ipairs(statement.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(arg, tabbing));\n\t\tend\n\t\tpush(\")\");\n\n\telseif(statement.kind == AstKind.AssignmentStatement) then\n\t\tfor i, primary_expr in ipairs(statement.lhs) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(primary_expr, tabbing));\n\t\tend\n\t\tpush(self:optionalWhitespace(), \"=\", self:optionalWhitespace());\n\t\tfor i, expr in ipairs(statement.rhs) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(expr, tabbing + 1));\n\t\tend\n\n\t-- Return Statement\n\telseif(statement.kind == AstKind.ReturnStatement) then\n\t\tpush(\"return\");\n\t\tif(#statement.args > 0) then\n\t\t\tlocal exprcode = self:unparseExpression(statement.args[1], tabbing);\n\t\t\tpush(self:whitespaceIfNeeded(exprcode), exprcode);\n\t\t\tfor i = 2, #statement.args, 1 do\n\t\t\t\texprcode = self:unparseExpression(statement.args[i], tabbing);\n\t\t\t\tpush(\",\", self:optionalWhitespace(), exprcode);\n\t\t\tend\n\t\tend\n\n\telseif self.luaVersion == LuaVersion.LuaU then\n\t\tlocal compoundOperators = {\n\t\t\t[AstKind.CompoundAddStatement] = \"+=\",\n\t\t\t[AstKind.CompoundSubStatement] = \"-=\",\n\t\t\t[AstKind.CompoundMulStatement] = \"*=\",\n\t\t\t[AstKind.CompoundDivStatement] = \"/=\",\n\t\t\t[AstKind.CompoundModStatement] = \"%=\",\n\t\t\t[AstKind.CompoundPowStatement] = \"^=\",\n\t\t\t[AstKind.CompoundConcatStatement] = \"..=\",\n\t\t}\n\n\t\tlocal operator = compoundOperators[statement.kind]\n\t\tif operator then\n\t\t\tpush(self:unparseExpression(statement.lhs, tabbing), self:optionalWhitespace(), operator,\n\t\t\t\tself:optionalWhitespace(), self:unparseExpression(statement.rhs, tabbing));\n\t\telse\n\t\t\tlogger:error(string.format(\"\\\"%s\\\" is not a valid unparseable statement in %s!\", statement.kind, self.luaVersion))\n\t\tend\n\tend\n\n\treturn self:tabs(tabbing, false) .. joinParts(parts);\nend\n\nfunction Unparser:unparseExpression(expression, tabbing)\n\tif expression.isParenthesizedExpression then\n\t\tlocal unwrapped = {}\n\t\tfor k, v in pairs(expression) do\n\t\t\tunwrapped[k] = v\n\t\tend\n\t\tunwrapped.isParenthesizedExpression = nil\n\t\treturn \"(\" .. self:unparseExpression(unwrapped, tabbing) .. \")\"\n\tend\n\n\tlocal parts = {};\n\tlocal function push(...)\n\t\tfor i = 1, select('#', ...) do\n\t\t\tparts[#parts + 1] = select(i, ...)\n\t\tend\n\tend\n\n\tif(expression.kind == AstKind.BooleanExpression) then\n\t\treturn expression.value and \"true\" or \"false\";\n\tend\n\n\tif(expression.kind == AstKind.NumberExpression) then\n\t\tlocal str = tostring(expression.value);\n\t\tif(str == \"inf\") then\n\t\t\treturn \"2e1024\"\n\t\tend\n\t\tif(str == \"-inf\") then\n\t\t\treturn \"-2e1024\"\n\t\tend\n\t\tif(str:sub(1, 2) == \"0.\") then\n\t\t\tstr = str:sub(2);\n\t\tend\n\t\treturn str;\n\tend\n\n\tif(expression.kind == AstKind.VariableExpression or expression.kind == AstKind.AssignmentVariable) then\n\t\treturn expression.scope:getVariableName(expression.id);\n\tend\n\n\tif(expression.kind == AstKind.StringExpression) then\n\t\treturn \"\\\"\" .. escapeString(expression.value) .. \"\\\"\";\n\tend\n\n\tif(expression.kind == AstKind.NilExpression) then\n\t\treturn \"nil\";\n\tend\n\n\tif(expression.kind == AstKind.VarargExpression) then\n\t\treturn \"...\";\n\tend\n\n\tlocal k = AstKind.OrExpression;\n\tif(expression.kind == k) then\n\t\tlocal lhs = self:unparseExpression(expression.lhs, tabbing);\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\treturn lhs .. self:whitespaceIfNeeded2(lhs) .. \"or\" .. self:whitespaceIfNeeded(rhs) .. rhs;\n\tend\n\n\tk = AstKind.AndExpression;\n\tif(expression.kind == k) then\n\t\tlocal lhs = self:unparseExpression(expression.lhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\tlhs = \"(\" .. lhs .. \")\";\n\t\tend\n\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn lhs .. self:whitespaceIfNeeded2(lhs) .. \"and\" .. self:whitespaceIfNeeded(rhs) .. rhs;\n\tend\n\n\tlocal comparisonOps = {\n\t\t[AstKind.LessThanExpression] = \"<\",\n\t\t[AstKind.GreaterThanExpression] = \">\",\n\t\t[AstKind.LessThanOrEqualsExpression] = \"<=\",\n\t\t[AstKind.GreaterThanOrEqualsExpression] = \">=\",\n\t\t[AstKind.NotEqualsExpression] = \"~=\",\n\t\t[AstKind.EqualsExpression] = \"==\",\n\t}\n\n\tlocal op = comparisonOps[expression.kind]\n\tif op then\n\t\tk = expression.kind\n\t\tlocal lhs = self:unparseExpression(expression.lhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\tlhs = \"(\" .. lhs .. \")\";\n\t\tend\n\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn lhs .. self:optionalWhitespace() .. op .. self:optionalWhitespace() .. rhs;\n\tend\n\n\tk = AstKind.StrCatExpression\n\tif expression.kind == k then\n\t\tlocal lhs = self:unparseExpression(expression.lhs, tabbing)\n\t\tif Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k) then\n\t\t\tlhs = \"(\" .. lhs .. \")\"\n\t\tend\n\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing)\n\t\tif Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k) then\n\t\t\trhs = \"(\" .. rhs .. \")\"\n\t\tend\n\n\t\tif self.numberCharsLookup[string.sub(lhs, #lhs, #lhs)] then\n\t\t\tlhs = lhs .. \" \"\n\t\tend\n\n\t\treturn lhs .. self:optionalWhitespace() .. (tostring(rhs):sub(1, 1) == \".\" and \".. \" or \"..\") .. self:optionalWhitespace() .. rhs\n\tend\n\n\tlocal arithmeticOps = {\n\t\t[AstKind.AddExpression] = \"+\",\n\t\t[AstKind.SubExpression] = \"-\",\n\t\t[AstKind.MulExpression] = \"*\",\n\t\t[AstKind.DivExpression] = \"/\",\n\t\t[AstKind.ModExpression] = \"%\",\n\t\t[AstKind.PowExpression] = \"^\",\n\t}\n\n\top = arithmeticOps[expression.kind]\n\tif op then\n\t\tk = expression.kind\n\t\tlocal lhs = self:unparseExpression(expression.lhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\tlhs = \"(\" .. lhs .. \")\";\n\t\tend\n\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\tif op == \"-\" and string.sub(rhs, 1, 1) == \"-\" then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn lhs .. self:optionalWhitespace() .. op .. self:optionalWhitespace() .. rhs;\n\tend\n\n\t-- Unary Expressions\n\tk = AstKind.NotExpression;\n\tif(expression.kind == k) then\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn \"not\" .. self:whitespaceIfNeeded(rhs) .. rhs;\n\tend\n\n\tk = AstKind.NegateExpression;\n\tif(expression.kind == k) then\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\tif string.sub(rhs, 1, 1) == \"-\" then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn \"-\" .. rhs;\n\tend\n\n\tk = AstKind.LenExpression;\n\tif(expression.kind == k) then\n\t\tlocal rhs = self:unparseExpression(expression.rhs, tabbing);\n\t\tif(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then\n\t\t\trhs = \"(\" .. rhs .. \")\";\n\t\tend\n\n\t\treturn \"#\" .. rhs;\n\tend\n\n\tk = AstKind.IndexExpression;\n\tif(expression.kind == k or expression.kind == AstKind.AssignmentIndexing) then\n\t\tlocal base = self:unparseExpression(expression.base, tabbing);\n\t\tif(expression.base.kind == AstKind.VarargExpression or Ast.astKindExpressionToNumber(expression.base.kind) > Ast.astKindExpressionToNumber(k) or expression.base.kind == AstKind.StringExpression or expression.base.kind == AstKind.NumberExpression or expression.base.kind == AstKind.NilExpression) then\n\t\t\tbase = \"(\" .. base .. \")\";\n\t\tend\n\n\t\t-- Identifier Indexing e.g: x.y instead of x[\"y\"];\n\t\tif(expression.index.kind == AstKind.StringExpression and self:isValidIdentifier(expression.index.value)) then\n\t\t\treturn base .. \".\" .. expression.index.value;\n\t\tend\n\n\t\t-- Index never needs parens\n\t\tlocal index = self:unparseExpression(expression.index, tabbing);\n\t\treturn base .. \"[\" .. index .. \"]\";\n\tend\n\n\tk = AstKind.FunctionCallExpression;\n\tif(expression.kind == k) then\n\t\tif not (expression.base.kind == AstKind.IndexExpression or expression.base.kind == AstKind.VariableExpression) then\n\t\t\tpush(\"(\", self:unparseExpression(expression.base, tabbing), \")\");\n\t\telse\n\t\t\tpush(self:unparseExpression(expression.base, tabbing));\n\t\tend\n\t\tpush(\"(\");\n\t\tfor i, arg in ipairs(expression.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(arg, tabbing));\n\t\tend\n\t\tpush(\")\");\n\t\treturn joinParts(parts);\n\tend\n\n\tk = AstKind.PassSelfFunctionCallExpression;\n\tif(expression.kind == k) then\n\t\tif not (expression.base.kind == AstKind.IndexExpression or expression.base.kind == AstKind.VariableExpression) then\n\t\t\tpush(\"(\", self:unparseExpression(expression.base, tabbing), \")\");\n\t\telse\n\t\t\tpush(self:unparseExpression(expression.base, tabbing));\n\t\tend\n\t\tpush(\":\", expression.passSelfFunctionName, \"(\");\n\t\tfor i, arg in ipairs(expression.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tpush(self:unparseExpression(arg, tabbing));\n\t\tend\n\t\tpush(\")\");\n\t\treturn joinParts(parts);\n\tend\n\n\tk = AstKind.FunctionLiteralExpression;\n\tif(expression.kind == k) then\n\t\tpush(\"function\", \"(\");\n\t\tfor i, arg in ipairs(expression.args) do\n\t\t\tif i > 1 then\n\t\t\t\tpush(\",\", self:optionalWhitespace());\n\t\t\tend\n\t\t\tif(arg.kind == AstKind.VarargExpression) then\n\t\t\t\tpush(\"...\");\n\t\t\telse\n\t\t\t\tpush(arg.scope:getVariableName(arg.id));\n\t\t\tend\n\t\tend\n\t\tpush(\")\");\n\t\tlocal bodyCode = self:unparseBlock(expression.body, tabbing);\n\t\tpush(self:newline(false), bodyCode, self:newline(false),\n\t\t\tself:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), \"end\");\n\t\treturn joinParts(parts);\n\tend\n\n\tk = AstKind.TableConstructorExpression;\n\tif(expression.kind == k) then\n\t\tif(#expression.entries == 0) then return \"{}\" end;\n\n\t\tlocal inlineTable = #expression.entries <= 3;\n\t\tlocal tableTabbing = tabbing + 1;\n\n\t\tpush(\"{\");\n\t\tif inlineTable then\n\t\t\tpush(self:optionalWhitespace());\n\t\telse\n\t\t\tpush(self:optionalWhitespace(self:newline() .. self:tabs(tableTabbing)));\n\t\tend\n\n\t\tlocal p = false;\n\t\tfor i, entry in ipairs(expression.entries) do\n\t\t\tp = true;\n\t\t\tlocal sep = self.prettyPrint and \",\" or (math.random(1, 2) == 1 and \",\" or \";\");\n\t\t\tif i > 1 and not inlineTable then\n\t\t\t\tpush(sep, self:optionalWhitespace(self:newline() .. self:tabs(tableTabbing)));\n\t\t\telseif i > 1 then\n\t\t\t\tpush(sep, self:optionalWhitespace());\n\t\t\tend\n\t\t\tif(entry.kind == AstKind.KeyedTableEntry) then\n\t\t\t\tif(entry.key.kind == AstKind.StringExpression and self:isValidIdentifier(entry.key.value)) then\n\t\t\t\t\tpush(entry.key.value);\n\t\t\t\telse\n\t\t\t\t\tpush(\"[\", self:unparseExpression(entry.key, tableTabbing), \"]\");\n\t\t\t\tend\n\t\t\t\tpush(self:optionalWhitespace(), \"=\", self:optionalWhitespace(), self:unparseExpression(entry.value, tableTabbing));\n\t\t\telse\n\t\t\t\tpush(self:unparseExpression(entry.value, tableTabbing));\n\t\t\tend\n\t\tend\n\n\t\tif inlineTable then\n\t\t\treturn joinParts(parts) .. self:optionalWhitespace() .. \"}\";\n\t\tend\n\n\t\treturn joinParts(parts) .. self:optionalWhitespace((p and \",\" or \"\") .. self:newline() .. self:tabs(tabbing)) .. \"}\";\n\tend\n\n\tif (self.luaVersion == LuaVersion.LuaU) then\n\t\tk = AstKind.IfElseExpression\n\t\tif(expression.kind == k) then\n\t\t\tpush(\"if \");\n\t\t\tpush(self:unparseExpression(expression.condition));\n\t\t\tpush(\" then \");\n\t\t\tpush(self:unparseExpression(expression.true_value));\n\t\t\tpush(\" else \");\n\t\t\tpush(self:unparseExpression(expression.false_value));\n\t\t\treturn joinParts(parts);\n\t\tend\n\tend\n\n\tlogger:error(string.format(\"\\\"%s\\\" is not a valid unparseable expression\", expression.kind));\nend\n\nreturn Unparser\n"
  },
  {
    "path": "src/prometheus/util.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- util.lua\n--\n-- This Script provides some utility functions for Prometheus.\n\nlocal function lookupify(tb)\n\tlocal tb2 = {};\n\tfor _, v in ipairs(tb) do\n\t\ttb2[v] = true\n\tend\n\treturn tb2\nend\n\nlocal function unlookupify(tb)\n\tlocal tb2 = {};\n\tfor v, _ in pairs(tb) do\n\t\ttable.insert(tb2, v);\n\tend\n\treturn tb2;\nend\n\nlocal function escape(str)\n\treturn str:gsub(\".\", function(char)\n\t\tlocal byte = string.byte(char)\n\t\tif byte >= 32 and byte <= 126 and char ~= \"\\\\\" and char ~= \"\\\"\" and char ~= \"\\'\" then\n\t\t\treturn char\n\t\tend\n\t\tif(char == \"\\\\\") then\n\t\t\treturn \"\\\\\\\\\";\n\t\tend\n\t\tif(char == \"\\n\") then\n\t\t\treturn \"\\\\n\";\n\t\tend\n\t\tif(char == \"\\r\") then\n\t\t\treturn \"\\\\r\";\n\t\tend\n\t\tif(char == \"\\\"\") then\n\t\t\treturn \"\\\\\\\"\";\n\t\tend\n\t\tif(char == \"\\'\") then\n\t\t\treturn \"\\\\\\'\";\n\t\tend\n\t\treturn string.format(\"\\\\%03d\", byte);\n\tend)\nend\n\nlocal function chararray(str)\n\tlocal tb = {};\n\tfor i = 1, str:len(), 1 do\n\t\ttable.insert(tb, str:sub(i, i));\n\tend\n\treturn tb;\nend\n\nlocal function keys(tb)\n\tlocal keyset = {}\n\tlocal n=0\n\tfor k, _ in pairs(tb) do\n\t\tn = n + 1\n\t\tkeyset[n] = k\n\tend\n\treturn keyset\nend\n\nlocal utf8char;\ndo\n\tlocal string_char = string.char\n\tfunction utf8char(cp)\n\t  if cp < 128 then\n\t\treturn string_char(cp)\n\t  end\n\t  local suffix = cp % 64\n\t  local c4 = 128 + suffix\n\t  cp = (cp - suffix) / 64\n\t  if cp < 32 then\n\t\treturn string_char(192 + cp, c4)\n\t  end\n\t  suffix = cp % 64\n\t  local c3 = 128 + suffix\n\t  cp = (cp - suffix) / 64\n\t  if cp < 16 then\n\t\treturn string_char(224 + cp, c3, c4)\n\t  end\n\t  suffix = cp % 64\n\t  cp = (cp - suffix) / 64\n\t  return string_char(240 + cp, 128 + suffix, c3, c4)\n\tend\n  end\n\nlocal function shuffle(tb)\n\tfor i = #tb, 2, -1 do\n\t\tlocal j = math.random(i)\n\t\ttb[i], tb[j] = tb[j], tb[i]\n\tend\n\treturn tb\nend\n\nlocal function readonly(obj)\n\tlocal r = newproxy(true);\n\tgetmetatable(r).__index = obj;\n\treturn r;\nend\n\nreturn {\n\tlookupify = lookupify,\n\tunlookupify = unlookupify,\n\tescape = escape,\n\tchararray = chararray,\n\tkeys = keys,\n\tshuffle = shuffle,\n\tutf8char = utf8char,\n\treadonly = readonly\n}\n"
  },
  {
    "path": "src/prometheus/visitast.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- visitast.lua\n--\n-- This Script provides a utility function for visiting each node of an AST.\n\nlocal Ast = require(\"prometheus.ast\");\nlocal util = require(\"prometheus.util\");\n\nlocal AstKind = Ast.AstKind;\nlocal lookupify = util.lookupify;\n\nlocal visitAst, visitBlock, visitStatement, visitExpression;\n\nfunction visitAst(ast, previsit, postvisit, data)\n\tast.isAst = true;\n\tdata = data or {};\n\tdata.scopeStack = {};\n\tdata.functionData = {\n\t\tdepth = 0;\n\t\tscope = ast.body.scope;\n\t\tnode = ast;\n\t};\n\tdata.scope = ast.globalScope;\n\tdata.globalScope = ast.globalScope;\n\tif(type(previsit) == \"function\") then\n\t\tlocal node, skip = previsit(ast, data);\n\t\tast = node or ast;\n\t\tif skip then\n\t\t\treturn ast;\n\t\tend\n\tend\n\n\t-- Is Function Block because global scope is treated like a Function\n\tvisitBlock(ast.body, previsit, postvisit, data, true);\n\n\tif(type(postvisit) == \"function\") then\n\t\tast = postvisit(ast, data) or ast;\n\tend\n\treturn ast;\nend\n\nlocal compundStats = lookupify{\n\tAstKind.CompoundAddStatement,\n\tAstKind.CompoundSubStatement,\n\tAstKind.CompoundMulStatement,\n\tAstKind.CompoundDivStatement,\n\tAstKind.CompoundModStatement,\n\tAstKind.CompoundPowStatement,\n\tAstKind.CompoundConcatStatement,\n}\n\nfunction visitBlock(block, previsit, postvisit, data, isFunctionBlock)\n\tblock.isBlock = true;\n\tblock.isFunctionBlock = isFunctionBlock or false;\n\tdata.scope = block.scope;\n\tlocal parentBlockData = data.blockData;\n\tdata.blockData = {};\n\ttable.insert(data.scopeStack, block.scope);\n\tif(type(previsit) == \"function\") then\n\t\tlocal node, skip = previsit(block, data);\n\t\tblock = node or block;\n\t\tif skip then\n\t\t\tdata.scope = table.remove(data.scopeStack);\n\t\t\treturn block\n\t\tend\n\tend\n\n\tlocal i = 1;\n\twhile i <= #block.statements do\n\t\tlocal statement = table.remove(block.statements, i);\n\t\ti = i - 1;\n\t\tlocal returnedStatements = {visitStatement(statement, previsit, postvisit, data)};\n\t\tfor j, statement in ipairs(returnedStatements) do\n\t\t\ti = i + 1;\n\t\t\ttable.insert(block.statements, i, statement);\n\t\tend\n\t\ti = i + 1;\n\tend\n\n\tif(type(postvisit) == \"function\") then\n\t\tblock = postvisit(block, data) or block;\n\tend\n\tdata.scope = table.remove(data.scopeStack);\n\tdata.blockData = parentBlockData;\n\treturn block;\nend\n\nfunction visitStatement(statement, previsit, postvisit, data)\n\tstatement.isStatement = true;\n\tif(type(previsit) == \"function\") then\n\t\tlocal node, skip = previsit(statement, data);\n\t\tstatement = node or statement;\n\t\tif skip then\n\t\t\treturn statement;\n\t\tend\n\tend\n\n\t-- Visit Child Nodes of Statement\n\tif(statement.kind == AstKind.ReturnStatement) then\n\t\tfor i, expression in ipairs(statement.args) do\n\t\t\tstatement.args[i] = visitExpression(expression, previsit, postvisit, data);\n\t\tend\n\telseif(statement.kind == AstKind.PassSelfFunctionCallStatement or statement.kind == AstKind.FunctionCallStatement) then\n\t\tstatement.base = visitExpression(statement.base, previsit, postvisit, data);\n\t\tfor i, expression in ipairs(statement.args) do\n\t\t\tstatement.args[i] = visitExpression(expression, previsit, postvisit, data);\n\t\tend\n\telseif(statement.kind == AstKind.AssignmentStatement) then\n\t\tfor i, primaryExpr in ipairs(statement.lhs) do\n\t\t\tstatement.lhs[i] = visitExpression(primaryExpr, previsit, postvisit, data);\n\t\tend\n\t\tfor i, expression in ipairs(statement.rhs) do\n\t\t\tstatement.rhs[i] = visitExpression(expression, previsit, postvisit, data);\n\t\tend\n\telseif(statement.kind == AstKind.FunctionDeclaration or statement.kind == AstKind.LocalFunctionDeclaration) then\n\t\tlocal parentFunctionData = data.functionData;\n\t\tdata.functionData = {\n\t\t\tdepth = parentFunctionData.depth + 1;\n\t\t\tscope = statement.body.scope;\n\t\t\tnode = statement;\n\t\t};\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data, true);\n\t\tdata.functionData = parentFunctionData;\n\telseif(statement.kind == AstKind.DoStatement) then\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data, false);\n\telseif(statement.kind == AstKind.WhileStatement) then\n\t\tstatement.condition = visitExpression(statement.condition, previsit, postvisit, data);\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data, false);\n\telseif(statement.kind == AstKind.RepeatStatement) then\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data);\n\t\tstatement.condition = visitExpression(statement.condition, previsit, postvisit, data);\n\telseif(statement.kind == AstKind.ForStatement) then\n\t\tstatement.initialValue = visitExpression(statement.initialValue, previsit, postvisit, data);\n\t\tstatement.finalValue = visitExpression(statement.finalValue, previsit, postvisit, data);\n\t\tstatement.incrementBy = visitExpression(statement.incrementBy, previsit, postvisit, data);\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data, false);\n\telseif(statement.kind == AstKind.ForInStatement) then\n\t\tfor i, expression in ipairs(statement.expressions) do\n\t\t\tstatement.expressions[i] = visitExpression(expression, previsit, postvisit, data);\n\t\tend\n\t\tvisitBlock(statement.body, previsit, postvisit, data, false);\n\telseif(statement.kind == AstKind.IfStatement) then\n\t\tstatement.condition = visitExpression(statement.condition, previsit, postvisit, data);\n\t\tstatement.body = visitBlock(statement.body, previsit, postvisit, data, false);\n\t\tfor i, eif in ipairs(statement.elseifs) do\n\t\t\teif.condition = visitExpression(eif.condition, previsit, postvisit, data);\n\t\t\teif.body = visitBlock(eif.body, previsit, postvisit, data, false);\n\t\tend\n\t\tif(statement.elsebody) then\n\t\t\tstatement.elsebody = visitBlock(statement.elsebody, previsit, postvisit, data, false);\n\t\tend\n\telseif(statement.kind == AstKind.LocalVariableDeclaration) then\n\t\tfor i, expression in ipairs(statement.expressions) do\n\t\t\tstatement.expressions[i] = visitExpression(expression, previsit, postvisit, data);\n\t\tend\n\telseif compundStats[statement.kind] then\n\t\tstatement.lhs = visitExpression(statement.lhs, previsit, postvisit, data);\n\t\tstatement.rhs = visitExpression(statement.rhs, previsit, postvisit, data);\n\tend\n\n\tif(type(postvisit) == \"function\") then\n\t\tlocal statements = {postvisit(statement, data)};\n\t\tif #statements > 0 then\n\t\t\treturn unpack(statements);\n\t\tend\n\tend\n\n\treturn statement;\nend\n\nlocal binaryExpressions = lookupify{\n\tAstKind.OrExpression,\n\tAstKind.AndExpression,\n\tAstKind.LessThanExpression,\n\tAstKind.GreaterThanExpression,\n\tAstKind.LessThanOrEqualsExpression,\n\tAstKind.GreaterThanOrEqualsExpression,\n\tAstKind.NotEqualsExpression,\n\tAstKind.EqualsExpression,\n\tAstKind.StrCatExpression,\n\tAstKind.AddExpression,\n\tAstKind.SubExpression,\n\tAstKind.MulExpression,\n\tAstKind.DivExpression,\n\tAstKind.ModExpression,\n\tAstKind.PowExpression,\n}\nfunction visitExpression(expression, previsit, postvisit, data)\n\texpression.isExpression = true;\n\tif(type(previsit) == \"function\") then\n\t\tlocal node, skip = previsit(expression, data);\n\t\texpression = node or expression;\n\t\tif skip then\n\t\t\treturn expression;\n\t\tend\n\tend\n\n\tif(binaryExpressions[expression.kind]) then\n\t\texpression.lhs = visitExpression(expression.lhs, previsit, postvisit, data);\n\t\texpression.rhs = visitExpression(expression.rhs, previsit, postvisit, data);\n\tend\n\n\tif(expression.kind == AstKind.NotExpression or expression.kind == AstKind.NegateExpression or expression.kind == AstKind.LenExpression) then\n\t\texpression.rhs = visitExpression(expression.rhs, previsit, postvisit, data);\n\tend\n\n\tif(expression.kind == AstKind.PassSelfFunctionCallExpression or expression.kind == AstKind.FunctionCallExpression) then\n\t\texpression.base = visitExpression(expression.base, previsit, postvisit, data);\n\t\tfor i, arg in ipairs(expression.args) do\n\t\t\texpression.args[i] = visitExpression(arg, previsit, postvisit, data);\n\t\tend\n\tend\n\n\tif(expression.kind == AstKind.FunctionLiteralExpression) then\n\t\tlocal parentFunctionData = data.functionData;\n\t\tdata.functionData = {\n\t\t\tdepth = parentFunctionData.depth + 1;\n\t\t\tscope = expression.body.scope;\n\t\t\tnode = expression;\n\t\t};\n\t\texpression.body = visitBlock(expression.body, previsit, postvisit, data, true);\n\t\tdata.functionData = parentFunctionData;\n\tend\n\n\tif(expression.kind == AstKind.TableConstructorExpression) then\n\t\tfor i, entry in ipairs(expression.entries) do\n\t\t\tif entry.kind == AstKind.KeyedTableEntry then\n\t\t\t\tentry.key = visitExpression(entry.key, previsit, postvisit, data);\n\t\t\tend\n\t\t\tentry.value = visitExpression(entry.value, previsit, postvisit, data);\n\t\tend\n\tend\n\n\tif(expression.kind == AstKind.IndexExpression or expression.kind == AstKind.AssignmentIndexing) then\n\t\texpression.base = visitExpression(expression.base, previsit, postvisit, data);\n\t\texpression.index = visitExpression(expression.index, previsit, postvisit, data);\n\tend\n\tif(expression.kind == AstKind.IfElseExpression) then\n\t\texpression.condition = visitExpression(expression.condition, previsit, postvisit, data);\n\t\texpression.true_expr = visitExpression(expression.true_expr, previsit, postvisit, data);\n\t\texpression.false_expr = visitExpression(expression.false_expr, previsit, postvisit, data);\n\tend\n\n\tif(type(postvisit) == \"function\") then\n\t\texpression = postvisit(expression, data) or expression;\n\tend\n\treturn expression;\nend\n\nreturn visitAst;\n"
  },
  {
    "path": "src/prometheus.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- prometheus.lua\n--\n-- This file is the entrypoint for Prometheus\n\n-- Configure package.path for require\nlocal function script_path()\n\tlocal str = debug.getinfo(2, \"S\").source:sub(2)\n\treturn str:match(\"(.*[/%\\\\])\")\nend\n\nlocal oldPkgPath = package.path;\npackage.path = script_path() .. \"?.lua;\" .. package.path;\n\n-- Math.random Fix for Lua5.1\n-- Check if fix is needed\nif not pcall(function()\n    return math.random(1, 2^40);\nend) then\n    local oldMathRandom = math.random;\n    math.random = function(a, b)\n        if not a and b then\n            return oldMathRandom();\n        end\n        if not b then\n            return math.random(1, a);\n        end\n        if a > b then\n            a, b = b, a;\n        end\n        local diff = b - a;\n        assert(diff >= 0);\n        if diff > 2 ^ 31 - 1 then\n            return math.floor(oldMathRandom() * diff + a);\n        else\n            return oldMathRandom(a, b);\n        end\n    end\nend\n\n-- newproxy polyfill\n_G.newproxy = _G.newproxy or function(arg)\n    if arg then\n        return setmetatable({}, {});\n    end\n    return {};\nend\n\n\n-- Require Prometheus Submodules\nlocal Pipeline = require(\"prometheus.pipeline\");\nlocal highlight = require(\"highlightlua\");\nlocal colors = require(\"colors\");\nlocal Logger = require(\"logger\");\nlocal Presets = require(\"presets\");\nlocal Config = require(\"config\");\nlocal util = require(\"prometheus.util\");\n\n-- Restore package.path\npackage.path = oldPkgPath;\n\n-- Export\nreturn {\n    Pipeline = Pipeline;\n    colors = colors;\n    Config = util.readonly(Config); -- Readonly\n    Logger = Logger;\n    highlight = highlight;\n    Presets = Presets;\n}\n\n"
  },
  {
    "path": "tests/ambiguous-call.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- ambiguous-call.lua\n--\n-- This Test demonstrates a reproduction for issue #203 where the parser misreads the following as arguments to the previous literal assignment.\nlocal counter = 1\n\n(function()\n\tcounter = counter + 1\n\tprint(\"counter:\", counter)\nend)();\n"
  },
  {
    "path": "tests/closures.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- closures.lua\n--\n-- This Test demonstrates deterministic closure behavior.\n\nlocal arr = {}\nfor i = 1, 100 do\n\tlocal x;\n\tx = (x or 1) + i;\n\tarr[i] = function()\n\t\treturn x;\n\tend\nend\n\nfor _, func in ipairs(arr) do\n\tprint(func())\nend"
  },
  {
    "path": "tests/coroutines.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- coroutines.lua\n--\n-- This Test demonstrates a deterministic coroutine driven sequence generator.\n\nlocal function squares(limit)\n    return coroutine.create(function()\n        for i = 1, limit do\n            coroutine.yield(i * i)\n        end\n    end)\nend\n\nlocal co = squares(6)\nwhile true do\n    local ok, value = coroutine.resume(co)\n    if not ok then\n        error(value)\n    end\n    if value == nil then\n        break\n    end\n    print(value)\nend\n"
  },
  {
    "path": "tests/fibonacci.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- fibonacci.lua\n--\n-- This Test demonstrates a simple fibonacci sequence.\n\nlocal function fibonacci(max)\n    local a, b = 0, 1\n    while a < max do\n        print(a)\n        a, b = b, a + b\n    end\nend\n\nfibonacci(1000)"
  },
  {
    "path": "tests/iterator.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- iterator.lua\n--\n-- This Test demonstrates a custom iterator that creates a predictable countdown.\n\nlocal function countdown(startValue, step)\n    local value = startValue + step\n    return function()\n        value = value - step\n        if value <= 0 then\n            return nil\n        end\n        return value\n    end\nend\n\nfor num in countdown(12, 3) do\n    print(num)\nend\n"
  },
  {
    "path": "tests/loops.lua",
    "content": "--============================================================\n-- Iteration Test Suite\n-- Target: General purpose\n-- Author: SpinnySpiwal\n-- Purpose: Validate functionality of iterative loops.\n-- Note: this test was rewritten after discovering yet another bug in the compiler.\n-- The bug was with negative for statements not functioning. And it slipped past the tests.\n-- This test ensures this won't happen again.\n--============================================================\n\nlocal TESTS_PASSED = 0\nlocal byte, floor = string.byte, math.floor\nlocal x, y, z, w, t, expected = nil, nil, nil, nil, nil, nil\nlocal function round(value, precision)\n    return floor(value * 10^precision) / 10^precision\nend\n\nlocal function str2num(str)\n    local result = 0\n    for i=1, #str, 1 do\n        result = result + byte(str, i)\n    end\n    return result\nend\n--============================================================\n-- Test 1: Ascending for loop (integer)\n--============================================================\ny = 0\nfor _=1, 100 do\n    y = y + 1\nend\n\nif y ~= 100 then\n    print(\"TEST 1: Ascending for loop (integer) FAILED! Expected 100, got \" .. y)\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\n--============================================================\n-- Test 2: Descending for loop (integer)\n--============================================================\nz = 0\nfor _=100, 1, -1 do\n    z = z + 1\nend\n\nif z ~= 100 then\n    print(\"TEST 2: Descending for loop (integer) FAILED! Expected 100, got \" .. z)\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\n--============================================================\n-- Test 3: Ascending for loop (float)\n--============================================================\nw = 0\nfor _=0, 100, 0.1 do\n    w = w + 0.1\nend\n\nif round(w, 1) ~= 100 then\n    print(\"TEST 3: Ascending for loop (float) FAILED! Expected 100, got \" .. round(w, 1))\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\n--============================================================\n-- Test 4: Descending for loop (float)\n--============================================================\nw = 0\nfor _=100, 0, -0.1 do\n    w = w + 0.1\nend\n\nif round(w, 1) ~= 100 then\n    print(\"TEST 4: Descending for loop (float) FAILED! Expected 100, got \" .. round(w, 1))\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\n--============================================================\n-- Test 5: Table iteration (ipairs)\n--============================================================\nt = {1, 2, 3, 4, 5}\nx = 0\nexpected = 20\nfor _,v in ipairs(t) do\n    x = x + (1+v)\nend\n\nif x ~= expected then\n    print(\"TEST 5: Table iteration (ipairs) FAILED! Expected 5, got \" .. x)\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\n--============================================================\n-- Test 6: Table iteration (pairs)\n-- Note: the test is written this way because the pairs function is not sequential.\n-- However, numbers when added together are always the same.\n--============================================================\nexpected = 750\nt = {a = 1, b = 2, c = 3, d = 4, e = 5}\ny = 0\n\nfor k, v in pairs(t) do\n    y = y + str2num(k .. v)\nend\n\nif y ~= expected then\n    print(\"TEST 6: Table iteration (pairs) FAILED! Expected \" .. expected .. \", got \" .. y)\nelse\n    TESTS_PASSED = TESTS_PASSED + 1\nend\n\nprint(\"TESTS PASSED: \" .. TESTS_PASSED .. \"/6\")"
  },
  {
    "path": "tests/matrix.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- matrix.lua\n--\n-- This Test demonstrates a deterministic 2x2 matrix multiplication example.\n\nlocal function multiply(a, b)\n    local result = {\n        { a[1][1] * b[1][1] + a[1][2] * b[2][1], a[1][1] * b[1][2] + a[1][2] * b[2][2] },\n        { a[2][1] * b[1][1] + a[2][2] * b[2][1], a[2][1] * b[1][2] + a[2][2] * b[2][2] }\n    }\n    return result\nend\n\nlocal A = {\n    {1, 2},\n    {3, 4}\n}\n\nlocal B = {\n    {5, 6},\n    {7, 8}\n}\n\nlocal C = multiply(A, B)\nfor row = 1, 2 do\n    print(string.format(\"%d,%d\", C[row][1], C[row][2]))\nend\n"
  },
  {
    "path": "tests/metatables.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- metatables.lua\n--\n-- This Test demonstrates a metamethod driven vector arithmetic.\n\nlocal Vector = {}\nVector.__index = Vector\n\nfunction Vector:new(x, y)\n    return setmetatable({ x = x, y = y }, self)\nend\n\nfunction Vector.__add(a, b)\n    return Vector:new(a.x + b.x, a.y + b.y)\nend\n\nfunction Vector:describe()\n    return string.format(\"(%d,%d)\", self.x, self.y)\nend\n\nlocal path = {\n    Vector:new(2, 3),\n    Vector:new(-1, 4),\n    Vector:new(0, -2)\n}\n\nlocal position = Vector:new(0, 0)\nfor idx, delta in ipairs(path) do\n    position = position + delta\n    print(string.format(\"step%d:%s\", idx, position:describe()))\nend\n"
  },
  {
    "path": "tests/multi-return.lua",
    "content": "--============================================================\n-- Multi-Return Test Suite\n-- Target: Compiler\n-- Author: SpinnySpiwal\n-- Purpose: Ensure multi-return behavior is not adversely affected by the new dynamic emission system.\n--============================================================\n\nlocal function half(number)\n    local divided = number / 2\n    return divided, divided\nend\n\nlocal a, b = half(10)\nassert(a == 5 and b == 5, \"Test 1 failed: basic multi-return\")\nprint(\"Test 1 passed: basic multi-return\", a, b)\n\nlocal function mixedReturn()\n    return 42, \"hello\", true, nil\nend\n\nlocal num, str, bool, nilVal = mixedReturn()\nassert(num == 42 and str == \"hello\" and bool == true and nilVal == nil, \"Test 2 failed: mixed types\")\nprint(\"Test 2 passed: mixed types\", num, str, bool, nilVal)\n\nlocal function threeValues()\n    return 1, 2, 3\nend\n\nlocal first = threeValues()\nassert(first == 1, \"Test 3 failed: discarding extra values\")\nprint(\"Test 3 passed: discarding extra values\", first)\n\nlocal x, y, z, w = threeValues()\nassert(x == 1 and y == 2 and z == 3 and w == nil, \"Test 4 failed: extra variables get nil\")\nprint(\"Test 4 passed: extra variables get nil\", x, y, z, w)\n\nlocal function pair()\n    return \"a\", \"b\"\nend\n\nlocal t1 = { pair() }\nassert(t1[1] == \"a\" and t1[2] == \"b\", \"Test 5 failed: multi-return in table (last)\")\nprint(\"Test 5 passed: multi-return in table (last)\", t1[1], t1[2])\n\nlocal t2 = { pair(), \"c\" }\nassert(t2[1] == \"a\" and t2[2] == \"c\" and t2[3] == nil, \"Test 6 failed: multi-return not last\")\nprint(\"Test 6 passed: multi-return not last\", t2[1], t2[2])\n\nlocal function double(a, b)\n    return a * 2, b * 2\nend\n\nlocal d1, d2 = double(threeValues())\nassert(d1 == 2 and d2 == 4, \"Test 7 failed: nested multi-return\")\nprint(\"Test 7 passed: nested multi-return\", d1, d2)\n\nlocal function fiveValues()\n    return 10, 20, 30, 40, 50\nend\n\nlocal count = select(\"#\", fiveValues())\nassert(count == 5, \"Test 8 failed: select count\")\nprint(\"Test 8 passed: select count\", count)\n\nlocal fourth = select(4, fiveValues())\nassert(fourth == 40, \"Test 9 failed: select specific\")\nprint(\"Test 9 passed: select specific\", fourth)\n\nlocal function varargReturn(...)\n    return ...\nend\n\nlocal v1, v2, v3 = varargReturn(100, 200, 300)\nassert(v1 == 100 and v2 == 200 and v3 == 300, \"Test 10 failed: vararg return\")\nprint(\"Test 10 passed: vararg return\", v1, v2, v3)\n\nlocal function sum(a, b, c)\n    return (a or 0) + (b or 0) + (c or 0)\nend\n\nlocal result = sum(threeValues())\nassert(result == 6, \"Test 11 failed: multi-return as arguments\")\nprint(\"Test 11 passed: multi-return as arguments\", result)\n\nprint(\"All multi-return tests passed!\")"
  },
  {
    "path": "tests/primes.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- primes.lua\n--\n-- This Test demonstrates a deterministic prime number generator.\n\nlocal function primes(n)\n    local function isPrime(n)\n        for i = 2, math.sqrt(n) do\n            if n % i == 0 then\n                return false\n            end\n        end\n        return true\n    end\n    for i = 2, n do\n        if isPrime(i) then\n            print(i)\n        end\n    end\nend\n\nprimes(20)"
  },
  {
    "path": "tests/repeat-test.lua",
    "content": "--============================================================\n-- Repeat-Until Semantics Test Suite\n-- Target: Vmify\n-- Author: Zaenalos\n-- Purpose: Validate correct scope, control flow, and condition\n--============================================================\n\nlocal TEST_ID = 0\n\nlocal function test(name, fn)\n  TEST_ID = TEST_ID + 1\n  local ok, err = pcall(fn)\n  if not ok then\n    error(string.format(\n      \"[FAIL] #%d %s\\n  → %s\",\n      TEST_ID, name, err\n    ), 2)\n  end\n  print(string.format(\"[PASS] #%d %s\", TEST_ID, name))\nend\n\n--============================================================\n-- Test 1: Basic repeat-until with local in condition scope\n--============================================================\ntest(\"Basic local visibility in until condition\", function()\n  local count = 0\n  repeat\n    local x = count\n    count = count + 1\n  until x == 5\n\n  assert(count == 6, \"count should be 6\")\nend)\n\n--============================================================\n-- Test 2: Locals do not leak outside repeat scope\n--============================================================\ntest(\"Repeat locals do not escape scope\", function()\n  repeat\n    local hidden = 123\n  until true\n\n  assert(_G.hidden == nil, \"local leaked into global scope\")\nend)\n\n--============================================================\n-- Test 3: Immediate exit still executes body once\n--============================================================\ntest(\"Immediate termination executes once\", function()\n  local iters = 0\n  repeat\n    iters = iters + 1\n  until true\n\n  assert(iters == 1, \"repeat body must run exactly once\")\nend)\n\n--============================================================\n-- Test 4: Multiple locals and arithmetic correctness\n--============================================================\ntest(\"Multiple locals and arithmetic\", function()\n  local i = 0\n  local c\n  repeat\n    local a = i\n    local b = a * 2\n    c = a + b\n    i = i + 1\n  until c >= 15\n\n  assert(i == 6, \"i should be 6 when c reaches 15\")\nend)\n\n--============================================================\n-- Test 5: Nested repeat-until with independent scopes\n--============================================================\ntest(\"Nested repeat loops\", function()\n  local outer = 0\n  local total_inner = 0\n\n  repeat\n    local inner = 0\n    repeat\n      total_inner = total_inner + 1\n      inner = inner + 1\n    until inner == 3\n    outer = outer + 1\n  until outer == 3\n\n  assert(outer == 3, \"outer loop count mismatch\")\n  assert(total_inner == 9, \"inner loop count mismatch\")\nend)\n\n--============================================================\n-- Test 6: Function call inside until condition\n--============================================================\ntest(\"Function call in until condition\", function()\n  local function check(x)\n    return x >= 3\n  end\n\n  local k = 0\n  local current\n  repeat\n    current = k\n    k = k + 1\n  until check(current)\n\n  assert(k == 4, \"termination point incorrect\")\nend)\n\n--============================================================\n-- Test 7: Upvalue capture inside repeat\n--============================================================\ntest(\"Upvalue capture from repeat body\", function()\n  local f\n  repeat\n    local x = 42\n    f = function()\n      return x\n    end\n  until true\n\n  assert(f() == 42, \"upvalue incorrectly captured\")\nend)\n\n--============================================================\n-- Test 8: Side effects inside until condition (corrected)\n--============================================================\ntest(\"Side effects in until condition\", function()\n  local log = {}\n  local i = 0\n\n  repeat\n    i = i + 1\n  until (function()\n    log[#log + 1] = i   -- explicit side effect\n    return i >= 3\n  end)()\n\n  assert(#log == 3, \"side effects count mismatch\")\n  assert(log[3] == 3, \"final side effect value incorrect\")\nend)\n\n--============================================================\n-- Test 9: Break skips until condition\n--============================================================\ntest(\"Break bypasses until evaluation\", function()\n  local evaluated = false\n\n  repeat\n    break\n  until (function()\n    evaluated = true\n    return true\n  end)()\n\n  assert(evaluated == false, \"`until` condition evaluated after break\")\nend)\n\n--============================================================\n-- Test 10: Slot reuse and shadowing correctness\n--============================================================\ntest(\"Local shadowing and slot reuse\", function()\n  local sum = 0\n  local i = 0\n\n  repeat\n    local v = i\n    sum = sum + v\n    do\n      local v = v * 2\n      sum = sum + v\n    end\n    i = i + 1\n  until i == 3\n\n  assert(sum == (0+0) + (1+2) + (2+4), \"slot corruption detected\")\nend)\n\n--============================================================\n-- Test 11: Table mutation inside repeat\n--============================================================\ntest(\"Table writes inside repeat\", function()\n  local t = {}\n  local i = 1\n\n  repeat\n    t[i] = i * i\n    i = i + 1\n  until i > 5\n\n  assert(#t == 5, \"table size incorrect\")\n  assert(t[5] == 25, \"table content incorrect\")\nend)\n\n--============================================================\n-- Test 12: Deterministic non-linear termination\n--============================================================\ntest(\"Non-linear termination logic\", function()\n  local x = 1\n  repeat\n    x = (x * 3 + 1) % 17\n  until x == 0\n\n  assert(x == 0, \"non-linear termination failed\")\nend)\n\n--============================================================\nprint(\"\\nAll repeat-until tests passed successfully.\")\n"
  },
  {
    "path": "tests/state-machine.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- state-machine.lua\n--\n-- This Test demonstrates a simple deterministic finite-state machine.\n\nlocal transitions = {\n    idle = { start = \"running\" },\n    running = { pause = \"paused\", stop = \"stopped\" },\n    paused = { resume = \"running\", stop = \"stopped\" }\n}\n\nlocal steps = {\n    { event = \"start\", expect = \"running\" },\n    { event = \"pause\", expect = \"paused\" },\n    { event = \"resume\", expect = \"running\" },\n    { event = \"stop\", expect = \"stopped\" }\n}\n\nlocal state = \"idle\"\nfor idx, step in ipairs(steps) do\n    local rule = transitions[state]\n    state = rule and rule[step.event]\n    assert(state == step.expect, string.format(\"bad transition at %d\", idx))\n    print(string.format(\"%d:%s->%s\", idx, step.event, state))\nend\n\nprint(\"final:\" .. state)\n"
  },
  {
    "path": "tests/strings.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- strings.lua\n--\n-- This Test demonstrates a deterministic text statistics for repeated words and prints the results.\n\nlocal passage = \"lorem ipsum dolor sit amet ipsum lorem\"\nlocal counts = {}\n\nfor word in passage:gmatch(\"%w+\") do\n    counts[word] = (counts[word] or 0) + 1\nend\n\nlocal order = {\"lorem\", \"ipsum\", \"dolor\", \"sit\", \"amet\"}\nfor _, word in ipairs(order) do\n    print(string.format(\"%s:%d\", word, counts[word] or 0))\nend\n"
  },
  {
    "path": "tests/syntax.lua",
    "content": "--============================================================\n-- Syntax Test Suite\n-- Target: Unparser\n-- Author: SpinnySpiwal\n-- Purpose: Validate appropriate parser & unparser functionality, specifically in unseen edge cases.\n-- Update 1: Added test for precedence bug fix in expressionPow.\n--============================================================\n\nlocal char = (\"\").char\nprint(char == string.char and \"yes\" or \"no\")\nlocal pc, _ = pcall(function()\n    return (0).char\nend)\n\n-- Checks for unparser bug\nprint(pc == false and \"yes\" or \"no\")\nlocal ok = pcall(function(...)\n    print(\"hello \" .. ...)\nend)\nprint(ok and \"no\" or \"yes\")\n\nlocal function getString()\n\treturn \"this string is 24 chars!\"\nend\n\n-- Test for precedence bug fix in expressionPow\nif 2 ^ #getString() == 16777216 then\n\tprint(\"TEST 1 PASSED\")\nelse\n\tprint(\"TEST 1 FAILED\")\nend\n\n-- Check if it still works the other way around\nif (#getString()) ^ 2 == 576 then\n\tprint(\"TEST 2 PASSED\")\nelse\n\tprint(\"TEST 2 FAILED\")\nend"
  },
  {
    "path": "tests/table-merge.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- table-merge.lua\n--\n-- This Test demonstrates a deterministic table merging and traversal.\n\nlocal breakfast = { eggs = 4, bacon = 3 }\nlocal lunch = { bacon = 1, toast = 5 }\n\nlocal function mergeQuantities(a, b)\n    local totals = {}\n    for k, v in pairs(a) do\n        totals[k] = v\n    end\n    for k, v in pairs(b) do\n        totals[k] = (totals[k] or 0) + v\n    end\n    return totals\nend\n\nlocal merged = mergeQuantities(breakfast, lunch)\nlocal order = {\"eggs\", \"bacon\", \"toast\"}\nfor _, item in ipairs(order) do\n    print(string.format(\"%s:%d\", item, merged[item] or 0))\nend\n"
  },
  {
    "path": "tests/upvalues.lua",
    "content": "-- This Test is Part of the Prometheus Obfuscator by Levno_710\n--\n-- upvalues.lua\n--\n-- This Test demonstrates a deterministic tests covering closure upvalues in nested functions and loops.\n\nlocal function emitList(label, list)\n    print(label .. \":\" .. table.concat(list, \",\"))\nend\n\nlocal function makeSeries(tag)\n    local total = 0\n    local function step(delta)\n        total = total + delta\n        return string.format(\"%s-%d\", tag, total)\n    end\n    local function runSeries(values)\n        local out = {}\n        for _, delta in ipairs(values) do\n            out[#out + 1] = step(delta)\n        end\n        return out\n    end\n    return runSeries\nend\n\nlocal alphaSeries = makeSeries(\"alpha\")\nemitList(\"series\", alphaSeries({ 1, 2, 1, 3 }))\n\n-- Verify each for-loop iteration captures its own upvalue\nlocal watchers = {}\nfor i = 1, 4 do\n    watchers[i] = function(mult)\n        return i * mult\n    end\nend\n\nfor idx, fn in ipairs(watchers) do\n    print(string.format(\"watch%d:%d\", idx, fn(idx + 1)))\nend\n\n-- Nested functions sharing a master accumulator through for-loops\nlocal function buildAccumulators()\n    local master = 0\n    local store = {}\n    for group = 1, 3 do\n        local localTotal = group\n        store[group] = function(iterations)\n            for step = 1, iterations do\n                localTotal = localTotal + group + step\n                master = master + group\n            end\n            return localTotal, master\n        end\n    end\n    return store\nend\n\nlocal runners = buildAccumulators()\nfor idx, fn in ipairs(runners) do\n    local value, master = fn(idx)\n    print(string.format(\"acc%d:%d|%d\", idx, value, master))\nend\n"
  },
  {
    "path": "tests.lua",
    "content": "-- This Script is Part of the Prometheus Obfuscator by Levno_710\n--\n-- tests.lua\n--\n-- This Script will Perform tests using all lua files within the tests directory\n\n-- Require Prometheus\nlocal Prometheus = require(\"src.prometheus\")\n\n-- Enable Debugging\n-- logger.logLevel = logger.LogLevel.Debug;\n\n-- Config Variables - Later passed as Parameters\nlocal noColors = false; -- Whether Colors in the Console output should be enabled\nlocal isWindows = package.config:sub(1, 1) == \"\\\\\"; -- Whether the Test are Performed on a Windows or Linux System\nlocal ciMode = false; -- Whether the Test error are ignored or not\nlocal iterationCount = 20; -- How often each test should be executed\n\nfor _, currArg in pairs(arg) do\n\tif currArg == \"--Linux\" then\n\t\tisWindows = false\n\tend\n\tif currArg == \"--Windows\" then\n\t\tisWindows = true\n\tend\n\tif currArg == \"--CI\" then\n\t\tciMode = true\n\tend\n\tlocal iterationValue = currArg:match(\"^%-%-iterations=(%d+)$\")\n\tif iterationValue then\n\t\titerationCount = math.max(tonumber(iterationValue), 1)\n\tend\nend\n\n--  Enable/Disable Console Colors - this may be needed because cmd.exe and powershell.exe do not support ANSI Color Escape Sequences. The Windows Terminal Application is needed\nPrometheus.colors.enabled = not noColors;\n\n-- Apply Obfuscation Pipeline\nlocal pipeline = Prometheus.Pipeline:new({\n\tSeed = 0; -- For Using Time as Seed\n\tVarNamePrefix = \"\"; -- No Custom Prefix\n});\n\n-- \"Mangled\" for names like this : a, b, c, d, ...\n-- \"MangledShuffled\" is the same except the chars come in a different order - Recommended\n-- \"Il\" for weird names like this : IlIIl1llI11l1  - Recommended to make less readable\n-- \"Number\" for names like this : _1, _2, _3, ...  - Not recommended\npipeline:setNameGenerator(\"MangledShuffled\");\n\nlocal function describePlatform()\n\treturn isWindows and \"Windows\" or \"Linux\"\nend\n\nprint(string.format(\n\t\"Performing Prometheus Tests (iterations=%d per file/preset, platform=%s)...\",\n\titerationCount,\n\tdescribePlatform()\n))\nlocal function scandir(directory)\n    local i, t, popen = 0, {}, io.popen\n    local pfile = popen(isWindows and 'dir \"'..directory..'\" /b' or 'ls -a \"'..directory..'\"')\n    if not pfile then\n    \terror(\"Failed to list files in test directory: \" .. tostring(directory))\n    end\n    for filename in pfile:lines() do\n\t\tif string.sub(filename, -4) == \".lua\" then\n\t\t\ti = i + 1\n\t\t\tt[i] = filename\n\t\tend\n    end\n    pfile:close()\n    return t\nend\n\nlocal function shallowcopy(orig)\n    local orig_type = type(orig)\n    local copy\n    if orig_type == 'table' then\n        copy = {}\n        for orig_key, orig_value in pairs(orig) do\n            copy[orig_key] = orig_value\n        end\n    else -- number, string, boolean, etc\n        copy = orig\n    end\n    return copy\nend\n\nlocal function validate(a, b)\n\tlocal outa = \"\";\n\tlocal outb = \"\";\n\n\tlocal enva = shallowcopy(getfenv(a));\n\tlocal envb = shallowcopy(getfenv(a));\n\n\tenva.print = function(...)\n\t\tfor _, v in ipairs({...}) do\n\t\t\touta = outa .. tostring(v);\n\t\tend\n\tend\n\n\tenvb.print = function(...)\n\t\tfor _, v in ipairs({...}) do\n\t\t\toutb = outb .. tostring(v);\n\t\tend\n\tend\n\n\tsetfenv(a, enva);\n\tsetfenv(b, envb);\n\n\tif(not pcall(a)) then error(\"Expected Reference Program not to Fail!\") end\n\tif(not pcall(b)) then return false, outa, nil end\n\n\treturn outa == outb, outa, outb\nend\n\n\nlocal presets = Prometheus.Presets;\nlocal testdir = \"./tests/\"\nPrometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Error;\nlocal fc = 0;\nfor _, filename in ipairs(scandir(testdir)) do\n\tlocal path = testdir .. filename;\n\tlocal file = io.open(path,\"r\");\n\n\tlocal code = file:read(\"*a\");\n\tprint(Prometheus.colors(\"[CURRENT] \", \"magenta\") .. filename);\n\tfor name, preset in pairs(presets) do\n\t\tfor i = #preset.Steps, 1, -1 do\n\t\t\tif preset.Steps[i].Name == \"AntiTamper\" then\n\t\t\t\ttable.remove(preset.Steps, i);\n\t\t\tend\n\t\tend\n\n\t\tfor _ = 1, iterationCount do\n\t\t\tpipeline = Prometheus.Pipeline:fromConfig(preset);\n\t\t\tlocal obfuscated = pipeline:apply(code);\n\n\t\t\tlocal funca = loadstring(code);\n\t\t\tlocal funcb = loadstring(obfuscated);\n\n\t\t\tif funcb == nil then\n\t\t\t\tprint(Prometheus.colors(\"[FAILED]  \", \"red\") .. \"(\" .. filename .. \"): \" .. name .. \", Invalid Lua!\");\n\t\t\t\tprint(\"[SOURCE]\", obfuscated);\n\t\t\t\tfc = fc + 1;\n\t\t\telse\n\t\t\t\tlocal validated, outa, outb = validate(funca, funcb);\n\n\t\t\t\tif not validated then\n\t\t\t\t\tprint(Prometheus.colors(\"[FAILED]  \", \"red\") .. \"(\" .. filename .. \"): \" .. name);\n\t\t\t\t\tprint(\"[OUTA]    \", outa);\n\t\t\t\t\tprint(\"[OUTB]    \", outb);\n\t\t\t\t\tprint(\"[SOURCE]\", obfuscated);\n\t\t\t\t\tfc = fc + 1;\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tfile:close();\nend\n\nif fc < 1 then\n\tprint(Prometheus.colors(\"[PASSED]  \", \"green\") .. \"All tests passed!\");\n\treturn 0;\nelse\n\tprint(Prometheus.colors(\"[FAILED]  \", \"red\") .. \"Some tests failed!\");\n\tif ciMode then\n\t\terror(\"Test Failed!\")\n\tend\n\treturn -1;\nend\n"
  }
]