[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: kishikawakatsumi\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"extends\": [\n    \"config:recommended\"\n  ],\n  \"packageRules\": [\n    {\n      \"matchUpdateTypes\": [\n        \"minor\",\n        \"patch\",\n        \"pin\",\n        \"digest\"\n      ],\n      \"automerge\": true\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [main]\n  schedule:\n    - cron: \"37 13 * * 1\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"javascript\"]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4\n        with:\n          languages: ${{ matrix.language }}\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n          # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v4\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 https://git.io/JvXDl\n\n      # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n      #    and modify them (or add more) to build your code if your project\n      #    uses a compiled language\n\n      #- run: |\n      #   make bootstrap\n      #   make release\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: CI\non:\n  pull_request:\n    branches: [main]\n  workflow_dispatch:\n\nenv:\n  FONTAWESOME_TOKEN: ${{ secrets.FONTAWESOME_TOKEN }}\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Build\n        run: |\n          set -ex\n          docker build --rm --no-cache --build-arg FONTAWESOME_TOKEN=${{ env.FONTAWESOME_TOKEN }} .\n"
  },
  {
    "path": ".gitignore",
    "content": "### Generated by gibo (https://github.com/simonwhitaker/gibo)\n### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Swift.gitignore\n\n# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n## User settings\nxcuserdata/\n\n## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)\n*.xcscmblueprint\n*.xccheckout\n\n## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)\nbuild/\nDerivedData/\n*.moved-aside\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\n\n## Obj-C/Swift specific\n*.hmap\n\n## App packaging\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n## Playgrounds\ntimeline.xctimeline\nplayground.xcworkspace\n\n# Swift Package Manager\n#\n# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.\n# Packages/\n# Package.pins\n# Package.resolved\n# *.xcodeproj\n#\n# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata\n# hence it is not needed unless you have added a package configuration file to your project\n# .swiftpm\n\n.build/\n\n# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# Pods/\n#\n# Add this line if you want to avoid checking in source code from the Xcode workspace\n# *.xcworkspace\n\n# Carthage\n#\n# Add this line if you want to avoid checking in source code from Carthage dependencies.\n# Carthage/Checkouts\n\nCarthage/Build/\n\n# Accio dependency management\nDependencies/\n.accio/\n\n# fastlane\n#\n# It is recommended to not store the screenshots in the git repo.\n# Instead, use fastlane to re-generate the screenshots whenever they are needed.\n# For more information about the recommended setup visit:\n# https://docs.fastlane.tools/best-practices/source-control/#source-control\n\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots/**/*.png\nfastlane/test_output\n\n# Code Injection\n#\n# After new code Injection tools there's a generated folder /iOSInjectionProject\n# https://github.com/johnno1962/injectionforxcode\n\niOSInjectionProject/\n\n\n### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Node.gitignore\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n.temp\n.cache\n\n# Docusaurus cache and generated files\n.docusaurus\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n\n### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Global/macOS.gitignore\n\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\r\r\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n\n"
  },
  {
    "path": ".swift-format",
    "content": "{\n  \"version\": 1,\n  \"lineLength\": 10000,\n  \"indentation\": {\n    \"spaces\": 2\n  }\n}\n"
  },
  {
    "path": ".swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/App.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1620\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"App\"\n               BuildableName = \"App\"\n               BlueprintName = \"App\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"App\"\n            BuildableName = \"App\"\n            BlueprintName = \"App\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"App\"\n            BuildableName = \"App\"\n            BlueprintName = \"App\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/DSLConverter.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1620\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"DSLConverter\"\n               BuildableName = \"DSLConverter\"\n               BlueprintName = \"DSLConverter\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"DSLConverter\"\n            BuildableName = \"DSLConverter\"\n            BlueprintName = \"DSLConverter\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"DSLConverter\"\n            BuildableName = \"DSLConverter\"\n            BlueprintName = \"DSLConverter\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/ExpressionParser.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1620\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"ExpressionParser\"\n               BuildableName = \"ExpressionParser\"\n               BlueprintName = \"ExpressionParser\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"ExpressionParser\"\n            BuildableName = \"ExpressionParser\"\n            BlueprintName = \"ExpressionParser\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"ExpressionParser\"\n            BuildableName = \"ExpressionParser\"\n            BlueprintName = \"ExpressionParser\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/Matcher.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1620\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"Matcher\"\n               BuildableName = \"Matcher\"\n               BlueprintName = \"Matcher\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"Matcher\"\n            BuildableName = \"Matcher\"\n            BlueprintName = \"Matcher\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"Matcher\"\n            BuildableName = \"Matcher\"\n            BlueprintName = \"Matcher\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/swiftregex-Package.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1620\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"App\"\n               BuildableName = \"App\"\n               BlueprintName = \"App\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"DSLConverter\"\n               BuildableName = \"DSLConverter\"\n               BlueprintName = \"DSLConverter\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"ExpressionParser\"\n               BuildableName = \"ExpressionParser\"\n               BlueprintName = \"ExpressionParser\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"Matcher\"\n               BuildableName = \"Matcher\"\n               BlueprintName = \"Matcher\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"RegexTests\"\n               BuildableName = \"RegexTests\"\n               BlueprintName = \"RegexTests\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"App\"\n            BuildableName = \"App\"\n            BlueprintName = \"App\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </MacroExpansion>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"Matcher\"\n            BuildableName = \"Matcher\"\n            BlueprintName = \"Matcher\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"configurations\": [\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug DSLParser\",\n      \"program\": \"${workspaceFolder:swiftregex}/.build/debug/DSLParser\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug DSLParser\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Release DSLParser\",\n      \"program\": \"${workspaceFolder:swiftregex}/.build/release/DSLParser\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release DSLParser\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Debug DSLConverter\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug DSLConverter\",\n      \"target\": \"DSLConverter\",\n      \"configuration\": \"debug\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Release DSLConverter\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release DSLConverter\",\n      \"target\": \"DSLConverter\",\n      \"configuration\": \"release\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Debug ExpressionParser\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug ExpressionParser\",\n      \"target\": \"ExpressionParser\",\n      \"configuration\": \"debug\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Release ExpressionParser\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release ExpressionParser\",\n      \"target\": \"ExpressionParser\",\n      \"configuration\": \"release\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Matcher\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug Matcher\",\n      \"target\": \"Matcher\",\n      \"configuration\": \"debug\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"name\": \"Release Matcher\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release Matcher\",\n      \"target\": \"Matcher\",\n      \"configuration\": \"release\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug PatternConverter\",\n      \"program\": \".build/debug/PatternConverter\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug PatternConverter\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Release PatternConverter\",\n      \"program\": \".build/release/PatternConverter\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release PatternConverter\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"sourceLanguages\": [\"swift\"],\n      \"name\": \"Debug App\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Debug App\",\n      \"target\": \"App\",\n      \"configuration\": \"debug\"\n    },\n    {\n      \"type\": \"swift\",\n      \"request\": \"launch\",\n      \"sourceLanguages\": [\"swift\"],\n      \"name\": \"Release App\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"preLaunchTask\": \"swift: Build Release App\",\n      \"target\": \"App\",\n      \"configuration\": \"release\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"name\": \"Debug Debugger\",\n      \"program\": \"${workspaceFolder:swiftregex}/.build/debug/Debugger\",\n      \"preLaunchTask\": \"swift: Build Debug Debugger\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:swiftregex}\",\n      \"name\": \"Release Debugger\",\n      \"program\": \"${workspaceFolder:swiftregex}/.build/release/Debugger\",\n      \"preLaunchTask\": \"swift: Build Release Debugger\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"lldb.library\": \"/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB\",\n  \"lldb.launch.expressions\": \"native\"\n}\n"
  },
  {
    "path": "DEPLOYMENT.md",
    "content": "# Deployment Instructions\n\n## Prerequisites\n\nBefore deploying, make sure you have the following software installed on your machine:\n\n- Node.js (v14 or newer)\n- Docker (v20.10 or newer)\n\nThe following environment variables are used for deployment:\n\n- `FONTAWESOME_TOKEN`: This token is used for authentication with the FontAwesome service. You need to obtain a valid token from your FontAwesome account and use it here. Please make sure not to expose this token publicly.\n\n## Local Deployment\n\n### Steps:\n\n1. Install the dependencies:\n\n```bash\nnpm install\n```\n\n2. Run Webpack to build the project:\n\n```bash\nnpm run prod\n```\n\n3. Run the application:\n\n```bash\nswift run\n```\n\nYou should now be able to see the application running at `localhost:8080`.\n\n## Production Deployment\n\nFor deploying to production, we recommend using [Render](https://render.com/). Render is a platform that allows you to deploy your application to the cloud with ease. It also provides a free tier that is sufficient for deploying this application.\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:lts-slim as node\n\nWORKDIR /build\n\nARG FONTAWESOME_TOKEN\nCOPY package*.json ./\nRUN echo \"@fortawesome:registry=https://npm.fontawesome.com/\\n//npm.fontawesome.com/:_authToken=${FONTAWESOME_TOKEN}\" > ./.npmrc \\\n    && npm ci \\\n    && rm -f ./.npmrc\n\nCOPY webpack.*.js ./\nCOPY Public ./Public/\nRUN npx webpack --config webpack.prod.js\n\n\nFROM swiftlang/swift:nightly-main-jammy as swift\nRUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \\\n    && apt-get -q update && apt-get -q dist-upgrade -y \\\n    && apt-get install -y --no-install-recommends libsqlite3-dev \\\n    && rm -rf /var/lib/apt/lists/*\n\nWORKDIR /build\nCOPY --from=node /build /build\nCOPY ./Package.* ./\nRUN swift package resolve\n\nCOPY . .\nRUN swift build -c release -Xswiftc -DPROCESSOR_MEASUREMENTS_ENABLED -Xswiftc -enable-testing\n\nWORKDIR /staging\n\nRUN BIN_PATH=\"$(swift build --package-path /build -c release --show-bin-path)\" \\\n    && cp \"$BIN_PATH/App\" ./ \\\n    && cp \"$BIN_PATH/DSLConverter\" ./ \\\n    && cp \"$BIN_PATH/ExpressionParser\" ./ \\\n    && cp \"$BIN_PATH/Matcher\" ./ \\\n    && find -L \"$BIN_PATH/\" -regex '.*\\.resources$' -exec cp -Ra {} ./ \\;\nRUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true\nRUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true\n\n\nFROM swiftlang/swift:nightly-main-jammy\nRUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \\\n    && apt-get -q update \\\n    && apt-get -q dist-upgrade -y \\\n    && apt-get -q install -y \\\n      ca-certificates \\\n      tzdata \\\n    && rm -r /var/lib/apt/lists/*\n\nRUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app vapor\n\nWORKDIR /app\nCOPY --from=swift --chown=vapor:vapor /staging /app\n\nUSER vapor:vapor\nEXPOSE 8080\n\nENTRYPOINT [\"./App\"]\nCMD [\"serve\", \"--env\", \"production\", \"--hostname\", \"0.0.0.0\", \"--port\", \"8080\"]\n "
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Kishikawa Katsumi\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\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"pins\" : [\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"3a5b74a58782c3b4c1f0bc75e9b67b10c2494e8f\",\n        \"version\" : \"1.33.1\"\n      }\n    },\n    {\n      \"identity\" : \"async-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/async-kit.git\",\n      \"state\" : {\n        \"revision\" : \"6bbb83cbf9d886623a967a965c8fb1b73e6566f9\",\n        \"version\" : \"1.22.0\"\n      }\n    },\n    {\n      \"identity\" : \"console-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/console-kit.git\",\n      \"state\" : {\n        \"revision\" : \"32ad16dfc7677b927b225595ed18f3debb32f577\",\n        \"version\" : \"4.16.0\"\n      }\n    },\n    {\n      \"identity\" : \"leaf\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/leaf.git\",\n      \"state\" : {\n        \"revision\" : \"b70a6108e4917f338f6b8848407bf655aa7e405f\",\n        \"version\" : \"4.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"leaf-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/leaf-kit.git\",\n      \"state\" : {\n        \"revision\" : \"6044b844caa858a0c5f2505ac166f5a057c990dc\",\n        \"version\" : \"1.14.2\"\n      }\n    },\n    {\n      \"identity\" : \"multipart-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/multipart-kit.git\",\n      \"state\" : {\n        \"revision\" : \"3498e60218e6003894ff95192d756e238c01f44e\",\n        \"version\" : \"4.7.1\"\n      }\n    },\n    {\n      \"identity\" : \"routing-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/routing-kit.git\",\n      \"state\" : {\n        \"revision\" : \"1a10ccea61e4248effd23b6e814999ce7bdf0ee0\",\n        \"version\" : \"4.9.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser\",\n      \"state\" : {\n        \"revision\" : \"626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b\",\n        \"version\" : \"1.7.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"9f542610331815e29cc3821d3b6f488db8715517\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"9d349bcc328ac3c31ce40e746b5882742a0d1272\",\n        \"version\" : \"1.1.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"b601256eab081c0f92f059e12818ac1d4f178ff7\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"24ccdeeeed4dfaae7955fcac9dbf5489ed4f1a25\",\n        \"version\" : \"1.18.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"6675bc0ff86e61436e615df6fc5174e043e57924\",\n        \"version\" : \"1.4.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-configuration\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-configuration.git\",\n      \"state\" : {\n        \"revision\" : \"be76c4ad929eb6c4bcaf3351799f2adf9e6848a9\",\n        \"version\" : \"1.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"bb4ba815dab96d4edc1e0b86d7b9acf9ff973a84\",\n        \"version\" : \"4.3.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-distributed-tracing\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-distributed-tracing.git\",\n      \"state\" : {\n        \"revision\" : \"dc4030184203ffafbb2ec614352487235d747fe0\",\n        \"version\" : \"1.4.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-experimental-string-processing\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-experimental-string-processing.git\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"6a693bb139fa9e4ddf522f80fb7026b3abb0cf1d\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"76d7627bd88b47bf5a0f8497dd244885960dde0b\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"45eb0224913ea070ec4fba17291b9e7ecf4749ca\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"8c0f217f01000dd30f60d6e536569ad4e74291f9\",\n        \"version\" : \"1.11.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-metrics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-metrics.git\",\n      \"state\" : {\n        \"revision\" : \"f17c111cec972c2a4922cef38cf64f76f7e87886\",\n        \"version\" : \"2.8.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"558f24a4647193b5a0e2104031b71c55d31ff83a\",\n        \"version\" : \"2.97.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"abcf5312eb8ed2fb11916078aef7c46b06f20813\",\n        \"version\" : \"1.33.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"6d8d596f0a9bfebb925733003731fe2d749b7e02\",\n        \"version\" : \"1.42.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"df9c3406028e3297246e6e7081977a167318b692\",\n        \"version\" : \"2.36.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-transport-services\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-transport-services.git\",\n      \"state\" : {\n        \"revision\" : \"60c3e187154421171721c1a38e800b390680fb5d\",\n        \"version\" : \"1.26.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"0c0290ff6b24942dadb83a929ffaaa1481df04a2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-context\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-service-context.git\",\n      \"state\" : {\n        \"revision\" : \"d0997351b0c7779017f88e7a93bc30a1878d7f29\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"9829955b385e5bb88128b73f1b8389e9b9c3191a\",\n        \"version\" : \"2.11.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"7c6ad0fc39d0763e0b699210e4124afd5041c5df\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"vapor\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/vapor.git\",\n      \"state\" : {\n        \"revision\" : \"cfd8f434843ac7850e2d97f46c1aa5ddb906cf1c\",\n        \"version\" : \"4.121.4\"\n      }\n    },\n    {\n      \"identity\" : \"websocket-kit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/vapor/websocket-kit.git\",\n      \"state\" : {\n        \"revision\" : \"90bbbdab3ede12c803cfbe91646f291c092517a3\",\n        \"version\" : \"2.16.2\"\n      }\n    }\n  ],\n  \"version\" : 2\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n  name: \"swiftregex\",\n  platforms: [\n    .macOS(.v12)\n  ],\n  dependencies: [\n    .package(url: \"https://github.com/swiftlang/swift-experimental-string-processing.git\", branch: \"main\"),\n    .package(url: \"https://github.com/vapor/vapor.git\", from: \"4.121.4\"),\n    .package(url: \"https://github.com/vapor/leaf.git\", from: \"4.5.1\"),\n  ],\n  targets: [\n    .executableTarget(\n      name: \"DSLConverter\",\n      dependencies: [\n        .product(name: \"_StringProcessing\", package: \"swift-experimental-string-processing\"),\n        .product(name: \"_RegexParser\", package: \"swift-experimental-string-processing\"),\n      ],\n      swiftSettings: [\n        .unsafeFlags([\"-Xfrontend\", \"-disable-availability-checking\"]),\n        .unsafeFlags([\"-cross-module-optimization\"], .when(configuration: .release)),\n      ]\n    ),\n    .executableTarget(\n      name: \"ExpressionParser\",\n      dependencies: [\n        .product(name: \"_StringProcessing\", package: \"swift-experimental-string-processing\"),\n        .product(name: \"_RegexParser\", package: \"swift-experimental-string-processing\"),\n      ],\n      swiftSettings: [\n        .unsafeFlags([\"-Xfrontend\", \"-disable-availability-checking\"]),\n        .unsafeFlags([\"-cross-module-optimization\"], .when(configuration: .release)),\n      ]\n    ),\n    .executableTarget(\n      name: \"Matcher\",\n      dependencies: [\n        .product(name: \"_StringProcessing\", package: \"swift-experimental-string-processing\"),\n        .product(name: \"_RegexParser\", package: \"swift-experimental-string-processing\"),\n      ],\n      swiftSettings: [\n        .unsafeFlags([\"-Xfrontend\", \"-disable-availability-checking\"]),\n        .unsafeFlags([\"-cross-module-optimization\"], .when(configuration: .release)),\n      ]\n    ),\n    .executableTarget(\n      name: \"App\",\n      dependencies: [\n        .product(name: \"_StringProcessing\", package: \"swift-experimental-string-processing\"),\n        .product(name: \"_RegexParser\", package: \"swift-experimental-string-processing\"),\n        .product(name: \"Vapor\", package: \"vapor\"),\n        .product(name: \"Leaf\", package: \"leaf\"),\n      ],\n      swiftSettings: [\n        .unsafeFlags([\"-Xfrontend\", \"-disable-availability-checking\"]),\n        .unsafeFlags([\"-cross-module-optimization\"], .when(configuration: .release)),\n      ]\n    ),\n    .testTarget(\n      name: \"RegexTests\", dependencies: [\n        .target(name: \"DSLConverter\"),\n        .target(name: \"ExpressionParser\"),\n        .target(name: \"Matcher\"),\n        .target(name: \"App\"),\n        .product(name: \"XCTVapor\", package: \"vapor\"),\n      ]\n    )\n  ]\n)\n"
  },
  {
    "path": "Public/css/common.css",
    "content": ".active-tick .checkable::after {\n  font-family: \"Font Awesome 6 Pro\";\n  content: \"\\f00c\";\n  color: #0d6efd;\n  display: none;\n}\n.active-tick svg {\n  color: #0d6efd;\n  float: right;\n  margin-top: 4px;\n  margin-left: 12px;\n}\n.dropdown-item .text-muted {\n  width: 86%;\n  font-size: 80%;\n}\n\n.error-message {\n  font-family: arial;\n  font-size: 90%;\n  background: rgba(255, 0, 0, 0.2);\n}\n\n.CodeMirror * {\n  font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n  max-height: 90vh;\n}\n.CodeMirror-line .cm-space::before,\n.CodeMirror-line .cm-special::before {\n  color: rgba(127, 127, 127, 0.33);\n  content: \"␣\";\n  position: absolute;\n}\n.CodeMirror-line .cm-special::before {\n  color: #d22;\n}\n.CodeMirror-line .cm-tab {\n  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6REZENEEyN0Q3NTc0MTFFNzlFMTZGQ0Q1MEREODEyREEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6REZENEEyN0U3NTc0MTFFNzlFMTZGQ0Q1MEREODEyREEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpERkQ0QTI3Qjc1NzQxMUU3OUUxNkZDRDUwREQ4MTJEQSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpERkQ0QTI3Qzc1NzQxMUU3OUUxNkZDRDUwREQ4MTJEQSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgWEz28AAABVSURBVHja7JVBCgAgCAQt+rf6cstjYN4iFxoQxJMjos3MCJlO4HyB26iqZLXyAswskQTUCmUSI7OruE4usxX9jCKELKK8w04e6Qqdmnfa/8SPmQIMANcZrZCVJGBIAAAAAElFTkSuQmCC);\n  background-position: 100%;\n  background-repeat: no-repeat;\n}\n.editor.multiline .CodeMirror-line:not(:last-child) > span:after {\n  pointer-events: none;\n  color: rgba(127, 127, 127, 0.33);\n  content: \"¬\";\n}\n\n.dropdown-menu {\n  min-width: 200px;\n}\n\n.button-circle {\n  width: 2rem;\n  height: 2rem;\n}\n\n.btn:focus {\n  outline: none;\n  box-shadow: none;\n}\n\n#match-count {\n  font-size: 90%;\n}\n\n#debugger-button {\n  background-color: rgba(51, 51, 51, 0.05);\n  border-radius: 15px;\n  border-width: 1px;\n  border-color: #dee2e6;\n  border-style: solid;\n  color: #333333;\n  cursor: pointer;\n  display: inline-block;\n  list-style: none;\n  margin: 0 6px;\n  padding: 4px 16px;\n  text-align: center;\n  transition: all 200ms;\n  vertical-align: baseline;\n  white-space: nowrap;\n  user-select: none;\n  -webkit-user-select: none;\n  touch-action: manipulation;\n  font-size: 90%;\n  font-weight: 600;\n}\n#debugger-button:hover {\n  background-color: #2222221a;\n}\n#debugger-button:disabled {\n  background-color: #e9ecef;\n  color: #6c757d;\n  cursor: not-allowed;\n}\n\n#debugger-regex {\n  font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n}\n\n@media (min-width: 576px) {\n  .root-container {\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "Public/css/highlight.css",
    "content": ".exp-related {\n  border-bottom: solid 1px rgba(16, 17, 18, 0.3);\n  border-top: solid 1px rgba(16, 17, 18, 0.3);\n  margin-bottom: -1px;\n  margin-top: -1px;\n}\n.exp-related-left {\n  border-left: solid 1px rgba(16, 17, 18, 0.3);\n  margin-left: -1px;\n}\n.exp-related-right {\n  border-right: solid 1px rgba(16, 17, 18, 0.3);\n  margin-right: -1px;\n}\n.exp-selected {\n  border-top: solid 2px rgba(16, 17, 18, 0.3);\n  border-bottom: solid 2px rgba(16, 17, 18, 0.3);\n}\n.exp-selected-left {\n  border-left: solid 2px rgba(16, 17, 18, 0.3);\n  margin-left: -2px;\n}\n.exp-selected-right {\n  border-right: solid 2px rgba(16, 17, 18, 0.3);\n  margin-right: -2px;\n}\n.exp-error {\n  border-bottom: solid 2px #d22;\n}\n.exp-warning {\n  border-bottom: dotted 2px #d22;\n}\n.exp-char {\n  color: #101112;\n}\n.exp-decorator {\n  color: #b7bcc0;\n}\n.exp-esc {\n  color: #c0c;\n}\n.exp-lazy,\n.exp-possessive,\n.exp-quant {\n  color: #58f;\n}\n.exp-alt {\n  color: #0a0;\n}\n.exp-anchor {\n  color: #840;\n}\n.exp-group,\n.exp-lookaround,\n.exp-ref {\n  color: #0a0;\n}\n.exp-charclass,\n.exp-set,\n.exp-subst {\n  color: #d70;\n}\n.exp-group-0 {\n  background: rgba(0, 238, 0, 0.11);\n}\n.exp-group-1 {\n  background: rgba(0, 238, 0, 0.22);\n}\n.exp-group-2 {\n  background: rgba(0, 238, 0, 0.33);\n}\n.exp-group-3 {\n  background: rgba(0, 238, 0, 0.44);\n}\n.exp-group-set {\n  background: rgba(255, 238, 0, 0.3);\n}\n.exp-comment {\n  color: #b7bcc0;\n  background: rgba(16, 17, 18, 0.05);\n  font-style: italic;\n  border-bottom: solid 3px #d7dadc;\n}\n.exp-special {\n  color: #c0c;\n}\n\n.exp-syntax-error {\n  border-top: solid 2px #d22;\n}\n\nspan.match-char {\n  background: rgba(112, 176, 224, 0.5);\n  color: #101112;\n  border-right: solid 1px #ffffff;\n  border-left: solid 1px #ffffff;\n  margin-right: -2px;\n}\nspan.match-left {\n  border-left: solid 2px rgba(255, 0, 0, 0.2);\n  margin-left: -2px;\n}\nspan.match-right {\n  border-right: solid 2px rgba(255, 0, 0, 0.2);\n  margin-right: -2px;\n}\n\nspan.error {\n  color: #d22;\n  font-weight: 700;\n}\nspan.error.warning {\n  color: #d22;\n}\nspan.highlight {\n  background: rgba(255, 128, 0, 0.5);\n  color: #101112;\n}\n\nspan.debuggermatch {\n  background: rgba(112, 176, 224, 0.5);\n  color: #101112;\n  border-right: solid 1px rgba(112, 176, 224, 0.5);\n  border-left: solid 1px rgba(112, 176, 224, 0.5);\n}\n\nspan.debuggerbacktrack {\n  background: rgba(255, 0, 0, 0.2);\n}\n"
  },
  {
    "path": "Public/error.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <!-- Simple HttpErrorPages | MIT License | https://github.com/AndiDittrich/HttpErrorPages -->\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n  <meta name=\"twitter:card\" content=\"summary\" />\n  <meta property=\"twitter:image\" content=\"https://swift-format.com/images/ogp_image.png\" />\n  <meta property=\"og:image\" content=\"https://swift-format.com/images/ogp_image.png\" />\n  <meta property=\"og:title\" content=\"Online Swift code formatter\">\n  <meta property=\"og:description\"\n    content=\"Format a Swift code, making it readable and pretty, with the proper indentation.\" />\n  <meta name=\"description\" content=\"Format a Swift code, making it readable and pretty, with the proper indentation.\" />\n  <meta property=\"og:site_name\" content=\"Swift Formatter - Online Swift code formatter\" />\n\n  <meta name=\"msapplication-TileColor\" content=\"#da532c\">\n  <meta name=\"msapplication-config\" content=\"/favicons/browserconfig.xml\">\n  <meta name=\"theme-color\" content=\"#ffffff\">\n  <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/favicons/apple-touch-icon.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicons/favicon-32x32.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicons/favicon-16x16.png\">\n  <link rel=\"manifest\" href=\"/favicons/site.webmanifest\">\n  <link rel=\"mask-icon\" href=\"/favicons/safari-pinned-tab.svg\" color=\"#5bbad5\">\n  <link rel=\"shortcut icon\" href=\"/favicons/favicon.ico\">\n\n  <title>#(title) | #(status) - #(error)</title>\n  <style type=\"text/css\">\n    /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */\n    html {\n      font-family: sans-serif;\n      line-height: 1.15;\n      -ms-text-size-adjust: 100%;\n      -webkit-text-size-adjust: 100%\n    }\n\n    body {\n      margin: 0\n    }\n\n    article,\n    aside,\n    footer,\n    header,\n    nav,\n    section {\n      display: block\n    }\n\n    h1 {\n      font-size: 2em;\n      margin: .67em 0\n    }\n\n    figcaption,\n    figure,\n    main {\n      display: block\n    }\n\n    figure {\n      margin: 1em 40px\n    }\n\n    hr {\n      box-sizing: content-box;\n      height: 0;\n      overflow: visible\n    }\n\n    pre {\n      font-family: monospace;\n      font-size: 1em\n    }\n\n    a {\n      background-color: transparent;\n      -webkit-text-decoration-skip: objects\n    }\n\n    a:active,\n    a:hover {\n      outline-width: 0\n    }\n\n    abbr[title] {\n      border-bottom: none;\n      text-decoration: underline;\n      text-decoration: underline dotted\n    }\n\n    b,\n    strong {\n      font-weight: inherit\n    }\n\n    b,\n    strong {\n      font-weight: bolder\n    }\n\n    code,\n    kbd,\n    samp {\n      font-family: monospace;\n      font-size: 1em\n    }\n\n    dfn {\n      font-style: italic\n    }\n\n    mark {\n      background-color: #ff0;\n      color: #000\n    }\n\n    small {\n      font-size: 80%\n    }\n\n    sub,\n    sup {\n      font-size: 75%;\n      line-height: 0;\n      position: relative;\n      vertical-align: baseline\n    }\n\n    sub {\n      bottom: -.25em\n    }\n\n    sup {\n      top: -.5em\n    }\n\n    audio,\n    video {\n      display: inline-block\n    }\n\n    audio:not([controls]) {\n      display: none;\n      height: 0\n    }\n\n    img {\n      border-style: none\n    }\n\n    svg:not(:root) {\n      overflow: hidden\n    }\n\n    button,\n    input,\n    optgroup,\n    select,\n    textarea {\n      font-family: sans-serif;\n      font-size: 100%;\n      line-height: 1.15;\n      margin: 0\n    }\n\n    button,\n    input {\n      overflow: visible\n    }\n\n    button,\n    select {\n      text-transform: none\n    }\n\n    [type=reset],\n    [type=submit],\n    button,\n    html [type=button] {\n      -webkit-appearance: button\n    }\n\n    [type=button]::-moz-focus-inner,\n    [type=reset]::-moz-focus-inner,\n    [type=submit]::-moz-focus-inner,\n    button::-moz-focus-inner {\n      border-style: none;\n      padding: 0\n    }\n\n    [type=button]:-moz-focusring,\n    [type=reset]:-moz-focusring,\n    [type=submit]:-moz-focusring,\n    button:-moz-focusring {\n      outline: 1px dotted ButtonText\n    }\n\n    fieldset {\n      border: 1px solid silver;\n      margin: 0 2px;\n      padding: .35em .625em .75em\n    }\n\n    legend {\n      box-sizing: border-box;\n      color: inherit;\n      display: table;\n      max-width: 100%;\n      padding: 0;\n      white-space: normal\n    }\n\n    progress {\n      display: inline-block;\n      vertical-align: baseline\n    }\n\n    textarea {\n      overflow: auto\n    }\n\n    [type=checkbox],\n    [type=radio] {\n      box-sizing: border-box;\n      padding: 0\n    }\n\n    [type=number]::-webkit-inner-spin-button,\n    [type=number]::-webkit-outer-spin-button {\n      height: auto\n    }\n\n    [type=search] {\n      -webkit-appearance: textfield;\n      outline-offset: -2px\n    }\n\n    [type=search]::-webkit-search-cancel-button,\n    [type=search]::-webkit-search-decoration {\n      -webkit-appearance: none\n    }\n\n    ::-webkit-file-upload-button {\n      -webkit-appearance: button;\n      font: inherit\n    }\n\n    details,\n    menu {\n      display: block\n    }\n\n    summary {\n      display: list-item\n    }\n\n    canvas {\n      display: inline-block\n    }\n\n    template {\n      display: none\n    }\n\n    [hidden] {\n      display: none\n    }\n\n    /*! Simple HttpErrorPages | MIT X11 License | https://github.com/AndiDittrich/HttpErrorPages */\n    body,\n    html {\n      width: 100%;\n      height: 100%;\n      background-color: white\n    }\n\n    body {\n      color: black;\n      text-align: center;\n      padding: 0;\n      min-height: 100%;\n      display: table;\n      font-family: \"Open Sans\", Arial, sans-serif\n    }\n\n    h1 {\n      font-family: inherit;\n      font-weight: 500;\n      line-height: 1.1;\n      color: inherit;\n      font-size: 36px\n    }\n\n    h1 small {\n      font-size: 68%;\n      font-weight: 400;\n      line-height: 1;\n      color: #777\n    }\n\n    a {\n      text-decoration: none;\n      color: #fff;\n      font-size: inherit;\n      border-bottom: dotted 1px #707070\n    }\n\n    .lead {\n      color: silver;\n      font-size: 21px;\n      line-height: 1.4\n    }\n\n    .cover {\n      display: table-cell;\n      vertical-align: middle;\n      padding: 0 20px\n    }\n\n    footer {\n      position: fixed;\n      width: 100%;\n      height: 40px;\n      left: 0;\n      bottom: 0;\n      color: #a0a0a0;\n      font-size: 14px\n    }\n  </style>\n</head>\n\n<body>\n  <div class=\"cover\">\n    <h1>#(error) <small>Error #(status)</small></h1>\n    <p class=\"lead\">#(reason)</p>\n  </div>\n</body>\n\n</html>\n"
  },
  {
    "path": "Public/favicons/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo src=\"/mstile-150x150.png\"/>\n            <TileColor>#2d89ef</TileColor>\n        </tile>\n    </msapplication>\n</browserconfig>\n"
  },
  {
    "path": "Public/favicons/site.webmanifest",
    "content": "{\n    \"name\": \"\",\n    \"short_name\": \"\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"/android-chrome-512x512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ],\n    \"theme_color\": \"#ffffff\",\n    \"background_color\": \"#ffffff\",\n    \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "Public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n  <meta name=\"twitter:card\" content=\"summary\" />\n  <meta property=\"twitter:image\" content=\"https://swiftregex.com/images/ogp_image.png\" />\n  <meta property=\"og:image\" content=\"https://swiftregex.com/images/ogp_image.png\" />\n  <meta property=\"og:title\" content=\"Swift Regex: Learn, build and test Swift Regex\">\n  <meta property=\"og:description\"\n    content=\"Regular Expression Tester with highlighting for Swift Regex. Quickly test and debug your regex and Regex Builder.\" />\n  <meta name=\"description\"\n    content=\"Swift Regex is an online tool to learn, build and test Swift Regex Regex and Regex Builder.\" />\n  <meta property=\"og:site_name\" content=\"Swift Regex: Learn, build and test Swift Regex and Regex Builder.\" />\n\n  <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\n  <link rel=\"manifest\" href=\"/site.webmanifest\">\n  <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"#5bbad5\">\n  <meta name=\"msapplication-TileColor\" content=\"#2d89ef\">\n  <meta name=\"theme-color\" content=\"#ffffff\">\n\n  <title>Swift Regex</title>\n</head>\n\n<body class=\"text-dark bg-light vh-100\">\n  <div class=\"container-fluid d-flex flex-column h-100\">\n    <div class=\"row\">\n      <div class=\"col-md-6\">\n        <h1 class=\"h5 my-2\">\n          <a class=\"text-decoration-none\" href=\"/\">\n            <span class=\"fa-brands fa-swift fa-lg text-primary\"></span>\n            <span class=\"ms-2 me-1 text-dark\">Swift Regex</span>\n          </a>\n        </h1>\n      </div>\n    </div>\n\n    <div class=\"root-container\">\n      <div class=\"d-flex flex-column h-100\">\n        <div class=\"row flex-grow-1\">\n          <div class=\"col-md-6 d-flex flex-column\">\n            <div class=\"h6 mt-1 mb-2\">Regular Expression</div>\n            <div class=\"border\">\n              <div id=\"expression-field-container\" class=\"d-flex flex-row-reverse\">\n                <div class=\"btn-group\">\n                  <button class=\"btn btn-link btn-sm dropdown-toggle text-decoration-none bg-white\" type=\"button\"\n                    data-bs-toggle=\"dropdown\" data-bs-auto-close=\"outside\" aria-expanded=\"false\">\n                    <span class=\"fa-solid fa-flag fa-sm\"></span>\n                  </button>\n                  <ul class=\"dropdown-menu\">\n                    <li class=\"match-options-item active-tick\" data-value=\"g\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          <span class=\"fw-bolder\">g</span><span>lobal</span>\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Don't return after first match\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item active-tick\" data-value=\"m\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          <span class=\"fw-bolder\">m</span>ultiline\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          ^ and $ match the start/end of line\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"i\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          case <span class=\"fw-bolder\">i</span>nsensitive\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Case insensitive match\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"s\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          <span class=\"fw-bolder\">s</span>ingle line\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Dot matches newline\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"asciiOnlyWordCharacters\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          ASCII only word\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Match only ASCII characters as word characters\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"asciiOnlyDigits\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          ASCII only digit\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Match only ASCII characters as digits\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"asciiOnlyWhitespace\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          ASCII only space\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Match only ASCII characters as space characters\n                        </div>\n                      </a>\n                    </li>\n                    <li class=\"match-options-item\" data-value=\"asciiOnlyCharacterClasses\">\n                      <a class=\"dropdown-item\" href=\"#\">\n                        <div class=\"checkable\">\n                          ASCII only POSIX properties\n                        </div>\n                        <div class=\"text-wrap text-muted\">\n                          Match only ASCII characters when matching character classes\n                        </div>\n                      </a>\n                    </li>\n                  </ul>\n                </div>\n                <div id=\"expression-field-error\" class=\"bg-white d-none\">\n                  <span class=\"fa-solid fa-octagon-xmark text-danger align-middle p-2\"></span>\n                </div>\n              </div>\n            </div>\n            <div class=\"row justify-content-between\">\n              <div class=\"col\">\n                <div class=\"h6 mt-4 mb-2\">Test String</div>\n              </div>\n              <div class=\"col align-self-center\">\n                <div class=\"text-end mt-2\">\n                  <button id=\"debugger-button\" class=\"px-3\" data-bs-toggle=\"modal\" data-bs-target=\"#debugger-modal\"\n                    disabled>\n                    <div data-bs-toggle=\"tooltip\" data-bs-placement=\"bottom\" data-bs-container=\"#debugger-button\"\n                      title=\"Debugger\" aria-label=\"Debugger\" style=\"display: inline-block;\">\n                      <span class=\"fa-solid fa-stethoscope\"></span>\n                    </div>\n                  </button>\n                  <span id=\"match-count\" class=\"text-bg-light border px-3 py-1\">no match</span>\n                </div>\n              </div>\n            </div>\n            <div class=\"flex-grow-1 border\">\n              <div class=\"test-editor-container editor multiline h-100\"></div>\n            </div>\n          </div>\n          <div class=\"col-md-6 d-flex flex-column\">\n            <div class=\"h6 mt-1 mb-2\">Builder DSL</div>\n            <div class=\"flex-grow-1 border\">\n              <div id=\"dsl-view-container\" class=\"h-100\"></div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"modal fade\" id=\"debugger-modal\" tabindex=\"-1\" aria-labelledby=\"debugger-modal-title\" aria-hidden=\"true\">\n      <div class=\"modal-dialog modal-dialog-scrollable modal-xl\">\n        <div class=\"modal-content\">\n          <div class=\"modal-header py-2\">\n            <h1 class=\"modal-title fs-5\" id=\"debugger-modal-title\">Regex Debugger</h1>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>\n          </div>\n          <div class=\"modal-body\">\n            <div class=\"d-flex\">\n              <div class=\"me-4\" style=\"max-width: 40%;\">\n                <div style=\"font-weight: 600;\">Metrics</div>\n                <div class=\"border my-2\" style=\"font-size: 90%;\">\n                  <table class=\"table table-borderless table-sm my-0\">\n                    <thead>\n                      <tr>\n                        <th>Cycle Count</th>\n                        <th>Resets</th>\n                        <th>Backtracks</th>\n                      </tr>\n                    </thead>\n                    <tbody>\n                      <tr class=\"font-monospace\">\n                        <td id=\"debugger-total-cycle-count\">0</td>\n                        <td id=\"debugger-resets\">0</td>\n                        <td id=\"debugger-backtracks\">0</td>\n                      </tr>\n                    </tbody>\n                  </table>\n                </div>\n\n                <div style=\"overflow: auto;\">\n                  <div class=\"my-2\" style=\"font-weight: 600;\">Instructions</div>\n                  <div class=\"my-2\" style=\"font-size: 90%;\">\n                    <table class=\"border table table-borderless table-sm font-monospace\">\n                      <tbody id=\"debugger-instructions\">\n                      </tbody>\n                    </table>\n                  </div>\n                </div>\n              </div>\n\n              <div class=\"flex-grow-1\">\n                <label for=\"debugger-step-range\" class=\"form-label\" style=\"font-weight: 600;\">Match Steps</label>\n                <div class=\"d-flex\">\n                  <input id=\"debugger-step-range\" type=\"range\" class=\"form-range flex-grow-1\" value=\"1\" min=\"1\"\n                    max=\"100\">\n                  <div class=\"ms-3\">\n                    <span id=\"debugger-step-range-max\" class=\"border px-3 py-1\"\n                      style=\"font-size: 80%; font-weight: 600;\">100</span>\n                  </div>\n                </div>\n                <p class=\"mb-0\" style=\"text-align: center\">\n                  <button id=\"debugger-go-start\" class=\"btn btn-sm btn-outline-secondary rounded-circle button-circle\">\n                    <i class=\"fa-solid fa-backward-step fa-lg\"></i>\n                  </button>\n                  <button id=\"debugger-step-backward\"\n                    class=\"btn btn-sm btn-outline-secondary rounded-circle button-circle\">\n                    <i class=\"fa-solid fa-caret-left fa-lg\"></i>\n                  </button>\n                  <button id=\"debugger-step-forward\"\n                    class=\"btn btn-sm btn-outline-secondary rounded-circle button-circle\">\n                    <i class=\"fa-solid fa-caret-right fa-lg\"></i>\n                  </button>\n                  <button id=\"debugger-go-end\" class=\"btn btn-sm btn-outline-secondary rounded-circle button-circle\">\n                    <i class=\"fa-solid fa-forward-step fa-lg\"></i>\n                  </button>\n                </p>\n\n                <div class=\"my-1\" style=\"font-weight: 600;\">Match Step <span id=\"debugger-match-step\"></span></div>\n                <div class=\"mb-3\">\n                  <input id=\"debugger-regex\" type=\"text\" class=\"form-control form-control-sm border shadow-none\"\n                    style=\"border-radius: 0;\" readonly>\n                </div>\n\n                <div class=\"border my-3\">\n                  <div id=\"debugger-text-container\" class=\"editor multiline\"></div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <footer class=\"row row-cols-1 g-0\">\n      <div class=\"col p-1 text-center\">\n        <div class=\"d-inline-block mx-2\">\n          <a class=\"text-reset text-decoration-none small\" href=\"https://status.swiftregex.com/\" target=\"_blank\"\n            rel=\"nofollow noopener noreferrer\"><span class=\"fa-light fa-monitor-heart-rate\"></span><span\n              class=\"mx-2\">System\n              Status</span></a>\n        </div>\n        <div class=\"d-inline-block mx-2\">\n          <a class=\"text-reset text-decoration-none small\" href=\"https://github.com/swiftfiddle/swiftregex/issues/new\"\n            target=\"_blank\" rel=\"nofollow noopener noreferrer\"><span class=\"fa-regular fa-message-smile\"></span><span\n              class=\"mx-2\">Feedback</span></a>\n        </div>\n        <div class=\"d-inline-block mx-2\">\n          <a class=\"text-reset text-decoration-none small\" href=\"https://github.com/swiftfiddle/swiftregex\"\n            target=\"_blank\" rel=\"nofollow noopener noreferrer\"><span class=\"fa-brands fa-github\"></span><span\n              class=\"mx-2\">Source\n              Code</span></a>\n        </div>\n        <div class=\"d-inline-block mx-2\">\n          <a class=\"text-reset text-decoration-none small\" href=\"https://hachyderm.io/@kishikawakatsumi\" target=\"_blank\"\n            rel=\"nofollow noopener noreferrer\"><span class=\"fa-regular fa-at\"></span><span\n              class=\"mx-2\">Creator</span></a>\n        </div>\n        <div class=\"d-inline-block mx-2\">\n          <a class=\"text-reset text-decoration-none small\" href=\"https://github.com/sponsors/kishikawakatsumi\"\n            target=\"_blank\" rel=\"nofollow noopener noreferrer\">\n            <span class=\"fa-solid fa-heart\" style=\"color: #bf3989;\"></span><span class=\"mx-2\">Donate</span></a>\n        </div>\n      </div>\n    </footer>\n  </div>\n</body>\n\n</html>\n"
  },
  {
    "path": "Public/index.js",
    "content": "\"use strict\";\n\nimport \"./scss/default.scss\";\nimport \"codemirror/lib/codemirror.css\";\nimport \"tippy.js/dist/tippy.css\";\nimport \"./css/common.css\";\nimport \"./css/highlight.css\";\n\nimport \"./js/misc/icons\";\n\nimport \"bootstrap\";\nimport { App } from \"./js/app\";\n\nnew App();\n"
  },
  {
    "path": "Public/js/app.js",
    "content": "\"use strict\";\n\nimport { Tooltip } from \"bootstrap\";\nimport { ExpressionField } from \"./views/expression_field\";\nimport { MatchOptions } from \"./views/match_options\";\nimport { TestEditor } from \"./views/test_editor\";\nimport { DSLView } from \"./views/dsl_view\";\nimport { DSLEditor } from \"./views/dsl_editor\";\nimport { DebuggerText } from \"./views/debugger_text\";\nimport { Runner } from \"./runner\";\n\nexport class App {\n  constructor() {\n    this.init();\n  }\n\n  init() {\n    [].slice\n      .call(document.querySelectorAll('[data-bs-toggle=\"tooltip\"]'))\n      .map((trigger) => {\n        return new Tooltip(trigger);\n      });\n\n    this.expressionField = new ExpressionField(\n      document.getElementById(\"expression-field-container\"),\n    );\n    this.expressionField.addEventListener(\"change\", () =>\n      this.onExpressionFieldChange(),\n    );\n\n    this.matchOptions = new MatchOptions();\n    this.matchOptions.addEventListener(\"change\", () =>\n      this.onExpressionFieldChange(),\n    );\n\n    this.patternTestEditor = new TestEditor(\n      document.querySelector(\".test-editor-container\"),\n    );\n    this.patternTestEditor.addEventListener(\"change\", () =>\n      this.onPatternTestEditorChange(),\n    );\n\n    this.debuggerText = new DebuggerText(\n      document.getElementById(\"debugger-text-container\"),\n    );\n\n    this.debuggerGoStartButton = document.getElementById(\"debugger-go-start\");\n    this.debuggerGoStartButton.addEventListener(\"click\", () => {\n      const matchStepRange = document.getElementById(\"debugger-step-range\");\n      matchStepRange.value = 1;\n      this.onDebuggerStepChange();\n    });\n\n    this.debuggerStepBackwardButton = document.getElementById(\n      \"debugger-step-backward\",\n    );\n    this.debuggerStepBackwardButton.addEventListener(\"click\", () => {\n      const matchStepRange = document.getElementById(\"debugger-step-range\");\n      matchStepRange.value = Math.max(1, parseInt(matchStepRange.value) - 1);\n      this.onDebuggerStepChange();\n    });\n\n    this.debuggerStepForwardButton = document.getElementById(\n      \"debugger-step-forward\",\n    );\n    this.debuggerStepForwardButton.addEventListener(\"click\", () => {\n      const matchStepRange = document.getElementById(\"debugger-step-range\");\n      matchStepRange.value = Math.min(\n        parseInt(matchStepRange.value) + 1,\n        parseInt(matchStepRange.max),\n      );\n      this.onDebuggerStepChange();\n    });\n\n    this.debuggerGoEndButton = document.getElementById(\"debugger-go-end\");\n    this.debuggerGoEndButton.addEventListener(\"click\", () => {\n      const matchStepRange = document.getElementById(\"debugger-step-range\");\n      matchStepRange.value = matchStepRange.max;\n      this.onDebuggerStepChange();\n    });\n\n    const matchStepRange = document.getElementById(\"debugger-step-range\");\n    matchStepRange.addEventListener(\"input\", () => {\n      this.onDebuggerStepChange();\n    });\n\n    this.debuggerModal = document.getElementById(\"debugger-modal\");\n    this.debuggerModal.addEventListener(\"shown.bs.modal\", () =>\n      this.launchDebugger(),\n    );\n\n    this.dslView = new DSLView(document.getElementById(\"dsl-view-container\"));\n\n    this.runner = new Runner();\n    this.runner.onready = this.onRunnerReady.bind(this);\n    this.runner.onresponse = this.onRunnerResponse.bind(this);\n\n    this.stateProxy = {\n      builder: \"\",\n      text2: \"\",\n    };\n\n    if (window.Worker) {\n      this.stateRestorationWorker = new Worker(\n        new URL(\"./state/worker.js\", import.meta.url),\n      );\n\n      if (window.location.search) {\n        this.decodeState();\n      } else {\n        this.expressionField.setDefaultValue();\n        this.patternTestEditor.setDefaultValue();\n\n        this.stateProxy.builder = DSLEditor.defaultValue;\n        this.stateProxy.text2 = TestEditor.defaultValue;\n      }\n      this.startStateRestoration();\n    }\n  }\n\n  startStateRestoration() {\n    if (!this.stateRestorationWorker) {\n      return;\n    }\n\n    const debounce = (() => {\n      const timers = {};\n      return function (callback, delay, id) {\n        delay = delay || 400;\n        id = id || \"duplicated event\";\n        if (timers[id]) {\n          clearTimeout(timers[id]);\n        }\n        timers[id] = setTimeout(callback, delay);\n      };\n    })();\n\n    this.stateRestorationWorker.onmessage = (e) => {\n      if (e.data && e.data.type === \"encode\") {\n        debounce(\n          () => {\n            history.replaceState(null, \"\", e.data.value);\n          },\n          400,\n          \"update_location\",\n        );\n      }\n      if (e.data && e.data.type === \"decode\") {\n        const expressionField = this.expressionField;\n        const matchOptions = this.matchOptions;\n        const patternTestEditor = this.patternTestEditor;\n\n        if (expressionField) {\n          expressionField.value = e.data.value.pattern;\n        }\n        if (matchOptions) {\n          matchOptions.value = e.data.value.options;\n        }\n        if (patternTestEditor) {\n          patternTestEditor.value = e.data.value.text1;\n        }\n      }\n    };\n  }\n\n  encodeState() {\n    if (!this.stateRestorationWorker) {\n      return;\n    }\n\n    const expressionField = this.expressionField;\n    const matchOptions = this.matchOptions;\n    const patternTestEditor = this.patternTestEditor;\n\n    this.stateRestorationWorker.postMessage({\n      type: \"encode\",\n      value: {\n        pattern: expressionField ? expressionField.value : \"\",\n        options: matchOptions ? matchOptions.value : [],\n        text1: patternTestEditor ? patternTestEditor.value : \"\",\n      },\n    });\n  }\n\n  decodeState() {\n    if (!this.stateRestorationWorker) {\n      return;\n    }\n\n    this.stateRestorationWorker.postMessage({\n      type: \"decode\",\n      value: window.location.search,\n    });\n  }\n\n  updateMatchCount(count, id) {\n    const matchCount = document.getElementById(id);\n    if (count > 1) {\n      matchCount.textContent = `${count} matches`;\n    } else if (count > 0) {\n      matchCount.textContent = \"1 match\";\n    } else {\n      matchCount.textContent = \"no match\";\n    }\n  }\n\n  launchDebugger() {\n    const expressionField = this.expressionField;\n    const patternTestEditor = this.patternTestEditor;\n\n    const expression = expressionField.value;\n    const text = patternTestEditor.value;\n\n    const matchStepRange = document.getElementById(\"debugger-step-range\");\n    matchStepRange.value = 1;\n    matchStepRange.min = 1;\n\n    const matchStep = document.getElementById(\"debugger-match-step\");\n    matchStep.textContent = \"1\";\n\n    const debuggerPattern = document.getElementById(\"debugger-regex\");\n    debuggerPattern.value = expression;\n\n    this.debuggerText.value = text;\n\n    this.onDebuggerStepChange();\n  }\n\n  onExpressionFieldChange() {\n    if (!this.expressionField.value) {\n      this.expressionField.tokens = [];\n      this.expressionField.error = null;\n      this.dslView.value = \"\";\n      this.dslView.error = null;\n      this.updateMatchCount(0, \"match-count\");\n      return;\n    }\n\n    this.run();\n\n    this.encodeState();\n  }\n\n  run() {\n    const methods = [\"parseExpression\", \"convertToDSL\", \"match\"];\n    const params = {\n      pattern: this.expressionField.value,\n      text: this.patternTestEditor.value,\n      matchOptions: this.matchOptions.value,\n    };\n\n    if (this.runner.isReady) {\n      for (const method of methods) {\n        this.runner.run({\n          method: method,\n          ...params,\n        });\n      }\n    } else {\n      const headers = {\n        Accept: \"application/json\",\n        \"Content-Type\": \"application/json\",\n      };\n      for (const method of methods) {\n        const body = JSON.stringify({\n          method: method,\n          ...params,\n        });\n        fetch(`/api/rest/${method}`, { method: \"POST\", headers, body })\n          .then((response) => {\n            return response.json();\n          })\n          .then((response) => {\n            this.onRunnerResponse(response);\n          });\n      }\n    }\n  }\n\n  onMatchOptionsChange() {\n    this.onPatternTestEditorChange();\n  }\n\n  onPatternTestEditorChange() {\n    const method = \"match\";\n    const params = {\n      method,\n      pattern: this.expressionField.value,\n      text: this.patternTestEditor.value,\n      matchOptions: this.matchOptions.value,\n    };\n\n    if (this.runner.isReady) {\n      this.runner.run(params);\n    } else {\n      const headers = {\n        Accept: \"application/json\",\n        \"Content-Type\": \"application/json\",\n      };\n      const body = JSON.stringify(params);\n      fetch(`/api/rest/${method}`, { method: \"POST\", headers, body })\n        .then((response) => {\n          return response.json();\n        })\n        .then((response) => {\n          this.onRunnerResponse(response);\n        });\n    }\n\n    this.encodeState();\n  }\n\n  onDebuggerStepChange() {\n    const method = \"debug\";\n    const params = {\n      method,\n      pattern: document.getElementById(\"debugger-regex\").value,\n      text: this.debuggerText.value,\n      matchOptions: this.matchOptions.value,\n      step: document.getElementById(\"debugger-step-range\").value,\n    };\n\n    if (this.runner.isReady) {\n      this.runner.run(params);\n    } else {\n      const headers = {\n        Accept: \"application/json\",\n        \"Content-Type\": \"application/json\",\n      };\n      const body = JSON.stringify(params);\n      fetch(`/api/rest/${method}`, { method: \"POST\", headers, body })\n        .then((response) => {\n          return response.json();\n        })\n        .then((response) => {\n          this.onRunnerResponse(response);\n        });\n    }\n  }\n\n  onRunnerReady() {\n    const value = this.expressionField.value;\n    if (value) {\n      this.onExpressionFieldChange();\n    }\n  }\n\n  onRunnerResponse(response) {\n    switch (response.method) {\n      case \"parseExpression\":\n        if (response.result) {\n          const tokens = JSON.parse(response.result);\n          this.expressionField.tokens = tokens;\n        } else {\n          this.expressionField.tokens = [];\n        }\n        if (response.error) {\n          try {\n            const error = JSON.parse(response.error);\n            if (error) {\n              this.expressionField.error = error;\n            }\n          } catch (e) {\n            this.expressionField.error = response.error;\n          }\n        } else {\n          this.expressionField.error = null;\n        }\n        break;\n      case \"convertToDSL\":\n        if (response.result) {\n          this.dslView.value = JSON.parse(response.result);\n        }\n        if (response.error) {\n          try {\n            const error = JSON.parse(response.error);\n            if (error) {\n              this.dslView.error = error;\n            }\n          } catch (e) {\n            this.dslView.error = response.error;\n          }\n        } else {\n          this.dslView.error = null;\n        }\n        break;\n      case \"match\":\n        const debuggerButton = document.getElementById(\"debugger-button\");\n\n        if (response.result) {\n          const matches = JSON.parse(response.result);\n          this.patternTestEditor.matches = matches;\n          this.updateMatchCount(matches.length, \"match-count\");\n\n          debuggerButton.disabled = matches.length === 0;\n        } else {\n          this.patternTestEditor.matches = [];\n          this.updateMatchCount(0, \"match-count\");\n\n          debuggerButton.disabled = true;\n        }\n\n        this.patternTestEditor.error = response.error;\n\n        break;\n      case \"debug\":\n        if (response.result) {\n          const metrics = JSON.parse(response.result);\n\n          const matchStep = document.getElementById(\"debugger-match-step\");\n          matchStep.textContent = metrics.step;\n\n          const matchStepRange = document.getElementById(\"debugger-step-range\");\n          matchStepRange.max = metrics.stepCount;\n\n          const matchStepRangeMax = document.getElementById(\n            \"debugger-step-range-max\",\n          );\n          matchStepRangeMax.textContent = metrics.stepCount;\n\n          const instructions = document.getElementById(\"debugger-instructions\");\n          instructions.innerHTML = \"\";\n\n          metrics.instructions.forEach((instruction, i) => {\n            const tr = document.createElement(\"tr\");\n\n            if (i === metrics.programCounter) {\n              tr.classList.add(\"table-primary\");\n            }\n\n            const programCounter = document.createElement(\"td\");\n            programCounter.style =\n              \"width: 1%; text-align: right; white-space: nowrap; padding-left: 1em; padding-right: 1em;\";\n            programCounter.textContent = i + 1;\n            tr.appendChild(programCounter);\n\n            const inst = document.createElement(\"td\");\n            inst.style = \"white-space: nowrap;\";\n            inst.textContent = instruction;\n            tr.appendChild(inst);\n            instructions.appendChild(tr);\n          });\n\n          const totalCycleCount = document.getElementById(\n            \"debugger-total-cycle-count\",\n          );\n          totalCycleCount.textContent = metrics.totalCycleCount;\n\n          const resets = document.getElementById(\"debugger-resets\");\n          resets.textContent = metrics.resets;\n\n          const backtracks = document.getElementById(\"debugger-backtracks\");\n\n          const previousBacktracks = Number(backtracks.textContent);\n          backtracks.textContent = metrics.backtracks;\n\n          this.debuggerText.highlighter.draw(\n            metrics.traces,\n            previousBacktracks < metrics.backtracks ? metrics.failure : null,\n          );\n        }\n        break;\n    }\n  }\n}\n"
  },
  {
    "path": "Public/js/docs/reference.js",
    "content": "\"use strict\";\n\nexport class Reference {\n  static get(category, key) {\n    return references[category][key];\n  }\n}\n\nconst references = {\n  charclasses: {\n    label: \"Character classes\",\n    desc: \"Character classes match a character from a specific set. There are a number of predefined character classes and you can also define your own sets.\",\n\n    set: {\n      title: \"Character set\",\n      detail: \"Match any character in the set.\",\n    },\n    setnot: {\n      title: \"Negated set\",\n      detail: \"Match any character that is not in the set.\",\n    },\n    range: {\n      title: \"Range\",\n      detail:\n        \"Matches a character in the range {{getChar(prev)}} to {{getChar(next)}} (char code {{prev.code}} to {{next.code}}). {{getInsensitive()}}\",\n    },\n    posixcharclass: {\n      title: \"POSIX class\",\n      detail:\n        \"Matches any character in the specified POSIX class. Must be in a character set. For example, <code>[[:alnum:]$]</code> will match alphanumeric characters and <code>$</code>.\",\n    },\n    dot: {\n      title: \"Dot\",\n      detail: \"Matches any character {{getDotAll()}}.\",\n    },\n    matchanyset: {\n      title: \"Match any\",\n      detail:\n        \"A character set that can be used to match any character, including line breaks, without the dotall flag (<code>s</code>).\" +\n        \"<p>An alternative is <code>[^]</code>, but it is not supported in all browsers.</p>\",\n    },\n    unicodegrapheme: {\n      title: \"Unicode grapheme\",\n      detail: \"Matches any single unicode grapheme (ie. character).\",\n    },\n    word: {\n      title: \"Word\",\n      detail: \"Matches any word character (alphanumeric & underscore).\",\n    },\n    notword: {\n      title: \"Not word\",\n      detail:\n        \"Matches any character that is not a word character (alphanumeric & underscore).\",\n    },\n    digit: {\n      title: \"Digit\",\n      detail: \"Matches any digit character (0-9).\",\n    },\n    notdigit: {\n      title: \"Not digit\",\n      detail: \"Matches any character that is not a digit character (0-9).\",\n    },\n    whitespace: {\n      title: \"Whitespace\",\n      detail: \"Matches any whitespace character (spaces, tabs, line breaks).\",\n    },\n    notwhitespace: {\n      title: \"Not whitespace\",\n      detail:\n        \"Matches any character that is not a whitespace character (spaces, tabs, line breaks).\",\n    },\n    hwhitespace: {\n      title: \"Horizontal whitespace\",\n      detail: \"Matches any horizontal whitespace character (spaces, tabs).\",\n    },\n    nothwhitespace: {\n      title: \"Not horizontal whitespace\",\n      detail:\n        \"Matches any character that is not a horizontal whitespace character (spaces, tabs).\",\n    },\n    vwhitespace: {\n      title: \"Vertical whitespace\",\n      detail: \"Matches any vertical whitespace character (line breaks).\",\n    },\n    notvwhitespace: {\n      title: \"Not vertical whitespace\",\n      detail:\n        \"Matches any character that is not a vertical whitespace character (line breaks).\",\n    },\n    linebreak: {\n      title: \"Line break\",\n      detail:\n        \"Matches any line break character, including the CRLF pair, and CR / LF individually.\",\n    },\n    notlinebreak: {\n      title: \"Not line break\",\n      detail: \"Matches any character that is not a line break.\",\n    },\n    unicodecat: {\n      title: \"Unicode category\",\n      detail:\n        \"Matches any character in the '{{getUniCat()}}' unicode category.\",\n    },\n    notunicodecat: {\n      title: \"Not unicode category\",\n      detail:\n        \"Matches any character that is not in the '{{getUniCat()}}' unicode category.\",\n    },\n    unicodescript: {\n      title: \"Unicode script\",\n      detail: \"Matches any character in the '{{value}}' unicode script.\",\n    },\n    notunicodescript: {\n      title: \"Not unicode script\",\n      detail:\n        \"Matches any character that is not in the '{{value}}' unicode script.\",\n    },\n\n    binary: {\n      title: \"Unicode property escapes\",\n      detail:\n        \"Allows for matching characters based on their Unicode properties.\",\n    },\n    script: {\n      title: \"Script\",\n      detail: \"No overview available.\",\n    },\n    scriptextension: {\n      title: \"Script extension\",\n      detail: \"No overview available.\",\n    },\n    named: {\n      title: \"Named\",\n      detail: \"No overview available.\",\n    },\n    numerictype: {\n      title: \"Numeric type\",\n      detail: \"No overview available.\",\n    },\n    numericvalue: {\n      title: \"Numeric value\",\n      detail: \"No overview available.\",\n    },\n    mapping: {\n      title: \"Mapping\",\n      detail: \"No overview available.\",\n    },\n    ccc: {\n      title: \"Custom character class\",\n      detail: \"No overview available.\",\n    },\n    age: {\n      title: \"Age\",\n      detail: \"No overview available.\",\n    },\n    block: {\n      title: \"Block\",\n      detail: \"No overview available.\",\n    },\n    pcrespecial: {\n      title: \"PCRE special\",\n      detail: \"No overview available.\",\n    },\n    javaspecial: {\n      title: \"Java special\",\n      detail: \"No overview available.\",\n    },\n\n    graphemecluster: {\n      title: \"Grapheme cluster\",\n      detail: \"No overview available.\",\n    },\n    trueanychar: {\n      title: \"Any character\",\n      detail: \"Equivalent to (?m:.)\",\n    },\n    textsegment: {\n      title: \"Text segment\",\n      detail: `Equivalent to (?>\\O(?:\\Y\\O)*)`,\n    },\n    nottextsegment: {\n      title: \"Not text segment\",\n      detail: \"Text segment non-boundary\",\n    },\n    keyboardcontrol: {\n      title: \"Control char\",\n      detail: \"No overview available.\",\n    },\n    keyboardmeta: {\n      title: \"Meta\",\n      detail: \"No overview available.\",\n    },\n    keyboardmetacontrol: {\n      title: \"Meta control char\",\n      detail: \"No overview available.\",\n    },\n    namedcharacter: {\n      title: \"Named character\",\n      detail: \"No overview available.\",\n    },\n    subpattern: {\n      title: \"Subpattern\",\n      detail: \"No overview available.\",\n    },\n    callout: {\n      title: \"Callout\",\n      detail: \"No overview available.\",\n    },\n\n    accept: {\n      title: \"Backtracking control\",\n      detail: `This verb causes the match to end successfully, skipping the remainder of the pattern. When inside a recursion, only the innermost pattern is ended immediately.`,\n    },\n    fail: {\n      title: \"Backtracking control\",\n      detail: `This verb causes the match to fail, forcing backtracking to occur. It is equivalent to (?!) but easier to read. The Perl documentation notes that it is probably useful only when combined with (?{}) or (??{}).`,\n    },\n    mark: {\n      title: \"Backtracking control\",\n      detail: \"No overview available.\",\n    },\n    commit: {\n      title: \"Backtracking control\",\n      detail: `This verb causes the whole match to fail outright if the rest of the pattern does not match. Even if the pattern is unanchored, no further attempts to find a match by advancing the start point take place. Once (*COMMIT) has been passed, re:run/3 is committed to finding a match at the current starting point, or not at all.`,\n    },\n    prune: {\n      title: \"Backtracking control\",\n      detail: `acktracking cannot cross (*PRUNE). In simple cases, the use of (*PRUNE) is just an alternative to an atomic group or possessive quantifier, but there are some uses of (*PRUNE) that cannot be expressed in any other way.`,\n    },\n    skip: {\n      title: \"Backtracking control\",\n      detail: `This verb is like (*PRUNE), except that if the pattern is unanchored, the \"bumpalong\" advance is not to the next character, but to the position in the subject where (*SKIP) was encountered. (*SKIP) signifies that whatever text was matched leading up to it cannot be part of a successful match.`,\n    },\n    then: {\n      title: \"Backtracking control\",\n      detail: `This verb causes a skip to the next alternation if the rest of the pattern does not match. That is, it cancels pending backtracking, but only within the current alternation.`,\n    },\n  },\n\n  anchors: {\n    label: \"Anchors\",\n    desc: \"Anchors are unique in that they match a position within a string, not a character.\",\n\n    bos: {\n      title: \"Beginning of string\",\n      detail: \"Matches the beginning of the string.\",\n    },\n    eos: {\n      title: \"End of string\",\n      detail: \"Matches the end of the string.\",\n    },\n    abseos: {\n      title: \"Strict end of string\",\n      detail:\n        \"Matches the end of the string. Unlike <code>$</code> or <code>\\\\Z</code>, it does not allow for a trailing newline.\",\n    },\n    bof: {\n      title: \"Beginning\",\n      detail:\n        \"Matches the beginning of the string, or the beginning of a line if the multiline flag (<code>m</code>) is enabled.\",\n    },\n    eof: {\n      title: \"End\",\n      detail:\n        \"Matches the end of the string, or the end of a line if the multiline flag (<code>m</code>) is enabled.\",\n    },\n    wordboundary: {\n      title: \"Word boundary\",\n      detail:\n        \"Matches a word boundary position between a word character and non-word character or position (start / end of string).\",\n    },\n    notwordboundary: {\n      title: \"Not word boundary\",\n      detail: \"Matches any position that is not a word boundary.\",\n    },\n    prevmatchend: {\n      title: \"Previous match end\",\n      detail: \"Matches the end position of the previous match.\",\n    },\n\n    invalid: {\n      title: \"Invalid character class\",\n      detail: \"No overview available.\",\n    },\n  },\n\n  escchars: {\n    label: \"Escaped characters\",\n    desc: \"Escape sequences can be used to insert reserved, special, and unicode characters. All escaped characters begin with the <code>\\\\</code> character.\",\n\n    reservedchar: {\n      title: \"Reserved characters\",\n      detail:\n        \"The following character have special meaning, and should be preceded by a <code>\\\\</code> (backslash) to represent a literal character:\" +\n        \"<p><code>{{getEscChars()}}</code></p>\" +\n        \"<p>Within a character set, only <code>\\\\</code>, <code>-</code>, and <code>]</code> need to be escaped.</p>\",\n    },\n    escoctal: {\n      title: \"Octal escape\",\n      detail: \"Octal escaped character in the form <code>\\\\000</code>.\",\n    },\n    eschexadecimal: {\n      title: \"Hexadecimal escape\",\n      detail: \"Hexadecimal escaped character in the form <code>\\\\xFF</code>.\",\n    },\n    escunicodeu: {\n      title: \"Unicode escape\",\n      detail: \"Unicode escaped character in the form <code>\\\\uFFFF</code>\",\n    },\n    escunicodeub: {\n      title: \"Extended unicode escape\",\n      detail: \"Unicode escaped character in the form <code>\\\\u{FFFF}</code>.\",\n    },\n    escunicodexb: {\n      title: \"Unicode escape\",\n      detail: \"Unicode escaped character in the form <code>\\\\x{FF}</code>.\",\n    },\n    esccontrolchar: {\n      title: \"Control character escape\",\n      detail: \"Escaped control character in the form <code>\\\\cZ</code>.\",\n    },\n    escsequence: {\n      title: \"Escape sequence\",\n      detail: \"Matches the literal string '{{value}}'.\",\n    },\n  },\n\n  groups: {\n    label: \"Groups & References\",\n    desc: \"Groups allow you to combine a sequence of tokens to operate on them together. Capture groups can be referenced by a backreference and accessed separately in the results.\",\n\n    group: {\n      title: \"Capturing group #{{group.num}}\",\n      detail:\n        \"Groups multiple tokens together and creates a capture group for extracting a substring or using a backreference.\",\n    },\n    namedgroup: {\n      title: \"Named capturing group\",\n      detail: \"Creates a capturing group named '{{name}}'.\",\n    },\n    namedref: {\n      title: \"Named reference\",\n      detail:\n        \"Matches the results of the capture group named '{{group.name}}'.\",\n    },\n    numref: {\n      title: \"Numeric reference\",\n      detail: \"Matches the results of capture group #{{group.num}}.\",\n    },\n    branchreset: {\n      title: \"Branch reset group\",\n      detail: \"Define alternative groups that share the same group numbers.\",\n    },\n    noncapgroup: {\n      title: \"Non-capturing group\",\n      detail:\n        \"Groups multiple tokens together without creating a capture group.\",\n    },\n    atomic: {\n      title: \"Atomic group\",\n      detail:\n        \"Non-capturing group that discards backtracking positions once matched.\",\n    },\n    define: {\n      title: \"Define\",\n      detail:\n        \"Used to define named groups for use as subroutines without including them in the match.\",\n    },\n    numsubroutine: {\n      title: \"Numeric subroutine\",\n      detail: \"Matches the expression in capture group #{{group.num}}.\",\n    },\n    namedsubroutine: {\n      title: \"Named subroutine\",\n      detail:\n        \"Matches the expression in the capture group named '{{group.name}}'.\",\n    },\n    balancedcapture: {\n      title: \"Balancing group\",\n      detail:\n        \"This allows nested constructs to be matched, such as parentheses or HTML tags. The previously defined group to balance against is specified by previous. Captures subpattern as a named group specified by name, or name can be omitted to capture as an unnamed group.\",\n    },\n\n    absentfunction: {\n      title: \"Absent function\",\n      detail: \"No overview available.\",\n    },\n  },\n\n  lookaround: {\n    label: \"Lookaround\",\n    desc:\n      \"Lookaround lets you match a group before (lookbehind) or after (lookahead) your main pattern without including it in the result.\" +\n      \"<p>Negative lookarounds specify a group that can NOT match before or after the pattern.</p>\",\n\n    poslookahead: {\n      title: \"Positive lookahead\",\n      detail:\n        \"Matches a group after the main expression without including it in the result.\",\n    },\n    neglookahead: {\n      title: \"Negative lookahead\",\n      detail:\n        \"Specifies a group that can not match after the main expression (if it matches, the result is discarded).\",\n    },\n    poslookbehind: {\n      title: \"Positive lookbehind\",\n      detail:\n        \"Matches a group before the main expression without including it in the result.\",\n    },\n    neglookbehind: {\n      title: \"Negative lookbehind\",\n      detail:\n        \"Specifies a group that can not match before the main expression (if it matches, the result is discarded).\",\n    },\n    keepout: {\n      title: \"Keep out\",\n      detail:\n        \"Keep text matched so far out of the returned match, essentially discarding the match up to this point.\",\n    },\n    nonatomicposlookahead: {\n      title: \"Non-atomic Positive lookahead\",\n      detail: \"No overview available.\",\n    },\n    nonatomicposlookbehind: {\n      title: \"Non-atomic Positive lookbehind\",\n      detail: \"No overview available.\",\n    },\n\n    scriptrun: {\n      title: \"Script run\",\n      detail: \"No overview available.\",\n    },\n    atomicscriptrun: {\n      title: \"Atomic script run\",\n      detail: \"No overview available.\",\n    },\n    changematchingoptions: {\n      title: \"Change matching options\",\n      detail: \"No overview available.\",\n    },\n  },\n\n  quants: {\n    label: \"Quantifiers & Alternation\",\n    desc:\n      \"Quantifiers indicate that the preceding token must be matched a certain number of times. By default, quantifiers are greedy, and will match as many characters as possible.\" +\n      \"<hr/>Alternation acts like a boolean OR, matching one sequence or another.\",\n\n    plus: {\n      title: \"Plus\",\n      detail: \"Matches 1 or more of the preceding token.\",\n    },\n    star: {\n      title: \"Star\",\n      detail: \"Matches 0 or more of the preceding token.\",\n    },\n    quant: {\n      title: \"Quantifier\",\n      detail: \"Match {{getQuant()}} of the preceding token.\",\n    },\n    opt: {\n      title: \"Optional\",\n      detail:\n        \"Matches 0 or 1 of the preceding token, effectively making it optional.\",\n    },\n    lazy: {\n      title: \"Lazy\",\n      detail:\n        \"Makes the preceding quantifier {{getLazy()}}, causing it to match as {{getLazyFew()}} characters as possible.\",\n    },\n    possessive: {\n      title: \"Possessive\",\n      detail:\n        \"Makes the preceding quantifier possessive. It will match as many characters as possible, and will not release them to match subsequent tokens.\",\n    },\n    alt: {\n      title: \"Alternation\",\n      detail:\n        \"Acts like a boolean OR. Matches the expression before or after the <code>|</code>.\",\n    },\n  },\n\n  other: {\n    label: \"Special\",\n    desc: \"Tokens that don't quite fit anywhere else.\",\n\n    comment: {\n      title: \"Comment\",\n      detail:\n        \"Allows you to insert a comment into your expression that is ignored when finding a match.\",\n    },\n    conditional: {\n      title: \"Conditional\",\n      detail:\n        \"Conditionally matches one of two options based on whether a lookaround is matched.\",\n    },\n    conditionalgroup: {\n      title: \"Group conditional\",\n      detail:\n        \"Conditionally matches one of two options based on whether group '{{name}}' matched.\",\n    },\n    recursion: {\n      title: \"Recursion\",\n      detail:\n        \"Attempts to match the full expression again at the current position.\",\n    },\n    mode: {\n      title: \"Mode modifier\",\n      detail: \"{{~getDesc()}}{{~getModes()}}\",\n    },\n\n    interpolation: {\n      title: \"Interpolation\",\n      detail: \"No overview available.\",\n    },\n  },\n\n  subst: {\n    label: \"Substitution\",\n    desc: \"These tokens are used in a substitution string to insert different parts of the match.\",\n\n    \"subst_$&match\": {\n      title: \"Match\",\n      detail: \"Inserts the matched text.\",\n    },\n    subst_0match: {\n      title: \"Match\",\n      detail: \"Inserts the matched text.\",\n    },\n    subst_group: {\n      title: \"Capture group\",\n      detail: \"Inserts the results of capture group #{{group.num}}.\",\n    },\n    subst_$before: {\n      title: \"Before match\",\n      detail:\n        \"Inserts the portion of the source string that precedes the match.\",\n    },\n    subst_$after: {\n      title: \"After match\",\n      detail:\n        \"Inserts the portion of the source string that follows the match.\",\n    },\n    subst_$esc: {\n      title: \"Escaped $\",\n      detail: \"Inserts a dollar sign character ($).\",\n    },\n    subst_esc: {\n      title: \"Escaped characters\",\n      detail:\n        \"For convenience, these escaped characters are supported in the Replace string in RegExr: <code>\\\\n</code>, <code>\\\\r</code>, <code>\\\\t</code>, <code>\\\\\\\\</code>, and unicode escapes <code>\\\\uFFFF</code>. This may vary in your deploy environment.\",\n    },\n  },\n\n  flags: {\n    label: \"Flags\",\n    desc: \"Expression flags change how the expression is interpreted. Flags follow the closing forward slash of the expression (ex. <code>/.+/igm</code> ).\",\n\n    caseinsensitive: {\n      title: \"Ignore case\",\n      detail: \"Makes the whole expression case-insensitive.\",\n    },\n    global: {\n      title: \"Global search\",\n      detail:\n        \"Retain the index of the last match, allowing iterative searches.\",\n    },\n    multiline: {\n      title: \"Multiline\",\n      detail:\n        \"Beginning/end anchors (<b>^</b>/<b>$</b>) will match the start/end of a line.\",\n    },\n    unicode: {\n      title: \"Unicode\",\n      detail: \"Enables <code>\\\\x{FFFFF}</code> unicode escapes.\",\n    },\n    sticky: {\n      title: \"Sticky\",\n      detail:\n        \"The expression will only match from its lastIndex position and ignores the global (<code>g</code>) flag if set.\",\n    },\n    dotall: {\n      title: \"Dot all\",\n      detail:\n        \"Dot (<code>.</code>) will match any character, including newline.\",\n    },\n    extended: {\n      title: \"Extended\",\n      detail:\n        \"Literal whitespace characters are ignored, except in character sets.\",\n    },\n    ungreedy: {\n      title: \"Ungreedy\",\n      detail: \"Makes quantifiers ungreedy (lazy) by default.\",\n    },\n  },\n\n  misc: {\n    label: \"Miscellaneous\",\n    desc: \"No overview available.\",\n\n    ignorews: {\n      title: \"Ignored whitespace\",\n      detail:\n        \"Whitespace character ignored due to the e<b>x</b>tended flag or mode.\",\n    },\n    extnumref: {\n      title: \"Numeric reference\",\n      detail: \"Matches the results of capture group #{{group.num}}.\",\n    },\n    char: {\n      title: \"Character\",\n      detail:\n        \"Matches a {{getChar()}} character (char code {{code}}). {{getInsensitive()}}\",\n    },\n    escchar: {\n      title: \"Escaped character\",\n      detail: \"Matches a {{getChar()}} character (char code {{code}}).\",\n    },\n    open: {\n      title: \"Open\",\n      detail: \"Indicates the start of a regular expression.\",\n    },\n    close: {\n      title: \"Close\",\n      detail:\n        \"Indicates the end of a regular expression and the start of expression flags.\",\n    },\n    condition: {\n      title: \"Condition\",\n      detail:\n        \"The lookaround to match in resolving the enclosing conditional statement. See 'conditional' in the Reference for info.\",\n    },\n    conditionalelse: {\n      title: \"Conditional else\",\n      detail: \"Delimits the 'else' portion of the conditional.\",\n    },\n\n    ERROR: {\n      title: \"Error\",\n      detail:\n        \"Errors in the expression are underlined in red. Roll over errors for more info.\",\n    },\n    PREG_INTERNAL_ERROR: {\n      title: \"Internal error\",\n      detail: \"Internal PCRE error\",\n    },\n    PREG_BACKTRACK_LIMIT_ERROR: {\n      title: \"Backtrack limit error\",\n      detail: \"Backtrack limit was exhausted.\",\n    },\n    PREG_RECURSION_LIMIT_ERROR: {\n      title: \"Recursion limit error\",\n      detail: \"Recursion limit was exhausted\",\n    },\n    PREG_BAD_UTF8_ERROR: {\n      title: \"Bad UTF8 error\",\n      detail: \"Malformed UTF-8 data\",\n    },\n    PREG_BAD_UTF8_OFFSET_ERROR: {\n      title: \"Bad UTF8 offset error\",\n      detail: \"Malformed UTF-8 data\",\n    },\n\n    any: {\n      title: \"Any\",\n      detail: \"No overview available.\",\n    },\n    assigned: {\n      title: \"Assigned\",\n      detail: \"No overview available.\",\n    },\n    ascii: {\n      title: \"Unicode property\",\n      detail: \"Matches any characters in the ASCII script extension.\",\n    },\n  },\n  errors: {\n    groupopen: \"Unmatched opening parenthesis.\",\n    groupclose: \"Unmatched closing parenthesis.\",\n    setopen: \"Unmatched opening square bracket.\",\n    rangerev:\n      \"Range values reversed. Start char code is greater than end char code.\",\n    quanttarg: \"Invalid target for quantifier.\",\n    quantrev: \"Quantifier minimum is greater than maximum.\",\n    esccharopen: \"Dangling backslash.\",\n    esccharbad: \"Unrecognized or malformed escape character.\",\n    unicodebad: \"Unrecognized unicode category or script.\",\n    posixcharclassbad: \"Unrecognized POSIX character class.\",\n    posixcharclassnoset: \"POSIX character class must be in a character set.\",\n    notsupported:\n      'The \"{{~getLabel()}}\" feature is not supported in this flavor of RegEx.',\n    fwdslash:\n      \"Unescaped forward slash. This may cause issues if copying/pasting this expression into code.\",\n    esccharbad: \"Invalid escape sequence.\",\n    servercomm: \"An error occurred while communicating with the server.\",\n    extraelse: \"Extra else in conditional group.\",\n    unmatchedref: 'Reference to non-existent group \"{{name}}\".',\n    modebad: 'Unrecognized mode flag \"<code>{{errmode}}</code>\".',\n    badname: \"Group name can not start with a digit.\",\n    dupname: \"Duplicate group name.\",\n    branchreseterr:\n      \"<b>Branch Reset.</b> Results will be ok, but RegExr's parser does not number branch reset groups correctly. Coming soon!\",\n    timeout: \"The expression took longer than 250ms to execute.\", // TODO: can we couple this to the help content somehow?\n\n    // warnings:\n    jsfuture:\n      'The \"{{~getLabel()}}\" feature may not be supported in all browsers.',\n    infinite:\n      \"The expression can return empty matches, and may match infinitely in some use cases.\", // TODO: can we couple this to the help content somehow?\n  },\n\n  empty: {\n    empty: {\n      title: \"Empty\",\n      detail: \"No overview available.\",\n    },\n  },\n};\n"
  },
  {
    "path": "Public/js/misc/icons.js",
    "content": "\"use strict\";\n\nimport { config, library, dom } from \"@fortawesome/fontawesome-svg-core\";\nimport {\n  faFlag,\n  faOctagonXmark,\n  faStethoscope,\n  faHeart,\n  faBackwardStep,\n  faForwardStep,\n  faCaretLeft,\n  faCaretRight,\n} from \"@fortawesome/pro-solid-svg-icons\";\nimport {\n  faAt,\n  faCheck,\n  faCommentAltSmile,\n} from \"@fortawesome/pro-regular-svg-icons\";\nimport { faMonitorHeartRate } from \"@fortawesome/pro-light-svg-icons\";\nimport { faSwift, faGithub } from \"@fortawesome/free-brands-svg-icons\";\n\nconfig.searchPseudoElements = true;\nlibrary.add(\n  faFlag,\n  faOctagonXmark,\n  faStethoscope,\n  faHeart,\n\n  faBackwardStep,\n  faForwardStep,\n  faCaretLeft,\n  faCaretRight,\n\n  faAt,\n  faCheck,\n  faCommentAltSmile,\n\n  faMonitorHeartRate,\n\n  faSwift,\n  faGithub\n);\ndom.watch();\n"
  },
  {
    "path": "Public/js/misc/utils.js",
    "content": "\"use strict\";\n\nconst Utils = {};\nexport default Utils;\n\nUtils.copy = function (target, source) {\n  for (let n in source) {\n    target[n] = source[n];\n  }\n  return target;\n};\n\nUtils.clone = function (o) {\n  // this seems hacky, but it's the fastest, easiest approach for now:\n  return JSON.parse(JSON.stringify(o));\n};\n\nUtils.htmlSafe = function (str) {\n  return str == null\n    ? \"\"\n    : (\"\" + str).replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\");\n};\n\nUtils.shorten = function (str, length, htmlSafe, tag = \"\") {\n  if (!str) {\n    return str;\n  }\n  let b = length > 0 && str.length > length;\n  if (b) {\n    str = str.substr(0, length - 1);\n  }\n  if (htmlSafe) {\n    str = Utils.htmlSafe(str);\n  }\n  return !b\n    ? str\n    : str + (tag && \"<\" + tag + \">\") + \"\\u2026\" + (tag && \"</\" + tag + \">\");\n};\n\nUtils.unescSubstStr = function (str) {\n  if (!str) {\n    return \"\";\n  }\n  return str.replace(\n    Utils.SUBST_ESC_RE,\n    (a, b, c) =>\n      Utils.SUBST_ESC_CHARS[b] || String.fromCharCode(parseInt(c, 16))\n  );\n};\n\nUtils.getRegExp = function (str) {\n  // returns a JS RegExp object.\n  let match = str.match(/^\\/(.+)\\/([a-z]+)?$/),\n    regex = null;\n  try {\n    regex = match ? new RegExp(match[1], match[2] || \"\") : new RegExp(str, \"g\");\n  } catch (e) {}\n  return regex;\n};\n\nUtils.decomposeRegEx = function (str, delim = \"/\") {\n  let re = new RegExp(\"^\" + delim + \"(.*)\" + delim + \"([igmsuUxy]*)$\");\n  let match = re.exec(str);\n  if (match) {\n    return { source: match[1], flags: match[2] };\n  } else {\n    return { source: str, flags: \"g\" };\n  }\n};\n\nUtils.isMac = function () {\n  return !!navigator.userAgent.match(/Mac\\sOS/i);\n};\n\nUtils.getCtrlKey = function () {\n  return Utils.isMac() ? \"cmd\" : \"ctrl\";\n};\n\nUtils.now = function () {\n  return window.performance ? performance.now() : Date.now();\n};\n\nUtils.getUrlParams = function () {\n  let match,\n    re = /([^&=]+)=?([^&]*)/g,\n    params = {};\n  let url = window.location.search.substr(1).replace(/\\+/g, \" \");\n  while ((match = re.exec(url))) {\n    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);\n  }\n  return params;\n};\n\nlet deferIds = {};\nUtils.defer = function (f, id, t = 1) {\n  clearTimeout(deferIds[id]);\n  if (f === null) {\n    delete deferIds[id];\n    return;\n  }\n  deferIds[id] = setTimeout(() => {\n    delete deferIds[id];\n    f();\n  }, t);\n};\n\nUtils.getHashCode = function (s) {\n  let hash = 0,\n    l = s.length,\n    i;\n  for (i = 0; i < l; i++) {\n    hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0;\n  }\n  return hash;\n};\n\nUtils.getPatternURL = function (pattern) {\n  let a = Utils.isLocal ? \"?id=\" : \"/\";\n  let url = window.location.origin,\n    id = (pattern && pattern.id) || \"\";\n  return url + a + id;\n};\n\nUtils.isLocal = window.location.hostname === \"localhost\";\n\nUtils.getPatternURLStr = function (pattern) {\n  if (!pattern || !pattern.id) {\n    return null;\n  }\n  let a = Utils.isLocal ? \"?id=\" : \"/\";\n  let url = window.location.host,\n    id = pattern.id;\n  return url + a + id;\n};\n\nUtils.SUBST_ESC_CHARS = {\n  // this is just the list supported in Replace. Others: b, f, \", etc.\n  n: \"\\n\",\n  r: \"\\r\",\n  t: \"\\t\",\n  \"\\\\\": \"\\\\\",\n};\n\nUtils.SUBST_ESC_RE = /\\\\([nrt\\\\]|u([A-Z0-9]{4}))/gi;\n"
  },
  {
    "path": "Public/js/runner.js",
    "content": "\"use strict\";\n\nimport ReconnectingWebSocket from \"reconnecting-websocket\";\n\nexport class Runner {\n  constructor() {\n    this.connection = this.createConnection(this.endpoint());\n\n    this.onconnect = () => {};\n    this.onready = () => {};\n    this.onresponse = () => {};\n  }\n\n  get isReady() {\n    return this.connection.readyState === 1;\n  }\n\n  run(request) {\n    const encoder = new TextEncoder();\n    this.connection.send(encoder.encode(JSON.stringify(request)));\n  }\n\n  createConnection(endpoint) {\n    if (\n      this.connection &&\n      (this.connection.readyState === 0 || this.connection.readyState === 1)\n    ) {\n      return this.connection;\n    }\n\n    const connection = new ReconnectingWebSocket(endpoint, [], {\n      maxReconnectionDelay: 10000,\n      minReconnectionDelay: 1000,\n      reconnectionDelayGrowFactor: 1.3,\n      connectionTimeout: 10000,\n      maxRetries: Infinity,\n      debug: false,\n    });\n    connection.bufferType = \"arraybuffer\";\n\n    connection.onopen = () => {\n      this.onconnect();\n      this.onready();\n    };\n\n    connection.onerror = (event) => {\n      connection.close();\n    };\n\n    connection.onmessage = (event) => {\n      if (event.data.trim()) {\n        this.onresponse(JSON.parse(event.data));\n      }\n    };\n    return connection;\n  }\n\n  endpoint() {\n    let endpoint;\n    if (window.location.protocol === \"https:\") {\n      endpoint = \"wss:\";\n    } else {\n      endpoint = \"ws:\";\n    }\n    endpoint += \"//\" + window.location.host;\n    endpoint += window.location.pathname + \"api/ws\";\n    return endpoint;\n  }\n}\n"
  },
  {
    "path": "Public/js/state/decoder.js",
    "content": "\"use strict\";\n\nimport { ungzip } from \"pako\";\n\nexport class Decoder {\n  static decode(string) {\n    const base64 = decodeURIComponent(string);\n    const gziped = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n    const json = ungzip(gziped, { to: \"string\" });\n    return JSON.parse(json);\n  }\n}\n"
  },
  {
    "path": "Public/js/state/encoder.js",
    "content": "\"use strict\";\n\nimport { gzip } from \"pako\";\n\nexport class Encoder {\n  static encode(data) {\n    const json = JSON.stringify(data);\n    const gziped = gzip(json);\n    const base64 = btoa(String.fromCharCode(...gziped));\n    return encodeURIComponent(base64);\n  }\n}\n"
  },
  {
    "path": "Public/js/state/worker.js",
    "content": "\"use strict\";\n\nimport { Decoder } from \"./decoder.js\";\nimport { Encoder } from \"./encoder.js\";\n\nonmessage = (e) => {\n  if (!e.data || !e.data.type || !e.data.value) {\n    return;\n  }\n  switch (e.data.type) {\n    case \"decode\": {\n      const searchParams = new URLSearchParams(e.data.value);\n      const query = Object.fromEntries(searchParams.entries());\n      if (!query.s) {\n        return;\n      }\n      try {\n        const data = Decoder.decode(query.s);\n        if (!data) {\n          return;\n        }\n\n        const pattern = data.p;\n        const options = data.o;\n        const text1 = data.t1;\n        const builder = data.b;\n        const text2 = data.t2;\n        postMessage({\n          type: e.data.type,\n          value: {\n            pattern,\n            options,\n            text1,\n            builder,\n            text2,\n          },\n        });\n      } catch (error) {}\n      break;\n    }\n    case \"encode\": {\n      postMessage({\n        type: e.data.type,\n        value: `?s=${Encoder.encode({\n          p: e.data.value.pattern,\n          o: e.data.value.options,\n          t1: e.data.value.text1,\n          b: e.data.value.builder,\n          t2: e.data.value.text2,\n        })}`,\n      });\n      break;\n    }\n  }\n};\n"
  },
  {
    "path": "Public/js/views/debugger_highlighter.js",
    "content": "\"use strict\";\n\nimport Editor from \"./editor\";\n\nexport default class DebuggerHighlighter {\n  constructor(editor) {\n    this.editor = editor;\n    this.activeMarks = [];\n    this.widgets = [];\n  }\n\n  draw(traces, backtrack) {\n    this.clear();\n\n    const editor = this.editor;\n    editor.operation(() => {\n      const doc = editor.getDoc();\n      const marks = this.activeMarks;\n\n      const defaultTextHeight = editor.defaultTextHeight();\n\n      for (const trace of traces) {\n        const className = \"debuggermatch\";\n\n        if (trace.location.start !== trace.location.end) {\n          const location = Editor.calcRangePos(\n            this.editor,\n            trace.location.start,\n            trace.location.end - trace.location.start\n          );\n\n          marks.push(\n            doc.markText(location.startPos, location.endPos, {\n              className: className,\n            })\n          );\n        } else {\n          const pos = doc.posFromIndex(trace.location.start);\n\n          const widget = document.createElement(\"span\");\n          widget.className = className;\n\n          widget.style.height = `${defaultTextHeight * 1.5}px`;\n          widget.style.width = \"1px\";\n          widget.style.zIndex = \"10\";\n\n          this.editor.addWidget(pos, widget);\n\n          const coords = editor.charCoords(pos, \"local\");\n          widget.style.left = `${coords.left}px`;\n          widget.style.top = `${coords.top + 2}px`;\n\n          this.widgets.push(widget);\n        }\n      }\n\n      if (backtrack) {\n        const pos = doc.posFromIndex(backtrack.start);\n\n        const widget = document.createElement(\"span\");\n        widget.className = \"debuggerbacktrack\";\n\n        widget.style.height = `${defaultTextHeight * 1.5}px`;\n        widget.style.width = `${editor.defaultCharWidth()}px`;\n        widget.style.zIndex = \"10\";\n\n        this.editor.addWidget(pos, widget);\n\n        const coords = editor.charCoords(pos, \"local\");\n        widget.style.left = `${coords.left}px`;\n        widget.style.top = `${coords.top + 2}px`;\n\n        this.widgets.push(widget);\n      }\n    });\n  }\n\n  clear() {\n    this.editor.operation(() => {\n      let marks = this.activeMarks;\n      for (var i = 0, l = marks.length; i < l; i++) {\n        marks[i].clear();\n      }\n      marks.length = 0;\n\n      for (const widget of this.widgets) {\n        widget.parentNode.removeChild(widget);\n      }\n      this.widgets.length = 0;\n    });\n  }\n}\n"
  },
  {
    "path": "Public/js/views/debugger_text.js",
    "content": "\"use strict\";\n\nimport Editor from \"./editor\";\nimport DebuggerHighlighter from \"./debugger_highlighter\";\n\nexport class DebuggerText {\n  constructor(container) {\n    this.container = container;\n    this.init(container);\n  }\n\n  get value() {\n    return this.editor.getValue();\n  }\n\n  set value(val) {\n    this.editor.setValue(val);\n  }\n\n  init(container) {\n    const editor = Editor.create(\n      container,\n      {\n        lineWrapping: true,\n        screenReaderLabel: \"Debugger Test View\",\n        readOnly: true,\n      },\n      \"100%\",\n      \"100%\"\n    );\n    this.editor = editor;\n\n    this.highlighter = new DebuggerHighlighter(editor);\n  }\n}\n"
  },
  {
    "path": "Public/js/views/dsl_editor.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\n\nimport Editor from \"./editor\";\nimport ErrorMessage from \"./error_message\";\nimport Utils from \"../misc/utils\";\n\nconst defaultValue = `Regex {\n  Capture {\n    ChoiceOf {\n      \"CREDIT\"\n      \"DEBIT\"\n    }\n  }\n  OneOrMore(.whitespace)\n  Capture {\n    Regex {\n      Repeat(1...2) {\n        One(.digit)\n      }\n      \"/\"\n      Repeat(1...2) {\n        One(.digit)\n      }\n      \"/\"\n      Repeat(count: 4) {\n        One(.digit)\n      }\n    }\n  }\n}\n`;\n\nexport class DSLEditor extends EventDispatcher {\n  static defaultValue = defaultValue;\n\n  constructor(container) {\n    super();\n    this.container = container;\n    this.init(container);\n  }\n\n  get value() {\n    return this.editor.getValue();\n  }\n\n  set value(val) {\n    this.editor.setValue(val);\n  }\n\n  set error(error) {\n    const editor = this.editor;\n    const widgets = this.widgets;\n\n    editor.operation(function () {\n      for (const widget of widgets) {\n        editor.removeLineWidget(widget);\n      }\n      widgets.length = 0;\n\n      if (!error) {\n        return;\n      }\n\n      widgets.push(\n        editor.addLineWidget(0, ErrorMessage.create(error), {\n          coverGutter: false,\n          noHScroll: true,\n          above: true,\n        })\n      );\n    });\n  }\n\n  init(container) {\n    this.editor = Editor.create(\n      container,\n      {\n        lineNumbers: true,\n        lineWrapping: false,\n        matchBrackets: true,\n        mode: \"swift\",\n        screenReaderLabel: \"Build DSL Editor\",\n      },\n      \"100%\",\n      \"100%\"\n    );\n    this.editor.setValue(defaultValue);\n    this.editor.setCursor(this.editor.lineCount(), 0);\n    this.editor.on(\"change\", (editor, event) =>\n      this.onEditorChange(editor, event)\n    );\n\n    this.widgets = [];\n  }\n\n  setDefaultValue() {\n    this.editor.setValue(defaultValue);\n  }\n\n  deferUpdate() {\n    Utils.defer(() => this.update(), \"DSLEditor.update\");\n  }\n\n  update() {\n    this.dispatchEvent(\"change\");\n  }\n\n  onEditorChange(editor, event) {\n    this.deferUpdate();\n  }\n}\n"
  },
  {
    "path": "Public/js/views/dsl_highlighter.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport Editor from \"./editor\";\n\nexport class DSLHighlighter extends EventDispatcher {\n  constructor(editor) {\n    super();\n    this.editor = editor;\n    this.activeMarks = [];\n  }\n\n  clear() {\n    this.editor.operation(() => {\n      let marks = this.activeMarks;\n      for (var i = 0, l = marks.length; i < l; i++) {\n        marks[i].clear();\n      }\n      marks.length = 0;\n    });\n  }\n\n  draw(tokens) {\n    const editor = this.editor;\n\n    this.clear();\n    editor.operation(() => {\n      const doc = editor.getDoc();\n      const marks = this.activeMarks;\n\n      for (const token of tokens) {\n        const className = \"highlight\";\n        const location = Editor.calcRangePos(\n          this.editor,\n          token.sourceLocation.start,\n          token.sourceLocation.end - token.sourceLocation.start\n        );\n        marks.push(\n          doc.markText(location.startPos, location.endPos, {\n            className: className,\n          })\n        );\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "Public/js/views/dsl_view.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport Editor from \"./editor\";\nimport ErrorMessage from \"./error_message\";\n\nexport class DSLView extends EventDispatcher {\n  constructor(container) {\n    super();\n    this.container = container;\n    this.init(container);\n  }\n\n  get value() {\n    return this.editor.getValue();\n  }\n\n  set value(val) {\n    this.editor.setValue(val);\n  }\n\n  set error(error) {\n    const editor = this.editor;\n    const widgets = this.widgets;\n\n    editor.operation(function () {\n      for (const widget of widgets) {\n        editor.removeLineWidget(widget);\n      }\n      widgets.length = 0;\n\n      if (!error) {\n        return;\n      }\n      if (typeof error === \"string\" || error instanceof String) {\n        widgets.push(\n          editor.addLineWidget(0, ErrorMessage.create(error), {\n            coverGutter: false,\n            noHScroll: true,\n            above: true,\n          }),\n        );\n      } else {\n        for (const e of error) {\n          const message = ErrorMessage.create(e.message);\n          widgets.push(\n            editor.addLineWidget(0, message, {\n              coverGutter: false,\n              noHScroll: true,\n              above: true,\n            }),\n          );\n        }\n      }\n    });\n  }\n\n  init(container) {\n    this.editor = Editor.create(\n      container,\n      {\n        lineNumbers: true,\n        lineWrapping: false,\n        matchBrackets: true,\n        mode: \"swift\",\n        readOnly: true,\n        screenReaderLabel: \"Build DSL View\",\n      },\n      \"100%\",\n      \"100%\",\n    );\n    this.widgets = [];\n  }\n}\n"
  },
  {
    "path": "Public/js/views/editor.js",
    "content": "\"use strict\";\n\nimport CodeMirror from \"codemirror\";\nimport \"codemirror/mode/swift/swift\";\n\nimport Utils from \"../misc/utils\";\n\nconst Editor = {};\nexport default Editor;\n\nEditor.create = (target, opts = {}, width = \"100%\", height = \"100%\") => {\n  const keys = {};\n\n  const o = Utils.copy(\n    {\n      extraKeys: keys,\n      indentWithTabs: false,\n      lineNumbers: false,\n      mode: \"null\",\n      specialChars:\n        /[ \\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028\\u2029\\ufeff]/,\n      specialCharPlaceholder: (ch) =>\n        createElement(\"span\", ch === \" \" ? \"cm-space\" : \"cm-special\", \" \"), // needs to be a space so wrapping works\n      tabSize: 2,\n    },\n    opts,\n  );\n\n  const cm = CodeMirror(target, o);\n  cm.setSize(width, height);\n\n  if (cm.getOption(\"maxLength\")) {\n    cm.on(\"beforeChange\", Editor.enforceMaxLength);\n  }\n  if (cm.getOption(\"singleLine\")) {\n    cm.on(\"beforeChange\", Editor.enforceSingleLine);\n  }\n\n  return cm;\n};\n\nEditor.enforceMaxLength = (cm, change) => {\n  let maxLength = cm.getOption(\"maxLength\");\n  if (maxLength && change.update) {\n    let str = change.text.join(\"\\n\");\n    let delta =\n      str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from));\n    if (delta <= 0) {\n      return true;\n    }\n    delta = cm.getValue().length + delta - maxLength;\n    if (delta > 0) {\n      str = str.substr(0, str.length - delta);\n      change.update(change.from, change.to, str.split(\"\\n\"));\n    }\n  }\n  return true;\n};\n\nEditor.enforceSingleLine = (cm, change) => {\n  if (change.update) {\n    let str = change.text.join(\"\").replace(/(\\n|\\r)/g, \"\");\n    change.update(change.from, change.to, [str]);\n  }\n  return true;\n};\n\nEditor.selectAll = (cm) => {\n  cm.focus();\n  cm.setSelection({ ch: 0, line: 0 }, { ch: 0, line: cm.lineCount() });\n};\n\nEditor.calcRangePos = (cm, i, l = 0, o = {}) => {\n  let doc = cm.getDoc();\n  o.startPos = doc.posFromIndex(i);\n  o.endPos = doc.posFromIndex(i + l);\n  return o;\n};\n\nfunction createElement(type, className, content, parent) {\n  let element = document.createElement(type || \"div\");\n  if (className) {\n    element.className = className;\n  }\n  if (content) {\n    if (content instanceof HTMLElement) {\n      element.appendChild(content);\n    } else {\n      element.innerHTML = content;\n    }\n  }\n  if (parent) {\n    parent.appendChild(element);\n  }\n  return element;\n}\n"
  },
  {
    "path": "Public/js/views/error_message.js",
    "content": "\"use strict\";\n\nconst ErrorMessage = {};\nexport default ErrorMessage;\n\nErrorMessage.create = (message) => {\n  const container = document.createElement(\"div\");\n  container.classList.add(\"error-message\", \"d-flex\", \"flex-row\");\n  const wrapper = document.createElement(\"div\");\n  wrapper.classList.add(\"d-flex\", \"flex-row\", \"overflow-hidden\", \"w-100\");\n  container.appendChild(wrapper);\n  const iconWrapper = document.createElement(\"div\");\n  wrapper.appendChild(iconWrapper);\n  const icon = document.createElement(\"span\");\n  icon.classList.add(\n    \"fa-solid\",\n    \"fa-octagon-xmark\",\n    \"fa-xs\",\n    \"text-danger\",\n    \"px-2\",\n  );\n  iconWrapper.appendChild(icon);\n  const messageWrapper = document.createElement(\"div\");\n  messageWrapper.classList.add(\"text-nowrap\");\n  messageWrapper.textContent = message;\n  wrapper.appendChild(messageWrapper);\n\n  return container;\n};\n"
  },
  {
    "path": "Public/js/views/expression_field.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport tippy from \"tippy.js\";\n\nimport Editor from \"./editor\";\nimport ExpressionHighlighter from \"./expression_highlighter\";\nimport Utils from \"../misc/utils\";\n\nexport class ExpressionField extends EventDispatcher {\n  constructor(container) {\n    super();\n    this.container = container;\n    this.init(container);\n  }\n\n  get value() {\n    return this.editor.getValue();\n  }\n\n  set value(val) {\n    this.editor.setValue(val);\n  }\n\n  set tokens(tokens) {\n    this.expressionTokens = tokens;\n    this.highlighter.draw(tokens);\n    this.resetTooltips();\n  }\n\n  set error(error) {\n    if (error.length) {\n      let message = \"\";\n      if (typeof error === \"string\" || error instanceof String) {\n        const errorMessage = Utils.htmlSafe(error);\n        message = `<span class=\"fw-bolder text-danger\">Parse Error:</span> ${errorMessage}`;\n      } else {\n        message = error\n          .map((e) => {\n            const errorMessage = Utils.htmlSafe(e.message);\n            return `<span class=\"fw-bolder text-danger\">${e.behavior}:</span> ${errorMessage}`;\n          })\n          .join(\"<br>\");\n        this.highlighter.drawError(error);\n      }\n      this.errorMessageTooltip.setContent(message);\n      document\n        .getElementById(\"expression-field-error\")\n        .classList.remove(\"d-none\");\n    } else {\n      this.errorMessageTooltip.setContent(\"\");\n      document.getElementById(\"expression-field-error\").classList.add(\"d-none\");\n      this.highlighter.clearError();\n    }\n\n    tippy(\".exp-syntax-error\", {\n      allowHTML: true,\n      animation: false,\n      placement: \"bottom\",\n    });\n  }\n\n  init(container) {\n    this.editor = Editor.create(\n      container,\n      {\n        autofocus: true,\n        maxLength: 2500,\n        singleLine: true,\n        screenReaderLabel: \"Regular Expression Field\",\n      },\n      \"100%\",\n      \"100%\",\n    );\n\n    this.editor.on(\"change\", (editor, event) =>\n      this.onEditorChange(editor, event),\n    );\n\n    this.highlighter = new ExpressionHighlighter(this.editor);\n    this.expressionTokens = [];\n    this.activeTooltips = [];\n\n    this.errorMessageTooltip = tippy(\n      document.getElementById(\"expression-field-error\"),\n      {\n        ...tooltipProps,\n      },\n    );\n  }\n\n  setDefaultValue() {\n    this.editor.setValue(defaultValue);\n    this.editor.setCursor(this.editor.lineCount(), 0);\n  }\n\n  resetTooltips() {\n    for (const tooltip of this.activeTooltips) {\n      tooltip.destroy();\n    }\n    this.activeTooltips = tippy(tooltipSelector, {\n      ...tooltipProps,\n      onShow: (instance) => {\n        const index = instance.reference.dataset.tokenIndex;\n        if (index === undefined) {\n          return false;\n        }\n        const token = this.expressionTokens[index];\n        this.onHover(token, instance);\n        return false;\n      },\n    });\n  }\n\n  deferUpdate() {\n    Utils.defer(() => this.update(), \"ExpressionField.update\");\n  }\n\n  update() {\n    this.dispatchEvent(\"change\");\n  }\n\n  onEditorChange(editor, event) {\n    this.deferUpdate();\n  }\n\n  onHover(token, tippyInstance) {\n    this.hoverToken = token;\n\n    this.highlighter.drawHover(token);\n    this.dispatchEvent(\"hover\");\n\n    for (const tooltip of this.activeTooltips) {\n      if (tooltip !== tippyInstance) {\n        tooltip.destroy();\n      }\n    }\n    this.activeTooltips = tippy(tooltipSelector, {\n      ...tooltipProps,\n      onUntrigger: (instance) => {\n        this.highlighter.clearHover();\n        this.resetTooltips();\n        this.dispatchEvent(\"unhover\");\n      },\n    });\n  }\n}\n\nconst defaultValue = `(CREDIT|DEBIT)\\\\s+(\\\\d{1,2}/\\\\d{1,2}/\\\\d{4})`;\n\nconst tooltipSelector = \"#expression-field-container span[data-tippy-content]\";\nconst tooltipProps = {\n  allowHTML: true,\n  animation: false,\n  placement: \"bottom\",\n};\n"
  },
  {
    "path": "Public/js/views/expression_highlighter.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport { Reference } from \"../docs/reference\";\nimport Editor from \"./editor\";\n\nexport default class ExpressionHighlighter extends EventDispatcher {\n  constructor(editor) {\n    super();\n    this.editor = editor;\n    this.activeMarks = [];\n    this.hoverMarks = [];\n    this.widgets = [];\n  }\n\n  draw(tokens) {\n    this.clear();\n\n    const pre = ExpressionHighlighter.CSS_PREFIX;\n    const editor = this.editor;\n    editor.operation(() => {\n      const doc = editor.getDoc();\n      const marks = this.activeMarks;\n\n      for (const [i, token] of Object.entries(tokens)) {\n        const location = Editor.calcRangePos(\n          this.editor,\n          token.location.start,\n          token.location.end - token.location.start,\n        );\n\n        const tooltipAttr = (() => {\n          if (token.tooltip) {\n            const reference = Reference.get(\n              token.tooltip.category,\n              token.tooltip.key,\n            );\n            let title = reference ? reference.title : token.tooltip.category;\n            let detail = reference ? reference.detail : token.tooltip.key;\n            for (const [k, v] of Object.entries(token.tooltip.substitution)) {\n              title = title.replaceAll(k, v);\n              detail = detail.replaceAll(k, v);\n            }\n            return {\n              \"data-tippy-content\": makeTooltip(title, detail),\n            };\n          } else {\n            return {};\n          }\n        })();\n        marks.push(\n          doc.markText(location.startPos, location.endPos, {\n            className: `${token.classes.map((c) => `${pre}-${c}`).join(\" \")}`,\n            attributes: {\n              ...tooltipAttr,\n              \"data-token-index\": i,\n            },\n          }),\n        );\n      }\n    });\n  }\n\n  drawError(errors) {\n    this.clearError();\n\n    const pre = ExpressionHighlighter.CSS_PREFIX;\n    const editor = this.editor;\n    editor.operation(() => {\n      for (const error of errors) {\n        const location = Editor.calcRangePos(\n          this.editor,\n          error.location.start,\n          error.location.end - error.location.start,\n        );\n        const widget = document.createElement(\"span\");\n        widget.className = `${pre}-syntax-error`;\n\n        widget.style.height = `5px`;\n        widget.style.zIndex = \"10\";\n        widget.setAttribute(\n          \"data-tippy-content\",\n          `<span class=\"fw-bolder text-danger\">${error.behavior}:</span> ${error.message}`,\n        );\n\n        editor.addWidget(location.startPos, widget);\n        const startCoords = editor.charCoords(location.startPos, \"local\");\n        const endCoords = editor.charCoords(location.endPos, \"local\");\n        widget.style.left = `${startCoords.left + 1}px`;\n        widget.style.top = `${startCoords.bottom - 1}px`;\n        if (error.location.start === error.location.end) {\n          widget.style.width = `${editor.defaultCharWidth()}px`;\n        } else {\n          widget.style.width = `${endCoords.left - startCoords.left - 2}px`;\n        }\n\n        this.widgets.push(widget);\n      }\n    });\n  }\n\n  clear() {\n    this.editor.operation(() => {\n      for (const mark of this.activeMarks) {\n        mark.clear();\n      }\n      this.activeMarks.length = 0;\n    });\n  }\n\n  clearError() {\n    this.editor.operation(() => {\n      for (const widget of this.widgets) {\n        widget.parentNode.removeChild(widget);\n      }\n      this.widgets.length = 0;\n    });\n  }\n\n  drawHover(token) {\n    const selection = token.selection;\n    const related = token.related;\n    if ((!selection && !related) || this.hoverMarks.length) {\n      return;\n    }\n\n    this.clearHover();\n\n    if (selection) {\n      this.drawBorder(selection, \"selected\");\n    }\n    if (related) {\n      this.drawBorder(related.location, \"related\");\n    }\n  }\n\n  drawBorder(range, className) {\n    const editor = this.editor;\n    const doc = editor.getDoc();\n\n    const pre = ExpressionHighlighter.CSS_PREFIX;\n    const left = Editor.calcRangePos(this.editor, range.start, 1);\n    const location = Editor.calcRangePos(\n      this.editor,\n      range.start,\n      range.end - range.start,\n    );\n    const right = Editor.calcRangePos(this.editor, range.end - 1, 1);\n    this.hoverMarks.push(\n      doc.markText(left.startPos, left.endPos, {\n        className: `${pre}-${className}-left`,\n      }),\n    );\n    this.hoverMarks.push(\n      doc.markText(location.startPos, location.endPos, {\n        className: `${pre}-${className}`,\n      }),\n    );\n    this.hoverMarks.push(\n      doc.markText(right.startPos, right.endPos, {\n        className: `${pre}-${className}-right`,\n      }),\n    );\n  }\n\n  clearHover() {\n    this.editor.operation(() => {\n      for (const mark of this.hoverMarks) {\n        mark.clear();\n      }\n      this.hoverMarks.length = 0;\n    });\n  }\n}\n\nfunction makeTooltip(label, desc) {\n  return `<div class=\"text-start\"><span class=\"fw-bolder\">${label}.</span><span> ${desc}</span></div>`;\n}\n\nExpressionHighlighter.CSS_PREFIX = \"exp\";\n"
  },
  {
    "path": "Public/js/views/match_options.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\n\nexport class MatchOptions extends EventDispatcher {\n  constructor() {\n    super();\n    this.init();\n  }\n\n  init() {\n    document.querySelectorAll(\".match-options-item\").forEach((listItem) => {\n      listItem.addEventListener(\"click\", (event) => {\n        listItem.classList.toggle(\"active-tick\");\n        this.dispatchEvent(\"change\");\n      });\n    });\n  }\n\n  get value() {\n    const options = [];\n    document.querySelectorAll(\".match-options-item\").forEach((listItem) => {\n      if (listItem.classList.contains(\"active-tick\")) {\n        options.push(listItem.dataset.value);\n      }\n    });\n    return options;\n  }\n\n  set value(options) {\n    document.querySelectorAll(\".match-options-item\").forEach((listItem) => {\n      if (options.includes(listItem.dataset.value)) {\n        listItem.classList.add(\"active-tick\");\n      }\n    });\n  }\n\n  setDefaultValue() {\n    this.value = [\"g\", \"m\"];\n  }\n}\n"
  },
  {
    "path": "Public/js/views/test_editor.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport tippy from \"tippy.js\";\n\nimport Editor from \"./editor\";\nimport TestHighlighter from \"./test_highlighter\";\nimport ErrorMessage from \"./error_message\";\nimport Utils from \"../misc/utils\";\n\nconst defaultValue = `KIND      DATE          INSTITUTION                AMOUNT\n----------------------------------------------------------------\nCREDIT    03/01/2022    Payroll from employer      $200.23\nCREDIT    03/03/2022    Suspect A                  $2,000,000.00\nDEBIT     03/03/2022    Ted's Pet Rock Sanctuary   $2,000,000.00\nDEBIT     03/05/2022    Doug's Dugout Dogs         $33.27\nDEBIT     06/03/2022    Oxford Comma Supply Ltd.   £57.33\n`;\n\nexport class TestEditor extends EventDispatcher {\n  static defaultValue = defaultValue;\n\n  constructor(container) {\n    super();\n    this.container = container;\n    this.init(container);\n  }\n\n  get value() {\n    return this.editor.getValue();\n  }\n\n  set value(val) {\n    this.editor.setValue(val);\n  }\n\n  set error(error) {\n    const editor = this.editor;\n    const widgets = this.widgets;\n\n    editor.operation(function () {\n      for (const widget of widgets) {\n        editor.removeLineWidget(widget);\n      }\n      widgets.length = 0;\n\n      if (!error) {\n        return;\n      }\n\n      widgets.push(\n        editor.addLineWidget(0, ErrorMessage.create(error), {\n          coverGutter: false,\n          noHScroll: true,\n          above: true,\n        }),\n      );\n    });\n  }\n\n  set matches(matches) {\n    this.highlighter.draw(matches);\n    tippy(\".test-editor-container span[data-tippy-content]\", {\n      allowHTML: true,\n      animation: false,\n      placement: \"bottom\",\n    });\n  }\n\n  init(container) {\n    const editor = Editor.create(\n      container,\n      { lineWrapping: true, screenReaderLabel: \"Pattern Test View\" },\n      \"100%\",\n      \"100%\",\n    );\n    this.editor = editor;\n\n    this.highlighter = new TestHighlighter(editor);\n    this.widgets = [];\n\n    editor.on(\"change\", (editor, event) => this.onEditorChange(editor, event));\n  }\n\n  setDefaultValue() {\n    this.editor.setValue(defaultValue);\n  }\n\n  deferUpdate() {\n    Utils.defer(() => this.update(), \"TestEditor.update\");\n  }\n\n  update() {\n    this.dispatchEvent(\"change\");\n  }\n\n  onEditorChange(editor, event) {\n    this.deferUpdate();\n  }\n}\n"
  },
  {
    "path": "Public/js/views/test_highlighter.js",
    "content": "\"use strict\";\n\nimport { EventDispatcher } from \"@createjs/easeljs\";\nimport Editor from \"./editor\";\nimport Utils from \"../misc/utils\";\n\nexport default class TestHighlighter extends EventDispatcher {\n  constructor(editor) {\n    super();\n\n    this.editor = editor;\n    this.activeMarks = [];\n    this.widgets = [];\n\n    this.textHeight = editor.defaultTextHeight();\n  }\n\n  draw(tokens) {\n    this.clear();\n\n    const editor = this.editor;\n    editor.operation(() => {\n      const doc = editor.getDoc();\n      const marks = this.activeMarks;\n\n      for (const token of tokens) {\n        const match = Utils.htmlSafe(token.value);\n        let tooltip = `<div class=\"text-start font-monospace\">\n        <div><span class=\"fw-bolder\">match:</span> ${match}</div>\n        <div class=\"fw-bolder\">range: ${token.location.start}-${token.location.end}</div>\n        </div>`;\n\n        if (token.captures.length) {\n          tooltip += \"<hr>\";\n          for (const [i, capture] of token.captures.entries()) {\n            const value = Utils.htmlSafe(capture.value || \"\");\n            const name = Utils.htmlSafe(capture.name || \"\");\n            tooltip += `<div class=\"text-start font-monospace\">\n            <div><span class=\"fw-bolder\">group #${i + 1}${\n              name ? ` ${name}` : \"\"\n            }:</span> ${value === \"\" ? \"empty string\" : value}</div>\n            </div>`;\n          }\n        }\n\n        if (token.location.start < token.location.end) {\n          const location = Editor.calcRangePos(\n            editor,\n            token.location.start,\n            token.location.end - token.location.start,\n          );\n          marks.push(\n            doc.markText(location.startPos, location.endPos, {\n              className: \"match-char\",\n              attributes: {\n                \"data-tippy-content\": tooltip,\n              },\n            }),\n          );\n        } else {\n          const location = Editor.calcRangePos(editor, token.location.start, 1);\n\n          if (\n            location.startPos.line === location.endPos.line &&\n            location.startPos.ch === location.endPos.ch\n          ) {\n            this.addLeftAnchor(location, { \"data-tippy-content\": tooltip });\n          }\n          if (location.startPos.line === location.endPos.line) {\n            if (location.startPos.ch < location.endPos.ch) {\n              marks.push(\n                doc.markText(location.startPos, location.endPos, {\n                  className: \"match-left\",\n                  attributes: {\n                    \"data-tippy-content\": tooltip,\n                  },\n                }),\n              );\n            } else {\n              // this.addRightAnchor(location, { \"data-tippy-content\": tooltip });\n            }\n          } else {\n            if (location.startPos.ch === 0 && location.endPos.ch === 0) {\n              this.addLeftAnchor(location, { \"data-tippy-content\": tooltip });\n            } else {\n              this.addRightAnchor(location, { \"data-tippy-content\": tooltip });\n            }\n          }\n        }\n      }\n    });\n  }\n\n  addLeftAnchor(location, attributes = {}) {\n    const widget = document.createElement(\"span\");\n    widget.className = \"match-left\";\n\n    widget.style.height = `${this.textHeight * 1.5}px`;\n    widget.style.width = \"1px\";\n    widget.style.zIndex = \"10\";\n\n    for (const [key, value] of Object.entries(attributes)) {\n      widget.setAttribute(key, value);\n    }\n\n    this.editor.addWidget(location.startPos, widget);\n\n    const coords = this.editor.charCoords(location.startPos, \"local\");\n    widget.style.left = `${coords.left}px`;\n    widget.style.top = `${coords.top + 2}px`;\n\n    this.widgets.push(widget);\n  }\n\n  addRightAnchor(location, attributes = {}) {\n    const widget = document.createElement(\"span\");\n    widget.className = \"match-right\";\n\n    widget.style.height = `${this.textHeight * 1.5}px`;\n    widget.style.width = \"1px\";\n    widget.style.zIndex = \"10\";\n\n    for (const [key, value] of Object.entries(attributes)) {\n      widget.setAttribute(key, value);\n    }\n\n    this.editor.addWidget(location.endPos, widget);\n\n    const coords = this.editor.charCoords(location.startPos, \"local\");\n    widget.style.left = `${coords.left}px`;\n    widget.style.top = `${coords.top + 2}px`;\n\n    this.widgets.push(widget);\n  }\n\n  clear() {\n    this.editor.operation(() => {\n      let marks = this.activeMarks;\n      for (var i = 0, l = marks.length; i < l; i++) {\n        marks[i].clear();\n      }\n      marks.length = 0;\n\n      for (const widget of this.widgets) {\n        widget.parentNode.removeChild(widget);\n      }\n      this.widgets.length = 0;\n    });\n  }\n}\n"
  },
  {
    "path": "Public/robots.txt",
    "content": ""
  },
  {
    "path": "Public/scss/default.scss",
    "content": "$table-cell-padding-y-sm: 0.05rem;\n\n@import \"bootstrap/scss/functions\";\n@import \"bootstrap/scss/variables\";\n@import \"bootstrap/scss/variables-dark\";\n@import \"bootstrap/scss/mixins\";\n@import \"bootstrap/scss/maps\";\n@import \"bootstrap/scss/utilities\";\n\n@import \"bootstrap/scss/root\";\n@import \"bootstrap/scss/reboot\";\n@import \"bootstrap/scss/type\";\n@import \"bootstrap/scss/containers\";\n@import \"bootstrap/scss/grid\";\n@import \"bootstrap/scss/tables\";\n@import \"bootstrap/scss/forms\";\n@import \"bootstrap/scss/buttons\";\n@import \"bootstrap/scss/transitions\";\n@import \"bootstrap/scss/dropdown\";\n@import \"bootstrap/scss/badge\";\n@import \"bootstrap/scss/close\";\n@import \"bootstrap/scss/modal\";\n@import \"bootstrap/scss/tooltip\";\n\n@import \"bootstrap/scss/helpers\";\n@import \"bootstrap/scss/utilities/api\";\n"
  },
  {
    "path": "README.md",
    "content": "<p>\n<a href=\"https://github.com/kishikawakatsumi/swiftregex/actions\">\n<img src=\"https://github.com/kishikawakatsumi/swiftregex/workflows/CI/badge.svg\">\n</a>\n<img src=\"https://img.shields.io/badge/os-macOS/Linux-green.svg?style=flat\" alt=\"macOS/Linux\">\n<a href=\"http://swift.org\">\n<img src=\"https://img.shields.io/badge/swift-5.7-orange.svg?style=flat\" alt=\"Swift 5.7 Compatible\">\n</a>\n<a href=\"https://github.com/kishikawakatsumi/swiftregex/blob/master/LICENSE\">\n<img src=\"https://img.shields.io/badge/license-MIT-yellow.svg?style=flat\" alt=\"MIT\">\n</a>\n</p>\n\n# Swift Regex\n\nRegular Expression Tester with highlighting for Swift Regex. Quickly test and debug your regex and Regex Builder.\n\n<a href=\"https://swiftregex.com\"><img width=\"1280\" alt=\"Screen Shot\" src=\"https://user-images.githubusercontent.com/40610/176885327-6b7b9b42-a9d8-4ac0-bf7d-4df6be021d89.png\"></a>\n\n<a href=\"https://swiftregex.com\"><img width=\"1280\" alt=\"Screen Shot\" src=\"https://user-images.githubusercontent.com/40610/176885334-dec898ce-a0b8-46a4-8ce0-81e8892d2e6e.png\"></a>\n\nhttps://swiftregex.com\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\nFor security related problems, please don't use the public issue tracker, but email [@kishikawakatsumi](https://github.com/kishikawakatsumi).\n"
  },
  {
    "path": "Sources/App/Debugger/Context.swift",
    "content": "import Foundation\n\nextension Debugger {\n  class Context {\n    var instructions: [String] = []\n    var programCounter = 0\n\n    var stepCount = 0\n    var breakPoint: Int?\n\n    var start: Int = 0\n    var current: Int = 0\n    var failurePosition: Int = 0\n\n    var totalCycleCount = 0\n    var resets = 0\n    var backtracks = 0\n\n    init(stepCount: Int = 0, breakPoint: Int? = nil) {\n      self.stepCount = stepCount\n      self.breakPoint = breakPoint\n    }\n\n    func reset() {\n      instructions = []\n      programCounter = 0\n\n      stepCount = 0\n      breakPoint = nil\n\n      start = 0\n      current = 0\n      failurePosition = 0\n\n      totalCycleCount = 0\n      resets = 0\n      backtracks = 0\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/App/Debugger/Debugger.swift",
    "content": "import Foundation\n\n@testable import _RegexParser\n@testable @_spi(RegexBenchmark) import _StringProcessing\n\nstruct Debugger {\n  func run(pattern: String, text: String, matchingOptions: [String] = [], context: Debugger.Context) throws {\n    let ast = try _RegexParser.parse(pattern, .traditional)\n\n    var sequence = [AST.MatchingOption]()\n    if matchingOptions.contains(\"i\") {\n      sequence.append(.init(.caseInsensitive, location: .fake))\n    }\n    if matchingOptions.contains(\"s\") {\n      sequence.append(.init(.singleLine, location: .fake))\n    }\n    if matchingOptions.contains(\"asciiOnlyWordCharacters\") {\n      sequence.append(.init(.asciiOnlyWord, location: .fake))\n    }\n    if matchingOptions.contains(\"asciiOnlyDigits\") {\n      sequence.append(.init(.asciiOnlyDigit, location: .fake))\n    }\n    if matchingOptions.contains(\"asciiOnlyWhitespace\") {\n      sequence.append(.init(.asciiOnlySpace, location: .fake))\n    }\n    if matchingOptions.contains(\"asciiOnlyCharacterClasses\") {\n      sequence.append(.init(.asciiOnlyPOSIXProps, location: .fake))\n    }\n    sequence.append(.init(.graphemeClusterSemantics, location: .fake))\n\n    var options = MatchingOptions()\n    options.apply(AST.MatchingOptionSequence(adding: sequence))\n\n    let program = try compile(ast, options: options)\n\n    context.instructions = program.instructions.map {\n      $0.description\n    }\n\n    let inputRange = text.startIndex..<text.endIndex\n\n    var cpu = Processor(\n      program: program,\n      input: text,\n      subjectBounds: inputRange,\n      searchBounds: inputRange,\n      matchMode: .partialFromFront\n    )\n\n    do {\n      _ = try Executor<AnyRegexOutput>._firstMatch(\n        program,\n        using: &cpu,\n        context: context\n      )\n    } catch {}\n  }\n\n  func compile(_ ast: AST, options: MatchingOptions) throws -> MEProgram {\n    let compiler = Compiler(tree: ast.dslTree, compileOptions: [.enableMetrics])\n    compiler.options = options\n    return try compiler.emit()\n  }\n\n  struct Metrics: Codable {\n    var instructions: [String]\n    var programCounter: Int\n\n    var stepCount: Int\n    var step: Int\n\n    var totalCycleCount: Int\n    var resets: Int\n    var backtracks: Int\n\n    var traces: [Trace]\n    var failure: Location\n  }\n\n  struct Trace: Codable {\n    let location: Location\n  }\n\n  struct Location: Codable {\n    let start: Int\n    let end: Int\n  }\n}\n"
  },
  {
    "path": "Sources/App/Debugger/Executor.swift",
    "content": "@testable import _RegexParser\n@testable import _StringProcessing\n\nenum Executor<Output> {\n  static func prefixMatch(\n    _ program: MEProgram,\n    _ input: String,\n    subjectBounds: Range<String.Index>,\n    searchBounds: Range<String.Index>,\n    context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    try Executor._run(\n      program,\n      input,\n      subjectBounds: subjectBounds,\n      searchBounds: searchBounds,\n      mode: .partialFromFront,\n      context: context\n    )\n  }\n\n  static func wholeMatch(\n    _ program: MEProgram,\n    _ input: String,\n    subjectBounds: Range<String.Index>,\n    searchBounds: Range<String.Index>,\n    context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    try Executor._run(\n      program,\n      input,\n      subjectBounds: subjectBounds,\n      searchBounds: searchBounds,\n      mode: .wholeString,\n      context: context\n    )\n  }\n\n  static func firstMatch(\n    _ program: MEProgram,\n    _ input: String,\n    subjectBounds: Range<String.Index>,\n    searchBounds: Range<String.Index>,\n    context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    var cpu = Processor(\n      program: program,\n      input: input,\n      subjectBounds: subjectBounds,\n      searchBounds: searchBounds,\n      matchMode: .partialFromFront\n    )\n    return try Executor._firstMatch(\n      program,\n      using: &cpu,\n      context: context\n    )\n  }\n\n  static func _firstMatch(\n    _ program: MEProgram,\n    using cpu: inout Processor,\n    context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    let isGraphemeSemantic = program.initialOptions.semanticLevel == .graphemeCluster\n\n    var low = cpu.searchBounds.lowerBound\n    let high = cpu.searchBounds.upperBound\n    while true {\n      if let m = try Executor._run(program, &cpu, context) {\n        return m\n      }\n      // Fast-path for start-anchored regex\n      if program.canOnlyMatchAtStart {\n        return nil\n      }\n      if low == high { return nil }\n      if isGraphemeSemantic {\n        cpu.input.formIndex(after: &low)\n      } else {\n        cpu.input.unicodeScalars.formIndex(after: &low)\n      }\n      guard low <= high else {\n        return nil\n      }\n      cpu.reset(currentPosition: low, searchBounds: cpu.searchBounds)\n    }\n  }\n}\n\nextension Executor {\n  static func _run(\n    _ program: MEProgram,\n    _ input: String,\n    subjectBounds: Range<String.Index>,\n    searchBounds: Range<String.Index>,\n    mode: MatchMode,\n    context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    var cpu = Processor(\n      program: program,\n      input: input,\n      subjectBounds: subjectBounds,\n      searchBounds: searchBounds,\n      matchMode: mode)\n    return try _run(program, &cpu, context)\n  }\n\n  static func _run(\n    _ program: MEProgram,\n    _ cpu: inout Processor,\n    _ context: Debugger.Context\n  ) throws -> Regex<Output>.Match? {\n    let startPosition = cpu.currentPosition\n    context.start = startPosition.utf16Offset(in: cpu.input)\n    guard let endIdx = try cpu.run(context) else {\n      return nil\n    }\n    let range = startPosition..<endIdx\n\n    let anyRegexOutput = AnyRegexOutput(\n      input: cpu.input, elements: []\n    )\n    return .init(anyRegexOutput: anyRegexOutput, range: range)\n  }\n}\n\nextension Processor {\n  fileprivate mutating func run(_ context: Debugger.Context) throws -> Input.Index? {\n    if self.state == .fail {\n      if let e = failureReason {\n        throw e\n      }\n      return nil\n    }\n    assert(isReset())\n    while true {\n      context.programCounter = controller.pc.rawValue\n\n      switch self.state {\n      case .accept:\n        return self.currentPosition\n      case .fail:\n        if let e = failureReason {\n          throw e\n        }\n        return nil\n      case .inProgress:\n        let failurePosition = currentPosition.utf16Offset(in: input)\n\n        self.cycle()\n\n        context.stepCount += 1\n        context.current = currentPosition.utf16Offset(in: input)\n        context.failurePosition = failurePosition\n        #if PROCESSOR_MEASUREMENTS_ENABLED\n          context.totalCycleCount = metrics.cycleCount\n          context.resets = metrics.resets\n          context.backtracks = metrics.backtracks\n        #endif\n        if context.stepCount == context.breakPoint {\n          throw CancellationError()\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/App/Middlewares/CommonErrorMiddleware.swift",
    "content": "import Vapor\n\nfinal class CommonErrorMiddleware: Middleware {\n  func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {\n    return next.respond(to: request).flatMapError { (error) in\n      let headers: HTTPHeaders\n      let status: HTTPResponseStatus\n      switch error {\n      case let abort as AbortError:\n        headers = abort.headers\n        status = abort.status\n      default:\n        headers = [:]\n        status = .internalServerError\n      }\n\n      let errorTitles: [UInt: String] = [\n        400: \"Bad Request\",\n        401: \"Unauthorized\",\n        403: \"Access Denied\",\n        404: \"Resource not found\",\n        500: \"Webservice currently unavailable\",\n        503: \"Webservice currently unavailable\",\n      ]\n\n      let errorReasons: [UInt: String] = [\n        400: \"The server cannot process the request due to something that is perceived to be a client error.\",\n        401: \"The requested resource requires an authentication.\",\n        403: \"The requested resource requires an authentication.\",\n        404: \"The requested resource could not be found but may be available again in the future.\",\n        500: \"An unexpected condition was encountered. Our service team has been dispatched to bring it back online.\",\n        503: \"We&#39;ve got some trouble with our backend upstream cluster. Our service team has been dispatched to bring it back online.\",\n      ]\n\n      if request.headers[.accept].map({ $0.lowercased() }).contains(\"application/json\") {\n        return request.eventLoop.makeSucceededFuture([\"error\": status.code])\n          .encodeResponse(status: status, headers: headers, for: request)\n      } else {\n        return request.view.render(\n          \"error\",\n          [\n            \"title\": \"We've got some trouble\",\n            \"error\": errorTitles[status.code],\n            \"reason\": errorReasons[status.code],\n            \"status\": \"\\(status.code)\",\n          ]\n        )\n        .encodeResponse(status: status, headers: headers, for: request)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/App/Middlewares/CustomHeaderMiddleware.swift",
    "content": "import Vapor\n\nfinal class CustomHeaderMiddleware: Middleware {\n  func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {\n    return next.respond(to: request).map { (response) in\n      response.headers.add(name: \"X-Frame-Options\", value: \"DENY\")\n      response.headers.add(name: \"Permissions-Policy\", value: \"interest-cohort=()\")\n      return response\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/App/Models/ExecRequest.swift",
    "content": "import Foundation\n\nstruct ExecRequest: Codable {\n  let method: RequestMethod\n  let pattern: String\n  let text: String\n  let matchOptions: [String]\n  let step: String?\n}\n\nenum RequestMethod: String, Codable {\n  case parseExpression\n  case convertToDSL\n  case match\n  case debug\n}\n"
  },
  {
    "path": "Sources/App/Models/ResultResponse.swift",
    "content": "import Foundation\nimport Vapor\n\nstruct ResultResponse: Content {\n  let method: RequestMethod\n  let result: String\n  let error: String\n}\n"
  },
  {
    "path": "Sources/App/configure.swift",
    "content": "import Leaf\nimport Vapor\n\npublic func configure(_ app: Application) async throws {\n  app.middleware = Middlewares()\n  app.middleware.use(CommonErrorMiddleware())\n  app.middleware.use(CustomHeaderMiddleware())\n\n  let publicDirectory = \"\\(app.directory.publicDirectory)/dist\"\n  app.middleware.use(FileMiddleware(publicDirectory: publicDirectory))\n\n  app.http.server.configuration.port = Environment.process.PORT.flatMap { Int($0) } ?? 8080\n  app.http.server.configuration.requestDecompression = .enabled\n  app.http.server.configuration.responseCompression = .enabled\n  app.http.server.configuration.supportPipelining = true\n\n  app.views.use(.leaf)\n  app.leaf.configuration.rootDirectory = publicDirectory\n  app.leaf.cache.isEnabled = app.environment.isRelease\n\n  try routes(app)\n}\n"
  },
  {
    "path": "Sources/App/entrypoint.swift",
    "content": "import Vapor\n\n@main\nenum Entrypoint {\n  static func main() async throws {\n    var env = try Environment.detect()\n    try LoggingSystem.bootstrap(from: &env)\n\n    let app = try await Application.make(env)\n\n    do {\n      try await configure(app)\n      try await app.execute()\n    } catch {\n      app.logger.report(error: error)\n      try? await app.asyncShutdown()\n      throw error\n    }\n    try await app.asyncShutdown()\n  }\n}\n"
  },
  {
    "path": "Sources/App/routes.swift",
    "content": "import Vapor\n\nfunc routes(_ app: Application) throws {\n  app.get(\"health\") { _ in [\"status\": \"pass\"] }\n  app.get(\"healthz\") { _ in [\"status\": \"pass\"] }\n\n  app.get { (req) in req.view.render(\"index\") }\n\n  app.webSocket(\"api\", \"ws\") { (req, ws) in\n    ws.onBinary { (ws, buffer) in\n      do {\n        guard let data = buffer.getData(at: 0, length: buffer.readableBytes) else { return }\n\n        let decoder = JSONDecoder()\n        let request = try decoder.decode(ExecRequest.self, from: data)\n\n        let encoder = JSONEncoder()\n\n        switch request.method {\n        case .parseExpression:\n          let pattern = request.pattern\n          let matchOptions = request.matchOptions\n          let response = try parseExpression(pattern: pattern, matchOptions: matchOptions)\n\n          if let message = String(data: try encoder.encode(response), encoding: .utf8) {\n            ws.send(message)\n          }\n        case .convertToDSL:\n          let pattern = request.pattern\n          let matchOptions = request.matchOptions\n          let response = try convertToDSL(pattern: pattern, matchOptions: matchOptions)\n\n          if let message = String(data: try encoder.encode(response), encoding: .utf8) {\n            ws.send(message)\n          }\n        case .match:\n          let pattern = request.pattern\n          let text = request.text\n          let matchOptions = request.matchOptions\n          let response = try match(pattern: pattern, text: text, matchOptions: matchOptions)\n\n          if let message = String(data: try encoder.encode(response), encoding: .utf8) {\n            ws.send(message)\n          }\n        case .debug:\n          let pattern = request.pattern\n          let text = request.text\n          let matchOptions = request.matchOptions\n          let step = request.step\n          let response = try debug(pattern: pattern, text: text, matchOptions: matchOptions, step: step)\n\n          if let message = String(data: try encoder.encode(response), encoding: .utf8) {\n            ws.send(message)\n          }\n        }\n      } catch {\n        req.logger.error(\"\\(error)\")\n      }\n    }\n  }\n\n  app.on(.POST, \"api\", \"rest\", \"parseExpression\", body: .collect(maxSize: \"1mb\")) { (req) -> ResultResponse in\n    guard let request = try? req.content.decode(ExecRequest.self) else {\n      throw Abort(.badRequest)\n    }\n\n    let pattern = request.pattern\n    let matchOptions = request.matchOptions\n    let response = try parseExpression(pattern: pattern, matchOptions: matchOptions)\n\n    return response\n  }\n\n  app.on(.POST, \"api\", \"rest\", \"convertToDSL\", body: .collect(maxSize: \"1mb\")) { (req) -> ResultResponse in\n    guard let request = try? req.content.decode(ExecRequest.self) else {\n      throw Abort(.badRequest)\n    }\n\n    let pattern = request.pattern\n    let matchOptions = request.matchOptions\n    let response = try convertToDSL(pattern: pattern, matchOptions: matchOptions)\n\n    return response\n  }\n\n  app.on(.POST, \"api\", \"rest\", \"match\", body: .collect(maxSize: \"1mb\")) { (req) -> ResultResponse in\n    guard let request = try? req.content.decode(ExecRequest.self) else {\n      throw Abort(.badRequest)\n    }\n\n    let pattern = request.pattern\n    let text = request.text\n    let matchOptions = request.matchOptions\n    let response = try match(pattern: pattern, text: text, matchOptions: matchOptions)\n\n    return response\n  }\n\n  app.on(.POST, \"api\", \"rest\", \"debug\", body: .collect(maxSize: \"1mb\")) { (req) -> ResultResponse in\n    guard let request = try? req.content.decode(ExecRequest.self) else {\n      throw Abort(.badRequest)\n    }\n\n    let pattern = request.pattern\n    let text = request.text\n    let matchOptions = request.matchOptions\n    let step = request.step\n\n    let response = try debug(pattern: pattern, text: text, matchOptions: matchOptions, step: step)\n\n    return response\n  }\n\n  func parseExpression(pattern: String, matchOptions: [String]) throws -> ResultResponse {\n    let (stdout, stderr) = try exec(command: \"ExpressionParser\", arguments: pattern, matchOptions.joined(separator: \",\"))\n    return ResultResponse(method: .parseExpression, result: stdout, error: stderr)\n  }\n\n  func convertToDSL(pattern: String, matchOptions: [String]) throws -> ResultResponse {\n    let (stdout, stderr) = try exec(command: \"DSLConverter\", arguments: pattern, matchOptions.joined(separator: \",\"))\n    return ResultResponse(method: .convertToDSL, result: stdout, error: stderr)\n  }\n\n  func match(pattern: String, text: String, matchOptions: [String]) throws -> ResultResponse {\n    let (stdout, stderr) = try exec(command: \"Matcher\", arguments: pattern, text, matchOptions.joined(separator: \",\"))\n    return ResultResponse(method: .match, result: stdout, error: stderr)\n  }\n\n  func debug(pattern: String, text: String, matchOptions: [String], step: String?) throws -> ResultResponse {\n    let context = Debugger.Context()\n\n    func run(pattern: String, text: String, matchingOptions: [String] = [], until step: Int? = nil) throws {\n      context.reset()\n      context.breakPoint = step\n\n      let debugger = Debugger()\n      try debugger.run(pattern: pattern, text: text, matchingOptions: matchingOptions, context: context)\n    }\n\n    let breakPoint: Int?\n    if let step {\n      breakPoint = Int(step)\n    } else {\n      breakPoint = nil\n    }\n    try run(pattern: pattern, text: text, matchingOptions: matchOptions)\n    let stepCount = context.stepCount\n\n    try run(pattern: pattern, text: text, matchingOptions: matchOptions, until: breakPoint)\n\n    let metrics = Debugger.Metrics(\n      instructions: context.instructions,\n      programCounter: context.programCounter,\n      stepCount: stepCount,\n      step: breakPoint ?? 1,\n      totalCycleCount: context.totalCycleCount,\n      resets: context.resets,\n      backtracks: context.backtracks,\n      traces: [\n        Debugger.Trace(\n          location: Debugger.Location(\n            start: context.start,\n            end: context.current\n          )\n        )\n      ],\n      failure: Debugger.Location(start: context.current, end: context.failurePosition),\n    )\n    let data = try JSONEncoder().encode(metrics)\n\n    let result = String(data: data, encoding: .utf8) ?? \"\"\n    let response = ResultResponse(method: .debug, result: result, error: \"\")\n\n    return response\n  }\n\n  func exec(command: String, arguments: String...) throws -> (stdout: String, stderr: String) {\n    let process = Process()\n    let executableURL = URL(\n      fileURLWithPath: app.directory.workingDirectory\n    )\n    .appendingPathComponent(command)\n\n    process.executableURL = executableURL\n    process.arguments = arguments\n\n    let standardOutput = Pipe()\n    let standardError = Pipe()\n    process.standardOutput = standardOutput\n    process.standardError = standardError\n\n    var stdoutData = Data()\n    var stderrData = Data()\n    let group = DispatchGroup()\n\n    group.enter()\n    standardOutput.fileHandleForReading.readabilityHandler = { handle in\n      let chunk = handle.availableData\n      if chunk.isEmpty {\n        standardOutput.fileHandleForReading.readabilityHandler = nil\n        group.leave()\n      } else {\n        stdoutData.append(chunk)\n      }\n    }\n\n    group.enter()\n    standardError.fileHandleForReading.readabilityHandler = { handle in\n      let chunk = handle.availableData\n      if chunk.isEmpty {\n        standardError.fileHandleForReading.readabilityHandler = nil\n        group.leave()\n      } else {\n        stderrData.append(chunk)\n      }\n    }\n\n    try process.run()\n\n    group.wait()\n    process.waitUntilExit()\n\n    guard let stdout = String(data: stdoutData, encoding: .utf8) else {\n      throw Abort(.internalServerError)\n    }\n\n    guard let stderr = String(data: stderrData, encoding: .utf8) else {\n      throw Abort(.internalServerError)\n    }\n\n    return (stdout, stderr)\n  }\n}\n"
  },
  {
    "path": "Sources/DSLConverter/DSLConverter.swift",
    "content": "import Foundation\n\n@testable import _RegexParser\n@testable @_spi(RegexBuilder) import _StringProcessing\n@testable @_spi(PatternConverter) import _StringProcessing\n\nclass DSLConverter {\n  private(set) var diagnostics: Diagnostics?\n\n  func convert(_ pattern: String, matchingOptions: [String] = []) throws -> String {\n    let ast = _RegexParser.parseWithRecovery(pattern, .traditional)\n    diagnostics = ast.diags\n\n    var builderDSL = renderAsBuilderDSL(ast: ast)\n    if builderDSL.last == \"\\n\" {\n      builderDSL = String(builderDSL.dropLast())\n    }\n\n    if matchingOptions.contains(\"m\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".anchorsMatchLineEndings()\")\n    }\n    if matchingOptions.contains(\"i\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".ignoresCase()\")\n    }\n    if matchingOptions.contains(\"s\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".dotMatchesNewlines()\")\n    }\n    if matchingOptions.contains(\"asciiOnlyWordCharacters\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".asciiOnlyWordCharacters()\")\n    }\n    if matchingOptions.contains(\"asciiOnlyDigits\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".asciiOnlyDigits()\")\n    }\n    if matchingOptions.contains(\"asciiOnlyWhitespace\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".asciiOnlyWhitespace()\")\n    }\n    if matchingOptions.contains(\"asciiOnlyCharacterClasses\") {\n      builderDSL.append(\"\\n\")\n      builderDSL.append(\".asciiOnlyCharacterClasses()\")\n    }\n    builderDSL.append(\"\\n\")\n\n    return builderDSL\n  }\n}\n"
  },
  {
    "path": "Sources/DSLConverter/Main.swift",
    "content": "import Foundation\n\n@main\nstruct Main {\n  static func main() throws {\n    do {\n      let pattern = CommandLine.arguments[1]\n      let matchingOptions = CommandLine.arguments[2]\n        .split(separator: \",\", omittingEmptySubsequences: true)\n        .map { String($0) }\n\n      let converter = DSLConverter()\n      let builderDSL = try converter.convert(pattern, matchingOptions: matchingOptions)\n\n      let data = try JSONEncoder().encode(builderDSL)\n      print(String(data: data, encoding: .utf8) ?? \"\")\n\n      if let diagnostics = converter.diagnostics {\n        let errors = diagnostics.diags.map {\n          let location = $0.location\n          let (start, end) = (location.start, location.end)\n\n          let behavior =\n            switch $0.behavior {\n            case .fatalError:\n              \"Fatal Error\"\n            case .error:\n              \"Error\"\n            case .warning:\n              \"Warning\"\n            }\n          return LocatedMessage(\n            behavior: behavior,\n            message: $0.message,\n            location: Location(\n              start: start.utf16Offset(in: pattern), end: end.utf16Offset(in: pattern)\n            )\n          )\n        }\n\n        let data = try JSONEncoder().encode(errors)\n        print(String(data: data, encoding: .utf8) ?? \"\", to: &standardError)\n      }\n    } catch {\n      print(\"\\(error)\", to: &standardError)\n    }\n  }\n}\n\nvar standardError = FileHandle.standardError\n\nextension FileHandle: @retroactive TextOutputStream {\n  public func write(_ string: String) {\n    guard let data = string.data(using: .utf8) else { return }\n    self.write(data)\n  }\n}\n\nstruct Location: Codable {\n  let start: Int\n  let end: Int\n}\n\nstruct LocatedMessage: Codable {\n  let behavior: String\n  let message: String\n  let location: Location\n}\n"
  },
  {
    "path": "Sources/ExpressionParser/ExpressionParser.swift",
    "content": "import Foundation\n\n@testable import _RegexParser\n@testable @_spi(RegexBuilder) import _StringProcessing\n\nstruct ExpressionParser {\n  struct Modes {\n    let matchingOptions: [String]\n\n    var i: Bool { matchingOptions.contains(\"i\") }\n    var s: Bool { matchingOptions.contains(\"s\") }\n  }\n\n  private(set) var tokens = [Token]()\n  private(set) var diagnostics: Diagnostics?\n\n  private let pattern: String\n  private let matchingOptions: [String]\n  private let modes: Modes\n\n  private var depth = 0\n  private var groupCount = 0\n\n  init(pattern: String, matchingOptions: [String]) {\n    self.pattern = pattern\n    self.matchingOptions = matchingOptions\n    modes = Modes(matchingOptions: matchingOptions)\n  }\n\n  mutating func parse() {\n    let ast = _RegexParser.parseWithRecovery(pattern, .traditional)\n    diagnostics = ast.diags\n    emitNode(ast.root)\n  }\n\n  private mutating func emitNode(_ node: AST.Node) {\n    switch node {\n    case .alternation(let alt):\n      emitAlternation(alt)\n    case .concatenation(let concatenation):\n      for node in concatenation.children {\n        emitNode(node)\n      }\n    case .group(let group):\n      emitGroup(group)\n    case .conditional(let conditional):\n      emitConditional(conditional)\n    case .quantification(let quant):\n      emitQuantification(quant)\n    case .quote(let quote):\n      emitQuote(quote)\n    case .trivia(let trivia):\n      emitTrivia(trivia)\n    case .interpolation(let interpolation):\n      emitInterpolation(interpolation)\n    case .atom(let atom):\n      emitAtom(atom)\n    case .customCharacterClass(let ccc):\n      emitCustomCharacterClass(ccc)\n    case .absentFunction(let absentFunction):\n      emitAbsentFunction(absentFunction)\n    case .empty(let empty):\n      emitEmpty(empty)\n    }\n  }\n\n  private mutating func emitAlternation(_ alt: AST.Alternation) {\n    let children = alt.children\n    for node in children.dropLast() {\n      emitNode(node)\n    }\n\n    for pipe in alt.pipes {\n      tokens.append(\n        Token(\n          classes: [\"alt\"],\n          location: Location(\n            start: pipe.start.utf16Offset(in: pattern),\n            end: pipe.end.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: pipe.start.utf16Offset(in: pattern),\n            end: pipe.end.utf16Offset(in: pattern)\n          ),\n          related: Related(\n            location: Location(\n              start: alt.startPosition.utf16Offset(in: pattern),\n              end: alt.endPosition.utf16Offset(in: pattern)\n            )\n          ),\n          tooltip: Tooltip(category: \"quants\", key: \"alt\")\n        )\n      )\n    }\n\n    emitNode(children.last!)\n  }\n\n  private mutating func emitGroup(_ group: AST.Group) {\n    let category: String\n    let key: String\n    var substitution = [String: String]()\n\n    switch group.kind.value {\n    case .capture:\n      groupCount += 1\n      category = \"groups\"\n      key = \"group\"\n      substitution = [\"{{group.num}}\": \"\\(groupCount)\"]\n    case .namedCapture(let name):\n      groupCount += 1\n      category = \"groups\"\n      key = \"namedgroup\"\n      substitution = [\"{{name}}\": name.value]\n    case .balancedCapture(_):\n      groupCount += 1\n      category = \"groups\"\n      key = \"balancedcapture\"\n    case .nonCapture:\n      category = \"groups\"\n      key = \"noncapgroup\"\n    case .nonCaptureReset:\n      category = \"groups\"\n      key = \"branchreset\"\n    case .atomicNonCapturing:\n      category = \"groups\"\n      key = \"atomic\"\n    case .lookahead:\n      category = \"lookaround\"\n      key = \"poslookahead\"\n    case .negativeLookahead:\n      category = \"lookaround\"\n      key = \"neglookahead\"\n    case .nonAtomicLookahead:\n      category = \"lookaround\"\n      key = \"nonatomicposlookahead\"\n    case .lookbehind:\n      category = \"lookaround\"\n      key = \"poslookbehind\"\n    case .negativeLookbehind:\n      category = \"lookaround\"\n      key = \"neglookbehind\"\n    case .nonAtomicLookbehind:\n      category = \"lookaround\"\n      key = \"nonatomicposlookbehind\"\n    case .scriptRun:\n      category = \"Script run. \"\n      key = \"\"\n    case .atomicScriptRun:\n      category = \"Atomic script run. \"\n      key = \"\"\n    case .changeMatchingOptions(_):\n      category = \"Change matching options\"\n      key = \"\"\n    }\n\n    // Content\n    tokens.append(\n      Token(\n        classes: [\"group-\\(depth)\"],\n        location: Location(\n          start: group.startPosition.utf16Offset(in: pattern),\n          end: group.endPosition.utf16Offset(in: pattern)\n        )\n      )\n    )\n\n    // Open parenthesis\n    tokens.append(\n      Token(\n        classes: [\"group\", \"group-\\(depth)\"],\n        location: Location(\n          start: group.kind.location.start.utf16Offset(in: pattern),\n          end: group.kind.location.end.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: group.startPosition.utf16Offset(in: pattern),\n          end: group.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key, substitution: substitution)\n      )\n    )\n\n    // Close parenthesis\n    tokens.append(\n      Token(\n        classes: [\"group\", \"group-\\(depth)\"],\n        location: Location(\n          start: group.child.location.end.utf16Offset(in: pattern),\n          end: group.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: group.startPosition.utf16Offset(in: pattern),\n          end: group.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key, substitution: substitution)\n      )\n    )\n\n    depth += 1\n    for node in group.children {\n      emitNode(node)\n    }\n\n    depth -= 1\n  }\n\n  private mutating func emitConditional(_ conditional: AST.Conditional) {\n    let category: String\n    let key: String\n    var substitution = [String: String]()\n\n    switch conditional.condition.kind {\n    case .groupMatched(let ref):\n      switch ref.kind {\n      case .absolute(let n):\n        category = \"other\"\n        key = \"conditionalgroup\"\n        substitution = [\"{{name}}\": \"\\(n)\"]\n      case .relative(let n):\n        category = \"other\"\n        key = \"conditionalgroup\"\n        substitution = [\"{{name}}\": \"\\(n)\"]\n      case .named(let name):\n        category = \"other\"\n        key = \"conditionalgroup\"\n        substitution = [\"{{name}}\": \"\\(name)\"]\n      }\n\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.condition.location.end.utf16Offset(in: pattern) + 1\n          ),\n          selection: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.endPosition.utf16Offset(in: pattern)\n          ),\n          tooltip: Tooltip(category: category, key: key, substitution: substitution)\n        )\n      )\n    case .recursionCheck:\n      break\n    case .groupRecursionCheck(_):\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.condition.location.end.utf16Offset(in: pattern) + 1\n          ),\n          selection: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.endPosition.utf16Offset(in: pattern)\n          ),\n          tooltip: Tooltip(category: \"other\", key: \"recursion\")\n        )\n      )\n    case .defineGroup:\n      break\n    case .pcreVersionCheck(_):\n      break\n    case .group(let group):\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.condition.location.start.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: conditional.startPosition.utf16Offset(in: pattern),\n            end: conditional.endPosition.utf16Offset(in: pattern)\n          ),\n          tooltip: Tooltip(category: \"other\", key: \"conditional\")\n        )\n      )\n\n      // Open parenthesis\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: group.kind.location.start.utf16Offset(in: pattern),\n            end: group.kind.location.end.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: group.startPosition.utf16Offset(in: pattern),\n            end: group.endPosition.utf16Offset(in: pattern)\n          ),\n          tooltip: Tooltip(category: \"misc\", key: \"condition\")\n        )\n      )\n\n      // Close parenthesis\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: group.child.location.end.utf16Offset(in: pattern),\n            end: group.endPosition.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: group.startPosition.utf16Offset(in: pattern),\n            end: group.endPosition.utf16Offset(in: pattern)\n          ),\n          tooltip: Tooltip(category: \"misc\", key: \"condition\")\n        )\n      )\n\n      for node in group.children {\n        emitNode(node)\n      }\n    }\n\n    emitNode(conditional.trueBranch)\n\n    if let pipe = conditional.pipe {\n      tokens.append(\n        Token(\n          classes: [\"special\"],\n          location: Location(\n            start: pipe.start.utf16Offset(in: pattern),\n            end: pipe.end.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: pipe.start.utf16Offset(in: pattern),\n            end: pipe.end.utf16Offset(in: pattern)\n          ),\n          related: Related(\n            location: Location(\n              start: conditional.startPosition.utf16Offset(in: pattern),\n              end: conditional.endPosition.utf16Offset(in: pattern)\n            )\n          ),\n          tooltip: Tooltip(category: \"misc\", key: \"conditionalelse\")\n        )\n      )\n    }\n\n    emitNode(conditional.falseBranch)\n\n    tokens.append(\n      Token(\n        classes: [\"special\"],\n        location: Location(\n          start: conditional.falseBranch.location.end.utf16Offset(in: pattern),\n          end: conditional.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: conditional.startPosition.utf16Offset(in: pattern),\n          end: conditional.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"other\", key: \"conditional\")\n      )\n    )\n  }\n\n  private mutating func emitQuantification(_ quant: AST.Quantification) {\n    emitNode(quant.child)\n\n    let substitution: [String: String]\n    switch quant.amount.value {\n    case .zeroOrMore:  // *\n      substitution = [\"{{getQuant()}}\": \"0 or more\"]\n    case .oneOrMore:  // +\n      substitution = [\"{{getQuant()}}\": \"1 or more\"]\n    case .zeroOrOne:  // ?\n      substitution = [\"{{getQuant()}}\": \"between 0 and 1\"]\n    case .exactly(let n):  // {n}\n      substitution = [\"{{getQuant()}}\": String(pattern[n.location.range])]\n    case .nOrMore(let n):  // {n,}\n      substitution = [\"{{getQuant()}}\": \"\\(pattern[n.location.range]) or more\"]\n    case .upToN(let n):  // {,n}\n      substitution = [\"{{getQuant()}}\": \"between 0 and \\(pattern[n.location.range])\"]\n    case .range(let n, let m):  // {n,m}\n      substitution = [\"{{getQuant()}}\": \"between \\(pattern[n.location.range]) and \\(pattern[m.location.range])\"]\n    }\n\n    tokens.append(\n      Token(\n        classes: [\"quant\"],\n        location: Location(\n          start: quant.amount.location.start.utf16Offset(in: pattern),\n          end: quant.amount.location.end.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: quant.amount.location.start.utf16Offset(in: pattern),\n          end: quant.amount.location.end.utf16Offset(in: pattern)\n        ),\n        related: Related(\n          location: Location(\n            start: quant.startPosition.utf16Offset(in: pattern),\n            end: quant.endPosition.utf16Offset(in: pattern)\n          )\n        ),\n        tooltip: Tooltip(category: \"quants\", key: \"quant\", substitution: substitution)\n      )\n    )\n\n    switch quant.kind.value {\n    case .eager:\n      break\n    case .reluctant:\n      tokens.append(\n        Token(\n          classes: [\"lazy\"],\n          location: Location(\n            start: quant.kind.location.start.utf16Offset(in: pattern),\n            end: quant.kind.location.end.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: quant.kind.location.start.utf16Offset(in: pattern),\n            end: quant.kind.location.end.utf16Offset(in: pattern)\n          ),\n          related: Related(\n            location: Location(\n              start: quant.amount.location.start.utf16Offset(in: pattern),\n              end: quant.amount.location.end.utf16Offset(in: pattern)\n            )\n          ),\n          tooltip: Tooltip(category: \"quants\", key: \"lazy\")\n        )\n      )\n    case .possessive:\n      tokens.append(\n        Token(\n          classes: [\"possessive\"],\n          location: Location(\n            start: quant.kind.location.start.utf16Offset(in: pattern),\n            end: quant.kind.location.end.utf16Offset(in: pattern)\n          ),\n          selection: Location(\n            start: quant.kind.location.start.utf16Offset(in: pattern),\n            end: quant.kind.location.end.utf16Offset(in: pattern)\n          ),\n          related: Related(\n            location: Location(\n              start: quant.amount.location.start.utf16Offset(in: pattern),\n              end: quant.amount.location.end.utf16Offset(in: pattern)\n            )\n          ),\n          tooltip: Tooltip(category: \"quants\", key: \"possessive\")\n        )\n      )\n    }\n  }\n\n  private mutating func emitQuote(_ quote: AST.Quote) {\n    tokens.append(\n      Token(\n        classes: [\"esc\"],\n        location: Location(\n          start: quote.startPosition.utf16Offset(in: pattern),\n          end: quote.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: quote.startPosition.utf16Offset(in: pattern),\n          end: quote.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"escchars\", key: \"escsequence\", substitution: [\"{{value}}\": quote.literal])\n      )\n    )\n  }\n\n  private mutating func emitTrivia(_ trivia: AST.Trivia) {\n    tokens.append(\n      Token(\n        classes: [\"comment\"],\n        location: Location(\n          start: trivia.startPosition.utf16Offset(in: pattern),\n          end: trivia.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: trivia.startPosition.utf16Offset(in: pattern),\n          end: trivia.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"other\", key: \"comment\")\n      )\n    )\n  }\n\n  private mutating func emitInterpolation(_ interpolation: AST.Interpolation) {\n    tokens.append(\n      Token(\n        classes: [\"interpolation\"],\n        location: Location(\n          start: interpolation.startPosition.utf16Offset(in: pattern),\n          end: interpolation.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: interpolation.startPosition.utf16Offset(in: pattern),\n          end: interpolation.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"other\", key: \"interpolation\")\n      )\n    )\n  }\n\n  private mutating func emitAtom(_ atom: AST.Atom) {\n    let `class`: String\n    let category: String\n    let key: String\n    var substitution = [String: String]()\n\n    switch atom.kind {\n    case .char(let c):\n      let charcode = c.unicodeScalars.map { String(format: \"U+%X\", $0.value) }.joined(separator: \" \")\n\n      let value = String(pattern[atom.location.range])\n      if value.hasPrefix(\"\\\\\") {\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\n          \"{{getChar()}}\": #\"\"\\#(c)\"\"#,\n          \"{{code}}\": charcode,\n          \"{{getInsensitive()}}\": \"Case \\(modes.i ? \"in\" : \"\")sensitive\",\n        ]\n      } else {\n        `class` = \"char\"\n        category = \"misc\"\n        key = \"char\"\n        substitution = [\n          \"{{getChar()}}\": #\"\"\\#(c)\"\"#,\n          \"{{code}}\": charcode,\n          \"{{getInsensitive()}}\": \"Case \\(modes.i ? \"in\" : \"\")sensitive\",\n        ]\n      }\n    case .scalar(let scalar):\n      `class` = \"char\"\n      category = \"misc\"\n      key = \"char\"\n      substitution = [\n        \"{{getChar()}}\": #\"\"\\#(String(scalar.value))\"\"#,\n        \"{{code}}\": String(format: \"U+%X\", scalar.value.value),\n        \"{{getInsensitive()}}\": \"Case \\(modes.i ? \"in\" : \"\")sensitive\",\n      ]\n    case .scalarSequence(let scalarSequence):\n      let scalars = scalarSequence.scalars\n      let value = scalars.map { String($0.value) }.joined()\n      let charcode = scalars.map { String(format: \"U+%X\", $0.value.value) }.joined(separator: \" \")\n\n      `class` = \"char\"\n      category = \"misc\"\n      key = \"char\"\n      substitution = [\n        \"{{getChar()}}\": #\"\"\\#(value)\"\"#,\n        \"{{code}}\": charcode,\n        \"{{getInsensitive()}}\": \"Case \\(modes.i ? \"in\" : \"\")sensitive\",\n      ]\n    case .property(let prop):\n      `class` = \"charclass\"\n\n      switch prop.kind {\n      case .any:\n        category = \"misc\"\n        key = \"any\"\n      case .assigned:\n        category = \"misc\"\n        key = \"assigned\"\n      case .ascii:\n        category = \"misc\"\n        key = \"ascii\"\n      case .generalCategory(let cat):\n        let uniCat: String\n        switch cat {\n        case .other:\n          uniCat = \"Other\"\n        case .control:\n          uniCat = \"Control\"\n        case .format:\n          uniCat = \"Format\"\n        case .unassigned:\n          uniCat = \"Unassigned\"\n        case .privateUse:\n          uniCat = \"Private use\"\n        case .surrogate:\n          uniCat = \"Surrogate\"\n        case .letter:\n          uniCat = \"Letter\"\n        case .casedLetter:\n          uniCat = \"Cased letter\"\n        case .lowercaseLetter:\n          uniCat = \"Lower case letter\"\n        case .modifierLetter:\n          uniCat = \"Modifier letter\"\n        case .otherLetter:\n          uniCat = \"Other letter\"\n        case .titlecaseLetter:\n          uniCat = \"Title case letter\"\n        case .uppercaseLetter:\n          uniCat = \"Upper case letter\"\n        case .mark:\n          uniCat = \"Mark\"\n        case .spacingMark:\n          uniCat = \"Spacing mark\"\n        case .enclosingMark:\n          uniCat = \"Enclosing mark\"\n        case .nonspacingMark:\n          uniCat = \"Non-spacing mark\"\n        case .number:\n          uniCat = \"Number\"\n        case .decimalNumber:\n          uniCat = \"Decimal number\"\n        case .letterNumber:\n          uniCat = \"Letter number\"\n        case .otherNumber:\n          uniCat = \"Other number\"\n        case .punctuation:\n          uniCat = \"Punctuation\"\n        case .connectorPunctuation:\n          uniCat = \"Connector punctuation\"\n        case .dashPunctuation:\n          uniCat = \"Dash punctuation\"\n        case .closePunctuation:\n          uniCat = \"Close punctuation\"\n        case .finalPunctuation:\n          uniCat = \"Final punctuation\"\n        case .initialPunctuation:\n          uniCat = \"Initial punctuation\"\n        case .otherPunctuation:\n          uniCat = \"Other punctuation\"\n        case .openPunctuation:\n          uniCat = \"Open punctuation\"\n        case .symbol:\n          uniCat = \"Symbol\"\n        case .currencySymbol:\n          uniCat = \"Currency symbol\"\n        case .modifierSymbol:\n          uniCat = \"Modifier symbol\"\n        case .mathSymbol:\n          uniCat = \"Mathematical symbol\"\n        case .otherSymbol:\n          uniCat = \"Other symbol\"\n        case .separator:\n          uniCat = \"Separator\"\n        case .lineSeparator:\n          uniCat = \"Line separator\"\n        case .paragraphSeparator:\n          uniCat = \"Paragraph separator\"\n        case .spaceSeparator:\n          uniCat = \"Space separator\"\n        }\n        category = \"charclasses\"\n        key = \"unicodecat\"\n        substitution = [\"{{getUniCat()}}\": uniCat]\n      case .binary(let property, let value):\n        switch property {\n        case .asciiHexDigit:\n          break\n        case .alphabetic:\n          break\n        case .bidiControl:\n          break\n        case .bidiMirrored:\n          break\n        case .cased:\n          break\n        case .compositionExclusion:\n          break\n        case .caseIgnorable:\n          break\n        case .changesWhenCasefolded:\n          break\n        case .changesWhenCasemapped:\n          break\n        case .changesWhenNFKCCasefolded:\n          break\n        case .changesWhenLowercased:\n          break\n        case .changesWhenTitlecased:\n          break\n        case .changesWhenUppercased:\n          break\n        case .dash:\n          break\n        case .deprecated:\n          break\n        case .defaultIgnorableCodePoint:\n          break\n        case .diacratic:\n          break\n        case .emojiModifierBase:\n          break\n        case .emojiComponent:\n          break\n        case .emojiModifier:\n          break\n        case .emoji:\n          break\n        case .emojiPresentation:\n          break\n        case .extender:\n          break\n        case .extendedPictographic:\n          break\n        case .fullCompositionExclusion:\n          break\n        case .graphemeBase:\n          break\n        case .graphemeExtended:\n          break\n        case .graphemeLink:\n          break\n        case .hexDigit:\n          break\n        case .hyphen:\n          break\n        case .idContinue:\n          break\n        case .ideographic:\n          break\n        case .idStart:\n          break\n        case .idsBinaryOperator:\n          break\n        case .idsTrinaryOperator:\n          break\n        case .joinControl:\n          break\n        case .logicalOrderException:\n          break\n        case .lowercase:\n          break\n        case .math:\n          break\n        case .noncharacterCodePoint:\n          break\n        case .otherAlphabetic:\n          break\n        case .otherDefaultIgnorableCodePoint:\n          break\n        case .otherGraphemeExtended:\n          break\n        case .otherIDContinue:\n          break\n        case .otherIDStart:\n          break\n        case .otherLowercase:\n          break\n        case .otherMath:\n          break\n        case .otherUppercase:\n          break\n        case .patternSyntax:\n          break\n        case .patternWhitespace:\n          break\n        case .prependedConcatenationMark:\n          break\n        case .quotationMark:\n          break\n        case .radical:\n          break\n        case .regionalIndicator:\n          break\n        case .softDotted:\n          break\n        case .sentenceTerminal:\n          break\n        case .terminalPunctuation:\n          break\n        case .unifiedIdiograph:\n          break\n        case .uppercase:\n          break\n        case .variationSelector:\n          break\n        case .whitespace:\n          break\n        case .xidContinue:\n          break\n        case .xidStart:\n          break\n        case .expandsOnNFC:\n          break\n        case .expandsOnNFD:\n          break\n        case .expandsOnNFKC:\n          break\n        case .expandsOnNFKD:\n          break\n        }\n        category = \"charclasses\"\n        key = \"binary\"\n      case .script(_):\n        category = \"charclasses\"\n        key = \"script\"\n      case .scriptExtension(_):\n        category = \"charclasses\"\n        key = \"scriptextension\"\n      case .named(_):\n        category = \"charclasses\"\n        key = \"named\"\n      case .numericType(_):\n        category = \"charclasses\"\n        key = \"numerictype\"\n      case .numericValue(_):\n        category = \"charclasses\"\n        key = \"numericvalue\"\n      case .mapping(_, _):\n        category = \"charclasses\"\n        key = \"mapping\"\n      case .ccc(_):\n        category = \"charclasses\"\n        key = \"ccc\"\n      case .age(let major, let minor):\n        category = \"charclasses\"\n        key = \"age\"\n      case .block(_):\n        category = \"charclasses\"\n        key = \"block\"\n      case .posix(let property):\n        switch property {\n        case .alnum:\n          break\n        case .blank:\n          break\n        case .graph:\n          break\n        case .print:\n          break\n        case .word:\n          break\n        case .xdigit:\n          break\n        }\n        category = \"charclasses\"\n        key = \"posixcharclass\"\n        substitution = [\"{{value}}\": \"\\(property)\"]\n      case .pcreSpecial(_):\n        category = \"pcreSpecial\"\n        key = \"pcrespecial\"\n      case .javaSpecial(_):\n        category = \"javaSpecial\"\n        key = \"javaspecial\"\n      case .invalid(key: let k, value: let v):\n        category = \"charclasses\"\n        key = \"invalid\"\n      }\n    case .escaped(let escaped):\n      switch escaped {\n      case .alarm:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"ALARM\", \"{{code}}\": \"(bell, 0x07)\"]\n      case .escape:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"ESCAPE\", \"{{code}}\": \"(escape, 0x1B)\"]\n      case .formfeed:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"FORM FEED\", \"{{code}}\": \"(form feed, 0x0C)\"]\n      case .newline:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"LINE FEED\", \"{{code}}\": \"(ASCII 0x0A)\"]\n      case .carriageReturn:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"CARRIAGE RETURN\", \"{{code}}\": \"(ASCII 0x0D)\"]\n      case .tab:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"TAB\", \"{{code}}\": \"(ASCII 0x09)\"]\n      case .singleDataUnit:\n        `class` = \"esc\"\n        category = \"misc\"\n        key = \"escchar\"\n        substitution = [\"{{getChar()}}\": \"SINGLE DATA UNIT\", \"{{code}}\": \"N/A\"]\n      case .decimalDigit:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"digit\"\n      case .notDecimalDigit:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"notdigit\"\n      case .horizontalWhitespace:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"hwhitespace\"\n      case .notHorizontalWhitespace:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"nothwhitespace\"\n      case .notNewline:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"notlinebreak\"\n      case .newlineSequence:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"linebreak\"\n      case .whitespace:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"whitespace\"\n      case .notWhitespace:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"notwhitespace\"\n      case .verticalTab:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"vwhitespace\"\n      case .notVerticalTab:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"notvwhitespace\"\n      case .wordCharacter:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"word\"\n      case .notWordCharacter:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"notword\"\n      case .backspace:\n        `class` = \"anchor\"\n        category = \"charclasses\"\n        key = \"wordboundary\"\n      case .graphemeCluster:\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"graphemecluster\"\n      case .wordBoundary:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"wordboundary\"\n      case .notWordBoundary:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"notwordboundary\"\n      case .startOfSubject:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"bos\"\n      case .endOfSubjectBeforeNewline:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"eos\"\n      case .endOfSubject:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"abseos\"\n      case .firstMatchingPositionInSubject:\n        `class` = \"anchor\"\n        category = \"anchors\"\n        key = \"prevmatchend\"\n      case .resetStartOfMatch:\n        `class` = \"charclass\"\n        category = \"lookaround\"\n        key = \"keepout\"\n      case .trueAnychar:\n        `class` = \"charclass\"\n        category = \"charclass\"\n        key = \"trueanychar\"\n      case .textSegment:\n        `class` = \"charclass\"\n        category = \"charclass\"\n        key = \"textsegment\"\n      case .notTextSegment:\n        `class` = \"charclass\"\n        category = \"charclass\"\n        key = \"nottextsegment\"\n      }\n    case .keyboardControl(_):\n      `class` = \"charclass\"\n      category = \"charclass\"\n      key = \"keyboardcontrol\"\n    case .keyboardMeta(_):\n      `class` = \"charclass\"\n      category = \"charclass\"\n      key = \"keyboardmeta\"\n    case .keyboardMetaControl(_):\n      `class` = \"charclass\"\n      category = \"charclass\"\n      key = \"keyboardmetacontrol\"\n    case .namedCharacter(_):\n      `class` = \"charclass\"\n      category = \"charclass\"\n      key = \"namedcharacter\"\n    case .dot:\n      `class` = \"charclass\"\n      category = \"charclasses\"\n      key = \"dot\"\n      substitution = [\"{{getDotAll()}}\": \"\\(modes.s ? \"including\" : \"except\") line breaks\"]\n    case .caretAnchor:\n      `class` = \"anchor\"\n      category = \"anchors\"\n      key = \"bof\"\n    case .dollarAnchor:\n      `class` = \"anchor\"\n      category = \"anchors\"\n      key = \"eof\"\n    case .backreference(let ref):\n      `class` = \"ref\"\n      switch ref.kind {\n      case .absolute(let n):\n        category = \"groups\"\n        key = \"numref\"\n        substitution = [\"{{group.num}}\": \"\\(n)\"]\n      case .relative(let n):\n        category = \"groups\"\n        key = \"numref\"\n        substitution = [\"{{group.num}}\": \"\\(n)\"]\n      case .named(let name):\n        category = \"groups\"\n        key = \"namedref\"\n        substitution = [\"{{group.name}}\": name]\n      }\n    case .subpattern(let ref):\n      if ref.kind.recursesWholePattern {\n        `class` = \"special\"\n        category = \"other\"\n        key = \"recursion\"\n      } else {\n        `class` = \"charclass\"\n        category = \"charclasses\"\n        key = \"subpattern\"\n      }\n    case .callout(_):\n      `class` = \"charclass\"\n      category = \"charclasses\"\n      key = \"callout\"\n    case .backtrackingDirective(let directive):\n      `class` = \"charclass\"\n\n      switch directive.kind.value {\n      case .accept:\n        category = \"charclass\"\n        key = \"accept\"\n      case .fail:\n        category = \"charclass\"\n        key = \"fail\"\n      case .mark:\n        category = \"charclass\"\n        key = \"mark\"\n      case .commit:\n        category = \"charclass\"\n        key = \"commit\"\n      case .prune:\n        category = \"charclass\"\n        key = \"skip\"\n      case .skip:\n        category = \"charclass\"\n        key = \"skip\"\n      case .then:\n        category = \"charclass\"\n        key = \"then\"\n      }\n    case .changeMatchingOptions(let matchingOptionSequence):\n      var set = Set<AST.MatchingOption>()\n      let removing = matchingOptionSequence.removing.filter { set.insert($0).inserted }\n\n      set = Set<AST.MatchingOption>()\n      let adding = matchingOptionSequence.adding.filter { set.insert($0).inserted }.filter { !removing.contains($0) }\n\n      let enable = adding.map { pattern[$0.location.range] }\n      let disable = removing.map { pattern[$0.location.range] }\n      var modes = \"\"\n      if !enable.isEmpty {\n        modes += #\" Enable \"\\#(enable.joined())\".\"#\n      }\n      if !disable.isEmpty {\n        modes += #\" Disable \"\\#(disable.joined())\".\"#\n      }\n\n      `class` = \"special\"\n      category = \"other\"\n      key = \"mode\"\n      substitution = [\n        \"{{~getDesc()}}\": \"Enables or disables modes for the remainder of the expression.\",\n        \"{{~getModes()}}\": modes,\n      ]\n    case .invalid:\n      `class` = \"charclass\"\n      category = \"charclasses\"\n      key = \"invalid\"\n    }\n\n    tokens.append(\n      Token(\n        classes: [`class`],\n        location: Location(\n          start: atom.startPosition.utf16Offset(in: pattern),\n          end: atom.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: atom.startPosition.utf16Offset(in: pattern),\n          end: atom.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key, substitution: substitution)\n      )\n    )\n  }\n\n  private mutating func emitCustomCharacterClass(_ ccc: AST.CustomCharacterClass) {\n    let category: String\n    let key: String\n\n    switch ccc.start.value {\n    case .normal:\n      category = \"charclasses\"\n      key = \"set\"\n    case .inverted:\n      category = \"charclasses\"\n      key = \"setnot\"\n    }\n\n    tokens.append(\n      Token(\n        classes: [\"set\"],\n        location: Location(\n          start: ccc.start.location.start.utf16Offset(in: pattern),\n          end: ccc.start.location.end.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: ccc.startPosition.utf16Offset(in: pattern),\n          end: ccc.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key)\n      )\n    )\n\n    tokens.append(\n      Token(\n        classes: [\"group-set\"],\n        location: Location(\n          start: ccc.startPosition.utf16Offset(in: pattern),\n          end: ccc.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: ccc.startPosition.utf16Offset(in: pattern),\n          end: ccc.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key)\n      )\n    )\n\n    for member in ccc.members {\n      switch member {\n      case .custom(let custom):\n        emitCustomCharacterClass(custom)\n      case .range(let range):\n        let lhs: String\n        let rhs: String\n        let dash = String(pattern[range.dashLoc.range])\n\n        switch range.lhs.kind {\n        case .char(let c):\n          lhs = String(c)\n        case .scalar(let scalar):\n          lhs = String(scalar.value)\n        case .scalarSequence(let scalarSequence):\n          lhs = String(scalarSequence.scalarValues)\n        case .property(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .escaped(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .keyboardControl(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .keyboardMeta(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .keyboardMetaControl(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .namedCharacter(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .dot:\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .caretAnchor:\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .dollarAnchor:\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .backreference(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .subpattern(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .callout(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .backtrackingDirective(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .changeMatchingOptions(_):\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        case .invalid:\n          lhs = String(pattern[range.lhs.startPosition..<range.lhs.endPosition])\n        }\n\n        switch range.rhs.kind {\n        case .char(let c):\n          rhs = String(c)\n        case .scalar(let scalar):\n          rhs = String(scalar.value)\n        case .scalarSequence(let scalarSequence):\n          rhs = String(scalarSequence.scalarValues)\n        case .property(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .escaped(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .keyboardControl(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .keyboardMeta(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .keyboardMetaControl(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .namedCharacter(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .dot:\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .caretAnchor:\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .dollarAnchor:\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .backreference(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .subpattern(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .callout(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .backtrackingDirective(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .changeMatchingOptions(_):\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        case .invalid:\n          rhs = String(pattern[range.rhs.startPosition..<range.rhs.endPosition])\n        }\n\n        let lhscharcode = lhs.unicodeScalars.map { String(format: \"U+%X\", $0.value) }.joined(separator: \" \")\n        let rhscharcode = rhs.unicodeScalars.map { String(format: \"U+%X\", $0.value) }.joined(separator: \" \")\n\n        let substitution = [\n          \"{{getChar(prev)}}\": #\"\"\\#(lhs)\"\"#,\n          \"{{getChar(next)}}\": #\"\"\\#(rhs)\"\"#,\n          \"{{prev.code}}\": #\"\"\\#(lhscharcode)\"\"#,\n          \"{{next.code}}\": #\"\"\\#(rhscharcode)\"\"#,\n          \"{{getInsensitive()}}\": \"Case \\(modes.i ? \"in\" : \"\")sensitive.\",\n        ]\n\n        tokens.append(\n          Token(\n            classes: [\"char\"],\n            location: Location(\n              start: range.lhs.startPosition.utf16Offset(in: pattern),\n              end: range.lhs.endPosition.utf16Offset(in: pattern)\n            ),\n            selection: Location(\n              start: range.lhs.startPosition.utf16Offset(in: pattern),\n              end: range.rhs.endPosition.utf16Offset(in: pattern)\n            ),\n            tooltip: Tooltip(category: \"charclasses\", key: \"range\", substitution: substitution)\n          )\n        )\n        tokens.append(\n          Token(\n            classes: [\"set\"],\n            location: Location(\n              start: range.dashLoc.start.utf16Offset(in: pattern),\n              end: range.dashLoc.end.utf16Offset(in: pattern)\n            ),\n            selection: Location(\n              start: range.lhs.startPosition.utf16Offset(in: pattern),\n              end: range.rhs.endPosition.utf16Offset(in: pattern)\n            ),\n            tooltip: Tooltip(category: \"charclasses\", key: \"range\", substitution: substitution)\n          )\n        )\n        tokens.append(\n          Token(\n            classes: [\"char\"],\n            location: Location(\n              start: range.rhs.startPosition.utf16Offset(in: pattern),\n              end: range.rhs.endPosition.utf16Offset(in: pattern)\n            ),\n            selection: Location(\n              start: range.lhs.startPosition.utf16Offset(in: pattern),\n              end: range.rhs.endPosition.utf16Offset(in: pattern)\n            ),\n            tooltip: Tooltip(category: \"charclasses\", key: \"range\", substitution: substitution)\n          )\n        )\n      case .atom(let atom):\n        emitAtom(atom)\n      case .quote(let quote):\n        emitQuote(quote)\n      case .trivia(let trivia):\n        emitTrivia(trivia)\n      case .setOperation(_, _, _):\n        break\n      }\n    }\n\n    tokens.append(\n      Token(\n        classes: [\"set\"],\n        location: Location(\n          start: ccc.endPosition.utf16Offset(in: pattern) - 1,\n          end: ccc.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: ccc.startPosition.utf16Offset(in: pattern),\n          end: ccc.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: category, key: key)\n      )\n    )\n  }\n\n  private mutating func emitAbsentFunction(_ absentFunction: AST.AbsentFunction) {\n    switch absentFunction.kind {\n    case .repeater(_):\n      break\n    case .expression(let absentee, let pipe, let expr):\n      break\n    case .stopper(_):\n      break\n    case .clearer:\n      break\n    }\n\n    // Content\n    tokens.append(\n      Token(\n        classes: [\"group-0\"],\n        location: Location(\n          start: absentFunction.startPosition.utf16Offset(in: pattern),\n          end: absentFunction.endPosition.utf16Offset(in: pattern)\n        )\n      )\n    )\n\n    // Open parenthesis\n    tokens.append(\n      Token(\n        classes: [\"group\", \"group-0\"],\n        location: Location(\n          start: absentFunction.start.start.utf16Offset(in: pattern),\n          end: absentFunction.start.end.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: absentFunction.startPosition.utf16Offset(in: pattern),\n          end: absentFunction.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"groups\", key: \"absentfunction\")\n      )\n    )\n\n    // Close parenthesis\n    tokens.append(\n      Token(\n        classes: [\"group\", \"group-0\"],\n        location: Location(\n          start: absentFunction.endPosition.utf16Offset(in: pattern),\n          end: absentFunction.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: absentFunction.startPosition.utf16Offset(in: pattern),\n          end: absentFunction.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"groups\", key: \"absentfunction\")\n      )\n    )\n  }\n\n  private mutating func emitEmpty(_ empty: AST.Empty) {\n    tokens.append(\n      Token(\n        classes: [\"empty\"],\n        location: Location(\n          start: empty.startPosition.utf16Offset(in: pattern),\n          end: empty.endPosition.utf16Offset(in: pattern)\n        ),\n        selection: Location(\n          start: empty.startPosition.utf16Offset(in: pattern),\n          end: empty.endPosition.utf16Offset(in: pattern)\n        ),\n        tooltip: Tooltip(category: \"empty\", key: \"empty\")\n      )\n    )\n  }\n}\n\nstruct Token: Codable {\n  let classes: [String]\n  let location: Location\n  let selection: Location?\n  let related: Related?\n  let tooltip: Tooltip?\n\n  init(classes: [String], location: Location, selection: Location? = nil, related: Related? = nil, tooltip: Tooltip? = nil) {\n    self.classes = classes\n    self.location = location\n    self.selection = selection\n    self.related = related\n    self.tooltip = tooltip\n  }\n}\n\nextension Token: CustomStringConvertible {\n  var description: String {\n    #\"\\#(classes) \\#(location)\"#\n  }\n}\n\nstruct Location: Codable {\n  let start: Int\n  let end: Int\n}\n\nextension Location: CustomStringConvertible {\n  var description: String {\n    \"\\(start)-\\(end)\"\n  }\n}\n\nstruct Tooltip: Codable {\n  let category: String\n  let key: String\n  let substitution: [String: String]\n\n  init(category: String, key: String, substitution: [String: String] = [:]) {\n    self.category = category\n    self.key = key\n    self.substitution = substitution\n  }\n}\n\nextension Tooltip: CustomStringConvertible {\n  var description: String {\n    \"\\(category).\\(key) \\(substitution)\"\n  }\n}\n\nstruct Related: Codable {\n  let location: Location\n}\n"
  },
  {
    "path": "Sources/ExpressionParser/Main.swift",
    "content": "import Foundation\n\n@main\nstruct Main {\n  static func main() {\n    do {\n      let pattern = CommandLine.arguments[1]\n      let matchingOptions = CommandLine.arguments[2]\n        .split(separator: \",\", omittingEmptySubsequences: true)\n        .map { String($0) }\n\n      var parser = ExpressionParser(pattern: pattern, matchingOptions: matchingOptions)\n      parser.parse()\n\n      let encoder = JSONEncoder()\n      let data = try encoder.encode(parser.tokens)\n      print(String(data: data, encoding: .utf8) ?? \"\")\n\n      if let diagnostics = parser.diagnostics {\n        let errors = diagnostics.diags.map {\n          let location = $0.location\n          let (start, end) = (location.start, location.end)\n\n          let behavior =\n            switch $0.behavior {\n            case .fatalError:\n              \"Fatal Error\"\n            case .error:\n              \"Error\"\n            case .warning:\n              \"Warning\"\n            }\n          return LocatedMessage(\n            behavior: behavior,\n            message: $0.message,\n            location: Location(\n              start: start.utf16Offset(in: pattern), end: end.utf16Offset(in: pattern)\n            )\n          )\n        }\n\n        let data = try JSONEncoder().encode(errors)\n        print(String(data: data, encoding: .utf8) ?? \"\", to: &standardError)\n      }\n    } catch {\n      print(\"\\(error)\", to: &standardError)\n    }\n  }\n}\n\nvar standardError = FileHandle.standardError\n\nextension FileHandle: @retroactive TextOutputStream {\n  public func write(_ string: String) {\n    guard let data = string.data(using: .utf8) else { return }\n    self.write(data)\n  }\n}\n\nstruct LocatedMessage: Codable {\n  let behavior: String\n  let message: String\n  let location: Location\n}\n"
  },
  {
    "path": "Sources/Matcher/Main.swift",
    "content": "import Foundation\nimport _RegexParser\n\n@main\nstruct Main {\n  static func main() throws {\n    do {\n      let pattern = CommandLine.arguments[1]\n      let text = CommandLine.arguments[2]\n      let matchingOptions = CommandLine.arguments[3]\n        .split(separator: \",\", omittingEmptySubsequences: true)\n        .map { String($0) }\n\n      do {\n        _ = try _RegexParser.parse(pattern, .traditional)\n      } catch {\n        return\n      }\n\n      let matches = try Matcher.match(pattern: pattern, text: text, matchingOptions: matchingOptions)\n\n      let data = try JSONEncoder().encode(matches)\n      print(String(data: data, encoding: .utf8) ?? \"\")\n    } catch {\n      print(\"\\(error)\", to: &standardError)\n    }\n  }\n}\n\nvar standardError = FileHandle.standardError\n\nextension FileHandle: @retroactive TextOutputStream {\n  public func write(_ string: String) {\n    guard let data = string.data(using: .utf8) else { return }\n    self.write(data)\n  }\n}\n"
  },
  {
    "path": "Sources/Matcher/Matcher.swift",
    "content": "import Foundation\n\n@testable @_spi(RegexBuilder) import _StringProcessing\n\nstruct Matcher {\n  static func match(pattern: String, text: String, matchingOptions: [String] = []) throws -> [Match] {\n    let regex = try Regex(pattern)\n      .anchorsMatchLineEndings(matchingOptions.contains(\"m\"))\n      .ignoresCase(matchingOptions.contains(\"i\"))\n      .dotMatchesNewlines(matchingOptions.contains(\"s\"))\n      .asciiOnlyWordCharacters(matchingOptions.contains(\"asciiOnlyWordCharacters\"))\n      .asciiOnlyDigits(matchingOptions.contains(\"asciiOnlyDigits\"))\n      .asciiOnlyWhitespace(matchingOptions.contains(\"asciiOnlyWhitespace\"))\n      .asciiOnlyCharacterClasses(matchingOptions.contains(\"asciiOnlyCharacterClasses\"))\n\n    let matches = matchingOptions.contains(\"g\") ? text.matches(of: regex) : text.firstMatch(of: regex).flatMap { [$0] } ?? []\n    return matches.map {\n      let captures: [Group] = $0.lazy.elements.dropFirst().map {\n        if let range = $0.range {\n          return Group(\n            location: Location(\n              start: range.lowerBound.utf16Offset(in: text),\n              end: range.upperBound.utf16Offset(in: text)\n            ),\n            value: String(text[range]),\n            name: $0.name\n          )\n        } else {\n          return Group(\n            location: nil,\n            value: nil,\n            name: $0.name\n          )\n        }\n      }\n      return Match(\n        location: Location(\n          start: $0.range.lowerBound.utf16Offset(in: text),\n          end: $0.range.upperBound.utf16Offset(in: text)\n        ),\n        value: String(text[$0.range]),\n        captures: captures\n      )\n    }\n  }\n}\n\nstruct Match: Codable {\n  let location: Location\n  let value: String\n\n  let captures: [Group]\n}\n\nstruct Group: Codable {\n  let location: Location?\n  let value: String?\n  let name: String?\n}\n\nstruct Location: Codable {\n  let start: Int\n  let end: Int\n}\n"
  },
  {
    "path": "Tests/RegexTests/ConverterTests.swift",
    "content": "import Foundation\nimport XCTest\n\n@testable import DSLConverter\n\nclass ConverterTests: XCTestCase {\n  func testConvertPattern() throws {\n    do {\n      let converter = DSLConverter()\n      let builderDSL = try converter.convert(#\"gray|grey\"#)\n      print(builderDSL)\n    }\n    do {\n      let converter = DSLConverter()\n      let builderDSL = try converter.convert(#\"\\b(?:[a-eg-z]|f(?!oo))\\w*\\b\"#)\n      print(builderDSL)\n    }\n    do {\n      let converter = DSLConverter()\n      let builderDSL = try converter.convert(#\"\\K\\K\"#)\n      print(builderDSL)\n    } catch {\n      print(error)\n    }\n  }\n}\n"
  },
  {
    "path": "Tests/RegexTests/ExpressionParserTests.swift",
    "content": "import Foundation\nimport XCTest\n\n@testable import ExpressionParser\n\nclass ParserTests: XCTestCase {\n  func testParseExpression() {\n    let options: [String] = []\n    do {\n      var parser = ExpressionParser(pattern: #\"a(?R)?b\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\d+(?(?=regex)then|else(?(?=regex)then|else))(a)^(START)?\\d+(?(1)END|\\b)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"^[^<>]*(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))$\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"hello\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"gray|grey\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"gr(a|e)y\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"gr[ae]y\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"colou?r\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"rege(x(es)?|xps?)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"go*gle\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"go+gle\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"g(oog)+le\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"z{3}\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"z{3,6}\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"z{3,}\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"[Bb]rainf\\*\\*k\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\d\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\d+\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\d{5}(-\\d{4})?\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"1\\d{10}\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"[2-9]|[12]\\d|3[0-6]\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"Hello\\nworld\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"mi.....ft\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\d+(\\.\\d\\d)?\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"[^i*&2@]\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"//[^\\r\\n]*[\\r\\n]\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"^dog\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"dog$\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"^dog$\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"\\w++\\d\\d\\w+\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"<(\\w+)>[^<]*</\\1>\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"Hillary(?=\\s+Clinton)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"q(?!u)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"(?<=-)\\p{L}+\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"[\\x41-\\x45]{3}\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"(?(?=regex)then|else)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n    do {\n      var parser = ExpressionParser(pattern: #\"(?<word>\\w+)\\W+(?<-word>\\w+)\"#, matchingOptions: options)\n      parser.parse()\n      print(parser.tokens)\n    }\n  }\n}\n"
  },
  {
    "path": "Tests/RegexTests/MatcherTests.swift",
    "content": "import Foundation\nimport XCTest\n\n@testable import Matcher\n\nclass MatchTest: XCTestCase {\n  func testMatch() throws {\n    do {\n      let pattern = #\"[A-Z]\\w+\"#\n      let text = \"\"\"\n        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n        \"\"\"\n\n      let matches = try Matcher.match(pattern: pattern, text: text)\n      print(matches)\n    }\n    do {\n      let pattern = #\"\\d+\"#\n      let text = \"\"\"\n        KIND      DATE          INSTITUTION                AMOUNT\n        ----------------------------------------------------------------\n        CREDIT    03/01/2022    Payroll from employer      $200.23\n        CREDIT    03/03/2022    Suspect A                  $2,000,000.00\n        DEBIT     03/03/2022    Ted's Pet Rock Sanctuary   $2,000,000.00\n        DEBIT     03/05/2022    Doug's Dugout Dogs         $33.27\n        DEBIT     06/03/2022    Oxford Comma Supply Ltd.   £57.33\n        \"\"\"\n\n      let matches = try Matcher.match(pattern: pattern, text: text)\n      print(matches)\n    }\n    do {\n      let pattern = #\"((\\d{3})(?:\\.|-))?(\\d{3})(?:\\.|-)(\\d{4})\"#\n      let text = \"\"\"\n        Call 555-1212 for info\n        \"\"\"\n\n      let matches = try Matcher.match(pattern: pattern, text: text)\n      print(matches)\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"swiftregex\",\n  \"scripts\": {\n    \"prod\": \"webpack --progress --config webpack.prod.js\",\n    \"dev\": \"webpack --progress --config webpack.dev.js\"\n  },\n  \"dependencies\": {\n    \"@createjs/easeljs\": \"2.0.0-beta.4\",\n    \"@fortawesome/fontawesome-svg-core\": \"7.2.0\",\n    \"@fortawesome/free-brands-svg-icons\": \"7.2.0\",\n    \"@fortawesome/free-solid-svg-icons\": \"^7.0.0\",\n    \"@fortawesome/pro-duotone-svg-icons\": \"6.1.1\",\n    \"@fortawesome/pro-light-svg-icons\": \"6.1.1\",\n    \"@fortawesome/pro-regular-svg-icons\": \"6.1.1\",\n    \"@fortawesome/pro-solid-svg-icons\": \"6.1.1\",\n    \"@popperjs/core\": \"2.11.8\",\n    \"bootstrap\": \"5.3.8\",\n    \"codemirror\": \"5.65.21\",\n    \"pako\": \"2.1.0\",\n    \"reconnecting-websocket\": \"4.4.0\",\n    \"tippy.js\": \"6.3.7\"\n  },\n  \"devDependencies\": {\n    \"autoprefixer\": \"10.5.0\",\n    \"copy-webpack-plugin\": \"14.0.0\",\n    \"css-loader\": \"7.1.4\",\n    \"html-webpack-plugin\": \"5.6.7\",\n    \"mini-css-extract-plugin\": \"2.10.2\",\n    \"postcss\": \"8.5.14\",\n    \"postcss-loader\": \"8.2.1\",\n    \"sass\": \"1.99.0\",\n    \"sass-loader\": \"16.0.8\",\n    \"style-loader\": \"4.0.0\",\n    \"webpack\": \"5.106.2\",\n    \"webpack-bundle-analyzer\": \"5.3.0\",\n    \"webpack-cli\": \"7.0.2\",\n    \"webpack-merge\": \"6.0.1\"\n  }\n}\n"
  },
  {
    "path": "webpack.common.js",
    "content": "const path = require(\"path\");\nconst CopyWebbackPlugin = require(\"copy-webpack-plugin\");\nconst HtmlWebpackPlugin = require(\"html-webpack-plugin\");\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\n\nmodule.exports = {\n  entry: {\n    index: \"./Public/index.js\",\n  },\n  output: {\n    globalObject: \"self\",\n    filename: \"[name].[contenthash].js\",\n    path: path.resolve(__dirname, \"Public/dist\"),\n    publicPath: \"/\",\n    clean: true,\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.scss$/,\n        use: [\n          {\n            loader: MiniCssExtractPlugin.loader,\n          },\n          {\n            loader: \"css-loader\",\n            options: {\n              url: false,\n              sourceMap: true,\n              importLoaders: 2,\n            },\n          },\n          {\n            loader: \"postcss-loader\",\n            options: {\n              sourceMap: true,\n              postcssOptions: {\n                plugins: [\"autoprefixer\"],\n              },\n            },\n          },\n          {\n            loader: \"sass-loader\",\n            options: {\n              sourceMap: true,\n            },\n          },\n        ],\n      },\n      {\n        test: /\\.css$/,\n        use: [\"style-loader\", \"css-loader\"],\n      },\n      {\n        test: /\\.(woff|woff2|eot|ttf|otf)$/i,\n        type: \"asset/resource\",\n      },\n    ],\n  },\n  plugins: [\n    new CopyWebbackPlugin({\n      patterns: [\n        { from: \"./Public/images/*.*\", to: \"images/[name][ext]\" },\n        { from: \"./Public/favicons/*.*\", to: \"[name][ext]\" },\n        { from: \"./Public/error.html\", to: \"error.leaf\" },\n        { from: \"./Public/robots.txt\", to: \"robots.txt\" },\n      ],\n    }),\n    new HtmlWebpackPlugin({\n      chunks: [\"index\"],\n      filename: \"index.leaf\",\n      template: \"./Public/index.html\",\n    }),\n    new MiniCssExtractPlugin({\n      filename: \"[name].[contenthash].css\",\n    }),\n  ],\n};\n"
  },
  {
    "path": "webpack.dev.js",
    "content": "const { merge } = require(\"webpack-merge\");\nconst common = require(\"./webpack.common.js\");\n\nmodule.exports = merge(common, {\n  mode: \"development\",\n  devtool: \"inline-source-map\",\n});\n"
  },
  {
    "path": "webpack.prod.js",
    "content": "const { merge } = require(\"webpack-merge\");\nconst common = require(\"./webpack.common.js\");\nconst BundleAnalyzerPlugin =\n  require(\"webpack-bundle-analyzer\").BundleAnalyzerPlugin;\n\nmodule.exports = merge(common, {\n  mode: \"production\",\n  devtool: \"source-map\",\n  plugins: [\n    new BundleAnalyzerPlugin({ analyzerMode: \"static\", openAnalyzer: false }),\n  ],\n});\n"
  }
]