diff --git a/.cargo/config.toml b/.cargo/config.toml index 22fe7e245bd6..4ec2f3b86203 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,9 +1,2 @@ [target.wasm32-unknown-unknown] runner = 'wasm-bindgen-test-runner' - -[target.'cfg(target_os = "macos")'] -# Required for prql-elixir on Mac -rustflags = [ - "-C", "link-arg=-undefined", - "-C", "link-arg=dynamic_lookup", -] diff --git a/.devcontainer/base-image/Dockerfile b/.devcontainer/base-image/Dockerfile new file mode 100644 index 000000000000..6d71dcde78cb --- /dev/null +++ b/.devcontainer/base-image/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/devcontainers/rust:0.203.8-1-bullseye + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + cmake \ + && rm -rf /var/lib/apt/lists/* \ + && sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin + +# ========= Install cargo-tools for non-root user (vscode) ========= +COPY Taskfile.cargo-tools.yml /tmp/Taskfile.cargo-tools.yml +WORKDIR /tmp +USER vscode +RUN task -t Taskfile.cargo-tools.yml install + +USER root diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..8af2eec50d0e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +// Dev Container for Rust, website, prql-js and prql-python +{ + "image": "ghcr.io/prql/prql-devcontainer-base:latest", + "features": { + "ghcr.io/devcontainers/features/hugo:1": {}, + "ghcr.io/devcontainers/features/python:1": {}, + "ghcr.io/devcontainers/features/node:1": {}, + "ghcr.io/eitsupi/devcontainer-features/go-task:1": {} + }, + "customizations": { + "vscode": { + "extensions": [ + // Keep in sync with Taskfile.yml + "prql-lang.prql-vscode", + "rust-lang.rust-analyzer", + "mitsuhiko.insta", + "esbenp.prettier-vscode", + "budparr.language-hugo-vscode" + ] + } + }, + "mounts": [ + { + "source": "devcontainer-cargo-cache", + "target": "/usr/local/cargo", + "type": "volume" + } + ], + "postCreateCommand": { + "install-maturin": "task install-maturin", + "install-npm-dependencies": "task install-npm-dependencies" + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000000..08196cce0f0d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,62 @@ +name: ๐Ÿ› Bug report +description: File a bug report to help us improve +labels: [bug] +body: + - type: textarea + id: what-happened + attributes: + label: What happened? + description: | + Thanks for reporting a bug! Feel free to add any initial context here. + + - type: textarea + id: prql-input + attributes: + label: PRQL input + description: | + A minimal, self-contained example that demonstrates the issue. + + This will be automatically formatted into code, so no need for markdown backticks. + render: elm + validations: + required: true + + - type: textarea + id: output + attributes: + label: SQL output + description: | + The SQL that PRQL currently compiles to. Feel free to use the [playground](https://prql-lang.org/playground/) to generate the SQL. + + This will be automatically formatted into code, so no need for markdown backticks. + render: SQL + validations: + required: true + + - type: textarea + id: expected-output + attributes: + label: Expected SQL output + + description: + Optional; no need to write out if it's obvious from the context + render: SQL + + - type: checkboxes + id: mvce-checkboxes + attributes: + label: MVCE confirmation + description: | + Please confirm that the bug report is minimal and doesn't exist already: + + - **Minimal example** โ€” the example is as focused as reasonably possible to demonstrate the underlying issue in PRQL. For example, it's not possible to exclude any line and still observe the bug. + + - **New issue** โ€” a search of GitHub Issues suggests this is not a duplicate. + options: + - label: Minimal example + - label: New issue + + - type: textarea + id: extra + attributes: + label: Anything else? diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 000000000000..0086358db1eb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/something_else.yaml b/.github/ISSUE_TEMPLATE/something_else.yaml new file mode 100644 index 000000000000..56f5cad63e46 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/something_else.yaml @@ -0,0 +1,7 @@ +name: Something else +description: Anything that's not a bug report +body: + - type: textarea + id: what-happened + attributes: + label: What's up? diff --git a/.github/actions/build-prql-js/action.yaml b/.github/actions/build-prql-js/action.yaml index 8843384d437c..556bc9170997 100644 --- a/.github/actions/build-prql-js/action.yaml +++ b/.github/actions/build-prql-js/action.yaml @@ -6,10 +6,9 @@ description: "" runs: using: composite steps: - - name: Install wasm-pack - uses: jetli/wasm-pack-action@v0.4.0 + - uses: baptiste0928/cargo-install@next with: - version: "latest" + crate: wasm-pack - name: Setup Node uses: actions/setup-node@v3 @@ -20,4 +19,4 @@ runs: - name: Run wasm-pack for node, bundler, & web run: npm run build shell: bash - working-directory: prql-js/ + working-directory: bindings/prql-js/ diff --git a/.github/actions/build-prql-python/action.yaml b/.github/actions/build-prql-python/action.yaml index 95c2429151e9..995e59f642e0 100644 --- a/.github/actions/build-prql-python/action.yaml +++ b/.github/actions/build-prql-python/action.yaml @@ -9,22 +9,23 @@ runs: - uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} + key: python - uses: messense/maturin-action@v1 if: runner.os == 'Linux' with: manylinux: auto command: build - args: --release -o dist -m prql-python/Cargo.toml + args: --release -o dist -m bindings/prql-python/Cargo.toml - uses: messense/maturin-action@v1 if: runner.os == 'Windows' with: command: build - args: --release -o dist -m prql-python/Cargo.toml + args: --release -o dist -m bindings/prql-python/Cargo.toml - uses: messense/maturin-action@v1 if: runner.os == 'macOS' with: command: build - args: --release -o dist --universal2 -m prql-python/Cargo.toml + args: --release -o dist --universal2 -m bindings/prql-python/Cargo.toml - name: Upload wheels uses: actions/upload-artifact@v3 with: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9037733798b9..28bf10f7d512 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -19,7 +19,7 @@ updates: prefix: "chore: " - package-ecosystem: "npm" - directory: "/playground" + directory: "/bindings/prql-js" schedule: interval: daily ignore: @@ -29,6 +29,24 @@ updates: commit-message: prefix: "chore: " + - package-ecosystem: "npm" + directory: "/web/playground" + schedule: + interval: daily + ignore: + - dependency-name: "*" + update-types: + - version-update:semver-patch + commit-message: + prefix: "chore: " + + - package-ecosystem: docker + directory: .devcontainer/base-image + schedule: + interval: daily + commit-message: + prefix: "chore: " + # For actions (rather than workflows), we need to list each directory, ref # https://github.com/dependabot/dependabot-core/issues/5137, from https://github.com/dependabot/dependabot-core/issues/4178#issuecomment-1118492006 - directory: ".github/actions/build-prql-js" diff --git a/.github/workflows/devcontainer.yaml b/.github/workflows/devcontainer.yaml new file mode 100644 index 000000000000..28c9cf330822 --- /dev/null +++ b/.github/workflows/devcontainer.yaml @@ -0,0 +1,67 @@ +name: devcontainer + +on: + # TODO: adopt our standard hierarchy for these, as per + # https://github.com/PRQL/prql/pull/1893#discussion_r1125736478, possibly only + # publishing on releases or on the `web` branch (which possibly should be + # renamed back to `stable` if we use it more for this) + push: + paths: + - .devcontainer/base-image/Dockerfile + - Taskfile.cargo-tools.yml + pull_request: + paths: + - .devcontainer/base-image/Dockerfile + - Taskfile.cargo-tools.yml + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: docker/metadata-action@v4 + id: meta + with: + images: ghcr.io/${{ github.repository_owner }}/prql-devcontainer-base + tags: | + type=raw,latest + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - id: set-options + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "push=true" >>$GITHUB_OUTPUT + else + echo "push=false" >>$GITHUB_OUTPUT + fi + + - name: Build + uses: docker/build-push-action@v4 + with: + file: "./.devcontainer/base-image/Dockerfile" + tags: ${{ steps.meta.outputs.tags }} + # TODO: add linux/arm64 + platforms: linux/amd64 + push: ${{ steps.set-options.outputs.push }} + cache-from: | + ${{ env.IMAGE_NAME }} + type=gha + cache-to: | + type=inline + type=gha,mode=max diff --git a/.github/workflows/mega-linter.yaml b/.github/workflows/mega-linter.yaml new file mode 100644 index 000000000000..542bcb365b59 --- /dev/null +++ b/.github/workflows/mega-linter.yaml @@ -0,0 +1,105 @@ +# This MegaLinter workflow is very experimental stage. Open to changes and updates. +# https://github.com/PRQL/prql/pull/1974 + +# MegaLinter GitHub Action configuration file +# More info at https://megalinter.io +name: MegaLinter + +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: # Comment env block if you do not want to apply fixes + # Apply linter fixes configuration + # APPLY_FIXES: all # When active, APPLY_FIXES must also be defined as environment variable (in github/workflows/mega-linter.yml or other CI tool) + APPLY_FIXES_EVENT: pull_request # Decide which event triggers application of fixes in a commit or a PR (pull_request, push, all) + APPLY_FIXES_MODE: commit # If APPLY_FIXES is used, defines if the fixes are directly committed (commit) or posted in a PR (pull_request) + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + build: + name: MegaLinter + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # If you use VALIDATE_ALL_CODEBASE = true, you can remove this line to improve performances + + # MegaLinter + - name: MegaLinter + id: ml + # You can override MegaLinter flavor used to have faster performances + # More info at https://megalinter.io/flavors/ + uses: oxsecurity/megalinter@v6 + env: + # All available variables are described in documentation + # https://megalinter.io/configuration/ + VALIDATE_ALL_CODEBASE: ${{ github.event_name == 'push' && github.ref + == 'refs/heads/main' }} # Validates all source when push on main, else just the git diff with main. Override with true if you always want to lint all sources + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # ADD YOUR CUSTOM ENV VARIABLES HERE OR DEFINE THEM IN A FILE .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY + # DISABLE: COPYPASTE,SPELL # Uncomment to disable copy-paste and spell checks + + # Upload MegaLinter artifacts + - name: Archive production artifacts + if: ${{ success() }} || ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: MegaLinter reports + path: | + megalinter-reports + mega-linter.log + + # Create pull request if applicable (for now works only on PR from same repository, not from forks) + - name: Create Pull Request with applied fixes + id: cpr + if: + steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == + 'all' || env.APPLY_FIXES_EVENT == github.event_name) && + env.APPLY_FIXES_MODE == 'pull_request' && (github.event_name == 'push' + || github.event.pull_request.head.repo.full_name == github.repository) + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} + commit-message: "[MegaLinter] Apply linters automatic fixes" + title: "[MegaLinter] Apply linters automatic fixes" + labels: bot + - name: Create PR output + if: + steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == + 'all' || env.APPLY_FIXES_EVENT == github.event_name) && + env.APPLY_FIXES_MODE == 'pull_request' && (github.event_name == 'push' + || github.event.pull_request.head.repo.full_name == github.repository) + run: | + echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" + echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" + + # Push new commit if applicable (for now works only on PR from same repository, not from forks) + - name: Prepare commit + if: + steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == + 'all' || env.APPLY_FIXES_EVENT == github.event_name) && + env.APPLY_FIXES_MODE == 'commit' && github.ref != 'refs/heads/main' && + (github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository) + run: sudo chown -Rc $UID .git/ + - name: Commit and push applied linter fixes + if: + steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == + 'all' || env.APPLY_FIXES_EVENT == github.event_name) && + env.APPLY_FIXES_MODE == 'commit' && github.ref != 'refs/heads/main' && + (github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository) + uses: stefanzweifel/git-auto-commit-action@v4 + with: + branch: + ${{ github.event.pull_request.head.ref || github.head_ref || + github.ref }} + commit_message: "[MegaLinter] Apply linters fixes" diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index a08c5f23f99c..47b83bb8f061 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -12,6 +12,10 @@ on: jobs: cargo-audit: runs-on: ubuntu-latest + # We can't read PRQL repo security events on forks, which causes this to + # incorrectly fail. So we disable. If we wanted to run checks on PRs, we + # could move this to `pull-request-target`. + if: "!github.event.pull_request.head.repo.fork" permissions: actions: read contents: read @@ -48,14 +52,15 @@ jobs: update-rust-toolchain: runs-on: ubuntu-latest + # Don't run on forks; will attempt to create a PR into the fork... + if: "!github.event.pull_request.head.repo.fork" steps: - name: ๐Ÿ“‚ Checkout code uses: actions/checkout@v3 - uses: a-kenji/update-rust-toolchain@main with: # Discussion in #1561 - # Move this up until 3, so the next upgrade is at 1.66 once 1.69 is released. - minor-version-delta: 2 + minor-version-delta: 3 toolchain-path: "./rust-toolchain.toml" pr-title: "build: Update rust toolchain version" diff --git a/.github/workflows/publish-web.yaml b/.github/workflows/publish-web.yaml index ba17deef1ae5..1260325e0760 100644 --- a/.github/workflows/publish-web.yaml +++ b/.github/workflows/publish-web.yaml @@ -6,23 +6,24 @@ on: # Called by pull-request when specifically requested workflow_call: +# Grant GITHUB_TOKEN the permissions required to make a Pages deployment +permissions: + pages: write + id-token: write + jobs: - publish-web: + build-web: runs-on: ubuntu-latest - # Skip running workflow on forks + # Skip running workflow on forks. This still runs on PRs, but then below we + # disable it from attempting to publish. if: github.repository_owner == 'prql' - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment - permissions: - pages: write - id-token: write - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: + - name: Setup Pages + id: pages + uses: actions/configure-pages@v3 + - name: ๐Ÿ“‚ Checkout code uses: actions/checkout@v3 @@ -31,10 +32,6 @@ jobs: uses: peaceiris/actions-hugo@v2.6.0 # Book setup, duplicated from `pull-request.yaml` - - name: ๐Ÿ’ฐ Cache - uses: Swatinem/rust-cache@v2 - with: - save-if: ${{ github.ref == 'refs/heads/web' }} - uses: baptiste0928/cargo-install@next with: crate: mdbook @@ -44,23 +41,35 @@ jobs: - uses: baptiste0928/cargo-install@next with: crate: mdbook-admonish + - name: ๐Ÿ’ฐ Cache + uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.ref == 'refs/heads/web' }} - uses: ./.github/actions/build-prql-js - uses: arduino/setup-task@v1 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" + - name: ๐Ÿ•ท๏ธ Build website run: task build-web - - name: Prepare tar for upload - uses: actions/upload-pages-artifact@v1.0.7 + - uses: actions/upload-pages-artifact@v1.0.7 with: path: website/public/ + deploy-web: + needs: build-web + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + # Don't attempt to publish if on a fork, including a PR. + if: "!github.event.pull_request.head.repo.fork" + + steps: - name: Deploy to GitHub Pages - # Even if called by another workflow, don't try and actually publish if - # on a fork (it'll fail the workflow even if it otherwise builds successfully) - if: "!github.event.pull_request.head.repo.fork" id: deployment - uses: actions/deploy-pages@v1.2.4 + uses: actions/deploy-pages@v1.2.5 diff --git a/.github/workflows/pull-request-target.yaml b/.github/workflows/pull-request-target.yaml index ff60c7b89406..81b8762bf0d7 100644 --- a/.github/workflows/pull-request-target.yaml +++ b/.github/workflows/pull-request-target.yaml @@ -5,7 +5,8 @@ on: types: [opened, edited, synchronize, labeled, closed] concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # Generally we use `github.ref`, but in pull_request_target, that's always `main`. + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: @@ -18,10 +19,31 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: requireScope: false + # The standard ones plus + # - "internal" for code quality / ergonomics improvements + # - "devops" for developer ergonomics + # - "web" for playground / website (but not docs) + types: | + feat + fix + docs + style + refactor + perf + test + build + ci + chore + revert + internal + devops + web backport: + # Backport to `web` branch on `pr-backport-web` + name: Backport to `web` branch runs-on: ubuntu-latest - # Confirm that it's merged and has a label to ensure nothing is merged without oversight + # Confirm that it's merged and has a label to ensure nothing is backported without oversight if: | github.event.pull_request.merged && ( diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index c00a026f61f2..0b34fb0e0ef8 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -42,10 +42,6 @@ jobs: - name: ๐Ÿ“‚ Checkout code uses: actions/checkout@v3 # Duplicated in `publish-web.yaml` - - name: ๐Ÿ’ฐ Cache - uses: Swatinem/rust-cache@v2 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - uses: baptiste0928/cargo-install@next with: crate: mdbook @@ -55,9 +51,13 @@ jobs: - uses: baptiste0928/cargo-install@next with: crate: mdbook-admonish + - name: ๐Ÿ’ฐ Cache + uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} - name: ๐Ÿ”จ Build run: mdbook build - working-directory: book/ + working-directory: web/book/ # Run Mac & Windows & bindings' other tests on a `pr-test-all` label or a `!` # (meanting a breaking change) in the PR title. (We also run on other diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c0e6e6cc0fba..d125cc2bf22a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,6 +24,81 @@ jobs: } }) + build-deb-package: + # TODO: currently this doesn't publish it as an artifact; would be a welcome contribution + # to add that (and eventually as a release asset) + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - name: ๐Ÿ‘ท Build prql-compiler + run: cargo build --release + - name: Copy files into .deb package + run: | + mkdir -p .debpkg/usr/bin + cp target/release/prqlc .debpkg/usr/bin/prqlc + chmod +x .debpkg/usr/bin/prqlc + - name: ๐Ÿ“ฆ Build .deb package + uses: jiro4989/build-deb-action@v2 + with: + package: prqlc + package_root: .debpkg + maintainer: The PRQL Project + version: ${{ github.ref }} + desc: | + prqlc is the CLI for the PRQL compiler. It compiles PRQL to SQL, and offers various diagnostics. + + PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement. + + build-rpm-package: + # TODO: This doesn't publish the rpm yet, that would be a welcome follow-up (even as a CI artifact) + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - name: ๐Ÿ‘ท Build prqlc + run: cargo build --bin prqlc --release + - name: Copy files into .rpm package + run: | + mkdir -p .rpmpkg/usr/bin + cp target/release/prqlc .rpmpkg/usr/bin/prqlc + chmod +x .rpmpkg/usr/bin/prqlc + - name: ๐Ÿ“ฆ Build .rpm package + uses: jiro4989/build-rpm-action@v2 + with: + summary: CLI for PRQL, a modern language for transforming data + package: prqlc + package_root: .rpmpkg + maintainer: The PRQL Project + version: ${{ github.ref }} + desc: | + prqlc is the CLI for the PRQL compiler. It compiles PRQL to SQL, and offers various diagnostics. + + PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement. + license: Apache-2.0 + + build-and-publish-snap: + runs-on: ubuntu-latest + + # Skip running workflow on forks + if: github.repository_owner == 'prql' + + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - name: ๐Ÿ“ฆ Build Snap + uses: snapcore/action-build@v1 + with: + path: packages/ + - name: ๐Ÿ†™ Publish Snap + uses: snapcore/action-publish@v1 + env: + SNAPCRAFT_STORE_CREDENTIALS: + ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + with: + snap: ${{ steps.build.outputs.snap }} + release: edge + build-python-wheels: runs-on: ${{ matrix.os }} strategy: @@ -57,7 +132,7 @@ jobs: - uses: ./.github/actions/build-prql-js - name: Publish package on npm run: npm publish - working-directory: prql-js/ + working-directory: bindings/prql-js/ env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -72,7 +147,7 @@ jobs: - uses: richb-hanover/cargo@v1.1.0 with: command: release - args: publish -x + args: publish --no-confirm -x env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test-all.yaml b/.github/workflows/test-all.yaml index 43de52522bb9..c51da0711cf2 100644 --- a/.github/workflows/test-all.yaml +++ b/.github/workflows/test-all.yaml @@ -18,7 +18,7 @@ on: concurrency: # See notes in `pull-request.yaml` - group: ${{ github.workflow }}-${{ github.ref }}-test-all + group: ${{ github.workflow }}-${{ github.ref }}-all cancel-in-progress: true jobs: @@ -27,11 +27,15 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] target_option: ["", --target=wasm32-unknown-unknown] + uses: ./.github/workflows/test-rust.yaml with: os: ${{ matrix.os }} target_option: ${{ matrix.target_option }} + test-php: + uses: ./.github/workflows/test-php.yaml + test-python: uses: ./.github/workflows/test-python.yaml @@ -44,6 +48,12 @@ jobs: test-elixir: uses: ./.github/workflows/test-elixir.yaml + test-dotnet: + uses: ./.github/workflows/test-dotnet.yaml + + test-lib: + uses: ./.github/workflows/test-lib.yaml + measure-code-cov: # Currently disabled due to https://github.com/actions-rs/tarpaulin/issues/21 if: false @@ -56,6 +66,8 @@ jobs: uses: actions/checkout@v3 - name: ๐Ÿ’ฐ Cache uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 with: @@ -105,7 +117,7 @@ jobs: - uses: baptiste0928/cargo-install@next with: crate: cargo-msrv - # Note this currently uses a manually mantained key in + # Note this currently uses a manually maintained key in # `prql-compiler/Cargo.toml`, because of # https://github.com/foresterre/cargo-msrv/issues/590 - name: Verify minimum rust version diff --git a/.github/workflows/test-dotnet.yaml b/.github/workflows/test-dotnet.yaml new file mode 100644 index 000000000000..2e9ffcfaa6a3 --- /dev/null +++ b/.github/workflows/test-dotnet.yaml @@ -0,0 +1,35 @@ +name: test-dotnet + +on: + pull_request: + paths: + - "bindings/prql-dotnet/**" + - "bindings/prql-lib/**" + - ".github/workflows/test-dotnet.yaml" + workflow_call: + +concurrency: + # See notes in `pull-request.yaml` + group: ${{ github.workflow }}-${{ github.ref }}-dotnet + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - name: ๐Ÿ— Build prql-lib + run: cargo build + working-directory: bindings/prql-lib + - name: ๐Ÿ”ง Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7 + - name: ๐Ÿงช Build and test + working-directory: bindings + run: | + dotnet build prql-dotnet + cp ../target/debug/libprql_lib.* prql-dotnet/PrqlCompiler/bin/Debug/net*/ + cp ../target/debug/libprql_lib.* prql-dotnet/PrqlCompiler.Tests/bin/Debug/net*/ + dotnet test prql-dotnet diff --git a/.github/workflows/test-elixir.yaml b/.github/workflows/test-elixir.yaml index e8fb34941c5f..99de8f1cf12d 100644 --- a/.github/workflows/test-elixir.yaml +++ b/.github/workflows/test-elixir.yaml @@ -1,22 +1,19 @@ name: test-elixir on: - push: - branches: - - main - pull_request: - paths: - - "prql-elixir/**" - - ".github/workflows/test-elixir.yaml" + # pull_request: + # paths: + # - "bindings/prql-elixir/**" + # - ".github/workflows/test-elixir.yaml" workflow_call: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-elixir cancel-in-progress: true defaults: run: - working-directory: prql-elixir + working-directory: bindings/prql-elixir env: MIX_ENV: test @@ -25,7 +22,8 @@ jobs: test: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # Currently disabling mac tests, see prql-elixir/readme.md for more details. + os: [ubuntu-latest, windows-latest] otp: ["25.1.2"] elixir: ["1.14.2"] runs-on: ${{matrix.os}} diff --git a/.github/workflows/test-java.yaml b/.github/workflows/test-java.yaml index b3940b283861..a16bae9e8b22 100644 --- a/.github/workflows/test-java.yaml +++ b/.github/workflows/test-java.yaml @@ -1,17 +1,15 @@ name: test-java on: - push: - branches: - - main pull_request: paths: - - "prql-java/**" + - "bindings/prql-java/**" + - "bindings/prql-lib/**" - ".github/workflows/test-java.yaml" workflow_call: concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-test-java + group: ${{ github.workflow }}-${{ github.ref }}-java cancel-in-progress: true jobs: @@ -24,5 +22,5 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Maven test - working-directory: prql-java/java/ + working-directory: bindings/prql-java/java/ run: ./mvnw test diff --git a/.github/workflows/test-js.yaml b/.github/workflows/test-js.yaml index 4dd0344226c4..8ec0b0158c55 100644 --- a/.github/workflows/test-js.yaml +++ b/.github/workflows/test-js.yaml @@ -1,29 +1,26 @@ name: test-js on: - push: - branches: - - main pull_request: paths: - - "prql-js/**" + - "bindings/prql-js/**" - ".github/workflows/test-js.yaml" workflow_call: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-js cancel-in-progress: true jobs: test: - # This is copy & pasted into test-all.yaml runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, macos-latest, windows-latest] + steps: - name: ๐Ÿ“‚ Checkout code uses: actions/checkout@v3 - uses: ./.github/actions/build-prql-js - run: npm cit - working-directory: prql-js + working-directory: bindings/prql-js diff --git a/.github/workflows/test-lib.yaml b/.github/workflows/test-lib.yaml new file mode 100644 index 000000000000..0e1a7a838cbb --- /dev/null +++ b/.github/workflows/test-lib.yaml @@ -0,0 +1,35 @@ +name: test-lib + +on: + pull_request: + paths: + # We also run the tests for the libraries that depend on this, from their workflow files. + - "bindings/prql-lib/**" + - ".github/workflows/test-lib.yaml" + workflow_call: + +concurrency: + # See notes in `pull-request.yaml` + group: ${{ github.workflow }}-${{ github.ref }}-lib + cancel-in-progress: true + +jobs: + test-lib: + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - name: ๐Ÿ’ฐ Cache + uses: Swatinem/rust-cache@v2 + with: + key: 0.6.0-${{ inputs.target_option }} + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: Build + uses: richb-hanover/cargo@v1.1.0 + with: + command: build + # Currently requires a release build; would be useful to allow a debug build. + args: --release -p prql-lib + - name: Run basic C example + working-directory: bindings/prql-lib/examples/minimal-c + run: make run diff --git a/.github/workflows/test-php.yaml b/.github/workflows/test-php.yaml new file mode 100644 index 000000000000..197bfc3477a8 --- /dev/null +++ b/.github/workflows/test-php.yaml @@ -0,0 +1,37 @@ +name: test-php + +on: + pull_request: + paths: + - "bindings/prql-php/**" + - "bindings/prql-lib/**" + - ".github/workflows/test-php.yaml" + workflow_call: + +concurrency: + # See notes in `pull-request.yaml` + group: ${{ github.workflow }}-${{ github.ref }}-php + cancel-in-progress: true + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - name: ๐Ÿ“‚ Checkout code + uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - run: sh build.sh + working-directory: bindings/prql-php + - name: ๐Ÿ“ฆ Install dependencies using Composer + uses: php-actions/composer@v6 + with: + args: --working-dir=bindings/prql-php + php_extensions: FFI + - name: ๐Ÿงช Run tests using PHPUnit + run: vendor/bin/phpunit tests + working-directory: bindings/prql-php diff --git a/.github/workflows/test-python.yaml b/.github/workflows/test-python.yaml index fd00d6c82242..43489999b4c3 100644 --- a/.github/workflows/test-python.yaml +++ b/.github/workflows/test-python.yaml @@ -1,12 +1,9 @@ name: test-python on: - push: - branches: - - main pull_request: paths: - - "prql-python/**" + - "bindings/prql-python/**" - ".github/workflows/test-python.yaml" workflow_call: @@ -45,4 +42,4 @@ jobs: hashFiles('prql-python/requirements.txt') }} - name: Run tests shell: bash - run: nox -s tests -f prql-python/noxfile.py + run: nox -s tests -f bindings/prql-python/noxfile.py diff --git a/.github/workflows/test-rust.yaml b/.github/workflows/test-rust.yaml index db2271459078..1ca2a4743354 100644 --- a/.github/workflows/test-rust.yaml +++ b/.github/workflows/test-rust.yaml @@ -9,19 +9,31 @@ on: target_option: type: string +env: + CARGO_TERM_COLOR: always + jobs: - test: + test-rust: runs-on: ${{ inputs.os }} steps: - name: ๐Ÿ“‚ Checkout code uses: actions/checkout@v3 + - uses: baptiste0928/cargo-install@next + with: + crate: wasm-bindgen-cli + if: inputs.target_option == '--target=wasm32-unknown-unknown' + - uses: baptiste0928/cargo-install@next + with: + crate: cargo-insta - name: ๐Ÿ’ฐ Cache uses: Swatinem/rust-cache@v2 with: - key: ${{ inputs.os }}-${{ inputs.target_option }} + # Based on https://github.com/PRQL/prql/issues/1985, attempting to + # reset the cache. If necessary, we could do this on each release and + # have this update automatically (there's no variable that contains + # the current version unfortunately, though). + key: 0.6.0-${{ inputs.target_option }} save-if: ${{ github.ref == 'refs/heads/main' }} - - uses: jetli/wasm-bindgen-action@v0.2.0 - if: inputs.target_option == '--target=wasm32-unknown-unknown' - name: ๐Ÿ“Ž Clippy uses: richb-hanover/cargo@v1.1.0 with: @@ -41,13 +53,12 @@ jobs: with: command: test args: ${{ inputs.target_option }} --no-run --locked - - uses: baptiste0928/cargo-install@next - with: - crate: cargo-insta - # Only check unreferenced snapshots on the default target tests on ubuntu (maybe - # there's a nicer approach where we can parameterize one step rather than - # have two different ones; we welcome a change to simplify this) - - name: ๐Ÿ“‹ Test + # Only check unreferenced snapshots on the default target tests on ubuntu + # + # (Maybe there's a nicer approach where we can parameterize one step + # rather than have two different ones? We welcome a change to simplify + # this.) + - name: ๐Ÿ“‹ Test default uses: richb-hanover/cargo@v1.1.0 if: ${{ (inputs.target_option != '--target=wasm32-unknown-unknown' && @@ -55,7 +66,7 @@ jobs: with: command: insta args: test --unreferenced=auto ${{ inputs.target_option }} - - name: ๐Ÿ“‹ Test + - name: ๐Ÿ“‹ Test wasm uses: richb-hanover/cargo@v1.1.0 if: ${{ ! (inputs.target_option != '--target=wasm32-unknown-unknown' && diff --git a/.github/workflows/test-taskfile.yaml b/.github/workflows/test-taskfile.yaml index 15dcec83ccb3..5a9fe8c3449c 100644 --- a/.github/workflows/test-taskfile.yaml +++ b/.github/workflows/test-taskfile.yaml @@ -1,18 +1,14 @@ # Workflow to check that the workflows in Taskfile pass. -name: taskfile +name: test-taskfile on: - push: - branches: - - main pull_request: - types: [opened, reopened, synchronize, labeled] paths: - Taskfile.yml - .github/workflows/test-taskfile.yaml concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-taskfile cancel-in-progress: true jobs: diff --git a/.gitignore b/.gitignore index 362d0c3c3c71..702287897cf2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,15 @@ .DS_Store dist -target* +target*/ +*.out lcov.info -**/.vscode/settings.json -**/.vscode/launch.json -**/.vscode/tasks.json -**/.idea +.vscode/settings.json +.vscode/launch.json +.vscode/tasks.json +.idea /*.prql _*.prql @@ -19,3 +20,10 @@ _*.prql **/node_modules/ .task + +# These shouldn't be committed, and cause watchers to re-run when they're +# created, which we don't want. That said, if ignoring them causes confusion +# (e.g. folks look at their git status to assess whether there are pending +# snapshots), we can adjust. +*.pending-snap +*.snap.new diff --git a/.mega-linter.yaml b/.mega-linter.yaml new file mode 100644 index 000000000000..4235b513c9cd --- /dev/null +++ b/.mega-linter.yaml @@ -0,0 +1,35 @@ +# This MegaLinter config is very experimental stage. Open to changes and updates. +# https://github.com/PRQL/prql/pull/1974 + +GITHUB_COMMENT_REPORTER: false +DISABLE: + - RUST + - JAVASCRIPT + - PYTHON +DISABLE_LINTERS: + - SPELL_CSPELL + - CSS_STYLELINT + - PHP_PSALM + - PHP_PHPSTAN # Disabled for now as we couldn't figure out how to prevent false positives. #2069 +DISABLE_ERRORS_LINTERS: + - COPYPASTE_JSCPD + - REPOSITORY_TRIVY + - REPOSITORY_CHECKOV + - REPOSITORY_DEVSKIM + - ACTION_ACTIONLINT + - BASH_SHELLCHECK + - C_CPPLINT + - CPP_CPPLINT + - DOCKERFILE_HADOLINT + - HTML_DJLINT + - HTML_HTMLHINT + - JAVA_CHECKSTYLE + - JAVA_PMD + - JSON_JSONLINT + - MAKEFILE_CHECKMAKE + - MARKDOWN_MARKDOWN_LINK_CHECK + - SPELL_MISSPELL + - SQL_TSQLLINT + - YAML_V8R +PHP_PHPCS_ARGUMENTS: + - --standard=PSR12 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2623a4cf4ae..f7ae30ed711c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,20 +8,20 @@ repos: - id: check-yaml - id: mixed-line-ending - repo: https://github.com/crate-ci/typos - rev: typos-dict-v0.9.16 + rev: v1.13.18 hooks: - id: typos # https://github.com/crate-ci/typos/issues/347 pass_filenames: false - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.4 + rev: v3.0.0-alpha.6 hooks: - id: prettier additional_dependencies: - prettier - prettier-plugin-go-template - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.246 + rev: v0.0.254 hooks: - id: ruff - repo: https://github.com/psf/black @@ -34,7 +34,7 @@ repos: - id: fmt - id: clippy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.0.0 + rev: v1.0.1 hooks: - id: mypy additional_dependencies: ["types-PyYAML==6.0.12.4"] diff --git a/.prettierignore b/.prettierignore index ca7368fe76a4..e8fe196147ea 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,10 +1,9 @@ *.min.* -/book/theme/highlight.js +/web/book/theme/highlight.js # TODO: move these into content out of layouts -/website/themes/prql-theme/layouts/partials/codes/ - -/website/themes/prql-theme/layouts/_default/_markup/render-link.html +/web/website/themes/prql-theme/layouts/partials/codes/ +/web/website/themes/prql-theme/layouts/_default/_markup/render-link.html # Ideally prettier would allow blanket gitignores, https://github.com/prettier/prettier/issues/8048 @@ -14,5 +13,5 @@ target/ .mypy_cache **/build **/dist -/book/book -/website/public +/web/book/book +/web/website/public diff --git a/CHANGELOG.md b/CHANGELOG.md index 294c08c83ebf..c131f1d28577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,129 @@ # PRQL Changelog +## 0.6.1 โ€” 2022-03-12 + +0.6.1 is a small release containing an internal refactoring and improved +bindings for C, PHP & .NET. + +This release has 54 commits from 6 contributors. Selected changes: + +**Fixes**: + +- No longer incorrectly compile to `DISTINCT` when a `take 1` refers to a + different set of columns than are in the `group`. (@max-sixty, with thanks to + @cottrell, #2109) +- The version specification of the dependency Chumsky was bumped from `0.9.0` to + `0.9.2`. `0.9.0` has a bug that causes an infinite loop. (@eitsupi, #2110) + +**Documentation**: + +- Add a policy for which bindings are supported / unsupported / nascent + (@max-sixty, #2062) + +**Integrations**: + +- [prql-lib] Added C++ header file. (@vanillajonathan, #2126) + +**Internal changes**: + +- Many of the items that were in the root of the repo have been aggregated into + `web` & `bindings`, simplifying the repo's structure. There's also `grammars` + & `packages` (@max-sixty, #2135, #2117, #2121). + +## 0.6.0 โ€” 2022-03-08 + +0.6.0 introduces a rewritten parser, giving us the ability to dramatically +improve error messages, renames `switch` to `case` and includes lots of minor +improvements and fixes. It also introduces `loop`, which compiles to +`WITH RECURSIVE`, as a highly experimental feature. + +There are a few cases of breaking changes, including switching `switch` to +`case`, in case that's confusing. There are also some minor parsing changes +outlined below. + +This release has 108 commits from 11 contributors. Selected changes: + +**Features**: + +- Add a (highly experimental) `loop` language feature, which translates to + `WITH RECURSIVE`. We expect changes and refinements in upcoming releases. + (#1642, @aljazerzen) +- Rename the experimental `switch` function to `case` given it more closely + matches the traditional semantics of `case`. (@max-sixty, #2036) +- Change the `case` syntax to use `=>` instead of `->` to distinguish it from + function syntax. +- Convert parser from pest to Chumsky (@aljazerzen, #1818) + - Improved error messages, and the potential to make even better in the + future. Many of these improvements come from error recovery. + - String escapes (`\n \t`). + - Raw strings that don't escape backslashes. + - String interpolations can only contain identifiers and not any expression. + - Operator associativity has been changed from right-to-left to left-to-right + to be more similar to other conventional languages. + - `and` now has a higher precedence than `or` (of same reason as the previous + point). + - Dates, times and timestamps have stricter parsing rules. + - `let`, `func`, `prql`, `case` are now treated as keywords. + - Float literals without fraction part are not allowed anymore (`1.`). +- Add a `--format` option to `prqlc parse` which can return the AST in YAML + (@max-sixty, #1962) +- A new compile target `"sql.any"`. When `"sql.any"` is used as the target of + the compile function's option, the target contained in the query header will + be used. (@aljazerzen, #1995) +- Support for SQL parameters with similar syntax (#1957, @aljazerzen) +- Allow `:` to be elided in timezones, such as `0800` in + `@2020-01-01T13:19:55-0800` (@max-sixty, #1991). +- Add `std.upper` and `std.lower` functions for changing string casing + (@Jelenkee, #2019). + +**Fixes**: + +- `prqlc compile` returns a non-zero exit code for invalid queries. (@max-sixty, + #1924) +- Identifiers can contain any alphabetic unicode characters (@max-sixty, #2003) + +**Documentation**: + +- Operator precedence (@aljazerzen, #1818) +- Error messages for invalid queries are displayed in the book (@max-sixty, + #2015) + +**Integrations**: + +- [prql-php] Added PHP bindings. (@vanillajonathan, #1860) +- [prql-dotnet] Added .NET bindings. (@vanillajonathan, #1917) +- [prql-lib] Added C header file. (@vanillajonathan, #1879) +- Added a workflow building a `.deb` on each release. (Note that it's not yet + published on each release). (@vanillajonathan, #1883) +- Added a workflow building a `.rpm` on each release. (Note that it's not yet + published on each release). (@vanillajonathan, #1918) +- Added a workflow building a Snap package on each release. (@vanillajonathan, + #1881) + +**Internal changes**: + +- Test that the output of our nascent autoformatter can be successfully compiled + into SQL. Failing examples are now clearly labeled. (@max-sixty, #2016) +- Definition files have been added to configure + [Dev Containers](https://containers.dev/) for Rust development environment. + (@eitsupi, #1893, #2025, #2028) + +**New Contributors**: + +- @linux-china, with #1971 +- @Jelenkee, with #2019 + +## 0.5.2 โ€” 2022-02-18 + +0.5.2 is a tiny release to fix an build issue in yesterday's `prql-js` 0.5.1 +release. + +This release has 7 commits from 2 contributors. + +**New Contributors**: + +- @matthias-Q, with #1873 + ## 0.5.1 โ€” 2022-02-17 0.5.1 contains a few fixes, and another change to how bindings handle default @@ -143,7 +267,7 @@ This release has 74 commits from 12 contributors. Selected changes: ## 0.4.0 โ€” 2022-01-15 -0.4.0 brings lots of new features including `switch`, `select ![]` and numbers +0.4.0 brings lots of new features including `case`, `select ![]` and numbers with underscores. We have initial (unpublished) bindings to Elixir. And there's the usual improvements to fixes & documentation (only a minority are listed below in this release). @@ -158,12 +282,12 @@ below in this release). [tables docs](https://prql-lang.org/book/queries/variables.html) for details. - _Experimental:_ The - [`switch`](https://prql-lang.org/book/language-features/switch.html) function - sets a variable to a value based on one of several expressions (@aljazerzen, + [`case`](https://prql-lang.org/book/language-features/case.html) function sets + a variable to a value based on one of several expressions (@aljazerzen, #1278). ```prql - derive var = switch [ + derive var = case [ score <= 10 -> "low", score <= 30 -> "medium", score <= 70 -> "high", @@ -187,8 +311,8 @@ below in this release). ``` Check out the - [`switch` docs](https://prql-lang.org/book/language-features/switch.html) for - more details. + [`case` docs](https://prql-lang.org/book/language-features/case.html) for more + details. - _Experimental:_ Columns can be excluded by name with `select` (@aljazerzen, #1329) @@ -627,8 +751,8 @@ improvements: - More examples on homepage; e.g. `join` & `window`, lots of small docs improvements - Automated releases to homebrew (@roG0d ) -- [prql-js](https://github.com/PRQL/prql/tree/main/prql-js) is now a single - package for node, browsers & webpack (@charlie-sanders ) +- [prql-js](https://github.com/PRQL/prql/tree/main/bindings/prql-js) is now a + single package for Node, browsers & webpack (@charlie-sanders ) - Parsing has some fixes, including `>=` and leading underscores in idents (@mklopets ) - Ranges receive correct syntax highlighting (@max-sixty ) diff --git a/Cargo.lock b/Cargo.lock index bc1e658fc3ff..9ab3d5af3278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" -dependencies = [ - "const-random", -] - [[package]] name = "ahash" version = "0.7.6" @@ -39,9 +30,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "const-random", @@ -76,9 +67,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" dependencies = [ "backtrace", ] @@ -104,7 +95,7 @@ version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87d948f553cf556656eb89265700258e1032d26fec9b7920cd20319336e06afd" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "arrow-arith", "arrow-array", "arrow-buffer", @@ -130,7 +121,7 @@ dependencies = [ "arrow-data", "arrow-schema", "chrono", - "half 2.1.0", + "half 2.2.1", "num", ] @@ -140,13 +131,13 @@ version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fe66ec388d882a61fff3eb613b5266af133aa08a3318e5e493daf0f5c1696cb" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "half 2.1.0", - "hashbrown 0.13.1", + "half 2.2.1", + "hashbrown 0.13.2", "num", ] @@ -156,7 +147,7 @@ version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef967dadbccd4586ec8d7aab27d7033ecb5dfae8a605c839613039eac227bda" dependencies = [ - "half 2.1.0", + "half 2.2.1", "num", ] @@ -184,7 +175,7 @@ checksum = "ee0c0e3c5d3b80be8f267f4b2af714c08cad630569be01a8379cfe27b4866495" dependencies = [ "arrow-buffer", "arrow-schema", - "half 2.1.0", + "half 2.2.1", "num", ] @@ -208,13 +199,13 @@ version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e064ac4e64960ebfbe35f218f5e7d9dc9803b59c2e56f611da28ce6d008f839e" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", - "half 2.1.0", - "hashbrown 0.13.1", + "half 2.2.1", + "hashbrown 0.13.2", ] [[package]] @@ -253,9 +244,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -317,19 +308,19 @@ dependencies = [ [[package]] name = "borsh" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +checksum = "40f9ca3698b2e4cb7c15571db0abc5551dca417a21ae8140460b50309bb2cc62" dependencies = [ "borsh-derive", - "hashbrown 0.11.2", + "hashbrown 0.13.2", ] [[package]] name = "borsh-derive" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +checksum = "598b3eacc6db9c3ee57b22707ad8f6a8d2f6d442bfe24ffeb8cbb70ca59e6a35" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", @@ -340,9 +331,9 @@ dependencies = [ [[package]] name = "borsh-derive-internal" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +checksum = "186b734fa1c9f6743e90c95d7233c9faab6360d1a96d4ffa19d9cfd1e9350f8a" dependencies = [ "proc-macro2", "quote", @@ -351,9 +342,9 @@ dependencies = [ [[package]] name = "borsh-schema-derive-internal" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +checksum = "99b7ff1008316626f485991b960ade129253d4034014616b94f309a15366cc49" dependencies = [ "proc-macro2", "quote", @@ -362,20 +353,21 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" dependencies = [ - "lazy_static", "memchr", + "once_cell", "regex-automata", + "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytecheck" @@ -406,9 +398,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cast" @@ -418,9 +410,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -454,11 +446,12 @@ dependencies = [ [[package]] name = "chumsky" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" +checksum = "23170228b96236b5a7299057ac284a321457700bc8c41a4476052f0f4ba5349d" dependencies = [ - "ahash 0.3.8", + "hashbrown 0.12.3", + "stacker", ] [[package]] @@ -502,13 +495,13 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.1" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", - "clap_lex 0.3.0", + "clap_lex 0.3.2", "is-terminal", "once_cell", "strsim", @@ -518,11 +511,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.0.6" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da" +checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b" dependencies = [ - "clap 4.1.1", + "clap 4.1.6", ] [[package]] @@ -549,9 +542,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" dependencies = [ "os_str_bytes", ] @@ -562,9 +555,9 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94b38784f05c5e908bb8751e9b9f29fbcd470f636c0d0a76a0f90c0c823f3b68" dependencies = [ - "clap 4.1.1", + "clap 4.1.6", "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -616,9 +609,9 @@ dependencies = [ [[package]] name = "comfy-table" -version = "6.1.3" +version = "6.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e621e7e86c46fd8a14c32c6ae3cb95656621b4743a27d0cffedb831d46e7ad21" +checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" dependencies = [ "strum", "strum_macros", @@ -627,21 +620,21 @@ dependencies = [ [[package]] name = "compile-files" -version = "0.5.1" +version = "0.6.1" dependencies = [ "prql-compiler", ] [[package]] name = "console" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -819,9 +812,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -831,9 +824,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -846,15 +839,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", @@ -899,9 +892,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encode_unicode" @@ -921,19 +914,6 @@ dependencies = [ "syn", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -992,23 +972,23 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1037,9 +1017,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -1047,15 +1027,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -1064,21 +1044,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-core", "futures-macro", @@ -1121,15 +1101,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -1146,9 +1126,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "half" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ "crunchy", "num-traits", @@ -1168,15 +1148,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.6", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1188,9 +1159,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] [[package]] name = "hashlink" @@ -1203,9 +1177,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -1225,6 +1199,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hmac" version = "0.12.1" @@ -1292,9 +1272,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" [[package]] name = "inotify" @@ -1343,24 +1323,24 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1508,9 +1488,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libduckdb-sys" @@ -1600,15 +1580,15 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ed28d5903dde77bd5182645078a37ee57014cac6ccb2d54e1d6496386648e4" +checksum = "0f387adfd85d2eeebe3d47d67b1e3f8c9d83ef327582558c5ca88f78d171e73a" dependencies = [ "anyhow", "chrono", - "clap 4.1.1", + "clap 4.1.6", "clap_complete", - "env_logger 0.10.0", + "env_logger", "handlebars", "log", "memchr", @@ -1626,10 +1606,10 @@ dependencies = [ [[package]] name = "mdbook-prql" -version = "0.5.1" +version = "0.6.1" dependencies = [ "anyhow", - "clap 4.1.1", + "clap 4.1.6", "globset", "insta", "itertools", @@ -1669,6 +1649,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minijinja" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95c8f0999f88dd509934824ce03582ffbbcabf8af6484107572618732660c54" +dependencies = [ + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1686,21 +1675,21 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -1721,7 +1710,7 @@ dependencies = [ "libc", "mio", "walkdir", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1751,9 +1740,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -1822,18 +1811,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -1843,9 +1832,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opener" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" +checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" dependencies = [ "bstr", "winapi", @@ -1884,15 +1873,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1903,9 +1892,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" dependencies = [ "thiserror", "ucd-trie", @@ -1913,9 +1902,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344" +checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" dependencies = [ "pest", "pest_generator", @@ -1923,9 +1912,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c" +checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" dependencies = [ "pest", "pest_meta", @@ -1936,13 +1925,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20" +checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" dependencies = [ "once_cell", "pest", - "sha1", + "sha2", ] [[package]] @@ -2111,16 +2100,16 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "prql" -version = "0.5.1" +version = "0.6.1" dependencies = [ "prql-compiler", "rustler", @@ -2128,7 +2117,7 @@ dependencies = [ [[package]] name = "prql-compiler" -version = "0.5.1" +version = "0.6.1" dependencies = [ "anyhow", "ariadne", @@ -2144,8 +2133,6 @@ dependencies = [ "lazy_static", "log", "once_cell", - "pest", - "pest_derive", "postgres", "pretty_assertions", "regex", @@ -2161,7 +2148,7 @@ dependencies = [ [[package]] name = "prql-compiler-macros" -version = "0.5.1" +version = "0.6.1" dependencies = [ "prql-compiler", "syn", @@ -2169,7 +2156,7 @@ dependencies = [ [[package]] name = "prql-java" -version = "0.5.1" +version = "0.6.1" dependencies = [ "jni", "prql-compiler", @@ -2177,7 +2164,7 @@ dependencies = [ [[package]] name = "prql-js" -version = "0.5.1" +version = "0.6.1" dependencies = [ "console_error_panic_hook", "prql-compiler", @@ -2187,7 +2174,7 @@ dependencies = [ [[package]] name = "prql-lib" -version = "0.5.1" +version = "0.6.1" dependencies = [ "libc", "prql-compiler", @@ -2196,7 +2183,7 @@ dependencies = [ [[package]] name = "prql-python" -version = "0.5.1" +version = "0.6.1" dependencies = [ "insta", "prql-compiler", @@ -2206,24 +2193,36 @@ dependencies = [ [[package]] name = "prqlc" -version = "0.5.1" +version = "0.6.1" dependencies = [ "anyhow", "ariadne", "atty", - "clap 4.1.1", + "clap 4.1.6", "clio", "color-eyre", - "env_logger 0.9.3", + "env_logger", "insta", "itertools", + "minijinja", "notify", "prql-compiler", + "regex", + "serde", "serde_json", "serde_yaml", "walkdir", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -2267,9 +2266,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd4149c8c3975099622b4e1962dac27565cf5663b76452c3e2b66e0b6824277" +checksum = "06a3d8e8a46ab2738109347433cb7b96dffda2e4a218b03ef27090238886b147" dependencies = [ "cfg-if", "indoc", @@ -2284,9 +2283,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd09fe469834db21ee60e0051030339e5d361293d8cb5ec02facf7fdcf52dbf" +checksum = "75439f995d07ddfad42b192dfcf3bc66a7ecfd8b4a1f5f6f046aa5c2c5d7677d" dependencies = [ "once_cell", "target-lexicon", @@ -2294,9 +2293,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c427c9a96b9c5b12156dbc11f76b14f49e9aae8905ca783ea87c249044ef137" +checksum = "839526a5c07a17ff44823679b68add4a58004de00512a95b6c1c98a6dcac0ee5" dependencies = [ "libc", "pyo3-build-config", @@ -2304,9 +2303,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b822bbba9d60630a44d2109bc410489bb2f439b33e3a14ddeb8a40b378a7c4" +checksum = "bd44cf207476c6a9760c4653559be4f206efafb924d3e4cbf2721475fc0d6cc5" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -2316,9 +2315,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ae898104f7c99db06231160770f3e40dad6eb9021daddc0fedfa3e41dff10a" +checksum = "dc1f43d8e30460f36350d18631ccf85ded64c059829208fe680904c65bcd0a4c" dependencies = [ "proc-macro2", "quote", @@ -2376,9 +2375,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2397,9 +2396,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2429,18 +2428,18 @@ dependencies = [ [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "c30f1d45d9aa61cbc8cd1eb87705470892289bb2d01943e7803b873a57404dc3" dependencies = [ "bytecheck", "hashbrown 0.12.3", @@ -2452,9 +2451,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "ff26ed6c7c4dfc2aa9480b86a60e3c7233543a270a680e10758a507c5a4ce476" dependencies = [ "proc-macro2", "quote", @@ -2478,9 +2477,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.27.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c321ee4e17d2b7abe12b5d20c1231db708dd36185c8a21e9de5fed6da4dbe9" +checksum = "e13cf35f7140155d02ba4ec3294373d513a3c7baa8364c162b030e33c61520a8" dependencies = [ "arrayvec", "borsh", @@ -2502,16 +2501,16 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "rustix" -version = "0.36.5" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -2603,18 +2602,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -2623,9 +2622,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -2634,9 +2633,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", "itoa", @@ -2645,17 +2644,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.6" @@ -2696,9 +2684,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -2732,14 +2720,27 @@ dependencies = [ [[package]] name = "sqlparser" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67dc6ef36edb658196c3fef0464a80b53dbbc194a904e81f9bd4190f9ecc5b" +checksum = "0366f270dbabb5cc2e4c88427dc4c08bba144f81e32fbd459a013f26a4d16aa0" dependencies = [ "log", "serde", ] +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2803,9 +2804,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" @@ -2823,21 +2824,21 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907" +checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a" dependencies = [ "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -2868,10 +2869,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -2916,9 +2918,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -2933,7 +2935,7 @@ dependencies = [ "mio", "pin-project-lite", "socket2", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -2962,9 +2964,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -2976,9 +2978,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -3033,9 +3035,9 @@ dependencies = [ [[package]] name = "trash" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f74274f95c7e7340d7c5bcd4863af87a9ed6a117cf73cf483c71cb4d744a948" +checksum = "a27b2a127810fceb959593bbc6c7b8e0282c2d318d76f0749252197c52a1dd0c" dependencies = [ "chrono", "libc", @@ -3070,9 +3072,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -3313,9 +3315,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.43.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -3328,9 +3339,18 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -3343,45 +3363,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "yaml-rust" diff --git a/Cargo.toml b/Cargo.toml index b51460ce07fc..30d13c22c079 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,32 +1,38 @@ [workspace] members = [ - "prql-compiler", - "prql-compiler/examples/compile-files", - "prql-compiler/prql-compiler-macros", - "prql-compiler/prqlc", - "prql-java", - "prql-lib", - "prql-js", - "prql-python", - "book", - "prql-elixir/native/prql", + # The main crate + "prql-compiler", # + # The CLI + "prql-compiler/prqlc", # + # Macros + "prql-compiler/prql-compiler-macros", # + # An example + "prql-compiler/examples/compile-files", # + # Bindings + "bindings/prql-elixir/native/prql", + "bindings/prql-java", + "bindings/prql-js", + "bindings/prql-lib", + "bindings/prql-python", # + # The book / docs + "web/book", ] # Note we don't have a `default-members = ["prql-compiler"]`, since that causes # commands like `cargo test` to only run tests from the default package. And # `cargo insta test` doesn't have a `--workspace` flag. +resolver = "2" [workspace.package] edition = "2021" license = "Apache-2.0" repository = "https://github.com/PRQL/prql" rust-version = "1.65.0" -version = "0.5.1" +version = "0.6.1" [profile.release.package.prql-js] -# Tell `rustc` to optimize for small code size. +# Tell `rust-js` to optimize for small code size. opt-level = "s" [workspace.metadata.release] allow-branch = ["*"] consolidate-commits = true -pre-release-commit-message = "chore: Release {{version}}" diff --git a/Dockerfile b/Dockerfile index 98050395e45e..f97fd15bfcb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,10 +45,10 @@ RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/loc # ========= Set up workdir & copy the taskfile ========= WORKDIR /src -COPY Taskfile.yml . +COPY Taskfile.cargo-tools.yml . # ========= Install cargo-tools ========= -RUN task install-cargo-tools +RUN task -t Taskfile.cargo-tools.yml install # TODO: currently this doesn't support doing things like running the playground, # since we don't install hugo & node. Default `apt` doesn't install up-to-date diff --git a/README.md b/README.md index f7b835a6efa8..cd843393f16a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Website](https://img.shields.io/badge/INTRO-WEB-blue?style=for-the-badge)](https://prql-lang.org) -[![Playground](https://img.shields.io/badge/INTRO-PLAYGROUND-blue?style=for-the-badge)](https://prql-lang.org/book) +[![Playground](https://img.shields.io/badge/INTRO-PLAYGROUND-blue?style=for-the-badge)](https://prql-lang.org/playground) [![Language Docs](https://img.shields.io/badge/DOCS-BOOK-blue?style=for-the-badge)](https://prql-lang.org/book) [![Discord](https://img.shields.io/discord/936728116712316989?label=discord%20chat&style=for-the-badge)](https://discord.gg/eQcfaCmsNc) diff --git a/Taskfile.cargo-tools.yml b/Taskfile.cargo-tools.yml new file mode 100644 index 000000000000..814e4108fece --- /dev/null +++ b/Taskfile.cargo-tools.yml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://json.schemastore.org/taskfile.json + +version: 3 + +tasks: + install: + # factored this out because it takes a long time to build + desc: Install cargo tools for PRQL development. + cmds: + # `--locked` installs from the underlying lock files (which is not the + # default?!) + - cargo install --locked bacon cargo-audit cargo-insta cargo-release + default-target mdbook mdbook-admonish mdbook-toc wasm-bindgen-cli + wasm-pack + # Can't install atm with `--locked` + - cargo install mdbook-footnote + - cmd: | + [ "$(which cargo-insta)" ] || echo "๐Ÿ”ด Can't find a binary that cargo just installed. Is the cargo bin path (generally at ~/.cargo/bin) on the \$PATH?" + silent: true diff --git a/Taskfile.yml b/Taskfile.yml index bcdf31a59a4b..ccce977c79f6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,6 +11,9 @@ version: "3" +includes: + cargo-tools: ./Taskfile.cargo-tools.yml + vars: # Keep in sync with .vscode/extensions.json vscode_extensions: | @@ -38,7 +41,7 @@ tasks: desc: Install tools for PRQL development. cmds: - - task: install-cargo-tools + - task: cargo-tools:install - task: install-brew-dependencies - task: install-maturin - task: install-pre-commit @@ -54,18 +57,9 @@ tasks: install-cargo-tools: # factored this out because it takes a long time to build - desc: Install cargo tools for PRQL development. - cmds: - # `--locked` installs from the underlying lock files (which is not the - # default?!) - - cargo install --locked cargo-audit cargo-insta cargo-release - default-target mdbook mdbook-admonish mdbook-toc wasm-bindgen-cli - wasm-pack - # Can't install atm with `--locked` - - cargo install mdbook-footnote - - cmd: | - [ "$(which default-target)" ] || echo "๐Ÿ”ด Can't find a binary that cargo just installed. Is the cargo bin path (generally at ~/.cargo/bin) on the \$PATH?" - silent: true + desc: Alias of `cargo-tools:install` + deps: + - cargo-tools:install install-maturin: desc: Install maturin. @@ -151,17 +145,9 @@ tasks: Build everything. Running this isn't required when developing; it's for caching or as a reference. - vars: - default_target: - sh: default-target - targets: | - {{ .default_target }} - wasm32-unknown-unknown cmds: - - | - {{ range ( .targets | trim | splitLines ) -}} - cargo build --all-targets --target={{.}} - {{ end -}} + - cargo build --all-targets --target=wasm32-unknown-unknown + - cargo build --all-targets - task: build-web test-all: @@ -177,14 +163,10 @@ tasks: - task: test-rust - task: build-all - task: test-js - - task: test-elixir - pre-commit run -a test-rust: desc: Test all rust code, accepting snapshots. - vars: - default_target: - sh: default-target # Commenting out the ability to watch, since Task seems to trip over itself, # starting a new process before the old one has finished when a process # changes output files. @@ -203,25 +185,16 @@ tasks: # - "**/*.md" # - "**/*.toml" # - "**/*.lock" - # - "**/*.pest" # - "**/*.snap" cmds: - # We run this `snapshot` first for the reasons given in - # `parser::test_parse_pipeline_parse_tree`. Ideally we would a) retain - # examples in the documentation (inc. tested), b) not rely on falliable - # tests to extract them, which include compiling prql-compiler, c) not - # fail other compilation steps if they can't be extracted. - - cargo insta test --accept -p mdbook-prql --test snapshot - --target={{.default_target}} # Only delete unreferenced snapshots on the default target โ€” lots are # excluded under wasm. Note that this will also over-delete on Windows. # Note that we need to pass the target explicitly to manage # https://github.com/rust-lang/cargo/issues/8899 - - cargo insta test --accept --target={{.default_target}} - --unreferenced=auto + - cargo insta test --accept --unreferenced=auto - cargo insta test --accept --target=wasm32-unknown-unknown # We build the book too, because that acts as a test - - cd book && mdbook build + - cd web/book && mdbook build test-rust-fast: desc: Test prql-compiler's unit tests. @@ -244,14 +217,13 @@ tasks: # by `--accept` and task not interrupting existing runs, so all files go # to `.snap`s which match the generated output?). - "prql-compiler/**/*.rs" - - "prql-compiler/**/*.pest" - "prql-compiler/**/*.snap" cmds: - - cargo insta test --accept -p prql-compiler --lib - --target=$(default-target) + - cargo insta test --accept -p prql-compiler --lib -- --quiet build-web: desc: Build the website, including the book & playground. + dir: web cmds: - cd website && hugo --minify - cd book && mdbook build @@ -276,30 +248,31 @@ tasks: - npm run build - npm cit - test-elixir: - dir: prql-elixir - cmds: - # We could move this line into an `install` task - - mix local.hex --force - - mix deps.get --force - - mix compile - - mix test + # Currently disabled; see prql-elixir/README.md for details + # test-elixir: + # dir: prql-elixir + # cmds: + # # We could move this line into an `install` task + # - mix local.hex --force + # - mix deps.get --force + # - mix compile + # - mix test run-website: desc: Build & serve the static website. - dir: website + dir: web/website cmds: - hugo server run-book: desc: Build & serve the book. - dir: book + dir: web/book cmds: - mdbook serve --port=3000 run-playground: desc: Build & serve the playground. - dir: playground + dir: web/playground cmds: # Must set `install-links=false` in the playground's `npm install` to # install prql-js as the regular dependency, instead of creating a diff --git a/_typos.toml b/_typos.toml index 1480bde0ec83..85153656cd55 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,7 +1,11 @@ [files] extend-exclude = [ - "book/theme/highlight.js", + "web/book/theme/highlight.js", "prql-compiler/tests/integration/data/", - "website/themes/prql-theme/static/plugins/bootstrap", - "website/themes/prql-theme/static/plugins/highlight/highlight.min.js", + "web/website/themes/prql-theme/static/plugins/bootstrap", + "web/website/themes/prql-theme/static/plugins/highlight/highlight.min.js", ] + +[default.extend-words] +# Java test framework +testng = "testng" diff --git a/bacon.toml b/bacon.toml new file mode 100644 index 000000000000..8b65fac4e6d6 --- /dev/null +++ b/bacon.toml @@ -0,0 +1,63 @@ +# Initial bacon config file; edits and contributions welcome. + +default_job = "check" + +# PRQL additions +[jobs.test-fast] +command = ['cargo', 'insta', 'test', '--accept', "--color=always", "-p=prql-compiler", "--lib"] +watch = ["tests", "benches", "examples"] + +[jobs.test] +command = ['cargo', 'insta', 'test', '--accept', "--color=always", "--unreferenced=auto"] +watch = ["tests", "benches", "examples"] + +# Standard tasks + +[jobs.check] +command = ["cargo", "check", "--color", "always"] +need_stdout = false +watch = ["tests", "benches", "examples"] + +[jobs.check-all] +command = ["cargo", "check", "--all-targets", "--color", "always"] +need_stdout = false +watch = ["tests", "benches", "examples"] + +[jobs.clippy] +command = ["cargo", "clippy", "--all-targets", "--color", "always"] +need_stdout = false +watch = ["tests", "benches", "examples"] + +[jobs.doc] +command = ["cargo", "doc", "--color", "always", "--no-deps"] +need_stdout = false + +# If the doc compiles, then it opens in your browser and bacon switches +# to the previous job +[jobs.doc-open] +command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"] +need_stdout = false +on_success = "back" # so that we don't open the browser at each change + +# You can run your application and have the result displayed in bacon, +# *if* it makes sense for this crate. You can run an example the same +# way. Don't forget the `--color always` part or the errors won't be +# properly parsed. +# If you want to pass options to your program, a `--` separator +# will be needed. +[jobs.run] +allow_warnings = true +command = ["cargo", "run", "--color", "always"] +need_stdout = true + +# You may define here keybindings that would be specific to +# a project, for example a shortcut to launch a specific job. +# Shortcuts to internal functions (scrolling, toggling, etc.) +# should go in your personal prefs.toml file instead. +[keybindings] +a = "job:check-all" +c = "job:clippy" +d = "job:doc-open" +f = "job:test-fast" +r = "job:run" +t = "job:test" diff --git a/bindings/prql-dotnet/.gitignore b/bindings/prql-dotnet/.gitignore new file mode 100644 index 000000000000..1746e3269ed0 --- /dev/null +++ b/bindings/prql-dotnet/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/bindings/prql-dotnet/PrqlCompiler.Tests/CompilerTest.cs b/bindings/prql-dotnet/PrqlCompiler.Tests/CompilerTest.cs new file mode 100644 index 000000000000..c9bb7f06ddfa --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler.Tests/CompilerTest.cs @@ -0,0 +1,52 @@ +using Prql.Compiler; + +namespace Prql.Compiler.Tests; + +sealed public class CompilerTest +{ + [Fact] + public void ToCompile_Works() + { + // Arrange + var expected = "SELECT * FROM employees"; + var options = new PrqlCompilerOptions + { + Format = false, + SignatureComment = false, + Target = "sql.mssql" + }; + + // Act + var result = PrqlCompiler.Compile("from employees", options); + + // Assert + Assert.Equal(expected, result.Output); + } + + [Fact(Skip = "Fails")] + public void TestOtherFunctions() + { + // Arrange + var query = """ + let a = (from employees | take 10) + + from a | select [first_name] + """; + var options = new PrqlCompilerOptions(); + + // Act and assert + var pl = PrqlCompiler.PrqlToPl(query); + Assert.Empty(pl.Messages); + + var rq = PrqlCompiler.PlToRq(pl.Output); + Assert.Empty(rq.Messages); + + var via_json = PrqlCompiler.RqToSql(rq.Output, options); + Assert.Empty(via_json.Messages); + + var direct = PrqlCompiler.Compile(query); + Assert.Empty(direct.Messages); + + Assert.Equal(via_json, direct); + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler.Tests/PrqlCompiler.Tests.csproj b/bindings/prql-dotnet/PrqlCompiler.Tests/PrqlCompiler.Tests.csproj new file mode 100644 index 000000000000..eeddc9523e9a --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler.Tests/PrqlCompiler.Tests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/bindings/prql-dotnet/PrqlCompiler.Tests/Usings.cs b/bindings/prql-dotnet/PrqlCompiler.Tests/Usings.cs new file mode 100644 index 000000000000..c802f4480b19 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; diff --git a/bindings/prql-dotnet/PrqlCompiler/Message.cs b/bindings/prql-dotnet/PrqlCompiler/Message.cs new file mode 100644 index 000000000000..d580dd823000 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/Message.cs @@ -0,0 +1,46 @@ +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// Compile result message. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct Message + { + /// + /// Message kind. Currently only Error is implemented. + /// + public MessageKind Kind { get; set; } + + /// + /// Machine-readable identifier of the error. + /// + public string Code { get; set; } + + /// + /// Plain text of the error. + /// + public string Reason { get; set; } + + /// + /// A list of suggestions of how to fix the error. + /// + public string Hint { get; set; } + + /// + /// Character offset of error origin within a source file. + /// + public Span Span { get; set; } + + /// + /// Annotated code, containing cause and hints. + /// + public string Display { get; set; } + + /// + /// Line and column number of error origin within a source file. + /// + public SourceLocation Location { get; set; } + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/MessageKind.cs b/bindings/prql-dotnet/PrqlCompiler/MessageKind.cs new file mode 100644 index 000000000000..efb0a4cb015f --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/MessageKind.cs @@ -0,0 +1,24 @@ +using System; + +namespace Prql.Compiler +{ + /// + /// Compile message kind. Currently only Error is implemented. + /// + [Serializable] + public enum MessageKind + { + /// + /// Error message. + /// + Error, + /// + /// Warning message. + /// + Warning, + /// + /// Lint message. + /// + Lint + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/NativePrqlCompilerOptions.cs b/bindings/prql-dotnet/PrqlCompiler/NativePrqlCompilerOptions.cs new file mode 100644 index 000000000000..c267e4eeed1e --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/NativePrqlCompilerOptions.cs @@ -0,0 +1,16 @@ +namespace Prql.Compiler +{ + internal struct NativePrqlCompilerOptions + { + public bool Format; + public string Target; + public bool SignatureComment; + + public NativePrqlCompilerOptions(PrqlCompilerOptions options) + { + Format = options.Format; + Target = options.Target; + SignatureComment = options.SignatureComment; + } + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/NativeResult.cs b/bindings/prql-dotnet/PrqlCompiler/NativeResult.cs new file mode 100644 index 000000000000..304559dee481 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/NativeResult.cs @@ -0,0 +1,11 @@ +using System; + +namespace Prql.Compiler +{ + internal struct NativeResult + { + public string Output; + public IntPtr Messages; + public int MessagesLen; + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.cs b/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.cs new file mode 100644 index 000000000000..a275bfcf168d --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.cs @@ -0,0 +1,141 @@ +using System; +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// The PRQL compiler transpiles RPQL queries. + /// + public static class PrqlCompiler + { + /// + /// Compile a PRQL string into a SQL string. + /// + /// A PRQL query. + /// SQL query. + /// is null or empty. + /// cannot be compiled. + public static Result Compile(string prqlQuery) + { + if (string.IsNullOrEmpty(prqlQuery)) + { + throw new ArgumentException(nameof(prqlQuery)); + } + + var options = new PrqlCompilerOptions(); + + return Compile(prqlQuery, options); + } + + /// + /// Compile a PRQL string into a SQL string. + /// + /// A PRQL query. + /// PRQL compiler options. + /// SQL query. + /// is null or empty. + /// is null. + /// cannot be compiled. + public static Result Compile(string prqlQuery, PrqlCompilerOptions options) + { + if (string.IsNullOrEmpty(prqlQuery)) + { + throw new ArgumentException(nameof(prqlQuery)); + } + + if (options is null) + { + throw new ArgumentException(nameof(options)); + } + + var nativeOptions = new NativePrqlCompilerOptions(options); + var nativeResult = CompileExtern(prqlQuery, ref nativeOptions); + var result = new Result(nativeResult); + + return result; + } + + /// + /// Build PL AST from a PRQL string. + /// + /// A PRQL query. + /// JSON. + /// is null or empty. + /// cannot be compiled. + /// https://docs.rs/prql-compiler/latest/prql_compiler/ast/pl + public static Result PrqlToPl(string prqlQuery) + { + if (string.IsNullOrEmpty(prqlQuery)) + { + throw new ArgumentException(nameof(prqlQuery)); + } + + var nativeResult = PrqlToPlExtern(prqlQuery); + var result = new Result(nativeResult); + + return result; + } + + /// + /// Finds variable references, validates functions calls, determines frames and converts PL to RQ. + /// + /// A PRQL query. + /// JSON. + /// is null or empty. + /// cannot be compiled. + /// https://docs.rs/prql-compiler/latest/prql_compiler/ast + public static Result PlToRq(string plJson) + { + if (string.IsNullOrEmpty(plJson)) + { + throw new ArgumentException(nameof(plJson)); + } + + var nativeResult = PlToRqExtern(plJson); + var result = new Result(nativeResult); + + return result; + } + + /// + /// Convert RQ AST into an SQL string. + /// + /// RQ string in JSON format. + /// PRQL compiler options. + /// JSON. + /// is null or empty. + /// is null. + /// cannot be compiled. + /// https://docs.rs/prql-compiler/latest/prql_compiler/ast/rq + public static Result RqToSql(string rqJson, PrqlCompilerOptions options) + { + if (string.IsNullOrEmpty(rqJson)) + { + throw new ArgumentException(nameof(rqJson)); + } + + if (options is null) + { + throw new ArgumentException(nameof(options)); + } + + var nativeOptions = new NativePrqlCompilerOptions(options); + var nativeResult = RqToSqlExtern(rqJson, ref nativeOptions); + var result = new Result(nativeResult); + + return result; + } + + [DllImport("libprql_lib", EntryPoint = "compile")] + private static extern NativeResult CompileExtern(string prqlQuery, ref NativePrqlCompilerOptions options); + + [DllImport("libprql_lib", EntryPoint = "prql_to_pl")] + private static extern NativeResult PrqlToPlExtern(string prqlQuery); + + [DllImport("libprql_lib", EntryPoint = "pl_to_rq")] + private static extern NativeResult PlToRqExtern(string plJson); + + [DllImport("libprql_lib", EntryPoint = "rq_to_sql")] + private static extern NativeResult RqToSqlExtern(string rqJson, ref NativePrqlCompilerOptions options); + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.csproj b/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.csproj new file mode 100644 index 000000000000..2d530e70a232 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/PrqlCompiler.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + + + + Prql.Compiler + 0.1.0 + PRQL Compiler + PRQL + Copyright ยฉ The PRQL Project 2023 + .NET bindings for the PRQL compiler. PRQL is a modern language for transforming data + Apache-2.0 + https://prql-lang.org/ + README.md + prql;sql + https://github.com/PRQL/prql + + + + + True + \ + + + + diff --git a/bindings/prql-dotnet/PrqlCompiler/PrqlCompilerOptions.cs b/bindings/prql-dotnet/PrqlCompiler/PrqlCompilerOptions.cs new file mode 100644 index 000000000000..4b827e91073f --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/PrqlCompilerOptions.cs @@ -0,0 +1,29 @@ +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// Compilation options for SQL backend of the compiler. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public class PrqlCompilerOptions + { + /// + /// Pass generated SQL string trough a formatter that splits it into + /// multiple lines and prettifies indentation and spacing. + /// + /// Defaults to true. + public bool Format { get; set; } = true; + + /// + /// Target and dialect to compile to. + /// + public string Target { get; set; } + + /// + /// Emits the compiler signature as a comment after generated SQL. + /// + /// Defaults to true. + public bool SignatureComment { get; set; } = true; + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/Result.cs b/bindings/prql-dotnet/PrqlCompiler/Result.cs new file mode 100644 index 000000000000..60568751d282 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/Result.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// Result of compilation. + /// + public class Result + { + private readonly IReadOnlyCollection _messages; + + internal Result(NativeResult result) + { + Output = result.Output; + + var messages = new List(); + + for (var i = 0; i < result.MessagesLen; i++) + { + messages.Add(Marshal.PtrToStructure(result.Messages)); + } + + _messages = messages.ToList().AsReadOnly(); + } + + /// + /// The compiler output. + /// + public string Output { get; } + + /// + /// Error, warning and lint messages. + /// + public IReadOnlyCollection Messages => _messages; + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/SourceLocation.cs b/bindings/prql-dotnet/PrqlCompiler/SourceLocation.cs new file mode 100644 index 000000000000..426c65835def --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/SourceLocation.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// Location within a source file. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct SourceLocation + { + /// + /// Start line. + /// + public int StartLine { get; set; } + + /// + /// Start column. + /// + public int StartCol { get; set; } + + /// + /// End line. + /// + public int EndLine { get; set; } + + /// + /// End column. + /// + public int EndCol { get; set; } + } +} diff --git a/bindings/prql-dotnet/PrqlCompiler/Span.cs b/bindings/prql-dotnet/PrqlCompiler/Span.cs new file mode 100644 index 000000000000..441347a96390 --- /dev/null +++ b/bindings/prql-dotnet/PrqlCompiler/Span.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +namespace Prql.Compiler +{ + /// + /// Identifier of a location in source. + /// Contains offsets in terms of chars. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct Span + { + /// + /// Start offset. + /// + public int Start { get; set; } + + /// + /// End offset. + /// + public int End { get; set; } + } +} diff --git a/bindings/prql-dotnet/README.md b/bindings/prql-dotnet/README.md new file mode 100644 index 000000000000..6021e017b360 --- /dev/null +++ b/bindings/prql-dotnet/README.md @@ -0,0 +1,38 @@ +# prql-dotnet + +`prql-net` offers PRQL bindings for .NET bindings as a `netstandard2.0` library. + +It provides the `PrqlCompiler` class which contains the `ToJson` and `ToSql` +static methods. + +It's still at an early stage, and isn't published to NuGet. Contributions are +welcome. + +## Installation + +Make sure that `libprql_lib.so` (Linux), `libprql_lib.dylib` (macOS) or +`libprql_lib.dll` (Windows) is in your project's `bin` directory together with +`PrqlCompiler.dll` and the rest of your project's compiled files. I.e. +`{your_project}/bin/Debug/net7.0/`. + +The `libprql_lib` library gets dynamically imported at runtime. + +## Usage + +```csharp +using Prql.Compiler; + +var options = new PrqlCompilerOptions +{ + Format = false, + SignatureComment = false, +}; +var sql = PrqlCompiler.Compile("from employees", options); +Console.WriteLine(sql); +``` + +# TODO + +This is currently at 0.1.0 because we're waiting to update prql-lib for the +latest API. When we've done that, we can match the version here with the broader +PRQL version. diff --git a/bindings/prql-dotnet/prql-net.sln b/bindings/prql-dotnet/prql-net.sln new file mode 100644 index 000000000000..76842e4e1189 --- /dev/null +++ b/bindings/prql-dotnet/prql-net.sln @@ -0,0 +1,28 @@ +๏ปฟ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrqlCompiler", "PrqlCompiler\PrqlCompiler.csproj", "{339EA2A6-23D2-4938-884F-052431AC0674}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrqlCompiler.Tests", "PrqlCompiler.Tests\PrqlCompiler.Tests.csproj", "{78C1AD08-6FF5-444E-9298-385887ABAA80}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {339EA2A6-23D2-4938-884F-052431AC0674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {339EA2A6-23D2-4938-884F-052431AC0674}.Debug|Any CPU.Build.0 = Debug|Any CPU + {339EA2A6-23D2-4938-884F-052431AC0674}.Release|Any CPU.ActiveCfg = Release|Any CPU + {339EA2A6-23D2-4938-884F-052431AC0674}.Release|Any CPU.Build.0 = Release|Any CPU + {78C1AD08-6FF5-444E-9298-385887ABAA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78C1AD08-6FF5-444E-9298-385887ABAA80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78C1AD08-6FF5-444E-9298-385887ABAA80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78C1AD08-6FF5-444E-9298-385887ABAA80}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/prql-elixir/.formatter.exs b/bindings/prql-elixir/.formatter.exs similarity index 100% rename from prql-elixir/.formatter.exs rename to bindings/prql-elixir/.formatter.exs diff --git a/prql-elixir/.gitignore b/bindings/prql-elixir/.gitignore similarity index 100% rename from prql-elixir/.gitignore rename to bindings/prql-elixir/.gitignore diff --git a/bindings/prql-elixir/README.md b/bindings/prql-elixir/README.md new file mode 100644 index 000000000000..e3b7c7e0b956 --- /dev/null +++ b/bindings/prql-elixir/README.md @@ -0,0 +1,102 @@ +# PRQL + +[PRQL](https://prql-lang.org/) bindings for Elixir. + +## Installation + +```elixir +def deps do + [ + {:prql, "~> 0.1.0"} + ] +end +``` + +## Basic Usage + +```elixir + iex> PRQL.compile("from customers") + {:ok, "SELECT\n *\nFROM\n customers\n\n-- Generated by PRQL compiler version 0.3.1 (https://prql-lang.org)\n"} + + + iex> PRQL.compile("from customers\ntake 10", dialect: :mssql) + {:ok, "SELECT\n TOP (10) *\nFROM\n customers\n\n-- Generated by PRQL compiler version 0.3.1 (https://prql-lang.org)\n"} +``` + +## Development + +We are in the early stages of developing Elixir bindings. + +We're using `Rustler` to provide Rust bindings for `prql-compiler`. + +Currently using the bindings in an Elixir project requires compiling the Rust +crate from this repo: + +- Install dependencies with `mix deps.get` +- Compile project `mix compile` +- Run tests `mix test` + +Future work includes publishing pre-compiled artifacts, so Elixir projects can +run PRQL without needing a Rust toolchain. + +## Mac + +We currently don't enable compilation for Mac. This is possible to enable, but +causes some issues with cargo's compilation cache. Briefly: it requires +`RUST_FLAGS` to be set, and because of + & +, any compilation of a different +target will bust the cache. + +The possible future workarounds include: + +- Passing `--target=aarch64-apple-darwin` to every cargo call, which is + inconvenient and can be difficult in some situations; e.g. Rust Analyzer. This + disables passing `RUST_FLAGS` (I'm actually unclear why `prql-elixir` builds + successfully in that case...) +- Directing other cargo calls to different paths, such as `/target-ra` for Rust + Analyzer and `/target-book` for the book building. But one `cargo build` from + the terminal without either the `target` or `target_dir` specified will bust + the cache! +- Never compiling for other targets. But our standard tests run for + `--target=wasm32-unknown-unknown`, so this requires refraining from using + them. +- Removing `prql-elixir` from our workspace, so that `cargo` commands in the + PRQL workspace don't require rust flags. This would work well, but means we + need separate test coverage for this crate, which adds some weight to the + tests. + +If `prql-elixir` becomes more used (for example, we start publishing to Hex, or +Mac developers want to work on it), then we can re-enable and deal with the +caching issues. We can also re-enable them if the `cargo` issue is resolved. + +To test on Mac temporarily โ€”ย for example if there's an error in GHA and we're on +a Mac locally โ€” apply a diff like this, and then run `cargo build` from the +`prql-elixir` path, which will enable the local +[`.cargo/config.toml`](native/prql/.cargo/config.toml)). (We could also make a +feature like `elixir-mac` which enabled building on Mac). + +```diff +diff --git a/prql-elixir/native/prql/Cargo.toml b/prql-elixir/native/prql/Cargo.toml +index a39a9ee..218abad 100644 +--- a/prql-elixir/native/prql/Cargo.toml ++++ b/prql-elixir/native/prql/Cargo.toml +@@ -17,7 +17,4 @@ path = "src/lib.rs" + + [dependencies] + prql-compiler = {path = "../../../prql-compiler", default-features = false, version = "0.5.2"} +- +-# See Readme for details on Mac +-[target.'cfg(not(any(target_family="wasm", target_os = "macos")))'.dependencies] + rustler = "0.27.0" +diff --git a/prql-elixir/native/prql/src/lib.rs b/prql-elixir/native/prql/src/lib.rs +index 97eaa11..7525479 100644 +--- a/prql-elixir/native/prql/src/lib.rs ++++ b/prql-elixir/native/prql/src/lib.rs +@@ -1,5 +1,3 @@ +-// See Readme for more information on Mac compiling +-#![cfg(not(target_os = "macos"))] + // These bindings aren't relevant on wasm + #![cfg(not(target_family = "wasm"))] + // TODO: unclear why we need this `allow`; it's required in `CompileOptions`, +``` diff --git a/prql-elixir/lib/prql.ex b/bindings/prql-elixir/lib/prql.ex similarity index 100% rename from prql-elixir/lib/prql.ex rename to bindings/prql-elixir/lib/prql.ex diff --git a/prql-elixir/lib/prql/errors.ex b/bindings/prql-elixir/lib/prql/errors.ex similarity index 100% rename from prql-elixir/lib/prql/errors.ex rename to bindings/prql-elixir/lib/prql/errors.ex diff --git a/prql-elixir/lib/prql/native.ex b/bindings/prql-elixir/lib/prql/native.ex similarity index 100% rename from prql-elixir/lib/prql/native.ex rename to bindings/prql-elixir/lib/prql/native.ex diff --git a/prql-elixir/mix.exs b/bindings/prql-elixir/mix.exs similarity index 100% rename from prql-elixir/mix.exs rename to bindings/prql-elixir/mix.exs diff --git a/prql-elixir/mix.lock b/bindings/prql-elixir/mix.lock similarity index 100% rename from prql-elixir/mix.lock rename to bindings/prql-elixir/mix.lock diff --git a/bindings/prql-elixir/native/prql/.cargo/config.toml b/bindings/prql-elixir/native/prql/.cargo/config.toml new file mode 100644 index 000000000000..a0a666544142 --- /dev/null +++ b/bindings/prql-elixir/native/prql/.cargo/config.toml @@ -0,0 +1,8 @@ +# Note that this doesn't apply when compiling from the workspace root. Because +# we're not currently compiling for Mac, it's also impotent for now. + +[target.'cfg(target_os = "macos")'] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/prql-elixir/native/prql/Cargo.toml b/bindings/prql-elixir/native/prql/Cargo.toml similarity index 64% rename from prql-elixir/native/prql/Cargo.toml rename to bindings/prql-elixir/native/prql/Cargo.toml index cffb57f7634f..76e536e96fe0 100644 --- a/prql-elixir/native/prql/Cargo.toml +++ b/bindings/prql-elixir/native/prql/Cargo.toml @@ -16,6 +16,8 @@ name = "prql" path = "src/lib.rs" [dependencies] -prql-compiler = {path = "../../../prql-compiler", default-features = false, version = "0.5.1" } -[target.'cfg(not(any(target_family="wasm")))'.dependencies] +prql-compiler = {path = "../../../../prql-compiler", default-features = false, version = "0.6.1" } + +# See Readme for details on Mac +[target.'cfg(not(any(target_family="wasm", target_os = "macos")))'.dependencies] rustler = "0.27.0" diff --git a/prql-elixir/native/prql/README.md b/bindings/prql-elixir/native/prql/README.md similarity index 100% rename from prql-elixir/native/prql/README.md rename to bindings/prql-elixir/native/prql/README.md diff --git a/prql-elixir/native/prql/src/lib.rs b/bindings/prql-elixir/native/prql/src/lib.rs similarity index 94% rename from prql-elixir/native/prql/src/lib.rs rename to bindings/prql-elixir/native/prql/src/lib.rs index 8ff3aeea7303..97eaa11f1c97 100644 --- a/prql-elixir/native/prql/src/lib.rs +++ b/bindings/prql-elixir/native/prql/src/lib.rs @@ -1,3 +1,5 @@ +// See Readme for more information on Mac compiling +#![cfg(not(target_os = "macos"))] // These bindings aren't relevant on wasm #![cfg(not(target_family = "wasm"))] // TODO: unclear why we need this `allow`; it's required in `CompileOptions`, @@ -118,7 +120,7 @@ pub struct Response { #[rustler::nif] /// compile a prql query into sql pub fn compile(prql_query: &str, options: CompileOptions) -> NifResult { - to_result_tuple(prql_compiler::compile(prql_query, options.into())) + to_result_tuple(prql_compiler::compile(prql_query, &options.into())) } #[rustler::nif] @@ -150,7 +152,7 @@ pub fn rq_to_sql(rq_json: &str) -> NifResult { .and_then(prql_compiler::json::to_rq) // Currently just using default options here; probably should pass // an argument from this func. - .and_then(|x| prql_compiler::rq_to_sql(x, prql_compiler::Options::default())), + .and_then(|x| prql_compiler::rq_to_sql(x, &prql_compiler::Options::default())), ) } diff --git a/prql-elixir/test/prql_test.exs b/bindings/prql-elixir/test/prql_test.exs similarity index 58% rename from prql-elixir/test/prql_test.exs rename to bindings/prql-elixir/test/prql_test.exs index 9053244598ce..bb7648b371a1 100644 --- a/prql-elixir/test/prql_test.exs +++ b/bindings/prql-elixir/test/prql_test.exs @@ -16,7 +16,7 @@ defmodule PRQLTest do test "return errors on invalid query" do excepted_result = - "{\"inner\":[{\"reason\":\"Unknown name invalid\",\"hint\":null,\"span\":{\"start\":0,\"end\":7},\"display\":\"Error: \\n โ•ญโ”€[:1:1]\\n โ”‚\\n 1 โ”‚ invalid\\n ยท โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€ \\n ยท โ•ฐโ”€โ”€โ”€โ”€โ”€ Unknown name invalid\\nโ”€โ”€โ”€โ•ฏ\\n\",\"location\":{\"start\":[0,0],\"end\":[0,7]}}]}" + "{\"inner\":[{\"code\":null,\"reason\":\"Unknown name invalid\",\"hint\":null,\"span\":{\"start\":0,\"end\":7},\"display\":\"Error: \\n โ•ญโ”€[:1:1]\\n โ”‚\\n 1 โ”‚ invalid\\n ยท โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€ \\n ยท โ•ฐโ”€โ”€โ”€โ”€โ”€ Unknown name invalid\\nโ”€โ”€โ”€โ•ฏ\\n\",\"location\":{\"start\":[0,0],\"end\":[0,7]}}]}" assert PRQL.compile("invalid", @compile_opts) == {:error, excepted_result} end diff --git a/prql-elixir/test/test_helper.exs b/bindings/prql-elixir/test/test_helper.exs similarity index 100% rename from prql-elixir/test/test_helper.exs rename to bindings/prql-elixir/test/test_helper.exs diff --git a/prql-java/.gitignore b/bindings/prql-java/.gitignore similarity index 100% rename from prql-java/.gitignore rename to bindings/prql-java/.gitignore diff --git a/prql-java/Cargo.toml b/bindings/prql-java/Cargo.toml similarity index 82% rename from prql-java/Cargo.toml rename to bindings/prql-java/Cargo.toml index 6a225e972546..4253773dbff8 100644 --- a/prql-java/Cargo.toml +++ b/bindings/prql-java/Cargo.toml @@ -15,7 +15,7 @@ test = false [dependencies] jni = "0.20.0" -prql-compiler = {path = "../prql-compiler", default-features = false} +prql-compiler = {path = "../../prql-compiler", default-features = false} [package.metadata.release] tag-name = "{{version}}" diff --git a/prql-java/DEVELOPMENT.md b/bindings/prql-java/DEVELOPMENT.md similarity index 93% rename from prql-java/DEVELOPMENT.md rename to bindings/prql-java/DEVELOPMENT.md index a8dc766aa1eb..5d2933a86a32 100644 --- a/prql-java/DEVELOPMENT.md +++ b/bindings/prql-java/DEVELOPMENT.md @@ -8,8 +8,8 @@ We implement Rust bindings to Java with [JNI](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/). First, define a native method -- -`public static native String toSql(String query)` for PrqlCompiler, `toJson` is -same. +`public static native String toSql(String query, String target, boolean format, boolean signature)` +for PrqlCompiler, `toJson` is same. And then implement it in Rust with this [crate](https://docs.rs/jni/latest/jni/). diff --git a/prql-java/README.md b/bindings/prql-java/README.md similarity index 100% rename from prql-java/README.md rename to bindings/prql-java/README.md diff --git a/prql-java/cross.sh b/bindings/prql-java/cross.sh similarity index 100% rename from prql-java/cross.sh rename to bindings/prql-java/cross.sh diff --git a/prql-java/java/.mvn/wrapper/maven-wrapper.jar b/bindings/prql-java/java/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from prql-java/java/.mvn/wrapper/maven-wrapper.jar rename to bindings/prql-java/java/.mvn/wrapper/maven-wrapper.jar diff --git a/prql-java/java/.mvn/wrapper/maven-wrapper.properties b/bindings/prql-java/java/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from prql-java/java/.mvn/wrapper/maven-wrapper.properties rename to bindings/prql-java/java/.mvn/wrapper/maven-wrapper.properties diff --git a/prql-java/java/build.sh b/bindings/prql-java/java/build.sh similarity index 80% rename from prql-java/java/build.sh rename to bindings/prql-java/java/build.sh index 97ade6799fd5..faa4a5ba092a 100755 --- a/prql-java/java/build.sh +++ b/bindings/prql-java/java/build.sh @@ -1,6 +1,8 @@ #!/bin/sh set -e +# TODO: use a task file for these build scripts + JAVA_SRC_HOME=$1 ARCH="$(uname -m)" KERNEL_NAME="$(uname -s)" @@ -9,7 +11,6 @@ KERNEL_VERSION="$(uname -r)" echo JAVA_SRC_HOME="$JAVA_SRC_HOME" cd "$JAVA_SRC_HOME" -# cd prql-java/ cd ../ echo Platform Info: "$ARCH" "$KERNEL_NAME" "$KERNEL_VERSION" @@ -17,7 +18,7 @@ echo Platform Info: "$ARCH" "$KERNEL_NAME" "$KERNEL_VERSION" echo building... cargo build --release echo building successfully -ls -la ../target/release +ls -la ../../target/release if [ "$KERNEL_NAME" = 'Linux' ]; then if [ "$ARCH" = 'arm64' ] || [ "$ARCH" = 'aarch64' ]; then @@ -27,7 +28,7 @@ if [ "$KERNEL_NAME" = 'Linux' ]; then else target='libprql_java-linux32.so' fi - cp -f ../target/release/libprql_java.so java/src/test/resources/${target} + cp -f ../../target/release/libprql_java.so java/src/test/resources/${target} elif [ "$KERNEL_NAME" = 'Darwin' ]; then if [ "$ARCH" = 'arm64' ] || [ "$ARCH" = 'aarch64' ]; then target='libprql_java-osx-arm64.dylib' @@ -37,7 +38,7 @@ elif [ "$KERNEL_NAME" = 'Darwin' ]; then echo [ERROR] have not support $ARCH:$$KERNEL_NAME yet exit 1 fi - cp -f ../target/release/libprql_java.dylib java/src/test/resources/${target} + cp -f ../../target/release/libprql_java.dylib java/src/test/resources/${target} fi ls -la ./java/src/main/resources diff --git a/prql-java/java/mvnw b/bindings/prql-java/java/mvnw similarity index 100% rename from prql-java/java/mvnw rename to bindings/prql-java/java/mvnw diff --git a/prql-java/java/mvnw.cmd b/bindings/prql-java/java/mvnw.cmd similarity index 100% rename from prql-java/java/mvnw.cmd rename to bindings/prql-java/java/mvnw.cmd diff --git a/prql-java/java/pom.xml b/bindings/prql-java/java/pom.xml similarity index 99% rename from prql-java/java/pom.xml rename to bindings/prql-java/java/pom.xml index 84d8bddd2339..d7276e6edff1 100644 --- a/prql-java/java/pom.xml +++ b/bindings/prql-java/java/pom.xml @@ -6,7 +6,7 @@ org.prqllang prql-java - 0.2.1 + 0.5.2 ${project.groupId}:${project.artifactId} prql-compiler api for java diff --git a/prql-java/java/src/main/java/org/prql/prql4j/Environment.java b/bindings/prql-java/java/src/main/java/org/prql/prql4j/Environment.java similarity index 100% rename from prql-java/java/src/main/java/org/prql/prql4j/Environment.java rename to bindings/prql-java/java/src/main/java/org/prql/prql4j/Environment.java diff --git a/prql-java/java/src/main/java/org/prql/prql4j/NativeLibraryLoader.java b/bindings/prql-java/java/src/main/java/org/prql/prql4j/NativeLibraryLoader.java similarity index 100% rename from prql-java/java/src/main/java/org/prql/prql4j/NativeLibraryLoader.java rename to bindings/prql-java/java/src/main/java/org/prql/prql4j/NativeLibraryLoader.java diff --git a/bindings/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java b/bindings/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java new file mode 100644 index 000000000000..3ced22ce59ad --- /dev/null +++ b/bindings/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java @@ -0,0 +1,27 @@ +package org.prql.prql4j; + +import java.io.IOException; + +public class PrqlCompiler { + + /** + * compile PRQL to SQL + * @param query PRQL query + * @param target target dialect, such as sql.mysql etc. Please refer PRQL Target and Version + * @param format format SQL or not + * @param signature comment signature or not + * @return SQL + * @throws Exception PRQL compile exception + */ + public static native String toSql(String query, String target, boolean format, boolean signature) throws Exception; + public static native String toJson(String query) throws Exception; + public static native String format(String query) throws Exception; + + static { + try { + NativeLibraryLoader.getInstance().loadLibrary(null); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/prql-java/java/src/main/resources/.gitkeep b/bindings/prql-java/java/src/main/resources/.gitkeep similarity index 100% rename from prql-java/java/src/main/resources/.gitkeep rename to bindings/prql-java/java/src/main/resources/.gitkeep diff --git a/prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java b/bindings/prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java similarity index 53% rename from prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java rename to bindings/prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java index 4ca948b87959..5a8627d62c57 100644 --- a/prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java +++ b/bindings/prql-java/java/src/test/java/org/prql/prql4j/PrqlCompilerTest.java @@ -4,8 +4,8 @@ public class PrqlCompilerTest { @Test - public void compile() { - String found = PrqlCompiler.toSql("from table"); + public void compile() throws Exception { + String found = PrqlCompiler.toSql("from table", "sql.mysql", true, true); // remove signature found = found.substring(0, found.indexOf("\n\n--")); @@ -16,4 +16,9 @@ public void compile() { " table"; assert expected.equalsIgnoreCase(found); } + + @Test(expected = Exception.class) + public void compileWithError() throws Exception { + PrqlCompiler.toSql("from table | filter id >> 1", "sql.mysql", true, true); + } } diff --git a/prql-java/java/src/test/resources/.gitkeep b/bindings/prql-java/java/src/test/resources/.gitkeep similarity index 100% rename from prql-java/java/src/test/resources/.gitkeep rename to bindings/prql-java/java/src/test/resources/.gitkeep diff --git a/bindings/prql-java/src/lib.rs b/bindings/prql-java/src/lib.rs new file mode 100644 index 000000000000..93e7052dc160 --- /dev/null +++ b/bindings/prql-java/src/lib.rs @@ -0,0 +1,77 @@ +use jni::objects::{JClass, JString}; +use jni::sys::{jboolean, jstring}; +use jni::JNIEnv; +use prql_compiler::{json, pl_to_prql, prql_to_pl, ErrorMessages, Options, Target}; +use std::str::FromStr; + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_org_prql_prql4j_PrqlCompiler_toSql( + env: JNIEnv, + _class: JClass, + query: JString, + target: JString, + format: jboolean, + signature: jboolean, +) -> jstring { + let prql_query: String = env + .get_string(query) + .expect("Couldn't get java string!") + .into(); + let target_str: String = env + .get_string(target) + .expect("Couldn't get java string") + .into(); + let prql_dialect: Target = Target::from_str(&target_str).unwrap_or(Target::Sql(None)); + let opt = Options { + format: format != 0, + target: prql_dialect, + signature_comment: signature != 0, + }; + let result = prql_compiler::compile(&prql_query, &opt); + java_string_with_exception(result, &env) +} + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_org_prql_prql4j_PrqlCompiler_format( + env: JNIEnv, + _class: JClass, + query: JString, +) -> jstring { + let prql_query: String = env + .get_string(query) + .expect("Couldn't get java string!") + .into(); + let result = prql_to_pl(&prql_query).and_then(pl_to_prql); + java_string_with_exception(result, &env) +} + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_org_prql_prql4j_PrqlCompiler_toJson( + env: JNIEnv, + _class: JClass, + query: JString, +) -> jstring { + let prql_query: String = env + .get_string(query) + .expect("Couldn't get java string!") + .into(); + let result = prql_to_pl(&prql_query).and_then(json::from_pl); + java_string_with_exception(result, &env) +} + +fn java_string_with_exception(result: Result, env: &JNIEnv) -> jstring { + if let Ok(text) = result { + env.new_string(text) + .expect("Couldn't create java string!") + .into_raw() + } else { + let exception = env.find_class("java/lang/Exception").unwrap(); + if let Err(e) = env.throw_new(exception, result.err().unwrap().to_string()) { + println!("Error throwing exception: {:?}", e); + } + std::ptr::null_mut() as jstring + } +} diff --git a/prql-js/Cargo.toml b/bindings/prql-js/Cargo.toml similarity index 58% rename from prql-js/Cargo.toml rename to bindings/prql-js/Cargo.toml index 2a3755f5fa25..b0c8cf259dbe 100644 --- a/prql-js/Cargo.toml +++ b/bindings/prql-js/Cargo.toml @@ -22,8 +22,13 @@ test = false default = ["console_error_panic_hook"] [dependencies] -prql-compiler = {path = "../prql-compiler", default-features = false} -wasm-bindgen = "0.2.80" +prql-compiler = {path = "../../prql-compiler", default-features = false} +# This was preventing the playground from working. It's possibly +# https://github.com/rustwasm/wasm-bindgen/issues/3276 +# If the playground works with a later version, we can unpin this. It's likely +# related to wasm-pack. So https://github.com/PRQL/prql/issues/1836 would likely +# solve it, along with simplifying the build process. +wasm-bindgen = "=0.2.83" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -44,12 +49,19 @@ tag-prefix = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "package.json" -replace = ' "version": "{{version}}"' -search = ' "version": "(\d+\.\d+\.\d+)"' +replace = '$1"{{version}}"' +search = '( "version": )"(\d+\.\d+\.\d+)"' + +[[package.metadata.release.pre-release-replacements]] +exactly = 2 +file = "package-lock.json" +replace = ''' +$1 +$2"{{version}}"''' +search = '(\s+"prql-js",)\n(\s+"version": )"[\d\.]+"' [[package.metadata.release.pre-release-replacements]] exactly = 1 -file = "../playground/package-lock.json" -replace = ''' "../prql-js": { - "version": "{{version}}"''' -search = ' "../prql-js": \{\n "version": "[\d\.]+"' +file = "../../web/playground/package-lock.json" +replace = '''$1"{{version}}"''' +search = '( "../prql-js": \{\n "version": )"[\d\.]+"' diff --git a/prql-js/README.md b/bindings/prql-js/README.md similarity index 97% rename from prql-js/README.md rename to bindings/prql-js/README.md index ba0606c2664a..500e55ee8d90 100644 --- a/prql-js/README.md +++ b/bindings/prql-js/README.md @@ -173,4 +173,4 @@ npm test [^1]: Though we would be very open to other approaches, given wasm-pack does not seem maintained, and we're eliding many of its features to build for three - targets. + targets. See for more details. diff --git a/prql-js/package-lock.json b/bindings/prql-js/package-lock.json similarity index 55% rename from prql-js/package-lock.json rename to bindings/prql-js/package-lock.json index db2e089642e7..025d9190129d 100644 --- a/prql-js/package-lock.json +++ b/bindings/prql-js/package-lock.json @@ -1,12 +1,12 @@ { "name": "prql-js", - "version": "0.4.2", - "lockfileVersion": 2, + "version": "0.6.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prql-js", - "version": "0.4.2", + "version": "0.6.1", "license": "Apache-2.0", "devDependencies": { "chai": "^4.3.6", @@ -997,709 +997,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/prql-js/package.json b/bindings/prql-js/package.json similarity index 78% rename from prql-js/package.json rename to bindings/prql-js/package.json index e3f7b8c0fc09..b20e185bd64d 100644 --- a/prql-js/package.json +++ b/bindings/prql-js/package.json @@ -18,11 +18,11 @@ }, "scripts": { "build": "npm run build:node && npm run build:bundler && npm run build:web", - "build:bundler": "wasm-pack build --target bundler --release --out-dir dist/bundler", - "build:node": "wasm-pack build --target nodejs --release --out-dir dist/node", - "build:web": "wasm-pack build --target no-modules --release --out-dir dist/web", - "test": "wasm-pack test --firefox && mocha tests" + "build:bundler": "wasm-pack build --target bundler --release --out-dir dist/bundler && rm dist/bundler/.gitignore", + "build:node": "wasm-pack build --target nodejs --release --out-dir dist/node && rm dist/node/.gitignore", + "build:web": "wasm-pack build --target no-modules --release --out-dir dist/web && rm dist/web/.gitignore", + "test": "mocha tests" }, "types": "dist/node/prql_js.d.ts", - "version": "0.5.1" + "version": "0.6.1" } diff --git a/prql-js/src/lib.rs b/bindings/prql-js/src/lib.rs similarity index 90% rename from prql-js/src/lib.rs rename to bindings/prql-js/src/lib.rs index aa4caca95c46..6a7a7add9571 100644 --- a/prql-js/src/lib.rs +++ b/bindings/prql-js/src/lib.rs @@ -10,7 +10,7 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn compile(prql_query: &str, options: Option) -> Option { return_or_throw( - prql_compiler::compile(prql_query, options.map(|x| x.into()).unwrap_or_default()) + prql_compiler::compile(prql_query, &options.map(|x| x.into()).unwrap_or_default()) .map_err(|e| e.composed("", prql_query, false)), ) } @@ -39,7 +39,7 @@ pub fn rq_to_sql(rq_json: &str) -> Option { return_or_throw( Ok(rq_json) .and_then(prql_compiler::json::to_rq) - .and_then(|x| prql_compiler::rq_to_sql(x, prql_compiler::Options::default())), + .and_then(|x| prql_compiler::rq_to_sql(x, &prql_compiler::Options::default())), ) } @@ -53,9 +53,6 @@ pub struct CompileOptions { /// Defaults to true. pub format: bool, - /// Target to compile to (e.g. sql.postgres) - /// - /// If `None` is used, the `target` argument from the query header is used. #[wasm_bindgen(skip)] pub target: String, @@ -90,6 +87,10 @@ impl CompileOptions { Self::default() } + /// Target to compile to (e.g. sql.postgres) + /// + /// Defaults to `sql.any`, which uses `target` argument from the query header to determine + /// the SQL dialect. #[wasm_bindgen(getter)] pub fn target(&self) -> String { self.target.clone() diff --git a/prql-js/src/utils.rs b/bindings/prql-js/src/utils.rs similarity index 100% rename from prql-js/src/utils.rs rename to bindings/prql-js/src/utils.rs diff --git a/prql-js/tests/test_all.js b/bindings/prql-js/tests/test_all.js similarity index 79% rename from prql-js/tests/test_all.js rename to bindings/prql-js/tests/test_all.js index 624ab4040efb..5fb0b8a5256f 100644 --- a/prql-js/tests/test_all.js +++ b/bindings/prql-js/tests/test_all.js @@ -35,7 +35,7 @@ describe("prql-js", () => { it("should throw an error on invalid prql", () => { expect(() => - prql.compile("Mississippi has four Sโ€™s and four Iโ€™s.") + prql.compile("Mississippi has four Ss and four Is.") ).to.throw("Error"); }); @@ -86,7 +86,7 @@ describe("prql-js", () => { it("should fallback to the target in header", () => { const opts = new prql.CompileOptions(); - opts.target = "sql.not_existing"; + opts.target = "sql.any"; const res = prql.compile("prql target:sql.mssql\nfrom a | take 1", opts); assert(res.includes("TOP (1)")); @@ -100,4 +100,28 @@ describe("prql-js", () => { assert(targets.includes("sql.sqlite")); }); }); + + describe("compile error", () => { + it("should contain json", () => { + try { + prql.compile("from x | select a | select b"); + } catch (error) { + const errorMessages = JSON.parse(error.message).inner; + + assert(errorMessages.length > 0); + assert(errorMessages[0].display.includes("\n")); + assert(!errorMessages[0].reason.includes("\n")); + } + }); + + it("should contain error code", () => { + try { + prql.compile("let a = (from x)"); + } catch (error) { + const errorMessages = JSON.parse(error.message).inner; + + assert(errorMessages[0].code == "E0001"); + } + }); + }); }); diff --git a/prql-lib/Cargo.toml b/bindings/prql-lib/Cargo.toml similarity index 83% rename from prql-lib/Cargo.toml rename to bindings/prql-lib/Cargo.toml index b1ea6bc72b77..0057fd96f4eb 100644 --- a/prql-lib/Cargo.toml +++ b/bindings/prql-lib/Cargo.toml @@ -15,7 +15,7 @@ test = false [dependencies] libc = "0.2" -prql-compiler = {path = "../prql-compiler", default-features = false} +prql-compiler = {path = "../../prql-compiler", default-features = false} serde_json = "1.0" [package.metadata.release] diff --git a/prql-lib/README.md b/bindings/prql-lib/README.md similarity index 76% rename from prql-lib/README.md rename to bindings/prql-lib/README.md index 9c17b3da2b21..eaea13af9701 100644 --- a/prql-lib/README.md +++ b/bindings/prql-lib/README.md @@ -1,18 +1,23 @@ -# PRQL library target +# PRQL C library ## Description This module compiles PRQL as a library (both `.a` and `.so` are generated). This allows embedding in languages that support FFI - looking at Golang. -## Usage +## Linking + +See [examples/minimal-c/Makefile](examples/minimal-c/Makefile). Copy the `.a` and `.so` files in a convenient place and add the following compile flags to Go (cgo): `CGO_LDFLAGS="-L/path/to/libprql_lib.a -lprql_lib -pthread -ldl" go build` -## Code +## Examples + +For a minimal example, see +[examples/minimal-c/main.c](examples/minimal-c/main.c). Below is an example from an actual application that is using PRQL in Go. @@ -82,3 +87,20 @@ func ToJSON(prql string) (string, error) { return "", errors.New(C.GoString(cstr)) } ``` + +## Development + +### C header file + +The C header file `libprql_lib.h` was generated using +[cbindgen](https://github.com/eqrion/cbindgen). To generate a new one run: + + cargo install --force cbindgen + cbindgen --crate prql-lib --output libprql_lib.h + +### C++ header file + +The C++ header file `libprql_lib.hpp` was generated using cbindgen. To generate +a new one run: + + cbindgen --crate prql-lib --lang C++ --output libprql_lib.hpp diff --git a/bindings/prql-lib/cbindgen.toml b/bindings/prql-lib/cbindgen.toml new file mode 100644 index 000000000000..73fbadea44a7 --- /dev/null +++ b/bindings/prql-lib/cbindgen.toml @@ -0,0 +1,12 @@ +language = "C" + +header = '''/* + * PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */''' + +autogen_warning = "/* This file is autogenerated. Do not modify this file manually. */" + +after_includes = '#define FFI_SCOPE "PRQL"' diff --git a/bindings/prql-lib/examples/minimal-c/Makefile b/bindings/prql-lib/examples/minimal-c/Makefile new file mode 100644 index 000000000000..dfd78af60a5f --- /dev/null +++ b/bindings/prql-lib/examples/minimal-c/Makefile @@ -0,0 +1,18 @@ +PRQL_PROJECT=../../../.. + +build-prql: + cargo build -p prql-lib --release + +# TODO: would be helpful to allow running with a debug build too. +build: main.c build-prql + gcc main.c -o main.out \ + -I${PRQL_PROJECT}/bindings/prql-lib \ + -L${PRQL_PROJECT}/target/release \ + -l:libprql_lib.a \ + -pthread -ldl -lm + +run: build + ./main.out + +valgrind: build + valgrind ./main.out diff --git a/bindings/prql-lib/examples/minimal-c/README.md b/bindings/prql-lib/examples/minimal-c/README.md new file mode 100644 index 000000000000..b1762921ca3f --- /dev/null +++ b/bindings/prql-lib/examples/minimal-c/README.md @@ -0,0 +1,7 @@ +# Basic C example + +A minimal example for using prql-lib with `gcc` and `make`. + +## How to run + + make run diff --git a/bindings/prql-lib/examples/minimal-c/main.c b/bindings/prql-lib/examples/minimal-c/main.c new file mode 100644 index 000000000000..e61638c67842 --- /dev/null +++ b/bindings/prql-lib/examples/minimal-c/main.c @@ -0,0 +1,65 @@ +#include + +#include + +void print_result(CompileResult res) { + printf("---- [ Compiled with %ld errors ]----\n", res.messages_len); + for (int i = 0; i < res.messages_len; i++) { + Message const* e = &res.messages[i]; + if (e->display != NULL) { + printf("%s", *e->display); + } else if (e->code != NULL) { + printf("[%s] Error: %s\n", *e->code, e->reason); + } else { + printf("Error: %s", e->reason); + } + } + if (*res.output == '\0') { + printf("Output: \n\n"); + } else { + printf("Output:\n%s\n\n", res.output); + } +} + +int main() { + char *prql_query; + prql_query = "from albums | select [album_id, title] | take 3"; + CompileResult res; + CompileResult res2; + + // default compile option + res = compile(prql_query, NULL); + print_result(res); + result_destroy(res); + + // custom compile options + Options opts; + opts.format = false; + opts.signature_comment = false; + opts.target = "sql.mssql"; + res = compile(prql_query, &opts); + print_result(res); + result_destroy(res); + + // error handling + res = compile("from album | select [album_id] | select [title]", NULL); + print_result(res); + result_destroy(res); + + // error handling + res = compile("let a = (from album)", NULL); + print_result(res); + result_destroy(res); + + // intermediate results + res = prql_to_pl(prql_query); + print_result(res); + + res2 = pl_to_rq(res.output); + result_destroy(res); + + print_result(res2); + result_destroy(res2); + + return 0; +} diff --git a/bindings/prql-lib/libprql_lib.h b/bindings/prql-lib/libprql_lib.h new file mode 100644 index 000000000000..e8c5f7f38993 --- /dev/null +++ b/bindings/prql-lib/libprql_lib.h @@ -0,0 +1,189 @@ +/* + * PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */ + +/* This file is autogenerated. Do not modify this file manually. */ + +#include +#include +#include +#include +#define FFI_SCOPE "PRQL" + +/** + * Compile message kind. Currently only Error is implemented. + */ +typedef enum MessageKind { + Error, + Warning, + Lint, +} MessageKind; + +/** + * Identifier of a location in source. + * Contains offsets in terms of chars. + */ +typedef struct Span { + size_t start; + size_t end; +} Span; + +/** + * Location within a source file. + */ +typedef struct SourceLocation { + size_t start_line; + size_t start_col; + size_t end_line; + size_t end_col; +} SourceLocation; + +/** + * Compile result message. + * + * Calling code is responsible for freeing all memory allocated + * for fields as well as strings. + */ +typedef struct Message { + /** + * Message kind. Currently only Error is implemented. + */ + enum MessageKind kind; + /** + * Machine-readable identifier of the error + */ + const int8_t *const *code; + /** + * Plain text of the error + */ + const int8_t *reason; + /** + * A list of suggestions of how to fix the error + */ + const int8_t *const *hint; + /** + * Character offset of error origin within a source file + */ + const struct Span *span; + /** + * Annotated code, containing cause and hints. + */ + const int8_t *const *display; + /** + * Line and column number of error origin within a source file + */ + const struct SourceLocation *location; +} Message; + +/** + * Result of compilation. + */ +typedef struct CompileResult { + const int8_t *output; + const struct Message *messages; + size_t messages_len; +} CompileResult; + +/** + * Compilation options + */ +typedef struct Options { + /** + * Pass generated SQL string trough a formatter that splits it + * into multiple lines and prettifies indentation and spacing. + * + * Defaults to true. + */ + bool format; + /** + * Target and dialect to compile to. + * + * Defaults to `sql.any`, which uses `target` argument from the query header to determine + * the SQL dialect. + */ + char *target; + /** + * Emits the compiler signature as a comment after generated SQL + * + * Defaults to true. + */ + bool signature_comment; +} Options; + +/** + * Compile a PRQL string into a SQL string. + * + * This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without converting to JSON + * between each of the functions. + * + * See `Options` struct for available compilation options. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult compile(const char *prql_query, const struct Options *options); + +/** + * Build PL AST from a PRQL string. PL in documented in the + * [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/pl). + * + * Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult prql_to_pl(const char *prql_query); + +/** + * Finds variable references, validates functions calls, determines frames and converts PL to RQ. + * PL and RQ are documented in the + * [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast). + * + * Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult pl_to_rq(const char *pl_json); + +/** + * Convert RQ AST into an SQL string. RQ is documented in the + * [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/rq). + * + * Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult rq_to_sql(const char *rq_json, const struct Options *options); + +/** + * Destroy a `CompileResult` once you are done with it. + * + * # Safety + * + * This function expects to be called exactly once after the call of any the functions + * that return CompileResult. No fields should be freed manually. + */ +void result_destroy(struct CompileResult res); diff --git a/bindings/prql-lib/libprql_lib.hpp b/bindings/prql-lib/libprql_lib.hpp new file mode 100644 index 000000000000..01730ea43e56 --- /dev/null +++ b/bindings/prql-lib/libprql_lib.hpp @@ -0,0 +1,152 @@ +/* + * PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */ + +/* This file is autogenerated. Do not modify this file manually. */ + +#include +#include +#include +#include +#include +#define FFI_SCOPE "PRQL" + +/// Compile message kind. Currently only Error is implemented. +enum class MessageKind { + Error, + Warning, + Lint, +}; + +/// Identifier of a location in source. +/// Contains offsets in terms of chars. +struct Span { + size_t start; + size_t end; +}; + +/// Location within a source file. +struct SourceLocation { + size_t start_line; + size_t start_col; + size_t end_line; + size_t end_col; +}; + +/// Compile result message. +/// +/// Calling code is responsible for freeing all memory allocated +/// for fields as well as strings. +struct Message { + /// Message kind. Currently only Error is implemented. + MessageKind kind; + /// Machine-readable identifier of the error + const int8_t *const *code; + /// Plain text of the error + const int8_t *reason; + /// A list of suggestions of how to fix the error + const int8_t *const *hint; + /// Character offset of error origin within a source file + const Span *span; + /// Annotated code, containing cause and hints. + const int8_t *const *display; + /// Line and column number of error origin within a source file + const SourceLocation *location; +}; + +/// Result of compilation. +struct CompileResult { + const int8_t *output; + const Message *messages; + size_t messages_len; +}; + +/// Compilation options +struct Options { + /// Pass generated SQL string trough a formatter that splits it + /// into multiple lines and prettifies indentation and spacing. + /// + /// Defaults to true. + bool format; + /// Target and dialect to compile to. + /// + /// Defaults to `sql.any`, which uses `target` argument from the query header to determine + /// the SQL dialect. + char *target; + /// Emits the compiler signature as a comment after generated SQL + /// + /// Defaults to true. + bool signature_comment; +}; + +extern "C" { + +/// Compile a PRQL string into a SQL string. +/// +/// This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without converting to JSON +/// between each of the functions. +/// +/// See `Options` struct for available compilation options. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult compile(const char *prql_query, const Options *options); + +/// Build PL AST from a PRQL string. PL in documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/pl). +/// +/// Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult prql_to_pl(const char *prql_query); + +/// Finds variable references, validates functions calls, determines frames and converts PL to RQ. +/// PL and RQ are documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast). +/// +/// Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult pl_to_rq(const char *pl_json); + +/// Convert RQ AST into an SQL string. RQ is documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/rq). +/// +/// Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult rq_to_sql(const char *rq_json, const Options *options); + +/// Destroy a `CompileResult` once you are done with it. +/// +/// # Safety +/// +/// This function expects to be called exactly once after the call of any the functions +/// that return CompileResult. No fields should be freed manually. +void result_destroy(CompileResult res); + +} // extern "C" diff --git a/bindings/prql-lib/src/lib.rs b/bindings/prql-lib/src/lib.rs new file mode 100644 index 000000000000..6ab3f3f01fed --- /dev/null +++ b/bindings/prql-lib/src/lib.rs @@ -0,0 +1,327 @@ +#![cfg(not(target_family = "wasm"))] + +extern crate libc; + +use libc::{c_char, size_t}; +use prql_compiler::ErrorMessages; +use prql_compiler::Target; +use std::ffi::CStr; +use std::ffi::CString; +use std::str::FromStr; + +/// Compile a PRQL string into a SQL string. +/// +/// This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without converting to JSON +/// between each of the functions. +/// +/// See `Options` struct for available compilation options. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +#[no_mangle] +pub unsafe extern "C" fn compile( + prql_query: *const c_char, + options: *const Options, +) -> CompileResult { + let prql_query: String = c_str_to_string(prql_query); + + let options = options.as_ref().map(convert_options).transpose(); + + let result = options + .and_then(|opts| { + Ok(prql_query.as_str()) + .and_then(prql_compiler::prql_to_pl) + .and_then(prql_compiler::pl_to_rq) + .and_then(|rq| prql_compiler::rq_to_sql(rq, &opts.unwrap_or_default())) + }) + .map_err(|e| e.composed("", &prql_query, false)); + + result_into_c_str(result) +} + +/// Build PL AST from a PRQL string. PL in documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/pl). +/// +/// Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +#[no_mangle] +pub unsafe extern "C" fn prql_to_pl(prql_query: *const c_char) -> CompileResult { + let prql_query: String = c_str_to_string(prql_query); + + let result = Ok(prql_query.as_str()) + .and_then(prql_compiler::prql_to_pl) + .and_then(prql_compiler::json::from_pl); + result_into_c_str(result) +} + +/// Finds variable references, validates functions calls, determines frames and converts PL to RQ. +/// PL and RQ are documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast). +/// +/// Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +#[no_mangle] +pub unsafe extern "C" fn pl_to_rq(pl_json: *const c_char) -> CompileResult { + let pl_json: String = c_str_to_string(pl_json); + + let result = Ok(pl_json.as_str()) + .and_then(prql_compiler::json::to_pl) + .and_then(prql_compiler::pl_to_rq) + .and_then(prql_compiler::json::from_rq); + result_into_c_str(result) +} + +/// Convert RQ AST into an SQL string. RQ is documented in the +/// [prql-compiler Rust crate](https://docs.rs/prql-compiler/latest/prql_compiler/ast/rq). +/// +/// Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +#[no_mangle] +pub unsafe extern "C" fn rq_to_sql( + rq_json: *const c_char, + options: *const Options, +) -> CompileResult { + let rq_json: String = c_str_to_string(rq_json); + + let options = options.as_ref().map(convert_options).transpose(); + + let result = options.and_then(|options| { + Ok(rq_json.as_str()) + .and_then(prql_compiler::json::to_rq) + .and_then(|x| prql_compiler::rq_to_sql(x, &options.unwrap_or_default())) + }); + result_into_c_str(result) +} + +/// Compilation options +#[repr(C)] +pub struct Options { + /// Pass generated SQL string trough a formatter that splits it + /// into multiple lines and prettifies indentation and spacing. + /// + /// Defaults to true. + pub format: bool, + + /// Target and dialect to compile to. + /// + /// Defaults to `sql.any`, which uses `target` argument from the query header to determine + /// the SQL dialect. + pub target: *mut c_char, + + /// Emits the compiler signature as a comment after generated SQL + /// + /// Defaults to true. + pub signature_comment: bool, +} + +/// Result of compilation. +#[repr(C)] +pub struct CompileResult { + pub output: *const i8, + pub messages: *const Message, + pub messages_len: size_t, +} + +/// Compile message kind. Currently only Error is implemented. +#[repr(C)] +pub enum MessageKind { + Error, + Warning, + Lint, +} + +/// Compile result message. +/// +/// Calling code is responsible for freeing all memory allocated +/// for fields as well as strings. +// Make sure to keep in sync with prql_compiler::ErrorMessage +#[repr(C)] +pub struct Message { + /// Message kind. Currently only Error is implemented. + pub kind: MessageKind, + /// Machine-readable identifier of the error + pub code: *const *const i8, + /// Plain text of the error + pub reason: *const i8, + /// A list of suggestions of how to fix the error + pub hint: *const *const i8, + /// Character offset of error origin within a source file + pub span: *const Span, + + /// Annotated code, containing cause and hints. + pub display: *const *const i8, + /// Line and column number of error origin within a source file + pub location: *const SourceLocation, +} + +/// Identifier of a location in source. +/// Contains offsets in terms of chars. +// Make sure to keep in sync with prql_compiler::Span +#[repr(C)] +pub struct Span { + pub start: size_t, + pub end: size_t, +} + +/// Location within a source file. +// Make sure to keep in sync with prql_compiler::SourceLocation +#[repr(C)] +pub struct SourceLocation { + pub start_line: size_t, + pub start_col: size_t, + + pub end_line: size_t, + pub end_col: size_t, +} + +/// Destroy a `CompileResult` once you are done with it. +/// +/// # Safety +/// +/// This function expects to be called exactly once after the call of any the functions +/// that return CompileResult. No fields should be freed manually. +#[no_mangle] +pub unsafe extern "C" fn result_destroy(res: CompileResult) { + // This is required because we are allocating memory for + // strings, vectors and options. + // For strings and vectors this is required, but options may be + // able to live entirely within the struct, instead of the heap. + + for i in 0..res.messages_len { + let e = &*res.messages.add(i); + + if !e.code.is_null() { + drop(CString::from_raw(*e.code as *mut i8)); + drop(Box::from_raw(e.code as *mut *const i8)); + } + drop(CString::from_raw(e.reason as *mut i8)); + if !e.hint.is_null() { + drop(CString::from_raw(*e.hint as *mut i8)); + drop(Box::from_raw(e.hint as *mut *const i8)); + } + if !e.span.is_null() { + drop(Box::from_raw(e.span as *mut Span)); + } + if !e.display.is_null() { + drop(CString::from_raw(*e.display as *mut i8)); + drop(Box::from_raw(e.display as *mut *const i8)); + } + if !e.location.is_null() { + drop(Box::from_raw(e.location as *mut SourceLocation)); + } + } + drop(Vec::from_raw_parts( + res.messages as *mut i8, + res.messages_len, + res.messages_len, + )); + drop(CString::from_raw(res.output as *mut i8)); +} + +unsafe fn result_into_c_str(result: Result) -> CompileResult { + match result { + Ok(output) => CompileResult { + output: convert_string(output), + messages: ::std::ptr::null_mut(), + messages_len: 0, + }, + Err(err) => { + let mut errors = Vec::with_capacity(err.inner.len()); + errors.extend(err.inner.into_iter().map(|e| Message { + kind: MessageKind::Error, + code: option_to_ptr(e.code.map(convert_string)), + reason: convert_string(e.reason), + hint: option_to_ptr(e.hint.map(convert_string)), + span: option_to_ptr(e.span.map(convert_span)), + display: option_to_ptr(e.display.map(convert_string)), + location: option_to_ptr(e.location.map(convert_source_location)), + })); + CompileResult { + output: CString::default().into_raw(), + messages_len: errors.len(), + messages: errors.leak().as_ptr(), + } + } + } +} + +/// Allocates the value on the heap and returns a pointer to it. +/// If the input is None, it returns null pointer. +fn option_to_ptr(o: Option) -> *const T { + match o { + Some(x) => { + let b = Box::new(x); + Box::into_raw(b) + } + None => ::std::ptr::null(), + } +} + +fn convert_string(x: String) -> *const i8 { + CString::new(x).unwrap_or_default().into_raw() +} + +fn convert_span(x: prql_compiler::Span) -> Span { + Span { + start: x.start, + end: x.end, + } +} + +fn convert_source_location(x: prql_compiler::SourceLocation) -> SourceLocation { + SourceLocation { + start_line: x.start.0, + start_col: x.start.1, + end_line: x.end.0, + end_col: x.end.1, + } +} + +unsafe fn c_str_to_string(c_str: *const c_char) -> String { + // inefficient, but simple + CStr::from_ptr(c_str).to_string_lossy().into_owned() +} + +fn convert_options(o: &Options) -> Result { + let target = if !o.target.is_null() { + Some(unsafe { c_str_to_string(o.target) }) + } else { + None + }; + let target = target + .as_deref() + .filter(|x| !x.is_empty()) + .unwrap_or("sql.any"); + + let target = Target::from_str(target).map_err(|e| prql_compiler::downcast(e.into()))?; + + Ok(prql_compiler::Options { + format: o.format, + target, + signature_comment: o.signature_comment, + }) +} diff --git a/bindings/prql-php/.gitignore b/bindings/prql-php/.gitignore new file mode 100644 index 000000000000..4c802cd562b2 --- /dev/null +++ b/bindings/prql-php/.gitignore @@ -0,0 +1,2 @@ +vendor/ +lib/ diff --git a/bindings/prql-php/README.md b/bindings/prql-php/README.md new file mode 100644 index 000000000000..73ae351e1958 --- /dev/null +++ b/bindings/prql-php/README.md @@ -0,0 +1,69 @@ +# prql-php + +`prql-php` offers PHP bindings to `prql-compiler` crate through FFI. + +It provides the `Compiler` class which contains `compile`, `prqlToPL`, `plToRQ` +and `rqToSQL` functions. + +It's still at an early stage, and isn't published to Composer. Contributions are +welcome. + +## Installation + +The [PHP FFI extension](https://www.php.net/manual/en/book.ffi.php) needs to be +enabled. Set `ffi.enable` in your php.ini configuration file to `"true"`. + +## Usage + +```php +compile("from employees"); + +echo $result->output; +``` + +## Development + +### Environment + +A way to establish a dev environment with PHP, the ext-ffi extension and +Composer is to use a [nix flake](https://github.com/loophp/nix-shell). After +installing nix, enable experimental flakes feature: + +``` +mkdir -p ~/.config/nix +echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf +``` + +Now you can spawn a shell from `prql-php/`: + +``` +nix shell github:loophp/nix-shell#env-php81 --impure +``` + +This will pull-in ext-ffi extension, because it's declared in `composer.json`. + +### Building + +There is a `build.sh` script that: + +- runs cargo to build `libprql_lib`, +- copies `libprql_lib.so` into `lib`, +- copies `libprql_lib.h` into `lib`. + +### Tests + +``` +sh build.sh +./vendor/bin/phpunit tests +``` + +### Code style + +``` +./vendor/bin/phpcs --standard=PSR12 src tests +``` diff --git a/bindings/prql-php/build.sh b/bindings/prql-php/build.sh new file mode 100644 index 000000000000..6964f183f88d --- /dev/null +++ b/bindings/prql-php/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# TODO: use a task file for these build scripts + +cargo build -p prql-lib --release + +mkdir -p lib +cp ../../target/release/libprql_lib.so ../prql-lib/libprql_lib.h lib diff --git a/bindings/prql-php/composer.json b/bindings/prql-php/composer.json new file mode 100644 index 000000000000..e224fe1ca46f --- /dev/null +++ b/bindings/prql-php/composer.json @@ -0,0 +1,38 @@ +{ + "name": "prql/compiler", + "description": "PRQL compiler bindings.", + "keywords": [ + "prql", + "sql" + ], + "homepage": "https://prql-lang.org/", + "type": "library", + "license": "Apache-2.0", + "autoload": { + "psr-4": { + "Prql\\Compiler\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Prql\\Tests\\": "tests/" + } + }, + "authors": [ + { + "name": "Jonathan" + } + ], + "support": { + "issues": "https://github.com/PRQL/prql/issues", + "source": "https://github.com/PRQL/prql", + "docs": "https://prql-lang.org/book/" + }, + "require": { + "ext-ffi": "*" + }, + "require-dev": { + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.7" + } +} diff --git a/bindings/prql-php/composer.lock b/bindings/prql-php/composer.lock new file mode 100644 index 000000000000..d1c8a284c6c4 --- /dev/null +++ b/bindings/prql-php/composer.lock @@ -0,0 +1,1519 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6463be3ca6ff9224f299805a4a534a78", + "packages": [], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": ["src/DeepCopy/deep_copy.php"], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "description": "Create deep copies (clones) of your objects", + "keywords": ["clone", "copy", "duplicate", "object", "object graph"], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.3", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": ["bin/php-parse"], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": ["parser", "php"], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + }, + "time": "2023-01-16T22:05:37+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bf4fbc9c13af7da12b3ea807574fb460f255daba", + "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.14", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": ["coverage", "testing", "xunit"], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:14:34+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd", + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": ["filesystem", "iterator"], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-10T16:53:14+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": ["process"], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": ["template"], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:46+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": ["timer"], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a6f61c5629dd95db79af72f1e94d56702187479a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a6f61c5629dd95db79af72f1e94d56702187479a", + "reference": "a6f61c5629dd95db79af72f1e94d56702187479a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.0", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.0", + "sebastian/global-state": "^6.0", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" + }, + "suggest": { + "ext-soap": "*" + }, + "bin": ["phpunit"], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.0-dev" + } + }, + "autoload": { + "files": ["src/Framework/Assert/Functions.php"], + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": ["phpunit", "testing", "xunit"], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.0.7" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-02-08T15:16:31+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:15+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": ["comparator", "compare", "equality"], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:07:16+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.10", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:47+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "70dd1b20bc198da394ad542e988381b44e64e39f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/70dd1b20bc198da394ad542e988381b44e64e39f", + "reference": "70dd1b20bc198da394ad542e988381b44e64e39f", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": ["diff", "udiff", "unidiff", "unified diff"], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:00:31+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b6f3694c6386c7959915a0037652e0c40f6f69cc", + "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": ["Xdebug", "environment", "hhvm"], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:03:04+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": ["export", "exporter"], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:49+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "aab257c712de87b90194febd52e4d184551c2d44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", + "reference": "aab257c712de87b90194febd52e4d184551c2d44", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": ["global state"], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:07:38+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.10", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:02+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": ["bin/phpcs", "bin/phpcbf"], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": ["phpcs", "standards", "static analysis"], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": ["src/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "ext-ffi": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/bindings/prql-php/phpstan.neon b/bindings/prql-php/phpstan.neon new file mode 100644 index 000000000000..e68e067527ee --- /dev/null +++ b/bindings/prql-php/phpstan.neon @@ -0,0 +1,16 @@ +parameters: + level: 9 + paths: + - src + - tests + ignoreErrors: + # Since PHPStan doesn't know about the existence of the functions in libprql, + # it complains since they get dynamically invoked on the FFI object instance + # without being statically defined, so they're unknown. + - '#Call to an undefined method FFI::compile\(\)\.#' + - '#Call to an undefined method FFI::prql_to_pl\(\)\.#' + - '#Call to an undefined method FFI::pl_to_rq\(\)\.#' + - '#Call to an undefined method FFI::rq_to_sql\(\)\.#' + - '#Cannot access property \$format on FFI\\CData\|null\.#' + - '#Cannot access property \$signature_comment on FFI\\CData\|null\.#' + - '#Cannot access property \$target on FFI\\CData\|null\.#' diff --git a/bindings/prql-php/src/Compiler.php b/bindings/prql-php/src/Compiler.php new file mode 100644 index 000000000000..c56a3a1f8eb0 --- /dev/null +++ b/bindings/prql-php/src/Compiler.php @@ -0,0 +1,277 @@ +optionsInit($options); + + $res = self::$ffi->compile($prql_query, \FFI::addr($ffi_options)); + + $this->optionsDestroy($ffi_options); + + return $this->convertResult($res); + } + + /** + * Compile a PRQL string into PL. + * + * @param string $prql_query PRQL query + * + * @return Result compilation result containing PL serialized as JSON + * + * @throws \InvalidArgumentException on NULL input + * + * @api + */ + public function prqlToPL(string $prql_query): Result + { + if (!$prql_query) { + throw new \InvalidArgumentException('No query given.'); + } + + $res = self::$ffi->prql_to_pl($prql_query); + + return $this->convertResult($res); + } + + /** + * Converts PL to RQ. + * + * @param string $pl_json PL serialized as JSON + * + * @return Result compilation result containing RQ serialized as JSON + * + * @throws \InvalidArgumentException on NULL input + * + * @api + */ + public function plToRQ(string $pl_json): Result + { + if (!$pl_json) { + throw new \InvalidArgumentException('No query given.'); + } + + $res = self::$ffi->pl_to_rq($pl_json); + + return $this->convertResult($res); + } + + /** + * Converts RQ to SQL. + * + * @param string $rq_json RQ serialized as JSON + * @param Options|null $options compile options + * + * @return Result compilation result containing SQL query + * + * @throws \InvalidArgumentException on NULL input + * + * @api + */ + public function rqToSQL(string $rq_json, ?Options $options = null): Result + { + if (!$rq_json) { + throw new \InvalidArgumentException('No query given.'); + } + + $ffi_options = $this->optionsInit($options); + + $res = self::$ffi->rq_to_sql($rq_json, \FFI::addr($ffi_options)); + + $this->optionsDestroy($ffi_options); + + return $this->convertResult($res); + } + + private function optionsInit(?Options $options = null) + { + if ($options === null) { + $options = new Options(); + } + + $ffi_options = self::$ffi->new('struct Options'); + $ffi_options->format = $options->format; + $ffi_options->signature_comment = $options->signature_comment; + + if (isset($options->target)) { + $len = strlen($options->target) + 1; + $ffi_options->target = \FFI::new("char[$len]", false); + \FFI::memcpy($ffi_options->target, $options->target, $len - 1); + } + + return $ffi_options; + } + + private function optionsDestroy($ffi_options) + { + if (!\FFI::isNull($ffi_options->target)) { + \FFI::free($ffi_options->target); + } + + unset($ffi_options); + } + + private function convertResult($ffi_res): Result + { + $res = new Result(); + + // convert string + $res->output = $this->convertString($ffi_res->output); + + $res->messages = []; + for ($i = 0; $i < $ffi_res->messages_len; ++$i) { + $res->messages[$i] = $this->convertMessage($ffi_res->messages[$i]); + } + + // free the ffi_result + self::$ffi->result_destroy($ffi_res); + + return $res; + } + + private function convertMessage($ffi_msg): Message + { + $msg = new Message(); + + // I'm using numbers here, I cannot find a way to refer to MessageKind.Error + if ($ffi_msg->kind == 0) { + $msg->kind = MessageKind::Error; + } elseif ($ffi_msg->kind == 1) { + $msg->kind = MessageKind::Warning; + } elseif ($ffi_msg->kind == 2) { + $msg->kind = MessageKind::Lint; + } + + $msg->code = $this->convertNullableString($ffi_msg->code); + $msg->reason = $this->convertString($ffi_msg->reason); + $msg->span = $this->convertSpan($ffi_msg->span); + $msg->hint = $this->convertNullableString($ffi_msg->hint); + + $msg->display = $this->convertNullableString($ffi_msg->display); + $msg->location = $this->convertLocation($ffi_msg->location); + + return $msg; + } + + private function convertSpan($ffi_ptr): ?Span + { + if (is_null($ffi_ptr) || \FFI::isNull($ffi_ptr)) { + return null; + } + + $span = new Span(); + $span->start = $ffi_ptr[0]->start; + $span->end = $ffi_ptr[0]->end; + + return $span; + } + + private function convertLocation($ffi_ptr): ?SourceLocation + { + if (is_null($ffi_ptr) || \FFI::isNull($ffi_ptr)) { + return null; + } + + $location = new SourceLocation(); + $location->start_line = $ffi_ptr[0]->start_line; + $location->start_col = $ffi_ptr[0]->start_col; + $location->end_line = $ffi_ptr[0]->end_line; + $location->end_col = $ffi_ptr[0]->end_col; + + return $location; + } + + private function convertNullableString($ffi_ptr): ?string + { + if (is_null($ffi_ptr) || \FFI::isNull($ffi_ptr)) { + return null; + } + // dereference + return $this->convertString($ffi_ptr[0]); + } + + private function convertString($ffi_ptr): string + { + return \FFI::string(\FFI::cast(\FFI::type('char*'), $ffi_ptr)); + } +} diff --git a/bindings/prql-php/src/Message.php b/bindings/prql-php/src/Message.php new file mode 100644 index 000000000000..9029fb7fd889 --- /dev/null +++ b/bindings/prql-php/src/Message.php @@ -0,0 +1,40 @@ + + */ + public array $messages; +} diff --git a/bindings/prql-php/src/SourceLocation.php b/bindings/prql-php/src/SourceLocation.php new file mode 100644 index 000000000000..e051549cb493 --- /dev/null +++ b/bindings/prql-php/src/SourceLocation.php @@ -0,0 +1,31 @@ +assertTrue(extension_loaded("ffi")); + } + + public function testPrqlLibraryFileExists(): void + { + $this->assertFileExists("lib/libprql_lib.so"); + } + + public function testPrqlHeaderFileExists(): void + { + $this->assertFileExists("lib/libprql_lib.h"); + } + + public function testInvalidQuery(): void + { + $prql = new Compiler(); + $res = $prql->compile("invalid"); + + $this->assertCount(1, $res->messages); + } + + public function testCompileWorks(): void + { + $options = new Options(); + $options->format = false; + $options->signature_comment = false; + $options->target = "sql.mssql"; + $prql = new Compiler(); + + $actual = $prql->compile("from employees | take 10", $options); + $this->assertCount(0, $actual->messages); + + $this->assertEquals("SELECT TOP (10) * FROM employees", $actual->output); + } + + public function testOtherFunctions(): void + { + $prql = new Compiler(); + + $query = " + let a = (from employees | take 10) + + from a | select [first_name] + "; + + $pl = $prql->prqlToPL($query); + $this->assertCount(0, $pl->messages); + + $rq = $prql->plToRQ($pl->output); + $this->assertCount(0, $rq->messages); + + $via_json = $prql->rqToSQL($rq->output); + $this->assertCount(0, $via_json->messages); + + $direct = $prql->compile($query); + $this->assertCount(0, $direct->messages); + + $this->assertEquals($via_json, $direct); + } +} diff --git a/prql-python/.gitignore b/bindings/prql-python/.gitignore similarity index 100% rename from prql-python/.gitignore rename to bindings/prql-python/.gitignore diff --git a/prql-python/Cargo.toml b/bindings/prql-python/Cargo.toml similarity index 88% rename from prql-python/Cargo.toml rename to bindings/prql-python/Cargo.toml index 0f5adc08c9dd..23933f393284 100644 --- a/prql-python/Cargo.toml +++ b/bindings/prql-python/Cargo.toml @@ -17,7 +17,7 @@ name = "prql_python" pyo3 = {version = "0.18.0", features = ["abi3-py37"]} [dependencies] -prql-compiler = {path = "../prql-compiler", default-features = false} +prql-compiler = {path = "../../prql-compiler", default-features = false} [dev-dependencies] insta = {version = "1.28", features = ["colors", "glob", "yaml"]} diff --git a/prql-python/README.md b/bindings/prql-python/README.md similarity index 100% rename from prql-python/README.md rename to bindings/prql-python/README.md diff --git a/prql-python/build.rs b/bindings/prql-python/build.rs similarity index 100% rename from prql-python/build.rs rename to bindings/prql-python/build.rs diff --git a/prql-python/noxfile.py b/bindings/prql-python/noxfile.py similarity index 86% rename from prql-python/noxfile.py rename to bindings/prql-python/noxfile.py index a695600cfbed..101a38c7e9e0 100644 --- a/prql-python/noxfile.py +++ b/bindings/prql-python/noxfile.py @@ -22,7 +22,7 @@ def tests(session: Session) -> None: """Run the test suite with pytest.""" print("CWD", os.getcwd()) session.install( - "-v", "--no-index", f"--find-links={Path('..', 'dist')}", "prql_python" + "-v", "--no-index", f"--find-links={Path('..', '..', 'dist')}", "prql_python" ) session.install("-v", "-r", "requirements.txt") session.run("pytest", str(Path("python", "tests"))) diff --git a/prql-python/pyproject.toml b/bindings/prql-python/pyproject.toml similarity index 100% rename from prql-python/pyproject.toml rename to bindings/prql-python/pyproject.toml diff --git a/prql-python/python/tests/conftest.py b/bindings/prql-python/python/tests/conftest.py similarity index 83% rename from prql-python/python/tests/conftest.py rename to bindings/prql-python/python/tests/conftest.py index 9bfb40dab01f..a12d02b8049e 100644 --- a/prql-python/python/tests/conftest.py +++ b/bindings/prql-python/python/tests/conftest.py @@ -5,7 +5,7 @@ @pytest.fixture() def example_queries(): - website_path = "../website/content/_index.md" + website_path = "../../web/website/content/_index.md" with open(website_path, "r") as f: website = f.read() website_yaml = yaml.safe_load(website.replace("---", "")) diff --git a/prql-python/python/tests/test_all.py b/bindings/prql-python/python/tests/test_all.py similarity index 82% rename from prql-python/python/tests/test_all.py rename to bindings/prql-python/python/tests/test_all.py index a1a364f0a840..481f9055cae2 100644 --- a/prql-python/python/tests/test_all.py +++ b/bindings/prql-python/python/tests/test_all.py @@ -49,24 +49,28 @@ def test_compile_options(): """ query_mssql = "prql target:sql.mssql\nfrom a | take 3" + assert prql.compile(query_mssql).startswith("SELECT\n TOP (3) *\nFROM\n a") + options_with_known_target = prql.CompileOptions( format=False, signature_comment=False, target="sql.sqlite" ) - options_without_target = prql.CompileOptions(format=False, signature_comment=False) - options_with_unknown_target = prql.CompileOptions( - format=False, signature_comment=False, target="foo" - ) - - assert prql.compile(query_mssql).startswith("SELECT\n TOP (3) *\nFROM\n a") assert ( prql.compile(query_mssql, options_with_known_target) == "SELECT * FROM a LIMIT 3" ) + + options_without_target = prql.CompileOptions(format=False, signature_comment=False) assert ( prql.compile(query_mssql, options_without_target) == "SELECT TOP (3) * FROM a" ) - # TODO: This should be unknown target error? + + options_with_any_target = prql.CompileOptions( + format=False, signature_comment=False, target="sql.any" + ) assert ( - prql.compile(query_mssql, options_with_unknown_target) - == "SELECT TOP (3) * FROM a" + prql.compile(query_mssql, options_with_any_target) == "SELECT TOP (3) * FROM a" ) + + options_default = prql.CompileOptions() + res = prql.compile(query_mssql, options_default) + assert res.startswith("SELECT\n TOP (3)") diff --git a/prql-python/python/tests/test_website_queries.py b/bindings/prql-python/python/tests/test_website_queries.py similarity index 100% rename from prql-python/python/tests/test_website_queries.py rename to bindings/prql-python/python/tests/test_website_queries.py diff --git a/prql-python/requirements.txt b/bindings/prql-python/requirements.txt similarity index 100% rename from prql-python/requirements.txt rename to bindings/prql-python/requirements.txt diff --git a/prql-python/src/lib.rs b/bindings/prql-python/src/lib.rs similarity index 69% rename from prql-python/src/lib.rs rename to bindings/prql-python/src/lib.rs index 4e7919d3aff7..332afdcd6df4 100644 --- a/prql-python/src/lib.rs +++ b/bindings/prql-python/src/lib.rs @@ -1,17 +1,22 @@ #![cfg(not(target_family = "wasm"))] use std::str::FromStr; -use prql_compiler::{self, IntoOnly, Target}; +use prql_compiler::{self, Target}; use pyo3::{exceptions, prelude::*}; #[pyfunction] pub fn compile(prql_query: &str, options: Option) -> PyResult { - Ok(prql_query) - .and_then(prql_compiler::prql_to_pl) - .and_then(prql_compiler::pl_to_rq) - .and_then(|rq| prql_compiler::rq_to_sql(rq, options.map(|o| o.into()).unwrap_or_default())) + let options = options.map(convert_options).transpose(); + + options + .and_then(|opts| { + Ok(prql_query) + .and_then(prql_compiler::prql_to_pl) + .and_then(prql_compiler::pl_to_rq) + .and_then(|rq| prql_compiler::rq_to_sql(rq, &opts.unwrap_or_default())) + }) .map_err(|e| e.composed("", prql_query, false)) - .map_err(|e| (PyErr::new::(e.into_only().unwrap().reason))) + .map_err(|e| (PyErr::new::(e.to_string()))) } #[pyfunction] @@ -35,7 +40,7 @@ pub fn pl_to_rq(pl_json: &str) -> PyResult { pub fn rq_to_sql(rq_json: &str) -> PyResult { Ok(rq_json) .and_then(prql_compiler::json::to_rq) - .and_then(|x| prql_compiler::rq_to_sql(x, prql_compiler::Options::default())) + .and_then(|x| prql_compiler::rq_to_sql(x, &prql_compiler::Options::default())) .map_err(|err| (PyErr::new::(err.to_json()))) } @@ -63,16 +68,10 @@ pub struct CompileOptions { /// Defaults to true. pub format: bool, - /// Target dialect to compile to. - /// - /// This is only changes the output for a relatively small subset of - /// features. + /// Target to compile to. /// - /// If something does not work in a specific dialect, please raise in a - /// GitHub issue. - /// - /// If `None` is used, the `target` argument from the query header is used. - /// If it does not exist, [Dialect::Generic] is used. + /// Defaults to "sql.any", which uses the `target` argument from the query + /// header to determine The SQL dialect. pub target: String, /// Emits the compiler signature as a comment after generated SQL @@ -84,8 +83,8 @@ pub struct CompileOptions { #[pymethods] impl CompileOptions { #[new] - pub fn new(format: bool, signature_comment: bool, target: Option) -> Self { - let target = target.unwrap_or_default(); + #[pyo3(signature = (*, format=true, signature_comment=true, target="sql.any".to_string()))] + pub fn new(format: bool, signature_comment: bool, target: String) -> Self { CompileOptions { format, target, @@ -94,16 +93,16 @@ impl CompileOptions { } } -impl From for prql_compiler::Options { - fn from(o: CompileOptions) -> Self { - let target = Target::from_str(&o.target).unwrap_or_default(); +fn convert_options( + o: CompileOptions, +) -> Result { + let target = Target::from_str(&o.target).map_err(|e| prql_compiler::downcast(e.into()))?; - prql_compiler::Options { - format: o.format, - target, - signature_comment: o.signature_comment, - } - } + Ok(prql_compiler::Options { + format: o.format, + target, + signature_comment: o.signature_comment, + }) } #[pyfunction] @@ -121,7 +120,7 @@ mod test { fn parse_for_python() { let opts = Some(CompileOptions { format: true, - target: String::new(), + target: "sql.any".to_string(), signature_comment: false, }); diff --git a/book/src/bindings/README.md b/book/src/bindings/README.md deleted file mode 100644 index f7056439154f..000000000000 --- a/book/src/bindings/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Bindings - -PRQL has bindings for many languages. These include: - -- [Java](./java.md) -- [JavaScript](./javascript.md) -- [Python](./python.md) -- [R](./r.md) -- [Rust](./rust.md) -- [Elixir](./elixir.md) diff --git a/book/src/bindings/elixir.md b/book/src/bindings/elixir.md deleted file mode 100644 index c2874dcf8a07..000000000000 --- a/book/src/bindings/elixir.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../../../prql-elixir/README.md}} diff --git a/book/src/bindings/javascript.md b/book/src/bindings/javascript.md deleted file mode 100644 index ea88f1817685..000000000000 --- a/book/src/bindings/javascript.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../../../prql-js/README.md}} diff --git a/book/src/bindings/rust.md b/book/src/bindings/rust.md deleted file mode 100644 index b9511f4fd427..000000000000 --- a/book/src/bindings/rust.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../../../prql-compiler/README.md}} diff --git a/book/src/integrations/rill.md b/book/src/integrations/rill.md deleted file mode 100644 index 950e0b5fb50d..000000000000 --- a/book/src/integrations/rill.md +++ /dev/null @@ -1,5 +0,0 @@ -# Rill - -PRQL has had some work to integrate with Rill. See the -[Rill Issues](https://github.com/PRQL/prql/issues?q=is%3Aissue+rill) for more -details. diff --git a/book/src/language-features/switch.md b/book/src/language-features/switch.md deleted file mode 100644 index 9570570eeb9c..000000000000 --- a/book/src/language-features/switch.md +++ /dev/null @@ -1,27 +0,0 @@ -# Switch - -```admonish note -`switch` is currently experimental and may change behavior in the near future -``` - -PRQL uses `switch` for both SQL's `CASE` and `IF` statements. Here's an example: - -```prql -from employees -derive distance = switch [ - city == "Calgary" -> 0, - city == "Edmonton" -> 300, -] -``` - -If no condition is met, the value takes a `null` value. To set a default, use a -`true` condition: - -```prql -from employees -derive distance = switch [ - city == "Calgary" -> 0, - city == "Edmonton" -> 300, - true -> "Unknown", -] -``` diff --git a/book/tests/prql/examples/cte-0.prql b/book/tests/prql/examples/cte-0.prql deleted file mode 100644 index c5d23ce84ec6..000000000000 --- a/book/tests/prql/examples/cte-0.prql +++ /dev/null @@ -1,16 +0,0 @@ -let newest_employees = ( - from employees - sort tenure - take 50 -) - -let average_salaries = ( - from salaries - group country ( - aggregate average_country_salary = (average salary) - ) -) - -from newest_employees -join average_salaries [==country] -select [name, salary, average_country_salary] diff --git a/book/tests/prql/examples/employees-0.prql b/book/tests/prql/examples/employees-0.prql deleted file mode 100644 index 78e6ad12a5d6..000000000000 --- a/book/tests/prql/examples/employees-0.prql +++ /dev/null @@ -1,11 +0,0 @@ -from salaries -group [emp_no] ( - aggregate [emp_salary = average salary] -) -join t=titles [==emp_no] -join dept_emp side:left [==emp_no] -group [dept_emp.dept_no, t.title] ( - aggregate [avg_salary = average emp_salary] -) -join departments [==dept_no] -select [dept_name, title, avg_salary] diff --git a/book/tests/prql/examples/employees-1.prql b/book/tests/prql/examples/employees-1.prql deleted file mode 100644 index a568c2949aa9..000000000000 --- a/book/tests/prql/examples/employees-1.prql +++ /dev/null @@ -1,16 +0,0 @@ -from e=employees -join salaries [==emp_no] -group [e.emp_no, e.gender] ( - aggregate [ - emp_salary = average salaries.salary - ] -) -join de=dept_emp [==emp_no] side:left -group [de.dept_no, gender] ( - aggregate [ - salary_avg = average emp_salary, - salary_sd = stddev emp_salary, - ] -) -join departments [==dept_no] -select [dept_name, gender, salary_avg, salary_sd] diff --git a/book/tests/prql/examples/employees-2.prql b/book/tests/prql/examples/employees-2.prql deleted file mode 100644 index 6164d8077a4a..000000000000 --- a/book/tests/prql/examples/employees-2.prql +++ /dev/null @@ -1,21 +0,0 @@ -from e=employees -join salaries [==emp_no] -group [e.emp_no, e.gender] ( - aggregate [ - emp_salary = average salaries.salary - ] -) -join de=dept_emp [==emp_no] -join dm=dept_manager [ - (dm.dept_no == de.dept_no) and s"(de.from_date, de.to_date) OVERLAPS (dm.from_date, dm.to_date)" -] -group [dm.emp_no, gender] ( - aggregate [ - salary_avg = average emp_salary, - salary_sd = stddev emp_salary - ] -) -derive mng_no = emp_no -join managers=employees [==emp_no] -derive mng_name = s"managers.first_name || ' ' || managers.last_name" -select [mng_name, managers.gender, salary_avg, salary_sd] diff --git a/book/tests/prql/examples/employees-3.prql b/book/tests/prql/examples/employees-3.prql deleted file mode 100644 index 9e9d9e47855f..000000000000 --- a/book/tests/prql/examples/employees-3.prql +++ /dev/null @@ -1,11 +0,0 @@ -from de=dept_emp -join s=salaries side:left [ - (s.emp_no == de.emp_no), - s"({s.from_date}, {s.to_date}) OVERLAPS ({de.from_date}, {de.to_date})" -] -group [de.emp_no, de.dept_no] ( - aggregate salary = (average s.salary) -) -join employees [==emp_no] -join titles [==emp_no] -select [dept_no, salary, employees.gender, titles.title] diff --git a/book/tests/prql/examples/list-equivalence-0.prql b/book/tests/prql/examples/list-equivalence-0.prql deleted file mode 100644 index ce126ab4c459..000000000000 --- a/book/tests/prql/examples/list-equivalence-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select salary diff --git a/book/tests/prql/examples/list-equivalence-1.prql b/book/tests/prql/examples/list-equivalence-1.prql deleted file mode 100644 index 3b89f4256948..000000000000 --- a/book/tests/prql/examples/list-equivalence-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select [salary] diff --git a/book/tests/prql/examples/list-equivalence-2.prql b/book/tests/prql/examples/list-equivalence-2.prql deleted file mode 100644 index f49937a6bfc6..000000000000 --- a/book/tests/prql/examples/list-equivalence-2.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -derive [ - gross_salary = salary + payroll_tax, - gross_cost = gross_salary + benefits_cost -] diff --git a/book/tests/prql/examples/list-equivalence-3.prql b/book/tests/prql/examples/list-equivalence-3.prql deleted file mode 100644 index f539c32d0469..000000000000 --- a/book/tests/prql/examples/list-equivalence-3.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -derive gross_salary = salary + payroll_tax -derive gross_cost = gross_salary + benefits_cost diff --git a/book/tests/prql/examples/misc-0.prql b/book/tests/prql/examples/misc-0.prql deleted file mode 100644 index e29792bf8486..000000000000 --- a/book/tests/prql/examples/misc-0.prql +++ /dev/null @@ -1,15 +0,0 @@ -# TODO: this table should have a column `part` with values 1..5, -# but such data declaration is not yet supported, see #286 -let parts = ( - from seq_1_to_5 -) - -from pl=prospect_lists_prospects -filter prospect_list_id == 'cc675eee-8bd1-237f-be5e-622ba511d65e' -join a=accounts [a.id == pl.related_id] -join er=email_addr_bean_rel [er.bean_id == a.id and er.primary_address == '1'] -join ea=email_addresses [ea.id == er.email_address_id] -select ea.email_address -derive prefix = s"regexp_replace(SUBSTRING_INDEX({email_address}, '@', 1), '[.0-9-_:]+', '.')" -derive stub = s"SUBSTRING_INDEX(SUBSTRING_INDEX({prefix}, '.', part), '.', -1)" -select [email_address, stub] diff --git a/book/tests/prql/examples/misc-1.prql b/book/tests/prql/examples/misc-1.prql deleted file mode 100644 index 7fc71d21985a..000000000000 --- a/book/tests/prql/examples/misc-1.prql +++ /dev/null @@ -1,7 +0,0 @@ -from club_ratings -filter rating != null -# TODO: this is real ugly. `average rating` should not require parenthesis -# TODO: why cannot we put comments in group's pipeline? -group year ( - derive [rating_norm = rating - (average rating) / (stddev rating)] -) diff --git a/book/tests/prql/examples/variables-1.prql b/book/tests/prql/examples/variables-1.prql deleted file mode 100644 index e0a4b962bb21..000000000000 --- a/book/tests/prql/examples/variables-1.prql +++ /dev/null @@ -1,15 +0,0 @@ -from employees -group [emp_no] ( - aggregate [ - emp_salary = average salary # average salary resolves to "AVG(salary)" (from stdlib) - ] -) -join titles [==emp_no] -group [title] ( - aggregate [ - avg_salary = average emp_salary - ] -) -select salary_k = avg_salary / 1000 # avg_salary should resolve to "AVG(emp_salary)" -take 10 # induces new SELECT -derive salary = salary_k * 1000 # salary_k should not resolve to "avg_salary / 1000" diff --git a/book/tests/prql/internals/functional-lang-0.prql b/book/tests/prql/internals/functional-lang-0.prql deleted file mode 100644 index 4b2a2780ffbd..000000000000 --- a/book/tests/prql/internals/functional-lang-0.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -filter age > 50 -sort name diff --git a/book/tests/prql/internals/functional-lang-1.prql b/book/tests/prql/internals/functional-lang-1.prql deleted file mode 100644 index b73c3e233564..000000000000 --- a/book/tests/prql/internals/functional-lang-1.prql +++ /dev/null @@ -1 +0,0 @@ -from employees | filter age > 50 | sort name diff --git a/book/tests/prql/internals/functional-lang-2.prql b/book/tests/prql/internals/functional-lang-2.prql deleted file mode 100644 index d2277f25634f..000000000000 --- a/book/tests/prql/internals/functional-lang-2.prql +++ /dev/null @@ -1 +0,0 @@ -filter age > 50 (from employees) | sort name diff --git a/book/tests/prql/internals/functional-lang-3.prql b/book/tests/prql/internals/functional-lang-3.prql deleted file mode 100644 index 40177f3353f9..000000000000 --- a/book/tests/prql/internals/functional-lang-3.prql +++ /dev/null @@ -1 +0,0 @@ -sort name (filter age > 50 (from employees)) diff --git a/book/tests/prql/internals/name-resolving-0.prql b/book/tests/prql/internals/name-resolving-0.prql deleted file mode 100644 index ec44dd3366e8..000000000000 --- a/book/tests/prql/internals/name-resolving-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select first_name diff --git a/book/tests/prql/internals/name-resolving-1.prql b/book/tests/prql/internals/name-resolving-1.prql deleted file mode 100644 index 9a0ec0db2a53..000000000000 --- a/book/tests/prql/internals/name-resolving-1.prql +++ /dev/null @@ -1,4 +0,0 @@ -from employees -derive [first_name, dept_id] -join d=departments [==dept_id] -select [first_name, d.title] diff --git a/book/tests/prql/introduction-0.prql b/book/tests/prql/introduction-0.prql deleted file mode 100644 index 9a6969d51382..000000000000 --- a/book/tests/prql/introduction-0.prql +++ /dev/null @@ -1,18 +0,0 @@ -from employees -filter start_date > @2021-01-01 # Clear date syntax -derive [ # `derive` adds columns / variables - gross_salary = salary + (tax ?? 0), # Terse coalesce - gross_cost = gross_salary + benefits_cost, # Variables can use other variables -] -filter gross_cost > 0 -group [title, country] ( # `group` runs a pipeline over each group - aggregate [ # `aggregate` reduces each group to a value - average gross_salary, - sum_gross_cost = sum gross_cost, # `=` sets a column name - ] -) -filter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING` -derive id = f"{title}_{country}" # F-strings like Python -derive country_code = s"LEFT(country, 2)" # S-strings allow using SQL as an escape hatch -sort [sum_gross_cost, -country] # `-country` means descending order -take 1..20 # Range expressions (also valid here as `take 20`) diff --git a/book/tests/prql/language-features/coalesce-0.prql b/book/tests/prql/language-features/coalesce-0.prql deleted file mode 100644 index 86640dac1782..000000000000 --- a/book/tests/prql/language-features/coalesce-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from orders -derive amount ?? 0 diff --git a/book/tests/prql/language-features/dates-and-times-0.prql b/book/tests/prql/language-features/dates-and-times-0.prql deleted file mode 100644 index 9bc1efe5bcae..000000000000 --- a/book/tests/prql/language-features/dates-and-times-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -derive age_at_year_end = (@2022-12-31 - dob) diff --git a/book/tests/prql/language-features/dates-and-times-1.prql b/book/tests/prql/language-features/dates-and-times-1.prql deleted file mode 100644 index 83107818e462..000000000000 --- a/book/tests/prql/language-features/dates-and-times-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from orders -derive should_have_shipped_today = (order_time < @08:30) diff --git a/book/tests/prql/language-features/dates-and-times-2.prql b/book/tests/prql/language-features/dates-and-times-2.prql deleted file mode 100644 index 12687310b181..000000000000 --- a/book/tests/prql/language-features/dates-and-times-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from commits -derive first_prql_commit = @2020-01-01T13:19:55-0800 diff --git a/book/tests/prql/language-features/dates-and-times-3.prql b/book/tests/prql/language-features/dates-and-times-3.prql deleted file mode 100644 index a22537d03c8e..000000000000 --- a/book/tests/prql/language-features/dates-and-times-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from projects -derive first_check_in = start + 10days diff --git a/book/tests/prql/language-features/distinct-0.prql b/book/tests/prql/language-features/distinct-0.prql deleted file mode 100644 index ed8a37d174b7..000000000000 --- a/book/tests/prql/language-features/distinct-0.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -select department -group department ( - take 1 -) diff --git a/book/tests/prql/language-features/distinct-1.prql b/book/tests/prql/language-features/distinct-1.prql deleted file mode 100644 index 70764167a1ab..000000000000 --- a/book/tests/prql/language-features/distinct-1.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -select department -group department (take 1) diff --git a/book/tests/prql/language-features/distinct-2.prql b/book/tests/prql/language-features/distinct-2.prql deleted file mode 100644 index f224527e39e3..000000000000 --- a/book/tests/prql/language-features/distinct-2.prql +++ /dev/null @@ -1,6 +0,0 @@ -# youngest employee from each department -from employees -group department ( - sort age - take 1 -) diff --git a/book/tests/prql/language-features/f-strings-0.prql b/book/tests/prql/language-features/f-strings-0.prql deleted file mode 100644 index 5916b81e2865..000000000000 --- a/book/tests/prql/language-features/f-strings-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select full_name = f"{first_name} {last_name}" diff --git a/book/tests/prql/language-features/f-strings-1.prql b/book/tests/prql/language-features/f-strings-1.prql deleted file mode 100644 index 6e8e37a471a0..000000000000 --- a/book/tests/prql/language-features/f-strings-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from web -select url = f"http{tls}://www.{domain}.{tld}/{page}" diff --git a/book/tests/prql/language-features/null-0.prql b/book/tests/prql/language-features/null-0.prql deleted file mode 100644 index 59e730264398..000000000000 --- a/book/tests/prql/language-features/null-0.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -filter first_name == null -filter null != last_name diff --git a/book/tests/prql/language-features/ranges-0.prql b/book/tests/prql/language-features/ranges-0.prql deleted file mode 100644 index d200b9b61999..000000000000 --- a/book/tests/prql/language-features/ranges-0.prql +++ /dev/null @@ -1,4 +0,0 @@ -from events -filter (date | in @1776-07-04..@1787-09-17) -filter (magnitude | in 50..100) -derive is_northern = (latitude | in 0..) diff --git a/book/tests/prql/language-features/ranges-1.prql b/book/tests/prql/language-features/ranges-1.prql deleted file mode 100644 index f6939b843a7c..000000000000 --- a/book/tests/prql/language-features/ranges-1.prql +++ /dev/null @@ -1,3 +0,0 @@ -from orders -sort [-value, date] -take 101..110 diff --git a/book/tests/prql/language-features/s-strings-0.prql b/book/tests/prql/language-features/s-strings-0.prql deleted file mode 100644 index 58aeda7a72fc..000000000000 --- a/book/tests/prql/language-features/s-strings-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select db_version = s"version()" diff --git a/book/tests/prql/language-features/s-strings-1.prql b/book/tests/prql/language-features/s-strings-1.prql deleted file mode 100644 index 00a434b7d91c..000000000000 --- a/book/tests/prql/language-features/s-strings-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -aggregate [average salary] diff --git a/book/tests/prql/language-features/s-strings-2.prql b/book/tests/prql/language-features/s-strings-2.prql deleted file mode 100644 index 60f8731d9746..000000000000 --- a/book/tests/prql/language-features/s-strings-2.prql +++ /dev/null @@ -1,7 +0,0 @@ -from de=dept_emp -join s=salaries side:left [ - (s.emp_no == de.emp_no), - s"""({s.from_date}, {s.to_date}) - OVERLAPS - ({de.from_date}, {de.to_date})""" -] diff --git a/book/tests/prql/language-features/s-strings-3.prql b/book/tests/prql/language-features/s-strings-3.prql deleted file mode 100644 index bca882d48c32..000000000000 --- a/book/tests/prql/language-features/s-strings-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from s"SELECT DISTINCT ON first_name, id, age FROM employees ORDER BY age ASC" -join s = s"SELECT * FROM salaries" [==id] diff --git a/book/tests/prql/language-features/s-strings-4.prql b/book/tests/prql/language-features/s-strings-4.prql deleted file mode 100644 index 782542d16c58..000000000000 --- a/book/tests/prql/language-features/s-strings-4.prql +++ /dev/null @@ -1,4 +0,0 @@ -from employees -derive [ - has_valid_title = s"regexp_contains(title, '([a-z0-9]*-){{2,}}')" -] diff --git a/book/tests/prql/language-features/s-strings-5.prql b/book/tests/prql/language-features/s-strings-5.prql deleted file mode 100644 index 58a535b9d514..000000000000 --- a/book/tests/prql/language-features/s-strings-5.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -derive [ - gross_salary = salary + benefits, - daily_rate = s"{gross_salary} / 365" -] diff --git a/book/tests/prql/language-features/s-strings-6.prql b/book/tests/prql/language-features/s-strings-6.prql deleted file mode 100644 index e8ba3a5b3cf2..000000000000 --- a/book/tests/prql/language-features/s-strings-6.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -derive [ - gross_salary = salary + benefits, - daily_rate = s"({gross_salary}) / 365" -] diff --git a/book/tests/prql/language-features/standard-library-0.prql b/book/tests/prql/language-features/standard-library-0.prql deleted file mode 100644 index 1a362939ebc3..000000000000 --- a/book/tests/prql/language-features/standard-library-0.prql +++ /dev/null @@ -1,6 +0,0 @@ -from employees -derive [ - gross_salary = (salary + payroll_tax | as int), - gross_salary_rounded = (gross_salary | round 0), - time = s"NOW()", # an s-string, given no `now` function exists in PRQL -] diff --git a/book/tests/prql/language-features/strings-0.prql b/book/tests/prql/language-features/strings-0.prql deleted file mode 100644 index 294ce2444927..000000000000 --- a/book/tests/prql/language-features/strings-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select x = "hello world" diff --git a/book/tests/prql/language-features/strings-1.prql b/book/tests/prql/language-features/strings-1.prql deleted file mode 100644 index 3591aa871e1b..000000000000 --- a/book/tests/prql/language-features/strings-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select x = 'hello world' diff --git a/book/tests/prql/language-features/strings-2.prql b/book/tests/prql/language-features/strings-2.prql deleted file mode 100644 index d91760c6bcd4..000000000000 --- a/book/tests/prql/language-features/strings-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select x = '"hello world"' diff --git a/book/tests/prql/language-features/strings-3.prql b/book/tests/prql/language-features/strings-3.prql deleted file mode 100644 index be93bbf93a6a..000000000000 --- a/book/tests/prql/language-features/strings-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select x = """I said "hello world"!""" diff --git a/book/tests/prql/language-features/strings-4.prql b/book/tests/prql/language-features/strings-4.prql deleted file mode 100644 index 92d3be607db4..000000000000 --- a/book/tests/prql/language-features/strings-4.prql +++ /dev/null @@ -1,2 +0,0 @@ -from my_table -select x = """""I said """hello world"""!""""" diff --git a/book/tests/prql/language-features/switch-0.prql b/book/tests/prql/language-features/switch-0.prql deleted file mode 100644 index e3284d640b83..000000000000 --- a/book/tests/prql/language-features/switch-0.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -derive distance = switch [ - city == "Calgary" -> 0, - city == "Edmonton" -> 300, -] diff --git a/book/tests/prql/language-features/switch-1.prql b/book/tests/prql/language-features/switch-1.prql deleted file mode 100644 index f8d840c11cb5..000000000000 --- a/book/tests/prql/language-features/switch-1.prql +++ /dev/null @@ -1,6 +0,0 @@ -from employees -derive distance = switch [ - city == "Calgary" -> 0, - city == "Edmonton" -> 300, - true -> "Unknown", -] diff --git a/book/tests/prql/language-features/target-0.prql b/book/tests/prql/language-features/target-0.prql deleted file mode 100644 index 21e54d9840c3..000000000000 --- a/book/tests/prql/language-features/target-0.prql +++ /dev/null @@ -1,5 +0,0 @@ -prql target:sql.postgres - -from employees -sort age -take 10 diff --git a/book/tests/prql/language-features/target-1.prql b/book/tests/prql/language-features/target-1.prql deleted file mode 100644 index 53433b4b16c0..000000000000 --- a/book/tests/prql/language-features/target-1.prql +++ /dev/null @@ -1,5 +0,0 @@ -prql target:sql.mssql - -from employees -sort age -take 10 diff --git a/book/tests/prql/language-features/target-2.prql b/book/tests/prql/language-features/target-2.prql deleted file mode 100644 index c452ea2a5aad..000000000000 --- a/book/tests/prql/language-features/target-2.prql +++ /dev/null @@ -1,3 +0,0 @@ -prql version:"0.5" - -from employees diff --git a/book/tests/prql/queries/functions-0.prql b/book/tests/prql/queries/functions-0.prql deleted file mode 100644 index b09007f54290..000000000000 --- a/book/tests/prql/queries/functions-0.prql +++ /dev/null @@ -1,4 +0,0 @@ -func fahrenheit_to_celsius temp -> (temp - 32) / 1.8 - -from cities -derive temp_c = (fahrenheit_to_celsius temp_f) diff --git a/book/tests/prql/queries/functions-1.prql b/book/tests/prql/queries/functions-1.prql deleted file mode 100644 index f2da76bdaed6..000000000000 --- a/book/tests/prql/queries/functions-1.prql +++ /dev/null @@ -1,7 +0,0 @@ -func interp lower:0 higher x -> (x - lower) / (higher - lower) - -from students -derive [ - sat_proportion_1 = (interp 1600 sat_score), - sat_proportion_2 = (interp lower:0 1600 sat_score), -] diff --git a/book/tests/prql/queries/functions-2.prql b/book/tests/prql/queries/functions-2.prql deleted file mode 100644 index f7d6afdbd8ac..000000000000 --- a/book/tests/prql/queries/functions-2.prql +++ /dev/null @@ -1,7 +0,0 @@ -func interp lower:0 higher x -> (x - lower) / (higher - lower) - -from students -derive [ - sat_proportion_1 = (sat_score | interp 1600), - sat_proportion_2 = (sat_score | interp lower:0 1600), -] diff --git a/book/tests/prql/queries/functions-3.prql b/book/tests/prql/queries/functions-3.prql deleted file mode 100644 index fc9447867a1c..000000000000 --- a/book/tests/prql/queries/functions-3.prql +++ /dev/null @@ -1,4 +0,0 @@ -func fahrenheit_to_celsius temp -> (temp - 32) / 1.8 - -from cities -derive temp_c = (temp_f | fahrenheit_to_celsius) diff --git a/book/tests/prql/queries/functions-4.prql b/book/tests/prql/queries/functions-4.prql deleted file mode 100644 index f692e9ffbeb7..000000000000 --- a/book/tests/prql/queries/functions-4.prql +++ /dev/null @@ -1,5 +0,0 @@ -func fahrenheit_to_celsius temp -> (temp - 32) / 1.8 -func interp lower:0 higher x -> (x - lower) / (higher - lower) - -from kettles -derive boiling_proportion = (temp_c | fahrenheit_to_celsius | interp 100) diff --git a/book/tests/prql/queries/pipelines-0.prql b/book/tests/prql/queries/pipelines-0.prql deleted file mode 100644 index 782143755f3b..000000000000 --- a/book/tests/prql/queries/pipelines-0.prql +++ /dev/null @@ -1 +0,0 @@ -from employees diff --git a/book/tests/prql/queries/pipelines-1.prql b/book/tests/prql/queries/pipelines-1.prql deleted file mode 100644 index c3b39d069fa8..000000000000 --- a/book/tests/prql/queries/pipelines-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -derive gross_salary = (salary + payroll_tax) diff --git a/book/tests/prql/queries/pipelines-2.prql b/book/tests/prql/queries/pipelines-2.prql deleted file mode 100644 index dee17ddc0e63..000000000000 --- a/book/tests/prql/queries/pipelines-2.prql +++ /dev/null @@ -1,6 +0,0 @@ -from e = employees -derive gross_salary = (salary + payroll_tax) -sort gross_salary -take 10 -join d = department [==dept_no] -select [e.name, gross_salary, d.name] diff --git a/book/tests/prql/queries/variables-0.prql b/book/tests/prql/queries/variables-0.prql deleted file mode 100644 index f130b7179c73..000000000000 --- a/book/tests/prql/queries/variables-0.prql +++ /dev/null @@ -1,8 +0,0 @@ -let top_50 = ( - from employees - sort salary - take 50 - aggregate [total_salary = sum salary] -) - -from top_50 # Starts a new pipeline diff --git a/book/tests/prql/queries/variables-1.prql b/book/tests/prql/queries/variables-1.prql deleted file mode 100644 index 320c82b3ddd9..000000000000 --- a/book/tests/prql/queries/variables-1.prql +++ /dev/null @@ -1,9 +0,0 @@ -let grouping = s""" - SELECT SUM(a) - FROM tbl - GROUP BY - GROUPING SETS - ((b, c, d), (d), (b, d)) -""" - -from grouping diff --git a/book/tests/prql/syntax-0.prql b/book/tests/prql/syntax-0.prql deleted file mode 100644 index d8b0ec6dd72e..000000000000 --- a/book/tests/prql/syntax-0.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -filter department == "Product" -select [first_name, last_name] diff --git a/book/tests/prql/syntax-1.prql b/book/tests/prql/syntax-1.prql deleted file mode 100644 index f28aaf723a02..000000000000 --- a/book/tests/prql/syntax-1.prql +++ /dev/null @@ -1 +0,0 @@ -from employees | filter department == "Product" | select [first_name, last_name] diff --git a/book/tests/prql/syntax-10.prql b/book/tests/prql/syntax-10.prql deleted file mode 100644 index b2ebba1b6488..000000000000 --- a/book/tests/prql/syntax-10.prql +++ /dev/null @@ -1,3 +0,0 @@ -prql target:sql.postgres -from employees -select `first name` diff --git a/book/tests/prql/syntax-11.prql b/book/tests/prql/syntax-11.prql deleted file mode 100644 index e855d0393136..000000000000 --- a/book/tests/prql/syntax-11.prql +++ /dev/null @@ -1 +0,0 @@ -from `dir/*.parquet` diff --git a/book/tests/prql/syntax-12.prql b/book/tests/prql/syntax-12.prql deleted file mode 100644 index a634b9554275..000000000000 --- a/book/tests/prql/syntax-12.prql +++ /dev/null @@ -1,3 +0,0 @@ -prql target:sql.bigquery -from `project-foo.dataset.table` -join `project-bar.dataset.table` [==col_bax] diff --git a/book/tests/prql/syntax-13.prql b/book/tests/prql/syntax-13.prql deleted file mode 100644 index a9ccfe89ed66..000000000000 --- a/book/tests/prql/syntax-13.prql +++ /dev/null @@ -1 +0,0 @@ -from `music.albums` diff --git a/book/tests/prql/syntax-14.prql b/book/tests/prql/syntax-14.prql deleted file mode 100644 index 15be2cb62f61..000000000000 --- a/book/tests/prql/syntax-14.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -filter id == $1 diff --git a/book/tests/prql/syntax-15.prql b/book/tests/prql/syntax-15.prql deleted file mode 100644 index 290e626cf825..000000000000 --- a/book/tests/prql/syntax-15.prql +++ /dev/null @@ -1,5 +0,0 @@ -from numbers -select [ - small = 1.000_000_1, - big = 5_000_000, -] diff --git a/book/tests/prql/syntax-2.prql b/book/tests/prql/syntax-2.prql deleted file mode 100644 index 738ad9c85a11..000000000000 --- a/book/tests/prql/syntax-2.prql +++ /dev/null @@ -1,10 +0,0 @@ -from numbers -derive [x = 1, y = 2] -derive [ - a = x, - b = y -] -derive [ - c = a, - d = b, -] diff --git a/book/tests/prql/syntax-3.prql b/book/tests/prql/syntax-3.prql deleted file mode 100644 index 7badae90b98a..000000000000 --- a/book/tests/prql/syntax-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select [first_name] diff --git a/book/tests/prql/syntax-4.prql b/book/tests/prql/syntax-4.prql deleted file mode 100644 index ec44dd3366e8..000000000000 --- a/book/tests/prql/syntax-4.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select first_name diff --git a/book/tests/prql/syntax-5.prql b/book/tests/prql/syntax-5.prql deleted file mode 100644 index 79e709893b7c..000000000000 --- a/book/tests/prql/syntax-5.prql +++ /dev/null @@ -1,6 +0,0 @@ -from foo -select [ - circumference = diameter * 3.14159, - color, -] -filter circumference > 10 and color != "red" diff --git a/book/tests/prql/syntax-6.prql b/book/tests/prql/syntax-6.prql deleted file mode 100644 index 9ce71c723b6a..000000000000 --- a/book/tests/prql/syntax-6.prql +++ /dev/null @@ -1,26 +0,0 @@ -from employees -# Requires parentheses, because it's contains a pipe -derive is_proximate = (distance | in 0..20) -# Requires parentheses, because it's a function call -derive total_distance = (sum distance) -# `??` doesn't require parentheses, as it's not a function call -derive min_capped_distance = (min distance ?? 5) -# No parentheses needed, because no function call -derive travel_time = distance / 40 -# No inner parentheses needed around `1+1` because no function call -derive distance_rounded_2_dp = (round 1+1 distance) -derive [ - # Requires parentheses, because it contains a pipe - is_far = (distance | in 100..), - # The left value of the range requires parentheses, - # because of the minus sign - is_negative = (distance | in (-100..0)), - # ...this is equivalent - is_negative = (distance | in (-100)..0), - # Doesn't require parentheses, because it's in a list (confusing, see footnote)! - average_distance = average distance, -] -# Requires parentheses because of the minus sign -sort (-distance) -# A list is fine too -sort [-distance] diff --git a/book/tests/prql/syntax-7.prql b/book/tests/prql/syntax-7.prql deleted file mode 100644 index f655f4637971..000000000000 --- a/book/tests/prql/syntax-7.prql +++ /dev/null @@ -1,7 +0,0 @@ -from employees -group [title, country] ( - aggregate [ - average salary, - ct = count - ] -) diff --git a/book/tests/prql/syntax-8.prql b/book/tests/prql/syntax-8.prql deleted file mode 100644 index 7ee340a4259a..000000000000 --- a/book/tests/prql/syntax-8.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees # Comment 1 -# Comment 2 -aggregate [average salary] diff --git a/book/tests/prql/syntax-9.prql b/book/tests/prql/syntax-9.prql deleted file mode 100644 index d768dedf3f09..000000000000 --- a/book/tests/prql/syntax-9.prql +++ /dev/null @@ -1,3 +0,0 @@ -prql target:sql.mysql -from employees -select `first name` diff --git a/book/tests/prql/transforms/aggregate-0.prql b/book/tests/prql/transforms/aggregate-0.prql deleted file mode 100644 index 12e9ff298a1a..000000000000 --- a/book/tests/prql/transforms/aggregate-0.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -aggregate [ - average salary, - ct = count -] diff --git a/book/tests/prql/transforms/aggregate-1.prql b/book/tests/prql/transforms/aggregate-1.prql deleted file mode 100644 index f655f4637971..000000000000 --- a/book/tests/prql/transforms/aggregate-1.prql +++ /dev/null @@ -1,7 +0,0 @@ -from employees -group [title, country] ( - aggregate [ - average salary, - ct = count - ] -) diff --git a/book/tests/prql/transforms/aggregate-2.prql b/book/tests/prql/transforms/aggregate-2.prql deleted file mode 100644 index d3276634b6af..000000000000 --- a/book/tests/prql/transforms/aggregate-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -derive [avg_sal = average salary] diff --git a/book/tests/prql/transforms/append-0.prql b/book/tests/prql/transforms/append-0.prql deleted file mode 100644 index ecadad3ed3d0..000000000000 --- a/book/tests/prql/transforms/append-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees_1 -append employees_2 diff --git a/book/tests/prql/transforms/append-1.prql b/book/tests/prql/transforms/append-1.prql deleted file mode 100644 index 427597e3dbad..000000000000 --- a/book/tests/prql/transforms/append-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees_1 -remove employees_2 diff --git a/book/tests/prql/transforms/append-2.prql b/book/tests/prql/transforms/append-2.prql deleted file mode 100644 index 08c142df97fe..000000000000 --- a/book/tests/prql/transforms/append-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees_1 -intersect employees_2 diff --git a/book/tests/prql/transforms/derive-0.prql b/book/tests/prql/transforms/derive-0.prql deleted file mode 100644 index 0ca92150a0a9..000000000000 --- a/book/tests/prql/transforms/derive-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -derive gross_salary = salary + payroll_tax diff --git a/book/tests/prql/transforms/derive-1.prql b/book/tests/prql/transforms/derive-1.prql deleted file mode 100644 index f49937a6bfc6..000000000000 --- a/book/tests/prql/transforms/derive-1.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -derive [ - gross_salary = salary + payroll_tax, - gross_cost = gross_salary + benefits_cost -] diff --git a/book/tests/prql/transforms/filter-0.prql b/book/tests/prql/transforms/filter-0.prql deleted file mode 100644 index 6ecce44d63af..000000000000 --- a/book/tests/prql/transforms/filter-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -filter age > 25 diff --git a/book/tests/prql/transforms/filter-1.prql b/book/tests/prql/transforms/filter-1.prql deleted file mode 100644 index 60ca713cdfe8..000000000000 --- a/book/tests/prql/transforms/filter-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -filter (age > 25 or department != "IT") diff --git a/book/tests/prql/transforms/filter-2.prql b/book/tests/prql/transforms/filter-2.prql deleted file mode 100644 index fe81ad79a5fb..000000000000 --- a/book/tests/prql/transforms/filter-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -filter (age | in 25..40) diff --git a/book/tests/prql/transforms/from-0.prql b/book/tests/prql/transforms/from-0.prql deleted file mode 100644 index 782143755f3b..000000000000 --- a/book/tests/prql/transforms/from-0.prql +++ /dev/null @@ -1 +0,0 @@ -from employees diff --git a/book/tests/prql/transforms/from-1.prql b/book/tests/prql/transforms/from-1.prql deleted file mode 100644 index f9ec0ba68c54..000000000000 --- a/book/tests/prql/transforms/from-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from e = employees -select e.first_name diff --git a/book/tests/prql/transforms/from_text-0.prql b/book/tests/prql/transforms/from_text-0.prql deleted file mode 100644 index cc3dd3464e80..000000000000 --- a/book/tests/prql/transforms/from_text-0.prql +++ /dev/null @@ -1,9 +0,0 @@ -from_text """ -a,b,c -1,2,3 -4,5,6 -""" -derive [ - d = b + c, - answer = 20 * 2 + 2, -] diff --git a/book/tests/prql/transforms/from_text-1.prql b/book/tests/prql/transforms/from_text-1.prql deleted file mode 100644 index 588486d405c0..000000000000 --- a/book/tests/prql/transforms/from_text-1.prql +++ /dev/null @@ -1,10 +0,0 @@ -let temp_format_lookup = from_text format:csv """ -country_code,format -uk,C -us,F -lr,F -de,C -""" - -from temperatures -join temp_format_lookup [==country_code] diff --git a/book/tests/prql/transforms/from_text-2.prql b/book/tests/prql/transforms/from_text-2.prql deleted file mode 100644 index be9c55da39b9..000000000000 --- a/book/tests/prql/transforms/from_text-2.prql +++ /dev/null @@ -1,16 +0,0 @@ -let x = from_text format:json """{ - "columns": ["a", "b", "c"], - "data": [ - [1, "x", false], - [4, "y", null] - ] -}""" - -let y = from_text format:json """ - [ - {"a": 1, "m": "5"}, - {"a": 4, "n": "6"} - ] -""" - -from x | join y [==a] diff --git a/book/tests/prql/transforms/group-0.prql b/book/tests/prql/transforms/group-0.prql deleted file mode 100644 index f655f4637971..000000000000 --- a/book/tests/prql/transforms/group-0.prql +++ /dev/null @@ -1,7 +0,0 @@ -from employees -group [title, country] ( - aggregate [ - average salary, - ct = count - ] -) diff --git a/book/tests/prql/transforms/group-1.prql b/book/tests/prql/transforms/group-1.prql deleted file mode 100644 index 1d1d99b09ba3..000000000000 --- a/book/tests/prql/transforms/group-1.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -sort join_date -take 1 diff --git a/book/tests/prql/transforms/group-2.prql b/book/tests/prql/transforms/group-2.prql deleted file mode 100644 index 47024bedc68e..000000000000 --- a/book/tests/prql/transforms/group-2.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -group role ( - sort join_date # taken from above - take 1 -) diff --git a/book/tests/prql/transforms/join-0.prql b/book/tests/prql/transforms/join-0.prql deleted file mode 100644 index df05df8dbed5..000000000000 --- a/book/tests/prql/transforms/join-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -join side:left positions [employees.id==positions.employee_id] diff --git a/book/tests/prql/transforms/join-1.prql b/book/tests/prql/transforms/join-1.prql deleted file mode 100644 index e074ffd3e034..000000000000 --- a/book/tests/prql/transforms/join-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -join side:left p=positions [employees.id==p.employee_id] diff --git a/book/tests/prql/transforms/join-2.prql b/book/tests/prql/transforms/join-2.prql deleted file mode 100644 index 37c18f95467b..000000000000 --- a/book/tests/prql/transforms/join-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -join positions [==emp_no] diff --git a/book/tests/prql/transforms/select-0.prql b/book/tests/prql/transforms/select-0.prql deleted file mode 100644 index 69ff35c8a5b6..000000000000 --- a/book/tests/prql/transforms/select-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select name = f"{first_name} {last_name}" diff --git a/book/tests/prql/transforms/select-1.prql b/book/tests/prql/transforms/select-1.prql deleted file mode 100644 index 346904e522e0..000000000000 --- a/book/tests/prql/transforms/select-1.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -select [ - name = f"{first_name} {last_name}", - age_eoy = dob - @2022-12-31, -] diff --git a/book/tests/prql/transforms/select-2.prql b/book/tests/prql/transforms/select-2.prql deleted file mode 100644 index ec44dd3366e8..000000000000 --- a/book/tests/prql/transforms/select-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -select first_name diff --git a/book/tests/prql/transforms/select-3.prql b/book/tests/prql/transforms/select-3.prql deleted file mode 100644 index d0f3f99260cb..000000000000 --- a/book/tests/prql/transforms/select-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from e=employees -select [e.first_name, e.last_name] diff --git a/book/tests/prql/transforms/select-4.prql b/book/tests/prql/transforms/select-4.prql deleted file mode 100644 index a473a7832655..000000000000 --- a/book/tests/prql/transforms/select-4.prql +++ /dev/null @@ -1,3 +0,0 @@ -prql target:sql.bigquery -from tracks -select ![milliseconds,bytes] diff --git a/book/tests/prql/transforms/select-5.prql b/book/tests/prql/transforms/select-5.prql deleted file mode 100644 index 06d8486c28a7..000000000000 --- a/book/tests/prql/transforms/select-5.prql +++ /dev/null @@ -1,3 +0,0 @@ -from tracks -select [track_id, title, composer, bytes] -select ![title, composer] diff --git a/book/tests/prql/transforms/select-6.prql b/book/tests/prql/transforms/select-6.prql deleted file mode 100644 index 78a496224d09..000000000000 --- a/book/tests/prql/transforms/select-6.prql +++ /dev/null @@ -1,3 +0,0 @@ -from artists -derive nick = name -select ![artists.*] diff --git a/book/tests/prql/transforms/sort-0.prql b/book/tests/prql/transforms/sort-0.prql deleted file mode 100644 index e019af01c2eb..000000000000 --- a/book/tests/prql/transforms/sort-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -sort age diff --git a/book/tests/prql/transforms/sort-1.prql b/book/tests/prql/transforms/sort-1.prql deleted file mode 100644 index a5b15d8d0fbf..000000000000 --- a/book/tests/prql/transforms/sort-1.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -sort [-age] diff --git a/book/tests/prql/transforms/sort-2.prql b/book/tests/prql/transforms/sort-2.prql deleted file mode 100644 index 7809593be39c..000000000000 --- a/book/tests/prql/transforms/sort-2.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -sort [age, -tenure, +salary] diff --git a/book/tests/prql/transforms/sort-3.prql b/book/tests/prql/transforms/sort-3.prql deleted file mode 100644 index c4c9bbd5e7a4..000000000000 --- a/book/tests/prql/transforms/sort-3.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -sort [s"substr({first_name}, 2, 5)"] diff --git a/book/tests/prql/transforms/sort-4.prql b/book/tests/prql/transforms/sort-4.prql deleted file mode 100644 index 7f4df2d22a48..000000000000 --- a/book/tests/prql/transforms/sort-4.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -sort tenure -derive name = f"{first_name} {last_name}" diff --git a/book/tests/prql/transforms/sort-5.prql b/book/tests/prql/transforms/sort-5.prql deleted file mode 100644 index 812dcdc7bdbe..000000000000 --- a/book/tests/prql/transforms/sort-5.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -sort tenure -join locations [==employee_id] diff --git a/book/tests/prql/transforms/take-0.prql b/book/tests/prql/transforms/take-0.prql deleted file mode 100644 index 63d11c6b23f7..000000000000 --- a/book/tests/prql/transforms/take-0.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -take 10 diff --git a/book/tests/prql/transforms/take-1.prql b/book/tests/prql/transforms/take-1.prql deleted file mode 100644 index f6939b843a7c..000000000000 --- a/book/tests/prql/transforms/take-1.prql +++ /dev/null @@ -1,3 +0,0 @@ -from orders -sort [-value, date] -take 101..110 diff --git a/book/tests/prql/transforms/window-0.prql b/book/tests/prql/transforms/window-0.prql deleted file mode 100644 index 6259783650c6..000000000000 --- a/book/tests/prql/transforms/window-0.prql +++ /dev/null @@ -1,7 +0,0 @@ -from employees -group employee_id ( - sort month - window rolling:12 ( - derive [trail_12_m_comp = sum paycheck] - ) -) diff --git a/book/tests/prql/transforms/window-1.prql b/book/tests/prql/transforms/window-1.prql deleted file mode 100644 index 551a99f49293..000000000000 --- a/book/tests/prql/transforms/window-1.prql +++ /dev/null @@ -1,11 +0,0 @@ -from orders -sort day -window rows:-3..3 ( - derive [centered_weekly_average = average value] -) -group [order_month] ( - sort day - window expanding:true ( - derive [monthly_running_total = sum value] - ) -) diff --git a/book/tests/prql/transforms/window-2.prql b/book/tests/prql/transforms/window-2.prql deleted file mode 100644 index 0093d51081cf..000000000000 --- a/book/tests/prql/transforms/window-2.prql +++ /dev/null @@ -1,3 +0,0 @@ -from employees -sort age -derive rnk = rank diff --git a/book/tests/prql/transforms/window-3.prql b/book/tests/prql/transforms/window-3.prql deleted file mode 100644 index ed779ac389a2..000000000000 --- a/book/tests/prql/transforms/window-3.prql +++ /dev/null @@ -1,5 +0,0 @@ -from employees -group department ( - sort age - derive rnk = rank -) diff --git a/book/tests/prql/transforms/window-4.prql b/book/tests/prql/transforms/window-4.prql deleted file mode 100644 index a28ee16df128..000000000000 --- a/book/tests/prql/transforms/window-4.prql +++ /dev/null @@ -1,2 +0,0 @@ -from employees -filter salary < (average salary) diff --git a/book/tests/snapshot.rs b/book/tests/snapshot.rs deleted file mode 100644 index e86b59ae20fc..000000000000 --- a/book/tests/snapshot.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![cfg(not(target_family = "wasm"))] -/// This test: -/// - Extracts PRQL code blocks into the `examples` path. -/// - Converts them to SQL using insta, raising an error if there's a diff. -/// - Replaces the PRQL code block with a comparison table. -/// -/// We also use this test to run tests on our Display trait output, currently as -/// another set of snapshots (more comments inline). -// -// Overall, this is overengineered โ€”ย it's complicated and took a long time to -// write. The intention is good โ€” have a version of the SQL that's committed -// into the repo, and join our tests with our docs. But it feels like overly -// custom code for quite a general problem, even if our preferences are slightly -// different from the general case. -// -// Possibly we should be using something like pandoc / -// https://github.com/gpoore/codebraid / which would run the transformation for -// us. They introduce a bunch of non-rust dependencies, which is not ideal, but -// passable. They don't let us customize our formatting (e.g. in a table). -// -use anyhow::{bail, Result}; -use globset::Glob; -use insta::{assert_display_snapshot, assert_snapshot, glob}; -use log::warn; -use prql_compiler::*; -use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag}; -use std::fs; -use std::path::Path; -use walkdir::WalkDir; - -#[test] -fn run_examples() -> Result<()> { - // TODO: In CI this could pass by replacing incorrect files. To catch that, - // we could check if there are any diffs after this has run? - - // Note that on windows, markdown is read differently, and so - // writing on Windows. ref https://github.com/PRQL/prql/issues/356 - #[cfg(not(target_family = "windows"))] - write_reference_prql()?; - run_reference_prql(); - - // TODO: Currently we run this in the same test, since we need the - // `write_reference_prql` function to have been run. If we could iterate - // over the PRQL examples without writing them to disk, we could run this as - // a separate test. (Though then we'd lose the deferred failures feature - // that insta's `glob!` macro provides.) - run_display_reference_prql(); - - Ok(()) -} - -/// Extract reference examples from the PRQL docs and write them to the -/// `tests/prql` path, one in each file. -// We could alternatively have used something like -// https://github.com/earldouglas/codedown, but it's not much code, and it -// requires no dependencies. -// -// We allow dead_code because of the window issue described above. (Can we allow -// it only for windows?) -#[allow(dead_code)] -fn write_reference_prql() -> Result<()> { - // Remove old .prql files, since we're going to rewrite them, and we don't want - // old files which wouldn't be rewritten from hanging around. - // We use `trash`, since we don't want to be removing files with test code - // in case there's a bug. - // - // A more elegant approach would be to keep a list of the files and remove - // the ones we don't write. - - let examples_path = Path::new("tests/prql"); - if examples_path.exists() { - trash::delete(Path::new("tests/prql")).unwrap_or_else(|e| { - warn!("Failed to delete old examples: {}", e); - }); - } - - let glob = Glob::new("**/*.md")?.compile_matcher(); - - WalkDir::new(Path::new("./src/")) - .into_iter() - .flatten() - .filter(|x| glob.is_match(x.path())) - .try_for_each(|dir_entry| { - let text = fs::read_to_string(dir_entry.path())?; - let mut parser = Parser::new(&text); - let mut prql_blocks = vec![]; - while let Some(event) = parser.next() { - match event.clone() { - // At the start of a PRQL code block, push the _next_ item. - // Note that on windows, we only get the next _line_, and so - // we exclude the writing in windows below; - // https://github.com/PRQL/prql/issues/356 - Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) - if lang == "prql".into() => - { - if let Some(Event::Text(text)) = parser.next() { - prql_blocks.push(text); - } else { - bail!("Expected text after PRQL code block"); - } - } - _ => {} - } - } - - // Write each one to a new file. - prql_blocks - .iter() - .enumerate() - .try_for_each(|(i, example)| { - let file_relative = &dir_entry - .path() - .strip_prefix("./src/")? - .to_str() - .unwrap() - .trim_end_matches(".md"); - let prql_path = format!("tests/prql/{file_relative}-{i}.prql"); - - fs::create_dir_all(Path::new(&prql_path).parent().unwrap())?; - fs::write(prql_path, example.to_string())?; - - Ok::<(), anyhow::Error>(()) - })?; - Ok(()) - })?; - - Ok(()) -} - -/// Snapshot the output of each example. -fn run_reference_prql() { - glob!("prql/**/*.prql", |path| { - let prql = fs::read_to_string(path).unwrap(); - - if prql.contains("skip_test") { - return; - } - - let opts = Options::default().no_signature(); - let sql = compile(&prql, opts).unwrap_or_else(|e| format!("{prql}\n\n{e}")); - // `glob!` gives us the file path in the test name anyway, so we pass an - // empty name. We pass `&prql` so the prql is in the snapshot (albeit in - // a single line, and, in the rare case that the SQL doesn't change, the - // PRQL only updates on running cargo insta with `--force-update-snapshots`). - assert_snapshot!("", &sql, &prql); - }); -} - -/// Snapshot the display trait output of each example. -// Currently not a separate test, see notes in caller. -// -// TODO: this involves writing out almost the same PRQL again โ€” instead we could -// compare the output of Display to the auto-formatted source. But we need an -// autoformatter for that (unless we want to raise on any non-matching input, -// which seems very strict) -fn run_display_reference_prql() { - glob!("prql/**/*.prql", |path| { - let prql = fs::read_to_string(path).unwrap(); - - if prql.contains("skip_test") { - return; - } - - assert_display_snapshot!(prql_to_pl(&prql).and_then(pl_to_prql).unwrap()); - }); -} diff --git a/book/tests/snapshots/snapshot__@examples__cte-0.prql.snap b/book/tests/snapshots/snapshot__@examples__cte-0.prql.snap deleted file mode 100644 index 5e55e6c27d2b..000000000000 --- a/book/tests/snapshots/snapshot__@examples__cte-0.prql.snap +++ /dev/null @@ -1,31 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "table newest_employees = (\n from employees\n sort tenure\n take 50\n)\n\ntable average_salaries = (\n from salaries\n group country (\n aggregate average_country_salary = (average salary)\n )\n)\n\nfrom newest_employees\njoin average_salaries [==country]\nselect [name, salary, average_country_salary]\n" -input_file: book/tests/prql/examples/cte-0.prql ---- -WITH average_salaries AS ( - SELECT - country, - AVG(salary) AS average_country_salary - FROM - salaries - GROUP BY - country -), -newest_employees AS ( - SELECT - * - FROM - employees - ORDER BY - tenure - LIMIT - 50 -) -SELECT - newest_employees.name, - newest_employees.salary, - average_salaries.average_country_salary -FROM - newest_employees - JOIN average_salaries ON newest_employees.country = average_salaries.country diff --git a/book/tests/snapshots/snapshot__@examples__employees-3.prql.snap b/book/tests/snapshots/snapshot__@examples__employees-3.prql.snap deleted file mode 100644 index fa439fbf23c0..000000000000 --- a/book/tests/snapshots/snapshot__@examples__employees-3.prql.snap +++ /dev/null @@ -1,27 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from de=dept_emp\njoin s=salaries side:left [\n (s.emp_no == de.emp_no),\n s\"({s.from_date}, {s.to_date}) OVERLAPS ({de.from_date}, {de.to_date})\"\n]\ngroup [de.emp_no, de.dept_no] (\n aggregate salary = (average s.salary)\n)\njoin employees [==emp_no]\njoin titles [==emp_no]\nselect [dept_no, salary, employees.gender, titles.title]\n" -input_file: book/tests/prql/examples/employees-3.prql ---- -WITH table_1 AS ( - SELECT - de.dept_no, - AVG(s.salary) AS salary, - de.emp_no - FROM - dept_emp AS de - LEFT JOIN salaries AS s ON s.emp_no = de.emp_no - AND (s.from_date, s.to_date) OVERLAPS (de.from_date, de.to_date) - GROUP BY - de.emp_no, - de.dept_no -) -SELECT - table_1.dept_no, - table_1.salary, - employees.gender, - titles.title -FROM - table_1 - JOIN employees ON table_1.emp_no = employees.emp_no - JOIN titles ON table_1.emp_no = titles.emp_no diff --git a/book/tests/snapshots/snapshot__@examples__misc-0.prql.snap b/book/tests/snapshots/snapshot__@examples__misc-0.prql.snap deleted file mode 100644 index b12126c24dd3..000000000000 --- a/book/tests/snapshots/snapshot__@examples__misc-0.prql.snap +++ /dev/null @@ -1,40 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "# TODO: this table should have a column `part` with values 1..5,\n# but such data declaration is not yet supported, see #286\ntable parts = (\n from seq_1_to_5\n)\n\nfrom pl=prospect_lists_prospects\nfilter prospect_list_id == 'cc675eee-8bd1-237f-be5e-622ba511d65e'\njoin a=accounts [a.id == pl.related_id]\njoin er=email_addr_bean_rel [er.bean_id == a.id and er.primary_address == '1']\njoin ea=email_addresses [ea.id == er.email_address_id]\nselect ea.email_address\nderive prefix = s\"regexp_replace(SUBSTRING_INDEX({email_address}, '@', 1), '[.0-9-_:]+', '.')\"\nderive stub = s\"SUBSTRING_INDEX(SUBSTRING_INDEX({prefix}, '.', part), '.', -1)\"\nselect [email_address, stub]\n" -input_file: book/tests/prql/examples/misc-0.prql ---- -WITH parts AS ( - SELECT - * - FROM - seq_1_to_5 -), -table_1 AS ( - SELECT - related_id - FROM - prospect_lists_prospects AS pl - WHERE - prospect_list_id = 'cc675eee-8bd1-237f-be5e-622ba511d65e' -) -SELECT - ea.email_address, - SUBSTRING_INDEX( - SUBSTRING_INDEX( - regexp_replace( - SUBSTRING_INDEX(ea.email_address, '@', 1), - '[.0-9-_:]+', - '.' - ), - '.', - part - ), - '.', - -1 - ) AS stub -FROM - table_1 - JOIN accounts AS a ON a.id = table_1.related_id - JOIN email_addr_bean_rel AS er ON er.bean_id = a.id - AND er.primary_address = '1' - JOIN email_addresses AS ea ON ea.id = er.email_address_id diff --git a/book/tests/snapshots/snapshot__@language-features__dates-and-times-2.prql.snap b/book/tests/snapshots/snapshot__@language-features__dates-and-times-2.prql.snap deleted file mode 100644 index c7886ddfa16a..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__dates-and-times-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from commits\nderive first_prql_commit = @2020-01-01T13:19:55-0800\n" -input_file: book/tests/prql/language-features/dates-and-times-2.prql ---- -SELECT - *, - TIMESTAMP '2020-01-01T13:19:55-0800' AS first_prql_commit -FROM - commits diff --git a/book/tests/snapshots/snapshot__@language-features__ranges-0.prql.snap b/book/tests/snapshots/snapshot__@language-features__ranges-0.prql.snap deleted file mode 100644 index 79caec12df4b..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__ranges-0.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from events\nfilter (date | in @1776-07-04..@1787-09-17)\nfilter (magnitude | in 50..100)\nderive is_northern = (latitude | in 0..)\n" -input_file: book/tests/prql/language-features/ranges-0.prql ---- -SELECT - *, - latitude >= 0 AS is_northern -FROM - events -WHERE - date BETWEEN DATE '1776-07-04' AND DATE '1787-09-17' - AND magnitude BETWEEN 50 AND 100 diff --git a/book/tests/snapshots/snapshot__@language-features__ranges-1.prql.snap b/book/tests/snapshots/snapshot__@language-features__ranges-1.prql.snap deleted file mode 100644 index 69b3698f9352..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__ranges-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from orders\nsort [-value, date]\ntake 101..110\n" -input_file: book/tests/prql/language-features/ranges-1.prql ---- -SELECT - * -FROM - orders -ORDER BY - value DESC, - date -LIMIT - 10 OFFSET 100 diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-4.prql.snap b/book/tests/snapshots/snapshot__@language-features__s-strings-4.prql.snap deleted file mode 100644 index 1c28726ce165..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from employees\nderive [\n has_valid_title = s\"regexp_contains(title, '([a-z0-9]*-){{2,}}')\"\n]\n" -input_file: book/tests/prql/language-features/s-strings-4.prql ---- -SELECT - *, - regexp_contains(title, '([a-z0-9]*-){2,}') AS has_valid_title -FROM - employees diff --git a/book/tests/snapshots/snapshot__@language-features__switch-0.prql.snap b/book/tests/snapshots/snapshot__@language-features__switch-0.prql.snap deleted file mode 100644 index 3fc3b672a06b..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__switch-0.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from employees\nderive distance = switch [\n city == \"Calgary\" -> 0,\n city == \"Edmonton\" -> 300,\n]\n" -input_file: book/tests/prql/language-features/switch-0.prql ---- -SELECT - *, - CASE - WHEN city = 'Calgary' THEN 0 - WHEN city = 'Edmonton' THEN 300 - ELSE NULL - END AS distance -FROM - employees diff --git a/book/tests/snapshots/snapshot__@language-features__switch-1.prql.snap b/book/tests/snapshots/snapshot__@language-features__switch-1.prql.snap deleted file mode 100644 index 3ce88aec5690..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__switch-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from employees\nderive distance = switch [\n city == \"Calgary\" -> 0,\n city == \"Edmonton\" -> 300,\n true -> \"Unknown\",\n]\n" -input_file: book/tests/prql/language-features/switch-1.prql ---- -SELECT - *, - CASE - WHEN city = 'Calgary' THEN 0 - WHEN city = 'Edmonton' THEN 300 - ELSE 'Unknown' - END AS distance -FROM - employees diff --git a/book/tests/snapshots/snapshot__@language-features__target-1.prql.snap b/book/tests/snapshots/snapshot__@language-features__target-1.prql.snap deleted file mode 100644 index 83b87715b88d..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__target-1.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "prql target:sql.mssql\n\nfrom employees\nsort age\ntake 10\n" -input_file: book/tests/prql/language-features/target-1.prql ---- -SELECT - TOP (10) * -FROM - employees -ORDER BY - age diff --git a/book/tests/snapshots/snapshot__@language-features__target-2.prql.snap b/book/tests/snapshots/snapshot__@language-features__target-2.prql.snap deleted file mode 100644 index 87aa49f8fbb7..000000000000 --- a/book/tests/snapshots/snapshot__@language-features__target-2.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "prql version:\"0.4\"\n\nfrom employees\n" -input_file: book/tests/prql/language-features/target-2.prql ---- -SELECT - * -FROM - employees diff --git a/book/tests/snapshots/snapshot__@queries__functions-0.prql.snap b/book/tests/snapshots/snapshot__@queries__functions-0.prql.snap deleted file mode 100644 index 332c716ae9d9..000000000000 --- a/book/tests/snapshots/snapshot__@queries__functions-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "func fahrenheit_to_celsius temp -> (temp - 32) / 1.8\n\nfrom cities\nderive temp_c = (fahrenheit_to_celsius temp_f)\n" -input_file: book/tests/prql/queries/functions-0.prql ---- -SELECT - *, - (temp_f - 32) / 1.8 AS temp_c -FROM - cities diff --git a/book/tests/snapshots/snapshot__@queries__functions-1.prql.snap b/book/tests/snapshots/snapshot__@queries__functions-1.prql.snap deleted file mode 100644 index 0aaeb32b5b0d..000000000000 --- a/book/tests/snapshots/snapshot__@queries__functions-1.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "func interp lower:0 higher x -> (x - lower) / (higher - lower)\n\nfrom students\nderive [\n sat_proportion_1 = (interp 1600 sat_score),\n sat_proportion_2 = (interp lower:0 1600 sat_score),\n]\n" -input_file: book/tests/prql/queries/functions-1.prql ---- -SELECT - *, - (sat_score - 0) / 1600 AS sat_proportion_1, - (sat_score - 0) / 1600 AS sat_proportion_2 -FROM - students diff --git a/book/tests/snapshots/snapshot__@queries__functions-2.prql.snap b/book/tests/snapshots/snapshot__@queries__functions-2.prql.snap deleted file mode 100644 index 273eb16a4244..000000000000 --- a/book/tests/snapshots/snapshot__@queries__functions-2.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "func interp lower:0 higher x -> (x - lower) / (higher - lower)\n\nfrom students\nderive [\n sat_proportion_1 = (sat_score | interp 1600),\n sat_proportion_2 = (sat_score | interp lower:0 1600),\n]\n" -input_file: book/tests/prql/queries/functions-2.prql ---- -SELECT - *, - (sat_score - 0) / 1600 AS sat_proportion_1, - (sat_score - 0) / 1600 AS sat_proportion_2 -FROM - students diff --git a/book/tests/snapshots/snapshot__@queries__functions-4.prql.snap b/book/tests/snapshots/snapshot__@queries__functions-4.prql.snap deleted file mode 100644 index 86c5ba85d58a..000000000000 --- a/book/tests/snapshots/snapshot__@queries__functions-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "func fahrenheit_to_celsius temp -> (temp - 32) / 1.8\nfunc interp lower:0 higher x -> (x - lower) / (higher - lower)\n\nfrom kettles\nderive boiling_proportion = (temp_c | fahrenheit_to_celsius | interp 100)\n" -input_file: book/tests/prql/queries/functions-4.prql ---- -SELECT - *, - ((temp_c - 32) / 1.8 - 0) / 100 AS boiling_proportion -FROM - kettles diff --git a/book/tests/snapshots/snapshot__@syntax-6.prql.snap b/book/tests/snapshots/snapshot__@syntax-6.prql.snap deleted file mode 100644 index 916f3d4cbe85..000000000000 --- a/book/tests/snapshots/snapshot__@syntax-6.prql.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from employees\n# Requires parentheses, because it's contains a pipe\nderive is_proximate = (distance | in 0..20)\n# Requires parentheses, because it's a function call\nderive total_distance = (sum distance)\n# `??` doesn't require parentheses, as it's not a function call\nderive min_capped_distance = (min distance ?? 5)\n# No parentheses needed, because no function call\nderive travel_time = distance / 40\n# No inner parentheses needed around `1+1` because no function call\nderive distance_rounded_2_dp = (round 1+1 distance)\nderive [\n # Requires parentheses, because it contains a pipe\n is_far = (distance | in 100..),\n # The left value of the range requires parentheses,\n # because of the minus sign\n is_negative = (distance | in (-100..0)),\n # ...this is equivalent\n is_negative = (distance | in (-100)..0),\n # Doesn't require parentheses, because it's in a list (confusing, see footnote)!\n average_distance = average distance,\n]\n# Requires parentheses because of the minus sign\nsort (-distance)\n# A list is fine too\nsort [-distance]\n" -input_file: book/tests/prql/syntax-6.prql ---- -SELECT - *, - distance BETWEEN 0 AND 20 AS is_proximate, - SUM(distance) OVER () AS total_distance, - MIN(COALESCE(distance, 5)) OVER () AS min_capped_distance, - distance / 40 AS travel_time, - ROUND(distance, 2) AS distance_rounded_2_dp, - distance >= 100 AS is_far, - distance BETWEEN -100 AND 0, - distance BETWEEN -100 AND 0 AS is_negative, - AVG(distance) OVER () AS average_distance -FROM - employees -ORDER BY - distance DESC diff --git a/book/tests/snapshots/snapshot__@transforms__select-4.prql.snap b/book/tests/snapshots/snapshot__@transforms__select-4.prql.snap deleted file mode 100644 index 3419030604ce..000000000000 --- a/book/tests/snapshots/snapshot__@transforms__select-4.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "prql target:sql.bigquery\nfrom tracks\nselect ![milliseconds,bytes]\n" -input_file: book/tests/prql/transforms/select-4.prql ---- -SELECT - * -EXCEPT - (milliseconds, bytes) -FROM - tracks diff --git a/book/tests/snapshots/snapshot__@transforms__select-5.prql.snap b/book/tests/snapshots/snapshot__@transforms__select-5.prql.snap deleted file mode 100644 index 33337b07109a..000000000000 --- a/book/tests/snapshots/snapshot__@transforms__select-5.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from tracks\nselect [track_id, title, composer, bytes]\nselect ![title, composer]\n" -input_file: book/tests/prql/transforms/select-5.prql ---- -SELECT - track_id, - bytes -FROM - tracks diff --git a/book/tests/snapshots/snapshot__@transforms__select-6.prql.snap b/book/tests/snapshots/snapshot__@transforms__select-6.prql.snap deleted file mode 100644 index 7aa04a75c903..000000000000 --- a/book/tests/snapshots/snapshot__@transforms__select-6.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from artists\nderive nick = name\nselect ![artists.*]\n" -input_file: book/tests/prql/transforms/select-6.prql ---- -SELECT - name AS nick -FROM - artists diff --git a/book/tests/snapshots/snapshot__@transforms__sort-3.prql.snap b/book/tests/snapshots/snapshot__@transforms__sort-3.prql.snap deleted file mode 100644 index 7b6e784e1da0..000000000000 --- a/book/tests/snapshots/snapshot__@transforms__sort-3.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from employees\nsort [s\"substr({first_name}, 2, 5)\"]\n" -input_file: book/tests/prql/transforms/sort-3.prql ---- -WITH table_1 AS ( - SELECT - *, - substr(first_name, 2, 5) AS _expr_0 - FROM - employees - ORDER BY - _expr_0 -) -SELECT - * -FROM - table_1 diff --git a/book/tests/snapshots/snapshot__@transforms__take-1.prql.snap b/book/tests/snapshots/snapshot__@transforms__take-1.prql.snap deleted file mode 100644 index 0e739ad8c893..000000000000 --- a/book/tests/snapshots/snapshot__@transforms__take-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: "from orders\nsort [-value, date]\ntake 101..110\n" -input_file: book/tests/prql/transforms/take-1.prql ---- -SELECT - * -FROM - orders -ORDER BY - value DESC, - date -LIMIT - 10 OFFSET 100 diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__cte-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__cte-0.prql.snap deleted file mode 100644 index b246de058091..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__cte-0.prql.snap +++ /dev/null @@ -1,34 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/cte-0.prql ---- -let newest_employees = ( - from employees - sort tenure - take 50 -) - - - -let average_salaries = ( - from salaries - group country ( - aggregate ( - average_country_salary = average salary -) -) -) - - - -from newest_employees -join average_salaries [==country] -select [ - name, - salary, - average_country_salary, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-0.prql.snap deleted file mode 100644 index 692c476512cc..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-0.prql.snap +++ /dev/null @@ -1,26 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/employees-0.prql ---- -from salaries -group [emp_no] ( - aggregate [emp_salary = average salary] -) -join t = titles [==emp_no] -join side:left dept_emp [==emp_no] -group [ - dept_emp.dept_no, - t.title, -] ( - aggregate [avg_salary = average emp_salary] -) -join departments [==dept_no] -select [ - dept_name, - title, - avg_salary, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-1.prql.snap deleted file mode 100644 index 665754bd3fb5..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-1.prql.snap +++ /dev/null @@ -1,33 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/employees-1.prql ---- -from e = employees -join salaries [==emp_no] -group [ - e.emp_no, - e.gender, -] ( - aggregate [emp_salary = average salaries.salary] -) -join side:left de = dept_emp [==emp_no] -group [ - de.dept_no, - gender, -] ( - aggregate [ - salary_avg = average emp_salary, - salary_sd = stddev emp_salary, -] -) -join departments [==dept_no] -select [ - dept_name, - gender, - salary_avg, - salary_sd, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-2.prql.snap deleted file mode 100644 index 3823173006c7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-2.prql.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/employees-2.prql ---- -from e = employees -join salaries [==emp_no] -group [ - e.emp_no, - e.gender, -] ( - aggregate [emp_salary = average salaries.salary] -) -join de = dept_emp [==emp_no] -join dm = dept_manager [dm.dept_no == de.dept_no and s"(de.from_date, de.to_date) OVERLAPS (dm.from_date, dm.to_date)"] -group [ - dm.emp_no, - gender, -] ( - aggregate [ - salary_avg = average emp_salary, - salary_sd = stddev emp_salary, -] -) -derive mng_no = emp_no -join managers = employees [==emp_no] -derive mng_name = s"managers.first_name || ' ' || managers.last_name" -select [ - mng_name, - managers.gender, - salary_avg, - salary_sd, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-3.prql.snap deleted file mode 100644 index 611769e8f06d..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__employees-3.prql.snap +++ /dev/null @@ -1,29 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/employees-3.prql ---- -from de = dept_emp -join side:left s = salaries [ - s.emp_no == de.emp_no, - s"({s.from_date}, {s.to_date}) OVERLAPS ({de.from_date}, {de.to_date})", -] -group [ - de.emp_no, - de.dept_no, -] ( - aggregate ( - salary = average s.salary -) -) -join employees [==emp_no] -join titles [==emp_no] -select [ - dept_no, - salary, - employees.gender, - titles.title, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-0.prql.snap deleted file mode 100644 index 085bada98319..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/list-equivalence-0.prql ---- -from employees -select salary - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-1.prql.snap deleted file mode 100644 index 6ab6d90cdd10..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/list-equivalence-1.prql ---- -from employees -select [salary] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-2.prql.snap deleted file mode 100644 index 5ba483c522d7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-2.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/list-equivalence-2.prql ---- -from employees -derive [ - gross_salary = salary + payroll_tax, - gross_cost = gross_salary + benefits_cost, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-3.prql.snap deleted file mode 100644 index c1c8e138f806..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__list-equivalence-3.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/list-equivalence-3.prql ---- -from employees -derive gross_salary = salary + payroll_tax -derive gross_cost = gross_salary + benefits_cost - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-0.prql.snap deleted file mode 100644 index 930d29f6d477..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-0.prql.snap +++ /dev/null @@ -1,26 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/misc-0.prql ---- -let parts = ( - from seq_1_to_5 -) - - - -from pl = prospect_lists_prospects -filter prospect_list_id == "cc675eee-8bd1-237f-be5e-622ba511d65e" -join a = accounts [a.id == pl.related_id] -join er = email_addr_bean_rel [er.bean_id == a.id and er.primary_address == "1"] -join ea = email_addresses [ea.id == er.email_address_id] -select ea.email_address -derive prefix = s"regexp_replace(SUBSTRING_INDEX({email_address}, '@', 1), '[.0-9-_:]+', '.')" -derive stub = s"SUBSTRING_INDEX(SUBSTRING_INDEX({prefix}, '.', part), '.', -1)" -select [ - email_address, - stub, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-1.prql.snap deleted file mode 100644 index d48e3fe1307a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__misc-1.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/misc-1.prql ---- -from club_ratings -filter rating != null -group year ( - derive [rating_norm = rating - ( average rating ) / ( stddev rating )] -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-0.prql.snap deleted file mode 100644 index c4e1df527811..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-0.prql.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/variables-0.prql ---- -from employees -filter country == "USA" -derive [ - gross_salary = salary + payroll_tax, - gross_cost = gross_salary + benefits_cost, -] -filter gross_cost > 0 -group [ - title, - country, -] ( - aggregate [ - average salary, - average gross_salary, - sum salary, - sum gross_salary, - average gross_cost, - sum_gross_cost = sum gross_cost, - ct = count, -] -) -sort sum_gross_cost -filter ct > 200 -take 20 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-1.prql.snap deleted file mode 100644 index 1f588bf0d309..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@examples__variables-1.prql.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/examples/variables-1.prql ---- -from employees -group [emp_no] ( - aggregate [emp_salary = average salary] -) -join titles [==emp_no] -group [title] ( - aggregate [avg_salary = average emp_salary] -) -select salary_k = avg_salary / 1000 -take 10 -derive salary = salary_k * 1000 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-0.prql.snap deleted file mode 100644 index 96d5d50a00e1..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-0.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/functional-lang-0.prql ---- -from employees -filter age > 50 -sort name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-1.prql.snap deleted file mode 100644 index ae83420f23d3..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-1.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/functional-lang-1.prql ---- -from employees -filter age > 50 -sort name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-2.prql.snap deleted file mode 100644 index f15a84e195b3..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-2.prql.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/functional-lang-2.prql ---- -filter age > 50 ( - from employees -) -sort name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-3.prql.snap deleted file mode 100644 index aa777d923706..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__functional-lang-3.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/functional-lang-3.prql ---- -sort name ( - filter age > 50 ( - from employees -) -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-0.prql.snap deleted file mode 100644 index a7bd8bc3730a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/name-resolving-0.prql ---- -from employees -select first_name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-1.prql.snap deleted file mode 100644 index 6161fd5892e6..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@internals__name-resolving-1.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/internals/name-resolving-1.prql ---- -from employees -derive [ - first_name, - dept_id, -] -join d = departments [==dept_id] -select [ - first_name, - d.title, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@introduction-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@introduction-0.prql.snap deleted file mode 100644 index bc40a390e21a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@introduction-0.prql.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/introduction-0.prql ---- -from employees -filter start_date > @2021-01-01 -derive [ - gross_salary = salary + tax ?? 0, - gross_cost = gross_salary + benefits_cost, -] -filter gross_cost > 0 -group [ - title, - country, -] ( - aggregate [ - average gross_salary, - sum_gross_cost = sum gross_cost, -] -) -filter sum_gross_cost > 100000 -derive id = f"{title}_{country}" -derive country_code = s"LEFT(country, 2)" -sort [ - sum_gross_cost, - -country, -] -take 1..20 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__coalesce-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__coalesce-0.prql.snap deleted file mode 100644 index 16833c5f8cfa..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__coalesce-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/coalesce-0.prql ---- -from orders -derive amount ?? 0 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-0.prql.snap deleted file mode 100644 index ca08e6d69631..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/dates-and-times-0.prql ---- -from employees -derive age_at_year_end = @2022-12-31 - dob - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-1.prql.snap deleted file mode 100644 index 07d9b568eac6..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/dates-and-times-1.prql ---- -from orders -derive should_have_shipped_today = order_time < @08:30 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-2.prql.snap deleted file mode 100644 index 6e0b688f7f7a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/dates-and-times-2.prql ---- -from commits -derive first_prql_commit = @2020-01-01T13:19:55-0800 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-3.prql.snap deleted file mode 100644 index 5080f647a12c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__dates-and-times-3.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/dates-and-times-3.prql ---- -from projects -derive first_check_in = start + 10days - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-0.prql.snap deleted file mode 100644 index 587f75ff1bd2..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-0.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/distinct-0.prql ---- -from employees -select department -group department ( - take 1 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-1.prql.snap deleted file mode 100644 index d6bcee7acf86..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-1.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/distinct-1.prql ---- -from employees -select department -group department ( - take 1 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-2.prql.snap deleted file mode 100644 index 4f2e5f381a88..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__distinct-2.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/distinct-2.prql ---- -from employees -group department ( - sort age - take 1 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-0.prql.snap deleted file mode 100644 index 2b8a822ba481..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/f-strings-0.prql ---- -from employees -select full_name = f"{first_name} {last_name}" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-1.prql.snap deleted file mode 100644 index b61f8780aa34..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__f-strings-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/f-strings-1.prql ---- -from web -select url = f"http{tls}://www.{domain}.{tld}/{page}" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__null-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__null-0.prql.snap deleted file mode 100644 index 016809741efe..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__null-0.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/null-0.prql ---- -from employees -filter first_name == null -filter null != last_name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-0.prql.snap deleted file mode 100644 index e20b2b97240c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-0.prql.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/ranges-0.prql ---- -from events -filter ( - date - in @1776-07-04..@1787-09-17 -) -filter ( - magnitude - in 50..100 -) -derive is_northern = ( - latitude - in 0.. -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-1.prql.snap deleted file mode 100644 index 46ce8b767eb3..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__ranges-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/ranges-1.prql ---- -from orders -sort [ - -value, - date, -] -take 101..110 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-0.prql.snap deleted file mode 100644 index fa9c95fb9f4c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-0.prql ---- -from my_table -select db_version = s"version()" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-1.prql.snap deleted file mode 100644 index 7de6990c149c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-1.prql ---- -from employees -aggregate [average salary] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-2.prql.snap deleted file mode 100644 index 8d39feed0f0d..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-2.prql.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-2.prql ---- -from de = dept_emp -join side:left s = salaries [ - s.emp_no == de.emp_no, - s"({s.from_date}, {s.to_date}) - OVERLAPS - ({de.from_date}, {de.to_date})", -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-3.prql.snap deleted file mode 100644 index 85661196009c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-3.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-3.prql ---- -from s"SELECT DISTINCT ON first_name, id, age FROM employees ORDER BY age ASC" -join s = s"SELECT * FROM salaries" [==id] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-4.prql.snap deleted file mode 100644 index d7482d2f4cfb..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-4.prql ---- -from employees -derive [has_valid_title = s"regexp_contains(title, '([a-z0-9]*-){2,}')"] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-5.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-5.prql.snap deleted file mode 100644 index 4313e1e554a7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-5.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-5.prql ---- -from employees -derive [ - gross_salary = salary + benefits, - daily_rate = s"{gross_salary} / 365", -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-6.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-6.prql.snap deleted file mode 100644 index 8ac94b9d968f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__s-strings-6.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/s-strings-6.prql ---- -from employees -derive [ - gross_salary = salary + benefits, - daily_rate = s"({gross_salary}) / 365", -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__standard-library-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__standard-library-0.prql.snap deleted file mode 100644 index 691cabab4a62..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__standard-library-0.prql.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/standard-library-0.prql ---- -from employees -derive [ - gross_salary = ( - salary + payroll_tax - as int -), - gross_salary_rounded = ( - gross_salary - round 0 -), - time = s"NOW()", -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-0.prql.snap deleted file mode 100644 index 08b198a45ab3..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/strings-0.prql ---- -from my_table -select x = "hello world" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-1.prql.snap deleted file mode 100644 index 014e0927ce0e..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/strings-1.prql ---- -from my_table -select x = "hello world" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-2.prql.snap deleted file mode 100644 index 52b190fec22a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/strings-2.prql ---- -from my_table -select x = '"hello world"' - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-3.prql.snap deleted file mode 100644 index eddb87ee8834..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-3.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/strings-3.prql ---- -from my_table -select x = 'I said "hello world"!' - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-4.prql.snap deleted file mode 100644 index 5966be260e48..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__strings-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/strings-4.prql ---- -from my_table -select x = 'I said """hello world"""!' - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-0.prql.snap deleted file mode 100644 index 85bb6bac32b8..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-0.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/switch-0.prql ---- -from employees -derive distance = switch [ - city == "Calgary" => 0 - city == "Edmonton" => 300 -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-1.prql.snap deleted file mode 100644 index dd4c64e0b6b0..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__switch-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/switch-1.prql ---- -from employees -derive distance = switch [ - city == "Calgary" => 0 - city == "Edmonton" => 300 - true => "Unknown" -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-0.prql.snap deleted file mode 100644 index 8d46fc69595c..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-0.prql.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/target-0.prql ---- -prql target:sql.postgres - - - -from employees -sort age -take 10 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-1.prql.snap deleted file mode 100644 index 2719c8e16911..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-1.prql.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/target-1.prql ---- -prql target:sql.mssql - - - -from employees -sort age -take 10 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-2.prql.snap deleted file mode 100644 index 5bf77265c6a4..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@language-features__target-2.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/language-features/target-2.prql ---- -prql version:^0.5 - - - -from employees - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-0.prql.snap deleted file mode 100644 index bde459b05cf8..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-0.prql.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/functions-0.prql ---- -func fahrenheit_to_celsius temp -> temp - 32 / 1.8 - - - -from cities -derive ( - temp_c = fahrenheit_to_celsius temp_f -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-1.prql.snap deleted file mode 100644 index d8d3e0185fd9..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-1.prql.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/functions-1.prql ---- -func interp higher x lower:0 -> x - lower / higher - lower - - - -from students -derive [ - sat_proportion_1 = interp 1600 sat_score, - sat_proportion_2 = interp lower:0 1600 sat_score, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-2.prql.snap deleted file mode 100644 index 49560fe48496..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-2.prql.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/functions-2.prql ---- -func interp higher x lower:0 -> x - lower / higher - lower - - - -from students -derive [ - sat_proportion_1 = ( - sat_score - interp 1600 -), - sat_proportion_2 = ( - sat_score - interp lower:0 1600 -), -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-3.prql.snap deleted file mode 100644 index 83e91977fe3f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-3.prql.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/functions-3.prql ---- -func fahrenheit_to_celsius temp -> temp - 32 / 1.8 - - - -from cities -derive temp_c = ( - temp_f - fahrenheit_to_celsius -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-4.prql.snap deleted file mode 100644 index 8ed62cca5158..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__functions-4.prql.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/functions-4.prql ---- -func fahrenheit_to_celsius temp -> temp - 32 / 1.8 - - - -func interp higher x lower:0 -> x - lower / higher - lower - - - -from kettles -derive boiling_proportion = ( - temp_c - fahrenheit_to_celsius - interp 100 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-0.prql.snap deleted file mode 100644 index 7162e861b213..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-0.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/pipelines-0.prql ---- -from employees - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-1.prql.snap deleted file mode 100644 index f64f5d68dc56..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/pipelines-1.prql ---- -from employees -derive gross_salary = salary + payroll_tax - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-2.prql.snap deleted file mode 100644 index e3ff662251a8..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__pipelines-2.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/pipelines-2.prql ---- -from e = employees -derive gross_salary = salary + payroll_tax -sort gross_salary -take 10 -join d = department [==dept_no] -select [ - e.name, - gross_salary, - d.name, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-0.prql.snap deleted file mode 100644 index fbb8c9dfa00f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-0.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/variables-0.prql ---- -let top_50 = ( - from employees - sort salary - take 50 - aggregate [total_salary = sum salary] -) - - - -from top_50 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-1.prql.snap deleted file mode 100644 index f7a3cbcd9431..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@queries__variables-1.prql.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/queries/variables-1.prql ---- -let grouping = s" - SELECT SUM(a) - FROM tbl - GROUP BY - GROUPING SETS - ((b, c, d), (d), (b, d)) -" - - - -from grouping - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-0.prql.snap deleted file mode 100644 index 16788f8c5410..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-0.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-0.prql ---- -from employees -filter department == "Product" -select [ - first_name, - last_name, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-1.prql.snap deleted file mode 100644 index 7d3684118966..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-1.prql ---- -from employees -filter department == "Product" -select [ - first_name, - last_name, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-10.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-10.prql.snap deleted file mode 100644 index e12239744931..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-10.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-10.prql ---- -prql target:sql.postgres - - - -from employees -select `first name` - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-11.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-11.prql.snap deleted file mode 100644 index f719b0acbbd4..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-11.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-11.prql ---- -from `dir/*.parquet` - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-12.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-12.prql.snap deleted file mode 100644 index 79ec4c9aa74d..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-12.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-12.prql ---- -prql target:sql.bigquery - - - -from `project-foo.dataset.table` -join `project-bar.dataset.table` [==col_bax] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-13.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-13.prql.snap deleted file mode 100644 index 6316d2280516..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-13.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-13.prql ---- -from `music.albums` - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-14.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-14.prql.snap deleted file mode 100644 index a38f2f750602..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-14.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-14.prql ---- -from employees -filter id == $1 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-15.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-15.prql.snap deleted file mode 100644 index 34d44267ba17..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-15.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-15.prql ---- -from numbers -select [ - small = 1.0000001, - big = 5000000, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-2.prql.snap deleted file mode 100644 index 8c5b5825e8c4..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-2.prql.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-2.prql ---- -from numbers -derive [ - x = 1, - y = 2, -] -derive [ - a = x, - b = y, -] -derive [ - c = a, - d = b, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-3.prql.snap deleted file mode 100644 index 6820f7c5e628..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-3.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-3.prql ---- -from employees -select [first_name] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-4.prql.snap deleted file mode 100644 index 6e06fe3ee2e5..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-4.prql ---- -from employees -select first_name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-5.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-5.prql.snap deleted file mode 100644 index f3ebf960c4bf..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-5.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-5.prql ---- -from foo -select [ - circumference = diameter * 3.14159, - color, -] -filter circumference > 10 and color != "red" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-6.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-6.prql.snap deleted file mode 100644 index d6668fe305c7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-6.prql.snap +++ /dev/null @@ -1,40 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-6.prql ---- -from employees -derive is_proximate = ( - distance - in 0..20 -) -derive ( - total_distance = sum distance -) -derive ( - min_capped_distance = min distance ?? 5 -) -derive travel_time = distance / 40 -derive ( - distance_rounded_2_dp = round 1 + 1 distance -) -derive [ - is_far = ( - distance - in 100.. -), - is_negative = ( - distance - in -100..0 -), - is_negative = ( - distance - in -100..0 -), - average_distance = average distance, -] -sort -distance -sort [-distance] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-7.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-7.prql.snap deleted file mode 100644 index 29e0c6d26c1e..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-7.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-7.prql ---- -from employees -group [ - title, - country, -] ( - aggregate [ - average salary, - ct = count, -] -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-8.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-8.prql.snap deleted file mode 100644 index 55a51e5d833f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-8.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-8.prql ---- -from employees -aggregate [average salary] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-9.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-9.prql.snap deleted file mode 100644 index 9e5a61c3e446..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@syntax-9.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/syntax-9.prql ---- -prql target:sql.mysql - - - -from employees -select `first name` - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-0.prql.snap deleted file mode 100644 index 89e3271c8316..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-0.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/aggregate-0.prql ---- -from employees -aggregate [ - average salary, - ct = count, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-1.prql.snap deleted file mode 100644 index bbf253ec6dd5..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-1.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/aggregate-1.prql ---- -from employees -group [ - title, - country, -] ( - aggregate [ - average salary, - ct = count, -] -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-2.prql.snap deleted file mode 100644 index 21468ff11aeb..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__aggregate-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/aggregate-2.prql ---- -from employees -derive [avg_sal = average salary] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-0.prql.snap deleted file mode 100644 index 821c0f010d36..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/append-0.prql ---- -from employees_1 -append employees_2 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-1.prql.snap deleted file mode 100644 index 2a61ec1251b1..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/append-1.prql ---- -from employees_1 -remove employees_2 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-2.prql.snap deleted file mode 100644 index dddeb63b7c9f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__append-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/append-2.prql ---- -from employees_1 -intersect employees_2 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-0.prql.snap deleted file mode 100644 index e90d9add3db4..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/derive-0.prql ---- -from employees -derive gross_salary = salary + payroll_tax - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-1.prql.snap deleted file mode 100644 index f2b9dd7bd20d..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__derive-1.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/derive-1.prql ---- -from employees -derive [ - gross_salary = salary + payroll_tax, - gross_cost = gross_salary + benefits_cost, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-0.prql.snap deleted file mode 100644 index 8a68bf997bd7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/filter-0.prql ---- -from employees -filter age > 25 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-1.prql.snap deleted file mode 100644 index 1bc8cf6d4a34..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/filter-1.prql ---- -from employees -filter age > 25 or department != "IT" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-2.prql.snap deleted file mode 100644 index ad800e09070f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__filter-2.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/filter-2.prql ---- -from employees -filter ( - age - in 25..40 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-0.prql.snap deleted file mode 100644 index 0adcf6191d9e..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-0.prql.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/from-0.prql ---- -from employees - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-1.prql.snap deleted file mode 100644 index b90898f6f93e..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/from-1.prql ---- -from e = employees -select e.first_name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-0.prql.snap deleted file mode 100644 index d86f2e1e86bb..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-0.prql.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/from_text-0.prql ---- -from_text " -a,b,c -1,2,3 -4,5,6 -" -derive [ - d = b + c, - answer = 20 * 2 + 2, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-1.prql.snap deleted file mode 100644 index 740349748e00..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-1.prql.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/from_text-1.prql ---- -let temp_format_lookup = ( - from_text format:csv " -country_code,format -uk,C -us,F -lr,F -de,C -" -) - - - -from temperatures -join temp_format_lookup [==country_code] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-2.prql.snap deleted file mode 100644 index a0aaabe15cb7..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__from_text-2.prql.snap +++ /dev/null @@ -1,33 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/from_text-2.prql ---- -let x = ( - from_text format:json '{ - "columns": ["a", "b", "c"], - "data": [ - [1, "x", false], - [4, "y", null] - ] -}' -) - - - -let y = ( - from_text format:json ' - [ - {"a": 1, "m": "5"}, - {"a": 4, "n": "6"} - ] -' -) - - - -from x -join y [==a] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-0.prql.snap deleted file mode 100644 index 754f23c1d76e..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-0.prql.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/group-0.prql ---- -from employees -group [ - title, - country, -] ( - aggregate [ - average salary, - ct = count, -] -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-1.prql.snap deleted file mode 100644 index f916e255f991..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-1.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/group-1.prql ---- -from employees -sort join_date -take 1 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-2.prql.snap deleted file mode 100644 index 6b2cb62586b3..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__group-2.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/group-2.prql ---- -from employees -group role ( - sort join_date - take 1 -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-0.prql.snap deleted file mode 100644 index 0334411b6614..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/join-0.prql ---- -from employees -join side:left positions [employees.id == positions.employee_id] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-1.prql.snap deleted file mode 100644 index 073e5b79b5ee..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/join-1.prql ---- -from employees -join side:left p = positions [employees.id == p.employee_id] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-2.prql.snap deleted file mode 100644 index 19b950c9d24b..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__join-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/join-2.prql ---- -from employees -join positions [==emp_no] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-0.prql.snap deleted file mode 100644 index 9841ec3d8249..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-0.prql ---- -from employees -select name = f"{first_name} {last_name}" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-1.prql.snap deleted file mode 100644 index 1ecfbb0be8f6..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-1.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-1.prql ---- -from employees -select [ - name = f"{first_name} {last_name}", - age_eoy = dob - @2022-12-31, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-2.prql.snap deleted file mode 100644 index 522050c7dd99..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-2.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-2.prql ---- -from employees -select first_name - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-3.prql.snap deleted file mode 100644 index d6596a9ce634..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-3.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-3.prql ---- -from e = employees -select [ - e.first_name, - e.last_name, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-4.prql.snap deleted file mode 100644 index c8837e7a5780..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-4.prql.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-4.prql ---- -prql target:sql.bigquery - - - -from tracks -select not [ - milliseconds, - bytes, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-5.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-5.prql.snap deleted file mode 100644 index ee470cf35cf6..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-5.prql.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-5.prql ---- -from tracks -select [ - track_id, - title, - composer, - bytes, -] -select not [ - title, - composer, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-6.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-6.prql.snap deleted file mode 100644 index 102555240bdb..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__select-6.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/select-6.prql ---- -from artists -derive nick = name -select not [artists.`*`] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-0.prql.snap deleted file mode 100644 index 91b36c10532f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-0.prql ---- -from employees -sort age - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-1.prql.snap deleted file mode 100644 index d7f443472fc9..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-1.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-1.prql ---- -from employees -sort [-age] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-2.prql.snap deleted file mode 100644 index 1df292e0b57f..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-2.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-2.prql ---- -from employees -sort [ - age, - -tenure, - salary, -] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-3.prql.snap deleted file mode 100644 index 3ef53a4676e9..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-3.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-3.prql ---- -from employees -sort [s"substr({first_name}, 2, 5)"] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-4.prql.snap deleted file mode 100644 index 01b5c740c41a..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-4.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-4.prql ---- -from employees -sort tenure -derive name = f"{first_name} {last_name}" - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-5.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-5.prql.snap deleted file mode 100644 index 52f805544586..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__sort-5.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/sort-5.prql ---- -from employees -sort tenure -join locations [==employee_id] - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-0.prql.snap deleted file mode 100644 index 7f798967b647..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-0.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/take-0.prql ---- -from employees -take 10 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-1.prql.snap deleted file mode 100644 index 4107c86bfd92..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__take-1.prql.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/take-1.prql ---- -from orders -sort [ - -value, - date, -] -take 101..110 - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-0.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-0.prql.snap deleted file mode 100644 index 2366506585b0..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-0.prql.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/window-0.prql ---- -from employees -group employee_id ( - sort month - window rolling:12 ( - derive [trail_12_m_comp = sum paycheck] -) -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-1.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-1.prql.snap deleted file mode 100644 index afffeb8eb185..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-1.prql.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/window-1.prql ---- -from orders -sort day -window rows:-3..3 ( - derive [centered_weekly_average = average value] -) -group [order_month] ( - sort day - window expanding:true ( - derive [monthly_running_total = sum value] -) -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-2.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-2.prql.snap deleted file mode 100644 index 3015829b90db..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-2.prql.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/window-2.prql ---- -from employees -sort age -derive rnk = rank - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-3.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-3.prql.snap deleted file mode 100644 index 26a5eb5d9d46..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-3.prql.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/window-3.prql ---- -from employees -group department ( - sort age - derive rnk = rank -) - - - diff --git a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-4.prql.snap b/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-4.prql.snap deleted file mode 100644 index be549656354d..000000000000 --- a/book/tests/snapshots/snapshot__run_display_reference_prql@transforms__window-4.prql.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: book/tests/snapshot.rs -expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap() -input_file: book/tests/prql/transforms/window-4.prql ---- -from employees -filter salary < ( average salary ) - - - diff --git a/book/theme/highlight.js b/book/theme/highlight.js deleted file mode 100644 index 070264e2e698..000000000000 --- a/book/theme/highlight.js +++ /dev/null @@ -1 +0,0 @@ -var hljs=function(){"use strict";var e={exports:{}};function n(e){return e instanceof Map?e.clear=e.delete=e.set=()=>{throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{throw Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{var a=e[t];"object"!=typeof a||Object.isFrozen(a)||n(a)}),e}e.exports=n,e.exports.default=n;var t=e.exports;class a{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function i(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n];return n.forEach(e=>{for(const n in e)t[n]=e[n]}),t}const s=e=>!!e.kind;class o{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=i(e)}openNode(e){if(!s(e))return;let n=e.kind;n=e.sublanguage?"language-"+n:((e,{prefix:n})=>{if(e.includes(".")){const t=e.split(".");return[`${n}${t.shift()}`,...t.map((e,n)=>`${e}${"_".repeat(n+1)}`)].join(" ")}return`${n}${e}`})(n,{prefix:this.classPrefix}),this.span(n)}closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){this.buffer+=``}}class l{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{l._collapse(e)}))}}class c extends l{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new o(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}function g(e){return m("(?=",e,")")}function u(e){return m("(?:",e,")*")}function b(e){return m("(?:",e,")?")}function m(...e){return e.map(e=>d(e)).join("")}function p(...e){const n=(e=>{const n=e[e.length-1];return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}})(e);return"("+(n.capture?"":"?:")+e.map(e=>d(e)).join("|")+")"}function _(e){return RegExp(e.toString()+"|").exec("").length-1}const h=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function f(e,{joinWith:n}){let t=0;return e.map(e=>{t+=1;const n=t;let a=d(e),i="";for(;a.length>0;){const e=h.exec(a);if(!e){i+=a;break}i+=a.substring(0,e.index),a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0],"("===e[0]&&t++)}return i}).map(e=>`(${e})`).join(n)}const E="[a-zA-Z]\\w*",y="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",k={begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[k]},x={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[k]},M=(e,n,t={})=>{const a=r({scope:"comment",begin:e,end:n,contains:[]},t);a.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const i=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return a.contains.push({begin:m(/[ ]+/,"(",i,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),a},S=M("//","$"),A=M("/\\*","\\*/"),C=M("#","$");var T=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:E,UNDERSCORE_IDENT_RE:y,NUMBER_RE:w,C_NUMBER_RE:N,BINARY_NUMBER_RE:v,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=m(n,/.*\b/,e.binary,/\b.*/)),r({scope:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:k,APOS_STRING_MODE:O,QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:{begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},COMMENT:M,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:A,HASH_COMMENT_MODE:C,NUMBER_MODE:{scope:"number",begin:w,relevance:0},C_NUMBER_MODE:{scope:"number",begin:N,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:v,relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[k,{begin:/\[/,end:/\]/,relevance:0,contains:[k]}]}]},TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_TITLE_MODE:{scope:"title",begin:y,relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})});function R(e,n){"."===e.input[e.index-1]&&n.ignoreMatch()}function D(e,n){void 0!==e.className&&(e.scope=e.className,delete e.className)}function I(e,n){n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=R,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function L(e,n){Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function B(e,n){if(e.match){if(e.begin||e.end)throw Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function $(e,n){void 0===e.relevance&&(e.relevance=1)}const z=(e,n)=>{if(!e.beforeMatch)return;if(e.starts)throw Error("beforeMatch cannot be used with starts");const t=Object.assign({},e);Object.keys(e).forEach(n=>{delete e[n]}),e.keywords=t.keywords,e.begin=m(t.beforeMatch,g(t.begin)),e.starts={relevance:0,contains:[Object.assign(t,{endsParent:!0})]},e.relevance=0,delete t.beforeMatch},F=["of","and","for","in","not","or","if","then","parent","list","value"];function U(e,n,t="keyword"){const a=Object.create(null);return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach(t=>{Object.assign(a,U(e[t],n,t))}),a;function i(e,t){n&&(t=t.map(e=>e.toLowerCase())),t.forEach(n=>{const t=n.split("|");a[t[0]]=[e,j(t[0],t[1])]})}}function j(e,n){return n?Number(n):(e=>F.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0)},Z=Error();function G(e,n,{key:t}){let a=0;const i=e[t],r={},s={};for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=_(n[e-1]);e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope}),(e=>{if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),Z;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"),Z;G(e,e.begin,{key:"beginScope"}),e.begin=f(e.begin,{joinWith:""})}})(e),(e=>{if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"),Z;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"),Z;G(e,e.end,{key:"endScope"}),e.end=f(e.end,{joinWith:""})}})(e)}function Q(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=_(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(f(e,{joinWith:"|"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),a=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,a)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;let t=n.exec(e);if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)}return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&this.considerAll()),t}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=r(e.classNameAliases||{}),function t(i,s){const o=i;if(i.isCompiled)return o;[D,B,W,z].forEach(e=>e(i,s)),e.compilerExtensions.forEach(e=>e(i,s)),i.__beforeBegin=null,[I,L,$].forEach(e=>e(i,s)),i.isCompiled=!0;let l=null;return"object"==typeof i.keywords&&i.keywords.$pattern&&(i.keywords=Object.assign({},i.keywords),l=i.keywords.$pattern,delete i.keywords.$pattern),l=l||/\w+/,i.keywords&&(i.keywords=U(i.keywords,e.case_insensitive)),o.keywordPatternRe=n(l,!0),s&&(i.begin||(i.begin=/\B|\b/),o.beginRe=n(o.begin),i.end||i.endsWithParent||(i.end=/\B|\b/),i.end&&(o.endRe=n(o.end)),o.terminatorEnd=d(o.end)||"",i.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(i.end?"|":"")+s.terminatorEnd)),i.illegal&&(o.illegalRe=n(i.illegal)),i.contains||(i.contains=[]),i.contains=[].concat(...i.contains.map(e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(n=>r(e,{variants:null},n))),e.cachedVariants?e.cachedVariants:X(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e))("self"===e?i:e))),i.contains.forEach(e=>{t(e,o)}),i.starts&&t(i.starts,s),o.matcher=(e=>{const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}}const J=i,Y=r,ee=Symbol("nomatch");var ne=(e=>{const n=Object.create(null),i=Object.create(null),r=[];let s=!0;const o="Could not find the language '{}', did you forget to load/include a language module?",l={disableAutodetect:!0,name:"Plain text",contains:[]};let d={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:c};function _(e){return d.noHighlightRe.test(e)}function h(e,n,t){let a="",i="";"object"==typeof n?(a=e,t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."),q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r);const s=r.result?r.result:f(r.language,r.code,t);return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){const l=Object.create(null);function c(){if(!O.keywords)return void M.addText(S);let e=0;O.keywordPatternRe.lastIndex=0;let n=O.keywordPatternRe.exec(S),t="";for(;n;){t+=S.substring(e,n.index);const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,O.keywords[a]);if(r){const[e,a]=r;if(M.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(A+=a),e.startsWith("_"))t+=n[0];else{const t=w.classNameAliases[e]||e;M.addKeyword(n[0],t)}}else t+=n[0];e=O.keywordPatternRe.lastIndex,n=O.keywordPatternRe.exec(S)}var a;t+=S.substr(e),M.addText(t)}function g(){null!=O.subLanguage?(()=>{if(""===S)return;let e=null;if("string"==typeof O.subLanguage){if(!n[O.subLanguage])return void M.addText(S);e=f(O.subLanguage,S,!0,x[O.subLanguage]),x[O.subLanguage]=e._top}else e=E(S,O.subLanguage.length?O.subLanguage:null);O.relevance>0&&(A+=e.relevance),M.addSublanguage(e._emitter,e.language)})():c(),S=""}function u(e,n){let t=1;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue}const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?M.addKeyword(i,a):(S=i,c(),S=""),t++}}function b(e,n){return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(M.addKeyword(S,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),S=""):e.beginScope._multi&&(u(e.beginScope,n),S="")),O=Object.create(e,{parent:{value:O}}),O}function m(e,n,t){let i=((e,n)=>{const t=e&&e.exec(n);return t&&0===t.index})(e.endRe,t);if(i){if(e["on:end"]){const t=new a(e);e["on:end"](n,t),t.isMatchIgnored&&(i=!1)}if(i){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return m(e.parent,n,t)}function p(e){return 0===O.matcher.regexIndex?(S+=e[0],1):(R=!0,0)}function _(e){const n=e[0],a=t.substr(e.index),i=m(O,e,a);if(!i)return ee;const r=O;O.endScope&&O.endScope._wrap?(g(),M.addKeyword(n,O.endScope._wrap)):O.endScope&&O.endScope._multi?(g(),u(O.endScope,e)):r.skip?S+=n:(r.returnEnd||r.excludeEnd||(S+=n),g(),r.excludeEnd&&(S=n));do{O.scope&&M.closeNode(),O.skip||O.subLanguage||(A+=O.relevance),O=O.parent}while(O!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length}let h={};function y(n,r){const o=r&&r[0];if(S+=n,null==o)return g(),0;if("begin"===h.type&&"end"===r.type&&h.index===r.index&&""===o){if(S+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`);throw n.languageName=e,n.badRule=h.rule,n}return 1}if(h=r,"begin"===r.type)return(e=>{const n=e[0],t=e.rule,i=new a(t),r=[t.__beforeBegin,t["on:begin"]];for(const t of r)if(t&&(t(e,i),i.isMatchIgnored))return p(n);return t.skip?S+=n:(t.excludeBegin&&(S+=n),g(),t.returnBegin||t.excludeBegin||(S=n)),b(t,e),t.returnBegin?0:n.length})(r);if("illegal"===r.type&&!i){const e=Error('Illegal lexeme "'+o+'" for mode "'+(O.scope||"")+'"');throw e.mode=O,e}if("end"===r.type){const e=_(r);if(e!==ee)return e}if("illegal"===r.type&&""===o)return 1;if(T>1e5&&T>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return S+=o,o.length}const w=v(e);if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"');const N=Q(w);let k="",O=r||N;const x={},M=new d.__emitter(d);(()=>{const e=[];for(let n=O;n!==w;n=n.parent)n.scope&&e.unshift(n.scope);e.forEach(e=>M.openNode(e))})();let S="",A=0,C=0,T=0,R=!1;try{for(O.matcher.considerAll();;){T++,R?R=!1:O.matcher.considerAll(),O.matcher.lastIndex=C;const e=O.matcher.exec(t);if(!e)break;const n=y(t.substring(C,e.index),e);C=e.index+n}return y(t.substr(C)),M.closeAllNodes(),M.finalize(),k=M.toHTML(),{language:e,value:k,relevance:A,illegal:!1,_emitter:M,_top:O}}catch(n){if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t),illegal:!0,relevance:0,_illegalBy:{message:n.message,index:C,context:t.slice(C-100,C+100),mode:n.mode,resultSoFar:k},_emitter:M};if(s)return{language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:M,_top:O};throw n}}function E(e,t){t=t||d.languages||Object.keys(n);const a=(e=>{const n={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new d.__emitter(d)};return n._emitter.addText(e),n})(e),i=t.filter(v).filter(O).map(n=>f(n,e,!1));i.unshift(a);const r=i.sort((e,n)=>{if(e.relevance!==n.relevance)return n.relevance-e.relevance;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1;if(v(n.language).supersetOf===e.language)return-1}return 0}),[s,o]=r,c=s;return c.secondBest=o,c}function y(e){let n=null;const t=(e=>{let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=d.languageDetectRe.exec(n);if(t){const n=v(t[1]);return n||(H(o.replace("{}",t[1])),H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>_(e)||v(e))})(e);if(_(t))return;if(x("before:highlightElement",{el:e,language:t}),e.children.length>0&&(d.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(e)),d.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML);n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a);e.innerHTML=r.value,((e,n,t)=>{const a=n&&i[n]||t;e.classList.add("hljs"),e.classList.add("language-"+a)})(e,t,r.language),e.result={language:r.language,re:r.relevance,relevance:r.relevance},r.secondBest&&(e.secondBest={language:r.secondBest.language,relevance:r.secondBest.relevance}),x("after:highlightElement",{el:e,result:r,text:a})}let w=!1;function N(){"loading"!==document.readyState?document.querySelectorAll(d.cssSelector).forEach(y):w=!0}function v(e){return e=(e||"").toLowerCase(),n[e]||n[i[e]]}function k(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{i[e.toLowerCase()]=n})}function O(e){const n=v(e);return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach(e=>{e[t]&&e[t](n)})}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",()=>{w&&N()},!1),Object.assign(e,{highlight:h,highlightAuto:E,highlightAll:N,highlightElement:y,highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"),q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{d=Y(d,e)},initHighlighting:()=>{N(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},initHighlightingOnLoad:()=>{N(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")},registerLanguage:(t,a)=>{let i=null;try{i=a(e)}catch(e){if(K("Language definition for '{}' could not be registered.".replace("{}",t)),!s)throw e;K(e),i=l}i.name||(i.name=t),n[t]=i,i.rawDefinition=a.bind(null,e),i.aliases&&k(i.aliases,{languageName:t})},unregisterLanguage:e=>{delete n[e];for(const n of Object.keys(i))i[n]===e&&delete i[n]},listLanguages:()=>Object.keys(n),getLanguage:v,registerAliases:k,autoDetection:O,inherit:Y,addPlugin:e=>{(e=>{e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{e["before:highlightBlock"](Object.assign({block:n.el},n))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}}),e.debugMode=()=>{s=!1},e.safeMode=()=>{s=!0},e.versionString="11.5.0",e.regex={concat:m,lookahead:g,either:p,optional:b,anyNumberOfTimes:u};for(const e in T)"object"==typeof T[e]&&t(T[e]);return Object.assign(e,T),e})({});const te=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z][A-Za-z0-9_-]*/}}),ae=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],ie=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],re=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],se=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],oe=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),le=re.concat(se);var ce="\\.([0-9](_*[0-9])*)",de="[0-9a-fA-F](_*[0-9a-fA-F])*",ge={className:"number",variants:[{begin:`(\\b([0-9](_*[0-9])*)((${ce})|\\.)?|(${ce}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b`},{begin:`\\b([0-9](_*[0-9])*)((${ce})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${ce})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{begin:`\\b0[xX]((${de})\\.?|(${de})?\\.(${de}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${de})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function ue(e,n,t){return-1===t?"":e.replace(n,a=>ue(e,n,t-1))}const be="[A-Za-z$_][0-9A-Za-z$_]*",me=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],pe=["true","false","null","undefined","NaN","Infinity"],_e=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],he=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],fe=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],Ee=["arguments","this","super","console","window","document","localStorage","module","global"],ye=[].concat(fe,_e,he);function we(e){const n=e.regex,t=be,a={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{const t=e[0].length+e.index,a=e.input[t];if("<"===a||","===a)return void n.ignoreMatch();let i;">"===a&&(((e,{after:n})=>{const t="",O={match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(k)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[_]};return{name:"Javascript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{PARAMS_CONTAINS:p,CLASS_REFERENCE:f},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,c,d,g,u,o,f,{className:"attr",begin:t+n.lookahead(":"),relevance:0},O,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[u,e.REGEXP_MODE,{className:"function",begin:k,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:p}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin,"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},E,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[_,e.inherit(e.TITLE_MODE,{begin:t,className:"title.function"})]},{match:/\.\.\./,relevance:0},N,{match:"\\$"+t,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[_]},y,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},h,v,{match:/\$[(.]/}]}}const Ne=e=>m(/\b/,e,/\w$/.test(e)?/\b/:/\B/),ve=["Protocol","Type"].map(Ne),ke=["init","self"].map(Ne),Oe=["Any","Self"],xe=["actor","associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Me=["false","nil","true"],Se=["assignment","associativity","higherThan","left","lowerThan","none","right"],Ae=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],Ce=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Te=p(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Re=p(Te,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),De=m(Te,Re,"*"),Ie=p(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),Le=p(Ie,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Be=m(Ie,Le,"*"),$e=m(/[A-Z]/,Le,"*"),ze=["autoclosure",m(/convention\(/,p("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",m(/objc\(/,Be,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","testable","UIApplicationMain","unknown","usableFromInline"],Fe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];var Ue=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{className:"variable",variants:[{begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t]},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,keyword:["if","then","else","elif","fi","for","while","in","do","done","case","esac","function"],literal:["true","false"],built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"]},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},t]}},grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),a="[a-zA-Z_]\\w*::",i="(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},s={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0},d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"],literal:"true false NULL",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"},u=[l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],b={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:g,contains:u.concat([{begin:/\(/,end:/\)/,keywords:g,contains:u.concat(["self"]),relevance:0}]),relevance:0},m={begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)",keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[e.inherit(c,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}]},r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C",aliases:["h"],keywords:g,disableAutodetect:!0,illegal:"=]/,contains:[{beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:l,strings:s,keywords:g}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),a="[a-zA-Z_]\\w*::",i="(?!struct)(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0},d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"]},u={className:"function.dispatch",relevance:0,keywords:{_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"]},begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/))},b=[u,l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],m={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:g,contains:b.concat([{begin:/\(/,end:/\)/,keywords:g,contains:b.concat(["self"]),relevance:0}]),relevance:0},p={className:"function",begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)",keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[c],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[s,o]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}]},r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:g,illegal:"",keywords:g,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:g},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]),built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"],literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/,keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]});s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t]},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{keyword:"if else elif endif define undef warning error line region endregion pragma checksum"}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial",relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[g,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{const n=e.regex,t=te(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+re.join("|")+")"},{begin:":(:)?("+se.join("|")+")"}]},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b"}]}},grmr_diff:e=>{const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}},grmr_go:e=>{const n={keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],literal:["true","false","iota","nil"],built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex,t={className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},s={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)),className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{const n=e.regex,t="[ร€-สธa-zA-Z_$][ร€-สธa-zA-Z_$0-9]*",a=t+ue("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed"],literal:["false","true","null"],type:["char","boolean","long","float","int","byte","short","double"],built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/,end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{begin:/import java\.[a-z]+\./,keywords:"import",relevance:2},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,className:"string",contains:[e.BACKSLASH_ESCAPE]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword",3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:i,relevance:0,contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,ge,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},ge,r]}},grmr_javascript:we,grmr_json:e=>({name:"JSON",contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0},e.QUOTE_STRING_MODE,{beginKeywords:"true false null"},e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}),grmr_kotlin:e=>{const n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(r,{className:"string"})]}]},l=ge,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g],{name:"Kotlin",aliases:["kt","kts"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},l]}},grmr_less:e=>{const n=te(e),t=le,a="([\\w-]+|@\\{[\\w-]+\\})",i=[],r=[],s=e=>({className:"string",begin:"~?"+e+".*?"+e}),o=(e,n,t)=>({className:e,begin:n,relevance:t}),l={$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},c={begin:"\\(",end:"\\)",contains:r,keywords:l,relevance:0};r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s("'"),s('"'),n.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},n.HEXCOLOR,c,o("variable","@@?[\\w-]+",10),o("variable","@\\{[\\w-]+\\}"),o("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},n.IMPORTANT);const d=r.concat({begin:/\{/,end:/\}/,contains:i}),g={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(r)},u={begin:a+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:r}}]},b={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",keywords:l,returnEnd:!0,contains:r,relevance:0}},m={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:d}},p={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:a,end:/\{/}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,o("keyword","all\\b"),o("variable","@\\{[\\w-]+\\}"),{begin:"\\b("+ae.join("|")+")\\b",className:"selector-tag"},n.CSS_NUMBER_MODE,o("selector-tag",a,0),o("selector-id","#"+a),o("selector-class","\\."+a,0),o("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",begin:":("+re.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+se.join("|")+")"},{begin:/\(/,end:/\)/,relevance:0,contains:d},{begin:"!important"},n.FUNCTION_DISPATCH]},_={begin:`[\\w-]+:(:)?(${t.join("|")})`,returnBegin:!0,contains:[p]};return i.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,b,m,_,u,p),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:i}},grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"]},i=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",t,{contains:[a],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:i}].concat(i)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{const n=e.regex,t=n.concat(/[A-Z_]/,n.optional(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),a={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[i,r,o,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:n.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}},grmr_markdown:e=>{const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},i={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[]}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r);let o=[n,t];return[a,i,r,s].forEach(e=>{e.contains=e.contains.concat(o)}),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o,end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n,keyword:["@interface","@class","@protocol","@implementation"]};return{name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:{"variable.language":["this","super"],$pattern:n,keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"],literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"],built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"],type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"]},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={$pattern:/[\w.]+/,keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0"},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/,end:/\}/},s={variants:[{begin:/\$\d/},{begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@][^\s\w{]/,relevance:0}]},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{const r="\\1"===i?i:n.concat(i,a);return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t)},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),r,{className:"string",contains:o,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a,contains:g}},grmr_php:e=>{const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/,contains:e.QUOTE_STRING_MODE.contains.concat(s)})]},d={scope:"number",variants:[{begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?"}],relevance:0},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={keyword:u,literal:(e=>{const n=[];return e.forEach(e=>{n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase())}),n})(g),built_in:b},p=e=>e.map(e=>e.replace(/\|\d+$/,"")),_={variants:[{match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant"}},{match:[/::/,/class/],scope:{2:"variable.language"}},{match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class",3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))],scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class",3:"variable.language"}}]},E={scope:"attr",match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0,begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_]},w={relevance:0,match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)],scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(w);const N=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1,keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/,endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]},contains:["self",...N]},...N,{scope:"meta",match:i}]},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/,keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE,contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,w,f,{match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use"},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m,contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait",illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/,contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{beginKeywords:"use",relevance:0,end:";",contains:[{match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]}},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a,built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],literal:["__debug__","Ellipsis","False","None","NotImplemented","True"],type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"]},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/,end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/,end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={className:"number",relevance:0,variants:[{begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})`},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})`}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i,illegal:/(<\/|->|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if",relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[m]},{variants:[{match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}},grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:t,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"},match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={"variable.constant":["__FILE__","__LINE__"],"variable.language":["self","super"],keyword:["alias","and","attr_accessor","attr_reader","attr_writer","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","include","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield"],built_in:["proc","lambda"],literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/,end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)),contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number",relevance:0,variants:[{begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0,keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{match:[/class\s+/,i]}],scope:{2:"title.class",4:"title.class.inherited"},keywords:r},{relevance:0,match:[i,/\.new[ (]/],scope:{1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},{match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l);c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m}},{className:"meta.prompt",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])",starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/,contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}},grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0,begin:n.concat(/\b/,/(?!let\b)/,e.IDENT_RE,n.lookahead(/\s*\(/))},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","panic!","file!","format!","format_args!","include_bin!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"];return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"],keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"],literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}},grmr_scss:e=>{const n=te(e),t=se,a=re,i="@[a-z-]+",r={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+a.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/,contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT]},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:i,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE]},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta.prompt",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}),grmr_sql:e=>{const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter(e=>!r.includes(e)),c={begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}};return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t;return n=n||[],e.map(e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)})(l,{when:e=>e.length<3}),literal:a,type:i,built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"]},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/,keyword:l.concat(s),literal:a,type:i}},{className:"type",begin:n.either("double precision","large object","with timezone","without timezone")},c,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={match:[/\./,p(...ve,...ke)],className:{2:"keyword"}},r={match:m(/\./,p(...xe)),relevance:0},s=xe.filter(e=>"string"==typeof e).concat(["_|0"]),o={variants:[{className:"keyword",match:p(...xe.filter(e=>"string"!=typeof e).concat(Oe).map(Ne),...ke)}]},l={$pattern:p(/\b\w+/,/#\w+/),keyword:s.concat(Ae),literal:Me},c=[i,r,o],d=[{match:m(/\./,p(...Ce)),relevance:0},{className:"built_in",match:m(/\b/,p(...Ce),/(?=\()/)}],u={match:/->/,relevance:0},b=[u,{className:"operator",relevance:0,variants:[{match:De},{match:`\\.(\\.|${Re})+`}]}],_="([0-9a-fA-F]_*)+",h={className:"number",relevance:0,variants:[{match:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{match:`\\b0x(${_})(\\.(${_}))?([pP][+-]?(([0-9]_*)+))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},f=(e="")=>({className:"subst",variants:[{match:m(/\\/,e,/[0\\tnr"']/)},{match:m(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),E=(e="")=>({className:"subst",match:m(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),y=(e="")=>({className:"subst",label:"interpol",begin:m(/\\/,e,/\(/),end:/\)/}),w=(e="")=>({begin:m(e,/"""/),end:m(/"""/,e),contains:[f(e),E(e),y(e)]}),N=(e="")=>({begin:m(e,/"/),end:m(/"/,e),contains:[f(e),y(e)]}),v={className:"string",variants:[w(),w("#"),w("##"),w("###"),N(),N("#"),N("##"),N("###")]},k={match:m(/`/,Be,/`/)},O=[k,{className:"variable",match:/\$\d+/},{className:"variable",match:`\\$${Le}+`}],x=[{match:/(@|#(un)?)available/,className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:Fe,contains:[...b,h,v]}]}},{className:"keyword",match:m(/@/,p(...ze))},{className:"meta",match:m(/@/,Be)}],M={match:g(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:m(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Le,"+")},{className:"type",match:$e,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:m(/\s+&\s+/,g($e)),relevance:0}]},S={begin://,keywords:l,contains:[...a,...c,...x,u,M]};M.contains.push(S);const A={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{match:m(Be,/\s*:/),keywords:"_|0",relevance:0},...a,...c,...d,...b,h,v,...O,...x,M]},C={begin://,contains:[...a,M]},T={begin:/\(/,end:/\)/,keywords:l,contains:[{begin:p(g(m(Be,/\s*:/)),g(m(Be,/\s+/,Be,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:Be}]},...a,...c,...b,h,v,...x,M,A],endsParent:!0,illegal:/["']/},R={match:[/func/,/\s+/,p(k.match,Be,De)],className:{1:"keyword",3:"title.function"},contains:[C,T,n],illegal:[/\[/,/%/]},D={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[C,T,n],illegal:/\[|%/},I={match:[/operator/,/\s+/,De],className:{1:"keyword",3:"title"}},L={begin:[/precedencegroup/,/\s+/,$e],className:{1:"keyword",3:"title"},contains:[M],keywords:[...Se,...Me],end:/}/};for(const e of v.variants){const n=e.contains.find(e=>"interpol"===e.label);n.keywords=l;const t=[...c,...d,...b,h,v,...O];n.contains=[...t,{begin:/\(/,end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l,contains:[...a,R,D,{beginKeywords:"struct protocol class extension enum actor",end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c]},I,L,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0},...c,...d,...b,h,v,...O,...x,M,A]}},grmr_typescript:e=>{const n=we(e),t=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],a={beginKeywords:"namespace",end:/\{/,excludeEnd:!0,contains:[n.exports.CLASS_REFERENCE]},i={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{keyword:"interface extends",built_in:t},contains:[n.exports.CLASS_REFERENCE]},r={$pattern:be,keyword:me.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]),literal:pe,built_in:ye.concat(t),"variable.language":Ee},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},o=(e,n,t)=>{const a=e.contains.findIndex(e=>e.label===n);if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)};return Object.assign(n.keywords,r),n.exports.PARAMS_CONTAINS.push(s),n.contains=n.contains.concat([s,a,i]),o(n,"shebang",e.SHEBANG()),o(n,"use_strict",{className:"meta",relevance:10,begin:/^\s*['"]use strict['"]/}),n.contains.find(e=>"func.def"===e.label).relevance=0,Object.assign(n,{name:"TypeScript",aliases:["ts","tsx"]}),n},grmr_vbnet:e=>{const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}]},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}]}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield",built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort",type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort",literal:"true false nothing"},illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/,end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0,variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{className:"label",begin:/^\w+:/},o,l,{className:"meta",begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/,end:/$/,keywords:{keyword:"const disable else elseif enable end externalsource if region then"},contains:[l]}]}},grmr_yaml:e=>{const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/,end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]",contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type",begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l];return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:l}}});const je=ne;for(const e of Object.keys(Ue)){const n=e.replace("grmr_","").replace("_","-");je.registerLanguage(n,Ue[e])}return je}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); diff --git a/book/src/internals/syntax-highlighting.md b/grammars/README.md similarity index 68% rename from book/src/internals/syntax-highlighting.md rename to grammars/README.md index ec1cc7d16695..b7a3ea320660 100644 --- a/book/src/internals/syntax-highlighting.md +++ b/grammars/README.md @@ -1,4 +1,4 @@ -# Syntax highlighting +# Grammars / syntax highlighting PRQL contains multiple grammar definitions to enable tools to highlight PRQL code. These are all intended to provide as good an experience as the grammar @@ -9,14 +9,14 @@ an index. - [Lezer](https://lezer.codemirror.net/) โ€” used by CodeMirror editors. The PRQL file is at - [`prql-lezer/README.me`](https://github.com/PRQL/prql/tree/main/prql-lezer/README.md). + [`grammars/prql-lezer/README.me`](https://github.com/PRQL/prql/tree/main/grammars/prql-lezer/README.md). - [Handlebars](https://handlebarsjs.com/) โ€” currently duplicated: - The book: - [`book/highlight-prql.js`](https://github.com/PRQL/prql/blob/main/book/highlight-prql.js) + [`book/highlight-prql.js`](https://github.com/PRQL/prql/blob/main/web/book/highlight-prql.js) - The website (outside of the book & playground): - [`website/themes/prql-theme/static/plugins/highlight/prql.js`](https://github.com/PRQL/prql/blob/main/book/highlight-prql.js) + [`website/themes/prql-theme/static/plugins/highlight/prql.js`](https://github.com/PRQL/prql/blob/main/web/book/highlight-prql.js) - [Textmate](https://macromates.com/manual/en/language_grammars) โ€” used by the VS Code extension. It's in the `prql-vscode` repo in @@ -24,9 +24,8 @@ an index. - [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) โ€” used by the Monaco editor, which we use for the Playground. The grammar is at - [`playground/src/workbench/prql-syntax.js`](https://github.com/PRQL/prql/blob/main/playground/src/workbench/prql-syntax.js). + [`playground/src/workbench/prql-syntax.js`](https://github.com/PRQL/prql/blob/main/web/playground/src/workbench/prql-syntax.js). -While the [pest](https://pest.rs/) grammar at -[`prql-compiler/src/parser/prql.pest`](https://github.com/PRQL/prql/blob/main/prql-compiler/src/parser/prql.pest) -isn't used for syntax highlighting, it's the arbiter of truth given it currently -powers the PRQL compiler. +- [Tree-Sitter](https://tree-sitter.github.io/tree-sitter) โ€” used by the neovim + and helix. The grammar can be found at + [https://github.com/PRQL/tree-sitter-prql](https://github.com/PRQL/tree-sitter-prql). diff --git a/prql-lezer/README.md b/grammars/prql-lezer/README.md similarity index 100% rename from prql-lezer/README.md rename to grammars/prql-lezer/README.md diff --git a/prql-lezer/src/prql.grammar b/grammars/prql-lezer/src/prql.grammar similarity index 96% rename from prql-lezer/src/prql.grammar rename to grammars/prql-lezer/src/prql.grammar index 803e6e61a6c0..6aea78ea786f 100644 --- a/prql-lezer/src/prql.grammar +++ b/grammars/prql-lezer/src/prql.grammar @@ -1,5 +1,5 @@ // TODO: -// - `switch` transform +// - `case` transform // - Do we want to highlight built-in transforms like `from` differently to // normal functions? // - A few small TODOs included below @@ -19,7 +19,7 @@ pipe { "|" | ~ambig_newline newline } List { "[" newline? list_item (("," newline? ) list_item)* ","? newline? "]" } -list_item { Assign_call | expr_call } +list_item { Assign_call | expr_call | Case_branch } expr_call { Expr | Func_call } // Ideally we would force a space after `Ident` to prevent an invalid s-string @@ -37,6 +37,7 @@ Expr { !term term (( Op_bin) term)* } Named_arg { ident_part ":" Expr } Assign { ident_part "=" Expr } Assign_call { ident_part "=" expr_call } +Case_branch { Expr "=>" Expr } Nested_pipeline { "(" newline* Pipeline ~ambig_newline newline? ")" } @@ -46,7 +47,8 @@ Nested_pipeline { "(" newline* Pipeline ~ambig_newline newline? ")" } // It's outside tokens because otherwise it conflicts with Ident Ident { ident_part ( "." ident_part)* } -// I don't think it's possible to have `Op_bin` & `Op_unary` as tokens โ€” that would mean `-` can't be both unary & bin. +// I don't think it's possible to have `Op_bin` & `Op_unary` as tokens โ€” that +// would mean `-` can't be both unary & bin. Op_bin { Op_bin_only | Op_unary } @tokens { diff --git a/packages/snap/snapcraft.yaml b/packages/snap/snapcraft.yaml new file mode 100644 index 000000000000..f4d96d6fa4b1 --- /dev/null +++ b/packages/snap/snapcraft.yaml @@ -0,0 +1,29 @@ +name: prqlc # you probably want to 'snapcraft register ' +title: PRQL Compiler +base: core20 # the base snap is the execution environment for this snap +version: git +summary: CLI for PRQL, a modern language for transforming data +description: | + prqlc is the CLI for the PRQL compiler. It compiles PRQL to SQL, and offers various diagnostics. + + PRQL is a modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement. +issues: https://github.com/PRQL/prql/issues +source-code: https://github.com/PRQL/prql +contact: https://twitter.com/prql_lang +website: https://prql-lang.org/ +license: Apache-2.0 +grade: devel # must be 'stable' to release into candidate/stable channels +confinement: strict +icon: ../web/website/themes/prql-theme/static/icon.svg + +parts: + prqlc: + plugin: rust + source: .. + rust-path: [prql-compiler/prqlc] + +apps: + prqlc: + command: bin/prqlc + plugs: + - home diff --git a/playground/src/sidebar/Sidebar.js b/playground/src/sidebar/Sidebar.js deleted file mode 100644 index 178c65408509..000000000000 --- a/playground/src/sidebar/Sidebar.js +++ /dev/null @@ -1,64 +0,0 @@ -import "./Sidebar.css"; -import React from "react"; - -function Sidebar({ library, onLoadFile }) { - function loadFile(section, file) { - onLoadFile(file, library[section][file]); - } - - const sections = []; - - for (const [section, files] of Object.entries(library)) { - const fileRows = []; - for (const [index, filename] of Object.keys(files).entries()) { - fileRows.push( -
loadFile(section, filename)} - > - {filename} -
- ); - } - - sections.push( -
-

{section}

- - {fileRows} -
- ); - } - - return ( -
-
-

PRQL Playground

-
-
-

External links

- - PRQL Website ↗ - - - Book ↗ - -
- - {sections} -
- ); -} - -export default Sidebar; diff --git a/prql-compiler/Cargo.toml b/prql-compiler/Cargo.toml index 296cf5b954fe..5cea1a4342f8 100644 --- a/prql-compiler/Cargo.toml +++ b/prql-compiler/Cargo.toml @@ -15,34 +15,37 @@ metadata.msrv = "1.65.0" [dependencies] anyhow = {version = "1.0.57", features = ["backtrace"]} ariadne = "0.1.5" -chumsky = "0.8.0" csv = "1.2.0" enum-as-inner = "0.5.0" itertools = "0.10.3" lazy_static = "1.4.0" log = "0.4.17" once_cell = "1.17.0" -pest = "2.5.0" -pest_derive = "2.5.0" regex = "1.7.0" semver = {version = "1.0.14", features = ["serde"]} serde = {version = "1.0.137", features = ["derive"]} serde_json = "1.0.81" sqlformat = "0.2.0" -sqlparser = {version = "0.30.0", features = ["serde"]} +sqlparser = {version = "0.32.0", features = ["serde"]} strum = {version = "0.24.0", features = ["std", "derive"]}# for converting enum variants to string strum_macros = "0.24.0" +# Chumsky issues in wasm (though we only see it when compiling on MacOS) +[target.'cfg(not(target_family="wasm"))'.dependencies] +chumsky = "0.9.2" +[target.'cfg(target_family="wasm")'.dependencies] +chumsky = {version = "0.9.2", features = ["ahash", "std"], default-features = false} + [dev-dependencies] cfg-if = "1.0" insta = {version = "1.28", features = ["colors", "glob", "yaml"]} # For integration tests [target.'cfg(not(target_family="wasm"))'.dev-dependencies] -chrono = "0.4" -pretty_assertions = "1.3.0" +chrono = {version = "0.4", features = [], default-features = false} criterion = "0.4.0" postgres = "0.19.3" +pretty_assertions = "1.3.0" rusqlite = {version = "0.28.0", features = ["bundled", "csvtab"]} # Re-enable on windows when duckdb supports it @@ -53,3 +56,11 @@ duckdb = {version = "0.7.0", features = ["bundled", "chrono"]} [[bench]] harness = false name = "bench" + +# Putting this in the workspace root causes it to refer to the path relative +# to `prql-compiler`? We can place it there when that's fixed. +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "../web/book/src/language-features/target.md" +replace = 'prql version:"{{version}}"' +search = 'prql version:"[\d.]+"' diff --git a/prql-compiler/README.md b/prql-compiler/README.md index 900be541dc03..abcdc1202bbe 100644 --- a/prql-compiler/README.md +++ b/prql-compiler/README.md @@ -8,7 +8,7 @@ For more on PRQL, check out the [PRQL website](https://prql-lang.org) or the For more usage examples and the library documentation, check out the [`prql-compiler` documentation](https://docs.rs/prql-compiler/latest/prql_compiler/). -# Installation +## Installation ```shell cargo add prql-compiler @@ -24,12 +24,12 @@ Compile a PRQL string to a SQLite dialect string. use prql_compiler::{compile, Options, Target, sql::Dialect}; let prql = "from employees | select [name, age]"; -let opt = Options { +let opts = &Options { format: false, target: Target::Sql(Some(Dialect::SQLite)), signature_comment: false }; -let sql = compile(&prql, opt).unwrap(); +let sql = compile(&prql, opts).unwrap(); assert_eq!("SELECT name, age FROM employees", sql); ``` diff --git a/prql-compiler/benches/bench.rs b/prql-compiler/benches/bench.rs index 8ab830c42513..a0ec550d6fb5 100644 --- a/prql-compiler/benches/bench.rs +++ b/prql-compiler/benches/bench.rs @@ -16,9 +16,9 @@ cfg_if::cfg_if! { use criterion::{criterion_group, criterion_main, Criterion}; use prql_compiler::*; - const CONTENT: &str = include_str!("../../book/tests/prql/examples/variables-0.prql"); + const CONTENT: &str = include_str!("../examples/compile-files/queries/variables.prql"); fn compile_query() -> Result { - compile(CONTENT, Options::default()) + compile(CONTENT, &Options::default()) } fn criterion_benchmark(c: &mut Criterion) { diff --git a/prql-compiler/examples/compile-files/build.rs b/prql-compiler/examples/compile-files/build.rs index 6eb9eea9ee68..6edbd2f1d397 100644 --- a/prql-compiler/examples/compile-files/build.rs +++ b/prql-compiler/examples/compile-files/build.rs @@ -19,7 +19,7 @@ fn main() { let prql_string = fs::read_to_string(prql_path).unwrap(); // compile - let sql_string = compile(&prql_string, Options::default()).unwrap(); + let sql_string = compile(&prql_string, &Options::default()).unwrap(); // write file fs::write(sql_path, sql_string).unwrap(); diff --git a/book/tests/prql/examples/variables-0.prql b/prql-compiler/examples/compile-files/queries/variables.prql similarity index 100% rename from book/tests/prql/examples/variables-0.prql rename to prql-compiler/examples/compile-files/queries/variables.prql diff --git a/prql-compiler/prql-compiler-macros/Cargo.toml b/prql-compiler/prql-compiler-macros/Cargo.toml index ee28586b7d37..b83c4ea33d55 100644 --- a/prql-compiler/prql-compiler-macros/Cargo.toml +++ b/prql-compiler/prql-compiler-macros/Cargo.toml @@ -14,8 +14,10 @@ proc_macro = true test = false [dependencies] -prql-compiler = {path = "..", default-features = false, version = "0.5.1" } -syn = "1.0" +prql-compiler = {path = "..", default-features = false, version = "0.6.1" } +# Was getting build errors with more recent versions; can remove pin if it +# successfully builds. +syn = "=1.0.107" [package.metadata.release] tag-name = "{{version}}" diff --git a/prql-compiler/prql-compiler-macros/src/lib.rs b/prql-compiler/prql-compiler-macros/src/lib.rs index 2e36ebf24760..88e65b2eb281 100644 --- a/prql-compiler/prql-compiler-macros/src/lib.rs +++ b/prql-compiler/prql-compiler-macros/src/lib.rs @@ -26,7 +26,7 @@ pub fn prql_to_sql(input: TokenStream) -> TokenStream { let opts = prql_compiler::Options::default().no_format().no_signature(); - let sql_string = match prql_compiler::compile(&prql_string, opts) { + let sql_string = match prql_compiler::compile(&prql_string, &opts) { Ok(r) => r, Err(err) => { panic!("{}", err); diff --git a/prql-compiler/prqlc/Cargo.toml b/prql-compiler/prqlc/Cargo.toml index 7a70d90ae406..e95f5a281877 100644 --- a/prql-compiler/prqlc/Cargo.toml +++ b/prql-compiler/prqlc/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "CLI interface for the PRQL compiler" +description = "CLI for the PRQL compiler" name = "prqlc" edition.workspace = true @@ -15,10 +15,13 @@ atty = "0.2.14" clap = {version = "4.1.1", features = ["derive"]} clio = {version = "0.2.4", features = ['clap-parse']} color-eyre = "0.6.1" -env_logger = {version = "0.9.1", features = ["termcolor"]} +env_logger = {version = "0.10.0", features = ["color"]} itertools = "0.10.3" +minijinja = {version = "0.30.4", features = ["unstable_machinery"]} notify = "^5.1.0" -prql-compiler = {path = '..', version = "0.5.1" } +prql-compiler = {path = '..', version = "0.6.1" } +regex = {version = "1.7.1", features = ["std", "unicode"]} +serde = "^1" serde_json = "1.0.81" serde_yaml = "0.9.1" walkdir = "^2.3.2" diff --git a/prql-compiler/prqlc/src/cli.rs b/prql-compiler/prqlc/src/cli.rs index 555b4c4aa479..41db7dc8825a 100644 --- a/prql-compiler/prqlc/src/cli.rs +++ b/prql-compiler/prqlc/src/cli.rs @@ -1,6 +1,6 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use ariadne::Source; -use clap::Parser; +use clap::{Parser, Subcommand}; use clio::{Input, Output}; use itertools::Itertools; use std::io::{Read, Write}; @@ -14,12 +14,13 @@ use prql_compiler::{downcast, Options}; use crate::watch; +/// Entrypoint called by [crate::main] pub fn main() -> color_eyre::eyre::Result<()> { env_logger::builder().format_timestamp(None).init(); color_eyre::install()?; let mut cli = Cli::parse(); - if let Err(error) = cli.run() { + if let Err(error) = cli.command.run() { eprintln!("{error}"); exit(1) } @@ -27,48 +28,73 @@ pub fn main() -> color_eyre::eyre::Result<()> { Ok(()) } -#[derive(Parser)] +#[derive(Parser, Debug, Clone)] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand, Debug, Clone)] #[clap(name = env!("CARGO_PKG_NAME"), about, version)] -pub enum Cli { - /// Parse PL AST - Parse(CommandIO), +enum Command { + /// Parse into PL AST + Parse { + #[clap(value_parser, default_value = "-")] + input: Input, + #[clap(value_parser, default_value = "-")] + output: Output, + #[arg(value_enum, long)] + format: Option, + }, /// Parse & generate PRQL code back #[clap(name = "fmt")] - Format(CommandIO), + Format(IoArgs), /// Parse, resolve & combine source with comments annotating relation type - Annotate(CommandIO), + Annotate(IoArgs), /// Parse & resolve, but don't lower into RQ - Debug(CommandIO), + Debug(IoArgs), /// Parse, resolve & lower into RQ - Resolve(CommandIO), + Resolve(IoArgs), /// Parse, resolve, lower into RQ & compile to SQL - Compile(CommandIO), + Compile(IoArgs), /// Watch a directory and compile .prql files to .sql files - Watch(watch::WatchCommand), + Watch(watch::WatchArgs), } -#[derive(clap::Args, Default)] -pub struct CommandIO { +#[derive(clap::Args, Default, Debug, Clone)] +pub struct IoArgs { #[clap(value_parser, default_value = "-")] input: Input, #[clap(value_parser, default_value = "-")] output: Output, + + // TODO: This should be only on some commands, is there an elegant way of + // doing that in Clap without lots of duplication? + #[arg(value_enum, long)] + format: Option, +} + +#[derive(clap::ValueEnum, Clone, Debug)] +enum Format { + Json, + Yaml, } fn is_stdin(input: &Input) -> bool { input.path() == "-" } -impl Cli { +impl Command { + /// Entrypoint called by [`main`] pub fn run(&mut self) -> Result<()> { - if let Cli::Watch(command) = self { + if let Command::Watch(command) = self { return watch::run(command); }; @@ -95,18 +121,16 @@ impl Cli { fn execute(&self, source: &str) -> Result> { Ok(match self { - Cli::Parse(_) => { - let ast = prql_to_pl(source).map_err(|e| anyhow!(e))?; - - serde_yaml::to_string(&ast)?.into_bytes() + Command::Parse { format, .. } => { + let ast = prql_to_pl(source)?; + match format { + Some(Format::Json) | None => serde_json::to_string_pretty(&ast)?.into_bytes(), + Some(Format::Yaml) => serde_yaml::to_string(&ast)?.into_bytes(), + } } - Cli::Format(_) => prql_to_pl(source) - .and_then(pl_to_prql) - .map_err(|x| anyhow!(x))? - .as_bytes() - .to_vec(), - Cli::Debug(_) => { - let stmts = prql_to_pl(source).map_err(|x| anyhow!(x))?; + Command::Format(_) => prql_to_pl(source).and_then(pl_to_prql)?.as_bytes().to_vec(), + Command::Debug(_) => { + let stmts = prql_to_pl(source)?; let (stmts, context) = semantic::resolve_only(stmts, None)?; let (references, stmts) = @@ -119,8 +143,11 @@ impl Cli { ] .concat() } - Cli::Annotate(_) => { - let stmts = prql_to_pl(source).map_err(|x| anyhow!(x))?; + Command::Annotate(_) => { + // TODO: potentially if there is code performing a role beyond + // presentation, it should be a library function; and we could + // promote it to the `prql-compiler` crate. + let stmts = prql_to_pl(source)?; // resolve let (stmts, _) = semantic::resolve_only(stmts, None)?; @@ -130,49 +157,55 @@ impl Cli { // combine with source combine_prql_and_frames(source, frames).as_bytes().to_vec() } - Cli::Resolve(_) => { - let ast = prql_to_pl(source).map_err(|x| anyhow!(x))?; + Command::Resolve(_) => { + // We can't currently have `--format=yaml` here, because + // serde_yaml is unable to serialize an Enum of an Enum; from + // https://github.com/dtolnay/serde-yaml/blob/68a9e95c9fd639498c85f55b5485f446b3f8465c/tests/test_error.rs#L175 + let ast = prql_to_pl(source)?; let ir = semantic::resolve(ast)?; - serde_json::to_string_pretty(&ir)?.into_bytes() } - Cli::Compile(_) => compile(source, Options::default()) - .map_or_else(|x| x.to_string(), |x| x) - .as_bytes() - .to_vec(), - Cli::Watch(_) => unreachable!(), + // TODO: Allow passing the `Options` to the CLI; map those through. + // We already do this in Watch. + Command::Compile(_) => compile(source, &Options::default())?.as_bytes().to_vec(), + Command::Watch(_) => unreachable!(), }) } fn read_input(&mut self) -> Result<(String, String)> { - use Cli::*; - match self { - Parse(io) | Format(io) | Debug(io) | Annotate(io) | Resolve(io) | Compile(io) => { - // Don't wait without a prompt when running `prql-compiler compile` โ€” - // it's confusing whether it's waiting for input or not. This - // offers the prompt. - if is_stdin(&io.input) && atty::is(atty::Stream::Stdin) { - println!("Enter PRQL, then ctrl-d:"); - println!(); - } - - let mut source = String::new(); - io.input.read_to_string(&mut source)?; - let source_id = (*io.input.path()).to_str().unwrap().to_string(); - Ok((source, source_id)) - } - Cli::Watch(_) => unreachable!(), + // TODO: possibly this should be called by the relevant subcommands + // passing in `input`, rather than matching on them and grabbing `input` + // from `self`. + use Command::*; + let mut input = match self { + Parse { input, .. } => input.clone(), + Format(io) | Debug(io) | Annotate(io) | Resolve(io) | Compile(io) => io.input.clone(), + Watch(_) => unreachable!(), + }; + // Don't wait without a prompt when running `prqlc compile` โ€” + // it's confusing whether it's waiting for input or not. This + // offers the prompt. + if is_stdin(&input) && atty::is(atty::Stream::Stdin) { + println!("Enter PRQL, then ctrl-d:"); + println!(); } + + let mut source = String::new(); + (input).read_to_string(&mut source)?; + let source_id = (input.path()).to_str().unwrap().to_string(); + Ok((source, source_id)) } fn write_output(&mut self, data: &[u8]) -> std::io::Result<()> { - use Cli::*; - match self { - Parse(io) | Format(io) | Debug(io) | Annotate(io) | Resolve(io) | Compile(io) => { - io.output.write_all(data) + use Command::*; + let mut output = match self { + Parse { output, .. } => output.to_owned(), + Format(io) | Debug(io) | Annotate(io) | Resolve(io) | Compile(io) => { + io.output.to_owned() } - Cli::Watch(_) => unreachable!(), - } + Watch(_) => unreachable!(), + }; + output.write_all(data) } } @@ -208,14 +241,30 @@ fn combine_prql_and_frames(source: &str, frames: Vec<(Span, Frame)>) -> String { #[cfg(test)] mod tests { - use insta::assert_snapshot; + use insta::{assert_display_snapshot, assert_snapshot}; + + // TODO: would be good to test the basic CLI interface โ€” i.e. snapshotting this: + + // $ prqlc parse --help + // + // Parse PL AST + // + // Usage: prqlc parse [OPTIONS] [INPUT] [OUTPUT] + // + // Arguments: + // [INPUT] [default: -] + // [OUTPUT] [default: -] + // + // Options: + // --format [possible values: json, yaml] + // -h, --help Print help use super::*; #[test] fn layouts() { - let output = Cli::execute( - &Cli::Annotate(CommandIO::default()), + let output = Command::execute( + &Command::Annotate(IoArgs::default()), r#" from initial_table select [f = first_name, l = last_name, gender] @@ -239,8 +288,8 @@ sort full #[test] fn format() { - let output = Cli::execute( - &Cli::Format(CommandIO::default()), + let output = Command::execute( + &Command::Format(IoArgs::default()), r#" from table.subdivision derive `ลพelva_means_turtle` = (`column with spaces` + 1) * 3 @@ -267,4 +316,53 @@ group a_column (take 10 | sort b_column | derive [the_number = rank, last = lag ) "###); } + + #[test] + fn compile() { + // Check we get an error on a bad input + let input = "asdf"; + let result = Command::execute(&Command::Compile(IoArgs::default()), input); + assert_display_snapshot!(result.unwrap_err(), @r###" + Error: + โ•ญโ”€[:1:1] + โ”‚ + 1 โ”‚ asdf + ยท โ”€โ”€โ”ฌโ”€ + ยท โ•ฐโ”€โ”€โ”€ Unknown name asdf + โ”€โ”€โ”€โ•ฏ + "###); + } + + #[test] + fn parse() { + let output = Command::execute( + &Command::Parse { + input: IoArgs::default().input, + output: IoArgs::default().output, + format: Some(Format::Yaml), + }, + "from x | select y", + ) + .unwrap(); + + assert_display_snapshot!(String::from_utf8(output).unwrap().trim(), @r###" + - Main: + Pipeline: + exprs: + - FuncCall: + name: + Ident: + - from + args: + - Ident: + - x + - FuncCall: + name: + Ident: + - select + args: + - Ident: + - y + "###); + } } diff --git a/prql-compiler/prqlc/src/jinja.rs b/prql-compiler/prqlc/src/jinja.rs new file mode 100644 index 000000000000..6bec828644c8 --- /dev/null +++ b/prql-compiler/prqlc/src/jinja.rs @@ -0,0 +1,177 @@ +//! Handling of Jinja templates +//! +//! dbt is using the following pipeline: `Jinja+SQL -> SQL -> execution`. +//! +//! To prevent messing up the templates, we have create the following pipeline: +//! ``` +//! Jinja+PRQL -> Jinja+SQL -> SQL -> execution +//! ``` +//! +//! But because prql-compiler does not (and should not) know how to handle Jinja, +//! we have to extract the interpolations, replace them something that is valid PRQL, +//! compile the query and inject interpolations back in. +//! +//! Unfortunately, this requires parsing Jinja. +//! +//! use crate::compiler::tokens::{Span, Token}; + +use std::collections::HashMap; + +use anyhow::Result; +use minijinja::machinery::{Span, Token}; +use regex::Regex; + +const ANCHOR_PREFIX: &str = "_jinja_"; + +#[derive(Debug)] +pub enum JinjaBlock<'a> { + Data(&'a str), + Interpolation(Vec<(Token<'a>, Span)>), +} + +#[derive(Default)] +pub struct JinjaContext<'a> { + anchor_map: HashMap, + header: Vec<&'a str>, +} + +/// Parse source as Jinja template, extract all interpolations +/// and replace them with anchors. +pub fn pre_process(source: &str) -> Result<(String, JinjaContext)> { + let mut blocks = Vec::new(); + let mut current_block = Vec::new(); + + for res in minijinja::machinery::tokenize(source, false) { + let (token, span) = res?; + + if let Token::TemplateData(data) = token { + if !current_block.is_empty() { + blocks.push(JinjaBlock::Interpolation(current_block)); + current_block = Vec::new(); + } + blocks.push(JinjaBlock::Data(data)) + } else { + current_block.push((token, span)); + } + } + if !current_block.is_empty() { + blocks.push(JinjaBlock::Interpolation(current_block)); + } + + let mut anchored_source = String::new(); + let mut next_anchor_id = 0; + let mut context = JinjaContext::default(); + for block in blocks { + match block { + JinjaBlock::Data(data) => anchored_source += data, + JinjaBlock::Interpolation(block) => { + let (tokens, spans): (Vec<_>, _) = block.into_iter().unzip(); + + let source_span = find_span(source, spans); + + if let Some(Token::Ident("config" | "set")) = tokens.get(1) { + context.header.push(source_span); + } else { + let id = format!("{ANCHOR_PREFIX}{next_anchor_id}"); + next_anchor_id += 1; + + anchored_source += &id; + + context.anchor_map.insert(id, source_span); + } + } + } + } + + Ok((anchored_source, context)) +} + +fn find_span(source: &str, spans: Vec) -> &str { + let start = spans.first().unwrap(); + let end = spans.last().unwrap(); + + let mut start_index = 0; + let mut end_index = source.len(); + + let mut line = 1; + let mut col = 0; + for (index, char) in source.chars().enumerate() { + if char == '\n' { + line += 1; + col = 0; + continue; + } else { + col += 1; + } + + if line == start.start_line && col == start.start_col { + start_index = index; + } + if line == end.end_line && col == end.end_col { + end_index = index + 1; + } + } + &source[start_index..end_index] +} + +/// Replace anchors with their values. +pub fn post_process(source: &str, context: JinjaContext) -> String { + let mut res = String::new(); + + for stmt in context.header { + res += stmt; + res += "\n"; + } + + let re = Regex::new(&format!(r"{ANCHOR_PREFIX}\d+")).unwrap(); + + let mut last_index = 0; + for cap in re.captures_iter(source) { + let cap = cap.get(0).unwrap(); + let index = cap.start(); + let anchor_id = cap.as_str(); + + res += &source[last_index..index]; + res += context.anchor_map.get(anchor_id).unwrap_or(&anchor_id); + + last_index = index + anchor_id.len(); + } + res += &source[last_index..]; + + res +} + +#[cfg(test)] +mod test { + use super::Span; + + #[test] + fn test_find_span() { + let text = r#"line 1 col 13 + line 2 col 21 + some text line 3 col 31 more text + "#; + + assert_eq!( + super::find_span( + text, + vec![ + Span { + start_line: 2, + start_col: 9, + end_line: 12123123, + end_col: 2930293, + }, + Span { + start_line: 7893648, + start_col: 79678, + end_line: 3, + end_col: 31, + } + ] + ), + r#"line 2 col 21 + some text line 3 col 31"# + ); + } +} diff --git a/prql-compiler/prqlc/src/main.rs b/prql-compiler/prqlc/src/main.rs index 98ed661a72d6..0a3e3d6edc14 100644 --- a/prql-compiler/prqlc/src/main.rs +++ b/prql-compiler/prqlc/src/main.rs @@ -7,6 +7,8 @@ #[cfg(not(target_family = "wasm"))] mod cli; #[cfg(not(target_family = "wasm"))] +mod jinja; +#[cfg(not(target_family = "wasm"))] mod watch; #[cfg(not(target_family = "wasm"))] diff --git a/prql-compiler/prqlc/src/watch.rs b/prql-compiler/prqlc/src/watch.rs index a6bd2c4d0a2b..a57322e116af 100644 --- a/prql-compiler/prqlc/src/watch.rs +++ b/prql-compiler/prqlc/src/watch.rs @@ -8,8 +8,10 @@ use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; use prql_compiler::downcast; use walkdir::WalkDir; -#[derive(Parser)] -pub struct WatchCommand { +use crate::jinja; + +#[derive(Parser, Debug, Clone)] +pub struct WatchArgs { /// Directory or file to watch for changes pub path: OsString, @@ -20,7 +22,7 @@ pub struct WatchCommand { pub no_signature: bool, } -pub fn run(command: &mut WatchCommand) -> Result<()> { +pub fn run(command: &mut WatchArgs) -> Result<()> { let opt = prql_compiler::Options { format: !command.no_format, target: prql_compiler::Target::Sql(None), @@ -33,7 +35,7 @@ pub fn run(command: &mut WatchCommand) -> Result<()> { // watch and compile println!("Watching path \"{}\"", path.display()); - watch_and_compile(path, &opt).unwrap(); + watch_and_compile(path, &opt)?; Ok(()) } @@ -108,9 +110,12 @@ fn compile_path(path: &Path, opt: &prql_compiler::Options) -> Result<()> { return Ok(()); } + // pre-process Jinja + let (prql_string, jinja_context) = jinja::pre_process(&prql_string)?; + // compile println!("Compiling {}", prql_path.display()); - let sql_string = match prql_compiler::compile(&prql_string, opt.clone()) { + let sql_string = match prql_compiler::compile(&prql_string, opt) { Ok(sql_string) => sql_string, Err(err) => { let source_id = &prql_path.to_str().unwrap_or_default(); @@ -119,6 +124,9 @@ fn compile_path(path: &Path, opt: &prql_compiler::Options) -> Result<()> { } }; + // post-process Jinja + let sql_string = jinja::post_process(&sql_string, jinja_context); + // write fs::write(sql_path, sql_string)?; diff --git a/prql-compiler/src/ast/pl/expr.rs b/prql-compiler/src/ast/pl/expr.rs index ea3dc6f0a7b7..fc98216c51fa 100644 --- a/prql-compiler/src/ast/pl/expr.rs +++ b/prql-compiler/src/ast/pl/expr.rs @@ -72,11 +72,15 @@ pub enum ExprKind { TransformCall(TransformCall), SString(Vec), FString(Vec), - Switch(Vec), + Case(Vec), BuiltInFunction { name: String, args: Vec, }, + Set(SetExpr), + + /// a placeholder for values provided after query is compiled + Param(String), } impl ExprKind { @@ -89,7 +93,16 @@ impl ExprKind { } #[derive( - Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, strum::Display, strum::EnumString, + Debug, + PartialEq, + Eq, + Clone, + Copy, + Hash, + Serialize, + Deserialize, + strum::Display, + strum::EnumString, )] pub enum BinOp { #[strum(to_string = "*")] @@ -122,12 +135,12 @@ pub enum BinOp { Coalesce, } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, strum::EnumString)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Serialize, Deserialize, strum::EnumString)] pub enum UnOp { #[strum(to_string = "-")] Neg, #[strum(to_string = "+")] - Add, + Add, // TODO: rename to Pos #[strum(to_string = "!")] Not, #[strum(to_string = "==")] @@ -142,6 +155,7 @@ pub struct ListItem(pub Expr); pub struct FuncCall { pub name: Box, pub args: Vec, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub named_args: HashMap, } @@ -163,8 +177,8 @@ pub struct Closure { pub body_ty: Option, pub args: Vec, - pub params: Vec, - pub named_params: Vec, + pub params: Vec, + pub named_params: Vec, pub env: HashMap, } @@ -177,6 +191,16 @@ impl Closure { } } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct ClosureParam { + pub name: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub ty: Option, + + pub default_value: Option, +} + #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct WindowFrame> { pub kind: WindowKind, @@ -310,6 +334,7 @@ pub enum TransformKind { pipeline: Box, }, Append(Box), + Loop(Box), } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -327,7 +352,7 @@ pub enum TableExternRef { /// Placeholder for a relation that will be provided later. /// This is very similar to relational s-strings and may not even be needed for now, so /// it's not documented anywhere. But it will be used in the future. - Anchor(String), + Param(String), // TODO: add other sources such as files, URLs } @@ -430,7 +455,6 @@ impl From for anyhow::Error { // https://github.com/bluejekyll/enum-as-inner/issues/84 #[allow(unreachable_code)] fn from(kind: ExprKind) -> Self { - // panic!("Failed to convert {item}") anyhow!("Failed to convert `{}`", Expr::from(kind)) } } @@ -439,7 +463,6 @@ impl From for anyhow::Error { // https://github.com/bluejekyll/enum-as-inner/issues/84 #[allow(unreachable_code)] fn from(kind: TransformKind) -> Self { - // panic!("Failed to convert {item}") anyhow!("Failed to convert `{kind:?}`") } } @@ -561,8 +584,8 @@ impl Display for Expr { ExprKind::Literal(literal) => { write!(f, "{}", literal)?; } - ExprKind::Switch(cases) => { - f.write_str("switch [\n")?; + ExprKind::Case(cases) => { + f.write_str("case [\n")?; for case in cases { writeln!(f, " {} => {}", case.condition, case.value)?; } @@ -571,6 +594,12 @@ impl Display for Expr { ExprKind::BuiltInFunction { .. } => { f.write_str("")?; } + ExprKind::Set(_) => { + writeln!(f, "")?; + } + ExprKind::Param(id) => { + writeln!(f, "${id}")?; + } } Ok(()) diff --git a/prql-compiler/src/ast/pl/fold.rs b/prql-compiler/src/ast/pl/fold.rs index 3af403b8525d..75bb3c2cf27c 100644 --- a/prql-compiler/src/ast/pl/fold.rs +++ b/prql-compiler/src/ast/pl/fold.rs @@ -36,10 +36,16 @@ pub trait AstFold { fn fold_exprs(&mut self, exprs: Vec) -> Result> { exprs.into_iter().map(|node| self.fold_expr(node)).collect() } - fn fold_var_def(&mut self, table: VarDef) -> Result { + fn fold_var_def(&mut self, var_def: VarDef) -> Result { Ok(VarDef { - name: table.name, - value: Box::new(self.fold_expr(*table.value)?), + name: var_def.name, + value: Box::new(self.fold_expr(*var_def.value)?), + }) + } + fn fold_type_def(&mut self, ty_def: TypeDef) -> Result { + Ok(TypeDef { + name: ty_def.name, + value: ty_def.value.map(|x| self.fold_expr(x)).transpose()?, }) } fn fold_pipeline(&mut self, pipeline: Pipeline) -> Result { @@ -61,7 +67,7 @@ pub trait AstFold { fold_interpolate_item(self, sstring_item) } fn fold_type(&mut self, t: Ty) -> Result { - fold_type(self, t) + Ok(t) } fn fold_window(&mut self, window: WindowFrame) -> Result { fold_window(self, window) @@ -100,7 +106,7 @@ pub fn fold_expr_kind(fold: &mut T, expr_kind: ExprKind) -> .map(|x| fold.fold_interpolate_item(x)) .try_collect()?, ), - Switch(cases) => Switch(fold_cases(fold, cases)?), + Case(cases) => Case(fold_cases(fold, cases)?), FuncCall(func_call) => FuncCall(fold.fold_func_call(func_call)?), Closure(closure) => Closure(Box::new(fold.fold_closure(*closure)?)), @@ -110,9 +116,10 @@ pub fn fold_expr_kind(fold: &mut T, expr_kind: ExprKind) -> name, args: fold.fold_exprs(args)?, }, + Param(id) => Param(id), // None of these capture variables, so we don't need to fold them. - Literal(_) => expr_kind, + Literal(_) | Set(_) => expr_kind, }) } @@ -121,6 +128,7 @@ pub fn fold_stmt_kind(fold: &mut T, stmt_kind: StmtKind) -> Ok(match stmt_kind { FuncDef(func) => FuncDef(fold.fold_func_def(func)?), VarDef(var_def) => VarDef(fold.fold_var_def(var_def)?), + TypeDef(type_def) => TypeDef(fold.fold_type_def(type_def)?), Main(expr) => Main(Box::new(fold.fold_expr(*expr)?)), QueryDef(_) => stmt_kind, }) @@ -273,6 +281,7 @@ pub fn fold_transform_kind( range: fold_range(fold, range)?, pipeline: Box::new(fold.fold_expr(*pipeline)?), }, + Loop(pipeline) => Loop(Box::new(fold.fold_expr(*pipeline)?)), }) } @@ -312,14 +321,3 @@ pub fn fold_func_param( }) .try_collect() } - -pub fn fold_type(fold: &mut T, t: Ty) -> Result { - Ok(match t { - Ty::Literal(_) => t, - Ty::Parameterized(t, p) => { - Ty::Parameterized(Box::new(fold.fold_type(*t)?), Box::new(fold.fold_type(*p)?)) - } - Ty::AnyOf(ts) => Ty::AnyOf(ts.into_iter().map(|t| fold_type(fold, t)).try_collect()?), - _ => t, - }) -} diff --git a/prql-compiler/src/ast/pl/stmt.rs b/prql-compiler/src/ast/pl/stmt.rs index 05c43066a00b..11f2c5124b77 100644 --- a/prql-compiler/src/ast/pl/stmt.rs +++ b/prql-compiler/src/ast/pl/stmt.rs @@ -27,6 +27,7 @@ pub enum StmtKind { QueryDef(QueryDef), FuncDef(FuncDef), VarDef(VarDef), + TypeDef(TypeDef), Main(Box), } @@ -44,15 +45,16 @@ pub struct FuncDef { pub positional_params: Vec, // ident pub named_params: Vec, // named expr pub body: Box, - pub return_ty: Option, + pub return_ty: Option, } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct FuncParam { pub name: String, + /// Parsed expression that will be resolved to a type #[serde(skip_serializing_if = "Option::is_none")] - pub ty: Option, + pub ty_expr: Option, pub default_value: Option, } @@ -63,6 +65,12 @@ pub struct VarDef { pub value: Box, } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct TypeDef { + pub name: String, + pub value: Option, +} + impl From for Stmt { fn from(kind: StmtKind) -> Self { Stmt { @@ -127,6 +135,13 @@ impl Display for StmtKind { } }; } + StmtKind::TypeDef(ty_def) => { + if let Some(value) = &ty_def.value { + write!(f, "type {} = {value}\n\n", ty_def.name)?; + } else { + write!(f, "type {}\n\n", ty_def.name)?; + } + } } Ok(()) } diff --git a/prql-compiler/src/ast/pl/types.rs b/prql-compiler/src/ast/pl/types.rs index 3d4c428d5d75..622b3117020a 100644 --- a/prql-compiler/src/ast/pl/types.rs +++ b/prql-compiler/src/ast/pl/types.rs @@ -1,20 +1,49 @@ -use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter, Result, Write}; use enum_as_inner::EnumAsInner; use serde::{Deserialize, Serialize}; -use super::Frame; +use super::{Frame, Literal}; -#[derive(Clone, PartialEq, Serialize, Deserialize, EnumAsInner)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EnumAsInner)] +pub enum SetExpr { + /// Set of a built-in primitive type + Primitive(TyLit), + + /// Set that contains only a literal value + Singleton(Literal), + + /// Union of sets (sum) + Union(Vec<(Option, SetExpr)>), + + /// Set of tuples (product) + Tuple(Vec), + + /// Set of arrays + Array(Box), + + /// Set of sets. + /// Used for exprs that can be converted to SetExpr and then used as a Ty. + Set, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum TupleElement { + Single(Option, SetExpr), + Wildcard, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EnumAsInner)] pub enum Ty { - Empty, - Literal(TyLit), - Named(String), - Parameterized(Box, Box), - AnyOf(Vec), + /// Value is an element of this [SetExpr] + SetExpr(SetExpr), + + /// Value is a function described by [TyFunc] + // TODO: convert into [Ty::Domain]. Function(TyFunc), + /// Special type for relations. + // TODO: convert into [Ty::Domain]. Table(Frame), /// Means that we have no information about the type of the variable and @@ -26,20 +55,23 @@ pub enum Ty { Debug, Clone, Serialize, Deserialize, PartialEq, Eq, strum::EnumString, strum::Display, )] pub enum TyLit { + // TODO: convert to a named expression #[strum(to_string = "list")] List, + // TODO: convert to a named expression #[strum(to_string = "column")] Column, + // TODO: convert to a named expression #[strum(to_string = "scalar")] Scalar, - #[strum(to_string = "integer")] - Integer, + #[strum(to_string = "int")] + Int, #[strum(to_string = "float")] Float, #[strum(to_string = "bool")] Bool, - #[strum(to_string = "string")] - String, + #[strum(to_string = "text")] + Text, #[strum(to_string = "date")] Date, #[strum(to_string = "time")] @@ -56,76 +88,33 @@ pub struct TyFunc { } impl Ty { - pub const fn column() -> Ty { - Ty::Literal(TyLit::Column) - } -} + pub fn is_superset_of(&self, subset: &Ty) -> bool { + match (self, subset) { + // Not handled here. See type_resolver. + (Ty::Infer, _) | (_, Ty::Infer) => false, -impl From for Ty { - fn from(lit: TyLit) -> Self { - Ty::Literal(lit) - } -} + (Ty::SetExpr(left), Ty::SetExpr(right)) => left.is_superset_of(right), -impl Default for Ty { - fn default() -> Self { - Ty::Infer + (Ty::Table(_), Ty::Table(_)) => true, + + (l, r) => l == r, + } } } -/// Implements a partial ordering or types: -/// - higher up are types that include many others (AnyOf, Any) and -/// - on the bottom are the atomic types (bool, string). -impl PartialOrd for Ty { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - // Not handled here. See type_resolver. - (Ty::Infer, _) | (_, Ty::Infer) => None, - - (Ty::Literal(TyLit::Column), Ty::Literal(TyLit::Column)) => Some(Ordering::Equal), - (Ty::Literal(TyLit::Column), Ty::Literal(_)) => Some(Ordering::Greater), - (Ty::Literal(_), Ty::Literal(TyLit::Column)) => Some(Ordering::Less), - - (Ty::Literal(l0), Ty::Literal(r0)) => { - if l0 == r0 { - Some(Ordering::Equal) - } else { - None - } - } - (Ty::AnyOf(many), one) => { - if many.iter().any(|m| m >= one) { - Some(Ordering::Greater) - } else { - None - } - } - (one, Ty::AnyOf(many)) => { - if many.iter().any(|m| m >= one) { - Some(Ordering::Less) - } else { - None - } - } - (Ty::Parameterized(l_ty, l_param), Ty::Parameterized(r_ty, r_param)) => { - if l_ty == r_ty && l_param == r_param { - Some(Ordering::Equal) - } else { - None - } - } - (Ty::Parameterized(l_ty, _), r_ty) if **l_ty == *r_ty => Some(Ordering::Equal), - (l_ty, Ty::Parameterized(r_ty, _)) if *l_ty == **r_ty => Some(Ordering::Equal), +impl SetExpr { + fn is_superset_of(&self, subset: &SetExpr) -> bool { + match (self, subset) { + // TODO: convert these to array + (SetExpr::Primitive(TyLit::Column), SetExpr::Primitive(TyLit::Column)) => true, + (SetExpr::Primitive(TyLit::Column), SetExpr::Primitive(_)) => true, + (SetExpr::Primitive(_), SetExpr::Primitive(TyLit::Column)) => false, - (Ty::Table(_), Ty::Table(_)) => Some(Ordering::Equal), + (SetExpr::Primitive(l0), SetExpr::Primitive(r0)) => l0 == r0, + (SetExpr::Union(many), one) => many.iter().any(|(_, any)| any.is_superset_of(one)), + (one, SetExpr::Union(many)) => many.iter().all(|(_, each)| one.is_superset_of(each)), - (l, r) => { - if l == r { - Some(Ordering::Equal) - } else { - None - } - } + (l, r) => l == r, } } } @@ -133,21 +122,7 @@ impl PartialOrd for Ty { impl Display for Ty { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match &self { - Ty::Empty => write!(f, "()"), - Ty::Literal(lit) => write!(f, "{:}", lit), - Ty::Named(name) => write!(f, "{:}", name), - Ty::Parameterized(t, param) => { - write!(f, "{t}<{}>", param) - } - Ty::AnyOf(ts) => { - for (i, t) in ts.iter().enumerate() { - write!(f, "{t}")?; - if i < ts.len() - 1 { - f.write_char('|')?; - } - } - Ok(()) - } + Ty::SetExpr(lit) => write!(f, "{:}", lit), Ty::Table(frame) => write!(f, "table<{frame}>"), Ty::Infer => write!(f, "infer"), Ty::Function(func) => { @@ -163,8 +138,40 @@ impl Display for Ty { } } -impl Debug for Ty { +impl Display for SetExpr { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Display::fmt(self, f) + match &self { + SetExpr::Primitive(lit) => write!(f, "{:}", lit), + SetExpr::Union(ts) => { + for (i, (_, e)) in ts.iter().enumerate() { + write!(f, "{e}")?; + if i < ts.len() - 1 { + f.write_char('|')?; + } + } + Ok(()) + } + SetExpr::Singleton(lit) => write!(f, "{:}", lit), + SetExpr::Tuple(elements) => { + write!(f, "[")?; + for e in elements { + match e { + TupleElement::Wildcard => { + write!(f, "*")?; + } + TupleElement::Single(name, expr) => { + if let Some(name) = name { + write!(f, "{name} = ")? + } + write!(f, "{expr}")? + } + } + write!(f, ",")? + } + Ok(()) + } + SetExpr::Set => write!(f, "set"), + SetExpr::Array(_) => todo!(), + } } } diff --git a/prql-compiler/src/ast/rq/expr.rs b/prql-compiler/src/ast/rq/expr.rs index bf70f4745ffd..d685459f268c 100644 --- a/prql-compiler/src/ast/rq/expr.rs +++ b/prql-compiler/src/ast/rq/expr.rs @@ -34,12 +34,15 @@ pub enum ExprKind { // TODO: convert this into built-in function FString(Vec>), - Switch(Vec>), + Case(Vec>), BuiltInFunction { name: String, args: Vec, }, + + /// Placeholder for + Param(String), } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] diff --git a/prql-compiler/src/ast/rq/fold.rs b/prql-compiler/src/ast/rq/fold.rs index d84d6385ba37..d0a35d89260a 100644 --- a/prql-compiler/src/ast/rq/fold.rs +++ b/prql-compiler/src/ast/rq/fold.rs @@ -187,6 +187,7 @@ pub fn fold_transform( filter: fold.fold_expr(filter)?, }, Append(bottom) => Append(fold.fold_table_ref(bottom)?), + Loop(transforms) => Loop(fold_transforms(fold, transforms)?), }; Ok(transform) } @@ -222,7 +223,7 @@ pub fn fold_expr_kind(fold: &mut F, kind: ExprKind) -> Resul ExprKind::SString(items) => ExprKind::SString(fold_interpolate_items(fold, items)?), ExprKind::FString(items) => ExprKind::FString(fold_interpolate_items(fold, items)?), - ExprKind::Switch(cases) => ExprKind::Switch( + ExprKind::Case(cases) => ExprKind::Case( cases .into_iter() .map(|c| fold_switch_case(fold, c)) @@ -232,6 +233,7 @@ pub fn fold_expr_kind(fold: &mut F, kind: ExprKind) -> Resul name, args: args.into_iter().map(|a| fold.fold_expr(a)).try_collect()?, }, + ExprKind::Param(id) => ExprKind::Param(id), ExprKind::Literal(_) => kind, }) diff --git a/prql-compiler/src/ast/rq/ids.rs b/prql-compiler/src/ast/rq/ids.rs index ecd27fb7245d..faced8329f5d 100644 --- a/prql-compiler/src/ast/rq/ids.rs +++ b/prql-compiler/src/ast/rq/ids.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; /// Column id -#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)] pub struct CId(usize); impl CId { diff --git a/prql-compiler/src/ast/rq/transform.rs b/prql-compiler/src/ast/rq/transform.rs index 7a946e1d2c13..875bc3d922b6 100644 --- a/prql-compiler/src/ast/rq/transform.rs +++ b/prql-compiler/src/ast/rq/transform.rs @@ -25,6 +25,7 @@ pub enum Transform { filter: Expr, }, Append(TableRef), + Loop(Vec), } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/prql-compiler/src/error.rs b/prql-compiler/src/error.rs index e9f9d9d78839..aa656e45fe71 100644 --- a/prql-compiler/src/error.rs +++ b/prql-compiler/src/error.rs @@ -6,9 +6,6 @@ use std::error::Error as StdError; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::{Add, Range}; -use crate::parser::PestError; -use crate::utils::IntoOnly; - #[derive(Clone, PartialEq, Eq, Copy, Serialize, Deserialize)] pub struct Span { pub start: usize, @@ -20,8 +17,12 @@ pub struct Error { pub span: Option, pub reason: Reason, pub help: Option, + pub code: Option<&'static str>, } +#[derive(Debug, Clone)] +pub struct Errors(pub Vec); + /// Location within the source file. /// Tuples contain: /// - line number (0-based), @@ -56,6 +57,7 @@ impl Error { span: None, reason, help: None, + code: None, } } @@ -72,10 +74,17 @@ impl Error { self.span = span; self } + + pub fn with_code(mut self, code: &'static str) -> Self { + self.code = Some(code); + self + } } -#[derive(Debug, Clone, Serialize)] +#[derive(Clone, Serialize)] pub struct ErrorMessage { + /// Machine-readable identifier of the error + pub code: Option, /// Plain text of the error pub reason: String, /// A list of suggestions of how to fix the error @@ -100,15 +109,28 @@ impl Display for ErrorMessage { .join("\n"); f.write_str(&message_without_trailing_spaces)?; } else { - f.write_str(&self.reason)?; + let code = (self.code.as_ref()) + .map(|c| format!("[{c}] ")) + .unwrap_or_default(); + + writeln!(f, "{}Error: {}", code, &self.reason)?; } Ok(()) } } +impl Debug for ErrorMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self, f) + } +} + // Needed for anyhow impl StdError for Error {} +// Needed for anyhow +impl StdError for Errors {} + // Needed for StdError impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -116,10 +138,18 @@ impl Display for Error { } } +// Needed for StdError +impl Display for Errors { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self, f) + } +} + #[derive(Debug, Clone, Serialize)] pub struct ErrorMessages { pub inner: Vec, } +impl StdError for ErrorMessages {} impl From for ErrorMessages { fn from(e: ErrorMessage) -> Self { @@ -137,6 +167,7 @@ impl Display for ErrorMessages { } pub fn downcast(error: anyhow::Error) -> ErrorMessages { + let mut code = None; let mut span = None; let mut hint = None; @@ -145,33 +176,35 @@ pub fn downcast(error: anyhow::Error) -> ErrorMessages { Err(error) => error, }; + let error = match error.downcast::() { + Ok(messages) => { + return ErrorMessages { + inner: messages + .0 + .into_iter() + .flat_map(|e| downcast(e.into()).inner) + .collect(), + } + } + Err(error) => error, + }; + let reason = match error.downcast::() { Ok(error) => { + code = error.code.map(|x| x.to_string()); span = error.span; hint = error.help; error.reason.message() } Err(error) => { - match error.downcast::() { - Ok(error) => { - let range = pest::as_range(&error); - span = Some(Span { - start: range.start, - end: range.end, - }); - - pest::as_message(&error) - } - Err(error) => { - // default to basic Display - format!("{:#?}", error) - } - } + // default to basic Display + format!("{:#?}", error) } }; ErrorMessage { + code, reason, hint, span, @@ -181,6 +214,12 @@ pub fn downcast(error: anyhow::Error) -> ErrorMessages { .into() } +impl From for ErrorMessages { + fn from(e: anyhow::Error) -> Self { + downcast(e) + } +} + impl ErrorMessages { pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap() @@ -199,14 +238,6 @@ impl ErrorMessages { } } -impl IntoOnly for ErrorMessages { - type Item = ErrorMessage; - - fn into_only(self) -> Result { - self.inner.into_only() - } -} - impl ErrorMessage { fn compose_display<'a, C>(&self, source_id: &'a str, cache: C, color: bool) -> Option where @@ -218,9 +249,12 @@ impl ErrorMessage { let mut report = Report::build(ReportKind::Error, source_id, span.start) .with_config(config) - .with_message("") .with_label(Label::new((source_id, span)).with_message(&self.reason)); + if let Some(code) = &self.code { + report = report.with_code(code); + } + if let Some(hint) = &self.hint { report.set_help(hint); } @@ -260,59 +294,6 @@ impl Reason { } } -mod pest { - use pest::error::{ErrorVariant, InputLocation}; - use std::ops::Range; - - use crate::parser::{PestError, PestRule}; - - pub fn as_range(error: &PestError) -> Range { - match error.location { - InputLocation::Pos(r) => r..r + 1, - InputLocation::Span(r) => r.0..r.1, - } - } - - pub fn as_message(error: &PestError) -> String { - match error.variant { - ErrorVariant::ParsingError { - ref positives, - ref negatives, - } => parsing_error_message(positives, negatives), - ErrorVariant::CustomError { ref message } => message.clone(), - } - } - - fn parsing_error_message(positives: &[PestRule], negatives: &[PestRule]) -> String { - match (negatives.is_empty(), positives.is_empty()) { - (false, false) => format!( - "unexpected {}; expected {}", - enumerate(negatives), - enumerate(positives) - ), - (false, true) => format!("unexpected {}", enumerate(negatives)), - (true, false) => format!("expected {}", enumerate(positives)), - (true, true) => "unknown parsing error".to_owned(), - } - } - - fn enumerate(rules: &[PestRule]) -> String { - match rules.len() { - 1 => format!("{:?}", rules[0]), - 2 => format!("{:?} or {:?}", rules[0], rules[1]), - l => { - let separated = rules - .iter() - .take(l - 1) - .map(|x| format!("{:?}", x)) - .collect::>() - .join(", "); - format!("{}, or {:?}", separated, rules[l - 1]) - } - } - } -} - impl From for Range { fn from(a: Span) -> Self { a.start..a.end diff --git a/prql-compiler/src/lib.rs b/prql-compiler/src/lib.rs index 2288b537b201..f88d4f133281 100644 --- a/prql-compiler/src/lib.rs +++ b/prql-compiler/src/lib.rs @@ -42,7 +42,7 @@ //! # fn main() -> Result<(), prql_compiler::ErrorMessages> { //! let sql = prql_compiler::compile( //! "from albums | select [title, artist_id]", -//! prql_compiler::Options::default().no_format() +//! &prql_compiler::Options::default().no_format() //! )?; //! assert_eq!(&sql[..35], "SELECT title, artist_id FROM albums"); //! # Ok(()) @@ -87,7 +87,6 @@ mod test; mod utils; pub use error::{downcast, Error, ErrorMessage, ErrorMessages, Reason, SourceLocation, Span}; -pub use utils::IntoOnly; use once_cell::sync::Lazy; use semver::Version; @@ -109,19 +108,19 @@ pub static PRQL_VERSION: Lazy = /// ``` /// use prql_compiler::{compile, Options, Target, sql::Dialect}; /// -/// let prql = "from employees | select [name,age] "; -/// let opt = Options { +/// let prql = "from employees | select [name,age]"; +/// let opts = Options { /// format: false, /// target: Target::Sql(Some(Dialect::SQLite)), /// signature_comment: false /// }; -/// let sql = compile(&prql, opt).unwrap(); +/// let sql = compile(&prql, &opts).unwrap(); /// println!("PRQL: {}\nSQLite: {}", prql, &sql); /// assert_eq!("SELECT name, age FROM employees", sql) /// /// ``` /// See [`sql::Options`](sql/struct.Options.html) and [`sql::Dialect`](sql/enum.Dialect.html) for options and supported SQL dialects. -pub fn compile(prql: &str, options: Options) -> Result { +pub fn compile(prql: &str, options: &Options) -> Result { parser::parse(prql) .and_then(semantic::resolve) .and_then(|rq| sql::compile(rq, options)) @@ -143,10 +142,12 @@ impl Default for Target { impl Target { pub fn names() -> Vec { - sql::Dialect::names() - .into_iter() - .map(|d| format!("sql.{d}")) - .collect() + let mut names = vec!["sql.any".to_string()]; + + let dialects = sql::Dialect::names(); + names.extend(dialects.into_iter().map(|d| format!("sql.{d}"))); + + names } } @@ -154,20 +155,20 @@ impl FromStr for Target { type Err = Error; fn from_str(s: &str) -> Result { - // We have a closure here because we can't create the error in the - // pipeline, since it needs to be in two places, and we'd need to clone. - // (Though possibly it's too optimize-y.) - let not_found_error = |s| { - Error::new(Reason::NotFound { - name: format!("{s:?}"), - namespace: "target".to_string(), - }) - }; - s.strip_prefix("sql.") - .ok_or_else(|| not_found_error(s)) - .map(sql::Dialect::from_str)? - .map(|x| Target::Sql(Some(x))) - .map_err(|_| not_found_error(s)) + if let Some(dialect) = s.strip_prefix("sql.") { + if dialect == "any" { + return Ok(Target::Sql(None)); + } + + if let Ok(dialect) = sql::Dialect::from_str(dialect) { + return Ok(Target::Sql(Some(dialect))); + } + } + + Err(Error::new(Reason::NotFound { + name: format!("{s:?}"), + namespace: "target".to_string(), + })) } } @@ -233,12 +234,12 @@ pub fn prql_to_pl(prql: &str) -> Result, ErrorMessages> { /// Perform semantic analysis and convert PL to RQ. pub fn pl_to_rq(pl: Vec) -> Result { - semantic::resolve(pl).map_err(error::downcast) + semantic::resolve(pl).map_err(|e| e.into()) } /// Generate SQL from RQ. -pub fn rq_to_sql(rq: ast::rq::Query, options: Options) -> Result { - sql::compile(rq, options).map_err(error::downcast) +pub fn rq_to_sql(rq: ast::rq::Query, options: &Options) -> Result { + sql::compile(rq, options).map_err(|e| e.into()) } /// Generate PRQL code from PL AST @@ -252,22 +253,22 @@ pub mod json { /// JSON serialization pub fn from_pl(pl: Vec) -> Result { - serde_json::to_string(&pl).map_err(|e| error::downcast(anyhow::anyhow!(e))) + serde_json::to_string(&pl).map_err(|e| anyhow::anyhow!(e).into()) } /// JSON deserialization pub fn to_pl(json: &str) -> Result, ErrorMessages> { - serde_json::from_str(json).map_err(|e| error::downcast(anyhow::anyhow!(e))) + serde_json::from_str(json).map_err(|e| anyhow::anyhow!(e).into()) } /// JSON serialization pub fn from_rq(rq: ast::rq::Query) -> Result { - serde_json::to_string(&rq).map_err(|e| error::downcast(anyhow::anyhow!(e))) + serde_json::to_string(&rq).map_err(|e| anyhow::anyhow!(e).into()) } /// JSON deserialization pub fn to_rq(json: &str) -> Result { - serde_json::from_str(json).map_err(|e| error::downcast(anyhow::anyhow!(e))) + serde_json::from_str(json).map_err(|e| anyhow::anyhow!(e).into()) } } @@ -298,6 +299,7 @@ mod tests { namespace: "target", }, help: None, + code: None, }, ) "###); @@ -311,6 +313,7 @@ mod tests { namespace: "target", }, help: None, + code: None, }, ) "###); diff --git a/prql-compiler/src/parser/chumsky.rs b/prql-compiler/src/parser/chumsky.rs deleted file mode 100644 index 95c031453c5e..000000000000 --- a/prql-compiler/src/parser/chumsky.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![allow(dead_code)] - -use chumsky::prelude::*; - -use crate::ast::pl::*; - -fn str(chars: &str) -> impl Parser> + '_ { - just(chars).to(()) -} - -fn operator() -> impl Parser> { - operator_binary().to(()).or(operator_unary().to(())) -} - -fn operator_binary() -> impl Parser> { - operator_mul() - .or(operator_add()) - .or(operator_compare()) - .or(operator_logical()) - .or(operator_coalesce()) -} -fn operator_unary() -> impl Parser> { - just('-') - .to(UnOp::Neg) - .or(just('+').to(UnOp::Add)) - .or(just('!').to(UnOp::Not)) - .or(str("==").to(UnOp::EqSelf)) -} -fn operator_mul() -> impl Parser> { - (just('*').to(BinOp::Mul)) - .or(just('/').to(BinOp::Div)) - .or(just('%').to(BinOp::Mod)) -} -fn operator_add() -> impl Parser> { - just('+').to(BinOp::Add).or(just('-').to(BinOp::Sub)) -} -fn operator_compare() -> impl Parser> { - str("==") - .to(BinOp::Eq) - .or(str("!=").to(BinOp::Ne)) - .or(str(">=").to(BinOp::Gte)) - .or(str("<=").to(BinOp::Lte)) - .or(str(">").to(BinOp::Gt)) - .or(str("<").to(BinOp::Lt)) -} -fn operator_logical() -> impl Parser> { - (just("and").to(BinOp::Add)) - .or(just("or").to(BinOp::Or)) - .then_ignore(text::whitespace()) -} -fn operator_coalesce() -> impl Parser> { - just("??").map(|_| BinOp::Coalesce) -} - -fn ident_part() -> impl Parser> { - let plain = filter(|c: &char| c.is_ascii_alphabetic() || *c == '_' || *c == '$') - .map(Some) - .chain::, _>( - filter(|c: &char| c.is_ascii_alphanumeric() || *c == '_').repeated(), - ) - .collect(); - - let backticks = just('`') - .ignore_then(filter(|c| *c != '`').repeated()) - .then_ignore(just('`')) - .collect::(); - - plain.or(backticks) -} - -pub fn ident() -> impl Parser> { - let star = just('*').map(|c| c.to_string()); - - // TODO: !operator ~ !(keyword ~ WHITESPACE) - // we probably need combinator::Not, which has not been released yet. - - ident_part() - .chain(just('.').ignore_then(ident_part().or(star)).repeated()) - .map(Ident::from_path::) -} diff --git a/prql-compiler/src/parser/expr.rs b/prql-compiler/src/parser/expr.rs new file mode 100644 index 000000000000..27840e0d26a4 --- /dev/null +++ b/prql-compiler/src/parser/expr.rs @@ -0,0 +1,315 @@ +use std::collections::HashMap; + +use chumsky::prelude::*; + +use crate::ast::pl::*; + +use super::common::*; +use super::interpolation; +use super::lexer::Token; + +pub fn expr_call() -> impl Parser> { + func_call(expr()) +} + +pub fn expr() -> impl Parser> + Clone { + recursive(|expr| { + let literal = select! { Token::Literal(lit) => ExprKind::Literal(lit) }; + + let ident_kind = ident().map(ExprKind::Ident); + + let nested_expr = pipeline(func_call(expr.clone())).boxed(); + + let list = ident_part() + .then_ignore(ctrl('=')) + .or_not() + .then(nested_expr.clone().map_with_span(into_expr)) + .map(|(alias, expr)| Expr { alias, ..expr }) + .padded_by(new_line().repeated()) + .separated_by(ctrl(',')) + .allow_trailing() + .then_ignore(new_line().repeated()) + .delimited_by(ctrl('['), ctrl(']')) + .recover_with(nested_delimiters( + Token::Control('['), + Token::Control(']'), + [ + (Token::Control('['), Token::Control(']')), + (Token::Control('('), Token::Control(')')), + ], + |_| vec![], + )) + .map(ExprKind::List) + .labelled("list"); + + let pipeline = + nested_expr + .delimited_by(ctrl('('), ctrl(')')) + .recover_with(nested_delimiters( + Token::Control('('), + Token::Control(')'), + [ + (Token::Control('['), Token::Control(']')), + (Token::Control('('), Token::Control(')')), + ], + |_| Expr::null().kind, + )); + + let interpolation = + select! { + Token::Interpolation('s', string) => (ExprKind::SString as fn(_) -> _, string), + Token::Interpolation('f', string) => (ExprKind::FString as fn(_) -> _, string), + } + .validate(|(finish, string), span: std::ops::Range, emit| { + match interpolation::parse(string, span.start + 2) { + Ok(items) => finish(items), + Err(errors) => { + for err in errors { + emit(err) + } + finish(vec![]) + } + } + }); + + let case = keyword("case") + .ignore_then( + func_call(expr.clone()) + .then_ignore(just(Token::ArrowDouble)) + .then(func_call(expr)) + .map(|(condition, value)| SwitchCase { condition, value }) + .padded_by(new_line().repeated()) + .separated_by(ctrl(',')) + .allow_trailing() + .then_ignore(new_line().repeated()) + .delimited_by(ctrl('['), ctrl(']')), + ) + .map(ExprKind::Case); + + let param = select! { Token::Param(id) => ExprKind::Param(id) }; + + let term = choice(( + literal, + list, + pipeline, + interpolation, + ident_kind, + case, + param, + )) + .map_with_span(into_expr) + .boxed(); + + // Unary operators + let term = term + .clone() + .or(operator_unary() + .then(term.map(Box::new)) + .map(|(op, expr)| ExprKind::Unary { op, expr }) + .map_with_span(into_expr)) + .boxed(); + + // Ranges have five cases we need to parse: + // x..y (bounded) + // x.. (only start bound) + // x (no-op) + // ..y (only end bound) + // .. (unbounded) + #[derive(Clone)] + enum RangeCase { + NoOp(Expr), + Range(Option, Option), + } + let term = choice(( + // with start bound (first 3 cases) + term.clone() + .then(choice(( + // range and end bound + just(Token::range(true, true)) + .ignore_then(term.clone()) + .map(|x| Some(Some(x))), + // range and no end bound + select! { Token::Range { bind_left: true, .. } => Some(None) }, + // no range + empty().to(None), + ))) + .map(|(start, range)| { + if let Some(end) = range { + RangeCase::Range(Some(start), end) + } else { + RangeCase::NoOp(start) + } + }), + // only end bound + select! { Token::Range { bind_right: true, .. } => () } + .ignore_then(term) + .map(|range| RangeCase::Range(None, Some(range))), + // unbounded + select! { Token::Range { .. } => RangeCase::Range(None, None) }, + )) + .map_with_span(|case, span| match case { + RangeCase::NoOp(x) => x, + RangeCase::Range(start, end) => { + let kind = ExprKind::Range(Range { + start: start.map(Box::new), + end: end.map(Box::new), + }); + into_expr(kind, span) + } + }) + .boxed(); + + // Binary operators + let expr = term; + let expr = binary_op_parser(expr, operator_mul()); + let expr = binary_op_parser(expr, operator_add()); + let expr = binary_op_parser(expr, operator_compare()); + let expr = binary_op_parser(expr, operator_coalesce()); + let expr = binary_op_parser(expr, operator_and()); + + binary_op_parser(expr, operator_or()) + }) +} + +pub fn pipeline(expr: E) -> impl Parser> +where + E: Parser>, +{ + // expr has to be a param, because it can be either a normal expr() or + // a recursive expr called from within expr() + + new_line() + .repeated() + .ignore_then( + expr.separated_by(ctrl('|').or(new_line().repeated().at_least(1).ignored())) + .at_least(1) + .map(|mut exprs| { + if exprs.len() == 1 { + exprs.remove(0).kind + } else { + ExprKind::Pipeline(Pipeline { exprs }) + } + }), + ) + .then_ignore(new_line().repeated()) + .labelled("pipeline") +} + +pub fn binary_op_parser<'a, Term, Op>( + term: Term, + op: Op, +) -> impl Parser> + 'a +where + Term: Parser> + 'a, + Op: Parser> + 'a, +{ + let term = term.map_with_span(|e, s| (e, s)).boxed(); + + (term.clone()) + .then(op.then(term).repeated()) + .foldl(|left, (op, right)| { + let span = left.1.start..right.1.end; + let kind = ExprKind::Binary { + left: Box::new(left.0), + op, + right: Box::new(right.0), + }; + (into_expr(kind, span.clone()), span) + }) + .map(|(e, _)| e) + .boxed() +} + +fn func_call(expr: E) -> impl Parser> +where + E: Parser> + Clone, +{ + let func = expr.clone(); + + let named_arg = ident_part() + .map(Some) + .then_ignore(ctrl(':')) + .then(expr.clone()); + + let assign_call = + ident_part() + .then_ignore(ctrl('=')) + .then(expr.clone()) + .map(|(alias, expr)| Expr { + alias: Some(alias), + ..expr + }); + let positional_arg = assign_call.or(expr).map(|expr| (None, expr)); + + let args = named_arg.or(positional_arg).repeated(); + + func.then(args) + .validate(|(name, args), span, emit| { + if args.is_empty() { + return name.kind; + } + + let mut named_args = HashMap::new(); + let mut positional = Vec::new(); + for (name, arg) in args { + if let Some(name) = name { + if named_args.contains_key(&name) { + let err = Simple::custom(span.clone(), "argument is used multiple times"); + emit(err) + } + named_args.insert(name, arg); + } else { + positional.push(arg); + } + } + + ExprKind::FuncCall(FuncCall { + name: Box::new(name), + args: positional, + named_args, + }) + }) + .map_with_span(into_expr) + .labelled("function call") +} + +pub fn ident() -> impl Parser> { + let star = ctrl('*').to("*".to_string()); + + ident_part() + .chain(ctrl('.').ignore_then(ident_part().or(star)).repeated()) + .map(Ident::from_path::) + .labelled("identifier") +} + +fn operator_unary() -> impl Parser> { + (ctrl('+').to(UnOp::Add)) + .or(ctrl('-').to(UnOp::Neg)) + .or(ctrl('!').to(UnOp::Not)) + .or(just(Token::Eq).to(UnOp::EqSelf)) +} +fn operator_mul() -> impl Parser> { + (ctrl('*').to(BinOp::Mul)) + .or(ctrl('/').to(BinOp::Div)) + .or(ctrl('%').to(BinOp::Mod)) +} +fn operator_add() -> impl Parser> { + (ctrl('+').to(BinOp::Add)).or(ctrl('-').to(BinOp::Sub)) +} +fn operator_compare() -> impl Parser> { + (just(Token::Eq).to(BinOp::Eq)) + .or(just(Token::Ne).to(BinOp::Ne)) + .or(just(Token::Lte).to(BinOp::Lte)) + .or(just(Token::Gte).to(BinOp::Gte)) + .or(ctrl('<').to(BinOp::Lt)) + .or(ctrl('>').to(BinOp::Gt)) +} +fn operator_and() -> impl Parser> { + just(Token::And).to(BinOp::And) +} +pub fn operator_or() -> impl Parser> { + just(Token::Or).to(BinOp::Or) +} +fn operator_coalesce() -> impl Parser> { + just(Token::Coalesce).to(BinOp::Coalesce) +} diff --git a/prql-compiler/src/parser/interpolation.rs b/prql-compiler/src/parser/interpolation.rs new file mode 100644 index 000000000000..2b3c75f92f5a --- /dev/null +++ b/prql-compiler/src/parser/interpolation.rs @@ -0,0 +1,55 @@ +use chumsky::{error::Cheap, prelude::*}; +use itertools::Itertools; + +use crate::ast::pl::*; + +use super::{common::into_expr, lexer::*}; + +/// Parses interpolated strings +pub fn parse( + string: String, + span_offset: usize, +) -> Result, Vec>> { + let res = parser(span_offset).parse(string); + + match res { + Ok(items) => Ok(items), + Err(errors) => Err(errors + .into_iter() + .map(|err| { + Simple::expected_input_found(offset_span(err.span(), span_offset), None, None) + }) + .collect_vec()), + } +} + +fn parser(span_offset: usize) -> impl Parser, Error = Cheap> { + let expr = ident_part() + .separated_by(just('.')) + .delimited_by(just('{'), just('}')) + .map(Ident::from_path) + .map(ExprKind::Ident) + .map_with_span(move |e, s| into_expr(e, offset_span(s, span_offset))) + .map(Box::new) + .map(InterpolateItem::Expr); + + let escape = (just("{{").to('{')) + .chain(just("}}").not().repeated()) + .chain(just("}}").to('}')) + .collect::() + .map(InterpolateItem::String); + + let string = none_of('{') + .repeated() + .at_least(1) + .collect::() + .map(InterpolateItem::String); + + escape.or(expr).or(string).repeated().then_ignore(end()) +} + +fn offset_span(mut span: std::ops::Range, span_offset: usize) -> std::ops::Range { + span.start += span_offset; + span.end += span_offset; + span +} diff --git a/prql-compiler/src/parser/lexer.rs b/prql-compiler/src/parser/lexer.rs new file mode 100644 index 000000000000..ed787d4dcf14 --- /dev/null +++ b/prql-compiler/src/parser/lexer.rs @@ -0,0 +1,420 @@ +use chumsky::{error::Cheap, prelude::*}; + +use crate::ast::pl::*; + +#[derive(Clone, PartialEq, Debug)] +pub enum Token { + NewLine, + + Ident(String), + Keyword(String), + Literal(Literal), + Param(String), + + Range { + bind_left: bool, + bind_right: bool, + }, + Interpolation(char, String), + + /// single-char control tokens + Control(char), + + // TODO: rename to ArrowThin + Arrow, // -> + // TODO: rename to ArrowFat + ArrowDouble, // => + Eq, // == + Ne, // != + Gte, // >= + Lte, // <= + And, // and + Or, // or + Coalesce, // ?? +} + +pub fn lexer() -> impl Parser)>, Error = Cheap> { + let new_line = just('\n').to(Token::NewLine); + let whitespace = one_of("\t \r").repeated().at_least(1).ignored(); + + let control_multi = choice(( + just("->").to(Token::Arrow), + just("=>").to(Token::ArrowDouble), + just("==").to(Token::Eq), + just("!=").to(Token::Ne), + just(">=").to(Token::Gte), + just("<=").to(Token::Lte), + just("and").then_ignore(end_expr()).to(Token::And), + just("or").then_ignore(end_expr()).to(Token::Or), + just("??").to(Token::Coalesce), + )); + + let control = one_of(">() + .map(Token::Param); + + // s-string and f-strings + let interpolation = one_of("sf") + .then(quoted_string(true)) + .map(|(c, s)| Token::Interpolation(c, s)); + + let token = choice(( + new_line.clone(), + control_multi, + interpolation, + param, + control, + literal, + keyword, + ident, + )) + .recover_with(skip_then_retry_until([]).skip_start()); + + let comment = just('#').then(none_of('\n').repeated()); + let comments = comment + .separated_by(new_line.then(whitespace.clone().or_not())) + .at_least(1) + .ignored(); + + let range = (whitespace.clone().or_not()) + .then_ignore(just("..")) + .then(whitespace.clone().or_not()) + .map(|(left, right)| Token::Range { + bind_left: left.is_none(), + bind_right: right.is_none(), + }) + .map_with_span(|tok, span| (tok, span)); + + // range needs to consume leading whitespace, + // so whitespace following a token must not be consumed + let ignored = comments.clone().or(whitespace).repeated(); + + comments + .or_not() + .ignore_then(choice(( + range, + ignored + .clone() + .ignore_then(token.map_with_span(|tok, span| (tok, span))), + ))) + .repeated() + .then_ignore(ignored) + .then_ignore(end()) +} + +pub fn ident_part() -> impl Parser> { + let plain = filter(|c: &char| c.is_alphabetic() || *c == '_') + .map(Some) + .chain::, _>(filter(|c: &char| c.is_alphanumeric() || *c == '_').repeated()) + .collect(); + + let backticks = just('`') + .ignore_then(none_of('`').repeated()) + .then_ignore(just('`')) + .collect::(); + + plain.or(backticks) +} + +fn literal() -> impl Parser> { + let exp = just('e').or(just('E')).ignore_then( + just('+') + .or(just('-')) + .or_not() + .chain::(text::digits(10)), + ); + + let integer = filter(|c: &char| c.is_ascii_digit() && *c != '0') + .chain::<_, Vec, _>(filter(|c: &char| c.is_ascii_digit() || *c == '_').repeated()) + .or(just('0').map(|c| vec![c])); + + let frac = just('.') + .chain::(filter(|c: &char| c.is_ascii_digit())) + .chain::(filter(|c: &char| c.is_ascii_digit() || *c == '_').repeated()); + + let number = just('+') + .or(just('-')) + .or_not() + .chain::(integer) + .chain::(frac.or_not().flatten()) + .chain::(exp.or_not().flatten()) + .try_map(|chars, span| { + let str = chars.into_iter().filter(|c| *c != '_').collect::(); + + if let Ok(i) = str.parse::() { + Ok(Literal::Integer(i)) + } else if let Ok(f) = str.parse::() { + Ok(Literal::Float(f)) + } else { + Err(Cheap::expected_input_found(span, None, None)) + } + }) + .labelled("number"); + + let string = quoted_string(true).map(Literal::String); + + let raw_string = just("r") + .ignore_then(quoted_string(false)) + .map(Literal::String); + + let bool = (just("true").to(true)) + .or(just("false").to(false)) + .then_ignore(end_expr()) + .map(Literal::Boolean); + + let null = just("null").to(Literal::Null).then_ignore(end_expr()); + + let value_and_unit = integer + .then(choice(( + just("microseconds"), + just("milliseconds"), + just("seconds"), + just("minutes"), + just("hours"), + just("days"), + just("weeks"), + just("months"), + just("years"), + ))) + .then_ignore(end_expr()) + .try_map(|(number, unit), span| { + let str = number.into_iter().filter(|c| *c != '_').collect::(); + if let Ok(n) = str.parse::() { + let unit = unit.to_string(); + Ok(ValueAndUnit { n, unit }) + } else { + Err(Cheap::expected_input_found(span, None, None)) + } + }) + .map(Literal::ValueAndUnit); + + let date_inner = digits(4) + .chain(just('-')) + .chain::(digits(2)) + .chain::(just('-')) + .chain::(digits(2)) + .boxed(); + + let time_inner = digits(2) + // minutes + .chain::(just(':').chain(digits(2)).or_not().flatten()) + // seconds + .chain::(just(':').chain(digits(2)).or_not().flatten()) + // milliseconds + .chain::( + just('.') + .chain( + filter(|c: &char| c.is_ascii_digit()) + .repeated() + .at_least(1) + .at_most(6), + ) + .or_not() + .flatten(), + ) + // timezone offset + .chain::( + one_of("-+") + .chain( + // TODO: This is repeated without the `:`~ with an `or` + // because using `.or_not` triggers a request for + // type hints, which seems difficult to provide... Is there + // an easier way? + // + // (digits(2).chain(just(':').or_not()).chain(digits(2))) + // + (digits(2).chain(just(':')).chain(digits(2))) + .or(digits(2).chain(digits(2))) + .or(just('Z').map(|x| vec![x])), + ) + .or_not() + .flatten(), + ) + .boxed(); + + let date = just('@') + .ignore_then(date_inner.clone()) + .then_ignore(end_expr()) + .collect::() + .map(Literal::Date); + + let time = just('@') + .ignore_then(time_inner.clone()) + .then_ignore(end_expr()) + .collect::() + .map(Literal::Time); + + let datetime = just('@') + .ignore_then(date_inner) + .chain(just('T')) + .chain::(time_inner) + .then_ignore(end_expr()) + .collect::() + .map(Literal::Timestamp); + + choice(( + string, + raw_string, + value_and_unit, + number, + bool, + null, + datetime, + date, + time, + )) +} + +fn quoted_string(escaped: bool) -> impl Parser> { + // I don't know how this could be simplified and implemented for n>3 in general + choice(( + quoted_string_inner(r#""""""""#, escaped), + quoted_string_inner(r#"""""""#, escaped), + quoted_string_inner(r#""""""#, escaped), + quoted_string_inner(r#"""""#, escaped), + quoted_string_inner(r#"""#, escaped), + quoted_string_inner(r#"''''''"#, escaped), + quoted_string_inner(r#"'''''"#, escaped), + quoted_string_inner(r#"''''"#, escaped), + quoted_string_inner(r#"'''"#, escaped), + quoted_string_inner(r#"'"#, escaped), + )) + .collect::() + .labelled("string") +} + +fn quoted_string_inner( + quotes: &str, + escaping: bool, +) -> impl Parser, Error = Cheap> + '_ { + let mut forbidden = just(quotes).boxed(); + + if escaping { + forbidden = just(quotes).or(just("\\")).boxed() + }; + + let mut inner = forbidden.not().boxed(); + + if escaping { + inner = inner + .or(just('\\').ignore_then( + just('\\') + .or(just('/')) + .or(just('"')) + .or(just('b').to('\x08')) + .or(just('f').to('\x0C')) + .or(just('n').to('\n')) + .or(just('r').to('\r')) + .or(just('t').to('\t')) + .or(just('u').ignore_then( + filter(|c: &char| c.is_ascii_hexdigit()) + .repeated() + .exactly(4) + .collect::() + .validate(|digits, span, emit| { + char::from_u32(u32::from_str_radix(&digits, 16).unwrap()) + .unwrap_or_else(|| { + emit(Cheap::expected_input_found(span, None, None)); + '\u{FFFD}' // unicode replacement character + }) + }), + )), + )) + .boxed(); + } + + inner.repeated().delimited_by(just(quotes), just(quotes)) +} + +fn digits(count: usize) -> impl Parser, Error = Cheap> { + filter(|c: &char| c.is_ascii_digit()) + .repeated() + .exactly(count) +} + +fn end_expr() -> impl Parser> { + choice((end(), one_of(",)]\r\n\t ").ignored(), just("..").ignored())).rewind() +} + +impl Token { + pub fn range(bind_left: bool, bind_right: bool) -> Self { + Token::Range { + bind_left, + bind_right, + } + } +} + +// This is here because Literal::Float(f64) does not implement Hash, so we cannot simply derive it. +// There are reasons for that, but chumsky::Error needs Hash for the Token, so it can deduplicate +// tokens in error. +// So this hack could lead to duplicated tokens in error messages. Oh no. +#[allow(clippy::derive_hash_xor_eq)] +impl std::hash::Hash for Token { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + } +} + +impl std::cmp::Eq for Token {} + +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NewLine => write!(f, "new line"), + Self::Ident(arg0) => { + if arg0.is_empty() { + write!(f, "an identifier") + } else { + write!(f, "`{arg0}`") + } + } + Self::Keyword(arg0) => write!(f, "keyword {arg0}"), + Self::Literal(arg0) => write!(f, "{arg0}"), + Self::Control(arg0) => write!(f, "{arg0}"), + + Self::Arrow => f.write_str("->"), + Self::ArrowDouble => f.write_str("=>"), + Self::Eq => f.write_str("=="), + Self::Ne => f.write_str("!="), + Self::Gte => f.write_str(">="), + Self::Lte => f.write_str("<="), + Self::And => f.write_str("and"), + Self::Or => f.write_str("or"), + Self::Coalesce => f.write_str("??"), + + Self::Param(id) => write!(f, "${id}"), + + Self::Range { + bind_left, + bind_right, + } => write!( + f, + "'{}..{}'", + if *bind_left { "" } else { " " }, + if *bind_right { "" } else { " " } + ), + Self::Interpolation(c, s) => { + write!(f, "{c}\"{}\"", s) + } + } + } +} diff --git a/prql-compiler/src/parser/mod.rs b/prql-compiler/src/parser/mod.rs index 12aad8385407..429c5fdd3b30 100644 --- a/prql-compiler/src/parser/mod.rs +++ b/prql-compiler/src/parser/mod.rs @@ -1,458 +1,213 @@ -//! This module contains the parser, which is responsible for converting a tree -//! of pest pairs into a tree of AST Items. It has a small function to call into -//! pest to get the parse tree / concrete syntax tree, and then a large -//! function for turning that into PRQL AST. -mod chumsky; - -use std::collections::HashMap; -use std::str::FromStr; - -use anyhow::bail; -use anyhow::{anyhow, Result}; +mod expr; +mod interpolation; +mod lexer; +mod stmt; + +use anyhow::Result; +use chumsky::{ + error::{Cheap, SimpleReason}, + prelude::*, + Stream, +}; use itertools::Itertools; -use pest::iterators::Pair; -use pest::iterators::Pairs; -use pest::Parser; -use pest_derive::Parser; -use super::ast::pl::*; -use super::utils::*; -use crate::error::Span; +use self::lexer::Token; -#[derive(Parser)] -#[grammar = "parser/prql.pest"] -struct PrqlParser; +use super::ast::pl::*; -pub(crate) type PestError = pest::error::Error; -pub(crate) type PestRule = Rule; +use crate::error::{Error, Errors, Reason}; /// Build PL AST from a PRQL query string. -pub fn parse(string: &str) -> Result> { - let pairs = parse_tree_of_str(string, Rule::statements)?; - - stmts_of_parse_pairs(pairs) -} - -/// Parse a string into a parse tree / concrete syntax tree, made up of pest Pairs. -fn parse_tree_of_str(source: &str, rule: Rule) -> Result> { - Ok(PrqlParser::parse(rule, source)?) -} +pub fn parse(source: &str) -> Result> { + let mut errors = Vec::new(); -/// Parses a parse tree of pest Pairs into a list of statements. -fn stmts_of_parse_pairs(pairs: Pairs) -> Result> { - pairs - .filter(|p| !matches!(p.as_rule(), Rule::EOI)) - .map(stmt_of_parse_pair) - .collect() -} + let (tokens, lex_errors) = ::chumsky::Parser::parse_recovery(&lexer::lexer(), source); -fn stmt_of_parse_pair(pair: Pair) -> Result { - let span = pair.as_span(); - let rule = pair.as_rule(); + errors.extend( + lex_errors + .into_iter() + .map(|e| convert_lexer_error(source, e)), + ); - let kind = match rule { - Rule::pipeline_stmt => { - let pipeline = expr_of_parse_pair(pair.into_inner().next().unwrap())?; - StmtKind::Main(Box::new(pipeline)) - } - Rule::query_def => { - let mut params: HashMap<_, _> = pair - .into_inner() - .map(|x| parse_named(x.into_inner())) - .try_collect()?; - - let version = params - .remove("version") - .map(|v| v.try_cast(|i| i.parse_version(), None, "semver version number string")) - .transpose()?; - - let other = params - .into_iter() - .flat_map(|(key, value)| match value.kind { - ExprKind::Ident(value) => Some((key, value.to_string())), - _ => None, - }) - .collect(); - - StmtKind::QueryDef(QueryDef { version, other }) - } - Rule::func_def => { - let mut pairs = pair.into_inner(); - let name = pairs.next().unwrap(); - let params = pairs.next().unwrap(); - let body = pairs.next().unwrap(); - - let (name, return_type, _) = parse_typed_ident(name)?; - - let params: Vec<_> = params - .into_inner() - .into_iter() - .map(parse_typed_ident) - .try_collect()?; - - let mut positional_params = vec![]; - let mut named_params = vec![]; - for (name, ty, default_value) in params { - let param = FuncParam { - name, - ty, - default_value, - }; - if param.default_value.is_some() { - named_params.push(param) - } else { - positional_params.push(param) - } - } + let ast = if let Some(tokens) = tokens { + let len = source.chars().count(); + let stream = Stream::from_iter(len..len + 1, tokens.into_iter()); - StmtKind::FuncDef(FuncDef { - name, - positional_params, - named_params, - body: Box::from(expr_of_parse_pair(body)?), - return_ty: return_type, - }) - } - Rule::var_def => { - let mut pairs = pair.into_inner(); + let (ast, parse_errors) = ::chumsky::Parser::parse_recovery(&stmt::source(), stream); - let name = parse_ident_part(pairs.next().unwrap()); - let value = Box::new(expr_of_parse_pair(pairs.next().unwrap())?); + errors.extend(parse_errors.into_iter().map(convert_parser_error)); - StmtKind::VarDef(VarDef { name, value }) - } - _ => unreachable!("{pair}"), + ast + } else { + None }; - let mut stmt = Stmt::from(kind); - stmt.span = Some(Span { - start: span.start(), - end: span.end(), - }); - Ok(stmt) -} -/// Parses a parse tree of pest Pairs into an AST. -fn exprs_of_parse_pairs(pairs: Pairs) -> Result> { - pairs - .filter(|p| !matches!(p.as_rule(), Rule::EOI)) - .map(expr_of_parse_pair) - .collect() + if errors.is_empty() { + Ok(ast.unwrap_or_default()) + } else { + Err(Errors(errors).into()) + } } -fn expr_of_parse_pair(pair: Pair) -> Result { - let span = pair.as_span(); - let rule = pair.as_rule(); - let mut alias = None; - - let kind = match rule { - Rule::list => ExprKind::List(exprs_of_parse_pairs(pair.into_inner())?), - Rule::expr_mul | Rule::expr_add | Rule::expr_compare | Rule::expr => { - let mut pairs = pair.into_inner(); - - let mut expr = expr_of_parse_pair(pairs.next().unwrap())?; - if let Some(op) = pairs.next() { - let op = BinOp::from_str(op.as_str())?; - - expr = Expr::from(ExprKind::Binary { - op, - left: Box::new(expr), - right: Box::new(expr_of_parse_pair(pairs.next().unwrap())?), - }); - } - - expr.kind - } - Rule::expr_unary => { - let mut pairs = pair.into_inner(); - - let op = pairs.next().unwrap(); - - let expr = expr_of_parse_pair(pairs.next().unwrap())?; - let op = UnOp::from_str(op.as_str()).unwrap(); - match op { - UnOp::Add => expr.kind, - _ => ExprKind::Unary { - op, - expr: Box::new(expr), - }, - } - } - - // With coalesce, we need to grab the left and the right, - // because we're transforming it into a function call rather - // than passing along the operator. So this is unlike the rest - // of the parsing (and maybe isn't optimal). - Rule::expr_coalesce => { - let mut pairs = pair.into_inner(); - let left = expr_of_parse_pair(pairs.next().unwrap())?; - - if pairs.next().is_none() { - // If there's no coalescing, just return the single expression. - left.kind - } else { - let right = expr_of_parse_pair(pairs.next().unwrap())?; - ExprKind::Binary { - left: Box::new(left), - op: BinOp::Coalesce, - right: Box::new(right), - } - } - } - // This makes the previous parsing a bit easier, but is hacky; - // ideally find a better way (but it doesn't seem that easy to - // parse parts of a Pairs). - Rule::operator_coalesce => ExprKind::Ident(Ident::from_name("-")), - - Rule::assign | Rule::alias => { - let (a, expr) = parse_named(pair.into_inner())?; - alias = Some(a); - expr.kind - } - Rule::func_call => { - let mut pairs = pair.into_inner(); - - let name = expr_of_parse_pair(pairs.next().unwrap())?; - - let mut named = HashMap::new(); - let mut positional = Vec::new(); - for arg in pairs { - match arg.as_rule() { - Rule::named_arg => { - let (a, expr) = parse_named(arg.into_inner())?; - named.insert(a, expr); - } - _ => { - positional.push(expr_of_parse_pair(arg)?); - } - } - } +fn convert_lexer_error(source: &str, e: Cheap) -> Error { + // TODO: is there a neater way of taking a span? We want to take it based on + // the chars, not the bytes, so can't just index into the str. + let found = source + .chars() + .skip(e.span().start) + .take(e.span().end() - e.span().start) + .collect(); + let span = common::into_span(e.span()); + + Error::new(Reason::Unexpected { found }).with_span(span) +} - ExprKind::FuncCall(FuncCall { - name: Box::new(name), - args: positional, - named_args: named, - }) - } - Rule::jinja => { - let inner = pair.as_str(); - ExprKind::Ident(Ident::from_name(inner)) - } - Rule::ident => { - // Pest has already parsed, so Chumsky should never fail - let ident = ::chumsky::Parser::parse(&chumsky::ident(), pair.as_str()).unwrap(); +fn convert_parser_error(e: Simple) -> Error { + let span = common::into_span(e.span()); - ExprKind::Ident(ident) - } + if let SimpleReason::Custom(message) = e.reason() { + return Error::new_simple(message).with_span(span); + } - Rule::number => { - // pest is responsible for ensuring these are in the correct place, - // so we just need to remove them. - let str = pair.as_str().replace('_', ""); - - let lit = if let Ok(i) = str.parse::() { - Literal::Integer(i) - } else if let Ok(f) = str.parse::() { - Literal::Float(f) - } else { - bail!("cannot parse {str} as number") - }; - ExprKind::Literal(lit) - } - Rule::null => ExprKind::Literal(Literal::Null), - Rule::boolean => ExprKind::Literal(Literal::Boolean(pair.as_str() == "true")), - Rule::string => { - // Takes the string_inner, without the quotes - let inner = pair.into_inner().into_only(); - let inner = inner.map(|x| x.as_str().to_string()).unwrap_or_default(); - ExprKind::Literal(Literal::String(inner)) - } - Rule::s_string => ExprKind::SString(ast_of_interpolate_items(pair)?), - Rule::f_string => ExprKind::FString(ast_of_interpolate_items(pair)?), - Rule::pipeline => { - let mut nodes = exprs_of_parse_pairs(pair.into_inner())?; - match nodes.len() { - 0 => unreachable!(), - 1 => nodes.remove(0).kind, - _ => ExprKind::Pipeline(Pipeline { exprs: nodes }), - } - } - Rule::nested_pipeline => { - if let Some(pipeline) = pair.into_inner().next() { - expr_of_parse_pair(pipeline)?.kind - } else { - ExprKind::Literal(Literal::Null) + let is_all_whitespace = e + .expected() + .all(|t| matches!(t, None | Some(Token::NewLine))); + let expecteds = e + .expected() + // TODO: could we collapse this into a `filter_map`? (though semantically + // identical) + // + // Only include whitespace if we're _only_ expecting whitespace + .filter(|t| is_all_whitespace || !matches!(t, None | Some(Token::NewLine))) + .map(|t| { + t.clone() + .map(|t| t.to_string()) + .unwrap_or_else(|| "end of input".to_string()) + }) + .collect_vec(); + + let expected = if expecteds.is_empty() || expecteds.len() > 10 { + return Error::new(Reason::Unexpected { + found: e + .found() + .map(|c| c.to_string()) + // I think a rare case where we have both no `expected` and no `found`. + // Would be good to know how often this happens; can improve if we are + // hitting it. + .unwrap_or_else(|| "end of input".to_string()), + }) + .with_span(span); + } else { + let mut expecteds = expecteds; + expecteds.sort(); + + match expecteds.len() { + 1 => expecteds.remove(0), + 2 => expecteds.join(" or "), + _ => { + let last = expecteds.pop().unwrap(); + format!("one of {} or {last}", expecteds.join(", ")) } } - Rule::range => { - let [start, end]: [Option>; 2] = pair - .into_inner() - // Iterate over `start` & `end` (separator is not a term). - .into_iter() - .map(|x| { - // Parse & Box each one. - exprs_of_parse_pairs(x.into_inner()) - .and_then(|x| x.into_only()) - .map(Box::new) - .ok() - }) - .collect::>() - .try_into() - .map_err(|e| anyhow!("Expected start, separator, end; {e:?}"))?; - ExprKind::Range(Range { start, end }) - } - - Rule::value_and_unit => { - let pairs: Vec<_> = pair.into_inner().into_iter().collect(); - let [n, unit]: [Pair; 2] = pairs - .try_into() - .map_err(|e| anyhow!("Expected two items; {e:?}"))?; - - ExprKind::Literal(Literal::ValueAndUnit(ValueAndUnit { - n: n.as_str().parse()?, - unit: unit.as_str().to_owned(), - })) - } - - Rule::date | Rule::time | Rule::timestamp => { - let inner = pair.into_inner().into_only()?.as_str().to_string(); - - ExprKind::Literal(match rule { - Rule::date => Literal::Date(inner), - Rule::time => Literal::Time(inner), - Rule::timestamp => Literal::Timestamp(inner), - _ => unreachable!(), - }) - } - - Rule::switch => { - let cases = pair - .into_inner() - .map(|pair| -> Result<_> { - let [condition, value]: [Expr; 2] = pair - .into_inner() - .map(expr_of_parse_pair) - .collect::>>()? - .try_into() - .unwrap(); - Ok(SwitchCase { condition, value }) - }) - .try_collect()?; - - ExprKind::Switch(cases) - } - - _ => unreachable!("{pair}"), }; - Ok(Expr { - span: Some(Span { - start: span.start(), - end: span.end(), + + match e.found() { + Some(found) => Error::new(Reason::Expected { + who: e.label().map(|x| x.to_string()), + expected, + found: found.to_string(), }), - alias, - ..Expr::from(kind) - }) + // We want a friendlier message than "found end of input"... + None => Error::new(Reason::Simple(format!( + "Expected {expected}, but didn't find anything before the end." + ))), + } + .with_span(span) } -fn type_of_parse_pair(pair: Pair) -> Result { - let any_of_terms: Vec<_> = pair - .into_inner() - .into_iter() - .map(|pair| -> Result { - let mut pairs = pair.into_inner(); - let name = parse_ident_part(pairs.next().unwrap()); - let typ = match TyLit::from_str(&name) { - Ok(t) => Ty::from(t), - Err(_) if name == "table" => Ty::Table(Frame::default()), - Err(_) => { - eprintln!("named type: {}", name); - Ty::Named(name.to_string()) - } - }; - - let param = pairs.next().map(type_of_parse_pair).transpose()?; - - Ok(if let Some(param) = param { - Ty::Parameterized(Box::new(typ), Box::new(param)) - } else { - typ - }) - }) - .try_collect()?; - - // is there only a single element? - Ok(match <[_; 1]>::try_from(any_of_terms) { - Ok([only]) => only, - Err(many) => Ty::AnyOf(many), - }) -} +mod common { + use chumsky::prelude::*; -fn parse_typed_ident(pair: Pair) -> Result<(String, Option, Option)> { - let mut pairs = pair.into_inner(); + use super::lexer::Token; + use crate::{ast::pl::*, Span}; - let name = parse_ident_part(pairs.next().unwrap()); + pub fn ident_part() -> impl Parser> { + select! { Token::Ident(ident) => ident }.map_err(|e: Simple| { + Simple::expected_input_found( + e.span(), + [Some(Token::Ident("".to_string()))], + e.found().cloned(), + ) + }) + } - let mut ty = None; - let mut default = None; - for pair in pairs { - if matches!(pair.as_rule(), Rule::type_def) { - ty = Some(type_of_parse_pair(pair)?); - } else { - default = Some(expr_of_parse_pair(pair)?); - } + pub fn keyword(kw: &'static str) -> impl Parser> + Clone { + just(Token::Keyword(kw.to_string())).ignored() } - Ok((name, ty, default)) -} + pub fn new_line() -> impl Parser> + Clone { + just(Token::NewLine).ignored() + } -fn parse_named(mut pairs: Pairs) -> Result<(String, Expr)> { - let alias = parse_ident_part(pairs.next().unwrap()); - let expr = expr_of_parse_pair(pairs.next().unwrap())?; + pub fn ctrl(char: char) -> impl Parser> + Clone { + just(Token::Control(char)).ignored() + } - Ok((alias, expr)) -} + pub fn into_stmt(kind: StmtKind, span: std::ops::Range) -> Stmt { + Stmt { + span: into_span(span), + ..Stmt::from(kind) + } + } -fn parse_ident_part(pair: Pair) -> String { - pair.into_inner().next().unwrap().as_str().to_string() -} + pub fn into_expr(kind: ExprKind, span: std::ops::Range) -> Expr { + Expr { + span: into_span(span), + ..Expr::from(kind) + } + } -fn ast_of_interpolate_items(pair: Pair) -> Result> { - pair.into_inner() - .map(|x| { - Ok(match x.as_rule() { - Rule::interpolate_string_inner_literal => { - InterpolateItem::String(x.as_str().to_string()) - } - Rule::double_open_bracket => InterpolateItem::String("{".to_string()), - Rule::double_close_bracket => InterpolateItem::String("}".to_string()), - _ => InterpolateItem::Expr(Box::new(expr_of_parse_pair(x)?)), - }) + pub fn into_span(span: std::ops::Range) -> Option { + Some(Span { + start: span.start, + end: span.end, }) - .collect::>() + } } #[cfg(test)] mod test { use super::*; - use insta::assert_yaml_snapshot; - - fn stmts_of_string(string: &str) -> Result> { - let pairs = parse_tree_of_str(string, Rule::statements)?; - - stmts_of_parse_pairs(pairs) + use anyhow::anyhow; + use insta::{assert_debug_snapshot, assert_yaml_snapshot}; + + fn parse_expr(source: &str) -> Result> { + let tokens = Parser::parse(&lexer::lexer(), source).map_err(|errs| { + errs.into_iter() + .map(|e| anyhow!(convert_lexer_error(source, e))) + .collect_vec() + })?; + + let len = source.chars().count(); + let stream = Stream::from_iter(len..len + 1, tokens.into_iter()); + Parser::parse(&expr::expr_call().then_ignore(end()), stream) + .map_err(|errs| errs.into_iter().map(|e| anyhow!(e)).collect_vec()) } - fn expr_of_string(string: &str, rule: Rule) -> Result { - let mut pairs = parse_tree_of_str(string, rule)?; - - expr_of_parse_pair(pairs.next().unwrap()) + #[test] + fn test_pipeline_parse_tree() { + assert_yaml_snapshot!(parse(include_str!( + "../../examples/compile-files/queries/variables.prql" + )) + .unwrap()); } #[test] - fn test_parse_take() -> Result<()> { - parse_tree_of_str("take 10", Rule::statements)?; + fn test_take() { + parse("take 10").unwrap(); - assert_yaml_snapshot!(stmts_of_string(r#"take 10"#)?, @r###" + assert_yaml_snapshot!(parse(r#"take 10"#).unwrap(), @r###" --- - Main: FuncCall: @@ -462,10 +217,24 @@ mod test { args: - Literal: Integer: 10 - named_args: {} "###); - assert_yaml_snapshot!(stmts_of_string(r#"take 1..10"#)?, @r###" + assert_yaml_snapshot!(parse(r#"take ..10"#).unwrap(), @r###" + --- + - Main: + FuncCall: + name: + Ident: + - take + args: + - Range: + start: ~ + end: + Literal: + Integer: 10 + "###); + + assert_yaml_snapshot!(parse(r#"take 1..10"#).unwrap(), @r###" --- - Main: FuncCall: @@ -480,44 +249,128 @@ mod test { end: Literal: Integer: 10 - named_args: {} "###); - - Ok(()) } #[test] - fn test_parse_pipeline_parse_tree() { - assert_yaml_snapshot!(stmts_of_parse_pairs( - parse_tree_of_str( - // It's useful to have canonical examples rather than copy-pasting - // everything, so we reference the prql file here. But a downside of - // this implementation is: if there's an error in extracting the - // example from the docs into the file specified here, this test - // won't compile. Because `cargo insta test --accept` on the - // workspace โ€” which extracts the example โ€” requires compiling this, - // we can get stuck. - // - // Breaking out of that requires running this `cargo insta test - // --accept` within `book`, and then running it on the workspace. - // `task test-all` does this. - // - // If we change this, it would great if we can retain having - // examples tested in the docs. - &include_str!("../../../book/tests/prql/examples/variables-0.prql") - .trim() - // Required for Windows - .replace("\r\n", "\n"), - Rule::statements - ) - .unwrap() - ) - .unwrap()); + fn test_ranges() { + assert_yaml_snapshot!(parse_expr(r#"3..5"#).unwrap(), @r###" + --- + Range: + start: + Literal: + Integer: 3 + end: + Literal: + Integer: 5 + "###); + + assert_yaml_snapshot!(parse_expr(r#"-2..-5"#).unwrap(), @r###" + --- + Range: + start: + Unary: + op: Neg + expr: + Literal: + Integer: 2 + end: + Unary: + op: Neg + expr: + Literal: + Integer: 5 + "###); + + assert_yaml_snapshot!(parse_expr(r#"(-2..(-5 | abs))"#).unwrap(), @r###" + --- + Range: + start: + Unary: + op: Neg + expr: + Literal: + Integer: 2 + end: + Pipeline: + exprs: + - Unary: + op: Neg + expr: + Literal: + Integer: 5 + - Ident: + - abs + "###); + + assert_yaml_snapshot!(parse_expr(r#"(2 + 5)..'a'"#).unwrap(), @r###" + --- + Range: + start: + Binary: + left: + Literal: + Integer: 2 + op: Add + right: + Literal: + Integer: 5 + end: + Literal: + String: a + "###); + + assert_yaml_snapshot!(parse_expr(r#"1.6..rel.col"#).unwrap(), @r###" + --- + Range: + start: + Literal: + Float: 1.6 + end: + Ident: + - rel + - col + "###); + + assert_yaml_snapshot!(parse_expr(r#"6.."#).unwrap(), @r###" + --- + Range: + start: + Literal: + Integer: 6 + end: ~ + "###); + assert_yaml_snapshot!(parse_expr(r#"..7"#).unwrap(), @r###" + --- + Range: + start: ~ + end: + Literal: + Integer: 7 + "###); + + assert_yaml_snapshot!(parse_expr(r#".."#).unwrap(), @r###" + --- + Range: + start: ~ + end: ~ + "###); + + assert_yaml_snapshot!(parse_expr(r#"@2020-01-01..@2021-01-01"#).unwrap(), @r###" + --- + Range: + start: + Literal: + Date: 2020-01-01 + end: + Literal: + Date: 2021-01-01 + "###); } #[test] - fn test_parse_basic_exprs() -> Result<()> { - assert_yaml_snapshot!(expr_of_string(r#"country == "USA""#, Rule::expr)?, @r###" + fn test_basic_exprs() { + assert_yaml_snapshot!(parse_expr(r#"country == "USA""#).unwrap(), @r###" --- Binary: left: @@ -528,7 +381,7 @@ mod test { Literal: String: USA "###); - assert_yaml_snapshot!(expr_of_string("select [a, b, c]", Rule::func_call)?, @r###" + assert_yaml_snapshot!(parse_expr("select [a, b, c]").unwrap(), @r###" --- FuncCall: name: @@ -542,14 +395,12 @@ mod test { - b - Ident: - c - named_args: {} "###); - assert_yaml_snapshot!(expr_of_string( + assert_yaml_snapshot!(parse_expr( "group [title, country] ( aggregate [sum salary] - )", - Rule::pipeline - )?, @r###" + )" + ).unwrap(), @r###" --- FuncCall: name: @@ -574,14 +425,10 @@ mod test { args: - Ident: - salary - named_args: {} - named_args: {} - named_args: {} "###); - assert_yaml_snapshot!(expr_of_string( - r#" filter country == "USA""#, - Rule::pipeline - )?, @r###" + assert_yaml_snapshot!(parse_expr( + r#" filter country == "USA""# + ).unwrap(), @r###" --- FuncCall: name: @@ -596,9 +443,8 @@ mod test { right: Literal: String: USA - named_args: {} "###); - assert_yaml_snapshot!(expr_of_string("[a, b, c,]", Rule::list)?, @r###" + assert_yaml_snapshot!(parse_expr("[a, b, c,]").unwrap(), @r###" --- List: - Ident: @@ -608,13 +454,12 @@ mod test { - Ident: - c "###); - assert_yaml_snapshot!(expr_of_string( + assert_yaml_snapshot!(parse_expr( r#"[ gross_salary = salary + payroll_tax, gross_cost = gross_salary + benefits_cost -]"#, - Rule::list - )?, @r###" +]"# + ).unwrap(), @r###" --- List: - Binary: @@ -637,10 +482,10 @@ mod test { alias: gross_cost "###); // Currently not putting comments in our parse tree, so this is blank. - assert_yaml_snapshot!(stmts_of_string( + assert_yaml_snapshot!(parse( r#"# this is a comment select a"# - )?, @r###" + ).unwrap(), @r###" --- - Main: FuncCall: @@ -650,12 +495,10 @@ mod test { args: - Ident: - a - named_args: {} "###); - assert_yaml_snapshot!(expr_of_string( - "join side:left country [id==employee_id]", - Rule::func_call - )?, @r###" + assert_yaml_snapshot!(parse_expr( + "join side:left country [id==employee_id]" + ).unwrap(), @r###" --- FuncCall: name: @@ -678,7 +521,7 @@ mod test { Ident: - left "###); - assert_yaml_snapshot!(expr_of_string("1 + 2", Rule::expr)?, @r###" + assert_yaml_snapshot!(parse_expr("1 + 2").unwrap(), @r###" --- Binary: left: @@ -688,101 +531,73 @@ mod test { right: Literal: Integer: 2 - "###); - Ok(()) + "###) } #[test] - fn test_parse_string() -> Result<()> { - let double_quoted_ast = expr_of_string(r#"" U S A ""#, Rule::string)?; + fn test_string() { + let double_quoted_ast = parse_expr(r#"" U S A ""#).unwrap(); assert_yaml_snapshot!(double_quoted_ast, @r###" --- Literal: String: " U S A " "###); - let single_quoted_ast = expr_of_string(r#"' U S A '"#, Rule::string)?; + let single_quoted_ast = parse_expr(r#"' U S A '"#).unwrap(); assert_eq!(single_quoted_ast, double_quoted_ast); // Single quotes within double quotes should produce a string containing // the single quotes (and vice versa). - assert_yaml_snapshot!(expr_of_string(r#""' U S A '""#, Rule::string)? , @r###" + assert_yaml_snapshot!(parse_expr(r#""' U S A '""#).unwrap(), @r###" --- Literal: String: "' U S A '" "###); - assert_yaml_snapshot!(expr_of_string(r#"'" U S A "'"#, Rule::string)? , @r###" + assert_yaml_snapshot!(parse_expr(r#"'" U S A "'"#).unwrap(), @r###" --- Literal: String: "\" U S A \"" "###); - assert!(expr_of_string(r#"" U S A"#, Rule::string).is_err()); - assert!(expr_of_string(r#"" U S A '"#, Rule::string).is_err()); + parse_expr(r#"" U S A"#).unwrap_err(); + parse_expr(r#"" U S A '"#).unwrap_err(); - // Escapes get passed through (the insta snapshot has them escaped I - // think, which isn't that clear, so repeated below). - let escaped_string = expr_of_string(r#"" \U S A ""#, Rule::string)?; - assert_yaml_snapshot!(escaped_string, @r###" + assert_yaml_snapshot!(parse_expr(r#"" \nU S A ""#).unwrap(), @r###" --- Literal: - String: " \\U S A " - "###); - assert_eq!( - escaped_string - .kind - .as_literal() - .unwrap() - .as_string() - .unwrap(), - r#" \U S A "# - ); + String: " \nU S A " + "###); - // Currently we don't allow escaping closing quotes โ€”ย because it's not - // trivial to do in pest, and I'm not sure it's a great idea either โ€”ย we - // should arguably encourage multiline-strings. (Though no objection if - // someone wants to implement it, this test is recording current - // behavior rather than maintaining a contract). - let escaped_quotes = expr_of_string(r#"" Canada \""#, Rule::string)?; - assert_yaml_snapshot!(escaped_quotes, @r###" + assert_yaml_snapshot!(parse_expr(r#"r" \nU S A ""#).unwrap(), @r###" --- Literal: - String: " Canada \\" - "###); - assert_eq!( - escaped_quotes - .kind - .as_literal() - .unwrap() - .as_string() - .unwrap(), - r#" Canada \"# - ); + String: " \\nU S A " + "###); - let multi_double = expr_of_string( + let multi_double = parse_expr( r#"""" '' Canada " """"#, - Rule::string, - )?; + ) + .unwrap(); assert_yaml_snapshot!(multi_double, @r###" --- Literal: String: "\n''\nCanada\n\"\n\n" "###); - let multi_single = expr_of_string( + let multi_single = parse_expr( r#"''' Canada " """ '''"#, - Rule::string, - )?; + ) + .unwrap(); assert_yaml_snapshot!(multi_single, @r###" --- Literal: @@ -790,19 +605,17 @@ Canada "###); assert_yaml_snapshot!( - expr_of_string("''", Rule::string).unwrap(), + parse_expr("''").unwrap(), @r###" --- Literal: String: "" "###); - - Ok(()) } #[test] - fn test_parse_s_string() -> Result<()> { - assert_yaml_snapshot!(expr_of_string(r#"s"SUM({col})""#, Rule::expr_call)?, @r###" + fn test_s_string() { + assert_yaml_snapshot!(parse_expr(r#"s"SUM({col})""#).unwrap(), @r###" --- SString: - String: SUM( @@ -811,83 +624,43 @@ Canada - col - String: ) "###); - assert_yaml_snapshot!(expr_of_string(r#"s"SUM({2 + 2})""#, Rule::expr_call)?, @r###" + assert_yaml_snapshot!(parse_expr(r#"s"SUM({rel.`Col name`})""#).unwrap(), @r###" --- SString: - String: SUM( - Expr: - Binary: - left: - Literal: - Integer: 2 - op: Add - right: - Literal: - Integer: 2 + Ident: + - rel + - Col name - String: ) - "###); - Ok(()) + "###) } #[test] - fn test_parse_s_string_braces() -> Result<()> { - assert_yaml_snapshot!(expr_of_string(r#"s"{{?crystal_var}}""#, Rule::expr_call)?, @r###" - --- - SString: - - String: "{" - - String: "?crystal_var" - - String: "}" - "###); - assert_yaml_snapshot!(expr_of_string(r#"s"foo{{bar""#, Rule::expr_call)?, @r###" + fn test_s_string_braces() { + assert_yaml_snapshot!(parse_expr(r#"s"{{?crystal_var}}""#).unwrap(), @r###" --- SString: - - String: foo - - String: "{" - - String: bar + - String: "{?crystal_var}" "###); - - Ok(()) + parse_expr(r#"s"foo{{bar""#).unwrap_err(); } #[test] - fn test_parse_jinja() -> Result<()> { - assert_yaml_snapshot!(stmts_of_string(r#" + #[ignore] + fn test_jinja() { + parse( + r#" from {{ ref('stg_orders') }} aggregate (sum order_id) - "#)?, @r###" - --- - - Main: - Pipeline: - exprs: - - FuncCall: - name: - Ident: - - from - args: - - Ident: - - "{{ ref('stg_orders') }}" - named_args: {} - - FuncCall: - name: - Ident: - - aggregate - args: - - FuncCall: - name: - Ident: - - sum - args: - - Ident: - - order_id - named_args: {} - named_args: {} - "###); - Ok(()) + "#, + ) + .unwrap_err(); } #[test] - fn test_parse_list() { - assert_yaml_snapshot!(expr_of_string(r#"[1 + 1, 2]"#, Rule::list).unwrap(), @r###" + fn test_list() { + assert_yaml_snapshot!(parse_expr(r#"[1 + 1, 2]"#).unwrap(), @r###" --- List: - Binary: @@ -901,7 +674,7 @@ Canada - Literal: Integer: 2 "###); - assert_yaml_snapshot!(expr_of_string(r#"[1 + (f 1), 2]"#, Rule::list).unwrap(), @r###" + assert_yaml_snapshot!(parse_expr(r#"[1 + (f 1), 2]"#).unwrap(), @r###" --- List: - Binary: @@ -917,16 +690,15 @@ Canada args: - Literal: Integer: 1 - named_args: {} - Literal: Integer: 2 "###); // Line breaks - assert_yaml_snapshot!(expr_of_string( + assert_yaml_snapshot!(parse_expr( r#"[1, - 2]"#, - Rule::list).unwrap(), @r###" + 2]"# + ).unwrap(), @r###" --- List: - Literal: @@ -935,8 +707,8 @@ Canada Integer: 2 "###); // Function call in a list - let ab = expr_of_string(r#"[a b]"#, Rule::list).unwrap(); - let a_comma_b = expr_of_string(r#"[a, b]"#, Rule::list).unwrap(); + let ab = parse_expr(r#"[a b]"#).unwrap(); + let a_comma_b = parse_expr(r#"[a, b]"#).unwrap(); assert_yaml_snapshot!(ab, @r###" --- List: @@ -947,7 +719,6 @@ Canada args: - Ident: - b - named_args: {} "###); assert_yaml_snapshot!(a_comma_b, @r###" --- @@ -959,13 +730,16 @@ Canada "###); assert_ne!(ab, a_comma_b); - assert_yaml_snapshot!(expr_of_string(r#"[amount, +amount, -amount]"#, Rule::list).unwrap(), @r###" + assert_yaml_snapshot!(parse_expr(r#"[amount, +amount, -amount]"#).unwrap(), @r###" --- List: - - Ident: - - amount - Ident: - amount + - Unary: + op: Add + expr: + Ident: + - amount - Unary: op: Neg expr: @@ -973,13 +747,16 @@ Canada - amount "###); // Operators in list items - assert_yaml_snapshot!(expr_of_string(r#"[amount, +amount, -amount]"#, Rule::list).unwrap(), @r###" + assert_yaml_snapshot!(parse_expr(r#"[amount, +amount, -amount]"#).unwrap(), @r###" --- List: - - Ident: - - amount - Ident: - amount + - Unary: + op: Add + expr: + Ident: + - amount - Unary: op: Neg expr: @@ -989,28 +766,28 @@ Canada } #[test] - fn test_parse_number() -> Result<()> { - assert_yaml_snapshot!(expr_of_string(r#"23"#, Rule::number)?, @r###" + fn test_number() { + assert_yaml_snapshot!(parse_expr(r#"23"#).unwrap(), @r###" --- Literal: Integer: 23 "###); - assert_yaml_snapshot!(expr_of_string(r#"2_3_4.5_6"#, Rule::number)?, @r###" + assert_yaml_snapshot!(parse_expr(r#"2_3_4.5_6"#).unwrap(), @r###" --- Literal: Float: 234.56 "###); - assert_yaml_snapshot!(expr_of_string(r#"23.6"#, Rule::number)?, @r###" + assert_yaml_snapshot!(parse_expr(r#"23.6"#).unwrap(), @r###" --- Literal: Float: 23.6 "###); - assert_yaml_snapshot!(expr_of_string(r#"23.0"#, Rule::number)?, @r###" + assert_yaml_snapshot!(parse_expr(r#"23.0"#).unwrap(), @r###" --- Literal: Float: 23 "###); - assert_yaml_snapshot!(expr_of_string(r#"2 + 2"#, Rule::expr_add)?, @r###" + assert_yaml_snapshot!(parse_expr(r#"2 + 2"#).unwrap(), @r###" --- Binary: left: @@ -1022,23 +799,22 @@ Canada Integer: 2 "###); - // Underscores can't go at the beginning or end of numbers - assert!(expr_of_string("_2", Rule::number).is_err()); - assert!(expr_of_string("_", Rule::number).is_err()); - assert!(expr_of_string("_2.3", Rule::number).is_err()); - // We need to test these with `stmts_of_string` because they start with - // a valid number (and pest will return as much as possible and then return) - let bad_numbers = vec!["2_", "2.3_"]; - for bad_number in bad_numbers { - assert!(stmts_of_string(bad_number).is_err()); - } - Ok(()) + // Underscores at the beginning are parsed as ident + parse_expr("_2").unwrap().kind.into_ident().unwrap(); + parse_expr("_").unwrap().kind.into_ident().unwrap(); + + // We don't allow empty fractions. + parse_expr(r#"add 1. 2"#).unwrap_err(); + + parse_expr("_2.3").unwrap_err(); + // expr_of_string("2_").unwrap_err(); // TODO + // expr_of_string("2.3_").unwrap_err(); // TODO } #[test] - fn test_parse_filter() { + fn test_filter() { assert_yaml_snapshot!( - stmts_of_string(r#"filter country == "USA""#).unwrap(), @r###" + parse(r#"filter country == "USA""#).unwrap(), @r###" --- - Main: FuncCall: @@ -1054,11 +830,10 @@ Canada right: Literal: String: USA - named_args: {} "###); assert_yaml_snapshot!( - stmts_of_string(r#"filter (upper country) == "USA""#).unwrap(), @r###" + parse(r#"filter (upper country) == "USA""#).unwrap(), @r###" --- - Main: FuncCall: @@ -1075,19 +850,17 @@ Canada args: - Ident: - country - named_args: {} op: Eq right: Literal: String: USA - named_args: {} "### ); } #[test] - fn test_parse_aggregate() { - let aggregate = stmts_of_string( + fn test_aggregate() { + let aggregate = parse( r"group [title] ( aggregate [sum salary, count] )", @@ -1118,13 +891,10 @@ Canada args: - Ident: - salary - named_args: {} - Ident: - count - named_args: {} - named_args: {} "###); - let aggregate = stmts_of_string( + let aggregate = parse( r"group [title] ( aggregate [sum salary] )", @@ -1155,16 +925,13 @@ Canada args: - Ident: - salary - named_args: {} - named_args: {} - named_args: {} "###); } #[test] - fn test_parse_derive() -> Result<()> { + fn test_derive() { assert_yaml_snapshot!( - expr_of_string(r#"derive [x = 5, y = (-x)]"#, Rule::func_call)? + parse_expr(r#"derive [x = 5, y = (-x)]"#).unwrap() , @r###" --- FuncCall: @@ -1182,16 +949,13 @@ Canada Ident: - x alias: y - named_args: {} "###); - - Ok(()) } #[test] - fn test_parse_select() -> Result<()> { + fn test_select() { assert_yaml_snapshot!( - expr_of_string(r#"select x"#, Rule::func_call)? + parse_expr(r#"select x"#).unwrap() , @r###" --- FuncCall: @@ -1201,11 +965,10 @@ Canada args: - Ident: - x - named_args: {} "###); assert_yaml_snapshot!( - expr_of_string(r#"select ![x]"#, Rule::func_call)? + parse_expr(r#"select ![x]"#).unwrap() , @r###" --- FuncCall: @@ -1219,11 +982,10 @@ Canada List: - Ident: - x - named_args: {} "###); assert_yaml_snapshot!( - expr_of_string(r#"select [x, y]"#, Rule::func_call)? + parse_expr(r#"select [x, y]"#).unwrap() , @r###" --- FuncCall: @@ -1236,16 +998,13 @@ Canada - x - Ident: - y - named_args: {} "###); - - Ok(()) } #[test] - fn test_parse_expr() -> Result<()> { + fn test_expr() { assert_yaml_snapshot!( - expr_of_string(r#"country == "USA""#, Rule::expr)? + parse_expr(r#"country == "USA""#).unwrap() , @r###" --- Binary: @@ -1257,12 +1016,11 @@ Canada Literal: String: USA "###); - assert_yaml_snapshot!(expr_of_string( + assert_yaml_snapshot!(parse_expr( r#"[ gross_salary = salary + payroll_tax, gross_cost = gross_salary + benefits_cost, -]"#, - Rule::list)?, @r###" +]"#).unwrap(), @r###" --- List: - Binary: @@ -1285,10 +1043,9 @@ Canada alias: gross_cost "###); assert_yaml_snapshot!( - expr_of_string( - "gross_salary = (salary + payroll_tax) * (1 + tax_rate)", - Rule::alias, - )?, + parse_expr( + "(salary + payroll_tax) * (1 + tax_rate)" + ).unwrap(), @r###" --- Binary: @@ -1311,14 +1068,12 @@ Canada right: Ident: - tax_rate - alias: gross_salary - "###); - Ok(()) + "###) } #[test] - fn test_parse_function() -> Result<()> { - assert_yaml_snapshot!(stmts_of_string("func plus_one x -> x + 1")?, @r###" + fn test_function() { + assert_yaml_snapshot!(parse("func plus_one x -> x + 1\n").unwrap(), @r###" --- - FuncDef: name: plus_one @@ -1337,7 +1092,7 @@ Canada Integer: 1 return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string("func identity x -> x")? + assert_yaml_snapshot!(parse("func identity x -> x\n").unwrap() , @r###" --- - FuncDef: @@ -1351,7 +1106,7 @@ Canada - x return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string("func plus_one x -> (x + 1)")? + assert_yaml_snapshot!(parse("func plus_one x -> (x + 1)\n").unwrap() , @r###" --- - FuncDef: @@ -1371,7 +1126,7 @@ Canada Integer: 1 return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string("func plus_one x -> x + 1")? + assert_yaml_snapshot!(parse("func plus_one x -> x + 1\n").unwrap() , @r###" --- - FuncDef: @@ -1391,7 +1146,8 @@ Canada Integer: 1 return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string("func foo x -> some_func (foo bar + 1) (plax) - baz")? + + assert_yaml_snapshot!(parse("func foo x -> some_func (foo bar + 1) (plax) - baz\n").unwrap() , @r###" --- - FuncDef: @@ -1419,7 +1175,6 @@ Canada right: Literal: Integer: 1 - named_args: {} - Binary: left: Ident: @@ -1428,11 +1183,10 @@ Canada right: Ident: - baz - named_args: {} return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string("func return_constant -> 42")?, @r###" + assert_yaml_snapshot!(parse("func return_constant -> 42\n").unwrap(), @r###" --- - FuncDef: name: return_constant @@ -1443,7 +1197,9 @@ Canada Integer: 42 return_ty: ~ "###); - assert_yaml_snapshot!(stmts_of_string(r#"func count X -> s"SUM({X})""#)?, @r###" + + assert_yaml_snapshot!(parse(r#"func count X -> s"SUM({X})" + "#).unwrap(), @r###" --- - FuncDef: name: count @@ -1461,24 +1217,59 @@ Canada return_ty: ~ "###); - /* TODO: Does not yet parse because `window` not yet implemented. - assert_debug_snapshot!(ast_of_parse_tree( - parse_tree_of_str( - r#" - func lag_day x -> ( - window x - by sec_id - sort date - lag 1 + assert_yaml_snapshot!(parse( + r#" + func lag_day x -> ( + window x + by sec_id + sort date + lag 1 + ) + "# ) - "#, - Rule::func_def - ) - .unwrap() - )); - */ + .unwrap(), @r###" + --- + - FuncDef: + name: lag_day + positional_params: + - name: x + default_value: ~ + named_params: [] + body: + Pipeline: + exprs: + - FuncCall: + name: + Ident: + - window + args: + - Ident: + - x + - FuncCall: + name: + Ident: + - by + args: + - Ident: + - sec_id + - FuncCall: + name: + Ident: + - sort + args: + - Ident: + - date + - FuncCall: + name: + Ident: + - lag + args: + - Literal: + Integer: 1 + return_ty: ~ + "###); - assert_yaml_snapshot!(stmts_of_string(r#"func add x to:a -> x + to"#)?, @r###" + assert_yaml_snapshot!(parse("func add x to:a -> x + to\n").unwrap(), @r###" --- - FuncDef: name: add @@ -1501,14 +1292,12 @@ Canada - to return_ty: ~ "###); - - Ok(()) } #[test] - fn test_parse_func_call() { + fn test_func_call() { // Function without argument - let ast = expr_of_string(r#"count"#, Rule::expr).unwrap(); + let ast = parse_expr(r#"count"#).unwrap(); let ident = ast.kind.into_ident().unwrap(); assert_yaml_snapshot!( ident, @r###" @@ -1516,8 +1305,21 @@ Canada - count "###); + let ast = parse_expr(r#"s 'foo'"#).unwrap(); + assert_yaml_snapshot!( + ast, @r###" + --- + FuncCall: + name: + Ident: + - s + args: + - Literal: + String: foo + "###); + // A non-friendly option for #154 - let ast = expr_of_string(r#"count s'*'"#, Rule::expr_call).unwrap(); + let ast = parse_expr(r#"count s'*'"#).unwrap(); let func_call: FuncCall = ast.kind.into_func_call().unwrap(); assert_yaml_snapshot!( func_call, @r###" @@ -1528,64 +1330,11 @@ Canada args: - SString: - String: "*" - named_args: {} "###); - assert_yaml_snapshot!(parse(r#"from mytable | select [a and b + c or (d e) and f]"#).unwrap(), @r###" - --- - - Main: - Pipeline: - exprs: - - FuncCall: - name: - Ident: - - from - args: - - Ident: - - mytable - named_args: {} - - FuncCall: - name: - Ident: - - select - args: - - List: - - Binary: - left: - Ident: - - a - op: And - right: - Binary: - left: - Binary: - left: - Ident: - - b - op: Add - right: - Ident: - - c - op: Or - right: - Binary: - left: - FuncCall: - name: - Ident: - - d - args: - - Ident: - - e - named_args: {} - op: And - right: - Ident: - - f - named_args: {} - "###); + parse_expr("plus_one x:0 x:0 ").unwrap_err(); - let ast = expr_of_string(r#"add bar to=3"#, Rule::expr_call).unwrap(); + let ast = parse_expr(r#"add bar to=3"#).unwrap(); assert_yaml_snapshot!( ast, @r###" --- @@ -1599,15 +1348,130 @@ Canada - Literal: Integer: 3 alias: to - named_args: {} "###); } #[test] - fn test_parse_table() -> Result<()> { - assert_yaml_snapshot!(stmts_of_string( + fn test_op_precedence() { + assert_yaml_snapshot!(parse_expr(r#"1 + 2 - 3 - 4"#).unwrap(), @r###" + --- + Binary: + left: + Binary: + left: + Binary: + left: + Literal: + Integer: 1 + op: Add + right: + Literal: + Integer: 2 + op: Sub + right: + Literal: + Integer: 3 + op: Sub + right: + Literal: + Integer: 4 + "###); + + assert_yaml_snapshot!(parse_expr(r#"1 / 2 - 3 * 4 + 1"#).unwrap(), @r###" + --- + Binary: + left: + Binary: + left: + Binary: + left: + Literal: + Integer: 1 + op: Div + right: + Literal: + Integer: 2 + op: Sub + right: + Binary: + left: + Literal: + Integer: 3 + op: Mul + right: + Literal: + Integer: 4 + op: Add + right: + Literal: + Integer: 1 + "###); + + assert_yaml_snapshot!(parse_expr(r#"a and b or c and d"#).unwrap(), @r###" + --- + Binary: + left: + Binary: + left: + Ident: + - a + op: And + right: + Ident: + - b + op: Or + right: + Binary: + left: + Ident: + - c + op: And + right: + Ident: + - d + "###); + + assert_yaml_snapshot!(parse_expr(r#"a and b + c or (d e) and f"#).unwrap(), @r###" + --- + Binary: + left: + Binary: + left: + Ident: + - a + op: And + right: + Binary: + left: + Ident: + - b + op: Add + right: + Ident: + - c + op: Or + right: + Binary: + left: + FuncCall: + name: + Ident: + - d + args: + - Ident: + - e + op: And + right: + Ident: + - f + "###); + } + + #[test] + fn test_var_def() { + assert_yaml_snapshot!(parse( "let newest_employees = (from employees)" - )?, @r###" + ).unwrap(), @r###" --- - VarDef: name: newest_employees @@ -1619,10 +1483,9 @@ Canada args: - Ident: - employees - named_args: {} "###); - assert_yaml_snapshot!(stmts_of_string( + assert_yaml_snapshot!(parse( r#" let newest_employees = ( from employees @@ -1633,7 +1496,7 @@ Canada ) sort tenure take 50 - )"#.trim())?, + )"#.trim()).unwrap(), @r###" --- - VarDef: @@ -1648,7 +1511,6 @@ Canada args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -1669,10 +1531,7 @@ Canada args: - Ident: - salary - named_args: {} alias: average_country_salary - named_args: {} - named_args: {} - FuncCall: name: Ident: @@ -1680,7 +1539,6 @@ Canada args: - Ident: - tenure - named_args: {} - FuncCall: name: Ident: @@ -1688,12 +1546,11 @@ Canada args: - Literal: Integer: 50 - named_args: {} "###); - assert_yaml_snapshot!(stmts_of_string(r#" + assert_yaml_snapshot!(parse(r#" let e = s"SELECT * FROM employees" - "#)?, @r###" + "#).unwrap(), @r###" --- - VarDef: name: e @@ -1702,7 +1559,7 @@ Canada - String: SELECT * FROM employees "###); - assert_yaml_snapshot!(stmts_of_string( + assert_yaml_snapshot!(parse( "let x = ( from x_table @@ -1712,7 +1569,7 @@ Canada ) from x" - )?, @r###" + ).unwrap(), @r###" --- - VarDef: name: x @@ -1726,7 +1583,6 @@ Canada args: - Ident: - x_table - named_args: {} - FuncCall: name: Ident: @@ -1735,7 +1591,6 @@ Canada - Ident: - foo alias: only_in_x - named_args: {} - Main: FuncCall: name: @@ -1744,15 +1599,12 @@ Canada args: - Ident: - x - named_args: {} "###); - - Ok(()) } #[test] fn test_inline_pipeline() { - assert_yaml_snapshot!(expr_of_string("(salary | percentile 50)", Rule::nested_pipeline).unwrap(), @r###" + assert_yaml_snapshot!(parse_expr("(salary | percentile 50)").unwrap(), @r###" --- Pipeline: exprs: @@ -1765,9 +1617,8 @@ Canada args: - Literal: Integer: 50 - named_args: {} "###); - assert_yaml_snapshot!(stmts_of_string("func median x -> (x | percentile 50)").unwrap(), @r###" + assert_yaml_snapshot!(parse("func median x -> (x | percentile 50)\n").unwrap(), @r###" --- - FuncDef: name: median @@ -1787,20 +1638,19 @@ Canada args: - Literal: Integer: 50 - named_args: {} return_ty: ~ "###); } #[test] - fn test_parse_sql_parameters() -> Result<()> { + fn test_sql_parameters() { assert_yaml_snapshot!(parse(r#" from mytable filter [ first_name == $1, last_name == $2.name ] - "#)?, @r###" + "#).unwrap(), @r###" --- - Main: Pipeline: @@ -1812,7 +1662,6 @@ Canada args: - Ident: - mytable - named_args: {} - FuncCall: name: Ident: @@ -1825,39 +1674,33 @@ Canada - first_name op: Eq right: - Ident: - - $1 + Param: "1" - Binary: left: Ident: - last_name op: Eq right: - Ident: - - $2 - - name - named_args: {} + Param: 2.name "###); - Ok(()) } #[test] - fn test_tab_characters() -> Result<()> { + fn test_tab_characters() { // #284 - - let prql = "from c_invoice + parse( + "from c_invoice join doc:c_doctype [==c_invoice_id] select [ \tinvoice_no, \tdocstatus -]"; - parse(prql)?; - - Ok(()) +]", + ) + .unwrap(); } #[test] - fn test_parse_backticks() -> Result<()> { + fn test_backticks() { let prql = " from `a/*.parquet` aggregate [max c] @@ -1866,7 +1709,7 @@ join `my-proj.dataset.table` join `my-proj`.`dataset`.`table` "; - assert_yaml_snapshot!(parse(prql)?, @r###" + assert_yaml_snapshot!(parse(prql).unwrap(), @r###" --- - Main: Pipeline: @@ -1878,7 +1721,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - a/*.parquet - named_args: {} - FuncCall: name: Ident: @@ -1892,8 +1734,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - c - named_args: {} - named_args: {} - FuncCall: name: Ident: @@ -1907,7 +1747,6 @@ join `my-proj`.`dataset`.`table` expr: Ident: - id - named_args: {} - FuncCall: name: Ident: @@ -1915,7 +1754,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - my-proj.dataset.table - named_args: {} - FuncCall: name: Ident: @@ -1925,14 +1763,11 @@ join `my-proj`.`dataset`.`table` - my-proj - dataset - table - named_args: {} "###); - - Ok(()) } #[test] - fn test_parse_sort() -> Result<()> { + fn test_sort() { assert_yaml_snapshot!(parse(" from invoices sort issued_at @@ -1952,7 +1787,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - invoices - named_args: {} - FuncCall: name: Ident: @@ -1960,7 +1794,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - issued_at - named_args: {} - FuncCall: name: Ident: @@ -1971,7 +1804,6 @@ join `my-proj`.`dataset`.`table` expr: Ident: - issued_at - named_args: {} - FuncCall: name: Ident: @@ -1980,7 +1812,6 @@ join `my-proj`.`dataset`.`table` - List: - Ident: - issued_at - named_args: {} - FuncCall: name: Ident: @@ -1992,7 +1823,6 @@ join `my-proj`.`dataset`.`table` expr: Ident: - issued_at - named_args: {} - FuncCall: name: Ident: @@ -2006,137 +1836,16 @@ join `my-proj`.`dataset`.`table` expr: Ident: - amount - - Ident: - - num_of_articles - named_args: {} - "###); - - Ok(()) - } - - #[test] - fn test_range() { - assert_yaml_snapshot!(parse(" - from employees - filter (distance | in 0..40) - filter (distance | in (0)..40) - derive [ - greater_than_ten = 11.., - less_than_ten = ..9, - negative = (-5..), - more_negative = -10.., - dates_open = @2020-01-01.., - dates = @2020-01-01..@2021-01-01, - ] - ").unwrap(), @r###" - --- - - Main: - Pipeline: - exprs: - - FuncCall: - name: - Ident: - - from - args: - - Ident: - - employees - named_args: {} - - FuncCall: - name: - Ident: - - filter - args: - - Pipeline: - exprs: - - Ident: - - distance - - FuncCall: - name: - Ident: - - in - args: - - Range: - start: - Literal: - Integer: 0 - end: - Literal: - Integer: 40 - named_args: {} - named_args: {} - - FuncCall: - name: - Ident: - - filter - args: - - Pipeline: - exprs: - - Ident: - - distance - - FuncCall: - name: - Ident: - - in - args: - - Range: - start: - Literal: - Integer: 0 - end: - Literal: - Integer: 40 - named_args: {} - named_args: {} - - FuncCall: - name: - Ident: - - derive - args: - - List: - - Range: - start: - Literal: - Integer: 11 - end: ~ - alias: greater_than_ten - - Range: - start: ~ - end: - Literal: - Integer: 9 - alias: less_than_ten - - Range: - start: - Literal: - Integer: -5 - end: ~ - alias: negative - - Range: - start: - Literal: - Integer: -10 - end: ~ - alias: more_negative - - Range: - start: - Literal: - Date: 2020-01-01 - end: ~ - alias: dates_open - - Range: - start: - Literal: - Date: 2020-01-01 - end: - Literal: - Date: 2021-01-01 - alias: dates - named_args: {} + - Unary: + op: Add + expr: + Ident: + - num_of_articles "###); } #[test] - fn test_dates() -> Result<()> { + fn test_dates() { assert_yaml_snapshot!(parse(" from employees derive [age_plus_two_years = (age + 2years)] @@ -2152,7 +1861,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -2170,40 +1878,30 @@ join `my-proj`.`dataset`.`table` n: 2 unit: years alias: age_plus_two_years - named_args: {} "###); - assert_yaml_snapshot!(parse(" - derive [ - date = @2011-02-01, - timestamp = @2011-02-01T10:00, - time = @14:00, - # datetime = @2011-02-01T10:00, - ] - ").unwrap(), @r###" + assert_yaml_snapshot!(parse_expr("@2011-02-01").unwrap(), @r###" --- - - Main: - FuncCall: - name: - Ident: - - derive - args: - - List: - - Literal: - Date: 2011-02-01 - alias: date - - Literal: - Timestamp: "2011-02-01T10:00" - alias: timestamp - - Literal: - Time: "14:00" - alias: time - named_args: {} + Literal: + Date: 2011-02-01 + "###); + assert_yaml_snapshot!(parse_expr("@2011-02-01T10:00").unwrap(), @r###" + --- + Literal: + Timestamp: "2011-02-01T10:00" + "###); + assert_yaml_snapshot!(parse_expr("@14:00").unwrap(), @r###" + --- + Literal: + Time: "14:00" "###); + // assert_yaml_snapshot!(parse_expr("@2011-02-01T10:00").unwrap(), @""); - assert!(parse("derive x = @2020-01-0").is_err()); + parse_expr("@2020-01-0").unwrap_err(); - Ok(()) + parse_expr("@2020-01-011").unwrap_err(); + + parse_expr("@2020-01-01T111").unwrap_err(); } #[test] @@ -2221,12 +1919,11 @@ join `my-proj`.`dataset`.`table` - Ident: - r alias: x - named_args: {} "### ) } #[test] - fn test_parse_coalesce() { + fn test_coalesce() { assert_yaml_snapshot!(parse(r###" from employees derive amount = amount ?? 0 @@ -2242,7 +1939,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -2257,12 +1953,11 @@ join `my-proj`.`dataset`.`table` Literal: Integer: 0 alias: amount - named_args: {} "### ) } #[test] - fn test_parse_literal() { + fn test_literal() { assert_yaml_snapshot!(parse(r###" derive x = true "###).unwrap(), @r###" @@ -2276,12 +1971,11 @@ join `my-proj`.`dataset`.`table` - Literal: Boolean: true alias: x - named_args: {} "###) } #[test] - fn test_parse_allowed_idents() { + fn test_allowed_idents() { assert_yaml_snapshot!(parse(r###" from employees join _salary [==employee_id] # table with leading underscore @@ -2299,7 +1993,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -2313,7 +2006,6 @@ join `my-proj`.`dataset`.`table` expr: Ident: - employee_id - named_args: {} - FuncCall: name: Ident: @@ -2325,9 +2017,7 @@ join `my-proj`.`dataset`.`table` - first_name op: Eq right: - Ident: - - $1 - named_args: {} + Param: "1" - FuncCall: name: Ident: @@ -2337,12 +2027,11 @@ join `my-proj`.`dataset`.`table` - Ident: - _employees - _underscored_column - named_args: {} "###) } #[test] - fn test_parse_gt_lt_gte_lte() { + fn test_gt_lt_gte_lte() { assert_yaml_snapshot!(parse(r###" from people filter age >= 100 @@ -2361,7 +2050,6 @@ join `my-proj`.`dataset`.`table` args: - Ident: - people - named_args: {} - FuncCall: name: Ident: @@ -2375,7 +2063,6 @@ join `my-proj`.`dataset`.`table` right: Literal: Integer: 100 - named_args: {} - FuncCall: name: Ident: @@ -2389,7 +2076,6 @@ join `my-proj`.`dataset`.`table` right: Literal: Integer: 10 - named_args: {} - FuncCall: name: Ident: @@ -2403,7 +2089,6 @@ join `my-proj`.`dataset`.`table` right: Literal: Integer: 0 - named_args: {} - FuncCall: name: Ident: @@ -2417,7 +2102,6 @@ join `my-proj`.`dataset`.`table` right: Literal: Integer: 2 - named_args: {} "###) } @@ -2438,7 +2122,6 @@ join s=salaries [==id] args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -2453,7 +2136,162 @@ join s=salaries [==id] expr: Ident: - id - named_args: {} + "###); + } + + #[test] + fn test_ident_with_keywords() { + assert_yaml_snapshot!(parse_expr(r"select [andrew, orion, lettuce, falsehood, null0]").unwrap(), @r###" + --- + FuncCall: + name: + Ident: + - select + args: + - List: + - Ident: + - andrew + - Ident: + - orion + - Ident: + - lettuce + - Ident: + - falsehood + - Ident: + - null0 + "###); + + assert_yaml_snapshot!(parse_expr(r"[false]").unwrap(), @r###" + --- + List: + - Literal: + Boolean: false + "###); + } + + #[test] + fn test_case() { + assert_yaml_snapshot!(parse_expr(r#"case [ + nickname != null => nickname, + true => null + ]"#).unwrap(), @r###" + --- + Case: + - condition: + Binary: + left: + Ident: + - nickname + op: Ne + right: + Literal: "Null" + value: + Ident: + - nickname + - condition: + Literal: + Boolean: true + value: + Literal: "Null" + "###); + } + + #[test] + fn test_params() { + assert_yaml_snapshot!(parse_expr(r#"$2"#).unwrap(), @r###" + --- + Param: "2" + "###); + + assert_yaml_snapshot!(parse_expr(r#"$2_any_text"#).unwrap(), @r###" + --- + Param: 2_any_text + "###); + } + + #[test] + fn test_unicode() { + let source = "from tรจte"; + assert_yaml_snapshot!(parse(source).unwrap(), @r###" + --- + - Main: + FuncCall: + name: + Ident: + - from + args: + - Ident: + - tรจte + "###); + } + + #[test] + fn test_error_unicode_string() { + // Test various unicode strings successfully parse errors. We were + // getting loops in the lexer before. + parse("sโ€™ ").unwrap_err(); + parse("sโ€™").unwrap_err(); + parse(" sโ€™").unwrap_err(); + parse(" โ€™ s").unwrap_err(); + parse("โ€™s").unwrap_err(); + parse("๐Ÿ‘ sโ€™").unwrap_err(); + + let source = "Mississippi has four Sโ€™s and four Iโ€™s."; + assert_debug_snapshot!(parse(source).unwrap_err(), @r###" + Errors( + [ + Error { + span: Some( + span-chars-22-23, + ), + reason: Unexpected { + found: "โ€™", + }, + help: None, + code: None, + }, + Error { + span: Some( + span-chars-35-36, + ), + reason: Unexpected { + found: "โ€™", + }, + help: None, + code: None, + }, + Error { + span: Some( + span-chars-38-39, + ), + reason: Simple( + "Expected * or an identifier, but didn't find anything before the end.", + ), + help: None, + code: None, + }, + ], + ) + "###); + } + + #[test] + fn test_error_unexpected() { + assert_debug_snapshot!(parse("Answer: T-H-A-T!").unwrap_err(), @r###" + Errors( + [ + Error { + span: Some( + span-chars-6-7, + ), + reason: Unexpected { + found: ":", + }, + help: None, + code: None, + }, + ], + ) "###); } } diff --git a/prql-compiler/src/parser/prql.pest b/prql-compiler/src/parser/prql.pest deleted file mode 100644 index 50cf6707ce68..000000000000 --- a/prql-compiler/src/parser/prql.pest +++ /dev/null @@ -1,153 +0,0 @@ -WHITESPACE = _{ " " | "\t" } - -// TODO: maybe pass comments to AST (and potentially put them into SQL comments) -COMMENT = _{ "#" ~ (!NEWLINE ~ ANY) * } - -statements = _{ SOI ~ NEWLINE* ~ query_def ? ~ (func_def | var_def)* ~ pipeline_stmt? ~ EOI } - -query_def = { "prql" ~ named_arg* ~ NEWLINE+ } - -func_def = { "func" ~ func_def_name ~ func_def_params ~ "->" ~ expr_call ~ ( NEWLINE+ | &EOI ) } - -func_def_name = { ident_part ~ type_def? } -func_def_params = { func_def_param* } -func_def_param = { ident_part ~ type_def? ~ (":" ~ expr)? } -type_def = { "<" ~ type_term ~ ( "|" ~ type_term)* ~ ">" } -type_term = { ident_part ~ type_def? } - -var_def = { "let" ~ ident_part ~ "=" ~ expr_call ~ ( NEWLINE+ | &EOI ) } - -pipeline_stmt = { pipeline ~ ( NEWLINE+ | &EOI ) } - -// An ident is a sequence of word-like terms, separated by `.`. Where surrounded -// by backticks, the term is taken as-is, including any periods it contains. -// We allow `e.*`, but not just `*`, since it would conflict with multiply in some cases. -ident = ${ - !operator - ~ (ident_plain | ident_backticks) - ~ ("." ~ (ident_plain | ident_backticks | ident_star))* -} -ident_part = ${ ident_plain | ident_backticks } -// Either a normal ident (starting with a letter, `$` or `_`), or any string surrounded -// by backticks. -ident_plain = { ((ASCII_ALPHA | "$" | "_") ~ (ASCII_ALPHANUMERIC | "_" )* ) } -ident_backticks = _{ PUSH("`") ~ (!NEWLINE ~ string_inner)* ~ POP } -// This is split out so we can make `ident_part_next` silent, but still capture it. -ident_star = { "*" } - -pipe = _{ NEWLINE+ | "|" } -pipeline = { WHITESPACE* ~ expr_call ~ (pipe ~ expr_call)* } - -// Whitespace is required to prevent matching s"string". Forbid `operator_binary` so `a -// - b` can't parse as `a` & `-b` (but allow `select ![a]`) -func_call = ${ ident ~ WHITESPACE+ ~ (!operator_binary ~ (named_arg | alias | expr) ~ WHITESPACE*)+ } - -named_arg = !{ ident_part ~ ":" ~ !":" ~ expr } -// alias needs to be distinct from assign, so that in `join s=salaries [==id]`, -// `s=salaries` is parsed separately from `[==id]`, since aliases allow for an -// expr as an rvalue, but not a function call. -alias = !{ ident_part ~ "=" ~ !"=" ~ expr } -assign = !{ ident_part ~ "=" ~ !"=" ~ expr_call } - -expr_call = _{ (func_call | expr) } - -expr = !{ expr_coalesce ~ (operator_logical ~ expr)? } -expr_coalesce = { expr_compare ~ (operator_coalesce ~ expr_coalesce)? } -expr_compare = { expr_add ~ (operator_compare ~ expr_add)? } -expr_add = { expr_mul ~ (operator_add ~ expr_add)? } -expr_mul = { term ~ (operator_mul ~ expr_mul)? } - -term = _{ ( switch | s_string | f_string | range | literal | ident | nested_pipeline | expr_unary | list | jinja ) } -expr_unary = { ( operator_unary ~ ( nested_pipeline | ident | list )) } -literal = _{ value_and_unit | number | boolean | null | string | timestamp | date | time | "(" ~ literal ~ ")" } -// `assign | pipeline` based on discussion in #648 -list = { "[" ~ (NEWLINE* ~ (assign | pipeline) ~ ("," ~ NEWLINE* ~ (assign | pipeline) )* ~ ","?)? ~ NEWLINE* ~ "]" } -nested_pipeline = { "(" ~ (WHITESPACE | NEWLINE)* ~ pipeline? ~ (WHITESPACE | NEWLINE)* ~ ")" } - -// We haven't implemented escapes โ€”ย I think we can mostly pass those through to -// SQL, but there may be things we're missing. -// https://pest.rs/book/examples/rust/literals.html - -// We need to have a non-silent rule which contains the quotes -// โ€”ย `string` in this case โ€” because of -// https://github.com/pest-parser/pest/issues/583. Then when converting to AST, -// we only keep the `string_inner` and discard the `string` given it contains -// the quotes. -// -// TODO: I'm still a bit unclear how preceding and trailing spaces are working -// -- it seems that inner spaces are included without an atomic operator (or -// with `ANY`), but prceeding & trailing spaces require both `ANY` _and_ an -// atomic operator. We have some rudimentary tests for these. - -single_quote = _{ "\"" | "'" } -multi_quote = _{ "\""{3,} | "'"{3,} } -opening_quote = _{ PUSH(multi_quote) | PUSH(single_quote) } -// PEEK refers to the opening quote; `"` or `'` or multiple quotes. -string_inner = { ( !( PEEK ) ~ ANY )+ } -// Either > 3 quotes, or just one. Currently both of those can be multiline. -string = ${ opening_quote ~ string_inner? ~ POP } - -// We can use underscores within numbers -number_char = _{ ASCII_DIGIT | "_" } -// Numbers need to start and end with a digit. We implement the end by matching -// either a number_char that's not the final char, or a final char that's a digit. -number_part = _{ ( ASCII_DIGIT ) ~ (( number_char ~ &number_char )* ~ ( ASCII_DIGIT ~ !number_char ))? } -number = ${ operator_add? ~ number_part ~ ("." ~ number_part)? } - -boolean = ${ "true" | "false" } - -null = ${ "null" } - -range = ${ range_edge ~ ".." ~ range_edge } -range_edge = ${ literal? } - -operator = _{ operator_unary | operator_binary } -operator_binary = _{ operator_mul | operator_add | operator_compare | operator_logical | operator_coalesce } -operator_unary = ${ "-" | "+" | "!" | "==" } -operator_mul = ${ "*" | "/" | "%" } -operator_add = ${ "+" | "-" } -operator_compare = ${ "==" | "!=" | ">=" | "<=" | ">" | "<" } -operator_logical = ${ ("and" | "or") ~ &WHITESPACE } -operator_coalesce = ${ "??" } - -// If we have lots more string prefixes then we could just have a type -// `prefixed` string and parse in the parser, but manageable for now. -s_string = ${ "s" ~ opening_quote ~ interpolate_string_inner ~ POP } -f_string = ${ "f" ~ opening_quote ~ interpolate_string_inner ~ POP } -interpolate_string_inner = _{ - ( - interpolate_string_inner_literal | - double_open_bracket | - double_close_bracket | - ( "{" ~ pipeline ~ "}" ) - )* -} -double_open_bracket = { "{{" } -double_close_bracket = { "}}" } -// Anything apart from the quote (PEEK), the beginning of an expr (`{`), or a `}}` -interpolate_string_inner_literal = { ( !( PEEK | "{" | double_close_bracket ) ~ ANY )+ } - -unit = { "microseconds" | "milliseconds" | "seconds" | "minutes" | "hours" | "days" | "weeks" | "months" | "years" } -value_and_unit = ${ number ~ unit } - -date = ${ "@" ~ date_inner ~ &end_expr } -time = ${ "@" ~ time_inner ~ &end_expr } -timestamp = ${ "@" ~ timestamp_inner ~ &end_expr } -// We use the `inner` types as containing the data that we want to retain in the AST. -date_inner = ${ ASCII_DIGIT{4} ~ "-" ~ ASCII_DIGIT{2} ~ "-" ~ ASCII_DIGIT{2} } -// Times are liberally defined atm, we could make this more robust. -time_inner = ${ ASCII_DIGIT{2} ~ (( ":" | "." ) ~ ASCII_DIGIT* )* ~ ((( "+" | "-" ) ~ (ASCII_DIGIT | ":" )*) | "Z")? } -timestamp_inner = ${ date_inner ~ "T" ~ time_inner } - -// We can use this when want to ensure something is ending, like a date, so `@20-01-0` -// isn't treated like a time `@20-01` `-` (minus) `0`. -// (Not sure whether `..` should be here or in the items that allow it; feel -// free to demote it to those items if `end_expr` is used somewhere where it's -// not supported) -end_expr = _{ WHITESPACE | "," | ")" | "]" | EOI | NEWLINE | ".." } - -// We pass text between `{{` and `}}` through, so dbt can use Jinja. -jinja = { ("{{" ~ (!"}}" ~ ANY)* ~ "}}") } - -switch = { "switch" ~ "[" ~ (NEWLINE* ~ switch_case ~ ("," ~ NEWLINE* ~ switch_case )* ~ ","?)? ~ NEWLINE* ~ "]" } -switch_case = { expr_call ~ "->" ~ expr_call } diff --git a/prql-compiler/src/parser/snapshots/prql_compiler__parser__test__parse_pipeline_parse_tree.snap b/prql-compiler/src/parser/snapshots/prql_compiler__parser__test__pipeline_parse_tree.snap similarity index 85% rename from prql-compiler/src/parser/snapshots/prql_compiler__parser__test__parse_pipeline_parse_tree.snap rename to prql-compiler/src/parser/snapshots/prql_compiler__parser__test__pipeline_parse_tree.snap index 66faeab9a4cb..16a11bf1858f 100644 --- a/prql-compiler/src/parser/snapshots/prql_compiler__parser__test__parse_pipeline_parse_tree.snap +++ b/prql-compiler/src/parser/snapshots/prql_compiler__parser__test__pipeline_parse_tree.snap @@ -1,6 +1,6 @@ --- source: prql-compiler/src/parser/mod.rs -expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../book/tests/prql/examples/variables-0.prql\").trim().replace(\"\\r\\n\",\n \"\\n\"), Rule::statements).unwrap()).unwrap()" +expression: "parse(include_str!(\"../../examples/compile-files/queries/variables.prql\")).unwrap()" --- - Main: Pipeline: @@ -12,7 +12,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - employees - named_args: {} - FuncCall: name: Ident: @@ -26,7 +25,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo right: Literal: String: USA - named_args: {} - FuncCall: name: Ident: @@ -51,7 +49,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo Ident: - benefits_cost alias: gross_cost - named_args: {} - FuncCall: name: Ident: @@ -65,7 +62,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo right: Literal: Integer: 0 - named_args: {} - FuncCall: name: Ident: @@ -89,7 +85,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - salary - named_args: {} - FuncCall: name: Ident: @@ -97,7 +92,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - gross_salary - named_args: {} - FuncCall: name: Ident: @@ -105,7 +99,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - salary - named_args: {} - FuncCall: name: Ident: @@ -113,7 +106,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - gross_salary - named_args: {} - FuncCall: name: Ident: @@ -121,7 +113,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - gross_cost - named_args: {} - FuncCall: name: Ident: @@ -129,13 +120,10 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - gross_cost - named_args: {} alias: sum_gross_cost - Ident: - count alias: ct - named_args: {} - named_args: {} - FuncCall: name: Ident: @@ -143,7 +131,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Ident: - sum_gross_cost - named_args: {} - FuncCall: name: Ident: @@ -157,7 +144,6 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo right: Literal: Integer: 200 - named_args: {} - FuncCall: name: Ident: @@ -165,5 +151,4 @@ expression: "stmts_of_parse_pairs(parse_tree_of_str(&include_str!(\"../../../boo args: - Literal: Integer: 20 - named_args: {} diff --git a/prql-compiler/src/parser/stmt.rs b/prql-compiler/src/parser/stmt.rs new file mode 100644 index 000000000000..393be2596b34 --- /dev/null +++ b/prql-compiler/src/parser/stmt.rs @@ -0,0 +1,140 @@ +use std::collections::HashMap; + +use chumsky::prelude::*; +use semver::VersionReq; + +use crate::ast::pl::*; + +use super::common::*; +use super::expr::*; +use super::lexer::Token; + +pub fn source() -> impl Parser, Error = Simple> { + query_def() + .or_not() + .chain::( + choice((type_def(), var_def(), function_def())) + .map_with_span(into_stmt) + .separated_by(new_line().repeated()) + .allow_leading() + .allow_trailing(), + ) + .chain(main_pipeline().or_not()) + .then_ignore(end()) + .labelled("source file") +} + +fn main_pipeline() -> impl Parser> { + pipeline(expr_call()) + .map_with_span(into_expr) + .map(Box::new) + .map(StmtKind::Main) + .map_with_span(into_stmt) + .labelled("main pipeline") +} + +fn query_def() -> impl Parser> { + new_line() + .repeated() + .ignore_then(keyword("prql")) + .ignore_then( + // named arg + ident_part().then_ignore(ctrl(':')).then(expr()).repeated(), + ) + .then_ignore(new_line()) + .try_map(|args, span| { + let mut args: HashMap<_, _> = args.into_iter().collect(); + + let version = args + .remove("version") + .map(|v| match v.kind { + ExprKind::Literal(Literal::String(v)) => { + VersionReq::parse(&v).map_err(|e| e.to_string()) + } + _ => Err("version must be a sting literal".to_string()), + }) + .transpose() + .map_err(|msg| Simple::custom(span, msg))?; + + let other = args + .into_iter() + .flat_map(|(key, value)| match value.kind { + ExprKind::Ident(value) => Some((key, value.to_string())), + _ => None, + }) + .collect(); + + Ok(StmtKind::QueryDef(QueryDef { version, other })) + }) + .map_with_span(into_stmt) + .labelled("query header") +} + +fn var_def() -> impl Parser> { + keyword("let") + .ignore_then(ident_part()) + .then_ignore(ctrl('=')) + .then(expr_call().map(Box::new)) + .map(|(name, value)| VarDef { name, value }) + .map(StmtKind::VarDef) + .labelled("variable definition") +} + +fn type_def() -> impl Parser> { + keyword("type") + .ignore_then(ident_part()) + .then(ctrl('=').ignore_then(expr_call()).or_not()) + .map(|(name, value)| TypeDef { name, value }) + .map(StmtKind::TypeDef) + .labelled("type definition") +} + +fn function_def() -> impl Parser> { + keyword("func") + .ignore_then( + // func name + ident_part().then(type_expr().or_not()), + ) + .then( + // params + ident_part() + .then(type_expr().or_not()) + .then(ctrl(':').ignore_then(expr()).or_not()) + .repeated(), + ) + .then_ignore(just(Token::Arrow)) + .then(expr_call().map(Box::new)) + .then_ignore(new_line()) + .map(|(((name, return_ty), params), body)| { + let (pos, nam) = params + .into_iter() + .map(|((name, ty_expr), default_value)| FuncParam { + name, + ty_expr, + default_value, + }) + .partition(|p| p.default_value.is_none()); + + FuncDef { + name, + positional_params: pos, + named_params: nam, + body, + return_ty, + } + }) + .map(StmtKind::FuncDef) + .labelled("function definition") +} + +pub fn type_expr() -> impl Parser> { + let literal = select! { Token::Literal(lit) => ExprKind::Literal(lit) }; + + let ident = ident().map(ExprKind::Ident); + + let term = literal.or(ident).map_with_span(into_expr); + + binary_op_parser(term, operator_or()) + .delimited_by(ctrl('<'), ctrl('>')) + .labelled("type expression") +} diff --git a/prql-compiler/src/semantic/context.rs b/prql-compiler/src/semantic/context.rs index e61af872ad52..06854632f614 100644 --- a/prql-compiler/src/semantic/context.rs +++ b/prql-compiler/src/semantic/context.rs @@ -75,7 +75,7 @@ pub enum TableExpr { LocalTable, /// A placeholder for a relation that will be provided later. - Anchor(String), + Param(String), } #[derive(Clone, Eq, Debug, PartialEq, Serialize, Deserialize)] @@ -135,7 +135,16 @@ impl Context { let expr = TableExpr::RelationVar(value); DeclKind::TableDecl(TableDecl { columns, expr }) } - Some(_) => DeclKind::Expr(var_def.value), + Some(_) => { + let mut value = var_def.value; + + // TODO: check that declaring module is std + if let Some(kind) = get_stdlib_decl(name.as_str()) { + value.kind = kind; + } + + DeclKind::Expr(value) + } None => { return Err( Error::new_simple("Cannot infer type. Type annotations needed.") @@ -368,6 +377,29 @@ impl Context { } } +fn get_stdlib_decl(name: &str) -> Option { + let ty_lit = match name { + "int" => TyLit::Int, + "float" => TyLit::Float, + "bool" => TyLit::Bool, + "text" => TyLit::Text, + "date" => TyLit::Date, + "time" => TyLit::Time, + "timestamp" => TyLit::Timestamp, + "table" => { + // TODO: this is just a dummy that gets intercepted when resolving types + return Some(ExprKind::Set(SetExpr::Array(Box::new(SetExpr::Singleton( + Literal::Null, + ))))); + } + "column" => TyLit::Column, + "list" => TyLit::List, + "scalar" => TyLit::Scalar, + _ => return None, + }; + Some(ExprKind::Set(SetExpr::Primitive(ty_lit))) +} + impl Default for DeclKind { fn default() -> Self { DeclKind::Module(Module::default()) diff --git a/prql-compiler/src/semantic/lowering.rs b/prql-compiler/src/semantic/lowering.rs index ed9d59bf8002..2545977b416d 100644 --- a/prql-compiler/src/semantic/lowering.rs +++ b/prql-compiler/src/semantic/lowering.rs @@ -32,20 +32,23 @@ pub fn lower_ast_to_ir(statements: Vec, context: Context) -> Result query_def = Some(def), - pl::StmtKind::Main(expr) => { + QueryDef(def) => query_def = Some(def), + Main(expr) => { let relation = l.lower_relation(*expr)?; main_pipeline = Some(relation); } - pl::StmtKind::FuncDef(_) | pl::StmtKind::VarDef(_) => {} + FuncDef(_) | VarDef(_) | TypeDef(_) => {} } } Ok(Query { def: query_def.unwrap_or_default(), tables: l.table_buffer, - relation: main_pipeline.ok_or_else(|| Error::new_simple("missing main pipeline"))?, + relation: main_pipeline + .ok_or_else(|| Error::new_simple("Missing query").with_code("E0001"))?, }) } @@ -269,7 +272,7 @@ impl Lowerer { let ty = expr.ty.clone(); let prev_pipeline = self.pipeline.drain(..).collect_vec(); - self.lower_pipeline(expr)?; + self.lower_pipeline(expr, None)?; let mut transforms = self.pipeline.drain(..).collect_vec(); let columns = self.push_select(ty, &mut transforms)?; @@ -284,10 +287,22 @@ impl Lowerer { } // Result is stored in self.pipeline - fn lower_pipeline(&mut self, ast: pl::Expr) -> Result<()> { + fn lower_pipeline(&mut self, ast: pl::Expr, closure_param: Option) -> Result<()> { let transform_call = match ast.kind { pl::ExprKind::TransformCall(transform) => transform, + pl::ExprKind::Closure(closure) => { + let param = closure.params.first(); + let param = param.and_then(|p| p.name.parse::().ok()); + return self.lower_pipeline(*closure.body, param); + } _ => { + if let Some(target) = ast.target_id { + if Some(target) == closure_param { + // ast is a closure param, so we can skip pushing From + return Ok(()); + } + } + let table_ref = self.lower_table_ref(ast)?; self.pipeline.push(Transform::From(table_ref)); return Ok(()); @@ -295,7 +310,7 @@ impl Lowerer { }; // lower input table - self.lower_pipeline(*transform_call.input)?; + self.lower_pipeline(*transform_call.input, closure_param)?; // ... and continues with transforms created in this function @@ -362,8 +377,16 @@ impl Lowerer { pl::TransformKind::Append(bottom) => { let bottom = self.lower_table_ref(*bottom)?; - let transform = Transform::Append(bottom); - self.pipeline.push(transform); + self.pipeline.push(Transform::Append(bottom)); + } + pl::TransformKind::Loop(pipeline) => { + let relation = self.lower_relation(*pipeline)?; + let mut pipeline = relation.kind.into_pipeline().unwrap(); + + // last select is not needed here + pipeline.pop(); + + self.pipeline.push(Transform::Loop(pipeline)); } pl::TransformKind::Group { .. } | pl::TransformKind::Window { .. } => unreachable!( "transform `{}` cannot be lowered.", @@ -618,7 +641,7 @@ impl Lowerer { pl::ExprKind::FString(items) => { rq::ExprKind::FString(self.lower_interpolations(items)?) } - pl::ExprKind::Switch(cases) => rq::ExprKind::Switch( + pl::ExprKind::Case(cases) => rq::ExprKind::Case( cases .into_iter() .map(|case| -> Result<_> { @@ -635,12 +658,13 @@ impl Lowerer { rq::ExprKind::BuiltInFunction { name, args } } - + pl::ExprKind::Param(id) => rq::ExprKind::Param(id), pl::ExprKind::FuncCall(_) | pl::ExprKind::Range(_) | pl::ExprKind::List(_) | pl::ExprKind::Closure(_) | pl::ExprKind::Pipeline(_) + | pl::ExprKind::Set(_) | pl::ExprKind::TransformCall(_) => { log::debug!("cannot lower {ast:?}"); return Err(Error::new(Reason::Unexpected { @@ -682,9 +706,10 @@ impl Lowerer { let name = match name { Some(v) => RelationColumn::Single(Some(v.clone())), None => return Err(Error::new_simple( - "This table contains unnamed columns, that need to be referenced by name", + "This table contains unnamed columns that need to be referenced by name", ) .with_span(self.context.span_map.get(&id).cloned()) + .with_help("The name may have been overridden later in the pipeline.") .into()), }; log::trace!("lookup cid of name={name:?} in input {input_columns:?}"); @@ -801,7 +826,7 @@ fn lower_table(lowerer: &mut Lowerer, table: context::TableDecl, fq_ident: Ident TableExpr::LocalTable => { extern_ref_to_relation(columns, TableExternRef::LocalTable(fq_ident.name)) } - TableExpr::Anchor(id) => extern_ref_to_relation(columns, TableExternRef::Anchor(id)), + TableExpr::Param(id) => extern_ref_to_relation(columns, TableExternRef::Param(id)), }; log::debug!("lowering table {name:?}, columns = {:?}", relation.columns); diff --git a/prql-compiler/src/semantic/mod.rs b/prql-compiler/src/semantic/mod.rs index 91e172cd341d..c0a5d72b3339 100644 --- a/prql-compiler/src/semantic/mod.rs +++ b/prql-compiler/src/semantic/mod.rs @@ -169,40 +169,6 @@ mod test { - Wildcard "### ); - assert_yaml_snapshot!(parse_and_resolve(r###" - prql target:sql.bigquery version:"0.5" - - from employees - "###).unwrap(), @r###" - --- - def: - version: ^0.5 - other: - target: sql.bigquery - tables: - - id: 0 - name: ~ - relation: - kind: - ExternRef: - LocalTable: employees - columns: - - Wildcard - relation: - kind: - Pipeline: - - From: - source: 0 - columns: - - - Wildcard - - 0 - name: employees - - Select: - - 0 - columns: - - Wildcard - "### ); - assert!(parse_and_resolve( r###" prql target:sql.bigquery version:foo diff --git a/prql-compiler/src/semantic/resolver.rs b/prql-compiler/src/semantic/resolver.rs index dcc68bdf1b62..e1dd506409c8 100644 --- a/prql-compiler/src/semantic/resolver.rs +++ b/prql-compiler/src/semantic/resolver.rs @@ -16,7 +16,7 @@ use super::context::{Context, Decl, DeclKind}; use super::module::{Module, NS_FRAME, NS_FRAME_RIGHT, NS_PARAM}; use super::reporting::debug_call_tree; use super::transforms::{self, Flattener}; -use super::type_resolver::{resolve_type, type_of_closure, validate_type}; +use super::type_resolver::{self, infer_type, type_of_closure, validate_type}; /// Runs semantic analysis on the query, using current state. /// @@ -69,11 +69,20 @@ impl AstFold for Resolver { } StmtKind::VarDef(var_def) => { let var_def = self.fold_var_def(var_def)?; + self.context.declare_var(var_def, stmt.id, stmt.span)?; + continue; + } + StmtKind::TypeDef(ty_def) => { let var_def = VarDef { - value: Box::new(Flattener::fold(*var_def.value)), - ..var_def + name: ty_def.name, + value: Box::new(ty_def.value.unwrap_or_else(|| { + let mut e = Expr::null(); + e.ty = Some(Ty::SetExpr(SetExpr::Set)); + e + })), }; + let var_def = self.fold_var_def(var_def)?; self.context.declare_var(var_def, stmt.id, stmt.span)?; continue; } @@ -88,6 +97,13 @@ impl AstFold for Resolver { Ok(res) } + fn fold_var_def(&mut self, var_def: VarDef) -> Result { + Ok(VarDef { + name: var_def.name, + value: Box::new(Flattener::fold(self.fold_expr(*var_def.value)?)), + }) + } + fn fold_expr(&mut self, node: Expr) -> Result { if node.id.is_some() && !matches!(node.kind, ExprKind::Closure(_)) { return Ok(node); @@ -114,7 +130,7 @@ impl AstFold for Resolver { match &entry.kind { // convert ident to function without args DeclKind::FuncDef(func_def) => { - let closure = closure_of_func_def(func_def, fq_ident); + let closure = self.closure_of_func_def(func_def.clone(), fq_ident)?; if self.in_func_call_name { Expr::from(ExprKind::Closure(Box::new(closure))) @@ -227,6 +243,11 @@ impl AstFold for Resolver { Expr { kind, ..node } } + ExprKind::Unary { + op: UnOp::Add, + expr, + } => self.fold_expr(*expr)?, + ExprKind::Unary { op: UnOp::Not, expr, @@ -285,7 +306,7 @@ impl AstFold for Resolver { r.span = r.span.or(span); if r.ty.is_none() { - r.ty = Some(resolve_type(&r, &self.context)?); + r.ty = Some(infer_type(&r, &self.context)?); } if let Some(Ty::Table(frame)) = &mut r.ty { if let Some(alias) = r.alias.take() { @@ -297,21 +318,6 @@ impl AstFold for Resolver { } } -fn closure_of_func_def(func_def: &FuncDef, fq_ident: Ident) -> Closure { - Closure { - name: Some(fq_ident), - body: func_def.body.clone(), - body_ty: func_def.return_ty.clone(), - - params: func_def.positional_params.clone(), - named_params: func_def.named_params.clone(), - - args: vec![], - - env: HashMap::default(), - } -} - impl Resolver { fn resolve_pipeline(&mut self, Pipeline { mut exprs }: Pipeline) -> Result { let mut value = exprs.remove(0); @@ -357,7 +363,7 @@ impl Resolver { body_ty: None, args: vec![], - params: vec![FuncParam { + params: vec![ClosureParam { name: closure_param.to_string(), default_value: None, ty: None, @@ -409,7 +415,17 @@ impl Resolver { closure.args.len(), closure.params.len() ); - let enough_args = closure.args.len() >= closure.params.len(); + + if closure.args.len() > closure.params.len() { + return Err(Error::new_simple(format!( + "Too many arguments to function `{}`", + closure.as_debug_name() + )) + .with_span(span) + .into()); + } + + let enough_args = closure.args.len() == closure.params.len(); let mut r = if enough_args { // push the env @@ -430,7 +446,11 @@ impl Resolver { let closure = self.resolve_function_args(closure)?; // evaluate - let needs_window = Some(Ty::column()) <= closure.body_ty; + let needs_window = (closure.body_ty) + .as_ref() + .map(|ty| ty.is_superset_of(&Ty::SetExpr(SetExpr::Primitive(TyLit::Column)))) + .unwrap_or_default(); + let mut res = match self.cast_built_in_function(closure)? { // this function call is a built-in function Ok(transform) => transform, @@ -588,12 +608,11 @@ impl Resolver { log::debug!("resolved arg to {}", arg.kind.as_ref()); // add table's frame into scope - if let Some(Ty::Table(frame)) = &arg.ty { - if is_last { - self.context.root_mod.insert_frame(frame, NS_FRAME); - } else { - self.context.root_mod.insert_frame(frame, NS_FRAME_RIGHT); - } + let frame = arg.ty.as_ref().unwrap().as_table().unwrap(); + if is_last { + self.context.root_mod.insert_frame(frame, NS_FRAME); + } else { + self.context.root_mod.insert_frame(frame, NS_FRAME_RIGHT); } closure.args[index] = arg; @@ -640,7 +659,7 @@ impl Resolver { fn fold_and_type_check( &mut self, arg: Expr, - param: &FuncParam, + param: &ClosureParam, func_name: &Option, ) -> Result { let mut arg = self.fold_within_namespace(arg, ¶m.name)?; @@ -723,6 +742,53 @@ impl Resolver { except, })) } + + fn closure_of_func_def(&mut self, func_def: FuncDef, fq_ident: Ident) -> Result { + let body_ty = self.fold_type_expr(func_def.return_ty)?; + + Ok(Closure { + name: Some(fq_ident), + body: func_def.body, + body_ty, + + params: self.fold_func_params(&func_def.positional_params)?, + named_params: self.fold_func_params(&func_def.named_params)?, + + args: vec![], + + env: HashMap::default(), + }) + } + + fn fold_func_params(&mut self, func_params: &[FuncParam]) -> Result> { + let mut params = Vec::with_capacity(func_params.len()); + for p in func_params.iter().cloned() { + params.push(ClosureParam { + name: p.name, + ty: self.fold_type_expr(p.ty_expr)?, + default_value: p.default_value, + }) + } + Ok(params) + } + + fn fold_type_expr(&mut self, expr: Option) -> Result> { + Ok(match expr { + Some(expr) => { + let expr = self.fold_expr(expr)?; + + let set_expr = type_resolver::coerce_to_set(expr, &self.context)?; + + // TODO: workaround + if let SetExpr::Array(_) = set_expr { + return Ok(Some(Ty::Table(Frame::default()))); + } + + Some(Ty::SetExpr(set_expr)) + } + None => None, + }) + } } fn env_of_closure(closure: Closure) -> (Module, Expr) { @@ -745,19 +811,19 @@ fn env_of_closure(closure: Closure) -> (Module, Expr) { mod test { use anyhow::Result; use insta::assert_yaml_snapshot; + use itertools::Itertools; use crate::ast::pl::{Expr, Ty}; use crate::semantic::resolve_only; - use crate::utils::IntoOnly; fn parse_and_resolve(query: &str) -> Result { let (stmts, _) = resolve_only(crate::parser::parse(query)?, None)?; - Ok(*stmts.into_only()?.kind.into_main()?) + Ok(*stmts.into_iter().exactly_one()?.kind.into_main()?) } fn resolve_type(query: &str) -> Result { - Ok(parse_and_resolve(query)?.ty.unwrap_or_default()) + Ok(parse_and_resolve(query)?.ty.unwrap_or(Ty::Infer)) } fn resolve_derive(query: &str) -> Result> { diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap index 9aaf81765509..a118b1d2ee1d 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap @@ -11,12 +11,12 @@ Table: input_name: customers except: [] inputs: - - id: 4 + - id: 6 name: table_1 table: - default_db - table_1 - - id: 8 + - id: 13 name: customers table: - default_db diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap index d6ad7c7d27bb..3063f2c406f6 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap @@ -8,23 +8,23 @@ Table: name: - e - emp_no - expr_id: 17 + expr_id: 24 - Single: name: - e - gender - expr_id: 18 + expr_id: 25 - Single: name: - emp_salary - expr_id: 26 + expr_id: 36 inputs: - - id: 4 + - id: 6 name: e table: - default_db - employees - - id: 11 + - id: 18 name: salaries table: - default_db diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap index e528472dda2c..e54c8881a653 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap @@ -8,22 +8,22 @@ Table: name: - orders - customer_no - expr_id: 11 + expr_id: 18 - Single: name: - orders - gross - expr_id: 12 + expr_id: 19 - Single: name: - orders - tax - expr_id: 13 + expr_id: 20 - Single: name: ~ - expr_id: 14 + expr_id: 21 inputs: - - id: 4 + - id: 6 name: orders table: - default_db diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_1.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_1.snap index f31194a7aa5d..c8780eb17a9a 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_1.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_1.snap @@ -2,26 +2,27 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n func subtract a b -> a - b\n\n from employees\n derive [\n net_salary = subtract gross_salary tax\n ]\n \"#).unwrap()" --- -- id: 13 +- id: 18 Binary: left: - id: 11 + id: 16 Ident: - _frame - employees - gross_salary - target_id: 5 + target_id: 7 ty: Infer op: Sub right: - id: 12 + id: 17 Ident: - _frame - employees - tax - target_id: 5 + target_id: 7 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column alias: net_salary diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_nested.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_nested.snap index 7901f4f36660..75477a3642f6 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_nested.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_nested.snap @@ -2,55 +2,57 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n func lag_day x -> s\"lag_day_todo({x})\"\n func ret x dividend_return -> x / (lag_day x) - 1 + dividend_return\n\n from a\n derive (ret b c)\n \"#).unwrap()" --- -- id: 14 +- id: 19 Binary: left: - id: 15 + id: 20 Binary: left: - id: 12 - Ident: - - _frame - - a - - b - target_id: 6 + id: 21 + Binary: + left: + id: 17 + Ident: + - _frame + - a + - b + target_id: 8 + ty: Infer + op: Div + right: + id: 26 + SString: + - String: lag_day_todo( + - Expr: + id: 17 + Ident: + - _frame + - a + - b + target_id: 8 + ty: Infer + - String: ) + ty: Infer ty: Infer - op: Div + op: Sub right: - id: 20 - SString: - - String: lag_day_todo( - - Expr: - id: 12 - Ident: - - _frame - - a - - b - target_id: 6 - ty: Infer - - String: ) - ty: Infer - ty: Infer - op: Sub - right: - id: 22 - Binary: - left: - id: 23 + id: 28 Literal: Integer: 1 ty: - Literal: Integer - op: Add - right: - id: 13 - Ident: - - _frame - - a - - c - target_id: 6 - ty: Infer + SetExpr: + Primitive: Int + ty: Infer + op: Add + right: + id: 18 + Ident: + - _frame + - a + - c + target_id: 8 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline-2.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline-2.snap index 1638ac3cfafa..ea54921c5edc 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline-2.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline-2.snap @@ -2,40 +2,43 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n func plus_one x -> x + 1\n func plus x y -> x + y\n\n from a\n derive [b = (sum foo | plus_one | plus 2)]\n \"#).unwrap()" --- -- id: 23 +- id: 31 Binary: left: - id: 17 + id: 25 Literal: Integer: 2 ty: - Literal: Integer + SetExpr: + Primitive: Int op: Add right: - id: 20 + id: 28 Binary: left: - id: 11 + id: 16 BuiltInFunction: name: std.sum args: - - id: 13 + - id: 21 Ident: - _frame - a - foo - target_id: 6 + target_id: 8 ty: Infer ty: Infer op: Add right: - id: 22 + id: 30 Literal: Integer: 1 ty: - Literal: Integer + SetExpr: + Primitive: Int ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column alias: b diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline.snap index 1ea6487bb964..335d1d89e686 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__functions_pipeline.snap @@ -2,18 +2,19 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n from a\n derive one = (foo | sum)\n \"#).unwrap()" --- -- id: 10 +- id: 15 BuiltInFunction: name: std.sum args: - - id: 9 + - id: 14 Ident: - _frame - a - foo - target_id: 4 + target_id: 6 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column alias: one diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__named_args.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__named_args.snap index d66d43bc57bc..5afd102820a2 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__named_args.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__named_args.snap @@ -2,44 +2,48 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n func add x to:1 -> x + to\n\n from foo_table\n derive [\n added = add bar to:3,\n added_default = add bar\n ]\n \"#).unwrap()" --- -- id: 13 +- id: 18 Binary: left: - id: 12 + id: 17 Ident: - _frame - foo_table - bar - target_id: 5 + target_id: 7 ty: Infer op: Add right: - id: 11 + id: 16 Literal: Integer: 3 ty: - Literal: Integer + SetExpr: + Primitive: Int ty: - Literal: Column + SetExpr: + Primitive: Column alias: added -- id: 20 +- id: 25 Binary: left: - id: 19 + id: 24 Ident: - _frame - foo_table - bar - target_id: 5 + target_id: 7 ty: Infer op: Add right: - id: 18 + id: 23 Literal: Integer: 1 ty: - Literal: Integer + SetExpr: + Primitive: Int ty: - Literal: Column + SetExpr: + Primitive: Column alias: added_default diff --git a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__variables_1.snap b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__variables_1.snap index ade0148c53ad..ea39d81c2bbc 100644 --- a/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__variables_1.snap +++ b/prql-compiler/src/semantic/snapshots/prql_compiler__semantic__resolver__test__variables_1.snap @@ -2,47 +2,49 @@ source: prql-compiler/src/semantic/resolver.rs expression: "resolve_derive(r#\"\n from employees\n derive [\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost\n ]\n \"#).unwrap()" --- -- id: 8 +- id: 13 Binary: left: - id: 9 + id: 14 Ident: - _frame - employees - salary - target_id: 4 + target_id: 6 ty: Infer op: Add right: - id: 10 + id: 15 Ident: - _frame - employees - payroll_tax - target_id: 4 + target_id: 6 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column alias: gross_salary -- id: 11 +- id: 16 Binary: left: - id: 12 + id: 17 Ident: - _frame - gross_salary - target_id: 8 + target_id: 13 ty: Infer op: Add right: - id: 13 + id: 18 Ident: - _frame - employees - benefits_cost - target_id: 4 + target_id: 6 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column alias: gross_cost diff --git a/prql-compiler/src/semantic/static_analysis.rs b/prql-compiler/src/semantic/static_analysis.rs index 4db3b93fd54d..081fff2b5821 100644 --- a/prql-compiler/src/semantic/static_analysis.rs +++ b/prql-compiler/src/semantic/static_analysis.rs @@ -131,7 +131,7 @@ fn eval(kind: ExprKind) -> ExprKind { } } - ExprKind::Switch(items) => { + ExprKind::Case(items) => { let mut res = Vec::with_capacity(items.len()); for item in items { if let ExprKind::Literal(Literal::Boolean(condition)) = item.condition.kind { @@ -160,7 +160,7 @@ fn eval(kind: ExprKind) -> ExprKind { } } - ExprKind::Switch(res) + ExprKind::Case(res) } k => k, diff --git a/prql-compiler/src/semantic/std.prql b/prql-compiler/src/semantic/std.prql index f906e2dc7b23..2e8042ecb27b 100644 --- a/prql-compiler/src/semantic/std.prql +++ b/prql-compiler/src/semantic/std.prql @@ -1,14 +1,14 @@ # Aggregate Functions -func min column -> null -func max column -> null -func sum column -> null -func avg column -> null -func stddev column -> null -func average column -> null -func count non_null:s"*" -> null +func min column -> null +func max column -> null +func sum column -> null +func avg column -> null +func stddev column -> null +func average column -> null +func count non_null:s"*" -> null # TODO: Possibly make this into `count distinct:true` (or like `distinct:` as an # abbreviation of that?) -func count_distinct column -> null +func count_distinct column -> null # Window functions func lag offset column -> null @@ -48,6 +48,7 @@ func remove `default_db.bottom`
top
-> ( filter (all (map _is_null b.*)) select t.* ) +func loop
pipeline top
-> null # List functions func all list -> null @@ -57,5 +58,21 @@ func _eq a -> null func _is_null a -> _param.a == null # Misc -func from_text
text `noresolve.format`:csv -> null -func anchor
id -> null +func from_text
input `noresolve.format`:csv -> null + +# String functions +func lower column -> null +func upper column -> null + +# type primitives +type int +type float +type bool +type text +type date +type time +type timestamp +type table +type column +type list +type scalar diff --git a/prql-compiler/src/semantic/transforms.rs b/prql-compiler/src/semantic/transforms.rs index b517b2a81c33..f400456636ac 100644 --- a/prql-compiler/src/semantic/transforms.rs +++ b/prql-compiler/src/semantic/transforms.rs @@ -10,7 +10,7 @@ use crate::ast::pl::*; use crate::ast::rq::RelationColumn; use crate::error::{Error, Reason, WithErrorInfo}; -use super::context::{Decl, DeclKind, TableDecl, TableExpr}; +use super::context::{Decl, DeclKind}; use super::module::{Module, NS_FRAME, NS_PARAM}; use super::resolver::Resolver; use super::{Context, Frame}; @@ -192,6 +192,13 @@ pub fn cast_transform(resolver: &mut Resolver, closure: Closure) -> Result { + let [pipeline, tbl] = unpack::<2>(closure); + + let pipeline = fold_by_simulating_eval(resolver, pipeline, tbl.ty.clone().unwrap())?; + + (TransformKind::Loop(Box::new(pipeline)), tbl) + } "std.in" => { // yes, this is not a transform, but this is the most appropriate place for it @@ -368,40 +375,6 @@ pub fn cast_transform(resolver: &mut Resolver, closure: Closure) -> Result { - // yes, this is not a transform, but this is the most appropriate place for it - - let [id_expr] = unpack::<1>(closure); - - let id = match id_expr.kind { - ExprKind::Literal(Literal::String(text)) => text, - _ => { - return Err(Error::new(Reason::Expected { - who: Some("std.anchor".to_string()), - expected: "a string literal".to_string(), - found: format!("`{id_expr}`"), - }) - .with_span(id_expr.span) - .into()); - } - }; - - let ident = Ident::from_path(vec!["_anchor".to_string(), id.clone()]); - let entry = Decl { - declared_at: id_expr.id, - kind: DeclKind::TableDecl(TableDecl { - columns: vec![RelationColumn::Wildcard], - expr: TableExpr::Anchor(id), - }), - order: 0, - }; - resolver.context.root_mod.insert(ident.clone(), entry)?; - - let mut res = Expr::from(ExprKind::Ident(ident)); - res.alias = Some("anchor".to_string()); - return Ok(Ok(res)); - } - _ => return Ok(Err(closure)), }; @@ -496,7 +469,7 @@ fn fold_by_simulating_eval( body_ty: None, args: vec![], - params: vec![FuncParam { + params: vec![ClosureParam { name: param_id.to_string(), ty: None, default_value: None, @@ -582,6 +555,7 @@ impl TransformCall { let bottom = ty_frame_or_default(bottom)?; append(top, bottom)? } + Loop(_) => ty_frame_or_default(&self.input)?, Sort { .. } | Filter { .. } | Take { .. } => ty_frame_or_default(&self.input)?, }) } @@ -1104,7 +1078,7 @@ mod tests { let query = parse( " from c_invoice - group date (aggregate average amount) + group issued_at (aggregate average amount) ", ) .unwrap(); @@ -1115,7 +1089,7 @@ mod tests { let query = parse( " from c_invoice - group date ( + group issued_at ( aggregate (average amount) ) ", @@ -1125,10 +1099,10 @@ mod tests { assert_yaml_snapshot!(result, @r###" --- - Main: - id: 18 + id: 28 TransformCall: input: - id: 4 + id: 6 Ident: - default_db - c_invoice @@ -1139,7 +1113,7 @@ mod tests { input_name: c_invoice except: [] inputs: - - id: 4 + - id: 6 name: c_invoice table: - default_db @@ -1147,26 +1121,27 @@ mod tests { kind: Aggregate: assigns: - - id: 15 + - id: 22 BuiltInFunction: name: std.average args: - - id: 17 + - id: 27 Ident: - _frame - c_invoice - amount - target_id: 4 + target_id: 6 ty: Infer ty: - Literal: Column + SetExpr: + Primitive: Column partition: - - id: 8 + - id: 12 Ident: - _frame - c_invoice - - date - target_id: 4 + - issued_at + target_id: 6 ty: Infer ty: Table: @@ -1174,13 +1149,13 @@ mod tests { - Single: name: - c_invoice - - date - expr_id: 8 + - issued_at + expr_id: 12 - Single: name: ~ - expr_id: 15 + expr_id: 22 inputs: - - id: 4 + - id: 6 name: c_invoice table: - default_db diff --git a/prql-compiler/src/semantic/type_resolver.rs b/prql-compiler/src/semantic/type_resolver.rs index e8c45fa0e0a3..229faacfb131 100644 --- a/prql-compiler/src/semantic/type_resolver.rs +++ b/prql-compiler/src/semantic/type_resolver.rs @@ -1,4 +1,3 @@ -use std::cmp::Ordering; use std::collections::HashSet; use anyhow::Result; @@ -8,7 +7,75 @@ use crate::error::{Error, Reason, WithErrorInfo}; use super::Context; -pub fn resolve_type(node: &Expr, context: &Context) -> Result { +/// Takes a resolved [Expr] and evaluates it a set expression that can be used to construct a type. +pub fn coerce_to_set(expr: Expr, context: &Context) -> Result { + coerce_to_named_set(expr, context).map(|(_, s)| s) +} + +fn coerce_to_named_set(expr: Expr, context: &Context) -> Result<(Option, SetExpr), Error> { + let name = expr.alias; + let expr = coerce_kind_to_set(expr.kind, context).map_err(|e| e.with_span(expr.span))?; + + Ok((name, expr)) +} + +fn coerce_kind_to_set(expr: ExprKind, context: &Context) -> Result { + // primitives + if let ExprKind::Set(set_expr) = expr { + return Ok(set_expr); + } + + // singletons + if let ExprKind::Literal(lit) = expr { + return Ok(SetExpr::Singleton(lit)); + } + + // tuples + if let ExprKind::List(elements) = expr { + let mut set_elements = Vec::with_capacity(elements.len()); + + for e in elements { + let (name, set) = coerce_to_named_set(e, context)?; + + set_elements.push(TupleElement::Single(name, set)); + } + + return Ok(SetExpr::Tuple(set_elements)); + } + + // unions + if let ExprKind::Binary { + left, + op: BinOp::Or, + right, + } = expr + { + let left = coerce_to_named_set(*left, context)?; + let right = coerce_to_named_set(*right, context)?; + + // flatten nested unions + let mut options = Vec::with_capacity(2); + if let SetExpr::Union(parts) = left.1 { + options.extend(parts); + } else { + options.push(left); + } + if let SetExpr::Union(parts) = right.1 { + options.extend(parts); + } else { + options.push(right); + } + + return Ok(SetExpr::Union(options)); + } + + Err(Error::new_simple(format!( + "not a set expression: {}", + Expr::from(expr) + ))) +} + +pub fn infer_type(node: &Expr, context: &Context) -> Result { if let Some(ty) = &node.ty { return Ok(ty.clone()); } @@ -16,13 +83,13 @@ pub fn resolve_type(node: &Expr, context: &Context) -> Result { Ok(match &node.kind { ExprKind::Literal(ref literal) => match literal { Literal::Null => Ty::Infer, - Literal::Integer(_) => TyLit::Integer.into(), - Literal::Float(_) => TyLit::Float.into(), - Literal::Boolean(_) => TyLit::Bool.into(), - Literal::String(_) => TyLit::String.into(), - Literal::Date(_) => TyLit::Date.into(), - Literal::Time(_) => TyLit::Time.into(), - Literal::Timestamp(_) => TyLit::Timestamp.into(), + Literal::Integer(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Int)), + Literal::Float(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Float)), + Literal::Boolean(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Bool)), + Literal::String(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Text)), + Literal::Date(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Date)), + Literal::Time(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Time)), + Literal::Timestamp(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Timestamp)), Literal::ValueAndUnit(_) => Ty::Infer, // TODO Literal::Relation(_) => unreachable!(), }, @@ -30,11 +97,11 @@ pub fn resolve_type(node: &Expr, context: &Context) -> Result { ExprKind::Ident(_) | ExprKind::Pipeline(_) | ExprKind::FuncCall(_) => Ty::Infer, ExprKind::SString(_) => Ty::Infer, - ExprKind::FString(_) => TyLit::String.into(), + ExprKind::FString(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::Text)), ExprKind::Range(_) => Ty::Infer, // TODO ExprKind::TransformCall(call) => Ty::Table(call.infer_type(context)?), - ExprKind::List(_) => Ty::Literal(TyLit::List), + ExprKind::List(_) => Ty::SetExpr(SetExpr::Primitive(TyLit::List)), _ => Ty::Infer, }) @@ -94,10 +161,7 @@ where }); } - let expected_is_above = matches!( - expected.partial_cmp(&found_ty), - Some(Ordering::Equal | Ordering::Greater) - ); + let expected_is_above = expected.is_superset_of(&found_ty); if !expected_is_above { let e = Err(Error::new(Reason::Expected { who: who(), diff --git a/prql-compiler/src/snapshots/prql_compiler__test__prql_to_sql_2.snap b/prql-compiler/src/snapshots/prql_compiler__test__prql_to_sql_2.snap index bbc44298cbb5..23a1cc2ad749 100644 --- a/prql-compiler/src/snapshots/prql_compiler__test__prql_to_sql_2.snap +++ b/prql-compiler/src/snapshots/prql_compiler__test__prql_to_sql_2.snap @@ -25,7 +25,7 @@ SELECT SUM(_expr_0) AS sum_gross_cost, COUNT(*) AS ct FROM - table_1 + table_1 AS table_0 WHERE _expr_0 > 0 GROUP BY @@ -37,3 +37,4 @@ ORDER BY sum_gross_cost LIMIT 20 + diff --git a/prql-compiler/src/sql/anchor.rs b/prql-compiler/src/sql/anchor.rs index 6740cd2cc391..a96d52300714 100644 --- a/prql-compiler/src/sql/anchor.rs +++ b/prql-compiler/src/sql/anchor.rs @@ -3,28 +3,28 @@ use itertools::Itertools; use std::collections::{HashMap, HashSet}; use crate::ast::rq::{ - self, fold_transform, CId, Compute, Expr, Relation, RelationColumn, RelationKind, RqFold, - TableDecl, TableRef, Transform, + self, fold_transform, CId, Compute, Expr, RelationColumn, RqFold, TableRef, Transform, }; +use crate::sql::context::SqlTableDecl; +use crate::sql::preprocess::{SqlRelation, SqlRelationKind}; use super::{ context::{AnchorContext, ColumnDecl}, preprocess::{SqlFold, SqlTransform}, }; -type RemainingPipeline = (Vec, Vec); - /// Splits pipeline into two parts, such that the second part contains /// maximum number of transforms while "fitting" into a SELECT query. pub(super) fn split_off_back( - ctx: &mut AnchorContext, - output: Vec, mut pipeline: Vec, -) -> (Option, Vec) { + ctx: &mut AnchorContext, +) -> (Option>, Vec) { if pipeline.is_empty() { return (None, Vec::new()); } + let output = AnchorContext::determine_select_columns(&pipeline); + log::debug!("traversing pipeline to obtain columns: {output:?}"); let mut following_transforms: HashSet = HashSet::new(); @@ -137,8 +137,9 @@ pub(super) fn split_off_back( None } else { // drop inputs that were satisfied in current pipeline + pipeline.push(SqlTransform::Super(Transform::Select(missing))); - Some((pipeline, missing)) + Some(pipeline) }; curr_pipeline_rev.reverse(); @@ -166,20 +167,23 @@ fn can_materialize(compute: &Compute, inputs_required: &[Requirement]) -> bool { } /// Applies adjustments to second part of a pipeline when it's split: -/// - prepend pipeline with From -/// - redefine columns materialized in preceding pipeline +/// - append Select to proceeding pipeline +/// - prepend From to atomic pipeline +/// - redefine columns materialized in atomic pipeline /// - redirect all references to original columns to the new ones pub(super) fn anchor_split( ctx: &mut AnchorContext, - first_table_name: &str, - cols_at_split: &[CId], - second_pipeline: Vec, + preceding: Vec, + atomic: Vec, ) -> Vec { let new_tid = ctx.tid.gen(); + let preceding_select = &preceding.last().unwrap().as_super().unwrap(); + let cols_at_split = preceding_select.as_select().unwrap(); + log::debug!("split pipeline, first pipeline output: {cols_at_split:?}"); - // define columns of the new CTE + // redefine columns of the atomic pipeline let mut cid_redirects = HashMap::::new(); let mut new_columns = Vec::new(); for old_cid in cols_at_split { @@ -204,32 +208,31 @@ pub(super) fn anchor_split( // define a new table ctx.table_decls.insert( new_tid, - TableDecl { + SqlTableDecl { id: new_tid, - name: Some(first_table_name.to_string()), - // here we should put the pipeline, but because how this function is called, - // we need to return the pipeline directly, so we just insert dummy expr instead - relation: Relation { - kind: RelationKind::SString(vec![]), - columns: vec![], - }, + name: None, + relation: Some(SqlRelation { + columns: cols_at_split + .iter() + .map(|_| RelationColumn::Single(None)) + .collect_vec(), + kind: SqlRelationKind::PreprocessedPipeline(preceding), + }), }, ); // define instance of that table - let table_ref = TableRef { + let table_ref = ctx.create_table_instance(TableRef { source: new_tid, - name: Some(first_table_name.to_string()), + name: None, columns: new_columns, - }; - ctx.create_table_instance(table_ref.clone()); + }); // adjust second part: prepend from and rewrite expressions to use new columns - let mut second = second_pipeline; + let mut second = atomic; second.insert(0, SqlTransform::Super(Transform::From(table_ref))); - let mut redirector = CidRedirector { ctx, cid_redirects }; - redirector.fold_sql_transforms(second).unwrap() + CidRedirector::redirect(second, cid_redirects, ctx) } /// Determines whether a pipeline must be split at a transform to @@ -248,6 +251,7 @@ fn is_split_required(transform: &SqlTransform, following: &mut HashSet) // - take (no limit) // - distinct // - append/except/intersect (no limit) + // - loop (max 1x) // // Select is not affected by the order. use SqlTransform::*; @@ -306,6 +310,7 @@ fn is_split_required(transform: &SqlTransform, following: &mut HashSet) "Distinct", ], ), + SqlTransform::Loop(_) => !following.is_empty(), _ => false, }; @@ -387,12 +392,13 @@ pub(super) fn get_requirements( cids } - Super(Append(_)) => unreachable!(), - Super(Select(_) | From(_) | Aggregate { .. }) + Super(Aggregate { .. } | Append(_) | Transform::Loop(_)) => unreachable!(), + Super(Select(_) | From(_)) | Distinct | Union { .. } | Except { .. } - | Intersect { .. } => return Vec::new(), + | Intersect { .. } + | SqlTransform::Loop(_) => return Vec::new(), }; // general case: determine complexity @@ -457,7 +463,7 @@ pub fn infer_complexity(compute: &Compute) -> Complexity { pub fn infer_complexity_expr(expr: &Expr) -> Complexity { match &expr.kind { - rq::ExprKind::Switch(_) => Complexity::NonGroup, + rq::ExprKind::Case(_) => Complexity::NonGroup, rq::ExprKind::Binary { left, right, .. } => { Complexity::max(infer_complexity_expr(left), infer_complexity_expr(right)) } @@ -470,6 +476,7 @@ pub fn infer_complexity_expr(expr: &Expr) -> Complexity { rq::ExprKind::ColumnRef(_) | rq::ExprKind::Literal(_) | rq::ExprKind::SString(_) + | rq::ExprKind::Param(_) | rq::ExprKind::FString(_) => Complexity::Plain, } } @@ -502,9 +509,20 @@ impl RqFold for CidCollector { } } -struct CidRedirector<'a> { - ctx: &'a mut AnchorContext, - cid_redirects: HashMap, +pub(super) struct CidRedirector<'a> { + pub ctx: &'a mut AnchorContext, + pub cid_redirects: HashMap, +} + +impl<'a> CidRedirector<'a> { + pub fn redirect( + pipeline: Vec, + cid_redirects: HashMap, + ctx: &mut AnchorContext, + ) -> Vec { + let mut redirector = CidRedirector { ctx, cid_redirects }; + redirector.fold_sql_transforms(pipeline).unwrap() + } } impl<'a> RqFold for CidRedirector<'a> { diff --git a/prql-compiler/src/sql/context.rs b/prql-compiler/src/sql/context.rs index 4fa41ea51e71..9c58e3cd156e 100644 --- a/prql-compiler/src/sql/context.rs +++ b/prql-compiler/src/sql/context.rs @@ -10,19 +10,19 @@ use itertools::Itertools; use crate::ast::pl::TableExternRef; use crate::ast::rq::{ - fold_table, CId, Compute, Query, RelationColumn, RelationKind, RqFold, TId, TableDecl, - TableRef, Transform, + fold_table, CId, Compute, Query, Relation, RelationColumn, RelationKind, RqFold, TId, + TableDecl, TableRef, Transform, }; use crate::utils::{IdGenerator, NameGenerator}; -use super::preprocess::SqlTransform; +use super::preprocess::{SqlRelation, SqlTransform}; -#[derive(Default)] +#[derive(Default, Debug)] pub struct AnchorContext { pub(super) column_decls: HashMap, pub(super) column_names: HashMap, - pub(super) table_decls: HashMap, + pub(super) table_decls: HashMap, pub(super) table_instances: HashMap, @@ -33,6 +33,20 @@ pub struct AnchorContext { pub(super) tid: IdGenerator, pub(super) tiid: IdGenerator, } + +#[derive(Debug, Clone)] +pub(super) struct SqlTableDecl { + #[allow(dead_code)] + pub id: TId, + + pub name: Option, + + /// Relation that still needs to be defined (usually as CTE) so it can be referenced by name. + /// None means that it has already been defined, or was not needed to be defined in the + /// first place. + pub relation: Option, +} + /// Table instance id #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TIId(usize); @@ -51,7 +65,7 @@ pub enum ColumnDecl { } impl AnchorContext { - pub fn of(query: Query) -> (Self, Query) { + pub fn of(query: Query) -> (Self, Relation) { let (cid, tid, query) = IdGenerator::load(query); let context = AnchorContext { @@ -78,7 +92,7 @@ impl AnchorContext { self.column_decls.insert(id, decl); } - pub fn create_table_instance(&mut self, mut table_ref: TableRef) { + pub fn create_table_instance(&mut self, mut table_ref: TableRef) -> TableRef { let tiid = self.tiid.gen(); for (col, cid) in &table_ref.columns { @@ -90,7 +104,8 @@ impl AnchorContext { table_ref.name = Some(self.table_name.gen()) } - self.table_instances.insert(tiid, table_ref); + self.table_instances.insert(tiid, table_ref.clone()); + table_ref } pub(crate) fn ensure_column_name(&mut self, cid: CId) -> Option<&String> { @@ -193,29 +208,45 @@ struct QueryLoader { } impl QueryLoader { - fn load(context: AnchorContext, query: Query) -> (AnchorContext, Query) { + fn load(context: AnchorContext, query: Query) -> (AnchorContext, Relation) { let mut loader = QueryLoader { context }; - let query = loader.fold_query(query).unwrap(); - (loader.context, query) + + for t in query.tables { + loader.load_table(t).unwrap(); + } + let relation = loader.fold_relation(query.relation).unwrap(); + (loader.context, relation) } -} -impl RqFold for QueryLoader { - fn fold_table(&mut self, table: TableDecl) -> Result { + fn load_table(&mut self, table: TableDecl) -> Result<()> { let mut decl = fold_table(self, table)?; + // assume name of the LocalTable that the relation is referencing if let RelationKind::ExternRef(TableExternRef::LocalTable(table)) = &decl.relation.kind { decl.name = Some(table.clone()); } + // generate name (if not present) if decl.name.is_none() && decl.relation.kind.as_extern_ref().is_none() { decl.name = Some(self.context.table_name.gen()); } - self.context.table_decls.insert(decl.id, decl.clone()); - Ok(decl) + let sql_decl = SqlTableDecl { + id: decl.id, + name: decl.name, + relation: if matches!(decl.relation.kind, RelationKind::ExternRef(_)) { + None + } else { + Some(decl.relation.into()) + }, + }; + + self.context.table_decls.insert(decl.id, sql_decl); + Ok(()) } +} +impl RqFold for QueryLoader { fn fold_compute(&mut self, compute: Compute) -> Result { self.context.register_compute(compute.clone()); Ok(compute) diff --git a/prql-compiler/src/sql/dialect.rs b/prql-compiler/src/sql/dialect.rs index ecf767817cff..71903dcbb6b8 100644 --- a/prql-compiler/src/sql/dialect.rs +++ b/prql-compiler/src/sql/dialect.rs @@ -14,6 +14,7 @@ use core::fmt::Debug; use serde::{Deserialize, Serialize}; +use std::any::{Any, TypeId}; use strum::{EnumMessage, IntoEnumIterator}; /// SQL dialect. @@ -28,6 +29,7 @@ use strum::{EnumMessage, IntoEnumIterator}; PartialEq, Eq, Clone, + Copy, Serialize, Deserialize, strum::Display, @@ -92,15 +94,23 @@ impl Default for Dialect { } } +#[derive(Debug)] pub struct GenericDialect; +#[derive(Debug)] pub struct SQLiteDialect; +#[derive(Debug)] pub struct MySqlDialect; +#[derive(Debug)] pub struct MsSqlDialect; +#[derive(Debug)] pub struct BigQueryDialect; +#[derive(Debug)] pub struct ClickHouseDialect; - +#[derive(Debug)] pub struct SnowflakeDialect; +#[derive(Debug)] pub struct DuckDbDialect; +#[derive(Debug)] pub struct PostgresDialect; pub(super) enum ColumnExclude { @@ -108,7 +118,7 @@ pub(super) enum ColumnExclude { Except, } -pub(super) trait DialectHandler { +pub(super) trait DialectHandler: Any + Debug { fn use_top(&self) -> bool { false } @@ -152,6 +162,18 @@ pub(super) trait DialectHandler { fn requires_quotes_intervals(&self) -> bool { false } + + /// Support for GROUP BY * + fn stars_in_group(&self) -> bool { + true + } +} + +impl dyn DialectHandler { + #[inline] + pub fn is(&self) -> bool { + TypeId::of::() == self.type_id() + } } impl DialectHandler for GenericDialect {} @@ -174,6 +196,10 @@ impl DialectHandler for SQLiteDialect { fn has_concat_function(&self) -> bool { false } + + fn stars_in_group(&self) -> bool { + false + } } impl DialectHandler for MsSqlDialect { diff --git a/prql-compiler/src/sql/gen_expr.rs b/prql-compiler/src/sql/gen_expr.rs index 3eb21149882e..ec1c8b464dfc 100644 --- a/prql-compiler/src/sql/gen_expr.rs +++ b/prql-compiler/src/sql/gen_expr.rs @@ -6,8 +6,7 @@ use lazy_static::lazy_static; use regex::Regex; use sqlparser::ast::{ self as sql_ast, BinaryOperator, DateTimeField, Function, FunctionArg, FunctionArgExpr, Ident, - Join, JoinConstraint, JoinOperator, ObjectName, OrderByExpr, SelectItem, TableAlias, - TableFactor, Top, UnaryOperator, Value, WindowFrameBound, WindowSpec, + ObjectName, OrderByExpr, SelectItem, Top, UnaryOperator, Value, WindowFrameBound, WindowSpec, }; use sqlparser::keywords::{ Keyword, ALL_KEYWORDS, ALL_KEYWORDS_INDEX, RESERVED_FOR_COLUMN_ALIAS, RESERVED_FOR_TABLE_ALIAS, @@ -15,8 +14,7 @@ use sqlparser::keywords::{ use std::collections::HashSet; use crate::ast::pl::{ - BinOp, ColumnSort, InterpolateItem, JoinSide, Literal, Range, SortDirection, TableExternRef, - WindowFrame, WindowKind, + BinOp, ColumnSort, InterpolateItem, Literal, Range, SortDirection, WindowFrame, WindowKind, }; use crate::ast::rq::*; use crate::error::{Error, Span}; @@ -93,9 +91,10 @@ pub(super) fn translate_expr_kind(item: ExprKind, ctx: &mut Context) -> Result sql_ast::Expr::Identifier(sql_ast::Ident::new(format!("${id}"))), ExprKind::FString(f_string_items) => translate_fstring(f_string_items, ctx)?, ExprKind::Literal(l) => translate_literal(l, ctx)?, - ExprKind::Switch(mut cases) => { + ExprKind::Case(mut cases) => { let default = cases .last() .filter(|last| { @@ -145,18 +144,17 @@ pub(super) fn translate_literal(l: Literal, ctx: &Context) -> Result sql_ast::Expr::Value(Value::Boolean(b)), Literal::Float(f) => sql_ast::Expr::Value(Value::Number(format!("{f:?}"), false)), Literal::Integer(i) => sql_ast::Expr::Value(Value::Number(format!("{i}"), false)), - Literal::Date(value) => sql_ast::Expr::TypedString { - data_type: sql_ast::DataType::Date, + Literal::Date(value) => translate_datetime_literal(sql_ast::DataType::Date, value, ctx), + Literal::Time(value) => translate_datetime_literal( + sql_ast::DataType::Time(None, sql_ast::TimezoneInfo::None), value, - }, - Literal::Time(value) => sql_ast::Expr::TypedString { - data_type: sql_ast::DataType::Time(None, sql_ast::TimezoneInfo::None), - value, - }, - Literal::Timestamp(value) => sql_ast::Expr::TypedString { - data_type: sql_ast::DataType::Timestamp(None, sql_ast::TimezoneInfo::None), + ctx, + ), + Literal::Timestamp(value) => translate_datetime_literal( + sql_ast::DataType::Timestamp(None, sql_ast::TimezoneInfo::None), value, - }, + ctx, + ), Literal::ValueAndUnit(vau) => { let sql_parser_datetime = match vau.unit.as_str() { "years" => DateTimeField::Year, @@ -186,8 +184,64 @@ pub(super) fn translate_literal(l: Literal, ctx: &Context) -> Result sql_ast::Expr { + if ctx.dialect.is::() { + translate_datetime_literal_with_sqlite_function(data_type, value) + } else { + translate_datetime_literal_with_typed_string(data_type, value) + } +} + +fn translate_datetime_literal_with_typed_string( + data_type: sql_ast::DataType, + value: String, +) -> sql_ast::Expr { + sql_ast::Expr::TypedString { data_type, value } +} + +fn translate_datetime_literal_with_sqlite_function( + data_type: sql_ast::DataType, + value: String, +) -> sql_ast::Expr { + // TODO: promote parsing timezone handling to the parser; we should be storing + // structured data rather than strings in the AST + let timezone_indicator_regex = Regex::new(r"([+-]\d{2}):?(\d{2})$").unwrap(); + let time_value = if let Some(groups) = timezone_indicator_regex.captures(value.as_str()) { + // formalize the timezone indicator to be [+-]HH:MM + // ref: https://www.sqlite.org/lang_datefunc.html + timezone_indicator_regex + .replace(&value, format!("{}:{}", &groups[1], &groups[2]).as_str()) + .to_string() + } else { + value + }; + + let arg = FunctionArg::Unnamed(FunctionArgExpr::Expr(sql_ast::Expr::Value( + Value::SingleQuotedString(time_value), + ))); + + let func_name = match data_type { + sql_ast::DataType::Date => data_type.to_string(), + sql_ast::DataType::Time(..) => data_type.to_string(), + sql_ast::DataType::Timestamp(..) => "DATETIME".to_string(), + _ => unreachable!(), + }; + + sql_ast::Expr::Function(Function { + name: ObjectName(vec![sql_ast::Ident::new(func_name)]), + args: vec![arg], + over: None, + distinct: false, + special: false, + }) +} + pub(super) fn translate_cid(cid: CId, ctx: &mut Context) -> Result { - if ctx.pre_projection { + if ctx.query.pre_projection { log::debug!("translating {cid:?} pre projection"); let decl = ctx.anchor.column_decls.get(&cid).expect("bad RQ ids"); @@ -206,7 +260,7 @@ pub(super) fn translate_cid(cid: CId, ctx: &mut Context) -> Result { let column = match col.clone() { - RelationColumn::Wildcard => "*".to_string(), + RelationColumn::Wildcard => translate_star(ctx, None)?, RelationColumn::Single(name) => name.unwrap(), }; let t = &ctx.anchor.table_instances[tiid]; @@ -227,11 +281,13 @@ pub(super) fn translate_cid(cid: CId, ctx: &mut Context) -> Result "*".to_string(), + ColumnDecl::RelationColumn(_, _, RelationColumn::Wildcard) => { + translate_star(ctx, None)? + } _ => { let name = ctx.anchor.column_names.get(&cid).cloned(); - name.expect("a name of this column to be set before generating SQL") + name.expect("name of this column has not been to be set before generating SQL") } }; @@ -244,35 +300,15 @@ pub(super) fn translate_cid(cid: CId, ctx: &mut Context) -> Result TableFactor { - let decl = ctx.anchor.table_decls.get(&table_ref.source).unwrap(); - - let name = match &decl.relation.kind { - // special case for anchor - RelationKind::ExternRef(TableExternRef::Anchor(anchor_id)) => { - sql_ast::ObjectName(vec![Ident::new(anchor_id.clone())]) - } - - // base case - _ => { - let decl_name = decl.name.clone().unwrap(); - - sql_ast::ObjectName(translate_ident(Some(decl_name), None, ctx)) - } - }; - - TableFactor::Table { - name, - alias: if decl.name == table_ref.name { - None - } else { - table_ref.name.map(|ident| TableAlias { - name: translate_ident_part(ident, ctx), - columns: vec![], - }) - }, - args: None, - with_hints: vec![], +pub(super) fn translate_star(ctx: &Context, span: Option) -> Result { + if !ctx.query.allow_stars { + Err( + Error::new_simple("Target dialect does not support * in this position.") + .with_span(span) + .into(), + ) + } else { + Ok("*".to_string()) } } @@ -625,23 +661,6 @@ pub(super) fn translate_column_sort( }) } -pub(super) fn translate_join( - (side, with, filter): (JoinSide, TableRef, Expr), - ctx: &mut Context, -) -> Result { - let constraint = JoinConstraint::On(translate_expr_kind(filter.kind, ctx)?); - - Ok(Join { - relation: table_factor_of_tid(with, ctx), - join_operator: match side { - JoinSide::Inner => JoinOperator::Inner(constraint), - JoinSide::Left => JoinOperator::LeftOuter(constraint), - JoinSide::Right => JoinOperator::RightOuter(constraint), - JoinSide::Full => JoinOperator::FullOuter(constraint), - }, - }) -} - /// Translate a PRQL Ident to a Vec of SQL Idents. // We return a vec of SQL Idents because sqlparser sometimes uses // [ObjectName](sql_ast::ObjectName) and sometimes uses @@ -653,7 +672,7 @@ pub(super) fn translate_ident( ctx: &Context, ) -> Vec { let mut parts = Vec::with_capacity(4); - if !ctx.omit_ident_prefix || column.is_none() { + if !ctx.query.omit_ident_prefix || column.is_none() { if let Some(table) = table_name { #[allow(clippy::if_same_then_else)] if ctx.dialect.big_query_quoting() { @@ -717,9 +736,6 @@ fn is_keyword(ident: &str) -> bool { } pub(super) fn translate_ident_part(ident: String, ctx: &Context) -> sql_ast::Ident { - // We'll remove this when we get the new dbt plugin working (so no need to - // integrate into the regex) - let is_jinja = ident.starts_with("{{") && ident.ends_with("}}"); lazy_static! { // One of: // - `*` @@ -732,7 +748,7 @@ pub(super) fn translate_ident_part(ident: String, ctx: &Context) -> sql_ast::Ide let is_bare = VALID_BARE_IDENT.is_match(&ident); - if is_jinja || is_bare && !is_keyword(&ident) { + if is_bare && !is_keyword(&ident) { sql_ast::Ident::new(ident) } else { sql_ast::Ident::with_quote(ctx.dialect.ident_quote(), ident) @@ -959,22 +975,12 @@ mod test { { let query = resolve(parse("from foo")?)?; let (anchor, _) = AnchorContext::of(query); - context_with_concat_function = Context { - dialect: Box::new(GenericDialect {}), - anchor, - omit_ident_prefix: false, - pre_projection: false, - }; + context_with_concat_function = Context::new(Box::new(GenericDialect {}), anchor); } { let query = resolve(parse("from foo")?)?; let (anchor, _) = AnchorContext::of(query); - context_without_concat_function = Context { - dialect: Box::new(SQLiteDialect {}), - anchor, - omit_ident_prefix: false, - pre_projection: false, - }; + context_without_concat_function = Context::new(Box::new(SQLiteDialect {}), anchor); } fn str_lit(s: &str) -> InterpolateItem { @@ -996,4 +1002,141 @@ mod test { Ok(()) } + + #[test] + fn test_translate_datetime_literal_with_sqlite_function() -> Result<()> { + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Date, + "2020-01-01".to_string(), + ), + @r###" +--- +Function: + name: + - value: DATE + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: 2020-01-01 + over: ~ + distinct: false + special: false +"### + ); + + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Time(None, sql_ast::TimezoneInfo::None), + "03:05".to_string(), + ), + @r###" +--- +Function: + name: + - value: TIME + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: "03:05" + over: ~ + distinct: false + special: false +"### + ); + + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Time(None, sql_ast::TimezoneInfo::None), + "03:05+08:00".to_string(), + ), + @r###" +--- +Function: + name: + - value: TIME + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: "03:05+08:00" + over: ~ + distinct: false + special: false +"### + ); + + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Time(None, sql_ast::TimezoneInfo::None), + "03:05+0800".to_string(), + ), + @r###" +--- +Function: + name: + - value: TIME + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: "03:05+08:00" + over: ~ + distinct: false + special: false +"### + ); + + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Timestamp(None, sql_ast::TimezoneInfo::None), + "2021-03-14T03:05+0800".to_string(), + ), + @r###" +--- +Function: + name: + - value: DATETIME + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: "2021-03-14T03:05+08:00" + over: ~ + distinct: false + special: false +"### + ); + + assert_yaml_snapshot!( + translate_datetime_literal_with_sqlite_function( + sql_ast::DataType::Timestamp(None, sql_ast::TimezoneInfo::None), + "2021-03-14T03:05+08:00".to_string(), + ), + @r###" +--- +Function: + name: + - value: DATETIME + quote_style: ~ + args: + - Unnamed: + Expr: + Value: + SingleQuotedString: "2021-03-14T03:05+08:00" + over: ~ + distinct: false + special: false +"### + ); + + Ok(()) + } } diff --git a/prql-compiler/src/sql/gen_projection.rs b/prql-compiler/src/sql/gen_projection.rs index bc40ab37fe61..c938c3514729 100644 --- a/prql-compiler/src/sql/gen_projection.rs +++ b/prql-compiler/src/sql/gen_projection.rs @@ -23,35 +23,37 @@ pub(super) fn try_into_exprs( ) -> Result> { let (cids, excluded) = translate_wildcards(&ctx.anchor, cids); - cids.into_iter() - .map(|cid| { - let decl = ctx.anchor.column_decls.get(&cid).unwrap(); + let mut res = Vec::new(); + for cid in cids { + let decl = ctx.anchor.column_decls.get(&cid).unwrap(); let tiid = if let ColumnDecl::RelationColumn(tiid, _, RelationColumn::Wildcard) = decl { tiid } else { // base case - return translate_cid(cid, ctx); + res.push(translate_cid(cid, ctx)?); + continue; }; - // wildcard - let t = &ctx.anchor.table_instances[tiid]; - let table_name = t.name.clone(); - - let ident = translate_ident(table_name, Some("*".to_string()), ctx); - if let Some(excluded) = excluded.get(&cid) { - if !excluded.is_empty() { - return Err(Error::new_simple( - "Excluding columns not supported as this position", - ) - .with_span(span) - .into()); - } + // star + let t = &ctx.anchor.table_instances[tiid]; + let table_name = t.name.clone(); + + let ident = translate_star(ctx, span)?; + if let Some(excluded) = excluded.get(&cid) { + if !excluded.is_empty() { + return Err( + Error::new_simple("Excluding columns not supported as this position") + .with_span(span) + .into(), + ); } + } + let ident = translate_ident(table_name, Some(ident), ctx); - Ok(sql_ast::Expr::CompoundIdentifier(ident)) - }) - .try_collect() + res.push(sql_ast::Expr::CompoundIdentifier(ident)); + } + Ok(res) } type Excluded = HashMap>; diff --git a/prql-compiler/src/sql/gen_query.rs b/prql-compiler/src/sql/gen_query.rs index a109eb8b0aa1..d9ec1e302f32 100644 --- a/prql-compiler/src/sql/gen_query.rs +++ b/prql-compiler/src/sql/gen_query.rs @@ -6,21 +6,24 @@ use std::collections::HashSet; use std::str::FromStr; use anyhow::{anyhow, Result}; -use enum_as_inner::EnumAsInner; use itertools::Itertools; use sqlparser::ast::{ - self as sql_ast, Ident, Select, SelectItem, SetExpr, TableAlias, TableFactor, TableWithJoins, + self as sql_ast, Ident, Join, JoinConstraint, JoinOperator, Select, SelectItem, SetExpr, + TableAlias, TableFactor, TableWithJoins, }; -use crate::ast::pl::{BinOp, Literal, RelationLiteral}; -use crate::ast::rq::{CId, Expr, ExprKind, Query, Relation, RelationKind, TableDecl, Transform}; -use crate::utils::{BreakUp, IntoOnly, Pluck}; +use crate::ast::pl::{BinOp, JoinSide, Literal, RelationLiteral}; +use crate::ast::rq::{CId, Expr, ExprKind, Query, RelationKind, TableRef, Transform}; +use crate::sql::anchor::anchor_split; +use crate::sql::preprocess::SqlRelationKind; +use crate::utils::{BreakUp, Pluck}; + use crate::Target; use super::context::AnchorContext; use super::gen_expr::*; use super::gen_projection::*; -use super::preprocess::{self, SqlTransform}; +use super::preprocess::{self, SqlRelation, SqlTransform}; use super::{anchor, Context, Dialect}; pub fn translate_query(query: Query, dialect: Option) -> Result { @@ -36,77 +39,17 @@ pub fn translate_query(query: Query, dialect: Option) -> Result { - // preprocess - let pipeline = Ok(pipeline) - .map(preprocess::normalize) - .map(preprocess::push_down_selects) - .map(preprocess::prune_inputs) - .map(preprocess::wrap) - .and_then(|p| preprocess::distinct(p, &mut context)) - .map(preprocess::union) - .and_then(|p| preprocess::except(p, &context)) - .and_then(|p| preprocess::intersect(p, &context)) - .map(preprocess::reorder)?; - - // load names of output columns - context.anchor.load_names(&pipeline, table.relation.columns); - - // split to atomics - let ats = split_into_atomics(name, pipeline, &mut context.anchor); - - // ensure names for all columns that need it - ensure_names(&ats, &mut context.anchor); - - atomics.extend(ats); - } - RelationKind::Literal(_) | RelationKind::SString(_) => atomics.push(AtomicQuery { - name, - relation: SqlRelation::Super(table.relation.kind), - }), - RelationKind::ExternRef(_) => { - // ref does not need it's own CTE - } - } - } - - // take last table - let main_query = atomics.remove(atomics.len() - 1); - let ctes = atomics; + let (anchor, main_relation) = AnchorContext::of(query); - // convert each of the CTEs - let ctes: Vec<_> = ctes - .into_iter() - .map(|t| table_to_sql_cte(t, &mut context)) - .try_collect()?; + let mut ctx = Context::new(dialect, anchor); - // convert main query - let mut main_query = sql_query_of_relation(main_query.relation, &mut context)?; + // compile main relation that will recursively compile CTEs + let mut main_query = sql_query_of_sql_relation(main_relation.into(), &mut ctx)?; // attach CTEs - if !ctes.is_empty() { + if !ctx.ctes.is_empty() { main_query.with = Some(sql_ast::With { - cte_tables: ctes, + cte_tables: ctx.ctes.drain(..).collect_vec(), recursive: false, }); } @@ -114,99 +57,188 @@ pub fn translate_query(query: Query, dialect: Option) -> Result Result { + use RelationKind::*; -#[derive(Debug, EnumAsInner)] -enum SqlRelation { - Super(RelationKind), - Pipeline(Vec), -} + // preprocess & split into atomics + match sql_relation.kind { + // base case + SqlRelationKind::Super(Pipeline(pipeline)) => { + // preprocess + let pipeline = Ok(pipeline) + .map(preprocess::normalize) + .map(preprocess::prune_inputs) + .map(preprocess::wrap) + .and_then(|p| preprocess::distinct(p, ctx)) + .map(preprocess::union) + .and_then(|p| preprocess::except(p, ctx)) + .and_then(|p| preprocess::intersect(p, ctx)) + .map(preprocess::reorder)?; + + // load names of output columns + ctx.anchor.load_names(&pipeline, sql_relation.columns); + + sql_query_of_pipeline(pipeline, ctx) + } -fn into_tables( - main_pipeline: Relation, - tables: Vec, - context: &mut Context, -) -> Result> { - let main = TableDecl { - id: context.anchor.tid.gen(), - name: None, - relation: main_pipeline, - }; - Ok([tables, vec![main]].concat()) + // no need to preprocess, has been done already + SqlRelationKind::PreprocessedPipeline(pipeline) => sql_query_of_pipeline(pipeline, ctx), + + // special case: literals + SqlRelationKind::Super(Literal(lit)) => sql_of_sample_data(lit, ctx), + + // special case: s-strings + SqlRelationKind::Super(SString(items)) => translate_query_sstring(items, ctx), + + // ref cannot be converted directly into query and does not need it's own CTE + SqlRelationKind::Super(ExternRef(_)) => unreachable!(), + } } -fn table_to_sql_cte(table: AtomicQuery, context: &mut Context) -> Result { - let alias = sql_ast::TableAlias { - name: translate_ident_part(table.name, context), - columns: vec![], +fn table_factor_of_table_ref(table_ref: TableRef, ctx: &mut Context) -> Result { + let table_ref_alias = (table_ref.name.clone()) + .map(|ident| translate_ident_part(ident, ctx)) + .map(simple_table_alias); + + let decl = ctx.anchor.table_decls.get_mut(&table_ref.source).unwrap(); + + // prepare names + let table_name = match &decl.name { + None => { + decl.name = Some(ctx.anchor.table_name.gen()); + decl.name.clone().unwrap() + } + Some(n) => n.clone(), }; - Ok(sql_ast::Cte { - alias, - query: Box::new(sql_query_of_relation(table.relation, context)?), - from: None, - }) -} -fn sql_query_of_relation(relation: SqlRelation, context: &mut Context) -> Result { - use RelationKind::*; + // ensure that the table is declared + if let Some(sql_relation) = decl.relation.take() { + // if we cannot use CTEs + if !ctx.query.allow_ctes { + // restore relation for other references + decl.relation = Some(sql_relation.clone()); + + // return a sub-query + let query = sql_query_of_sql_relation(sql_relation, ctx)?; + return Ok(TableFactor::Derived { + lateral: false, + subquery: Box::new(query), + alias: table_ref_alias, + }); + } - match relation { - SqlRelation::Super(ExternRef(_)) | SqlRelation::Super(Pipeline(_)) => unreachable!(), - SqlRelation::Pipeline(pipeline) => sql_query_of_pipeline(pipeline, context), - SqlRelation::Super(Literal(lit)) => Ok(sql_of_sample_data(lit, context)?), - SqlRelation::Super(SString(items)) => translate_query_sstring(items, context), + let query = sql_query_of_sql_relation(sql_relation, ctx)?; + let alias = sql_ast::TableAlias { + name: translate_ident_part(table_name.clone(), ctx), + columns: vec![], + }; + + ctx.ctes.push(sql_ast::Cte { + alias, + query: Box::new(query), + from: None, + }) } + + // let name = match &decl.relation { + // // special case for anchor + // // TODO + // // Some(SqlRelationKind::Super(RelationKind::ExternRef(TableExternRef::Anchor( + // // anchor_id, + // // )))) => sql_ast::ObjectName(vec![Ident::new(anchor_id.clone())]), + + // // base case + // _ => { + + // } + // }; + + let name = sql_ast::ObjectName(translate_ident(Some(table_name.clone()), None, ctx)); + + Ok(TableFactor::Table { + name, + alias: if Some(table_name) == table_ref.name { + None + } else { + table_ref_alias + }, + args: None, + with_hints: vec![], + }) +} + +fn translate_join( + (side, with, filter): (JoinSide, TableRef, Expr), + ctx: &mut Context, +) -> Result { + let relation = table_factor_of_table_ref(with, ctx)?; + + let constraint = JoinConstraint::On(translate_expr_kind(filter.kind, ctx)?); + + Ok(Join { + relation, + join_operator: match side { + JoinSide::Inner => JoinOperator::Inner(constraint), + JoinSide::Left => JoinOperator::LeftOuter(constraint), + JoinSide::Right => JoinOperator::RightOuter(constraint), + JoinSide::Full => JoinOperator::FullOuter(constraint), + }, + }) } fn sql_query_of_pipeline( - pipeline: Vec, - context: &mut Context, + mut pipeline: Vec, + ctx: &mut Context, ) -> Result { use SqlTransform::*; + // special case: loop + if pipeline.iter().any(|t| matches!(t, Loop(_))) { + pipeline = sql_of_loop(pipeline, ctx)?; + } + + // extract an atomic pipeline from back of the pipeline and stash preceding part into context + let pipeline = extract_atomic(pipeline, &mut ctx.anchor); + + // ensure names for all columns that need it + ensure_names(&pipeline, &mut ctx.anchor); + let (select, set_ops) = pipeline.break_up(|t| matches!(t, Union { .. } | Except { .. } | Intersect { .. })); - let select = sql_select_query_of_pipeline(select, context)?; + let select = sql_select_query_of_pipeline(select, ctx)?; - sql_set_ops_of_pipeline(select, set_ops, context) + sql_set_ops_of_pipeline(select, set_ops, ctx) } fn sql_select_query_of_pipeline( mut pipeline: Vec, - context: &mut Context, + ctx: &mut Context, ) -> Result { let table_count = count_tables(&pipeline); log::debug!("atomic query contains {table_count} tables"); - context.omit_ident_prefix = table_count == 1; - - context.pre_projection = true; - - let projection = pipeline - .pluck(|t| t.into_super_and(|t| t.into_select())) - .into_only() - .unwrap(); - let projection = translate_wildcards(&context.anchor, projection); - let projection = translate_select_items(projection.0, projection.1, context)?; + ctx.push_query(); + ctx.query.omit_ident_prefix = table_count == 1; + ctx.query.pre_projection = true; - let mut from = pipeline + let mut from: Vec<_> = pipeline .pluck(|t| t.into_super_and(|t| t.into_from())) .into_iter() - .map(|source| TableWithJoins { - relation: table_factor_of_tid(source, context), - joins: vec![], + .map(|source| -> Result { + Ok(TableWithJoins { + relation: table_factor_of_table_ref(source, ctx)?, + joins: vec![], + }) }) - .collect::>(); + .try_collect()?; let joins = pipeline .pluck(|t| t.into_super_and(|t| t.into_join())) .into_iter() - .map(|j| translate_join(j, context)) + .map(|j| translate_join(j, ctx)) .collect::>>()?; if !joins.is_empty() { if let Some(from) = from.last_mut() { @@ -216,6 +248,14 @@ fn sql_select_query_of_pipeline( } } + let projection = pipeline + .pluck(|t| t.into_super_and(|t| t.into_select())) + .into_iter() + .exactly_one() + .unwrap(); + let projection = translate_wildcards(&ctx.anchor, projection); + let projection = translate_select_items(projection.0, projection.1, ctx)?; + let sorts = pipeline.pluck(|t| t.into_super_and(|t| t.into_sort())); let takes = pipeline.pluck(|t| t.into_super_and(|t| t.into_take())); let distinct = pipeline.iter().any(|t| matches!(t, SqlTransform::Distinct)); @@ -231,11 +271,11 @@ fn sql_select_query_of_pipeline( // WHERE and HAVING let where_ = filter_of_conditions( before_agg.pluck(|t| t.into_super_and(|t| t.into_filter())), - context, + ctx, )?; let having = filter_of_conditions( after_agg.pluck(|t| t.into_super_and(|t| t.into_filter())), - context, + ctx, )?; // GROUP BY @@ -244,9 +284,11 @@ fn sql_select_query_of_pipeline( .into_iter() .next(); let group_by: Vec = aggregate.map(|(part, _)| part).unwrap_or_default(); - let group_by = try_into_exprs(group_by, context, None)?; + ctx.query.allow_stars = ctx.dialect.stars_in_group(); + let group_by = try_into_exprs(group_by, ctx, None)?; + ctx.query.allow_stars = true; - context.pre_projection = false; + ctx.query.pre_projection = false; let ranges = takes.into_iter().map(|x| x.range).collect(); let take = range_of_ranges(ranges)?; @@ -257,7 +299,7 @@ fn sql_select_query_of_pipeline( None } else { Some(sqlparser::ast::Offset { - value: translate_expr_kind(ExprKind::Literal(Literal::Integer(offset)), context)?, + value: translate_expr_kind(ExprKind::Literal(Literal::Integer(offset)), ctx)?, rows: sqlparser::ast::OffsetRows::None, }) }; @@ -268,17 +310,20 @@ fn sql_select_query_of_pipeline( .map(|sorts| { sorts .iter() - .map(|s| translate_column_sort(s, context)) + .map(|s| translate_column_sort(s, ctx)) .try_collect() }) .transpose()? .unwrap_or_default(); - let (top, limit) = if context.dialect.use_top() { - (limit.map(|l| top_of_i64(l, context)), None) + let (top, limit) = if ctx.dialect.use_top() { + (limit.map(|l| top_of_i64(l, ctx)), None) } else { (None, limit.map(expr_of_i64)) }; + + ctx.pop_query(); + Ok(sql_ast::Query { order_by, limit, @@ -322,36 +367,7 @@ fn sql_set_ops_of_pipeline( }; // prepare top - let top_is_simple = top.with.is_none() - && top.order_by.is_empty() - && top.limit.is_none() - && top.offset.is_none() - && top.fetch.is_none() - && top.locks.is_empty(); - - let left = if top_is_simple { - top.body - } else { - // top is not simple, so we need to wrap it into - // `SELECT * FROM top` - Box::new(SetExpr::Select(Box::new(Select { - projection: vec![SelectItem::Wildcard( - sql_ast::WildcardAdditionalOptions::default(), - )], - from: vec![TableWithJoins { - relation: TableFactor::Derived { - lateral: false, - subquery: Box::new(top), - alias: Some(TableAlias { - name: Ident::new(context.anchor.table_name.gen()), - columns: Vec::new(), - }), - }, - joins: vec![], - }], - ..default_select() - }))) - }; + let left = query_to_set_expr(top, context); top = default_query(SetExpr::SetOperation { left, @@ -360,7 +376,7 @@ fn sql_set_ops_of_pipeline( sql_ast::WildcardAdditionalOptions::default(), )], from: vec![TableWithJoins { - relation: table_factor_of_tid(bottom, context), + relation: table_factor_of_table_ref(bottom, context)?, joins: vec![], }], ..default_select() @@ -381,6 +397,99 @@ fn sql_set_ops_of_pipeline( Ok(top) } +fn sql_of_loop(pipeline: Vec, ctx: &mut Context) -> Result> { + // split the pipeline + let (mut initial, mut following) = pipeline.break_up(|t| matches!(t, SqlTransform::Loop(_))); + let loop_ = following.remove(0); + let step = loop_.into_loop().unwrap(); + + // RECURSIVE can only follow WITH directly, which means that if we want to use it for + // an arbitrary query, we have to defined a *nested* WITH RECURSIVE and not use + // the top-level list of CTEs. + + // determine columns of the initial table + let recursive_columns = AnchorContext::determine_select_columns(&initial); + + // do the same thing we do when splitting a pipeline + // (defining new columns, redirecting cids) + let recursive_columns = SqlTransform::Super(Transform::Select(recursive_columns)); + initial.push(recursive_columns.clone()); + let step = anchor_split(&mut ctx.anchor, initial, step); + let from = step.first().unwrap().as_super().unwrap().as_from().unwrap(); + + let initial = ctx.anchor.table_decls.get_mut(&from.source).unwrap(); + initial.name = Some("loop".to_string()); + let initial_relation = initial.relation.take().unwrap(); + + let initial = initial_relation.kind.into_preprocessed_pipeline().unwrap(); + + // compile initial + let initial = query_to_set_expr(sql_query_of_pipeline(initial, ctx)?, ctx); + + // compile step (without producing CTEs) + ctx.push_query(); + ctx.query.allow_ctes = false; + + let step = query_to_set_expr(sql_query_of_pipeline(step, ctx)?, ctx); + + ctx.pop_query(); + + // build CTE and it's SELECT + let cte = sql_ast::Cte { + alias: simple_table_alias(Ident::new("loop")), + query: Box::new(default_query(SetExpr::SetOperation { + op: sql_ast::SetOperator::Union, + set_quantifier: sql_ast::SetQuantifier::All, + left: initial, + right: step, + })), + from: None, + }; + let query = Box::new(sql_ast::Query { + with: Some(sql_ast::With { + recursive: true, + cte_tables: vec![cte], + }), + ..default_query(sql_ast::SetExpr::Select(Box::new(sql_ast::Select { + projection: vec![SelectItem::Wildcard( + sql_ast::WildcardAdditionalOptions::default(), + )], + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: sql_ast::ObjectName(vec![Ident::new("loop")]), + alias: None, + args: None, + with_hints: Vec::new(), + }, + joins: vec![], + }], + ..default_select() + }))) + }); + + // create a split between the loop SELECT statement and the following pipeline + let mut following = anchor_split(&mut ctx.anchor, vec![recursive_columns], following); + + let from = following.first_mut().unwrap(); + let from = from.as_super().unwrap().as_from().unwrap(); + + // this will be table decl that references the whole loop expression + let loop_decl = ctx.anchor.table_decls.get_mut(&from.source).unwrap(); + + let loop_name = ctx.anchor.table_name.gen(); + loop_decl.name = Some(loop_name.clone()); + loop_decl.relation = None; + + // push the whole thing into WITH of the main query + ctx.ctes.push(sql_ast::Cte { + alias: simple_table_alias(Ident::new(loop_name)), + query, + from: None, + }); + + Ok(following) +} + fn sql_of_sample_data(data: RelationLiteral, ctx: &Context) -> Result { // TODO: this could be made to use VALUES instead of SELECT UNION ALL SELECT // I'm not sure about compatibility though. @@ -416,121 +525,65 @@ fn sql_of_sample_data(data: RelationLiteral, ctx: &Context) -> Result, - ctx: &mut AnchorContext, -) -> Vec { - let outputs_cid = AnchorContext::determine_select_columns(&pipeline); - - let mut required_cols = outputs_cid.clone(); - - // split pipeline, back to front - let mut parts_rev = Vec::new(); - loop { - let (preceding, split) = anchor::split_off_back(ctx, required_cols, pipeline); - - if let Some((preceding, cols_at_split)) = preceding { - log::debug!( - "pipeline split after {}", - preceding.last().unwrap().as_str() - ); - parts_rev.push((split, cols_at_split.clone())); - - pipeline = preceding; - required_cols = cols_at_split; - } else { - parts_rev.push((split, Vec::new())); - break; - } - } - parts_rev.reverse(); - let mut parts = parts_rev; +/// Extract last part of pipeline that is able to "fit" into a single SELECT statement. +/// Remaining proceeding pipeline is declared as a table and stored in AnchorContext. +fn extract_atomic(pipeline: Vec, ctx: &mut AnchorContext) -> Vec { + let (preceding, atomic) = anchor::split_off_back(pipeline, ctx); - // sometimes, additional columns will be added into select, which have to - // be filtered out here, using additional CTE - if let Some((pipeline, _)) = parts.last() { - let select_cols = pipeline - .first() - .unwrap() - .as_super() - .unwrap() - .as_select() - .unwrap(); - - if select_cols.iter().any(|c| !outputs_cid.contains(c)) { - parts.push(( - vec![SqlTransform::Super(Transform::Select(outputs_cid))], - select_cols.clone(), - )); - } - } + if let Some(preceding) = preceding { + log::debug!( + "pipeline split after {}", + preceding.last().unwrap().as_str() + ); - // add names to pipelines, anchor, front to back - let mut atomics = Vec::with_capacity(parts.len()); - let last = parts.pop().unwrap(); - - let last_pipeline = if parts.is_empty() { - last.0 + anchor::anchor_split(ctx, preceding, atomic) } else { - // this code chunk is bloated but I cannot find a more concise alternative - let first = parts.remove(0); - - let first_name = ctx.table_name.gen(); - atomics.push(AtomicQuery { - name: first_name.clone(), - relation: SqlRelation::Pipeline(first.0), - }); - - let mut prev_name = first_name; - for (pipeline, cols_before) in parts.into_iter() { - let name = ctx.table_name.gen(); - let pipeline = anchor::anchor_split(ctx, &prev_name, &cols_before, pipeline); - - atomics.push(AtomicQuery { - name: name.clone(), - relation: SqlRelation::Pipeline(pipeline), - }); - - prev_name = name; - } - - anchor::anchor_split(ctx, &prev_name, &last.1, last.0) - }; - atomics.push(AtomicQuery { - name, - relation: SqlRelation::Pipeline(last_pipeline), - }); + atomic + } - atomics + // TODO + // sometimes, additional columns will be added into select, which have to + // be filtered out here, using additional CTE + // if let Some((pipeline, _)) = parts.last() { + // let select_cols = pipeline + // .first() + // .unwrap() + // .as_super() + // .unwrap() + // .as_select() + // .unwrap(); + + // if select_cols.iter().any(|c| !outputs_cid.contains(c)) { + // parts.push(( + // vec![SqlTransform::Super(Transform::Select(outputs_cid))], + // select_cols.clone(), + // )); + // } + // } } -fn ensure_names(atomics: &[AtomicQuery], ctx: &mut AnchorContext) { - // ensure column names for columns that need it - for a in atomics { - let empty = HashSet::new(); - for t in a.relation.as_pipeline().unwrap() { - match t { - SqlTransform::Super(Transform::Sort(_)) => { - for r in anchor::get_requirements(t, &empty) { - ctx.ensure_column_name(r.col); - } +fn ensure_names(transforms: &[SqlTransform], ctx: &mut AnchorContext) { + let empty = HashSet::new(); + for t in transforms { + match t { + SqlTransform::Super(Transform::Sort(_)) => { + for r in anchor::get_requirements(t, &empty) { + ctx.ensure_column_name(r.col); } - SqlTransform::Super(Transform::Select(cids)) => { - for cid in cids { - let _decl = &ctx.column_decls[cid]; - //let name = match decl { - // ColumnDecl::RelationColumn(_, _, _) => todo!(), - // ColumnDecl::Compute(_) => ctx.column_names[..], - //}; - } + } + SqlTransform::Super(Transform::Select(cids)) => { + for cid in cids { + let _decl = &ctx.column_decls[cid]; + //let name = match decl { + // ColumnDecl::RelationColumn(_, _, _) => todo!(), + // ColumnDecl::Compute(_) => ctx.column_names[..], + //}; } - _ => (), } + _ => (), } } } - fn filter_of_conditions(exprs: Vec, context: &mut Context) -> Result> { Ok(if let Some(cond) = all(exprs) { Some(translate_expr_kind(cond.kind, context)?) @@ -584,6 +637,45 @@ fn default_select() -> Select { } } +fn simple_table_alias(name: Ident) -> TableAlias { + TableAlias { + name, + columns: Vec::new(), + } +} + +fn query_to_set_expr(query: sql_ast::Query, context: &mut Context) -> Box { + let is_simple = query.with.is_none() + && query.order_by.is_empty() + && query.limit.is_none() + && query.offset.is_none() + && query.fetch.is_none() + && query.locks.is_empty(); + + if is_simple { + return query.body; + } + + // query is not simple, so we need to wrap it into + // `SELECT * FROM (query)` + Box::new(SetExpr::Select(Box::new(Select { + projection: vec![SelectItem::Wildcard( + sql_ast::WildcardAdditionalOptions::default(), + )], + from: vec![TableWithJoins { + relation: TableFactor::Derived { + lateral: false, + subquery: Box::new(query), + alias: Some(simple_table_alias(Ident::new( + context.anchor.table_name.gen(), + ))), + }, + joins: vec![], + }], + ..default_select() + }))) +} + fn count_tables(transforms: &[SqlTransform]) -> usize { let mut count = 0; for transform in transforms { @@ -603,19 +695,36 @@ mod test { fn parse_and_resolve(prql: &str) -> Result<(Vec, Context)> { let query = resolve(parse(prql)?)?; - let (anchor, query) = AnchorContext::of(query); - let context = Context { - dialect: Box::new(GenericDialect {}), - anchor, - omit_ident_prefix: false, - pre_projection: false, - }; + let (anchor, main_relation) = AnchorContext::of(query); + let context = Context::new(Box::new(GenericDialect {}), anchor); - let pipeline = query.relation.kind.into_pipeline().unwrap(); + let pipeline = main_relation.kind.into_pipeline().unwrap(); Ok((preprocess::reorder(preprocess::wrap(pipeline)), context)) } + fn count_atomics(prql: &str) -> usize { + let (mut pipeline, mut context) = parse_and_resolve(prql).unwrap(); + context.anchor.table_decls.clear(); + + let mut atomics = 0; + loop { + let _ = extract_atomic(pipeline, &mut context.anchor); + atomics += 1; + + if let Some((_, decl)) = context.anchor.table_decls.drain().next() { + if let Some(relation) = decl.relation { + if let SqlRelationKind::PreprocessedPipeline(p) = relation.kind { + pipeline = p; + continue; + } + } + } + break; + } + atomics + } + #[test] fn test_ctes_of_pipeline() { // One aggregate, take at the end @@ -627,9 +736,7 @@ mod test { take 20 "###; - let (pipeline, mut context) = parse_and_resolve(prql).unwrap(); - let queries = split_into_atomics("".to_string(), pipeline, &mut context.anchor); - assert_eq!(queries.len(), 1); + assert_eq!(count_atomics(prql), 1); // One aggregate, but take at the top let prql: &str = r###" @@ -640,9 +747,7 @@ mod test { sort sal "###; - let (pipeline, mut context) = parse_and_resolve(prql).unwrap(); - let queries = split_into_atomics("".to_string(), pipeline, &mut context.anchor); - assert_eq!(queries.len(), 2); + assert_eq!(count_atomics(prql), 2); // A take, then two aggregates let prql: &str = r###" @@ -654,9 +759,7 @@ mod test { sort sal2 "###; - let (pipeline, mut context) = parse_and_resolve(prql).unwrap(); - let queries = split_into_atomics("".to_string(), pipeline, &mut context.anchor); - assert_eq!(queries.len(), 3); + assert_eq!(count_atomics(prql), 3); // A take, then a select let prql: &str = r###" @@ -665,9 +768,7 @@ mod test { select first_name "###; - let (pipeline, mut context) = parse_and_resolve(prql).unwrap(); - let queries = split_into_atomics("".to_string(), pipeline, &mut context.anchor); - assert_eq!(queries.len(), 1); + assert_eq!(count_atomics(prql), 1); } #[test] @@ -719,7 +820,7 @@ mod test { *, RANK() OVER () AS rank FROM - table_1 + table_1 AS table_0 WHERE country = 'USA' "###); @@ -744,7 +845,7 @@ mod test { SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 > 3 "###); diff --git a/prql-compiler/src/sql/mod.rs b/prql-compiler/src/sql/mod.rs index 0be592ecf4b3..231a27e26b73 100644 --- a/prql-compiler/src/sql/mod.rs +++ b/prql-compiler/src/sql/mod.rs @@ -18,9 +18,9 @@ use crate::{ast::rq::Query, Options, PRQL_VERSION}; use self::{context::AnchorContext, dialect::DialectHandler}; /// Translate a PRQL AST into a SQL string. -pub fn compile(query: Query, options: Options) -> Result { +pub fn compile(query: Query, options: &Options) -> Result { let crate::Target::Sql(dialect) = options.target; - let sql_ast = gen_query::translate_query(query, dialect.clone())?; + let sql_ast = gen_query::translate_query(query, dialect)?; let sql = sql_ast.to_string(); @@ -32,10 +32,7 @@ pub fn compile(query: Query, options: Options) -> Result { sqlformat::FormatOptions::default(), ); - // The sql formatter turns `{{` into `{ {`, and while that's reasonable SQL, - // we want to allow jinja expressions through. So we (somewhat hackily) replace - // any `{ {` with `{{`. - formatted.replace("{ {", "{{").replace("} }", "}}") + "\n" + formatted + "\n" } else { sql }; @@ -59,10 +56,23 @@ pub fn compile(query: Query, options: Options) -> Result { Ok(sql) } +#[derive(Debug)] struct Context { pub dialect: Box, pub anchor: AnchorContext, + // stuff regarding current query + query: QueryOpts, + + // stuff regarding parent queries + query_stack: Vec, + + pub ctes: Vec, +} + +#[derive(Clone, Debug)] +struct QueryOpts { + /// When true, column references will not include table names prefixes. pub omit_ident_prefix: bool, /// True iff codegen should generate expressions before SELECT's projection is applied. @@ -70,6 +80,43 @@ struct Context { /// - WHERE needs `pre_projection=true`, but /// - ORDER BY needs `pre_projection=false`. pub pre_projection: bool, + + /// When false, queries will contain nested sub-queries instead of WITH CTEs. + pub allow_ctes: bool, + + /// When false, * are not allowed. + pub allow_stars: bool, +} + +impl Default for QueryOpts { + fn default() -> Self { + QueryOpts { + omit_ident_prefix: false, + pre_projection: false, + allow_ctes: true, + allow_stars: true, + } + } +} + +impl Context { + fn new(dialect: Box, anchor: AnchorContext) -> Self { + Context { + dialect, + anchor, + query: QueryOpts::default(), + query_stack: Vec::new(), + ctes: Vec::new(), + } + } + + fn push_query(&mut self) { + self.query_stack.push(self.query.clone()); + } + + fn pop_query(&mut self) { + self.query = self.query_stack.pop().unwrap(); + } } #[cfg(test)] @@ -79,7 +126,7 @@ mod test { #[test] fn test_end_with_new_line() { - let sql = compile("from a", Options::default().no_signature()).unwrap(); + let sql = compile("from a", &Options::default().no_signature()).unwrap(); assert_eq!(sql, "SELECT\n *\nFROM\n a\n") } } diff --git a/prql-compiler/src/sql/preprocess.rs b/prql-compiler/src/sql/preprocess.rs index b6fa76e8ac01..0e3e0a5c1f09 100644 --- a/prql-compiler/src/sql/preprocess.rs +++ b/prql-compiler/src/sql/preprocess.rs @@ -10,7 +10,8 @@ use crate::ast::pl::{ BinOp, ColumnSort, InterpolateItem, JoinSide, Literal, Range, WindowFrame, WindowKind, }; use crate::ast::rq::{ - self, new_binop, CId, Compute, Expr, ExprKind, RqFold, TableRef, Transform, Window, + self, new_binop, CId, Compute, Expr, ExprKind, Relation, RelationColumn, RelationKind, RqFold, + TableRef, Transform, Window, }; use crate::error::Error; use crate::sql::context::AnchorContext; @@ -18,31 +19,45 @@ use crate::sql::context::AnchorContext; use super::anchor::{infer_complexity, CidCollector, Complexity}; use super::Context; -#[derive(Debug, EnumAsInner, strum::AsRefStr)] +#[derive(Debug, Clone, EnumAsInner)] +pub(super) enum SqlRelationKind { + Super(RelationKind), + PreprocessedPipeline(Vec), +} + +#[derive(Debug, Clone)] +pub(super) struct SqlRelation { + pub kind: SqlRelationKind, + pub columns: Vec, +} + +#[derive(Debug, Clone, EnumAsInner, strum::AsRefStr)] pub(super) enum SqlTransform { Super(Transform), Distinct, Except { bottom: TableRef, distinct: bool }, Intersect { bottom: TableRef, distinct: bool }, Union { bottom: TableRef, distinct: bool }, + Loop(Vec), } -/// Pushes all [Transform::Select]s to the back of the pipeline. -pub(super) fn push_down_selects(pipeline: Vec) -> Vec { - let mut select = None; - let mut res = Vec::with_capacity(pipeline.len()); - for t in pipeline { - if let Transform::Select(_) = t { - select = Some(t); - } else { - res.push(t); - } - } - if let Some(select) = select { - res.push(select); - } - res -} +// This function was disabled because it changes semantics of the pipeline in some cases. +// /// Pushes all [Transform::Select]s to the back of the pipeline. +// pub(super) fn push_down_selects(pipeline: Vec) -> Vec { +// let mut select = None; +// let mut res = Vec::with_capacity(pipeline.len()); +// for t in pipeline { +// if let Transform::Select(_) = t { +// select = Some(t); +// } else { +// res.push(t); +// } +// } +// if let Some(select) = select { +// res.push(select); +// } +// res +// } /// Removes unused relation inputs pub(super) fn prune_inputs(mut pipeline: Vec) -> Vec { @@ -76,7 +91,18 @@ pub(super) fn prune_inputs(mut pipeline: Vec) -> Vec { } pub(super) fn wrap(pipe: Vec) -> Vec { - pipe.into_iter().map(SqlTransform::Super).collect() + pipe.into_iter() + .map(|t| match t { + Transform::Loop(pipeline) => SqlTransform::Loop(wrap(pipeline)), + _ => SqlTransform::Super(t), + }) + .collect() +} + +fn vecs_contain_same_elements(a: &[T], b: &[T]) -> bool { + let a: HashSet<&T, RandomState> = a.iter().collect(); + let b: HashSet<&T, RandomState> = b.iter().collect(); + a == b } /// Creates [SqlTransform::Distinct] from [Transform::Take] @@ -88,7 +114,7 @@ pub(super) fn distinct( use Transform::*; let mut res = Vec::new(); - for transform in pipeline { + for transform in pipeline.clone() { match transform { Super(Take(rq::Take { ref partition, .. })) if partition.is_empty() => { res.push(transform); @@ -106,8 +132,13 @@ pub(super) fn distinct( let take_only_first = range_int.start.unwrap_or(1) == 1 && matches!(range_int.end, Some(1)); - if take_only_first && sort.is_empty() { - // TODO: use distinct only if `by == all columns in frame` + + // Check whether the columns within the partition are the same + // as the columns in the table; otherwise we can't use DISTINCT. + let columns_in_frame = AnchorContext::determine_select_columns(&pipeline.clone()); + let matching_columns = vecs_contain_same_elements(&columns_in_frame, &partition); + + if take_only_first && sort.is_empty() && matching_columns { res.push(Distinct); continue; } @@ -581,6 +612,15 @@ impl SqlTransform { } } +impl From for SqlRelation { + fn from(rel: Relation) -> Self { + SqlRelation { + kind: SqlRelationKind::Super(rel.kind), + columns: rel.columns, + } + } +} + pub(super) trait SqlFold: RqFold { fn fold_sql_transforms(&mut self, transforms: Vec) -> Result> { transforms @@ -605,6 +645,7 @@ pub(super) trait SqlFold: RqFold { bottom: self.fold_table_ref(bottom)?, distinct, }, + SqlTransform::Loop(pipeline) => SqlTransform::Loop(self.fold_sql_transforms(pipeline)?), }) } } diff --git a/prql-compiler/src/sql/snapshots/prql_compiler__sql__gen_query__test__variable_after_aggregate.snap b/prql-compiler/src/sql/snapshots/prql_compiler__sql__gen_query__test__variable_after_aggregate.snap index 5b7e140a157a..1164db5605aa 100644 --- a/prql-compiler/src/sql/snapshots/prql_compiler__sql__gen_query__test__variable_after_aggregate.snap +++ b/prql-compiler/src/sql/snapshots/prql_compiler__sql__gen_query__test__variable_after_aggregate.snap @@ -16,6 +16,7 @@ SELECT title, AVG(_expr_0) AS avg_salary FROM - table_1 + table_1 AS table_0 GROUP BY title + diff --git a/prql-compiler/src/sql/std_impl.prql b/prql-compiler/src/sql/std_impl.prql index 91013ab5f9eb..1d781067d0ff 100644 --- a/prql-compiler/src/sql/std_impl.prql +++ b/prql-compiler/src/sql/std_impl.prql @@ -1,13 +1,13 @@ -func min column -> s"MIN({column})" -func max column -> s"MAX({column})" -func sum column -> s"SUM({column})" -func avg column -> s"AVG({column})" -func stddev column -> s"STDDEV({column})" -func average column -> s"AVG({column})" -func count non_null:s"*" -> s"COUNT({non_null})" +func min column -> s"MIN({column})" +func max column -> s"MAX({column})" +func sum column -> s"SUM({column})" +func avg column -> s"AVG({column})" +func stddev column -> s"STDDEV({column})" +func average column -> s"AVG({column})" +func count non_null:s"*" -> s"COUNT({non_null})" # TODO: Possibly make this into `count distinct:true` (or like `distinct:` as an # abbreviation of that?) -func count_distinct column -> s"COUNT(DISTINCT {column})" +func count_distinct column -> s"COUNT(DISTINCT {column})" # Window functions func lag offset column -> s"LAG({column}, {offset})" @@ -21,3 +21,7 @@ func row_number -> s"ROW_NUMBER()" # Other functions func round n_digits column -> s"ROUND({column}, {n_digits})" func as `noresolve.type` column -> s"CAST({column} AS {type})" + +# String functions +func lower column -> s"LOWER({column})" +func upper column -> s"UPPER({column})" diff --git a/prql-compiler/src/test.rs b/prql-compiler/src/test.rs index d1a4f1f2dd5b..b1c9384dce31 100644 --- a/prql-compiler/src/test.rs +++ b/prql-compiler/src/test.rs @@ -4,7 +4,7 @@ use crate::{parser::parse, sql, Options, Target}; use insta::{assert_display_snapshot, assert_snapshot}; pub fn compile(prql: &str) -> Result { - crate::compile(prql, Options::default().no_signature()) + crate::compile(prql, &Options::default().no_signature()) } #[test] @@ -154,29 +154,6 @@ fn test_precedence() { ); } -#[test] -fn test_pipelines() { - assert_display_snapshot!((compile(r###" - from employees - group dept (take 1) - "###).unwrap()), @r###" - SELECT - DISTINCT * - FROM - employees - "###); - - assert_display_snapshot!((compile(r###" - from employees - select [age | in 5..10] - "###).unwrap()), @r###" - SELECT - age BETWEEN 5 AND 10 - FROM - employees - "###); -} - #[test] fn test_append() { assert_display_snapshot!(compile(r###" @@ -205,7 +182,7 @@ fn test_append() { take 10 ) "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT *, name, @@ -227,13 +204,13 @@ fn test_append() { employees LIMIT 3 - ) AS table_3 + ) AS table_2 UNION ALL SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 "###); assert_display_snapshot!(compile(r###" @@ -310,7 +287,7 @@ fn test_remove() { ) "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM @@ -325,7 +302,7 @@ fn test_remove() { SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -337,7 +314,7 @@ fn test_remove() { ) "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM @@ -348,9 +325,9 @@ fn test_remove() { album.title FROM album - LEFT JOIN table_1 AS table_0 ON album.artist_id = table_0.artist_id + LEFT JOIN table_0 AS table_1 ON album.artist_id = table_1.artist_id WHERE - table_0.artist_id IS NULL + table_1.artist_id IS NULL "### ); @@ -360,7 +337,9 @@ fn test_remove() { from album remove artist "#).unwrap_err(), - @"Your dialect does not support EXCEPT ALL" + @r###" + Error: Your dialect does not support EXCEPT ALL + "### ); assert_display_snapshot!(compile(r#" @@ -382,11 +361,11 @@ fn test_remove() { album ) SELECT - table_1.artist_id, - table_1.title + table_0.artist_id, + table_0.title FROM - table_1 - LEFT JOIN bottom AS b ON table_1.artist_id = b.* + table_1 AS table_0 + LEFT JOIN bottom AS b ON table_0.artist_id = b.* WHERE b.* IS NULL "### @@ -443,7 +422,7 @@ fn test_intersect() { ) "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM @@ -458,7 +437,7 @@ fn test_intersect() { SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -474,22 +453,28 @@ fn test_intersect() { distinct "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM artist + ), + table_3 AS ( + SELECT + artist_id + FROM + album + INTERSECT + DISTINCT + SELECT + * + FROM + table_0 AS table_1 ) SELECT - artist_id - FROM - album - INTERSECT - DISTINCT - SELECT - * + DISTINCT artist_id FROM - table_1 AS table_0 + table_3 AS table_2 "### ); @@ -504,22 +489,28 @@ fn test_intersect() { distinct "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM artist + ), + table_3 AS ( + SELECT + artist_id + FROM + album + INTERSECT + ALL + SELECT + * + FROM + table_0 AS table_1 ) SELECT - artist_id - FROM - album - INTERSECT - DISTINCT - SELECT - * + DISTINCT artist_id FROM - table_1 AS table_0 + table_3 AS table_2 "### ); @@ -534,7 +525,7 @@ fn test_intersect() { ) "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT artist_id FROM @@ -549,7 +540,7 @@ fn test_intersect() { SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -559,7 +550,9 @@ fn test_intersect() { from album intersect artist "#).unwrap_err(), - @"Your dialect does not support INTERCEPT ALL" + @r###" + Error: Your dialect does not support INTERCEPT ALL + "### ); } @@ -574,28 +567,28 @@ fn test_rn_ids_are_unique() { take 3 ) "###).unwrap()), @r###" - WITH table_1 AS ( + WITH table_3 AS ( SELECT *, - ROW_NUMBER() OVER (PARTITION BY y_id) AS _expr_0 + ROW_NUMBER() OVER (PARTITION BY y_id) AS _expr_1 FROM y_orig ), - table_2 AS ( + table_1 AS ( SELECT *, - ROW_NUMBER() OVER (PARTITION BY x_id) AS _expr_1 + ROW_NUMBER() OVER (PARTITION BY x_id) AS _expr_0 FROM - table_1 + table_3 AS table_2 WHERE - _expr_0 <= 2 + _expr_1 <= 2 ) SELECT * FROM - table_2 + table_1 AS table_0 WHERE - _expr_1 <= 3 + _expr_0 <= 3 "###); } @@ -690,19 +683,13 @@ fn test_sorts() { select [renamed = somefield] "### ).unwrap()), @r###" - WITH table_1 AS ( - SELECT - 'something' AS renamed, - 'something' AS _expr_0 - FROM - x - ORDER BY - _expr_0 - ) SELECT - renamed + 'something' AS renamed, + 'something' AS _expr_0 FROM - table_1 + x + ORDER BY + _expr_0 "###); } @@ -877,7 +864,7 @@ fn test_window_functions_02() { order_day ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS num_books_last_week FROM - table_1 + table_1 AS table_0 ORDER BY order_day "###); @@ -1261,7 +1248,7 @@ fn test_take() { SELECT * FROM - table_1 + table_1 AS table_0 ORDER BY name LIMIT @@ -1340,7 +1327,7 @@ fn test_distinct() { SELECT * FROM - table_1 + table_1 AS table_0 WHERE rn > 2 "###); @@ -1370,18 +1357,34 @@ fn test_distinct() { employees "###); - // TODO: this should not use DISTINCT but ROW_NUMBER and WHERE, because we want - // row distinct only over first_name and last_name. + // We want distinct only over first_name and last_name, so we can't use a + // `DISTINCT *` here. assert_display_snapshot!((compile(r###" from employees group [first_name, last_name] (take 1) "###).unwrap()), @r###" + WITH table_1 AS ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY first_name, last_name) AS _expr_0 + FROM + employees + ) SELECT - DISTINCT * + * FROM - employees + table_1 AS table_0 + WHERE + _expr_0 <= 1 "###); + // Check that a different order doesn't stop distinct from being used. + assert!(compile( + "from employees | select [first_name, last_name] | group [last_name, first_name] (take 1)" + ) + .unwrap() + .contains("DISTINCT")); + // head assert_display_snapshot!((compile(r###" from employees @@ -1397,7 +1400,7 @@ fn test_distinct() { SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 <= 3 "###); @@ -1420,7 +1423,7 @@ fn test_distinct() { SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 BETWEEN 2 AND 3 "###); @@ -1443,22 +1446,36 @@ fn test_distinct() { SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 = 4 "###); -} -#[test] -fn test_dbt_query() { - assert_display_snapshot!((compile(r###" - from {{ ref('stg_orders') }} - aggregate (min order_id) - "###).unwrap()), @r###" + assert_display_snapshot!(compile(" + from invoices + select [billing_country, billing_city] + group [billing_city] ( + take 1 + ) + sort billing_city + ").unwrap(), @r###" + WITH table_1 AS ( + SELECT + billing_country, + billing_city, + ROW_NUMBER() OVER (PARTITION BY billing_city) AS _expr_0 + FROM + invoices + ) SELECT - MIN(order_id) + billing_country, + billing_city FROM - {{ ref('stg_orders') }} + table_1 AS table_0 + WHERE + _expr_0 <= 1 + ORDER BY + billing_city "###); } @@ -1506,14 +1523,14 @@ select [mng_name, managers.gender, salary_avg, salary_sd]"#; let sql_from_prql = parse(original_prql) .and_then(crate::semantic::resolve) - .and_then(|rq| sql::compile(rq, Options::default())) + .and_then(|rq| sql::compile(rq, &Options::default())) .unwrap(); let sql_from_json = crate::prql_to_pl(original_prql) .and_then(crate::json::from_pl) .and_then(|json| crate::json::to_pl(&json)) .and_then(crate::pl_to_rq) - .and_then(|rq| crate::rq_to_sql(rq, Options::default())) + .and_then(|rq| crate::rq_to_sql(rq, &Options::default())) .unwrap(); assert_eq!(sql_from_prql, sql_from_json); @@ -1550,7 +1567,7 @@ fn test_f_string() { assert_display_snapshot!( crate::compile( query, - Options::default() + &Options::default() .no_signature() .with_target(Target::Sql(Some(sql::Dialect::SQLite))) @@ -1832,16 +1849,7 @@ fn test_prql_to_sql_table() { let sql = compile(query).unwrap(); assert_display_snapshot!(sql, @r###" - WITH average_salaries AS ( - SELECT - country, - AVG(salary) AS average_country_salary - FROM - salaries - GROUP BY - country - ), - newest_employees AS ( + WITH newest_employees AS ( SELECT * FROM @@ -1850,6 +1858,14 @@ fn test_prql_to_sql_table() { tenure LIMIT 50 + ), average_salaries AS ( + SELECT + country, + AVG(salary) AS average_country_salary + FROM + salaries + GROUP BY + country ) SELECT newest_employees.name, @@ -1883,7 +1899,7 @@ fn test_nonatomic() { "###; assert_display_snapshot!((compile(query).unwrap()), @r###" - WITH table_1 AS ( + WITH table_3 AS ( SELECT title, country, @@ -1892,13 +1908,13 @@ fn test_nonatomic() { employees LIMIT 20 - ), table_2 AS ( + ), table_1 AS ( SELECT title, country, AVG(salary) AS _expr_0 FROM - table_1 + table_3 AS table_2 WHERE country = 'USA' GROUP BY @@ -1910,7 +1926,7 @@ fn test_nonatomic() { country, AVG(_expr_0) AS sum_gross_cost FROM - table_2 + table_1 AS table_0 GROUP BY title, country @@ -1963,7 +1979,7 @@ fn test_nonatomic_table() { "###; assert_display_snapshot!((compile(query).unwrap()), @r###" - WITH table_0 AS ( + WITH table_1 AS ( SELECT country FROM @@ -1975,7 +1991,7 @@ fn test_nonatomic_table() { country, count(*) FROM - table_0 + table_1 AS table_0 GROUP BY country ) @@ -2012,12 +2028,12 @@ fn test_table_names_between_splits() { 10 ) SELECT - table_1.emp_no, - table_1.name, + table_0.emp_no, + table_0.name, s.salary FROM - table_1 - JOIN salaries AS s ON table_1.emp_no = s.emp_no + table_1 AS table_0 + JOIN salaries AS s ON table_0.emp_no = s.emp_no "###); let prql = r###" @@ -2037,11 +2053,11 @@ fn test_table_names_between_splits() { 10 ) SELECT - table_1.*, + table_0.*, salaries.salary FROM - table_1 - JOIN salaries ON table_1.emp_no = salaries.emp_no + table_1 AS table_0 + JOIN salaries ON table_0.emp_no = salaries.emp_no "###); } @@ -2259,7 +2275,7 @@ fn test_double_aggregate() { let query = r###" from numbers - group [type] ( + group [`type`] ( aggregate [ total_amt = sum amount, max amount @@ -2321,12 +2337,6 @@ fn test_toposort() { * FROM somesource - ), - a AS ( - SELECT - * - FROM - b ) SELECT * @@ -2341,12 +2351,12 @@ fn test_inline_tables() { assert_display_snapshot!(compile(r###" ( from employees - select [emp_id, name, surname, type, amount] + select [emp_id, name, surname, `type`, amount] ) join s = (from salaries | select [emp_id, salary]) [==emp_id] "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT emp_id, salary @@ -2359,11 +2369,11 @@ fn test_inline_tables() { employees.surname, employees.type, employees.amount, - table_0.emp_id, - table_0.salary + table_1.emp_id, + table_1.salary FROM employees - JOIN table_1 AS table_0 ON employees.emp_id = table_0.emp_id + JOIN table_0 AS table_1 ON employees.emp_id = table_1.emp_id "### ); } @@ -2431,11 +2441,11 @@ fn test_unused_alias() { select n = [account.name] "###).unwrap_err(), @r###" Error: - โ•ญโ”€[:3:12] + โ•ญโ”€[:3:16] โ”‚ 3 โ”‚ select n = [account.name] - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - ยท โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ unexpected assign to `n` + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ unexpected assign to `n` ยท ยท Help: move assign into the list: `[n = ...]` โ”€โ”€โ”€โ•ฏ @@ -2448,7 +2458,7 @@ fn test_table_s_string() { s"SELECT DISTINCT ON first_name, age FROM employees ORDER BY age ASC" "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT DISTINCT ON first_name, age @@ -2459,7 +2469,7 @@ fn test_table_s_string() { ) SELECT FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -2470,7 +2480,7 @@ fn test_table_s_string() { join s = s"SELECT * FROM salaries" [==id] "###).unwrap(), @r###" - WITH table_2 AS ( + WITH table_0 AS ( SELECT DISTINCT ON first_name, id, @@ -2480,18 +2490,18 @@ fn test_table_s_string() { ORDER BY age ASC ), - table_3 AS ( + table_1 AS ( SELECT * FROM salaries ) SELECT - table_0.*, - table_1.* + table_2.*, + table_3.* FROM - table_2 AS table_0 - JOIN table_3 AS table_1 ON table_0.id = table_1.id + table_0 AS table_2 + JOIN table_1 AS table_3 ON table_2.id = table_3.id "### ); @@ -2500,7 +2510,7 @@ fn test_table_s_string() { filter country == "USA" "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT * FROM @@ -2509,7 +2519,7 @@ fn test_table_s_string() { SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 WHERE country = 'USA' "### @@ -2520,7 +2530,7 @@ fn test_table_s_string() { filter e.country == "USA" "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT * FROM @@ -2529,7 +2539,7 @@ fn test_table_s_string() { SELECT * FROM - table_1 AS table_0 + table_0 AS table_1 WHERE country = 'USA' "### @@ -2542,7 +2552,7 @@ fn test_table_s_string() { weeks_between @2022-06-03 (current_week + 4) "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT generate_series( DATE '2022-06-03', @@ -2552,7 +2562,7 @@ fn test_table_s_string() { ) SELECT FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -2560,7 +2570,7 @@ fn test_table_s_string() { s"SELECT * FROM {default_db.x}" "###).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT * FROM @@ -2568,7 +2578,7 @@ fn test_table_s_string() { ) SELECT FROM - table_1 AS table_0 + table_0 AS table_1 "### ); } @@ -2583,11 +2593,11 @@ fn test_direct_table_references() { ) .unwrap_err(), @r###" Error: - โ•ญโ”€[:3:15] + โ•ญโ”€[:3:14] โ”‚ 3 โ”‚ select s"{x}.field" - ยท โ”ฌ - ยท โ•ฐโ”€โ”€ table instance cannot be referenced directly + ยท โ”€โ”ฌโ”€ + ยท โ•ฐโ”€โ”€โ”€ table instance cannot be referenced directly ยท ยท Help: did you forget to specify the column name? โ”€โ”€โ”€โ•ฏ @@ -2653,36 +2663,21 @@ fn test_name_shadowing() { fn test_group_all() { assert_display_snapshot!(compile( r###" - from e=employees - take 10 - join salaries [==emp_no] - group [e.*] (aggregate sal = (sum salaries.salary)) - "###).unwrap(), - @r###" - WITH table_1 AS ( - SELECT - * - FROM - employees AS e - LIMIT - 10 - ) - SELECT - table_1.*, - SUM(salaries.salary) AS sal - FROM - table_1 - JOIN salaries ON table_1.emp_no = salaries.emp_no - GROUP BY - table_1.* - "### - ); + prql target:sql.sqlite + + from a=albums + group a.* (aggregate count) + "###).unwrap_err(), @r###" + Error: Target dialect does not support * in this position. + "###); assert_display_snapshot!(compile( r###" from e=albums group ![genre_id] (aggregate count) - "###).unwrap_err(), @"Excluding columns not supported as this position"); + "###).unwrap_err(), @r###" + Error: Excluding columns not supported as this position + "###); } #[test] @@ -2705,7 +2700,7 @@ fn test_output_column_deduplication() { SELECT * FROM - table_1 + table_1 AS table_0 WHERE r = 1 "### @@ -2713,13 +2708,13 @@ fn test_output_column_deduplication() { } #[test] -fn test_switch() { +fn test_case() { assert_display_snapshot!(compile( r###" from employees - derive display_name = switch [ - nickname != null -> nickname, - true -> f'{first_name} {last_name}' + derive display_name = case [ + nickname != null => nickname, + true => f'{first_name} {last_name}' ] "###).unwrap(), @r###" @@ -2737,9 +2732,9 @@ fn test_switch() { assert_display_snapshot!(compile( r###" from employees - derive display_name = switch [ - nickname != null -> nickname, - first_name != null -> f'{first_name} {last_name}' + derive display_name = case [ + nickname != null => nickname, + first_name != null => f'{first_name} {last_name}' ] "###).unwrap(), @r###" @@ -2758,8 +2753,8 @@ fn test_switch() { assert_display_snapshot!(compile( r###" from tracks - select category = switch [ - length > avg_length -> 'long' + select category = case [ + length > avg_length => 'long' ] group category (aggregate count) "###).unwrap(), @@ -2779,7 +2774,7 @@ fn test_switch() { category, COUNT(*) FROM - table_1 + table_1 AS table_0 GROUP BY category "### @@ -2789,13 +2784,13 @@ fn test_switch() { #[test] fn test_sql_options() { let options = Options::default(); - let sql = crate::compile("from x", options).unwrap(); + let sql = crate::compile("from x", &options).unwrap(); assert!(sql.contains('\n')); assert!(sql.contains("-- Generated by")); let options = Options::default().no_signature().no_format(); - let sql = crate::compile("from x", options).unwrap(); + let sql = crate::compile("from x", &options).unwrap(); assert!(!sql.contains('\n')); assert!(!sql.contains("-- Generated by")); @@ -2829,13 +2824,13 @@ fn test_static_analysis() { a3 = null ?? y, - a3 = switch [ - false == true -> 1, - 7 == 3 -> 2, - 7 == y -> 3, - 7.3 == 7.3 -> 4, - z -> 5, - true -> 6 + a3 = case [ + false == true => 1, + 7 == 3 => 2, + 7 == y => 3, + 7.3 == 7.3 => 4, + z => 5, + true => 6 ] ] "###).unwrap(), @@ -2896,6 +2891,35 @@ fn test_closures_and_pipelines() { /// Start testing some error messages. This can hopefully be expanded significantly. // It's also fine to put errors by the things that they're testing. fn test_errors() { + assert_display_snapshot!(compile(r###" + func addadd a b -> a + b + + from x + derive y = (addadd 4 5 6) + "###).unwrap_err(), + @r###" + Error: + โ•ญโ”€[:5:16] + โ”‚ + 5 โ”‚ derive y = (addadd 4 5 6) + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Too many arguments to function `addadd` + โ”€โ”€โ”€โ•ฏ + "###); + + assert_display_snapshot!(compile(r###" + from a select b + "###).unwrap_err(), + @r###" + Error: + โ•ญโ”€[:2:5] + โ”‚ + 2 โ”‚ from a select b + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Too many arguments to function `from` + โ”€โ”€โ”€โ•ฏ + "###); + assert_display_snapshot!(compile(r###" from x select a @@ -2924,6 +2948,48 @@ fn test_errors() { ยท โ•ฐโ”€โ”€โ”€ `take` expected int or range, but found 1.8 โ”€โ”€โ”€โ•ฏ "###); + + assert_display_snapshot!(compile("Mississippi has four Sโ€™s and four Iโ€™s.").unwrap_err(), @r###" + Error: + โ•ญโ”€[:1:23] + โ”‚ + 1 โ”‚ Mississippi has four Sโ€™s and four Iโ€™s. + ยท โ”ฌ + ยท โ•ฐโ”€โ”€ unexpected โ€™ + โ”€โ”€โ”€โ•ฏ + Error: + โ•ญโ”€[:1:36] + โ”‚ + 1 โ”‚ Mississippi has four Sโ€™s and four Iโ€™s. + ยท โ”ฌ + ยท โ•ฐโ”€โ”€ unexpected โ€™ + โ”€โ”€โ”€โ•ฏ + Error: + โ•ญโ”€[:1:39] + โ”‚ + 1 โ”‚ Mississippi has four Sโ€™s and four Iโ€™s. + ยท โ”ฌ + ยท โ•ฐโ”€โ”€ Expected * or an identifier, but didn't find anything before the end. + โ”€โ”€โ”€โ•ฏ + "###); + + let err = compile( + r###" + let a = (from x) + "###, + ) + .unwrap_err(); + assert_eq!(err.inner[0].code.as_ref().unwrap(), "E0001"); + + assert_display_snapshot!(compile("Answer: T-H-A-T!").unwrap_err(), @r###" + Error: + โ•ญโ”€[:1:7] + โ”‚ + 1 โ”‚ Answer: T-H-A-T! + ยท โ”ฌ + ยท โ•ฐโ”€โ”€ unexpected : + โ”€โ”€โ”€โ•ฏ + "###); } #[test] @@ -3135,7 +3201,7 @@ a,b,c select [b, c] "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT '1' AS a, '2' AS b, @@ -3151,7 +3217,7 @@ a,b,c b, c FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -3162,7 +3228,7 @@ a,b,c select [b, c] "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT 1 AS a, 'x' AS b, @@ -3178,7 +3244,7 @@ a,b,c b, c FROM - table_1 AS table_0 + table_0 AS table_1 "### ); @@ -3193,7 +3259,7 @@ a,b,c select [b, c] "#).unwrap(), @r###" - WITH table_1 AS ( + WITH table_0 AS ( SELECT 1 AS a, 'x' AS b, @@ -3209,7 +3275,7 @@ a,b,c b, c FROM - table_1 AS table_0 + table_0 AS table_1 "### ); } @@ -3219,10 +3285,193 @@ fn test_header_target_error() { assert_display_snapshot!(compile(r#" prql target:foo from a - "#).unwrap_err(),@r###"target `"foo"` not found"###); + "#).unwrap_err(),@r###" + Error: target `"foo"` not found + "###); assert_display_snapshot!(compile(r#" prql target:sql.foo from a - "#).unwrap_err(),@r###"target `"sql.foo"` not found"###) + "#).unwrap_err(),@r###" + Error: target `"sql.foo"` not found + "###); + + assert_display_snapshot!(compile(r#" + prql target:foo.bar + from a + "#).unwrap_err(),@r###" + Error: target `"foo.bar"` not found + "###); +} + +#[test] +fn test_loop() { + assert_display_snapshot!(compile(r#" + from_text format:json '[{"n": 1 }]' + select n = n - 2 + loop ( + select n = n+1 + filter n<5 + ) + select n = n * 2 + take 4 + "#).unwrap(), + @r###" + WITH table_0 AS ( + SELECT + 1 AS n + ), + table_6 AS ( + WITH RECURSIVE loop AS ( + SELECT + n - 2 AS _expr_0 + FROM + table_0 AS table_1 + UNION + ALL + SELECT + _expr_1 + FROM + ( + SELECT + _expr_0 + 1 AS _expr_1 + FROM + loop AS table_2 + ) AS table_3 + WHERE + _expr_1 < 5 + ) + SELECT + * + FROM + loop + ) + SELECT + _expr_0 * 2 AS n + FROM + table_6 AS table_5 + LIMIT + 4 + "### + ); +} + +#[test] +fn test_params() { + assert_display_snapshot!(compile(r#" + from i = invoices + filter $1 <= i.date or i.date <= $2 + select [ + i.id, + i.total, + ] + filter i.total > $3 + "#).unwrap(), + @r###" + SELECT + id, + total + FROM + invoices AS i + WHERE + ( + $1 <= date + OR date <= $2 + ) + AND total > $3 + "### + ) +} + +// for #1969 +#[test] +fn test_datetime() { + let query = &r#" + from test_table + select [date = @2022-12-31, time = @08:30, timestamp = @2020-01-01T13:19:55-0800] + "#; + + assert_snapshot!( + compile(query).unwrap(), + @r###"SELECT + DATE '2022-12-31' AS date, + TIME '08:30' AS time, + TIMESTAMP '2020-01-01T13:19:55-0800' AS timestamp +FROM + test_table +"### + ) +} + +// for #1969 +#[test] +fn test_datetime_sqlite() { + let query = &r#" + from test_table + select [date = @2022-12-31, time = @08:30, timestamp = @2020-01-01T13:19:55-0800] + "#; + + let opts = Options::default() + .no_signature() + .with_target(Target::Sql(Some(sql::Dialect::SQLite))); + + assert_snapshot!( + crate::compile(query, &opts).unwrap(), + @r###"SELECT + DATE('2022-12-31') AS date, + TIME('08:30') AS time, + DATETIME('2020-01-01T13:19:55-08:00') AS timestamp +FROM + test_table +"### + ); +} + +#[test] +fn test_datetime_parsing() { + assert_display_snapshot!(compile(r#" + from test_tables + select [date = @2022-12-31, time = @08:30, timestamp = @2020-01-01T13:19:55-0800] + "#).unwrap(), + @r###" + SELECT + DATE '2022-12-31' AS date, + TIME '08:30' AS time, + TIMESTAMP '2020-01-01T13:19:55-0800' AS timestamp + FROM + test_tables + "### + ); +} + +#[test] +fn test_lower() { + assert_display_snapshot!(compile(r#" + from test_tables + derive [lower_name = (name | lower)] + "#).unwrap(), + @r###" + SELECT + *, + LOWER(name) AS lower_name + FROM + test_tables + "### + ); +} + +#[test] +fn test_upper() { + assert_display_snapshot!(compile(r#" + from test_tables + derive [upper_name = upper name] + select [upper_name] + "#).unwrap(), + @r###" + SELECT + UPPER(name) AS upper_name + FROM + test_tables + "### + ); } diff --git a/prql-compiler/src/utils/mod.rs b/prql-compiler/src/utils/mod.rs index 0ff61841b1b1..792d55fbdf64 100644 --- a/prql-compiler/src/utils/mod.rs +++ b/prql-compiler/src/utils/mod.rs @@ -3,11 +3,11 @@ mod only; mod toposort; pub use id_gen::{IdGenerator, NameGenerator}; -use itertools::Itertools; pub use only::*; pub use toposort::toposort; use anyhow::Result; +use itertools::Itertools; pub trait OrMap { /// Merges two options into one using `f`. diff --git a/prql-compiler/src/utils/only.rs b/prql-compiler/src/utils/only.rs index 1a52e97027ee..579152d87807 100644 --- a/prql-compiler/src/utils/only.rs +++ b/prql-compiler/src/utils/only.rs @@ -1,83 +1,27 @@ -use anyhow::{anyhow, Result}; -use itertools::{Itertools, Position}; +use anyhow::Result; use crate::ast::pl::Expr; use crate::error::{Error, Reason}; -// Inspired by version in sqlparser-rs; I'm surprised there isn't a version in -// the stdlib / Itertools. -/// Returns the only element of an Iterator, or an error if it has more than one element. -pub trait IntoOnly { - type Item; - - fn into_only(self) -> Result; -} - -impl IntoOnly for I -where - I: IntoIterator, - // See below re using Debug. - // I: std::fmt::Debug, - // ::IntoIter: std::fmt::Debug, -{ - type Item = T; - - fn into_only(self) -> Result { - match self.into_iter().with_position().next() { - Some(Position::Only(item)) => Ok(item), - // Can't get the debug of the iterator because it's already - // consumed; is there a way around this? I guess we could show - // the items after the second, which is kinda weird. - Some(Position::First(_)) => Err(anyhow!("Expected only one element, but found more.",)), - None => Err(anyhow!("Expected one element, but found none.",)), - _ => unreachable!(), - } - } -} - -pub trait IntoOnlyNode { - fn into_only_node(self, who: &str, occupation: &str) -> Result; +pub trait ExactlyOneNode { + fn exactly_one_node(self, who: &str, occupation: &str) -> Result; } -impl IntoOnlyNode for Vec { - fn into_only_node(mut self, who: &str, occupation: &str) -> Result { +impl ExactlyOneNode for Vec { + fn exactly_one_node(mut self, who: &str, occupation: &str) -> Result { match self.len() { 1 => Ok(self.remove(0)), - 0 => Err(Error { - reason: Reason::Expected { - who: Some(who.to_string()), - expected: format!("only one {occupation}"), - found: "none".to_string(), - }, - span: None, - help: None, - }), - _ => Err(Error { - reason: Reason::Expected { - who: Some(who.to_string()), - expected: format!("only one {occupation}"), - found: "more".to_string(), - }, - span: self[1].span, - help: None, - }), - } - } -} - -pub trait Only { - fn only(&self) -> Result<&T>; -} - -impl Only for [T] -where - T: std::fmt::Debug, -{ - fn only(&self) -> Result<&T> { - if self.len() == 1 { - Ok(self.first().unwrap()) - } else { - Err(anyhow!("Expected 1 item, got {}; {:?}", self.len(), self)) + 0 => Err(Error::new(Reason::Expected { + who: Some(who.to_string()), + expected: format!("only one {occupation}"), + found: "none".to_string(), + })), + _ => Err(Error::new(Reason::Expected { + who: Some(who.to_string()), + expected: format!("only one {occupation}"), + found: "more".to_string(), + }) + .with_span(self[1].span)), } } } diff --git a/prql-compiler/tests/integration/data/chinook/tracks.csv b/prql-compiler/tests/integration/data/chinook/tracks.csv index 60f177ca91b3..66246430590c 100644 --- a/prql-compiler/tests/integration/data/chinook/tracks.csv +++ b/prql-compiler/tests/integration/data/chinook/tracks.csv @@ -2677,7 +2677,7 @@ track_id,name,album_id,media_type_id,genre_id,composer,milliseconds,bytes,unit_p 2676,Intro,217,1,1,Jagger/Richards,49737,1618591,0.99 2677,You Got Me Rocking,217,1,1,Jagger/Richards,205766,6734385,0.99 2678,Gimmie Shelters,217,1,1,Jagger/Richards,382119,12528764,0.99 -2679,Flip The Switch,217,1,1,Jagger/Richards,252421,8336591,0.99 +2679,Flip The Case,217,1,1,Jagger/Richards,252421,8336591,0.99 2680,Memory Motel,217,1,1,Jagger/Richards,365844,11982431,0.99 2681,Corinna,217,1,1,Jesse Ed Davis III/Taj Mahal,257488,8449471,0.99 2682,Saint Of Me,217,1,1,Jagger/Richards,325694,10725160,0.99 diff --git a/prql-compiler/tests/integration/main.rs b/prql-compiler/tests/integration/main.rs index c51ae2fd1dc7..666d005862cd 100644 --- a/prql-compiler/tests/integration/main.rs +++ b/prql-compiler/tests/integration/main.rs @@ -34,14 +34,16 @@ mod tests { let opts = Options::default() .with_target(Target::Sql(Some(Dialect::SQLite))) .no_format(); - let sql = prql_compiler::compile(&prql, opts).unwrap(); + let sql = prql_compiler::compile(&prql, &opts) + .unwrap_or_else(|e| panic!("Failed to compile\n\n{prql}\n\n{e}")); + let sqlite_out = sqlite::query_csv(&sqlite_conn, &sql); // save both csv files as same snapshot let opts = Options::default() .with_target(Target::Sql(Some(Dialect::DuckDb))) .no_format(); - let sql = prql_compiler::compile(&prql, opts).unwrap(); + let sql = prql_compiler::compile(&prql, &opts).unwrap(); let duckdb_out = duckdb::query_csv(&duckdb_conn, &sql); pretty_assertions::assert_eq!(sqlite_out, duckdb_out, "SQLite == DuckDB: {test_name}"); @@ -49,7 +51,7 @@ mod tests { let opts = Options::default() .with_target(Target::Sql(Some(Dialect::PostgreSql))) .no_format(); - let sql = prql_compiler::compile(&prql, opts).unwrap(); + let sql = prql_compiler::compile(&prql, &opts).unwrap(); let pg_out = postgres::query_csv(pg_client, &sql); pretty_assertions::assert_eq!(sqlite_out, pg_out, "SQLite == PG: {test_name}"); } @@ -86,7 +88,13 @@ mod tests { } pub fn query_csv(conn: &Connection, sql: &str) -> String { - let mut statement = conn.prepare(sql).unwrap(); + let mut statement = conn + .prepare(sql) + .map_err(|e| { + println!("{e}"); + e + }) + .unwrap(); let csv_header = statement.column_names().join(","); let column_count = statement.column_count(); @@ -173,6 +181,13 @@ mod tests { let dt = DateTime::::column_result(value).unwrap(); dt.format("%Y-%m-%d %H:%M:%S").to_string() } + ValueRef::Boolean(b) => { + if b { + "1".to_string() + } else { + "0".to_string() + } + } t => unimplemented!("{t:?}"), } }) diff --git a/prql-compiler/tests/integration/queries/distinct.prql b/prql-compiler/tests/integration/queries/distinct.prql new file mode 100644 index 000000000000..593d4fdea3de --- /dev/null +++ b/prql-compiler/tests/integration/queries/distinct.prql @@ -0,0 +1,4 @@ +from tracks +select [album_id, genre_id] +group tracks.* (take 1) +sort tracks.* diff --git a/prql-compiler/tests/integration/queries/group_all.prql b/prql-compiler/tests/integration/queries/group_all.prql new file mode 100644 index 000000000000..a5a73e1c98ae --- /dev/null +++ b/prql-compiler/tests/integration/queries/group_all.prql @@ -0,0 +1,5 @@ +from a=albums +sort album_id +take 10 +join tracks [==album_id] +group [a.album_id, a.title] (aggregate price = (sum tracks.unit_price)) diff --git a/prql-compiler/tests/integration/queries/loop.prql b/prql-compiler/tests/integration/queries/loop.prql new file mode 100644 index 000000000000..e83d02e37057 --- /dev/null +++ b/prql-compiler/tests/integration/queries/loop.prql @@ -0,0 +1,7 @@ +from_text format:json '[{"n": 1 }]' +select n = n - 2 +loop ( + filter n<4 + select n = n+1 +) +select n = n * 2 diff --git a/prql-compiler/tests/integration/queries/pipelines.prql b/prql-compiler/tests/integration/queries/pipelines.prql new file mode 100644 index 000000000000..2b3c6c6cf64a --- /dev/null +++ b/prql-compiler/tests/integration/queries/pipelines.prql @@ -0,0 +1,4 @@ +from tracks +sort track_id +take 20..25 +select [is_in_range = album_id | in 5..10] diff --git a/prql-compiler/tests/integration/queries/switch.prql b/prql-compiler/tests/integration/queries/switch.prql new file mode 100644 index 000000000000..7214c48fc708 --- /dev/null +++ b/prql-compiler/tests/integration/queries/switch.prql @@ -0,0 +1,8 @@ +from tracks +sort milliseconds +select display = case [ + composer != null => composer, + genre_id < 17 => 'no composer', + true => f'unknown composer' +] +take 10 diff --git a/prql-compiler/tests/integration/snapshots/integration__tests__test@distinct.prql.snap b/prql-compiler/tests/integration/snapshots/integration__tests__test@distinct.prql.snap new file mode 100644 index 000000000000..765cba4ceca1 --- /dev/null +++ b/prql-compiler/tests/integration/snapshots/integration__tests__test@distinct.prql.snap @@ -0,0 +1,106 @@ +--- +source: prql-compiler/tests/integration/main.rs +expression: sqlite_out +input_file: prql-compiler/tests/integration/queries/distinct.prql +--- +album_id,genre_id +1,1 +2,1 +3,1 +4,1 +5,1 +6,1 +7,1 +8,2 +9,3 +10,1 +11,4 +12,5 +13,2 +14,3 +15,3 +16,3 +17,3 +18,4 +19,3 +20,6 +21,7 +22,7 +23,7 +24,7 +25,7 +26,8 +27,8 +28,7 +29,9 +30,1 +31,1 +32,10 +33,7 +34,7 +35,3 +36,1 +37,1 +38,2 +39,4 +40,1 +41,7 +42,4 +43,1 +44,1 +45,7 +46,1 +47,7 +48,2 +49,2 +50,1 +51,2 +52,11 +53,7 +54,1 +55,1 +56,7 +57,7 +58,1 +59,1 +60,1 +61,1 +62,1 +63,1 +64,1 +65,1 +66,1 +67,1 +68,2 +69,7 +70,7 +71,7 +72,6 +73,6 +73,7 +74,4 +75,4 +76,1 +77,4 +78,7 +79,1 +80,1 +81,4 +82,1 +83,12 +84,7 +85,10 +86,7 +87,2 +88,3 +89,4 +90,1 +91,1 +92,3 +93,2 +94,1 +95,3 +96,3 +97,1 +98,13 +99,1 diff --git a/prql-compiler/tests/integration/snapshots/integration__tests__test@group_all.prql.snap b/prql-compiler/tests/integration/snapshots/integration__tests__test@group_all.prql.snap new file mode 100644 index 000000000000..1b5c0359f1bb --- /dev/null +++ b/prql-compiler/tests/integration/snapshots/integration__tests__test@group_all.prql.snap @@ -0,0 +1,16 @@ +--- +source: prql-compiler/tests/integration/main.rs +expression: sqlite_out +input_file: prql-compiler/tests/integration/queries/group_all.prql +--- +album_id,title,price +1,For Those About To Rock We Salute You,9.9 +2,Balls to the Wall,0.99 +3,Restless and Wild,2.9699999999999998 +4,Let There Be Rock,7.920000000000001 +5,Big Ones,14.850000000000001 +6,Jagged Little Pill,12.870000000000001 +7,Facelift,11.88 +8,Warner 25 Anos,13.860000000000001 +9,Plays Metallica By Four Cellos,7.920000000000001 +10,Audioslave,13.860000000000001 diff --git a/prql-compiler/tests/integration/snapshots/integration__tests__test@loop.prql.snap b/prql-compiler/tests/integration/snapshots/integration__tests__test@loop.prql.snap new file mode 100644 index 000000000000..482dc9933c4d --- /dev/null +++ b/prql-compiler/tests/integration/snapshots/integration__tests__test@loop.prql.snap @@ -0,0 +1,12 @@ +--- +source: prql-compiler/tests/integration/main.rs +expression: sqlite_out +input_file: prql-compiler/tests/integration/queries/loop.prql +--- +n +-2 +0 +2 +4 +6 +8 diff --git a/prql-compiler/tests/integration/snapshots/integration__tests__test@pipelines.prql.snap b/prql-compiler/tests/integration/snapshots/integration__tests__test@pipelines.prql.snap new file mode 100644 index 000000000000..f039b00e19b4 --- /dev/null +++ b/prql-compiler/tests/integration/snapshots/integration__tests__test@pipelines.prql.snap @@ -0,0 +1,12 @@ +--- +source: prql-compiler/tests/integration/main.rs +expression: sqlite_out +input_file: prql-compiler/tests/integration/queries/pipelines.prql +--- +is_in_range,track_id +0,20 +0,21 +0,22 +1,23 +1,24 +1,25 diff --git a/prql-compiler/tests/integration/snapshots/integration__tests__test@switch.prql.snap b/prql-compiler/tests/integration/snapshots/integration__tests__test@switch.prql.snap new file mode 100644 index 000000000000..2a9e5eec12b4 --- /dev/null +++ b/prql-compiler/tests/integration/snapshots/integration__tests__test@switch.prql.snap @@ -0,0 +1,16 @@ +--- +source: prql-compiler/tests/integration/main.rs +expression: sqlite_out +input_file: prql-compiler/tests/integration/queries/case.prql +--- +display,milliseconds +Samuel Rosa,1071 +no composer,4884 +no composer,6373 +no composer,6635 +L. Muggerud,7941 +no composer,11650 +L. Muggerud,21211 +unknown composer,29048 +Gilberto Gil,32287 +Chico Science,33149 diff --git a/prql-elixir/README.md b/prql-elixir/README.md deleted file mode 100644 index 2ff3e499141c..000000000000 --- a/prql-elixir/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# PRQL - -[PRQL](https://prql-lang.org/) bindings for Elixir. - -## Installation - -```elixir -def deps do - [ - {:prql, "~> 0.1.0"} - ] -end -``` - -## Basic Usage - -```elixir - iex> PRQL.compile("from customers") - {:ok, "SELECT\n *\nFROM\n customers\n\n-- Generated by PRQL compiler version 0.3.1 (https://prql-lang.org)\n"} - - - iex> PRQL.compile("from customers\ntake 10", dialect: :mssql) - {:ok, "SELECT\n TOP (10) *\nFROM\n customers\n\n-- Generated by PRQL compiler version 0.3.1 (https://prql-lang.org)\n"} -``` - -## Development - -We are in the early stages of developing Elixir bindings. - -We're using `Rustler` to provide Rust bindings for `prql-compiler`. - -Currently using the bindings in an Elixir project requires compiling the Rust -crate from this repo: - -- Install dependencies with `mix deps.get` -- Compile project `mix compile` -- Run tests `mix test` - -Future work includes publishing pre-compiled artifacts, so Elixir projects can -run PRQL without needing a Rust toolchain. diff --git a/prql-elixir/native/prql/.cargo/config.toml b/prql-elixir/native/prql/.cargo/config.toml deleted file mode 100644 index 20f03f3d8054..000000000000 --- a/prql-elixir/native/prql/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[target.'cfg(target_os = "macos")'] -rustflags = [ - "-C", "link-arg=-undefined", - "-C", "link-arg=dynamic_lookup", -] diff --git a/prql-elixir/native/prql/Cargo.lock b/prql-elixir/native/prql/Cargo.lock deleted file mode 100644 index af9fef0a93fd..000000000000 --- a/prql-elixir/native/prql/Cargo.lock +++ /dev/null @@ -1,695 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" -dependencies = [ - "const-random", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" -dependencies = [ - "backtrace", -] - -[[package]] -name = "ariadne" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c" -dependencies = [ - "yansi", -] - -[[package]] -name = "backtrace" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chumsky" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" -dependencies = [ - "ahash", -] - -[[package]] -name = "const-random" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" -dependencies = [ - "const-random-macro", - "proc-macro-hack", -] - -[[package]] -name = "const-random-macro" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" -dependencies = [ - "getrandom", - "once_cell", - "proc-macro-hack", - "tiny-keccak", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "enum-as-inner" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "nom" -version = "7.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "object" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d864c91689fdc196779b98dba0aceac6118594c2df6ee5d943eb6a8df4d107a" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "pest" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" -dependencies = [ - "once_cell", - "pest", - "sha1", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prql" -version = "0.1.0" -dependencies = [ - "prql-compiler", - "rustler", -] - -[[package]] -name = "prql-compiler" -version = "0.4.2" -dependencies = [ - "anyhow", - "ariadne", - "chumsky", - "csv", - "enum-as-inner", - "itertools", - "lazy_static", - "log", - "once_cell", - "pest", - "pest_derive", - "regex", - "semver", - "serde", - "serde_json", - "sqlformat", - "sqlparser", - "strum", - "strum_macros", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustler" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61e8ddf75de20513455d7b6f17241a595abbb01b53a6340cecc798a1b13422d" -dependencies = [ - "lazy_static", - "rustler_codegen", - "rustler_sys", -] - -[[package]] -name = "rustler_codegen" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa2e45c0165272070f80ce93bcd7dd5407a3c84a1ef73ab9900e00f00ef3d36" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "rustler_sys" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff26a42e62d538f82913dd34f60105ecfdffbdb25abdc3c3580b0c622285332" -dependencies = [ - "regex", - "unreachable", -] - -[[package]] -name = "rustversion" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "semver" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" -dependencies = [ - "itoa 1.0.5", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sqlformat" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlparser" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67dc6ef36edb658196c3fef0464a80b53dbbc194a904e81f9bd4190f9ecc5b" -dependencies = [ - "log", - "serde", -] - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -dependencies = [ - "void", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java b/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java deleted file mode 100644 index 49f47d19728d..000000000000 --- a/prql-java/java/src/main/java/org/prql/prql4j/PrqlCompiler.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.prql.prql4j; - -import java.io.IOException; - -public class PrqlCompiler { - public static native String toSql(String query); - public static native String toJson(String query); - - static { - try { - NativeLibraryLoader.getInstance().loadLibrary(null); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/prql-java/src/lib.rs b/prql-java/src/lib.rs deleted file mode 100644 index b4238032486c..000000000000 --- a/prql-java/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -use jni::objects::{JClass, JString}; -use jni::sys::jstring; -use jni::JNIEnv; -use prql_compiler::{json, prql_to_pl, Options}; - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "system" fn Java_org_prql_prql4j_PrqlCompiler_toSql( - env: JNIEnv, - _class: JClass, - query: JString, -) -> jstring { - let prql_query: String = env - .get_string(query) - .expect("Couldn't get java string!") - .into(); - let rs_sql_str: String = prql_compiler::compile(&prql_query, Options::default()) - .expect("Couldn't compile query to prql!"); - env.new_string(rs_sql_str) - .expect("Couldn't create java string!") - .into_raw() -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "system" fn Java_org_prql_prql4j_PrqlCompiler_toJson( - env: JNIEnv, - _class: JClass, - query: JString, -) -> jstring { - let prql_query: String = env - .get_string(query) - .expect("Couldn't get java string!") - .into(); - let rs_json_str: String = { prql_to_pl(&prql_query).and_then(json::from_pl) } - .expect("Couldn't get json from prql query!"); - env.new_string(rs_json_str) - .expect("Couldn't create java string!") - .into_raw() -} diff --git a/prql-lib/src/lib.rs b/prql-lib/src/lib.rs deleted file mode 100644 index 2d920ad2474c..000000000000 --- a/prql-lib/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![cfg(not(target_family = "wasm"))] - -extern crate libc; - -use libc::{c_char, c_int}; -use prql_compiler::Options; -use prql_compiler::{json, prql_to_pl}; -use std::ffi::CStr; -use std::ffi::CString; - -#[no_mangle] -#[allow(non_snake_case)] -/// # Safety -/// -/// This function is inherently unsafe because it is using C ABI. -pub unsafe extern "C" fn to_sql(query: *const c_char, out: *mut c_char) -> c_int { - let prql_query: String = CStr::from_ptr(query).to_string_lossy().into_owned(); - - let (isErr, sql_result) = match prql_compiler::compile(&prql_query, Options::default()) { - Ok(sql_str) => (false, sql_str), - Err(err) => { - //let err_str = format!("{}", err); - (true, err.to_string()) - } - }; - - let copylen = sql_result.len(); - let c_str = CString::new(sql_result).unwrap(); - - out.copy_from(c_str.as_ptr(), copylen); - let end_of_string_ptr = out.add(copylen); - *end_of_string_ptr = 0; - - match isErr { - true => -1, - false => 0, - } -} - -#[no_mangle] -#[allow(non_snake_case)] -/// # Safety -/// -/// This function is inherently unsafe because it using C ABI. -pub unsafe extern "C" fn to_json(query: *const c_char, out: *mut c_char) -> c_int { - let prql_query: String = CStr::from_ptr(query).to_string_lossy().into_owned(); - - let (isErr, sql_result) = match prql_to_pl(&prql_query).and_then(json::from_pl) { - Ok(sql_str) => (false, sql_str), - Err(err) => { - //let err_str = format!("{}", err); - (true, err.to_string()) - } - }; - - let copylen = sql_result.len(); - let c_str = CString::new(sql_result).unwrap(); - - out.copy_from(c_str.as_ptr(), copylen); - let end_of_string_ptr = out.add(copylen); - *end_of_string_ptr = 0; - - match isErr { - true => -1, - false => 0, - } -} diff --git a/book/.gitignore b/web/book/.gitignore similarity index 100% rename from book/.gitignore rename to web/book/.gitignore diff --git a/book/Cargo.toml b/web/book/Cargo.toml similarity index 93% rename from book/Cargo.toml rename to web/book/Cargo.toml index 171876dd6ee3..d209d8cb3e1a 100644 --- a/book/Cargo.toml +++ b/web/book/Cargo.toml @@ -19,7 +19,7 @@ test = false anyhow = "1.0.57" globset = "0.4.8" itertools = "0.10.3" -prql-compiler = {path = "../prql-compiler", default-features = false} +prql-compiler = {path = "../../prql-compiler", default-features = false} pulldown-cmark = "0.9.1" pulldown-cmark-to-cmark = "10.0.1" semver = "1.0.9" diff --git a/book/README.md b/web/book/README.md similarity index 97% rename from book/README.md rename to web/book/README.md index ec088b512dcb..57b803d93865 100644 --- a/book/README.md +++ b/web/book/README.md @@ -1,4 +1,4 @@ -# PRQL Language Book +# PRQL language book These docs serve as a language book, for users of the language. They should be friendly & accessible, at a minimum to those who understand basic SQL. diff --git a/book/book.toml b/web/book/book.toml similarity index 71% rename from book/book.toml rename to web/book/book.toml index f503dc2bb7a9..1ce7a8769425 100644 --- a/book/book.toml +++ b/web/book/book.toml @@ -2,7 +2,7 @@ description = "Modern language for transforming data โ€” a simple, powerful, pipelined SQL replacement" language = "en" multilingual = false -title = "PRQL Language Book" +title = "PRQL language book" [output.html] additional-css = ["comparison-table.css", "mdbook-admonish.css"] @@ -13,10 +13,7 @@ git-repository-url = "https://github.com/PRQL/prql" # This is required because mdbook-prql isn't necessarily installed; maybe # there's a better way. # -# We put the target directory in `target-book` because of -# https://github.com/rust-lang/cargo/issues/8899, as an alternative to requiring -# `default-target` to be installed. -command = "cargo run --bin mdbook-prql --target-dir=target-book" +command = "cargo run --bin mdbook-prql" [preprocessor.admonish] assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` diff --git a/book/comparison-table.css b/web/book/comparison-table.css similarity index 100% rename from book/comparison-table.css rename to web/book/comparison-table.css diff --git a/book/highlight-prql.js b/web/book/highlight-prql.js similarity index 93% rename from book/highlight-prql.js rename to web/book/highlight-prql.js index 6955c600b0cc..bc78c7c7d511 100644 --- a/book/highlight-prql.js +++ b/web/book/highlight-prql.js @@ -1,7 +1,7 @@ // Syntax highlighting for PRQL. // Keep consistent with -// https://github.com/PRQL/prql/blob/main/website/themes/prql-theme/static/highlight/prql.js +// https://github.com/PRQL/prql/blob/main/web/website/themes/prql-theme/static/highlight/prql.js // TODO: can we import one from the other at build time? // Inspired by [Pest's book](https://github.com/pest-parser/book) @@ -35,7 +35,7 @@ formatting = function (hljs) { "union", "window", ]; - const BUILTIN_FUNCTIONS = ["switch", "in", "as"]; + const BUILTIN_FUNCTIONS = ["case", "in", "as"]; const KEYWORDS = ["func", "let", "prql"]; return { name: "PRQL", @@ -164,7 +164,7 @@ formatting = function (hljs) { { scope: "operator", match: - /(>)|(<)|(==)|(\+)|(\-)|(\/)|(\*)|(!=)|(<=)|(>=)|(\band\b)|(\bor\b)/, + /(>)|(<)|(==)|(\+)|(\-)|(\/)|(\*)|(!=)|(->)|(=>)|(<=)|(>=)|(\band\b)|(\bor\b)/, relevance: 10, }, { @@ -205,6 +205,8 @@ formatting = function (hljs) { hljs.registerLanguage("prql", formatting); hljs.registerLanguage("prql_no_test", formatting); +hljs.registerLanguage("prql_error", formatting); +hljs.registerLanguage("prql_no_fmt", formatting); hljs.registerLanguage("elm", formatting); // These lines should only exists in the book, not the website. @@ -215,6 +217,10 @@ Array.from(document.querySelectorAll("code.language-prql")).forEach( (a) => console.log(a) || hljs.highlightBlock(a) ); +Array.from(document.querySelectorAll("code.language-prql_error")).forEach( + (a) => console.log(a) || hljs.highlightBlock(a) +); + Array.from(document.querySelectorAll("code.language-prql_no_test")).forEach( (a) => console.log(a) || hljs.highlightBlock(a) ); diff --git a/book/mdbook-admonish.css b/web/book/mdbook-admonish.css similarity index 100% rename from book/mdbook-admonish.css rename to web/book/mdbook-admonish.css diff --git a/book/src/SUMMARY.md b/web/book/src/SUMMARY.md similarity index 87% rename from book/src/SUMMARY.md rename to web/book/src/SUMMARY.md index 153e70bdb090..62c5d04bf13e 100644 --- a/book/src/SUMMARY.md +++ b/web/book/src/SUMMARY.md @@ -36,11 +36,13 @@ - [Ranges](./language-features/ranges.md) - [Regex](./language-features/regex.md) - - [Stdlib](./language-features/standard-library.md) + - [Standard library](./language-features/standard-library/README.md) + - [From text](./language-features/standard-library/from-text.md) + - [Loop](./language-features/standard-library/loop.md) - [Strings](./language-features/strings.md) - [S-strings](./language-features/s-strings.md) - [F-strings](./language-features/f-strings.md) - - [Switch](./language-features/switch.md) + - [Case](./language-features/case.md) - [Target & Version](./language-features/target.md) - [Bindings](./bindings/README.md) @@ -70,7 +72,8 @@ - [Contributing to PRQL](./contributing/README.md) - [Development](./contributing/development.md) - - [Using Docker](./contributing/using-docker.md) + - [Developing with Docker](./contributing/developing-with-docker.md) + - [Developing with Dev Containers](./contributing/developing-with-dev-containers.md) - [Internals](./internals/README.md) diff --git a/web/book/src/bindings/README.md b/web/book/src/bindings/README.md new file mode 100644 index 000000000000..e2125ccc2db3 --- /dev/null +++ b/web/book/src/bindings/README.md @@ -0,0 +1,46 @@ +# Bindings + +PRQL has bindings for many languages. These include: + +We have three tiers of bindings: + +- Supported +- Unsupported +- Nascent + +## Supported + +Supported bindings require: + +- A maintainer. +- Implementations of the + [core compile functions](https://docs.rs/prql-compiler/latest/prql_compiler/#functions). +- Test coverage for these functions. +- A published package to the language's standard package repository. +- A script in `Taskfile.yml` to bootstrap a development environment. +- Any dev tools, such as a linter & formatter, in pre-commit or MegaLinter. + +- [JavaScript](./javascript.md) +- [Python](./python.md) +- [R](./r.md) +- [Rust](./rust.md) + +Most of these are in the main PRQL repo, and we gate any changes to the +compiler's API on compatible changes to the bindings. + +## Unsupported + +Unsupported bindings work, but don't fulfil all of the above criteria. We don't +gate changes to the compiler's API. If they stop working, we'll demote them to +nascent. + +- [Java](./java.md) +- [Elixir](./elixir.md) +- `prql-lib`, the C bindings + +## Nascent + +Nascent bindings are in development, and may not yet fully work. + +- [.NET](./net.md) +- [PHP](./php.md) diff --git a/web/book/src/bindings/dotnet.md b/web/book/src/bindings/dotnet.md new file mode 100644 index 000000000000..3cfe08319f7a --- /dev/null +++ b/web/book/src/bindings/dotnet.md @@ -0,0 +1 @@ +{{#include ../../../../bindings/prql-dotnet/README.md}} diff --git a/web/book/src/bindings/elixir.md b/web/book/src/bindings/elixir.md new file mode 100644 index 000000000000..ac694430503a --- /dev/null +++ b/web/book/src/bindings/elixir.md @@ -0,0 +1 @@ +{{#include ../../../../bindings/prql-elixir/README.md}} diff --git a/book/src/bindings/java.md b/web/book/src/bindings/java.md similarity index 100% rename from book/src/bindings/java.md rename to web/book/src/bindings/java.md diff --git a/web/book/src/bindings/javascript.md b/web/book/src/bindings/javascript.md new file mode 100644 index 000000000000..4ba2eaf85f67 --- /dev/null +++ b/web/book/src/bindings/javascript.md @@ -0,0 +1 @@ +{{#include ../../../../bindings/prql-js/README.md}} diff --git a/web/book/src/bindings/php.md b/web/book/src/bindings/php.md new file mode 100644 index 000000000000..43726cc12334 --- /dev/null +++ b/web/book/src/bindings/php.md @@ -0,0 +1 @@ +{{#include ../../../../bindings/prql-php/README.md}} diff --git a/book/src/bindings/python.md b/web/book/src/bindings/python.md similarity index 100% rename from book/src/bindings/python.md rename to web/book/src/bindings/python.md diff --git a/book/src/bindings/r.md b/web/book/src/bindings/r.md similarity index 100% rename from book/src/bindings/r.md rename to web/book/src/bindings/r.md diff --git a/web/book/src/bindings/rust.md b/web/book/src/bindings/rust.md new file mode 100644 index 000000000000..cc99cc895889 --- /dev/null +++ b/web/book/src/bindings/rust.md @@ -0,0 +1 @@ +{{#include ../../../../prql-compiler/README.md}} diff --git a/book/src/contributing/README.md b/web/book/src/contributing/README.md similarity index 100% rename from book/src/contributing/README.md rename to web/book/src/contributing/README.md diff --git a/web/book/src/contributing/developing-with-dev-containers.md b/web/book/src/contributing/developing-with-dev-containers.md new file mode 100644 index 000000000000..d595961af25d --- /dev/null +++ b/web/book/src/contributing/developing-with-dev-containers.md @@ -0,0 +1,22 @@ +# Developing with Dev Containers + +```admonish note +Currently the Dev Container included in this repository only supports the `amd64` platform. +``` + +[Dev Containers](https://containers.dev/) are a way to package a number of +developer tools (compilers, bundlers, package managers, loaders, etc.) into a +single object. This is helpful when many people want to contribute to a project: +each person only has to install the Dev Container on their own machine to start +working. By definition, the Dev Container has a consistent set of tools that are +known to work together. This avoids a fuss with finding the proper version of +each of the build tools. + +While there are a variety of tools that support Dev Containers, the focus here +is on developing with VS Code in a container by +[GitHub Codespaces](https://docs.github.com/en/codespaces/overview) or +[VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). + +To use a Dev Container on a local computer with VS Code, install the +[VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +and its system requirements. Then refer to the links above to get started. diff --git a/book/src/contributing/using-docker.md b/web/book/src/contributing/developing-with-docker.md similarity index 99% rename from book/src/contributing/using-docker.md rename to web/book/src/contributing/developing-with-docker.md index ee8a1ce789fe..80977a29ceba 100644 --- a/book/src/contributing/using-docker.md +++ b/web/book/src/contributing/developing-with-docker.md @@ -1,4 +1,4 @@ -# Using the Dockerfile +# Developing with Docker The `Dockerfile` in this repo builds a Docker image that has current versions of our Rust development tools. This can be the lowest-effort way of setting up a diff --git a/book/src/contributing/development.md b/web/book/src/contributing/development.md similarity index 85% rename from book/src/contributing/development.md rename to web/book/src/contributing/development.md index cca2d9145743..7e711a2515a4 100644 --- a/book/src/contributing/development.md +++ b/web/book/src/contributing/development.md @@ -162,7 +162,7 @@ change! - This means PRs almost always need a test to demonstrate incremental progress. - If a change breaks functionality without breaking tests, our tests were - insufficient. + probably insufficient. - If a change breaks existing tests (for example, changing an external API), that indicates we should be careful about merging a change, including soliciting others' views. @@ -182,38 +182,68 @@ change! - We should revert quickly if the impact of a PR turns out not to be consistent with our expectations, or there isn't as much consensus on a decision as we had hoped. It's very easy to revert code and then re-revert when we've - resolved the issue; it's a sign of moving quickly. + resolved the issue; it's a sign of moving quickly. Other options which resolve + issues immediately are also fine, such as commenting out an incorrect test or + adding a quick fix for the underlying issue. + +## Docs + +We're very keen on contributions to improve our documentation. + +This includes our docs in the book, on the website, in our code, or in a Readme. +We also appreciate issues pointing out that our documentation was confusing, +incorrect, or stale โ€” if it's confusing for you, it's probably confusing for +others. + +Some principles for ensuring our docs remain maintainable: + +- Docs should be as close as possible to the code. Doctests are ideal on this + dimension โ€” they're literally very close to the code and they can't drift + apart since they're tested on every commit. Or, for example, it's better to + add text to a `--help` message, rather than write a paragraph in the Readme + explaining the CLI. +- We should have some visualization of how to maintain docs when we add them. + Docs have a habit of falling out of date โ€” the folks reading them are often + different from those writing them, they're sparse from the code, generally not + possible to test, and are rarely the by-product of other contributions. Docs + that are concise & specific are easier to maintain. +- Docs should be specifically relevant to PRQL; anything else we can instead + link to. + +If something doesn't fit into one of these categories, there are still lots of +ways of getting the word out there โ€” a blog post / gist / etc. Let us know and +we're happy to link to it / tweet it. ## Components of PRQL The PRQL project has several components. Instructions for working with them are in the **README.md** file in their respective paths. Here's an overview: -**[book](https://github.com/PRQL/prql/blob/main/book/README.md)**: The PRQL +**[book](https://github.com/PRQL/prql/blob/main/web/book/README.md)**: The PRQL language book, which documents the language. -**[playground](https://github.com/PRQL/prql/blob/main/playground/README.md)**: A -web GUI for the PRQL compiler. It shows the PRQL source beside the resulting SQL -output. +**[playground](https://github.com/PRQL/prql/blob/main/web/playground/README.md)**: +A web GUI for the PRQL compiler. It shows the PRQL source beside the resulting +SQL output. **[prql-compiler](https://github.com/PRQL/prql/blob/main/prql-compiler/README.md)**: Installation and usage instructions for building and running the `prql-compiler`. -**[prql-java](https://github.com/PRQL/prql/blob/main/prql-java/README.md)**: +**[prql-java](https://github.com/PRQL/prql/blob/main/bindings/prql-java/README.md)**: Rust bindings to the `prql-compiler` Rust library. -**[prql-js](https://github.com/PRQL/prql/blob/main/prql-js/README.md)**: +**[prql-js](https://github.com/PRQL/prql/blob/main/bindings/prql-js/README.md)**: Javascript bindings to the `prql-compiler` Rust library. -**[prql-lib](https://github.com/PRQL/prql/blob/main/prql-lib/README.md)**: +**[prql-lib](https://github.com/PRQL/prql/blob/main/bindings/prql-lib/README.md)**: Generates `.a` and `.so` libraries from the `prql-compiler` Rust library for bindings to other languages -**[prql-python](https://github.com/PRQL/prql/blob/main/prql-python/README.md)**: +**[prql-python](https://github.com/PRQL/prql/blob/main/bindings/prql-python/README.md)**: Python bindings to the `prql-compiler` Rust library. -**[website](https://github.com/PRQL/prql/blob/main/website/README.md)**: Our +**[website](https://github.com/PRQL/prql/blob/main/web/website/README.md)**: Our website, hosted at , built with `hugo`. ## How we test @@ -251,6 +281,9 @@ Our tests, from the bottom of the pyramid to the top: on GitHub on every commit; any changes they make are added onto the branch automatically in an additional commit. + - Checking by [MegaLinter](https://megalinter.io/latest/), which includes more + Linters, is also done automatically on GitHub. (experimental) + - **Unit tests & inline insta snapshots** โ€” we rely on unit tests to rapidly check that our code basically works. We extensively use [Insta](https://insta.rs/), a snapshot testing tool which writes out the @@ -301,18 +334,18 @@ inconsistent in watchexec. Let's revert back if it gets solved. get into a loop of writing snapshot files, triggering a change, writing a snapshot file, etc. --> -- **[Examples](https://github.com/PRQL/prql/blob/main/book/tests/snapshot.rs)** +- **[Examples](https://github.com/PRQL/prql/blob/main/web/book/tests/snapshot.rs)** โ€”ย we compile all examples in the PRQL Book, to test that they produce the SQL we expect, and that changes to our code don't cause any unexpected regressions. -- **[Integration tests](https://github.com/PRQL/prql/blob/main/book/src/integrations/README.md)** +- **[Integration tests](https://github.com/PRQL/prql/blob/main/web/book/src/integrations/README.md)** โ€” these run tests against real databases, to ensure we're producing correct SQL. - **[GitHub Actions on every commit](https://github.com/PRQL/prql/blob/main/.github/workflows/pull-request.yaml)** โ€” we run the tests described up to this point on every commit to a pull - request. These are designed to run in under two minutes, and we should be + request. These are designed to run in under five minutes, and we should be reassessing their scope if they grow beyond that. Once these pass, a pull request can be merged. @@ -422,6 +455,3 @@ Currently we release in a semi-automated way: ``` We may make this more automated in future; e.g. automatic changelog creation. - -[^wrap]: -[^perms]: diff --git a/book/src/contributing/fortnightly-dev-call.ics b/web/book/src/contributing/fortnightly-dev-call.ics similarity index 100% rename from book/src/contributing/fortnightly-dev-call.ics rename to web/book/src/contributing/fortnightly-dev-call.ics diff --git a/book/src/examples/README.md b/web/book/src/examples/README.md similarity index 100% rename from book/src/examples/README.md rename to web/book/src/examples/README.md diff --git a/book/src/examples/cte.md b/web/book/src/examples/cte.md similarity index 95% rename from book/src/examples/cte.md rename to web/book/src/examples/cte.md index 138b47154575..eab8c1592c7a 100644 --- a/book/src/examples/cte.md +++ b/web/book/src/examples/cte.md @@ -1,4 +1,4 @@ -```prql +```prql_no_fmt let newest_employees = ( from employees sort tenure diff --git a/book/src/examples/employees.md b/web/book/src/examples/employees.md similarity index 99% rename from book/src/examples/employees.md rename to web/book/src/examples/employees.md index 5090a78155e2..48467dc87ecb 100644 --- a/book/src/examples/employees.md +++ b/web/book/src/examples/employees.md @@ -99,7 +99,7 @@ select [mng_name, managers.gender, salary_avg, salary_sd] > Find distributions of titles, salaries and genders for each department. -```prql +```prql_no_fmt from de=dept_emp join s=salaries side:left [ (s.emp_no == de.emp_no), diff --git a/book/src/examples/list-equivalence.md b/web/book/src/examples/list-equivalence.md similarity index 100% rename from book/src/examples/list-equivalence.md rename to web/book/src/examples/list-equivalence.md diff --git a/book/src/examples/misc.md b/web/book/src/examples/misc.md similarity index 100% rename from book/src/examples/misc.md rename to web/book/src/examples/misc.md diff --git a/book/src/examples/variables.md b/web/book/src/examples/variables.md similarity index 100% rename from book/src/examples/variables.md rename to web/book/src/examples/variables.md diff --git a/book/src/integrations/README.md b/web/book/src/integrations/README.md similarity index 100% rename from book/src/integrations/README.md rename to web/book/src/integrations/README.md diff --git a/book/src/integrations/dbt.md b/web/book/src/integrations/dbt.md similarity index 100% rename from book/src/integrations/dbt.md rename to web/book/src/integrations/dbt.md diff --git a/book/src/integrations/jupyter.md b/web/book/src/integrations/jupyter.md similarity index 99% rename from book/src/integrations/jupyter.md rename to web/book/src/integrations/jupyter.md index 176a47ac11ad..7b0bdd3c063d 100644 --- a/book/src/integrations/jupyter.md +++ b/web/book/src/integrations/jupyter.md @@ -24,7 +24,7 @@ could to go! We bundle in `IPython` and `pandas`, though you'll need to install pip install pyprql ``` -### Set Up +### Set up Open up either an `IPython` terminal or `Jupyter` notebook. First, we need to load the extension and connect to a database. diff --git a/book/src/integrations/prefect.md b/web/book/src/integrations/prefect.md similarity index 100% rename from book/src/integrations/prefect.md rename to web/book/src/integrations/prefect.md diff --git a/web/book/src/integrations/rill.md b/web/book/src/integrations/rill.md new file mode 100644 index 000000000000..0e2f4729e32e --- /dev/null +++ b/web/book/src/integrations/rill.md @@ -0,0 +1,5 @@ +# Rill + +PRQL has had some work to integrate with Rill. See the +[Rill ssues](https://github.com/PRQL/prql/issues?q=is%3Aissue+rill) for more +details. diff --git a/book/src/integrations/vscode.md b/web/book/src/integrations/vscode.md similarity index 100% rename from book/src/integrations/vscode.md rename to web/book/src/integrations/vscode.md diff --git a/book/src/internals/README.md b/web/book/src/internals/README.md similarity index 67% rename from book/src/internals/README.md rename to web/book/src/internals/README.md index 38571d2be4ac..dadf1de088ae 100644 --- a/book/src/internals/README.md +++ b/web/book/src/internals/README.md @@ -6,3 +6,7 @@ their meaning. It's intended for advanced users and compiler contributors. - [Name resolving](./name-resolving.md) - [Functions](./functional-lang.md) - [Syntax highlighting](./syntax-highlighting.md) + +It's also worth checking out the +[`prql-compiler` docs](https://docs.rs/prql-compiler/latest/prql_compiler/) for +more details on its API. diff --git a/book/src/internals/compiler-architecture.md b/web/book/src/internals/compiler-architecture.md similarity index 97% rename from book/src/internals/compiler-architecture.md rename to web/book/src/internals/compiler-architecture.md index b7702d3a222f..3679719f0a2f 100644 --- a/book/src/internals/compiler-architecture.md +++ b/web/book/src/internals/compiler-architecture.md @@ -4,7 +4,7 @@ Compiler works in the following stages: 1. Lexing & parsing - split PRQL text into tokens, build parse tree and convert into our AST (Abstract Syntax Tree, see `ast` module). Parsing is done using - PEST parser (`prql.pest`), AST is constructed in `parser.rs`. + the chumsky parser, AST is constructed in `parser.rs`. 2. Semantic analysis - resolves names (identifiers), extracts declarations, determines frames (columns of the table in each step). It declares `Context` diff --git a/book/src/internals/functional-lang.md b/web/book/src/internals/functional-lang.md similarity index 100% rename from book/src/internals/functional-lang.md rename to web/book/src/internals/functional-lang.md diff --git a/book/src/internals/language-design.md b/web/book/src/internals/language-design.md similarity index 100% rename from book/src/internals/language-design.md rename to web/book/src/internals/language-design.md diff --git a/book/src/internals/name-resolving.md b/web/book/src/internals/name-resolving.md similarity index 96% rename from book/src/internals/name-resolving.md rename to web/book/src/internals/name-resolving.md index 74b590e93a14..78a153358959 100644 --- a/book/src/internals/name-resolving.md +++ b/web/book/src/internals/name-resolving.md @@ -50,7 +50,7 @@ three things can happen: ## Translating to SQL -When translating into a SQL statement which references only one table, there is +When translating into an SQL statement which references only one table, there is no need to reference column names with table prefix. ```prql diff --git a/web/book/src/internals/syntax-highlighting.md b/web/book/src/internals/syntax-highlighting.md new file mode 100644 index 000000000000..e15d25a9f14e --- /dev/null +++ b/web/book/src/internals/syntax-highlighting.md @@ -0,0 +1 @@ +{{#include ../../../../grammars/README.md}} diff --git a/book/src/introduction.md b/web/book/src/introduction.md similarity index 100% rename from book/src/introduction.md rename to web/book/src/introduction.md diff --git a/book/src/language-features/README.md b/web/book/src/language-features/README.md similarity index 59% rename from book/src/language-features/README.md rename to web/book/src/language-features/README.md index c03c67d19706..a75dfe4e9e88 100644 --- a/book/src/language-features/README.md +++ b/web/book/src/language-features/README.md @@ -3,17 +3,16 @@ The pages of this section describe how PRQL handles various aspects of the language. -| Feature | Purpose | -| ---------------- | ------------------------------------------------------------------------- | -| Coalesce | [Handle potentially NULL values](./coalesce.md) | -| Dates & times | [Handle dates and times](./dates-and-times.md) | -| Distinct | [Equivalent of SQL `DISTINCT`](./distinct.md) | -| Null handling | [Handle `NULL` values](./null.md) | -| Ranges | [Syntax for all forms of ranges](./ranges.md) | -| Regex | [Handle regular expressions](./regex.md) | -| Stdlib | [PRQL's "builtin" set of functions](./standard-library.md) | -| Strings | [Rules for creating strings](./strings.md) | -| S-strings | [Insert SQL directly into a query with an S-string](./s-strings.md) | -| F-strings | [Combine several column's data with F-strings](./f-strings.md) | -| Switch | [Create a new column based on the contents of other columns](./switch.md) | -| Target & Version | [Specify a target SQL engine and PRQL version](./target.md) | +| Feature | Purpose | +| ---------------- | ----------------------------------------------------------------------- | +| Coalesce | [Handle potentially NULL values](./coalesce.md) | +| Dates & times | [Handle dates and times](./dates-and-times.md) | +| Distinct | [Equivalent of SQL `DISTINCT`](./distinct.md) | +| Null handling | [Handle `NULL` values](./null.md) | +| Ranges | [Syntax for all forms of ranges](./ranges.md) | +| Regex | [Handle regular expressions](./regex.md) | +| Strings | [Rules for creating strings](./strings.md) | +| S-strings | [Insert SQL directly into a query with an S-string](./s-strings.md) | +| F-strings | [Combine several column's data with F-strings](./f-strings.md) | +| Case | [Create a new column based on the contents of other columns](./case.md) | +| Target & Version | [Specify a target SQL engine and PRQL version](./target.md) | diff --git a/web/book/src/language-features/case.md b/web/book/src/language-features/case.md new file mode 100644 index 000000000000..f91768d21952 --- /dev/null +++ b/web/book/src/language-features/case.md @@ -0,0 +1,27 @@ +# Case + +```admonish note +`case` is currently experimental and may change behavior in the near future +``` + +PRQL uses `case` for both SQL's `CASE` and `IF` statements. Here's an example: + +```prql_no_fmt +from employees +derive distance = case [ + city == "Calgary" => 0, + city == "Edmonton" => 300, +] +``` + +If no condition is met, the value takes a `null` value. To set a default, use a +`true` condition: + +```prql_no_fmt +from employees +derive distance = case [ + city == "Calgary" => 0, + city == "Edmonton" => 300, + true => "Unknown", +] +``` diff --git a/book/src/language-features/coalesce.md b/web/book/src/language-features/coalesce.md similarity index 100% rename from book/src/language-features/coalesce.md rename to web/book/src/language-features/coalesce.md diff --git a/book/src/language-features/dates-and-times.md b/web/book/src/language-features/dates-and-times.md similarity index 84% rename from book/src/language-features/dates-and-times.md rename to web/book/src/language-features/dates-and-times.md index 55d26e009a67..e788ffc33165 100644 --- a/book/src/language-features/dates-and-times.md +++ b/web/book/src/language-features/dates-and-times.md @@ -36,12 +36,12 @@ derive should_have_shipped_today = (order_time < @08:30) Timestamps are represented by `@{yyyy-mm-ddTHH:mm:ss.SSSยฑZ}` / `@{date}T{time}`, with any time parts not supplied being rounded to zero, including the timezone, -which is represented by `+HH:mm`, `-HH:mm` or `Z`. This isย `@` followed by the -ISO8601 datetime format, which uses `T` to separate date & time. +which is represented by `+HH:mm`, `-HH:mm` or `Z` (`:` is optional). This isย `@` +followed by the ISO8601 datetime format, which uses `T` to separate date & time. ```prql from commits -derive first_prql_commit = @2020-01-01T13:19:55-0800 +derive first_prql_commit = @2020-01-01T13:19:55-08:00 ``` ## Intervals @@ -66,9 +66,9 @@ derive first_check_in = start + 10days Here's a fuller list of examples: -- `@20221231` is forbidden โ€” it must contain full punctuation (`-` and `:`), +- `@20221231` is invalid โ€” it must contain full punctuation (`-` and `:`), - `@2022-12-31` is a date -- `@2022-12` or `@2022` are forbidden โ€” SQL can't express a month, only a date +- `@2022-12` or `@2022` are invalid โ€” SQL can't express a month, only a date - `@16:54:32.123456` is a time - `@16:54:32`, `@16:54`, `@16` are all allowed, expressing `@16:54:32.000000`, `@16:54:00.000000`, `@16:00:00.000000` respectively @@ -76,8 +76,8 @@ Here's a fuller list of examples: - `@2022-12-31T16:54:32.123456Z` is a timestamp in UTC - `@2022-12-31T16:54+02` is timestamp in UTC+2 - `@2022-12-31T16:54+02:00` and `@2022-12-31T16:54+02` are datetimes in UTC+2 -- `@16:54+02` is forbidden โ€” time is always local, so it cannot have a timezone -- `@2022-12-31+02` is forbidden โ€” date is always local, so it cannot have a +- `@16:54+02` is invalid โ€” time is always local, so it cannot have a timezone +- `@2022-12-31+02` is invalid โ€” date is always local, so it cannot have a timezone ## Roadmap diff --git a/book/src/language-features/distinct.md b/web/book/src/language-features/distinct.md similarity index 80% rename from book/src/language-features/distinct.md rename to web/book/src/language-features/distinct.md index 598360aa0ab9..4a05bd81235f 100644 --- a/book/src/language-features/distinct.md +++ b/web/book/src/language-features/distinct.md @@ -34,6 +34,14 @@ group department ( ) ``` +Note that we can't always compile to `DISTINCT`; when the columns in the `group` +aren't all the available columns, we need to use a window function: + +```prql +from employees +group [first_name, last_name] (take 1) +``` + ## Roadmap When using Postgres dialect, we are planning to compile: diff --git a/book/src/language-features/f-strings.md b/web/book/src/language-features/f-strings.md similarity index 85% rename from book/src/language-features/f-strings.md rename to web/book/src/language-features/f-strings.md index 1e1ada7cae71..24df646f9c8e 100644 --- a/book/src/language-features/f-strings.md +++ b/web/book/src/language-features/f-strings.md @@ -16,6 +16,9 @@ from web select url = f"http{tls}://www.{domain}.{tld}/{page}" ``` +Note that interpolations can only contain plain variable names and not whole +expression like Python. + ## Roadmap In the future, f-strings may incorporate string formatting such as datetimes, diff --git a/book/src/language-features/null.md b/web/book/src/language-features/null.md similarity index 100% rename from book/src/language-features/null.md rename to web/book/src/language-features/null.md diff --git a/book/src/language-features/ranges.md b/web/book/src/language-features/ranges.md similarity index 90% rename from book/src/language-features/ranges.md rename to web/book/src/language-features/ranges.md index 4c3d674c9e75..fb4efbd3c953 100644 --- a/book/src/language-features/ranges.md +++ b/web/book/src/language-features/ranges.md @@ -8,7 +8,7 @@ including dates: ```prql from events -filter (date | in @1776-07-04..@1787-09-17) +filter (created_at | in @1776-07-04..@1787-09-17) filter (magnitude | in 50..100) derive is_northern = (latitude | in 0..) ``` @@ -20,7 +20,7 @@ in `take`: ```prql from orders -sort [-value, date] +sort [-value, created_at] take 101..110 ``` diff --git a/book/src/language-features/regex.md b/web/book/src/language-features/regex.md similarity index 100% rename from book/src/language-features/regex.md rename to web/book/src/language-features/regex.md diff --git a/book/src/language-features/s-strings.md b/web/book/src/language-features/s-strings.md similarity index 88% rename from book/src/language-features/s-strings.md rename to web/book/src/language-features/s-strings.md index fe2d133ef001..8cde2da4560d 100644 --- a/book/src/language-features/s-strings.md +++ b/web/book/src/language-features/s-strings.md @@ -38,8 +38,11 @@ join s=salaries side:left [ For those who have used Python, s-strings are similar to Python's f-strings, but the result is SQL code, rather than a string literal. For example, a Python -f-string of `f"average{col}"` would produce `"average(salary)"`, with quotes; -while in PRQL, `s"average{col}"` produces `average(salary)`, without quotes. +f-string of `f"average({col})"` would produce `"average(salary)"`, with quotes; +while in PRQL, `s"average({col})"` produces `average(salary)`, without quotes. + +Note that interpolations can only contain plain variable names and not whole +expression like Python. We can also use s-strings to produce a full table: @@ -58,7 +61,7 @@ should implement it in PRQL or PRQL's stdlib. To output braces from an s-string, use double braces: -```prql +```prql_no_fmt from employees derive [ has_valid_title = s"regexp_contains(title, '([a-z0-9]*-){{2,}}')" diff --git a/book/src/language-features/standard-library.md b/web/book/src/language-features/standard-library/README.md similarity index 92% rename from book/src/language-features/standard-library.md rename to web/book/src/language-features/standard-library/README.md index 220565d5cf3d..5d8f602afcd5 100644 --- a/book/src/language-features/standard-library.md +++ b/web/book/src/language-features/standard-library/README.md @@ -1,4 +1,4 @@ -# Standard Library +# Standard library The standard library currently contains commonly used functions that are used in SQL. It's not yet as broad as we'd like, and we're very open to expanding it. @@ -17,7 +17,7 @@ Here's the source of the current [PRQL `std`](https://github.com/PRQL/prql/blob/main/prql-compiler/src/semantic/std.prql): ```prql_no_test -{{#include ../../../prql-compiler/src/semantic/std.prql}} +{{#include ../../../../../prql-compiler/src/semantic/std.prql}} ``` And a couple of examples: diff --git a/book/src/transforms/from_text.md b/web/book/src/language-features/standard-library/from-text.md similarity index 75% rename from book/src/transforms/from_text.md rename to web/book/src/language-features/standard-library/from-text.md index 0a9b51160ca7..29229f6db0ef 100644 --- a/book/src/transforms/from_text.md +++ b/web/book/src/language-features/standard-library/from-text.md @@ -1,4 +1,4 @@ -# From Text +# From text It's often useful to make a small table inline, for example when exploring how a database will evaluate an expression, or to have a small lookup table inline. @@ -8,10 +8,11 @@ PRQL uses `from_text` for this. It accepts a few formats: -- `format:csv`; also the default, for CSV. -- `format:json` for either: - - A list of dicts, - - or a schema of `columns` & `data>`. +- `format:csv` parses CSV (default), +- `format:json` parses either: + - an array of objects each of which represents a row, or + - an object with fields `columns` & `data`, where `columns` take an array of + column names and `data` takes an array of arrays. ```prql from_text """ diff --git a/web/book/src/language-features/standard-library/loop.md b/web/book/src/language-features/standard-library/loop.md new file mode 100644 index 000000000000..061ec312a932 --- /dev/null +++ b/web/book/src/language-features/standard-library/loop.md @@ -0,0 +1,50 @@ +# Loop + +> _experimental_ + +```prql_no_test +loop {step_function} {initial_relation} +``` + +Iteratively applies `step` function to `initial` relation until the `step` +returns an empty table. Returns a relation that contains rows of initial +relation and all intermediate relations. + +This behavior could be expressed with following pseudo-code: + +```python +def loop(step, initial): + result = [] + current = initial + while current is not empty: + result = append(result, current) + current = step(current) + + return result +``` + +## Examples + +```prql +from_text format:json '[{"n": 1 }]' +loop ( + filter n<4 + select n = n+1 +) + +# returns [1, 2, 3, 4] +``` + +```admonish +Behavior of WITH RECURSIVE may depend on database configuration (MySQL). +prql-compiler assumes behavior described by +[Postgres documentation](https://www.postgresql.org/docs/15/queries-with.html#QUERIES-WITH-RECURSIVE) +and will not produce correct results for +[alternative configurations of MySQL](https://dev.mysql.com/doc/refman/8.0/en/with.html#common-table-expressions-recursive). +``` + +```admonish +Currently, `loop` may produce references to the recursive CTE in sub-queries, +which is not supported by some database engines (SQLite). For now, we suggest you keep step +functions simple enough to fit into a single SELECT statement. +``` diff --git a/book/src/language-features/strings.md b/web/book/src/language-features/strings.md similarity index 75% rename from book/src/language-features/strings.md rename to web/book/src/language-features/strings.md index db4b9d9e07db..23acb935ce77 100644 --- a/book/src/language-features/strings.md +++ b/web/book/src/language-features/strings.md @@ -13,7 +13,7 @@ select x = 'hello world' ``` To quote a string containing quotes, either use the "other" type of quote, or -use three-or-more quotes, and close with the same number. +use 3, 4, 5 or 6 quotes, and close with the same number. ```prql from my_table @@ -30,7 +30,15 @@ from my_table select x = """""I said """hello world"""!""""" ``` -## F-strings and s-strings +Strings can also contain any escape defined by +[JSON standard](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/). + +```prql_no_fmt +from my_table +select x = "\t\tline ends here\n \\ " +``` + +## F-Strings and S-Strings These special case strings can be used to: @@ -40,10 +48,6 @@ values [S-strings](./s-strings.md) - Insert SQL statements directly into the query. Use when PRQL doesn't have an equivalent facility. -```admonish note -Currently PRQL does not adjust escape characters. -``` - ```admonish warning Currently PRQL allows multiline strings with either a single character or multiple character quotes. This may change for strings using a single character diff --git a/book/src/language-features/target.md b/web/book/src/language-features/target.md similarity index 96% rename from book/src/language-features/target.md rename to web/book/src/language-features/target.md index 558e338292cc..9a7a87391457 100644 --- a/book/src/language-features/target.md +++ b/web/book/src/language-features/target.md @@ -15,7 +15,7 @@ sort age take 10 ``` -```prql +```prql_no_fmt prql target:sql.mssql from employees @@ -47,8 +47,8 @@ very welcome. PRQL allows specifying a version of the language in the PRQL header, like: -```prql -prql version:"0.5" +```prql_no_fmt +prql version:"0.6.1" from employees ``` diff --git a/book/src/lib.rs b/web/book/src/lib.rs similarity index 61% rename from book/src/lib.rs rename to web/book/src/lib.rs index 9c69858dc66a..4197a12530c5 100644 --- a/book/src/lib.rs +++ b/web/book/src/lib.rs @@ -13,7 +13,6 @@ use prql_compiler::compile; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; use pulldown_cmark_to_cmark::cmark; use semver::{Version, VersionReq}; -use similar::DiffableStr; use std::{io, process}; /// Checks renderer support and runs the preprocessor. @@ -97,29 +96,68 @@ impl Preprocessor for ComparisonPreprocessor { } } +pub fn code_block_lang<'a>(event: &'a Event) -> Option<&'a str> { + if let Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) = event { + Some(lang.as_ref()) + } else { + None + } +} + fn replace_examples(text: &str) -> Result { let mut parser = Parser::new_ext(text, Options::all()); let mut cmark_acc = vec![]; while let Some(event) = parser.next() { - match event.clone() { - Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) if lang == "prql".into() => { - if let Some(Event::Text(text)) = parser.next() { - let prql = text.to_string(); - let options = prql_compiler::Options::default().no_signature(); - let html = table_of_comparison( - text.as_str().unwrap(), - &compile(&prql, options).unwrap(), - ); - cmark_acc.push(Event::Html(html.into())); - - // Skip ending tag - parser.next(); - } else { - bail!("Expected text after PRQL code block"); + if let Some(lang) = code_block_lang(&event) { + let Some(Event::Text(text)) = parser.next() + else { + bail!("Expected text within code block") + }; + let prql = text.to_string(); + let options = prql_compiler::Options::default().no_signature(); + let result = compile(&prql, &options); + + match lang { + "prql" | "prql_no_fmt" => cmark_acc.push(Event::Html( + table_of_comparison( + &prql, + result + .unwrap_or_else(|_| { + panic!("{}", format!("Query raised an error:\n\n {prql}\n\n")) + }) + .as_str(), + ) + .into(), + )), + "prql_error" => cmark_acc.push(Event::Html( + table_of_error( + &prql, + result + .expect_err( + &format!( + "Query was labeled to raise an error, but succeeded.\n {prql}\n\n" + ) + .to_string(), + ) + .to_string() + .as_str(), + ) + .into(), + )), + "prql_no_test" => {} + _ => { + if lang.starts_with("prql") { + bail!("Unknown code block language: {}", lang) + } else { + cmark_acc.push(event.to_owned()) + } } - } - _ => cmark_acc.push(event.to_owned()), + }; + // Skip ending tag + parser.next(); + } else { + cmark_acc.push(event.to_owned()) } } let mut buf = String::new(); @@ -160,6 +198,39 @@ fn table_of_comparison(prql: &str, sql: &str) -> String { .to_string() } +// Exactly the same as `table_of_comparison`, but with a different title for the second column. +fn table_of_error(prql: &str, error: &str) -> String { + format!( + r#" +
+ +
+

PRQL

+ +```prql +{prql} +``` + +
+ +
+

Error

+ +``` +{error} +``` + +
+ +
+"#, + prql = prql.trim(), + error = error, + ) + .trim_start() + .to_string() +} + #[test] fn test_table() -> Result<()> { use insta::assert_display_snapshot; diff --git a/book/src/main.rs b/web/book/src/main.rs similarity index 100% rename from book/src/main.rs rename to web/book/src/main.rs diff --git a/book/src/queries/README.md b/web/book/src/queries/README.md similarity index 100% rename from book/src/queries/README.md rename to web/book/src/queries/README.md diff --git a/book/src/queries/functions.md b/web/book/src/queries/functions.md similarity index 65% rename from book/src/queries/functions.md rename to web/book/src/queries/functions.md index f49626786b04..5f31e4de8903 100644 --- a/book/src/queries/functions.md +++ b/web/book/src/queries/functions.md @@ -16,25 +16,25 @@ Functions have two types of parameters: So this function is named `fahrenheit_to_celsius` and has one parameter `temp`: -```prql +```prql_no_fmt func fahrenheit_to_celsius temp -> (temp - 32) / 1.8 from cities derive temp_c = (fahrenheit_to_celsius temp_f) ``` -This function is named `interp`, and has two positional parameters named -`higher` and `x`, and one named parameter named `lower` which takes a default -argument of `0`. It calculates the proportion of the distance that `x` is -between `lower` and `higher`. +This function is named `interp`, and has two positional parameters named `high` +and `x`, and one named parameter named `low` which takes a default argument of +`0`. It calculates the proportion of the distance that `x` is between `low` and +`high`. ```prql -func interp lower:0 higher x -> (x - lower) / (higher - lower) +func interp low:0 high x -> (x - low) / (high - low) from students derive [ sat_proportion_1 = (interp 1600 sat_score), - sat_proportion_2 = (interp lower:0 1600 sat_score), + sat_proportion_2 = (interp low:0 1600 sat_score), ] ``` @@ -47,12 +47,12 @@ positional parameter of the function. Here's the same result as the examples above with an alternative construction: ```prql -func interp lower:0 higher x -> (x - lower) / (higher - lower) +func interp low:0 high x -> (x - low) / (high - low) from students derive [ sat_proportion_1 = (sat_score | interp 1600), - sat_proportion_2 = (sat_score | interp lower:0 1600), + sat_proportion_2 = (sat_score | interp low:0 1600), ] ``` @@ -69,27 +69,28 @@ We can combine a chain of functions, which makes logic more readable: ```prql func fahrenheit_to_celsius temp -> (temp - 32) / 1.8 -func interp lower:0 higher x -> (x - lower) / (higher - lower) +func interp low:0 high x -> (x - low) / (high - low) from kettles derive boiling_proportion = (temp_c | fahrenheit_to_celsius | interp 100) ``` -## Roadmap +## Scope ### Late binding -Currently, functions require a binding to variables in scope; they can't -late-bind to column names; so for example: - -```prql_no_test -func return price -> (price - dividend) / price_yesterday -``` +Functions can binding to any variables in scope when the function is executed. +For example, here `cost_total` refers to the column that's introduced in the +`from`. -...isn't yet a valid function, and instead would needs to be: +```prql +func cost_share cost -> cost / cost_total -```prql_no_test -func return price dividend price_yesterday -> (price - dividend) / (price_yesterday) +from costs +select [materials, labor, overhead, cost_total] +derive [ + materials_share = (cost_share materials), + labor_share = (cost_share labor), + overhead_share = (cost_share overhead), +] ``` - -(which makes functions in this case not useful) diff --git a/book/src/queries/pipelines.md b/web/book/src/queries/pipelines.md similarity index 100% rename from book/src/queries/pipelines.md rename to web/book/src/queries/pipelines.md diff --git a/book/src/queries/variables.md b/web/book/src/queries/variables.md similarity index 100% rename from book/src/queries/variables.md rename to web/book/src/queries/variables.md diff --git a/book/src/syntax.md b/web/book/src/syntax.md similarity index 84% rename from book/src/syntax.md rename to web/book/src/syntax.md index 3c0afc5dd5b7..900f671388c3 100644 --- a/book/src/syntax.md +++ b/web/book/src/syntax.md @@ -14,7 +14,7 @@ A summary of PRQL syntax | --------------- | -------------------------------------------------------------------- | ------------------------------------------------------- | | \| | [Pipelines](queries/pipelines.md) | from employees \| select first_name | | `=` | [Assigns](transforms/select.md) & [Aliases](transforms/join.md) | `from e = employees`
`derive total = (sum salary)` | -| `:` | [Named args & Parameters](queries/functions.md) | `interp lower:0 1600 sat_score` | +| `:` | [Named args & Parameters](queries/functions.md) | `interp low:0 1600 sat_score` | | `[]` | [Lists](./syntax.md#lists) | `select [id, amount]` | | `()` | [Precedence & Parentheses](./syntax.md#precedence-and-parentheses) | `derive celsius = (fahrenheit - 32) / 1.8` | | `''` & `""` | [Strings](language-features/strings.md) | `derive name = 'Mary'` | @@ -137,7 +137,7 @@ function call, like `foo + bar`. Here's a full rundown of times this applier: -```prql +```prql_no_fmt from employees # Requires parentheses, because it's contains a pipe derive is_proximate = (distance | in 0..20) @@ -166,6 +166,31 @@ sort (-distance) sort [-distance] ``` +This doesn't work, for example (though it should provide a much better error +message): + +```prql_error +from employees +derive total_distance = sum distance +``` + +For a more formal definition, refer to this precedence table. Because function +calls have the lowest precedence, nested function calls or arguments that start +or end with an operator require parenthesis. + +| Group | Operators | Precedence | Associativity | +| -------------- | ----------------- | ---------- | ------------- | +| identifier dot | `.` | 1 | | +| unary | `- + ! ==` | 2 | | +| range | `..` | 3 | | +| mul | `* / %` | 4 | left-to-right | +| add | `+ -` | 5 | left-to-right | +| compare | `== != <= >= < >` | 6 | left-to-right | +| coalesce | `??` | 7 | left-to-right | +| and | `and` | 8 | left-to-right | +| or | `or` | 9 | left-to-right | +| function call | | 10 | | + ## Inner Transforms Parentheses are also used for transforms (such as `group` and `window`) that @@ -261,3 +286,23 @@ select [ big = 5_000_000, ] ``` + +## Keywords + +At the moment, PRQL uses only four keywords: + +- `prql` +- `let` +- `func` +- `case` + +To use these names as columns or relations, use backticks: `` `case` ``. + +It may seem that transforms are also keywords, but they are normal function +within std namespace: + +```prql +std.from my_table +std.select [from = my_table.a, take = my_table.b] +std.take 3 +``` diff --git a/book/src/transforms/README.md b/web/book/src/transforms/README.md similarity index 100% rename from book/src/transforms/README.md rename to web/book/src/transforms/README.md diff --git a/book/src/transforms/aggregate.md b/web/book/src/transforms/aggregate.md similarity index 100% rename from book/src/transforms/aggregate.md rename to web/book/src/transforms/aggregate.md diff --git a/book/src/transforms/append.md b/web/book/src/transforms/append.md similarity index 100% rename from book/src/transforms/append.md rename to web/book/src/transforms/append.md diff --git a/book/src/transforms/derive.md b/web/book/src/transforms/derive.md similarity index 100% rename from book/src/transforms/derive.md rename to web/book/src/transforms/derive.md diff --git a/book/src/transforms/filter.md b/web/book/src/transforms/filter.md similarity index 100% rename from book/src/transforms/filter.md rename to web/book/src/transforms/filter.md diff --git a/book/src/transforms/from.md b/web/book/src/transforms/from.md similarity index 100% rename from book/src/transforms/from.md rename to web/book/src/transforms/from.md diff --git a/web/book/src/transforms/from_text.md b/web/book/src/transforms/from_text.md new file mode 100644 index 000000000000..a05d2a4b011d --- /dev/null +++ b/web/book/src/transforms/from_text.md @@ -0,0 +1 @@ +# From Text diff --git a/book/src/transforms/group.md b/web/book/src/transforms/group.md similarity index 100% rename from book/src/transforms/group.md rename to web/book/src/transforms/group.md diff --git a/book/src/transforms/join.md b/web/book/src/transforms/join.md similarity index 100% rename from book/src/transforms/join.md rename to web/book/src/transforms/join.md diff --git a/book/src/transforms/select.md b/web/book/src/transforms/select.md similarity index 97% rename from book/src/transforms/select.md rename to web/book/src/transforms/select.md index aa330ac45b8e..b6f9694ef6d9 100644 --- a/book/src/transforms/select.md +++ b/web/book/src/transforms/select.md @@ -50,19 +50,19 @@ We can use `!` to exclude a list of columns. This can operate in two ways: Some examples: -```prql +```prql_no_fmt prql target:sql.bigquery from tracks select ![milliseconds,bytes] ``` -```prql +```prql_no_fmt from tracks select [track_id, title, composer, bytes] select ![title, composer] ``` -```prql +```prql_no_fmt from artists derive nick = name select ![artists.*] diff --git a/book/src/transforms/sort.md b/web/book/src/transforms/sort.md similarity index 100% rename from book/src/transforms/sort.md rename to web/book/src/transforms/sort.md diff --git a/book/src/transforms/take.md b/web/book/src/transforms/take.md similarity index 90% rename from book/src/transforms/take.md rename to web/book/src/transforms/take.md index c12493a7e3e9..2a908e4ed64b 100644 --- a/book/src/transforms/take.md +++ b/web/book/src/transforms/take.md @@ -18,6 +18,6 @@ take 10 ```prql from orders -sort [-value, date] +sort [-value, created_at] take 101..110 ``` diff --git a/book/src/transforms/window.md b/web/book/src/transforms/window.md similarity index 100% rename from book/src/transforms/window.md rename to web/book/src/transforms/window.md diff --git a/web/book/tests/snapshot.rs b/web/book/tests/snapshot.rs new file mode 100644 index 000000000000..ddb8e7f5579c --- /dev/null +++ b/web/book/tests/snapshot.rs @@ -0,0 +1,133 @@ +#![cfg(not(target_family = "wasm"))] +use anyhow::{bail, Result}; +use globset::Glob; +use insta::assert_snapshot; +use prql_compiler::*; +use std::path::{Path, PathBuf}; +use std::{collections::HashMap, fs}; +use walkdir::WalkDir; + +#[test] +/// This test: +/// - Extracts PRQL code blocks from the book +/// - Compiles them to SQL, comparing to a snapshot. Insta raises an error if +/// there's a diff. +/// +/// This mirrors the process in [replace_examples], which inserts a +/// comparison table of SQL into the book, and so serves as a snapshot test of +/// those examples. +/// Snapshot the SQL output of each example. +fn test_prql_examples() { + let opts = Options::default().no_signature(); + let examples = collect_book_examples().unwrap(); + + for (path, prql) in examples { + // Whether it's a success or a failure, get the string. + let sql = compile(&prql, &opts).unwrap_or_else(|e| e.to_string()); + assert_snapshot!(path.to_str().unwrap(), &sql, &prql); + } +} + +const ROOT_EXAMPLES_PATH: &str = "tests/prql"; + +/// Collect all the PRQL examples in the book, as a map of . +fn collect_book_examples() -> Result> { + use pulldown_cmark::{Event, Parser}; + let glob = Glob::new("**/*.md")?.compile_matcher(); + let examples_in_book: HashMap = WalkDir::new(Path::new("./src/")) + .into_iter() + .flatten() + .filter(|x| glob.is_match(x.path())) + .flat_map(|dir_entry| { + let text = fs::read_to_string(dir_entry.path())?; + // TODO: Still slightly duplicative logic here and in + // [lib.rs/replace_examples], but not sure how to avoid it. + // + let mut parser = Parser::new(&text); + let mut prql_blocks = vec![]; + while let Some(event) = parser.next() { + match mdbook_prql::code_block_lang(&event) { + Some(lang) if lang.starts_with("prql") => { + let mut text = String::new(); + while let Some(Event::Text(line)) = parser.next() { + text.push_str(line.to_string().as_str()); + } + if text.is_empty() { + bail!("Expected text after PRQL code block"); + } + if lang == "prql" { + prql_blocks.push(text.to_string()); + } else if lang == "prql_error" { + prql_blocks.push(format!("# Error expected\n\n{text}")); + } else if lang == "prql_no_fmt" { + prql_blocks.push(format!("# Can't yet format & compile\n\n{text}")); + } + } + _ => {} + } + } + let snapshot_prefix = &dir_entry + .path() + .strip_prefix("./src/")? + .to_str() + .unwrap() + .trim_end_matches(".md"); + Ok(prql_blocks + .iter() + .enumerate() + .map(|(i, example)| { + ( + Path::new(&format!("{ROOT_EXAMPLES_PATH}/{snapshot_prefix}-{i}.prql")) + .to_path_buf(), + example.to_string(), + ) + }) + .collect::>()) + }) + .flatten() + .collect(); + + Ok(examples_in_book) +} + +/// Test that the formatted result (the `Display` result) of each example can be +/// compiled. +// +// We previously snapshot all the queries. But that was a lot of output, for +// something we weren't yet looking at. +// +// The ideal would be to auto-format the examples themselves, likely during the +// compilation. For that to provide a good output, we need to implement a proper +// autoformatter. +#[test] +fn test_display() -> Result<(), ErrorMessages> { + collect_book_examples()? + .iter() + .try_for_each(|(path, prql)| { + if prql.contains("# Error expected") || prql.contains("# Can't yet format & compile") { + return Ok(()); + } + prql_to_pl(prql) + .and_then(pl_to_prql) + .and_then(|formatted| compile(&formatted, &Options::default())) + .unwrap_or_else(|_| { + panic!( + " +Failed compiling the formatted result of {path:?} +To skip this test for an example, use `prql_no_fmt` as the language label. + +The original PRQL was: + +{prql} + +", + path = path.canonicalize().unwrap(), + prql = prql + ) + }); + + Ok::<(), ErrorMessages>(()) + })?; + + Ok(()) +} diff --git a/web/book/tests/snapshots/snapshot__tests__prql__examples__cte-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__cte-0.prql.snap new file mode 100644 index 000000000000..f71d66ed6d20 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__cte-0.prql.snap @@ -0,0 +1,30 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nlet newest_employees = (\n from employees\n sort tenure\n take 50\n)\n\nlet average_salaries = (\n from salaries\n group country (\n aggregate average_country_salary = (average salary)\n )\n)\n\nfrom newest_employees\njoin average_salaries [==country]\nselect [name, salary, average_country_salary]\n" +--- +WITH newest_employees AS ( + SELECT + * + FROM + employees + ORDER BY + tenure + LIMIT + 50 +), average_salaries AS ( + SELECT + country, + AVG(salary) AS average_country_salary + FROM + salaries + GROUP BY + country +) +SELECT + newest_employees.name, + newest_employees.salary, + average_salaries.average_country_salary +FROM + newest_employees + JOIN average_salaries ON newest_employees.country = average_salaries.country + diff --git a/book/tests/snapshots/snapshot__@examples__employees-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-0.prql.snap similarity index 62% rename from book/tests/snapshots/snapshot__@examples__employees-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__employees-0.prql.snap index 2ed2d4eba75e..8732d541bc3a 100644 --- a/book/tests/snapshots/snapshot__@examples__employees-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-0.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "from salaries\ngroup [emp_no] (\n aggregate [emp_salary = average salary]\n)\njoin t=titles [==emp_no]\njoin dept_emp side:left [==emp_no]\ngroup [dept_emp.dept_no, t.title] (\n aggregate [avg_salary = average emp_salary]\n)\njoin departments [==dept_no]\nselect [dept_name, title, avg_salary]\n" -input_file: book/tests/prql/examples/employees-0.prql --- -WITH table_1 AS ( +WITH table_3 AS ( SELECT AVG(salary) AS _expr_0, emp_no @@ -12,23 +11,24 @@ WITH table_1 AS ( GROUP BY emp_no ), -table_2 AS ( +table_1 AS ( SELECT t.title, - AVG(table_1._expr_0) AS avg_salary, + AVG(table_2._expr_0) AS avg_salary, dept_emp.dept_no FROM - table_1 - JOIN titles AS t ON table_1.emp_no = t.emp_no - LEFT JOIN dept_emp ON table_1.emp_no = dept_emp.emp_no + table_3 AS table_2 + JOIN titles AS t ON table_2.emp_no = t.emp_no + LEFT JOIN dept_emp ON table_2.emp_no = dept_emp.emp_no GROUP BY dept_emp.dept_no, t.title ) SELECT departments.dept_name, - table_2.title, - table_2.avg_salary + table_0.title, + table_0.avg_salary FROM - table_2 - JOIN departments ON table_2.dept_no = departments.dept_no + table_1 AS table_0 + JOIN departments ON table_0.dept_no = departments.dept_no + diff --git a/book/tests/snapshots/snapshot__@examples__employees-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-1.prql.snap similarity index 64% rename from book/tests/snapshots/snapshot__@examples__employees-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__employees-1.prql.snap index 434d377e6067..7a7a20d9d486 100644 --- a/book/tests/snapshots/snapshot__@examples__employees-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-1.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "from e=employees\njoin salaries [==emp_no]\ngroup [e.emp_no, e.gender] (\n aggregate [\n emp_salary = average salaries.salary\n ]\n)\njoin de=dept_emp [==emp_no] side:left\ngroup [de.dept_no, gender] (\n aggregate [\n salary_avg = average emp_salary,\n salary_sd = stddev emp_salary,\n ]\n)\njoin departments [==dept_no]\nselect [dept_name, gender, salary_avg, salary_sd]\n" -input_file: book/tests/prql/examples/employees-1.prql --- -WITH table_1 AS ( +WITH table_3 AS ( SELECT e.gender, AVG(salaries.salary) AS _expr_0, @@ -15,24 +14,25 @@ WITH table_1 AS ( e.emp_no, e.gender ), -table_2 AS ( +table_1 AS ( SELECT - table_1.gender, - AVG(table_1._expr_0) AS salary_avg, - STDDEV(table_1._expr_0) AS salary_sd, + table_2.gender, + AVG(table_2._expr_0) AS salary_avg, + STDDEV(table_2._expr_0) AS salary_sd, de.dept_no FROM - table_1 - LEFT JOIN dept_emp AS de ON table_1.emp_no = de.emp_no + table_3 AS table_2 + LEFT JOIN dept_emp AS de ON table_2.emp_no = de.emp_no GROUP BY de.dept_no, - table_1.gender + table_2.gender ) SELECT departments.dept_name, - table_2.gender, - table_2.salary_avg, - table_2.salary_sd + table_0.gender, + table_0.salary_avg, + table_0.salary_sd FROM - table_2 - JOIN departments ON table_2.dept_no = departments.dept_no + table_1 AS table_0 + JOIN departments ON table_0.dept_no = departments.dept_no + diff --git a/book/tests/snapshots/snapshot__@examples__employees-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-2.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@examples__employees-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__employees-2.prql.snap index 6918730104c0..606c82c45116 100644 --- a/book/tests/snapshots/snapshot__@examples__employees-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-2.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "from e=employees\njoin salaries [==emp_no]\ngroup [e.emp_no, e.gender] (\n aggregate [\n emp_salary = average salaries.salary\n ]\n)\njoin de=dept_emp [==emp_no]\njoin dm=dept_manager [\n (dm.dept_no == de.dept_no) and s\"(de.from_date, de.to_date) OVERLAPS (dm.from_date, dm.to_date)\"\n]\ngroup [dm.emp_no, gender] (\n aggregate [\n salary_avg = average emp_salary,\n salary_sd = stddev emp_salary\n ]\n)\nderive mng_no = emp_no\njoin managers=employees [==emp_no]\nderive mng_name = s\"managers.first_name || ' ' || managers.last_name\"\nselect [mng_name, managers.gender, salary_avg, salary_sd]\n" -input_file: book/tests/prql/examples/employees-2.prql --- -WITH table_1 AS ( +WITH table_3 AS ( SELECT e.gender, AVG(salaries.salary) AS _expr_0, @@ -15,25 +14,26 @@ WITH table_1 AS ( e.emp_no, e.gender ), -table_2 AS ( +table_1 AS ( SELECT - AVG(table_1._expr_0) AS salary_avg, - STDDEV(table_1._expr_0) AS salary_sd, + AVG(table_2._expr_0) AS salary_avg, + STDDEV(table_2._expr_0) AS salary_sd, dm.emp_no FROM - table_1 - JOIN dept_emp AS de ON table_1.emp_no = de.emp_no + table_3 AS table_2 + JOIN dept_emp AS de ON table_2.emp_no = de.emp_no JOIN dept_manager AS dm ON dm.dept_no = de.dept_no AND (de.from_date, de.to_date) OVERLAPS (dm.from_date, dm.to_date) GROUP BY dm.emp_no, - table_1.gender + table_2.gender ) SELECT managers.first_name || ' ' || managers.last_name AS mng_name, managers.gender, - table_2.salary_avg, - table_2.salary_sd + table_0.salary_avg, + table_0.salary_sd FROM - table_2 - JOIN employees AS managers ON table_2.emp_no = managers.emp_no + table_1 AS table_0 + JOIN employees AS managers ON table_0.emp_no = managers.emp_no + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-3.prql.snap new file mode 100644 index 000000000000..f98020d2457a --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__employees-3.prql.snap @@ -0,0 +1,27 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom de=dept_emp\njoin s=salaries side:left [\n (s.emp_no == de.emp_no),\n s\"({s.from_date}, {s.to_date}) OVERLAPS ({de.from_date}, {de.to_date})\"\n]\ngroup [de.emp_no, de.dept_no] (\n aggregate salary = (average s.salary)\n)\njoin employees [==emp_no]\njoin titles [==emp_no]\nselect [dept_no, salary, employees.gender, titles.title]\n" +--- +WITH table_1 AS ( + SELECT + de.dept_no, + AVG(s.salary) AS salary, + de.emp_no + FROM + dept_emp AS de + LEFT JOIN salaries AS s ON s.emp_no = de.emp_no + AND (s.from_date, s.to_date) OVERLAPS (de.from_date, de.to_date) + GROUP BY + de.emp_no, + de.dept_no +) +SELECT + table_0.dept_no, + table_0.salary, + employees.gender, + titles.title +FROM + table_1 AS table_0 + JOIN employees ON table_0.emp_no = employees.emp_no + JOIN titles ON table_0.emp_no = titles.emp_no + diff --git a/book/tests/snapshots/snapshot__@examples__list-equivalence-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-0.prql.snap similarity index 65% rename from book/tests/snapshots/snapshot__@examples__list-equivalence-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-0.prql.snap index 9d105a9ac969..917f7babe1eb 100644 --- a/book/tests/snapshots/snapshot__@examples__list-equivalence-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect salary\n" -input_file: book/tests/prql/examples/list-equivalence-0.prql --- SELECT salary FROM employees + diff --git a/book/tests/snapshots/snapshot__@examples__list-equivalence-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-1.prql.snap similarity index 66% rename from book/tests/snapshots/snapshot__@examples__list-equivalence-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-1.prql.snap index 7659c80e47e0..65309b667121 100644 --- a/book/tests/snapshots/snapshot__@examples__list-equivalence-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-1.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect [salary]\n" -input_file: book/tests/prql/examples/list-equivalence-1.prql --- SELECT salary FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__derive-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-2.prql.snap similarity index 84% rename from book/tests/snapshots/snapshot__@transforms__derive-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-2.prql.snap index bd3e6b7781ac..b99c1d44b18a 100644 --- a/book/tests/snapshots/snapshot__@transforms__derive-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost\n]\n" -input_file: book/tests/prql/transforms/derive-1.prql --- SELECT *, @@ -9,3 +8,4 @@ SELECT salary + payroll_tax + benefits_cost AS gross_cost FROM employees + diff --git a/book/tests/snapshots/snapshot__@examples__list-equivalence-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-3.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@examples__list-equivalence-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-3.prql.snap index 211db678219b..3503ac3ba3bc 100644 --- a/book/tests/snapshots/snapshot__@examples__list-equivalence-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__list-equivalence-3.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive gross_salary = salary + payroll_tax\nderive gross_cost = gross_salary + benefits_cost\n" -input_file: book/tests/prql/examples/list-equivalence-3.prql --- SELECT *, @@ -9,3 +8,4 @@ SELECT salary + payroll_tax + benefits_cost AS gross_cost FROM employees + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__examples__misc-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__misc-0.prql.snap new file mode 100644 index 000000000000..795e18eb21ee --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__misc-0.prql.snap @@ -0,0 +1,34 @@ +--- +source: book/tests/snapshot.rs +expression: "# TODO: this table should have a column `part` with values 1..5,\n# but such data declaration is not yet supported, see #286\nlet parts = (\n from seq_1_to_5\n)\n\nfrom pl=prospect_lists_prospects\nfilter prospect_list_id == 'cc675eee-8bd1-237f-be5e-622ba511d65e'\njoin a=accounts [a.id == pl.related_id]\njoin er=email_addr_bean_rel [er.bean_id == a.id and er.primary_address == '1']\njoin ea=email_addresses [ea.id == er.email_address_id]\nselect ea.email_address\nderive prefix = s\"regexp_replace(SUBSTRING_INDEX({email_address}, '@', 1), '[.0-9-_:]+', '.')\"\nderive stub = s\"SUBSTRING_INDEX(SUBSTRING_INDEX({prefix}, '.', part), '.', -1)\"\nselect [email_address, stub]\n" +--- +WITH table_1 AS ( + SELECT + related_id + FROM + prospect_lists_prospects AS pl + WHERE + prospect_list_id = 'cc675eee-8bd1-237f-be5e-622ba511d65e' +) +SELECT + ea.email_address, + SUBSTRING_INDEX( + SUBSTRING_INDEX( + regexp_replace( + SUBSTRING_INDEX(ea.email_address, '@', 1), + '[.0-9-_:]+', + '.' + ), + '.', + part + ), + '.', + -1 + ) AS stub +FROM + table_1 AS table_0 + JOIN accounts AS a ON a.id = table_0.related_id + JOIN email_addr_bean_rel AS er ON er.bean_id = a.id + AND er.primary_address = '1' + JOIN email_addresses AS ea ON ea.id = er.email_address_id + diff --git a/book/tests/snapshots/snapshot__@examples__misc-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__misc-1.prql.snap similarity index 90% rename from book/tests/snapshots/snapshot__@examples__misc-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__misc-1.prql.snap index 692a81d2c3ee..bf33decb9e96 100644 --- a/book/tests/snapshots/snapshot__@examples__misc-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__misc-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from club_ratings\nfilter rating != null\n# TODO: this is real ugly. `average rating` should not require parenthesis\n# TODO: why cannot we put comments in group's pipeline?\ngroup year (\n derive [rating_norm = rating - (average rating) / (stddev rating)]\n)\n" -input_file: book/tests/prql/examples/misc-1.prql --- SELECT *, @@ -10,3 +9,4 @@ FROM club_ratings WHERE rating IS NOT NULL + diff --git a/book/tests/snapshots/snapshot__@examples__variables-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__variables-0.prql.snap similarity index 95% rename from book/tests/snapshots/snapshot__@examples__variables-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__variables-0.prql.snap index 9f7411a758ba..9991d13535ad 100644 --- a/book/tests/snapshots/snapshot__@examples__variables-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__variables-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter country == \"USA\" # Each line transforms the previous result.\nderive [ # This adds columns / variables.\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost # Variables can use other variables.\n]\nfilter gross_cost > 0\ngroup [title, country] ( # For each group use a nested pipeline\n aggregate [ # Aggregate each group to a single row\n average salary,\n average gross_salary,\n sum salary,\n sum gross_salary,\n average gross_cost,\n sum_gross_cost = sum gross_cost,\n ct = count,\n ]\n)\nsort sum_gross_cost\nfilter ct > 200\ntake 20\n" -input_file: book/tests/prql/examples/variables-0.prql --- WITH table_1 AS ( SELECT @@ -26,7 +25,7 @@ SELECT SUM(_expr_0) AS sum_gross_cost, COUNT(*) AS ct FROM - table_1 + table_1 AS table_0 WHERE _expr_0 > 0 GROUP BY @@ -38,3 +37,4 @@ ORDER BY sum_gross_cost LIMIT 20 + diff --git a/book/tests/snapshots/snapshot__@examples__variables-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__examples__variables-1.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@examples__variables-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__examples__variables-1.prql.snap index ea3da8460bb5..e6ba77039637 100644 --- a/book/tests/snapshots/snapshot__@examples__variables-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__examples__variables-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup [emp_no] (\n aggregate [\n emp_salary = average salary # average salary resolves to \"AVG(salary)\" (from stdlib)\n ]\n)\njoin titles [==emp_no]\ngroup [title] (\n aggregate [\n avg_salary = average emp_salary\n ]\n)\nselect salary_k = avg_salary / 1000 # avg_salary should resolve to \"AVG(emp_salary)\"\ntake 10 # induces new SELECT\nderive salary = salary_k * 1000 # salary_k should not resolve to \"avg_salary / 1000\"\n" -input_file: book/tests/prql/examples/variables-1.prql --- WITH table_1 AS ( SELECT @@ -13,12 +12,13 @@ WITH table_1 AS ( emp_no ) SELECT - AVG(table_1._expr_0) / 1000 AS salary_k, - AVG(table_1._expr_0) / 1000 * 1000 AS salary + AVG(table_0._expr_0) / 1000 AS salary_k, + AVG(table_0._expr_0) / 1000 * 1000 AS salary FROM - table_1 - JOIN titles ON table_1.emp_no = titles.emp_no + table_1 AS table_0 + JOIN titles ON table_0.emp_no = titles.emp_no GROUP BY titles.title LIMIT 10 + diff --git a/book/tests/snapshots/snapshot__@internals__functional-lang-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-0.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@internals__functional-lang-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-0.prql.snap index ad5671afca07..99428ff3ef14 100644 --- a/book/tests/snapshots/snapshot__@internals__functional-lang-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter age > 50\nsort name\n" -input_file: book/tests/prql/internals/functional-lang-0.prql --- SELECT * @@ -11,3 +10,4 @@ WHERE age > 50 ORDER BY name + diff --git a/book/tests/snapshots/snapshot__@internals__functional-lang-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-1.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@internals__functional-lang-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-1.prql.snap index 5757f55e1b85..392cd3e8ac16 100644 --- a/book/tests/snapshots/snapshot__@internals__functional-lang-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees | filter age > 50 | sort name\n" -input_file: book/tests/prql/internals/functional-lang-1.prql --- SELECT * @@ -11,3 +10,4 @@ WHERE age > 50 ORDER BY name + diff --git a/book/tests/snapshots/snapshot__@internals__functional-lang-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-2.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@internals__functional-lang-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-2.prql.snap index 03421c4e7de6..42c319e1cee7 100644 --- a/book/tests/snapshots/snapshot__@internals__functional-lang-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "filter age > 50 (from employees) | sort name\n" -input_file: book/tests/prql/internals/functional-lang-2.prql --- SELECT * @@ -11,3 +10,4 @@ WHERE age > 50 ORDER BY name + diff --git a/book/tests/snapshots/snapshot__@internals__functional-lang-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-3.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@internals__functional-lang-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-3.prql.snap index 08c9e99fcc9e..0e124447b84b 100644 --- a/book/tests/snapshots/snapshot__@internals__functional-lang-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__functional-lang-3.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "sort name (filter age > 50 (from employees))\n" -input_file: book/tests/prql/internals/functional-lang-3.prql --- SELECT * @@ -11,3 +10,4 @@ WHERE age > 50 ORDER BY name + diff --git a/book/tests/snapshots/snapshot__@syntax-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-0.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@syntax-4.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-0.prql.snap index ddc00aa96ddf..9621e1555906 100644 --- a/book/tests/snapshots/snapshot__@syntax-4.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect first_name\n" -input_file: book/tests/prql/syntax-4.prql --- SELECT first_name FROM employees + diff --git a/book/tests/snapshots/snapshot__@internals__name-resolving-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-1.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@internals__name-resolving-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-1.prql.snap index b9193ffa8f59..f5a7aa7115c1 100644 --- a/book/tests/snapshots/snapshot__@internals__name-resolving-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__internals__name-resolving-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [first_name, dept_id]\njoin d=departments [==dept_id]\nselect [first_name, d.title]\n" -input_file: book/tests/prql/internals/name-resolving-1.prql --- SELECT employees.first_name, @@ -9,3 +8,4 @@ SELECT FROM employees JOIN departments AS d ON employees.dept_id = d.dept_id + diff --git a/book/tests/snapshots/snapshot__@introduction-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__introduction-0.prql.snap similarity index 69% rename from book/tests/snapshots/snapshot__@introduction-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__introduction-0.prql.snap index 741163b2be4d..1519f4a7ce9f 100644 --- a/book/tests/snapshots/snapshot__@introduction-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__introduction-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs -expression: "from employees\nfilter start_date > @2021-01-01 # Clear date syntax\nderive [ # `derive` adds columns / variables\n gross_salary = salary + (tax ?? 0), # Terse coalesce\n gross_cost = gross_salary + benefits_cost, # Variables can use other variables\n]\nfilter gross_cost > 0\ngroup [title, country] ( # `group` runs a pipeline over each group\n aggregate [ # `aggregate` reduces each group to a value\n average gross_salary,\n sum_gross_cost = sum gross_cost, # `=` sets a column name\n ]\n)\nfilter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING`\nderive id = f\"{title}_{country}\" # F-strings like python\nderive country_code = s\"LEFT(country, 2)\" # S-strings allow using SQL as an escape hatch\nsort [sum_gross_cost, -country] # `-country` means descending order\ntake 1..20 # Range expressions (also valid here as `take 20`)\n" -input_file: book/tests/prql/introduction-0.prql +expression: "from employees\nfilter start_date > @2021-01-01 # Clear date syntax\nderive [ # `derive` adds columns / variables\n gross_salary = salary + (tax ?? 0), # Terse coalesce\n gross_cost = gross_salary + benefits_cost, # Variables can use other variables\n]\nfilter gross_cost > 0\ngroup [title, country] ( # `group` runs a pipeline over each group\n aggregate [ # `aggregate` reduces each group to a value\n average gross_salary,\n sum_gross_cost = sum gross_cost, # `=` sets a column name\n ]\n)\nfilter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING`\nderive id = f\"{title}_{country}\" # F-strings like Python\nderive country_code = s\"LEFT(country, 2)\" # S-strings allow using SQL as an escape hatch\nsort [sum_gross_cost, -country] # `-country` means descending order\ntake 1..20 # Range expressions (also valid here as `take 20`)\n" --- WITH table_1 AS ( SELECT @@ -22,7 +21,7 @@ SELECT CONCAT(title, '_', country) AS id, LEFT(country, 2) AS country_code FROM - table_1 + table_1 AS table_0 WHERE _expr_0 > 0 GROUP BY @@ -35,3 +34,4 @@ ORDER BY country DESC LIMIT 20 + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-0.prql.snap new file mode 100644 index 000000000000..3ec06a4f67a0 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-0.prql.snap @@ -0,0 +1,14 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom employees\nderive distance = case [\n city == \"Calgary\" => 0,\n city == \"Edmonton\" => 300,\n]\n" +--- +SELECT + *, + CASE + WHEN city = 'Calgary' THEN 0 + WHEN city = 'Edmonton' THEN 300 + ELSE NULL + END AS distance +FROM + employees + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-1.prql.snap new file mode 100644 index 000000000000..9dab9677a80a --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__case-1.prql.snap @@ -0,0 +1,14 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom employees\nderive distance = case [\n city == \"Calgary\" => 0,\n city == \"Edmonton\" => 300,\n true => \"Unknown\",\n]\n" +--- +SELECT + *, + CASE + WHEN city = 'Calgary' THEN 0 + WHEN city = 'Edmonton' THEN 300 + ELSE 'Unknown' + END AS distance +FROM + employees + diff --git a/book/tests/snapshots/snapshot__@language-features__coalesce-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__coalesce-0.prql.snap similarity index 68% rename from book/tests/snapshots/snapshot__@language-features__coalesce-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__coalesce-0.prql.snap index 6daa5339b530..5503518b55c7 100644 --- a/book/tests/snapshots/snapshot__@language-features__coalesce-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__coalesce-0.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from orders\nderive amount ?? 0\n" -input_file: book/tests/prql/language-features/coalesce-0.prql --- SELECT *, COALESCE(amount, 0) FROM orders + diff --git a/book/tests/snapshots/snapshot__@language-features__dates-and-times-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-0.prql.snap similarity index 73% rename from book/tests/snapshots/snapshot__@language-features__dates-and-times-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-0.prql.snap index 340f30bf1f57..033ee31ffdd0 100644 --- a/book/tests/snapshots/snapshot__@language-features__dates-and-times-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-0.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive age_at_year_end = (@2022-12-31 - dob)\n" -input_file: book/tests/prql/language-features/dates-and-times-0.prql --- SELECT *, DATE '2022-12-31' - dob AS age_at_year_end FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__dates-and-times-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-1.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@language-features__dates-and-times-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-1.prql.snap index 2c2f18a79a1e..37e1970cb15b 100644 --- a/book/tests/snapshots/snapshot__@language-features__dates-and-times-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-1.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from orders\nderive should_have_shipped_today = (order_time < @08:30)\n" -input_file: book/tests/prql/language-features/dates-and-times-1.prql --- SELECT *, order_time < TIME '08:30' AS should_have_shipped_today FROM orders + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-2.prql.snap new file mode 100644 index 000000000000..d67550773cb6 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-2.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "from commits\nderive first_prql_commit = @2020-01-01T13:19:55-08:00\n" +--- +SELECT + *, + TIMESTAMP '2020-01-01T13:19:55-08:00' AS first_prql_commit +FROM + commits + diff --git a/book/tests/snapshots/snapshot__@language-features__dates-and-times-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-3.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@language-features__dates-and-times-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-3.prql.snap index a3a25183480b..8b39bb21363a 100644 --- a/book/tests/snapshots/snapshot__@language-features__dates-and-times-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__dates-and-times-3.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from projects\nderive first_check_in = start + 10days\n" -input_file: book/tests/prql/language-features/dates-and-times-3.prql --- SELECT *, start + INTERVAL 10 DAY AS first_check_in FROM projects + diff --git a/book/tests/snapshots/snapshot__@language-features__distinct-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-0.prql.snap similarity index 73% rename from book/tests/snapshots/snapshot__@language-features__distinct-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-0.prql.snap index 5e3488b70da3..582111718d00 100644 --- a/book/tests/snapshots/snapshot__@language-features__distinct-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect department\ngroup department (\n take 1\n)\n" -input_file: book/tests/prql/language-features/distinct-0.prql --- SELECT DISTINCT department FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__distinct-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-1.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@language-features__distinct-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-1.prql.snap index 15a58a07f008..ed2b6e9f87b9 100644 --- a/book/tests/snapshots/snapshot__@language-features__distinct-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-1.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect department\ngroup department (take 1)\n" -input_file: book/tests/prql/language-features/distinct-1.prql --- SELECT DISTINCT department FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__distinct-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-2.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@language-features__distinct-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-2.prql.snap index 2d9d6d5178f5..ec9e32e03e2c 100644 --- a/book/tests/snapshots/snapshot__@language-features__distinct-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "# youngest employee from each department\nfrom employees\ngroup department (\n sort age\n take 1\n)\n" -input_file: book/tests/prql/language-features/distinct-2.prql --- WITH table_1 AS ( SELECT @@ -17,6 +16,7 @@ WITH table_1 AS ( SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 <= 1 + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-3.prql.snap new file mode 100644 index 000000000000..9bb590d55652 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__distinct-3.prql.snap @@ -0,0 +1,18 @@ +--- +source: book/tests/snapshot.rs +expression: "from employees\ngroup [first_name, last_name] (take 1)\n" +--- +WITH table_1 AS ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY first_name, last_name) AS _expr_0 + FROM + employees +) +SELECT + * +FROM + table_1 AS table_0 +WHERE + _expr_0 <= 1 + diff --git a/book/tests/snapshots/snapshot__@language-features__f-strings-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-0.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@language-features__f-strings-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-0.prql.snap index 051d83e14d1a..f1bfda0e4a6e 100644 --- a/book/tests/snapshots/snapshot__@language-features__f-strings-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect full_name = f\"{first_name} {last_name}\"\n" -input_file: book/tests/prql/language-features/f-strings-0.prql --- SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__f-strings-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-1.prql.snap similarity index 79% rename from book/tests/snapshots/snapshot__@language-features__f-strings-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-1.prql.snap index ff9e275ebaf9..84324ae55337 100644 --- a/book/tests/snapshots/snapshot__@language-features__f-strings-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__f-strings-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from web\nselect url = f\"http{tls}://www.{domain}.{tld}/{page}\"\n" -input_file: book/tests/prql/language-features/f-strings-1.prql --- SELECT CONCAT( @@ -16,3 +15,4 @@ SELECT ) AS url FROM web + diff --git a/book/tests/snapshots/snapshot__@language-features__null-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__null-0.prql.snap similarity index 78% rename from book/tests/snapshots/snapshot__@language-features__null-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__null-0.prql.snap index 7db4048fec29..04239f880972 100644 --- a/book/tests/snapshots/snapshot__@language-features__null-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__null-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter first_name == null\nfilter null != last_name\n" -input_file: book/tests/prql/language-features/null-0.prql --- SELECT * @@ -10,3 +9,4 @@ FROM WHERE first_name IS NULL AND last_name IS NOT NULL + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-0.prql.snap new file mode 100644 index 000000000000..468a1475016a --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-0.prql.snap @@ -0,0 +1,13 @@ +--- +source: book/tests/snapshot.rs +expression: "from events\nfilter (created_at | in @1776-07-04..@1787-09-17)\nfilter (magnitude | in 50..100)\nderive is_northern = (latitude | in 0..)\n" +--- +SELECT + *, + latitude >= 0 AS is_northern +FROM + events +WHERE + created_at BETWEEN DATE '1776-07-04' AND DATE '1787-09-17' + AND magnitude BETWEEN 50 AND 100 + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-1.prql.snap new file mode 100644 index 000000000000..3d0c33aa0b0e --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__ranges-1.prql.snap @@ -0,0 +1,14 @@ +--- +source: book/tests/snapshot.rs +expression: "from orders\nsort [-value, created_at]\ntake 101..110\n" +--- +SELECT + * +FROM + orders +ORDER BY + value DESC, + created_at +LIMIT + 10 OFFSET 100 + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-0.prql.snap similarity index 70% rename from book/tests/snapshots/snapshot__@language-features__s-strings-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-0.prql.snap index 305be40f0809..b37f802598a4 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect db_version = s\"version()\"\n" -input_file: book/tests/prql/language-features/s-strings-0.prql --- SELECT version() AS db_version FROM my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-1.prql.snap similarity index 68% rename from book/tests/snapshots/snapshot__@language-features__s-strings-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-1.prql.snap index 79a6ff83ecef..68274ddf6d00 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-1.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\naggregate [average salary]\n" -input_file: book/tests/prql/language-features/s-strings-1.prql --- SELECT AVG(salary) FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-2.prql.snap similarity index 85% rename from book/tests/snapshots/snapshot__@language-features__s-strings-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-2.prql.snap index 5aad5f54eaf3..46b2693ffa99 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from de=dept_emp\njoin s=salaries side:left [\n (s.emp_no == de.emp_no),\n s\"\"\"({s.from_date}, {s.to_date})\n OVERLAPS\n ({de.from_date}, {de.to_date})\"\"\"\n]\n" -input_file: book/tests/prql/language-features/s-strings-2.prql --- SELECT de.*, @@ -10,3 +9,4 @@ FROM dept_emp AS de LEFT JOIN salaries AS s ON s.emp_no = de.emp_no AND (s.from_date, s.to_date) OVERLAPS (de.from_date, de.to_date) + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-3.prql.snap similarity index 63% rename from book/tests/snapshots/snapshot__@language-features__s-strings-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-3.prql.snap index 3f5c880fd181..93099ceca1a6 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-3.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "from s\"SELECT DISTINCT ON first_name, id, age FROM employees ORDER BY age ASC\"\njoin s = s\"SELECT * FROM salaries\" [==id]\n" -input_file: book/tests/prql/language-features/s-strings-3.prql --- -WITH table_2 AS ( +WITH table_0 AS ( SELECT DISTINCT ON first_name, id, @@ -13,15 +12,16 @@ WITH table_2 AS ( ORDER BY age ASC ), -table_3 AS ( +table_1 AS ( SELECT * FROM salaries ) SELECT - table_0.*, - table_1.* + table_2.*, + table_3.* FROM - table_2 AS table_0 - JOIN table_3 AS table_1 ON table_0.id = table_1.id + table_0 AS table_2 + JOIN table_1 AS table_3 ON table_2.id = table_3.id + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-4.prql.snap new file mode 100644 index 000000000000..90310f1f64a8 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-4.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom employees\nderive [\n has_valid_title = s\"regexp_contains(title, '([a-z0-9]*-){{2,}}')\"\n]\n" +--- +SELECT + *, + regexp_contains(title, '([a-z0-9]*-){2,}') AS has_valid_title +FROM + employees + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-5.prql.snap similarity index 80% rename from book/tests/snapshots/snapshot__@language-features__s-strings-5.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-5.prql.snap index 978a638e672c..fb3adff9254d 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-5.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-5.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [\n gross_salary = salary + benefits,\n daily_rate = s\"{gross_salary} / 365\"\n]\n" -input_file: book/tests/prql/language-features/s-strings-5.prql --- SELECT *, @@ -9,3 +8,4 @@ SELECT salary + benefits / 365 AS daily_rate FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__s-strings-6.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-6.prql.snap similarity index 81% rename from book/tests/snapshots/snapshot__@language-features__s-strings-6.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-6.prql.snap index 46bcfcd6e40a..40fbc41c342c 100644 --- a/book/tests/snapshots/snapshot__@language-features__s-strings-6.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__s-strings-6.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [\n gross_salary = salary + benefits,\n daily_rate = s\"({gross_salary}) / 365\"\n]\n" -input_file: book/tests/prql/language-features/s-strings-6.prql --- SELECT *, @@ -9,3 +8,4 @@ SELECT (salary + benefits) / 365 AS daily_rate FROM employees + diff --git a/book/tests/snapshots/snapshot__@language-features__standard-library-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__README-0.prql.snap similarity index 86% rename from book/tests/snapshots/snapshot__@language-features__standard-library-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__README-0.prql.snap index 31e030d55e0b..ecdb8feb1703 100644 --- a/book/tests/snapshots/snapshot__@language-features__standard-library-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__README-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [\n gross_salary = (salary + payroll_tax | as int),\n gross_salary_rounded = (gross_salary | round 0),\n time = s\"NOW()\", # an s-string, given no `now` function exists in PRQL\n]\n" -input_file: book/tests/prql/language-features/standard-library-0.prql --- SELECT *, @@ -10,3 +9,4 @@ SELECT NOW() AS time FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__from_text-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-0.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@transforms__from_text-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-0.prql.snap index 59de499b2072..3e0b263aa879 100644 --- a/book/tests/snapshots/snapshot__@transforms__from_text-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-0.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "from_text \"\"\"\na,b,c\n1,2,3\n4,5,6\n\"\"\"\nderive [\n d = b + c,\n answer = 20 * 2 + 2,\n]\n" -input_file: book/tests/prql/transforms/from_text-0.prql --- -WITH table_1 AS ( +WITH table_0 AS ( SELECT '1' AS a, '2' AS b, @@ -22,5 +21,5 @@ SELECT b + c AS d, 42 AS answer FROM - table_1 AS table_0 + table_0 AS table_1 diff --git a/book/tests/snapshots/snapshot__@transforms__from_text-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-1.prql.snap similarity index 93% rename from book/tests/snapshots/snapshot__@transforms__from_text-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-1.prql.snap index 9ae070f052eb..112dba19d2e7 100644 --- a/book/tests/snapshots/snapshot__@transforms__from_text-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "let temp_format_lookup = from_text format:csv \"\"\"\ncountry_code,format\nuk,C\nus,F\nlr,F\nde,C\n\"\"\"\n\nfrom temperatures\njoin temp_format_lookup [==country_code]\n" -input_file: book/tests/prql/transforms/from_text-1.prql --- WITH table_0 AS ( SELECT diff --git a/book/tests/snapshots/snapshot__@transforms__from_text-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-2.prql.snap similarity index 93% rename from book/tests/snapshots/snapshot__@transforms__from_text-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-2.prql.snap index 085bbccf68f8..2a06e6465d95 100644 --- a/book/tests/snapshots/snapshot__@transforms__from_text-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__from-text-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "let x = from_text format:json \"\"\"{\n \"columns\": [\"a\", \"b\", \"c\"],\n \"data\": [\n [1, \"x\", false],\n [4, \"y\", null]\n ]\n}\"\"\"\n\nlet y = from_text format:json \"\"\"\n [\n {\"a\": 1, \"m\": \"5\"},\n {\"a\": 4, \"n\": \"6\"}\n ]\n\"\"\"\n\nfrom x | join y [==a]\n" -input_file: book/tests/prql/transforms/from_text-2.prql --- WITH table_0 AS ( SELECT diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__loop-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__loop-0.prql.snap new file mode 100644 index 000000000000..8f697534e2e8 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__standard-library__loop-0.prql.snap @@ -0,0 +1,33 @@ +--- +source: book/tests/snapshot.rs +expression: "from_text format:json '[{\"n\": 1 }]'\nloop (\n filter n<4\n select n = n+1\n)\n\n# returns [1, 2, 3, 4]\n" +--- +WITH table_0 AS ( + SELECT + 1 AS n +), +table_4 AS ( + WITH RECURSIVE loop AS ( + SELECT + n + FROM + table_0 AS table_1 + UNION + ALL + SELECT + n + 1 + FROM + loop AS table_2 + WHERE + n < 4 + ) + SELECT + * + FROM + loop +) +SELECT + n +FROM + table_4 AS table_3 + diff --git a/book/tests/snapshots/snapshot__@language-features__strings-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-0.prql.snap similarity index 69% rename from book/tests/snapshots/snapshot__@language-features__strings-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-0.prql.snap index 7ef0767b367b..8e27a7bae79f 100644 --- a/book/tests/snapshots/snapshot__@language-features__strings-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect x = \"hello world\"\n" -input_file: book/tests/prql/language-features/strings-0.prql --- SELECT 'hello world' AS x FROM my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__strings-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-1.prql.snap similarity index 69% rename from book/tests/snapshots/snapshot__@language-features__strings-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-1.prql.snap index dc23e42969d7..4ef9ca322260 100644 --- a/book/tests/snapshots/snapshot__@language-features__strings-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-1.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect x = 'hello world'\n" -input_file: book/tests/prql/language-features/strings-1.prql --- SELECT 'hello world' AS x FROM my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__strings-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-2.prql.snap similarity index 70% rename from book/tests/snapshots/snapshot__@language-features__strings-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-2.prql.snap index 648630dd4c11..dcb4881cfd6b 100644 --- a/book/tests/snapshots/snapshot__@language-features__strings-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-2.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect x = '\"hello world\"'\n" -input_file: book/tests/prql/language-features/strings-2.prql --- SELECT '"hello world"' AS x FROM my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__strings-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-3.prql.snap similarity index 73% rename from book/tests/snapshots/snapshot__@language-features__strings-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-3.prql.snap index 9a8356d373ce..e49aa3eecd10 100644 --- a/book/tests/snapshots/snapshot__@language-features__strings-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-3.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect x = \"\"\"I said \"hello world\"!\"\"\"\n" -input_file: book/tests/prql/language-features/strings-3.prql --- SELECT 'I said "hello world"!' AS x FROM my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__strings-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-4.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@language-features__strings-4.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-4.prql.snap index 25f04f7996c7..e6297e16c531 100644 --- a/book/tests/snapshots/snapshot__@language-features__strings-4.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-4.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from my_table\nselect x = \"\"\"\"\"I said \"\"\"hello world\"\"\"!\"\"\"\"\"\n" -input_file: book/tests/prql/language-features/strings-4.prql --- SELECT 'I said """hello world"""!' AS x FROM my_table + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-5.prql.snap new file mode 100644 index 000000000000..b28334b03be5 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__strings-5.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom my_table\nselect x = \"\\t\\tline ends here\\n \\\\ \"\n" +--- +SELECT + ' line ends here + \ ' AS x +FROM + my_table + diff --git a/book/tests/snapshots/snapshot__@language-features__target-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-0.prql.snap similarity index 60% rename from book/tests/snapshots/snapshot__@language-features__target-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__language-features__target-0.prql.snap index cab9b249fc4e..b0923a3791ed 100644 --- a/book/tests/snapshots/snapshot__@language-features__target-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-0.prql.snap @@ -1,7 +1,6 @@ --- -source: book/tests/snapshot.rs +source: web/book/tests/snapshot.rs expression: "prql target:sql.postgres\n\nfrom employees\nsort age\ntake 10\n" -input_file: book/tests/prql/language-features/target-0.prql --- SELECT * @@ -11,3 +10,4 @@ ORDER BY age LIMIT 10 + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-1.prql.snap new file mode 100644 index 000000000000..d6e4b0820e63 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-1.prql.snap @@ -0,0 +1,11 @@ +--- +source: web/book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nprql target:sql.mssql\n\nfrom employees\nsort age\ntake 10\n" +--- +SELECT + TOP (10) * +FROM + employees +ORDER BY + age + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-2.prql.snap new file mode 100644 index 000000000000..ea2581281aed --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__language-features__target-2.prql.snap @@ -0,0 +1,9 @@ +--- +source: web/book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nprql version:\"0.6.0\"\n\nfrom employees\n" +--- +SELECT + * +FROM + employees + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-0.prql.snap new file mode 100644 index 000000000000..233a443272ed --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-0.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfunc fahrenheit_to_celsius temp -> (temp - 32) / 1.8\n\nfrom cities\nderive temp_c = (fahrenheit_to_celsius temp_f)\n" +--- +SELECT + *, + (temp_f - 32) / 1.8 AS temp_c +FROM + cities + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-1.prql.snap new file mode 100644 index 000000000000..d0d37fc7950c --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-1.prql.snap @@ -0,0 +1,11 @@ +--- +source: book/tests/snapshot.rs +expression: "func interp low:0 high x -> (x - low) / (high - low)\n\nfrom students\nderive [\n sat_proportion_1 = (interp 1600 sat_score),\n sat_proportion_2 = (interp low:0 1600 sat_score),\n]\n" +--- +SELECT + *, + (sat_score - 0) / 1600 AS sat_proportion_1, + (sat_score - 0) / 1600 AS sat_proportion_2 +FROM + students + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-2.prql.snap new file mode 100644 index 000000000000..03a3eca6f2fd --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-2.prql.snap @@ -0,0 +1,11 @@ +--- +source: book/tests/snapshot.rs +expression: "func interp low:0 high x -> (x - low) / (high - low)\n\nfrom students\nderive [\n sat_proportion_1 = (sat_score | interp 1600),\n sat_proportion_2 = (sat_score | interp low:0 1600),\n]\n" +--- +SELECT + *, + (sat_score - 0) / 1600 AS sat_proportion_1, + (sat_score - 0) / 1600 AS sat_proportion_2 +FROM + students + diff --git a/book/tests/snapshots/snapshot__@queries__functions-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-3.prql.snap similarity index 81% rename from book/tests/snapshots/snapshot__@queries__functions-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__functions-3.prql.snap index 65115fa5932e..263a8b82b296 100644 --- a/book/tests/snapshots/snapshot__@queries__functions-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-3.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "func fahrenheit_to_celsius temp -> (temp - 32) / 1.8\n\nfrom cities\nderive temp_c = (temp_f | fahrenheit_to_celsius)\n" -input_file: book/tests/prql/queries/functions-3.prql --- SELECT *, (temp_f - 32) / 1.8 AS temp_c FROM cities + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-4.prql.snap new file mode 100644 index 000000000000..ac68df1425bc --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-4.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "func fahrenheit_to_celsius temp -> (temp - 32) / 1.8\nfunc interp low:0 high x -> (x - low) / (high - low)\n\nfrom kettles\nderive boiling_proportion = (temp_c | fahrenheit_to_celsius | interp 100)\n" +--- +SELECT + *, + ((temp_c - 32) / 1.8 - 0) / 100 AS boiling_proportion +FROM + kettles + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-5.prql.snap new file mode 100644 index 000000000000..05ce9c885a1c --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__functions-5.prql.snap @@ -0,0 +1,15 @@ +--- +source: book/tests/snapshot.rs +expression: "func cost_share cost -> cost / cost_total\n\nfrom costs\nselect [materials, labor, overhead, cost_total]\nderive [\n materials_share = (cost_share materials),\n labor_share = (cost_share labor),\n overhead_share = (cost_share overhead),\n]\n" +--- +SELECT + materials, + labor, + overhead, + cost_total, + materials / cost_total AS materials_share, + labor / cost_total AS labor_share, + overhead / cost_total AS overhead_share +FROM + costs + diff --git a/book/tests/snapshots/snapshot__@transforms__from-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-0.prql.snap similarity index 65% rename from book/tests/snapshots/snapshot__@transforms__from-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-0.prql.snap index efa18103e60b..ef73243f301a 100644 --- a/book/tests/snapshots/snapshot__@transforms__from-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\n" -input_file: book/tests/prql/transforms/from-0.prql --- SELECT * FROM employees + diff --git a/book/tests/snapshots/snapshot__@queries__pipelines-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-1.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@queries__pipelines-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-1.prql.snap index 6daec46c4b39..9711846bf22b 100644 --- a/book/tests/snapshots/snapshot__@queries__pipelines-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-1.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive gross_salary = (salary + payroll_tax)\n" -input_file: book/tests/prql/queries/pipelines-1.prql --- SELECT *, salary + payroll_tax AS gross_salary FROM employees + diff --git a/book/tests/snapshots/snapshot__@queries__pipelines-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-2.prql.snap similarity index 71% rename from book/tests/snapshots/snapshot__@queries__pipelines-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-2.prql.snap index 48c578cb780a..ca1e515fde69 100644 --- a/book/tests/snapshots/snapshot__@queries__pipelines-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__pipelines-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from e = employees\nderive gross_salary = (salary + payroll_tax)\nsort gross_salary\ntake 10\njoin d = department [==dept_no]\nselect [e.name, gross_salary, d.name]\n" -input_file: book/tests/prql/queries/pipelines-2.prql --- WITH table_1 AS ( SELECT @@ -16,9 +15,10 @@ WITH table_1 AS ( 10 ) SELECT - table_1.name, - table_1.gross_salary, + table_0.name, + table_0.gross_salary, d.name FROM - table_1 - JOIN department AS d ON table_1.dept_no = d.dept_no + table_1 AS table_0 + JOIN department AS d ON table_0.dept_no = d.dept_no + diff --git a/book/tests/snapshots/snapshot__@queries__variables-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__variables-0.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@queries__variables-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__variables-0.prql.snap index e37a88722453..378a0b651fca 100644 --- a/book/tests/snapshots/snapshot__@queries__variables-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__variables-0.prql.snap @@ -1,9 +1,8 @@ --- source: book/tests/snapshot.rs expression: "let top_50 = (\n from employees\n sort salary\n take 50\n aggregate [total_salary = sum salary]\n)\n\nfrom top_50 # Starts a new pipeline\n" -input_file: book/tests/prql/queries/variables-0.prql --- -WITH table_0 AS ( +WITH table_1 AS ( SELECT salary FROM @@ -16,7 +15,7 @@ WITH table_0 AS ( SELECT SUM(salary) AS total_salary FROM - table_0 + table_1 AS table_0 ) SELECT total_salary diff --git a/book/tests/snapshots/snapshot__@queries__variables-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__queries__variables-1.prql.snap similarity index 88% rename from book/tests/snapshots/snapshot__@queries__variables-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__queries__variables-1.prql.snap index 143fdae66f49..5a56be225071 100644 --- a/book/tests/snapshots/snapshot__@queries__variables-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__queries__variables-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "let grouping = s\"\"\"\n SELECT SUM(a)\n FROM tbl\n GROUP BY\n GROUPING SETS\n ((b, c, d), (d), (b, d))\n\"\"\"\n\nfrom grouping\n" -input_file: book/tests/prql/queries/variables-1.prql --- WITH table_0 AS ( SELECT diff --git a/book/tests/snapshots/snapshot__@syntax-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-0.prql.snap similarity index 83% rename from book/tests/snapshots/snapshot__@syntax-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-0.prql.snap index bc4245d27e99..ff6897edf956 100644 --- a/book/tests/snapshots/snapshot__@syntax-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter department == \"Product\"\nselect [first_name, last_name]\n" -input_file: book/tests/prql/syntax-0.prql --- SELECT first_name, @@ -10,3 +9,4 @@ FROM employees WHERE department = 'Product' + diff --git a/book/tests/snapshots/snapshot__@syntax-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-1.prql.snap similarity index 83% rename from book/tests/snapshots/snapshot__@syntax-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-1.prql.snap index 25db89b07dac..7684886350f9 100644 --- a/book/tests/snapshots/snapshot__@syntax-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees | filter department == \"Product\" | select [first_name, last_name]\n" -input_file: book/tests/prql/syntax-1.prql --- SELECT first_name, @@ -10,3 +9,4 @@ FROM employees WHERE department = 'Product' + diff --git a/book/tests/snapshots/snapshot__@syntax-9.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-10.prql.snap similarity index 78% rename from book/tests/snapshots/snapshot__@syntax-9.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-10.prql.snap index 10db59599207..142f58a54334 100644 --- a/book/tests/snapshots/snapshot__@syntax-9.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-10.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "prql target:sql.mysql\nfrom employees\nselect `first name`\n" -input_file: book/tests/prql/syntax-9.prql --- SELECT `first name` FROM employees + diff --git a/book/tests/snapshots/snapshot__@syntax-10.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-11.prql.snap similarity index 78% rename from book/tests/snapshots/snapshot__@syntax-10.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-11.prql.snap index f1b7d23890a6..cc696fab2ab0 100644 --- a/book/tests/snapshots/snapshot__@syntax-10.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-11.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "prql target:sql.postgres\nfrom employees\nselect `first name`\n" -input_file: book/tests/prql/syntax-10.prql --- SELECT "first name" FROM employees + diff --git a/book/tests/snapshots/snapshot__@syntax-11.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-12.prql.snap similarity index 71% rename from book/tests/snapshots/snapshot__@syntax-11.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-12.prql.snap index 2ce70ad17f23..afd329815111 100644 --- a/book/tests/snapshots/snapshot__@syntax-11.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-12.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from `dir/*.parquet`\n" -input_file: book/tests/prql/syntax-11.prql --- SELECT * FROM "dir/*.parquet" + diff --git a/book/tests/snapshots/snapshot__@syntax-12.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-13.prql.snap similarity index 89% rename from book/tests/snapshots/snapshot__@syntax-12.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-13.prql.snap index 89258a4e0a89..0d3be609df1c 100644 --- a/book/tests/snapshots/snapshot__@syntax-12.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-13.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "prql target:sql.bigquery\nfrom `project-foo.dataset.table`\njoin `project-bar.dataset.table` [==col_bax]\n" -input_file: book/tests/prql/syntax-12.prql --- SELECT `project-foo.dataset.table`.*, @@ -9,3 +8,4 @@ SELECT FROM `project-foo.dataset.table` JOIN `project-bar.dataset.table` ON `project-foo.dataset.table`.col_bax = `project-bar.dataset.table`.col_bax + diff --git a/book/tests/snapshots/snapshot__@syntax-13.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-14.prql.snap similarity index 71% rename from book/tests/snapshots/snapshot__@syntax-13.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-14.prql.snap index c1617c87e881..dd305dbdadf6 100644 --- a/book/tests/snapshots/snapshot__@syntax-13.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-14.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from `music.albums`\n" -input_file: book/tests/prql/syntax-13.prql --- SELECT * FROM music.albums + diff --git a/book/tests/snapshots/snapshot__@syntax-14.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-15.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@syntax-14.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-15.prql.snap index 427590aa8b77..4f7d34ea2e79 100644 --- a/book/tests/snapshots/snapshot__@syntax-14.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-15.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter id == $1\n" -input_file: book/tests/prql/syntax-14.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees WHERE id = $1 + diff --git a/book/tests/snapshots/snapshot__@syntax-15.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-16.prql.snap similarity index 81% rename from book/tests/snapshots/snapshot__@syntax-15.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-16.prql.snap index dddfae4b8812..e20d96b994cb 100644 --- a/book/tests/snapshots/snapshot__@syntax-15.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-16.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from numbers\nselect [\n small = 1.000_000_1,\n big = 5_000_000,\n]\n" -input_file: book/tests/prql/syntax-15.prql --- SELECT 1.0000001 AS small, 5000000 AS big FROM numbers + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__syntax-17.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-17.prql.snap new file mode 100644 index 000000000000..da5f149207ad --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-17.prql.snap @@ -0,0 +1,12 @@ +--- +source: book/tests/snapshot.rs +expression: "std.from my_table\nstd.select [from = my_table.a, take = my_table.b]\nstd.take 3\n" +--- +SELECT + a AS "from", + b AS take +FROM + my_table +LIMIT + 3 + diff --git a/book/tests/snapshots/snapshot__@syntax-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-2.prql.snap similarity index 85% rename from book/tests/snapshots/snapshot__@syntax-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-2.prql.snap index c30a9bccb908..034438f6196e 100644 --- a/book/tests/snapshots/snapshot__@syntax-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from numbers\nderive [x = 1, y = 2]\nderive [\n a = x,\n b = y\n]\nderive [\n c = a,\n d = b,\n]\n" -input_file: book/tests/prql/syntax-2.prql --- SELECT *, @@ -13,3 +12,4 @@ SELECT 2 AS d FROM numbers + diff --git a/book/tests/snapshots/snapshot__@syntax-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-3.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@syntax-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-3.prql.snap index 9927316e1bc0..b24a1679b7c9 100644 --- a/book/tests/snapshots/snapshot__@syntax-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-3.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect [first_name]\n" -input_file: book/tests/prql/syntax-3.prql --- SELECT first_name FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__select-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-4.prql.snap similarity index 70% rename from book/tests/snapshots/snapshot__@transforms__select-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-4.prql.snap index e2ca00c69505..9621e1555906 100644 --- a/book/tests/snapshots/snapshot__@transforms__select-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-4.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect first_name\n" -input_file: book/tests/prql/transforms/select-2.prql --- SELECT first_name FROM employees + diff --git a/book/tests/snapshots/snapshot__@syntax-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-5.prql.snap similarity index 87% rename from book/tests/snapshots/snapshot__@syntax-5.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-5.prql.snap index 374cbd663cf3..7fe2bb668def 100644 --- a/book/tests/snapshots/snapshot__@syntax-5.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-5.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from foo\nselect [\n circumference = diameter * 3.14159,\n color,\n]\nfilter circumference > 10 and color != \"red\"\n" -input_file: book/tests/prql/syntax-5.prql --- WITH table_1 AS ( SELECT @@ -14,7 +13,8 @@ SELECT circumference, color FROM - table_1 + table_1 AS table_0 WHERE circumference > 10 AND color <> 'red' + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__syntax-6.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-6.prql.snap new file mode 100644 index 000000000000..56a6f70b79a0 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-6.prql.snap @@ -0,0 +1,20 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom employees\n# Requires parentheses, because it's contains a pipe\nderive is_proximate = (distance | in 0..20)\n# Requires parentheses, because it's a function call\nderive total_distance = (sum distance)\n# `??` doesn't require parentheses, as it's not a function call\nderive min_capped_distance = (min distance ?? 5)\n# No parentheses needed, because no function call\nderive travel_time = distance / 40\n# No inner parentheses needed around `1+1` because no function call\nderive distance_rounded_2_dp = (round 1+1 distance)\nderive [\n # Requires parentheses, because it contains a pipe\n is_far = (distance | in 100..),\n # The left value of the range requires parentheses,\n # because of the minus sign\n is_negative = (distance | in (-100..0)),\n # ...this is equivalent\n is_negative = (distance | in (-100)..0),\n # Doesn't require parentheses, because it's in a list (confusing, see footnote)!\n average_distance = average distance,\n]\n# Requires parentheses because of the minus sign\nsort (-distance)\n# A list is fine too\nsort [-distance]\n" +--- +SELECT + *, + distance BETWEEN 0 AND 20 AS is_proximate, + SUM(distance) OVER () AS total_distance, + MIN(COALESCE(distance, 5)) OVER () AS min_capped_distance, + distance / 40 AS travel_time, + ROUND(distance, 2) AS distance_rounded_2_dp, + distance >= 100 AS is_far, + distance BETWEEN -100 AND 0, + distance BETWEEN -100 AND 0 AS is_negative, + AVG(distance) OVER () AS average_distance +FROM + employees +ORDER BY + distance DESC + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__syntax-7.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-7.prql.snap new file mode 100644 index 000000000000..d796d80ca8d8 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-7.prql.snap @@ -0,0 +1,12 @@ +--- +source: book/tests/snapshot.rs +expression: "# Error expected\n\nfrom employees\nderive total_distance = sum distance\n" +--- +Error: + โ•ญโ”€[:4:29] + โ”‚ + 4 โ”‚ derive total_distance = sum distance + ยท โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€ + ยท โ•ฐโ”€โ”€โ”€โ”€โ”€ Unknown name distance +โ”€โ”€โ”€โ•ฏ + diff --git a/book/tests/snapshots/snapshot__@syntax-7.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-8.prql.snap similarity index 86% rename from book/tests/snapshots/snapshot__@syntax-7.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-8.prql.snap index ef01d0c90ef5..eda9f5af0327 100644 --- a/book/tests/snapshots/snapshot__@syntax-7.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-8.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup [title, country] (\n aggregate [\n average salary,\n ct = count\n ]\n)\n" -input_file: book/tests/prql/syntax-7.prql --- SELECT title, @@ -13,3 +12,4 @@ FROM GROUP BY title, country + diff --git a/book/tests/snapshots/snapshot__@syntax-8.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__syntax-9.prql.snap similarity index 79% rename from book/tests/snapshots/snapshot__@syntax-8.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__syntax-9.prql.snap index ea09601c7034..86e0b07ded9c 100644 --- a/book/tests/snapshots/snapshot__@syntax-8.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__syntax-9.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees # Comment 1\n# Comment 2\naggregate [average salary]\n" -input_file: book/tests/prql/syntax-8.prql --- SELECT AVG(salary) FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__aggregate-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-0.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@transforms__aggregate-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-0.prql.snap index 3e9550e9528f..f8d6249f2964 100644 --- a/book/tests/snapshots/snapshot__@transforms__aggregate-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-0.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\naggregate [\n average salary,\n ct = count\n]\n" -input_file: book/tests/prql/transforms/aggregate-0.prql --- SELECT AVG(salary), COUNT(*) AS ct FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__group-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-1.prql.snap similarity index 83% rename from book/tests/snapshots/snapshot__@transforms__group-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-1.prql.snap index 1ce5fa06bce4..eda9f5af0327 100644 --- a/book/tests/snapshots/snapshot__@transforms__group-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup [title, country] (\n aggregate [\n average salary,\n ct = count\n ]\n)\n" -input_file: book/tests/prql/transforms/group-0.prql --- SELECT title, @@ -13,3 +12,4 @@ FROM GROUP BY title, country + diff --git a/book/tests/snapshots/snapshot__@transforms__aggregate-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-2.prql.snap similarity index 74% rename from book/tests/snapshots/snapshot__@transforms__aggregate-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-2.prql.snap index 9cd168662d59..1be9f1cc15df 100644 --- a/book/tests/snapshots/snapshot__@transforms__aggregate-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__aggregate-2.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [avg_sal = average salary]\n" -input_file: book/tests/prql/transforms/aggregate-2.prql --- SELECT *, AVG(salary) OVER () AS avg_sal FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__append-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-0.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@transforms__append-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__append-0.prql.snap index e082d762a2a6..0c40c37cfd24 100644 --- a/book/tests/snapshots/snapshot__@transforms__append-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees_1\nappend employees_2\n" -input_file: book/tests/prql/transforms/append-0.prql --- SELECT * @@ -13,3 +12,4 @@ SELECT * FROM employees_2 + diff --git a/book/tests/snapshots/snapshot__@transforms__append-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-1.prql.snap similarity index 76% rename from book/tests/snapshots/snapshot__@transforms__append-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__append-1.prql.snap index 45a979b98908..06806c9b0b78 100644 --- a/book/tests/snapshots/snapshot__@transforms__append-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees_1\nremove employees_2\n" -input_file: book/tests/prql/transforms/append-1.prql --- SELECT * @@ -13,3 +12,4 @@ SELECT * FROM employees_2 AS b + diff --git a/book/tests/snapshots/snapshot__@transforms__append-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-2.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@transforms__append-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__append-2.prql.snap index f0fe1a1555f9..9b8906ab950f 100644 --- a/book/tests/snapshots/snapshot__@transforms__append-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__append-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees_1\nintersect employees_2\n" -input_file: book/tests/prql/transforms/append-2.prql --- SELECT * @@ -13,3 +12,4 @@ SELECT * FROM employees_2 AS b + diff --git a/book/tests/snapshots/snapshot__@transforms__derive-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-0.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@transforms__derive-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-0.prql.snap index 452801423ae4..7d9fe604e6e7 100644 --- a/book/tests/snapshots/snapshot__@transforms__derive-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-0.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive gross_salary = salary + payroll_tax\n" -input_file: book/tests/prql/transforms/derive-0.prql --- SELECT *, salary + payroll_tax AS gross_salary FROM employees + diff --git a/book/tests/snapshots/snapshot__@examples__list-equivalence-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-1.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@examples__list-equivalence-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-1.prql.snap index 5662e8a95ef6..b99c1d44b18a 100644 --- a/book/tests/snapshots/snapshot__@examples__list-equivalence-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__derive-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nderive [\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost\n]\n" -input_file: book/tests/prql/examples/list-equivalence-2.prql --- SELECT *, @@ -9,3 +8,4 @@ SELECT salary + payroll_tax + benefits_cost AS gross_cost FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__filter-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-0.prql.snap similarity index 71% rename from book/tests/snapshots/snapshot__@transforms__filter-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-0.prql.snap index be2a38821ec6..5d75ef6cf24c 100644 --- a/book/tests/snapshots/snapshot__@transforms__filter-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter age > 25\n" -input_file: book/tests/prql/transforms/filter-0.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees WHERE age > 25 + diff --git a/book/tests/snapshots/snapshot__@transforms__filter-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-1.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@transforms__filter-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-1.prql.snap index 86f72be77cd1..6696a54facce 100644 --- a/book/tests/snapshots/snapshot__@transforms__filter-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter (age > 25 or department != \"IT\")\n" -input_file: book/tests/prql/transforms/filter-1.prql --- SELECT * @@ -10,3 +9,4 @@ FROM WHERE age > 25 OR department <> 'IT' + diff --git a/book/tests/snapshots/snapshot__@transforms__filter-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-2.prql.snap similarity index 74% rename from book/tests/snapshots/snapshot__@transforms__filter-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-2.prql.snap index c5dda610f1bc..f90f4c01e310 100644 --- a/book/tests/snapshots/snapshot__@transforms__filter-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__filter-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter (age | in 25..40)\n" -input_file: book/tests/prql/transforms/filter-2.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees WHERE age BETWEEN 25 AND 40 + diff --git a/book/tests/snapshots/snapshot__@queries__pipelines-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__from-0.prql.snap similarity index 64% rename from book/tests/snapshots/snapshot__@queries__pipelines-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__from-0.prql.snap index 0d1764763376..ef73243f301a 100644 --- a/book/tests/snapshots/snapshot__@queries__pipelines-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__from-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\n" -input_file: book/tests/prql/queries/pipelines-0.prql --- SELECT * FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__from-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__from-1.prql.snap similarity index 72% rename from book/tests/snapshots/snapshot__@transforms__from-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__from-1.prql.snap index f865a3f558d7..31af40868d65 100644 --- a/book/tests/snapshots/snapshot__@transforms__from-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__from-1.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from e = employees\nselect e.first_name\n" -input_file: book/tests/prql/transforms/from-1.prql --- SELECT first_name FROM employees AS e + diff --git a/book/tests/snapshots/snapshot__@transforms__aggregate-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-0.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@transforms__aggregate-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__group-0.prql.snap index b0ed3d496abb..eda9f5af0327 100644 --- a/book/tests/snapshots/snapshot__@transforms__aggregate-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup [title, country] (\n aggregate [\n average salary,\n ct = count\n ]\n)\n" -input_file: book/tests/prql/transforms/aggregate-1.prql --- SELECT title, @@ -13,3 +12,4 @@ FROM GROUP BY title, country + diff --git a/book/tests/snapshots/snapshot__@transforms__group-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-1.prql.snap similarity index 74% rename from book/tests/snapshots/snapshot__@transforms__group-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__group-1.prql.snap index 4e6ea09db0db..325ee4f4b76e 100644 --- a/book/tests/snapshots/snapshot__@transforms__group-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort join_date\ntake 1\n" -input_file: book/tests/prql/transforms/group-1.prql --- SELECT * @@ -11,3 +10,4 @@ ORDER BY join_date LIMIT 1 + diff --git a/book/tests/snapshots/snapshot__@transforms__group-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-2.prql.snap similarity index 84% rename from book/tests/snapshots/snapshot__@transforms__group-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__group-2.prql.snap index a11503ab56c8..a25e11fedceb 100644 --- a/book/tests/snapshots/snapshot__@transforms__group-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__group-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup role (\n sort join_date # taken from above\n take 1\n)\n" -input_file: book/tests/prql/transforms/group-2.prql --- WITH table_1 AS ( SELECT @@ -17,6 +16,7 @@ WITH table_1 AS ( SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 <= 1 + diff --git a/book/tests/snapshots/snapshot__@transforms__join-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-0.prql.snap similarity index 83% rename from book/tests/snapshots/snapshot__@transforms__join-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__join-0.prql.snap index b5e14abc2d70..455f56a1eb72 100644 --- a/book/tests/snapshots/snapshot__@transforms__join-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\njoin side:left positions [employees.id==positions.employee_id]\n" -input_file: book/tests/prql/transforms/join-0.prql --- SELECT employees.*, @@ -9,3 +8,4 @@ SELECT FROM employees LEFT JOIN positions ON employees.id = positions.employee_id + diff --git a/book/tests/snapshots/snapshot__@transforms__join-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-1.prql.snap similarity index 81% rename from book/tests/snapshots/snapshot__@transforms__join-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__join-1.prql.snap index 4415ba7cafcb..b4c86487a35d 100644 --- a/book/tests/snapshots/snapshot__@transforms__join-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\njoin side:left p=positions [employees.id==p.employee_id]\n" -input_file: book/tests/prql/transforms/join-1.prql --- SELECT employees.*, @@ -9,3 +8,4 @@ SELECT FROM employees LEFT JOIN positions AS p ON employees.id = p.employee_id + diff --git a/book/tests/snapshots/snapshot__@transforms__join-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-2.prql.snap similarity index 80% rename from book/tests/snapshots/snapshot__@transforms__join-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__join-2.prql.snap index 8a9c3bf8c180..fe00d3aab1cf 100644 --- a/book/tests/snapshots/snapshot__@transforms__join-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__join-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\njoin positions [==emp_no]\n" -input_file: book/tests/prql/transforms/join-2.prql --- SELECT employees.*, @@ -9,3 +8,4 @@ SELECT FROM employees JOIN positions ON employees.emp_no = positions.emp_no + diff --git a/book/tests/snapshots/snapshot__@transforms__select-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-0.prql.snap similarity index 77% rename from book/tests/snapshots/snapshot__@transforms__select-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__select-0.prql.snap index 1247bb79dcbc..480e714b0ffe 100644 --- a/book/tests/snapshots/snapshot__@transforms__select-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-0.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect name = f\"{first_name} {last_name}\"\n" -input_file: book/tests/prql/transforms/select-0.prql --- SELECT CONCAT(first_name, ' ', last_name) AS name FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__select-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-1.prql.snap similarity index 83% rename from book/tests/snapshots/snapshot__@transforms__select-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__select-1.prql.snap index ba5a1e89bb47..69d637694b3f 100644 --- a/book/tests/snapshots/snapshot__@transforms__select-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-1.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect [\n name = f\"{first_name} {last_name}\",\n age_eoy = dob - @2022-12-31,\n]\n" -input_file: book/tests/prql/transforms/select-1.prql --- SELECT CONCAT(first_name, ' ', last_name) AS name, dob - DATE '2022-12-31' AS age_eoy FROM employees + diff --git a/book/tests/snapshots/snapshot__@internals__name-resolving-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-2.prql.snap similarity index 67% rename from book/tests/snapshots/snapshot__@internals__name-resolving-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__select-2.prql.snap index 8343f6242c89..9621e1555906 100644 --- a/book/tests/snapshots/snapshot__@internals__name-resolving-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-2.prql.snap @@ -1,9 +1,9 @@ --- source: book/tests/snapshot.rs expression: "from employees\nselect first_name\n" -input_file: book/tests/prql/internals/name-resolving-0.prql --- SELECT first_name FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__select-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-3.prql.snap similarity index 75% rename from book/tests/snapshots/snapshot__@transforms__select-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__select-3.prql.snap index d016d5f07e32..659d581346a5 100644 --- a/book/tests/snapshots/snapshot__@transforms__select-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-3.prql.snap @@ -1,10 +1,10 @@ --- source: book/tests/snapshot.rs expression: "from e=employees\nselect [e.first_name, e.last_name]\n" -input_file: book/tests/prql/transforms/select-3.prql --- SELECT first_name, last_name FROM employees AS e + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-4.prql.snap new file mode 100644 index 000000000000..31ea529b998a --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-4.prql.snap @@ -0,0 +1,11 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nprql target:sql.bigquery\nfrom tracks\nselect ![milliseconds,bytes]\n" +--- +SELECT + * +EXCEPT + (milliseconds, bytes) +FROM + tracks + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-5.prql.snap new file mode 100644 index 000000000000..019026c050fa --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-5.prql.snap @@ -0,0 +1,10 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom tracks\nselect [track_id, title, composer, bytes]\nselect ![title, composer]\n" +--- +SELECT + track_id, + bytes +FROM + tracks + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-6.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-6.prql.snap new file mode 100644 index 000000000000..2640271d11dc --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__select-6.prql.snap @@ -0,0 +1,9 @@ +--- +source: book/tests/snapshot.rs +expression: "# Can't yet format & compile\n\nfrom artists\nderive nick = name\nselect ![artists.*]\n" +--- +SELECT + name AS nick +FROM + artists + diff --git a/book/tests/snapshots/snapshot__@transforms__sort-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-0.prql.snap similarity index 70% rename from book/tests/snapshots/snapshot__@transforms__sort-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-0.prql.snap index fd9d4e99f2e9..25f7e08c2d90 100644 --- a/book/tests/snapshots/snapshot__@transforms__sort-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort age\n" -input_file: book/tests/prql/transforms/sort-0.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees ORDER BY age + diff --git a/book/tests/snapshots/snapshot__@transforms__sort-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-1.prql.snap similarity index 71% rename from book/tests/snapshots/snapshot__@transforms__sort-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-1.prql.snap index 25afb720d44e..084bfc47f8f6 100644 --- a/book/tests/snapshots/snapshot__@transforms__sort-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort [-age]\n" -input_file: book/tests/prql/transforms/sort-1.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees ORDER BY age DESC + diff --git a/book/tests/snapshots/snapshot__@transforms__sort-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-2.prql.snap similarity index 76% rename from book/tests/snapshots/snapshot__@transforms__sort-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-2.prql.snap index 11975c45e9d6..b22c7b0de187 100644 --- a/book/tests/snapshots/snapshot__@transforms__sort-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort [age, -tenure, +salary]\n" -input_file: book/tests/prql/transforms/sort-2.prql --- SELECT * @@ -11,3 +10,4 @@ ORDER BY age, tenure DESC, salary + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-3.prql.snap new file mode 100644 index 000000000000..d1b930a965a2 --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-3.prql.snap @@ -0,0 +1,12 @@ +--- +source: book/tests/snapshot.rs +expression: "from employees\nsort [s\"substr({first_name}, 2, 5)\"]\n" +--- +SELECT + *, + substr(first_name, 2, 5) AS _expr_0 +FROM + employees +ORDER BY + _expr_0 + diff --git a/book/tests/snapshots/snapshot__@transforms__sort-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-4.prql.snap similarity index 81% rename from book/tests/snapshots/snapshot__@transforms__sort-4.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-4.prql.snap index 63bf1bd5a668..802b2a250f26 100644 --- a/book/tests/snapshots/snapshot__@transforms__sort-4.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-4.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort tenure\nderive name = f\"{first_name} {last_name}\"\n" -input_file: book/tests/prql/transforms/sort-4.prql --- SELECT *, @@ -10,3 +9,4 @@ FROM employees ORDER BY tenure + diff --git a/book/tests/snapshots/snapshot__@transforms__sort-5.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-5.prql.snap similarity index 61% rename from book/tests/snapshots/snapshot__@transforms__sort-5.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-5.prql.snap index de8d52ca5f42..b01e3c7e4a87 100644 --- a/book/tests/snapshots/snapshot__@transforms__sort-5.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__sort-5.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort tenure\njoin locations [==employee_id]\n" -input_file: book/tests/prql/transforms/sort-5.prql --- WITH table_1 AS ( SELECT @@ -12,8 +11,9 @@ WITH table_1 AS ( tenure ) SELECT - table_1.*, + table_0.*, locations.* FROM - table_1 - JOIN locations ON table_1.employee_id = locations.employee_id + table_1 AS table_0 + JOIN locations ON table_0.employee_id = locations.employee_id + diff --git a/book/tests/snapshots/snapshot__@transforms__take-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__take-0.prql.snap similarity index 69% rename from book/tests/snapshots/snapshot__@transforms__take-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__take-0.prql.snap index 8f2ff2547ca5..01ea3a5179e1 100644 --- a/book/tests/snapshots/snapshot__@transforms__take-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__take-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ntake 10\n" -input_file: book/tests/prql/transforms/take-0.prql --- SELECT * @@ -9,3 +8,4 @@ FROM employees LIMIT 10 + diff --git a/web/book/tests/snapshots/snapshot__tests__prql__transforms__take-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__take-1.prql.snap new file mode 100644 index 000000000000..3d0c33aa0b0e --- /dev/null +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__take-1.prql.snap @@ -0,0 +1,14 @@ +--- +source: book/tests/snapshot.rs +expression: "from orders\nsort [-value, created_at]\ntake 101..110\n" +--- +SELECT + * +FROM + orders +ORDER BY + value DESC, + created_at +LIMIT + 10 OFFSET 100 + diff --git a/book/tests/snapshots/snapshot__@transforms__window-0.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-0.prql.snap similarity index 86% rename from book/tests/snapshots/snapshot__@transforms__window-0.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__window-0.prql.snap index ce88b096a0dd..8e596631305e 100644 --- a/book/tests/snapshots/snapshot__@transforms__window-0.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-0.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup employee_id (\n sort month\n window rolling:12 (\n derive [trail_12_m_comp = sum paycheck]\n )\n)\n" -input_file: book/tests/prql/transforms/window-0.prql --- SELECT *, @@ -12,3 +11,4 @@ SELECT ) AS trail_12_m_comp FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__window-1.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-1.prql.snap similarity index 91% rename from book/tests/snapshots/snapshot__@transforms__window-1.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__window-1.prql.snap index c89b2c172602..2d48f3d23922 100644 --- a/book/tests/snapshots/snapshot__@transforms__window-1.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-1.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from orders\nsort day\nwindow rows:-3..3 (\n derive [centered_weekly_average = average value]\n)\ngroup [order_month] (\n sort day\n window expanding:true (\n derive [monthly_running_total = sum value]\n )\n)\n" -input_file: book/tests/prql/transforms/window-1.prql --- SELECT *, @@ -16,3 +15,4 @@ SELECT ) AS monthly_running_total FROM orders + diff --git a/book/tests/snapshots/snapshot__@transforms__window-2.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-2.prql.snap similarity index 82% rename from book/tests/snapshots/snapshot__@transforms__window-2.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__window-2.prql.snap index b99ff3b65568..a4a8a7c5c0c3 100644 --- a/book/tests/snapshots/snapshot__@transforms__window-2.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-2.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nsort age\nderive rnk = rank\n" -input_file: book/tests/prql/transforms/window-2.prql --- SELECT *, @@ -13,3 +12,4 @@ FROM employees ORDER BY age + diff --git a/book/tests/snapshots/snapshot__@transforms__window-3.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-3.prql.snap similarity index 84% rename from book/tests/snapshots/snapshot__@transforms__window-3.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__window-3.prql.snap index 4e5e3cc14c09..b48b206027fc 100644 --- a/book/tests/snapshots/snapshot__@transforms__window-3.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-3.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\ngroup department (\n sort age\n derive rnk = rank\n)\n" -input_file: book/tests/prql/transforms/window-3.prql --- SELECT *, @@ -12,3 +11,4 @@ SELECT ) AS rnk FROM employees + diff --git a/book/tests/snapshots/snapshot__@transforms__window-4.prql.snap b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-4.prql.snap similarity index 79% rename from book/tests/snapshots/snapshot__@transforms__window-4.prql.snap rename to web/book/tests/snapshots/snapshot__tests__prql__transforms__window-4.prql.snap index 13bb14a01bc7..f1c96b857e54 100644 --- a/book/tests/snapshots/snapshot__@transforms__window-4.prql.snap +++ b/web/book/tests/snapshots/snapshot__tests__prql__transforms__window-4.prql.snap @@ -1,7 +1,6 @@ --- source: book/tests/snapshot.rs expression: "from employees\nfilter salary < (average salary)\n" -input_file: book/tests/prql/transforms/window-4.prql --- WITH table_1 AS ( SELECT @@ -13,6 +12,7 @@ WITH table_1 AS ( SELECT * FROM - table_1 + table_1 AS table_0 WHERE salary < _expr_0 + diff --git a/web/book/theme/highlight.js b/web/book/theme/highlight.js new file mode 100644 index 000000000000..7f5ea2af40b3 --- /dev/null +++ b/web/book/theme/highlight.js @@ -0,0 +1 @@ +var hljs=function(){"use strict";var e={exports:{}};function n(e){return e instanceof Map?e.clear=e.delete=e.set=()=>{throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{throw Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{var a=e[t];"object"!=typeof a||Object.isFrozen(a)||n(a)}),e}e.exports=n,e.exports.default=n;var t=e.exports;class a{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function i(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n];return n.forEach(e=>{for(const n in e)t[n]=e[n]}),t}const s=e=>!!e.kind;class o{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=i(e)}openNode(e){if(!s(e))return;let n=e.kind;n=e.sublanguage?"language-"+n:((e,{prefix:n})=>{if(e.includes(".")){const t=e.split(".");return[`${n}${t.shift()}`,...t.map((e,n)=>`${e}${"_".repeat(n+1)}`)].join(" ")}return`${n}${e}`})(n,{prefix:this.classPrefix}),this.span(n)}closeNode(e){s(e)&&(this.buffer+="
")}value(){return this.buffer}span(e){this.buffer+=``}}class l{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{l._collapse(e)}))}}class c extends l{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new o(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}function g(e){return m("(?=",e,")")}function u(e){return m("(?:",e,")*")}function b(e){return m("(?:",e,")?")}function m(...e){return e.map(e=>d(e)).join("")}function p(...e){const n=(e=>{const n=e[e.length-1];return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}})(e);return"("+(n.capture?"":"?:")+e.map(e=>d(e)).join("|")+")"}function _(e){return RegExp(e.toString()+"|").exec("").length-1}const h=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function f(e,{joinWith:n}){let t=0;return e.map(e=>{t+=1;const n=t;let a=d(e),i="";for(;a.length>0;){const e=h.exec(a);if(!e){i+=a;break}i+=a.substring(0,e.index),a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0],"("===e[0]&&t++)}return i}).map(e=>`(${e})`).join(n)}const E="[a-zA-Z]\\w*",y="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",k={begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[k]},x={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[k]},M=(e,n,t={})=>{const a=r({scope:"comment",begin:e,end:n,contains:[]},t);a.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const i=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return a.contains.push({begin:m(/[ ]+/,"(",i,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),a},S=M("//","$"),A=M("/\\*","\\*/"),C=M("#","$");var T=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:E,UNDERSCORE_IDENT_RE:y,NUMBER_RE:w,C_NUMBER_RE:N,BINARY_NUMBER_RE:v,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=m(n,/.*\b/,e.binary,/\b.*/)),r({scope:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:k,APOS_STRING_MODE:O,QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:{begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},COMMENT:M,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:A,HASH_COMMENT_MODE:C,NUMBER_MODE:{scope:"number",begin:w,relevance:0},C_NUMBER_MODE:{scope:"number",begin:N,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:v,relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[k,{begin:/\[/,end:/\]/,relevance:0,contains:[k]}]}]},TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_TITLE_MODE:{scope:"title",begin:y,relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})});function R(e,n){"."===e.input[e.index-1]&&n.ignoreMatch()}function D(e,n){void 0!==e.className&&(e.scope=e.className,delete e.className)}function I(e,n){n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=R,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function L(e,n){Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function B(e,n){if(e.match){if(e.begin||e.end)throw Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function $(e,n){void 0===e.relevance&&(e.relevance=1)}const z=(e,n)=>{if(!e.beforeMatch)return;if(e.starts)throw Error("beforeMatch cannot be used with starts");const t=Object.assign({},e);Object.keys(e).forEach(n=>{delete e[n]}),e.keywords=t.keywords,e.begin=m(t.beforeMatch,g(t.begin)),e.starts={relevance:0,contains:[Object.assign(t,{endsParent:!0})]},e.relevance=0,delete t.beforeMatch},F=["of","and","for","in","not","or","if","then","parent","list","value"];function U(e,n,t="keyword"){const a=Object.create(null);return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach(t=>{Object.assign(a,U(e[t],n,t))}),a;function i(e,t){n&&(t=t.map(e=>e.toLowerCase())),t.forEach(n=>{const t=n.split("|");a[t[0]]=[e,j(t[0],t[1])]})}}function j(e,n){return n?Number(n):(e=>F.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0)},Z=Error();function G(e,n,{key:t}){let a=0;const i=e[t],r={},s={};for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=_(n[e-1]);e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope}),(e=>{if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),Z;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"),Z;G(e,e.begin,{key:"beginScope"}),e.begin=f(e.begin,{joinWith:""})}})(e),(e=>{if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"),Z;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"),Z;G(e,e.end,{key:"endScope"}),e.end=f(e.end,{joinWith:""})}})(e)}function Q(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=_(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(f(e,{joinWith:"|"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),a=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,a)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;let t=n.exec(e);if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)}return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&this.considerAll()),t}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=r(e.classNameAliases||{}),function t(i,s){const o=i;if(i.isCompiled)return o;[D,B,W,z].forEach(e=>e(i,s)),e.compilerExtensions.forEach(e=>e(i,s)),i.__beforeBegin=null,[I,L,$].forEach(e=>e(i,s)),i.isCompiled=!0;let l=null;return"object"==typeof i.keywords&&i.keywords.$pattern&&(i.keywords=Object.assign({},i.keywords),l=i.keywords.$pattern,delete i.keywords.$pattern),l=l||/\w+/,i.keywords&&(i.keywords=U(i.keywords,e.case_insensitive)),o.keywordPatternRe=n(l,!0),s&&(i.begin||(i.begin=/\B|\b/),o.beginRe=n(o.begin),i.end||i.endsWithParent||(i.end=/\B|\b/),i.end&&(o.endRe=n(o.end)),o.terminatorEnd=d(o.end)||"",i.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(i.end?"|":"")+s.terminatorEnd)),i.illegal&&(o.illegalRe=n(i.illegal)),i.contains||(i.contains=[]),i.contains=[].concat(...i.contains.map(e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(n=>r(e,{variants:null},n))),e.cachedVariants?e.cachedVariants:X(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e))("self"===e?i:e))),i.contains.forEach(e=>{t(e,o)}),i.starts&&t(i.starts,s),o.matcher=(e=>{const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}}const J=i,Y=r,ee=Symbol("nomatch");var ne=(e=>{const n=Object.create(null),i=Object.create(null),r=[];let s=!0;const o="Could not find the language '{}', did you forget to load/include a language module?",l={disableAutodetect:!0,name:"Plain text",contains:[]};let d={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:c};function _(e){return d.noHighlightRe.test(e)}function h(e,n,t){let a="",i="";"object"==typeof n?(a=e,t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."),q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r);const s=r.result?r.result:f(r.language,r.code,t);return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){const l=Object.create(null);function c(){if(!O.keywords)return void M.addText(S);let e=0;O.keywordPatternRe.lastIndex=0;let n=O.keywordPatternRe.exec(S),t="";for(;n;){t+=S.substring(e,n.index);const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,O.keywords[a]);if(r){const[e,a]=r;if(M.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(A+=a),e.startsWith("_"))t+=n[0];else{const t=w.classNameAliases[e]||e;M.addKeyword(n[0],t)}}else t+=n[0];e=O.keywordPatternRe.lastIndex,n=O.keywordPatternRe.exec(S)}var a;t+=S.substr(e),M.addText(t)}function g(){null!=O.subLanguage?(()=>{if(""===S)return;let e=null;if("string"==typeof O.subLanguage){if(!n[O.subLanguage])return void M.addText(S);e=f(O.subLanguage,S,!0,x[O.subLanguage]),x[O.subLanguage]=e._top}else e=E(S,O.subLanguage.length?O.subLanguage:null);O.relevance>0&&(A+=e.relevance),M.addSublanguage(e._emitter,e.language)})():c(),S=""}function u(e,n){let t=1;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue}const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?M.addKeyword(i,a):(S=i,c(),S=""),t++}}function b(e,n){return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(M.addKeyword(S,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),S=""):e.beginScope._multi&&(u(e.beginScope,n),S="")),O=Object.create(e,{parent:{value:O}}),O}function m(e,n,t){let i=((e,n)=>{const t=e&&e.exec(n);return t&&0===t.index})(e.endRe,t);if(i){if(e["on:end"]){const t=new a(e);e["on:end"](n,t),t.isMatchIgnored&&(i=!1)}if(i){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return m(e.parent,n,t)}function p(e){return 0===O.matcher.regexIndex?(S+=e[0],1):(R=!0,0)}function _(e){const n=e[0],a=t.substr(e.index),i=m(O,e,a);if(!i)return ee;const r=O;O.endScope&&O.endScope._wrap?(g(),M.addKeyword(n,O.endScope._wrap)):O.endScope&&O.endScope._multi?(g(),u(O.endScope,e)):r.skip?S+=n:(r.returnEnd||r.excludeEnd||(S+=n),g(),r.excludeEnd&&(S=n));do{O.scope&&M.closeNode(),O.skip||O.subLanguage||(A+=O.relevance),O=O.parent}while(O!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length}let h={};function y(n,r){const o=r&&r[0];if(S+=n,null==o)return g(),0;if("begin"===h.type&&"end"===r.type&&h.index===r.index&&""===o){if(S+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`);throw n.languageName=e,n.badRule=h.rule,n}return 1}if(h=r,"begin"===r.type)return(e=>{const n=e[0],t=e.rule,i=new a(t),r=[t.__beforeBegin,t["on:begin"]];for(const t of r)if(t&&(t(e,i),i.isMatchIgnored))return p(n);return t.skip?S+=n:(t.excludeBegin&&(S+=n),g(),t.returnBegin||t.excludeBegin||(S=n)),b(t,e),t.returnBegin?0:n.length})(r);if("illegal"===r.type&&!i){const e=Error('Illegal lexeme "'+o+'" for mode "'+(O.scope||"")+'"');throw e.mode=O,e}if("end"===r.type){const e=_(r);if(e!==ee)return e}if("illegal"===r.type&&""===o)return 1;if(T>1e5&&T>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return S+=o,o.length}const w=v(e);if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"');const N=Q(w);let k="",O=r||N;const x={},M=new d.__emitter(d);(()=>{const e=[];for(let n=O;n!==w;n=n.parent)n.scope&&e.unshift(n.scope);e.forEach(e=>M.openNode(e))})();let S="",A=0,C=0,T=0,R=!1;try{for(O.matcher.considerAll();;){T++,R?R=!1:O.matcher.considerAll(),O.matcher.lastIndex=C;const e=O.matcher.exec(t);if(!e)break;const n=y(t.substring(C,e.index),e);C=e.index+n}return y(t.substr(C)),M.closeAllNodes(),M.finalize(),k=M.toHTML(),{language:e,value:k,relevance:A,illegal:!1,_emitter:M,_top:O}}catch(n){if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t),illegal:!0,relevance:0,_illegalBy:{message:n.message,index:C,context:t.slice(C-100,C+100),mode:n.mode,resultSoFar:k},_emitter:M};if(s)return{language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:M,_top:O};throw n}}function E(e,t){t=t||d.languages||Object.keys(n);const a=(e=>{const n={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new d.__emitter(d)};return n._emitter.addText(e),n})(e),i=t.filter(v).filter(O).map(n=>f(n,e,!1));i.unshift(a);const r=i.sort((e,n)=>{if(e.relevance!==n.relevance)return n.relevance-e.relevance;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1;if(v(n.language).supersetOf===e.language)return-1}return 0}),[s,o]=r,c=s;return c.secondBest=o,c}function y(e){let n=null;const t=(e=>{let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=d.languageDetectRe.exec(n);if(t){const n=v(t[1]);return n||(H(o.replace("{}",t[1])),H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>_(e)||v(e))})(e);if(_(t))return;if(x("before:highlightElement",{el:e,language:t}),e.children.length>0&&(d.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(e)),d.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML);n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a);e.innerHTML=r.value,((e,n,t)=>{const a=n&&i[n]||t;e.classList.add("hljs"),e.classList.add("language-"+a)})(e,t,r.language),e.result={language:r.language,re:r.relevance,relevance:r.relevance},r.secondBest&&(e.secondBest={language:r.secondBest.language,relevance:r.secondBest.relevance}),x("after:highlightElement",{el:e,result:r,text:a})}let w=!1;function N(){"loading"!==document.readyState?document.querySelectorAll(d.cssSelector).forEach(y):w=!0}function v(e){return e=(e||"").toLowerCase(),n[e]||n[i[e]]}function k(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{i[e.toLowerCase()]=n})}function O(e){const n=v(e);return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach(e=>{e[t]&&e[t](n)})}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",()=>{w&&N()},!1),Object.assign(e,{highlight:h,highlightAuto:E,highlightAll:N,highlightElement:y,highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"),q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{d=Y(d,e)},initHighlighting:()=>{N(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},initHighlightingOnLoad:()=>{N(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")},registerLanguage:(t,a)=>{let i=null;try{i=a(e)}catch(e){if(K("Language definition for '{}' could not be registered.".replace("{}",t)),!s)throw e;K(e),i=l}i.name||(i.name=t),n[t]=i,i.rawDefinition=a.bind(null,e),i.aliases&&k(i.aliases,{languageName:t})},unregisterLanguage:e=>{delete n[e];for(const n of Object.keys(i))i[n]===e&&delete i[n]},listLanguages:()=>Object.keys(n),getLanguage:v,registerAliases:k,autoDetection:O,inherit:Y,addPlugin:e=>{(e=>{e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{e["before:highlightBlock"](Object.assign({block:n.el},n))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}}),e.debugMode=()=>{s=!1},e.safeMode=()=>{s=!0},e.versionString="11.5.0",e.regex={concat:m,lookahead:g,either:p,optional:b,anyNumberOfTimes:u};for(const e in T)"object"==typeof T[e]&&t(T[e]);return Object.assign(e,T),e})({});const te=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z][A-Za-z0-9_-]*/}}),ae=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],ie=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],re=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],se=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],oe=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),le=re.concat(se);var ce="\\.([0-9](_*[0-9])*)",de="[0-9a-fA-F](_*[0-9a-fA-F])*",ge={className:"number",variants:[{begin:`(\\b([0-9](_*[0-9])*)((${ce})|\\.)?|(${ce}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b`},{begin:`\\b([0-9](_*[0-9])*)((${ce})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${ce})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{begin:`\\b0[xX]((${de})\\.?|(${de})?\\.(${de}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${de})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function ue(e,n,t){return-1===t?"":e.replace(n,a=>ue(e,n,t-1))}const be="[A-Za-z$_][0-9A-Za-z$_]*",me=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","case","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],pe=["true","false","null","undefined","NaN","Infinity"],_e=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],he=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],fe=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],Ee=["arguments","this","super","console","window","document","localStorage","module","global"],ye=[].concat(fe,_e,he);function we(e){const n=e.regex,t=be,a={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{const t=e[0].length+e.index,a=e.input[t];if("<"===a||","===a)return void n.ignoreMatch();let i;">"===a&&(((e,{after:n})=>{const t="",O={match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(k)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[_]};return{name:"Javascript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{PARAMS_CONTAINS:p,CLASS_REFERENCE:f},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,c,d,g,u,o,f,{className:"attr",begin:t+n.lookahead(":"),relevance:0},O,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[u,e.REGEXP_MODE,{className:"function",begin:k,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:p}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin,"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},E,{beginKeywords:"while if case catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[_,e.inherit(e.TITLE_MODE,{begin:t,className:"title.function"})]},{match:/\.\.\./,relevance:0},N,{match:"\\$"+t,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[_]},y,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},h,v,{match:/\$[(.]/}]}}const Ne=e=>m(/\b/,e,/\w$/.test(e)?/\b/:/\B/),ve=["Protocol","Type"].map(Ne),ke=["init","self"].map(Ne),Oe=["Any","Self"],xe=["actor","associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","case","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Me=["false","nil","true"],Se=["assignment","associativity","higherThan","left","lowerThan","none","right"],Ae=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],Ce=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Te=p(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Re=p(Te,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),De=m(Te,Re,"*"),Ie=p(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),Le=p(Ie,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Be=m(Ie,Le,"*"),$e=m(/[A-Z]/,Le,"*"),ze=["autoclosure",m(/convention\(/,p("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",m(/objc\(/,Be,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","testable","UIApplicationMain","unknown","usableFromInline"],Fe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];var Ue=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{className:"variable",variants:[{begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t]},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,keyword:["if","then","else","elif","fi","for","while","in","do","done","case","esac","function"],literal:["true","false"],built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"]},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},t]}},grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),a="[a-zA-Z_]\\w*::",i="(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},s={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0},d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","case","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"],literal:"true false NULL",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"},u=[l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],b={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:g,contains:u.concat([{begin:/\(/,end:/\)/,keywords:g,contains:u.concat(["self"]),relevance:0}]),relevance:0},m={begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)",keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[e.inherit(c,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}]},r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C",aliases:["h"],keywords:g,disableAutodetect:!0,illegal:"=]/,contains:[{beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:l,strings:s,keywords:g}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),a="[a-zA-Z_]\\w*::",i="(?!struct)(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0},d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","case","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"]},u={className:"function.dispatch",relevance:0,keywords:{_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"]},begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!case)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/))},b=[u,l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],m={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:g,contains:b.concat([{begin:/\(/,end:/\)/,keywords:g,contains:b.concat(["self"]),relevance:0}]),relevance:0},p={className:"function",begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)",keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[c],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[s,o]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/,keywords:g,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}]},r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:g,illegal:"",keywords:g,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:g},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","case","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]),built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"],literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/,keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]});s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t]},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{keyword:"if else elif endif define undef warning error line region endregion pragma checksum"}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial",relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[g,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{const n=e.regex,t=te(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+re.join("|")+")"},{begin:":(:)?("+se.join("|")+")"}]},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b"}]}},grmr_diff:e=>{const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}},grmr_go:e=>{const n={keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","case","type","var"],type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],literal:["true","false","iota","nil"],built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex,t={className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},s={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)),className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{const n=e.regex,t="[ร€-สธa-zA-Z_$][ร€-สธa-zA-Z_$0-9]*",a=t+ue("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","case","continue","throws","protected","public","private","module","requires","exports","do","sealed"],literal:["false","true","null"],type:["char","boolean","long","float","int","byte","short","double"],built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/,end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{begin:/import java\.[a-z]+\./,keywords:"import",relevance:2},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,className:"string",contains:[e.BACKSLASH_ESCAPE]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword",3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:i,relevance:0,contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,ge,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},ge,r]}},grmr_javascript:we,grmr_json:e=>({name:"JSON",contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0},e.QUOTE_STRING_MODE,{beginKeywords:"true false null"},e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}),grmr_kotlin:e=>{const n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(r,{className:"string"})]}]},l=ge,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g],{name:"Kotlin",aliases:["kt","kts"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},l]}},grmr_less:e=>{const n=te(e),t=le,a="([\\w-]+|@\\{[\\w-]+\\})",i=[],r=[],s=e=>({className:"string",begin:"~?"+e+".*?"+e}),o=(e,n,t)=>({className:e,begin:n,relevance:t}),l={$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},c={begin:"\\(",end:"\\)",contains:r,keywords:l,relevance:0};r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s("'"),s('"'),n.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},n.HEXCOLOR,c,o("variable","@@?[\\w-]+",10),o("variable","@\\{[\\w-]+\\}"),o("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},n.IMPORTANT);const d=r.concat({begin:/\{/,end:/\}/,contains:i}),g={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(r)},u={begin:a+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:r}}]},b={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",keywords:l,returnEnd:!0,contains:r,relevance:0}},m={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:d}},p={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:a,end:/\{/}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,o("keyword","all\\b"),o("variable","@\\{[\\w-]+\\}"),{begin:"\\b("+ae.join("|")+")\\b",className:"selector-tag"},n.CSS_NUMBER_MODE,o("selector-tag",a,0),o("selector-id","#"+a),o("selector-class","\\."+a,0),o("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",begin:":("+re.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+se.join("|")+")"},{begin:/\(/,end:/\)/,relevance:0,contains:d},{begin:"!important"},n.FUNCTION_DISPATCH]},_={begin:`[\\w-]+:(:)?(${t.join("|")})`,returnBegin:!0,contains:[p]};return i.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,b,m,_,u,p),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:i}},grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"]},i=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",t,{contains:[a],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:i}].concat(i)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{const n=e.regex,t=n.concat(/[A-Z_]/,n.optional(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),a={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[i,r,o,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:n.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}},grmr_markdown:e=>{const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},i={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[]}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r);let o=[n,t];return[a,i,r,s].forEach(e=>{e.contains=e.contains.concat(o)}),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o,end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n,keyword:["@interface","@class","@protocol","@implementation"]};return{name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:{"variable.language":["this","super"],$pattern:n,keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","case","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"],literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"],built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"],type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"]},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={$pattern:/[\w.]+/,keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0"},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/,end:/\}/},s={variants:[{begin:/\$\d/},{begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@][^\s\w{]/,relevance:0}]},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{const r="\\1"===i?i:n.concat(i,a);return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t)},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),r,{className:"string",contains:o,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a,contains:g}},grmr_php:e=>{const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/,contains:e.QUOTE_STRING_MODE.contains.concat(s)})]},d={scope:"number",variants:[{begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?"}],relevance:0},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endcase","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","case","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={keyword:u,literal:(e=>{const n=[];return e.forEach(e=>{n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase())}),n})(g),built_in:b},p=e=>e.map(e=>e.replace(/\|\d+$/,"")),_={variants:[{match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant"}},{match:[/::/,/class/],scope:{2:"variable.language"}},{match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class",3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))],scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class",3:"variable.language"}}]},E={scope:"attr",match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0,begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_]},w={relevance:0,match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)],scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(w);const N=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1,keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/,endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]},contains:["self",...N]},...N,{scope:"meta",match:i}]},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/,keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE,contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,w,f,{match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use"},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m,contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait",illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/,contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{beginKeywords:"use",relevance:0,end:";",contains:[{match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]}},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a,built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],literal:["__debug__","Ellipsis","False","None","NotImplemented","True"],type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"]},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/,end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/,end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={className:"number",relevance:0,variants:[{begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})`},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})`}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i,illegal:/(<\/|->|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if",relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[m]},{variants:[{match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}},grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:t,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum case tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"},match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={"variable.constant":["__FILE__","__LINE__"],"variable.language":["self","super"],keyword:["alias","and","attr_accessor","attr_reader","attr_writer","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","include","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield"],built_in:["proc","lambda"],literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/,end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)),contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number",relevance:0,variants:[{begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0,keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{match:[/class\s+/,i]}],scope:{2:"title.class",4:"title.class.inherited"},keywords:r},{relevance:0,match:[i,/\.new[ (]/],scope:{1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},{match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l);c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m}},{className:"meta.prompt",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])",starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/,contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}},grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0,begin:n.concat(/\b/,/(?!let\b)/,e.IDENT_RE,n.lookahead(/\s*\(/))},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","panic!","file!","format!","format_args!","include_bin!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"];return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"],keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"],literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}},grmr_scss:e=>{const n=te(e),t=se,a=re,i="@[a-z-]+",r={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+a.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/,contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT]},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:i,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE]},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta.prompt",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}),grmr_sql:e=>{const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter(e=>!r.includes(e)),c={begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}};return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t;return n=n||[],e.map(e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)})(l,{when:e=>e.length<3}),literal:a,type:i,built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"]},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/,keyword:l.concat(s),literal:a,type:i}},{className:"type",begin:n.either("double precision","large object","with timezone","without timezone")},c,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={match:[/\./,p(...ve,...ke)],className:{2:"keyword"}},r={match:m(/\./,p(...xe)),relevance:0},s=xe.filter(e=>"string"==typeof e).concat(["_|0"]),o={variants:[{className:"keyword",match:p(...xe.filter(e=>"string"!=typeof e).concat(Oe).map(Ne),...ke)}]},l={$pattern:p(/\b\w+/,/#\w+/),keyword:s.concat(Ae),literal:Me},c=[i,r,o],d=[{match:m(/\./,p(...Ce)),relevance:0},{className:"built_in",match:m(/\b/,p(...Ce),/(?=\()/)}],u={match:/->/,relevance:0},b=[u,{className:"operator",relevance:0,variants:[{match:De},{match:`\\.(\\.|${Re})+`}]}],_="([0-9a-fA-F]_*)+",h={className:"number",relevance:0,variants:[{match:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{match:`\\b0x(${_})(\\.(${_}))?([pP][+-]?(([0-9]_*)+))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},f=(e="")=>({className:"subst",variants:[{match:m(/\\/,e,/[0\\tnr"']/)},{match:m(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),E=(e="")=>({className:"subst",match:m(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),y=(e="")=>({className:"subst",label:"interpol",begin:m(/\\/,e,/\(/),end:/\)/}),w=(e="")=>({begin:m(e,/"""/),end:m(/"""/,e),contains:[f(e),E(e),y(e)]}),N=(e="")=>({begin:m(e,/"/),end:m(/"/,e),contains:[f(e),y(e)]}),v={className:"string",variants:[w(),w("#"),w("##"),w("###"),N(),N("#"),N("##"),N("###")]},k={match:m(/`/,Be,/`/)},O=[k,{className:"variable",match:/\$\d+/},{className:"variable",match:`\\$${Le}+`}],x=[{match:/(@|#(un)?)available/,className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:Fe,contains:[...b,h,v]}]}},{className:"keyword",match:m(/@/,p(...ze))},{className:"meta",match:m(/@/,Be)}],M={match:g(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:m(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Le,"+")},{className:"type",match:$e,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:m(/\s+&\s+/,g($e)),relevance:0}]},S={begin://,keywords:l,contains:[...a,...c,...x,u,M]};M.contains.push(S);const A={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{match:m(Be,/\s*:/),keywords:"_|0",relevance:0},...a,...c,...d,...b,h,v,...O,...x,M]},C={begin://,contains:[...a,M]},T={begin:/\(/,end:/\)/,keywords:l,contains:[{begin:p(g(m(Be,/\s*:/)),g(m(Be,/\s+/,Be,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:Be}]},...a,...c,...b,h,v,...x,M,A],endsParent:!0,illegal:/["']/},R={match:[/func/,/\s+/,p(k.match,Be,De)],className:{1:"keyword",3:"title.function"},contains:[C,T,n],illegal:[/\[/,/%/]},D={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[C,T,n],illegal:/\[|%/},I={match:[/operator/,/\s+/,De],className:{1:"keyword",3:"title"}},L={begin:[/precedencegroup/,/\s+/,$e],className:{1:"keyword",3:"title"},contains:[M],keywords:[...Se,...Me],end:/}/};for(const e of v.variants){const n=e.contains.find(e=>"interpol"===e.label);n.keywords=l;const t=[...c,...d,...b,h,v,...O];n.contains=[...t,{begin:/\(/,end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l,contains:[...a,R,D,{beginKeywords:"struct protocol class extension enum actor",end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c]},I,L,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0},...c,...d,...b,h,v,...O,...x,M,A]}},grmr_typescript:e=>{const n=we(e),t=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],a={beginKeywords:"namespace",end:/\{/,excludeEnd:!0,contains:[n.exports.CLASS_REFERENCE]},i={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{keyword:"interface extends",built_in:t},contains:[n.exports.CLASS_REFERENCE]},r={$pattern:be,keyword:me.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]),literal:pe,built_in:ye.concat(t),"variable.language":Ee},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},o=(e,n,t)=>{const a=e.contains.findIndex(e=>e.label===n);if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)};return Object.assign(n.keywords,r),n.exports.PARAMS_CONTAINS.push(s),n.contains=n.contains.concat([s,a,i]),o(n,"shebang",e.SHEBANG()),o(n,"use_strict",{className:"meta",relevance:10,begin:/^\s*['"]use strict['"]/}),n.contains.find(e=>"func.def"===e.label).relevance=0,Object.assign(n,{name:"TypeScript",aliases:["ts","tsx"]}),n},grmr_vbnet:e=>{const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}]},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}]}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield",built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort",type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort",literal:"true false nothing"},illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/,end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0,variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{className:"label",begin:/^\w+:/},o,l,{className:"meta",begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/,end:/$/,keywords:{keyword:"const disable else elseif enable end externalsource if region then"},contains:[l]}]}},grmr_yaml:e=>{const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/,end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]",contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type",begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l];return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:l}}});const je=ne;for(const e of Object.keys(Ue)){const n=e.replace("grmr_","").replace("_","-");je.registerLanguage(n,Ue[e])}return je}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); diff --git a/playground/.gitignore b/web/playground/.gitignore similarity index 96% rename from playground/.gitignore rename to web/playground/.gitignore index b60248144766..a5f9e423a32e 100644 --- a/playground/.gitignore +++ b/web/playground/.gitignore @@ -24,3 +24,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +book.json diff --git a/playground/README.md b/web/playground/README.md similarity index 100% rename from playground/README.md rename to web/playground/README.md diff --git a/web/playground/generateBook.js b/web/playground/generateBook.js new file mode 100644 index 000000000000..d5e5c3e2296f --- /dev/null +++ b/web/playground/generateBook.js @@ -0,0 +1,138 @@ +const { readdir, stat, readFile, writeFile } = require("fs/promises"); +const { join, relative, sep, normalize, basename } = require("path"); +const { EOL } = require("os"); + +async function* getAllFiles(dirPath) { + const files = await readdir(dirPath); + files.sort((a, b) => +isFile(a) - +isFile(b)); + + for (const file of files) { + const fullPath = join(dirPath, file); + if ((await stat(fullPath)).isDirectory()) { + yield fullPath; + yield* getAllFiles(fullPath); + } else { + if (fullPath.endsWith(".md")) { + yield fullPath; + } + } + } +} + +function depth(path) { + return path.split(sep).length; +} + +function isFile(path) { + return path.endsWith(".md"); +} + +/** + * @param {string} content + * @param {string} file + * @returns {{title:string, prql:string}[]} + */ +function getSnippets(content, file) { + const name = file.trim().toLowerCase().replace(/\s/g, "_"); + let heading = ""; + let prql = null; + const arr = []; + let index = 1; + const titles = new Set(); + content.split(/\r\n|\n/).forEach((line) => { + if (prql == null && line.startsWith("#") && line.includes("# ")) { + const spaceIndex = line.indexOf("# "); + heading = line + .slice(spaceIndex + 2) + .trim() + .toLowerCase() + .replace(/\s/g, "_"); + return; + } + if (line.trim() === "```prql") { + prql = ""; + return; + } + if (prql != null && line.trim() === "```") { + let title = heading || name; + if (titles.has(title)) { + title += `_${++index}`; + } else { + index = 1; + } + arr.push({ + title: title + ".prql", + prql: prql.trim(), + }); + titles.add(title); + prql = null; + return; + } + if (prql != null) { + prql = prql + line + EOL; + } + }); + if (arr.length !== titles.size) { + throw new Error("duplicate titles"); + } + return arr; +} + +(async () => { + const fileObject = {}; + const dir = join(__dirname, "..", "book", "src"); + const files = []; + let minDepth = 1e10; + for await (const file of getAllFiles(dir)) { + files.push(file); + minDepth = Math.min(depth(file)); + } + + for (const filePath of files) { + const relativeFile = relative(dir, filePath); + const snippets = isFile(filePath) + ? getSnippets( + (await readFile(filePath)).toString(), + basename(filePath).replace(/\..+/g, "").trim() + ) + : []; + if (!snippets.length && isFile(filePath)) { + continue; + } + const dept = depth(filePath) - minDepth; + fileObject[relativeFile] = [ + "", // editor + "", // content + dept, // depth + normalize(join(relativeFile, "..")), // parent + relativeFile, // id + basename(relativeFile).replace(/\..+/g, "").trim(), // name + ]; + for (const snippet of snippets) { + const id = relativeFile + "_" + snippet.title; + fileObject[id] = [ + "sql", + snippet.prql, + dept + 1, + relativeFile, + id, + snippet.title, + ]; + } + } + + // remove empty folders + const keys = Object.keys(fileObject); + const parents = new Set(); + Object.values(fileObject).forEach((v) => parents.add(v[3])); + for (const key of keys) { + if (!parents.has(key) && !fileObject[key][0]) { + delete fileObject[key]; + } + } + + await writeFile(join("src", "book.json"), JSON.stringify(fileObject)); +})().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/playground/package-lock.json b/web/playground/package-lock.json similarity index 97% rename from playground/package-lock.json rename to web/playground/package-lock.json index a6a833ef6c20..890656b3512f 100644 --- a/playground/package-lock.json +++ b/web/playground/package-lock.json @@ -9,26 +9,44 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { - "@duckdb/duckdb-wasm": "^1.21.0", + "@duckdb/duckdb-wasm": "^1.24.0", "@monaco-editor/react": "^4.4.6", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", - "monaco-editor": "^0.35.0", - "prql-js": "file:../prql-js", + "monaco-editor": "^0.36.0", + "prql-js": "file:../../bindings/prql-js", "react": "^18.2.0", "react-dom": "^18.2.0", "react-syntax-highlighter": "^15.5.0", "wasm-react-scripts": "5.0.3", - "web-vitals": "^3.1.0", + "web-vitals": "^3.3.0", "yaml": "^2.2.1" }, "devDependencies": { "npm-watch": "^0.11.0" } }, + "../../bindings/prql-js": { + "version": "0.6.0", + "license": "Apache-2.0", + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0" + } + }, + "../../prql-js": { + "version": "0.6.0", + "extraneous": true, + "license": "Apache-2.0", + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0" + } + }, "../prql-js": { - "version": "0.5.1", + "version": "0.6.1", + "extraneous": true, "license": "Apache-2.0", "devDependencies": { "chai": "^4.3.6", @@ -2281,11 +2299,11 @@ } }, "node_modules/@duckdb/duckdb-wasm": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.21.0.tgz", - "integrity": "sha512-3HBqwAhkjGWUGY2zSk2YG9i6bjNeTO5tyhBTJC9asv4ChTjx4mY6j4PFpAgUNomLm+QEFx1XXZN/BjRfENLdZQ==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.24.0.tgz", + "integrity": "sha512-ow3y5kNV5saNd9dK1PkoKq7I0yqOJsnuRA15p92/gIl37wsNq/hI1R5m1HBe02CAEBQPi14w7+EV9xzvduSfZA==", "dependencies": { - "apache-arrow": "^9.0.0" + "apache-arrow": "^11.0.0" } }, "node_modules/@eslint/eslintrc": { @@ -3330,6 +3348,126 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", @@ -3928,6 +4066,15 @@ "resolved": "https://registry.npmjs.org/@types/flatbuffers/-/flatbuffers-1.10.0.tgz", "integrity": "sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA==" }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -4031,6 +4178,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "node_modules/@types/node": { "version": "18.8.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", @@ -4124,6 +4276,15 @@ "@types/node": "*" } }, + "node_modules/@types/shelljs": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.11.tgz", + "integrity": "sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==", + "dependencies": { + "@types/glob": "*", + "@types/node": "*" + } + }, "node_modules/@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -4913,14 +5074,14 @@ } }, "node_modules/apache-arrow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-9.0.0.tgz", - "integrity": "sha512-Myt0vtm7eRtg4dYQp1hb2A77jbPhCYZcPeNp1F0u7te3rWQtcDI3EOSLxToFywdLQ1hfPzhzdLfDL0tPQObJjw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-11.0.0.tgz", + "integrity": "sha512-M8J4y+DimIyS44w2KOmVfzNHbTroR1oDpBKK6BYnlu8xVB41lxTz0yLmapo8/WJVAt5XcinAxMm14M771dm/rA==", "dependencies": { "@types/command-line-args": "5.2.0", "@types/command-line-usage": "5.0.2", "@types/flatbuffers": "*", - "@types/node": "^17.0.36", + "@types/node": "18.7.23", "@types/pad-left": "2.1.1", "command-line-args": "5.2.1", "command-line-usage": "6.1.3", @@ -4934,9 +5095,9 @@ } }, "node_modules/apache-arrow/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + "version": "18.7.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.23.tgz", + "integrity": "sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==" }, "node_modules/arg": { "version": "5.0.2", @@ -5380,6 +5541,11 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, "node_modules/bfj": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", @@ -6775,6 +6941,11 @@ "node": ">= 0.8" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -9250,6 +9421,14 @@ "node": ">= 0.4" } }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -9482,6 +9661,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -12026,6 +12213,31 @@ "tmpl": "1.0.5" } }, + "node_modules/matcher": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz", + "integrity": "sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/matcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -12234,9 +12446,14 @@ } }, "node_modules/monaco-editor": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.35.0.tgz", - "integrity": "sha512-BJfkAZ0EJ7JgrgWzqjfBNP9hPSS8NlfECEDMEIIiozV2UaPq22yeuOjgbd3TwMh3anH0krWZirXZfn8KUSxiOA==" + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.36.0.tgz", + "integrity": "sha512-1Pn3AatfK88flUigyBozA4mt8+SB5xlgloQDu1RqivARw9yKaml/jceIvndae7Z2Nq8T7xZccFlmH+n6rkFg6g==", + "dependencies": { + "@types/shelljs": "^0.8.11", + "pin-github-action": "^1.8.0", + "shelljs": "^0.8.5" + } }, "node_modules/ms": { "version": "2.1.3", @@ -12310,6 +12527,44 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -12874,6 +13129,50 @@ "node": ">=0.10.0" } }, + "node_modules/pin-github-action": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pin-github-action/-/pin-github-action-1.8.0.tgz", + "integrity": "sha512-8QMKGbDUmMLFSyeV7hDIVmlI8B3ThJed1uFYuhcCBLi/w8xHPbrPhnCvJndYdugNc8aj1FrijrOMDLQ93ATc7A==", + "dependencies": { + "@octokit/rest": "^18", + "commander": "^9", + "debug": "^4.3.4", + "matcher": "^4.0.0", + "yaml": "^2.1.3" + }, + "bin": { + "pin-github-action": "bin.js" + } + }, + "node_modules/pin-github-action/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/pin-github-action/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pin-github-action/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -14327,7 +14626,7 @@ } }, "node_modules/prql-js": { - "resolved": "../prql-js", + "resolved": "../../bindings/prql-js", "link": true }, "node_modules/prr": { @@ -14627,26 +14926,26 @@ "node": ">=8.10.0" } }, - "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dependencies": { - "minimatch": "3.0.4" + "resolve": "^1.1.6" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dependencies": { - "brace-expansion": "^1.1.7" + "minimatch": "^3.0.5" }, "engines": { - "node": "*" + "node": ">=6.0.0" } }, "node_modules/redent": { @@ -15344,6 +15643,22 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -16468,6 +16783,11 @@ "node": ">=8" } }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -16737,9 +17057,9 @@ } }, "node_modules/web-vitals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.1.0.tgz", - "integrity": "sha512-zCeQ+bOjWjJbXv5ZL0r8Py3XP2doCQMZXNKlBGfUjPAVZWokApdeF/kFlK1peuKlCt8sL9TFkKzyXE9/cmNJQA==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.3.0.tgz", + "integrity": "sha512-GZsEmJBNclIpViS/7QVOTr7Kbt4BgLeR7kQ5zCCtJVuiWsA+K6xTXaoEXssvl8yYFICEyNmA2Nr+vgBYTnS4bA==" }, "node_modules/webidl-conversions": { "version": "6.1.0", @@ -19063,11 +19383,11 @@ "requires": {} }, "@duckdb/duckdb-wasm": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.21.0.tgz", - "integrity": "sha512-3HBqwAhkjGWUGY2zSk2YG9i6bjNeTO5tyhBTJC9asv4ChTjx4mY6j4PFpAgUNomLm+QEFx1XXZN/BjRfENLdZQ==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.24.0.tgz", + "integrity": "sha512-ow3y5kNV5saNd9dK1PkoKq7I0yqOJsnuRA15p92/gIl37wsNq/hI1R5m1HBe02CAEBQPi14w7+EV9xzvduSfZA==", "requires": { - "apache-arrow": "^9.0.0" + "apache-arrow": "^11.0.0" } }, "@eslint/eslintrc": { @@ -19904,6 +20224,118 @@ "fastq": "^1.6.0" } }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + }, "@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", @@ -20317,6 +20749,15 @@ "resolved": "https://registry.npmjs.org/@types/flatbuffers/-/flatbuffers-1.10.0.tgz", "integrity": "sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA==" }, + "@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "requires": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -20413,6 +20854,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "@types/node": { "version": "18.8.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", @@ -20506,6 +20952,15 @@ "@types/node": "*" } }, + "@types/shelljs": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.11.tgz", + "integrity": "sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==", + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, "@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -21077,14 +21532,14 @@ } }, "apache-arrow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-9.0.0.tgz", - "integrity": "sha512-Myt0vtm7eRtg4dYQp1hb2A77jbPhCYZcPeNp1F0u7te3rWQtcDI3EOSLxToFywdLQ1hfPzhzdLfDL0tPQObJjw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-11.0.0.tgz", + "integrity": "sha512-M8J4y+DimIyS44w2KOmVfzNHbTroR1oDpBKK6BYnlu8xVB41lxTz0yLmapo8/WJVAt5XcinAxMm14M771dm/rA==", "requires": { "@types/command-line-args": "5.2.0", "@types/command-line-usage": "5.0.2", "@types/flatbuffers": "*", - "@types/node": "^17.0.36", + "@types/node": "18.7.23", "@types/pad-left": "2.1.1", "command-line-args": "5.2.1", "command-line-usage": "6.1.3", @@ -21095,9 +21550,9 @@ }, "dependencies": { "@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + "version": "18.7.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.23.tgz", + "integrity": "sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==" } } }, @@ -21435,6 +21890,11 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, "bfj": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", @@ -22452,6 +22912,11 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -24243,6 +24708,11 @@ "side-channel": "^1.0.4" } }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, "ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -24384,6 +24854,11 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, "is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -26390,6 +26865,21 @@ "tmpl": "1.0.5" } }, + "matcher": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz", + "integrity": "sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==", + "requires": { + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + } + } + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -26537,9 +27027,14 @@ } }, "monaco-editor": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.35.0.tgz", - "integrity": "sha512-BJfkAZ0EJ7JgrgWzqjfBNP9hPSS8NlfECEDMEIIiozV2UaPq22yeuOjgbd3TwMh3anH0krWZirXZfn8KUSxiOA==" + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.36.0.tgz", + "integrity": "sha512-1Pn3AatfK88flUigyBozA4mt8+SB5xlgloQDu1RqivARw9yKaml/jceIvndae7Z2Nq8T7xZccFlmH+n6rkFg6g==", + "requires": { + "@types/shelljs": "^0.8.11", + "pin-github-action": "^1.8.0", + "shelljs": "^0.8.5" + } }, "ms": { "version": "2.1.3", @@ -26595,6 +27090,35 @@ "tslib": "^2.0.3" } }, + "node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -26991,6 +27515,38 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" }, + "pin-github-action": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pin-github-action/-/pin-github-action-1.8.0.tgz", + "integrity": "sha512-8QMKGbDUmMLFSyeV7hDIVmlI8B3ThJed1uFYuhcCBLi/w8xHPbrPhnCvJndYdugNc8aj1FrijrOMDLQ93ATc7A==", + "requires": { + "@octokit/rest": "^18", + "commander": "^9", + "debug": "^4.3.4", + "matcher": "^4.0.0", + "yaml": "^2.1.3" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -27862,7 +28418,7 @@ } }, "prql-js": { - "version": "file:../prql-js", + "version": "file:../../bindings/prql-js", "requires": { "chai": "^4.3.6", "mocha": "^10.0.0" @@ -28091,22 +28647,20 @@ "picomatch": "^2.2.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "requires": { + "resolve": "^1.1.6" + } + }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "requires": { - "minimatch": "3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^3.0.5" } }, "redent": { @@ -28605,6 +29159,16 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -29450,6 +30014,11 @@ "crypto-random-string": "^2.0.0" } }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -29651,9 +30220,9 @@ } }, "web-vitals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.1.0.tgz", - "integrity": "sha512-zCeQ+bOjWjJbXv5ZL0r8Py3XP2doCQMZXNKlBGfUjPAVZWokApdeF/kFlK1peuKlCt8sL9TFkKzyXE9/cmNJQA==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.3.0.tgz", + "integrity": "sha512-GZsEmJBNclIpViS/7QVOTr7Kbt4BgLeR7kQ5zCCtJVuiWsA+K6xTXaoEXssvl8yYFICEyNmA2Nr+vgBYTnS4bA==" }, "webidl-conversions": { "version": "6.1.0", diff --git a/playground/package.json b/web/playground/package.json similarity index 62% rename from playground/package.json rename to web/playground/package.json index 8a2295b250b7..0ba2ec02f7af 100644 --- a/playground/package.json +++ b/web/playground/package.json @@ -12,18 +12,18 @@ ] }, "dependencies": { - "@duckdb/duckdb-wasm": "^1.21.0", + "@duckdb/duckdb-wasm": "^1.24.0", "@monaco-editor/react": "^4.4.6", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", - "monaco-editor": "^0.35.0", - "prql-js": "file:../prql-js", + "monaco-editor": "^0.36.0", + "prql-js": "file:../../bindings/prql-js", "react": "^18.2.0", "react-dom": "^18.2.0", "react-syntax-highlighter": "^15.5.0", "wasm-react-scripts": "5.0.3", - "web-vitals": "^3.1.0", + "web-vitals": "^3.3.0", "yaml": "^2.2.1" }, "devDependencies": { @@ -39,11 +39,12 @@ "name": "prql-playground", "private": true, "scripts": { - "build": "react-scripts build", - "eject": "react-scripts eject", - "preinstall": "cp -r ../prql-compiler/tests/integration/data public && cd ../prql-js && npm run build:bundler", - "start": "react-scripts start", - "test": "react-scripts test" + "build": "npm run genBook && react-scripts build", + "eject": "npm run genBook && react-scripts eject", + "preinstall": "cp -r ../../prql-compiler/tests/integration/data public && cd ../../bindings/prql-js && npm run build:bundler", + "start": "npm run genBook && react-scripts start", + "test": "npm run genBook && react-scripts test", + "genBook": "node generateBook.js" }, "version": "0.1.0" } diff --git a/playground/public/favicon.ico b/web/playground/public/favicon.ico similarity index 100% rename from playground/public/favicon.ico rename to web/playground/public/favicon.ico diff --git a/playground/public/index.html b/web/playground/public/index.html similarity index 100% rename from playground/public/index.html rename to web/playground/public/index.html diff --git a/playground/public/manifest.json b/web/playground/public/manifest.json similarity index 100% rename from playground/public/manifest.json rename to web/playground/public/manifest.json diff --git a/playground/public/robots.txt b/web/playground/public/robots.txt similarity index 100% rename from playground/public/robots.txt rename to web/playground/public/robots.txt diff --git a/playground/src/app/App.css b/web/playground/src/app/App.css similarity index 100% rename from playground/src/app/App.css rename to web/playground/src/app/App.css diff --git a/playground/src/app/App.js b/web/playground/src/app/App.js similarity index 97% rename from playground/src/app/App.js rename to web/playground/src/app/App.js index a0fe7531c76a..2bae9b889f0d 100644 --- a/playground/src/app/App.js +++ b/web/playground/src/app/App.js @@ -2,6 +2,7 @@ import "./App.css"; import Workbench from "../workbench/Workbench"; import Sidebar from "../sidebar/Sidebar"; import examples from "../examples"; +import book from "../book.json"; import * as duckdb from "../workbench/duckdb"; import React from "react"; @@ -26,6 +27,7 @@ class App extends React.Component { library: { examples, chinook, + book, "local storage": loadLocalStorage(), }, }; diff --git a/playground/src/examples.js b/web/playground/src/examples.js similarity index 97% rename from playground/src/examples.js rename to web/playground/src/examples.js index 751c1434e64a..e9955d897e75 100644 --- a/playground/src/examples.js +++ b/web/playground/src/examples.js @@ -1,6 +1,6 @@ const examples = { "introduction.prql": [ - "arrow", + "sql", `from invoices filter invoice_date >= @1970-01-16 derive [ # This adds columns @@ -27,7 +27,7 @@ derive db_version = s"version()" # S-string, escape hatch to SQL ], "let-table-0.prql": [ - "arrow", + "sql", `let soundtracks = ( from playlists filter name == 'TV Shows' @@ -56,7 +56,7 @@ take 10 ], "artists-0.prql": [ - "arrow", + "sql", `from tracks select [album_id, name, unit_price] sort [-unit_price, name] diff --git a/playground/src/highlight.css b/web/playground/src/highlight.css similarity index 100% rename from playground/src/highlight.css rename to web/playground/src/highlight.css diff --git a/playground/src/index.css b/web/playground/src/index.css similarity index 100% rename from playground/src/index.css rename to web/playground/src/index.css diff --git a/playground/src/index.js b/web/playground/src/index.js similarity index 100% rename from playground/src/index.js rename to web/playground/src/index.js diff --git a/playground/src/output/Output.css b/web/playground/src/output/Output.css similarity index 100% rename from playground/src/output/Output.css rename to web/playground/src/output/Output.css diff --git a/playground/src/output/Output.js b/web/playground/src/output/Output.js similarity index 100% rename from playground/src/output/Output.js rename to web/playground/src/output/Output.js diff --git a/playground/src/reportWebVitals.js b/web/playground/src/reportWebVitals.js similarity index 100% rename from playground/src/reportWebVitals.js rename to web/playground/src/reportWebVitals.js diff --git a/playground/src/setupTests.js b/web/playground/src/setupTests.js similarity index 100% rename from playground/src/setupTests.js rename to web/playground/src/setupTests.js diff --git a/playground/src/sidebar/Sidebar.css b/web/playground/src/sidebar/Sidebar.css similarity index 55% rename from playground/src/sidebar/Sidebar.css rename to web/playground/src/sidebar/Sidebar.css index 7a7293a025ce..8f6cca67af79 100644 --- a/playground/src/sidebar/Sidebar.css +++ b/web/playground/src/sidebar/Sidebar.css @@ -1,5 +1,7 @@ .sidebar { padding-top: 1rem; + min-width: fit-content; + overflow-y: auto; } section:not(:last-child) { border-bottom: 1px solid #666; @@ -20,6 +22,28 @@ section h2 { background-color: #ffffff1f; } +.folderRow ~ .fileRow:not(.folderRow)::before { + content: ""; + margin-right: 5px; +} + +.folderRow { + text-transform: capitalize; +} + +.folderRow::before { + content: "\1F892 "; + margin-right: 5px; + display: inline-block; + transition: transform 0.3s ease; + transform-origin: center; + transform: scale(1.25, 1.25); +} + +.folderRow.open::before { + transform: scale(1.25, 1.25) rotate(90deg); +} + section h1, section h2, section p, diff --git a/web/playground/src/sidebar/Sidebar.js b/web/playground/src/sidebar/Sidebar.js new file mode 100644 index 000000000000..defd3f824017 --- /dev/null +++ b/web/playground/src/sidebar/Sidebar.js @@ -0,0 +1,98 @@ +import "./Sidebar.css"; +import React from "react"; +import { useState } from "react"; + +function Sidebar({ library, onLoadFile }) { + function loadFile(section, file) { + onLoadFile(file, library[section][file]); + } + + function toggleFolder(id) { + openFolders[id] = !Boolean(openFolders[id]); + setOpenFolders(() => openFolders); + } + + function handleClick(section, file, id) { + if (isFile(file)) { + loadFile(section, file); + } else { + toggleFolder(id); + } + sv(($) => $ + 1); + } + + function isFile(path) { + return path.endsWith(".prql"); + } + + const sections = []; + const [openFolders, setOpenFolders] = useState({}); + const [v, sv] = useState(1); + + for (const [section, files] of Object.entries(library)) { + const fileRows = []; + for (const [index, filename] of Object.keys(files).entries()) { + const array = files[filename]; + const depth = array[2]; + const parent = array[3]; + const id = array[4]; + const name = array[5]; + fileRows.push( + + {(parent == null || openFolders[parent] || depth === 0) && ( +
handleClick(section, filename, id)} + > + {name ?? filename} +
+ )} +
+ ); + } + + sections.push( +
+

{section}

+ + {fileRows} +
+ ); + } + + return ( +
+
+

PRQL Playground

+
+
+

External links

+ + PRQL Website ↗ + + + Book ↗ + +
+ + {sections} +
+ ); +} + +export default Sidebar; diff --git a/playground/src/workbench/Workbench.css b/web/playground/src/workbench/Workbench.css similarity index 100% rename from playground/src/workbench/Workbench.css rename to web/playground/src/workbench/Workbench.css diff --git a/playground/src/workbench/Workbench.js b/web/playground/src/workbench/Workbench.js similarity index 100% rename from playground/src/workbench/Workbench.js rename to web/playground/src/workbench/Workbench.js diff --git a/playground/src/workbench/duckdb.js b/web/playground/src/workbench/duckdb.js similarity index 100% rename from playground/src/workbench/duckdb.js rename to web/playground/src/workbench/duckdb.js diff --git a/playground/src/workbench/monaco-theme.json b/web/playground/src/workbench/monaco-theme.json similarity index 100% rename from playground/src/workbench/monaco-theme.json rename to web/playground/src/workbench/monaco-theme.json diff --git a/playground/src/workbench/prql-syntax.js b/web/playground/src/workbench/prql-syntax.js similarity index 96% rename from playground/src/workbench/prql-syntax.js rename to web/playground/src/workbench/prql-syntax.js index 6adce08b273c..c835de0c9ffa 100644 --- a/playground/src/workbench/prql-syntax.js +++ b/web/playground/src/workbench/prql-syntax.js @@ -13,7 +13,7 @@ const TRANSFORMS = [ "union", "window", ]; -const BUILTIN_FUNCTIONS = ["switch"]; // "in", "as" +const BUILTIN_FUNCTIONS = ["case"]; // "in", "as" const KEYWORDS = ["func", "let", "prql"]; const LITERALS = ["null", "true", "false"]; const OPERATORS = ["and", "or"]; // "not" @@ -39,6 +39,8 @@ const def = { "-", "==", "!=", + "->", + "=>", ">", "<", ">=", diff --git a/website/.gitignore b/web/website/.gitignore similarity index 100% rename from website/.gitignore rename to web/website/.gitignore diff --git a/website/README.md b/web/website/README.md similarity index 100% rename from website/README.md rename to web/website/README.md diff --git a/website/archetypes/default.md b/web/website/archetypes/default.md similarity index 100% rename from website/archetypes/default.md rename to web/website/archetypes/default.md diff --git a/website/config.yml b/web/website/config.yml similarity index 100% rename from website/config.yml rename to web/website/config.yml diff --git a/website/content/_index.md b/web/website/content/_index.md similarity index 98% rename from website/content/_index.md rename to web/website/content/_index.md index c6729e872c06..349317aaf594 100644 --- a/website/content/_index.md +++ b/web/website/content/_index.md @@ -246,7 +246,7 @@ showcase_section: SELECT * FROM - table_1 + table_1 AS table_0 WHERE _expr_0 <= 1 @@ -380,12 +380,13 @@ tools_section: `pip install pyprql` - - link: "https://github.com/PRQL/prql" - label: "prql-compiler" + - link: https://crates.io/crates/prqlc + label: "prqlc" text: | - Reference compiler implementation. Has a CLI utility that can transpile, format and annotate PRQL queries. + A CLI for PRQL compiler, written in Rust. - `brew install prql/PRQL/prql-compiler` + `cargo install prqlc` + `brew install prql/PRQL/prqlc` integrations_section: enable: true @@ -430,12 +431,10 @@ bindings_section: label: "prqlr" text: "R bindings for prql-compiler." - - link: https://crates.io/crates/prqlc - label: "prqlc" + - link: "https://crates.io/crates/prql-compiler" + label: "prql-compiler" text: | - A CLI for PRQL compiler, written in Rust. - - `cargo install prqlc` + Reference compiler implementation, written in Rust. Transpile, format and annotate PRQL queries. comments_section: enable: true diff --git a/website/content/book_integrated.html b/web/website/content/book_integrated.html similarity index 100% rename from website/content/book_integrated.html rename to web/website/content/book_integrated.html diff --git a/website/content/faq.md b/web/website/content/faq.md similarity index 100% rename from website/content/faq.md rename to web/website/content/faq.md diff --git a/website/content/playground.html b/web/website/content/playground.html similarity index 100% rename from website/content/playground.html rename to web/website/content/playground.html diff --git a/website/content/posts/2022-05-19-examples.md b/web/website/content/posts/2022-05-19-examples.md similarity index 100% rename from website/content/posts/2022-05-19-examples.md rename to web/website/content/posts/2022-05-19-examples.md diff --git a/website/content/posts/2023-01-07-functional-relations.md b/web/website/content/posts/2023-01-07-functional-relations.md similarity index 100% rename from website/content/posts/2023-01-07-functional-relations.md rename to web/website/content/posts/2023-01-07-functional-relations.md diff --git a/website/content/posts/2023-01-27-prql-query.md b/web/website/content/posts/2023-01-27-prql-query.md similarity index 100% rename from website/content/posts/2023-01-27-prql-query.md rename to web/website/content/posts/2023-01-27-prql-query.md diff --git a/website/content/posts/2023-01-28-format-pretty-reports/_index.md b/web/website/content/posts/2023-01-28-format-pretty-reports/_index.md similarity index 100% rename from website/content/posts/2023-01-28-format-pretty-reports/_index.md rename to web/website/content/posts/2023-01-28-format-pretty-reports/_index.md diff --git a/website/content/posts/2023-01-28-format-pretty-reports/query_result.png b/web/website/content/posts/2023-01-28-format-pretty-reports/query_result.png similarity index 100% rename from website/content/posts/2023-01-28-format-pretty-reports/query_result.png rename to web/website/content/posts/2023-01-28-format-pretty-reports/query_result.png diff --git a/website/content/posts/2023-02-02-one-year/7cpDySb.png b/web/website/content/posts/2023-02-02-one-year/7cpDySb.png similarity index 100% rename from website/content/posts/2023-02-02-one-year/7cpDySb.png rename to web/website/content/posts/2023-02-02-one-year/7cpDySb.png diff --git a/website/content/posts/2023-02-02-one-year/FQ9QSOo.png b/web/website/content/posts/2023-02-02-one-year/FQ9QSOo.png similarity index 100% rename from website/content/posts/2023-02-02-one-year/FQ9QSOo.png rename to web/website/content/posts/2023-02-02-one-year/FQ9QSOo.png diff --git a/website/content/posts/2023-02-02-one-year/GXLvoXn.png b/web/website/content/posts/2023-02-02-one-year/GXLvoXn.png similarity index 100% rename from website/content/posts/2023-02-02-one-year/GXLvoXn.png rename to web/website/content/posts/2023-02-02-one-year/GXLvoXn.png diff --git a/website/content/posts/2023-02-02-one-year/URpCf29.png b/web/website/content/posts/2023-02-02-one-year/URpCf29.png similarity index 100% rename from website/content/posts/2023-02-02-one-year/URpCf29.png rename to web/website/content/posts/2023-02-02-one-year/URpCf29.png diff --git a/website/content/posts/2023-02-02-one-year/_index.md b/web/website/content/posts/2023-02-02-one-year/_index.md similarity index 98% rename from website/content/posts/2023-02-02-one-year/_index.md rename to web/website/content/posts/2023-02-02-one-year/_index.md index 8ec6ef20c85e..c591feb5e7b3 100644 --- a/website/content/posts/2023-02-02-one-year/_index.md +++ b/web/website/content/posts/2023-02-02-one-year/_index.md @@ -31,7 +31,7 @@ Language design & development in the last year have been focused on these areas: - small quality-of-life language features (e.g. syntax for [f-strings, dates, coalesce operator](https://prql-lang.org/book/syntax.html), - [switch](https://github.com/PRQL/prql/issues/504)), + [case](https://github.com/PRQL/prql/issues/504)), PRQL is now in a state where it can greatly improve the developer experience for writing complex analytical queries, but it does require a bit of fiddling to set diff --git a/website/content/posts/2023-02-02-one-year/ncVXken.png b/web/website/content/posts/2023-02-02-one-year/ncVXken.png similarity index 100% rename from website/content/posts/2023-02-02-one-year/ncVXken.png rename to web/website/content/posts/2023-02-02-one-year/ncVXken.png diff --git a/website/content/roadmap.md b/web/website/content/roadmap.md similarity index 99% rename from website/content/roadmap.md rename to web/website/content/roadmap.md index 7a9cb1b107c6..46c5deb259e4 100644 --- a/website/content/roadmap.md +++ b/web/website/content/roadmap.md @@ -78,7 +78,7 @@ This requires development across multiple dimensions โ€” writing an [LSP server](https://langserver.org/), better support for typing in the compiler, and possibly database cohesion. -While PRQL compiler will never depend on a database to compile queries, LPS +While PRQL compiler will never depend on a database to compile queries, an LSP server could greatly help with generating type definitions from the information schema of a database. diff --git a/website/static/CNAME b/web/website/static/CNAME similarity index 100% rename from website/static/CNAME rename to web/website/static/CNAME diff --git a/website/static/img/apple-touch-icon.png b/web/website/static/img/apple-touch-icon.png similarity index 100% rename from website/static/img/apple-touch-icon.png rename to web/website/static/img/apple-touch-icon.png diff --git a/website/static/img/favicon-16x16.png b/web/website/static/img/favicon-16x16.png similarity index 100% rename from website/static/img/favicon-16x16.png rename to web/website/static/img/favicon-16x16.png diff --git a/website/static/img/favicon-32x32.png b/web/website/static/img/favicon-32x32.png similarity index 100% rename from website/static/img/favicon-32x32.png rename to web/website/static/img/favicon-32x32.png diff --git a/website/static/img/favicon.ico b/web/website/static/img/favicon.ico similarity index 100% rename from website/static/img/favicon.ico rename to web/website/static/img/favicon.ico diff --git a/website/static/img/icon.svg b/web/website/static/img/icon.svg similarity index 100% rename from website/static/img/icon.svg rename to web/website/static/img/icon.svg diff --git a/website/themes/prql-theme/archetypes/default.md b/web/website/themes/prql-theme/archetypes/default.md similarity index 100% rename from website/themes/prql-theme/archetypes/default.md rename to web/website/themes/prql-theme/archetypes/default.md diff --git a/website/themes/prql-theme/layouts/404.html b/web/website/themes/prql-theme/layouts/404.html similarity index 100% rename from website/themes/prql-theme/layouts/404.html rename to web/website/themes/prql-theme/layouts/404.html diff --git a/website/themes/prql-theme/layouts/_default/_markup/render-link.html b/web/website/themes/prql-theme/layouts/_default/_markup/render-link.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/_markup/render-link.html rename to web/website/themes/prql-theme/layouts/_default/_markup/render-link.html diff --git a/website/themes/prql-theme/layouts/_default/article.html b/web/website/themes/prql-theme/layouts/_default/article.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/article.html rename to web/website/themes/prql-theme/layouts/_default/article.html diff --git a/website/themes/prql-theme/layouts/_default/baseof.html b/web/website/themes/prql-theme/layouts/_default/baseof.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/baseof.html rename to web/website/themes/prql-theme/layouts/_default/baseof.html diff --git a/website/themes/prql-theme/layouts/_default/big_iframe.html b/web/website/themes/prql-theme/layouts/_default/big_iframe.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/big_iframe.html rename to web/website/themes/prql-theme/layouts/_default/big_iframe.html diff --git a/website/themes/prql-theme/layouts/_default/home.html b/web/website/themes/prql-theme/layouts/_default/home.html similarity index 99% rename from website/themes/prql-theme/layouts/_default/home.html rename to web/website/themes/prql-theme/layouts/_default/home.html index 581c747d4558..9875e4dedfe7 100644 --- a/website/themes/prql-theme/layouts/_default/home.html +++ b/web/website/themes/prql-theme/layouts/_default/home.html @@ -2,7 +2,7 @@ {{ with .Params.hero_section }} {{ if .enable }} -
+
diff --git a/website/themes/prql-theme/layouts/_default/list.html b/web/website/themes/prql-theme/layouts/_default/list.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/list.html rename to web/website/themes/prql-theme/layouts/_default/list.html diff --git a/website/themes/prql-theme/layouts/_default/single.html b/web/website/themes/prql-theme/layouts/_default/single.html similarity index 100% rename from website/themes/prql-theme/layouts/_default/single.html rename to web/website/themes/prql-theme/layouts/_default/single.html diff --git a/website/themes/prql-theme/layouts/partials/footer.html b/web/website/themes/prql-theme/layouts/partials/footer.html similarity index 100% rename from website/themes/prql-theme/layouts/partials/footer.html rename to web/website/themes/prql-theme/layouts/partials/footer.html diff --git a/website/themes/prql-theme/layouts/partials/head.html b/web/website/themes/prql-theme/layouts/partials/head.html similarity index 100% rename from website/themes/prql-theme/layouts/partials/head.html rename to web/website/themes/prql-theme/layouts/partials/head.html diff --git a/website/themes/prql-theme/layouts/partials/header.html b/web/website/themes/prql-theme/layouts/partials/header.html similarity index 100% rename from website/themes/prql-theme/layouts/partials/header.html rename to web/website/themes/prql-theme/layouts/partials/header.html diff --git a/website/themes/prql-theme/layouts/partials/section-cards.html b/web/website/themes/prql-theme/layouts/partials/section-cards.html similarity index 100% rename from website/themes/prql-theme/layouts/partials/section-cards.html rename to web/website/themes/prql-theme/layouts/partials/section-cards.html diff --git a/website/themes/prql-theme/layouts/partials/section-comments.html b/web/website/themes/prql-theme/layouts/partials/section-comments.html similarity index 100% rename from website/themes/prql-theme/layouts/partials/section-comments.html rename to web/website/themes/prql-theme/layouts/partials/section-comments.html diff --git a/website/themes/prql-theme/layouts/shortcodes/columns.html b/web/website/themes/prql-theme/layouts/shortcodes/columns.html similarity index 100% rename from website/themes/prql-theme/layouts/shortcodes/columns.html rename to web/website/themes/prql-theme/layouts/shortcodes/columns.html diff --git a/website/themes/prql-theme/layouts/shortcodes/faq.html b/web/website/themes/prql-theme/layouts/shortcodes/faq.html similarity index 100% rename from website/themes/prql-theme/layouts/shortcodes/faq.html rename to web/website/themes/prql-theme/layouts/shortcodes/faq.html diff --git a/website/themes/prql-theme/layouts/shortcodes/link.html b/web/website/themes/prql-theme/layouts/shortcodes/link.html similarity index 100% rename from website/themes/prql-theme/layouts/shortcodes/link.html rename to web/website/themes/prql-theme/layouts/shortcodes/link.html diff --git a/website/themes/prql-theme/static/apple-touch-icon.png b/web/website/themes/prql-theme/static/apple-touch-icon.png similarity index 100% rename from website/themes/prql-theme/static/apple-touch-icon.png rename to web/website/themes/prql-theme/static/apple-touch-icon.png diff --git a/website/themes/prql-theme/static/favicon-16x16.png b/web/website/themes/prql-theme/static/favicon-16x16.png similarity index 100% rename from website/themes/prql-theme/static/favicon-16x16.png rename to web/website/themes/prql-theme/static/favicon-16x16.png diff --git a/website/themes/prql-theme/static/favicon-32x32.png b/web/website/themes/prql-theme/static/favicon-32x32.png similarity index 100% rename from website/themes/prql-theme/static/favicon-32x32.png rename to web/website/themes/prql-theme/static/favicon-32x32.png diff --git a/website/themes/prql-theme/static/favicon.ico b/web/website/themes/prql-theme/static/favicon.ico similarity index 100% rename from website/themes/prql-theme/static/favicon.ico rename to web/website/themes/prql-theme/static/favicon.ico diff --git a/website/themes/prql-theme/static/fonts/boxicons.eot b/web/website/themes/prql-theme/static/fonts/boxicons.eot similarity index 100% rename from website/themes/prql-theme/static/fonts/boxicons.eot rename to web/website/themes/prql-theme/static/fonts/boxicons.eot diff --git a/website/themes/prql-theme/static/fonts/boxicons.min.css b/web/website/themes/prql-theme/static/fonts/boxicons.min.css similarity index 100% rename from website/themes/prql-theme/static/fonts/boxicons.min.css rename to web/website/themes/prql-theme/static/fonts/boxicons.min.css diff --git a/website/themes/prql-theme/static/fonts/boxicons.ttf b/web/website/themes/prql-theme/static/fonts/boxicons.ttf similarity index 100% rename from website/themes/prql-theme/static/fonts/boxicons.ttf rename to web/website/themes/prql-theme/static/fonts/boxicons.ttf diff --git a/website/themes/prql-theme/static/fonts/boxicons.woff b/web/website/themes/prql-theme/static/fonts/boxicons.woff similarity index 100% rename from website/themes/prql-theme/static/fonts/boxicons.woff rename to web/website/themes/prql-theme/static/fonts/boxicons.woff diff --git a/website/themes/prql-theme/static/fonts/boxicons.woff2 b/web/website/themes/prql-theme/static/fonts/boxicons.woff2 similarity index 100% rename from website/themes/prql-theme/static/fonts/boxicons.woff2 rename to web/website/themes/prql-theme/static/fonts/boxicons.woff2 diff --git a/website/themes/prql-theme/static/fonts/inter-extra-bold.woff2 b/web/website/themes/prql-theme/static/fonts/inter-extra-bold.woff2 similarity index 100% rename from website/themes/prql-theme/static/fonts/inter-extra-bold.woff2 rename to web/website/themes/prql-theme/static/fonts/inter-extra-bold.woff2 diff --git a/website/themes/prql-theme/static/fonts/inter-extra-nold.woff b/web/website/themes/prql-theme/static/fonts/inter-extra-nold.woff similarity index 100% rename from website/themes/prql-theme/static/fonts/inter-extra-nold.woff rename to web/website/themes/prql-theme/static/fonts/inter-extra-nold.woff diff --git a/website/themes/prql-theme/static/icon.svg b/web/website/themes/prql-theme/static/icon.svg similarity index 100% rename from website/themes/prql-theme/static/icon.svg rename to web/website/themes/prql-theme/static/icon.svg diff --git a/website/themes/prql-theme/static/main.js b/web/website/themes/prql-theme/static/main.js similarity index 100% rename from website/themes/prql-theme/static/main.js rename to web/website/themes/prql-theme/static/main.js diff --git a/web/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js b/web/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js new file mode 100644 index 000000000000..be8801f6ab34 --- /dev/null +++ b/web/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.1.3 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;sfunction(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!l||!l[r])return;return void I(t,l,r,o,s?i:null)}c&&Object.keys(l).forEach((i=>{!function(t,e,i,n){const s=e[i]||{};Object.keys(s).forEach((o=>{if(o.includes(n)){const n=s[o];I(t,e,i,n.originalHandler,n.delegationSelector)}}))}(t,l,i,e.slice(1))}));const h=l[r]||{};Object.keys(h).forEach((i=>{const n=i.replace(E,"");if(!a||e.includes(n)){const e=h[i];I(t,l,r,e.originalHandler,e.delegationSelector)}}))},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=f(),s=P(e),o=e!==s,r=k.has(s);let a,l=!0,c=!0,h=!1,d=null;return o&&n&&(a=n.Event(e,i),n(t).trigger(a),l=!a.isPropagationStopped(),c=!a.isImmediatePropagationStopped(),h=a.isDefaultPrevented()),r?(d=document.createEvent("HTMLEvents"),d.initEvent(s,l,!0)):d=new CustomEvent(e,{bubbles:l,cancelable:!0}),void 0!==i&&Object.keys(i).forEach((t=>{Object.defineProperty(d,t,{get:()=>i[t]})})),h&&d.preventDefault(),c&&t.dispatchEvent(d),d.defaultPrevented&&void 0!==a&&a.preventDefault(),d}},M=new Map,H={set(t,e,i){M.has(t)||M.set(t,new Map);const n=M.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>M.has(t)&&M.get(t).get(e)||null,remove(t,e){if(!M.has(t))return;const i=M.get(t);i.delete(e),0===i.size&&M.delete(t)}};class B{constructor(t){(t=r(t))&&(this._element=t,H.set(this._element,this.constructor.DATA_KEY,this))}dispose(){H.remove(this._element,this.constructor.DATA_KEY),j.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach((t=>{this[t]=null}))}_queueCallback(t,e,i=!0){b(t,e,i)}static getInstance(t){return H.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.1.3"}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}}const R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;j.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),c(this))return;const o=n(this)||this.closest(`.${s}`);t.getOrCreateInstance(o)[e]()}))};class W extends B{static get NAME(){return"alert"}close(){if(j.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),j.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=W.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(W,"close"),g(W);const $='[data-bs-toggle="button"]';class z extends B{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=z.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}function q(t){return"true"===t||"false"!==t&&(t===Number(t).toString()?Number(t):""===t||"null"===t?null:t)}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}j.on(document,"click.bs.button.data-api",$,(t=>{t.preventDefault();const e=t.target.closest($);z.getOrCreateInstance(e).toggle()})),g(z);const U={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={};return Object.keys(t.dataset).filter((t=>t.startsWith("bs"))).forEach((i=>{let n=i.replace(/^bs/,"");n=n.charAt(0).toLowerCase()+n.slice(1,n.length),e[n]=q(t.dataset[i])})),e},getDataAttribute:(t,e)=>q(t.getAttribute(`data-bs-${F(e)}`)),offset(t){const e=t.getBoundingClientRect();return{top:e.top+window.pageYOffset,left:e.left+window.pageXOffset}},position:t=>({top:t.offsetTop,left:t.offsetLeft})},V={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(", ");return this.find(e,t).filter((t=>!c(t)&&l(t)))}},K="carousel",X={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},Y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},Q="next",G="prev",Z="left",J="right",tt={ArrowLeft:J,ArrowRight:Z},et="slid.bs.carousel",it="active",nt=".active.carousel-item";class st extends B{constructor(t,e){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._indicatorsElement=V.findOne(".carousel-indicators",this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent),this._addEventListeners()}static get Default(){return X}static get NAME(){return K}next(){this._slide(Q)}nextWhenVisible(){!document.hidden&&l(this._element)&&this.next()}prev(){this._slide(G)}pause(t){t||(this._isPaused=!0),V.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(s(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=V.findOne(nt,this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding)return void j.one(this._element,et,(()=>this.to(t)));if(e===t)return this.pause(),void this.cycle();const i=t>e?Q:G;this._slide(i,this._items[t])}_getConfig(t){return t={...X,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(K,t,Y),t}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=40)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?J:Z)}_addEventListeners(){this._config.keyboard&&j.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(j.on(this._element,"mouseenter.bs.carousel",(t=>this.pause(t))),j.on(this._element,"mouseleave.bs.carousel",(t=>this.cycle(t)))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()}_addTouchEventListeners(){const t=t=>this._pointerEvent&&("pen"===t.pointerType||"touch"===t.pointerType),e=e=>{t(e)?this.touchStartX=e.clientX:this._pointerEvent||(this.touchStartX=e.touches[0].clientX)},i=t=>{this.touchDeltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this.touchStartX},n=e=>{t(e)&&(this.touchDeltaX=e.clientX-this.touchStartX),this._handleSwipe(),"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((t=>this.cycle(t)),500+this._config.interval))};V.find(".carousel-item img",this._element).forEach((t=>{j.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()))})),this._pointerEvent?(j.on(this._element,"pointerdown.bs.carousel",(t=>e(t))),j.on(this._element,"pointerup.bs.carousel",(t=>n(t))),this._element.classList.add("pointer-event")):(j.on(this._element,"touchstart.bs.carousel",(t=>e(t))),j.on(this._element,"touchmove.bs.carousel",(t=>i(t))),j.on(this._element,"touchend.bs.carousel",(t=>n(t))))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=tt[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?V.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const i=t===Q;return v(this._items,e,i,this._config.wrap)}_triggerSlideEvent(t,e){const i=this._getItemIndex(t),n=this._getItemIndex(V.findOne(nt,this._element));return j.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:n,to:i})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=V.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=V.find("[data-bs-target]",this._indicatorsElement);for(let e=0;e{j.trigger(this._element,et,{relatedTarget:o,direction:d,from:s,to:r})};if(this._element.classList.contains("slide")){o.classList.add(h),u(o),n.classList.add(c),o.classList.add(c);const t=()=>{o.classList.remove(c,h),o.classList.add(it),n.classList.remove(it,h,c),this._isSliding=!1,setTimeout(f,0)};this._queueCallback(t,n,!0)}else n.classList.remove(it),o.classList.add(it),this._isSliding=!1,f();a&&this.cycle()}_directionToOrder(t){return[J,Z].includes(t)?m()?t===Z?G:Q:t===Z?Q:G:t}_orderToDirection(t){return[Q,G].includes(t)?m()?t===G?Z:J:t===G?J:Z:t}static carouselInterface(t,e){const i=st.getOrCreateInstance(t,e);let{_config:n}=i;"object"==typeof e&&(n={...n,...e});const s="string"==typeof e?e:n.slide;if("number"==typeof e)i.to(e);else if("string"==typeof s){if(void 0===i[s])throw new TypeError(`No method named "${s}"`);i[s]()}else n.interval&&n.ride&&(i.pause(),i.cycle())}static jQueryInterface(t){return this.each((function(){st.carouselInterface(this,t)}))}static dataApiClickHandler(t){const e=n(this);if(!e||!e.classList.contains("carousel"))return;const i={...U.getDataAttributes(e),...U.getDataAttributes(this)},s=this.getAttribute("data-bs-slide-to");s&&(i.interval=!1),st.carouselInterface(e,i),s&&st.getInstance(e).to(s),t.preventDefault()}}j.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",st.dataApiClickHandler),j.on(window,"load.bs.carousel.data-api",(()=>{const t=V.find('[data-bs-ride="carousel"]');for(let e=0,i=t.length;et===this._element));null!==s&&o.length&&(this._selector=s,this._triggerArray.push(e))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return rt}static get NAME(){return ot}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t,e=[];if(this._config.parent){const t=V.find(ut,this._config.parent);e=V.find(".collapse.show, .collapse.collapsing",this._config.parent).filter((e=>!t.includes(e)))}const i=V.findOne(this._selector);if(e.length){const n=e.find((t=>i!==t));if(t=n?pt.getInstance(n):null,t&&t._isTransitioning)return}if(j.trigger(this._element,"show.bs.collapse").defaultPrevented)return;e.forEach((e=>{i!==e&&pt.getOrCreateInstance(e,{toggle:!1}).hide(),t||H.set(e,"bs.collapse",null)}));const n=this._getDimension();this._element.classList.remove(ct),this._element.classList.add(ht),this._element.style[n]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s=`scroll${n[0].toUpperCase()+n.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct,lt),this._element.style[n]="",j.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[n]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(j.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,u(this._element),this._element.classList.add(ht),this._element.classList.remove(ct,lt);const e=this._triggerArray.length;for(let t=0;t{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct),j.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(lt)}_getConfig(t){return(t={...rt,...U.getDataAttributes(this._element),...t}).toggle=Boolean(t.toggle),t.parent=r(t.parent),a(ot,t,at),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=V.find(ut,this._config.parent);V.find(ft,this._config.parent).filter((e=>!t.includes(e))).forEach((t=>{const e=n(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}))}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach((t=>{e?t.classList.remove(dt):t.classList.add(dt),t.setAttribute("aria-expanded",e)}))}static jQueryInterface(t){return this.each((function(){const e={};"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1);const i=pt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}j.on(document,"click.bs.collapse.data-api",ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const e=i(this);V.find(e).forEach((t=>{pt.getOrCreateInstance(t,{toggle:!1}).toggle()}))})),g(pt);var mt="top",gt="bottom",_t="right",bt="left",vt="auto",yt=[mt,gt,_t,bt],wt="start",Et="end",At="clippingParents",Tt="viewport",Ot="popper",Ct="reference",kt=yt.reduce((function(t,e){return t.concat([e+"-"+wt,e+"-"+Et])}),[]),Lt=[].concat(yt,[vt]).reduce((function(t,e){return t.concat([e,e+"-"+wt,e+"-"+Et])}),[]),xt="beforeRead",Dt="read",St="afterRead",Nt="beforeMain",It="main",Pt="afterMain",jt="beforeWrite",Mt="write",Ht="afterWrite",Bt=[xt,Dt,St,Nt,It,Pt,jt,Mt,Ht];function Rt(t){return t?(t.nodeName||"").toLowerCase():null}function Wt(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function $t(t){return t instanceof Wt(t).Element||t instanceof Element}function zt(t){return t instanceof Wt(t).HTMLElement||t instanceof HTMLElement}function qt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof Wt(t).ShadowRoot||t instanceof ShadowRoot)}const Ft={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];zt(s)&&Rt(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});zt(n)&&Rt(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function Ut(t){return t.split("-")[0]}function Vt(t,e){var i=t.getBoundingClientRect();return{width:i.width/1,height:i.height/1,top:i.top/1,right:i.right/1,bottom:i.bottom/1,left:i.left/1,x:i.left/1,y:i.top/1}}function Kt(t){var e=Vt(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Xt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&qt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Yt(t){return Wt(t).getComputedStyle(t)}function Qt(t){return["table","td","th"].indexOf(Rt(t))>=0}function Gt(t){return(($t(t)?t.ownerDocument:t.document)||window.document).documentElement}function Zt(t){return"html"===Rt(t)?t:t.assignedSlot||t.parentNode||(qt(t)?t.host:null)||Gt(t)}function Jt(t){return zt(t)&&"fixed"!==Yt(t).position?t.offsetParent:null}function te(t){for(var e=Wt(t),i=Jt(t);i&&Qt(i)&&"static"===Yt(i).position;)i=Jt(i);return i&&("html"===Rt(i)||"body"===Rt(i)&&"static"===Yt(i).position)?e:i||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&zt(t)&&"fixed"===Yt(t).position)return null;for(var i=Zt(t);zt(i)&&["html","body"].indexOf(Rt(i))<0;){var n=Yt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function ee(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}var ie=Math.max,ne=Math.min,se=Math.round;function oe(t,e,i){return ie(t,ne(e,i))}function re(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function ae(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const le={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=Ut(i.placement),l=ee(a),c=[bt,_t].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return re("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:ae(t,yt))}(s.padding,i),d=Kt(o),u="y"===l?mt:bt,f="y"===l?gt:_t,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=te(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,E=oe(v,w,y),A=l;i.modifiersData[n]=((e={})[A]=E,e.centerOffset=E-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Xt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ce(t){return t.split("-")[1]}var he={top:"auto",right:"auto",bottom:"auto",left:"auto"};function de(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=!0===h?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:se(se(e*n)/n)||0,y:se(se(i*n)/n)||0}}(r):"function"==typeof h?h(r):r,u=d.x,f=void 0===u?0:u,p=d.y,m=void 0===p?0:p,g=r.hasOwnProperty("x"),_=r.hasOwnProperty("y"),b=bt,v=mt,y=window;if(c){var w=te(i),E="clientHeight",A="clientWidth";w===Wt(i)&&"static"!==Yt(w=Gt(i)).position&&"absolute"===a&&(E="scrollHeight",A="scrollWidth"),w=w,s!==mt&&(s!==bt&&s!==_t||o!==Et)||(v=gt,m-=w[E]-n.height,m*=l?1:-1),s!==bt&&(s!==mt&&s!==gt||o!==Et)||(b=_t,f-=w[A]-n.width,f*=l?1:-1)}var T,O=Object.assign({position:a},c&&he);return l?Object.assign({},O,((T={})[v]=_?"0":"",T[b]=g?"0":"",T.transform=(y.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",T)):Object.assign({},O,((e={})[v]=_?m+"px":"",e[b]=g?f+"px":"",e.transform="",e))}const ue={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:Ut(e.placement),variation:ce(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,de(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,de(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var fe={passive:!0};const pe={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=Wt(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,fe)})),a&&l.addEventListener("resize",i.update,fe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,fe)})),a&&l.removeEventListener("resize",i.update,fe)}},data:{}};var me={left:"right",right:"left",bottom:"top",top:"bottom"};function ge(t){return t.replace(/left|right|bottom|top/g,(function(t){return me[t]}))}var _e={start:"end",end:"start"};function be(t){return t.replace(/start|end/g,(function(t){return _e[t]}))}function ve(t){var e=Wt(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ye(t){return Vt(Gt(t)).left+ve(t).scrollLeft}function we(t){var e=Yt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Rt(t))>=0?t.ownerDocument.body:zt(t)&&we(t)?t:Ee(Zt(t))}function Ae(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=Wt(n),r=s?[o].concat(o.visualViewport||[],we(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Ae(Zt(r)))}function Te(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e){return e===Tt?Te(function(t){var e=Wt(t),i=Gt(t),n=e.visualViewport,s=i.clientWidth,o=i.clientHeight,r=0,a=0;return n&&(s=n.width,o=n.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(r=n.offsetLeft,a=n.offsetTop)),{width:s,height:o,x:r+ye(t),y:a}}(t)):zt(e)?function(t){var e=Vt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Te(function(t){var e,i=Gt(t),n=ve(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ie(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ie(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ye(t),l=-n.scrollTop;return"rtl"===Yt(s||i).direction&&(a+=ie(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Gt(t)))}function Ce(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?Ut(s):null,r=s?ce(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;case(o){case mt:e={x:a,y:i.y-n.height};break;case gt:e={x:a,y:i.y+i.height};break;case _t:e={x:i.x+i.width,y:l};break;case bt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?ee(o):null;if(null!=c){var h="y"===c?"height":"width";case(r){case wt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Et:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.boundary,r=void 0===o?At:o,a=i.rootBoundary,l=void 0===a?Tt:a,c=i.elementContext,h=void 0===c?Ot:c,d=i.altBoundary,u=void 0!==d&&d,f=i.padding,p=void 0===f?0:f,m=re("number"!=typeof p?p:ae(p,yt)),g=h===Ot?Ct:Ot,_=t.rects.popper,b=t.elements[u?g:h],v=function(t,e,i){var n="clippingParents"===e?function(t){var e=Ae(Zt(t)),i=["absolute","fixed"].indexOf(Yt(t).position)>=0&&zt(t)?te(t):t;return $t(i)?e.filter((function(t){return $t(t)&&Xt(t,i)&&"body"!==Rt(t)})):[]}(t):[].concat(e),s=[].concat(n,[i]),o=s[0],r=s.reduce((function(e,i){var n=Oe(t,i);return e.top=ie(n.top,e.top),e.right=ne(n.right,e.right),e.bottom=ne(n.bottom,e.bottom),e.left=ie(n.left,e.left),e}),Oe(t,o));return r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}($t(b)?b:b.contextElement||Gt(t.elements.popper),r,l),y=Vt(t.elements.reference),w=Ce({reference:y,element:_,strategy:"absolute",placement:s}),E=Te(Object.assign({},_,w)),A=h===Ot?E:y,T={top:v.top-A.top+m.top,bottom:A.bottom-v.bottom+m.bottom,left:v.left-A.left+m.left,right:A.right-v.right+m.right},O=t.modifiersData.offset;if(h===Ot&&O){var C=O[s];Object.keys(T).forEach((function(t){var e=[_t,gt].indexOf(t)>=0?1:-1,i=[mt,gt].indexOf(t)>=0?"y":"x";T[t]+=C[i]*e}))}return T}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?Lt:l,h=ce(n),d=h?a?kt:kt.filter((function(t){return ce(t)===h})):yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[Ut(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const xe={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=Ut(g),b=l||(_!==g&&p?function(t){if(Ut(t)===vt)return[];var e=ge(t);return[be(t),e,be(e)]}(g):[ge(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(Ut(i)===vt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,A=!0,T=v[0],O=0;O=0,D=x?"width":"height",S=ke(e,{placement:C,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),N=x?L?_t:bt:L?gt:mt;y[D]>w[D]&&(N=ge(N));var I=ge(N),P=[];if(o&&P.push(S[k]<=0),a&&P.push(S[N]<=0,S[I]<=0),P.every((function(t){return t}))){T=C,A=!1;break}E.set(C,P)}if(A)for(var j=function(t){var e=v.find((function(e){var i=E.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Se(t){return[mt,_t,gt,bt].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Se(l),d=Se(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Ie={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=Lt.reduce((function(t,i){return t[i]=function(t,e,i){var n=Ut(t),s=[bt,mt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[bt,_t].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},Pe={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=Ce({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},je={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=Ut(e.placement),b=ce(e.placement),v=!b,y=ee(_),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,A=e.rects.reference,T=e.rects.popper,O="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,C={x:0,y:0};if(E){if(o||a){var k="y"===y?mt:bt,L="y"===y?gt:_t,x="y"===y?"height":"width",D=E[y],S=E[y]+g[k],N=E[y]-g[L],I=f?-T[x]/2:0,P=b===wt?A[x]:T[x],j=b===wt?-T[x]:-A[x],M=e.elements.arrow,H=f&&M?Kt(M):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=B[k],W=B[L],$=oe(0,A[x],H[x]),z=v?A[x]/2-I-$-R-O:P-$-R-O,q=v?-A[x]/2+I+$+W+O:j+$+W+O,F=e.elements.arrow&&te(e.elements.arrow),U=F?"y"===y?F.clientTop||0:F.clientLeft||0:0,V=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,K=E[y]+z-V-U,X=E[y]+q-V;if(o){var Y=oe(f?ne(S,K):S,D,f?ie(N,X):N);E[y]=Y,C[y]=Y-D}if(a){var Q="x"===y?mt:bt,G="x"===y?gt:_t,Z=E[w],J=Z+g[Q],tt=Z-g[G],et=oe(f?ne(J,K):J,Z,f?ie(tt,X):tt);E[w]=et,C[w]=et-Z}}e.modifiersData[n]=C}},requiresIfExists:["offset"]};function Me(t,e,i){void 0===i&&(i=!1);var n=zt(e);zt(e)&&function(t){var e=t.getBoundingClientRect();e.width,t.offsetWidth,e.height,t.offsetHeight}(e);var s,o,r=Gt(e),a=Vt(t),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(n||!n&&!i)&&(("body"!==Rt(e)||we(r))&&(l=(s=e)!==Wt(s)&&zt(s)?{scrollLeft:(o=s).scrollLeft,scrollTop:o.scrollTop}:ve(s)),zt(e)?((c=Vt(e)).x+=e.clientLeft,c.y+=e.clientTop):r&&(c.x=ye(r))),{x:a.left+l.scrollLeft-c.x,y:a.top+l.scrollTop-c.y,width:a.width,height:a.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Be={placement:"bottom",modifiers:[],strategy:"absolute"};function Re(){for(var t=arguments.length,e=new Array(t),i=0;ij.on(t,"mouseover",d))),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Je),this._element.classList.add(Je),j.trigger(this._element,"shown.bs.dropdown",t)}hide(){if(c(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){j.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._popper&&this._popper.destroy(),this._menu.classList.remove(Je),this._element.classList.remove(Je),this._element.setAttribute("aria-expanded","false"),U.removeDataAttribute(this._menu,"popper"),j.trigger(this._element,"hidden.bs.dropdown",t))}_getConfig(t){if(t={...this.constructor.Default,...U.getDataAttributes(this._element),...t},a(Ue,t,this.constructor.DefaultType),"object"==typeof t.reference&&!o(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ue.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(t){if(void 0===Fe)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=t:o(this._config.reference)?e=r(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const i=this._getPopperConfig(),n=i.modifiers.find((t=>"applyStyles"===t.name&&!1===t.enabled));this._popper=qe(e,this._menu,i),n&&U.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.classList.contains(Je)}_getMenuElement(){return V.next(this._element,ei)[0]}_getPlacement(){const t=this._element.parentNode;if(t.classList.contains("dropend"))return ri;if(t.classList.contains("dropstart"))return ai;const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ni:ii:e?oi:si}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return"static"===this._config.display&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=V.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(l);i.length&&v(i,e,t===Ye,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(t&&(2===t.button||"keyup"===t.type&&"Tab"!==t.key))return;const e=V.find(ti);for(let i=0,n=e.length;ie+t)),this._setElementAttributes(di,"paddingRight",(e=>e+t)),this._setElementAttributes(ui,"marginRight",(e=>e-t))}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t)[e];t.style[e]=`${i(Number.parseFloat(s))}px`}))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(di,"paddingRight"),this._resetElementAttributes(ui,"marginRight")}_saveInitialAttribute(t,e){const i=t.style[e];i&&U.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=U.getDataAttribute(t,e);void 0===i?t.style.removeProperty(e):(U.removeDataAttribute(t,e),t.style[e]=i)}))}_applyManipulationCallback(t,e){o(t)?e(t):V.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const pi={className:"modal-backdrop",isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null},mi={className:"string",isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)"},gi="show",_i="mousedown.bs.backdrop";class bi{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){this._config.isVisible?(this._append(),this._config.isAnimated&&u(this._getElement()),this._getElement().classList.add(gi),this._emulateAnimation((()=>{_(t)}))):_(t)}hide(t){this._config.isVisible?(this._getElement().classList.remove(gi),this._emulateAnimation((()=>{this.dispose(),_(t)}))):_(t)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_getConfig(t){return(t={...pi,..."object"==typeof t?t:{}}).rootElement=r(t.rootElement),a("backdrop",t,mi),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),j.on(this._getElement(),_i,(()=>{_(this._config.clickCallback)})),this._isAppended=!0)}dispose(){this._isAppended&&(j.off(this._element,_i),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){b(t,this._getElement(),this._config.isAnimated)}}const vi={trapElement:null,autofocus:!0},yi={trapElement:"element",autofocus:"boolean"},wi=".bs.focustrap",Ei="backward";class Ai{constructor(t){this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}activate(){const{trapElement:t,autofocus:e}=this._config;this._isActive||(e&&t.focus(),j.off(document,wi),j.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),j.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,j.off(document,wi))}_handleFocusin(t){const{target:e}=t,{trapElement:i}=this._config;if(e===document||e===i||i.contains(e))return;const n=V.focusableChildren(i);0===n.length?i.focus():this._lastTabNavDirection===Ei?n[n.length-1].focus():n[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Ei:"forward")}_getConfig(t){return t={...vi,..."object"==typeof t?t:{}},a("focustrap",t,yi),t}}const Ti="modal",Oi="Escape",Ci={backdrop:!0,keyboard:!0,focus:!0},ki={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"},Li="hidden.bs.modal",xi="show.bs.modal",Di="resize.bs.modal",Si="click.dismiss.bs.modal",Ni="keydown.dismiss.bs.modal",Ii="mousedown.dismiss.bs.modal",Pi="modal-open",ji="show",Mi="modal-static";class Hi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._dialog=V.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollBar=new fi}static get Default(){return Ci}static get NAME(){return Ti}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||j.trigger(this._element,xi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.classList.add(Pi),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),j.on(this._dialog,Ii,(()=>{j.one(this._element,"mouseup.dismiss.bs.modal",(t=>{t.target===this._element&&(this._ignoreBackdropClick=!0)}))})),this._showBackdrop((()=>this._showElement(t))))}hide(){if(!this._isShown||this._isTransitioning)return;if(j.trigger(this._element,"hide.bs.modal").defaultPrevented)return;this._isShown=!1;const t=this._isAnimated();t&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.deactivate(),this._element.classList.remove(ji),j.off(this._element,Si),j.off(this._dialog,Ii),this._queueCallback((()=>this._hideModal()),this._element,t)}dispose(){[window,this._dialog].forEach((t=>j.off(t,".bs.modal"))),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new bi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_getConfig(t){return t={...Ci,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Ti,t,ki),t}_showElement(t){const e=this._isAnimated(),i=V.findOne(".modal-body",this._dialog);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0,i&&(i.scrollTop=0),e&&u(this._element),this._element.classList.add(ji),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,j.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,e)}_setEscapeEvent(){this._isShown?j.on(this._element,Ni,(t=>{this._config.keyboard&&t.key===Oi?(t.preventDefault(),this.hide()):this._config.keyboard||t.key!==Oi||this._triggerBackdropTransition()})):j.off(this._element,Ni)}_setResizeEvent(){this._isShown?j.on(window,Di,(()=>this._adjustDialog())):j.off(window,Di)}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Pi),this._resetAdjustments(),this._scrollBar.reset(),j.trigger(this._element,Li)}))}_showBackdrop(t){j.on(this._element,Si,(t=>{this._ignoreBackdropClick?this._ignoreBackdropClick=!1:t.target===t.currentTarget&&(!0===this._config.backdrop?this.hide():"static"===this._config.backdrop&&this._triggerBackdropTransition())})),this._backdrop.show(t)}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(j.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const{classList:t,scrollHeight:e,style:i}=this._element,n=e>document.documentElement.clientHeight;!n&&"hidden"===i.overflowY||t.contains(Mi)||(n||(i.overflowY="hidden"),t.add(Mi),this._queueCallback((()=>{t.remove(Mi),n||this._queueCallback((()=>{i.overflowY=""}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;(!i&&t&&!m()||i&&!t&&m())&&(this._element.style.paddingLeft=`${e}px`),(i&&!t&&!m()||!i&&t&&m())&&(this._element.style.paddingRight=`${e}px`)}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}j.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=n(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),j.one(e,xi,(t=>{t.defaultPrevented||j.one(e,Li,(()=>{l(this)&&this.focus()}))}));const i=V.findOne(".modal.show");i&&Hi.getInstance(i).hide(),Hi.getOrCreateInstance(e).toggle(this)})),R(Hi),g(Hi);const Bi="offcanvas",Ri={backdrop:!0,keyboard:!0,scroll:!1},Wi={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"},$i="show",zi=".offcanvas.show",qi="hidden.bs.offcanvas";class Fi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get NAME(){return Bi}static get Default(){return Ri}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||j.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||(new fi).hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add($i),this._queueCallback((()=>{this._config.scroll||this._focustrap.activate(),j.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(j.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.remove($i),this._backdrop.hide(),this._queueCallback((()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||(new fi).reset(),j.trigger(this._element,qi)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_getConfig(t){return t={...Ri,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Bi,t,Wi),t}_initializeBackDrop(){return new bi({className:"offcanvas-backdrop",isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_addEventListeners(){j.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{this._config.keyboard&&"Escape"===t.key&&this.hide()}))}static jQueryInterface(t){return this.each((function(){const e=Fi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}j.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=n(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this))return;j.one(e,qi,(()=>{l(this)&&this.focus()}));const i=V.findOne(zi);i&&i!==e&&Fi.getInstance(i).hide(),Fi.getOrCreateInstance(e).toggle(this)})),j.on(window,"load.bs.offcanvas.data-api",(()=>V.find(zi).forEach((t=>Fi.getOrCreateInstance(t).show())))),R(Fi),g(Fi);const Ui=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Vi=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Ki=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,Xi=(t,e)=>{const i=t.nodeName.toLowerCase();if(e.includes(i))return!Ui.has(i)||Boolean(Vi.test(t.nodeValue)||Ki.test(t.nodeValue));const n=e.filter((t=>t instanceof RegExp));for(let t=0,e=n.length;t{Xi(t,r)||i.removeAttribute(t.nodeName)}))}return n.body.innerHTML}const Qi="tooltip",Gi=new Set(["sanitize","allowList","sanitizeFn"]),Zi={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},Ji={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},tn={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},en={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},nn="fade",sn="show",on="show",rn="out",an=".tooltip-inner",ln=".modal",cn="hide.bs.modal",hn="hover",dn="focus";class un extends B{constructor(t,e){if(void 0===Fe)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return tn}static get NAME(){return Qi}static get Event(){return en}static get DefaultType(){return Zi}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains(sn))return void this._leave(null,this);this._enter(null,this)}}dispose(){clearTimeout(this._timeout),j.off(this._element.closest(ln),cn,this._hideModalHandler),this.tip&&this.tip.remove(),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this.isWithContent()||!this._isEnabled)return;const t=j.trigger(this._element,this.constructor.Event.SHOW),e=h(this._element),i=null===e?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!i)return;"tooltip"===this.constructor.NAME&&this.tip&&this.getTitle()!==this.tip.querySelector(an).innerHTML&&(this._disposePopper(),this.tip.remove(),this.tip=null);const n=this.getTipElement(),s=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME);n.setAttribute("id",s),this._element.setAttribute("aria-describedby",s),this._config.animation&&n.classList.add(nn);const o="function"==typeof this._config.placement?this._config.placement.call(this,n,this._element):this._config.placement,r=this._getAttachment(o);this._addAttachmentClass(r);const{container:a}=this._config;H.set(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(a.append(n),j.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=qe(this._element,n,this._getPopperConfig(r)),n.classList.add(sn);const l=this._resolvePossibleFunction(this._config.customClass);l&&n.classList.add(...l.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>{j.on(t,"mouseover",d)}));const c=this.tip.classList.contains(nn);this._queueCallback((()=>{const t=this._hoverState;this._hoverState=null,j.trigger(this._element,this.constructor.Event.SHOWN),t===rn&&this._leave(null,this)}),this.tip,c)}hide(){if(!this._popper)return;const t=this.getTipElement();if(j.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.remove(sn),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1;const e=this.tip.classList.contains(nn);this._queueCallback((()=>{this._isWithActiveTrigger()||(this._hoverState!==on&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),j.trigger(this._element,this.constructor.Event.HIDDEN),this._disposePopper())}),this.tip,e),this._hoverState=""}update(){null!==this._popper&&this._popper.update()}isWithContent(){return Boolean(this.getTitle())}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove(nn,sn),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),an)}_sanitizeAndSetContent(t,e,i){const n=V.findOne(i,t);e||!n?this.setElementContent(n,e):n.remove()}setElementContent(t,e){if(null!==t)return o(e)?(e=r(e),void(this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent)):void(this._config.html?(this._config.sanitize&&(e=Yi(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e)}getTitle(){const t=this._element.getAttribute("data-bs-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return"right"===t?"end":"left"===t?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return"function"==typeof t?t.call(this._element):t}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:t=>this._handlePopperPlacementChange(t)}],onFirstUpdate:t=>{t.options.placement!==t.placement&&this._handlePopperPlacementChange(t)}};return{...e,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return Ji[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach((t=>{if("click"===t)j.on(this._element,this.constructor.Event.CLICK,this._config.selector,(t=>this.toggle(t)));else if("manual"!==t){const e=t===hn?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,i=t===hn?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;j.on(this._element,e,this._config.selector,(t=>this._enter(t))),j.on(this._element,i,this._config.selector,(t=>this._leave(t)))}})),this._hideModalHandler=()=>{this._element&&this.hide()},j.on(this._element.closest(ln),cn,this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?dn:hn]=!0),e.getTipElement().classList.contains(sn)||e._hoverState===on?e._hoverState=on:(clearTimeout(e._timeout),e._hoverState=on,e._config.delay&&e._config.delay.show?e._timeout=setTimeout((()=>{e._hoverState===on&&e.show()}),e._config.delay.show):e.show())}_leave(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?dn:hn]=e._element.contains(t.relatedTarget)),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=rn,e._config.delay&&e._config.delay.hide?e._timeout=setTimeout((()=>{e._hoverState===rn&&e.hide()}),e._config.delay.hide):e.hide())}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=U.getDataAttributes(this._element);return Object.keys(e).forEach((t=>{Gi.has(t)&&delete e[t]})),(t={...this.constructor.Default,...e,..."object"==typeof t&&t?t:{}}).container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a(Qi,t,this.constructor.DefaultType),t.sanitize&&(t.template=Yi(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),i=t.getAttribute("class").match(e);null!==i&&i.length>0&&i.map((t=>t.trim())).forEach((e=>t.classList.remove(e)))}_getBasicClassPrefix(){return"bs-tooltip"}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each((function(){const e=un.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(un);const fn={...un.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:''},pn={...un.DefaultType,content:"(string|element|function)"},mn={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"};class gn extends un{static get Default(){return fn}static get NAME(){return"popover"}static get Event(){return mn}static get DefaultType(){return pn}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".popover-header"),this._sanitizeAndSetContent(t,this._getContent(),".popover-body")}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return"bs-popover"}static jQueryInterface(t){return this.each((function(){const e=gn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(gn);const _n="scrollspy",bn={offset:10,method:"auto",target:""},vn={offset:"number",method:"string",target:"(string|element)"},yn="active",wn=".nav-link, .list-group-item, .dropdown-item",En="position";class An extends B{constructor(t,e){super(t),this._scrollElement="BODY"===this._element.tagName?window:this._element,this._config=this._getConfig(e),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,j.on(this._scrollElement,"scroll.bs.scrollspy",(()=>this._process())),this.refresh(),this._process()}static get Default(){return bn}static get NAME(){return _n}refresh(){const t=this._scrollElement===this._scrollElement.window?"offset":En,e="auto"===this._config.method?t:this._config.method,n=e===En?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),V.find(wn,this._config.target).map((t=>{const s=i(t),o=s?V.findOne(s):null;if(o){const t=o.getBoundingClientRect();if(t.width||t.height)return[U[e](o).top+n,s]}return null})).filter((t=>t)).sort(((t,e)=>t[0]-e[0])).forEach((t=>{this._offsets.push(t[0]),this._targets.push(t[1])}))}dispose(){j.off(this._scrollElement,".bs.scrollspy"),super.dispose()}_getConfig(t){return(t={...bn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}}).target=r(t.target)||document.documentElement,a(_n,t,vn),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),i=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=i){const t=this._targets[this._targets.length-1];this._activeTarget!==t&&this._activate(t)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(let e=this._offsets.length;e--;)this._activeTarget!==this._targets[e]&&t>=this._offsets[e]&&(void 0===this._offsets[e+1]||t`${e}[data-bs-target="${t}"],${e}[href="${t}"]`)),i=V.findOne(e.join(","),this._config.target);i.classList.add(yn),i.classList.contains("dropdown-item")?V.findOne(".dropdown-toggle",i.closest(".dropdown")).classList.add(yn):V.parents(i,".nav, .list-group").forEach((t=>{V.prev(t,".nav-link, .list-group-item").forEach((t=>t.classList.add(yn))),V.prev(t,".nav-item").forEach((t=>{V.children(t,".nav-link").forEach((t=>t.classList.add(yn)))}))})),j.trigger(this._scrollElement,"activate.bs.scrollspy",{relatedTarget:t})}_clear(){V.find(wn,this._config.target).filter((t=>t.classList.contains(yn))).forEach((t=>t.classList.remove(yn)))}static jQueryInterface(t){return this.each((function(){const e=An.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(window,"load.bs.scrollspy.data-api",(()=>{V.find('[data-bs-spy="scroll"]').forEach((t=>new An(t)))})),g(An);const Tn="active",On="fade",Cn="show",kn=".active",Ln=":scope > li > .active";class xn extends B{static get NAME(){return"tab"}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.classList.contains(Tn))return;let t;const e=n(this._element),i=this._element.closest(".nav, .list-group");if(i){const e="UL"===i.nodeName||"OL"===i.nodeName?Ln:kn;t=V.find(e,i),t=t[t.length-1]}const s=t?j.trigger(t,"hide.bs.tab",{relatedTarget:this._element}):null;if(j.trigger(this._element,"show.bs.tab",{relatedTarget:t}).defaultPrevented||null!==s&&s.defaultPrevented)return;this._activate(this._element,i);const o=()=>{j.trigger(t,"hidden.bs.tab",{relatedTarget:this._element}),j.trigger(this._element,"shown.bs.tab",{relatedTarget:t})};e?this._activate(e,e.parentNode,o):o()}_activate(t,e,i){const n=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?V.children(e,kn):V.find(Ln,e))[0],s=i&&n&&n.classList.contains(On),o=()=>this._transitionComplete(t,n,i);n&&s?(n.classList.remove(Cn),this._queueCallback(o,t,!0)):o()}_transitionComplete(t,e,i){if(e){e.classList.remove(Tn);const t=V.findOne(":scope > .dropdown-menu .active",e.parentNode);t&&t.classList.remove(Tn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add(Tn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u(t),t.classList.contains(On)&&t.classList.add(Cn);let n=t.parentNode;if(n&&"LI"===n.nodeName&&(n=n.parentNode),n&&n.classList.contains("dropdown-menu")){const e=t.closest(".dropdown");e&&V.find(".dropdown-toggle",e).forEach((t=>t.classList.add(Tn))),t.setAttribute("aria-expanded",!0)}i&&i()}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this)||xn.getOrCreateInstance(this).show()})),g(xn);const Dn="toast",Sn="hide",Nn="show",In="showing",Pn={animation:"boolean",autohide:"boolean",delay:"number"},jn={animation:!0,autohide:!0,delay:5e3};class Mn extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get DefaultType(){return Pn}static get Default(){return jn}static get NAME(){return Dn}show(){j.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(Sn),u(this._element),this._element.classList.add(Nn),this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.remove(In),j.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this._element.classList.contains(Nn)&&(j.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.add(Sn),this._element.classList.remove(In),this._element.classList.remove(Nn),j.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this._element.classList.contains(Nn)&&this._element.classList.remove(Nn),super.dispose()}_getConfig(t){return t={...jn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}},a(Dn,t,this.constructor.DefaultType),t}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){case(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){j.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),j.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Mn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Mn),g(Mn),{Alert:W,Button:z,Carousel:st,Collapse:pt,Dropdown:hi,Modal:Hi,Offcanvas:Fi,Popover:gn,ScrollSpy:An,Tab:xn,Toast:Mn,Tooltip:un}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map diff --git a/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.min.css b/web/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.min.css similarity index 100% rename from website/themes/prql-theme/static/plugins/bootstrap/bootstrap.min.css rename to web/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.min.css diff --git a/website/themes/prql-theme/static/plugins/highlight/highlight.css b/web/website/themes/prql-theme/static/plugins/highlight/highlight.css similarity index 100% rename from website/themes/prql-theme/static/plugins/highlight/highlight.css rename to web/website/themes/prql-theme/static/plugins/highlight/highlight.css diff --git a/website/themes/prql-theme/static/plugins/highlight/highlight.min.js b/web/website/themes/prql-theme/static/plugins/highlight/highlight.min.js similarity index 96% rename from website/themes/prql-theme/static/plugins/highlight/highlight.min.js rename to web/website/themes/prql-theme/static/plugins/highlight/highlight.min.js index 4bb7f08157d4..9c9d11726b67 100644 --- a/website/themes/prql-theme/static/plugins/highlight/highlight.min.js +++ b/web/website/themes/prql-theme/static/plugins/highlight/highlight.min.js @@ -304,7 +304,7 @@ anyNumberOfTimes:u};for(const t in A)"object"==typeof A[t]&&e.exports(A[t]) ;return Object.assign(t,A),t})({});return te}() ;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `javascript` grammar compiled for Highlight.js 11.7.0 */ (()=>{var e=(()=>{"use strict" -;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","module","global"],i=[].concat(r,t,s) +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","case","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","module","global"],i=[].concat(r,t,s) ;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ const a=e[0].length+e.index,t=e.input[a] @@ -372,7 +372,7 @@ relevance:0},{variants:[{begin:"<>",end:""},{ match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, "on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},O,{ -beginKeywords:"while if switch catch for"},{ +beginKeywords:"while if case catch for"},{ begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", returnBegin:!0,label:"func.def",contains:[S,o.inherit(o.TITLE_MODE,{begin:b, className:"title.function"})]},{match:/\.\.\./,relevance:0},x,{match:"\\$"+b, diff --git a/website/themes/prql-theme/static/plugins/highlight/prql.js b/web/website/themes/prql-theme/static/plugins/highlight/prql.js similarity index 97% rename from website/themes/prql-theme/static/plugins/highlight/prql.js rename to web/website/themes/prql-theme/static/plugins/highlight/prql.js index ed51a6923d3c..37632c50d1ea 100644 --- a/website/themes/prql-theme/static/plugins/highlight/prql.js +++ b/web/website/themes/prql-theme/static/plugins/highlight/prql.js @@ -23,7 +23,7 @@ formatting = function (hljs) { "union", "window", ]; - const BUILTIN_FUNCTIONS = ["switch", "in", "as"]; + const BUILTIN_FUNCTIONS = ["case", "in", "as"]; const KEYWORDS = ["func", "let", "prql"]; return { name: "PRQL", @@ -152,7 +152,7 @@ formatting = function (hljs) { { scope: "operator", match: - /(>)|(<)|(==)|(\+)|(\-)|(\/)|(\*)|(!=)|(<=)|(>=)|(\band\b)|(\bor\b)/, + /(>)|(<)|(==)|(\+)|(\-)|(\/)|(\*)|(!=)|(->)|(=>)|(<=)|(>=)|(\band\b)|(\bor\b)/, relevance: 10, }, { diff --git a/website/themes/prql-theme/static/style.css b/web/website/themes/prql-theme/static/style.css similarity index 98% rename from website/themes/prql-theme/static/style.css rename to web/website/themes/prql-theme/static/style.css index df439c136c00..77fe18979bfa 100644 --- a/website/themes/prql-theme/static/style.css +++ b/web/website/themes/prql-theme/static/style.css @@ -155,8 +155,9 @@ pre ::-webkit-scrollbar-thumb:hover { border-bottom: 1px solid rgba(0, 0, 0, 0.125); } -#header .logo img { +#header .logo { max-height: 50px; + text-decoration: none; } #header .logo h1 { @@ -287,7 +288,7 @@ pre ::-webkit-scrollbar-thumb:hover { --------------------------------------------------------------*/ .hero { width: 100%; - min-height: 100vh; + min-height: calc(100vh - 81px); background: #f2f9ff; background: repeating-linear-gradient( 45deg, @@ -297,7 +298,10 @@ pre ::-webkit-scrollbar-thumb:hover { #f2f9ff 300px ), linear-gradient(#daeaff, #f2f9ff); - padding-top: 100px; + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 0 fit-content; } .hero h4 { diff --git a/website/themes/prql-theme/theme.toml b/web/website/themes/prql-theme/theme.toml similarity index 100% rename from website/themes/prql-theme/theme.toml rename to web/website/themes/prql-theme/theme.toml diff --git a/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js b/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js deleted file mode 100644 index 6c0869bc3b2d..000000000000 --- a/website/themes/prql-theme/static/plugins/bootstrap/bootstrap.bundle.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;sfunction(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!l||!l[r])return;return void I(t,l,r,o,s?i:null)}c&&Object.keys(l).forEach((i=>{!function(t,e,i,n){const s=e[i]||{};Object.keys(s).forEach((o=>{if(o.includes(n)){const n=s[o];I(t,e,i,n.originalHandler,n.delegationSelector)}}))}(t,l,i,e.slice(1))}));const h=l[r]||{};Object.keys(h).forEach((i=>{const n=i.replace(E,"");if(!a||e.includes(n)){const e=h[i];I(t,l,r,e.originalHandler,e.delegationSelector)}}))},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=f(),s=P(e),o=e!==s,r=k.has(s);let a,l=!0,c=!0,h=!1,d=null;return o&&n&&(a=n.Event(e,i),n(t).trigger(a),l=!a.isPropagationStopped(),c=!a.isImmediatePropagationStopped(),h=a.isDefaultPrevented()),r?(d=document.createEvent("HTMLEvents"),d.initEvent(s,l,!0)):d=new CustomEvent(e,{bubbles:l,cancelable:!0}),void 0!==i&&Object.keys(i).forEach((t=>{Object.defineProperty(d,t,{get:()=>i[t]})})),h&&d.preventDefault(),c&&t.dispatchEvent(d),d.defaultPrevented&&void 0!==a&&a.preventDefault(),d}},M=new Map,H={set(t,e,i){M.has(t)||M.set(t,new Map);const n=M.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>M.has(t)&&M.get(t).get(e)||null,remove(t,e){if(!M.has(t))return;const i=M.get(t);i.delete(e),0===i.size&&M.delete(t)}};class B{constructor(t){(t=r(t))&&(this._element=t,H.set(this._element,this.constructor.DATA_KEY,this))}dispose(){H.remove(this._element,this.constructor.DATA_KEY),j.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach((t=>{this[t]=null}))}_queueCallback(t,e,i=!0){b(t,e,i)}static getInstance(t){return H.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.1.3"}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}}const R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;j.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),c(this))return;const o=n(this)||this.closest(`.${s}`);t.getOrCreateInstance(o)[e]()}))};class W extends B{static get NAME(){return"alert"}close(){if(j.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),j.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=W.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(W,"close"),g(W);const $='[data-bs-toggle="button"]';class z extends B{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=z.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}function q(t){return"true"===t||"false"!==t&&(t===Number(t).toString()?Number(t):""===t||"null"===t?null:t)}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}j.on(document,"click.bs.button.data-api",$,(t=>{t.preventDefault();const e=t.target.closest($);z.getOrCreateInstance(e).toggle()})),g(z);const U={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={};return Object.keys(t.dataset).filter((t=>t.startsWith("bs"))).forEach((i=>{let n=i.replace(/^bs/,"");n=n.charAt(0).toLowerCase()+n.slice(1,n.length),e[n]=q(t.dataset[i])})),e},getDataAttribute:(t,e)=>q(t.getAttribute(`data-bs-${F(e)}`)),offset(t){const e=t.getBoundingClientRect();return{top:e.top+window.pageYOffset,left:e.left+window.pageXOffset}},position:t=>({top:t.offsetTop,left:t.offsetLeft})},V={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(", ");return this.find(e,t).filter((t=>!c(t)&&l(t)))}},K="carousel",X={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},Y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},Q="next",G="prev",Z="left",J="right",tt={ArrowLeft:J,ArrowRight:Z},et="slid.bs.carousel",it="active",nt=".active.carousel-item";class st extends B{constructor(t,e){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._indicatorsElement=V.findOne(".carousel-indicators",this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent),this._addEventListeners()}static get Default(){return X}static get NAME(){return K}next(){this._slide(Q)}nextWhenVisible(){!document.hidden&&l(this._element)&&this.next()}prev(){this._slide(G)}pause(t){t||(this._isPaused=!0),V.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(s(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=V.findOne(nt,this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding)return void j.one(this._element,et,(()=>this.to(t)));if(e===t)return this.pause(),void this.cycle();const i=t>e?Q:G;this._slide(i,this._items[t])}_getConfig(t){return t={...X,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(K,t,Y),t}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=40)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?J:Z)}_addEventListeners(){this._config.keyboard&&j.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(j.on(this._element,"mouseenter.bs.carousel",(t=>this.pause(t))),j.on(this._element,"mouseleave.bs.carousel",(t=>this.cycle(t)))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()}_addTouchEventListeners(){const t=t=>this._pointerEvent&&("pen"===t.pointerType||"touch"===t.pointerType),e=e=>{t(e)?this.touchStartX=e.clientX:this._pointerEvent||(this.touchStartX=e.touches[0].clientX)},i=t=>{this.touchDeltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this.touchStartX},n=e=>{t(e)&&(this.touchDeltaX=e.clientX-this.touchStartX),this._handleSwipe(),"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((t=>this.cycle(t)),500+this._config.interval))};V.find(".carousel-item img",this._element).forEach((t=>{j.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()))})),this._pointerEvent?(j.on(this._element,"pointerdown.bs.carousel",(t=>e(t))),j.on(this._element,"pointerup.bs.carousel",(t=>n(t))),this._element.classList.add("pointer-event")):(j.on(this._element,"touchstart.bs.carousel",(t=>e(t))),j.on(this._element,"touchmove.bs.carousel",(t=>i(t))),j.on(this._element,"touchend.bs.carousel",(t=>n(t))))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=tt[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?V.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const i=t===Q;return v(this._items,e,i,this._config.wrap)}_triggerSlideEvent(t,e){const i=this._getItemIndex(t),n=this._getItemIndex(V.findOne(nt,this._element));return j.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:n,to:i})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=V.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=V.find("[data-bs-target]",this._indicatorsElement);for(let e=0;e{j.trigger(this._element,et,{relatedTarget:o,direction:d,from:s,to:r})};if(this._element.classList.contains("slide")){o.classList.add(h),u(o),n.classList.add(c),o.classList.add(c);const t=()=>{o.classList.remove(c,h),o.classList.add(it),n.classList.remove(it,h,c),this._isSliding=!1,setTimeout(f,0)};this._queueCallback(t,n,!0)}else n.classList.remove(it),o.classList.add(it),this._isSliding=!1,f();a&&this.cycle()}_directionToOrder(t){return[J,Z].includes(t)?m()?t===Z?G:Q:t===Z?Q:G:t}_orderToDirection(t){return[Q,G].includes(t)?m()?t===G?Z:J:t===G?J:Z:t}static carouselInterface(t,e){const i=st.getOrCreateInstance(t,e);let{_config:n}=i;"object"==typeof e&&(n={...n,...e});const s="string"==typeof e?e:n.slide;if("number"==typeof e)i.to(e);else if("string"==typeof s){if(void 0===i[s])throw new TypeError(`No method named "${s}"`);i[s]()}else n.interval&&n.ride&&(i.pause(),i.cycle())}static jQueryInterface(t){return this.each((function(){st.carouselInterface(this,t)}))}static dataApiClickHandler(t){const e=n(this);if(!e||!e.classList.contains("carousel"))return;const i={...U.getDataAttributes(e),...U.getDataAttributes(this)},s=this.getAttribute("data-bs-slide-to");s&&(i.interval=!1),st.carouselInterface(e,i),s&&st.getInstance(e).to(s),t.preventDefault()}}j.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",st.dataApiClickHandler),j.on(window,"load.bs.carousel.data-api",(()=>{const t=V.find('[data-bs-ride="carousel"]');for(let e=0,i=t.length;et===this._element));null!==s&&o.length&&(this._selector=s,this._triggerArray.push(e))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return rt}static get NAME(){return ot}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t,e=[];if(this._config.parent){const t=V.find(ut,this._config.parent);e=V.find(".collapse.show, .collapse.collapsing",this._config.parent).filter((e=>!t.includes(e)))}const i=V.findOne(this._selector);if(e.length){const n=e.find((t=>i!==t));if(t=n?pt.getInstance(n):null,t&&t._isTransitioning)return}if(j.trigger(this._element,"show.bs.collapse").defaultPrevented)return;e.forEach((e=>{i!==e&&pt.getOrCreateInstance(e,{toggle:!1}).hide(),t||H.set(e,"bs.collapse",null)}));const n=this._getDimension();this._element.classList.remove(ct),this._element.classList.add(ht),this._element.style[n]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s=`scroll${n[0].toUpperCase()+n.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct,lt),this._element.style[n]="",j.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[n]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(j.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,u(this._element),this._element.classList.add(ht),this._element.classList.remove(ct,lt);const e=this._triggerArray.length;for(let t=0;t{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct),j.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(lt)}_getConfig(t){return(t={...rt,...U.getDataAttributes(this._element),...t}).toggle=Boolean(t.toggle),t.parent=r(t.parent),a(ot,t,at),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=V.find(ut,this._config.parent);V.find(ft,this._config.parent).filter((e=>!t.includes(e))).forEach((t=>{const e=n(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}))}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach((t=>{e?t.classList.remove(dt):t.classList.add(dt),t.setAttribute("aria-expanded",e)}))}static jQueryInterface(t){return this.each((function(){const e={};"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1);const i=pt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}j.on(document,"click.bs.collapse.data-api",ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const e=i(this);V.find(e).forEach((t=>{pt.getOrCreateInstance(t,{toggle:!1}).toggle()}))})),g(pt);var mt="top",gt="bottom",_t="right",bt="left",vt="auto",yt=[mt,gt,_t,bt],wt="start",Et="end",At="clippingParents",Tt="viewport",Ot="popper",Ct="reference",kt=yt.reduce((function(t,e){return t.concat([e+"-"+wt,e+"-"+Et])}),[]),Lt=[].concat(yt,[vt]).reduce((function(t,e){return t.concat([e,e+"-"+wt,e+"-"+Et])}),[]),xt="beforeRead",Dt="read",St="afterRead",Nt="beforeMain",It="main",Pt="afterMain",jt="beforeWrite",Mt="write",Ht="afterWrite",Bt=[xt,Dt,St,Nt,It,Pt,jt,Mt,Ht];function Rt(t){return t?(t.nodeName||"").toLowerCase():null}function Wt(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function $t(t){return t instanceof Wt(t).Element||t instanceof Element}function zt(t){return t instanceof Wt(t).HTMLElement||t instanceof HTMLElement}function qt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof Wt(t).ShadowRoot||t instanceof ShadowRoot)}const Ft={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];zt(s)&&Rt(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});zt(n)&&Rt(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function Ut(t){return t.split("-")[0]}function Vt(t,e){var i=t.getBoundingClientRect();return{width:i.width/1,height:i.height/1,top:i.top/1,right:i.right/1,bottom:i.bottom/1,left:i.left/1,x:i.left/1,y:i.top/1}}function Kt(t){var e=Vt(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Xt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&qt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Yt(t){return Wt(t).getComputedStyle(t)}function Qt(t){return["table","td","th"].indexOf(Rt(t))>=0}function Gt(t){return(($t(t)?t.ownerDocument:t.document)||window.document).documentElement}function Zt(t){return"html"===Rt(t)?t:t.assignedSlot||t.parentNode||(qt(t)?t.host:null)||Gt(t)}function Jt(t){return zt(t)&&"fixed"!==Yt(t).position?t.offsetParent:null}function te(t){for(var e=Wt(t),i=Jt(t);i&&Qt(i)&&"static"===Yt(i).position;)i=Jt(i);return i&&("html"===Rt(i)||"body"===Rt(i)&&"static"===Yt(i).position)?e:i||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&zt(t)&&"fixed"===Yt(t).position)return null;for(var i=Zt(t);zt(i)&&["html","body"].indexOf(Rt(i))<0;){var n=Yt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function ee(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}var ie=Math.max,ne=Math.min,se=Math.round;function oe(t,e,i){return ie(t,ne(e,i))}function re(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function ae(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const le={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=Ut(i.placement),l=ee(a),c=[bt,_t].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return re("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:ae(t,yt))}(s.padding,i),d=Kt(o),u="y"===l?mt:bt,f="y"===l?gt:_t,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=te(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,E=oe(v,w,y),A=l;i.modifiersData[n]=((e={})[A]=E,e.centerOffset=E-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Xt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ce(t){return t.split("-")[1]}var he={top:"auto",right:"auto",bottom:"auto",left:"auto"};function de(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=!0===h?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:se(se(e*n)/n)||0,y:se(se(i*n)/n)||0}}(r):"function"==typeof h?h(r):r,u=d.x,f=void 0===u?0:u,p=d.y,m=void 0===p?0:p,g=r.hasOwnProperty("x"),_=r.hasOwnProperty("y"),b=bt,v=mt,y=window;if(c){var w=te(i),E="clientHeight",A="clientWidth";w===Wt(i)&&"static"!==Yt(w=Gt(i)).position&&"absolute"===a&&(E="scrollHeight",A="scrollWidth"),w=w,s!==mt&&(s!==bt&&s!==_t||o!==Et)||(v=gt,m-=w[E]-n.height,m*=l?1:-1),s!==bt&&(s!==mt&&s!==gt||o!==Et)||(b=_t,f-=w[A]-n.width,f*=l?1:-1)}var T,O=Object.assign({position:a},c&&he);return l?Object.assign({},O,((T={})[v]=_?"0":"",T[b]=g?"0":"",T.transform=(y.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",T)):Object.assign({},O,((e={})[v]=_?m+"px":"",e[b]=g?f+"px":"",e.transform="",e))}const ue={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:Ut(e.placement),variation:ce(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,de(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,de(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var fe={passive:!0};const pe={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=Wt(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,fe)})),a&&l.addEventListener("resize",i.update,fe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,fe)})),a&&l.removeEventListener("resize",i.update,fe)}},data:{}};var me={left:"right",right:"left",bottom:"top",top:"bottom"};function ge(t){return t.replace(/left|right|bottom|top/g,(function(t){return me[t]}))}var _e={start:"end",end:"start"};function be(t){return t.replace(/start|end/g,(function(t){return _e[t]}))}function ve(t){var e=Wt(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ye(t){return Vt(Gt(t)).left+ve(t).scrollLeft}function we(t){var e=Yt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Rt(t))>=0?t.ownerDocument.body:zt(t)&&we(t)?t:Ee(Zt(t))}function Ae(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=Wt(n),r=s?[o].concat(o.visualViewport||[],we(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Ae(Zt(r)))}function Te(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e){return e===Tt?Te(function(t){var e=Wt(t),i=Gt(t),n=e.visualViewport,s=i.clientWidth,o=i.clientHeight,r=0,a=0;return n&&(s=n.width,o=n.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(r=n.offsetLeft,a=n.offsetTop)),{width:s,height:o,x:r+ye(t),y:a}}(t)):zt(e)?function(t){var e=Vt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Te(function(t){var e,i=Gt(t),n=ve(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ie(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ie(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ye(t),l=-n.scrollTop;return"rtl"===Yt(s||i).direction&&(a+=ie(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Gt(t)))}function Ce(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?Ut(s):null,r=s?ce(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case mt:e={x:a,y:i.y-n.height};break;case gt:e={x:a,y:i.y+i.height};break;case _t:e={x:i.x+i.width,y:l};break;case bt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?ee(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case wt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Et:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.boundary,r=void 0===o?At:o,a=i.rootBoundary,l=void 0===a?Tt:a,c=i.elementContext,h=void 0===c?Ot:c,d=i.altBoundary,u=void 0!==d&&d,f=i.padding,p=void 0===f?0:f,m=re("number"!=typeof p?p:ae(p,yt)),g=h===Ot?Ct:Ot,_=t.rects.popper,b=t.elements[u?g:h],v=function(t,e,i){var n="clippingParents"===e?function(t){var e=Ae(Zt(t)),i=["absolute","fixed"].indexOf(Yt(t).position)>=0&&zt(t)?te(t):t;return $t(i)?e.filter((function(t){return $t(t)&&Xt(t,i)&&"body"!==Rt(t)})):[]}(t):[].concat(e),s=[].concat(n,[i]),o=s[0],r=s.reduce((function(e,i){var n=Oe(t,i);return e.top=ie(n.top,e.top),e.right=ne(n.right,e.right),e.bottom=ne(n.bottom,e.bottom),e.left=ie(n.left,e.left),e}),Oe(t,o));return r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}($t(b)?b:b.contextElement||Gt(t.elements.popper),r,l),y=Vt(t.elements.reference),w=Ce({reference:y,element:_,strategy:"absolute",placement:s}),E=Te(Object.assign({},_,w)),A=h===Ot?E:y,T={top:v.top-A.top+m.top,bottom:A.bottom-v.bottom+m.bottom,left:v.left-A.left+m.left,right:A.right-v.right+m.right},O=t.modifiersData.offset;if(h===Ot&&O){var C=O[s];Object.keys(T).forEach((function(t){var e=[_t,gt].indexOf(t)>=0?1:-1,i=[mt,gt].indexOf(t)>=0?"y":"x";T[t]+=C[i]*e}))}return T}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?Lt:l,h=ce(n),d=h?a?kt:kt.filter((function(t){return ce(t)===h})):yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[Ut(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const xe={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=Ut(g),b=l||(_!==g&&p?function(t){if(Ut(t)===vt)return[];var e=ge(t);return[be(t),e,be(e)]}(g):[ge(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(Ut(i)===vt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,A=!0,T=v[0],O=0;O=0,D=x?"width":"height",S=ke(e,{placement:C,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),N=x?L?_t:bt:L?gt:mt;y[D]>w[D]&&(N=ge(N));var I=ge(N),P=[];if(o&&P.push(S[k]<=0),a&&P.push(S[N]<=0,S[I]<=0),P.every((function(t){return t}))){T=C,A=!1;break}E.set(C,P)}if(A)for(var j=function(t){var e=v.find((function(e){var i=E.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Se(t){return[mt,_t,gt,bt].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Se(l),d=Se(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Ie={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=Lt.reduce((function(t,i){return t[i]=function(t,e,i){var n=Ut(t),s=[bt,mt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[bt,_t].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},Pe={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=Ce({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},je={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=Ut(e.placement),b=ce(e.placement),v=!b,y=ee(_),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,A=e.rects.reference,T=e.rects.popper,O="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,C={x:0,y:0};if(E){if(o||a){var k="y"===y?mt:bt,L="y"===y?gt:_t,x="y"===y?"height":"width",D=E[y],S=E[y]+g[k],N=E[y]-g[L],I=f?-T[x]/2:0,P=b===wt?A[x]:T[x],j=b===wt?-T[x]:-A[x],M=e.elements.arrow,H=f&&M?Kt(M):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=B[k],W=B[L],$=oe(0,A[x],H[x]),z=v?A[x]/2-I-$-R-O:P-$-R-O,q=v?-A[x]/2+I+$+W+O:j+$+W+O,F=e.elements.arrow&&te(e.elements.arrow),U=F?"y"===y?F.clientTop||0:F.clientLeft||0:0,V=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,K=E[y]+z-V-U,X=E[y]+q-V;if(o){var Y=oe(f?ne(S,K):S,D,f?ie(N,X):N);E[y]=Y,C[y]=Y-D}if(a){var Q="x"===y?mt:bt,G="x"===y?gt:_t,Z=E[w],J=Z+g[Q],tt=Z-g[G],et=oe(f?ne(J,K):J,Z,f?ie(tt,X):tt);E[w]=et,C[w]=et-Z}}e.modifiersData[n]=C}},requiresIfExists:["offset"]};function Me(t,e,i){void 0===i&&(i=!1);var n=zt(e);zt(e)&&function(t){var e=t.getBoundingClientRect();e.width,t.offsetWidth,e.height,t.offsetHeight}(e);var s,o,r=Gt(e),a=Vt(t),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(n||!n&&!i)&&(("body"!==Rt(e)||we(r))&&(l=(s=e)!==Wt(s)&&zt(s)?{scrollLeft:(o=s).scrollLeft,scrollTop:o.scrollTop}:ve(s)),zt(e)?((c=Vt(e)).x+=e.clientLeft,c.y+=e.clientTop):r&&(c.x=ye(r))),{x:a.left+l.scrollLeft-c.x,y:a.top+l.scrollTop-c.y,width:a.width,height:a.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Be={placement:"bottom",modifiers:[],strategy:"absolute"};function Re(){for(var t=arguments.length,e=new Array(t),i=0;ij.on(t,"mouseover",d))),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Je),this._element.classList.add(Je),j.trigger(this._element,"shown.bs.dropdown",t)}hide(){if(c(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){j.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._popper&&this._popper.destroy(),this._menu.classList.remove(Je),this._element.classList.remove(Je),this._element.setAttribute("aria-expanded","false"),U.removeDataAttribute(this._menu,"popper"),j.trigger(this._element,"hidden.bs.dropdown",t))}_getConfig(t){if(t={...this.constructor.Default,...U.getDataAttributes(this._element),...t},a(Ue,t,this.constructor.DefaultType),"object"==typeof t.reference&&!o(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ue.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(t){if(void 0===Fe)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=t:o(this._config.reference)?e=r(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const i=this._getPopperConfig(),n=i.modifiers.find((t=>"applyStyles"===t.name&&!1===t.enabled));this._popper=qe(e,this._menu,i),n&&U.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.classList.contains(Je)}_getMenuElement(){return V.next(this._element,ei)[0]}_getPlacement(){const t=this._element.parentNode;if(t.classList.contains("dropend"))return ri;if(t.classList.contains("dropstart"))return ai;const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ni:ii:e?oi:si}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return"static"===this._config.display&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=V.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(l);i.length&&v(i,e,t===Ye,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(t&&(2===t.button||"keyup"===t.type&&"Tab"!==t.key))return;const e=V.find(ti);for(let i=0,n=e.length;ie+t)),this._setElementAttributes(di,"paddingRight",(e=>e+t)),this._setElementAttributes(ui,"marginRight",(e=>e-t))}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t)[e];t.style[e]=`${i(Number.parseFloat(s))}px`}))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(di,"paddingRight"),this._resetElementAttributes(ui,"marginRight")}_saveInitialAttribute(t,e){const i=t.style[e];i&&U.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=U.getDataAttribute(t,e);void 0===i?t.style.removeProperty(e):(U.removeDataAttribute(t,e),t.style[e]=i)}))}_applyManipulationCallback(t,e){o(t)?e(t):V.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const pi={className:"modal-backdrop",isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null},mi={className:"string",isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)"},gi="show",_i="mousedown.bs.backdrop";class bi{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){this._config.isVisible?(this._append(),this._config.isAnimated&&u(this._getElement()),this._getElement().classList.add(gi),this._emulateAnimation((()=>{_(t)}))):_(t)}hide(t){this._config.isVisible?(this._getElement().classList.remove(gi),this._emulateAnimation((()=>{this.dispose(),_(t)}))):_(t)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_getConfig(t){return(t={...pi,..."object"==typeof t?t:{}}).rootElement=r(t.rootElement),a("backdrop",t,mi),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),j.on(this._getElement(),_i,(()=>{_(this._config.clickCallback)})),this._isAppended=!0)}dispose(){this._isAppended&&(j.off(this._element,_i),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){b(t,this._getElement(),this._config.isAnimated)}}const vi={trapElement:null,autofocus:!0},yi={trapElement:"element",autofocus:"boolean"},wi=".bs.focustrap",Ei="backward";class Ai{constructor(t){this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}activate(){const{trapElement:t,autofocus:e}=this._config;this._isActive||(e&&t.focus(),j.off(document,wi),j.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),j.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,j.off(document,wi))}_handleFocusin(t){const{target:e}=t,{trapElement:i}=this._config;if(e===document||e===i||i.contains(e))return;const n=V.focusableChildren(i);0===n.length?i.focus():this._lastTabNavDirection===Ei?n[n.length-1].focus():n[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Ei:"forward")}_getConfig(t){return t={...vi,..."object"==typeof t?t:{}},a("focustrap",t,yi),t}}const Ti="modal",Oi="Escape",Ci={backdrop:!0,keyboard:!0,focus:!0},ki={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"},Li="hidden.bs.modal",xi="show.bs.modal",Di="resize.bs.modal",Si="click.dismiss.bs.modal",Ni="keydown.dismiss.bs.modal",Ii="mousedown.dismiss.bs.modal",Pi="modal-open",ji="show",Mi="modal-static";class Hi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._dialog=V.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollBar=new fi}static get Default(){return Ci}static get NAME(){return Ti}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||j.trigger(this._element,xi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.classList.add(Pi),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),j.on(this._dialog,Ii,(()=>{j.one(this._element,"mouseup.dismiss.bs.modal",(t=>{t.target===this._element&&(this._ignoreBackdropClick=!0)}))})),this._showBackdrop((()=>this._showElement(t))))}hide(){if(!this._isShown||this._isTransitioning)return;if(j.trigger(this._element,"hide.bs.modal").defaultPrevented)return;this._isShown=!1;const t=this._isAnimated();t&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.deactivate(),this._element.classList.remove(ji),j.off(this._element,Si),j.off(this._dialog,Ii),this._queueCallback((()=>this._hideModal()),this._element,t)}dispose(){[window,this._dialog].forEach((t=>j.off(t,".bs.modal"))),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new bi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_getConfig(t){return t={...Ci,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Ti,t,ki),t}_showElement(t){const e=this._isAnimated(),i=V.findOne(".modal-body",this._dialog);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0,i&&(i.scrollTop=0),e&&u(this._element),this._element.classList.add(ji),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,j.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,e)}_setEscapeEvent(){this._isShown?j.on(this._element,Ni,(t=>{this._config.keyboard&&t.key===Oi?(t.preventDefault(),this.hide()):this._config.keyboard||t.key!==Oi||this._triggerBackdropTransition()})):j.off(this._element,Ni)}_setResizeEvent(){this._isShown?j.on(window,Di,(()=>this._adjustDialog())):j.off(window,Di)}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Pi),this._resetAdjustments(),this._scrollBar.reset(),j.trigger(this._element,Li)}))}_showBackdrop(t){j.on(this._element,Si,(t=>{this._ignoreBackdropClick?this._ignoreBackdropClick=!1:t.target===t.currentTarget&&(!0===this._config.backdrop?this.hide():"static"===this._config.backdrop&&this._triggerBackdropTransition())})),this._backdrop.show(t)}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(j.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const{classList:t,scrollHeight:e,style:i}=this._element,n=e>document.documentElement.clientHeight;!n&&"hidden"===i.overflowY||t.contains(Mi)||(n||(i.overflowY="hidden"),t.add(Mi),this._queueCallback((()=>{t.remove(Mi),n||this._queueCallback((()=>{i.overflowY=""}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;(!i&&t&&!m()||i&&!t&&m())&&(this._element.style.paddingLeft=`${e}px`),(i&&!t&&!m()||!i&&t&&m())&&(this._element.style.paddingRight=`${e}px`)}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}j.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=n(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),j.one(e,xi,(t=>{t.defaultPrevented||j.one(e,Li,(()=>{l(this)&&this.focus()}))}));const i=V.findOne(".modal.show");i&&Hi.getInstance(i).hide(),Hi.getOrCreateInstance(e).toggle(this)})),R(Hi),g(Hi);const Bi="offcanvas",Ri={backdrop:!0,keyboard:!0,scroll:!1},Wi={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"},$i="show",zi=".offcanvas.show",qi="hidden.bs.offcanvas";class Fi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get NAME(){return Bi}static get Default(){return Ri}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||j.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||(new fi).hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add($i),this._queueCallback((()=>{this._config.scroll||this._focustrap.activate(),j.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(j.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.remove($i),this._backdrop.hide(),this._queueCallback((()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||(new fi).reset(),j.trigger(this._element,qi)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_getConfig(t){return t={...Ri,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Bi,t,Wi),t}_initializeBackDrop(){return new bi({className:"offcanvas-backdrop",isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_addEventListeners(){j.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{this._config.keyboard&&"Escape"===t.key&&this.hide()}))}static jQueryInterface(t){return this.each((function(){const e=Fi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}j.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=n(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this))return;j.one(e,qi,(()=>{l(this)&&this.focus()}));const i=V.findOne(zi);i&&i!==e&&Fi.getInstance(i).hide(),Fi.getOrCreateInstance(e).toggle(this)})),j.on(window,"load.bs.offcanvas.data-api",(()=>V.find(zi).forEach((t=>Fi.getOrCreateInstance(t).show())))),R(Fi),g(Fi);const Ui=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Vi=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Ki=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,Xi=(t,e)=>{const i=t.nodeName.toLowerCase();if(e.includes(i))return!Ui.has(i)||Boolean(Vi.test(t.nodeValue)||Ki.test(t.nodeValue));const n=e.filter((t=>t instanceof RegExp));for(let t=0,e=n.length;t{Xi(t,r)||i.removeAttribute(t.nodeName)}))}return n.body.innerHTML}const Qi="tooltip",Gi=new Set(["sanitize","allowList","sanitizeFn"]),Zi={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},Ji={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},tn={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},en={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},nn="fade",sn="show",on="show",rn="out",an=".tooltip-inner",ln=".modal",cn="hide.bs.modal",hn="hover",dn="focus";class un extends B{constructor(t,e){if(void 0===Fe)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return tn}static get NAME(){return Qi}static get Event(){return en}static get DefaultType(){return Zi}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains(sn))return void this._leave(null,this);this._enter(null,this)}}dispose(){clearTimeout(this._timeout),j.off(this._element.closest(ln),cn,this._hideModalHandler),this.tip&&this.tip.remove(),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this.isWithContent()||!this._isEnabled)return;const t=j.trigger(this._element,this.constructor.Event.SHOW),e=h(this._element),i=null===e?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!i)return;"tooltip"===this.constructor.NAME&&this.tip&&this.getTitle()!==this.tip.querySelector(an).innerHTML&&(this._disposePopper(),this.tip.remove(),this.tip=null);const n=this.getTipElement(),s=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME);n.setAttribute("id",s),this._element.setAttribute("aria-describedby",s),this._config.animation&&n.classList.add(nn);const o="function"==typeof this._config.placement?this._config.placement.call(this,n,this._element):this._config.placement,r=this._getAttachment(o);this._addAttachmentClass(r);const{container:a}=this._config;H.set(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(a.append(n),j.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=qe(this._element,n,this._getPopperConfig(r)),n.classList.add(sn);const l=this._resolvePossibleFunction(this._config.customClass);l&&n.classList.add(...l.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>{j.on(t,"mouseover",d)}));const c=this.tip.classList.contains(nn);this._queueCallback((()=>{const t=this._hoverState;this._hoverState=null,j.trigger(this._element,this.constructor.Event.SHOWN),t===rn&&this._leave(null,this)}),this.tip,c)}hide(){if(!this._popper)return;const t=this.getTipElement();if(j.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.remove(sn),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1;const e=this.tip.classList.contains(nn);this._queueCallback((()=>{this._isWithActiveTrigger()||(this._hoverState!==on&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),j.trigger(this._element,this.constructor.Event.HIDDEN),this._disposePopper())}),this.tip,e),this._hoverState=""}update(){null!==this._popper&&this._popper.update()}isWithContent(){return Boolean(this.getTitle())}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove(nn,sn),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),an)}_sanitizeAndSetContent(t,e,i){const n=V.findOne(i,t);e||!n?this.setElementContent(n,e):n.remove()}setElementContent(t,e){if(null!==t)return o(e)?(e=r(e),void(this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent)):void(this._config.html?(this._config.sanitize&&(e=Yi(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e)}getTitle(){const t=this._element.getAttribute("data-bs-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return"right"===t?"end":"left"===t?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return"function"==typeof t?t.call(this._element):t}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:t=>this._handlePopperPlacementChange(t)}],onFirstUpdate:t=>{t.options.placement!==t.placement&&this._handlePopperPlacementChange(t)}};return{...e,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return Ji[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach((t=>{if("click"===t)j.on(this._element,this.constructor.Event.CLICK,this._config.selector,(t=>this.toggle(t)));else if("manual"!==t){const e=t===hn?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,i=t===hn?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;j.on(this._element,e,this._config.selector,(t=>this._enter(t))),j.on(this._element,i,this._config.selector,(t=>this._leave(t)))}})),this._hideModalHandler=()=>{this._element&&this.hide()},j.on(this._element.closest(ln),cn,this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?dn:hn]=!0),e.getTipElement().classList.contains(sn)||e._hoverState===on?e._hoverState=on:(clearTimeout(e._timeout),e._hoverState=on,e._config.delay&&e._config.delay.show?e._timeout=setTimeout((()=>{e._hoverState===on&&e.show()}),e._config.delay.show):e.show())}_leave(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?dn:hn]=e._element.contains(t.relatedTarget)),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=rn,e._config.delay&&e._config.delay.hide?e._timeout=setTimeout((()=>{e._hoverState===rn&&e.hide()}),e._config.delay.hide):e.hide())}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=U.getDataAttributes(this._element);return Object.keys(e).forEach((t=>{Gi.has(t)&&delete e[t]})),(t={...this.constructor.Default,...e,..."object"==typeof t&&t?t:{}}).container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a(Qi,t,this.constructor.DefaultType),t.sanitize&&(t.template=Yi(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),i=t.getAttribute("class").match(e);null!==i&&i.length>0&&i.map((t=>t.trim())).forEach((e=>t.classList.remove(e)))}_getBasicClassPrefix(){return"bs-tooltip"}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each((function(){const e=un.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(un);const fn={...un.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:''},pn={...un.DefaultType,content:"(string|element|function)"},mn={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"};class gn extends un{static get Default(){return fn}static get NAME(){return"popover"}static get Event(){return mn}static get DefaultType(){return pn}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".popover-header"),this._sanitizeAndSetContent(t,this._getContent(),".popover-body")}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return"bs-popover"}static jQueryInterface(t){return this.each((function(){const e=gn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(gn);const _n="scrollspy",bn={offset:10,method:"auto",target:""},vn={offset:"number",method:"string",target:"(string|element)"},yn="active",wn=".nav-link, .list-group-item, .dropdown-item",En="position";class An extends B{constructor(t,e){super(t),this._scrollElement="BODY"===this._element.tagName?window:this._element,this._config=this._getConfig(e),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,j.on(this._scrollElement,"scroll.bs.scrollspy",(()=>this._process())),this.refresh(),this._process()}static get Default(){return bn}static get NAME(){return _n}refresh(){const t=this._scrollElement===this._scrollElement.window?"offset":En,e="auto"===this._config.method?t:this._config.method,n=e===En?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),V.find(wn,this._config.target).map((t=>{const s=i(t),o=s?V.findOne(s):null;if(o){const t=o.getBoundingClientRect();if(t.width||t.height)return[U[e](o).top+n,s]}return null})).filter((t=>t)).sort(((t,e)=>t[0]-e[0])).forEach((t=>{this._offsets.push(t[0]),this._targets.push(t[1])}))}dispose(){j.off(this._scrollElement,".bs.scrollspy"),super.dispose()}_getConfig(t){return(t={...bn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}}).target=r(t.target)||document.documentElement,a(_n,t,vn),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),i=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=i){const t=this._targets[this._targets.length-1];this._activeTarget!==t&&this._activate(t)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(let e=this._offsets.length;e--;)this._activeTarget!==this._targets[e]&&t>=this._offsets[e]&&(void 0===this._offsets[e+1]||t`${e}[data-bs-target="${t}"],${e}[href="${t}"]`)),i=V.findOne(e.join(","),this._config.target);i.classList.add(yn),i.classList.contains("dropdown-item")?V.findOne(".dropdown-toggle",i.closest(".dropdown")).classList.add(yn):V.parents(i,".nav, .list-group").forEach((t=>{V.prev(t,".nav-link, .list-group-item").forEach((t=>t.classList.add(yn))),V.prev(t,".nav-item").forEach((t=>{V.children(t,".nav-link").forEach((t=>t.classList.add(yn)))}))})),j.trigger(this._scrollElement,"activate.bs.scrollspy",{relatedTarget:t})}_clear(){V.find(wn,this._config.target).filter((t=>t.classList.contains(yn))).forEach((t=>t.classList.remove(yn)))}static jQueryInterface(t){return this.each((function(){const e=An.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(window,"load.bs.scrollspy.data-api",(()=>{V.find('[data-bs-spy="scroll"]').forEach((t=>new An(t)))})),g(An);const Tn="active",On="fade",Cn="show",kn=".active",Ln=":scope > li > .active";class xn extends B{static get NAME(){return"tab"}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.classList.contains(Tn))return;let t;const e=n(this._element),i=this._element.closest(".nav, .list-group");if(i){const e="UL"===i.nodeName||"OL"===i.nodeName?Ln:kn;t=V.find(e,i),t=t[t.length-1]}const s=t?j.trigger(t,"hide.bs.tab",{relatedTarget:this._element}):null;if(j.trigger(this._element,"show.bs.tab",{relatedTarget:t}).defaultPrevented||null!==s&&s.defaultPrevented)return;this._activate(this._element,i);const o=()=>{j.trigger(t,"hidden.bs.tab",{relatedTarget:this._element}),j.trigger(this._element,"shown.bs.tab",{relatedTarget:t})};e?this._activate(e,e.parentNode,o):o()}_activate(t,e,i){const n=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?V.children(e,kn):V.find(Ln,e))[0],s=i&&n&&n.classList.contains(On),o=()=>this._transitionComplete(t,n,i);n&&s?(n.classList.remove(Cn),this._queueCallback(o,t,!0)):o()}_transitionComplete(t,e,i){if(e){e.classList.remove(Tn);const t=V.findOne(":scope > .dropdown-menu .active",e.parentNode);t&&t.classList.remove(Tn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add(Tn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u(t),t.classList.contains(On)&&t.classList.add(Cn);let n=t.parentNode;if(n&&"LI"===n.nodeName&&(n=n.parentNode),n&&n.classList.contains("dropdown-menu")){const e=t.closest(".dropdown");e&&V.find(".dropdown-toggle",e).forEach((t=>t.classList.add(Tn))),t.setAttribute("aria-expanded",!0)}i&&i()}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this)||xn.getOrCreateInstance(this).show()})),g(xn);const Dn="toast",Sn="hide",Nn="show",In="showing",Pn={animation:"boolean",autohide:"boolean",delay:"number"},jn={animation:!0,autohide:!0,delay:5e3};class Mn extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get DefaultType(){return Pn}static get Default(){return jn}static get NAME(){return Dn}show(){j.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(Sn),u(this._element),this._element.classList.add(Nn),this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.remove(In),j.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this._element.classList.contains(Nn)&&(j.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.add(Sn),this._element.classList.remove(In),this._element.classList.remove(Nn),j.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this._element.classList.contains(Nn)&&this._element.classList.remove(Nn),super.dispose()}_getConfig(t){return t={...jn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}},a(Dn,t,this.constructor.DefaultType),t}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){j.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),j.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Mn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Mn),g(Mn),{Alert:W,Button:z,Carousel:st,Collapse:pt,Dropdown:hi,Modal:Hi,Offcanvas:Fi,Popover:gn,ScrollSpy:An,Tab:xn,Toast:Mn,Tooltip:un}})); -//# sourceMappingURL=bootstrap.bundle.min.js.map