diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 300a4f9..53430dc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,5 +1,4 @@ name: CI - on: push: tags: @@ -7,34 +6,42 @@ on: branches: - main pull_request: - permissions: checks: write pull-requests: write env: CARGO_TERM_COLOR: always - jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: submodules: recursive - uses: Swatinem/rust-cache@v2 with: - prefix-key: v1-rust + prefix-key: v1-rust-${{ matrix.os }} shared-key: debug cache-all-crates: true - name: Setup Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - - name: Install moonbit + components: rustfmt, clippy + - name: Install moonbit (Unix) + if: runner.os != 'Windows' run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.19 + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.24+012953835 echo "$HOME/.moon/bin" >> $GITHUB_PATH + - name: Install moonbit (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $env:MOONBIT_INSTALL_VERSION = "0.6.24+012953835" + irm https://cli.moonbitlang.com/install/powershell.ps1 | iex + echo "$env:USERPROFILE\.moon\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Bundle core MoonBit library run: moon bundle --target wasm working-directory: core @@ -46,28 +53,38 @@ jobs: run: cargo build --all-features --all-targets test: needs: [build] - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: submodules: recursive - uses: Swatinem/rust-cache@v2 with: - prefix-key: v1-rust + prefix-key: v1-rust-${{ matrix.os }} shared-key: debug cache-all-crates: false - name: Setup Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true + components: rustfmt, clippy - uses: cargo-bins/cargo-binstall@main - name: Install wasmtime-cli run: cargo binstall --force --locked wasmtime-cli@33.0.0 - - name: Install moonbit + - name: Install moonbit (Unix) + if: runner.os != 'Windows' run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.19 + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.24+012953835 echo "$HOME/.moon/bin" >> $GITHUB_PATH + - name: Install moonbit (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $env:MOONBIT_INSTALL_VERSION = "0.6.24+012953835" + irm https://cli.moonbitlang.com/install/powershell.ps1 | iex + echo "$env:USERPROFILE\.moon\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Bundle core MoonBit library run: moon bundle --target wasm working-directory: core @@ -83,7 +100,7 @@ jobs: publish: needs: [test] if: "startsWith(github.ref, 'refs/tags/v')" - runs-on: ubuntu-latest + runs-on: ubuntu-latest # Publish on Ubuntu only steps: - name: Checkout uses: actions/checkout@v4 @@ -96,7 +113,7 @@ jobs: override: true - name: Install moonbit run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.19 + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.24 echo "$HOME/.moon/bin" >> $GITHUB_PATH - name: Bundle core MoonBit library run: moon bundle --target wasm diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..3373f20 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,5 @@ +[tools] +rust = "stable" + +[env] +PATH = "/home/{{env.USER}}/.moon/bin:{{env.PATH}}" \ No newline at end of file diff --git a/README.md b/README.md index 7919bcd..ec45283 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,80 @@ fn main() { - `core` is a git submodule containing the MoonBit core library (https://github.com/moonbitlang/core) - `bundled-core` is the MoonBit core library (source and compiled for wasm), included in this repository to avoid users of the crate from having to build `core` themselves. -To update and build the MoonBit core library: +### System Requirements -**NOTE**: requires the 0.6.19 version of MoonBit currently +#### Debian/Ubuntu +The following system packages are required: +```bash +# Essential build tools and C compiler for Rust build dependencies +sudo apt-get update && sudo apt-get install -y build-essential +# Optional but recommended: curl for downloading tools +sudo apt-get install -y curl + +# Git for submodule management +sudo apt-get install -y git +``` + +**Note**: The project has been tested on Debian 12 (Bookworm) with kernel 6.1.0-38-amd64 and works correctly with the standard Debian package repositories. + +### Development Setup + +1. **Install mise** (development environment manager): +```bash +# Install mise if not already present +curl https://mise.run | sh +``` + +2. **Install development tools** via mise: +```bash +# This installs Rust stable toolchain as configured in .mise.toml +mise install +``` + +3. **Install MoonBit** (version 0.6.24): +```bash +curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.24 +# Add to PATH (the installer does this automatically for new shells) +export PATH="$HOME/.moon/bin:$PATH" +``` + +4. **Initialize git submodules** and build the core library: +```bash +git submodule update --init --recursive +cd core && moon bundle --target wasm && cd .. +``` + +5. **Install test dependencies**: +```bash +# Install cargo-binstall for faster binary installation +curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash + +# Install wasmtime-cli with required features +cargo binstall --force --locked wasmtime-cli@33.0.0 -y +``` + +### Building and Testing + +```bash +# Format check +cargo fmt -- --check + +# Lint check +cargo clippy -- -Dwarnings + +# Build all features and targets +cargo build --all-features --all-targets + +# Run tests (requires wasmtime-cli) +cargo test -p moonbit-component-generator -- --nocapture --test-threads=1 ``` -curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- 0.6.19 + +### Updating the MoonBit Core Library + +To update and rebuild the MoonBit core library bundle: + +```bash git submodule update --recursive ./update-bundle.sh ``` @@ -85,6 +153,9 @@ The bundled core library is included in the compiled crate using the `include_di It is also pushed into the repository (`bundled-core` directory) to avoid users of the crate from having to build the MoonBit core themselves as part of the Rust build process. -Running the tests require `wasmtime-cli` to be installed with the following features enabled: -- `component-model` -- `wasi-config` +### Notes + +- Running tests requires `wasmtime-cli` version 33.0.0 with the following features enabled: + - `component-model` + - `wasi-config` +- The MoonBit compiler runs in WASM on V8, so the first compilation in a session may take longer due to V8 initialization diff --git a/bundled-core/.githooks/README.md b/bundled-core/.githooks/README.md new file mode 100644 index 0000000..205b7fd --- /dev/null +++ b/bundled-core/.githooks/README.md @@ -0,0 +1,80 @@ +# MoonBit Git Hooks + +This directory contains Git hooks to ensure code quality and consistency in the MoonBit core library. + +## Setup + +To enable the hooks for this repository, run: + +```bash +./.githooks/setup.sh +``` + +Or manually configure: + +```bash +git config core.hooksPath .githooks +chmod +x .githooks/* +``` + +## Available Hooks + +### pre-commit + +Runs before each commit to ensure code quality: + +- โœ… **moon check** - Validates code syntax, types, and formatting +- โŒ **Blocks commit** if any issues are found +- ๐Ÿ’ก **Suggests fixes** like running `moon fmt` + +### Usage + +The hooks run automatically when you commit: + +```bash +git commit -m "your message" +# โ†’ Runs moon check automatically +``` + +To bypass hooks temporarily (not recommended for production): + +```bash +git commit --no-verify -m "your message" +``` + +## Troubleshooting + +### Hook fails with "moon command not found" + +Install the MoonBit toolchain: +- Visit: https://www.moonbitlang.com/download/ +- Follow the installation instructions for your platform + +### Hook fails due to formatting issues + +Run the auto-formatter: + +```bash +moon fmt +``` + +### Hook fails due to type errors + +Fix the reported type errors and try committing again. The hook output will show specific error locations. + +## Contributing + +When adding new hooks: + +1. Create the hook file in `.githooks/` +2. Make it executable: `chmod +x .githooks/hook-name` +3. Update this README +4. Test the hook thoroughly + +## Philosophy + +These hooks enforce quality standards to: +- ๐Ÿ› **Catch errors early** before they reach CI/CD +- ๐ŸŽจ **Maintain consistent formatting** across the codebase +- โšก **Speed up development** by providing immediate feedback +- ๐Ÿค **Help contributors** follow project conventions diff --git a/bundled-core/.githooks/pre-commit b/bundled-core/.githooks/pre-commit new file mode 100755 index 0000000..3943cc7 --- /dev/null +++ b/bundled-core/.githooks/pre-commit @@ -0,0 +1,88 @@ +#!/bin/bash + +# Enhanced pre-commit hook for MoonBit +# This version also runs formatting and provides more detailed feedback +# To use this instead of the basic version, rename it to 'pre-commit' + +set -e + +echo "๐ŸŒ™ Running MoonBit pre-commit checks (enhanced)..." + +# Check if moon is available +if ! command -v moon &> /dev/null; then + echo "โŒ Error: 'moon' command not found. Please install MoonBit toolchain." + echo " Visit: https://www.moonbitlang.com/download/" + exit 1 +fi + +# Check for staged files +STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(mbt|mbti)$' || true) + +# If no mbt/mbti files are staged, exit +if [ -z "$STAGED_FILES" ]; then + echo "โ„น๏ธ No MoonBit files staged for commit." + exit 0 +fi + +echo "๐Ÿ“ Found staged MoonBit files:" +echo "$STAGED_FILES" | sed 's/^/ - /' +echo "" + +# Run formatting first +echo "๐ŸŽจ Running 'moon fmt'..." +if ! moon fmt; then + echo "โŒ Formatting failed! Please check the output above." + exit 1 +fi + +# Check if formatting changed any files +CHANGED_FILES=$(git diff --name-only | grep -E '\.(mbt|mbti)$' || true) +if [ -n "$CHANGED_FILES" ]; then + echo "โš ๏ธ Formatting changes detected. Please stage the formatted files:" + echo "$CHANGED_FILES" | sed 's/^/ - /' + echo "" + echo "๐Ÿ’ก Run: git add . && git commit" + exit 1 +fi + +# Run moon check and capture output +echo "๐Ÿ“‹ Running 'moon check'..." +CHECK_OUTPUT=$(moon check --target all 2>&1) +CHECK_EXIT_CODE=$? + +if [ $CHECK_EXIT_CODE -ne 0 ]; then + echo "" + echo "โŒ Pre-commit hook failed!" + echo " 'moon check' found issues in your code:" + echo "" + echo "๐Ÿ“ Error details:" + echo "$CHECK_OUTPUT" | sed 's/^/ /' + echo "" + echo "๐Ÿ’ก Common fixes:" + echo " - Check for type errors and fix them" + echo " - Ensure all imports are valid" + echo " - Run 'moon fmt' if formatting issues persist" + echo " - Review the error messages above for specific issues" + exit 1 +else + echo " $CHECK_OUTPUT" +fi + +# Optional: Run tests on changed modules (uncomment if desired) +# echo "๐Ÿงช Running tests for changed modules..." +# for file in $STAGED_FILES; do +# MODULE_DIR=$(dirname "$file") +# if [ -f "$MODULE_DIR/moon.pkg.json" ]; then +# echo " Testing $MODULE_DIR..." +# if ! (cd "$MODULE_DIR" && moon test); then +# echo "โŒ Tests failed in $MODULE_DIR" +# exit 1 +# fi +# fi +# done + +echo "โœ… All checks passed! Proceeding with commit..." +echo " ๐Ÿ“ Files: $(echo "$STAGED_FILES" | wc -l | xargs) MoonBit files" +echo " ๐ŸŽจ Formatted: โœ“" +echo " ๐Ÿ“‹ Checked: โœ“" +echo "" diff --git a/bundled-core/.githooks/setup.sh b/bundled-core/.githooks/setup.sh new file mode 100755 index 0000000..93182d7 --- /dev/null +++ b/bundled-core/.githooks/setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Setup script for MoonBit pre-commit hooks +# Run this script to configure Git hooks for this repository + +echo "๐Ÿ”ง Setting up MoonBit pre-commit hooks..." + +# Configure Git to use the custom hooks directory +git config core.hooksPath .githooks + +# Make all hooks executable +chmod +x .githooks/* + +echo "โœ… Git hooks configured successfully!" +echo "" +echo "๐Ÿ“ The following hooks are now active:" +echo " - pre-commit: Runs 'moon check' before each commit" +echo "" +echo "๐Ÿ’ก To disable hooks temporarily, use: git commit --no-verify" +echo " (Not recommended for production commits)" +echo "" diff --git a/bundled-core/.github/workflows/bleeding-check.yml b/bundled-core/.github/workflows/bleeding-check.yml index 1086861..38ecddd 100644 --- a/bundled-core/.github/workflows/bleeding-check.yml +++ b/bundled-core/.github/workflows/bleeding-check.yml @@ -1,4 +1,4 @@ -name: bleeding-check +name: nightly-check on: push: @@ -7,14 +7,14 @@ on: pull_request: jobs: - bleeding-check: + nightly-check: continue-on-error: true strategy: matrix: - os: [macos-latest, ubuntu-latest, windows-latest, macos-13] + os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} if: | - (github.event_name == 'pull_request' && startsWith(github.head_ref, 'bleeding/')) || + (github.event_name == 'pull_request' && startsWith(github.head_ref, 'nightly/')) || (github.event_name == 'push' && github.ref == 'refs/heads/main') steps: - uses: actions/checkout@v4 @@ -22,12 +22,12 @@ jobs: - name: install if: ${{ matrix.os != 'windows-latest' }} run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s bleeding + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s nightly echo "$HOME/.moon/bin" >> $GITHUB_PATH - name: install on windows env: - MOONBIT_INSTALL_VERSION: bleeding + MOONBIT_INSTALL_VERSION: nightly if: ${{ matrix.os == 'windows-latest' }} run: | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex @@ -59,7 +59,7 @@ jobs: if: ${{ matrix.os == 'windows-latest' }} run: | moon test --release --target all - + - name: Run moon test on Windows (--target native) if: ${{ matrix.os == 'windows-latest' }} run: | @@ -69,11 +69,11 @@ jobs: run: | moon clean moon test --target wasm - env: + env: MOONC_INTERNAL_PARAMS: allocator = tlsf-mbt | - - name: moon check - run: moon check + - name: check + run: moon check --deny-warn - name: moon bundle run: moon bundle --all @@ -91,14 +91,14 @@ jobs: moon fmt git diff - bleeding-native-opt-test: + nightly-native-opt-test: strategy: matrix: - os: [ubuntu-latest, macos-latest, macos-13, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: false runs-on: ${{ matrix.os }} if: | - (github.event_name == 'pull_request' && startsWith(github.head_ref, 'bleeding/')) || + (github.event_name == 'pull_request' && startsWith(github.head_ref, 'nightly/')) || (github.event_name == 'push' && github.ref == 'refs/heads/main') continue-on-error: false steps: @@ -107,12 +107,12 @@ jobs: - name: install if: ${{ matrix.os != 'windows-latest' }} run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s bleeding + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s nightly echo "$HOME/.moon/bin" >> $GITHUB_PATH - name: install on windows env: - MOONBIT_INSTALL_VERSION: bleeding + MOONBIT_INSTALL_VERSION: nightly if: ${{ matrix.os == 'windows-latest' }} run: | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex @@ -131,7 +131,7 @@ jobs: - name: Setup MSVC if: ${{ matrix.os == 'windows-latest' }} uses: ilammy/msvc-dev-cmd@v1 - + - name: Run moon test on Windows (--release + --target native) if: ${{ matrix.os == 'windows-latest' }} run: | diff --git a/bundled-core/.github/workflows/pre-release-check.yml b/bundled-core/.github/workflows/pre-release-check.yml index 0634935..7cf2066 100644 --- a/bundled-core/.github/workflows/pre-release-check.yml +++ b/bundled-core/.github/workflows/pre-release-check.yml @@ -4,15 +4,140 @@ on: push: branches: - main + - "pre-release*" pull_request: - merge_group: jobs: + version-check: + runs-on: ubuntu-latest + outputs: + should-skip: ${{ steps.check.outputs.should-skip }} + steps: + - uses: actions/checkout@v4 + + - name: install pre-release + run: | + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s pre-release + echo "$HOME/.moon/bin" >> $GITHUB_PATH + + - name: get latest version info + id: latest + run: | + curl -s https://cli.moonbitlang.com/version.json > version.json + echo "Latest version info:" + cat version.json + + # Extract moonc version from latest release using jq + LATEST_MOONC_VERSION=$(cat version.json | jq -r '.items[] | select(.name == "moonc") | .version') + echo "Latest moonc version: $LATEST_MOONC_VERSION" + + # Extract date from moonc version (format: v0.6.25+d6913262c (2025-08-27)) + LATEST_DATE=$(echo "$LATEST_MOONC_VERSION" | grep -o '([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\})' | tr -d '()') + echo "Latest date: $LATEST_DATE" + echo "latest-date=$LATEST_DATE" >> $GITHUB_OUTPUT + + - name: get pre-release version info + id: prerelease + run: | + # Get pre-release moonc version + PRERELEASE_MOONC_VERSION=$(moonc -v 2>&1 | head -1) + echo "Pre-release moonc version: $PRERELEASE_MOONC_VERSION" + + # Extract date from pre-release version (format: v0.6.25+d6913262c (2025-08-27)) + PRERELEASE_DATE=$(echo "$PRERELEASE_MOONC_VERSION" | grep -o '([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\})' | tr -d '()') + echo "Pre-release date: $PRERELEASE_DATE" + echo "prerelease-date=$PRERELEASE_DATE" >> $GITHUB_OUTPUT + + - name: check if should skip + id: check + run: | + LATEST_DATE="${{ steps.latest.outputs.latest-date }}" + PRERELEASE_DATE="${{ steps.prerelease.outputs.prerelease-date }}" + BRANCH_NAME="${{ github.ref_name }}" + + echo "Latest date: $LATEST_DATE" + echo "Pre-release date: $PRERELEASE_DATE" + echo "Branch name: $BRANCH_NAME" + + # Always run for pre-release branches + if [[ "$BRANCH_NAME" == pre-release* ]]; then + echo "Pre-release branch detected, running all jobs" + echo "should-skip=false" >> $GITHUB_OUTPUT + exit 0 + fi + + # Compare dates (YYYY-MM-DD format) + if [[ "$LATEST_DATE" < "$PRERELEASE_DATE" ]] ; then + echo "Pre-release is newer than latest release, running jobs" + echo "should-skip=false" >> $GITHUB_OUTPUT + else + echo "Latest release is newer than or equal to pre-release, skipping jobs" + echo "should-skip=true" >> $GITHUB_OUTPUT + fi + + moon-info-check: + needs: version-check + if: ${{ needs.version-check.outputs.should-skip == 'false' }} + continue-on-error: true + strategy: + matrix: + os: [ubuntu-latest] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: install + run: | + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s pre-release + echo "$HOME/.moon/bin" >> $GITHUB_PATH + + - name: moon version + run: | + moon version --all + moonrun --version + + - name: moon info + run: | + moon info --target wasm,wasm-gc,js,native + git diff --exit-code + + moon-fmt-check: + needs: version-check + if: ${{ needs.version-check.outputs.should-skip == 'false' }} + continue-on-error: true + strategy: + matrix: + os: [ubuntu-latest] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: install + if: ${{ matrix.os != 'windows-latest' }} + run: | + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s pre-release + echo "$HOME/.moon/bin" >> $GITHUB_PATH + + - name: moon version + run: | + moon version --all + moonrun --version + + - name: format diff + run: | + moon fmt + git diff --exit-code + pre-release-check: + needs: version-check + if: ${{ needs.version-check.outputs.should-skip == 'false' }} continue-on-error: true strategy: matrix: - os: [macos-latest, ubuntu-latest, windows-latest, macos-13] + os: [macos-latest, ubuntu-latest, windows-latest] + fail-fast: false runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -36,19 +161,9 @@ jobs: moon version --all moonrun --version - - name: moon check + - name: check run: moon check --deny-warn - - name: moon info - run: | - moon info --target wasm,wasm-gc,js,native - git diff --exit-code - - - name: format diff - run: | - moon fmt - git diff --exit-code - - name: Set ulimit and run moon test if: ${{ matrix.os != 'windows-latest' }} run: | @@ -71,12 +186,12 @@ jobs: if: ${{ matrix.os == 'windows-latest' }} run: | moon test --release --target all - + - name: Run moon test on Windows (--target native) if: ${{ matrix.os == 'windows-latest' }} run: | moon test --target native - + - name: Run moon test on Windows (--release + --target native) if: ${{ matrix.os == 'windows-latest' }} run: | @@ -89,6 +204,13 @@ jobs: - name: moon bundle run: moon bundle --all + - name: check coverage + run: | + moon test --enable-coverage + moon coverage report -f summary > coverage_summary.txt + # Put the coverage report in the pipeline output + cat coverage_summary.txt >> "$GITHUB_STEP_SUMMARY" + - name: check core size if: ${{ matrix.os != 'windows-latest' }} run: find ./target -name '*.core' | xargs ls -lh diff --git a/bundled-core/.github/workflows/stable-check.yml b/bundled-core/.github/workflows/stable-check.yml index 410bcc2..e7f5027 100644 --- a/bundled-core/.github/workflows/stable-check.yml +++ b/bundled-core/.github/workflows/stable-check.yml @@ -11,19 +11,19 @@ jobs: setup-matrix: runs-on: ubuntu-latest outputs: - os : ${{ steps.setup-os-matrix.outputs.os }} + os: ${{ steps.setup-os-matrix.outputs.os }} steps: - name: setup os matrix id: setup-os-matrix run: | if ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}; then - os='["ubuntu-latest","macos-latest", "macos-13", "windows-latest"]' + os='["ubuntu-latest","macos-latest", "windows-latest"]' else os='["ubuntu-latest","macos-latest","windows-latest"]' fi list=$(echo ${os} | jq -c) echo "os=${list}" >> $GITHUB_OUTPUT - + stable-check: needs: setup-matrix strategy: @@ -52,8 +52,8 @@ jobs: moon version --all moonrun --version - - name: moon check - run: moon check --deny-warn + - name: check + run: moon check --deny-warn --target all - name: moon info run: | @@ -86,7 +86,7 @@ jobs: if: ${{ matrix.os == 'windows-latest' }} run: | moon test --release --target all - + - name: Run moon test on Windows (--target native) if: ${{ matrix.os == 'windows-latest' }} run: | @@ -102,7 +102,7 @@ jobs: - name: check core size on windows if: ${{ matrix.os == 'windows-latest' }} run: Get-ChildItem -Path ".\target" -Recurse -Filter "*.core" | ForEach-Object { "{0} ({1} bytes)" -f $_.FullName, $_.Length } - + stable-native-opt-test: needs: setup-matrix strategy: @@ -129,8 +129,8 @@ jobs: - name: moon version run: | moon version --all - - - name: moon check + + - name: check run: moon check --target native --deny-warn - name: Set ulimit and run moon test (--release + --target native) @@ -142,10 +142,9 @@ jobs: - name: Setup MSVC if: ${{ matrix.os == 'windows-latest' }} uses: ilammy/msvc-dev-cmd@v1 - + - name: Run moon test on Windows (--release + --target native) if: ${{ matrix.os == 'windows-latest' }} run: | moon test --target native --release - \ No newline at end of file diff --git a/bundled-core/.gitignore b/bundled-core/.gitignore index 8c34ff3..6fe22fb 100644 --- a/bundled-core/.gitignore +++ b/bundled-core/.gitignore @@ -4,6 +4,9 @@ moonbit-coverage-*.txt _coverage bisect.coverage +*.log .DS_Store -.ai/ \ No newline at end of file +.ai/ +.auto-coder/ +.moonagent/ \ No newline at end of file diff --git a/bundled-core/AGENTS.md b/bundled-core/AGENTS.md index 8b9b6f2..2188d98 100644 --- a/bundled-core/AGENTS.md +++ b/bundled-core/AGENTS.md @@ -28,4 +28,7 @@ Each package has its files and blackbox test files (common, ending in `_test.mbt - In the toplevel directory, this is a `moon.mod.json` file listing about the module and some meta information. +- When writing tests, you are encouraged to use `inspect` and run `moon test --update` to update the snapshots, only use assertions + like `assert_eq` when you are in some loops where each snapshot may vary. You can use `moon coverage analyze > uncovered.log` to see which parts of your code are not covered by tests. +- agent-todo.md has some small taks that are easy for AI to pick up, agent is welcome to finish the taks and check the box when you are done \ No newline at end of file diff --git a/bundled-core/CONTRIBUTING.md b/bundled-core/CONTRIBUTING.md index 877e4a9..ba88c8c 100644 --- a/bundled-core/CONTRIBUTING.md +++ b/bundled-core/CONTRIBUTING.md @@ -101,4 +101,4 @@ After submitting your pull request, request a review from the project maintainer - function names, `snake_case` is preferred. - type parameters, one character starting from `A` is preferred, e.g, `fn[A,B] Array::map(self : Array[A], f : (A) -> (B)) -> Array[B]`, for some established conventions, `Map[K,V]` it is also accepted. -- type names, `CamelCase` is preferred, if one package is centered around one specific type, short name `T` is preferred, e.g, `@sorted_set.T`. +- type names, `CamelCase` is preferred. diff --git a/bundled-core/README.md b/bundled-core/README.md index aac0641..5c1cdce 100644 --- a/bundled-core/README.md +++ b/bundled-core/README.md @@ -1,6 +1,10 @@ # moonbitlang/core [![check](https://github.com/moonbitlang/core/actions/workflows/stable-check.yml/badge.svg)](https://github.com/moonbitlang/core/actions/workflows/stable-check.yml) [![Coverage Status](https://coveralls.io/repos/github/moonbitlang/core/badge.svg?branch=main)](https://coveralls.io/github/moonbitlang/core?branch=main) +[![doc](https://img.shields.io/badge/docs-mooncakes.io-green)](https://mooncakes.io/docs/moonbitlang/core) + + + moonbitlang/core is the standard library of the [MoonBit language](https://www.moonbitlang.com/). It is released alongside the compiler. You can view the documentation for the latest official release at . This repository serves as the development repository. diff --git a/bundled-core/abort/abort.mbti b/bundled-core/abort/pkg.mbti similarity index 100% rename from bundled-core/abort/abort.mbti rename to bundled-core/abort/pkg.mbti diff --git a/bundled-core/array/README.mbt.md b/bundled-core/array/README.mbt.md index fce100a..0bda098 100644 --- a/bundled-core/array/README.mbt.md +++ b/bundled-core/array/README.mbt.md @@ -63,9 +63,9 @@ test "sorting" { sorted2.sort_by((a, b) => a.length().compare(b.length())) inspect( sorted2, - content= + content=( #|["b", "aa", "ccc"] - , + ), ) // Sort by key @@ -74,9 +74,9 @@ test "sorting" { sorted3.sort_by_key(p => p.0) inspect( sorted3, - content= + content=( #|[(1, "a"), (2, "b"), (3, "c")] - , + ), ) } ``` @@ -94,10 +94,6 @@ test "array views" { // Map view to new array let doubled = view.map(x => x * 2) inspect(doubled, content="[4, 6, 8]") - - // Modify view in-place - view.map_inplace(x => x + 1) - inspect(arr, content="[1, 3, 4, 5, 5]") } ``` diff --git a/bundled-core/array/array.mbt b/bundled-core/array/array.mbt index cba891e..d1e75c9 100644 --- a/bundled-core/array/array.mbt +++ b/bundled-core/array/array.mbt @@ -52,42 +52,6 @@ pub fn[T] Array::push_iter(self : Self[T], iter : Iter[T]) -> Unit { } } -///| -/// Creates a new array of the specified length, where each element is -/// initialized using an index-based initialization function. -/// -/// Parameters: -/// -/// * `length` : The length of the new array. If `length` is less than or equal -/// to 0, returns an empty array. -/// * `initializer` : A function that takes an index (starting from 0) and -/// returns a value of type `T`. This function is called for each index to -/// initialize the corresponding element. -/// -/// Returns a new array of type `Array[T]` with the specified length, where each -/// element is initialized using the provided function. -/// -/// Example: -/// -/// ```moonbit -/// let arr = Array::makei(3, i => i * 2) -/// inspect(arr, content="[0, 2, 4]") -/// ``` -pub fn[T] Array::makei( - length : Int, - value : (Int) -> T raise? -) -> Array[T] raise? { - if length <= 0 { - [] - } else { - let array = Array::make(length, value(0)) - for i in 1.. Int) -> Array[T] { /// pub fn[A, B] filter_map( self : Array[A], - f : (A) -> B? raise? + f : (A) -> B? raise?, ) -> Array[B] raise? { let result = [] for x in self { @@ -185,7 +149,7 @@ pub fn[A] last(self : Array[A]) -> A? { } } -///| +///| /// Zips two arrays into a single array of tuples. /// /// Parameters: @@ -238,7 +202,7 @@ pub fn[T1, T2] unzip(self : Array[(T1, T2)]) -> (Array[T1], Array[T2]) { (arr1, arr2) } -///| +///| /// Zips two arrays into a single array by applying a function to each pair of elements. /// /// Parameters: @@ -255,12 +219,12 @@ pub fn[T1, T2] unzip(self : Array[(T1, T2)]) -> (Array[T1], Array[T2]) { /// let arr1 = [1, 2, 3] /// let arr2 = [4, 5, 6] /// let add = (a, b) => a + b -/// inspect(zip_with(arr1, arr2, add), content="[5, 7, 9]") +/// inspect(@array.zip_with(arr1, arr2, add), content="[5, 7, 9]") /// ``` pub fn[A, B, C] zip_with( l : Array[A], r : Array[B], - merge : (A, B) -> C raise? + merge : (A, B) -> C raise?, ) -> Array[C] raise? { let length = if l.length() < r.length() { l.length() } else { r.length() } let arr = Array::new(capacity=length) @@ -271,7 +235,7 @@ pub fn[A, B, C] zip_with( } } -///| +///| /// Zips two arrays into an iterator that yields corresponding elements. /// /// Parameters: @@ -297,7 +261,7 @@ pub fn[A, B] zip_to_iter2(self : Array[A], other : Array[B]) -> Iter2[A, B] { other.length() } for i in 0.. Iter2[A, B] { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Array[X] with arbitrary( size, - rs + rs, ) { let len = if size == 0 { 0 } else { rs.next_positive_int() % size } Array::makei(len, x => X::arbitrary(x, rs)) @@ -322,6 +286,14 @@ pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Array[X] with arbi /// * `separator` : The string inserted between each element. /// /// Returns a single concatenated `String`. -pub fn join(self : Array[String], separator : @string.View) -> String { +/// # Example: +/// ```moonbit +/// let s = "hello world" +/// inspect(s.split(" ").to_array().join(":"), content="hello:world") +/// ``` +pub fn[A : @string.ToStringView] Array::join( + self : Array[A], + separator : @string.View, +) -> String { self[:].join(separator) } diff --git a/bundled-core/array/array_test.mbt b/bundled-core/array/array_test.mbt index e73a779..171331e 100644 --- a/bundled-core/array/array_test.mbt +++ b/bundled-core/array/array_test.mbt @@ -119,9 +119,9 @@ test "zip" { let arr4 = ["a", "b", "c"] inspect( arr3.zip(arr4), - content= + content=( #|[(1, "a"), (2, "b")] - , + ), ) // Test with an empty array @@ -146,22 +146,22 @@ test "unzip" { } ///| -test "zip_with" { +test "@array.zip_with" { // Test with two non-empty arrays and a function let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] let add = (a, b) => a + b - inspect(zip_with(arr1, arr2, add), content="[5, 7, 9]") + inspect(@array.zip_with(arr1, arr2, add), content="[5, 7, 9]") // Test with arrays of different lengths and a function let arr3 = [1, 2] let arr4 = [4, 5, 6] - inspect(zip_with(arr3, arr4, add), content="[5, 7]") + inspect(@array.zip_with(arr3, arr4, add), content="[5, 7]") // Test with an empty array and a function let arr5 : Array[Int] = [] let arr6 = [4, 5, 6] - inspect(zip_with(arr5, arr6, add), content="[]") + inspect(@array.zip_with(arr5, arr6, add), content="[]") } ///| @@ -179,9 +179,9 @@ test "zip_to_iter2" { let arr4 = ["a", "b", "c"] inspect( arr3.zip_to_iter2(arr4).to_array(), - content= + content=( #|[(1, "a"), (2, "b")] - , + ), ) // Test with an empty array @@ -190,6 +190,31 @@ test "zip_to_iter2" { inspect(arr5.zip_to_iter2(arr6), content="[]") } +///| +test "zip_to_iter2 early termination" { + // This test should trigger the uncovered line 301: break IterEnd + // We create an iterator that terminates early when it encounters a specific value + let arr1 = [1, 2, 3, 4, 5] + let arr2 = ['a', 'b', 'c', 'd', 'e'] + let mut count = 0 + let mut found_target = false + + // Use run to iterate with early termination + let _ = arr1 + .zip_to_iter2(arr2) + .run((x, _y) => { + count += 1 + if x == 3 { + found_target = true + IterEnd // This should trigger the uncovered line 301 + } else { + IterContinue + } + }) + inspect(found_target, content="true") + inspect(count, content="3") // Should visit (1,'a'), (2,'b'), (3,'c') and terminate +} + ///| test "zip_to_iter2 current behavior" { // Only for recording what can be done, but not what should be done @@ -222,7 +247,7 @@ test "Array[String]::join" { inspect(["a", "b", "c"].join(" "), content="a b c") inspect(["123", "456"].join(""), content="123456") inspect(["aaa", "bbb", "ccc"].join(" "), content="aaa bbb ccc") - inspect([].join(" ")) + inspect(([] : Array[@string.View]).join(" ")) } ///| @@ -234,5 +259,35 @@ test "Array[View]::join" { inspect(["a", "b", "c"][:].join(" "), content="a b c") inspect(["123", "456"][:].join(""), content="123456") inspect(["aaa", "bbb", "ccc"][:].join(" "), content="aaa bbb ccc") - inspect([][:].join(" ")) + inspect(([] : Array[String])[:].join(" ")) +} + +///| +test "fill" { + let arr = [1, 2, 3, 4, 5] + arr.fill(0) + inspect(arr, content="[0, 0, 0, 0, 0]") + let arr2 = [1, 2, 3, 4, 5] + arr2.fill(99, start=1, end=3) + inspect(arr2, content="[1, 99, 99, 4, 5]") + let arr3 = ["a", "b", "c", "d"] + arr3.fill("x", start=2) + inspect( + arr3, + content=( + #|["a", "b", "x", "x"] + ), + ) +} + +///| +test "panic fill with invalid start" { + let arr = [1, 2, 3, 4, 5] + arr.fill(0, start=-1) +} + +///| +test "panic fill with invalid end" { + let arr = [1, 2, 3, 4, 5] + arr.fill(0, start=2, end=10) } diff --git a/bundled-core/array/bitstring.mbt b/bundled-core/array/bitstring.mbt new file mode 100644 index 0000000..e0ad4d6 --- /dev/null +++ b/bundled-core/array/bitstring.mbt @@ -0,0 +1,368 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// offset and len are all in bits + +///| +/// Extract a single bit. +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_bit( + bs : ArrayView[Byte], + offset : Int, + _len : Int, +) -> UInt { + let byte_index = offset >> 3 + let bit_mask = (1 << (7 - (offset & 7))).to_byte() + // TODO: branchless for performance + if (bs.unsafe_get(byte_index) & bit_mask) != 0 { + 1 + } else { + 0 + } +} + +///| +/// Extract [2..8] bits. +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_byte( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> UInt { + let byte_index = offset >> 3 + if (offset & 7) == 0 { + // byte-aligned case + let byte = bs.unsafe_get(byte_index) + (byte >> (8 - len)).to_uint() + } else if (offset & 7) + len <= 8 { + // All bits are within the same byte - no need to read next byte + let byte = bs.unsafe_get(byte_index).to_uint() + let shift = 8 - ((offset & 7) + len) + let mask = (1U << len) - 1 + (byte >> shift) & mask + } else { + // extract 16 bits at [byte_index, byte_index + 1] + let b0 = bs.unsafe_get(byte_index).to_uint() + let b1 = bs.unsafe_get(byte_index + 1).to_uint() + let data = (b0 << 8) | b1 + // mask off the top bits + let bit_mask = (1U << (16 - (offset & 7))) - 1 + let data = data & bit_mask + let shift = 16 - ((offset & 7) + len) + data >> shift + } +} + +///| +/// Extract [9..32] bits in little-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 2 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length +/// +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_uint_le( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> UInt { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8) + match bytes_needed { + 2 => { + let b1 = bs.unsafe_extract_byte(offset + 8, len - 8) + (b1 << 8) | b0 + } + 3 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, len - 16) + (b2 << 16) | (b1 << 8) | b0 + } + 4 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, 8) + let b3 = bs.unsafe_extract_byte(offset + 24, len - 24) + (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + _ => abort("Invalid byte count for int32 extraction") + } +} + +///| +/// Extract [9..32] bits in big-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 2 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length +/// +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_uint_be( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> UInt { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8) + match bytes_needed { + 2 => { + let b1 = bs.unsafe_extract_byte(offset + 8, len - 8) + let shift = 16 - len + let data = (b0 << 8) | (b1 << shift) + data >> shift + } + 3 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, len - 16) + let shift = 24 - len + let data = (b0 << 16) | (b1 << 8) | (b2 << shift) + data >> shift + } + 4 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, 8) + let b3 = bs.unsafe_extract_byte(offset + 24, len - 24) + let shift = 32 - len + let data = (b0 << 24) | (b1 << 16) | (b2 << 8) | (b3 << shift) + data >> shift + } + _ => abort("Invalid byte count for int32 extraction") + } +} + +///| +/// Extract [33..64] bits in little-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 5 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length (5-8 bytes) +/// - For bit lengths < 33, use unsafe_extract_int_le instead +/// +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_uint64_le( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> UInt64 { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8).to_uint64() + let b1 = bs.unsafe_extract_byte(offset + 8, 8).to_uint64() + let b2 = bs.unsafe_extract_byte(offset + 16, 8).to_uint64() + let b3 = bs.unsafe_extract_byte(offset + 24, 8).to_uint64() + match bytes_needed { + 5 => { + let b4 = bs.unsafe_extract_byte(offset + 32, len - 32).to_uint64() + (b4 << 32) | (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + 6 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, len - 40).to_uint64() + (b5 << 40) | (b4 << 32) | (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + 7 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, len - 48).to_uint64() + (b6 << 48) | + (b5 << 40) | + (b4 << 32) | + (b3 << 24) | + (b2 << 16) | + (b1 << 8) | + b0 + } + 8 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, 8).to_uint64() + let b7 = bs.unsafe_extract_byte(offset + 56, len - 56).to_uint64() + (b7 << 56) | + (b6 << 48) | + (b5 << 40) | + (b4 << 32) | + (b3 << 24) | + (b2 << 16) | + (b1 << 8) | + b0 + } + _ => abort("Invalid byte count for int64 extraction") + } +} + +///| +/// Extract [33..64] bits in big-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 5 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length (5-8 bytes) +/// - For bit lengths < 33, use unsafe_extract_int_be instead +/// +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_uint64_be( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> UInt64 { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8).to_uint64() + let b1 = bs.unsafe_extract_byte(offset + 8, 8).to_uint64() + let b2 = bs.unsafe_extract_byte(offset + 16, 8).to_uint64() + let b3 = bs.unsafe_extract_byte(offset + 24, 8).to_uint64() + match bytes_needed { + 5 => { + let b4 = bs.unsafe_extract_byte(offset + 32, len - 32).to_uint64() + let shift = 40 - len + let data = (b0 << 32) | + (b1 << 24) | + (b2 << 16) | + (b3 << 8) | + (b4 << shift) + data >> shift + } + 6 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, len - 40).to_uint64() + let shift = 48 - len + let data = (b0 << 40) | + (b1 << 32) | + (b2 << 24) | + (b3 << 16) | + (b4 << 8) | + (b5 << shift) + data >> shift + } + 7 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, len - 48).to_uint64() + let shift = 56 - len + let data = (b0 << 48) | + (b1 << 40) | + (b2 << 32) | + (b3 << 24) | + (b4 << 16) | + (b5 << 8) | + (b6 << shift) + data >> shift + } + 8 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, 8).to_uint64() + let b7 = bs.unsafe_extract_byte(offset + 56, len - 56).to_uint64() + let shift = 64 - len + let data = (b0 << 56) | + (b1 << 48) | + (b2 << 40) | + (b3 << 32) | + (b4 << 24) | + (b5 << 16) | + (b6 << 8) | + (b7 << shift) + data >> shift + } + _ => abort("Invalid byte count for int64 extraction") + } +} + +///| +/// Extract a subview from a view. `offset` and `len` are in bits and must be +/// aligned to bytes. +#internal(experimental, "subject to breaking change without notice") +pub fn ArrayView::unsafe_extract_bytesview( + bs : ArrayView[Byte], + offset : Int, + len : Int, +) -> ArrayView[Byte] { + let start = offset >> 3 + let end = start + (len >> 3) + bs[start:end] +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_bit( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_bit(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_byte( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_byte(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_uint_le( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_uint_le(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_uint_be( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_uint_be(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_uint64_le( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt64 { + bs[:].unsafe_extract_uint64_le(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_uint64_be( + bs : Array[Byte], + offset : Int, + len : Int, +) -> UInt64 { + bs[:].unsafe_extract_uint64_be(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Array::unsafe_extract_bytesview( + bs : Array[Byte], + offset : Int, + len : Int, +) -> ArrayView[Byte] { + bs[:].unsafe_extract_bytesview(offset, len) +} diff --git a/bundled-core/array/bitstring_test.mbt b/bundled-core/array/bitstring_test.mbt new file mode 100644 index 0000000..98770cd --- /dev/null +++ b/bundled-core/array/bitstring_test.mbt @@ -0,0 +1,168 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "extract bit" { + let bs1 = b"\x89\xab\xcd\xef" + let bs2 : Array[Byte] = bs1.to_array() + for i in 0..=(bs1.length() * 8 - 1) { + assert_eq(bs1.unsafe_extract_bit(i, 1), bs2.unsafe_extract_bit(i, 1)) + } +} + +///| +test "extract byte" { + let bs1 = b"\x89\xab\xcd\xef" + let bs2 : Array[Byte] = bs1.to_array() + for i in 0..=(bs1.length() * 8 - 8) { + assert_eq(bs1.unsafe_extract_byte(i, 2), bs2.unsafe_extract_byte(i, 2)) + assert_eq(bs1.unsafe_extract_byte(i, 3), bs2.unsafe_extract_byte(i, 3)) + assert_eq(bs1.unsafe_extract_byte(i, 4), bs2.unsafe_extract_byte(i, 4)) + assert_eq(bs1.unsafe_extract_byte(i, 5), bs2.unsafe_extract_byte(i, 5)) + assert_eq(bs1.unsafe_extract_byte(i, 8), bs2.unsafe_extract_byte(i, 8)) + } +} + +///| +test "extract int32" { + let bs1 = b"\x89\xab\xcd\xef\x01\x23\x45\x67" + let bs2 : Array[Byte] = bs1.to_array() + for i in 0..=(bs1.length() * 8 - 32) { + assert_eq( + bs1.unsafe_extract_uint_be(i, 9), + bs2.unsafe_extract_uint_be(i, 9), + ) + assert_eq( + bs1.unsafe_extract_uint_be(i, 12), + bs2.unsafe_extract_uint_be(i, 12), + ) + assert_eq( + bs1.unsafe_extract_uint_be(i, 16), + bs2.unsafe_extract_uint_be(i, 16), + ) + assert_eq( + bs1.unsafe_extract_uint_be(i, 23), + bs2.unsafe_extract_uint_be(i, 23), + ) + assert_eq( + bs1.unsafe_extract_uint_be(i, 24), + bs2.unsafe_extract_uint_be(i, 24), + ) + assert_eq( + bs1.unsafe_extract_uint_be(i, 32), + bs2.unsafe_extract_uint_be(i, 32), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 9), + bs2.unsafe_extract_uint_le(i, 9), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 12), + bs2.unsafe_extract_uint_le(i, 12), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 16), + bs2.unsafe_extract_uint_le(i, 16), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 23), + bs2.unsafe_extract_uint_le(i, 23), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 24), + bs2.unsafe_extract_uint_le(i, 24), + ) + assert_eq( + bs1.unsafe_extract_uint_le(i, 32), + bs2.unsafe_extract_uint_le(i, 32), + ) + } +} + +///| +test "extract int64" { + let bs1 = b"\x89\xab\xcd\xef\x01\x23\x45\x67\x01\x23\x45\x67\x89\xab\xcd\xef" + let bs2 : Array[Byte] = bs1.to_array() + for i in 0..=(bs1.length() * 8 - 64) { + assert_eq( + bs1.unsafe_extract_uint64_be(i, 33), + bs2.unsafe_extract_uint64_be(i, 33), + ) + assert_eq( + bs1.unsafe_extract_uint64_be(i, 40), + bs2.unsafe_extract_uint64_be(i, 40), + ) + assert_eq( + bs1.unsafe_extract_uint64_be(i, 45), + bs2.unsafe_extract_uint64_be(i, 45), + ) + assert_eq( + bs1.unsafe_extract_uint64_be(i, 48), + bs2.unsafe_extract_uint64_be(i, 48), + ) + assert_eq( + bs1.unsafe_extract_uint64_be(i, 56), + bs2.unsafe_extract_uint64_be(i, 56), + ) + assert_eq( + bs1.unsafe_extract_uint64_be(i, 64), + bs2.unsafe_extract_uint64_be(i, 64), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 33), + bs2.unsafe_extract_uint64_le(i, 33), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 40), + bs2.unsafe_extract_uint64_le(i, 40), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 45), + bs2.unsafe_extract_uint64_le(i, 45), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 48), + bs2.unsafe_extract_uint64_le(i, 48), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 56), + bs2.unsafe_extract_uint64_le(i, 56), + ) + assert_eq( + bs1.unsafe_extract_uint64_le(i, 64), + bs2.unsafe_extract_uint64_le(i, 64), + ) + } +} + +///| +test "extract view" { + let bs1 = b"\x89\xab\xcd\xef" + let bs2 : Array[Byte] = bs1.to_array() + fn equal(a : @bytes.View, b : ArrayView[Byte]) raise { + assert_eq(a.length(), b.length()) + for i in 0.. Unit { - let src_len = src.length() - guard bytes_offset >= 0 && bytes_offset + src_len - 1 < self.length() - for i = 0, j = bytes_offset; i < src_len; i = i + 1, j = j + 1 { - self[j] = src[i] - } + FixedArray::blit_from_bytes( + self, + bytes_offset, + src.data(), + src.start_offset(), + src.length(), + ) } diff --git a/bundled-core/array/deprecated.mbt b/bundled-core/array/deprecated.mbt index 2b58b13..4e36a8a 100644 --- a/bundled-core/array/deprecated.mbt +++ b/bundled-core/array/deprecated.mbt @@ -11,7 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use method call") -pub fnalias Array::push_iter diff --git a/bundled-core/array/fixedarray.mbt b/bundled-core/array/fixedarray.mbt index 3556f61..5ceba2d 100644 --- a/bundled-core/array/fixedarray.mbt +++ b/bundled-core/array/fixedarray.mbt @@ -29,7 +29,7 @@ /// ``` pub fn[T] FixedArray::each( self : FixedArray[T], - f : (T) -> Unit raise? + f : (T) -> Unit raise?, ) -> Unit raise? { for v in self { f(v) @@ -50,18 +50,18 @@ test "each" { i = 0 ([] : FixedArray[_]).each(f) assert_false(failed) - assert_eq(i, 0) + inspect(i, content="0") } { i = 0 ([1] : FixedArray[_]).each(f) assert_false(failed) - assert_eq(i, 1) + inspect(i, content="1") } i = 0 ([1, 2, 3, 4, 5] : FixedArray[_]).each(f) assert_false(failed) - assert_eq(i, 5) + inspect(i, content="5") } ///| @@ -81,7 +81,7 @@ test "each" { /// ``` pub fn[T] FixedArray::eachi( self : FixedArray[T], - f : (Int, T) -> Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { for i, v in self { f(i, v) @@ -102,18 +102,18 @@ test "eachi" { i = 0 ([] : FixedArray[_]).eachi(f) assert_false(failed) - assert_eq(i, 0) + inspect(i, content="0") } { i = 0 ([1] : FixedArray[_]).eachi(f) assert_false(failed) - assert_eq(i, 1) + inspect(i, content="1") } i = 0 ([1, 2, 3, 4, 5] : FixedArray[_]).eachi(f) assert_false(failed) - assert_eq(i, 5) + inspect(i, content="5") } ///| @@ -133,7 +133,7 @@ test "eachi" { /// ``` pub fn[T] FixedArray::rev_each( self : FixedArray[T], - f : (T) -> Unit raise? + f : (T) -> Unit raise?, ) -> Unit raise? { for i = self.length() - 1; i >= 0; i = i - 1 { f(self[i]) @@ -154,18 +154,18 @@ test "rev_each" { i = 1 ([] : FixedArray[_]).rev_each(f) assert_false(failed) - assert_eq(i, 1) + inspect(i, content="1") } { i = 2 ([1] : FixedArray[_]).rev_each(f) assert_false(failed) - assert_eq(i, 1) + inspect(i, content="1") } i = 6 ([1, 2, 3, 4, 5] : FixedArray[_]).rev_each(f) assert_false(failed) - assert_eq(i, 1) + inspect(i, content="1") } ///| @@ -185,7 +185,7 @@ test "rev_each" { /// ``` pub fn[T] FixedArray::rev_eachi( self : FixedArray[T], - f : (Int, T) -> Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { let len = self.length() for i in 0.. U raise? + f : (T) -> U raise?, ) -> FixedArray[U] raise? { if self.length() == 0 { return [] @@ -276,7 +276,7 @@ test "map" { /// ``` pub fn[T, U] FixedArray::mapi( self : FixedArray[T], - f : (Int, T) -> U raise? + f : (Int, T) -> U raise?, ) -> FixedArray[U] raise? { if self.length() == 0 { return [] @@ -322,7 +322,7 @@ test "mapi" { /// ``` pub fn[T] FixedArray::makei( length : Int, - value : (Int) -> T raise? + value : (Int) -> T raise?, ) -> FixedArray[T] raise? { if length <= 0 { [] @@ -338,14 +338,14 @@ pub fn[T] FixedArray::makei( ///| test "fixedarray_new_with_index" { let empty = FixedArray::makei(0, i => i) - assert_eq(empty.length(), 0) + inspect(empty.length(), content="0") let simple_arr = FixedArray::makei(1, i => i) - assert_eq(simple_arr.length(), 1) - assert_eq(simple_arr[0], 0) + inspect(simple_arr.length(), content="1") + inspect(simple_arr[0], content="0") let arr = FixedArray::makei(2, i => i) - assert_eq(arr.length(), 2) - assert_eq(arr[0], 0) - assert_eq(arr[1], 1) + inspect(arr.length(), content="2") + inspect(arr[0], content="0") + inspect(arr[1], content="1") } ///| @@ -382,12 +382,12 @@ test "from_array" { /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5].fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn[A, B] FixedArray::fold( self : FixedArray[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(acc, self[i]) @@ -399,12 +399,12 @@ pub fn[A, B] FixedArray::fold( ///| test "fold" { let sum = ([] : FixedArray[_]).fold(init=1, (sum, elem) => sum + elem) - assert_eq(sum, 1) + inspect(sum, content="1") let sum = ([1] : FixedArray[_]).fold(init=2, (sum, elem) => sum + elem) - assert_eq(sum, 3) + inspect(sum, content="3") let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold(init=0, (sum, elem) => sum + elem) - assert_eq(sum, 15) + inspect(sum, content="15") } ///| @@ -413,12 +413,12 @@ test "fold" { /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5].rev_fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn[A, B] FixedArray::rev_fold( self : FixedArray[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = self.length() - 1, acc = init; i >= 0; { continue i - 1, f(acc, self[i]) @@ -430,12 +430,12 @@ pub fn[A, B] FixedArray::rev_fold( ///| test "rev_fold" { let sum = ([] : FixedArray[_]).rev_fold(init=1, (sum, elem) => sum + elem) - assert_eq(sum, 1) + inspect(sum, content="1") let sum = ([1] : FixedArray[_]).rev_fold(init=2, (sum, elem) => sum + elem) - assert_eq(sum, 3) + inspect(sum, content="3") let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).rev_fold(init=0, (sum, elem) => sum + elem) - assert_eq(sum, 15) + inspect(sum, content="15") } ///| @@ -444,12 +444,12 @@ test "rev_fold" { /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5].foldi(init=0, (index, sum, _elem) => sum + index) -/// assert_eq(sum, 10) +/// inspect(sum, content="10") /// ``` pub fn[A, B] FixedArray::foldi( self : FixedArray[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(i, acc, self[i]) @@ -463,14 +463,14 @@ test "fold_lefti" { let f = (index, sum, elem) => index + sum + elem { let sum = ([] : FixedArray[_]).foldi(init=1, f) - assert_eq(sum, 1) + inspect(sum, content="1") } { let sum = ([1] : FixedArray[_]).foldi(init=2, f) - assert_eq(sum, 3) + inspect(sum, content="3") } let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).foldi(init=0, f) - assert_eq(sum, 25) + inspect(sum, content="25") } ///| @@ -479,12 +479,12 @@ test "fold_lefti" { /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5].rev_foldi(init=0, (index, sum, _elem) => sum + index) -/// assert_eq(sum, 10) +/// inspect(sum, content="10") /// ``` pub fn[A, B] FixedArray::rev_foldi( self : FixedArray[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { let len = self.length() for i = len - 1, acc = init; i >= 0; { @@ -499,14 +499,14 @@ test "rev_foldi" { let f = (index, sum, elem) => index + sum + elem { let sum = ([] : FixedArray[_]).rev_foldi(init=1, f) - assert_eq(sum, 1) + inspect(sum, content="1") } { let sum = ([1] : FixedArray[_]).rev_foldi(init=2, f) - assert_eq(sum, 3) + inspect(sum, content="3") } let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).rev_foldi(init=0, f) - assert_eq(sum, 25) + inspect(sum, content="25") } ///| @@ -664,10 +664,10 @@ test "swap" { /// ``` pub fn[T] FixedArray::all( self : FixedArray[T], - f : (T) -> Bool raise? + f : (T) -> Bool raise?, ) -> Bool raise? { for i in 0.. Bool raise? + f : (T) -> Bool raise?, ) -> Bool raise? { for i in 0.. Bool { if prefix.length() > self.length() { return false @@ -877,7 +877,7 @@ test "starts_with" { /// ``` pub fn[T : Eq] FixedArray::ends_with( self : FixedArray[T], - suffix : FixedArray[T] + suffix : FixedArray[T], ) -> Bool { let self_len = self.length() let suf_len = suffix.length() @@ -944,9 +944,9 @@ test "ends_with" { /// inspect(arr1 == arr2, content="true") /// inspect(arr1 == arr3, content="false") /// ``` -pub impl[T : Eq] Eq for FixedArray[T] with op_equal( +pub impl[T : Eq] Eq for FixedArray[T] with equal( self : FixedArray[T], - that : FixedArray[T] + that : FixedArray[T], ) -> Bool { if self.length() != that.length() { return false @@ -988,7 +988,7 @@ test "op_equal" { } ///| -/// Compares two fixed arrays lexicographically based on their elements. First +/// Compares two fixed arrays based on shortlex order by their elements. First /// compares the lengths of the arrays, then compares elements pairwise until a /// difference is found or all elements have been compared. /// @@ -1047,7 +1047,7 @@ pub impl[T : Compare] Compare for FixedArray[T] with compare(self, other) { /// let arr2 : FixedArray[Int] = [4, 5, 6] /// inspect(arr1 + arr2, content="[1, 2, 3, 4, 5, 6]") /// ``` -pub impl[T] Add for FixedArray[T] with op_add(self, other) { +pub impl[T] Add for FixedArray[T] with add(self, other) { let slen = self.length() let nlen = other.length() FixedArray::makei(slen + nlen, i => if i < slen { @@ -1095,14 +1095,14 @@ test "iter" { assert_eq(i, arr.length()) inspect( exb, - content= + content=( #|1 #|2 #|3 #|4 #|5 #| - , + ), ) } @@ -1163,7 +1163,7 @@ pub fn[A] FixedArray::last(self : FixedArray[A]) -> A? { /// ``` pub fn FixedArray::join( self : FixedArray[String], - separator : @string.View + separator : @string.View, ) -> String { let len = self.length() if len == 0 { @@ -1238,7 +1238,7 @@ test "FixedArray::join" { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for FixedArray[X] with arbitrary( size, - rs + rs, ) { let len = if size == 0 { 0 } else { rs.next_positive_int() % size } FixedArray::makei(len, i => X::arbitrary(i, rs)) diff --git a/bundled-core/array/fixedarray_sort.mbt b/bundled-core/array/fixedarray_sort.mbt index 06add0f..16a16a4 100644 --- a/bundled-core/array/fixedarray_sort.mbt +++ b/bundled-core/array/fixedarray_sort.mbt @@ -80,7 +80,7 @@ fn[T : Compare] timsort(arr : FixedArraySlice[T]) -> Unit { ///| fn[T : Compare] FixedArraySlice::insertion_sort( - arr : FixedArraySlice[T] + arr : FixedArraySlice[T], ) -> Unit { for i in 1.. 0 && arr[j] < arr[j - 1]; j = j - 1 { @@ -150,7 +150,7 @@ fn[T : Compare] find_streak(arr : FixedArraySlice[T]) -> (Int, Bool) { fn[T : Compare] provide_sorted_batch( arr : FixedArraySlice[T], start : Int, - end : Int + end : Int, ) -> Int { let len = arr.length() // This value is a balance between least comparisons and best performance, as @@ -171,13 +171,14 @@ fn[T : Compare] provide_sorted_batch( } } -///| // TimSort is infamous for its buggy implementations, as described here: // http://envisage-project.eu/timsort-specification-and-verification/ // // This function correctly checks invariants for the top four runs. Additionally, if the top // run starts at index 0, it will always demand a merge operation until the stack is fully // collapsed, in order to complete the sort. + +///| fn collapse(runs : Array[TimSortRun], stop : Int) -> Int? { let n : Int = runs.length() if n >= 2 && @@ -221,7 +222,7 @@ pub fn[T : Compare] FixedArray::sort(self : FixedArray[T]) -> Unit { fn[T : Compare] fixed_quick_sort( arr : FixedArraySlice[T], pred : T?, - limit : Int + limit : Int, ) -> Unit { let mut limit = limit let mut arr = arr @@ -253,7 +254,7 @@ fn[T : Compare] fixed_quick_sort( let (pivot, partitioned) = fixed_partition(arr, pivot_index) was_partitioned = partitioned balanced = minimum(pivot, len - pivot) >= len / 8 - if not(balanced) { + if !balanced { limit -= 1 } if pred is Some(pred) { @@ -308,7 +309,7 @@ fn[T : Compare] fixed_try_bubble_sort(arr : FixedArraySlice[T]) -> Bool { sorted = false arr.swap(j, j - 1) } - if not(sorted) { + if !sorted { tries += 1 if tries > max_tries { return false @@ -336,14 +337,14 @@ fn[T : Compare] fixed_bubble_sort(arr : FixedArraySlice[T]) -> Unit { test "fixed_try_bubble_sort" { let arr : FixedArray[_] = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = fixed_try_bubble_sort({ array: arr, start: 0, end: 8 }) - assert_eq(sorted, true) + inspect(sorted, content="true") assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8]) } ///| fn[T : Compare] fixed_partition( arr : FixedArraySlice[T], - pivot_index : Int + pivot_index : Int, ) -> (Int, Bool) { arr.swap(pivot_index, arr.length() - 1) let pivot = arr[arr.length() - 1] @@ -551,39 +552,39 @@ test "stable_sort_complex" { test "find_streak with empty array" { let arr : FixedArray[Int] = [] let (streak_end, was_reversed) = find_streak({ array: arr, start: 0, end: 0 }) - assert_eq(streak_end, 0) - assert_eq(was_reversed, false) + inspect(streak_end, content="0") + inspect(was_reversed, content="false") } ///| test "find_streak with single element array" { let arr : FixedArray[_] = [1] let (streak_end, was_reversed) = find_streak({ array: arr, start: 0, end: 1 }) - assert_eq(streak_end, 1) - assert_eq(was_reversed, false) + inspect(streak_end, content="1") + inspect(was_reversed, content="false") } ///| test "find_streak with increasing elements" { let arr : FixedArray[_] = [1, 2, 3, 4, 5] let (streak_end, was_reversed) = find_streak({ array: arr, start: 0, end: 5 }) - assert_eq(streak_end, 5) - assert_eq(was_reversed, false) + inspect(streak_end, content="5") + inspect(was_reversed, content="false") } ///| test "find_streak with decreasing elements" { let arr : FixedArray[_] = [5, 4, 3, 2, 1] let (streak_end, was_reversed) = find_streak({ array: arr, start: 0, end: 5 }) - assert_eq(streak_end, 5) - assert_eq(was_reversed, true) + inspect(streak_end, content="5") + inspect(was_reversed, content="true") } ///| test "provide_sorted_batch with long run" { let arr : FixedArray[_] = [1, 2, 3, 4, 5] let end = provide_sorted_batch({ array: arr, start: 0, end: 5 }, 0, 5) - assert_eq(end, 5) + inspect(end, content="5") } ///| diff --git a/bundled-core/array/fixedarray_sort_by.mbt b/bundled-core/array/fixedarray_sort_by.mbt index 5280b31..b8fd61e 100644 --- a/bundled-core/array/fixedarray_sort_by.mbt +++ b/bundled-core/array/fixedarray_sort_by.mbt @@ -26,7 +26,7 @@ /// ``` pub fn[T, K : Compare] FixedArray::sort_by_key( self : FixedArray[T], - map : (T) -> K + map : (T) -> K, ) -> Unit { fixed_quick_sort_by( { array: self, start: 0, end: self.length() }, @@ -60,7 +60,7 @@ test "@array.sort_by_key/basic" { /// ``` pub fn[T] FixedArray::sort_by( self : FixedArray[T], - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> Unit { fixed_quick_sort_by( { array: self, start: 0, end: self.length() }, @@ -127,7 +127,7 @@ fn[T] fixed_quick_sort_by( arr : FixedArraySlice[T], cmp : (T, T) -> Int, pred : T?, - limit : Int + limit : Int, ) -> Unit { let mut limit = limit let mut arr = arr @@ -159,7 +159,7 @@ fn[T] fixed_quick_sort_by( let (pivot, partitioned) = fixed_partition_by(arr, cmp, pivot_index) was_partitioned = partitioned balanced = minimum(pivot, len - pivot) >= len / 8 - if not(balanced) { + if !balanced { limit -= 1 } if pred is Some(pred) { @@ -196,7 +196,7 @@ fn[T] fixed_quick_sort_by( /// Returns whether the array is sorted. fn[T] fixed_try_bubble_sort_by( arr : FixedArraySlice[T], - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> Bool { let max_tries = 8 let mut tries = 0 @@ -206,7 +206,7 @@ fn[T] fixed_try_bubble_sort_by( sorted = false arr.swap(j, j - 1) } - if not(sorted) { + if !sorted { tries += 1 if tries > max_tries { return false @@ -224,7 +224,7 @@ fn[T] fixed_try_bubble_sort_by( /// Returns whether the array is sorted. fn[T] fixed_bubble_sort_by( arr : FixedArraySlice[T], - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> Unit { for i in 1.. 0 && cmp(arr[j - 1], arr[j]) > 0; j = j - 1 { @@ -238,7 +238,7 @@ test "try_bubble_sort" { let arr : FixedArray[_] = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = fixed_try_bubble_sort_by({ array: arr, start: 0, end: 8 }, (a, b) => a - b) - assert_eq(sorted, true) + inspect(sorted, content="true") assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8]) } @@ -246,7 +246,7 @@ test "try_bubble_sort" { fn[T] fixed_partition_by( arr : FixedArraySlice[T], cmp : (T, T) -> Int, - pivot_index : Int + pivot_index : Int, ) -> (Int, Bool) { arr.swap(pivot_index, arr.length() - 1) let pivot = arr[arr.length() - 1] @@ -273,7 +273,7 @@ fn[T] fixed_partition_by( /// Returns the pivot index and whether the array is likely sorted. fn[T] fixed_choose_pivot_by( arr : FixedArraySlice[T], - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> (Int, Bool) { let len = arr.length() let use_median_of_medians = 50 @@ -323,7 +323,7 @@ fn[T] fixed_heap_sort_by(arr : FixedArraySlice[T], cmp : (T, T) -> Int) -> Unit fn[T] fixed_sift_down_by( arr : FixedArraySlice[T], index : Int, - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> Unit { let mut index = index let len = arr.length() diff --git a/bundled-core/array/fixedarray_test.mbt b/bundled-core/array/fixedarray_test.mbt index 0552d08..db105cf 100644 --- a/bundled-core/array/fixedarray_test.mbt +++ b/bundled-core/array/fixedarray_test.mbt @@ -16,14 +16,14 @@ test "FixedArray::from_array with empty array" { let array : Array[Int] = [] let fixedArray = FixedArray::from_array(array) - assert_eq(fixedArray.length(), 0) + inspect(fixedArray.length(), content="0") } ///| test "FixedArray::from_array with non-empty array" { let array : Array[Int] = [1, 2, 3, 4, 5] let fixedArray = FixedArray::from_array(array) - assert_eq(fixedArray.length(), 5) + inspect(fixedArray.length(), content="5") for i in 0..<5 { assert_eq(fixedArray[i], array[i]) } @@ -33,7 +33,7 @@ test "FixedArray::from_array with non-empty array" { test "FixedArray::from_array with array of different type" { let array : Array[String] = ["a", "b", "c"] let fixedArray = FixedArray::from_array(array) - assert_eq(fixedArray.length(), 3) + inspect(fixedArray.length(), content="3") for i in 0..<3 { assert_eq(fixedArray[i], array[i]) } @@ -65,16 +65,16 @@ test "FixedArray::last" { inspect(FixedArray::last([1, 2, 3]), content="Some(3)") inspect( FixedArray::last(["a", "b", "c"]), - content= + content=( #|Some("c") - , + ), ) inspect(FixedArray::last([1]), content="Some(1)") inspect( FixedArray::last(["single"]), - content= + content=( #|Some("single") - , + ), ) } @@ -94,13 +94,31 @@ test "FixedArray::arbitrary" { ///| test "FixedArray::makei" { let empty = FixedArray::makei(0, _i => Ref::{ val: 3 }) - assert_eq(empty.length(), 0) + inspect(empty.length(), content="0") let simple_arr = FixedArray::makei(1, _i => Ref::{ val: 2 }) - assert_eq(simple_arr.length(), 1) - assert_eq(simple_arr[0].val, 2) + inspect(simple_arr.length(), content="1") + inspect(simple_arr[0].val, content="2") let arr = FixedArray::makei(2, _i => Ref::{ val: 1 }) - assert_eq(arr.length(), 2) + inspect(arr.length(), content="2") @test.is_not(arr[0], arr[1]) - assert_eq(arr[0].val, 1) - assert_eq(arr[1].val, 1) + inspect(arr[0].val, content="1") + inspect(arr[1].val, content="1") +} + +///| +test "FixedArray compare" { + let arr1 : FixedArray[Int] = [1, 2, 3] + let arr2 : FixedArray[Int] = [1, 2, 4] + let arr3 : FixedArray[Int] = [1, 2] + inspect(arr1.compare(arr2), content="-1") // arr1 < arr2 + inspect(arr2.compare(arr1), content="1") // arr2 > arr1 + inspect(arr1.compare(arr3), content="1") // arr1 > arr3 (longer) + inspect(arr1.compare(arr1), content="0") // arr1 = arr1 + + // Test empty arrays + let empty1 : FixedArray[Int] = [] + let empty2 : FixedArray[Int] = [] + inspect(empty1.compare(empty2), content="0") + inspect(empty1.compare(arr1), content="-1") + inspect(arr1.compare(empty1), content="1") } diff --git a/bundled-core/array/array.mbti b/bundled-core/array/pkg.generated.mbti similarity index 78% rename from bundled-core/array/array.mbti rename to bundled-core/array/pkg.generated.mbti index 2537b23..8a1b0d0 100644 --- a/bundled-core/array/array.mbti +++ b/bundled-core/array/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/array" import( @@ -7,11 +8,10 @@ import( ) // Values -#deprecated -fn[T] push_iter(Array[T], Iter[T]) -> Unit - fn[A, B, C] zip_with(Array[A], Array[B], (A, B) -> C raise?) -> Array[C] raise? +// Errors + // Types and methods fn[T] FixedArray::all(Self[T], (T) -> Bool raise?) -> Bool raise? fn[T] FixedArray::any(Self[T], (T) -> Bool raise?) -> Bool raise? @@ -26,7 +26,7 @@ fn[A, B] FixedArray::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B rai fn[T] FixedArray::from_array(Array[T]) -> Self[T] fn[T] FixedArray::from_iter(Iter[T]) -> Self[T] fn[T : Compare] FixedArray::is_sorted(Self[T]) -> Bool -fn FixedArray::join(Self[String], @string.StringView) -> String +fn FixedArray::join(Self[String], @string.View) -> String fn[A] FixedArray::last(Self[A]) -> A? fn[T] FixedArray::makei(Int, (Int) -> T raise?) -> Self[T] raise? fn[T, U] FixedArray::map(Self[T], (T) -> U raise?) -> Self[U] raise? @@ -53,15 +53,21 @@ impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for FixedArray[X] fn[T] Array::copy(Self[T]) -> Self[T] fn[A, B] Array::filter_map(Self[A], (A) -> B? raise?) -> Self[B] raise? fn[T] Array::from_iter(Iter[T]) -> Self[T] -fn Array::join(Self[String], @string.StringView) -> String +fn[A : @string.ToStringView] Array::join(Self[A], @string.View) -> String fn[A] Array::last(Self[A]) -> A? -fn[T] Array::makei(Int, (Int) -> T raise?) -> Self[T] raise? fn[T] Array::push_iter(Self[T], Iter[T]) -> Unit fn[T] Array::shuffle(Self[T], rand~ : (Int) -> Int) -> Self[T] fn[T] Array::shuffle_in_place(Self[T], rand~ : (Int) -> Int) -> Unit fn[T : Compare] Array::sort(Self[T]) -> Unit fn[T] Array::sort_by(Self[T], (T, T) -> Int) -> Unit fn[T, K : Compare] Array::sort_by_key(Self[T], (T) -> K) -> Unit +fn Array::unsafe_extract_bit(Self[Byte], Int, Int) -> UInt +fn Array::unsafe_extract_byte(Self[Byte], Int, Int) -> UInt +fn Array::unsafe_extract_bytesview(Self[Byte], Int, Int) -> ArrayView[Byte] +fn Array::unsafe_extract_uint64_be(Self[Byte], Int, Int) -> UInt64 +fn Array::unsafe_extract_uint64_le(Self[Byte], Int, Int) -> UInt64 +fn Array::unsafe_extract_uint_be(Self[Byte], Int, Int) -> UInt +fn Array::unsafe_extract_uint_le(Self[Byte], Int, Int) -> UInt fn[T1, T2] Array::unzip(Self[(T1, T2)]) -> (Self[T1], Self[T2]) fn[A, B] Array::zip(Self[A], Self[B]) -> Self[(A, B)] fn[A, B] Array::zip_to_iter2(Self[A], Self[B]) -> Iter2[A, B] @@ -77,15 +83,25 @@ fn[A, B] ArrayView::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? fn[A, B] ArrayView::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? fn[A] ArrayView::iter(Self[A]) -> Iter[A] fn[A] ArrayView::iter2(Self[A]) -> Iter2[Int, A] -fn ArrayView::join(Self[String], @string.StringView) -> String +fn[A : @string.ToStringView] ArrayView::join(Self[A], @string.View) -> String fn[T, U] ArrayView::map(Self[T], (T) -> U raise?) -> Array[U] raise? +#deprecated fn[T] ArrayView::map_inplace(Self[T], (T) -> T raise?) -> Unit raise? fn[T, U] ArrayView::mapi(Self[T], (Int, T) -> U raise?) -> Array[U] raise? +#deprecated fn[T] ArrayView::mapi_inplace(Self[T], (Int, T) -> T raise?) -> Unit raise? fn[A, B] ArrayView::rev_fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? fn[A, B] ArrayView::rev_foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? +#deprecated fn[T] ArrayView::rev_inplace(Self[T]) -> Unit fn[T] ArrayView::to_array(Self[T]) -> Array[T] +fn ArrayView::unsafe_extract_bit(Self[Byte], Int, Int) -> UInt +fn ArrayView::unsafe_extract_byte(Self[Byte], Int, Int) -> UInt +fn ArrayView::unsafe_extract_bytesview(Self[Byte], Int, Int) -> Self[Byte] +fn ArrayView::unsafe_extract_uint64_be(Self[Byte], Int, Int) -> UInt64 +fn ArrayView::unsafe_extract_uint64_le(Self[Byte], Int, Int) -> UInt64 +fn ArrayView::unsafe_extract_uint_be(Self[Byte], Int, Int) -> UInt +fn ArrayView::unsafe_extract_uint_le(Self[Byte], Int, Int) -> UInt impl[T : Compare] Compare for ArrayView[T] impl[T : Eq] Eq for ArrayView[T] impl[A : Hash] Hash for ArrayView[A] diff --git a/bundled-core/array/slice.mbt b/bundled-core/array/slice.mbt index 2776223..133661d 100644 --- a/bundled-core/array/slice.mbt +++ b/bundled-core/array/slice.mbt @@ -33,7 +33,7 @@ fn[T] FixedArraySlice::op_get(self : FixedArraySlice[T], index : Int) -> T { fn[T] FixedArraySlice::op_set( self : FixedArraySlice[T], index : Int, - value : T + value : T, ) -> Unit { self.array[self.start + index] = value } @@ -42,7 +42,7 @@ fn[T] FixedArraySlice::op_set( fn[T] FixedArraySlice::swap( self : FixedArraySlice[T], a : Int, - b : Int + b : Int, ) -> Unit { self.array.swap(self.start + a, self.start + b) } @@ -61,7 +61,7 @@ fn[T] FixedArraySlice::rev_inplace(self : FixedArraySlice[T]) -> Unit { fn[T] FixedArraySlice::slice( self : FixedArraySlice[T], start : Int, - end : Int + end : Int, ) -> FixedArraySlice[T] { { array: self.array, start: self.start + start, end: self.start + end } } diff --git a/bundled-core/array/sort.mbt b/bundled-core/array/sort.mbt index 0e2e23d..db3df9b 100644 --- a/bundled-core/array/sort.mbt +++ b/bundled-core/array/sort.mbt @@ -61,7 +61,7 @@ fn[T : Compare] quick_sort(arr : ArrayView[T], pred : T?, limit : Int) -> Unit { let (pivot, partitioned) = partition(arr, pivot_index) was_partitioned = partitioned balanced = minimum(pivot, len - pivot) >= len / 8 - if not(balanced) { + if !balanced { limit -= 1 } if pred is Some(pred) { @@ -116,7 +116,7 @@ fn[T : Compare] try_bubble_sort(arr : ArrayView[T]) -> Bool { sorted = false arr.swap(j, j - 1) } - if not(sorted) { + if !sorted { tries += 1 if tries > max_tries { return false @@ -254,7 +254,7 @@ fn test_sort(f : (Array[Int]) -> Unit) -> Unit raise { test "try_bubble_sort" { let arr = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = try_bubble_sort(arr[0:8]) - assert_eq(sorted, true) + inspect(sorted, content="true") assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8]) } diff --git a/bundled-core/array/sort_by.mbt b/bundled-core/array/sort_by.mbt index 6b1052b..61eff9c 100644 --- a/bundled-core/array/sort_by.mbt +++ b/bundled-core/array/sort_by.mbt @@ -54,7 +54,7 @@ fn[T] quick_sort_by( arr : ArrayView[T], cmp : (T, T) -> Int, pred : T?, - limit : Int + limit : Int, ) -> Unit { let mut limit = limit let mut arr = arr @@ -86,7 +86,7 @@ fn[T] quick_sort_by( let (pivot, partitioned) = partition_by(arr, cmp, pivot_index) was_partitioned = partitioned balanced = minimum(pivot, len - pivot) >= len / 8 - if not(balanced) { + if !balanced { limit -= 1 } if pred is Some(pred) { @@ -130,7 +130,7 @@ fn[T] try_bubble_sort_by(arr : ArrayView[T], cmp : (T, T) -> Int) -> Bool { sorted = false arr.swap(j, j - 1) } - if not(sorted) { + if !sorted { tries += 1 if tries > max_tries { return false @@ -158,7 +158,7 @@ fn[T] bubble_sort_by(arr : ArrayView[T], cmp : (T, T) -> Int) -> Unit { fn[T] partition_by( arr : ArrayView[T], cmp : (T, T) -> Int, - pivot_index : Int + pivot_index : Int, ) -> (Int, Bool) { arr.swap(pivot_index, arr.length() - 1) let pivot = arr[arr.length() - 1] @@ -232,7 +232,7 @@ fn[T] heap_sort_by(arr : ArrayView[T], cmp : (T, T) -> Int) -> Unit { fn[T] sift_down_by( arr : ArrayView[T], index : Int, - cmp : (T, T) -> Int + cmp : (T, T) -> Int, ) -> Unit { let mut index = index let len = arr.length() @@ -254,7 +254,7 @@ fn[T] sift_down_by( test "try_bubble_sort" { let arr = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = try_bubble_sort_by(arr[0:8], (a, b) => a - b) - assert_eq(sorted, true) + inspect(sorted, content="true") assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8]) } diff --git a/bundled-core/array/view.mbt b/bundled-core/array/view.mbt index f9a931a..5669566 100644 --- a/bundled-core/array/view.mbt +++ b/bundled-core/array/view.mbt @@ -19,27 +19,15 @@ /// # Example /// /// ```moonbit -/// let arr = [1, 2, 3, 4, 5] -/// let view = arr[1:4] // Creates a view of elements at indices 1,2,3 -/// assert_eq(view[0], 2) -/// assert_eq(view.length(), 3) +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] // Creates a view of elements at indices 1,2,3 +/// inspect(view[0], content="2") +/// inspect(view.length(), content="3") /// ``` pub typealias ArrayView as View ///| -/// Reverses the elements in the array view in place. -/// -/// Parameters: -/// -/// * `self` : The array view whose elements are to be reversed. -/// -/// Example: -/// -/// ```moonbit -/// let arr = [1, 2, 3, 4, 5] -/// arr[:].rev_inplace() -/// inspect(arr, content="[5, 4, 3, 2, 1]") -/// ``` +#deprecated("ArrayView will be immutable, use array if you need mutation") pub fn[T] View::rev_inplace(self : View[T]) -> Unit { let mid_len = self.length() / 2 for i in 0.. Unit raise?) -> Unit raise? { /// let v = [3, 4, 5][:] /// let mut sum = 0 /// v.eachi((i, x) => { sum = sum + x + i }) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn[T] View::eachi( self : View[T], - f : (Int, T) -> Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { for i, v in self { f(i, v) @@ -103,7 +91,7 @@ pub fn[T] View::eachi( /// ``` pub fn[T] View::all(self : View[T], f : (T) -> Bool raise?) -> Bool raise? { for v in self { - if not(f(v)) { + if !f(v) { return false } } @@ -216,12 +204,12 @@ pub fn[A] View::iter2(self : View[A]) -> Iter2[Int, A] { /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5][:].fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn[A, B] View::fold( self : View[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(acc, self[i]) @@ -236,12 +224,12 @@ pub fn[A, B] View::fold( /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5][:].rev_fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn[A, B] View::rev_fold( self : View[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = self.length() - 1, acc = init; i >= 0; { continue i - 1, f(acc, self[i]) @@ -256,12 +244,12 @@ pub fn[A, B] View::rev_fold( /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5][:].foldi(init=0, (index, sum, _elem) => sum + index) -/// assert_eq(sum, 10) +/// inspect(sum, content="10") /// ``` pub fn[A, B] View::foldi( self : View[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(i, acc, self[i]) @@ -276,12 +264,12 @@ pub fn[A, B] View::foldi( /// # Example /// ```mbt /// let sum = [1, 2, 3, 4, 5][:].rev_foldi(init=0, (index, sum, _elem)=> { sum + index }) -/// assert_eq(sum, 10) +/// inspect(sum, content="10") /// ``` pub fn[A, B] View::rev_foldi( self : View[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { let len = self.length() for i = len - 1, acc = init; i >= 0; { @@ -310,12 +298,7 @@ pub fn[T, U] View::map(self : View[T], f : (T) -> U raise?) -> Array[U] raise? { ///| /// Maps a function over the elements of the array view in place. /// -/// # Example -/// ```mbt -/// let v = [3, 4, 5] -/// v[1:].map_inplace((x) => {x + 1}) -/// assert_eq(v, [3, 5, 6]) -/// ``` +#deprecated("ArrayView will be immutable, use Array if you need mutation") pub fn[T] View::map_inplace(self : View[T], f : (T) -> T raise?) -> Unit raise? { for i, v in self { self[i] = f(v) @@ -333,7 +316,7 @@ pub fn[T] View::map_inplace(self : View[T], f : (T) -> T raise?) -> Unit raise? /// ``` pub fn[T, U] View::mapi( self : View[T], - f : (Int, T) -> U raise? + f : (Int, T) -> U raise?, ) -> Array[U] raise? { if self.length() == 0 { return [] @@ -342,17 +325,10 @@ pub fn[T, U] View::mapi( } ///| -/// Maps a function over the elements of the array view with index in place. -/// -/// # Example -/// ```mbt -/// let v = [3, 4, 5] -/// v[1:].mapi_inplace((i, x) => {x + i}) -/// assert_eq(v, [3, 4, 6]) -/// ``` +#deprecated("ArrayView will be immutable, use array if you need mutation") pub fn[T] View::mapi_inplace( self : View[T], - f : (Int, T) -> T raise? + f : (Int, T) -> T raise?, ) -> Unit raise? { for i, v in self { self[i] = f(i, v) @@ -370,7 +346,7 @@ pub fn[T] View::mapi_inplace( /// ``` pub fn[T] View::filter( self : View[T], - f : (T) -> Bool raise? + f : (T) -> Bool raise?, ) -> Array[T] raise? { let arr = [] for v in self { @@ -413,29 +389,38 @@ pub fn[T] View::to_array(self : View[T]) -> Array[T] { /// let array_view = a[:] /// inspect(array_view.join(","), content="1,2,3") /// ``` -pub fn View::join(self : ArrayView[String], separator : @string.View) -> String { +pub fn[A : @string.ToStringView] View::join( + self : ArrayView[A], + separator : @string.View, +) -> String { match self { [] => "" [hd, .. tl] => { + let hd = hd.to_string_view() let mut size_hint = hd.length() for s in tl { - size_hint += s.length() + separator.length() + size_hint += s.to_string_view().length() + separator.length() } size_hint = size_hint << 1 let buf = StringBuilder::new(size_hint~) - buf.write_string(hd) + // buf.write_string(hd) + buf.write_substring(hd.data(), hd.start_offset(), hd.length()) if separator is "" { for s in tl { - buf.write_string(s) + // buf.write_string(s) + let s = s.to_string_view() + buf.write_substring(s.data(), s.start_offset(), s.length()) } } else { for s in tl { + let s = s.to_string_view() buf.write_substring( separator.data(), separator.start_offset(), separator.length(), ) - buf.write_string(s) + // buf.write_string(s) + buf.write_substring(s.data(), s.start_offset(), s.length()) } } buf.to_string() @@ -449,12 +434,12 @@ pub impl[X : Show] Show for View[X] with output(self, logger) { } ///| -pub impl[T : Eq] Eq for View[T] with op_equal(self, other) -> Bool { +pub impl[T : Eq] Eq for View[T] with equal(self, other) -> Bool { if self.length() != other.length() { return false } for i in 0.. sum = sum + x) - assert_eq(sum, 12) + inspect(sum, content="12") } ///| @@ -147,48 +147,28 @@ test "arrayview_rev_foldi" { test "arrayview_map" { let arr = [1, 2, 3] let mapped = arr[1:].map(x => x * 2) - assert_eq(mapped.length(), 2) - assert_eq(mapped[0], 4) - assert_eq(mapped[1], 6) + inspect(mapped.length(), content="2") + inspect(mapped[0], content="4") + inspect(mapped[1], content="6") @json.inspect(([1] : Array[Int])[1:].map(x => x), content=[]) } -///| -test "arrayview_map_inplace" { - let arr = [1, 2, 3] - arr[1:].map_inplace(x => x * 2) - assert_eq(arr.length(), 3) - assert_eq(arr[0], 1) - assert_eq(arr[1], 4) - assert_eq(arr[2], 6) -} - ///| test "arrayview_mapi" { let arr = [1, 2, 3] let mapped = arr[1:].mapi((i, x) => i + x) - assert_eq(mapped.length(), 2) - assert_eq(mapped[0], 2) - assert_eq(mapped[1], 4) + inspect(mapped.length(), content="2") + inspect(mapped[0], content="2") + inspect(mapped[1], content="4") inspect(([1] : Array[Int])[1:].mapi((_idx, x) => x), content="[]") } -///| -test "arrayview_mapi_inplace" { - let arr = [1, 2, 3] - arr[1:].mapi_inplace((i, x) => i + x) - assert_eq(arr.length(), 3) - assert_eq(arr[0], 1) - assert_eq(arr[1], 2) - assert_eq(arr[2], 4) -} - ///| test "arrayview_filter" { let arr = [1, 2, 3, 4, 5] let filtered = arr[2:].filter(x => x % 2 == 0) - assert_eq(filtered.length(), 1) - assert_eq(filtered[0], 4) + inspect(filtered.length(), content="1") + inspect(filtered[0], content="4") } ///| @@ -204,7 +184,7 @@ test "arrayview_to_array" { ///| test "arrayview_arbitrary" { - let arr : Array[View[Int]] = @quickcheck.samples(20) + let arr : Array[@array.View[Int]] = @quickcheck.samples(20) inspect(arr[5:9], content="[[], [], [0], [0, 0]]") inspect( arr[10:15], @@ -214,7 +194,16 @@ test "arrayview_arbitrary" { ///| test "arrayview_hash" { - let arr : Array[View[Int]] = @quickcheck.samples(20) + let arr : Array[@array.View[Int]] = @quickcheck.samples(20) inspect(arr[5:9].hash(), content="-966877954") inspect(arr[10:15].hash(), content="-951019668") } + +///| +test "arrayview hash invariant" { + let arr : Array[Array[Int]] = @quickcheck.samples(20) + for v in arr { + let view = v[:] + assert_eq(v.hash(), view.hash()) + } +} diff --git a/bundled-core/bench/README.mbt.md b/bundled-core/bench/README.mbt.md new file mode 100644 index 0000000..eb9d2c3 --- /dev/null +++ b/bundled-core/bench/README.mbt.md @@ -0,0 +1,338 @@ +# Bench Package Documentation + +This package provides benchmarking utilities for measuring the performance of MoonBit code. It includes functions for timing code execution, collecting statistics, and generating performance reports. + +## Basic Benchmarking + +Use the `single_bench` function to benchmark individual operations: + +```moonbit +#skip("slow tests") +test "basic benchmarking" { + fn simple_calc(n : Int) -> Int { + n * 2 + 1 + } + // Benchmark a simple computation + let summary = @bench.single_bench(name="simple_calc", fn() { + ignore(simple_calc(5)) + }) + + // The benchmark ran successfully (we can't inspect exact timing) + inspect(summary.to_json().stringify().length() > 0, content="true") +} +``` + +## Benchmark Collection + +Use the `T` type to collect multiple benchmarks: + +```moonbit +#skip("slow tests") +test "benchmark collection" { + let bencher = @bench.new() + + // Add multiple benchmarks to the collection + bencher.bench(name="array_creation", fn() { + let arr = Array::new() + for i in 0..<5 { + arr.push(i) + } + }) + + bencher.bench(name="array_iteration", fn() { + let arr = [1, 2, 3, 4, 5] + let mut sum = 0 + for x in arr { + sum = sum + x + } + }) + + // Generate benchmark report + let report = bencher.dump_summaries() + inspect(report.length() > 0, content="true") +} +``` + +## Benchmarking Different Algorithms + +Compare the performance of different implementations: + +```moonbit +#skip("slow tests") +test "algorithm comparison" { + let bencher = @bench.new() + + // Benchmark linear search + bencher.bench(name="linear_search", fn() { + let arr = [1, 2, 3, 4, 5] + let target = 3 + let mut found = false + for x in arr { + if x == target { + found = true + break + } + } + ignore(found) + }) + + // Benchmark using built-in contains (likely optimized) + bencher.bench(name="builtin_contains", fn() { + let arr = [1, 2, 3, 4, 5] + ignore(arr.contains(3)) + }) + + let results = bencher.dump_summaries() + inspect(results.length() > 10, content="true") // Should have benchmark data +} +``` + +## Data Structure Benchmarks + +Benchmark different data structure operations: + +```moonbit +#skip("slow tests") +test "data structure benchmarks" { + let bencher = @bench.new() + + // Benchmark Array operations + bencher.bench(name="array_append", fn() { + let arr = Array::new() + for i in 0..<5 { + arr.push(i) + } + }) + + // Benchmark FixedArray access + bencher.bench(name="fixedarray_access", fn() { + let arr = [0, 1, 2, 3, 4] + let mut sum = 0 + for i in 0.. 50, content="true") // Should have benchmark data +} +``` + +## String Operations Benchmarking + +Measure string manipulation performance: + +```moonbit +#skip("slow tests") +test "string benchmarks" { + let bencher = @bench.new() + + // Benchmark string concatenation + bencher.bench(name="string_concat", fn() { + let mut result = "" + for i in 0..<5 { + result = result + "x" + } + }) + + // Benchmark StringBuilder (should be faster) + bencher.bench(name="stringbuilder", fn() { + let builder = StringBuilder::new() + for i in 0..<5 { + builder.write_string("x") + } + ignore(builder.to_string()) + }) + + let results = bencher.dump_summaries() + inspect(results.length() > 50, content="true") // Should have benchmark data +} +``` + +## Memory Usage Prevention + +Use `keep` to prevent compiler optimizations from eliminating benchmarked code: + +```moonbit +#skip("slow tests") +test "preventing optimization" { + let bencher = @bench.new() + + bencher.bench(name="with_keep", fn() { + let result = Array::makei(5, fn(i) { i * i }) + // Prevent the compiler from optimizing away the computation + bencher.keep(result) + }) + + let report = bencher.dump_summaries() + inspect(report.length() > 30, content="true") // Should have benchmark data +} +``` + +## Iteration Count Control + +Control the number of benchmark iterations: + +```moonbit +#skip("slow tests") +test "iteration control" { + let bencher = @bench.new() + + // Run with more iterations for more stable results + bencher.bench(name="stable_benchmark", fn() { + let arr = [1, 2, 3, 4, 5] + let sum = arr.fold(init=0, fn(acc, x) { acc + x }) + ignore(sum) + }, count=20) + + // Run with fewer iterations for quick testing + bencher.bench(name="quick_benchmark", fn() { + let mut result = 0 + for i in 0..<10 { + result = result + i + } + ignore(result) + }, count=2) + + let results = bencher.dump_summaries() + inspect(results.length() > 50, content="true") // Should have benchmark data +} +``` + +## Benchmarking Best Practices + +### 1. Isolate What You're Measuring + +```moonbit +#skip("slow tests") +test "isolation example" { + let bencher = @bench.new() + + // Good: Measure only the operation of interest + let data = Array::makei(10, fn(i) { i }) // Setup outside benchmark + + bencher.bench(name="array_sum", fn() { + let mut sum = 0 + for x in data { + sum = sum + x + } + bencher.keep(sum) // Prevent optimization + }) + + let results = bencher.dump_summaries() + inspect(results.length() > 0, content="true") +} +``` + +### 2. Warm Up Before Measuring + +```moonbit +#skip("slow tests") +test "warmup example" { + let bencher = @bench.new() + + fn expensive_operation() -> Int { + let mut result = 0 + for i in 0..<5 { + result = result + i * i + } + result + } + + // Warm up the function (not measured) + for _ in 0..<5 { + ignore(expensive_operation()) + } + + // Now benchmark the warmed-up function + bencher.bench(name="warmed_up", fn() { + let result = expensive_operation() + bencher.keep(result) + }) + + let report = bencher.dump_summaries() + inspect(report.length() > 30, content="true") // Should have benchmark data +} +``` + +### 3. Use Meaningful Names + +```moonbit +#skip("slow tests") +test "meaningful names" { + let bencher = @bench.new() + + // Good: Descriptive names that explain what's being measured + bencher.bench(name="array_insert_10_items", fn() { + let arr = Array::new() + for i in 0..<10 { + arr.push(i * 2) + } + bencher.keep(arr) + }) + + bencher.bench(name="array_search_sorted_10", fn() { + let arr = Array::makei(10, fn(i) { i }) + let result = arr.contains(5) // Linear search in this case + bencher.keep(result) + }) + + let results = bencher.dump_summaries() + inspect(results.length() > 50, content="true") // Should have benchmark data +} +``` + +## Performance Analysis + +The benchmark results include statistical information: + +- **Timing measurements**: Microsecond precision timing +- **Statistical analysis**: Median, percentiles, and outlier detection +- **Batch sizing**: Automatic adjustment for stable measurements +- **JSON output**: Machine-readable results for analysis + +## Integration with Testing + +Benchmarks can be integrated into your testing workflow: + +```moonbit +#skip("slow tests") +test "performance regression test" { + let bencher = @bench.new() + + // Benchmark a critical path + bencher.bench(name="critical_algorithm", fn() { + let data = [5, 2, 8, 1, 9, 3, 7, 4, 6] + let sorted = Array::new() + for x in data { + sorted.push(x) + } + sorted.sort() + bencher.keep(sorted) + }) + + let results = bencher.dump_summaries() + // In a real scenario, you might parse results and assert performance bounds + inspect(results.length() > 50, content="true") // Should have substantial data +} +``` + +## Common Benchmarking Patterns + +1. **Before/After comparisons**: Benchmark code before and after optimizations +2. **Algorithm comparison**: Compare different implementations of the same functionality +3. **Scaling analysis**: Benchmark with different input sizes +4. **Memory vs. speed tradeoffs**: Compare memory-efficient vs. speed-optimized approaches +5. **Platform differences**: Compare performance across different targets (JS, WASM, native) + +## Tips for Accurate Benchmarks + +- Run benchmarks multiple times and look for consistency +- Be aware of system load and other processes affecting timing +- Use appropriate iteration counts (more for stable results, fewer for quick feedback) +- Measure what matters to your use case +- Consider both average case and worst case performance +- Profile memory usage separately if memory performance is important + +The bench package provides essential tools for performance analysis and optimization in MoonBit applications. diff --git a/bundled-core/bench/bench.mbt b/bundled-core/bench/bench.mbt index 968a7df..b4b71ca 100644 --- a/bundled-core/bench/bench.mbt +++ b/bundled-core/bench/bench.mbt @@ -55,10 +55,10 @@ pub fn bench( self : T, name? : String, f : () -> Unit, - count~ : UInt = 10 + count? : UInt = 10, ) -> Unit { let summary = iter_count(name?, f, count) - if not(self.buffer.is_empty()) { + if !self.buffer.is_empty() { self.buffer.write_char(',') } self.buffer.write_string(summary.to_json().stringify(escape_slash=true)) @@ -70,7 +70,7 @@ pub fn bench( pub fn single_bench( name? : String, f : () -> Unit, - count~ : UInt = 10 + count? : UInt = 10, ) -> Summary { iter_count(name?, f, count) } @@ -94,6 +94,7 @@ pub fn dump_summaries(self : T) -> String { } ///| +#skip("slow benchmarking test") test "bench - single_bench basic functionality" { let bench_result = single_bench( () => { @@ -112,6 +113,7 @@ test "bench - single_bench basic functionality" { } ///| +#skip("slow benchmarking test") test "bench - Summary struct properties" { // Create a very simple bench and extract the summary let bench_result = single_bench( @@ -141,6 +143,7 @@ test "bench - Summary struct properties" { } ///| +#skip("slow benchmarking test") test "bench - T struct and bench method" { let bench_obj = new() @@ -177,6 +180,7 @@ test "bench - T struct and bench method" { } ///| +#skip("slow benchmarking test") test "bench - monotonic clock functionality" { // Test that the clock functions work properly let start = monotonic_clock_start() @@ -192,3 +196,13 @@ test "bench - monotonic clock functionality" { // Elapsed time should be non-negative inspect(elapsed >= 0.0, content="true") } + +///| +test "bench - keep stores value" { + let b = new() + assert_eq(b.summaries.length(), 0) + b.keep(123) + assert_eq(b.summaries.length(), 0) + b.keep(456) + assert_eq(b.summaries.length(), 0) +} diff --git a/bundled-core/bench/bench_test.mbt b/bundled-core/bench/bench_test.mbt new file mode 100644 index 0000000..4e36a8a --- /dev/null +++ b/bundled-core/bench/bench_test.mbt @@ -0,0 +1,13 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/bundled-core/bench/bench.mbti b/bundled-core/bench/pkg.generated.mbti similarity index 62% rename from bundled-core/bench/bench.mbti rename to bundled-core/bench/pkg.generated.mbti index 2be0437..8ae9d8e 100644 --- a/bundled-core/bench/bench.mbti +++ b/bundled-core/bench/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/bench" // Values @@ -7,14 +8,16 @@ fn monotonic_clock_start() -> Timestamp fn new() -> T -fn single_bench(name? : String, () -> Unit, count~ : UInt = ..) -> Summary +fn single_bench(name? : String, () -> Unit, count? : UInt) -> Summary + +// Errors // Types and methods type Summary impl ToJson for Summary type T -fn T::bench(Self, name? : String, () -> Unit, count~ : UInt = ..) -> Unit +fn T::bench(Self, name? : String, () -> Unit, count? : UInt) -> Unit fn T::dump_summaries(Self) -> String fn[Any] T::keep(Self, Any) -> Unit diff --git a/bundled-core/bench/stats.mbt b/bundled-core/bench/stats.mbt index 432afaa..414a408 100644 --- a/bundled-core/bench/stats.mbt +++ b/bundled-core/bench/stats.mbt @@ -38,7 +38,7 @@ struct Summary { fn Summary::new( name? : String, sorted_data~ : Array[Double], - batch_size : Int + batch_size : Int, ) -> Summary { let sum = sum(sorted_data) let min = min(sorted_data~) @@ -229,3 +229,19 @@ test { "runs": 8, }) } + +///| +test "std_dev_pct zero mean" { + assert_eq(std_dev_pct(mean=0.0, std_dev=5.0), 0.0) +} + +///| +test "median_abs_dev_pct zero median" { + assert_eq(median_abs_dev_pct(median=0.0, median_abs_dev=3.0), 0.0) +} + +///| +test "percentile edge cases" { + assert_eq(percentile(sorted_data=[42.0], pct=50.0), 42.0) + assert_eq(percentile(sorted_data=[1.0, 2.0, 3.0], pct=100.0), 3.0) +} diff --git a/bundled-core/bigint/README.mbt.md b/bundled-core/bigint/README.mbt.md new file mode 100644 index 0000000..1a64965 --- /dev/null +++ b/bundled-core/bigint/README.mbt.md @@ -0,0 +1,309 @@ +# BigInt Package Documentation + +This package provides arbitrary-precision integer arithmetic through the `BigInt` type. BigInt allows you to work with integers of unlimited size, making it perfect for cryptographic operations, mathematical computations, and any scenario where standard integer types are insufficient. + +## Creating BigInt Values + +There are several ways to create BigInt values: + +```moonbit +test "creating bigint values" { + // From integer literals with 'N' suffix + let big1 = 12345678901234567890N + inspect(big1, content="12345678901234567890") + + // From regular integers + let big2 = @bigint.BigInt::from_int(42) + inspect(big2, content="42") + + // From Int64 values + let big3 = @bigint.BigInt::from_int64(9223372036854775807L) + inspect(big3, content="9223372036854775807") + + // From strings + let big4 = @bigint.BigInt::from_string("123456789012345678901234567890") + inspect(big4, content="123456789012345678901234567890") + + // From hexadecimal strings + let big5 = @bigint.BigInt::from_hex("1a2b3c4d5e6f") + inspect(big5, content="28772997619311") +} +``` + +## Basic Arithmetic Operations + +BigInt supports all standard arithmetic operations: + +```moonbit +test "arithmetic operations" { + let a = 123456789012345678901234567890N + let b = 987654321098765432109876543210N + + // Addition + let sum = a + b + inspect(sum, content="1111111110111111111011111111100") + + // Subtraction + let diff = b - a + inspect(diff, content="864197532086419753208641975320") + + // Multiplication + let product = @bigint.BigInt::from_int(123) * @bigint.BigInt::from_int(456) + inspect(product, content="56088") + + // Division + let quotient = @bigint.BigInt::from_int(1000) / @bigint.BigInt::from_int(7) + inspect(quotient, content="142") + + // Modulo + let remainder = @bigint.BigInt::from_int(1000) % @bigint.BigInt::from_int(7) + inspect(remainder, content="6") + + // Negation + let neg = -a + inspect(neg, content="-123456789012345678901234567890") +} +``` + +## Comparison Operations + +Compare BigInt values with each other and with regular integers: + +```moonbit +test "comparisons" { + let big = 12345N + let small = 123N + + // BigInt to BigInt comparison + inspect(big > small, content="true") + inspect(big == small, content="false") + inspect(small < big, content="true") + + // BigInt to Int comparison + inspect(big.equal_int(12345), content="true") + inspect(big.compare_int(12345), content="0") + inspect(big.compare_int(1000), content="1") // greater than + inspect(small.compare_int(200), content="-1") // less than + + // BigInt to Int64 comparison + let big64 = @bigint.BigInt::from_int64(9223372036854775807L) + inspect(big64.equal_int64(9223372036854775807L), content="true") +} +``` + +## Bitwise Operations + +BigInt supports bitwise operations for bit manipulation: + +```moonbit +test "bitwise operations" { + let a = 0b11110000N // 240 in decimal + let b = 0b10101010N // 170 in decimal + + // Bitwise AND + let and_result = a & b + inspect(and_result, content="160") // 0b10100000 + + // Bitwise OR + let or_result = a | b + inspect(or_result, content="250") // 0b11111010 + + // Bitwise XOR + let xor_result = a ^ b + inspect(xor_result, content="90") // 0b01011010 + + // Bit length + let big_num = 255N + inspect(big_num.bit_length(), content="8") + + // Count trailing zeros + let with_zeros = 1000N // Has trailing zeros in binary + let ctz = with_zeros.ctz() + inspect(ctz >= 0, content="true") +} +``` + +## Power and Modular Arithmetic + +BigInt provides efficient power and modular exponentiation: + +```moonbit +test "power operations" { + // Basic power + let base = 2N + let exponent = 10N + let power = base.pow(exponent) + inspect(power, content="1024") + + // Modular exponentiation (useful for cryptography) + let base2 = 3N + let exp2 = 5N + let modulus = 7N + let mod_power = base2.pow(exp2, modulus=modulus) + inspect(mod_power, content="5") // (3^5) % 7 = 243 % 7 = 5 + + // Large modular exponentiation (optimized for speed) + let large_base = 123N + let large_exp = 20N + let large_mod = 1000007N + let result = large_base.pow(large_exp, modulus=large_mod) + inspect(result, content="378446") // (123^20) % 1000007 +} +``` + +## String and Hexadecimal Conversion + +Convert BigInt to and from various string representations: + +```moonbit +test "string conversions" { + let big = 255N + + // Decimal string + let decimal = big.to_string() + inspect(decimal, content="255") + + // Hexadecimal (lowercase) + let hex_lower = big.to_hex() + inspect(hex_lower, content="FF") + + // Hexadecimal (uppercase) + let hex_upper = big.to_hex(uppercase=true) + inspect(hex_upper, content="FF") + + // Parse from hex + let from_hex = @bigint.BigInt::from_hex("deadbeef") + inspect(from_hex, content="3735928559") + + // Round-trip conversion + let original = 98765432109876543210N + let as_string = original.to_string() + let parsed_back = @bigint.BigInt::from_string(as_string) + inspect(original == parsed_back, content="true") +} +``` + +## Byte Array Conversion + +Convert BigInt to and from byte arrays: + +```moonbit +test "byte conversions" { + let big = 0x123456789abcdefN + + // Convert to bytes + let bytes = big.to_octets() + inspect(bytes.length() > 0, content="true") + + // Convert from bytes (positive number) + let from_bytes = @bigint.BigInt::from_octets(bytes) + inspect(from_bytes == big, content="true") + + // Convert with specific length + let fixed_length = @bigint.BigInt::from_int(255).to_octets(length=4) + inspect(fixed_length.length(), content="4") + + // Negative numbers + // let negative = -big + // let neg_bytes = negative.to_octets() + // to_octets does not accept negative numbers + // let neg_from_bytes = @bigint.BigInt::from_octets(neg_bytes, signum=-1) + // inspect(neg_from_bytes == negative, content="true") +} +``` + +## Type Conversions + +Convert BigInt to standard integer types: + +```moonbit +test "type conversions" { + let big = 12345N + + // To Int (truncates if too large) + let as_int = big.to_int() + inspect(as_int, content="12345") + + // To Int64 + let as_int64 = big.to_int64() + inspect(as_int64, content="12345") + + // To UInt + let as_uint = big.to_uint() + inspect(as_uint, content="12345") + + // To smaller types + let small = 255N + let as_int16 = small.to_int16() + inspect(as_int16, content="255") + + let as_uint16 = small.to_uint16() + inspect(as_uint16, content="255") +} +``` + +## JSON Serialization + +BigInt values can be serialized to and from JSON: + +```moonbit +test "json serialization" { + let big = 12345678901234567890N + + // Convert to JSON (as string to preserve precision) + let json = big.to_json() + inspect(json, content="String(\"12345678901234567890\")") + + // Large numbers that exceed JavaScript's safe integer range + let very_big = @bigint.BigInt::from_string("123456789012345678901234567890") + let big_json = very_big.to_json() + inspect(big_json, content="String(\"123456789012345678901234567890\")") +} +``` + +## Utility Functions + +Check properties of BigInt values: + +```moonbit +test "utility functions" { + let zero = 0N + let positive = 42N + let negative = -42N + + // Check if zero + inspect(zero.is_zero(), content="true") + inspect(positive.is_zero(), content="false") + + // Sign testing through comparison + inspect(positive > zero, content="true") + inspect(negative < zero, content="true") + inspect(zero == zero, content="true") +} +``` + +## Use Cases and Applications + +BigInt is particularly useful for: + +1. **Cryptography**: RSA encryption, digital signatures, and key generation +2. **Mathematical computations**: Factorial calculations, Fibonacci sequences, prime number testing +3. **Financial calculations**: High-precision monetary computations +4. **Scientific computing**: Large integer calculations in physics and chemistry +5. **Data processing**: Handling large numeric IDs and checksums + +## Performance Considerations + +- BigInt operations are slower than regular integer operations due to arbitrary precision +- Addition and subtraction are generally fast +- Multiplication and division become slower with larger numbers +- Modular exponentiation is optimized for cryptographic use cases +- String conversions can be expensive for very large numbers + +## Best Practices + +1. **Use regular integers when possible**: Only use BigInt when you need arbitrary precision +2. **Cache string representations**: If you need to display the same BigInt multiple times +3. **Use modular arithmetic**: For cryptographic applications, always use modular exponentiation +4. **Be careful with conversions**: Converting very large BigInt to regular integers will truncate +5. **Consider memory usage**: Very large BigInt values consume more memory diff --git a/bundled-core/bigint/bigint.mbt b/bundled-core/bigint/bigint.mbt index 21f2319..692fe3d 100644 --- a/bundled-core/bigint/bigint.mbt +++ b/bundled-core/bigint/bigint.mbt @@ -31,9 +31,12 @@ /// ```moonbit /// let big = 12345678901234567890N /// let json = big.to_json() -/// inspect(json, content= -/// #|String("12345678901234567890") -/// ) +/// inspect( +/// json, +/// content=( +/// #|String("12345678901234567890") +/// ), +/// ) /// ``` /// pub impl ToJson for BigInt with to_json(self : BigInt) -> Json { @@ -60,7 +63,7 @@ pub impl @quickcheck.Arbitrary for BigInt with arbitrary(size, rs) { } } -///| +///| /// Returns the default value `0` for `BigInt` pub impl Default for BigInt with default() { zero @@ -114,11 +117,11 @@ pub fn BigInt::equal_int(self : BigInt, other : Int) -> Bool { /// Example: /// /// ```moonbit -/// let big = BigInt::from_int64(9223372036854775807L) // Int64 max value +/// let big = @bigint.BigInt::from_int64(9223372036854775807L) // Int64 max value /// inspect(big.equal_int64(9223372036854775807L), content="true") /// inspect(big.equal_int64(42L), content="false") /// -/// let overflow = BigInt::from_string("9223372036854775808") // Beyond Int64 range +/// let overflow = @bigint.BigInt::from_string("9223372036854775808") // Beyond Int64 range /// inspect(overflow.equal_int64(9223372036854775807L), content="false") /// ``` /// diff --git a/bundled-core/bigint/bigint_js.mbt b/bundled-core/bigint/bigint_js.mbt index be9aafa..bd429ec 100644 --- a/bundled-core/bigint/bigint_js.mbt +++ b/bundled-core/bigint/bigint_js.mbt @@ -46,7 +46,7 @@ pub extern "js" fn BigInt::from_hex(str : String) -> BigInt = ///| pub extern "js" fn BigInt::to_hex( self : BigInt, - uppercase~ : Bool = true + uppercase? : Bool = true, ) -> String = #|(x, uppercase) => { #| const r = x.toString(16); @@ -58,7 +58,7 @@ extern "js" fn hex2(b : Byte) -> String = #|(x) => x.toString(16).padStart(2, '0') ///| -pub fn BigInt::from_octets(octets : Bytes, signum~ : Int = 1) -> BigInt { +pub fn BigInt::from_octets(octets : Bytes, signum? : Int = 1) -> BigInt { if signum < 0 { return -1N * BigInt::from_octets(octets, signum=1) } @@ -129,7 +129,7 @@ extern "js" fn BigInt::equal(self : BigInt, other : BigInt) -> Bool = #|(x, y) => x === y ///| -pub impl Eq for BigInt with op_equal(self, other) { +pub impl Eq for BigInt with equal(self, other) { self.equal(other) } @@ -158,7 +158,7 @@ extern "js" fn BigInt::op_neg_ffi(self : BigInt) -> BigInt = #|(x) => -x ///| -pub impl Neg for BigInt with op_neg(self) { +pub impl Neg for BigInt with neg(self) { self.op_neg_ffi() } @@ -167,7 +167,7 @@ extern "js" fn BigInt::op_add_ffi(self : BigInt, other : BigInt) -> BigInt = #|(x, y) => x + y ///| -pub impl Add for BigInt with op_add(self, other) { +pub impl Add for BigInt with add(self, other) { self.op_add_ffi(other) } @@ -176,7 +176,7 @@ extern "js" fn BigInt::op_sub_ffi(self : BigInt, other : BigInt) -> BigInt = #|(x, y) => x - y ///| -pub impl Sub for BigInt with op_sub(self, other) { +pub impl Sub for BigInt with sub(self, other) { self.op_sub_ffi(other) } @@ -185,7 +185,7 @@ extern "js" fn BigInt::op_mul_ffi(self : BigInt, other : BigInt) -> BigInt = #|(x, y) => x * y ///| -pub impl Mul for BigInt with op_mul(self, other) { +pub impl Mul for BigInt with mul(self, other) { self.op_mul_ffi(other) } @@ -194,7 +194,7 @@ extern "js" fn BigInt::op_div_ffi(self : BigInt, other : BigInt) -> BigInt = #|(x, y) => x / y ///| -pub impl Div for BigInt with op_div(self, other) { +pub impl Div for BigInt with div(self, other) { self.op_div_ffi(other) } @@ -203,7 +203,7 @@ extern "js" fn BigInt::op_mod_ffi(self : BigInt, other : BigInt) -> BigInt = #|(x, y) => x % y ///| -pub impl Mod for BigInt with op_mod(self, other) { +pub impl Mod for BigInt with mod(self, other) { self.op_mod_ffi(other) } @@ -211,7 +211,7 @@ pub impl Mod for BigInt with op_mod(self, other) { extern "js" fn BigInt::modpow_ffi( self : BigInt, exponent : BigInt, - modulus : BigInt + modulus : BigInt, ) -> BigInt = #|(x, y, z) => { #| if (z === 1n) return 0n; @@ -235,7 +235,7 @@ extern "js" fn BigInt::pow_ffi(self : BigInt, exponent : BigInt) -> BigInt = pub fn BigInt::pow( self : BigInt, exponent : BigInt, - modulus? : BigInt + modulus? : BigInt, ) -> BigInt { if exponent < 0 { abort("negative exponent") @@ -256,7 +256,7 @@ extern "js" fn BigInt::to_byte(self : BigInt) -> Byte = #|(x) => Number(BigInt.asUintN(8, x)) | 0 ///| -pub impl Shl for BigInt with op_shl(self : BigInt, n : Int) -> BigInt { +pub impl Shl for BigInt with shl(self : BigInt, n : Int) -> BigInt { if n < 0 { abort("negative shift count") } @@ -264,7 +264,7 @@ pub impl Shl for BigInt with op_shl(self : BigInt, n : Int) -> BigInt { } ///| -pub impl Shr for BigInt with op_shr(self : BigInt, n : Int) -> BigInt { +pub impl Shr for BigInt with shr(self : BigInt, n : Int) -> BigInt { if n < 0 { abort("negative shift count") } @@ -306,11 +306,11 @@ extern "js" fn BigInt::js_lor(self : BigInt, other : BigInt) -> BigInt = /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") -/// let b = BigInt::from_string("-12") +/// let a = @bigint.BigInt::from_string("42") +/// let b = @bigint.BigInt::from_string("-12") /// inspect(a | b, content="-2") -/// let c = BigInt::from_string("-8") -/// let d = BigInt::from_string("-4") +/// let c = @bigint.BigInt::from_string("-8") +/// let d = @bigint.BigInt::from_string("-4") /// inspect(c | d, content="-4") /// ``` /// @@ -342,15 +342,15 @@ extern "js" fn BigInt::js_lxor(self : BigInt, other : BigInt) -> BigInt = /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") // 0b101010 -/// let b = BigInt::from_string("25") // 0b011001 +/// let a = @bigint.BigInt::from_string("42") // 0b101010 +/// let b = @bigint.BigInt::from_string("25") // 0b011001 /// inspect(a.lxor(b), content="51") // 0b110011 /// -/// let a = BigInt::from_string("42") -/// let b = BigInt::from_string("-7") +/// let a = @bigint.BigInt::from_string("42") +/// let b = @bigint.BigInt::from_string("-7") /// inspect(a.lxor(b), content="-45") /// -/// let a = BigInt::from_string("42") +/// let a = @bigint.BigInt::from_string("42") /// inspect(a.lxor(a), content="0") // XOR with self gives 0 /// ``` /// @@ -474,7 +474,8 @@ pub extern "js" fn BigInt::bit_length(self : BigInt) -> Int = #| } #|} -///| Returns the number of trailing zero bits in the binary representation of +///| +/// Returns the number of trailing zero bits in the binary representation of /// the absolute value of this BigInt. /// /// For zero, it returns -1. @@ -511,7 +512,7 @@ extern "js" fn is_neg(x : BigInt) -> Bool = ///| fn BigInt::limbs(self : Self) -> Iter[UInt] { - guard not(self.is_zero()) else { Iter::singleton(0) } + guard !self.is_zero() else { Iter::singleton(0) } Iter::new(yield_ => for n = self * BigInt::from_int(self.signum()) n > 0 n = n >> 32 { diff --git a/bundled-core/bigint/bigint_nonjs.mbt b/bundled-core/bigint/bigint_nonjs.mbt index f767795..8941e0b 100644 --- a/bundled-core/bigint/bigint_nonjs.mbt +++ b/bundled-core/bigint/bigint_nonjs.mbt @@ -90,9 +90,9 @@ let one : BigInt = 1N /// Example: /// /// ```moonbit -/// let big = BigInt::from_int(42) +/// let big = @bigint.BigInt::from_int(42) /// inspect(big, content="42") -/// let neg = BigInt::from_int(-42) +/// let neg = @bigint.BigInt::from_int(-42) /// inspect(neg, content="-42") /// ``` pub fn BigInt::from_int(n : Int) -> BigInt { @@ -112,7 +112,7 @@ pub fn BigInt::from_int(n : Int) -> BigInt { /// /// ```moonbit /// let n = 42U -/// inspect(BigInt::from_uint(n), content="42") +/// inspect(@bigint.BigInt::from_uint(n), content="42") /// ``` pub fn BigInt::from_uint(n : UInt) -> BigInt { BigInt::from_uint64(n.to_uint64()) @@ -131,9 +131,9 @@ pub fn BigInt::from_uint(n : UInt) -> BigInt { /// Example: /// /// ```moonbit -/// let big = BigInt::from_int64(9223372036854775807L) // max value of Int64 +/// let big = @bigint.BigInt::from_int64(9223372036854775807L) // max value of Int64 /// inspect(big, content="9223372036854775807") -/// let neg = BigInt::from_int64(-9223372036854775808L) // min value of Int64 +/// let neg = @bigint.BigInt::from_int64(-9223372036854775808L) // min value of Int64 /// inspect(neg, content="-9223372036854775808") /// ``` pub fn BigInt::from_int64(n : Int64) -> BigInt { @@ -158,9 +158,9 @@ pub fn BigInt::from_int64(n : Int64) -> BigInt { /// Example: /// /// ```moonbit -/// let n = BigInt::from_uint64(12345678901234567890UL) +/// let n = @bigint.BigInt::from_uint64(12345678901234567890UL) /// inspect(n, content="12345678901234567890") -/// let zero = BigInt::from_uint64(0UL) +/// let zero = @bigint.BigInt::from_uint64(0UL) /// inspect(zero, content="0") /// ``` pub fn BigInt::from_uint64(n : UInt64) -> BigInt { @@ -198,7 +198,7 @@ pub fn BigInt::from_uint64(n : UInt64) -> BigInt { /// inspect(-(-42N), content="42") /// inspect(-0N, content="0") /// ``` -pub impl Neg for BigInt with op_neg(self : BigInt) -> BigInt { +pub impl Neg for BigInt with neg(self : BigInt) -> BigInt { if self.is_zero() { return zero } @@ -225,7 +225,7 @@ pub impl Neg for BigInt with op_neg(self : BigInt) -> BigInt { /// inspect(a + b, content="9223372036854775808") // Beyond Int64 range /// inspect(-a + -b, content="-9223372036854775808") /// ``` -pub impl Add for BigInt with op_add(self : BigInt, other : BigInt) -> BigInt { +pub impl Add for BigInt with add(self : BigInt, other : BigInt) -> BigInt { if self.sign == Negative { if other.sign == Negative { return -(-other + -self) @@ -271,7 +271,7 @@ pub impl Add for BigInt with op_add(self : BigInt, other : BigInt) -> BigInt { /// inspect(a - b, content="2469135690246913569") /// inspect(-a - b, content="-22222222112222222211") /// ``` -pub impl Sub for BigInt with op_sub(self : BigInt, other : BigInt) -> BigInt { +pub impl Sub for BigInt with sub(self : BigInt, other : BigInt) -> BigInt { // first make sure self and other > 0 if self.sign == Negative { if other.sign == Negative { @@ -292,16 +292,8 @@ pub impl Sub for BigInt with op_sub(self : BigInt, other : BigInt) -> BigInt { let mut borrow = 0L let mut i = 0 while i < self_len || i < other_len || borrow != 0L { - let a = if i < self_len { - self.limbs[i].to_uint64().reinterpret_as_int64() - } else { - 0 - } - let b = if i < other_len { - other.limbs[i].to_uint64().reinterpret_as_int64() - } else { - 0 - } + let a = if i < self_len { self.limbs[i].to_int64() } else { 0 } + let b = if i < other_len { other.limbs[i].to_int64() } else { 0 } let diff = a - b - borrow // 0 <= a < radix, 0 <= b < radix, 0 <= borrow <= 1 => -radix <= diff < radix if diff < 0L { limbs[i] = (diff + radix.reinterpret_as_int64()) @@ -345,7 +337,7 @@ pub impl Sub for BigInt with op_sub(self : BigInt, other : BigInt) -> BigInt { /// inspect(a * b, content="-1219326311370217952237463801111263526900") /// inspect(a * 0N, content="0") /// ``` -pub impl Mul for BigInt with op_mul(self : BigInt, other : BigInt) -> BigInt { +pub impl Mul for BigInt with mul(self : BigInt, other : BigInt) -> BigInt { if self.is_zero() || other.is_zero() { return zero } @@ -357,8 +349,9 @@ pub impl Mul for BigInt with op_mul(self : BigInt, other : BigInt) -> BigInt { { ..ret, sign: if self.sign == other.sign { Positive } else { Negative } } } -///| // Simplest way to multiply two BigInts. + +///| fn BigInt::grade_school_mul(self : BigInt, other : BigInt) -> BigInt { let self_len = self.len let other_len = other.len @@ -381,8 +374,9 @@ fn BigInt::grade_school_mul(self : BigInt, other : BigInt) -> BigInt { { limbs, sign: Positive, len } } -///| // Karatsuba multiplication + +///| fn BigInt::karatsuba_mul(self : BigInt, other : BigInt) -> BigInt { let half = (max(self.len, other.len) + 1) / 2 let (xl, xh) = self.split(half) @@ -395,8 +389,9 @@ fn BigInt::karatsuba_mul(self : BigInt, other : BigInt) -> BigInt { p2 } -///| // Get the lower half of the number. + +///| fn BigInt::split(self : BigInt, half : Int) -> (BigInt, BigInt) { if self.len <= half { return ({ ..self, sign: Positive }, zero) @@ -434,15 +429,17 @@ fn BigInt::split(self : BigInt, half : Int) -> (BigInt, BigInt) { /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("100") -/// let b = BigInt::from_string("20") +/// let a = @bigint.BigInt::from_string("100") +/// let b = @bigint.BigInt::from_string("20") /// inspect(a / b, content="5") /// inspect(-a / b, content="-5") /// inspect(a / -b, content="-5") /// inspect(-a / -b, content="5") /// ``` -pub impl Div for BigInt with op_div(self : BigInt, other : BigInt) -> BigInt { - guard other != zero else { abort("division by zero") } +pub impl Div for BigInt with div(self : BigInt, other : BigInt) -> BigInt { + if other is 0 { + abort("division by zero") + } // Handle negative numbers if self.sign == Negative { @@ -480,7 +477,7 @@ pub impl Div for BigInt with op_div(self : BigInt, other : BigInt) -> BigInt { /// let d = -5N /// inspect(c % d, content="-2") /// ``` -pub impl Mod for BigInt with op_mod(self : BigInt, other : BigInt) -> BigInt { +pub impl Mod for BigInt with mod(self : BigInt, other : BigInt) -> BigInt { if other == zero { abort("division by zero") } @@ -498,9 +495,10 @@ pub impl Mod for BigInt with op_mod(self : BigInt, other : BigInt) -> BigInt { } } -///| // Simplest way to divide two BigInts. // Assumption: other != zero. + +///| fn BigInt::grade_school_div(self : BigInt, other : BigInt) -> (BigInt, BigInt) { // Handle edge cases if self < other { @@ -547,11 +545,7 @@ fn BigInt::grade_school_div(self : BigInt, other : BigInt) -> (BigInt, BigInt) { // where a and b represent the limbs of the adjusted dividend and divisor let lshift = max( 0, - radix_bit_len - - ( - 64 - - divisor.limbs[divisor.len - 1].to_uint64().reinterpret_as_int64().clz() - ), + radix_bit_len - (64 - divisor.limbs[divisor.len - 1].to_int64().clz()), ) let a_len = dividend.len let dividend = dividend << lshift @@ -663,11 +657,11 @@ fn BigInt::grade_school_div(self : BigInt, other : BigInt) -> (BigInt, BigInt) { /// let y = -5N /// inspect(y << 2, content="-20") /// ``` -pub impl Shl for BigInt with op_shl(self : BigInt, n : Int) -> BigInt { +pub impl Shl for BigInt with shl(self : BigInt, n : Int) -> BigInt { if n < 0 { abort("negative shift count") } - if not(self.is_zero()) { + if !self.is_zero() { let new_limbs = FixedArray::make( self.len + (n + radix_bit_len - 1) / radix_bit_len, // ceiling(n / radix_bit_len) 0U, @@ -715,12 +709,12 @@ pub impl Shl for BigInt with op_shl(self : BigInt, n : Int) -> BigInt { /// Example: /// /// ```moonbit -/// let n = BigInt::from_string("1024") +/// let n = @bigint.BigInt::from_string("1024") /// inspect(n >> 3, content="128") -/// let neg = BigInt::from_string("-1024") +/// let neg = @bigint.BigInt::from_string("-1024") /// inspect(neg >> 3, content="-128") /// ``` -pub impl Shr for BigInt with op_shr(self : BigInt, n : Int) -> BigInt { +pub impl Shr for BigInt with shr(self : BigInt, n : Int) -> BigInt { if n < 0 { abort("negative shift count") } @@ -797,9 +791,9 @@ pub fn BigInt::is_zero(self : BigInt) -> Bool { /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") -/// let b = BigInt::from_string("24") -/// let c = BigInt::from_string("-42") +/// let a = @bigint.BigInt::from_string("42") +/// let b = @bigint.BigInt::from_string("24") +/// let c = @bigint.BigInt::from_string("-42") /// inspect(a.compare(b), content="1") // 42 > 24 /// inspect(b.compare(a), content="-1") // 24 < 42 /// inspect(c.compare(a), content="-1") // -42 < 42 @@ -850,7 +844,7 @@ pub impl Compare for BigInt with compare(self, other) { /// inspect(a == b, content="true") /// inspect(a == c, content="false") /// ``` -pub impl Eq for BigInt with op_equal(self, other) { +pub impl Eq for BigInt with equal(self, other) { if self.sign != other.sign || self.len != other.len { return false } @@ -902,7 +896,7 @@ pub fn BigInt::to_string(self : BigInt) -> String { let v = Array::make(decimal_len, 0L) let mut v_idx = 0 for i = self.len - 1; i >= 0; i = i - 1 { - let mut x = self.limbs[i].to_uint64().reinterpret_as_int64() + let mut x = self.limbs[i].to_int64() for j in 0.. BigInt { /// Example: /// /// ```moonbit -/// inspect(BigInt::from_hex("ff"), content="255") -/// inspect(BigInt::from_hex("-ff"), content="-255") -/// inspect(BigInt::from_hex("DEADBEEF"), content="3735928559") +/// inspect(@bigint.BigInt::from_hex("ff"), content="255") +/// inspect(@bigint.BigInt::from_hex("-ff"), content="-255") +/// inspect(@bigint.BigInt::from_hex("DEADBEEF"), content="3735928559") /// ``` pub fn BigInt::from_hex(input : String) -> BigInt { // WARN: this implementation assumes that `radix_bit_len` is a multiple of 4. @@ -1108,14 +1102,14 @@ pub fn BigInt::from_hex(input : String) -> BigInt { /// Example: /// /// ```moonbit -/// let pos = BigInt::from_string("255") -/// let neg = BigInt::from_string("-255") +/// let pos = @bigint.BigInt::from_string("255") +/// let neg = @bigint.BigInt::from_string("-255") /// inspect(pos.to_hex(), content="FF") /// inspect(neg.to_hex(), content="-FF") /// inspect(pos.to_hex(uppercase=false), content="ff") /// inspect(0N.to_hex(), content="0") /// ``` -pub fn BigInt::to_hex(self : BigInt, uppercase~ : Bool = true) -> String { +pub fn BigInt::to_hex(self : BigInt, uppercase? : Bool = true) -> String { if self.is_zero() { return "0" } @@ -1198,10 +1192,10 @@ fn[T : Compare] max(a : T, b : T) -> T { /// Example: /// /// ```moonbit -/// let base = BigInt::from_string("3") -/// let exp = BigInt::from_string("4") +/// let base = @bigint.BigInt::from_string("3") +/// let exp = @bigint.BigInt::from_string("4") /// inspect(base.pow(exp), content="81") -/// inspect(base.pow(exp, modulus=BigInt::from_string("10")), content="1") +/// inspect(base.pow(exp, modulus=@bigint.BigInt::from_string("10")), content="1") /// ``` pub fn BigInt::pow(self : BigInt, exp : BigInt, modulus? : BigInt) -> BigInt { if exp.sign == Negative { @@ -1222,7 +1216,7 @@ pub fn BigInt::pow(self : BigInt, exp : BigInt, modulus? : BigInt) -> BigInt { result } Some(modulus) => { - guard not(modulus.is_zero() || modulus.sign == Negative) + guard !(modulus.is_zero() || modulus.sign == Negative) let mut result = 1N let mut base = self let mut exp = exp @@ -1261,12 +1255,12 @@ pub fn BigInt::pow(self : BigInt, exp : BigInt, modulus? : BigInt) -> BigInt { /// /// ```moonbit /// let bytes = b"\x01\x02\x03" // Represents 0x010203 -/// let positive = BigInt::from_octets(bytes) -/// let negative = BigInt::from_octets(bytes, signum=-1) +/// let positive = @bigint.BigInt::from_octets(bytes) +/// let negative = @bigint.BigInt::from_octets(bytes, signum=-1) /// inspect(positive, content="66051") /// inspect(negative, content="-66051") /// ``` -pub fn BigInt::from_octets(input : Bytes, signum~ : Int = 1) -> BigInt { +pub fn BigInt::from_octets(input : Bytes, signum? : Int = 1) -> BigInt { let len = input.length() // number of bytes if signum == 0 { return zero @@ -1282,16 +1276,14 @@ pub fn BigInt::from_octets(input : Bytes, signum~ : Int = 1) -> BigInt { let limbs = FixedArray::make(limbs_len, 0U) // head at most significant limb for i in 0..<(mod / 8) { - limbs[limbs_len - 1] = (limbs[limbs_len - 1] << 8) | - input[i].to_int().reinterpret_as_uint() + limbs[limbs_len - 1] = (limbs[limbs_len - 1] << 8) | input[i].to_uint() } let byte_per_limb = radix_bit_len / 8 // tail for i in 0..
BigInt { /// Example: /// /// ```moonbit -/// let n = BigInt::from_hex("abcdef") +/// let n = @bigint.BigInt::from_hex("abcdef") /// inspect(n.to_octets(length=4), content="b\"\\x00\\xab\\xcd\\xef\"") -/// let m = BigInt::from_string("0") +/// let m = @bigint.BigInt::from_string("0") /// inspect(m.to_octets(), content="b\"\\x00\"") /// ``` pub fn BigInt::to_octets(self : BigInt, length? : Int) -> Bytes { @@ -1371,12 +1363,12 @@ pub fn BigInt::to_octets(self : BigInt, length? : Int) -> Bytes { /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") // 0b101010 -/// let b = BigInt::from_string("-12") // ~0b1011 + 1 +/// let a = @bigint.BigInt::from_string("42") // 0b101010 +/// let b = @bigint.BigInt::from_string("-12") // ~0b1011 + 1 /// inspect(a & b, content="32") // 0b100000 /// -/// let a = BigInt::from_string("-8") // ~0b111 + 1 -/// let b = BigInt::from_string("-4") // ~0b11 + 1 +/// let a = @bigint.BigInt::from_string("-8") // ~0b111 + 1 +/// let b = @bigint.BigInt::from_string("-4") // ~0b11 + 1 /// inspect(a & b, content="-8") // ~0b1011 + 1 /// ``` pub impl BitAnd for BigInt with land(self : BigInt, other : BigInt) -> BigInt { @@ -1468,11 +1460,11 @@ pub impl BitAnd for BigInt with land(self : BigInt, other : BigInt) -> BigInt { /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") -/// let b = BigInt::from_string("-12") +/// let a = @bigint.BigInt::from_string("42") +/// let b = @bigint.BigInt::from_string("-12") /// inspect(a | b, content="-2") -/// let c = BigInt::from_string("-8") -/// let d = BigInt::from_string("-4") +/// let c = @bigint.BigInt::from_string("-8") +/// let d = @bigint.BigInt::from_string("-4") /// inspect(c | d, content="-4") /// ``` pub impl BitOr for BigInt with lor(self : BigInt, other : BigInt) -> BigInt { @@ -1565,11 +1557,11 @@ pub impl BitOr for BigInt with lor(self : BigInt, other : BigInt) -> BigInt { /// Example: /// /// ```moonbit -/// let a = BigInt::from_string("42") -/// let b = BigInt::from_string("-7") +/// let a = @bigint.BigInt::from_string("42") +/// let b = @bigint.BigInt::from_string("-7") /// inspect(a ^ b, content="-45") /// -/// let a = BigInt::from_string("42") +/// let a = @bigint.BigInt::from_string("42") /// inspect(a ^ a, content="0") // XOR with self gives 0 /// ``` pub impl BitXOr for BigInt with lxor(self : BigInt, other : BigInt) -> BigInt { @@ -1778,7 +1770,8 @@ pub fn BigInt::bit_length(self : BigInt) -> Int { bit_length } -///| Returns the number of trailing zero bits in the binary representation of +///| +/// Returns the number of trailing zero bits in the binary representation of /// the absolute value of this BigInt. /// /// Example: diff --git a/bundled-core/bigint/bigint_nonjs_wbtest.mbt b/bundled-core/bigint/bigint_nonjs_wbtest.mbt index c302199..e30ff50 100644 --- a/bundled-core/bigint/bigint_nonjs_wbtest.mbt +++ b/bundled-core/bigint/bigint_nonjs_wbtest.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -type MyBigInt BigInt +struct MyBigInt(BigInt) ///| impl Show for MyBigInt with output(self, logger) { @@ -42,7 +42,7 @@ test "debug_string" { // Logger::trait_method() inspect( buf, - content= + content=( #|{limbs : [0, 0], sign : Positive, len : 1 } #|{limbs : [1, 0], sign : Positive, len : 1 } #|{limbs : [2, 0], sign : Positive, len : 1 } @@ -52,7 +52,7 @@ test "debug_string" { #|{limbs : [1, 0], sign : Negative, len : 1 } #|{limbs : [2, 0], sign : Negative, len : 1 } #|{limbs : [3, 0], sign : Negative, len : 1 } - , + ), ) } diff --git a/bundled-core/bigint/bigint_test.mbt b/bundled-core/bigint/bigint_test.mbt index 7bfe5d9..5698424 100644 --- a/bundled-core/bigint/bigint_test.mbt +++ b/bundled-core/bigint/bigint_test.mbt @@ -14,16 +14,16 @@ ///| test "neg" { - let a = BigInt::from_int64(123456789012345678) + let a = @bigint.BigInt::from_int64(123456789012345678) inspect(-a, content="-123456789012345678") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( -a, content="-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( @@ -34,13 +34,13 @@ test "neg" { ///| test "add" { - let a = BigInt::from_int64(123456789012345678) - let b = BigInt::from_int64(876543210987654321) + let a = @bigint.BigInt::from_int64(123456789012345678) + let b = @bigint.BigInt::from_int64(876543210987654321) inspect(a + b, content="999999999999999999") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "876543210987654321876543210987654321876543210987654321876543210987654321", ) inspect( @@ -55,13 +55,13 @@ test "add" { ///| test "sub" { - let a = BigInt::from_int64(123456789012345678) - let b = BigInt::from_int64(876543210987654321) + let a = @bigint.BigInt::from_int64(123456789012345678) + let b = @bigint.BigInt::from_int64(876543210987654321) inspect(a - b, content="-753086421975308643") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "876543210987654321876543210987654321876543210987654321876543210987654321", ) inspect( @@ -72,13 +72,13 @@ test "sub" { ///| test "mul" { - let a = BigInt::from_int64(123456789012345678) - let b = BigInt::from_int64(876543210987654321) + let a = @bigint.BigInt::from_int64(123456789012345678) + let b = @bigint.BigInt::from_int64(876543210987654321) inspect(a * b, content="108215210259106841348574911222374638") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "876543210987654321876543210987654321876543210987654321876543210987654321", ) inspect( @@ -89,14 +89,14 @@ test "mul" { ///| test "div" { - let a = BigInt::from_int64(123456789012345678) - let b = BigInt::from_int64(876543210987654321) + let a = @bigint.BigInt::from_int64(123456789012345678) + let b = @bigint.BigInt::from_int64(876543210987654321) inspect(a / b, content="0") inspect(a % b, content="123456789012345678") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "876543210987654321876543210987654321876543210987654321876543210987654321", ) inspect(a / b, content="0") @@ -108,10 +108,10 @@ test "div" { ///| test "neg" { - let a = BigInt::from_int64(123456789012345678L) + let a = @bigint.BigInt::from_int64(123456789012345678L) let b = -a inspect(b.to_string(), content="-123456789012345678") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) let b = -a @@ -119,7 +119,7 @@ test "neg" { b.to_string(), content="-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) let b = -a @@ -131,27 +131,29 @@ test "neg" { ///| test "add" { - let a = BigInt::from_int64(123456789012345678L) - let b = BigInt::from_int64(987654321098765432L) + let a = @bigint.BigInt::from_int64(123456789012345678L) + let b = @bigint.BigInt::from_int64(987654321098765432L) let c = a + b inspect(c.to_string(), content="1111111110111111110") - let a = BigInt::from_string("123456789012345678123456789012345678") - let b = BigInt::from_string("9876543210987654329876543210987654321241243") + let a = @bigint.BigInt::from_string("123456789012345678123456789012345678") + let b = @bigint.BigInt::from_string( + "9876543210987654329876543210987654321241243", + ) let c = a + b inspect(c.to_string(), content="9876543334444443342222221334444443333586921") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-345678987654356798765467898765456789098764567890987655678", ) - let b = BigInt::from_string("76678908909876567890987656789098789") + let b = @bigint.BigInt::from_string("76678908909876567890987656789098789") let c = a + b inspect( c.to_string(), content="-345678987654356798765391219856546912530873580234198556889", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "-5467890987656789098765678909876789098767098767890987657890987689", ) let c = a + b @@ -159,10 +161,10 @@ test "add" { c.to_string(), content="-123456794480236665780245887778024588000245887779444446014444446903333367", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "-5467890987656789098765678909876789098767098767890987657890987689", ) let c = a + b @@ -170,8 +172,10 @@ test "add" { c.to_string(), content="123456783544454690466667690246666768246667690245246910232469131121357989", ) - let a = BigInt::from_string("123456789012345678123456789012345678123456789") - let b = BigInt::from_string( + let a = @bigint.BigInt::from_string( + "123456789012345678123456789012345678123456789", + ) + let b = @bigint.BigInt::from_string( "98765432109876543298765432109876543298765432112341241213125125", ) let c = a + b @@ -184,66 +188,68 @@ test "add" { d.to_string(), content="98765432109876543422222221122222221422222221124686919336581914", ) - let a = BigInt::from_string("1") - let b = BigInt::from_string("1") + let a = @bigint.BigInt::from_string("1") + let b = @bigint.BigInt::from_string("1") let c = a + b inspect(c.to_string(), content="2") } ///| test "sub" { - let a = BigInt::from_int64(987654321098765432L) - let b = BigInt::from_int64(123456789012345678L) + let a = @bigint.BigInt::from_int64(987654321098765432L) + let b = @bigint.BigInt::from_int64(123456789012345678L) let c = a - b inspect(c.to_string(), content="864197532086419754") let c = b - a inspect(c.to_string(), content="-864197532086419754") - let a = BigInt::from_string("987654321098765432987654321098765432") - let b = BigInt::from_string("123456789012345678123456789012345678") + let a = @bigint.BigInt::from_string("987654321098765432987654321098765432") + let b = @bigint.BigInt::from_string("123456789012345678123456789012345678") let c = a - b inspect(c.to_string(), content="864197532086419754864197532086419754") let c = b - a inspect(c.to_string(), content="-864197532086419754864197532086419754") - let a = BigInt::from_string("-123456789012345678123456789012345678") - let b = BigInt::from_string("-987654321098765432987654321098765432") + let a = @bigint.BigInt::from_string("-123456789012345678123456789012345678") + let b = @bigint.BigInt::from_string("-987654321098765432987654321098765432") let c = a - b inspect(c.to_string(), content="864197532086419754864197532086419754") let c = b - a inspect(c.to_string(), content="-864197532086419754864197532086419754") - let a = BigInt::from_string("123456789012345678123456789012345678233") - let b = BigInt::from_string("-987654321098765432987654321098765432") + let a = @bigint.BigInt::from_string("123456789012345678123456789012345678233") + let b = @bigint.BigInt::from_string("-987654321098765432987654321098765432") let c = a - b inspect(c.to_string(), content="124444443333444443556444443333444443665") - let a = BigInt::from_string("-123456789012345678123456789012345678233") - let b = BigInt::from_string("987654321098765432987654321098765432") + let a = @bigint.BigInt::from_string( + "-123456789012345678123456789012345678233", + ) + let b = @bigint.BigInt::from_string("987654321098765432987654321098765432") let c = a - b inspect(c.to_string(), content="-124444443333444443556444443333444443665") - let a = BigInt::from_string("123456789012345678123456789012345678233") - let b = BigInt::from_string("987") + let a = @bigint.BigInt::from_string("123456789012345678123456789012345678233") + let b = @bigint.BigInt::from_string("987") let c = a - b inspect(c.to_string(), content="123456789012345678123456789012345677246") } ///| test "mul" { - let a = BigInt::from_int64(987654321098765432L) - let b = BigInt::from_int64(123456789012345678L) + let a = @bigint.BigInt::from_int64(987654321098765432L) + let b = @bigint.BigInt::from_int64(123456789012345678L) let c = a * b inspect(c.to_string(), content="121932631137021794322511812221002896") - let b = BigInt::from_int(0) + let b = @bigint.BigInt::from_int(0) let c = a * b inspect(c.to_string(), content="0") - let a = BigInt::from_string("987654321098765432987654321098765432") - let b = BigInt::from_string("123456789012345678123456789012345678") + let a = @bigint.BigInt::from_string("987654321098765432987654321098765432") + let b = @bigint.BigInt::from_string("123456789012345678123456789012345678") let c = a * b inspect( c.to_string(), content="121932631137021794566377074495046484766956255579027586322511812221002896", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "5467890987656789098765678909876789098767098767890987657890987689", ) let c = a * b @@ -251,10 +257,10 @@ test "mul" { c.to_string(), content="-675048264005650638331575538351330675368295268968297112032725993817064025468035871811413387811508597465733350774788866848766914110358142", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678", ) let c = a * b @@ -262,10 +268,10 @@ test "mul" { c.to_string(), content="15241578753238836558451457271757357101661335790275877644871214308794398188081092827312918731290971345831439274500849864349959817710728382868480360920606901387000904130485419905521447340363938424041990550242456942562533760120975461083076969999493979603620179878012498124163389756531016644691358056296296328691358056296296328691358056296296328691358056296296328691358056296296328691358056296296328691358056296296328691358056296296328676116477543057492132906599024538971589696720506020451046486841987501930503276963468983409960067084950464889416857206431946368873647327913427848330437449394909327787227570876390807244017692357872286700807813839353766157597935320835245614388056802316725071178178283798204527968299765279684", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "1234567890123456781234567890123456781234567890123456712345678901234567812345678901234567812345678901234567123456789012345678123456789012345678123456789012345671234567890123456781234567890123456781234567890123456778123456789012345678123456789012345677812345678901234567812345678901234567", ) let c = a * b @@ -277,22 +283,22 @@ test "mul" { ///| test "div" { - let a = BigInt::from_int64(987654321098765432L) - let b = BigInt::from_int64(123456789012345678L) + let a = @bigint.BigInt::from_int64(987654321098765432L) + let b = @bigint.BigInt::from_int64(123456789012345678L) let c = a / b inspect(c.to_string(), content="8") let c = a % b inspect(c.to_string(), content="9000000008") - let a = BigInt::from_string("987654321098765432987654321098765432") - let b = BigInt::from_string("123456789012345678123456789012345678") + let a = @bigint.BigInt::from_string("987654321098765432987654321098765432") + let b = @bigint.BigInt::from_string("123456789012345678123456789012345678") let c = a / b inspect(c.to_string(), content="8") let c = a % b inspect(c.to_string(), content="9000000008000000009000000008") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "-5467890987656789098765678909876789098767098767890987657890987689", ) let c = a / b @@ -302,10 +308,10 @@ test "div" { c.to_string(), content="-1411754890143397710214334775703365651947321477507789807694283800", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "12421645375698213532453474567345623538756734578959876125298763582362", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "-5467890987656789098765678909876789098767098767890987657890987689", ) let c = a / b @@ -315,10 +321,10 @@ test "div" { c.to_string(), content="4064942729645489156617763015435495456653277079443154228330540643", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "559480073748030317374803031737502937374948313029373748143063751326169", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "5467890987656789098765678909876789098767098767890987657890987689", ) let c = a / b @@ -327,10 +333,10 @@ test "div" { inspect(c.to_string(), content="0") let c = b / a inspect(c.to_string(), content="0") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "98765432109876543298765432109876543298765432112341241213125125", ) let c = a / b @@ -340,14 +346,14 @@ test "div" { c.to_string(), content="-60185184318518518460185184318518518457104311955145277319847178", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) let c = a / b inspect(c.to_string(), content="-1") let c = a % b inspect(c.to_string(), content="0") - let b = BigInt::from_int(42) + let b = @bigint.BigInt::from_int(42) let c = a / b inspect( c.to_string(), @@ -355,10 +361,10 @@ test "div" { ) let c = a % b inspect(c.to_string(), content="-18") - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "192840512535448761530339373212972361809285001825938158026158292411480026580386667968523131569543343891463401449181505398836", ) - let b = BigInt::from_string( + let b = @bigint.BigInt::from_string( "53114991887765067119604462397623222751521283658033792", ) let c = a / b @@ -366,8 +372,8 @@ test "div" { c.to_string(), content="3630623025283172274355511610456320508397929760764978568884844414130904", ) - let a = BigInt::from_string("65535232222222222222222222222") - let b = BigInt::from_string("1") + let a = @bigint.BigInt::from_string("65535232222222222222222222222") + let b = @bigint.BigInt::from_string("1") let c = a / b inspect(c.to_string(), content="65535232222222222222222222222") let c = a % b @@ -376,7 +382,7 @@ test "div" { ///| test "op_shl" { - let a = BigInt::from_int64(1234567890123456789) + let a = @bigint.BigInt::from_int64(1234567890123456789) let b = a << 1 inspect(b.to_string(), content="2469135780246913578") let c = a << 64 @@ -388,70 +394,79 @@ test "op_shl" { ///| test "op_shr" { - let a = BigInt::from_int64(1234567890123456789L) + let a = @bigint.BigInt::from_int64(1234567890123456789L) let b = a >> 1 inspect(b.to_string(), content="617283945061728394") let c = a >> 64 inspect(c.to_string(), content="0") - let a = BigInt::from_int64(-1234567890123456789L) + let a = @bigint.BigInt::from_int64(-1234567890123456789L) let b = a >> 1 inspect(b.to_string(), content="-617283945061728395") let c = a >> 64 inspect(c.to_string(), content="-1") - assert_eq(BigInt::from_int64(0b1111_1111L) >> 4, BigInt::from_int64(0b1111L)) - assert_eq(BigInt::from_int64(0b1111_1111L) >> 24, BigInt::from_int64(0)) - assert_eq(BigInt::from_int64(0b1111_1111L) >> 44, BigInt::from_int64(0)) + assert_eq( + @bigint.BigInt::from_int64(0b1111_1111L) >> 4, + @bigint.BigInt::from_int64(0b1111L), + ) + assert_eq( + @bigint.BigInt::from_int64(0b1111_1111L) >> 24, + @bigint.BigInt::from_int64(0), + ) + assert_eq( + @bigint.BigInt::from_int64(0b1111_1111L) >> 44, + @bigint.BigInt::from_int64(0), + ) } ///| test "decimal_string" { - let a = BigInt::from_string("0") + let a = @bigint.BigInt::from_string("0") inspect(a.to_string(), content="0") - let a = BigInt::from_string("123") + let a = @bigint.BigInt::from_string("123") inspect(a.to_string(), content="123") - assert_eq(a, BigInt::from_int64(123L)) - let a = BigInt::from_string("1234567890123456789") + assert_eq(a, @bigint.BigInt::from_int64(123L)) + let a = @bigint.BigInt::from_string("1234567890123456789") inspect(a.to_string(), content="1234567890123456789") - let b = BigInt::from_string("-1234567890") + let b = @bigint.BigInt::from_string("-1234567890") inspect(b.to_string(), content="-1234567890") - assert_eq(a, BigInt::from_int64(1234567890123456789L)) + assert_eq(a, @bigint.BigInt::from_int64(1234567890123456789L)) let str = "12345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812" - let a = BigInt::from_string(str) + let a = @bigint.BigInt::from_string(str) inspect(a.to_string(), content=str) - let a = BigInt::from_int64(1234567890123456789L) + let a = @bigint.BigInt::from_int64(1234567890123456789L) inspect(a.to_string(), content="1234567890123456789") - let b = BigInt::from_int64(-1234567890L) + let b = @bigint.BigInt::from_int64(-1234567890L) inspect(b.to_string(), content="-1234567890") } ///| test "from_int" { - let a = BigInt::from_int(1234567899) + let a = @bigint.BigInt::from_int(1234567899) inspect(a.to_string(), content="1234567899") - let b = BigInt::from_int(-1234567890) + let b = @bigint.BigInt::from_int(-1234567890) inspect(b.to_string(), content="-1234567890") } ///| test "from_int" { - let a = BigInt::from_int(1234567899) + let a = @bigint.BigInt::from_int(1234567899) inspect(a.to_string(), content="1234567899") - let b = BigInt::from_int(-1234567890) + let b = @bigint.BigInt::from_int(-1234567890) inspect(b.to_string(), content="-1234567890") } ///| test "compare" { - let a = BigInt::from_int64(1234567890123456789L) - let b = BigInt::from_int64(-1234567890123456789L) + let a = @bigint.BigInt::from_int64(1234567890123456789L) + let b = @bigint.BigInt::from_int64(-1234567890123456789L) inspect(a.compare(b), content="1") inspect(b.compare(a), content="-1") let a = -a - let b = BigInt::from_int64(-1234567890123456788L) + let b = @bigint.BigInt::from_int64(-1234567890123456788L) assert_true(a.compare(b) < 0) assert_true(b.compare(a) > 0) - let a = BigInt::from_int64(-1234567890123456789L) - let b = BigInt::from_int64(-1234569L) + let a = @bigint.BigInt::from_int64(-1234567890123456789L) + let b = @bigint.BigInt::from_int64(-1234569L) assert_true(a.compare(b) < 0) assert_true(b.compare(a) > 0) } @@ -459,39 +474,39 @@ test "compare" { ///| test "from_hex" { // Check zero - let a = BigInt::from_hex("0") + let a = @bigint.BigInt::from_hex("0") inspect(a.to_string(), content="0") // Test positive number - let a = BigInt::from_hex("1") + let a = @bigint.BigInt::from_hex("1") inspect(a.to_string(), content="1") // Test negative number - let a = BigInt::from_hex("-F") + let a = @bigint.BigInt::from_hex("-F") inspect(a.to_string(), content="-15") - let a = BigInt::from_hex("-a") + let a = @bigint.BigInt::from_hex("-a") inspect(a.to_string(), content="-10") // Test large positive number - let a = BigInt::from_hex("112210F47DE98115") + let a = @bigint.BigInt::from_hex("112210F47DE98115") inspect(a.to_string(), content="1234567890123456789") // Test very large positive number - let a = BigInt::from_hex( + let a = @bigint.BigInt::from_hex( "123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( a.to_string(), content="35365207917649046390549507392234216535182073572857507984542859337680634154115797374584", ) - let a = BigInt::from_hex( + let a = @bigint.BigInt::from_hex( "11E3444DC1F35F057AD2CBC2791737468140A426FAC3CBA7AF8C92A8F34E", ) inspect( a.to_string(), content="123456789012345678123456789012345678123456789012345678123456789012345678", ) - let a = BigInt::from_hex( + let a = @bigint.BigInt::from_hex( "805146F2F58580962A0A2E6134BC75E25AD97AE3D09CD34BA4F629AB8911F3F5CB8573A62EDD16B0D46775A415F545A75518DA3439914D9CAA26449067D85E704E8FCF9B29182485B41F952616BACDFDDF52B413B524D0EB743E8264A9C6AE32D12C3D20C5B81189060F4AC5D216713D503A69644EA8E4EA220A720C41F6B3D18BED65B4238318E6B0A41D8540D756865EC92DF40E8D365A230F17DF1D0A440BC6A557CD46D00B10D74C0E75500B2ADB3A0336223F9285B78CD04F485E417E1DB562B9EFCF79433209E6D6E2F43A484E471DE4F1F5AE38E08E7DAEB644C2C0A22697DD6D29BE0B40FF50DB575FEF02FA5525953C7C198B4A3600BA8CE1F917852A4B957151189F09DCDFCB79963E7D850127858A97855B94870ACCBE8203E73FE79791EE6EA1B1282A0CEAC54D6F6B7CD6C7B8D8092E949FF0A84", ) inspect( @@ -500,7 +515,7 @@ test "from_hex" { ) // Test very large negative number - let a = BigInt::from_hex( + let a = @bigint.BigInt::from_hex( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( @@ -512,39 +527,39 @@ test "from_hex" { ///| test "to_hex" { // Check zero - let a = BigInt::from_hex("00") + let a = @bigint.BigInt::from_hex("00") inspect(a.to_hex(), content="0") // Test negative number - let a = BigInt::from_hex("-F") + let a = @bigint.BigInt::from_hex("-F") inspect(a.to_hex(), content="-F") // Test positive number - let a = BigInt::from_hex("F") + let a = @bigint.BigInt::from_hex("F") inspect(a.to_hex(), content="F") // Test positive number with leading zero - let a = BigInt::from_hex("10") + let a = @bigint.BigInt::from_hex("10") inspect(a.to_hex(), content="10") // Test large positive number - let a = BigInt::from_hex("01234567890123456789") + let a = @bigint.BigInt::from_hex("01234567890123456789") inspect(a.to_hex(), content="1234567890123456789") // Check padding - let a = BigInt::from_hex("100000") + let a = @bigint.BigInt::from_hex("100000") inspect(a.to_string(), content="1048576") inspect(a.to_hex(), content="100000") // Test very large positive number - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( a.to_hex(), content="11E3444DC1F35F057AD2CBC2791737468140A426FAC3CBA7AF8C92A8F34E", ) - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "35365207917649046390549507392234216535182073572857507984542859337680634154115797374584", ) inspect( @@ -552,7 +567,7 @@ test "to_hex" { content="123456789012345678123456789012345678123456789012345678123456789012345678", ) let str = "12345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812" - let a = BigInt::from_string(str) + let a = @bigint.BigInt::from_string(str) inspect( a.to_hex(), content="805146F2F58580962A0A2E6134BC75E25AD97AE3D09CD34BA4F629AB8911F3F5CB8573A62EDD16B0D46775A415F545A75518DA3439914D9CAA26449067D85E704E8FCF9B29182485B41F952616BACDFDDF52B413B524D0EB743E8264A9C6AE32D12C3D20C5B81189060F4AC5D216713D503A69644EA8E4EA220A720C41F6B3D18BED65B4238318E6B0A41D8540D756865EC92DF40E8D365A230F17DF1D0A440BC6A557CD46D00B10D74C0E75500B2ADB3A0336223F9285B78CD04F485E417E1DB562B9EFCF79433209E6D6E2F43A484E471DE4F1F5AE38E08E7DAEB644C2C0A22697DD6D29BE0B40FF50DB575FEF02FA5525953C7C198B4A3600BA8CE1F917852A4B957151189F09DCDFCB79963E7D850127858A97855B94870ACCBE8203E73FE79791EE6EA1B1282A0CEAC54D6F6B7CD6C7B8D8092E949FF0A84", @@ -563,7 +578,7 @@ test "to_hex" { ) // Test very large negative number - let a = BigInt::from_string( + let a = @bigint.BigInt::from_string( "-123456789012345678123456789012345678123456789012345678123456789012345678", ) inspect( @@ -579,120 +594,120 @@ test "to_hex" { ///| test "pow" { let x = 2N.pow(5664N).to_hex() - assert_eq(x.charcode_at(0), '1') - assert_eq(x.charcodes(start=1), String::make(5664 / 4, '0')[:]) + assert_eq(x.get_char(0).unwrap(), '1') + assert_eq(x[1:], String::make(5664 / 4, '0')[:]) } ///| test "land_1" { - let a = BigInt::from_string("-10") - let b = BigInt::from_string("11") + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_string("11") inspect(a & b, content="2") } ///| test "land_2" { - let a = BigInt::from_string("-10") - let b = BigInt::from_hex("fffffffffffffff") // 15 'f' chars + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_hex("fffffffffffffff") // 15 'f' chars inspect(a & b, content="1152921504606846966") } ///| test "land_3" { - let a = BigInt::from_hex("ffffffffffffffff") // 16 'f' chars - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_hex("ffffffffffffffff") // 16 'f' chars + let b = @bigint.BigInt::from_string("-10") inspect(a & b, content="18446744073709551606") } ///| test "land_4" { - let a = BigInt::from_string("0") - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_string("0") + let b = @bigint.BigInt::from_string("-10") inspect(a & b, content="0") } ///| test "land_5" { - let a = BigInt::from_hex("ffffffffffffffff") - let b = BigInt::from_hex("ffffffffffffffff") + let a = @bigint.BigInt::from_hex("ffffffffffffffff") + let b = @bigint.BigInt::from_hex("ffffffffffffffff") inspect(a & b, content="18446744073709551615") } ///| test "lor_1" { - let a = BigInt::from_string("-10") - let b = BigInt::from_string("11") + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_string("11") inspect(a | b, content="-1") } ///| test "lor_2" { - let a = BigInt::from_string("-10") - let b = BigInt::from_hex("fffffffffffffff") // 15 'f' chars + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_hex("fffffffffffffff") // 15 'f' chars inspect(a | b, content="-1") } ///| test "lor_3" { - let a = BigInt::from_hex("ffffffffffffffff") // 16 'f' chars - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_hex("ffffffffffffffff") // 16 'f' chars + let b = @bigint.BigInt::from_string("-10") inspect(a | b, content="-1") } ///| test "lor_4" { - let a = BigInt::from_string("0") - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_string("0") + let b = @bigint.BigInt::from_string("-10") inspect(a | b, content="-10") } ///| test "lor_5" { - let a = BigInt::from_hex("ffffffffffffffff") - let b = BigInt::from_string("0") + let a = @bigint.BigInt::from_hex("ffffffffffffffff") + let b = @bigint.BigInt::from_string("0") inspect(a | b, content="18446744073709551615") } ///| test "lxor_1" { - let a = BigInt::from_string("-10") - let b = BigInt::from_string("11") + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_string("11") inspect(a ^ b, content="-3") } ///| test "lxor_2" { - let a = BigInt::from_string("-10") - let b = BigInt::from_hex("fffffffffffffff") // 15 'f' chars + let a = @bigint.BigInt::from_string("-10") + let b = @bigint.BigInt::from_hex("fffffffffffffff") // 15 'f' chars inspect(a ^ b, content="-1152921504606846967") } ///| test "lxor_3" { - let a = BigInt::from_hex("ffffffffffffffff") // 16 'f' chars - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_hex("ffffffffffffffff") // 16 'f' chars + let b = @bigint.BigInt::from_string("-10") inspect(a ^ b, content="-18446744073709551607") } ///| test "lxor_4" { - let a = BigInt::from_string("0") - let b = BigInt::from_string("-10") + let a = @bigint.BigInt::from_string("0") + let b = @bigint.BigInt::from_string("-10") inspect(a ^ b, content="-10") } ///| test "lxor_5" { - let a = BigInt::from_string("0") - let b = BigInt::from_hex("ffffffffffffffff") + let a = @bigint.BigInt::from_string("0") + let b = @bigint.BigInt::from_hex("ffffffffffffffff") inspect(a ^ b, content="18446744073709551615") } ///| test "to_int" { - let a = BigInt::from_int(1234567899) + let a = @bigint.BigInt::from_int(1234567899) inspect(a.to_int(), content="1234567899") - let b = BigInt::from_int(-1234567890) + let b = @bigint.BigInt::from_int(-1234567890) inspect(b.to_int(), content="-1234567890") let max = 2N.pow(32) - 1 inspect(max.to_int(), content="-1") @@ -701,9 +716,9 @@ test "to_int" { ///| test "to_uint" { - let a = BigInt::from_uint(1234567899) + let a = @bigint.BigInt::from_uint(1234567899) inspect(a.to_uint(), content="1234567899") - let b = BigInt::from_int(-1234567890) + let b = @bigint.BigInt::from_int(-1234567890) inspect(b.to_uint(), content="3060399406") let max = 2N.pow(32) - 1 inspect(max.to_uint(), content="4294967295") @@ -712,9 +727,9 @@ test "to_uint" { ///| test "to_int64" { - let a = BigInt::from_int64(1234567890123456789L) + let a = @bigint.BigInt::from_int64(1234567890123456789L) inspect(a.to_int64(), content="1234567890123456789") - let b = BigInt::from_int64(-1234567890123456789L) + let b = @bigint.BigInt::from_int64(-1234567890123456789L) inspect(b.to_int64(), content="-1234567890123456789") let max = 2N.pow(63) - 1 inspect(max.to_int64(), content="9223372036854775807") @@ -723,9 +738,9 @@ test "to_int64" { ///| test "to_uint64" { - let a = BigInt::from_uint64(1234567890123456789UL) + let a = @bigint.BigInt::from_uint64(1234567890123456789UL) inspect(a.to_uint64(), content="1234567890123456789") - let b = BigInt::from_int64(-1234567890123456789L) + let b = @bigint.BigInt::from_int64(-1234567890123456789L) inspect(b.to_uint64(), content="17212176183586094827") let max = 2N.pow(64) - 1 inspect(max.to_uint64(), content="18446744073709551615") @@ -734,243 +749,271 @@ test "to_uint64" { ///| test "equal_int" { - assert_true(BigInt::equal_int(1N, 1)) - assert_true(BigInt::equal_int(-1N, -1)) - assert_false(BigInt::equal_int(1N, -1)) - assert_false(BigInt::equal_int(-1N, 1)) - assert_true(BigInt::equal_int(0N, 0)) - assert_false(BigInt::equal_int(0N, 1)) - assert_false(BigInt::equal_int(1N, 0)) + assert_true(@bigint.BigInt::equal_int(1N, 1)) + assert_true(@bigint.BigInt::equal_int(-1N, -1)) + assert_false(@bigint.BigInt::equal_int(1N, -1)) + assert_false(@bigint.BigInt::equal_int(-1N, 1)) + assert_true(@bigint.BigInt::equal_int(0N, 0)) + assert_false(@bigint.BigInt::equal_int(0N, 1)) + assert_false(@bigint.BigInt::equal_int(1N, 0)) // Test with max/min int values - assert_true(BigInt::equal_int(2147483647N, 2147483647)) - assert_true(BigInt::equal_int(-2147483648N, -2147483648)) - assert_false(BigInt::equal_int(2147483647N, -2147483648)) - assert_false(BigInt::equal_int(-2147483648N, 2147483647)) + assert_true(@bigint.BigInt::equal_int(2147483647N, 2147483647)) + assert_true(@bigint.BigInt::equal_int(-2147483648N, -2147483648)) + assert_false(@bigint.BigInt::equal_int(2147483647N, -2147483648)) + assert_false(@bigint.BigInt::equal_int(-2147483648N, 2147483647)) // Test with large BigInt that doesn't fit in int let large = 2N.pow(32) - assert_false(BigInt::equal_int(large, 0)) - assert_false(BigInt::equal_int(large, 1)) - assert_false(BigInt::equal_int(large, -1)) + assert_false(@bigint.BigInt::equal_int(large, 0)) + assert_false(@bigint.BigInt::equal_int(large, 1)) + assert_false(@bigint.BigInt::equal_int(large, -1)) // Test with negative large BigInt let neg_large = -2N.pow(32) - assert_false(BigInt::equal_int(neg_large, 0)) - assert_false(BigInt::equal_int(neg_large, 1)) - assert_false(BigInt::equal_int(neg_large, -1)) + assert_false(@bigint.BigInt::equal_int(neg_large, 0)) + assert_false(@bigint.BigInt::equal_int(neg_large, 1)) + assert_false(@bigint.BigInt::equal_int(neg_large, -1)) } ///| test "equal_int64" { - assert_true(BigInt::equal_int64(1N, 1L)) - assert_true(BigInt::equal_int64(-1N, -1L)) - assert_false(BigInt::equal_int64(1N, -1L)) - assert_false(BigInt::equal_int64(-1N, 1L)) - assert_true(BigInt::equal_int64(0N, 0L)) - assert_false(BigInt::equal_int64(0N, 1L)) - assert_false(BigInt::equal_int64(1N, 0L)) + assert_true(@bigint.BigInt::equal_int64(1N, 1L)) + assert_true(@bigint.BigInt::equal_int64(-1N, -1L)) + assert_false(@bigint.BigInt::equal_int64(1N, -1L)) + assert_false(@bigint.BigInt::equal_int64(-1N, 1L)) + assert_true(@bigint.BigInt::equal_int64(0N, 0L)) + assert_false(@bigint.BigInt::equal_int64(0N, 1L)) + assert_false(@bigint.BigInt::equal_int64(1N, 0L)) // Test with max/min int64 values - assert_true(BigInt::equal_int64(9223372036854775807N, 9223372036854775807L)) - assert_true(BigInt::equal_int64(-9223372036854775808N, -9223372036854775808L)) - assert_false(BigInt::equal_int64(9223372036854775807N, -9223372036854775808L)) - assert_false(BigInt::equal_int64(-9223372036854775808N, 9223372036854775807L)) + assert_true( + @bigint.BigInt::equal_int64(9223372036854775807N, 9223372036854775807L), + ) + assert_true( + @bigint.BigInt::equal_int64(-9223372036854775808N, -9223372036854775808L), + ) + assert_false( + @bigint.BigInt::equal_int64(9223372036854775807N, -9223372036854775808L), + ) + assert_false( + @bigint.BigInt::equal_int64(-9223372036854775808N, 9223372036854775807L), + ) // Test with large values that fit in int64 - assert_true(BigInt::equal_int64(1234567890123456789N, 1234567890123456789L)) - assert_true(BigInt::equal_int64(-1234567890123456789N, -1234567890123456789L)) - assert_false(BigInt::equal_int64(1234567890123456789N, -1234567890123456789L)) - assert_false(BigInt::equal_int64(-1234567890123456789N, 1234567890123456789L)) + assert_true( + @bigint.BigInt::equal_int64(1234567890123456789N, 1234567890123456789L), + ) + assert_true( + @bigint.BigInt::equal_int64(-1234567890123456789N, -1234567890123456789L), + ) + assert_false( + @bigint.BigInt::equal_int64(1234567890123456789N, -1234567890123456789L), + ) + assert_false( + @bigint.BigInt::equal_int64(-1234567890123456789N, 1234567890123456789L), + ) // Test with BigInt that doesn't fit in int64 let large = 2N.pow(64) - assert_false(BigInt::equal_int64(large, 0L)) - assert_false(BigInt::equal_int64(large, 1L)) - assert_false(BigInt::equal_int64(large, -1L)) - assert_false(BigInt::equal_int64(large, 9223372036854775807L)) - assert_false(BigInt::equal_int64(large, -9223372036854775808L)) + assert_false(@bigint.BigInt::equal_int64(large, 0L)) + assert_false(@bigint.BigInt::equal_int64(large, 1L)) + assert_false(@bigint.BigInt::equal_int64(large, -1L)) + assert_false(@bigint.BigInt::equal_int64(large, 9223372036854775807L)) + assert_false(@bigint.BigInt::equal_int64(large, -9223372036854775808L)) // Test with negative large BigInt that doesn't fit in int64 let neg_large = -2N.pow(64) - assert_false(BigInt::equal_int64(neg_large, 0L)) - assert_false(BigInt::equal_int64(neg_large, 1L)) - assert_false(BigInt::equal_int64(neg_large, -1L)) - assert_false(BigInt::equal_int64(neg_large, 9223372036854775807L)) - assert_false(BigInt::equal_int64(neg_large, -9223372036854775808L)) + assert_false(@bigint.BigInt::equal_int64(neg_large, 0L)) + assert_false(@bigint.BigInt::equal_int64(neg_large, 1L)) + assert_false(@bigint.BigInt::equal_int64(neg_large, -1L)) + assert_false(@bigint.BigInt::equal_int64(neg_large, 9223372036854775807L)) + assert_false(@bigint.BigInt::equal_int64(neg_large, -9223372036854775808L)) // Test edge cases around int64 boundaries let max_plus_one = 9223372036854775808N // 2^63 let min_minus_one = -9223372036854775809N // -2^63 - 1 - assert_false(BigInt::equal_int64(max_plus_one, 9223372036854775807L)) - assert_false(BigInt::equal_int64(min_minus_one, -9223372036854775808L)) + assert_false(@bigint.BigInt::equal_int64(max_plus_one, 9223372036854775807L)) + assert_false( + @bigint.BigInt::equal_int64(min_minus_one, -9223372036854775808L), + ) // Test with powers of 2 - assert_true(BigInt::equal_int64(2N.pow(32), 4294967296L)) - assert_true(BigInt::equal_int64(2N.pow(62), 4611686018427387904L)) - assert_false(BigInt::equal_int64(2N.pow(63), 9223372036854775807L)) // This would overflow + assert_true(@bigint.BigInt::equal_int64(2N.pow(32), 4294967296L)) + assert_true(@bigint.BigInt::equal_int64(2N.pow(62), 4611686018427387904L)) + assert_false(@bigint.BigInt::equal_int64(2N.pow(63), 9223372036854775807L)) // This would overflow } ///| test "compare_int" { // Test with zero - assert_eq(BigInt::compare_int(0N, 0), 0) - assert_eq(BigInt::compare_int(0N, 1), -1) - assert_eq(BigInt::compare_int(0N, -1), 1) + assert_eq(@bigint.BigInt::compare_int(0N, 0), 0) + assert_eq(@bigint.BigInt::compare_int(0N, 1), -1) + assert_eq(@bigint.BigInt::compare_int(0N, -1), 1) // Test with positive values - assert_eq(BigInt::compare_int(1N, 1), 0) - assert_eq(BigInt::compare_int(1N, 0), 1) - assert_eq(BigInt::compare_int(1N, 2), -1) - assert_eq(BigInt::compare_int(42N, 42), 0) - assert_eq(BigInt::compare_int(100N, 50), 1) - assert_eq(BigInt::compare_int(25N, 100), -1) + assert_eq(@bigint.BigInt::compare_int(1N, 1), 0) + assert_eq(@bigint.BigInt::compare_int(1N, 0), 1) + assert_eq(@bigint.BigInt::compare_int(1N, 2), -1) + assert_eq(@bigint.BigInt::compare_int(42N, 42), 0) + assert_eq(@bigint.BigInt::compare_int(100N, 50), 1) + assert_eq(@bigint.BigInt::compare_int(25N, 100), -1) // Test with negative values - assert_eq(BigInt::compare_int(-1N, -1), 0) - assert_eq(BigInt::compare_int(-1N, 0), -1) - assert_eq(BigInt::compare_int(-1N, 1), -1) - assert_eq(BigInt::compare_int(-42N, -42), 0) - assert_eq(BigInt::compare_int(-25N, -100), 1) - assert_eq(BigInt::compare_int(-100N, -50), -1) + assert_eq(@bigint.BigInt::compare_int(-1N, -1), 0) + assert_eq(@bigint.BigInt::compare_int(-1N, 0), -1) + assert_eq(@bigint.BigInt::compare_int(-1N, 1), -1) + assert_eq(@bigint.BigInt::compare_int(-42N, -42), 0) + assert_eq(@bigint.BigInt::compare_int(-25N, -100), 1) + assert_eq(@bigint.BigInt::compare_int(-100N, -50), -1) // Test with mixed signs - assert_eq(BigInt::compare_int(1N, -1), 1) - assert_eq(BigInt::compare_int(-1N, 1), -1) - assert_eq(BigInt::compare_int(100N, -50), 1) - assert_eq(BigInt::compare_int(-100N, 50), -1) + assert_eq(@bigint.BigInt::compare_int(1N, -1), 1) + assert_eq(@bigint.BigInt::compare_int(-1N, 1), -1) + assert_eq(@bigint.BigInt::compare_int(100N, -50), 1) + assert_eq(@bigint.BigInt::compare_int(-100N, 50), -1) // Test with Int boundary values - assert_eq(BigInt::compare_int(2147483647N, 2147483647), 0) // Int.max_value - assert_eq(BigInt::compare_int(-2147483648N, -2147483648), 0) // Int.min_value - assert_eq(BigInt::compare_int(2147483647N, 2147483646), 1) - assert_eq(BigInt::compare_int(2147483646N, 2147483647), -1) - assert_eq(BigInt::compare_int(-2147483648N, -2147483647), -1) - assert_eq(BigInt::compare_int(-2147483647N, -2147483648), 1) + assert_eq(@bigint.BigInt::compare_int(2147483647N, 2147483647), 0) // Int.max_value + assert_eq(@bigint.BigInt::compare_int(-2147483648N, -2147483648), 0) // Int.min_value + assert_eq(@bigint.BigInt::compare_int(2147483647N, 2147483646), 1) + assert_eq(@bigint.BigInt::compare_int(2147483646N, 2147483647), -1) + assert_eq(@bigint.BigInt::compare_int(-2147483648N, -2147483647), -1) + assert_eq(@bigint.BigInt::compare_int(-2147483647N, -2147483648), 1) // Test with BigInt that doesn't fit in Int let large = 2147483648N // Int.max_value + 1 - assert_eq(BigInt::compare_int(large, 2147483647), 1) - assert_eq(BigInt::compare_int(large, -2147483648), 1) - assert_eq(BigInt::compare_int(large, 0), 1) + assert_eq(@bigint.BigInt::compare_int(large, 2147483647), 1) + assert_eq(@bigint.BigInt::compare_int(large, -2147483648), 1) + assert_eq(@bigint.BigInt::compare_int(large, 0), 1) let neg_large = -2147483649N // Int.min_value - 1 - assert_eq(BigInt::compare_int(neg_large, -2147483648), -1) - assert_eq(BigInt::compare_int(neg_large, 2147483647), -1) - assert_eq(BigInt::compare_int(neg_large, 0), -1) + assert_eq(@bigint.BigInt::compare_int(neg_large, -2147483648), -1) + assert_eq(@bigint.BigInt::compare_int(neg_large, 2147483647), -1) + assert_eq(@bigint.BigInt::compare_int(neg_large, 0), -1) // Test with very large BigInt values - let very_large = BigInt::from_string("123456789012345678901234567890") - assert_eq(BigInt::compare_int(very_large, 2147483647), 1) - assert_eq(BigInt::compare_int(very_large, -2147483648), 1) - assert_eq(BigInt::compare_int(very_large, 0), 1) - let very_neg_large = BigInt::from_string("-123456789012345678901234567890") - assert_eq(BigInt::compare_int(very_neg_large, 2147483647), -1) - assert_eq(BigInt::compare_int(very_neg_large, -2147483648), -1) - assert_eq(BigInt::compare_int(very_neg_large, 0), -1) + let very_large = @bigint.BigInt::from_string("123456789012345678901234567890") + assert_eq(@bigint.BigInt::compare_int(very_large, 2147483647), 1) + assert_eq(@bigint.BigInt::compare_int(very_large, -2147483648), 1) + assert_eq(@bigint.BigInt::compare_int(very_large, 0), 1) + let very_neg_large = @bigint.BigInt::from_string( + "-123456789012345678901234567890", + ) + assert_eq(@bigint.BigInt::compare_int(very_neg_large, 2147483647), -1) + assert_eq(@bigint.BigInt::compare_int(very_neg_large, -2147483648), -1) + assert_eq(@bigint.BigInt::compare_int(very_neg_large, 0), -1) // Test with powers of 2 - assert_eq(BigInt::compare_int(2N.pow(10), 1024), 0) - assert_eq(BigInt::compare_int(2N.pow(20), 1048576), 0) - assert_eq(BigInt::compare_int(2N.pow(30), 1073741824), 0) - assert_eq(BigInt::compare_int(2N.pow(31), 2147483647), 1) // 2^31 > Int.max_value + assert_eq(@bigint.BigInt::compare_int(2N.pow(10), 1024), 0) + assert_eq(@bigint.BigInt::compare_int(2N.pow(20), 1048576), 0) + assert_eq(@bigint.BigInt::compare_int(2N.pow(30), 1073741824), 0) + assert_eq(@bigint.BigInt::compare_int(2N.pow(31), 2147483647), 1) // 2^31 > Int.max_value } ///| test "compare_int64" { // Test with zero - assert_eq(BigInt::compare_int64(0N, 0L), 0) - assert_eq(BigInt::compare_int64(0N, 1L), -1) - assert_eq(BigInt::compare_int64(0N, -1L), 1) + assert_eq(@bigint.BigInt::compare_int64(0N, 0L), 0) + assert_eq(@bigint.BigInt::compare_int64(0N, 1L), -1) + assert_eq(@bigint.BigInt::compare_int64(0N, -1L), 1) // Test with positive values - assert_eq(BigInt::compare_int64(1N, 1L), 0) - assert_eq(BigInt::compare_int64(42N, 42L), 0) - assert_eq(BigInt::compare_int64(100N, 50L), 1) - assert_eq(BigInt::compare_int64(25N, 100L), -1) + assert_eq(@bigint.BigInt::compare_int64(1N, 1L), 0) + assert_eq(@bigint.BigInt::compare_int64(42N, 42L), 0) + assert_eq(@bigint.BigInt::compare_int64(100N, 50L), 1) + assert_eq(@bigint.BigInt::compare_int64(25N, 100L), -1) // Test with negative values - assert_eq(BigInt::compare_int64(-1N, -1L), 0) - assert_eq(BigInt::compare_int64(-1N, 0L), -1) - assert_eq(BigInt::compare_int64(-1N, 1L), -1) - assert_eq(BigInt::compare_int64(-42N, -42L), 0) - assert_eq(BigInt::compare_int64(-25N, -100L), 1) - assert_eq(BigInt::compare_int64(-100N, -50L), -1) + assert_eq(@bigint.BigInt::compare_int64(-1N, -1L), 0) + assert_eq(@bigint.BigInt::compare_int64(-1N, 0L), -1) + assert_eq(@bigint.BigInt::compare_int64(-1N, 1L), -1) + assert_eq(@bigint.BigInt::compare_int64(-42N, -42L), 0) + assert_eq(@bigint.BigInt::compare_int64(-25N, -100L), 1) + assert_eq(@bigint.BigInt::compare_int64(-100N, -50L), -1) // Test with mixed signs - assert_eq(BigInt::compare_int64(1N, -1L), 1) - assert_eq(BigInt::compare_int64(-1N, 1L), -1) - assert_eq(BigInt::compare_int64(100N, -50L), 1) - assert_eq(BigInt::compare_int64(-100N, 50L), -1) + assert_eq(@bigint.BigInt::compare_int64(1N, -1L), 1) + assert_eq(@bigint.BigInt::compare_int64(-1N, 1L), -1) + assert_eq(@bigint.BigInt::compare_int64(100N, -50L), 1) + assert_eq(@bigint.BigInt::compare_int64(-100N, 50L), -1) // Test with Int64 boundary values assert_eq( - BigInt::compare_int64(9223372036854775807N, 9223372036854775807L), + @bigint.BigInt::compare_int64(9223372036854775807N, 9223372036854775807L), 0, ) // Int64.max_value assert_eq( - BigInt::compare_int64(-9223372036854775808N, -9223372036854775808L), + @bigint.BigInt::compare_int64(-9223372036854775808N, -9223372036854775808L), 0, ) // Int64.min_value assert_eq( - BigInt::compare_int64(9223372036854775807N, 9223372036854775806L), + @bigint.BigInt::compare_int64(9223372036854775807N, 9223372036854775806L), 1, ) assert_eq( - BigInt::compare_int64(9223372036854775806N, 9223372036854775807L), + @bigint.BigInt::compare_int64(9223372036854775806N, 9223372036854775807L), -1, ) assert_eq( - BigInt::compare_int64(-9223372036854775808N, -9223372036854775807L), + @bigint.BigInt::compare_int64(-9223372036854775808N, -9223372036854775807L), -1, ) assert_eq( - BigInt::compare_int64(-9223372036854775807N, -9223372036854775808L), + @bigint.BigInt::compare_int64(-9223372036854775807N, -9223372036854775808L), 1, ) // Test with BigInt that doesn't fit in Int64 - let large = BigInt::from_string("9223372036854775808") // Int64.max_value + 1 - assert_eq(BigInt::compare_int64(large, 9223372036854775807L), 1) - assert_eq(BigInt::compare_int64(large, -9223372036854775808L), 1) - assert_eq(BigInt::compare_int64(large, 0L), 1) - let neg_large = BigInt::from_string("-9223372036854775809") // Int64.min_value - 1 - assert_eq(BigInt::compare_int64(neg_large, -9223372036854775808L), -1) - assert_eq(BigInt::compare_int64(neg_large, 9223372036854775807L), -1) - assert_eq(BigInt::compare_int64(neg_large, 0L), -1) + let large = @bigint.BigInt::from_string("9223372036854775808") // Int64.max_value + 1 + assert_eq(@bigint.BigInt::compare_int64(large, 9223372036854775807L), 1) + assert_eq(@bigint.BigInt::compare_int64(large, -9223372036854775808L), 1) + assert_eq(@bigint.BigInt::compare_int64(large, 0L), 1) + let neg_large = @bigint.BigInt::from_string("-9223372036854775809") // Int64.min_value - 1 + assert_eq(@bigint.BigInt::compare_int64(neg_large, -9223372036854775808L), -1) + assert_eq(@bigint.BigInt::compare_int64(neg_large, 9223372036854775807L), -1) + assert_eq(@bigint.BigInt::compare_int64(neg_large, 0L), -1) // Test with very large BigInt values - let very_large = BigInt::from_string("123456789012345678901234567890") - assert_eq(BigInt::compare_int64(very_large, 9223372036854775807L), 1) - assert_eq(BigInt::compare_int64(very_large, -9223372036854775808L), 1) - assert_eq(BigInt::compare_int64(very_large, 0L), 1) - let very_neg_large = BigInt::from_string("-123456789012345678901234567890") - assert_eq(BigInt::compare_int64(very_neg_large, 9223372036854775807L), -1) - assert_eq(BigInt::compare_int64(very_neg_large, -9223372036854775808L), -1) - assert_eq(BigInt::compare_int64(very_neg_large, 0L), -1) + let very_large = @bigint.BigInt::from_string("123456789012345678901234567890") + assert_eq(@bigint.BigInt::compare_int64(very_large, 9223372036854775807L), 1) + assert_eq(@bigint.BigInt::compare_int64(very_large, -9223372036854775808L), 1) + assert_eq(@bigint.BigInt::compare_int64(very_large, 0L), 1) + let very_neg_large = @bigint.BigInt::from_string( + "-123456789012345678901234567890", + ) + assert_eq( + @bigint.BigInt::compare_int64(very_neg_large, 9223372036854775807L), + -1, + ) + assert_eq( + @bigint.BigInt::compare_int64(very_neg_large, -9223372036854775808L), + -1, + ) + assert_eq(@bigint.BigInt::compare_int64(very_neg_large, 0L), -1) // Test with powers of 2 - assert_eq(BigInt::compare_int64(2N.pow(10), 1024L), 0) - assert_eq(BigInt::compare_int64(2N.pow(20), 1048576L), 0) - assert_eq(BigInt::compare_int64(2N.pow(30), 1073741824L), 0) - assert_eq(BigInt::compare_int64(2N.pow(40), 1099511627776L), 0) - assert_eq(BigInt::compare_int64(2N.pow(50), 1125899906842624L), 0) - assert_eq(BigInt::compare_int64(2N.pow(60), 1152921504606846976L), 0) - assert_eq(BigInt::compare_int64(2N.pow(62), 4611686018427387904L), 0) - assert_eq(BigInt::compare_int64(2N.pow(63), 9223372036854775807L), 1) // 2^63 > Int64.max_value + assert_eq(@bigint.BigInt::compare_int64(2N.pow(10), 1024L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(20), 1048576L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(30), 1073741824L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(40), 1099511627776L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(50), 1125899906842624L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(60), 1152921504606846976L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(62), 4611686018427387904L), 0) + assert_eq(@bigint.BigInt::compare_int64(2N.pow(63), 9223372036854775807L), 1) // 2^63 > Int64.max_value // Test with values around 32-bit boundaries (to ensure 64-bit handling) let val_32bit = 4294967296N // 2^32 - assert_eq(BigInt::compare_int64(val_32bit, 4294967296L), 0) - assert_eq(BigInt::compare_int64(val_32bit, 4294967295L), 1) - assert_eq(BigInt::compare_int64(val_32bit, 4294967297L), -1) + assert_eq(@bigint.BigInt::compare_int64(val_32bit, 4294967296L), 0) + assert_eq(@bigint.BigInt::compare_int64(val_32bit, 4294967295L), 1) + assert_eq(@bigint.BigInt::compare_int64(val_32bit, 4294967297L), -1) let neg_val_32bit = -4294967296N // -2^32 - assert_eq(BigInt::compare_int64(neg_val_32bit, -4294967296L), 0) - assert_eq(BigInt::compare_int64(neg_val_32bit, -4294967295L), -1) - assert_eq(BigInt::compare_int64(neg_val_32bit, -4294967297L), 1) + assert_eq(@bigint.BigInt::compare_int64(neg_val_32bit, -4294967296L), 0) + assert_eq(@bigint.BigInt::compare_int64(neg_val_32bit, -4294967295L), -1) + assert_eq(@bigint.BigInt::compare_int64(neg_val_32bit, -4294967297L), 1) } ///| -test "BigInt::hash" { +test "@bigint.BigInt::hash" { // Test zero inspect(0N.hash(), content="-813420232") assert_eq(0N.hash(), (-0N).hash()) @@ -996,7 +1039,7 @@ test "BigInt::hash" { // Test that equal BigInts have equal hashes let a = 987654321098765432N - let b = BigInt::from_string("00987654321098765432") + let b = @bigint.BigInt::from_string("00987654321098765432") inspect(a.hash(), content="-1950963429") assert_eq(a.hash(), b.hash()) @@ -1007,7 +1050,7 @@ test "BigInt::hash" { } ///| -test "BigInt::hash consistency" { +test "@bigint.BigInt::hash consistency" { // Test that hash is consistent across multiple calls let big = 999888777666555444333222111N let hash1 = big.hash() @@ -1026,6 +1069,6 @@ test "BigInt::hash consistency" { ///| test "default" { - let a = BigInt::default() + let a = @bigint.BigInt::default() assert_eq(a, 0N) } diff --git a/bundled-core/bigint/bigint_wbtest.mbt b/bundled-core/bigint/bigint_wbtest.mbt index bd75119..dd06178 100644 --- a/bundled-core/bigint/bigint_wbtest.mbt +++ b/bundled-core/bigint/bigint_wbtest.mbt @@ -648,9 +648,9 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x00" - , + ), ) // Test positive number @@ -658,9 +658,9 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x0f" - , + ), ) // Test positive number with leading zero @@ -668,9 +668,9 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x10" - , + ), ) // Test large positive number @@ -678,9 +678,9 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89" - , + ), ) // Check padding @@ -688,9 +688,9 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x10\x00\x00" - , + ), ) // Test very large positive number @@ -698,25 +698,25 @@ test "to_octets" { check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x11\xe3\x44\x4d\xc1\xf3\x5f\x05\x7a\xd2\xcb\xc2\x79\x17\x37\x46\x81\x40\xa4\x26\xfa\xc3\xcb\xa7\xaf\x8c\x92\xa8\xf3\x4e" - , + ), ) let a = 0x123456789012345678123456789012345678123456789012345678123456789012345678N check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x12\x34\x56\x78\x90\x12\x34\x56\x78\x12\x34\x56\x78\x90\x12\x34\x56\x78\x12\x34\x56\x78\x90\x12\x34\x56\x78" - , + ), ) let a = 0x805146F2F58580962A0A2E6134BC75E25AD97AE3D09CD34BA4F629AB8911F3F5CB8573A62EDD16B0D46775A415F545A75518DA3439914D9CAA26449067D85E704E8FCF9B29182485B41F952616BACDFDDF52B413B524D0EB743E8264A9C6AE32D12C3D20C5B81189060F4AC5D216713D503A69644EA8E4EA220A720C41F6B3D18BED65B4238318E6B0A41D8540D756865EC92DF40E8D365A230F17DF1D0A440BC6A557CD46D00B10D74C0E75500B2ADB3A0336223F9285B78CD04F485E417E1DB562B9EFCF79433209E6D6E2F43A484E471DE4F1F5AE38E08E7DAEB644C2C0A22697DD6D29BE0B40FF50DB575FEF02FA5525953C7C198B4A3600BA8CE1F917852A4B957151189F09DCDFCB79963E7D850127858A97855B94870ACCBE8203E73FE79791EE6EA1B1282A0CEAC54D6F6B7CD6C7B8D8092E949FF0A84N check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x08\x05\x14\x6f\x2f\x58\x58\x09\x62\xa0\xa2\xe6\x13\x4b\xc7\x5e\x25\xad\x97\xae\x3d\x09\xcd\x34\xba\x4f\x62\x9a\xb8\x91\x1f\x3f\x5c\xb8\x57\x3a\x62\xed\xd1\x6b\x0d\x46\x77\x5a\x41\x5f\x54\x5a\x75\x51\x8d\xa3\x43\x99\x14\xd9\xca\xa2\x64\x49\x06\x7d\x85\xe7\x04\xe8\xfc\xf9\xb2\x91\x82\x48\x5b\x41\xf9\x52\x61\x6b\xac\xdf\xdd\xf5\x2b\x41\x3b\x52\x4d\x0e\xb7\x43\xe8\x26\x4a\x9c\x6a\xe3\x2d\x12\xc3\xd2\x0c\x5b\x81\x18\x90\x60\xf4\xac\x5d\x21\x67\x13\xd5\x03\xa6\x96\x44\xea\x8e\x4e\xa2\x20\xa7\x20\xc4\x1f\x6b\x3d\x18\xbe\xd6\x5b\x42\x38\x31\x8e\x6b\x0a\x41\xd8\x54\x0d\x75\x68\x65\xec\x92\xdf\x40\xe8\xd3\x65\xa2\x30\xf1\x7d\xf1\xd0\xa4\x40\xbc\x6a\x55\x7c\xd4\x6d\x00\xb1\x0d\x74\xc0\xe7\x55\x00\xb2\xad\xb3\xa0\x33\x62\x23\xf9\x28\x5b\x78\xcd\x04\xf4\x85\xe4\x17\xe1\xdb\x56\x2b\x9e\xfc\xf7\x94\x33\x20\x9e\x6d\x6e\x2f\x43\xa4\x84\xe4\x71\xde\x4f\x1f\x5a\xe3\x8e\x08\xe7\xda\xeb\x64\x4c\x2c\x0a\x22\x69\x7d\xd6\xd2\x9b\xe0\xb4\x0f\xf5\x0d\xb5\x75\xfe\xf0\x2f\xa5\x52\x59\x53\xc7\xc1\x98\xb4\xa3\x60\x0b\xa8\xce\x1f\x91\x78\x52\xa4\xb9\x57\x15\x11\x89\xf0\x9d\xcd\xfc\xb7\x99\x63\xe7\xd8\x50\x12\x78\x58\xa9\x78\x55\xb9\x48\x70\xac\xcb\xe8\x20\x3e\x73\xfe\x79\x79\x1e\xe6\xea\x1b\x12\x82\xa0\xce\xac\x54\xd6\xf6\xb7\xcd\x6c\x7b\x8d\x80\x92\xe9\x49\xff\x0a\x84" - , + ), ) } @@ -726,24 +726,24 @@ test "to_octets with padding" { let a = zero inspect( a.to_octets(length=4), - content= + content=( #|b"\x00\x00\x00\x00" - , + ), ) // some random number let a = 0x10000N check_invariant(a) inspect( a.to_octets(), - content= + content=( #|b"\x01\x00\x00" - , + ), ) inspect( a.to_octets(length=4), - content= + content=( #|b"\x00\x01\x00\x00" - , + ), ) } diff --git a/bundled-core/bigint/panic_test.mbt b/bundled-core/bigint/panic_test.mbt new file mode 100644 index 0000000..96ac7f1 --- /dev/null +++ b/bundled-core/bigint/panic_test.mbt @@ -0,0 +1,44 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "panic division by zero" { + let a = @bigint.BigInt::from_int(10) + let b = @bigint.BigInt::from_int(0) + (a / b) |> ignore +} + +///| +test "panic modulo by zero" { + let a = @bigint.BigInt::from_int(10) + let b = @bigint.BigInt::from_int(0) + (a % b) |> ignore +} + +///| +test "panic negative shift left" { + let a = @bigint.BigInt::from_int(10) + (a << -1) |> ignore +} + +///| +test "panic negative shift right" { + let a = @bigint.BigInt::from_int(10) + (a >> -1) |> ignore +} + +///| +test "panic from_string empty" { + @bigint.BigInt::from_string("") |> ignore +} diff --git a/bundled-core/bigint/bigint.mbti b/bundled-core/bigint/pkg.generated.mbti similarity index 90% rename from bundled-core/bigint/bigint.mbti rename to bundled-core/bigint/pkg.generated.mbti index a1d11ef..40e383f 100644 --- a/bundled-core/bigint/bigint.mbti +++ b/bundled-core/bigint/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/bigint" import( @@ -6,6 +7,8 @@ import( // Values +// Errors + // Types and methods type BigInt #deprecated @@ -19,7 +22,7 @@ fn BigInt::equal_int64(Self, Int64) -> Bool fn BigInt::from_hex(String) -> Self fn BigInt::from_int(Int) -> Self fn BigInt::from_int64(Int64) -> Self -fn BigInt::from_octets(Bytes, signum~ : Int = ..) -> Self +fn BigInt::from_octets(Bytes, signum? : Int) -> Self fn BigInt::from_string(String) -> Self fn BigInt::from_uint(UInt) -> Self fn BigInt::from_uint64(UInt64) -> Self @@ -31,7 +34,7 @@ fn BigInt::pow(Self, Self, modulus? : Self) -> Self fn BigInt::shl(Self, Int) -> Self #deprecated fn BigInt::shr(Self, Int) -> Self -fn BigInt::to_hex(Self, uppercase~ : Bool = ..) -> String +fn BigInt::to_hex(Self, uppercase? : Bool) -> String fn BigInt::to_int(Self) -> Int fn BigInt::to_int16(Self) -> Int16 fn BigInt::to_int64(Self) -> Int64 diff --git a/bundled-core/bool/README.mbt.md b/bundled-core/bool/README.mbt.md index 106c405..19f5389 100644 --- a/bundled-core/bool/README.mbt.md +++ b/bundled-core/bool/README.mbt.md @@ -1,39 +1,121 @@ # `bool` -This package provides utility functions for working with boolean values, converting them to different numeric types. +This package provides utility functions for working with boolean values in MoonBit, primarily focused on type conversions that are useful in systems programming, bitwise operations, and numerical computations. -## Conversion to Integers +## Overview -The package allows converting boolean values to various integer types. `true` is converted to `1` and `false` to `0`. +Boolean values in MoonBit can be seamlessly converted to numeric types, following the standard convention where `true` maps to `1` and `false` maps to `0`. This is particularly useful for: + +- Conditional arithmetic and accumulation +- Interfacing with C libraries or low-level code +- Implementing boolean algebra with numeric operations +- Converting logical results to flags or indices + +## Basic Integer Conversion + +Convert boolean values to standard integers for arithmetic operations: ```moonbit test "bool to integer conversions" { - // Method syntax + // Basic conversions inspect(true.to_int(), content="1") inspect(false.to_int(), content="0") + + // Useful for conditional arithmetic + let score = 100 + let bonus_applied = true + let final_score = score + (bonus_applied.to_int() * 50) + inspect(final_score, content="150") + + // Accumulating boolean conditions + let conditions = [true, false, true, true, false] + let count = conditions.fold(init=0, fn(acc, cond) { acc + cond.to_int() }) + inspect(count, content="3") } ``` -## Different Integer Types +## Specialized Integer Types + +For specific use cases requiring different integer widths and signedness: + +```moonbit +test "bool to specialized integer types" { + let flag = true + let no_flag = false + + // UInt - useful for bit manipulation and flags + inspect(flag.to_uint(), content="1") + inspect(no_flag.to_uint(), content="0") + + // Int64 - for large computations and compatibility + inspect(flag.to_int64(), content="1") + inspect(no_flag.to_int64(), content="0") + + // UInt64 - for unsigned 64-bit operations + inspect(flag.to_uint64(), content="1") + inspect(no_flag.to_uint64(), content="0") +} +``` -Besides regular `Int`, the package supports conversion to other integer types: +## Practical Use Cases -- `UInt` for 32-bit unsigned integers -- `Int64` for 64-bit signed integers -- `UInt64` for 64-bit unsigned integers +### Boolean Indexing and Selection ```moonbit -test "bool to other integer types" { - // UInt - inspect(true.to_uint(), content="1") - inspect(false.to_uint(), content="0") - - // Int64 - inspect(true.to_int64(), content="1") - inspect(false.to_int64(), content="0") - - // UInt64 - inspect(true.to_uint64(), content="1") - inspect(false.to_uint64(), content="0") +test "boolean indexing" { + // Use boolean conversion for array indexing + let options = ["default", "enhanced"] + let use_enhanced = true + let selected = options[use_enhanced.to_int()] + inspect(selected, content="enhanced") + + // Conditional selection without branching + let base_value = 10 + let multiplier = 2 + let apply_multiplier = false + let result = base_value * (1 + apply_multiplier.to_int() * (multiplier - 1)) + inspect(result, content="10") // 10 * (1 + 0 * 1) = 10 } ``` + +### Bit Manipulation and Flags + +```moonbit +test "flags and bit operations" { + // Convert booleans to create bit flags + let read_permission = true + let write_permission = false + let execute_permission = true + + let permissions = (read_permission.to_uint() << 2) | + (write_permission.to_uint() << 1) | + execute_permission.to_uint() + inspect(permissions, content="5") // Binary: 101 (read + execute) +} +``` + +### Statistical and Mathematical Operations + +```moonbit +test "statistical operations" { + // Calculate success rate from boolean results + let test_results = [true, true, false, true, false, true, true] + let successes = test_results.fold(init=0, fn(acc, result) { acc + result.to_int() }) + let total = test_results.length() + let success_rate = successes.to_double() / total.to_double() + inspect(success_rate > 0.7, content="true") + + // Boolean to numeric conversion for weighted calculations + let feature_enabled = [true, false, true] + let weights = [0.6, 0.3, 0.1] + + // Calculate weighted score manually to avoid zip complexity + let score1 = feature_enabled[0].to_int().to_double() * weights[0] + let score2 = feature_enabled[1].to_int().to_double() * weights[1] + let score3 = feature_enabled[2].to_int().to_double() * weights[2] + let weighted_score = score1 + score2 + score3 + inspect(weighted_score == 0.7, content="true") +} +``` + +This package provides the essential bridge between MoonBit's boolean logic and numeric computations, enabling elegant solutions for conditional arithmetic, flag operations, and data processing workflows. diff --git a/bundled-core/bool/bool_test.mbt b/bundled-core/bool/bool_test.mbt index dca2d13..9f47396 100644 --- a/bundled-core/bool/bool_test.mbt +++ b/bundled-core/bool/bool_test.mbt @@ -14,8 +14,8 @@ ///| test "Bool hash function" { - assert_eq(true.hash(), 1) - assert_eq(false.hash(), 0) + inspect(true.hash(), content="1") + inspect(false.hash(), content="0") } ///| diff --git a/bundled-core/bool/bool.mbti b/bundled-core/bool/pkg.generated.mbti similarity index 84% rename from bundled-core/bool/bool.mbti rename to bundled-core/bool/pkg.generated.mbti index ee0fc14..76bac34 100644 --- a/bundled-core/bool/bool.mbti +++ b/bundled-core/bool/pkg.generated.mbti @@ -1,7 +1,10 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/bool" // Values +// Errors + // Types and methods fn Bool::to_int(Bool) -> Int fn Bool::to_int16(Bool) -> Int16 diff --git a/bundled-core/buffer/README.mbt.md b/bundled-core/buffer/README.mbt.md index ac8db35..4a565d6 100644 --- a/bundled-core/buffer/README.mbt.md +++ b/bundled-core/buffer/README.mbt.md @@ -21,9 +21,9 @@ test "basic buffer operations" { let bytes = buf.contents() inspect( bytes, - content= + content=( #|b"\x48\x69" - , + ), ) // Reset buffer @@ -44,9 +44,9 @@ test "number serialization" { ..write_int_be(42) ..write_int_le(42) .to_bytes(), - content= + content=( #|b"\x00\x00\x00\x2a\x2a\x00\x00\x00" - , + ), ) inspect( @buffer.new() @@ -54,9 +54,9 @@ test "number serialization" { ..write_float_be(3.14) ..write_float_le(3.14) .to_bytes(), - content= + content=( #|b"\x40\x48\xf5\xc3\xc3\xf5\x48\x40" - , + ), ) inspect( @buffer.new() @@ -64,9 +64,9 @@ test "number serialization" { ..write_int64_be(0xAABBCCDDEEL) ..write_int64_le(0xAABBCCDDEEL) .to_bytes(), - content= + content=( #|b"\x00\x00\x00\xaa\xbb\xcc\xdd\xee\xee\xdd\xcc\xbb\xaa\x00\x00\x00" - , + ), ) inspect( @buffer.new() @@ -74,9 +74,9 @@ test "number serialization" { ..write_uint_be(0x2077U) ..write_uint_le(0x2077U) .to_bytes(), - content= + content=( #|b"\x00\x00\x20\x77\x77\x20\x00\x00" - , + ), ) } ``` @@ -98,9 +98,9 @@ test "byte sequence writing" { let contents = buf.to_bytes() inspect( contents, - content= + content=( #|b"\x48\x65\x6c\x6c\x6f\x48\x65\x6c\x6c\x6f" - , + ), ) // "Hello" written twice } ``` @@ -120,9 +120,9 @@ test "object writing" { let contents = buf.contents() inspect( contents, - content= + content=( #|b"\x34\x00\x32\x00" - , + ), ) } ``` @@ -160,9 +160,9 @@ test "buffer as logger" { let contents = buf.contents() inspect( contents, - content= + content=( #|b"\x5b\x00\x31\x00\x2c\x00\x20\x00\x32\x00\x2c\x00\x20\x00\x33\x00\x5d\x00" - , + ), ) } ``` @@ -180,9 +180,9 @@ test "buffer conversion" { let bytes = buf.to_bytes() inspect( bytes, - content= + content=( #|b"\x61\x62\x63" - , + ), ) } ``` @@ -201,9 +201,9 @@ test "byte view writing" { let contents = buf.to_bytes() inspect( contents, - content= + content=( #|b"\x48\x65\x6c\x6c\x6f" - , + ), ) } ``` diff --git a/bundled-core/buffer/buffer.mbt b/bundled-core/buffer/buffer.mbt index 5b9d824..14140a1 100644 --- a/bundled-core/buffer/buffer.mbt +++ b/bundled-core/buffer/buffer.mbt @@ -26,11 +26,14 @@ /// let buf = @buffer.new(size_hint=100) /// buf.write_string("Tes") /// buf.write_char('t') -/// inspect(buf.contents(), content= -/// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" +/// inspect( +/// buf.contents(), +/// content=( +/// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" +/// ), /// ) /// ``` -struct T { +struct Buffer { mut data : FixedArray[Byte] mut len : Int initial_data : FixedArray[Byte] @@ -38,7 +41,7 @@ struct T { ///| /// Expand the buffer size if capacity smaller than required space. -fn grow_if_necessary(self : T, required : Int) -> Unit { +fn grow_if_necessary(self : Buffer, required : Int) -> Unit { let start = if self.data.length() <= 0 { 1 } else { self.data.length() } let enough_space = for space = start { if space >= required { @@ -69,7 +72,7 @@ fn grow_if_necessary(self : T, required : Int) -> Unit { /// buf.write_string("Test") /// inspect(buf.length(), content="8") // each char takes 2 bytes in UTF-16 /// ``` -pub fn length(self : T) -> Int { +pub fn length(self : Buffer) -> Int { self.len } @@ -91,7 +94,7 @@ pub fn length(self : T) -> Int { /// buf.write_string("test") /// inspect(buf.is_empty(), content="false") /// ``` -pub fn is_empty(self : T) -> Bool { +pub fn is_empty(self : Buffer) -> Bool { self.len == 0 } @@ -111,12 +114,12 @@ pub fn is_empty(self : T) -> Bool { /// buf.write_string("Test") /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" -/// , +/// ), /// ) /// ``` -pub fn contents(self : T) -> Bytes { +pub fn contents(self : Buffer) -> Bytes { @bytes.from_fixedarray(self.data, len=self.len) } @@ -129,7 +132,7 @@ pub fn contents(self : T) -> Bytes { /// /// * `size_hint` : Initial capacity of the buffer in bytes. Defaults to 0. /// -/// Returns a new buffer of type `T`. +/// Returns a new buffer of type `Buffer`. /// /// Example: /// @@ -139,14 +142,15 @@ pub fn contents(self : T) -> Bytes { /// buf.write_string("test") /// inspect(buf.length(), content="8") /// ``` -pub fn new(size_hint~ : Int = 0) -> T { +pub fn new(size_hint? : Int = 0) -> Buffer { let initial = if size_hint < 1 { 1 } else { size_hint } let data = FixedArray::make(initial, Byte::default()) { data, len: 0, initial_data: data } } -///| Create a buffer from a bytes. -pub fn from_bytes(bytes : Bytes) -> T { +///| +/// Create a buffer from a bytes. +pub fn from_bytes(bytes : Bytes) -> Buffer { let val_len = bytes.length() let buf = new(size_hint=val_len) // inline write_bytes, skip grow_if_necessary check @@ -158,7 +162,7 @@ pub fn from_bytes(bytes : Bytes) -> T { ///| /// Create a buffer from an array. -pub fn from_array(arr : Array[Byte]) -> T { +pub fn from_array(arr : Array[Byte]) -> Buffer { let buf = new(size_hint=arr.length()) for byte in arr { // inline write_byte, skip grow_if_necessary check @@ -171,7 +175,7 @@ pub fn from_array(arr : Array[Byte]) -> T { ///| /// Create a buffer from an iterator. -pub fn from_iter(iter : Iter[Byte]) -> T { +pub fn from_iter(iter : Iter[Byte]) -> Buffer { let buf = new() let mut capacity = buf.data.length() for byte in iter { @@ -208,12 +212,12 @@ pub fn from_iter(iter : Iter[Byte]) -> T { /// // 't' -> [0x74, 0x00] /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" -/// , +/// ), /// ) /// ``` -pub impl Logger for T with write_string(self, value) { +pub impl Logger for Buffer with write_string(self, value) { self.grow_if_necessary(self.len + value.length() * 2) self.data.blit_from_string(self.len, value, 0, value.length()) self.len += value.length() * 2 @@ -236,12 +240,12 @@ pub impl Logger for T with write_string(self, value) { /// // Bytes are written in big-endian order /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\xaa\xbb\xcc\xdd\x11\x22\x33\x44" -/// , +/// ), /// ) /// ``` -pub fn write_uint64_be(self : T, value : UInt64) -> Unit { +pub fn write_uint64_be(self : Buffer, value : UInt64) -> Unit { self.write_byte((value >> 56).to_byte()) self.write_byte((value >> 48).to_byte()) self.write_byte((value >> 40).to_byte()) @@ -268,12 +272,12 @@ pub fn write_uint64_be(self : T, value : UInt64) -> Unit { /// buf.write_uint64_le(0x0123456789ABCDEF) /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\xef\xcd\xab\x89\x67\x45\x23\x01" -/// , +/// ), /// ) /// ``` -pub fn write_uint64_le(self : T, value : UInt64) -> Unit { +pub fn write_uint64_le(self : Buffer, value : UInt64) -> Unit { self.write_byte(value.to_byte()) self.write_byte((value >> 8).to_byte()) self.write_byte((value >> 16).to_byte()) @@ -300,12 +304,12 @@ pub fn write_uint64_le(self : T, value : UInt64) -> Unit { /// buf.write_int64_be(0x0102030405060708L) /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x01\x02\x03\x04\x05\x06\x07\x08" -/// , +/// ), /// ) /// ``` -pub fn write_int64_be(self : T, value : Int64) -> Unit { +pub fn write_int64_be(self : Buffer, value : Int64) -> Unit { self.write_uint64_be(value.reinterpret_as_uint64()) } @@ -322,11 +326,14 @@ pub fn write_int64_be(self : T, value : Int64) -> Unit { /// ```moonbit /// let buf = @buffer.new() /// buf.write_int64_le(-1L) -/// inspect(buf.contents(), content= -/// #|b"\xff\xff\xff\xff\xff\xff\xff\xff" -/// ) +/// inspect( +/// buf.contents(), +/// content=( +/// #|b"\xff\xff\xff\xff\xff\xff\xff\xff" +/// ), +/// ) /// ``` -pub fn write_int64_le(self : T, value : Int64) -> Unit { +pub fn write_int64_le(self : Buffer, value : Int64) -> Unit { self.write_uint64_le(value.reinterpret_as_uint64()) } @@ -346,7 +353,7 @@ pub fn write_int64_le(self : T, value : Int64) -> Unit { /// buf.write_uint_be(0x12345678) /// inspect(buf.contents(), content="b\"\\x12\\x34\\x56\\x78\"") /// ``` -pub fn write_uint_be(self : T, value : UInt) -> Unit { +pub fn write_uint_be(self : Buffer, value : UInt) -> Unit { self.write_byte((value >> 24).to_byte()) self.write_byte((value >> 16).to_byte()) self.write_byte((value >> 8).to_byte()) @@ -370,7 +377,7 @@ pub fn write_uint_be(self : T, value : UInt) -> Unit { /// buf.write_uint_le(0x12345678) /// inspect(buf.contents(), content="b\"\\x78\\x56\\x34\\x12\"") /// ``` -pub fn write_uint_le(self : T, value : UInt) -> Unit { +pub fn write_uint_le(self : Buffer, value : UInt) -> Unit { self.write_byte(value.to_byte()) self.write_byte((value >> 8).to_byte()) self.write_byte((value >> 16).to_byte()) @@ -393,7 +400,7 @@ pub fn write_uint_le(self : T, value : UInt) -> Unit { /// buf.write_int_be(0x12345678) /// inspect(buf.contents(), content="b\"\\x12\\x34\\x56\\x78\"") /// ``` -pub fn write_int_be(self : T, value : Int) -> Unit { +pub fn write_int_be(self : Buffer, value : Int) -> Unit { self.write_uint_be(value.reinterpret_as_uint()) } @@ -414,7 +421,7 @@ pub fn write_int_be(self : T, value : Int) -> Unit { /// buf.write_int_le(-1) /// inspect(buf.contents(), content="b\"\\xff\\xff\\xff\\xff\"") /// ``` -pub fn write_int_le(self : T, value : Int) -> Unit { +pub fn write_int_le(self : Buffer, value : Int) -> Unit { self.write_uint_le(value.reinterpret_as_uint()) } @@ -434,7 +441,7 @@ pub fn write_int_le(self : T, value : Int) -> Unit { /// buf.write_double_be(1.0) /// inspect(buf.contents(), content="b\"\\x3f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"") /// ``` -pub fn write_double_be(self : T, value : Double) -> Unit { +pub fn write_double_be(self : Buffer, value : Double) -> Unit { self.write_uint64_be(value.reinterpret_as_uint64()) } @@ -457,7 +464,7 @@ pub fn write_double_be(self : T, value : Double) -> Unit { /// content="b\"\\x1f\\x85\\xeb\\x51\\xb8\\x1e\\x09\\x40\"", /// ) /// ``` -pub fn write_double_le(self : T, value : Double) -> Unit { +pub fn write_double_le(self : Buffer, value : Double) -> Unit { self.write_uint64_le(value.reinterpret_as_uint64()) } @@ -479,7 +486,7 @@ pub fn write_double_le(self : T, value : Double) -> Unit { /// // In big-endian format, 3.14 is represented as [0x40, 0x48, 0xF5, 0xC3] /// inspect(buf.contents(), content="b\"\\x40\\x48\\xf5\\xc3\"") /// ``` -pub fn write_float_be(self : T, value : Float) -> Unit { +pub fn write_float_be(self : Buffer, value : Float) -> Unit { self.write_uint_be(value.reinterpret_as_uint()) } @@ -500,7 +507,7 @@ pub fn write_float_be(self : T, value : Float) -> Unit { /// // The bytes are written in little-endian format /// inspect(buf.contents(), content="b\"\\xc3\\xf5\\x48\\x40\"") /// ``` -pub fn write_float_le(self : T, value : Float) -> Unit { +pub fn write_float_le(self : Buffer, value : Float) -> Unit { self.write_uint_le(value.reinterpret_as_uint()) } @@ -522,7 +529,7 @@ pub fn write_float_le(self : T, value : Float) -> Unit { /// buf.write_object(42) /// inspect(buf.contents().to_unchecked_string(), content="42") /// ``` -pub fn write_object(self : T, value : &Show) -> Unit { +pub fn write_object(self : Buffer, value : &Show) -> Unit { self.write_string(value.to_string()) } @@ -541,12 +548,12 @@ pub fn write_object(self : T, value : &Show) -> Unit { /// buf.write_bytes(b"Test") /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x54\x65\x73\x74" -/// , +/// ), /// ) /// ``` -pub fn write_bytes(self : T, value : Bytes) -> Unit { +pub fn write_bytes(self : Buffer, value : Bytes) -> Unit { let val_len = value.length() self.grow_if_necessary(self.len + val_len) self.data.blit_from_bytes(self.len, value, 0, val_len) @@ -569,12 +576,12 @@ pub fn write_bytes(self : T, value : Bytes) -> Unit { /// buf.write_bytesview(view) /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x65\x73" -/// , +/// ), /// ) /// ``` -pub fn write_bytesview(self : T, value : @bytes.View) -> Unit { +pub fn write_bytesview(self : Buffer, value : @bytes.View) -> Unit { let val_len = value.length() self.grow_if_necessary(self.len + val_len) self.data.blit_from_bytes( @@ -587,8 +594,121 @@ pub fn write_bytesview(self : T, value : @bytes.View) -> Unit { } ///| -/// Writes a portion of a string into the buffer in UTF-16LE encoding. -/// +/// Write a char into buffer as UTF8. +pub fn Buffer::write_char_utf8(buf : Self, value : Char) -> Unit { + let code = value.to_uint() + match code { + _..<0x80 => { + buf.grow_if_necessary(buf.len + 1) + buf.data[buf.len] = ((code & 0x7F) | 0x00).to_byte() + buf.len += 1 + } + _..<0x0800 => { + buf.grow_if_necessary(buf.len + 2) + buf.data[buf.len] = (((code >> 6) & 0x1F) | 0xC0).to_byte() + buf.data[buf.len + 1] = ((code & 0x3F) | 0x80).to_byte() + buf.len += 2 + } + _..<0x010000 => { + buf.grow_if_necessary(buf.len + 3) + buf.data[buf.len] = (((code >> 12) & 0x0F) | 0xE0).to_byte() + buf.data[buf.len + 1] = (((code >> 6) & 0x3F) | 0x80).to_byte() + buf.data[buf.len + 2] = ((code & 0x3F) | 0x80).to_byte() + buf.len += 3 + } + _..<0x110000 => { + buf.grow_if_necessary(buf.len + 4) + buf.data[buf.len] = (((code >> 18) & 0x07) | 0xF0).to_byte() + buf.data[buf.len + 1] = (((code >> 12) & 0x3F) | 0x80).to_byte() + buf.data[buf.len + 2] = (((code >> 6) & 0x3F) | 0x80).to_byte() + buf.data[buf.len + 3] = ((code & 0x3F) | 0x80).to_byte() + buf.len += 4 + } + _ => abort("Char out of range") + } +} + +///| +/// Write a char into buffer as UTF16LE. +pub fn Buffer::write_char_utf16le(buf : Self, value : Char) -> Unit { + let code = value.to_uint() + if code < 0x10000 { + buf.grow_if_necessary(buf.len + 2) + buf.data[buf.len + 0] = (code & 0xFF).to_byte() + buf.data[buf.len + 1] = (code >> 8).to_byte() + buf.len += 2 + } else if code < 0x110000 { + let hi = code - 0x10000 + let lo = (hi >> 10) | 0xD800 + let hi = (hi & 0x3FF) | 0xDC00 + buf.grow_if_necessary(buf.len + 4) + buf.data[buf.len + 0] = (lo & 0xFF).to_byte() + buf.data[buf.len + 1] = (lo >> 8).to_byte() + buf.data[buf.len + 2] = (hi & 0xFF).to_byte() + buf.data[buf.len + 3] = (hi >> 8).to_byte() + buf.len += 4 + } else { + abort("Char out of range") + } +} + +///| +/// Write a char into buffer as UTF16BE. +pub fn Buffer::write_char_utf16be(buf : Self, value : Char) -> Unit { + let code = value.to_uint() + if code < 0x10000 { + buf.grow_if_necessary(buf.len + 2) + buf.data[buf.len + 0] = (code >> 8).to_byte() + buf.data[buf.len + 1] = (code & 0xFF).to_byte() + buf.len += 2 + } else if code < 0x110000 { + buf.grow_if_necessary(buf.len + 4) + let hi = code - 0x10000 + let lo = (hi >> 10) | 0xD800 + let hi = (hi & 0x3FF) | 0xDC00 + buf.data[buf.len + 0] = (lo >> 8).to_byte() + buf.data[buf.len + 1] = (lo & 0xFF).to_byte() + buf.data[buf.len + 2] = (hi >> 8).to_byte() + buf.data[buf.len + 3] = (hi & 0xFF).to_byte() + buf.len += 4 + } else { + abort("Char out of range") + } +} + +///| +pub fn Buffer::write_string_utf8(buf : Self, string : @string.View) -> Unit { + for ch in string { + buf.write_char_utf8(ch) + } +} + +///| +#alias(write_stringview, deprecated="use write_string_utf16le instead") +pub fn Buffer::write_string_utf16le(buf : Self, string : @string.View) -> Unit { + let len = string.length() + buf.grow_if_necessary(buf.len + len * 2) + for i = 0, j = buf.len; i < len; i = i + 1, j = j + 2 { + let c = string.unsafe_charcode_at(i).reinterpret_as_uint() + buf.data[j] = (c & 0xff).to_byte() + buf.data[j + 1] = (c >> 8).to_byte() + } + buf.len += len * 2 +} + +///| +pub fn Buffer::write_string_utf16be(buf : Self, string : @string.View) -> Unit { + let len = string.length() + buf.grow_if_necessary(buf.len + len * 2) + for i = 0, j = buf.len; i < len; i = i + 1, j = j + 2 { + let c = string.unsafe_charcode_at(i).reinterpret_as_uint() + buf.data[j + 1] = (c & 0xff).to_byte() + buf.data[j] = (c >> 8).to_byte() + } + buf.len += len * 2 +} + +///| /// Parameters: /// /// * `self` : The buffer to write to. @@ -605,16 +725,16 @@ pub fn write_bytesview(self : T, value : @bytes.View) -> Unit { /// buf.write_substring("Hello, World!", 0, 5) /// inspect( /// buf.contents(), -/// content= +/// content=( /// #|b"\x48\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00" -/// , +/// ), /// ) /// ``` -pub impl Logger for T with write_substring( - self : T, +pub impl Logger for Buffer with write_substring( + self : Buffer, value : String, start : Int, - len : Int + len : Int, ) -> Unit { guard start >= 0 && len >= 0 && start + len <= value.length() self.grow_if_necessary(self.len + len * 2) @@ -622,18 +742,6 @@ pub impl Logger for T with write_substring( self.len += len * 2 } -///| -pub fn write_stringview(self : T, value : @string.View) -> Unit { - let len = value.length() - self.grow_if_necessary(self.len + len * 2) - for i = 0, j = self.len; i < len; i = i + 1, j = j + 2 { - let c = value.unsafe_charcode_at(i).reinterpret_as_uint() - self.data[j] = (c & 0xff).to_byte() - self.data[j + 1] = (c >> 8).to_byte() - } - self.len += len * 2 -} - ///| /// Writes a UTF-16LE encoded character into the buffer. Automatically grows the /// buffer if necessary. @@ -648,11 +756,14 @@ pub fn write_stringview(self : T, value : @string.View) -> Unit { /// ```moonbit /// let buf = @buffer.new() /// buf.write_char('A') -/// inspect(buf.contents(), content= -/// #|b"\x41\x00" -/// ) +/// inspect( +/// buf.contents(), +/// content=( +/// #|b"\x41\x00" +/// ), +/// ) /// ``` -pub impl Logger for T with write_char(self : T, value : Char) -> Unit { +pub impl Logger for Buffer with write_char(self : Buffer, value : Char) -> Unit { self.grow_if_necessary(self.len + 4) let inc = self.data.set_utf16le_char(self.len, value) self.len += inc @@ -674,7 +785,7 @@ pub impl Logger for T with write_char(self : T, value : Char) -> Unit { /// buf.write_byte(b'\x41') /// inspect(buf.contents(), content="b\"\\x41\"") /// ``` -pub fn write_byte(self : T, value : Byte) -> Unit { +pub fn write_byte(self : Buffer, value : Byte) -> Unit { self.grow_if_necessary(self.len + 1) self.data[self.len] = value self.len += 1 @@ -694,11 +805,14 @@ pub fn write_byte(self : T, value : Byte) -> Unit { /// let buf = @buffer.new() /// let bytes = b"Hello" /// buf.write_iter(bytes.iter()) -/// inspect(buf.contents(), content= -/// #|b"\x48\x65\x6c\x6c\x6f" -/// ) +/// inspect( +/// buf.contents(), +/// content=( +/// #|b"\x48\x65\x6c\x6c\x6f" +/// ), +/// ) /// ``` -pub fn write_iter(self : T, iter : Iter[Byte]) -> Unit { +pub fn write_iter(self : Buffer, iter : Iter[Byte]) -> Unit { for byte in iter { self.write_byte(byte) } @@ -721,7 +835,7 @@ pub fn write_iter(self : T, iter : Iter[Byte]) -> Unit { /// inspect(buf.length(), content="0") /// inspect(buf.is_empty(), content="true") /// ``` -pub fn reset(self : T) -> Unit { +pub fn reset(self : Buffer) -> Unit { self.data = self.initial_data self.len = 0 } @@ -744,11 +858,11 @@ pub fn reset(self : T) -> Unit { /// let bytes = buf.to_bytes() /// inspect(bytes.length(), content="8") /// ``` -pub fn to_bytes(self : T) -> Bytes { +pub fn to_bytes(self : Buffer) -> Bytes { @bytes.from_fixedarray(self.data, len=self.len) } ///| -pub impl Show for T with output(self, logger) { +pub impl Show for Buffer with output(self, logger) { logger.write_string(self.contents().to_unchecked_string()) } diff --git a/bundled-core/buffer/buffer.mbti b/bundled-core/buffer/buffer.mbti deleted file mode 100644 index 6da552a..0000000 --- a/bundled-core/buffer/buffer.mbti +++ /dev/null @@ -1,49 +0,0 @@ -package "moonbitlang/core/buffer" - -import( - "moonbitlang/core/bytes" - "moonbitlang/core/string" -) - -// Values -fn from_array(Array[Byte]) -> T - -fn from_bytes(Bytes) -> T - -fn from_iter(Iter[Byte]) -> T - -fn new(size_hint~ : Int = ..) -> T - -// Types and methods -type T -fn T::contents(Self) -> Bytes -fn T::is_empty(Self) -> Bool -fn T::length(Self) -> Int -fn T::reset(Self) -> Unit -fn T::to_bytes(Self) -> Bytes -fn T::write_byte(Self, Byte) -> Unit -fn T::write_bytes(Self, Bytes) -> Unit -fn T::write_bytesview(Self, @bytes.View) -> Unit -fn T::write_double_be(Self, Double) -> Unit -fn T::write_double_le(Self, Double) -> Unit -fn T::write_float_be(Self, Float) -> Unit -fn T::write_float_le(Self, Float) -> Unit -fn T::write_int64_be(Self, Int64) -> Unit -fn T::write_int64_le(Self, Int64) -> Unit -fn T::write_int_be(Self, Int) -> Unit -fn T::write_int_le(Self, Int) -> Unit -fn T::write_iter(Self, Iter[Byte]) -> Unit -fn T::write_object(Self, &Show) -> Unit -fn T::write_stringview(Self, @string.StringView) -> Unit -fn T::write_uint64_be(Self, UInt64) -> Unit -fn T::write_uint64_le(Self, UInt64) -> Unit -fn T::write_uint_be(Self, UInt) -> Unit -fn T::write_uint_le(Self, UInt) -> Unit -impl Logger for T -impl Show for T - -// Type aliases -pub typealias T as Buffer - -// Traits - diff --git a/bundled-core/buffer/buffer_test.mbt b/bundled-core/buffer/buffer_test.mbt index 28f5b78..287722a 100644 --- a/bundled-core/buffer/buffer_test.mbt +++ b/bundled-core/buffer/buffer_test.mbt @@ -62,98 +62,6 @@ test "expect method with matching content" { inspect(buf, content="Test") } -///| -test "expect method with non-matching content" { - let buf = @buffer.new(size_hint=100) - buf.write_string("Test") - inspect(buf, content="Test") -} - -///| -test "grow_if_necessary method" { - let buf = @buffer.new(size_hint=10) - buf.write_string( - "This is a test string that is longer than the initial capacity", - ) - assert_true(buf.to_bytes().length() >= 60) -} - -///| -test "write_substring method" { - let buf = @buffer.new(size_hint=10) - buf.write_substring("Hello, World!", 7, 5) - inspect(buf, content="World") -} - -///| -test "write_byte method" { - let buf = @buffer.new(size_hint=10) - buf.write_byte(b'A') - buf.write_byte(b'\x00') - inspect(buf, content="A") -} - -///| -test "to_bytes method" { - let buf = @buffer.new(size_hint=10) - buf.write_string("Test") - let bytes = buf.to_bytes() - assert_eq(bytes.length(), 8) // Each character in "Test" is 2 bytes -} - -///| -test "expect method with non-matching content" { - let buf = @buffer.new(size_hint=100) - buf.write_string("Test") - inspect(buf, content="Test") -} - -///| -test "grow_if_necessary method" { - let buf = @buffer.new(size_hint=10) - buf.write_string( - "This is a test string that is longer than the initial capacity", - ) - assert_true(buf.to_bytes().length() >= 60) -} - -///| -test "write_substring method" { - let buf = @buffer.new(size_hint=10) - buf.write_substring("Hello, World!", 7, 5) - inspect(buf, content="World") -} - -///| -test "write_byte method" { - let buf = @buffer.new(size_hint=10) - buf.write_byte(b'A') - buf.write_byte(b'\x00') - inspect(buf, content="A") -} - -///| -test "to_bytes method" { - let buf = @buffer.new(size_hint=10) - buf.write_string("Test") - let bytes = buf.to_bytes() - assert_eq(bytes.length(), 8) // Each character in "Test" is 2 bytes -} - -///| -test "expect method with matching content" { - let buf = @buffer.new(size_hint=100) - buf.write_string("Test") - inspect(buf, content="Test") -} - -///| -test "expect method with non-matching content" { - let buf = @buffer.new(size_hint=100) - buf.write_string("Test") - inspect(buf, content="Test") -} - ///| test "grow_if_necessary method" { let buf = @buffer.new(size_hint=10) @@ -183,7 +91,7 @@ test "to_bytes method" { let buf = @buffer.new(size_hint=10) buf.write_string("Test") let bytes = buf.to_bytes() - assert_eq(bytes.length(), 8) // Each character in "Test" is 2 bytes + inspect(bytes.length(), content="8") // Each character in "Test" is 2 bytes } ///| @@ -201,9 +109,9 @@ test "write_uint64_le method" { buf.write_uint64_le(0xfacefeedaabbccdd) inspect( buf.to_bytes(), - content= + content=( #|b"\xdd\xcc\xbb\xaa\xef\xbe\xad\xde\xdd\xcc\xbb\xaa\xed\xfe\xce\xfa" - , + ), ) } @@ -214,9 +122,9 @@ test "write_uint64_be method" { buf.write_uint64_be(0xfacefeedaabbccdd) inspect( buf.to_bytes(), - content= + content=( #|b"\xde\xad\xbe\xef\xaa\xbb\xcc\xdd\xfa\xce\xfe\xed\xaa\xbb\xcc\xdd" - , + ), ) } @@ -227,9 +135,9 @@ test "write_int64_le method" { buf.write_int64_le(0xdeadbeeffacefeed) inspect( buf.to_bytes(), - content= + content=( #|b"\xfe\xff\xff\xff\xff\xff\xff\xff\xed\xfe\xce\xfa\xef\xbe\xad\xde" - , + ), ) } @@ -240,9 +148,9 @@ test "write_int64_be method" { buf.write_int64_be(0xdeadbeef) inspect( buf.to_bytes(), - content= + content=( #|b"\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\xde\xad\xbe\xef" - , + ), ) } @@ -253,9 +161,9 @@ test "write_uint_le method" { buf.write_uint_le(0xdeadc0de) inspect( buf.to_bytes(), - content= + content=( #|b"\xef\xbe\xad\xde\xde\xc0\xad\xde" - , + ), ) } @@ -266,9 +174,9 @@ test "write_uint_be method" { buf.write_uint_be(0xdeadc0de) inspect( buf.to_bytes(), - content= + content=( #|b"\xde\xad\xbe\xef\xde\xad\xc0\xde" - , + ), ) } @@ -279,9 +187,9 @@ test "write_int_le method" { buf.write_int_le(0xdeadbeef) inspect( buf.to_bytes(), - content= + content=( #|b"\xfe\xff\xff\xff\xef\xbe\xad\xde" - , + ), ) } @@ -292,9 +200,9 @@ test "write_int_be method" { buf.write_int_be(0xdeadbeef) inspect( buf.to_bytes(), - content= + content=( #|b"\xff\xff\xff\xfe\xde\xad\xbe\xef" - , + ), ) } @@ -305,9 +213,9 @@ test "write_double_le method" { buf.write_double_le(3.14) inspect( buf.to_bytes(), - content= + content=( #|b"\x00\x00\x00\x00\x00\x00\x00\xc0\x1f\x85\xeb\x51\xb8\x1e\x09\x40" - , + ), ) } @@ -318,9 +226,9 @@ test "write_double_be method" { buf.write_double_be(3.14) inspect( buf.to_bytes(), - content= + content=( #|b"\xc0\x00\x00\x00\x00\x00\x00\x00\x40\x09\x1e\xb8\x51\xeb\x85\x1f" - , + ), ) } @@ -331,9 +239,9 @@ test "write_float_le method" { buf.write_float_le(3.14) inspect( buf.to_bytes(), - content= + content=( #|b"\x00\x00\x00\xc0\xc3\xf5\x48\x40" - , + ), ) } @@ -344,9 +252,9 @@ test "write_float_be method" { buf.write_float_be(3.14) inspect( buf.to_bytes(), - content= + content=( #|b"\xc0\x00\x00\x00\x40\x48\xf5\xc3" - , + ), ) } @@ -357,17 +265,17 @@ test "write_iter" { buf.write_iter(bytes.iter()) inspect( buf.contents(), - content= + content=( #|b"\x68\x65\x6c\x6c\x6f" - , + ), ) let bytes_view = bytes[1:3] buf.write_iter(bytes_view.iter()) inspect( buf.contents(), - content= + content=( #|b"\x68\x65\x6c\x6c\x6f\x65\x6c" - , + ), ) } @@ -377,20 +285,119 @@ test "write_bytesview" { buf.write_bytesview(b"Test"[1:3]) inspect( buf.contents(), - content= + content=( #|b"\x65\x73" - , + ), ) } ///| test "write_stringview" { let buf = @buffer.new() - buf.write_stringview("hello".view(start_offset=1, end_offset=3)) + buf.write_string_utf16le("hello"[1:3]) inspect( buf.contents(), - content= + content=( #|b"\x65\x00\x6c\x00" - , + ), + ) +} + +///| +test "write_char_utf8" { + let buf = @buffer.new(size_hint=10) + buf.write_char_utf8('A') + buf.write_char_utf8('ฮฑ') + buf.write_char_utf8('ๅ•Š') + buf.write_char_utf8('๐Ÿ˜ฆ') + inspect( + buf.to_bytes(), + content=( + #|b"\x41\xce\xb1\xe5\x95\x8a\xf0\x9f\x98\xa6" + ), + ) +} + +///| +test "write_char" { + let buf = @buffer.new(size_hint=10) + buf.write_char('A') + buf.write_char('ฮฑ') + buf.write_char('ๅ•Š') + buf.write_char('๐Ÿ˜ฆ') + inspect( + buf.to_bytes(), + content=( + #|b"\x41\x00\xb1\x03\x4a\x55\x3d\xd8\x26\xde" + ), + ) +} + +///| +test "write_char_utf16le" { + let buf = @buffer.new(size_hint=10) + buf.write_char_utf16le('A') + buf.write_char_utf16le('ฮฑ') + buf.write_char_utf16le('ๅ•Š') + buf.write_char_utf16le('๐Ÿ˜ฆ') + inspect( + buf.to_bytes(), + content=( + #|b"\x41\x00\xb1\x03\x4a\x55\x3d\xd8\x26\xde" + ), + ) +} + +///| +test "write_char_utf16be" { + let buf = @buffer.new(size_hint=10) + buf.write_char_utf16be('A') + buf.write_char_utf16be('ฮฑ') + buf.write_char_utf16be('ๅ•Š') + buf.write_char_utf16be('๐Ÿ˜ฆ') + inspect( + buf.to_bytes(), + content=( + #|b"\x00\x41\x03\xb1\x55\x4a\xd8\x3d\xde\x26" + ), + ) +} + +///| +const BOM : Char = '\u{FEFF}' + +///| +test "write_bom_utf8" { + let buf = @buffer.new() + buf.write_char_utf8(BOM) + inspect( + buf.to_bytes(), + content=( + #|b"\xef\xbb\xbf" + ), + ) +} + +///| +test "write_bom_utf16le" { + let buf = @buffer.new() + buf.write_char_utf16le(BOM) + inspect( + buf.to_bytes(), + content=( + #|b"\xff\xfe" + ), + ) +} + +///| +test "write_bom_utf16be" { + let buf = @buffer.new() + buf.write_char_utf16be(BOM) + inspect( + buf.to_bytes(), + content=( + #|b"\xfe\xff" + ), ) } diff --git a/bundled-core/buffer/deprecated.mbt b/bundled-core/buffer/deprecated.mbt index 1b614dc..7db0caf 100644 --- a/bundled-core/buffer/deprecated.mbt +++ b/bundled-core/buffer/deprecated.mbt @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| Extensible buffer. -#deprecated("Use type `T` instead") -pub typealias T as Buffer +///| +/// Extensible buffer. +#deprecated("Use type `Buffer` instead") +pub typealias Buffer as T diff --git a/bundled-core/buffer/moon.pkg.json b/bundled-core/buffer/moon.pkg.json index 44d76d0..6493ad7 100644 --- a/bundled-core/buffer/moon.pkg.json +++ b/bundled-core/buffer/moon.pkg.json @@ -4,5 +4,6 @@ "moonbitlang/core/bytes", "moonbitlang/core/array", "moonbitlang/core/string" - ] + ], + "test-import": ["moonbitlang/core/int"] } diff --git a/bundled-core/buffer/pkg.generated.mbti b/bundled-core/buffer/pkg.generated.mbti new file mode 100644 index 0000000..b97ae7e --- /dev/null +++ b/bundled-core/buffer/pkg.generated.mbti @@ -0,0 +1,64 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/buffer" + +import( + "moonbitlang/core/bytes" + "moonbitlang/core/string" +) + +// Values +fn from_array(Array[Byte]) -> Buffer + +fn from_bytes(Bytes) -> Buffer + +fn from_iter(Iter[Byte]) -> Buffer + +fn new(size_hint? : Int) -> Buffer + +// Errors + +// Types and methods +type Buffer +fn Buffer::contents(Self) -> Bytes +fn Buffer::is_empty(Self) -> Bool +fn Buffer::length(Self) -> Int +fn Buffer::reset(Self) -> Unit +fn Buffer::to_bytes(Self) -> Bytes +fn Buffer::write_byte(Self, Byte) -> Unit +fn Buffer::write_bytes(Self, Bytes) -> Unit +fn Buffer::write_bytesview(Self, @bytes.View) -> Unit +fn Buffer::write_char_utf16be(Self, Char) -> Unit +fn Buffer::write_char_utf16le(Self, Char) -> Unit +fn Buffer::write_char_utf8(Self, Char) -> Unit +fn Buffer::write_double_be(Self, Double) -> Unit +fn Buffer::write_double_le(Self, Double) -> Unit +fn Buffer::write_float_be(Self, Float) -> Unit +fn Buffer::write_float_le(Self, Float) -> Unit +fn Buffer::write_int64_be(Self, Int64) -> Unit +fn Buffer::write_int64_le(Self, Int64) -> Unit +fn Buffer::write_int_be(Self, Int) -> Unit +fn Buffer::write_int_le(Self, Int) -> Unit +fn Buffer::write_iter(Self, Iter[Byte]) -> Unit +fn[A : Leb128] Buffer::write_leb128(Self, A) -> Unit +fn Buffer::write_object(Self, &Show) -> Unit +fn Buffer::write_string_utf16be(Self, @string.View) -> Unit +#alias(write_stringview, deprecated) +fn Buffer::write_string_utf16le(Self, @string.View) -> Unit +fn Buffer::write_string_utf8(Self, @string.View) -> Unit +fn Buffer::write_uint64_be(Self, UInt64) -> Unit +fn Buffer::write_uint64_le(Self, UInt64) -> Unit +fn Buffer::write_uint_be(Self, UInt) -> Unit +fn Buffer::write_uint_le(Self, UInt) -> Unit +impl Logger for Buffer +impl Show for Buffer + +// Type aliases +pub typealias Buffer as T + +// Traits +trait Leb128 +impl Leb128 for Int +impl Leb128 for Int64 +impl Leb128 for UInt +impl Leb128 for UInt64 + diff --git a/bundled-core/buffer/sleb128.mbt b/bundled-core/buffer/sleb128.mbt new file mode 100644 index 0000000..7cf021f --- /dev/null +++ b/bundled-core/buffer/sleb128.mbt @@ -0,0 +1,65 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +trait Leb128 { + output(Self, Buffer) -> Unit +} + +///| +pub impl Leb128 for Int with output(self, buffer) { + for value = self { + let byte = value & 0x7f // Get the low 7 bits + let next_value = value >> 7 // Arithmetic right shift + let sign_bit_set = (byte & 0x40) != 0 + let need_more = if value >= 0 { + next_value != 0 + } else { + next_value != -1 || !sign_bit_set + } + if need_more { + buffer.write_byte((byte | 0x80).to_byte()) + continue next_value + } else { + buffer.write_byte(byte.to_byte()) + break + } + } +} + +///| +pub impl Leb128 for Int64 with output(self, buffer) { + for value = self { + let byte = value & 0x7f // Get the low 7 bits + let next_value = value >> 7 // Arithmetic right shift + let sign_bit_set = (byte & 0x40) != 0 + let need_more = if value >= 0 { + next_value != 0 + } else { + next_value != -1 || !sign_bit_set + } + if need_more { + buffer.write_byte((byte | 0x80).to_byte()) + continue next_value + } else { + buffer.write_byte(byte.to_byte()) + break + } + } +} + +///| +pub fn[A : Leb128] Buffer::write_leb128(buffer : Buffer, value : A) -> Unit { + value.output(buffer) +} diff --git a/bundled-core/buffer/sleb128_test.mbt b/bundled-core/buffer/sleb128_test.mbt new file mode 100644 index 0000000..86f4873 --- /dev/null +++ b/bundled-core/buffer/sleb128_test.mbt @@ -0,0 +1,260 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +fn[A : Leb128] @buffer.Buffer::test_leb128(self : Self, x : A) -> Unit { + self.reset() + self.write_leb128(x) +} + +///| +test "write_leb128 Int" { + let buffer = @buffer.new() + buffer.test_leb128(0) + inspect( + buffer.to_bytes(), + content=( + #|b"\x00" + ), + ) + buffer.test_leb128(-1) + inspect( + buffer.to_bytes(), + content=( + #|b"\x7f" + ), + ) + buffer.test_leb128(-64) + inspect( + buffer.to_bytes(), + content=( + #|b"\x40" + ), + ) + buffer.test_leb128(@int.min_value) + inspect( + buffer.to_bytes(), + content=( + #|b"\x80\x80\x80\x80\x78" + ), + ) + buffer.test_leb128(128) + assert_eq(buffer.to_bytes(), b"\x80\x01") + buffer.test_leb128(129) + assert_eq(buffer.to_bytes(), b"\x81\x01") + buffer.test_leb128(16383) + assert_eq(buffer.to_bytes(), b"\xff\x7f") + buffer.test_leb128(16384) + assert_eq(buffer.to_bytes(), b"\x80\x80\x01") + buffer.test_leb128(2097151) + assert_eq(buffer.to_bytes(), b"\xff\xff\x7f") +} + +///| +test "write_leb128 Int64" { + let buffer = @buffer.new() + buffer.write_leb128(0L) + assert_eq(buffer.to_bytes(), b"\x00") + buffer.reset() + buffer.write_leb128(127L) + assert_eq(buffer.to_bytes(), b"\x7f") + buffer.reset() + buffer.write_leb128(128L) + assert_eq(buffer.to_bytes(), b"\x80\x01") + buffer.reset() + buffer.write_leb128(129L) + assert_eq(buffer.to_bytes(), b"\x81\x01") + buffer.reset() + buffer.write_leb128(16383L) + assert_eq(buffer.to_bytes(), b"\xff\x7f") + buffer.reset() + buffer.write_leb128(16384L) + assert_eq(buffer.to_bytes(), b"\x80\x80\x01") + buffer.reset() + buffer.write_leb128(2097151L) + assert_eq(buffer.to_bytes(), b"\xff\xff\x7f") +} + +///| +test "write_leb128 UInt" { + let buffer = @buffer.new() + // Test zero + buffer.write_leb128(0U) + assert_eq(buffer.to_bytes(), b"\x00") + buffer.reset() + + // Test single byte values (0-127) + buffer.write_leb128(1U) + assert_eq(buffer.to_bytes(), b"\x01") + buffer.reset() + buffer.write_leb128(127U) + assert_eq(buffer.to_bytes(), b"\x7f") + buffer.reset() + + // Test two byte values (128-16383) + buffer.write_leb128(128U) + assert_eq(buffer.to_bytes(), b"\x80\x01") + buffer.reset() + buffer.write_leb128(129U) + assert_eq(buffer.to_bytes(), b"\x81\x01") + buffer.reset() + buffer.write_leb128(16383U) + assert_eq(buffer.to_bytes(), b"\xff\x7f") + buffer.reset() + + // Test three byte values (16384-2097151) + buffer.write_leb128(16384U) + assert_eq(buffer.to_bytes(), b"\x80\x80\x01") + buffer.reset() + buffer.write_leb128(2097151U) + assert_eq(buffer.to_bytes(), b"\xff\xff\x7f") + buffer.reset() + + // Test four byte values (2097152-268435455) + buffer.write_leb128(2097152U) + assert_eq(buffer.to_bytes(), b"\x80\x80\x80\x01") + buffer.reset() + buffer.write_leb128(268435455U) + assert_eq(buffer.to_bytes(), b"\xff\xff\xff\x7f") + buffer.reset() + + // Test five byte values (268435456+) + buffer.write_leb128(268435456U) + assert_eq(buffer.to_bytes(), b"\x80\x80\x80\x80\x01") + buffer.reset() +} + +///| +test "write_leb128_edge_cases" { + let buffer = @buffer.new() + + // Test boundary values for each byte length + buffer.write_leb128(0U) + assert_eq(buffer.to_bytes(), b"\x00") + buffer.reset() + buffer.write_leb128(0x7fU) // 127 (max 1 byte) + assert_eq(buffer.to_bytes(), b"\x7f") + buffer.reset() + buffer.write_leb128(0x80U) // 128 (min 2 byte) + assert_eq(buffer.to_bytes(), b"\x80\x01") + buffer.reset() + buffer.write_leb128(0x3fffU) // 16383 (max 2 byte) + assert_eq(buffer.to_bytes(), b"\xff\x7f") + buffer.reset() + buffer.write_leb128(0x4000U) // 16384 (min 3 byte) + assert_eq(buffer.to_bytes(), b"\x80\x80\x01") + buffer.reset() + buffer.write_leb128(0x1fffffU) // 2097151 (max 3 byte) + assert_eq(buffer.to_bytes(), b"\xff\xff\x7f") + buffer.reset() + buffer.write_leb128(0x200000U) // 2097152 (min 4 byte) + assert_eq(buffer.to_bytes(), b"\x80\x80\x80\x01") + buffer.reset() + buffer.write_leb128(0xfffffffU) // 268435455 (max 4 byte) + assert_eq(buffer.to_bytes(), b"\xff\xff\xff\x7f") + buffer.reset() + buffer.write_leb128(0x10000000U) // 268435456 (min 5 byte) + assert_eq(buffer.to_bytes(), b"\x80\x80\x80\x80\x01") + buffer.reset() +} + +///| +test "write_leb128_consecutive_writes" { + let buffer = @buffer.new() + + // Test writing multiple values consecutively + buffer.write_leb128(1U) + buffer.write_leb128(127U) + buffer.write_leb128(128U) + buffer.write_leb128(16384U) + let expected = b"\x01\x7f\x80\x01\x80\x80\x01" + assert_eq(buffer.to_bytes(), expected) +} + +///| +test "write_leb128_negative_values" { + let buffer = @buffer.new() + + // Test negative values for SLEB128 + buffer.write_leb128(-1) + assert_eq(buffer.to_bytes(), b"\x7f") + buffer.reset() + buffer.write_leb128(-64) + assert_eq(buffer.to_bytes(), b"\x40") + buffer.reset() + buffer.write_leb128(-65) + assert_eq(buffer.to_bytes(), b"\xbf\x7f") + buffer.reset() + buffer.write_leb128(-128) + assert_eq(buffer.to_bytes(), b"\x80\x7f") + buffer.reset() + buffer.write_leb128(-129) + assert_eq(buffer.to_bytes(), b"\xff\x7e") + buffer.reset() +} + +///| +test "write_leb128_negative_Int64" { + let buffer = @buffer.new() + + // Test negative Int64 values for SLEB128 + buffer.write_leb128(-1L) + assert_eq(buffer.to_bytes(), b"\x7f") + buffer.reset() + buffer.write_leb128(-64L) + assert_eq(buffer.to_bytes(), b"\x40") + buffer.reset() + buffer.write_leb128(-65L) + assert_eq(buffer.to_bytes(), b"\xbf\x7f") + buffer.reset() + buffer.write_leb128(-128L) + assert_eq(buffer.to_bytes(), b"\x80\x7f") + buffer.reset() + buffer.write_leb128(-129L) + assert_eq(buffer.to_bytes(), b"\xff\x7e") + buffer.reset() + + // Test large negative values + buffer.write_leb128(-9223372036854775808L) // Int64 minimum + assert_eq(buffer.to_bytes(), b"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f") + buffer.reset() +} + +// Concrete counterexample: -65 +// โ€ข First step: +// โ€ข b = (-65 & 0x7F) = 0x3F โ†’ signBit = 0 +// โ€ข v >>= 7 โ†’ -1 +// โ€ข If you stopped here using only v == -1, youโ€™d emit just [0x3F]. +// โ€ข Decoder sees final byte with signBit = 0 โ†’ zero-extends โ†’ decodes to +63, not -65 โŒ +// โ€ข Correct behavior: because signBit == 0, you must continue: +// โ€ข Emit 0x3F | 0x80 = 0xBF, then next step yields final byte 0x7F +// โ€ข Correct encoding: [0xBF, 0x7F] โœ… + +// When v == -1 is enough + +// For values whose current 7-bit chunk already has signBit == 1, e.g. -1 .. -64, stopping is correct: +// โ€ข -1 โ†’ b=0x7F (signBit=1), v>>=7 โ†’ -1 โ†’ stop โ†’ [0x7F] +// โ€ข -2 โ†’ b=0x7E (signBit=1) โ€ฆ โ†’ stop โ†’ [0x7E] +// โ€ข -64 โ†’ b=0x40 (signBit=1) โ€ฆ โ†’ stop โ†’ [0x40] + +// TL;DR +// โ€ข Need both: stop when (v == -1 && signBit == 1) for negatives. +// โ€ข This ensures the final byte encodes the sign, so the decoder sign-extends and you get the correct (and minimal) result. + +///| +test { + let buffer = @buffer.new() + buffer.write_leb128(-65) + assert_eq(buffer.to_bytes(), b"\xbf\x7f") +} diff --git a/bundled-core/buffer/uleb128.mbt b/bundled-core/buffer/uleb128.mbt new file mode 100644 index 0000000..37fab5e --- /dev/null +++ b/bundled-core/buffer/uleb128.mbt @@ -0,0 +1,43 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +pub impl Leb128 for UInt with output(self, buffer) { + for value = self { + if value < 128 { + // Single byte: no continuation bit (0xxxxxxx) + buffer.write_byte(value.to_byte()) + break + } else { + // Multiple bytes: set continuation bit (1xxxxxxx) and continue + buffer.write_byte(((value % 128) | 128).to_byte()) + continue value / 128 + } + } +} + +///| +pub impl Leb128 for UInt64 with output(self, buffer) { + for value = self { + if value < 128 { + // Single byte: no continuation bit (0xxxxxxx) + buffer.write_byte(value.to_byte()) + break + } else { + // Multiple bytes: set continuation bit (1xxxxxxx) and continue + buffer.write_byte(((value % 128) | 128).to_byte()) + continue value / 128 + } + } +} diff --git a/bundled-core/builtin/LinkedHashMap.mbt.md b/bundled-core/builtin/LinkedHashMap.mbt.md index 254b837..0f113ae 100644 --- a/bundled-core/builtin/LinkedHashMap.mbt.md +++ b/bundled-core/builtin/LinkedHashMap.mbt.md @@ -45,9 +45,9 @@ test { map.remove("a") inspect( map, - content= + content=( #|{"b": 2, "c": 3} - , + ), ) } ``` @@ -72,7 +72,7 @@ You can use `size()` to get the number of key-value pairs in the map, or `capaci test { let map = { "a": 1, "b": 2, "c": 3 } inspect(map.size(), content="3") - inspect(map.capacity(), content="8") + inspect(map.capacity(), content="4") } ``` diff --git a/bundled-core/builtin/README.mbt.md b/bundled-core/builtin/README.mbt.md new file mode 100644 index 0000000..ac95254 --- /dev/null +++ b/bundled-core/builtin/README.mbt.md @@ -0,0 +1,305 @@ +# Builtin Package Documentation + +This package provides the core built-in types, functions, and utilities that are fundamental to MoonBit programming. It includes basic data structures, iterators, assertions, and core language features. + +## Core Types and Functions + +### Assertions and Testing + +MoonBit provides built-in assertion functions for testing: + +```moonbit +test "assertions" { + // Basic equality assertion + assert_eq(1 + 1, 2) + assert_eq("hello", "hello") + + // Boolean assertions + assert_true(5 > 3) + assert_false(2 > 5) + + // Inequality assertion + assert_not_eq(1, 2) + assert_not_eq("foo", "bar") +} +``` + +### Inspect Function + +The `inspect` function is used for testing and debugging: + +```moonbit +test "inspect usage" { + let value = 42 + inspect(value, content="42") + + let list = [1, 2, 3] + inspect(list, content="[1, 2, 3]") + + let result : Result[Int, String] = Ok(100) + inspect(result, content="Ok(100)") +} +``` + +## Result Type + +The `Result[T, E]` type represents operations that can succeed or fail: + +```moonbit +test "result type" { + fn divide(a : Int, b : Int) -> Result[Int, String] { + if b == 0 { + Err("Division by zero") + } else { + Ok(a / b) + } + } + + // Success case + let result1 = divide(10, 2) + inspect(result1, content="Ok(5)") + + // Error case + let result2 = divide(10, 0) + inspect(result2, content="Err(\"Division by zero\")") + + // Pattern matching on Result + match result1 { + Ok(value) => inspect(value, content="5") + Err(_) => inspect(false, content="true") + } +} +``` + +## Option Type + +The `Option[T]` type represents values that may or may not exist: + +```moonbit +test "option type" { + fn find_first_even(numbers : Array[Int]) -> Option[Int] { + for num in numbers { + if num % 2 == 0 { + return Some(num) + } + } + None + } + + // Found case + let result1 = find_first_even([1, 3, 4, 5]) + inspect(result1, content="Some(4)") + + // Not found case + let result2 = find_first_even([1, 3, 5]) + inspect(result2, content="None") + + // Pattern matching on Option + match result1 { + Some(value) => inspect(value, content="4") + None => inspect(false, content="true") + } +} +``` + +## Iterator Type + +The `Iter[T]` type provides lazy iteration over sequences: + +```moonbit +test "iterators" { + // Create iterator from array + let numbers = [1, 2, 3, 4, 5] + let iter = numbers.iter() + + // Collect back to array + let collected = iter.collect() + inspect(collected, content="[1, 2, 3, 4, 5]") + + // Map transformation + let doubled = numbers.iter().map(fn(x) { x * 2 }).collect() + inspect(doubled, content="[2, 4, 6, 8, 10]") + + // Filter elements + let evens = numbers.iter().filter(fn(x) { x % 2 == 0 }).collect() + inspect(evens, content="[2, 4]") + + // Fold (reduce) operation + let sum = numbers.iter().fold(init=0, fn(acc, x) { acc + x }) + inspect(sum, content="15") +} +``` + +## Array and FixedArray + +Built-in array types for storing collections: + +```moonbit +test "arrays" { + // Dynamic arrays + let arr = Array::new() + arr.push(1) + arr.push(2) + arr.push(3) + inspect(arr, content="[1, 2, 3]") + + // Array from literal + let fixed_arr = [10, 20, 30] + inspect(fixed_arr, content="[10, 20, 30]") + + // Array operations + let length = fixed_arr.length() + inspect(length, content="3") + + let first = fixed_arr[0] + inspect(first, content="10") +} +``` + +## String Operations + +Basic string functionality: + +```moonbit +test "strings" { + let text = "Hello, World!" + + // String length + let len = text.length() + inspect(len, content="13") + + // String concatenation + let greeting = "Hello" + ", " + "World!" + inspect(greeting, content="Hello, World!") + + // String comparison + let equal = "test" == "test" + inspect(equal, content="true") +} +``` + +## StringBuilder + +Efficient string building: + +```moonbit +test "string builder" { + let builder = StringBuilder::new() + builder.write_string("Hello") + builder.write_string(", ") + builder.write_string("World!") + + let result = builder.to_string() + inspect(result, content="Hello, World!") +} +``` + +## JSON Support + +Basic JSON operations: + +```moonbit +test "json" { + // JSON values + let json_null = null + inspect(json_null, content="Null") + + let json_bool = true.to_json() + inspect(json_bool, content="True") + + let json_number = (42 : Int).to_json() + inspect(json_number, content="Number(42)") + + let json_string = "hello".to_json() + inspect(json_string, content=( + #|String("hello") + )) +} +``` + +## Comparison Operations + +Built-in comparison operators: + +```moonbit +test "comparisons" { + // Equality + inspect(5 == 5, content="true") + inspect(5 != 3, content="true") + + // Ordering + inspect(3 < 5, content="true") + inspect(5 > 3, content="true") + inspect(5 >= 5, content="true") + inspect(3 <= 5, content="true") + + // String comparison + inspect("apple" < "banana", content="true") + inspect("hello" == "hello", content="true") +} +``` + +## Utility Functions + +Helpful utility functions: + +```moonbit +test "utilities" { + // Identity and ignore + let value = 42 + ignore(value) // Discards the value + + // Boolean negation + let result = not(false) + inspect(result, content="true") + + // Physical equality (reference equality) + let arr1 = [1, 2, 3] + let arr2 = [1, 2, 3] + let same_ref = arr1 + + inspect(physical_equal(arr1, arr2), content="false") // Different objects + inspect(physical_equal(arr1, same_ref), content="true") // Same reference +} +``` + +## Error Handling + +Basic error handling with panic and abort: + +```moonbit +test "error handling" { + // This would panic in a real scenario, but we demonstrate the concept + fn safe_divide(a : Int, b : Int) -> Int { + if b == 0 { + // In real code: panic() + // For testing, we return a default value + 0 + } else { + a / b + } + } + + let result = safe_divide(10, 2) + inspect(result, content="5") + + let safe_result = safe_divide(10, 0) + inspect(safe_result, content="0") +} +``` + +## Best Practices + +1. **Use assertions liberally in tests**: They help catch bugs early and document expected behavior +2. **Prefer `Result` over exceptions**: For recoverable errors, use `Result[T, E]` instead of panicking +3. **Use `Option` for nullable values**: Instead of null pointers, use `Option[T]` +4. **Leverage iterators for data processing**: They provide composable and efficient data transformations +5. **Use `StringBuilder` for string concatenation**: More efficient than repeated string concatenation +6. **Pattern match on `Result` and `Option`**: Handle both success and failure cases explicitly + +## Performance Notes + +- Arrays have O(1) access and O(1) amortized append +- Iterators are lazy and don't allocate intermediate collections +- StringBuilder is more efficient than string concatenation for building large strings +- Physical equality is faster than structural equality but should be used carefully diff --git a/bundled-core/builtin/array.mbt b/bundled-core/builtin/array.mbt index be82d0c..06d61c9 100644 --- a/bundled-core/builtin/array.mbt +++ b/bundled-core/builtin/array.mbt @@ -58,14 +58,14 @@ pub fn[T] Array::from_fixed_array(arr : FixedArray[T]) -> Array[T] { /// let arr = Array::make(3, 42) /// inspect(arr, content="[42, 42, 42]") /// ``` -/// +/// /// WARNING: A common pitfall is creating with the same initial value, for example: /// ```moonbit /// let two_dimension_array = Array::make(10, Array::make(10, 0)) /// two_dimension_array[0][5] = 10 /// assert_eq(two_dimension_array[5][5], 10) /// ``` -/// This is because all the cells reference to the same object (the Array[Int] in this case). +/// This is because all the cells reference to the same object (the Array[Int] in this case). /// One should use makei() instead which creates an object for each index. pub fn[T] Array::make(len : Int, elem : T) -> Array[T] { let arr = Array::make_uninit(len) @@ -75,6 +75,43 @@ pub fn[T] Array::make(len : Int, elem : T) -> Array[T] { arr } +///| +/// Creates a new array of the specified length, where each element is +/// initialized using an index-based initialization function. +/// +/// Parameters: +/// +/// * `length` : The length of the new array. If `length` is less than or equal +/// to 0, returns an empty array. +/// * `initializer` : A function that takes an index (starting from 0) and +/// returns a value of type `T`. This function is called for each index to +/// initialize the corresponding element. +/// +/// Returns a new array of type `Array[T]` with the specified length, where each +/// element is initialized using the provided function. +/// +/// Example: +/// +/// ```moonbit +/// let arr = Array::makei(3, i => i * 2) +/// inspect(arr, content="[0, 2, 4]") +/// ``` +#locals(value) +pub fn[T] Array::makei( + length : Int, + value : (Int) -> T raise?, +) -> Array[T] raise? { + if length <= 0 { + [] + } else { + let array = Array::make_uninit(length) + for i in 0.. Array[T] { /// NOTE: The capacity of an array may not be consistent across different backends /// and/or different versions of the MoonBit compiler/core. pub fn[T] Array::capacity(self : Array[T]) -> Int { - self.buffer().inner().length() + self.buffer().0.length() } ///| @@ -220,7 +257,7 @@ pub fn[T] Array::op_set(self : Array[T], index : Int, value : T) -> Unit { /// inspect(arr1 == arr2, content="true") /// inspect(arr1 == arr3, content="false") /// ``` -pub impl[T : Eq] Eq for Array[T] with op_equal(self, other) { +pub impl[T : Eq] Eq for Array[T] with equal(self, other) { let self_len = self.length() let other_len = other.length() guard self_len == other_len else { return false } @@ -239,7 +276,7 @@ pub impl[T : Hash] Hash for Array[T] with hash_combine(self, hasher) { } ///| -/// Compares two arrays lexicographically. +/// Compares two arrays based on shortlex order. /// /// First compares the lengths of the arrays. If they differ, returns -1 if the /// first array is shorter, 1 if it's longer. If the lengths are equal, compares @@ -299,7 +336,7 @@ pub impl[T : Compare] Compare for Array[T] with compare(self, other) { /// let b = [4, 5] /// inspect(a + b, content="[1, 2, 3, 4, 5]") /// ``` -pub impl[T] Add for Array[T] with op_add(self, other) { +pub impl[T] Add for Array[T] with add(self, other) { let result = Array::make_uninit(self.length() + other.length()) UninitializedArray::unsafe_blit( result.buffer(), @@ -414,7 +451,7 @@ pub fn[T] Array::rev_each(self : Array[T], f : (T) -> Unit) -> Unit { #locals(f) pub fn[T] Array::rev_eachi( self : Array[T], - f : (Int, T) -> Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { let len = self.length() for i in 0.. Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { for i, v in self { f(i, v) @@ -469,7 +506,7 @@ pub fn[T] Array::clear(self : Array[T]) -> Unit { #locals(f) pub fn[T, U] Array::map( self : Array[T], - f : (T) -> U raise? + f : (T) -> U raise?, ) -> Array[U] raise? { let arr = Array::make_uninit(self.length()) for i, v in self { @@ -490,7 +527,7 @@ pub fn[T, U] Array::map( #locals(f) pub fn[T] Array::map_inplace( self : Array[T], - f : (T) -> T raise? + f : (T) -> T raise?, ) -> Unit raise? { for i, v in self { self[i] = f(v) @@ -509,7 +546,7 @@ pub fn[T] Array::map_inplace( #locals(f) pub fn[T, U] Array::mapi( self : Array[T], - f : (Int, T) -> U raise? + f : (Int, T) -> U raise?, ) -> Array[U] raise? { if self.length() == 0 { return [] @@ -533,7 +570,7 @@ pub fn[T, U] Array::mapi( #locals(f) pub fn[T] Array::mapi_inplace( self : Array[T], - f : (Int, T) -> T raise? + f : (Int, T) -> T raise?, ) -> Unit raise? { for i, v in self { self[i] = f(i, v) @@ -563,7 +600,7 @@ pub fn[T] Array::mapi_inplace( #locals(f) pub fn[T] Array::filter( self : Array[T], - f : (T) -> Bool raise? + f : (T) -> Bool raise?, ) -> Array[T] raise? { let arr = [] for v in self { @@ -845,7 +882,7 @@ pub fn[T : Eq] Array::ends_with(self : Array[T], suffix : Array[T]) -> Bool { /// ``` pub fn[T : Eq] Array::strip_prefix( self : Array[T], - prefix : Array[T] + prefix : Array[T], ) -> Array[T]? { if self.starts_with(prefix) { let v = Array::make_uninit(self.length() - prefix.length()) @@ -875,7 +912,7 @@ pub fn[T : Eq] Array::strip_prefix( /// ``` pub fn[T : Eq] Array::strip_suffix( self : Array[T], - suffix : Array[T] + suffix : Array[T], ) -> Array[T]? { if self.ends_with(suffix) { let v = Array::make_uninit(self.length() - suffix.length()) @@ -916,7 +953,8 @@ pub fn[T : Eq] Array::search(self : Array[T], value : T) -> Int? { } } -///| Search the index of the first element that satisfies the predicate. +///| +/// Search the index of the first element that satisfies the predicate. /// /// # Example /// @@ -928,6 +966,7 @@ pub fn[T : Eq] Array::search(self : Array[T], value : T) -> Int? { /// } /// ``` #locals(f) +#alias(find_index, deprecated) pub fn[T] Array::search_by(self : Array[T], f : (T) -> Bool) -> Int? { for i, v in self { if f(v) { @@ -963,7 +1002,7 @@ pub fn[T] Array::search_by(self : Array[T], f : (T) -> Bool) -> Int? { /// - If the array is not sorted, the returned result is undefined and should not be relied on. pub fn[T : Compare] Array::binary_search( self : Array[T], - value : T + value : T, ) -> Result[Int, Int] { let len = self.length() for i = 0, j = len; i < j; { @@ -1010,23 +1049,11 @@ pub fn[T : Compare] Array::binary_search( /// ```moonbit /// let arr = [1, 3, 5, 7, 9] /// let find_3 = arr.binary_search_by((x) => { -/// if x < 3 { -/// -1 -/// } else if x > 3 { -/// 1 -/// } else { -/// 0 -/// } +/// x.compare(3) /// }) /// inspect(find_3, content="Ok(1)") /// let find_4 = arr.binary_search_by((x) => { -/// if x < 4 { -/// -1 -/// } else if x > 4 { -/// 1 -/// } else { -/// 0 -/// } +/// x.compare(4) /// }) /// inspect(find_4, content="Err(2)") /// ``` @@ -1041,7 +1068,7 @@ pub fn[T : Compare] Array::binary_search( #locals(cmp) pub fn[T] Array::binary_search_by( self : Array[T], - cmp : (T) -> Int + cmp : (T) -> Int, ) -> Result[Int, Int] { let len = self.length() for i = 0, j = len; i < j; { @@ -1225,10 +1252,11 @@ pub fn[T] Array::repeat(self : Array[T], times : Int) -> Array[T] { /// assert_eq(sum, 15) /// ``` #locals(f) +#alias(fold_left, deprecated) pub fn[A, B] Array::fold( self : Array[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(acc, self[i]) @@ -1247,10 +1275,11 @@ pub fn[A, B] Array::fold( /// assert_eq(sum, 15) /// ``` #locals(f) +#alias(fold_right, deprecated) pub fn[A, B] Array::rev_fold( self : Array[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { for i = self.length() - 1, acc = init; i >= 0; { continue i - 1, f(acc, self[i]) @@ -1269,10 +1298,11 @@ pub fn[A, B] Array::rev_fold( /// assert_eq(sum, 10) /// ``` #locals(f) +#alias(fold_lefti, deprecated) pub fn[A, B] Array::foldi( self : Array[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { for i = 0, acc = init; i < self.length(); { continue i + 1, f(i, acc, self[i]) @@ -1291,10 +1321,11 @@ pub fn[A, B] Array::foldi( /// assert_eq(sum, 10) /// ``` #locals(f) +#alias(fold_righti, deprecated) pub fn[A, B] Array::rev_foldi( self : Array[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { let len = self.length() for i = len - 1, acc = init; i >= 0; { @@ -1391,7 +1422,8 @@ pub fn[T] Array::extract_if(self : Array[T], f : (T) -> Bool) -> Array[T] { /// Parameters: /// /// * `array` : The array to be divided into chunks. -/// * `size` : The size of each chunk. Must be a positive integer. +/// * `size` : The size of each chunk. Must be a positive integer, otherwise it will panic. +/// /// /// Returns an array of arrays, where each inner array is a chunk containing /// elements from the original array. If the length of the original array is not @@ -1408,6 +1440,7 @@ pub fn[T] Array::extract_if(self : Array[T], f : (T) -> Bool) -> Array[T] { /// inspect(arr.chunks(3), content="[]") /// ``` pub fn[T] Array::chunks(self : Array[T], size : Int) -> Array[Array[T]] { + guard size > 0 let chunks = [] let mut i = 0 while i < self.length() { @@ -1447,7 +1480,7 @@ pub fn[T] Array::chunks(self : Array[T], size : Int) -> Array[Array[T]] { #locals(pred) pub fn[T] Array::chunk_by( self : Array[T], - pred : (T, T) -> Bool raise? + pred : (T, T) -> Bool raise?, ) -> Array[Array[T]] raise? { let chunks = [] let mut i = 0 @@ -1470,7 +1503,7 @@ pub fn[T] Array::chunk_by( /// Parameters: /// /// * `array` : The array to be processed with sliding windows. -/// * `size` : The window length. Must be a positive integer. +/// * `size` : The window length. Must be a positive integer, otherwise it will panic. /// /// Returns an array of slices, where each inner slice is a contiguous subslice /// of the original array. Windows are produced with a step size of 1. If the @@ -1488,6 +1521,7 @@ pub fn[T] Array::chunk_by( /// inspect(arr.windows(3), content="[]") /// ``` pub fn[T] Array::windows(self : Array[T], size : Int) -> Array[ArrayView[T]] { + guard size > 0 let len = self.length() - size + 1 if len < 1 { return [] @@ -1526,13 +1560,13 @@ pub fn[T] Array::windows(self : Array[T], size : Int) -> Array[ArrayView[T]] { #locals(pred) pub fn[T] Array::split( self : Array[T], - pred : (T) -> Bool raise? + pred : (T) -> Bool raise?, ) -> Array[Array[T]] raise? { let chunks = [] let mut i = 0 while i < self.length() { let chunk = [] - while i < self.length() && not(pred(self[i])) { + while i < self.length() && !pred(self[i]) { chunk.push(self[i]) i = i + 1 } @@ -1654,7 +1688,7 @@ pub fn[A] Array::unsafe_pop_back(self : Array[A]) -> Unit { ///| /// Truncates the array in-place to the specified length. /// -/// If `len` is greater than or equal to the current array length, +/// If `len` is greater than or equal to the current array length, /// the function does nothing. If `len` is 0, the array is cleared. /// Otherwise, removes elements from the end until the array reaches the given length. /// @@ -1663,7 +1697,7 @@ pub fn[A] Array::unsafe_pop_back(self : Array[A]) -> Unit { /// * `self` : The target array (modified in-place). /// * `len` : The new desired length (must be non-negative). /// -/// Important: +/// Important: /// - If `len` is negative, the function does nothing. /// - If `len` exceeds current length, the array remains unchanged. /// @@ -1678,3 +1712,38 @@ pub fn[A] Array::truncate(self : Array[A], len : Int) -> Unit { guard len >= 0 && len < self.length() else { return } self.unsafe_truncate_to_length(len) } + +///| +/// In-place filter and map for Array +/// +/// # Example +/// ```moonbit +/// let arr = [1, 2, 3, 4, 5] +/// arr.retain_map(fn(x) { +/// if x % 2 == 0 { +/// Some(x * 2) +/// } else { +/// None +/// } +/// }) +/// inspect(arr,content = "[4, 8]") +/// ``` +pub fn[A] Array::retain_map(self : Array[A], f : (A) -> A?) -> Unit { + if self.is_empty() { + return + } + let buf = self.buffer() + let len = self.length() + let mut write_idx = 0 + for read_idx in 0.. { + buf[write_idx] = new_val + write_idx += 1 + } + None => () + } + } + self.unsafe_truncate_to_length(write_idx) +} diff --git a/bundled-core/builtin/array_block.mbt b/bundled-core/builtin/array_block.mbt index 77020e2..8005a51 100644 --- a/bundled-core/builtin/array_block.mbt +++ b/bundled-core/builtin/array_block.mbt @@ -49,12 +49,12 @@ pub fn[A] Array::unsafe_blit( dst_offset : Int, src : Array[A], src_offset : Int, - len : Int + len : Int, ) -> Unit { FixedArray::unsafe_blit( - dst.buffer().inner(), + dst.buffer().0, dst_offset, - src.buffer().inner(), + src.buffer().0, src_offset, len, ) @@ -91,7 +91,7 @@ pub fn[A] Array::unsafe_blit_fixed( dst_offset : Int, src : FixedArray[A], src_offset : Int, - len : Int + len : Int, ) -> Unit { UninitializedArray::unsafe_blit_fixed( dst.buffer(), @@ -137,8 +137,8 @@ pub fn[A] Array::blit_to( self : Array[A], dst : Array[A], len~ : Int, - src_offset~ : Int = 0, - dst_offset~ : Int = 0 + src_offset? : Int = 0, + dst_offset? : Int = 0, ) -> Unit { guard len >= 0 && dst_offset >= 0 && @@ -250,7 +250,8 @@ test "panic Array::blit_to/boundary_cases" { ignore(Array::blit_to(src, dst, len=5, dst_offset=6)) } -///| TODO +///| +/// TODO /// 1. allow skip /// 2. verify test /// 3. concurrency test diff --git a/bundled-core/builtin/array_test.mbt b/bundled-core/builtin/array_test.mbt index feec328..6fc5cc1 100644 --- a/bundled-core/builtin/array_test.mbt +++ b/bundled-core/builtin/array_test.mbt @@ -184,12 +184,12 @@ test "array_rev_eachi" { ..write_string("\n")) inspect( buf, - content= + content=( #|0: 'c' #|1: 'b' #|2: 'a' #| - , + ), ) } @@ -445,6 +445,13 @@ test "array_chunks" { inspect(arr.chunks(3), content="[[1, 2, 3], [4, 5, 6], [7, 8, 9]]") } +///| +test "panic_array_chunks_by_zero" { + let arr = [1, 2, 3, 4, 5] + let _ = arr.chunks(0) + +} + ///| test "array_chunks_by" { let v = [1, 1, 2, 3, 2, 3, 2, 3, 4] @@ -465,6 +472,13 @@ test "array_windows" { inspect(windows, content="[[1], [2], [3], [4], [5], [6]]") } +///| +test "panic_array_windows_by_zero" { + let arr = [1, 2, 3, 4, 5] + let _ = arr.windows(0) + +} + ///| test "array_reserve_capacity" { let a = [1, 2, 3] @@ -621,7 +635,7 @@ test "each with error callback 2" { }) catch { Failure(_) => inspect(sum, content="1") - } else { + } noraise { _ => assert_true(false) } } @@ -653,7 +667,7 @@ test "eachi with error callback 2" { }) catch { Failure(_) => inspect(sum, content="1") - } else { + } noraise { _ => assert_true(false) } } @@ -669,9 +683,9 @@ test "map with error callback" { }) inspect( v, - content= + content=( #|Err(Failure("Error at 2")) - , + ), ) } @@ -687,7 +701,7 @@ test "map with error callback 2" { }) catch { Failure(_) => () - } else { + } noraise { _ => assert_true(false) } } @@ -703,9 +717,9 @@ test "mapi with error callback" { }) inspect( v, - content= + content=( #|Err(Failure("Error at 2")) - , + ), ) } @@ -721,7 +735,7 @@ test "mapi with error callback 2" { }) catch { Failure(_) => () - } else { + } noraise { _ => assert_true(false) } } @@ -737,9 +751,9 @@ test "filter with error callback" { }) inspect( v, - content= + content=( #|Err(Failure("Error at 2")) - , + ), ) } @@ -755,7 +769,7 @@ test "filter with error callback 2" { }) catch { Failure(_) => () - } else { + } noraise { _ => assert_true(false) } } @@ -771,9 +785,9 @@ test "map_inplace with error callback" { }) inspect( v, - content= + content=( #|Err(Failure("Error at 2")) - , + ), ) } @@ -789,7 +803,7 @@ test "map_inplace with error callback 2" { }) catch { Failure(_) => () - } else { + } noraise { _ => assert_true(false) } } @@ -805,8 +819,104 @@ test "mapi_inplace with error callback" { }) inspect( v, - content= + content=( #|Err(Failure("Error at 2")) - , + ), ) } + +///| +test "retain_map" { + let arr = [2, 3, 4] + arr.retain_map(x => if x < 4 { Some(x + 1) } else { None }) + inspect(arr, content="[3, 4]") +} + +///| +test "array_fill - basic functionality" { + let arr = [1, 2, 3, 4, 5] + arr.fill(42) + inspect(arr, content="[42, 42, 42, 42, 42]") +} + +///| +test "array_fill - with start parameter only" { + let arr = [1, 2, 3, 4, 5] + arr.fill(99, start=2) + inspect(arr, content="[1, 2, 99, 99, 99]") +} + +///| +test "array_fill - with start and end parameters" { + let arr = [1, 2, 3, 4, 5] + arr.fill(77, start=1, end=3) + inspect(arr, content="[1, 77, 77, 4, 5]") +} + +///| +test "array_fill - start equals end" { + let arr = [1, 2, 3, 4, 5] + arr.fill(88, start=2, end=2) + inspect(arr, content="[1, 2, 3, 4, 5]") // No change expected +} + +///| +test "array_fill - start at beginning" { + let arr = [1, 2, 3, 4, 5] + arr.fill(10, start=0, end=2) + inspect(arr, content="[10, 10, 3, 4, 5]") +} + +///| +test "array_fill - end at array length" { + let arr = [1, 2, 3, 4, 5] + arr.fill(20, start=3, end=5) + inspect(arr, content="[1, 2, 3, 20, 20]") +} + +///| +test "array_fill - single element" { + let arr = [100] + arr.fill(50) + inspect(arr, content="[50]") +} + +///| +test "array_fill - empty array" { + let arr : Array[Int] = [] + arr.fill(123) + inspect(arr, content="[]") // Should remain empty +} + +///| +test "array_fill - with different types" { + let str_arr = ["a", "b", "c", "d"] + str_arr.fill("x", start=1, end=3) + inspect(str_arr, content="[\"a\", \"x\", \"x\", \"d\"]") + let bool_arr = [true, false, true, false] + bool_arr.fill(true, start=0, end=2) + inspect(bool_arr, content="[true, true, true, false]") +} + +///| +test "array_fill - boundary conditions start" { + let arr = [1, 2, 3, 4, 5] + // Test with start at last valid index + arr.fill(555, start=4, end=5) + inspect(arr, content="[1, 2, 3, 4, 555]") +} + +///| +test "array_fill - boundary conditions end" { + let arr = [1, 2, 3, 4, 5] + // Test with end at array length + arr.fill(666, start=2, end=5) + inspect(arr, content="[1, 2, 666, 666, 666]") +} + +///| +test "array_fill - full range explicit" { + let arr = [1, 2, 3, 4, 5] + arr.fill(777, start=0, end=5) + inspect(arr, content="[777, 777, 777, 777, 777]") +} diff --git a/bundled-core/builtin/arraycore_js.mbt b/bundled-core/builtin/arraycore_js.mbt index e40defa..3f2fbdc 100644 --- a/bundled-core/builtin/arraycore_js.mbt +++ b/bundled-core/builtin/arraycore_js.mbt @@ -52,7 +52,7 @@ extern "js" fn JSArray::pop(self : JSArray) -> JSValue = extern "js" fn JSArray::splice( self : JSArray, index : Int, - count : Int + count : Int, ) -> JSArray = #| (arr, idx, cnt) => arr.splice(idx, cnt) @@ -61,10 +61,19 @@ extern "js" fn JSArray::splice1( self : JSArray, index : Int, count : Int, - value : JSValue + value : JSValue, ) -> JSArray = #| (arr, idx, cnt, val) => arr.splice(idx, cnt, val) +///| +extern "js" fn JSArray::fill( + self : JSArray, + value : JSValue, + start : Int, + end : Int, +) -> Unit = + #| (arr, val, start, end) => arr.fill(val, start, end) + //#endregion ///| @@ -78,7 +87,7 @@ fn[T] Array::make_uninit(len : Int) -> Array[T] = "%fixedarray.make_uninit" ///| /// Creates a new array. -pub fn[T] Array::new(capacity~ : Int = 0) -> Array[T] { +pub fn[T] Array::new(capacity? : Int = 0) -> Array[T] { ignore(capacity) [] } @@ -203,12 +212,14 @@ pub fn[T] Array::pop(self : Array[T]) -> T? { } } -///| Removes the last element from a array and returns it. +///| +/// Removes the last element from a array and returns it. /// /// **NOTE** This method will not cause a panic, but it may result in undefined /// behavior on the JavaScript platform. Use with caution. /// #internal(unsafe, "Panic if the array is empty.") +#alias(pop_exn, deprecated) pub fn[T] Array::unsafe_pop(self : Array[T]) -> T { JSArray::ofAnyArray(self).pop().toAny() } @@ -291,3 +302,59 @@ fn[T] Array::unsafe_grow_to_length(self : Array[T], new_len : Int) -> Unit { guard new_len >= self.length() JSArray::ofAnyArray(self).set_length(new_len) } + +///| +/// Fills an Array with a specified value. +/// +/// This method fills all or part of an Array with the given value. +/// +/// # Parameters +/// - `value`: The value to fill the array with +/// - `start`: The starting index (inclusive, default: 0) +/// - `end`: The ending index (exclusive, optional) +/// +/// If `end` is not provided, fills from `start` to the end of the array. +/// If `start` equals `end`, no elements are modified. +/// +/// # Panics +/// - Panics if `start` is negative or greater than or equal to the array length +/// - Panics if `end` is provided and is less than `start` or greater than array length +/// - Does nothing if the array is empty +/// +/// # Example +/// ```moonbit +/// // Fill entire array +/// let arr = [1, 2, 3, 4, 5] +/// arr.fill(0) +/// inspect(arr, content="[0, 0, 0, 0, 0]") +/// +/// // Fill from index 1 to 3 (exclusive) +/// let arr2 = [1, 2, 3, 4, 5] +/// arr2.fill(99, start=1, end=3) +/// inspect(arr2, content="[1, 99, 99, 4, 5]") +/// +/// // Fill from index 2 to end +/// let arr3 = ["a", "b", "c", "d"] +/// arr3.fill("x", start=2) +/// inspect(arr3, content=( +/// #|["a", "b", "x", "x"] +/// )) +/// ``` +pub fn[A] Array::fill( + self : Array[A], + value : A, + start? : Int = 0, + end? : Int, +) -> Unit { + let array_length = self.length() + guard array_length > 0 else { return } + guard start >= 0 && start < array_length + let end = match end { + None => array_length + Some(e) => { + guard e >= 0 && e <= array_length + e + } + } + JSArray::ofAnyArray(self).fill(JSValue::ofAny(value), start, end) +} diff --git a/bundled-core/builtin/arraycore_nonjs.mbt b/bundled-core/builtin/arraycore_nonjs.mbt index e5ec45d..772a990 100644 --- a/bundled-core/builtin/arraycore_nonjs.mbt +++ b/bundled-core/builtin/arraycore_nonjs.mbt @@ -49,7 +49,7 @@ fn[T] Array::make_uninit(len : Int) -> Array[T] { /// let arr : Array[Int] = Array::new() /// inspect(arr.length(), content="0") /// ``` -pub fn[T] Array::new(capacity~ : Int = 0) -> Array[T] { +pub fn[T] Array::new(capacity? : Int = 0) -> Array[T] { if capacity == 0 { [] } else { @@ -126,7 +126,7 @@ fn[T] Array::buffer(self : Array[T]) -> UninitializedArray[T] { fn[T] Array::resize_buffer(self : Array[T], new_capacity : Int) -> Unit { let new_buf = UninitializedArray::make(new_capacity) let old_buf = self.buf - let old_cap = old_buf.inner().length() + let old_cap = old_buf.0.length() let copy_len = if old_cap < new_capacity { old_cap } else { new_capacity } UninitializedArray::unsafe_blit(new_buf, 0, old_buf, 0, copy_len) self.buf = new_buf @@ -177,7 +177,7 @@ test "Array::resize_buffer" { arr.push(1) arr.push(2) arr.resize_buffer(4) - assert_eq(arr.buffer().inner().length() >= 4, true) + assert_eq(arr.buffer().0.length() >= 4, true) arr.push(3) arr.push(4) assert_eq(arr.length(), 4) @@ -244,7 +244,7 @@ pub fn[T] Array::shrink_to_fit(self : Array[T]) -> Unit { /// v.push(3) /// ``` pub fn[T] Array::push(self : Array[T], value : T) -> Unit { - if self.length() == self.buffer().inner().length() { + if self.length() == self.buffer().0.length() { self.realloc() } let length = self.length() @@ -292,6 +292,7 @@ pub fn[T] Array::pop(self : Array[T]) -> T? { /// ``` /// #internal(unsafe, "Panic if the array is empty.") +#alias(pop_exn, deprecated) pub fn[T] Array::unsafe_pop(self : Array[T]) -> T { let len = self.length() guard len != 0 @@ -376,7 +377,7 @@ pub fn[T] Array::insert(self : Array[T], index : Int, value : T) -> Unit { "index out of bounds: the len is from 0 to \{self.length()} but the index is \{index}", ) } - if self.length() == self.buffer().inner().length() { + if self.length() == self.buffer().0.length() { self.realloc() } UninitializedArray::unsafe_blit( @@ -405,3 +406,59 @@ fn[T] Array::unsafe_grow_to_length(self : Array[T], new_len : Int) -> Unit { self.len = new_len self.buf = new_buf } + +///| +/// Fills an Array with a specified value. +/// +/// This method fills all or part of an Array with the given value. +/// +/// # Parameters +/// - `value`: The value to fill the array with +/// - `start`: The starting index (inclusive, default: 0) +/// - `end`: The ending index (exclusive, optional) +/// +/// If `end` is not provided, fills from `start` to the end of the array. +/// If `start` equals `end`, no elements are modified. +/// +/// # Panics +/// - Panics if `start` is negative or greater than or equal to the array length +/// - Panics if `end` is provided and is less than `start` or greater than array length +/// - Does nothing if the array is empty +/// +/// # Example +/// ```moonbit +/// // Fill entire array +/// let arr = [1, 2, 3, 4, 5] +/// arr.fill(0) +/// inspect(arr, content="[0, 0, 0, 0, 0]") +/// +/// // Fill from index 1 to 3 (exclusive) +/// let arr2 = [1, 2, 3, 4, 5] +/// arr2.fill(99, start=1, end=3) +/// inspect(arr2, content="[1, 99, 99, 4, 5]") +/// +/// // Fill from index 2 to end +/// let arr3 = ["a", "b", "c", "d"] +/// arr3.fill("x", start=2) +/// inspect(arr3, content=( +/// #|["a", "b", "x", "x"] +/// )) +/// ``` +pub fn[A] Array::fill( + self : Array[A], + value : A, + start? : Int = 0, + end? : Int, +) -> Unit { + let array_length = self.length() + guard array_length > 0 else { return } + guard start >= 0 && start < array_length + let length = match end { + None => array_length + Some(e) => { + guard e >= start && e <= array_length + e + } + } + self.buf.unchecked_fill(start, value, length - start) +} diff --git a/bundled-core/builtin/arrayview.mbt b/bundled-core/builtin/arrayview.mbt index c8bdcde..7bace04 100644 --- a/bundled-core/builtin/arrayview.mbt +++ b/bundled-core/builtin/arrayview.mbt @@ -25,11 +25,23 @@ /// ``` #deprecated("use @array.View instead") #builtin.valtype -struct ArrayView[T] { - buf : UninitializedArray[T] - start : Int - len : Int -} +type ArrayView[T] + +///| +fn[T] ArrayView::buf(self : ArrayView[T]) -> UninitializedArray[T] = "%arrayview.buf" + +///| +fn[T] ArrayView::start(self : ArrayView[T]) -> Int = "%arrayview.start" + +///| +fn[T] ArrayView::len(self : ArrayView[T]) -> Int = "%arrayview.len" + +///| +fn[T] ArrayView::make( + buf : UninitializedArray[T], + start : Int, + len : Int, +) -> ArrayView[T] = "%arrayview.make" ///| /// Returns the length (number of elements) of an array view. @@ -48,7 +60,7 @@ struct ArrayView[T] { /// inspect(view.length(), content="2") /// ``` pub fn[T] ArrayView::length(self : ArrayView[T]) -> Int { - self.len + self.len() } ///| @@ -74,12 +86,12 @@ pub fn[T] ArrayView::length(self : ArrayView[T]) -> Int { /// inspect(view[1], content="4") /// ``` pub fn[T] ArrayView::op_get(self : ArrayView[T], index : Int) -> T { - guard index >= 0 && index < self.len else { + guard index >= 0 && index < self.len() else { abort( - "index out of bounds: the len is from 0 to \{self.len} but the index is \{index}", + "index out of bounds: the len is from 0 to \{self.len()} but the index is \{index}", ) } - self.buf[self.start + index] + self.buf()[self.start() + index] } ///| @@ -104,7 +116,7 @@ pub fn[T] ArrayView::op_get(self : ArrayView[T], index : Int) -> T { #intrinsic("%arrayview.unsafe_get") #internal(unsafe, "Panic if index is out of bounds") pub fn[T] ArrayView::unsafe_get(self : ArrayView[T], index : Int) -> T { - self.buf[self.start + index] + self.buf()[self.start() + index] } ///| @@ -119,58 +131,32 @@ pub fn[T] ArrayView::unsafe_get(self : ArrayView[T], index : Int) -> T { /// Throws a panic if `index` is negative or greater than or equal to the length /// of the array view. /// -/// Example: -/// -/// ```moonbit -/// let arr = [1, 2, 3, 4, 5] -/// let view = arr[1:4] // view contains [2, 3, 4] -/// view.op_set(1, 42) -/// inspect(arr, content="[1, 2, 42, 4, 5]") -/// ``` /// +#deprecated("ArrayView will be immutable, use array if you need mutation") pub fn[T] ArrayView::op_set( self : ArrayView[T], index : Int, - value : T + value : T, ) -> Unit { - guard index >= 0 && index < self.len else { + guard index >= 0 && index < self.len() else { abort( - "index out of bounds: the len is from 0 to \{self.len} but the index is \{index}", + "index out of bounds: the len is from 0 to \{self.len()} but the index is \{index}", ) } - self.buf[self.start + index] = value + self.buf()[self.start() + index] = value } ///| -/// Swaps two elements in the array view at the specified indices. -/// -/// Parameters: -/// -/// * `self` : The array view in which to swap elements. -/// * `i` : The index of the first element to be swapped. -/// * `j` : The index of the second element to be swapped. -/// -/// Throws a panic if either index is negative or greater than or equal to the -/// length of the array view. -/// -/// Example: -/// -/// ```moonbit -/// let arr = [1, 2, 3, 4, 5] -/// let view = arr[1:4] // view = [2, 3, 4] -/// view.swap(0, 2) // view = [4, 3, 2] -/// inspect(view, content="[4, 3, 2]") -/// ``` -/// +#deprecated("ArrayView will be immutable, use array if you need mutation") pub fn[T] ArrayView::swap(self : ArrayView[T], i : Int, j : Int) -> Unit { - guard i >= 0 && i < self.len && j >= 0 && j < self.len else { + guard i >= 0 && i < self.len() && j >= 0 && j < self.len() else { abort( - "index out of bounds: the len is from 0 to \{self.len} but the index is (\{i}, \{j})", + "index out of bounds: the len is from 0 to \{self.len()} but the index is (\{i}, \{j})", ) } - let temp = self.buf[self.start + i] - self.buf[self.start + i] = self.buf[self.start + j] - self.buf[self.start + j] = temp + let temp = self.buf()[self.start() + i] + self.buf()[self.start() + i] = self.buf()[self.start() + j] + self.buf()[self.start() + j] = temp } ///| @@ -200,8 +186,8 @@ pub fn[T] ArrayView::swap(self : ArrayView[T], i : Int, j : Int) -> Unit { /// ``` pub fn[T] Array::op_as_view( self : Array[T], - start~ : Int = 0, - end? : Int + start? : Int = 0, + end? : Int, ) -> ArrayView[T] { let len = self.length() let end = match end { @@ -212,7 +198,7 @@ pub fn[T] Array::op_as_view( guard start >= 0 && start <= end && end <= len else { abort("View index out of bounds") } - ArrayView::{ buf: self.buffer(), start, len: end - start } + ArrayView::make(self.buffer(), start, end - start) } ///| @@ -246,8 +232,8 @@ pub fn[T] Array::op_as_view( /// ``` pub fn[T] ArrayView::op_as_view( self : ArrayView[T], - start~ : Int = 0, - end? : Int + start? : Int = 0, + end? : Int, ) -> ArrayView[T] { let len = self.length() let end = match end { @@ -258,5 +244,20 @@ pub fn[T] ArrayView::op_as_view( guard start >= 0 && start <= end && end <= len else { abort("View index out of bounds") } - ArrayView::{ buf: self.buf, start: self.start + start, len: end - start } + ArrayView::make(self.buf(), self.start() + start, end - start) +} + +///| +#deprecated("ArrayView will be immutable, use array if you need mutation") +pub fn[T] ArrayView::blit_to(self : ArrayView[T], dst : ArrayView[T]) -> Unit { + let src_len = self.length() + let dst_len = dst.length() + guard dst_len >= src_len + UninitializedArray::unsafe_blit( + dst.buf(), + dst.start(), + self.buf(), + self.start(), + src_len, + ) } diff --git a/bundled-core/builtin/arrayview_test.mbt b/bundled-core/builtin/arrayview_test.mbt index 5621bc9..0704e76 100644 --- a/bundled-core/builtin/arrayview_test.mbt +++ b/bundled-core/builtin/arrayview_test.mbt @@ -28,22 +28,6 @@ test "op_get" { inspect([1, 2, 3][:][2], content="3") } -///| -test "op_set" { - let v = [1, 2, 3][:] - inspect(v[1], content="2") - v[1] = 4 - inspect(v[1], content="4") -} - -///| -test "swap" { - let v = [1, 2, 3][:] - inspect(v[1], content="2") - v.swap(1, 2) - inspect(v[1], content="3") -} - ///| test "negative index1" { let arr = [1, 2, 3] diff --git a/bundled-core/builtin/assert.mbt b/bundled-core/builtin/assert.mbt index d9065e1..4938c2d 100644 --- a/bundled-core/builtin/assert.mbt +++ b/bundled-core/builtin/assert.mbt @@ -40,12 +40,13 @@ fn[T : Show] debug_string(t : T) -> String { /// assert_eq(1, 1) /// assert_eq("hello", "hello") /// ``` +#callsite(autofill(loc)) #coverage.skip pub fn[T : Eq + Show] assert_eq( a : T, b : T, msg? : String, - loc~ : SourceLoc = _ + loc~ : SourceLoc, ) -> Unit raise { if a != b { let fail_msg = match msg { @@ -77,14 +78,15 @@ pub fn[T : Eq + Show] assert_eq( /// assert_not_eq(1, 2) // Passes /// /// ``` +#callsite(autofill(loc)) #coverage.skip pub fn[T : Eq + Show] assert_not_eq( a : T, b : T, msg? : String, - loc~ : SourceLoc = _ + loc~ : SourceLoc, ) -> Unit raise { - if not(a != b) { + if !(a != b) { let fail_msg = match msg { Some(msg) => msg None => "`\{debug_string(a)} == \{debug_string(b)}`" @@ -111,9 +113,10 @@ pub fn[T : Eq + Show] assert_not_eq( /// ```moonbit /// assert_true(true) /// ``` +#callsite(autofill(loc)) #coverage.skip -pub fn assert_true(x : Bool, msg? : String, loc~ : SourceLoc = _) -> Unit raise { - if not(x) { +pub fn assert_true(x : Bool, msg? : String, loc~ : SourceLoc) -> Unit raise { + if !x { let fail_msg = match msg { Some(msg) => msg None => "`\{x}` is not true" @@ -141,12 +144,9 @@ pub fn assert_true(x : Bool, msg? : String, loc~ : SourceLoc = _) -> Unit raise /// assert_false(false) /// assert_false(1 > 2) /// ``` +#callsite(autofill(loc)) #coverage.skip -pub fn assert_false( - x : Bool, - msg? : String, - loc~ : SourceLoc = _ -) -> Unit raise { +pub fn assert_false(x : Bool, msg? : String, loc~ : SourceLoc) -> Unit raise { if x { let fail_msg = match msg { Some(msg) => msg diff --git a/bundled-core/builtin/autoloc.mbt b/bundled-core/builtin/autoloc.mbt index 7b07481..0ecbfb7 100644 --- a/bundled-core/builtin/autoloc.mbt +++ b/bundled-core/builtin/autoloc.mbt @@ -49,7 +49,7 @@ pub impl Show for SourceLoc with output(self, logger) { /// array of optional source locations, where each element corresponds to an /// argument's location in the source code. Used internally by the compiler for /// error reporting and debugging purposes. -pub(all) type ArgsLoc Array[SourceLoc?] derive(Show) +pub(all) struct ArgsLoc(Array[SourceLoc?]) derive(Show) ///| /// Converts an array of optional source locations to its JSON string diff --git a/bundled-core/builtin/byte.mbt b/bundled-core/builtin/byte.mbt index 058dbd0..09529c6 100644 --- a/bundled-core/builtin/byte.mbt +++ b/bundled-core/builtin/byte.mbt @@ -33,7 +33,7 @@ /// let c = b'\xFF' /// inspect(c * c, content="b'\\x01'") // 255 * 255 = 65025, truncated to 1 /// ``` -pub impl Mul for Byte with op_mul(self : Byte, that : Byte) -> Byte { +pub impl Mul for Byte with mul(self : Byte, that : Byte) -> Byte { (self.to_int() * that.to_int()).to_byte() } @@ -55,12 +55,12 @@ pub impl Mul for Byte with op_mul(self : Byte, that : Byte) -> Byte { /// let b = b'\x03' // 3 /// inspect(a / b, content="b'\\x55'") // 255 / 3 = 85 (0x55) /// ``` -pub impl Div for Byte with op_div(self : Byte, that : Byte) -> Byte { +pub impl Div for Byte with div(self : Byte, that : Byte) -> Byte { (self.to_int() / that.to_int()).to_byte() } ///| -pub impl Mod for Byte with op_mod(self : Byte, that : Byte) -> Byte { +pub impl Mod for Byte with mod(self : Byte, that : Byte) -> Byte { (self.to_int() % that.to_int()).to_byte() } @@ -73,7 +73,7 @@ pub impl Mod for Byte with op_mod(self : Byte, that : Byte) -> Byte { /// - `that` : The second `Byte` value to compare. /// /// Returns `true` if the two `Byte` values are equal, otherwise `false`. -pub impl Eq for Byte with op_equal(self : Byte, that : Byte) -> Bool { +pub impl Eq for Byte with equal(self : Byte, that : Byte) -> Bool { self.to_int() == that.to_int() } @@ -86,7 +86,7 @@ pub impl Eq for Byte with op_equal(self : Byte, that : Byte) -> Bool { /// - `byte2` : The second `Byte` value to be added. /// /// Returns the sum of `byte1` and `byte2` as a `Byte`. -pub impl Add for Byte with op_add(self : Byte, that : Byte) -> Byte { +pub impl Add for Byte with add(self : Byte, that : Byte) -> Byte { (self.to_int() + that.to_int()).to_byte() } @@ -100,7 +100,7 @@ pub impl Add for Byte with op_add(self : Byte, that : Byte) -> Byte { /// - `that` : The byte to subtract from the first byte. /// /// Returns the result of the subtraction as a byte. -pub impl Sub for Byte with op_sub(self : Byte, that : Byte) -> Byte { +pub impl Sub for Byte with sub(self : Byte, that : Byte) -> Byte { (self.to_int() - that.to_int()).to_byte() } @@ -288,7 +288,7 @@ pub fn Byte::to_uint(self : Byte) -> UInt { /// the left. /// /// Returns the resulting `Byte` value after the shift operation. -pub impl Shl for Byte with op_shl(self : Byte, count : Int) -> Byte { +pub impl Shl for Byte with shl(self : Byte, count : Int) -> Byte { (self.to_int() << count).to_byte() } @@ -303,6 +303,6 @@ pub impl Shl for Byte with op_shl(self : Byte, count : Int) -> Byte { /// right. /// /// Returns the resulting `Byte` value after the bitwise right shift operation. -pub impl Shr for Byte with op_shr(self : Byte, count : Int) -> Byte { +pub impl Shr for Byte with shr(self : Byte, count : Int) -> Byte { (self.to_uint() >> count).reinterpret_as_int().to_byte() } diff --git a/bundled-core/builtin/byte_test.mbt b/bundled-core/builtin/byte_test.mbt index 537cf71..7b836c5 100644 --- a/bundled-core/builtin/byte_test.mbt +++ b/bundled-core/builtin/byte_test.mbt @@ -40,13 +40,13 @@ test "byte_op_equal" { let b1 = b'\x00' let b2 = b'\x0F' let b3 = b'\xFF' - assert_true(b1.op_equal(b1)) - assert_true(b1.op_equal(b2) |> not) - assert_true(b2.op_equal(b1) |> not) - assert_true(b2.op_equal(b2)) - assert_true(b2.op_equal(b3) |> not) - assert_true(b3.op_equal(b2) |> not) - assert_true(b3.op_equal(b3)) + assert_true(b1 == b1) + assert_true(b1 != b2) + assert_true(b2 != b1) + assert_true(b2 == b2) + assert_true(b2 != b3) + assert_true(b3 != b2) + assert_true(b3 == b3) } ///| diff --git a/bundled-core/builtin/bytes.mbt b/bundled-core/builtin/bytes.mbt index 108ad3f..5ade888 100644 --- a/bundled-core/builtin/bytes.mbt +++ b/bundled-core/builtin/bytes.mbt @@ -14,16 +14,16 @@ ///| /// Reinterpret the byte sequence as Bytes. -/// +/// /// Notice that this will make the `Bytes` object to be a view of the original /// byte sequence, so any modification to the original byte sequence will be /// reflected in the `Bytes` object. #internal(unsafe, "Creating mutable Bytes") pub fn FixedArray::unsafe_reinterpret_as_bytes( - self : FixedArray[Byte] + self : FixedArray[Byte], ) -> Bytes = "%identity" -///| +///| /// Creates a new byte sequence of the specified length, where each byte is /// initialized using a function that maps indices to bytes. /// @@ -43,7 +43,7 @@ pub fn FixedArray::unsafe_reinterpret_as_bytes( /// let bytes = Bytes::makei(3, (i) => { (i + 65).to_byte() }) /// assert_eq(bytes, b"ABC") /// ``` -pub fn Bytes::makei(length : Int, value : (Int) -> Byte) -> Bytes { +pub fn Bytes::makei(length : Int, value : (Int) -> Byte raise?) -> Bytes raise? { if length <= 0 { return [] } @@ -56,23 +56,24 @@ pub fn Bytes::makei(length : Int, value : (Int) -> Byte) -> Bytes { ///| /// TODO: support local primitive declaration +#owned(bytes) fn unsafe_sub_string( bytes : Bytes, byte_offset : Int, - byte_length : Int + byte_length : Int, ) -> String = "$moonbit.unsafe_bytes_sub_string" ///| -/// Return an unchecked string, containing the subsequence of `self` that starts at -/// `offset` and has length `length`. Both `offset` and `length` +/// Return an unchecked string, containing the subsequence of `self` that starts at +/// `offset` and has length `length`. Both `offset` and `length` /// are indexed by byte. -/// -/// Note this function does not validate the encoding of the byte sequence, +/// +/// Note this function does not validate the encoding of the byte sequence, /// it simply copy the bytes into a new String. pub fn Bytes::to_unchecked_string( self : Bytes, - offset~ : Int = 0, - length? : Int + offset? : Int = 0, + length? : Int, ) -> String { let len = self.length() let length = if length is Some(l) { l } else { len - offset } @@ -109,20 +110,22 @@ pub fn Bytes::to_unchecked_string( /// ```moonbit /// let bytes = FixedArray::make(6, b'\x00') /// bytes.blit_from_string(0, "ABC", 0, 3) -/// inspect(bytes[0], content="b'\\x41'") // 'A' -/// inspect(bytes[1], content="b'\\x00'") -/// inspect(bytes[2], content="b'\\x42'") // 'B' +/// @json.inspect(bytes, content=[65,0,66,0,67,0]) // 'A' +/// bytes.blit_from_string(0, "ไฝ ๅฅฝๅ•Š", 0, 3) +/// @json.inspect(bytes, content=[96,79,125,89,74,85]) // 'ไฝ ๅฅฝๅ•Š' +/// bytes.blit_from_string(0, "๐Ÿ˜ˆ", 0, 2) +/// @json.inspect(bytes, content=[61,216,8,222,74,85]) // '๐Ÿ˜ˆ' /// ``` pub fn FixedArray::blit_from_string( self : FixedArray[Byte], bytes_offset : Int, str : String, str_offset : Int, - length : Int + length : Int, ) -> Unit { let s1 = bytes_offset let s2 = str_offset - let e1 = bytes_offset + length - 1 + let e1 = bytes_offset + length * 2 - 1 let e2 = str_offset + length - 1 let len1 = self.length() let len2 = str.length() @@ -135,6 +138,10 @@ pub fn FixedArray::blit_from_string( } } +///| +/// TODO: specific copy +fn unsafe_from_bytes(bytes : Bytes) -> FixedArray[Byte] = "%identity" + ///| /// Copy `length` chars from byte sequence `src`, starting at `src_offset`, /// into byte sequence `self`, starting at `bytes_offset`. @@ -143,7 +150,7 @@ pub fn FixedArray::blit_from_bytes( bytes_offset : Int, src : Bytes, src_offset : Int, - length : Int + length : Int, ) -> Unit { let s1 = bytes_offset let s2 = src_offset @@ -152,10 +159,13 @@ pub fn FixedArray::blit_from_bytes( let len1 = self.length() let len2 = src.length() guard length >= 0 && s1 >= 0 && e1 < len1 && s2 >= 0 && e2 < len2 - let end_src_offset = src_offset + length - for i = src_offset, j = bytes_offset; i < end_src_offset; i = i + 1, j = j + 1 { - self[j] = src[i] - } + FixedArray::unsafe_blit( + self, + bytes_offset, + unsafe_from_bytes(src), + src_offset, + length, + ) } ///| @@ -186,7 +196,7 @@ pub fn FixedArray::blit_from_bytes( pub fn FixedArray::set_utf8_char( self : FixedArray[Byte], offset : Int, - value : Char + value : Char, ) -> Int { let code = value.to_uint() match code { @@ -219,12 +229,12 @@ pub fn FixedArray::set_utf8_char( ///| /// Fill UTF16LE encoded char `value` into byte sequence `self`, starting at `offset`. /// It return the length of bytes has been written. -/// +/// /// This function will panic if the `value` is out of range. pub fn FixedArray::set_utf16le_char( self : FixedArray[Byte], offset : Int, - value : Char + value : Char, ) -> Int { let code = value.to_uint() if code < 0x10000 { @@ -248,16 +258,16 @@ pub fn FixedArray::set_utf16le_char( ///| /// Fill UTF16BE encoded char `value` into byte sequence `self`, starting at `offset`. /// It return the length of bytes has been written. -/// +/// /// This function will panic if the `value` is out of range. pub fn FixedArray::set_utf16be_char( self : FixedArray[Byte], offset : Int, - value : Char + value : Char, ) -> Int { let code = value.to_uint() if code < 0x10000 { - self[offset] = (code >> 0xFF).to_byte() + self[offset] = (code >> 8).to_byte() self[offset + 1] = (code & 0xFF).to_byte() 2 } else if code < 0x110000 { @@ -294,7 +304,7 @@ pub fn FixedArray::set_utf16be_char( /// inspect(bytes1 == bytes2, content="true") /// inspect(bytes1 == bytes3, content="false") /// ``` -pub impl Eq for Bytes with op_equal(self : Bytes, other : Bytes) -> Bool { +pub impl Eq for Bytes with equal(self : Bytes, other : Bytes) -> Bool { if self.length() != other.length() { false } else { @@ -310,7 +320,7 @@ pub impl Eq for Bytes with op_equal(self : Bytes, other : Bytes) -> Bool { } ///| -/// Compares two byte sequences lexicographically. First compares the lengths of +/// Compares two byte sequences based on shortlex order. First compares the lengths of /// the sequences, then compares bytes pairwise until a difference is found or /// all bytes have been compared. /// @@ -333,7 +343,7 @@ pub impl Eq for Bytes with op_equal(self : Bytes, other : Bytes) -> Bool { /// inspect(a.compare(b), content="-1") // a < b /// inspect(b.compare(a), content="1") // b > a /// inspect(a.compare(a), content="0") // a = a -/// +/// /// let a = b"\x01\x02" /// let b = b"\x01\x02\x03" /// inspect(a.compare(b), content="-1") // shorter sequence is less diff --git a/bundled-core/builtin/bytes_test.mbt b/bundled-core/builtin/bytes_test.mbt index bb8802e..5e50850 100644 --- a/bundled-core/builtin/bytes_test.mbt +++ b/bundled-core/builtin/bytes_test.mbt @@ -15,6 +15,17 @@ ///| const HELLO_WORLD = b"H\x00e\x00l\x00l\x00o\x00,\x00 \x00W\x00o\x00r\x00l\x00d\x00!\x00" +///| +test "json repr" { + @json.inspect(b"hello", content="hello") + @json.inspect( + b"\x00\x01\x02\x03", + content=( + #|\x00\x01\x02\x03 + ), + ) +} + ///| test "to_string" { assert_eq(HELLO_WORLD.to_unchecked_string(), "Hello, World!") @@ -42,9 +53,9 @@ test "set_utf8_char (ASCII)" { assert_eq(len, 1) inspect( bytes, - content= + content=( #|b"\x41\x00\x00\x00" - , + ), ) } @@ -57,9 +68,9 @@ test "set_utf8_char (CJK)" { assert_eq(len, 3) inspect( bytes, - content= + content=( #|b"\xe6\x8b\xbc\x00" - , + ), ) } @@ -73,9 +84,9 @@ test "set_utf8_char (0x600)" { assert_eq(len, 2) inspect( bytes, - content= + content=( #|b"\xd8\x80\x00\x00" - , + ), ) } @@ -88,9 +99,9 @@ test "set_utf8_char (Emoji)" { assert_eq(len, 4) inspect( bytes, - content= + content=( #|b"\xf0\x9f\x99\x85" - , + ), ) } @@ -104,9 +115,9 @@ test "set_utf16le_char" { assert_eq(bytes.to_unchecked_string(offset=0, length=2), "A") inspect( bytes, - content= + content=( #|b"\x41\x00\x00\x00" - , + ), ) } @@ -120,9 +131,9 @@ test "set_utf16le_char (surrogate pair)" { assert_eq(bytes.to_unchecked_string(offset=0, length=4), "๐Ÿ‰‘") inspect( bytes, - content= + content=( #|b"\x3c\xd8\x51\xde" - , + ), ) } @@ -135,10 +146,20 @@ test "set_utf16be_char" { assert_eq(len, 2) inspect( bytes, - content= + content=( #|b"\x00\x41\x00\x00" - , + ), ) + let arr = FixedArray::make(2, b'\x00') + let _ = arr.set_utf16be_char(0, 'ไธญ') // Unicode U+4E2D + inspect(arr[0], content="b'\\x4E'") + inspect(arr[1], content="b'\\x2D'") + let arr = FixedArray::make(4, b'\x00') + let _ = arr.set_utf16be_char(0, '๐Ÿ˜ƒ') // Unicode U+1F603 + inspect(arr[0], content="b'\\xD8'") + inspect(arr[1], content="b'\\x3D'") + inspect(arr[2], content="b'\\xDE'") + inspect(arr[3], content="b'\\x03'") } ///| diff --git a/bundled-core/builtin/char_test.mbt b/bundled-core/builtin/char_test.mbt index 360c498..8a2391b 100644 --- a/bundled-core/builtin/char_test.mbt +++ b/bundled-core/builtin/char_test.mbt @@ -12,15 +12,39 @@ // See the License for the specific language governing permissions and // limitations under the License. +///| +enum H { + A(Char) +} derive(Show) + +///| +test "char nested" { + inspect(H::A('ไธญ'), content="A('ไธญ')") + inspect(H::A('\b'), content="A('\\b')") + inspect(H::A('\r'), content="A('\\r')") + inspect(H::A('\n'), content="A('\\n')") + inspect(H::A('\t'), content="A('\\t')") + inspect(H::A('\u0000'), content="A('\\u{00}')") + inspect(H::A('\u0001'), content="A('\\u{01}')") + inspect(H::A('\u0002'), content="A('\\u{02}')") + // emoji + inspect(H::A('๐Ÿš€'), content="A('๐Ÿš€')") +} + ///| test "char show" { assert_eq('\b'.to_string(), "\u0008") - // assert_eq('\b'.to_string(), "\u{8}") + assert_eq('\b'.to_string(), "\u{8}") + inspect('\b', content="\u{8}") assert_eq('\r'.to_string(), "\u000d") + inspect('\r', content="\u{d}") assert_eq('\n'.to_string(), "\u000a") + inspect('\n', content="\u{a}") assert_eq('\t'.to_string(), "\u0009") + inspect('\t', content="\u{9}") assert_eq('\u0000'.to_string(), "\u0000") assert_eq('\u0001'.to_string(), "\u0001") assert_eq('\u0002'.to_string(), "\u0002") assert_eq('\b'.to_string().escape(), "\"\\b\"") + inspect('ไธญ', content="ไธญ") } diff --git a/bundled-core/builtin/console.mbt b/bundled-core/builtin/console.mbt index 4ca38ba..4382f0f 100644 --- a/bundled-core/builtin/console.mbt +++ b/bundled-core/builtin/console.mbt @@ -174,11 +174,12 @@ test { /// inspect("hello", content="hello") /// inspect([1, 2, 3], content="[1, 2, 3]") /// ``` +#callsite(autofill(args_loc, loc)) pub fn inspect( obj : &Show, - content~ : String = "", - loc~ : SourceLoc = _, - args_loc~ : ArgsLoc = _ + content? : String = "", + loc~ : SourceLoc, + args_loc~ : ArgsLoc, ) -> Unit raise InspectError { let actual = obj.to_string() if actual != content { diff --git a/bundled-core/builtin/deprecated.mbt b/bundled-core/builtin/deprecated.mbt index 8acf5b1..3c55371 100644 --- a/bundled-core/builtin/deprecated.mbt +++ b/bundled-core/builtin/deprecated.mbt @@ -26,11 +26,11 @@ /// Returns an iterator that iterates over the range of Int from `start` to `end - 1`. #deprecated("Use `..<` in for loop or `until` method instead") #coverage.skip -pub fn Int::upto(self : Int, end : Int, inclusive~ : Bool = false) -> Iter[Int] { +pub fn Int::upto(self : Int, end : Int, inclusive? : Bool = false) -> Iter[Int] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -60,12 +60,12 @@ pub fn Int::upto(self : Int, end : Int, inclusive~ : Bool = false) -> Iter[Int] pub fn UInt::upto( self : UInt, end : UInt, - inclusive~ : Bool = false + inclusive? : Bool = false, ) -> Iter[UInt] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -95,12 +95,12 @@ pub fn UInt::upto( pub fn UInt64::upto( self : UInt64, end : UInt64, - inclusive~ : Bool = false + inclusive? : Bool = false, ) -> Iter[UInt64] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -130,12 +130,12 @@ pub fn UInt64::upto( pub fn Int64::upto( self : Int64, end : Int64, - inclusive~ : Bool = false + inclusive? : Bool = false, ) -> Iter[Int64] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -165,12 +165,12 @@ pub fn Int64::upto( pub fn Float::upto( self : Float, end : Float, - inclusive~ : Bool = false + inclusive? : Bool = false, ) -> Iter[Float] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -200,12 +200,12 @@ pub fn Float::upto( pub fn Double::upto( self : Double, end : Double, - inclusive~ : Bool = false + inclusive? : Bool = false, ) -> Iter[Double] { yield_ => { let mut i = self while i < end || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } if i == end { @@ -218,120 +218,10 @@ pub fn Double::upto( } } -///| -/// Searches the array for the first element that satisfies the predicate -/// function. -/// -/// Parameters: -/// -/// * `array` : The array to search in. -/// * `predicate` : A function that takes an element and returns a boolean -/// indicating whether the element satisfies the search condition. -/// -/// Returns the index of the first element that satisfies the predicate, or -/// `None` if no such element is found. -/// -/// Example: -/// -/// ```moonbit -/// let arr = [1, 2, 3, 4, 5] -/// inspect(arr.search_by((x) => { x > 3 }), content="Some(3)") -/// inspect(arr.search_by((x) => { x > 10 }), content="None") -/// ``` -/// -#deprecated("Use `search_by` instead.") -#coverage.skip -pub fn[T] Array::find_index(self : Array[T], f : (T) -> Bool) -> Int? { - self.search_by(f) -} - ///| /// Search the index of the first element that satisfies the predicate. /// -///| -/// Fold out values from an array according to certain rules. -/// -/// Example: -/// -/// ```moonbit -/// let sum = [1, 2, 3, 4, 5].fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) -/// ``` -#deprecated("Use `fold` instead.") -#coverage.skip -pub fn[T, U] Array::fold_left( - self : Array[T], - f : (U, T) -> U raise?, - init~ : U -) -> U raise? { - self.fold(init~, f) -} - -///| -/// Fold out values from an array according to certain rules in reversed turn. -/// -/// Example: -/// -/// ```moonbit -/// let sum = [1, 2, 3, 4, 5].rev_fold(init=0, (sum, elem) => sum + elem) -/// assert_eq(sum, 15) -/// ``` -#deprecated("Use `rev_fold` instead.") -#coverage.skip -pub fn[T, U] Array::fold_right( - self : Array[T], - f : (U, T) -> U raise?, - init~ : U -) -> U raise? { - self.rev_fold(init~, f) -} - -///| -/// Fold out values from an array according to certain rules with index. -/// -/// Example: -/// -/// ```moonbit -/// let sum = [1, 2, 3, 4, 5].foldi(init=0, (index, sum, _elem) => sum + index) -/// assert_eq(sum, 10) -/// ``` -#deprecated("Use `foldi` instead.") -#coverage.skip -pub fn[T, U] Array::fold_lefti( - self : Array[T], - f : (Int, U, T) -> U raise?, - init~ : U -) -> U raise? { - self.foldi(init~, f) -} - -///| -/// Fold out values from an array according to certain rules in reversed turn with index. -/// -/// Example: -/// -/// ```moonbit -/// let sum = [1, 2, 3, 4, 5].rev_foldi(init=0, (index, sum, _elem) => sum + index) -/// assert_eq(sum, 10) -/// ``` -#deprecated("Use `rev_foldi` instead.") -#coverage.skip -pub fn[T, U] Array::fold_righti( - self : Array[T], - f : (Int, U, T) -> U raise?, - init~ : U -) -> U raise? { - self.rev_foldi(init~, f) -} - -///| -#deprecated("Use `unsafe_pop` instead") -#coverage.skip -pub fn[T] Array::pop_exn(self : Array[T]) -> T { - self.unsafe_pop() -} - ///| /// Creates a byte sequence from a UTF-16 encoded string. Each character in the /// string is encoded as a pair of bytes in little-endian order. @@ -397,8 +287,9 @@ pub fn Byte::lsr(self : Byte, count : Int) -> Byte { ///| /// Prints and returns the value of a given expression for quick and dirty debugging. +#callsite(autofill(loc)) #deprecated("This function is for debugging only and should not be used in production") -pub fn[T] dump(t : T, name? : String, loc~ : SourceLoc = _) -> T { +pub fn[T] dump(t : T, name? : String, loc~ : SourceLoc) -> T { let name = match name { Some(name) => name None => "" @@ -416,9 +307,9 @@ pub fn[T] dump(t : T, name? : String, loc~ : SourceLoc = _) -> T { /// # Examples /// /// ```mbt -/// let s = "Hello๐Ÿคฃ"; -/// inspect(s.iter().nth(0).unwrap(), content="H"); -/// inspect(s.iter().nth(5).unwrap(), content="๐Ÿคฃ"); // Returns full emoji character +/// let s = "Hello๐Ÿคฃ" +/// inspect(s.get_char(0).unwrap(), content="H") +/// inspect(s.get_char(5).unwrap(), content="๐Ÿคฃ") /// ``` /// /// # Panics @@ -434,9 +325,9 @@ pub fn String::codepoint_at(self : String, index : Int) -> Char { char_count < charcode_len && utf16_offset < index char_count = char_count + 1, utf16_offset = utf16_offset + 1 { let c1 = self.unsafe_charcode_at(char_count) - if is_leading_surrogate(c1) && char_count + 1 < charcode_len { + if c1.is_leading_surrogate() && char_count + 1 < charcode_len { let c2 = self.unsafe_charcode_at(char_count + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { continue char_count + 2, utf16_offset + 1 } else { abort("invalid surrogate pair") @@ -447,9 +338,9 @@ pub fn String::codepoint_at(self : String, index : Int) -> Char { abort("index out of bounds") } let c1 = self.unsafe_charcode_at(char_count) - if is_leading_surrogate(c1) && char_count + 1 < charcode_len { + if c1.is_leading_surrogate() && char_count + 1 < charcode_len { let c2 = self.unsafe_charcode_at(char_count + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { code_point_of_surrogate_pair(c1, c2) } else { abort("invalid surrogate pair") @@ -461,11 +352,14 @@ pub fn String::codepoint_at(self : String, index : Int) -> Char { } ///| -#deprecated("Use `char_length` instead.") -pub fn String::codepoint_length( - self : String, - start_offset~ : Int = 0, - end_offset? : Int -) -> Int { - self.char_length(start_offset~, end_offset?) +/// Returns the Unicode code point at the given index without bounds checking. +#deprecated("Use `s.get_char(i).unwrap()` instead") +pub fn String::unsafe_char_at(self : String, index : Int) -> Char { + let c1 = self.unsafe_charcode_at(index) + if c1.is_leading_surrogate() { + let c2 = self.unsafe_charcode_at(index + 1) + code_point_of_surrogate_pair(c1, c2) + } else { + c1.unsafe_to_char() + } } diff --git a/bundled-core/builtin/failure.mbt b/bundled-core/builtin/failure.mbt index b2019e6..9c63bf3 100644 --- a/bundled-core/builtin/failure.mbt +++ b/bundled-core/builtin/failure.mbt @@ -16,7 +16,7 @@ /// Represents a generic test failure type used primarily in test assertions and /// validations. /// -/// Since this is a type definition using `type!` syntax, it creates an error +/// Since this is a type definition using `suberror` syntax, it creates an error /// type `Failure` that wraps a `String` value containing the failure message. /// /// Parameters: @@ -26,12 +26,13 @@ /// Example: /// /// ```moonbit -/// let err : Failure = Failure("Test assertion failed") -/// match err { -/// Failure(msg) => inspect(msg, content="Test assertion failed") -/// } +/// let err : Failure = Failure("Test assertion failed") +/// match err { +/// Failure(msg) => inspect(msg, content="Test assertion failed") +/// } +/// @json.inspect(err, content=["Failure", "Test assertion failed"]) /// ``` -pub(all) suberror Failure String derive(ToJson, Show) +pub(all) suberror Failure String derive(ToJson(style="flat"), Show) ///| /// Raises a `Failure` error with a given message and source location. @@ -47,6 +48,7 @@ pub(all) suberror Failure String derive(ToJson, Show) /// /// Throws an error of type `Failure` with a message that includes both the /// source location and the provided error message. -pub fn[T] fail(msg : String, loc~ : SourceLoc = _) -> T raise Failure { - raise Failure("FAILED: \{loc} \{msg}") +#callsite(autofill(loc)) +pub fn[T] fail(msg : String, loc~ : SourceLoc) -> T raise Failure { + raise Failure("\{loc} FAILED: \{msg}") } diff --git a/bundled-core/builtin/feature_test.mbt b/bundled-core/builtin/feature_test.mbt index 6f3933c..08b251c 100644 --- a/bundled-core/builtin/feature_test.mbt +++ b/bundled-core/builtin/feature_test.mbt @@ -18,10 +18,11 @@ struct RangeImpl[T] { hi : T } derive(Show, ToJson) -///| // pub // priv // default to abstract + +///| trait Range { range(Self, Self) -> RangeImpl[Self] } @@ -82,3 +83,13 @@ test "labeld_break/cross_threshold" { let xss = [[3.0, 4.0], [5.0, 6.0]] inspect(labeld_break(xss), content="Some(12)") } + +///| +test "try!" { + let a = "hello world" + fn hello() noraise { + try! a[1:] + } + + inspect(hello(), content="ello world") +} diff --git a/bundled-core/builtin/fixedarray.mbt b/bundled-core/builtin/fixedarray.mbt index 82aa646..c659028 100644 --- a/bundled-core/builtin/fixedarray.mbt +++ b/bundled-core/builtin/fixedarray.mbt @@ -117,15 +117,69 @@ pub impl[X] Default for FixedArray[X] with default() { ///| /// Fill the array with a given value. -/// +/// +/// This method fills all or part of a FixedArray with the given value. +/// +/// # Parameters +/// - `value`: The value to fill the array with +/// - `start`: The starting index (inclusive, default: 0) +/// - `end`: The ending index (exclusive, optional) +/// +/// If `end` is not provided, fills from `start` to the end of the array. +/// If `start` equals `end`, no elements are modified. +/// +/// # Panics +/// - Panics if `start` is negative or greater than or equal to the array length +/// - Panics if `end` is provided and is less than `start` or greater than array length +/// - Does nothing if the array is empty +/// /// # Example -/// ```mbt -/// let fa : FixedArray[Int] = [0, 0, 0, 0, 0] -/// fa.fill(3) -/// assert_eq(fa[0], 3) +/// ```moonbit +/// // Fill entire array +/// let fa : FixedArray[Int] = [0, 0, 0, 0, 0] +/// fa.fill(3) +/// inspect(fa, content="[3, 3, 3, 3, 3]") +/// +/// // Fill from index 1 to 3 (exclusive) +/// let fa2 : FixedArray[Int] = [0, 0, 0, 0, 0] +/// fa2.fill(9, start=1, end=3) +/// inspect(fa2, content="[0, 9, 9, 0, 0]") +/// +/// // Fill from index 2 to end +/// let fa3 : FixedArray[String] = ["a", "b", "c", "d"] +/// fa3.fill("x", start=2) +/// inspect(fa3, content=( +/// #|["a", "b", "x", "x"] +/// )) /// ``` -pub fn[T] FixedArray::fill(self : FixedArray[T], value : T) -> Unit { - for i in 0.. Unit { + let array_length = self.length() + guard array_length > 0 else { return } + guard start >= 0 && start < array_length + let length = match end { + None => array_length - start + Some(e) => { + guard e >= start && e <= array_length + e - start + } + } + self.unchecked_fill(start, value, length) +} + +///| +#intrinsic("%fixedarray.fill") +fn[T] FixedArray::unchecked_fill( + self : FixedArray[T], + start : Int, + value : T, + len : Int, +) -> Unit { + for i in start..<(start + len) { self[i] = value } } @@ -177,7 +231,7 @@ pub fn[T] FixedArray::is_empty(self : FixedArray[T]) -> Bool { /// - If the array is not sorted, the returned result is undefined and should not be relied on. pub fn[T : Compare] FixedArray::binary_search( self : FixedArray[T], - value : T + value : T, ) -> Result[Int, Int] { let len = self.length() for i = 0, j = len; i < j; { @@ -224,23 +278,11 @@ pub fn[T : Compare] FixedArray::binary_search( /// ```moonbit /// let arr : FixedArray[Int] = [1, 3, 5, 7, 9] /// let find_3 = arr.binary_search_by((x) => { -/// if x < 3 { -/// -1 -/// } else if x > 3 { -/// 1 -/// } else { -/// 0 -/// } +/// x.compare(3) /// }) /// inspect(find_3, content="Ok(1)") /// let find_4 = arr.binary_search_by((x) => { -/// if x < 4 { -/// -1 -/// } else if x > 4 { -/// 1 -/// } else { -/// 0 -/// } +/// x.compare(4) /// }) /// inspect(find_4, content="Err(2)") /// ``` @@ -255,7 +297,7 @@ pub fn[T : Compare] FixedArray::binary_search( #locals(cmp) pub fn[T] FixedArray::binary_search_by( self : FixedArray[T], - cmp : (T) -> Int raise? + cmp : (T) -> Int raise?, ) -> Result[Int, Int] raise? { let len = self.length() for i = 0, j = len; i < j; { diff --git a/bundled-core/builtin/fixedarray_block.mbt b/bundled-core/builtin/fixedarray_block.mbt index 6b3aa20..88e4de1 100644 --- a/bundled-core/builtin/fixedarray_block.mbt +++ b/bundled-core/builtin/fixedarray_block.mbt @@ -40,7 +40,7 @@ pub fn[A] FixedArray::unsafe_blit( dst_offset : Int, src : FixedArray[A], src_offset : Int, - len : Int + len : Int, ) -> Unit { if physical_equal(dst, src) && dst_offset < src_offset { for i in 0.. Unit { for i = len - 1; i >= 0; i = i - 1 { dst[dst_offset + i] = src[src_offset + i] @@ -101,8 +101,8 @@ pub fn[A] FixedArray::blit_to( self : FixedArray[A], dst : FixedArray[A], len~ : Int, - src_offset~ : Int = 0, - dst_offset~ : Int = 0 + src_offset? : Int = 0, + dst_offset? : Int = 0, ) -> Unit { guard dst_offset >= 0 && src_offset >= 0 && diff --git a/bundled-core/builtin/fixedarray_test.mbt b/bundled-core/builtin/fixedarray_test.mbt index 5c7a8fd..143bb77 100644 --- a/bundled-core/builtin/fixedarray_test.mbt +++ b/bundled-core/builtin/fixedarray_test.mbt @@ -146,3 +146,110 @@ test "fixedarray_binary_search_by_test" { target = { num2: 49 } assert_eq(arr.binary_search_by(cmp), Err(4)) } + +///| +test "fixedarray_fill - basic functionality" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(42) + inspect(fa, content="[42, 42, 42, 42, 42]") +} + +///| +test "fixedarray_fill - with start parameter only" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(99, start=2) + inspect(fa, content="[1, 2, 99, 99, 99]") +} + +///| +test "fixedarray_fill - with start and end parameters" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(77, start=1, end=3) + inspect(fa, content="[1, 77, 77, 4, 5]") +} + +///| +test "fixedarray_fill - start equals end" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(88, start=2, end=2) + inspect(fa, content="[1, 2, 3, 4, 5]") // No change expected +} + +///| +test "fixedarray_fill - start at beginning" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(10, start=0, end=2) + inspect(fa, content="[10, 10, 3, 4, 5]") +} + +///| +test "fixedarray_fill - end at array length" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(20, start=3, end=5) + inspect(fa, content="[1, 2, 3, 20, 20]") +} + +///| +test "fixedarray_fill - single element" { + let fa : FixedArray[Int] = [100] + fa.fill(50) + inspect(fa, content="[50]") +} + +///| +test "fixedarray_fill - empty array" { + let fa : FixedArray[Int] = [] + fa.fill(123) + inspect(fa, content="[]") // Should remain empty +} + +///| +test "fixedarray_fill - with different types" { + let str_fa : FixedArray[String] = ["a", "b", "c", "d"] + str_fa.fill("x", start=1, end=3) + inspect(str_fa, content="[\"a\", \"x\", \"x\", \"d\"]") + let bool_fa : FixedArray[Bool] = [true, false, true, false] + bool_fa.fill(true, start=0, end=2) + inspect(bool_fa, content="[true, true, true, false]") +} + +///| +test "fixedarray_fill - boundary conditions start" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + // Test with start at last valid index + fa.fill(555, start=4, end=5) + inspect(fa, content="[1, 2, 3, 4, 555]") +} + +///| +test "fixedarray_fill - boundary conditions end" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + // Test with end at array length + fa.fill(666, start=2, end=5) + inspect(fa, content="[1, 2, 666, 666, 666]") +} + +///| +test "fixedarray_fill - full range explicit" { + let fa : FixedArray[Int] = [1, 2, 3, 4, 5] + fa.fill(777, start=0, end=5) + inspect(fa, content="[777, 777, 777, 777, 777]") +} + +///| +test "fixedarray_fill - test with FixedArray::make" { + let fa = FixedArray::make(4, 0) + fa.fill(42, start=1, end=3) + inspect(fa, content="[0, 42, 42, 0]") +} + +///| +test "fixedarray_fill - existing documentation example" { + let fa : FixedArray[Int] = [0, 0, 0, 0, 0] + fa.fill(3) + assert_eq(fa[0], 3) + assert_eq(fa[1], 3) + assert_eq(fa[2], 3) + assert_eq(fa[3], 3) + assert_eq(fa[4], 3) +} diff --git a/bundled-core/builtin/guard_feature_test.mbt b/bundled-core/builtin/guard_feature_test.mbt index 14edab1..dc13529 100644 --- a/bundled-core/builtin/guard_feature_test.mbt +++ b/bundled-core/builtin/guard_feature_test.mbt @@ -20,7 +20,7 @@ enum Expr { Div(Expr, Expr) Lit(Int) Var(String) -} derive(Show, ToJson, Eq) +} derive(Show, ToJson(style="flat"), Eq) ///| fn simplify(e : Expr) -> Expr { @@ -42,6 +42,11 @@ fn simplify(e : Expr) -> Expr { test "simplify" { let _ignored = Add(Div(Lit(1), Lit(2)), Div(Lit(1), Lit(2))) let e = Sub(Mul(Lit(1), Var("x")), Mul(Lit(1), Var("x"))) + @json.inspect(e, content=[ + "Sub", + ["Mul", ["Lit", 1], ["Var", "x"]], + ["Mul", ["Lit", 1], ["Var", "x"]], + ]) let simplified = simplify(e) - inspect(simplified, content="Lit(0)") + @json.inspect(simplified, content=["Lit", 0]) } diff --git a/bundled-core/builtin/hasher.mbt b/bundled-core/builtin/hasher.mbt index 3a96198..1e9068b 100644 --- a/bundled-core/builtin/hasher.mbt +++ b/bundled-core/builtin/hasher.mbt @@ -68,7 +68,7 @@ struct Hasher { /// h2.combine(x) /// inspect(h1.finalize() != h2.finalize(), content="true") // Different seeds produce different hashes /// ``` -pub fn Hasher::new(seed~ : Int = 0) -> Hasher { +pub fn Hasher::new(seed? : Int = 0) -> Hasher { { acc: seed.reinterpret_as_uint() + GPRIME5 } } @@ -462,13 +462,6 @@ pub impl Hash for String with hash_combine(self, hasher) { /// let y = -42 /// inspect(Hash::hash(y), content="1617647962") /// ``` -/// TODO: This implementation is **different** from the default implementation of the hash trait. -/// So it will be replaced with the default implementation in the future **(breaking change)**, -/// and users should not rely on this particular hash value -/// ```moonbit -/// let x = 42 -/// assert_not_eq(Hash::hash(x),Hasher::new()..combine(x).finalize()) -/// ``` pub impl Hash for Int with hash(self) { let self = self.reinterpret_as_uint() let mut x = self ^ (self >> 17) @@ -595,7 +588,7 @@ pub impl[X : Hash] Hash for X? with hash_combine(self, hasher) { /// ``` pub impl[T : Hash, E : Hash] Hash for Result[T, E] with hash_combine( self, - hasher + hasher, ) { match self { Ok(x) => hasher..combine_int(0)..combine(x) diff --git a/bundled-core/builtin/int.mbt b/bundled-core/builtin/int.mbt new file mode 100644 index 0000000..1f8fbea --- /dev/null +++ b/bundled-core/builtin/int.mbt @@ -0,0 +1,142 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// Returns the smallest power of two greater than or equal to `self`. +/// This function will panic if `self` is negative. For values greater than +/// the largest representable power of two (2^30 = 1073741824), it returns +/// the largest representable power of two. +/// +/// Example: +/// ```moonbit +/// inspect((0).next_power_of_two(), content="1") +/// inspect((1).next_power_of_two(), content="1") +/// inspect((2).next_power_of_two(), content="2") +/// inspect((3).next_power_of_two(), content="4") +/// inspect((8).next_power_of_two(), content="8") +/// inspect((1073741824).next_power_of_two(), content="1073741824") +/// inspect((2000000000).next_power_of_two(), content="1073741824") +/// ``` +pub fn Int::next_power_of_two(self : Int) -> Int { + guard self >= 0 + if self <= 1 { + return 1 + } + // The largest power of 2 that fits in a 32-bit signed integer is 2^30 + let max_power_of_two = 1073741824 // 2^30 + if self > max_power_of_two { + return max_power_of_two + } + // 2147483647 is the largest value of an integer + (2147483647 >> ((self - 1).clz() - 1)) + 1 +} + +///| +/// Returns the minimum of two integers. +/// +/// Example: +/// ```moonbit +/// inspect((1).min(2), content="1") +/// inspect((2).min(1), content="1") +/// ``` +pub fn Int::min(self : Int, other : Int) -> Int { + if self < other { + self + } else { + other + } +} + +///| +/// Returns the maximum of two integers. +/// +/// Example: +/// ```moonbit +/// inspect((1).max(2), content="2") +/// inspect((2).max(1), content="2") +/// ``` +pub fn Int::max(self : Int, other : Int) -> Int { + if self > other { + self + } else { + other + } +} + +///| +/// Clamps the value `self` between `min` and `max`. +/// +/// Example: +/// ```moonbit +/// inspect((1).clamp(min=0, max=2), content="1") +/// inspect((-1).clamp(min=0, max=2), content="0") +/// inspect((3).clamp(min=0, max=2), content="2") +/// inspect((-1).clamp(min=0, max=2), content="0") +/// ``` +pub fn Int::clamp(self : Int, min~ : Int, max~ : Int) -> Int { + guard min <= max + if self < min { + min + } else if self > max { + max + } else { + self + } +} + +///| +/// Checks if the integer value represents a UTF-16 leading surrogate. +/// Leading surrogates are in the range 0xD800 to 0xDBFF. +/// +/// Example: +/// ```moonbit +/// inspect((0xD800).is_leading_surrogate(), content="true") +/// inspect((0xDBFF).is_leading_surrogate(), content="true") +/// inspect((0xDC00).is_leading_surrogate(), content="false") +/// inspect((0x41).is_leading_surrogate(), content="false") // 'A' +/// ``` +pub fn Int::is_leading_surrogate(self : Int) -> Bool { + 0xD800 <= self && self <= 0xDBFF +} + +///| +/// Checks if the integer value represents a UTF-16 trailing surrogate. +/// Trailing surrogates are in the range 0xDC00 to 0xDFFF. +/// +/// Example: +/// ```moonbit +/// inspect((0xDC00).is_trailing_surrogate(), content="true") +/// inspect((0xDFFF).is_trailing_surrogate(), content="true") +/// inspect((0xD800).is_trailing_surrogate(), content="false") +/// inspect((0x41).is_trailing_surrogate(), content="false") // 'A' +/// ``` +pub fn Int::is_trailing_surrogate(self : Int) -> Bool { + 0xDC00 <= self && self <= 0xDFFF +} + +///| +/// Checks if the integer value represents any UTF-16 surrogate (leading or trailing). +/// Surrogates are in the range 0xD800 to 0xDFFF. +/// +/// Example: +/// ```moonbit +/// inspect((0xD800).is_surrogate(), content="true") // leading surrogate +/// inspect((0xDC00).is_surrogate(), content="true") // trailing surrogate +/// inspect((0xDFFF).is_surrogate(), content="true") // trailing surrogate +/// inspect((0x41).is_surrogate(), content="false") // 'A' +/// inspect((0x1F600).is_surrogate(), content="false") // ๐Ÿ˜€ emoji codepoint +/// ``` +pub fn Int::is_surrogate(self : Int) -> Bool { + 0xD800 <= self && self <= 0xDFFF +} diff --git a/bundled-core/builtin/int64_js.mbt b/bundled-core/builtin/int64_js.mbt index c8789e0..64bc32b 100644 --- a/bundled-core/builtin/int64_js.mbt +++ b/bundled-core/builtin/int64_js.mbt @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| //region MyInt64 + +///| priv struct MyInt64 { hi : Int lo : Int @@ -26,7 +27,7 @@ fn MyInt64::to_int64(self : MyInt64) -> Int64 = "%identity" fn MyInt64::from_int64(value : Int64) -> MyInt64 = "%identity" ///| -impl Neg for MyInt64 with op_neg(self : MyInt64) -> MyInt64 { +impl Neg for MyInt64 with neg(self : MyInt64) -> MyInt64 { if self.lo == 0 { { hi: self.hi.lnot() + 1, lo: 0 } } else { @@ -47,12 +48,12 @@ fn MyInt64::add_hi_lo(self : MyInt64, bhi : Int, blo : Int) -> MyInt64 { } ///| -impl Add for MyInt64 with op_add(self : MyInt64, other : MyInt64) -> MyInt64 { +impl Add for MyInt64 with add(self : MyInt64, other : MyInt64) -> MyInt64 { self.add_hi_lo(other.hi, other.lo) } ///| -impl Sub for MyInt64 with op_sub(self : MyInt64, other : MyInt64) -> MyInt64 { +impl Sub for MyInt64 with sub(self : MyInt64, other : MyInt64) -> MyInt64 { if other.lo == 0 { { hi: self.hi - other.hi, lo: self.lo } } else { @@ -61,7 +62,7 @@ impl Sub for MyInt64 with op_sub(self : MyInt64, other : MyInt64) -> MyInt64 { } ///| -impl Mul for MyInt64 with op_mul(self : MyInt64, other : MyInt64) -> MyInt64 { +impl Mul for MyInt64 with mul(self : MyInt64, other : MyInt64) -> MyInt64 { let { hi: ahi, lo: alo } = self let { hi: bhi, lo: blo } = other let ahi = ahi.reinterpret_as_uint() @@ -119,7 +120,7 @@ extern "js" fn get_int64_wasm_helper() -> Int64WasmHelper = #|} ///| -impl Div for MyInt64 with op_div(self : MyInt64, other : MyInt64) -> MyInt64 { +impl Div for MyInt64 with div(self : MyInt64, other : MyInt64) -> MyInt64 { let exports = get_int64_wasm_helper() let { hi: ahi, lo: alo } = self let { hi: bhi, lo: blo } = other @@ -139,7 +140,7 @@ fn MyInt64::div_u(self : MyInt64, other : MyInt64) -> MyInt64 { } ///| -impl Mod for MyInt64 with op_mod(self : MyInt64, other : MyInt64) -> MyInt64 { +impl Mod for MyInt64 with mod(self : MyInt64, other : MyInt64) -> MyInt64 { let exports = get_int64_wasm_helper() let { hi: ahi, lo: alo } = self let { hi: bhi, lo: blo } = other @@ -254,7 +255,7 @@ fn MyInt64::popcnt(self : MyInt64) -> Int { } ///| -impl Eq for MyInt64 with op_equal(self : MyInt64, other : MyInt64) -> Bool { +impl Eq for MyInt64 with equal(self : MyInt64, other : MyInt64) -> Bool { self.hi == other.hi && self.lo == other.lo } @@ -368,33 +369,33 @@ extern "js" fn MyInt64::convert_to_double(self : MyInt64) -> Double = //endregion ///| -pub impl Neg for Int64 with op_neg(self : Int64) -> Int64 { +pub impl Neg for Int64 with neg(self : Int64) -> Int64 { (-MyInt64::from_int64(self)).to_int64() } ///| -pub impl Add for Int64 with op_add(self : Int64, other : Int64) -> Int64 { - MyInt64::from_int64(self).op_add(MyInt64::from_int64(other)).to_int64() +pub impl Add for Int64 with add(self : Int64, other : Int64) -> Int64 { + MyInt64::from_int64(self).add(MyInt64::from_int64(other)).to_int64() } ///| -pub impl Sub for Int64 with op_sub(self : Int64, other : Int64) -> Int64 { - MyInt64::from_int64(self).op_sub(MyInt64::from_int64(other)).to_int64() +pub impl Sub for Int64 with sub(self : Int64, other : Int64) -> Int64 { + MyInt64::from_int64(self).sub(MyInt64::from_int64(other)).to_int64() } ///| -pub impl Mul for Int64 with op_mul(self : Int64, other : Int64) -> Int64 { - MyInt64::from_int64(self).op_mul(MyInt64::from_int64(other)).to_int64() +pub impl Mul for Int64 with mul(self : Int64, other : Int64) -> Int64 { + MyInt64::from_int64(self).mul(MyInt64::from_int64(other)).to_int64() } ///| -pub impl Div for Int64 with op_div(self : Int64, other : Int64) -> Int64 { - MyInt64::from_int64(self).op_div(MyInt64::from_int64(other)).to_int64() +pub impl Div for Int64 with div(self : Int64, other : Int64) -> Int64 { + MyInt64::from_int64(self).div(MyInt64::from_int64(other)).to_int64() } ///| -pub impl Mod for Int64 with op_mod(self : Int64, other : Int64) -> Int64 { - MyInt64::from_int64(self).op_mod(MyInt64::from_int64(other)).to_int64() +pub impl Mod for Int64 with mod(self : Int64, other : Int64) -> Int64 { + MyInt64::from_int64(self).mod(MyInt64::from_int64(other)).to_int64() } ///| @@ -453,12 +454,12 @@ pub fn Int64::asr(self : Int64, other : Int) -> Int64 { } ///| -pub impl Shr for Int64 with op_shr(self : Int64, other : Int) -> Int64 { +pub impl Shr for Int64 with shr(self : Int64, other : Int) -> Int64 { MyInt64::from_int64(self).asr(other).to_int64() } ///| -pub impl Shl for Int64 with op_shl(self : Int64, other : Int) -> Int64 { +pub impl Shl for Int64 with shl(self : Int64, other : Int) -> Int64 { MyInt64::from_int64(self).lsl(other).to_int64() } @@ -478,7 +479,7 @@ pub fn Int64::popcnt(self : Int64) -> Int { } ///| -pub impl Eq for Int64 with op_equal(self : Int64, other : Int64) -> Bool { +pub impl Eq for Int64 with equal(self : Int64, other : Int64) -> Bool { MyInt64::from_int64(self) == MyInt64::from_int64(other) } @@ -596,27 +597,27 @@ pub fn Int64::to_uint64(self : Int64) -> UInt64 = "%identity" pub fn Int64::reinterpret_as_uint64(self : Int64) -> UInt64 = "%identity" ///| -pub impl Add for UInt64 with op_add(self : UInt64, other : UInt64) -> UInt64 { - MyInt64::from_uint64(self).op_add(MyInt64::from_uint64(other)).to_uint64() +pub impl Add for UInt64 with add(self : UInt64, other : UInt64) -> UInt64 { + MyInt64::from_uint64(self).add(MyInt64::from_uint64(other)).to_uint64() } ///| -pub impl Sub for UInt64 with op_sub(self : UInt64, other : UInt64) -> UInt64 { - MyInt64::from_uint64(self).op_sub(MyInt64::from_uint64(other)).to_uint64() +pub impl Sub for UInt64 with sub(self : UInt64, other : UInt64) -> UInt64 { + MyInt64::from_uint64(self).sub(MyInt64::from_uint64(other)).to_uint64() } ///| -pub impl Mul for UInt64 with op_mul(self : UInt64, other : UInt64) -> UInt64 { - MyInt64::from_uint64(self).op_mul(MyInt64::from_uint64(other)).to_uint64() +pub impl Mul for UInt64 with mul(self : UInt64, other : UInt64) -> UInt64 { + MyInt64::from_uint64(self).mul(MyInt64::from_uint64(other)).to_uint64() } ///| -pub impl Div for UInt64 with op_div(self : UInt64, other : UInt64) -> UInt64 { +pub impl Div for UInt64 with div(self : UInt64, other : UInt64) -> UInt64 { MyInt64::from_uint64(self).div_u(MyInt64::from_uint64(other)).to_uint64() } ///| -pub impl Mod for UInt64 with op_mod(self : UInt64, other : UInt64) -> UInt64 { +pub impl Mod for UInt64 with mod(self : UInt64, other : UInt64) -> UInt64 { MyInt64::from_uint64(self).mod_u(MyInt64::from_uint64(other)).to_uint64() } @@ -649,8 +650,8 @@ pub impl Compare for UInt64 with compare(self : UInt64, other : UInt64) -> Int { } ///| -pub impl Eq for UInt64 with op_equal(self : UInt64, other : UInt64) -> Bool { - MyInt64::from_uint64(self).op_equal(MyInt64::from_uint64(other)) +pub impl Eq for UInt64 with equal(self : UInt64, other : UInt64) -> Bool { + MyInt64::from_uint64(self).equal(MyInt64::from_uint64(other)) } ///| @@ -707,12 +708,12 @@ pub fn UInt64::shr(self : UInt64, shift : Int) -> UInt64 { } ///| -pub impl Shl for UInt64 with op_shl(self : UInt64, shift : Int) -> UInt64 { +pub impl Shl for UInt64 with shl(self : UInt64, shift : Int) -> UInt64 { MyInt64::lsl(MyInt64::from_uint64(self), shift).to_uint64() } ///| -pub impl Shr for UInt64 with op_shr(self : UInt64, shift : Int) -> UInt64 { +pub impl Shr for UInt64 with shr(self : UInt64, shift : Int) -> UInt64 { MyInt64::lsr(MyInt64::from_uint64(self), shift).to_uint64() } diff --git a/bundled-core/builtin/int64_nonjs.mbt b/bundled-core/builtin/int64_nonjs.mbt index fa1b163..a7dd609 100644 --- a/bundled-core/builtin/int64_nonjs.mbt +++ b/bundled-core/builtin/int64_nonjs.mbt @@ -32,7 +32,7 @@ /// inspect(--42L, content="42") /// inspect(-9223372036854775808L, content="-9223372036854775808") // negating min value /// ``` -pub impl Neg for Int64 with op_neg(self : Int64) -> Int64 = "%i64_neg" +pub impl Neg for Int64 with neg(self : Int64) -> Int64 = "%i64_neg" ///| /// Adds two 64-bit integers together. Performs overflow checking and wrapping @@ -53,7 +53,7 @@ pub impl Neg for Int64 with op_neg(self : Int64) -> Int64 = "%i64_neg" /// inspect(a + b, content="-9223372036854775808") // Wraps around to minimum value /// inspect(42L + -42L, content="0") /// ``` -pub impl Add for Int64 with op_add(self, other) = "%i64_add" +pub impl Add for Int64 with add(self, other) = "%i64_add" ///| /// Performs subtraction between two 64-bit integers. @@ -75,7 +75,7 @@ pub impl Add for Int64 with op_add(self, other) = "%i64_add" /// let d = 1L /// inspect(c - d, content="9223372036854775807") /// ``` -pub impl Sub for Int64 with op_sub(self, other) = "%i64_sub" +pub impl Sub for Int64 with sub(self, other) = "%i64_sub" ///| /// Multiplies two 64-bit integers. @@ -98,7 +98,7 @@ pub impl Sub for Int64 with op_sub(self, other) = "%i64_sub" /// let c = -42L /// inspect(c * b, content="-4200") /// ``` -pub impl Mul for Int64 with op_mul(self, other) = "%i64_mul" +pub impl Mul for Int64 with mul(self, other) = "%i64_mul" ///| /// Performs integer division between two 64-bit integers. Truncates the result @@ -123,7 +123,7 @@ pub impl Mul for Int64 with op_mul(self, other) = "%i64_mul" /// let d = 5L /// inspect(c / d, content="-8") /// ``` -pub impl Div for Int64 with op_div(self, other) = "%i64_div" +pub impl Div for Int64 with div(self, other) = "%i64_div" ///| /// Calculates the remainder of the division between two 64-bit integers. The @@ -146,7 +146,7 @@ pub impl Div for Int64 with op_div(self, other) = "%i64_div" /// inspect(-7L % 3L, content="-1") /// inspect(7L % -3L, content="1") /// ``` -pub impl Mod for Int64 with op_mod(self, other) = "%i64_mod" +pub impl Mod for Int64 with mod(self, other) = "%i64_mod" ///| /// Performs a bitwise NOT operation on a 64-bit integer. Each bit in the input @@ -377,7 +377,7 @@ pub fn Int64::shr(self : Int64, other : Int) -> Int64 = "%i64_shr" /// let m = -4L /// inspect(m << 2, content="-16") // -4 shifted left by 2 positions becomes -16 /// ``` -pub impl Shl for Int64 with op_shl(self, other) = "%i64_shl" +pub impl Shl for Int64 with shl(self, other) = "%i64_shl" ///| /// Performs arithmetic right shift operation on a 64-bit integer value by a @@ -402,7 +402,7 @@ pub impl Shl for Int64 with op_shl(self, other) = "%i64_shl" /// let p = 1024L /// inspect(p >> 3, content="128") // Regular right shift for positive numbers /// ``` -pub impl Shr for Int64 with op_shr(self, other) = "%i64_shr" +pub impl Shr for Int64 with shr(self, other) = "%i64_shr" ///| /// Returns the number of trailing zero bits in a 64-bit integer. For zero input, @@ -486,7 +486,7 @@ pub fn Int64::popcnt(self : Int64) -> Int = "%i64_popcnt" /// inspect(a == b, content="true") /// inspect(a == c, content="false") /// ``` -pub impl Eq for Int64 with op_equal(self : Int64, other : Int64) -> Bool = "%i64_eq" +pub impl Eq for Int64 with equal(self : Int64, other : Int64) -> Bool = "%i64_eq" ///| /// Compares two 64-bit integers and returns their relative order. @@ -1083,7 +1083,7 @@ pub fn UInt64::to_double(self : UInt64) -> Double = "%u64.to_f64" /// let max = 18446744073709551615UL /// inspect(max + 1UL, content="0") /// ``` -pub impl Add for UInt64 with op_add(self, other) = "%u64.add" +pub impl Add for UInt64 with add(self, other) = "%u64.add" ///| /// Performs subtraction between two unsigned 64-bit integers. When the result @@ -1107,7 +1107,7 @@ pub impl Add for UInt64 with op_add(self, other) = "%u64.add" /// let d = 5UL /// inspect(c - d, content="18446744073709551614") // wraps around to 2^64 - 2 /// ``` -pub impl Sub for UInt64 with op_sub(self, other) = "%u64.sub" +pub impl Sub for UInt64 with sub(self, other) = "%u64.sub" ///| /// Performs multiplication between two unsigned 64-bit integers. @@ -1131,7 +1131,7 @@ pub impl Sub for UInt64 with op_sub(self, other) = "%u64.sub" /// let max = 18446744073709551615UL /// inspect(max * 2UL, content="18446744073709551614") // Wraps around to max - 1 /// ``` -pub impl Mul for UInt64 with op_mul(self, other) = "%u64.mul" +pub impl Mul for UInt64 with mul(self, other) = "%u64.mul" ///| /// Performs division operation between two unsigned 64-bit integers. @@ -1152,7 +1152,7 @@ pub impl Mul for UInt64 with op_mul(self, other) = "%u64.mul" /// let b = 20UL /// inspect(a / b, content="5") // Using infix operator /// ``` -pub impl Div for UInt64 with op_div(self, other) = "%u64.div" +pub impl Div for UInt64 with div(self, other) = "%u64.div" ///| /// Calculates the remainder of dividing one unsigned 64-bit integer by another. @@ -1174,7 +1174,7 @@ pub impl Div for UInt64 with op_div(self, other) = "%u64.div" /// inspect(a % b, content="2") // 17 divided by 5 gives quotient 3 and remainder 2 /// inspect(7UL % 4UL, content="3") /// ``` -pub impl Mod for UInt64 with op_mod(self, other) = "%u64.mod" +pub impl Mod for UInt64 with mod(self, other) = "%u64.mod" ///| /// Compares two unsigned 64-bit integers and determines their relative order. @@ -1220,7 +1220,7 @@ pub impl Compare for UInt64 with compare(self, other) = "%u64.compare" /// inspect(a == b, content="true") /// inspect(a == c, content="false") /// ``` -pub impl Eq for UInt64 with op_equal(self : UInt64, other : UInt64) -> Bool = "%u64.eq" +pub impl Eq for UInt64 with equal(self : UInt64, other : UInt64) -> Bool = "%u64.eq" ///| /// Performs a bitwise AND operation between two unsigned 64-bit integers. @@ -1419,7 +1419,7 @@ pub fn UInt64::lsr(self : UInt64, shift : Int) -> UInt64 = "%u64.shr" /// inspect(x << 3, content="8") // 1 shifted left by 3 positions becomes 8 /// inspect(x << 63, content="9223372036854775808") // 1 shifted left by 63 positions /// ``` -pub impl Shl for UInt64 with op_shl(self, shift) = "%u64.shl" +pub impl Shl for UInt64 with shl(self, shift) = "%u64.shl" ///| /// Performs a logical right shift operation on a 64-bit unsigned integer by a @@ -1443,7 +1443,7 @@ pub impl Shl for UInt64 with op_shl(self, shift) = "%u64.shl" /// inspect(x >> 8, content="71777214294589695") // Shifted right by 8 bits /// inspect(x >> 64, content="18374966859414961920") // Equivalent to x >> 0 due to masking /// ``` -pub impl Shr for UInt64 with op_shr(self, shift) = "%u64.shr" +pub impl Shr for UInt64 with shr(self, shift) = "%u64.shr" ///| /// Counts the number of leading zero bits in a 64-bit unsigned integer, starting diff --git a/bundled-core/builtin/int64_test.mbt b/bundled-core/builtin/int64_test.mbt index 1e47094..469dd35 100644 --- a/bundled-core/builtin/int64_test.mbt +++ b/bundled-core/builtin/int64_test.mbt @@ -123,607 +123,607 @@ test "-9223372036854775807 = -9223372036854775807" { ///| test "0 + 0 = 0" { - assert_eq(0L.op_add(0L), 0L) + assert_eq(0L.add(0L), 0L) } ///| test "0 + -1 = -1" { - assert_eq(0L.op_add(-1L), -1L) + assert_eq(0L.add(-1L), -1L) } ///| test "0 + 1 = 1" { - assert_eq(0L.op_add(1L), 1L) + assert_eq(0L.add(1L), 1L) } ///| test "0 + 2147483647 = 2147483647" { - assert_eq(0L.op_add(2147483647L), 2147483647L) + assert_eq(0L.add(2147483647L), 2147483647L) } ///| test "0 + -2147483648 = -2147483648" { - assert_eq(0L.op_add(-2147483648L), -2147483648L) + assert_eq(0L.add(-2147483648L), -2147483648L) } ///| test "0 + 2147483648 = 2147483648" { - assert_eq(0L.op_add(2147483648L), 2147483648L) + assert_eq(0L.add(2147483648L), 2147483648L) } ///| test "0 + -2147483649 = -2147483649" { - assert_eq(0L.op_add(-2147483649L), -2147483649L) + assert_eq(0L.add(-2147483649L), -2147483649L) } ///| test "0 + 9223372036854775807 = 9223372036854775807" { - assert_eq(0L.op_add(9223372036854775807L), 9223372036854775807L) + assert_eq(0L.add(9223372036854775807L), 9223372036854775807L) } ///| test "0 + -9223372036854775808 = -9223372036854775808" { - assert_eq(0L.op_add(-9223372036854775808L), -9223372036854775808L) + assert_eq(0L.add(-9223372036854775808L), -9223372036854775808L) } ///| test "0 + -9223372036854775808 = -9223372036854775808" { - assert_eq(0L.op_add(-9223372036854775808L), -9223372036854775808L) + assert_eq(0L.add(-9223372036854775808L), -9223372036854775808L) } ///| test "0 + 9223372036854775807 = 9223372036854775807" { - assert_eq(0L.op_add(9223372036854775807L), 9223372036854775807L) + assert_eq(0L.add(9223372036854775807L), 9223372036854775807L) } ///| test "-1 + 0 = -1" { - assert_eq((-1L).op_add(0L), -1L) + assert_eq((-1L).add(0L), -1L) } ///| test "-1 + -1 = -2" { - assert_eq((-1L).op_add(-1L), -2L) + assert_eq((-1L).add(-1L), -2L) } ///| test "-1 + 1 = 0" { - assert_eq((-1L).op_add(1L), 0L) + assert_eq((-1L).add(1L), 0L) } ///| test "-1 + 2147483647 = 2147483646" { - assert_eq((-1L).op_add(2147483647L), 2147483646L) + assert_eq((-1L).add(2147483647L), 2147483646L) } ///| test "-1 + -2147483648 = -2147483649" { - assert_eq((-1L).op_add(-2147483648L), -2147483649L) + assert_eq((-1L).add(-2147483648L), -2147483649L) } ///| test "-1 + 2147483648 = 2147483647" { - assert_eq((-1L).op_add(2147483648L), 2147483647L) + assert_eq((-1L).add(2147483648L), 2147483647L) } ///| test "-1 + -2147483649 = -2147483650" { - assert_eq((-1L).op_add(-2147483649L), -2147483650L) + assert_eq((-1L).add(-2147483649L), -2147483650L) } ///| test "-1 + 9223372036854775807 = 9223372036854775806" { - assert_eq((-1L).op_add(9223372036854775807L), 9223372036854775806L) + assert_eq((-1L).add(9223372036854775807L), 9223372036854775806L) } ///| test "-1 + -9223372036854775808 = 9223372036854775807" { - assert_eq((-1L).op_add(-9223372036854775808L), 9223372036854775807L) + assert_eq((-1L).add(-9223372036854775808L), 9223372036854775807L) } ///| test "-1 + -9223372036854775808 = 9223372036854775807" { - assert_eq((-1L).op_add(-9223372036854775808L), 9223372036854775807L) + assert_eq((-1L).add(-9223372036854775808L), 9223372036854775807L) } ///| test "-1 + 9223372036854775807 = 9223372036854775806" { - assert_eq((-1L).op_add(9223372036854775807L), 9223372036854775806L) + assert_eq((-1L).add(9223372036854775807L), 9223372036854775806L) } ///| test "1 + 0 = 1" { - assert_eq(1L.op_add(0L), 1L) + assert_eq(1L.add(0L), 1L) } ///| test "1 + -1 = 0" { - assert_eq(1L.op_add(-1L), 0L) + assert_eq(1L.add(-1L), 0L) } ///| test "1 + 1 = 2" { - assert_eq(1L.op_add(1L), 2L) + assert_eq(1L.add(1L), 2L) } ///| test "1 + 2147483647 = 2147483648" { - assert_eq(1L.op_add(2147483647L), 2147483648L) + assert_eq(1L.add(2147483647L), 2147483648L) } ///| test "1 + -2147483648 = -2147483647" { - assert_eq(1L.op_add(-2147483648L), -2147483647L) + assert_eq(1L.add(-2147483648L), -2147483647L) } ///| test "1 + 2147483648 = 2147483649" { - assert_eq(1L.op_add(2147483648L), 2147483649L) + assert_eq(1L.add(2147483648L), 2147483649L) } ///| test "1 + -2147483649 = -2147483648" { - assert_eq(1L.op_add(-2147483649L), -2147483648L) + assert_eq(1L.add(-2147483649L), -2147483648L) } ///| test "1 + 9223372036854775807 = -9223372036854775808" { - assert_eq(1L.op_add(9223372036854775807L), -9223372036854775808L) + assert_eq(1L.add(9223372036854775807L), -9223372036854775808L) } ///| test "1 + -9223372036854775808 = -9223372036854775807" { - assert_eq(1L.op_add(-9223372036854775808L), -9223372036854775807L) + assert_eq(1L.add(-9223372036854775808L), -9223372036854775807L) } ///| test "1 + -9223372036854775808 = -9223372036854775807" { - assert_eq(1L.op_add(-9223372036854775808L), -9223372036854775807L) + assert_eq(1L.add(-9223372036854775808L), -9223372036854775807L) } ///| test "1 + 9223372036854775807 = -9223372036854775808" { - assert_eq(1L.op_add(9223372036854775807L), -9223372036854775808L) + assert_eq(1L.add(9223372036854775807L), -9223372036854775808L) } ///| test "2147483647 + 0 = 2147483647" { - assert_eq(2147483647L.op_add(0L), 2147483647L) + assert_eq(2147483647L.add(0L), 2147483647L) } ///| test "2147483647 + -1 = 2147483646" { - assert_eq(2147483647L.op_add(-1L), 2147483646L) + assert_eq(2147483647L.add(-1L), 2147483646L) } ///| test "2147483647 + 1 = 2147483648" { - assert_eq(2147483647L.op_add(1L), 2147483648L) + assert_eq(2147483647L.add(1L), 2147483648L) } ///| test "2147483647 + 2147483647 = 4294967294" { - assert_eq(2147483647L.op_add(2147483647L), 4294967294L) + assert_eq(2147483647L.add(2147483647L), 4294967294L) } ///| test "2147483647 + -2147483648 = -1" { - assert_eq(2147483647L.op_add(-2147483648L), -1L) + assert_eq(2147483647L.add(-2147483648L), -1L) } ///| test "2147483647 + 2147483648 = 4294967295" { - assert_eq(2147483647L.op_add(2147483648L), 4294967295L) + assert_eq(2147483647L.add(2147483648L), 4294967295L) } ///| test "2147483647 + -2147483649 = -2" { - assert_eq(2147483647L.op_add(-2147483649L), -2L) + assert_eq(2147483647L.add(-2147483649L), -2L) } ///| test "2147483647 + 9223372036854775807 = -9223372034707292162" { - assert_eq(2147483647L.op_add(9223372036854775807L), -9223372034707292162L) + assert_eq(2147483647L.add(9223372036854775807L), -9223372034707292162L) } ///| test "2147483647 + -9223372036854775808 = -9223372034707292161" { - assert_eq(2147483647L.op_add(-9223372036854775808L), -9223372034707292161L) + assert_eq(2147483647L.add(-9223372036854775808L), -9223372034707292161L) } ///| test "2147483647 + -9223372036854775808 = -9223372034707292161" { - assert_eq(2147483647L.op_add(-9223372036854775808L), -9223372034707292161L) + assert_eq(2147483647L.add(-9223372036854775808L), -9223372034707292161L) } ///| test "2147483647 + 9223372036854775807 = -9223372034707292162" { - assert_eq(2147483647L.op_add(9223372036854775807L), -9223372034707292162L) + assert_eq(2147483647L.add(9223372036854775807L), -9223372034707292162L) } ///| test "-2147483648 + 0 = -2147483648" { - assert_eq((-2147483648L).op_add(0L), -2147483648L) + assert_eq((-2147483648L).add(0L), -2147483648L) } ///| test "-2147483648 + -1 = -2147483649" { - assert_eq((-2147483648L).op_add(-1L), -2147483649L) + assert_eq((-2147483648L).add(-1L), -2147483649L) } ///| test "-2147483648 + 1 = -2147483647" { - assert_eq((-2147483648L).op_add(1L), -2147483647L) + assert_eq((-2147483648L).add(1L), -2147483647L) } ///| test "-2147483648 + 2147483647 = -1" { - assert_eq((-2147483648L).op_add(2147483647L), -1L) + assert_eq((-2147483648L).add(2147483647L), -1L) } ///| test "-2147483648 + -2147483648 = -4294967296" { - assert_eq((-2147483648L).op_add(-2147483648L), -4294967296L) + assert_eq((-2147483648L).add(-2147483648L), -4294967296L) } ///| test "-2147483648 + 2147483648 = 0" { - assert_eq((-2147483648L).op_add(2147483648L), 0L) + assert_eq((-2147483648L).add(2147483648L), 0L) } ///| test "-2147483648 + -2147483649 = -4294967297" { - assert_eq((-2147483648L).op_add(-2147483649L), -4294967297L) + assert_eq((-2147483648L).add(-2147483649L), -4294967297L) } ///| test "-2147483648 + 9223372036854775807 = 9223372034707292159" { - assert_eq((-2147483648L).op_add(9223372036854775807L), 9223372034707292159L) + assert_eq((-2147483648L).add(9223372036854775807L), 9223372034707292159L) } ///| test "-2147483648 + -9223372036854775808 = 9223372034707292160" { - assert_eq((-2147483648L).op_add(-9223372036854775808L), 9223372034707292160L) + assert_eq((-2147483648L).add(-9223372036854775808L), 9223372034707292160L) } ///| test "-2147483648 + -9223372036854775808 = 9223372034707292160" { - assert_eq((-2147483648L).op_add(-9223372036854775808L), 9223372034707292160L) + assert_eq((-2147483648L).add(-9223372036854775808L), 9223372034707292160L) } ///| test "-2147483648 + 9223372036854775807 = 9223372034707292159" { - assert_eq((-2147483648L).op_add(9223372036854775807L), 9223372034707292159L) + assert_eq((-2147483648L).add(9223372036854775807L), 9223372034707292159L) } ///| test "2147483648 + 0 = 2147483648" { - assert_eq(2147483648L.op_add(0L), 2147483648L) + assert_eq(2147483648L.add(0L), 2147483648L) } ///| test "2147483648 + -1 = 2147483647" { - assert_eq(2147483648L.op_add(-1L), 2147483647L) + assert_eq(2147483648L.add(-1L), 2147483647L) } ///| test "2147483648 + 1 = 2147483649" { - assert_eq(2147483648L.op_add(1L), 2147483649L) + assert_eq(2147483648L.add(1L), 2147483649L) } ///| test "2147483648 + 2147483647 = 4294967295" { - assert_eq(2147483648L.op_add(2147483647L), 4294967295L) + assert_eq(2147483648L.add(2147483647L), 4294967295L) } ///| test "2147483648 + -2147483648 = 0" { - assert_eq(2147483648L.op_add(-2147483648L), 0L) + assert_eq(2147483648L.add(-2147483648L), 0L) } ///| test "2147483648 + 2147483648 = 4294967296" { - assert_eq(2147483648L.op_add(2147483648L), 4294967296L) + assert_eq(2147483648L.add(2147483648L), 4294967296L) } ///| test "2147483648 + -2147483649 = -1" { - assert_eq(2147483648L.op_add(-2147483649L), -1L) + assert_eq(2147483648L.add(-2147483649L), -1L) } ///| test "2147483648 + 9223372036854775807 = -9223372034707292161" { - assert_eq(2147483648L.op_add(9223372036854775807L), -9223372034707292161L) + assert_eq(2147483648L.add(9223372036854775807L), -9223372034707292161L) } ///| test "2147483648 + -9223372036854775808 = -9223372034707292160" { - assert_eq(2147483648L.op_add(-9223372036854775808L), -9223372034707292160L) + assert_eq(2147483648L.add(-9223372036854775808L), -9223372034707292160L) } ///| test "2147483648 + -9223372036854775808 = -9223372034707292160" { - assert_eq(2147483648L.op_add(-9223372036854775808L), -9223372034707292160L) + assert_eq(2147483648L.add(-9223372036854775808L), -9223372034707292160L) } ///| test "2147483648 + 9223372036854775807 = -9223372034707292161" { - assert_eq(2147483648L.op_add(9223372036854775807L), -9223372034707292161L) + assert_eq(2147483648L.add(9223372036854775807L), -9223372034707292161L) } ///| test "-2147483649 + 0 = -2147483649" { - assert_eq((-2147483649L).op_add(0L), -2147483649L) + assert_eq((-2147483649L).add(0L), -2147483649L) } ///| test "-2147483649 + -1 = -2147483650" { - assert_eq((-2147483649L).op_add(-1L), -2147483650L) + assert_eq((-2147483649L).add(-1L), -2147483650L) } ///| test "-2147483649 + 1 = -2147483648" { - assert_eq((-2147483649L).op_add(1L), -2147483648L) + assert_eq((-2147483649L).add(1L), -2147483648L) } ///| test "-2147483649 + 2147483647 = -2" { - assert_eq((-2147483649L).op_add(2147483647L), -2L) + assert_eq((-2147483649L).add(2147483647L), -2L) } ///| test "-2147483649 + -2147483648 = -4294967297" { - assert_eq((-2147483649L).op_add(-2147483648L), -4294967297L) + assert_eq((-2147483649L).add(-2147483648L), -4294967297L) } ///| test "-2147483649 + 2147483648 = -1" { - assert_eq((-2147483649L).op_add(2147483648L), -1L) + assert_eq((-2147483649L).add(2147483648L), -1L) } ///| test "-2147483649 + -2147483649 = -4294967298" { - assert_eq((-2147483649L).op_add(-2147483649L), -4294967298L) + assert_eq((-2147483649L).add(-2147483649L), -4294967298L) } ///| test "-2147483649 + 9223372036854775807 = 9223372034707292158" { - assert_eq((-2147483649L).op_add(9223372036854775807L), 9223372034707292158L) + assert_eq((-2147483649L).add(9223372036854775807L), 9223372034707292158L) } ///| test "-2147483649 + -9223372036854775808 = 9223372034707292159" { - assert_eq((-2147483649L).op_add(-9223372036854775808L), 9223372034707292159L) + assert_eq((-2147483649L).add(-9223372036854775808L), 9223372034707292159L) } ///| test "-2147483649 + -9223372036854775808 = 9223372034707292159" { - assert_eq((-2147483649L).op_add(-9223372036854775808L), 9223372034707292159L) + assert_eq((-2147483649L).add(-9223372036854775808L), 9223372034707292159L) } ///| test "-2147483649 + 9223372036854775807 = 9223372034707292158" { - assert_eq((-2147483649L).op_add(9223372036854775807L), 9223372034707292158L) + assert_eq((-2147483649L).add(9223372036854775807L), 9223372034707292158L) } ///| test "9223372036854775807 + 0 = 9223372036854775807" { - assert_eq(9223372036854775807L.op_add(0L), 9223372036854775807L) + assert_eq(9223372036854775807L.add(0L), 9223372036854775807L) } ///| test "9223372036854775807 + -1 = 9223372036854775806" { - assert_eq(9223372036854775807L.op_add(-1L), 9223372036854775806L) + assert_eq(9223372036854775807L.add(-1L), 9223372036854775806L) } ///| test "9223372036854775807 + 1 = -9223372036854775808" { - assert_eq(9223372036854775807L.op_add(1L), -9223372036854775808L) + assert_eq(9223372036854775807L.add(1L), -9223372036854775808L) } ///| test "9223372036854775807 + 2147483647 = -9223372034707292162" { - assert_eq(9223372036854775807L.op_add(2147483647L), -9223372034707292162L) + assert_eq(9223372036854775807L.add(2147483647L), -9223372034707292162L) } ///| test "9223372036854775807 + -2147483648 = 9223372034707292159" { - assert_eq(9223372036854775807L.op_add(-2147483648L), 9223372034707292159L) + assert_eq(9223372036854775807L.add(-2147483648L), 9223372034707292159L) } ///| test "9223372036854775807 + 2147483648 = -9223372034707292161" { - assert_eq(9223372036854775807L.op_add(2147483648L), -9223372034707292161L) + assert_eq(9223372036854775807L.add(2147483648L), -9223372034707292161L) } ///| test "9223372036854775807 + -2147483649 = 9223372034707292158" { - assert_eq(9223372036854775807L.op_add(-2147483649L), 9223372034707292158L) + assert_eq(9223372036854775807L.add(-2147483649L), 9223372034707292158L) } ///| test "9223372036854775807 + 9223372036854775807 = -2" { - assert_eq(9223372036854775807L.op_add(9223372036854775807L), -2L) + assert_eq(9223372036854775807L.add(9223372036854775807L), -2L) } ///| test "9223372036854775807 + -9223372036854775808 = -1" { - assert_eq(9223372036854775807L.op_add(-9223372036854775808L), -1L) + assert_eq(9223372036854775807L.add(-9223372036854775808L), -1L) } ///| test "9223372036854775807 + -9223372036854775808 = -1" { - assert_eq(9223372036854775807L.op_add(-9223372036854775808L), -1L) + assert_eq(9223372036854775807L.add(-9223372036854775808L), -1L) } ///| test "9223372036854775807 + 9223372036854775807 = -2" { - assert_eq(9223372036854775807L.op_add(9223372036854775807L), -2L) + assert_eq(9223372036854775807L.add(9223372036854775807L), -2L) } ///| test "-9223372036854775808 + 0 = -9223372036854775808" { - assert_eq((-9223372036854775808L).op_add(0L), -9223372036854775808L) + assert_eq((-9223372036854775808L).add(0L), -9223372036854775808L) } ///| test "-9223372036854775808 + -1 = 9223372036854775807" { - assert_eq((-9223372036854775808L).op_add(-1L), 9223372036854775807L) + assert_eq((-9223372036854775808L).add(-1L), 9223372036854775807L) } ///| test "-9223372036854775808 + 1 = -9223372036854775807" { - assert_eq((-9223372036854775808L).op_add(1L), -9223372036854775807L) + assert_eq((-9223372036854775808L).add(1L), -9223372036854775807L) } ///| test "-9223372036854775808 + 2147483647 = -9223372034707292161" { - assert_eq((-9223372036854775808L).op_add(2147483647L), -9223372034707292161L) + assert_eq((-9223372036854775808L).add(2147483647L), -9223372034707292161L) } ///| test "-9223372036854775808 + -2147483648 = 9223372034707292160" { - assert_eq((-9223372036854775808L).op_add(-2147483648L), 9223372034707292160L) + assert_eq((-9223372036854775808L).add(-2147483648L), 9223372034707292160L) } ///| test "-9223372036854775808 + 2147483648 = -9223372034707292160" { - assert_eq((-9223372036854775808L).op_add(2147483648L), -9223372034707292160L) + assert_eq((-9223372036854775808L).add(2147483648L), -9223372034707292160L) } ///| test "-9223372036854775808 + -2147483649 = 9223372034707292159" { - assert_eq((-9223372036854775808L).op_add(-2147483649L), 9223372034707292159L) + assert_eq((-9223372036854775808L).add(-2147483649L), 9223372034707292159L) } ///| test "-9223372036854775808 + 9223372036854775807 = -1" { - assert_eq((-9223372036854775808L).op_add(9223372036854775807L), -1L) + assert_eq((-9223372036854775808L).add(9223372036854775807L), -1L) } ///| test "-9223372036854775808 + -9223372036854775808 = 0" { - assert_eq((-9223372036854775808L).op_add(-9223372036854775808L), 0L) + assert_eq((-9223372036854775808L).add(-9223372036854775808L), 0L) } ///| test "-9223372036854775808 + -9223372036854775808 = 0" { - assert_eq((-9223372036854775808L).op_add(-9223372036854775808L), 0L) + assert_eq((-9223372036854775808L).add(-9223372036854775808L), 0L) } ///| test "-9223372036854775808 + 9223372036854775807 = -1" { - assert_eq((-9223372036854775808L).op_add(9223372036854775807L), -1L) + assert_eq((-9223372036854775808L).add(9223372036854775807L), -1L) } ///| test "-9223372036854775808 + 0 = -9223372036854775808" { - assert_eq((-9223372036854775808L).op_add(0L), -9223372036854775808L) + assert_eq((-9223372036854775808L).add(0L), -9223372036854775808L) } ///| test "-9223372036854775808 + -1 = 9223372036854775807" { - assert_eq((-9223372036854775808L).op_add(-1L), 9223372036854775807L) + assert_eq((-9223372036854775808L).add(-1L), 9223372036854775807L) } ///| test "-9223372036854775808 + 1 = -9223372036854775807" { - assert_eq((-9223372036854775808L).op_add(1L), -9223372036854775807L) + assert_eq((-9223372036854775808L).add(1L), -9223372036854775807L) } ///| test "-9223372036854775808 + 2147483647 = -9223372034707292161" { - assert_eq((-9223372036854775808L).op_add(2147483647L), -9223372034707292161L) + assert_eq((-9223372036854775808L).add(2147483647L), -9223372034707292161L) } ///| test "-9223372036854775808 + -2147483648 = 9223372034707292160" { - assert_eq((-9223372036854775808L).op_add(-2147483648L), 9223372034707292160L) + assert_eq((-9223372036854775808L).add(-2147483648L), 9223372034707292160L) } ///| test "-9223372036854775808 + 2147483648 = -9223372034707292160" { - assert_eq((-9223372036854775808L).op_add(2147483648L), -9223372034707292160L) + assert_eq((-9223372036854775808L).add(2147483648L), -9223372034707292160L) } ///| test "-9223372036854775808 + -2147483649 = 9223372034707292159" { - assert_eq((-9223372036854775808L).op_add(-2147483649L), 9223372034707292159L) + assert_eq((-9223372036854775808L).add(-2147483649L), 9223372034707292159L) } ///| test "-9223372036854775808 + 9223372036854775807 = -1" { - assert_eq((-9223372036854775808L).op_add(9223372036854775807L), -1L) + assert_eq((-9223372036854775808L).add(9223372036854775807L), -1L) } ///| test "-9223372036854775808 + -9223372036854775808 = 0" { - assert_eq((-9223372036854775808L).op_add(-9223372036854775808L), 0L) + assert_eq((-9223372036854775808L).add(-9223372036854775808L), 0L) } ///| test "-9223372036854775808 + -9223372036854775808 = 0" { - assert_eq((-9223372036854775808L).op_add(-9223372036854775808L), 0L) + assert_eq((-9223372036854775808L).add(-9223372036854775808L), 0L) } ///| test "-9223372036854775808 + 9223372036854775807 = -1" { - assert_eq((-9223372036854775808L).op_add(9223372036854775807L), -1L) + assert_eq((-9223372036854775808L).add(9223372036854775807L), -1L) } ///| test "9223372036854775807 + 0 = 9223372036854775807" { - assert_eq(9223372036854775807L.op_add(0L), 9223372036854775807L) + assert_eq(9223372036854775807L.add(0L), 9223372036854775807L) } ///| test "9223372036854775807 + -1 = 9223372036854775806" { - assert_eq(9223372036854775807L.op_add(-1L), 9223372036854775806L) + assert_eq(9223372036854775807L.add(-1L), 9223372036854775806L) } ///| test "9223372036854775807 + 1 = -9223372036854775808" { - assert_eq(9223372036854775807L.op_add(1L), -9223372036854775808L) + assert_eq(9223372036854775807L.add(1L), -9223372036854775808L) } ///| test "9223372036854775807 + 2147483647 = -9223372034707292162" { - assert_eq(9223372036854775807L.op_add(2147483647L), -9223372034707292162L) + assert_eq(9223372036854775807L.add(2147483647L), -9223372034707292162L) } ///| test "9223372036854775807 + -2147483648 = 9223372034707292159" { - assert_eq(9223372036854775807L.op_add(-2147483648L), 9223372034707292159L) + assert_eq(9223372036854775807L.add(-2147483648L), 9223372034707292159L) } ///| test "9223372036854775807 + 2147483648 = -9223372034707292161" { - assert_eq(9223372036854775807L.op_add(2147483648L), -9223372034707292161L) + assert_eq(9223372036854775807L.add(2147483648L), -9223372034707292161L) } ///| test "9223372036854775807 + -2147483649 = 9223372034707292158" { - assert_eq(9223372036854775807L.op_add(-2147483649L), 9223372034707292158L) + assert_eq(9223372036854775807L.add(-2147483649L), 9223372034707292158L) } ///| test "9223372036854775807 + 9223372036854775807 = -2" { - assert_eq(9223372036854775807L.op_add(9223372036854775807L), -2L) + assert_eq(9223372036854775807L.add(9223372036854775807L), -2L) } ///| test "9223372036854775807 + -9223372036854775808 = -1" { - assert_eq(9223372036854775807L.op_add(-9223372036854775808L), -1L) + assert_eq(9223372036854775807L.add(-9223372036854775808L), -1L) } ///| test "9223372036854775807 + -9223372036854775808 = -1" { - assert_eq(9223372036854775807L.op_add(-9223372036854775808L), -1L) + assert_eq(9223372036854775807L.add(-9223372036854775808L), -1L) } ///| test "9223372036854775807 + 9223372036854775807 = -2" { - assert_eq(9223372036854775807L.op_add(9223372036854775807L), -2L) + assert_eq(9223372036854775807L.add(9223372036854775807L), -2L) } ///| diff --git a/bundled-core/builtin/intrinsics.mbt b/bundled-core/builtin/intrinsics.mbt index f5d4d4d..b3077ac 100644 --- a/bundled-core/builtin/intrinsics.mbt +++ b/bundled-core/builtin/intrinsics.mbt @@ -102,7 +102,7 @@ pub fn not(x : Bool) -> Bool = "%bool_not" /// inspect(true == false, content="false") /// inspect(false == false, content="true") /// ``` -pub impl Eq for Bool with op_equal(self : Bool, other : Bool) -> Bool = "%bool_eq" +pub impl Eq for Bool with equal(self : Bool, other : Bool) -> Bool = "%bool_eq" ///| /// Compares two boolean values and returns their relative order. This is a @@ -195,7 +195,7 @@ pub impl Default for Bool with default() = "%bool_default" /// inspect(42, content="42") /// inspect(--2147483647, content="2147483647") // negating near min value /// ``` -pub impl Neg for Int with op_neg(self) = "%i32_neg" +pub impl Neg for Int with neg(self) = "%i32_neg" ///| /// Adds two 32-bit signed integers. Performs two's complement arithmetic, which @@ -217,7 +217,7 @@ pub impl Neg for Int with op_neg(self) = "%i32_neg" /// inspect(42 + 1, content="43") /// inspect(2147483647 + 1, content="-2147483648") // Overflow wraps around to minimum value /// ``` -pub impl Add for Int with op_add(self, other) = "%i32_add" +pub impl Add for Int with add(self, other) = "%i32_add" ///| /// Performs subtraction between two 32-bit integers, following standard two's @@ -240,7 +240,7 @@ pub impl Add for Int with op_add(self, other) = "%i32_add" /// let max = 2147483647 // Int maximum value /// inspect(max - -1, content="-2147483648") // Overflow case /// ``` -pub impl Sub for Int with op_sub(self, other) = "%i32_sub" +pub impl Sub for Int with sub(self, other) = "%i32_sub" ///| /// Multiplies two 32-bit integers. This is the implementation of the `*` @@ -262,7 +262,7 @@ pub impl Sub for Int with op_sub(self, other) = "%i32_sub" /// let max = 2147483647 // Int.max_value /// inspect(max * 2, content="-2") // Overflow wraps around /// ``` -pub impl Mul for Int with op_mul(self, other) = "%i32_mul" +pub impl Mul for Int with mul(self, other) = "%i32_mul" ///| /// Performs integer division between two 32-bit integers. The result is @@ -285,7 +285,7 @@ pub impl Mul for Int with op_mul(self, other) = "%i32_mul" /// inspect(-10 / 3, content="-3") /// inspect(10 / -3, content="-3") /// ``` -pub impl Div for Int with op_div(self, other) = "%i32_div" +pub impl Div for Int with div(self, other) = "%i32_div" ///| /// Calculates the remainder of dividing one integer by another. The result @@ -307,7 +307,7 @@ pub impl Div for Int with op_div(self, other) = "%i32_div" /// inspect(-7 % 3, content="-1") /// inspect(7 % -3, content="1") /// ``` -pub impl Mod for Int with op_mod(self, other) = "%i32_mod" +pub impl Mod for Int with mod(self, other) = "%i32_mod" ///| /// Performs a bitwise NOT operation on a 32-bit integer. Flips each bit in the @@ -416,7 +416,7 @@ pub fn Int::lxor(self : Int, other : Int) -> Int = "%i32_lxor" /// let y = -4 /// inspect(y << 2, content="-16") // Binary: 100 -> 10000 /// ``` -pub impl Shl for Int with op_shl(self, other) = "%i32_shl" +pub impl Shl for Int with shl(self, other) = "%i32_shl" ///| /// Performs an arithmetic right shift operation on an integer value. Shifts the @@ -441,7 +441,7 @@ pub impl Shl for Int with op_shl(self, other) = "%i32_shl" /// let p = 16 /// inspect(p >> 2, content="4") // Regular right shift for positive numbers /// ``` -pub impl Shr for Int with op_shr(self, other) = "%i32_shr" +pub impl Shr for Int with shr(self, other) = "%i32_shr" ///| /// Performs a left shift operation on a 32-bit integer. Shifts each bit in the @@ -635,7 +635,7 @@ pub fn Int::popcnt(self : Int) -> Int = "%i32_popcnt" /// inspect(42 == 42, content="true") /// inspect(42 == -42, content="false") /// ``` -pub impl Eq for Int with op_equal(self : Int, other : Int) -> Bool = "%i32_eq" +pub impl Eq for Int with equal(self : Int, other : Int) -> Bool = "%i32_eq" ///| /// Compares two integers and returns their relative order. @@ -822,7 +822,7 @@ pub fn Int::to_uint64(self : Int) -> UInt64 { /// inspect(--42.0, content="42") /// inspect(-(0.0 / 0.0), content="NaN") // Negating NaN returns NaN /// ``` -pub impl Neg for Double with op_neg(self) = "%f64_neg" +pub impl Neg for Double with neg(self) = "%f64_neg" ///| /// Adds two double-precision floating-point numbers together following IEEE 754 @@ -846,7 +846,7 @@ pub impl Neg for Double with op_neg(self) = "%f64_neg" /// inspect(2.5 + 3.7, content="6.2") /// inspect(1.0 / 0.0 + -1.0 / 0.0, content="NaN") // Infinity + -Infinity = NaN /// ``` -pub impl Add for Double with op_add(self, other) = "%f64_add" +pub impl Add for Double with add(self, other) = "%f64_add" ///| /// Performs subtraction between two double-precision floating-point numbers. @@ -867,7 +867,7 @@ pub impl Add for Double with op_add(self, other) = "%f64_add" /// inspect(a - b, content="2") /// inspect(0.0 / 0.0 - 1.0, content="NaN") // NaN - anything = NaN /// ``` -pub impl Sub for Double with op_sub(self, other) = "%f64_sub" +pub impl Sub for Double with sub(self, other) = "%f64_sub" ///| /// Multiplies two double-precision floating-point numbers. This is the @@ -895,7 +895,7 @@ pub impl Sub for Double with op_sub(self, other) = "%f64_sub" /// let nan = 0.0 / 0.0 // NaN /// inspect(nan * 1.0, content="NaN") /// ``` -pub impl Mul for Double with op_mul(self, other) = "%f64_mul" +pub impl Mul for Double with mul(self, other) = "%f64_mul" ///| /// Performs division between two double-precision floating-point numbers. @@ -923,7 +923,7 @@ pub impl Mul for Double with op_mul(self, other) = "%f64_mul" /// inspect(-6.0 / 2.0, content="-3") /// inspect(1.0 / 0.0, content="Infinity") /// ``` -pub impl Div for Double with op_div(self, other) = "%f64_div" +pub impl Div for Double with div(self, other) = "%f64_div" ///| /// Calculates the square root of a double-precision floating-point number. For @@ -971,7 +971,7 @@ pub fn Double::sqrt(self : Double) -> Double = "%f64_sqrt" /// let nan = 0.0 / 0.0 // NaN /// inspect(nan == nan, content="false") // Note: NaN equals itself in MoonBit /// ``` -pub impl Eq for Double with op_equal(self : Double, other : Double) -> Bool = "%f64_eq" +pub impl Eq for Double with equal(self : Double, other : Double) -> Bool = "%f64_eq" ///| /// Tests for inequality between two double-precision floating-point numbers. @@ -1128,7 +1128,7 @@ pub fn Char::from_int(val : Int) -> Char = "%char_from_int" /// inspect(a == b, content="true") /// inspect(a == c, content="false") /// ``` -pub impl Eq for Char with op_equal(self : Char, other : Char) -> Bool = "%char_eq" +pub impl Eq for Char with equal(self : Char, other : Char) -> Bool = "%char_eq" ///| /// Compares two characters based on their Unicode code points. Returns a @@ -1253,9 +1253,12 @@ pub fn Bytes::length(self : Bytes) -> Int = "%bytes_length" /// /// ```moonbit /// let bytes = Bytes::make(3, b'\xFF') -/// inspect(bytes, content= -/// #|b"\xff\xff\xff" -/// ) +/// inspect( +/// bytes, +/// content=( +/// #|b"\xff\xff\xff" +/// ), +/// ) /// let empty = Bytes::make(0, b'\x00') /// inspect(empty, content="b\"\"") /// ``` @@ -1400,7 +1403,7 @@ pub fn[T] FixedArray::unsafe_get(self : FixedArray[T], idx : Int) -> T = "%fixed pub fn[T] FixedArray::unsafe_set( self : FixedArray[T], idx : Int, - val : T + val : T, ) -> Unit = "%fixedarray.unsafe_set" ///| @@ -1518,38 +1521,20 @@ pub fn[T] FixedArray::make(len : Int, init : T) -> FixedArray[T] = "%fixedarray. /// inspect("๐Ÿคฃ".length(), content="2") // Emoji uses two UTF-16 code units /// inspect("".length(), content="0") // Empty string /// ``` +#alias(charcode_length, deprecated) pub fn String::length(self : String) -> Int = "%string_length" ///| -#deprecated("use `length` instead") -pub fn String::charcode_length(self : String) -> Int = "%string_length" - -///| -#deprecated("use `String::charcode_at` and `Int::to_char` instead") -pub fn String::op_get(self : String, idx : Int) -> Char = "%string_get" - -///| -/// Retrieves the character at the specified index in a string. +/// Returns the UTF-16 code unit at the given index. /// /// Parameters: /// /// * `string` : The string to access. -/// * `index` : The position in the string from which to retrieve the character. -/// -/// Returns a Unicode character at the specified position in the string. -/// -/// Throws a runtime error if the index is negative or greater than or equal to -/// the length of the string. -/// -/// Example: +/// * `index` : The position in the string from which to retrieve the code unit. /// -/// ```moonbit -/// let s = "Hello, ไธ–็•Œ" -/// inspect(s.charcode_at(0), content="72") -/// inspect(s.char_at(7), content="ไธ–") -/// ``` -#deprecated("use `charcode_at` instead") -pub fn String::get(self : String, idx : Int) -> Char = "%string_get" +/// This method has O(1) complexity. +#alias(charcode_at, deprecated) +pub fn String::op_get(self : String, idx : Int) -> Int = "%string_get" ///| /// Returns the UTF-16 code unit at a given position in the string without @@ -1566,12 +1551,15 @@ pub fn String::get(self : String, idx : Int) -> Char = "%string_get" /// Example: /// /// ```moonbit -/// let s = "Hello๐Ÿคฃ" -/// inspect(s.unsafe_charcode_at(0), content="72") // 'H' -/// inspect(s.unsafe_charcode_at(5), content="55358") // First surrogate of ๐Ÿคฃ -/// inspect(s.unsafe_charcode_at(6), content="56611") // Second surrogate of ๐Ÿคฃ +/// let str = "B๐Ÿคฃ๐ŸคฃC" +/// inspect(str.unsafe_charcode_at(0), content="66") // 'B' +/// inspect(str.unsafe_charcode_at(1), content="55358") // First surrogate of ๐Ÿคฃ +/// inspect(str.unsafe_charcode_at(2), content="56611") // Second surrogate of ๐Ÿคฃ +/// inspect(str.unsafe_charcode_at(3), content="55358") // First surrogate of ๐Ÿคฃ +/// inspect(str.unsafe_charcode_at(4), content="56611") // Second surrogate of ๐Ÿคฃ +/// inspect(str.unsafe_charcode_at(5), content="67") // 'C' /// ``` -/// +/// TODO: rename to `unsafe_get` #internal(unsafe, "Panic if index is out of bounds.") pub fn String::unsafe_charcode_at(self : String, idx : Int) -> Int = "%string.unsafe_get" @@ -1594,7 +1582,7 @@ pub fn String::unsafe_charcode_at(self : String, idx : Int) -> Int = "%string.un /// inspect(hello + world, content="Hello World!") /// inspect("" + "abc", content="abc") // concatenating with empty string /// ``` -pub impl Add for String with op_add(self, other) = "%string_add" +pub impl Add for String with add(self, other) = "%string_add" ///| /// Tests whether two strings are equal by comparing their characters. @@ -1616,7 +1604,7 @@ pub impl Add for String with op_add(self, other) = "%string_add" /// inspect(str1 == str2, content="true") /// inspect(str1 == str3, content="false") /// ``` -pub impl Eq for String with op_equal(self : String, other : String) -> Bool = "%string_eq" +pub impl Eq for String with equal(self : String, other : String) -> Bool = "%string_eq" ///| /// Returns the string itself without any modifications. This method is primarily @@ -1636,8 +1624,9 @@ pub impl Eq for String with op_equal(self : String, other : String) -> Bool = "% /// ``` pub fn String::to_string(self : String) -> String = "%string_to_string" -///| // For internal use only + +///| priv type UnsafeMaybeUninit[_] ///| @@ -1751,7 +1740,7 @@ pub fn UInt::to_int(self : UInt) -> Int = "%u32.to_i32_reinterpret" /// let max = 4294967295U // UInt::max_value /// inspect(max + 1U, content="0") /// ``` -pub impl Add for UInt with op_add(self, other) = "%u32.add" +pub impl Add for UInt with add(self, other) = "%u32.add" ///| /// Performs subtraction between two unsigned 32-bit integers. When the result @@ -1777,7 +1766,7 @@ pub impl Add for UInt with op_add(self, other) = "%u32.add" /// let d = 5U /// inspect(c - d, content="4294967294") // wraps around to 2^32 - 2 /// ``` -pub impl Sub for UInt with op_sub(self, other) = "%u32.sub" +pub impl Sub for UInt with sub(self, other) = "%u32.sub" ///| /// Performs multiplication between two unsigned 32-bit integers. The result @@ -1801,7 +1790,7 @@ pub impl Sub for UInt with op_sub(self, other) = "%u32.sub" /// let max = 4294967295U /// inspect(max * 2U, content="4294967294") // Wraps around to max * 2 % 2^32 /// ``` -pub impl Mul for UInt with op_mul(self, other) = "%u32.mul" +pub impl Mul for UInt with mul(self, other) = "%u32.mul" ///| /// Performs division between two unsigned 32-bit integers. The operation follows @@ -1822,7 +1811,7 @@ pub impl Mul for UInt with op_mul(self, other) = "%u32.mul" /// let b = 5U /// inspect(a / b, content="8") // Using infix operator /// ``` -pub impl Div for UInt with op_div(self, other) = "%u32.div" +pub impl Div for UInt with div(self, other) = "%u32.div" ///| /// Calculates the remainder of dividing one unsigned integer by another. @@ -1844,7 +1833,7 @@ pub impl Div for UInt with op_div(self, other) = "%u32.div" /// inspect(a % b, content="2") // 17 divided by 5 gives quotient 3 and remainder 2 /// inspect(7U % 4U, content="3") /// ``` -pub impl Mod for UInt with op_mod(self, other) = "%u32.mod" +pub impl Mod for UInt with mod(self, other) = "%u32.mod" ///| /// Compares two unsigned 32-bit integers for equality. @@ -1865,7 +1854,7 @@ pub impl Mod for UInt with op_mod(self, other) = "%u32.mod" /// inspect(a == b, content="true") /// inspect(a == c, content="false") /// ``` -pub impl Eq for UInt with op_equal(self : UInt, other : UInt) -> Bool = "%u32.eq" +pub impl Eq for UInt with equal(self : UInt, other : UInt) -> Bool = "%u32.eq" ///| /// Checks if two unsigned 32-bit integers are not equal. @@ -2118,7 +2107,7 @@ pub fn UInt::shr(self : UInt, shift : Int) -> UInt = "%u32.shr" /// let y = 0xFFFFFFFFU /// inspect(y << 16, content="4294901760") // All bits after position 16 are discarded /// ``` -pub impl Shl for UInt with op_shl(self, shift) = "%u32.shl" +pub impl Shl for UInt with shl(self, shift) = "%u32.shl" ///| /// Performs a logical right shift operation on an unsigned 32-bit integer. The @@ -2145,7 +2134,7 @@ pub impl Shl for UInt with op_shl(self, shift) = "%u32.shl" /// let x = 0xFF000000U /// inspect(x >> 32, content="4278190080") // Same as x >> 0 due to masking /// ``` -pub impl Shr for UInt with op_shr(self, shift) = "%u32.shr" +pub impl Shr for UInt with shr(self, shift) = "%u32.shr" ///| /// Counts the number of leading zero bits in an unsigned 32-bit integer, @@ -2304,7 +2293,7 @@ pub fn UInt::to_double(self : UInt) -> Double = "%u32.to_f64" /// let zero = 0.0.to_float() /// inspect((-zero).to_double(), content="0") /// ``` -pub impl Neg for Float with op_neg(self) = "%f32.neg" +pub impl Neg for Float with neg(self) = "%f32.neg" ///| /// Performs addition between two single-precision floating-point numbers. @@ -2326,7 +2315,7 @@ pub impl Neg for Float with op_neg(self) = "%f32.neg" /// let sum = a + b /// inspect(sum.to_double(), content="6") /// ``` -pub impl Add for Float with op_add(self, other) = "%f32.add" +pub impl Add for Float with add(self, other) = "%f32.add" ///| /// Performs subtraction between two single-precision floating-point numbers. @@ -2347,7 +2336,7 @@ pub impl Add for Float with op_add(self, other) = "%f32.add" /// let result = x - y /// inspect(result.to_double(), content="2.140000104904175") /// ``` -pub impl Sub for Float with op_sub(self, other) = "%f32.sub" +pub impl Sub for Float with sub(self, other) = "%f32.sub" ///| /// Performs multiplication between two single-precision floating-point numbers @@ -2370,7 +2359,7 @@ pub impl Sub for Float with op_sub(self, other) = "%f32.sub" /// let z = x * y /// inspect(z.to_double(), content="6") /// ``` -pub impl Mul for Float with op_mul(self, other) = "%f32.mul" +pub impl Mul for Float with mul(self, other) = "%f32.mul" ///| /// Performs division between two 32-bit floating-point numbers according to IEEE @@ -2397,7 +2386,7 @@ pub impl Mul for Float with op_mul(self, other) = "%f32.mul" /// inspect(result, content="3") /// inspect((0.0.to_float() / 0.0.to_float()).to_double(), content="NaN") /// ``` -pub impl Div for Float with op_div(self, other) = "%f32.div" +pub impl Div for Float with div(self, other) = "%f32.div" ///| /// Calculates the square root of a floating-point number. For non-negative @@ -2449,7 +2438,7 @@ pub fn Float::sqrt(self : Float) -> Float = "%f32.sqrt" /// inspect(x == y, content="true") /// inspect(z == z, content="false") // NaN is not equal to itself /// ``` -pub impl Eq for Float with op_equal(self : Float, other : Float) -> Bool = "%f32.eq" +pub impl Eq for Float with equal(self : Float, other : Float) -> Bool = "%f32.eq" ///| /// Tests if two single-precision floating-point numbers are not equal. This @@ -2650,7 +2639,8 @@ pub fn UInt::reinterpret_as_float(self : UInt) -> Float = "%i32.to_f32_reinterpr /// ``` pub fn Byte::to_float(self : Byte) -> Float = "%byte.to_f32" -///| TODO: use intrinsics implement this +///| +/// TODO: use intrinsics implement this pub fn Byte::to_double(self : Byte) -> Double { self.to_int().to_double() } diff --git a/bundled-core/builtin/intrinsics_test.mbt b/bundled-core/builtin/intrinsics_test.mbt index 6edeb29..fd14459 100644 --- a/bundled-core/builtin/intrinsics_test.mbt +++ b/bundled-core/builtin/intrinsics_test.mbt @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| test Int::land +///| +/// test Int::land test "Int::land should perform bitwise AND operation" { let a = 0b101 let b = 0b011 @@ -22,7 +23,8 @@ test "Int::land should perform bitwise AND operation" { inspect((0xffff_ffff & 0xffff_ffff) == 0xffff_ffff, content="true") } -///| test Int::lor +///| +/// test Int::lor test "Int::lor should perform bitwise OR operation" { inspect((0b101 | 0b011) == 0b111, content="true") inspect((0xffff_ffff | 0xa000_0000) == 0xffff_ffff, content="true") @@ -30,7 +32,8 @@ test "Int::lor should perform bitwise OR operation" { inspect((0xffff_ffff | 0xffff_ffff) == 0xffff_ffff, content="true") } -///| test Int::lxor +///| +/// test Int::lxor test "Int::lxor should perform bitwise XOR operation" { inspect((0b101 ^ 0b011) == 0b110, content="true") inspect((0xffff_ffff ^ 0xa000_0000) == 0x5fff_ffff, content="true") @@ -38,7 +41,8 @@ test "Int::lxor should perform bitwise XOR operation" { inspect((0xffff_ffff ^ 0xffff_ffff) == 0, content="true") } -///| test Int::op_shl +///| +/// test Int::op_shl test "Int::op_shl should perform left shift operation" { inspect(7 << 1, content="14") inspect(0b101 << 1 == 0b1010, content="true") diff --git a/bundled-core/builtin/iter.mbt b/bundled-core/builtin/iter.mbt index f4a5984..34891ee 100644 --- a/bundled-core/builtin/iter.mbt +++ b/bundled-core/builtin/iter.mbt @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // Types -type Iter[T] ((T) -> IterResult) -> IterResult ///| +struct Iter[T](((T) -> IterResult) -> IterResult) + //TODO: Add intrinsic for Iter::run + +///| pub fn[T] Iter::run(self : Iter[T], f : (T) -> IterResult) -> IterResult { self(f) } @@ -65,7 +67,7 @@ pub fn[T] Iter::any(self : Iter[T], f : (T) -> Bool) -> Bool { ///| pub fn[T] Iter::all(self : Iter[T], f : (T) -> Bool) -> Bool { - self.run(k => if not(f(k)) { IterEnd } else { IterContinue }) == IterContinue + self.run(k => if !f(k) { IterEnd } else { IterContinue }) is IterContinue } ///| @@ -82,7 +84,7 @@ pub fn[T] Iter::all(self : Iter[T], f : (T) -> Bool) -> Bool { /// TODO: Add intrinsic pub fn[T] Iter::eachi( self : Iter[T], - f : (Int, T) -> Unit raise? + f : (Int, T) -> Unit raise?, ) -> Unit raise? { let mut i = 0 for a in self { @@ -113,7 +115,7 @@ pub fn[T] Iter::eachi( pub fn[T, B] Iter::fold( self : Iter[T], init~ : B, - f : (B, T) -> B raise? + f : (B, T) -> B raise?, ) -> B raise? { let mut acc = init for a in self { @@ -219,8 +221,8 @@ pub fn[T] Iter::repeat(a : T) -> Iter[T] { pub fn Int::until( self : Int, end : Int, - step~ : Int = 1, - inclusive~ : Bool = false + step? : Int = 1, + inclusive? : Bool = false, ) -> Iter[Int] { if step == 0 { return Iter::empty() @@ -230,7 +232,7 @@ pub fn Int::until( while (step > 0 && i < end) || (step < 0 && i > end) || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } let next = i + step @@ -262,8 +264,8 @@ pub fn Int::until( pub fn Int64::until( self : Int64, end : Int64, - step~ : Int64 = 1L, - inclusive~ : Bool = false + step? : Int64 = 1L, + inclusive? : Bool = false, ) -> Iter[Int64] { if step == 0 { return Iter::empty() @@ -273,7 +275,7 @@ pub fn Int64::until( while (step > 0 && i < end) || (step < 0 && i > end) || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } let next = i + step @@ -305,8 +307,8 @@ pub fn Int64::until( pub fn Float::until( self : Float, end : Float, - step~ : Float = 1.0, - inclusive~ : Bool = false + step? : Float = 1.0, + inclusive? : Bool = false, ) -> Iter[Float] { if step == 0.0 { return Iter::empty() @@ -316,7 +318,7 @@ pub fn Float::until( while (step > 0.0 && i < end) || (step < 0.0 && i > end) || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } let next = i + step @@ -348,8 +350,8 @@ pub fn Float::until( pub fn Double::until( self : Double, end : Double, - step~ : Double = 1.0, - inclusive~ : Bool = false + step? : Double = 1.0, + inclusive? : Bool = false, ) -> Iter[Double] { if step == 0.0 { return Iter::empty() @@ -359,7 +361,7 @@ pub fn Double::until( while (step > 0.0 && i < end) || (step < 0.0 && i > end) || (inclusive && i == end) { - if yield_(i) == IterEnd { + if yield_(i) is IterEnd { break IterEnd } let next = i + step @@ -483,7 +485,7 @@ pub fn[T, R] Iter::flat_map(self : Iter[T], f : (T) -> Iter[R]) -> Iter[R] { yield_ => self.run(x => f(x).run(yield_)) } -///| +///| /// iter.map(f).flatten() == iter..flat_map(f) /// ```moonbit /// // ignore this test case for now since it will cause ice in release build @@ -534,25 +536,24 @@ pub fn[T] Iter::tap(self : Iter[T], f : (T) -> Unit) -> Iter[T] { /// # Returns /// /// A new iterator that contains the first `n` elements. -#intrinsic("%iter.take") +// #intrinsic("%iter.take") pub fn[T] Iter::take(self : Iter[T], n : Int) -> Iter[T] { yield_ => { // [..take(10,seq), next] would continue // even if seq has less than 10 elements // but `for x in [..take(10,seq), next ] { break }` would stop // + guard n > 0 else { return IterContinue } let mut i = 0 let mut r = IterContinue - self.just_run(a => if i < n { - if yield_(a) == IterContinue { - i = i + 1 - IterContinue - } else { + self.just_run(a => { + i = i + 1 + guard yield_(a) is IterContinue else { r = IterEnd - IterEnd + return IterEnd } - } else { - IterEnd + guard i < n else { return IterEnd } + IterContinue }) r } @@ -583,7 +584,7 @@ pub fn[T] Iter::take_while(self : Iter[T], f : (T) -> Bool) -> Iter[T] { // See test "take_while2" let mut r : IterResult = IterContinue self.just_run(a => if f(a) { - if yield_(a) == IterContinue { + if yield_(a) is IterContinue { IterContinue } else { r = IterEnd @@ -603,7 +604,7 @@ pub fn[A, B] Iter::map_while(self : Iter[A], f : (A) -> B?) -> Iter[B] { let mut r : IterResult = IterContinue self.just_run(a => match f(a) { Some(b) => - if yield_(b) == IterContinue { + if yield_(b) is IterContinue { IterContinue } else { r = IterEnd @@ -721,7 +722,7 @@ pub fn[T] Iter::peek(self : Iter[T]) -> T? { /// Returns a new iterator with the element `a` prepended to the original iterator. #deprecated("Use `Iter::singleton(a) + self` instead") pub fn[T] Iter::prepend(self : Iter[T], a : T) -> Iter[T] { - yield_ => if yield_(a) == IterContinue { self.run(yield_) } else { IterEnd } + yield_ => if yield_(a) is IterContinue { self.run(yield_) } else { IterEnd } } ///| @@ -741,7 +742,7 @@ pub fn[T] Iter::prepend(self : Iter[T], a : T) -> Iter[T] { /// Returns a new iterator with the element `a` appended to the original iterator. #deprecated("Use `self + Iter::singleton(a)` instead") pub fn[T] Iter::append(self : Iter[T], a : T) -> Iter[T] { - yield_ => if self.run(yield_) == IterContinue { yield_(a) } else { IterEnd } + yield_ => if self.run(yield_) is IterContinue { yield_(a) } else { IterEnd } } ///| @@ -761,7 +762,7 @@ pub fn[T] Iter::append(self : Iter[T], a : T) -> Iter[T] { /// Returns a new iterator that contains the elements of `self` followed by the elements of `other`. #intrinsic("%iter.concat") pub fn[T] Iter::concat(self : Iter[T], other : Iter[T]) -> Iter[T] { - yield_ => if self.run(yield_) == IterContinue { + yield_ => if self.run(yield_) is IterContinue { other.run(yield_) } else { IterEnd @@ -769,7 +770,7 @@ pub fn[T] Iter::concat(self : Iter[T], other : Iter[T]) -> Iter[T] { } ///| -pub impl[T] Add for Iter[T] with op_add(self, other) { +pub impl[T] Add for Iter[T] with add(self, other) { Iter::concat(self, other) } @@ -877,7 +878,7 @@ pub fn[A] Iter::intersperse(self : Iter[A], sep : A) -> Iter[A] { self.run(x => if first { first = false yield_(x) - } else if yield_(sep) == IterEnd { + } else if yield_(sep) is IterEnd { IterEnd } else { yield_(x) @@ -888,8 +889,8 @@ pub fn[A] Iter::intersperse(self : Iter[A], sep : A) -> Iter[A] { ///| pub fn[A] Iter::op_as_view( self : Iter[A], - start~ : Int = 0, - end? : Int + start? : Int = 0, + end? : Int, ) -> Iter[A] { // Note here we mark `end` as an optional parameter // since the meaningful default value of `end` is the length of the iterator @@ -991,7 +992,7 @@ pub fn[T : Compare] Iter::minimum(self : Iter[T]) -> T? { #deprecated pub fn[T, K : Eq + Hash] Iter::group_by( self : Iter[T], - f : (T) -> K + f : (T) -> K, ) -> Map[K, Array[T]] { let result = Map::new() for element in self { diff --git a/bundled-core/builtin/iter2.mbt b/bundled-core/builtin/iter2.mbt index e1f68ff..d5a113d 100644 --- a/bundled-core/builtin/iter2.mbt +++ b/bundled-core/builtin/iter2.mbt @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // Similar to Iter but used for two values // It is useful for iterating over map entries // without boxing @@ -20,13 +19,16 @@ // ``` // for k, v in map { ... } // ``` -type Iter2[A, B] ((A, B) -> IterResult) -> IterResult ///| +struct Iter2[A, B](((A, B) -> IterResult) -> IterResult) + //TODO: Add intrinsic for Iter::run + +///| pub fn[A, B] Iter2::run( self : Iter2[A, B], - f : (A, B) -> IterResult + f : (A, B) -> IterResult, ) -> IterResult { self(f) } @@ -36,7 +38,7 @@ pub impl[A : Show, B : Show] Show for Iter2[A, B] with output(self, logger) { logger.write_string("[") let mut first = true for k, v in self { - if not(first) { + if !first { // AI: support !first ? logger.write_string(", ") } else { @@ -54,7 +56,7 @@ pub impl[A : Show, B : Show] Show for Iter2[A, B] with output(self, logger) { ///| pub fn[A, B] Iter2::new( - f : ((A, B) -> IterResult) -> IterResult + f : ((A, B) -> IterResult) -> IterResult, ) -> Iter2[A, B] { Iter2(f) } @@ -112,10 +114,10 @@ pub fn[A, B] Iter2::to_array(self : Iter2[A, B]) -> Array[(A, B)] { /// ``` pub fn[A, B] Iter2::concat( self : Iter2[A, B], - other : Iter2[A, B] + other : Iter2[A, B], ) -> Iter2[A, B] { yield_ => { - guard self.run(yield_) == IterContinue else { IterEnd } + guard self.run(yield_) is IterContinue else { IterEnd } other.run(yield_) } } diff --git a/bundled-core/builtin/iter_test.mbt b/bundled-core/builtin/iter_test.mbt index b9595d6..06b309c 100644 --- a/bundled-core/builtin/iter_test.mbt +++ b/bundled-core/builtin/iter_test.mbt @@ -50,6 +50,35 @@ test "take" { let exb = StringBuilder::new(size_hint=0) iter.take(3).each(x => exb.write_char(x)) inspect(exb, content="123") + // normal take + let evaluated = [] + let collected = (1).until(20).tap(evaluated.push(_)).take(10).collect() + inspect(collected, content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]") + inspect(evaluated, content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]") + // take with concat + let evaluated2 = [] + let collected2a = (1).until(20).tap(i => evaluated2.push("a\{i}")).take(5) + let collected2b = (1).until(20).tap(i => evaluated2.push("b\{i}")).take(5) + let collected2 = collected2a.concat(collected2b).collect() + inspect(collected2, content="[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]") + inspect( + evaluated2, + content=( + #|["a1", "a2", "a3", "a4", "a5", "b1", "b2", "b3", "b4", "b5"] + ), + ) + // take 0 with concat + let evaluated3 = [] + let collected3a = (1).until(20).tap(i => evaluated3.push("a\{i}")).take(0) + let collected3b = (1).until(20).tap(i => evaluated3.push("b\{i}")).take(5) + let collected3 = collected3a.concat(collected3b).collect() + inspect(collected3, content="[1, 2, 3, 4, 5]") + inspect( + evaluated3, + content=( + #|["b1", "b2", "b3", "b4", "b5"] + ), + ) } ///| @@ -431,11 +460,12 @@ test "eachi" { inspect(exb, content="13579") } -///| // For testing purposes + +///| fn[T] test_from_array(arr : Array[T]) -> Iter[T] { Iter::new(yield_ => for i in 0.. Iter[Int] { Leaf(x) => yield_(x) Node(l, v, r) => // ([ .. l, v , .. r]).apply(f) - if l.iter().run(yield_) == IterEnd { + if l.iter().run(yield_) is IterEnd { IterEnd - } else if yield_(v) == IterEnd { + } else if yield_(v) is IterEnd { IterEnd } else { r.iter().run(yield_) diff --git a/bundled-core/builtin/json.mbt b/bundled-core/builtin/json.mbt index 4f9483a..773656e 100644 --- a/bundled-core/builtin/json.mbt +++ b/bundled-core/builtin/json.mbt @@ -13,31 +13,41 @@ // limitations under the License. ///| -#visibility(change_to="readonly", "Use helper functions like `Json::object(...)` instead") -pub(all) enum Json { +pub enum Json { Null True False - Number(Double) + Number(Double, repr~ : String?) // 1.0000000000000000000e100 String(String) Array(Array[Json]) Object(Map[String, Json]) -} derive(Eq) +} + +///| +pub impl Eq for Json with equal(a, b) { + match (a, b) { + (Null, Null) => true + (True, True) => true + (False, False) => true + (Number(a_num, ..), Number(b_num, ..)) => a_num == b_num + (String(a_str), String(b_str)) => a_str == b_str + (Array(a_arr), Array(b_arr)) => a_arr == b_arr + (Object(a_obj), Object(b_obj)) => a_obj == b_obj + _ => false + } +} ///| /// Creates a JSON null value. /// /// Returns a JSON value representing `null`. -/// -/// Example: -/// -/// ```moonbit -/// inspect(Json::null(), content="Null") -/// ``` pub fn Json::null() -> Json { return Null } +///| +pub let null : Json = Null + ///| /// Creates a JSON number value from a double-precision floating-point number. /// @@ -51,10 +61,14 @@ pub fn Json::null() -> Json { /// Example: /// /// ```moonbit -/// inspect(Json::number(3.14), content="Number(3.14)") +/// inspect(Json::number(3.14), content="Number(3.14)") +/// inspect( +/// Json::number(@double.infinity, repr="1e9999999999999999999999999999999").stringify(), +/// content="1e9999999999999999999999999999999" +/// ) /// ``` -pub fn Json::number(number : Double) -> Json { - return Number(number) +pub fn Json::number(number : Double, repr? : String) -> Json { + return Number(number, repr~) } ///| @@ -160,12 +174,31 @@ pub impl ToJson for Bool with to_json(self : Bool) -> Json { ///| pub impl ToJson for Byte with to_json(self : Byte) -> Json { - Number(self.to_double()) + Json::number(self.to_double()) +} + +///| +/// Converts a `Bytes` value to a JSON representation. +/// The representation is picked for easier debugging. +/// Printable ASCII characters (from space to tilde, excluding '"' and '\') are output as-is. +/// All other bytes are represented as \xHH, where HH is the two-digit hexadecimal value of the byte. +pub impl ToJson for Bytes with to_json(self : Bytes) -> Json { + let sb = StringBuilder::new() + for b in self { + if b is (b' '..=b'~') && b != b'"' && b != b'\\' { + sb.write_char(b.to_char()) + } else { + sb.write_string("\\x") + sb.write_char(to_hex_digit(b.to_int() / 16)) + sb.write_char(to_hex_digit(b.to_int() % 16)) + } + } + Json::string(sb.to_string()) } ///| pub impl ToJson for Int with to_json(self : Int) -> Json { - Number(self.to_double()) + Json::number(self.to_double()) } ///| @@ -175,7 +208,7 @@ pub impl ToJson for Int64 with to_json(self : Int64) -> Json { ///| pub impl ToJson for UInt with to_json(self : UInt) -> Json { - Number(self.to_uint64().to_double()) + Json::number(self.to_uint64().to_double()) } ///| @@ -185,17 +218,20 @@ pub impl ToJson for UInt64 with to_json(self : UInt64) -> Json { ///| pub impl ToJson for Double with to_json(self : Double) -> Json { - if self != self || - self > 0x7FEFFFFFFFFFFFFFL.reinterpret_as_double() || - self < 0xFFEFFFFFFFFFFFFFL.reinterpret_as_double() { - return Null + if self != self { + Json::string("NaN") + } else if self > 0x7FEFFFFFFFFFFFFFL.reinterpret_as_double() { + Json::string("Infinity") + } else if self < 0xFFEFFFFFFFFFFFFFL.reinterpret_as_double() { + Json::string("-Infinity") + } else { + Json::number(self) } - Number(self) } ///| pub impl ToJson for Float with to_json(self : Float) -> Json { - Number(self.to_double()) + Json::number(self.to_double()) } ///| @@ -247,22 +283,23 @@ pub impl[K : Show, V : ToJson] ToJson for Map[K, V] with to_json(self) { pub impl[T : ToJson] ToJson for T? with to_json(self) { match self { None => Null - Some(value) => [value.to_json()] + Some(value) => [value] } } ///| pub impl[Ok : ToJson, Err : ToJson] ToJson for Result[Ok, Err] with to_json( - self : Result[Ok, Err] + self : Result[Ok, Err], ) -> Json { match self { - Ok(ok) => { "Ok": ok.to_json() } - Err(err) => { "Err": err.to_json() } + Ok(ok) => { "Ok": ok } + Err(err) => { "Err": err } } } -///| //| unit + +///| pub impl ToJson for Unit with to_json(_self) { Null } diff --git a/bundled-core/builtin/json_test.mbt b/bundled-core/builtin/json_test.mbt index f48f63e..bf58ffa 100644 --- a/bundled-core/builtin/json_test.mbt +++ b/bundled-core/builtin/json_test.mbt @@ -34,7 +34,26 @@ test "UInt to_json" { ///| test "double to json with positive infinity" { let pos_inf = 1.0 / 0.0 - inspect(pos_inf.to_json(), content="Null") + inspect( + pos_inf.to_json(), + content=( + #|String("Infinity") + ), + ) + @json.inspect(pos_inf, content="Infinity") +} + +///| +test "double to json with negative infinity" { + let neg_inf = -1.0 / 0.0 + inspect( + neg_inf.to_json(), + content=( + #|String("-Infinity") + ), + ) + @json.inspect(neg_inf, content="-Infinity") + @json.inspect(0.0 / 0.0, content="NaN") } ///| @@ -98,9 +117,9 @@ test "escape control characters" { let json = str.to_json() inspect( json, - content= + content=( #|String("abc\u{01}def") - , + ), ) } @@ -110,9 +129,9 @@ test "test carriage return and backspace" { let json = test_string.to_json() inspect( json, - content= + content=( #|String("CR\rBS\b") - , + ), ) } @@ -122,9 +141,9 @@ test "test form feed" { let json = test_string.to_json() inspect( json, - content= + content=( #|String("Form\u{0c}Feed") - , + ), ) } diff --git a/bundled-core/builtin/linked_hash_map.mbt b/bundled-core/builtin/linked_hash_map.mbt index 998e6bf..6de2778 100644 --- a/bundled-core/builtin/linked_hash_map.mbt +++ b/bundled-core/builtin/linked_hash_map.mbt @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // Types + +///| priv struct Entry[K, V] { mut prev : Int mut next : Entry[K, V]? @@ -47,37 +48,12 @@ struct Map[K, V] { // Implementations -///| -fn power_2_above(x : Int, n : Int) -> Int { - for i = x { - if i >= n { - break i - } - let next = i << 1 - if next < 0 { - // overflow happened - break i - } - continue next - } -} - -///| -test "power_2_above" { - inspect(power_2_above(1, 15), content="16") - inspect(power_2_above(1, 16), content="16") - inspect(power_2_above(1, 17), content="32") - inspect(power_2_above(1, 32), content="32") - inspect(power_2_above(128, 33), content="128") - inspect(power_2_above(1, 2147483647), content="1073741824") -} - ///| /// Create a hash map. /// The capacity of the map will be the smallest power of 2 that is /// greater than or equal to the provided [capacity]. -pub fn[K, V] Map::new(capacity~ : Int = 8) -> Map[K, V] { - let capacity = power_2_above(8, capacity) +pub fn[K, V] Map::new(capacity? : Int = 8) -> Map[K, V] { + let capacity = capacity.next_power_of_two() { size: 0, capacity, @@ -98,12 +74,39 @@ pub fn[K : Hash + Eq, V] Map::from_array(arr : Array[(K, V)]) -> Map[K, V] { } ///| -/// Set a key-value pair into the hash map. +/// Sets a key-value pair into the hash map. If the key already exists, updates +/// its value. If the hash map is near full capacity, automatically +/// grows the internal storage to accommodate more entries. +/// +/// Parameters: +/// +/// * `map` : The hash map to modify. +/// * `key` : The key to insert or update. Must implement `Hash` and `Eq` traits. +/// * `value` : The value to associate with the key. +/// +/// Example: +/// +/// ```moonbit +/// let map : Map[String, Int] = Map::new() +/// map.set("key", 42) +/// inspect(map.get("key"), content="Some(42)") +/// map.set("key", 24) // update existing key +/// inspect(map.get("key"), content="Some(24)") +/// ``` pub fn[K : Hash + Eq, V] Map::set(self : Map[K, V], key : K, value : V) -> Unit { + self.set_with_hash(key, value, key.hash()) +} + +///| +fn[K : Eq, V] set_with_hash( + self : Map[K, V], + key : K, + value : V, + hash : Int, +) -> Unit { if self.size >= self.grow_at { self.grow() } - let hash = key.hash() let (idx, psl) = for psl = 0, idx = hash & self.capacity_mask { match self.entries[idx] { None => break (idx, psl) @@ -128,7 +131,7 @@ pub fn[K : Hash + Eq, V] Map::set(self : Map[K, V], key : K, value : V) -> Unit fn[K, V] Map::push_away( self : Map[K, V], idx : Int, - entry : Entry[K, V] + entry : Entry[K, V], ) -> Unit { for psl = entry.psl + 1, idx = (idx + 1) & self.capacity_mask, entry = entry { match self.entries[idx] { @@ -155,7 +158,7 @@ fn[K, V] Map::push_away( fn[K, V] Map::set_entry( self : Map[K, V], entry : Entry[K, V], - new_idx : Int + new_idx : Int, ) -> Unit { self.entries[new_idx] = Some(entry) match entry.next { @@ -165,16 +168,50 @@ fn[K, V] Map::set_entry( } ///| +/// Sets the value associated with a key in the hash map. If the key already +/// exists, updates its value; otherwise, adds a new key-value pair. This +/// function is automatically called when using the index assignment syntax +/// `map[key] = value`. +/// +/// Parameters: +/// +/// * `map` : The hash map to modify. +/// * `key` : The key to associate with the value. Must implement `Hash` and `Eq` +/// traits. +/// * `value` : The value to associate with the key. +/// +/// Example: +/// +/// ```moonbit +/// let map : Map[String, Int] = Map::new() +/// map["key"] = 42 +/// inspect(map.get("key"), content="Some(42)") +/// ``` pub fn[K : Hash + Eq, V] Map::op_set( self : Map[K, V], key : K, - value : V + value : V, ) -> Unit { self.set(key, value) } ///| -/// Get the value associated with a key. +/// Retrieves the value associated with a given key in the hash map. +/// +/// Parameters: +/// +/// * `self` : The hash map to search in. +/// * `key` : The key to look up in the map. +/// +/// Returns `Some(value)` if the key exists in the map, `None` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// let map = { "key": 42 } +/// inspect(map.get("key"), content="Some(42)") +/// inspect(map.get("nonexistent"), content="None") +/// ``` pub fn[K : Hash + Eq, V] Map::get(self : Map[K, V], key : K) -> V? { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { @@ -190,9 +227,16 @@ pub fn[K : Hash + Eq, V] Map::get(self : Map[K, V], key : K) -> V? { } ///| -#deprecated("Use `get` instead. `op_get` will return `V` instead of `Option[V]` in the future.") -pub fn[K : Hash + Eq, V] Map::op_get(self : Map[K, V], key : K) -> V? { - self.get(key) +pub fn[K : Hash + Eq, V] Map::op_get(self : Map[K, V], key : K) -> V { + let hash = key.hash() + for i = 0, idx = hash & self.capacity_mask { + guard self.entries[idx] is Some(entry) + if entry.hash == hash && entry.key == key { + return entry.value + } + guard i <= entry.psl + continue i + 1, (idx + 1) & self.capacity_mask + } } ///| @@ -219,7 +263,7 @@ pub fn[K : Hash + Eq, V] Map::op_get(self : Map[K, V], key : K) -> V? { pub fn[K : Hash + Eq, V] Map::get_or_default( self : Map[K, V], key : K, - default : V + default : V, ) -> V { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { @@ -243,16 +287,48 @@ pub fn[K : Hash + Eq, V] Map::get_or_default( pub fn[K : Hash + Eq, V] Map::get_or_init( self : Map[K, V], key : K, - default : () -> V + default : () -> V, ) -> V { - match self.get(key) { - Some(v) => v - None => { - let v = default() - self.set(key, v) - v + let hash = key.hash() + let (idx, psl, new_value, push_away) = for psl = 0, idx = hash & + self.capacity_mask { + match self.entries[idx] { + Some(entry) => { + if entry.hash == hash && entry.key == key { + return entry.value + } + if psl > entry.psl { + let new_value = default() + self.push_away(idx, entry) + break (idx, psl, new_value, Some(entry)) + } + continue psl + 1, (idx + 1) & self.capacity_mask + } + None => { + let new_value = default() + break (idx, psl, new_value, None) + } } } + if self.size >= self.grow_at { + // Slow path, we need to resize + self.grow() + self.set_with_hash(key, new_value, hash) + } else { + if push_away is Some(entry) { + self.push_away(idx, entry) + } + let entry = { + prev: self.tail, + next: None, + psl, + hash, + key, + value: new_value, + } + self.add_entry_to_tail(idx, entry) + } + new_value } ///| @@ -296,7 +372,7 @@ pub fn[K : Hash + Eq, V] Map::contains(self : Map[K, V], key : K) -> Bool { pub fn[K : Hash + Eq, V : Eq] Map::contains_kv( self : Map[K, V], key : K, - value : V + value : V, ) -> Bool { // inline Map::get to avoid boxing let hash = key.hash() @@ -313,9 +389,34 @@ pub fn[K : Hash + Eq, V : Eq] Map::contains_kv( } ///| -/// Remove a key-value pair from hash map. +/// Removes the entry for the specified key from the hash map. If the key exists +/// in the map, removes its entry and adjusts the probe sequence length (PSL) of +/// subsequent entries to maintain the Robin Hood hashing invariant. If the key +/// does not exist, the map remains unchanged. +/// +/// Parameters: +/// +/// * `self` : The hash map to remove the entry from. +/// * `key` : The key to remove from the map. +/// +/// Example: +/// +/// ```moonbit +/// let map = { "a": 1, "b": 2 } +/// map.remove("a") +/// inspect(map.get("a"), content="None") +/// inspect(map.size(), content="1") +/// ``` pub fn[K : Hash + Eq, V] Map::remove(self : Map[K, V], key : K) -> Unit { - let hash = key.hash() + self.remove_with_hash(key, key.hash()) +} + +///| +fn[K : Eq, V] Map::remove_with_hash( + self : Map[K, V], + key : K, + hash : Int, +) -> Unit { for i = 0, idx = hash & self.capacity_mask { guard self.entries[idx] is Some(entry) else { break } if entry.hash == hash && entry.key == key { @@ -335,7 +436,7 @@ pub fn[K : Hash + Eq, V] Map::remove(self : Map[K, V], key : K) -> Unit { fn[K, V] Map::add_entry_to_tail( self : Map[K, V], idx : Int, - entry : Entry[K, V] + entry : Entry[K, V], ) -> Unit { match self.tail { -1 => self.head = Some(entry) @@ -372,7 +473,7 @@ fn[K, V] Map::shift_back(self : Map[K, V], idx : Int) -> Unit { } ///| -fn[K : Hash + Eq, V] Map::grow(self : Map[K, V]) -> Unit { +fn[K : Eq, V] Map::grow(self : Map[K, V]) -> Unit { let old_head = self.head let new_capacity = self.capacity << 1 self.entries = FixedArray::make(new_capacity, None) @@ -383,8 +484,8 @@ fn[K : Hash + Eq, V] Map::grow(self : Map[K, V]) -> Unit { self.head = None self.tail = -1 loop old_head { - Some({ next, key, value, .. }) => { - self.set(key, value) + Some({ next, key, value, hash, .. }) => { + self.set_with_hash(key, value, hash) continue next } None => break @@ -436,7 +537,7 @@ pub fn[K, V] Map::is_empty(self : Map[K, V]) -> Bool { #locals(f) pub fn[K, V] Map::each( self : Map[K, V], - f : (K, V) -> Unit raise? + f : (K, V) -> Unit raise?, ) -> Unit raise? { loop self.head { Some({ key, value, next, .. }) => { @@ -452,7 +553,7 @@ pub fn[K, V] Map::each( #locals(f) pub fn[K, V] Map::eachi( self : Map[K, V], - f : (Int, K, V) -> Unit raise? + f : (Int, K, V) -> Unit raise?, ) -> Unit raise? { loop (0, self.head) { (i, Some({ key, value, next, .. })) => { @@ -534,9 +635,9 @@ pub fn[K, V] Map::to_array(self : Map[K, V]) -> Array[(K, V)] { } ///| -pub impl[K : Hash + Eq, V : Eq] Eq for Map[K, V] with op_equal( +pub impl[K : Hash + Eq, V : Eq] Eq for Map[K, V] with equal( self : Map[K, V], - that : Map[K, V] + that : Map[K, V], ) -> Bool { guard self.size == that.size else { return false } for k, v in self { @@ -634,3 +735,157 @@ pub fn[K, V] Map::copy(self : Map[K, V]) -> Map[K, V] { } other } + +///| +/// Retains only the key-value pairs that satisfy the given predicate function. +/// This method modifies the map in-place, removing all entries for which +/// the predicate returns `false`. The order of remaining elements is preserved. +/// +/// Parameters: +/// +/// * `self` : The map to be filtered. +/// * `predicate` : A function that takes a key and value as arguments and returns +/// `true` if the key-value pair should be kept, `false` if it should be removed. +/// +/// Example: +/// +/// ```moonbit +/// let map = { "a": 1, "b": 2, "c": 3, "d": 4 } +/// map.retain((_k, v) => v % 2 == 0) // Keep only even values +/// inspect(map.size(), content="2") +/// inspect(map.get("a"), content="None") +/// inspect(map.get("b"), content="Some(2)") +/// inspect(map.get("c"), content="None") +/// inspect(map.get("d"), content="Some(4)") +/// ``` +#locals(f) +pub fn[K, V] Map::retain(self : Map[K, V], f : (K, V) -> Bool) -> Unit { + loop (self.head, false) { + (Some({ key, value, next, prev: idx, .. }), remove_prev) => { + if remove_prev { + guard self.entries[idx] is Some(entry) + self.remove_entry(entry) + self.shift_back(idx) + self.size -= 1 + } + continue (next, !f(key, value)) + } + (None, remove_prev) => + if remove_prev { + let idx = self.tail + guard self.entries[idx] is Some(entry) + self.remove_entry(entry) + self.shift_back(idx) + self.size -= 1 + } + } +} + +///| +/// Updates a value in the map based on the existing value. +/// +/// This method allows you to conditionally update, insert, or remove a key-value pair +/// based on whether the key already exists in the map. The provided function `f` is +/// called with `Some(current_value)` if the key exists, or `None` if it doesn't. +/// +/// Parameters: +/// +/// * `self` : The map to update. +/// * `key` : The key to update. +/// * `f` : A function that takes the current value (wrapped in `Option`) and returns +/// the new value (wrapped in `Option`). Returning `None` will remove the key-value +/// pair from the map. +/// +/// Behavior: +/// +/// * If the key exists and `f` returns `Some(new_value)`, the value is updated. +/// * If the key exists and `f` returns `None`, the key-value pair is removed. +/// * If the key doesn't exist and `f` returns `Some(new_value)`, a new pair is inserted. +/// * If the key doesn't exist and `f` returns `None`, no operation is performed. +/// +/// Example: +/// +/// ```moonbit +/// let map = { "a": 1, "b": 2 } +/// +/// // Update existing value +/// map.update("a", fn(v) { +/// match v { +/// Some(x) => Some(x + 10) +/// None => Some(0) +/// } +/// }) +/// inspect(map, content=( +/// #|{"a": 11, "b": 2} +/// )) +/// +/// // Insert new value +/// map.update("c", fn(v) { +/// match v { +/// Some(x) => Some(x) +/// None => Some(3) +/// } +/// }) +/// inspect(map, content=( +/// #|{"a": 11, "b": 2, "c": 3} +/// )) +/// +/// // Remove existing value +/// map.update("b", fn(_) { None }) +/// inspect(map, content=( +/// #|{"a": 11, "c": 3} +/// )) +/// ``` +pub fn[K : Hash + Eq, V] Map::update( + self : Map[K, V], + key : K, + f : (V?) -> V?, +) -> Unit { + let hash = key.hash() + let (idx, psl, new_value, push_away) = for psl = 0, idx = hash & + self.capacity_mask { + match self.entries[idx] { + Some(entry) => { + if entry.hash == hash && entry.key == key { + // Found the entry, update its value + if f(Some(entry.value)) is Some(new_value) { + entry.value = new_value + } else { + // Remove the entry since the new value is None + self.remove_entry(entry) + self.shift_back(idx) + self.size -= 1 + } + return + } + if psl > entry.psl { + guard f(None) is Some(new_value) else { return } + break (idx, psl, new_value, Some(entry)) + } + continue psl + 1, (idx + 1) & self.capacity_mask + } + None => { + guard f(None) is Some(new_value) else { return } + break (idx, psl, new_value, None) + } + } + } + if self.size >= self.grow_at { + // Slow path, we need to resize + self.grow() + self.set(key, new_value) + } else { + if push_away is Some(entry) { + self.push_away(idx, entry) + } + let entry = { + prev: self.tail, + next: None, + psl, + hash, + key, + value: new_value, + } + self.add_entry_to_tail(idx, entry) + } +} diff --git a/bundled-core/builtin/linked_hash_map_test.mbt b/bundled-core/builtin/linked_hash_map_test.mbt index f11f46d..2451b10 100644 --- a/bundled-core/builtin/linked_hash_map_test.mbt +++ b/bundled-core/builtin/linked_hash_map_test.mbt @@ -18,9 +18,9 @@ test "Map keys iter" { let v = map.keys() inspect( v, - content= + content=( #|["a", "b", "c"] - , + ), ) inspect(map.values(), content="[1, 2, 3]") inspect(({} : Map[String, Int]).keys(), content="[]") @@ -33,9 +33,9 @@ test "Map::from_iter" { let map = Map::from_iter(iter) inspect( map, - content= + content=( #|{"a": 1, "b": 2, "c": 3} - , + ), ) } @@ -66,9 +66,9 @@ test "Map::map" { let v = map.map((k, v) => k + v.to_string()) inspect( v, - content= + content=( #|{"a": "a1", "b": "b2", "c": "c3"} - , + ), ) map["d"] = 10 map["e"] = 20 @@ -76,9 +76,9 @@ test "Map::map" { let v = map.map((k, v) => k + v.to_string()) inspect( v, - content= + content=( #|{"a": "a1", "b": "b2", "d": "d10", "e": "e20"} - , + ), ) let v : Map[String, String] = {}.map((k, v) => k + v) inspect(v, content="{}") @@ -90,9 +90,9 @@ test "Map::copy" { let copy = map.copy() inspect( copy, - content= + content=( #|{"a": 1, "b": 2, "c": 3} - , + ), ) map["d"] = 10 map["e"] = 20 @@ -100,10 +100,294 @@ test "Map::copy" { let copy = map.copy() inspect( copy, - content= + content=( #|{"a": 1, "b": 2, "d": 10, "e": 20} - , + ), ) let copy : Map[String, String] = {}.copy() inspect(copy, content="{}") } + +///| +test "Map::update" { + // Test updating existing value + let map = { "a": 1, "b": 2, "c": 3 } + map.update("a", fn(v) { + match v { + Some(x) => Some(x + 10) + None => Some(0) + } + }) + inspect(map.get("a"), content="Some(11)") + inspect(map.size(), content="3") + + // Test inserting new value when key doesn't exist + map.update("d", fn(v) { + match v { + Some(x) => Some(x) + None => Some(4) + } + }) + inspect(map.get("d"), content="Some(4)") + inspect(map.size(), content="4") + inspect( + map, + content=( + #|{"a": 11, "b": 2, "c": 3, "d": 4} + ), + ) + + // Test removing existing value by returning None + map.update("b", fn(_) { None }) + inspect(map.get("b"), content="None") + inspect(map.size(), content="3") + inspect( + map, + content=( + #|{"a": 11, "c": 3, "d": 4} + ), + ) + + // Test no-op when key doesn't exist and function returns None + map.update("e", fn(_) { None }) + inspect(map.get("e"), content="None") + inspect(map.size(), content="3") + + // Test incrementing a counter (common use case) + let counter_map : Map[String, Int] = {} + counter_map.update("clicks", fn(v) { + match v { + Some(count) => Some(count + 1) + None => Some(1) + } + }) + inspect(counter_map.get("clicks"), content="Some(1)") + counter_map.update("clicks", fn(v) { + match v { + Some(count) => Some(count + 1) + None => Some(1) + } + }) + inspect(counter_map.get("clicks"), content="Some(2)") + + // Test on empty map + let empty_map : Map[String, Int] = {} + + // Test no-op on empty map + empty_map.update("empty", fn(_) { None }) + + // Test inserting new value on empty map + empty_map.update("new", fn(v) { + match v { + Some(x) => Some(x * 2) + None => Some(42) + } + }) + inspect(empty_map.get("new"), content="Some(42)") + inspect(empty_map.size(), content="1") + + // Test adding to empty map + let empty_map : Map[String, Int] = Map::new(capacity=1) + empty_map.update("a", _ => Some(1)) + empty_map.update("b", _ => Some(2)) + empty_map.update("c", _ => Some(3)) +} + +///| +test "Map::retain - keep even values" { + let map = { "a": 1, "b": 2, "c": 3, "d": 4, "e": 5 } + map.retain((_k, v) => v % 2 == 0) + inspect(map.size(), content="2") + inspect(map.get("a"), content="None") + inspect(map.get("b"), content="Some(2)") + inspect(map.get("c"), content="None") + inspect(map.get("d"), content="Some(4)") + inspect(map.get("e"), content="None") +} + +///| +test "Map::retain - keep all" { + let map = { "a": 1, "b": 2, "c": 3 } + map.retain((_k, _v) => true) + inspect(map.size(), content="3") + inspect(map.get("a"), content="Some(1)") + inspect(map.get("b"), content="Some(2)") + inspect(map.get("c"), content="Some(3)") +} + +///| +test "Map::retain - remove all" { + let map = { "a": 1, "b": 2, "c": 3 } + map.retain((_k, _v) => false) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") + inspect(map.get("a"), content="None") + inspect(map.get("b"), content="None") + inspect(map.get("c"), content="None") +} + +///| +test "Map::retain - empty map" { + let map : Map[String, Int] = {} + map.retain((_k, _v) => true) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") +} + +///| +test "Map::retain - key-based filtering" { + let map = { "apple": 5, "banana": 6, "cherry": 5, "date": 4 } + map.retain((k, _v) => k.length() >= 6) // Keep keys with 6+ characters + inspect(map.size(), content="2") + inspect(map.get("apple"), content="None") // 5 chars + inspect(map.get("banana"), content="Some(6)") // 6 chars + inspect(map.get("cherry"), content="Some(5)") // 6 chars + inspect(map.get("date"), content="None") // 4 chars +} + +///| +test "Map::retain - preserves order" { + let map = { "first": 1, "second": 2, "third": 3, "fourth": 4 } + map.retain((_k, v) => v % 2 == 0) + + // Check that the order is preserved + let result = [] + map.each((k, v) => result.push((k, v))) + inspect(result, content="[(\"second\", 2), (\"fourth\", 4)]") +} + +///| +test "Map::retain - single element map" { + let map = { "only": 42 } + + // Test keeping the element + map.retain((_k, v) => v > 30) + inspect(map.size(), content="1") + inspect(map.get("only"), content="Some(42)") + + // Test removing the element + map.retain((_k, v) => v < 30) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") +} + +///| +test "Map::retain - filter by both key and value" { + let map = { "a1": 1, "b2": 2, "c3": 3, "d4": 4, "e5": 5 } + map.retain((k, v) => k.contains("2") || v > 3) // Keep keys containing "2" or values > 3 + inspect(map.size(), content="3") + inspect(map.get("a1"), content="None") + inspect(map.get("b2"), content="Some(2)") // key contains "2" + inspect(map.get("c3"), content="None") + inspect(map.get("d4"), content="Some(4)") // value > 3 + inspect(map.get("e5"), content="Some(5)") // value > 3 +} + +///| +test "Map::retain - large map performance" { + let map : Map[String, Int] = Map::new() + for i = 0; i < 1000; i = i + 1 { + map.set("key" + i.to_string(), i) + } + + // Keep only even values + map.retain((_k, v) => v % 2 == 0) + inspect(map.size(), content="500") + + // Verify some specific entries + inspect(map.get("key0"), content="Some(0)") + inspect(map.get("key2"), content="Some(2)") + inspect(map.get("key1"), content="None") + inspect(map.get("key3"), content="None") +} + +///| +test "Map::retain - preserve map integrity after retain" { + let map = { "x": 1, "y": 2, "z": 3 } + map.retain((_k, v) => v != 2) // Remove middle element + + // Test that map operations still work correctly + map.set("w", 4) + inspect(map.get("w"), content="Some(4)") + inspect(map.size(), content="3") + map.remove("x") + inspect(map.get("x"), content="None") + inspect(map.size(), content="2") + map.clear() + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") +} + +///| +test "Map::retain - complex predicate with string operations" { + let map = { + "apple": 5, + "banana": 6, + "cherry": 6, + "date": 4, + "elderberry": 10, + } + map.retain((k, v) => k.strip_prefix("a") is Some(_) || + k.strip_suffix("y") is Some(_) || + v >= 10) + inspect(map.size(), content="3") + inspect(map.get("apple"), content="Some(5)") // starts with "a" + inspect(map.get("banana"), content="None") // no match + inspect(map.get("cherry"), content="Some(6)") // ends with "y" + inspect(map.get("date"), content="None") // no match + inspect(map.get("elderberry"), content="Some(10)") // value >= 10 +} + +///| +test "Map::retain - iteration order after retain" { + let map = { "first": 1, "second": 2, "third": 3, "fourth": 4, "fifth": 5 } + map.retain((_k, v) => v != 3) // Remove third element + + // Check that insertion order is maintained for remaining elements + let keys = [] + map.each((k, _v) => keys.push(k)) + inspect(keys, content="[\"first\", \"second\", \"fourth\", \"fifth\"]") + let values = [] + map.each((_k, v) => values.push(v)) + inspect(values, content="[1, 2, 4, 5]") +} + +///| +test "Map::retain - edge case with all elements removed then re-added" { + let map = { "a": 1, "b": 2, "c": 3 } + map.retain((_k, _v) => false) // Remove all + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") + + // Re-add elements + map.set("x", 10) + map.set("y", 20) + inspect(map.size(), content="2") + inspect(map.get("x"), content="Some(10)") + inspect(map.get("y"), content="Some(20)") + + // Original keys should still be gone + inspect(map.get("a"), content="None") + inspect(map.get("b"), content="None") + inspect(map.get("c"), content="None") +} + +///| +test "Map::retain - with hash collisions simulation" { + let map : Map[String, Int] = Map::new() + // Create keys that might have similar hash values + let keys = ["a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa"] + for i = 0; i < keys.length(); i = i + 1 { + map.set(keys[i], i + 1) + } + + // Keep only keys with odd length + map.retain((k, _v) => k.length() % 2 == 1) + inspect(map.size(), content="3") + inspect(map.get("a"), content="Some(1)") // length 1 (odd) + inspect(map.get("aa"), content="None") // length 2 (even) + inspect(map.get("aaa"), content="Some(3)") // length 3 (odd) + inspect(map.get("aaaa"), content="None") // length 4 (even) + inspect(map.get("aaaaa"), content="Some(5)") // length 5 (odd) + inspect(map.get("aaaaaa"), content="None") // length 6 (even) +} diff --git a/bundled-core/builtin/linked_hash_map_wbtest.mbt b/bundled-core/builtin/linked_hash_map_wbtest.mbt index e40b495..9cc3cfa 100644 --- a/bundled-core/builtin/linked_hash_map_wbtest.mbt +++ b/bundled-core/builtin/linked_hash_map_wbtest.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -type MyString String derive(Eq) +struct MyString(String) derive(Eq) ///| impl Hash for MyString with hash(self) { @@ -67,6 +67,9 @@ test "get" { assert_eq(m.get("b"), Some(2)) assert_eq(m.get("c"), Some(3)) assert_eq(m.get("d"), None) + + // pattern + guard m is { "a": 1, "b": 2, "c": 3, "d"? : None, .. } } ///| @@ -89,16 +92,16 @@ test "get_or_init" { m.get_or_init("a", () => Array::new()).push(3) assert_eq(m.get("a"), Some([1, 3])) assert_eq(m.get("b"), Some([2])) + assert_eq(m.size(), 2) } ///| -test "get_or_init" { - let m : Map[String, Array[Int]] = Map::new() - m.get_or_init("a", () => Array::new()).push(1) - m.get_or_init("b", () => Array::new()).push(2) - m.get_or_init("a", () => Array::new()).push(3) - assert_eq(m.get("a"), Some([1, 3])) - assert_eq(m.get("b"), Some([2])) +test "get_or_init full" { + let m = Map::new(capacity=2) + m.get_or_init("a", () => 0) |> ignore + m.get_or_init("b", () => 0) |> ignore + m.get_or_init("c", () => 0) |> ignore + assert_eq(m.size(), 3) } ///| @@ -115,9 +118,16 @@ test "op_get" { let m : Map[String, Int] = Map::new() m.set("a", 1) m.set("b", 2) - assert_eq(m["a"], Some(1)) - assert_eq(m["b"], Some(2)) - assert_eq(m["c"], None) + assert_eq(m["a"], 1) + assert_eq(m["b"], 2) +} + +///| +test "panic op_get" { + let m : Map[String, Int] = Map::new() + m.set("a", 1) + m.set("b", 2) + m["c"] |> ignore } ///| @@ -192,7 +202,7 @@ test "clear" { let m = { "a": 1, "b": 2, "c": 3 } m.clear() assert_eq(m.size, 0) - assert_eq(m.capacity, 8) + inspect(m.capacity, content="4") assert_eq(m.head, None) assert_eq(m.tail, -1) for i in 0.. String { } ///| -impl[K : Eq, V] Eq for Entry[K, V] with op_equal(self, other) { +impl[K : Eq, V] Eq for Entry[K, V] with equal(self, other) { self.hash == other.hash && self.key == other.key } diff --git a/bundled-core/builtin/moon.pkg.json b/bundled-core/builtin/moon.pkg.json index 46be3e2..ea26bf4 100644 --- a/bundled-core/builtin/moon.pkg.json +++ b/bundled-core/builtin/moon.pkg.json @@ -15,6 +15,7 @@ "moonbitlang/core/json", "moonbitlang/core/bigint" ], + "alert-list": "-test_import_all", "targets": { "int64_js.mbt": ["js"], "int64_nonjs.mbt": ["not", "js"], diff --git a/bundled-core/builtin/op.mbt b/bundled-core/builtin/op.mbt index d4d6f61..932e386 100644 --- a/bundled-core/builtin/op.mbt +++ b/bundled-core/builtin/op.mbt @@ -39,5 +39,5 @@ pub fn[T : Compare] op_ge(self_ : T, other : T) -> Bool { ///| #coverage.skip pub fn[T : Eq] op_notequal(x : T, y : T) -> Bool { - not(x == y) + !(x == y) } diff --git a/bundled-core/builtin/operators.mbt b/bundled-core/builtin/operators.mbt index 1f50220..249eb65 100644 --- a/bundled-core/builtin/operators.mbt +++ b/bundled-core/builtin/operators.mbt @@ -15,37 +15,43 @@ ///| /// types implementing this trait can use the `+` operator pub(open) trait Add { - op_add(Self, Self) -> Self + add(Self, Self) -> Self = _ + op_add(Self, Self) -> Self = _ } ///| /// types implementing this trait can use the `-` operator pub(open) trait Sub { - op_sub(Self, Self) -> Self + sub(Self, Self) -> Self = _ + op_sub(Self, Self) -> Self = _ } ///| /// types implementing this trait can use the `*` operator pub(open) trait Mul { - op_mul(Self, Self) -> Self + mul(Self, Self) -> Self = _ + op_mul(Self, Self) -> Self = _ } ///| /// types implementing this trait can use the `/` operator pub(open) trait Div { - op_div(Self, Self) -> Self + div(Self, Self) -> Self = _ + op_div(Self, Self) -> Self = _ } ///| /// types implementing this trait can use the unary `-` operator pub(open) trait Neg { - op_neg(Self) -> Self + neg(Self) -> Self = _ + op_neg(Self) -> Self = _ } ///| /// types implementing this trait can use the `%` operator pub(open) trait Mod { - op_mod(Self, Self) -> Self + mod(Self, Self) -> Self = _ + op_mod(Self, Self) -> Self = _ } ///| @@ -69,11 +75,101 @@ pub(open) trait BitXOr { ///| /// types implementing this trait can use the `<<` operator pub(open) trait Shl { - op_shl(Self, Int) -> Self + shl(Self, Int) -> Self = _ + op_shl(Self, Int) -> Self = _ } ///| /// types implementing this trait can use the `>>` operator pub(open) trait Shr { - op_shr(Self, Int) -> Self + shr(Self, Int) -> Self = _ + op_shr(Self, Int) -> Self = _ +} + +///| +#deprecated("replace `impl op_add` with `impl add`") +impl Add with add(self, other) { + Add::op_add(self, other) +} + +///| +impl Add with op_add(self, other) { + Add::add(self, other) +} + +///| +#deprecated("replace `impl op_sub` with `impl sub`") +impl Sub with sub(self, other) { + Sub::op_sub(self, other) +} + +///| +impl Sub with op_sub(self, other) { + Sub::sub(self, other) +} + +///| +#deprecated("replace `impl op_mul` with `impl mul`") +impl Mul with mul(self, other) { + Mul::op_mul(self, other) +} + +///| +impl Mul with op_mul(self, other) { + Mul::mul(self, other) +} + +///| +#deprecated("replace `impl op_div` with `impl div`") +impl Div with div(self, other) { + Div::op_div(self, other) +} + +///| +impl Div with op_div(self, other) { + Div::div(self, other) +} + +///| +#deprecated("replace `impl op_neg` with `impl neg`") +impl Neg with neg(self) { + Neg::op_neg(self) +} + +///| +impl Neg with op_neg(self) { + Neg::neg(self) +} + +///| +#deprecated("replace `impl op_mod` with `impl mod`") +impl Mod with mod(self, other) { + Mod::op_mod(self, other) +} + +///| +impl Mod with op_mod(self, other) { + Mod::mod(self, other) +} + +///| +#deprecated("replace `impl op_shl` with `impl shl`") +impl Shl with shl(self, i) { + Shl::op_shl(self, i) +} + +///| +impl Shl with op_shl(self, i) { + Shl::shl(self, i) +} + +///| +#deprecated("replace `impl op_shr` with `impl shr`") +impl Shr with shr(self, i) { + Shr::op_shr(self, i) +} + +///| +impl Shr with op_shr(self, i) { + Shr::shr(self, i) } diff --git a/bundled-core/builtin/option.mbt b/bundled-core/builtin/option.mbt index ec02664..9b06829 100644 --- a/bundled-core/builtin/option.mbt +++ b/bundled-core/builtin/option.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -pub impl[X : Eq] Eq for X? with op_equal(self, other) { +pub impl[X : Eq] Eq for X? with equal(self, other) { match (self, other) { (None, None) => true (Some(x), Some(y)) => x == y diff --git a/bundled-core/builtin/output.mbt b/bundled-core/builtin/output.mbt index 97eb32d..50abd7f 100644 --- a/bundled-core/builtin/output.mbt +++ b/bundled-core/builtin/output.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -fn Int64::output(self : Int64, logger : &Logger, radix~ : Int = 10) -> Unit { +fn Int64::output(self : Int64, logger : &Logger, radix? : Int = 10) -> Unit { fn abs(n : Int64) -> Int64 { if n < 0L { 0L - n @@ -40,7 +40,7 @@ fn Int64::output(self : Int64, logger : &Logger, radix~ : Int = 10) -> Unit { } ///| -fn Int::output(self : Int, logger : &Logger, radix~ : Int = 10) -> Unit { +fn Int::output(self : Int, logger : &Logger, radix? : Int = 10) -> Unit { fn abs(n : Int) -> Int { if n < 0 { 0 - n @@ -64,7 +64,7 @@ fn Int::output(self : Int, logger : &Logger, radix~ : Int = 10) -> Unit { } ///| -fn UInt::output(self : UInt, logger : &Logger, radix~ : Int = 10) -> Unit { +fn UInt::output(self : UInt, logger : &Logger, radix? : Int = 10) -> Unit { let radix : UInt = radix.reinterpret_as_uint() fn write_digits(num : UInt) { let num2 = num / radix @@ -80,7 +80,7 @@ fn UInt::output(self : UInt, logger : &Logger, radix~ : Int = 10) -> Unit { } ///| -fn UInt64::output(self : UInt64, logger : &Logger, radix~ : Int = 10) -> Unit { +fn UInt64::output(self : UInt64, logger : &Logger, radix? : Int = 10) -> Unit { let radix : UInt64 = radix.to_uint64() fn write_digits(num : UInt64) { let num2 = num / radix @@ -96,7 +96,7 @@ fn UInt64::output(self : UInt64, logger : &Logger, radix~ : Int = 10) -> Unit { } ///| -fn Int64::output_size_hint(radix~ : Int = 10) -> Int { +fn Int64::output_size_hint(radix? : Int = 10) -> Int { match radix { 2..<7 => 70 // max length is 64, 70 is enough 8..<15 => 30 // max length is 23, 30 is enough @@ -106,7 +106,7 @@ fn Int64::output_size_hint(radix~ : Int = 10) -> Int { } ///| -fn Int::output_size_hint(radix~ : Int = 10) -> Int { +fn Int::output_size_hint(radix? : Int = 10) -> Int { match radix { 2..<7 => 36 // max length is 32, 36 is enough 8..<15 => 18 // max length is 12, 18 is enough @@ -116,11 +116,11 @@ fn Int::output_size_hint(radix~ : Int = 10) -> Int { } ///| -fn UInt::output_size_hint(radix~ : Int = 10) -> Int { +fn UInt::output_size_hint(radix? : Int = 10) -> Int { Int::output_size_hint(radix~) } ///| -fn UInt64::output_size_hint(radix~ : Int = 10) -> Int { +fn UInt64::output_size_hint(radix? : Int = 10) -> Int { Int64::output_size_hint(radix~) } diff --git a/bundled-core/builtin/output_test.mbt b/bundled-core/builtin/output_test.mbt new file mode 100644 index 0000000..8641db1 --- /dev/null +++ b/bundled-core/builtin/output_test.mbt @@ -0,0 +1,28 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "to_string with uncommon radices" { + // Int radices + inspect((123).to_string(radix=5), content="443") + inspect((123).to_string(radix=12), content="a3") + // Int64 radices + inspect(123L.to_string(radix=5), content="443") + inspect(123L.to_string(radix=12), content="a3") +} + +///| +test "panic Int64::to_string invalid radix" { + 1L.to_string(radix=1) |> ignore +} diff --git a/bundled-core/builtin/panic_test.mbt b/bundled-core/builtin/panic_test.mbt index cee681f..6f76f73 100644 --- a/bundled-core/builtin/panic_test.mbt +++ b/bundled-core/builtin/panic_test.mbt @@ -107,16 +107,6 @@ test "panic array_as_view_get_index_error" { [1, 2, 3][:][5] |> ignore } -///| -test "panic array_as_view_set_index_error" { - [1, 2, 3][:][5] = 0 -} - -///| -test "panic array_as_view_swap_index_error" { - [1, 2, 3][:].swap(1, 9) -} - ///| test "panic op_div coverage for division by zero" { let a = BigInt::from_int(123456789) @@ -311,23 +301,3 @@ test "panic" { let _ : Int = Option::None.unwrap() } - -///| -test "panic substring_start_index_error" { - "test".substring(start=-1, end=0) |> ignore -} - -///| -test "panic substring_end_index_error" { - "test".substring(start=0, end=-1) |> ignore -} - -///| -test "panic substring_start_end_index_error" { - "test".substring(start=1, end=0) |> ignore -} - -///| -test "panic substring_length_index_error" { - "test".substring(start=0, end=5) |> ignore -} diff --git a/bundled-core/builtin/builtin.mbti b/bundled-core/builtin/pkg.generated.mbti similarity index 87% rename from bundled-core/builtin/builtin.mbti rename to bundled-core/builtin/pkg.generated.mbti index 3ae35e4..a4ed473 100644 --- a/bundled-core/builtin/builtin.mbti +++ b/bundled-core/builtin/pkg.generated.mbti @@ -1,27 +1,37 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/builtin" // Values fn[T] abort(String) -> T -fn[T : Eq + Show] assert_eq(T, T, msg? : String, loc~ : SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn[T : Eq + Show] assert_eq(T, T, msg? : String, loc~ : SourceLoc) -> Unit raise -fn assert_false(Bool, msg? : String, loc~ : SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn assert_false(Bool, msg? : String, loc~ : SourceLoc) -> Unit raise -fn[T : Eq + Show] assert_not_eq(T, T, msg? : String, loc~ : SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn[T : Eq + Show] assert_not_eq(T, T, msg? : String, loc~ : SourceLoc) -> Unit raise -fn assert_true(Bool, msg? : String, loc~ : SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn assert_true(Bool, msg? : String, loc~ : SourceLoc) -> Unit raise #deprecated -fn[T] dump(T, name? : String, loc~ : SourceLoc = _) -> T +#callsite(autofill(loc)) +fn[T] dump(T, name? : String, loc~ : SourceLoc) -> T -fn[T] fail(String, loc~ : SourceLoc = _) -> T raise Failure +#callsite(autofill(loc)) +fn[T] fail(String, loc~ : SourceLoc) -> T raise Failure fn[T] ignore(T) -> Unit -fn inspect(&Show, content~ : String = .., loc~ : SourceLoc = _, args_loc~ : ArgsLoc = _) -> Unit raise InspectError +#callsite(autofill(args_loc, loc)) +fn inspect(&Show, content? : String, loc~ : SourceLoc, args_loc~ : ArgsLoc) -> Unit raise InspectError fn not(Bool) -> Bool +let null : Json + fn[T : Compare] op_ge(T, T) -> Bool fn[T : Compare] op_gt(T, T) -> Bool @@ -40,8 +50,19 @@ fn[T : Show] println(T) -> Unit fn[T : Show] repr(T) -> String +// Errors +pub(all) suberror BenchError String + +pub(all) suberror Failure String +impl Show for Failure +impl ToJson for Failure + +pub(all) suberror InspectError String + +pub(all) suberror SnapshotError String + // Types and methods -pub(all) type ArgsLoc Array[SourceLoc?] +pub(all) struct ArgsLoc(Array[SourceLoc?]) fn ArgsLoc::inner(Self) -> Array[SourceLoc?] fn ArgsLoc::to_json(Self) -> String impl Show for ArgsLoc @@ -50,7 +71,7 @@ type Array[T] fn[T] Array::append(Self[T], Self[T]) -> Unit fn[T : Compare] Array::binary_search(Self[T], T) -> Result[Int, Int] fn[T] Array::binary_search_by(Self[T], (T) -> Int) -> Result[Int, Int] -fn[A] Array::blit_to(Self[A], Self[A], len~ : Int, src_offset~ : Int = .., dst_offset~ : Int = ..) -> Unit +fn[A] Array::blit_to(Self[A], Self[A], len~ : Int, src_offset? : Int, dst_offset? : Int) -> Unit fn[T] Array::capacity(Self[T]) -> Int fn[T] Array::chunk_by(Self[T], (T, T) -> Bool raise?) -> Self[Self[T]] raise? fn[T] Array::chunks(Self[T], Int) -> Self[Self[T]] @@ -62,19 +83,12 @@ fn[T] Array::each(Self[T], (T) -> Unit raise?) -> Unit raise? fn[T] Array::eachi(Self[T], (Int, T) -> Unit raise?) -> Unit raise? fn[T : Eq] Array::ends_with(Self[T], Self[T]) -> Bool fn[T] Array::extract_if(Self[T], (T) -> Bool) -> Self[T] +fn[A] Array::fill(Self[A], A, start? : Int, end? : Int) -> Unit fn[T] Array::filter(Self[T], (T) -> Bool raise?) -> Self[T] raise? -#deprecated -fn[T] Array::find_index(Self[T], (T) -> Bool) -> Int? fn[T] Array::flatten(Self[Self[T]]) -> Self[T] +#alias(fold_left, deprecated) fn[A, B] Array::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? -#deprecated -fn[T, U] Array::fold_left(Self[T], (U, T) -> U raise?, init~ : U) -> U raise? -#deprecated -fn[T, U] Array::fold_lefti(Self[T], (Int, U, T) -> U raise?, init~ : U) -> U raise? -#deprecated -fn[T, U] Array::fold_right(Self[T], (U, T) -> U raise?, init~ : U) -> U raise? -#deprecated -fn[T, U] Array::fold_righti(Self[T], (Int, U, T) -> U raise?, init~ : U) -> U raise? +#alias(fold_lefti, deprecated) fn[A, B] Array::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? fn[T] Array::from_fixed_array(FixedArray[T]) -> Self[T] fn[T] Array::get(Self[T], Int) -> T? @@ -85,31 +99,34 @@ fn[T] Array::iter(Self[T]) -> Iter[T] fn[A] Array::iter2(Self[A]) -> Iter2[Int, A] fn[T] Array::length(Self[T]) -> Int fn[T] Array::make(Int, T) -> Self[T] +fn[T] Array::makei(Int, (Int) -> T raise?) -> Self[T] raise? fn[T, U] Array::map(Self[T], (T) -> U raise?) -> Self[U] raise? fn[T] Array::map_inplace(Self[T], (T) -> T raise?) -> Unit raise? fn[T, U] Array::mapi(Self[T], (Int, T) -> U raise?) -> Self[U] raise? fn[T] Array::mapi_inplace(Self[T], (Int, T) -> T raise?) -> Unit raise? -fn[T] Array::new(capacity~ : Int = ..) -> Self[T] -fn[T] Array::op_as_view(Self[T], start~ : Int = .., end? : Int) -> ArrayView[T] +fn[T] Array::new(capacity? : Int) -> Self[T] +fn[T] Array::op_as_view(Self[T], start? : Int, end? : Int) -> ArrayView[T] fn[T] Array::op_get(Self[T], Int) -> T fn[T] Array::op_set(Self[T], Int, T) -> Unit fn[T] Array::pop(Self[T]) -> T? -#deprecated -fn[T] Array::pop_exn(Self[T]) -> T fn[T] Array::push(Self[T], T) -> Unit fn[T] Array::remove(Self[T], Int) -> T fn[T] Array::repeat(Self[T], Int) -> Self[T] fn[T] Array::reserve_capacity(Self[T], Int) -> Unit fn[T] Array::resize(Self[T], Int, T) -> Unit fn[T] Array::retain(Self[T], (T) -> Bool raise?) -> Unit raise? +fn[A] Array::retain_map(Self[A], (A) -> A?) -> Unit fn[T] Array::rev(Self[T]) -> Self[T] fn[T] Array::rev_each(Self[T], (T) -> Unit) -> Unit fn[T] Array::rev_eachi(Self[T], (Int, T) -> Unit raise?) -> Unit raise? +#alias(fold_right, deprecated) fn[A, B] Array::rev_fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? +#alias(fold_righti, deprecated) fn[A, B] Array::rev_foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? fn[T] Array::rev_inplace(Self[T]) -> Unit fn[T] Array::rev_iter(Self[T]) -> Iter[T] fn[T : Eq] Array::search(Self[T], T) -> Int? +#alias(find_index, deprecated) fn[T] Array::search_by(Self[T], (T) -> Bool) -> Int? fn[T] Array::shrink_to_fit(Self[T]) -> Unit fn[T] Array::split(Self[T], (T) -> Bool raise?) -> Self[Self[T]] raise? @@ -122,6 +139,7 @@ fn[A] Array::truncate(Self[A], Int) -> Unit fn[A] Array::unsafe_blit(Self[A], Int, Self[A], Int, Int) -> Unit fn[A] Array::unsafe_blit_fixed(Self[A], Int, FixedArray[A], Int, Int) -> Unit fn[T] Array::unsafe_get(Self[T], Int) -> T +#alias(pop_exn, deprecated) fn[T] Array::unsafe_pop(Self[T]) -> T fn[A] Array::unsafe_pop_back(Self[A]) -> Unit fn[T] Array::windows(Self[T], Int) -> Self[ArrayView[T]] @@ -134,20 +152,18 @@ impl[X : Show] Show for Array[X] impl[X : ToJson] ToJson for Array[X] type ArrayView[T] +#deprecated +fn[T] ArrayView::blit_to(Self[T], Self[T]) -> Unit fn[T] ArrayView::length(Self[T]) -> Int -fn[T] ArrayView::op_as_view(Self[T], start~ : Int = .., end? : Int) -> Self[T] +fn[T] ArrayView::op_as_view(Self[T], start? : Int, end? : Int) -> Self[T] fn[T] ArrayView::op_get(Self[T], Int) -> T +#deprecated fn[T] ArrayView::op_set(Self[T], Int, T) -> Unit +#deprecated fn[T] ArrayView::swap(Self[T], Int, Int) -> Unit fn[T] ArrayView::unsafe_get(Self[T], Int) -> T impl[X : ToJson] ToJson for ArrayView[X] -pub(all) suberror BenchError String - -pub(all) suberror Failure String -impl Show for Failure -impl ToJson for Failure - type Hasher fn[T : Hash] Hasher::combine(Self, T) -> Unit fn Hasher::combine_bool(Self, Bool) -> Unit @@ -163,9 +179,7 @@ fn Hasher::combine_uint(Self, UInt) -> Unit fn Hasher::combine_uint64(Self, UInt64) -> Unit fn Hasher::combine_unit(Self) -> Unit fn Hasher::finalize(Self) -> Int -fn Hasher::new(seed~ : Int = ..) -> Self - -pub(all) suberror InspectError String +fn Hasher::new(seed? : Int) -> Self type Iter[T] fn[T] Iter::all(Self[T], (T) -> Bool) -> Bool @@ -205,7 +219,7 @@ fn[T : Compare] Iter::maximum(Self[T]) -> T? fn[T : Compare] Iter::minimum(Self[T]) -> T? fn[T] Iter::new(((T) -> IterResult) -> IterResult) -> Self[T] fn[T] Iter::nth(Self[T], Int) -> T? -fn[A] Iter::op_as_view(Self[A], start~ : Int = .., end? : Int) -> Self[A] +fn[A] Iter::op_as_view(Self[A], start? : Int, end? : Int) -> Self[A] fn[T] Iter::peek(Self[T]) -> T? #deprecated fn[T] Iter::prepend(Self[T], T) -> Self[T] @@ -235,11 +249,11 @@ pub(all) enum IterResult { } impl Eq for IterResult -pub(all) enum Json { +pub enum Json { Null True False - Number(Double) + Number(Double, repr~ : String?) String(String) Array(Array[Json]) Object(Map[String, Json]) @@ -247,7 +261,7 @@ pub(all) enum Json { fn Json::array(Array[Self]) -> Self fn Json::boolean(Bool) -> Self fn Json::null() -> Self -fn Json::number(Double) -> Self +fn Json::number(Double, repr? : String) -> Self fn Json::object(Map[String, Self]) -> Self fn Json::string(String) -> Self impl Default for Json @@ -271,30 +285,29 @@ fn[K, V] Map::iter(Self[K, V]) -> Iter[(K, V)] fn[K, V] Map::iter2(Self[K, V]) -> Iter2[K, V] fn[K, V] Map::keys(Self[K, V]) -> Iter[K] fn[K, V, V2] Map::map(Self[K, V], (K, V) -> V2) -> Self[K, V2] -fn[K, V] Map::new(capacity~ : Int = ..) -> Self[K, V] +fn[K, V] Map::new(capacity? : Int) -> Self[K, V] fn[K : Hash + Eq, V] Map::of(FixedArray[(K, V)]) -> Self[K, V] -#deprecated -fn[K : Hash + Eq, V] Map::op_get(Self[K, V], K) -> V? +fn[K : Hash + Eq, V] Map::op_get(Self[K, V], K) -> V fn[K : Hash + Eq, V] Map::op_set(Self[K, V], K, V) -> Unit fn[K : Hash + Eq, V] Map::remove(Self[K, V], K) -> Unit +fn[K, V] Map::retain(Self[K, V], (K, V) -> Bool) -> Unit fn[K : Hash + Eq, V] Map::set(Self[K, V], K, V) -> Unit fn[K, V] Map::size(Self[K, V]) -> Int fn[K, V] Map::to_array(Self[K, V]) -> Array[(K, V)] +fn[K : Hash + Eq, V] Map::update(Self[K, V], K, (V?) -> V?) -> Unit fn[K, V] Map::values(Self[K, V]) -> Iter[V] impl[K, V] Default for Map[K, V] impl[K : Hash + Eq, V : Eq] Eq for Map[K, V] impl[K : Show, V : Show] Show for Map[K, V] impl[K : Show, V : ToJson] ToJson for Map[K, V] -pub(all) suberror SnapshotError String - pub(all) type SourceLoc fn SourceLoc::to_string(Self) -> String impl Show for SourceLoc type StringBuilder fn StringBuilder::is_empty(Self) -> Bool -fn StringBuilder::new(size_hint~ : Int = ..) -> Self +fn StringBuilder::new(size_hint? : Int) -> Self fn StringBuilder::reset(Self) -> Unit fn StringBuilder::to_string(Self) -> String fn StringBuilder::write_iter(Self, Iter[Char]) -> Unit @@ -305,7 +318,7 @@ impl Show for StringBuilder type UninitializedArray[T] fn[A] UninitializedArray::length(Self[A]) -> Int fn[T] UninitializedArray::make(Int) -> Self[T] -fn[T] UninitializedArray::op_as_view(Self[T], start~ : Int = .., end? : Int) -> ArrayView[T] +fn[T] UninitializedArray::op_as_view(Self[T], start? : Int, end? : Int) -> ArrayView[T] fn[T] UninitializedArray::op_get(Self[T], Int) -> T fn[T] UninitializedArray::op_set(Self[T], Int, T) -> Unit fn[T] UninitializedArray::unsafe_blit(Self[T], Int, Self[T], Int, Int) -> Unit @@ -335,12 +348,16 @@ fn Char::to_uint(Char) -> UInt #deprecated fn Int::asr(Int, Int) -> Int +fn Int::clamp(Int, min~ : Int, max~ : Int) -> Int fn Int::clz(Int) -> Int fn Int::ctz(Int) -> Int +fn Int::is_leading_surrogate(Int) -> Bool fn Int::is_neg(Int) -> Bool fn Int::is_non_neg(Int) -> Bool fn Int::is_non_pos(Int) -> Bool fn Int::is_pos(Int) -> Bool +fn Int::is_surrogate(Int) -> Bool +fn Int::is_trailing_surrogate(Int) -> Bool fn Int::land(Int, Int) -> Int fn Int::lnot(Int) -> Int fn Int::lor(Int, Int) -> Int @@ -349,6 +366,9 @@ fn Int::lsl(Int, Int) -> Int #deprecated fn Int::lsr(Int, Int) -> Int fn Int::lxor(Int, Int) -> Int +fn Int::max(Int, Int) -> Int +fn Int::min(Int, Int) -> Int +fn Int::next_power_of_two(Int) -> Int fn Int::popcnt(Int) -> Int fn Int::reinterpret_as_float(Int) -> Float fn Int::reinterpret_as_uint(Int) -> UInt @@ -362,20 +382,20 @@ fn Int::to_double(Int) -> Double fn Int::to_float(Int) -> Float fn Int::to_int16(Int) -> Int16 fn Int::to_int64(Int) -> Int64 -fn Int::to_string(Int, radix~ : Int = ..) -> String +fn Int::to_string(Int, radix? : Int) -> String #deprecated fn Int::to_uint(Int) -> UInt fn Int::to_uint16(Int) -> UInt16 fn Int::to_uint64(Int) -> UInt64 fn Int::unsafe_to_char(Int) -> Char -fn Int::until(Int, Int, step~ : Int = .., inclusive~ : Bool = ..) -> Iter[Int] +fn Int::until(Int, Int, step? : Int, inclusive? : Bool) -> Iter[Int] #deprecated -fn Int::upto(Int, Int, inclusive~ : Bool = ..) -> Iter[Int] +fn Int::upto(Int, Int, inclusive? : Bool) -> Iter[Int] fn Int16::to_byte(Int16) -> Byte fn Int16::to_int(Int16) -> Int fn Int16::to_int64(Int16) -> Int64 -fn Int16::to_string(Int16, radix~ : Int = ..) -> String +fn Int16::to_string(Int16, radix? : Int) -> String #deprecated fn Int64::asr(Int64, Int) -> Int64 @@ -398,13 +418,13 @@ fn Int64::to_double(Int64) -> Double fn Int64::to_float(Int64) -> Float fn Int64::to_int(Int64) -> Int fn Int64::to_int16(Int64) -> Int16 -fn Int64::to_string(Int64, radix~ : Int = ..) -> String +fn Int64::to_string(Int64, radix? : Int) -> String fn Int64::to_uint16(Int64) -> UInt16 #deprecated fn Int64::to_uint64(Int64) -> UInt64 -fn Int64::until(Int64, Int64, step~ : Int64 = .., inclusive~ : Bool = ..) -> Iter[Int64] +fn Int64::until(Int64, Int64, step? : Int64, inclusive? : Bool) -> Iter[Int64] #deprecated -fn Int64::upto(Int64, Int64, inclusive~ : Bool = ..) -> Iter[Int64] +fn Int64::upto(Int64, Int64, inclusive? : Bool) -> Iter[Int64] fn UInt::clz(UInt) -> Int fn UInt::ctz(UInt) -> Int @@ -429,16 +449,16 @@ fn UInt::to_double(UInt) -> Double fn UInt::to_float(UInt) -> Float #deprecated fn UInt::to_int(UInt) -> Int -fn UInt::to_string(UInt, radix~ : Int = ..) -> String +fn UInt::to_string(UInt, radix? : Int) -> String fn UInt::to_uint64(UInt) -> UInt64 fn UInt::trunc_double(Double) -> UInt #deprecated -fn UInt::upto(UInt, UInt, inclusive~ : Bool = ..) -> Iter[UInt] +fn UInt::upto(UInt, UInt, inclusive? : Bool) -> Iter[UInt] fn UInt16::to_byte(UInt16) -> Byte fn UInt16::to_int(UInt16) -> Int fn UInt16::to_int64(UInt16) -> Int64 -fn UInt16::to_string(UInt16, radix~ : Int = ..) -> String +fn UInt16::to_string(UInt16, radix? : Int) -> String fn UInt64::clz(UInt64) -> Int fn UInt64::ctz(UInt64) -> Int @@ -461,20 +481,20 @@ fn UInt64::to_float(UInt64) -> Float fn UInt64::to_int(UInt64) -> Int #deprecated fn UInt64::to_int64(UInt64) -> Int64 -fn UInt64::to_string(UInt64, radix~ : Int = ..) -> String +fn UInt64::to_string(UInt64, radix? : Int) -> String fn UInt64::to_uint(UInt64) -> UInt fn UInt64::trunc_double(Double) -> UInt64 #deprecated -fn UInt64::upto(UInt64, UInt64, inclusive~ : Bool = ..) -> Iter[UInt64] +fn UInt64::upto(UInt64, UInt64, inclusive? : Bool) -> Iter[UInt64] fn Float::op_neq(Float, Float) -> Bool fn Float::reinterpret_as_int(Float) -> Int fn Float::reinterpret_as_uint(Float) -> UInt fn Float::sqrt(Float) -> Float fn Float::to_double(Float) -> Double -fn Float::until(Float, Float, step~ : Float = .., inclusive~ : Bool = ..) -> Iter[Float] +fn Float::until(Float, Float, step? : Float, inclusive? : Bool) -> Iter[Float] #deprecated -fn Float::upto(Float, Float, inclusive~ : Bool = ..) -> Iter[Float] +fn Float::upto(Float, Float, inclusive? : Bool) -> Iter[Float] fn Double::convert_uint(UInt) -> Double fn Double::convert_uint64(UInt64) -> Double @@ -490,29 +510,27 @@ fn Double::to_float(Double) -> Float fn Double::to_int(Double) -> Int fn Double::to_int64(Double) -> Int64 fn Double::to_uint64(Double) -> UInt64 -fn Double::until(Double, Double, step~ : Double = .., inclusive~ : Bool = ..) -> Iter[Double] +fn Double::until(Double, Double, step? : Double, inclusive? : Bool) -> Iter[Double] #deprecated -fn Double::upto(Double, Double, inclusive~ : Bool = ..) -> Iter[Double] +fn Double::upto(Double, Double, inclusive? : Bool) -> Iter[Double] -fn String::char_length(String, start_offset~ : Int = .., end_offset? : Int) -> Int -fn String::charcode_at(String, Int) -> Int -#deprecated -fn String::charcode_length(String) -> Int +#alias(codepoint_length, deprecated) +fn String::char_length(String, start_offset? : Int, end_offset? : Int) -> Int #deprecated fn String::codepoint_at(String, Int) -> Char -#deprecated -fn String::codepoint_length(String, start_offset~ : Int = .., end_offset? : Int) -> Int fn String::escape(String) -> String -#deprecated -fn String::get(String, Int) -> Char +#alias(charcode_length, deprecated) fn String::length(String) -> Int fn String::make(Int, Char) -> String +#alias(charcode_at, deprecated) +fn String::op_get(String, Int) -> Int #deprecated -fn String::op_get(String, Int) -> Char -fn String::substring(String, start~ : Int = .., end? : Int) -> String +fn String::substring(String, start? : Int, end? : Int) -> String fn String::to_string(String) -> String +#deprecated fn String::unsafe_char_at(String, Int) -> Char fn String::unsafe_charcode_at(String, Int) -> Int +fn String::unsafe_substring(String, start~ : Int, end~ : Int) -> String fn[X : Show] Option::to_string(X?) -> String fn[X] Option::unwrap(X?) -> X @@ -521,8 +539,8 @@ fn[T : Compare] FixedArray::binary_search(Self[T], T) -> Result[Int, Int] fn[T] FixedArray::binary_search_by(Self[T], (T) -> Int raise?) -> Result[Int, Int] raise? fn FixedArray::blit_from_bytes(Self[Byte], Int, Bytes, Int, Int) -> Unit fn FixedArray::blit_from_string(Self[Byte], Int, String, Int, Int) -> Unit -fn[A] FixedArray::blit_to(Self[A], Self[A], len~ : Int, src_offset~ : Int = .., dst_offset~ : Int = ..) -> Unit -fn[T] FixedArray::fill(Self[T], T) -> Unit +fn[A] FixedArray::blit_to(Self[A], Self[A], len~ : Int, src_offset? : Int, dst_offset? : Int) -> Unit +fn[T] FixedArray::fill(Self[T], T, start? : Int, end? : Int) -> Unit fn[T] FixedArray::get(Self[T], Int) -> T? fn[T] FixedArray::is_empty(Self[T]) -> Bool fn[T] FixedArray::iter(Self[T]) -> Iter[T] @@ -544,22 +562,23 @@ fn[T] FixedArray::unsafe_set(Self[T], Int, T) -> Unit fn Bytes::copy(Bytes) -> Bytes fn Bytes::length(Bytes) -> Int fn Bytes::make(Int, Byte) -> Bytes -fn Bytes::makei(Int, (Int) -> Byte) -> Bytes +fn Bytes::makei(Int, (Int) -> Byte raise?) -> Bytes raise? fn Bytes::new(Int) -> Bytes #deprecated fn Bytes::of_string(String) -> Bytes fn Bytes::op_get(Bytes, Int) -> Byte -fn Bytes::to_unchecked_string(Bytes, offset~ : Int = .., length? : Int) -> String +fn Bytes::to_unchecked_string(Bytes, offset? : Int, length? : Int) -> String fn Bytes::unsafe_get(Bytes, Int) -> Byte -fn[T : Show] Logger::write_iter(&Self, Iter[T], prefix~ : String = .., suffix~ : String = .., sep~ : String = .., trailing~ : Bool = ..) -> Unit +fn[T : Show] Logger::write_iter(&Self, Iter[T], prefix? : String, suffix? : String, sep? : String, trailing? : Bool) -> Unit fn[Obj : Show] Logger::write_object(&Self, Obj) -> Unit // Type aliases // Traits pub(open) trait Add { - op_add(Self, Self) -> Self + add(Self, Self) -> Self = _ + op_add(Self, Self) -> Self = _ } impl Add for Byte impl Add for Int @@ -633,7 +652,8 @@ impl Default for Double impl[X] Default for FixedArray[X] pub(open) trait Div { - op_div(Self, Self) -> Self + div(Self, Self) -> Self = _ + op_div(Self, Self) -> Self = _ } impl Div for Byte impl Div for Int @@ -644,7 +664,8 @@ impl Div for Float impl Div for Double pub(open) trait Eq { - op_equal(Self, Self) -> Bool + equal(Self, Self) -> Bool = _ + op_equal(Self, Self) -> Bool = _ } impl Eq for Unit impl Eq for Bool @@ -701,7 +722,8 @@ pub(open) trait Logger { } pub(open) trait Mod { - op_mod(Self, Self) -> Self + mod(Self, Self) -> Self = _ + op_mod(Self, Self) -> Self = _ } impl Mod for Byte impl Mod for Int @@ -710,7 +732,8 @@ impl Mod for UInt impl Mod for UInt64 pub(open) trait Mul { - op_mul(Self, Self) -> Self + mul(Self, Self) -> Self = _ + op_mul(Self, Self) -> Self = _ } impl Mul for Byte impl Mul for Int @@ -721,7 +744,8 @@ impl Mul for Float impl Mul for Double pub(open) trait Neg { - op_neg(Self) -> Self + neg(Self) -> Self = _ + op_neg(Self) -> Self = _ } impl Neg for Int impl Neg for Int64 @@ -729,7 +753,8 @@ impl Neg for Float impl Neg for Double pub(open) trait Shl { - op_shl(Self, Int) -> Self + shl(Self, Int) -> Self = _ + op_shl(Self, Int) -> Self = _ } impl Shl for Byte impl Shl for Int @@ -773,7 +798,8 @@ impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show, T12 : Show, T13 : Show, T14 : Show, T15 : Show] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) pub(open) trait Shr { - op_shr(Self, Int) -> Self + shr(Self, Int) -> Self = _ + op_shr(Self, Int) -> Self = _ } impl Shr for Byte impl Shr for Int @@ -782,7 +808,8 @@ impl Shr for UInt impl Shr for UInt64 pub(open) trait Sub { - op_sub(Self, Self) -> Self + sub(Self, Self) -> Self = _ + op_sub(Self, Self) -> Self = _ } impl Sub for Byte impl Sub for Int @@ -808,6 +835,7 @@ impl ToJson for String impl[T : ToJson] ToJson for T? impl[Ok : ToJson, Err : ToJson] ToJson for Result[Ok, Err] impl[X : ToJson] ToJson for FixedArray[X] +impl ToJson for Bytes impl[A : ToJson, B : ToJson] ToJson for (A, B) impl[A : ToJson, B : ToJson, C : ToJson] ToJson for (A, B, C) impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson] ToJson for (A, B, C, D) diff --git a/bundled-core/builtin/repr_test.mbt b/bundled-core/builtin/repr_test.mbt index 17c4dc3..012ebec 100644 --- a/bundled-core/builtin/repr_test.mbt +++ b/bundled-core/builtin/repr_test.mbt @@ -22,8 +22,8 @@ test "repr" { inspect(repr('a'), content="'a'") inspect( repr((1, "x", '\n', ['a'])), - content= + content=( #|(1, "x", '\n', ['a']) - , + ), ) } diff --git a/bundled-core/builtin/result.mbt b/bundled-core/builtin/result.mbt index 70c2737..48ba63f 100644 --- a/bundled-core/builtin/result.mbt +++ b/bundled-core/builtin/result.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -pub impl[T : Eq, E : Eq] Eq for Result[T, E] with op_equal(self, other) { +pub impl[T : Eq, E : Eq] Eq for Result[T, E] with equal(self, other) { match (self, other) { (Ok(x), Ok(y)) => x == y (Err(x), Err(y)) => x == y diff --git a/bundled-core/builtin/show.mbt b/bundled-core/builtin/show.mbt index d9c47e2..3323daa 100644 --- a/bundled-core/builtin/show.mbt +++ b/bundled-core/builtin/show.mbt @@ -100,48 +100,67 @@ pub impl Show for Bytes with output(self, logger) { ///| pub impl Show for String with output(self, logger) { logger.write_char('"') - let mut segment_start = 0 - fn flush_segment(i : Int) { - if i > segment_start { - logger.write_substring(self, segment_start, i - segment_start) + fn flush_segment(seg : Int, i : Int) { + if i > seg { + logger.write_substring(self, seg, i - seg) } - segment_start = i + 1 } - - for i in 0..= len { + // If we reached the end of the string, flush any remaining segment + // and break out of the loop. + flush_segment(seg, i) + break + } + let code = self.unsafe_charcode_at(i) + match code { '"' | '\\' as c => { - flush_segment(i) + flush_segment(seg, i) logger..write_char('\\')..write_char(c.unsafe_to_char()) + // Advance both pointers: continue with next index, new segment starts after current char + continue i + 1, i + 1 } '\n' => { - flush_segment(i) + flush_segment(seg, i) logger.write_string("\\n") + continue i + 1, i + 1 } '\r' => { - flush_segment(i) + flush_segment(seg, i) logger.write_string("\\r") + continue i + 1, i + 1 } '\b' => { - flush_segment(i) + flush_segment(seg, i) logger.write_string("\\b") + continue i + 1, i + 1 } '\t' => { - flush_segment(i) + flush_segment(seg, i) logger.write_string("\\t") + continue i + 1, i + 1 } code => if code < ' ' { - flush_segment(i) + flush_segment(seg, i) logger ..write_string("\\u{") ..write_char(to_hex_digit(code / 16)) ..write_char(to_hex_digit(code % 16)) ..write_char('}') + continue i + 1, i + 1 + } else { + // Normal character, keep scanning; only advance index. + continue i + 1, seg } } } - flush_segment(self.length()) logger.write_char('"') } @@ -164,6 +183,13 @@ pub impl Show for String with to_string(self) { ///| /// Returns a valid MoonBit string literal representation of a string, /// add quotes and escape special characters. +/// # Examples +/// +/// ```mbt +/// let str = "Hello \n" +/// inspect(str.to_string(), content="Hello \n") +/// inspect(str.escape(), content="\"Hello \\n\"") +/// ``` pub fn String::escape(self : String) -> String { let buf = StringBuilder::new() Show::output(self, buf) diff --git a/bundled-core/builtin/show_test.mbt b/bundled-core/builtin/show_test.mbt index 3e905b7..7453b2c 100644 --- a/bundled-core/builtin/show_test.mbt +++ b/bundled-core/builtin/show_test.mbt @@ -74,15 +74,15 @@ test "Show for UInt16" { test "Show for Bytes" { inspect( Show::to_string(b"abcdefg"), - content= + content=( #|b"\x61\x62\x63\x64\x65\x66\x67" - , + ), ) inspect( Show::to_string(b"\x00\xff\x63"), - content= + content=( #|b"\x00\xff\x63" - , + ), ) } @@ -90,15 +90,15 @@ test "Show for Bytes" { test "Show for BytesView" { inspect( b"abcdefg"[1:5].to_string(), - content= + content=( #|b"\x62\x63\x64\x65" - , + ), ) inspect( b"\x00\xff\x63"[:].to_string(), - content= + content=( #|b"\x00\xff\x63" - , + ), ) } @@ -112,24 +112,24 @@ test "Show for String" { inspect( to_string_using_output("abcdefgไธญๆ–‡ๅญ—็ฌฆ"), - content= + content=( #|"abcdefgไธญๆ–‡ๅญ—็ฌฆ" - , + ), ) inspect(Show::to_string("abcdefgไธญๆ–‡ๅญ—็ฌฆ"), content="abcdefgไธญๆ–‡ๅญ—็ฌฆ") inspect( to_string_using_output("---\"---'---\\---\n---\t---\u{67}---"), - content= + content=( #|"---\"---'---\\---\n---\t---g---" - , + ), ) // should be identity inspect( Show::to_string("---\"---'---\\---\n---\t---\u{67}---"), - content= + content=( #|---"---'---\--- #|--- ---g--- - , + ), ) } @@ -142,15 +142,15 @@ test "Show for Result" { inspect( result_to_string(Ok("abc")), - content= + content=( #|Ok("abc") - , + ), ) inspect( result_to_string(Err("def")), - content= + content=( #|Err("def") - , + ), ) } @@ -158,9 +158,9 @@ test "Show for Result" { test "Show for Ref" { inspect( { val: "abc" }.to_string(), - content= + content=( #|{val: "abc"} - , + ), ) } @@ -168,9 +168,9 @@ test "Show for Ref" { test "Show for FixedArray" { inspect( (["a", "b", "c"] : FixedArray[_]).to_string(), - content= + content=( #|["a", "b", "c"] - , + ), ) inspect(([] : FixedArray[String]).to_string(), content="[]") } @@ -179,9 +179,9 @@ test "Show for FixedArray" { test "Show for Array" { inspect( ["a", "b", "c"].to_string(), - content= + content=( #|["a", "b", "c"] - , + ), ) inspect(([] : Array[String]).to_string(), content="[]") } @@ -191,9 +191,9 @@ test "Show for ArrayView" { let arr = ["a", "b", "c", "d"] inspect( arr[1:3].to_string(), - content= + content=( #|["b", "c"] - , + ), ) inspect(arr[2:2].to_string(), content="[]") } @@ -204,17 +204,17 @@ test "Show for Char" { inspect(Show::to_string('ๅญ—'), content="ๅญ—") inspect( Show::to_string('\n'), - content= + content=( #| #| - , + ), ) inspect(Show::to_string('\t'), content="\t") inspect( Show::to_string('"'), - content= + content=( #|" - , + ), ) inspect(Show::to_string('\''), content="'") inspect(Show::to_string('\\'), content="\\") @@ -222,17 +222,17 @@ test "Show for Char" { inspect('ๅญ—'.to_string(), content="ๅญ—") inspect( '\n'.to_string(), - content= + content=( #| #| - , + ), ) inspect('\t'.to_string(), content="\t") inspect( '"'.to_string(), - content= + content=( #|" - , + ), ) inspect('\''.to_string(), content="'") inspect('\\'.to_string(), content="\\") @@ -247,6 +247,14 @@ test "char show hex digits" { inspect('\u{0b}' |> repr, content="'\\u{0b}'") } +///| +test "char show hex digits extended" { + inspect((0x605).unsafe_to_char() |> repr, content="'\\u{605}'") + inspect((0xFDD0).unsafe_to_char() |> repr, content="'\\u{fdd0}'") + inspect((0x1FFFE).unsafe_to_char() |> repr, content="'\\u{1fffe}'") + inspect((0x10FFFE).unsafe_to_char() |> repr, content="'\\u{10fffe}'") +} + ///| test "char_special" { inspect('\r', content="\r") @@ -258,9 +266,9 @@ test "Show for Failure" { let f = Failure("test error message") inspect( f.to_string(), - content= + content=( #|Failure("test error message") - , + ), ) } diff --git a/bundled-core/builtin/string.mbt b/bundled-core/builtin/string.mbt index 5165476..df176cb 100644 --- a/bundled-core/builtin/string.mbt +++ b/bundled-core/builtin/string.mbt @@ -34,61 +34,11 @@ pub fn String::make(length : Int, value : Char) -> String { } } -///| -let min_leading_surrogate = 0xD800 - -///| -let max_leading_surrogate = 0xDBFF - -///| -let min_trailing_surrogate = 0xDC00 - -///| -let max_trailing_surrogate = 0xDFFF - -///| -fn is_leading_surrogate(c : Int) -> Bool { - min_leading_surrogate <= c && c <= max_leading_surrogate -} - -///| -fn is_trailing_surrogate(c : Int) -> Bool { - min_trailing_surrogate <= c && c <= max_trailing_surrogate -} - ///| fn code_point_of_surrogate_pair(leading : Int, trailing : Int) -> Char { ((leading - 0xD800) * 0x400 + trailing - 0xDC00 + 0x10000).unsafe_to_char() } -///| -/// Returns the UTF-16 code unit at the given index. -/// -/// This function will panic if the index is out of bounds. -/// -/// # Examples -/// -/// ```mbt -/// let s = "Hello๐Ÿคฃ"; -/// inspect(s.charcode_at(0), content="72"); -/// inspect(s.charcode_at(5), content="55358"); // First surrogate of ๐Ÿคฃ -/// inspect(s.charcode_at(6), content="56611"); // Second surrogate of ๐Ÿคฃ -/// ``` -/// -pub fn String::charcode_at(self : String, index : Int) -> Int = "%string_get" - -///| -/// Returns the Unicode code point at the given index without bounds checking. -pub fn String::unsafe_char_at(self : String, index : Int) -> Char { - let c1 = self.unsafe_charcode_at(index) - if is_leading_surrogate(c1) { - let c2 = self.unsafe_charcode_at(index + 1) - code_point_of_surrogate_pair(c1, c2) - } else { - c1.unsafe_to_char() - } -} - ///| /// Returns the number of Unicode code points (characters) in the string. /// @@ -103,10 +53,11 @@ pub fn String::unsafe_char_at(self : String, index : Int) -> Char { /// inspect(s.char_length(), content = "6"); // 6 actual characters /// inspect(s.length(), content = "7"); // 5 ASCII chars + 2 surrogate pairs /// ``` +#alias(codepoint_length, deprecated) pub fn String::char_length( self : String, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> Int { let end_offset = if end_offset is Some(o) { o } else { self.length() } guard start_offset >= 0 && @@ -118,9 +69,9 @@ pub fn String::char_length( utf16_index < end_offset utf16_index = utf16_index + 1, char_count = char_count + 1 { let c1 = self.unsafe_charcode_at(utf16_index) - if is_leading_surrogate(c1) && utf16_index + 1 < end_offset { + if c1.is_leading_surrogate() && utf16_index + 1 < end_offset { let c2 = self.unsafe_charcode_at(utf16_index + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { continue utf16_index + 2, char_count + 1 } else { abort("invalid surrogate pair") @@ -133,7 +84,11 @@ pub fn String::char_length( ///| #intrinsic("%string.substring") -fn unsafe_substring(str : String, start : Int, end : Int) -> String { +pub fn String::unsafe_substring( + str : String, + start~ : Int, + end~ : Int, +) -> String { let len = end - start let bytes = FixedArray::make(len * 2, Byte::default()) bytes.blit_from_string(0, str, start, len) @@ -153,26 +108,15 @@ fn unsafe_substring(str : String, start : Int, end : Int) -> String { /// /// Returns a new string containing the specified substring. /// -/// Example: -/// -/// ```moonbit -/// let s = "Hello world" -/// inspect(s.substring(start=0, end=5), content="Hello") -/// inspect(s.substring(start=6, end=11), content="world") -/// inspect(s.substring(), content="Hello world") -/// -/// let s = "test" -/// inspect(s.substring(start=2, end=2), content="") -/// inspect("".substring(), content="") -/// ``` -pub fn String::substring(self : String, start~ : Int = 0, end? : Int) -> String { +#deprecated("Use `str[:]` or `str[:].to_string()` instead") +pub fn String::substring(self : String, start? : Int = 0, end? : Int) -> String { let len = self.length() let end = match end { Some(end) => end None => len } guard start >= 0 && start <= end && end <= len - unsafe_substring(self, start, end) + self.unsafe_substring(start~, end~) } ///| diff --git a/bundled-core/builtin/string_overloading_feature_test.mbt b/bundled-core/builtin/string_overloading_feature_test.mbt index d661015..1dfbbfb 100644 --- a/bundled-core/builtin/string_overloading_feature_test.mbt +++ b/bundled-core/builtin/string_overloading_feature_test.mbt @@ -18,16 +18,17 @@ trait FromString { } ///| -type MyInt Int derive(Show) +struct MyInt(Int) derive(Show) -///| // _ allow in names + +///| impl FromString for MyInt with from_string(_s) { 0 } ///| -type MyString String derive(Show) +struct MyString(String) derive(Show) ///| impl FromString for MyString with from_string(_s) { @@ -50,9 +51,9 @@ test { let u1 : MyInt = FromString::from_string("") inspect( (u0, u1), - content= + content=( #|(MyString(""), MyInt(0)) - , + ), ) need_int(FromString::from_string("")) need_string(FromString::from_string("")) diff --git a/bundled-core/builtin/string_test.mbt b/bundled-core/builtin/string_test.mbt index f64fe4f..c47a3ee 100644 --- a/bundled-core/builtin/string_test.mbt +++ b/bundled-core/builtin/string_test.mbt @@ -16,38 +16,30 @@ test "String::escape" { inspect( "abc,def".escape(), - content= + content=( #|"abc,def" - , + ), ) inspect( "\n\t\\".escape(), - content= + content=( #|"\n\t\\" - , + ), ) inspect( "\"'".escape(), - content= + content=( #|"\"'" - , + ), ) inspect( "\u{00}\u{01}\u{20}\u{21}".escape(), - content= + content=( #|"\u{00}\u{01} !" - , + ), ) } -///| -test "substring" { - assert_eq("abc".substring(), "abc") - assert_eq("abc".substring(start=1), "bc") - assert_eq("abc".substring(end=2), "ab") - assert_eq("abc".substring(start=1, end=2), "b") -} - ///| test "panic codepoint_at1" { let str = "Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ" @@ -55,25 +47,6 @@ test "panic codepoint_at1" { } -///| -// test "panic codepoint_at2" { -// let str = "Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ" -// let _ = str.iter().nth(-1).unwrap() - -// } - -///| -test "panic charcode_at with negative index" { - let s = "Hello" - ignore(s.charcode_at(-1)) -} - -///| -test "charcode_at with valid index" { - let s = "Hello" - inspect(s.charcode_at(0), content="72") -} - ///| test "codepoint_at regular character at end of string" { let s = "Hello" @@ -86,16 +59,6 @@ test "codepoint_at emoji" { inspect(s.iter().nth(0).unwrap(), content="๐Ÿ‘‹") } -///| -test "unsafe_char_at" { - let s = "a๐Ÿ‘‹b" - inspect(s.unsafe_char_at(0), content="a") - inspect(s.unsafe_char_at(1), content="๐Ÿ‘‹") - // invalid char cannot be inspected - inspect(s.unsafe_char_at(2).to_int(), content="56395") - inspect(s.unsafe_char_at(3), content="b") -} - ///| test "char_length basic" { let str = "hello" diff --git a/bundled-core/builtin/stringbuilder.mbt b/bundled-core/builtin/stringbuilder.mbt index 314dcd6..b9acd7d 100644 --- a/bundled-core/builtin/stringbuilder.mbt +++ b/bundled-core/builtin/stringbuilder.mbt @@ -16,7 +16,7 @@ /// Writes the string representation of a object to the StringBuilder. pub fn[T : Show] StringBuilder::write_object( self : StringBuilder, - obj : T + obj : T, ) -> Unit { obj.output(self) } @@ -39,7 +39,7 @@ pub fn[T : Show] StringBuilder::write_object( /// ``` pub fn StringBuilder::write_iter( self : StringBuilder, - iter : Iter[Char] + iter : Iter[Char], ) -> Unit { for ch in iter { self.write_char(ch) diff --git a/bundled-core/builtin/stringbuilder_buffer.mbt b/bundled-core/builtin/stringbuilder_buffer.mbt index eacc9f2..86c952a 100644 --- a/bundled-core/builtin/stringbuilder_buffer.mbt +++ b/bundled-core/builtin/stringbuilder_buffer.mbt @@ -29,7 +29,7 @@ struct StringBuilder { /// /// Returns a new `StringBuilder` instance with the specified initial capacity. /// -pub fn StringBuilder::new(size_hint~ : Int = 0) -> StringBuilder { +pub fn StringBuilder::new(size_hint? : Int = 0) -> StringBuilder { let initial = if size_hint < 1 { 1 } else { size_hint } let data : FixedArray[Byte] = FixedArray::make(initial, 0) { data, len: 0 } @@ -44,7 +44,7 @@ pub fn StringBuilder::is_empty(self : StringBuilder) -> Bool { ///| fn StringBuilder::grow_if_necessary( self : StringBuilder, - required : Int + required : Int, ) -> Unit { let current_len = self.data.length() if required <= current_len { @@ -98,7 +98,7 @@ pub impl Logger for StringBuilder with write_substring( self : StringBuilder, str : String, start : Int, - len : Int + len : Int, ) -> Unit { guard start >= 0 && len >= 0 && start + len <= str.length() self.grow_if_necessary(self.len + len * 2) @@ -106,7 +106,7 @@ pub impl Logger for StringBuilder with write_substring( self.len += len * 2 } -///| +///| /// Returns the current content of the StringBuilder as a string. pub fn StringBuilder::to_string(self : StringBuilder) -> String { self.data @@ -124,8 +124,19 @@ pub impl Show for StringBuilder with output(self, logger) { ) } -///| +///| /// Resets the string builder to an empty state. pub fn StringBuilder::reset(self : StringBuilder) -> Unit { self.len = 0 } + +// pub fn StringBuilder::write_stringview( +// self : StringBuilder, +// view : StringView, +// ) -> Unit { +// self.write_substring( +// view.data(), +// view.offset(), +// view.length(), +// ) +// } diff --git a/bundled-core/builtin/stringbuilder_concat.mbt b/bundled-core/builtin/stringbuilder_concat.mbt index 545ceb2..89f63fa 100644 --- a/bundled-core/builtin/stringbuilder_concat.mbt +++ b/bundled-core/builtin/stringbuilder_concat.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -type StringBuilder Ref[String] +struct StringBuilder(Ref[String]) ///| /// Creates a new string builder with an optional initial capacity hint. @@ -26,7 +26,7 @@ type StringBuilder Ref[String] /// /// Returns a new `StringBuilder` instance with the specified initial capacity. /// -pub fn StringBuilder::new(size_hint~ : Int = 0) -> StringBuilder { +pub fn StringBuilder::new(size_hint? : Int = 0) -> StringBuilder { ignore(size_hint) { val: "" } } @@ -76,7 +76,7 @@ pub impl Logger for StringBuilder with write_substring( self : StringBuilder, str : String, start : Int, - len : Int + len : Int, ) -> Unit { self.val += str.substring(start~, end=start + len) } @@ -86,13 +86,13 @@ pub impl Show for StringBuilder with output(self, logger) { logger.write_string(self.val) } -///| +///| /// Returns the current content of the StringBuilder as a string. pub fn StringBuilder::to_string(self : StringBuilder) -> String { self.val } -///| +///| /// Resets the string builder to an empty state. pub fn StringBuilder::reset(self : StringBuilder) -> Unit { self.val = "" diff --git a/bundled-core/builtin/stringbuilder_test.mbt b/bundled-core/builtin/stringbuilder_test.mbt index 7a4b7e1..07d8121 100644 --- a/bundled-core/builtin/stringbuilder_test.mbt +++ b/bundled-core/builtin/stringbuilder_test.mbt @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // when we write the string into the buffer // we assume the string is utf16 and blit // we assume the bytes (in valid region [0, len)) is utf16 // so that + +///| fn id(s : String) -> String { StringBuilder::new()..write_string(s).to_string() } @@ -53,7 +54,7 @@ test "write_iter" { let sb = StringBuilder::new() sb.write_iter(str.iter()) inspect(sb.to_string(), content="Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ") - let str_view = str.view(start_offset=1, end_offset=7) + let str_view = str[1:7] sb.write_iter(str_view.iter()) inspect(sb.to_string(), content="Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃello๐Ÿคฃ") } diff --git a/bundled-core/builtin/to_string.mbt b/bundled-core/builtin/to_string.mbt index dfe998e..92e69d6 100644 --- a/bundled-core/builtin/to_string.mbt +++ b/bundled-core/builtin/to_string.mbt @@ -16,7 +16,7 @@ const ALPHABET : String = "0123456789abcdefghijklmnopqrstuvwxyz" ///| -pub fn Int64::to_string(self : Int64, radix~ : Int = 10) -> String { +pub fn Int64::to_string(self : Int64, radix? : Int = 10) -> String { let buf = StringBuilder::new(size_hint=Int64::output_size_hint(radix~)) self.output(buf, radix~) buf.to_string() @@ -28,7 +28,7 @@ pub impl Show for Int64 with to_string(self) { } ///| -pub fn Int::to_string(self : Int, radix~ : Int = 10) -> String { +pub fn Int::to_string(self : Int, radix? : Int = 10) -> String { let buf = StringBuilder::new(size_hint=Int::output_size_hint(radix~)) self.output(buf, radix~) buf.to_string() @@ -40,7 +40,7 @@ pub impl Show for Int with to_string(self) { } ///| -pub fn UInt::to_string(self : UInt, radix~ : Int = 10) -> String { +pub fn UInt::to_string(self : UInt, radix? : Int = 10) -> String { let buf = StringBuilder::new(size_hint=UInt::output_size_hint(radix~)) self.output(buf, radix~) buf.to_string() @@ -59,7 +59,7 @@ test "UInt::to_string" { } ///| -pub fn UInt64::to_string(self : UInt64, radix~ : Int = 10) -> String { +pub fn UInt64::to_string(self : UInt64, radix? : Int = 10) -> String { let buf = StringBuilder::new(size_hint=UInt64::output_size_hint(radix~)) self.output(buf, radix~) buf.to_string() @@ -71,7 +71,7 @@ pub impl Show for UInt64 with to_string(self) { } ///| -pub fn Int16::to_string(self : Int16, radix~ : Int = 10) -> String { +pub fn Int16::to_string(self : Int16, radix? : Int = 10) -> String { self.to_int().to_string(radix~) } @@ -81,7 +81,7 @@ pub impl Show for Int16 with to_string(self) { } ///| -pub fn UInt16::to_string(self : UInt16, radix~ : Int = 10) -> String { +pub fn UInt16::to_string(self : UInt16, radix? : Int = 10) -> String { self.to_int().to_string(radix~) } @@ -159,6 +159,8 @@ test "to_string with radix" { 1.0.reinterpret_as_uint64().to_string(radix=16), content="3ff0000000000000", ) + inspect(0b101L.to_string(radix=2), content="101") + inspect(0o17L.to_string(radix=8), content="17") // UInt64 inspect(0UL.to_string(radix=16), content="0") diff --git a/bundled-core/builtin/traits.mbt b/bundled-core/builtin/traits.mbt index 096ab7c..1fa82ab 100644 --- a/bundled-core/builtin/traits.mbt +++ b/bundled-core/builtin/traits.mbt @@ -15,7 +15,8 @@ ///| /// Trait for types whose elements can test for equality pub(open) trait Eq { - op_equal(Self, Self) -> Bool + equal(Self, Self) -> Bool = _ + op_equal(Self, Self) -> Bool = _ } ///| @@ -31,6 +32,15 @@ pub(open) trait Compare: Eq { ///| /// Trait for types that can be hashed +/// +/// The `hash` method should return a hash value for the type, which is used in hash tables and other data structures. +/// The `hash_combine` method is used to combine the hash of the current value with another hash value, +/// typically used to hash composite types. +/// +/// When two values are equal according to the `Eq` trait, they should produce the same hash value. +/// +/// The `hash` method does not need to be implemented if `hash_combine` is implemented, +/// When implemented separately, `hash` **does not need** to produce a hash value that is consistent with `hash_combine`. pub(open) trait Hash { hash_combine(Self, Hasher) -> Unit hash(Self) -> Int = _ @@ -76,8 +86,9 @@ pub(open) trait Show { to_string(Self) -> String = _ } -///| // Default implementation for `Show::to_string`, uses a `Buffer` + +///| impl Show with to_string(self) { let logger = StringBuilder::new() self.output(logger) @@ -93,10 +104,10 @@ pub fn[Obj : Show] &Logger::write_object(self : &Logger, obj : Obj) -> Unit { pub fn[T : Show] &Logger::write_iter( self : &Logger, iter : Iter[T], - prefix~ : String = "[", - suffix~ : String = "]", - sep~ : String = ", ", - trailing~ : Bool = false + prefix? : String = "[", + suffix? : String = "]", + sep? : String = ", ", + trailing? : Bool = false, ) -> Unit { self.write_string(prefix) if trailing { @@ -126,3 +137,14 @@ pub fn[T : Show] repr(t : T) -> String { t.output(logger) logger.to_string() } + +///| +#deprecated("replace `impl op_equal` with `impl equal`") +impl Eq with equal(self, other) { + Eq::op_equal(self, other) +} + +///| +impl Eq with op_equal(self, other) { + Eq::equal(self, other) +} diff --git a/bundled-core/builtin/tuple_compare.mbt b/bundled-core/builtin/tuple_compare.mbt index eb820a4..958edad 100644 --- a/bundled-core/builtin/tuple_compare.mbt +++ b/bundled-core/builtin/tuple_compare.mbt @@ -15,7 +15,7 @@ ///| pub impl[T0 : Compare, T1 : Compare] Compare for (T0, T1) with compare( self : (T0, T1), - other : (T0, T1) + other : (T0, T1), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -25,7 +25,7 @@ pub impl[T0 : Compare, T1 : Compare] Compare for (T0, T1) with compare( ///| pub impl[T0 : Compare, T1 : Compare, T2 : Compare] Compare for (T0, T1, T2) with compare( self : (T0, T1, T2), - other : (T0, T1, T2) + other : (T0, T1, T2), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -70,16 +70,16 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare] C } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5) with compare( self : (T0, T1, T2, T3, T4, T5), - other : (T0, T1, T2, T3, T4, T5) + other : (T0, T1, T2, T3, T4, T5), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -95,17 +95,17 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6) with compare( self : (T0, T1, T2, T3, T4, T5, T6), - other : (T0, T1, T2, T3, T4, T5, T6) + other : (T0, T1, T2, T3, T4, T5, T6), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -123,18 +123,18 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7), - other : (T0, T1, T2, T3, T4, T5, T6, T7) + other : (T0, T1, T2, T3, T4, T5, T6, T7), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -154,19 +154,19 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -188,20 +188,20 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -225,21 +225,21 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -265,22 +265,22 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare, T11 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, + T11 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -308,23 +308,23 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare, T11 : Compare, T12 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, + T11 : Compare, + T12 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -354,24 +354,24 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare, T11 : Compare, T12 : Compare, T13 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, + T11 : Compare, + T12 : Compare, + T13 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -403,25 +403,25 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare, T11 : Compare, T12 : Compare, T13 : Compare, T14 : Compare] Compare for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, -) with compare( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, + T11 : Compare, + T12 : Compare, + T13 : Compare, + T14 : Compare, +] Compare for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } @@ -455,7 +455,24 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T } ///| -pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T5 : Compare, T6 : Compare, T7 : Compare, T8 : Compare, T9 : Compare, T10 : Compare, T11 : Compare, T12 : Compare, T13 : Compare, T14 : Compare, T15 : Compare] Compare for ( +pub impl[ + T0 : Compare, + T1 : Compare, + T2 : Compare, + T3 : Compare, + T4 : Compare, + T5 : Compare, + T6 : Compare, + T7 : Compare, + T8 : Compare, + T9 : Compare, + T10 : Compare, + T11 : Compare, + T12 : Compare, + T13 : Compare, + T14 : Compare, + T15 : Compare, +] Compare for ( T0, T1, T2, @@ -474,7 +491,7 @@ pub impl[T0 : Compare, T1 : Compare, T2 : Compare, T3 : Compare, T4 : Compare, T T15, ) with compare( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), ) -> Int { let t0 = self.0.compare(other.0) guard t0 == 0 else { return t0 } diff --git a/bundled-core/builtin/tuple_eq.mbt b/bundled-core/builtin/tuple_eq.mbt index 1c94afe..2c929c4 100644 --- a/bundled-core/builtin/tuple_eq.mbt +++ b/bundled-core/builtin/tuple_eq.mbt @@ -13,25 +13,25 @@ // limitations under the License. ///| -pub impl[T0 : Eq, T1 : Eq] Eq for (T0, T1) with op_equal( +pub impl[T0 : Eq, T1 : Eq] Eq for (T0, T1) with equal( self : (T0, T1), - other : (T0, T1) + other : (T0, T1), ) -> Bool { self.0 == other.0 && self.1 == other.1 } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq] Eq for (T0, T1, T2) with op_equal( +pub impl[T0 : Eq, T1 : Eq, T2 : Eq] Eq for (T0, T1, T2) with equal( self : (T0, T1, T2), - other : (T0, T1, T2) + other : (T0, T1, T2), ) -> Bool { self.0 == other.0 && self.1 == other.1 && self.2 == other.2 } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq] Eq for (T0, T1, T2, T3) with op_equal( +pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq] Eq for (T0, T1, T2, T3) with equal( self : (T0, T1, T2, T3), - other : (T0, T1, T2, T3) + other : (T0, T1, T2, T3), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -46,7 +46,7 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq] Eq for ( T2, T3, T4, -) with op_equal(self : (T0, T1, T2, T3, T4), other : (T0, T1, T2, T3, T4)) -> Bool { +) with equal(self : (T0, T1, T2, T3, T4), other : (T0, T1, T2, T3, T4)) -> Bool { self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && @@ -62,10 +62,7 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq] Eq for ( T3, T4, T5, -) with op_equal( - self : (T0, T1, T2, T3, T4, T5), - other : (T0, T1, T2, T3, T4, T5) -) -> Bool { +) with equal(self : (T0, T1, T2, T3, T4, T5), other : (T0, T1, T2, T3, T4, T5)) -> Bool { self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && @@ -83,9 +80,9 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq] Eq for ( T4, T5, T6, -) with op_equal( +) with equal( self : (T0, T1, T2, T3, T4, T5, T6), - other : (T0, T1, T2, T3, T4, T5, T6) + other : (T0, T1, T2, T3, T4, T5, T6), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -106,9 +103,9 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq] T5, T6, T7, -) with op_equal( +) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7), - other : (T0, T1, T2, T3, T4, T5, T6, T7) + other : (T0, T1, T2, T3, T4, T5, T6, T7), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -121,19 +118,19 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq] } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -147,20 +144,20 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -175,21 +172,21 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -205,22 +202,22 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq, T11 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, + T11 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -237,23 +234,23 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq, T11 : Eq, T12 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, + T11 : Eq, + T12 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -271,24 +268,24 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq, T11 : Eq, T12 : Eq, T13 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, + T11 : Eq, + T12 : Eq, + T13 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -307,25 +304,25 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq, T11 : Eq, T12 : Eq, T13 : Eq, T14 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, + T11 : Eq, + T12 : Eq, + T13 : Eq, + T14 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), ) -> Bool { self.0 == other.0 && self.1 == other.1 && @@ -345,26 +342,26 @@ pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, } ///| -pub impl[T0 : Eq, T1 : Eq, T2 : Eq, T3 : Eq, T4 : Eq, T5 : Eq, T6 : Eq, T7 : Eq, T8 : Eq, T9 : Eq, T10 : Eq, T11 : Eq, T12 : Eq, T13 : Eq, T14 : Eq, T15 : Eq] Eq for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, -) with op_equal( +pub impl[ + T0 : Eq, + T1 : Eq, + T2 : Eq, + T3 : Eq, + T4 : Eq, + T5 : Eq, + T6 : Eq, + T7 : Eq, + T8 : Eq, + T9 : Eq, + T10 : Eq, + T11 : Eq, + T12 : Eq, + T13 : Eq, + T14 : Eq, + T15 : Eq, +] Eq for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) with equal( self : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), - other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) + other : (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), ) -> Bool { self.0 == other.0 && self.1 == other.1 && diff --git a/bundled-core/builtin/tuple_hash.mbt b/bundled-core/builtin/tuple_hash.mbt index da99dba..98427f5 100644 --- a/bundled-core/builtin/tuple_hash.mbt +++ b/bundled-core/builtin/tuple_hash.mbt @@ -21,7 +21,7 @@ pub impl[A : Hash, B : Hash] Hash for (A, B) with hash_combine(self, hasher) { ///| pub impl[A : Hash, B : Hash, C : Hash] Hash for (A, B, C) with hash_combine( self, - hasher + hasher, ) { let (a, b, c) = self hasher..combine(a)..combine(b)..combine(c) @@ -30,7 +30,7 @@ pub impl[A : Hash, B : Hash, C : Hash] Hash for (A, B, C) with hash_combine( ///| pub impl[A : Hash, B : Hash, C : Hash, D : Hash] Hash for (A, B, C, D) with hash_combine( self, - hasher + hasher, ) { let (a, b, c, d) = self hasher..combine(a)..combine(b)..combine(c)..combine(d) diff --git a/bundled-core/builtin/tuple_show.mbt b/bundled-core/builtin/tuple_show.mbt index aa169c2..dd1dc4f 100644 --- a/bundled-core/builtin/tuple_show.mbt +++ b/bundled-core/builtin/tuple_show.mbt @@ -26,7 +26,7 @@ pub impl[A : Show, B : Show] Show for (A, B) with output(self, logger) { ///| pub impl[A : Show, B : Show, C : Show] Show for (A, B, C) with output( self, - logger + logger, ) { let (a, b, c) = self logger @@ -42,7 +42,7 @@ pub impl[A : Show, B : Show, C : Show] Show for (A, B, C) with output( ///| pub impl[A : Show, B : Show, C : Show, D : Show] Show for (A, B, C, D) with output( self, - logger + logger, ) { let (a, b, c, d) = self logger @@ -136,16 +136,16 @@ pub impl[A : Show, B : Show, C : Show, D : Show, E : Show, F : Show, G : Show] S } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7) with output(self, logger) { let (x0, x1, x2, x3, x4, x5, x6, x7) = self logger ..write_string("(") @@ -168,17 +168,17 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8) with output(self, logger) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8) = self logger ..write_string("(") @@ -203,18 +203,18 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) with output(self, logger) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) = self logger ..write_string("(") @@ -241,19 +241,22 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) with output( + self, + logger, +) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = self logger ..write_string("(") @@ -282,20 +285,23 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, + T11 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) with output( + self, + logger, +) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) = self logger ..write_string("(") @@ -326,21 +332,24 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show, T12 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, + T11 : Show, + T12 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) with output( + self, + logger, +) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) = self logger ..write_string("(") @@ -373,22 +382,25 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show, T12 : Show, T13 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, + T11 : Show, + T12 : Show, + T13 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) with output( + self, + logger, +) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) = self logger ..write_string("(") @@ -423,23 +435,26 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show, T12 : Show, T13 : Show, T14 : Show] Show for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, -) with output(self, logger) { +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, + T11 : Show, + T12 : Show, + T13 : Show, + T14 : Show, +] Show for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) with output( + self, + logger, +) { let (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) = self logger ..write_string("(") @@ -476,7 +491,24 @@ pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : } ///| -pub impl[T0 : Show, T1 : Show, T2 : Show, T3 : Show, T4 : Show, T5 : Show, T6 : Show, T7 : Show, T8 : Show, T9 : Show, T10 : Show, T11 : Show, T12 : Show, T13 : Show, T14 : Show, T15 : Show] Show for ( +pub impl[ + T0 : Show, + T1 : Show, + T2 : Show, + T3 : Show, + T4 : Show, + T5 : Show, + T6 : Show, + T7 : Show, + T8 : Show, + T9 : Show, + T10 : Show, + T11 : Show, + T12 : Show, + T13 : Show, + T14 : Show, + T15 : Show, +] Show for ( T0, T1, T2, diff --git a/bundled-core/builtin/tuple_to_json.mbt b/bundled-core/builtin/tuple_to_json.mbt index a2a9bc6..050a335 100644 --- a/bundled-core/builtin/tuple_to_json.mbt +++ b/bundled-core/builtin/tuple_to_json.mbt @@ -14,21 +14,24 @@ ///| pub impl[A : ToJson, B : ToJson] ToJson for (A, B) with to_json(self) { - [self.0.to_json(), self.1.to_json()] + let (a0, a1) = self + [a0, a1] } ///| pub impl[A : ToJson, B : ToJson, C : ToJson] ToJson for (A, B, C) with to_json( - self + self, ) { - [self.0.to_json(), self.1.to_json(), self.2.to_json()] + let (a0, a1, a2) = self + [a0, a1, a2] } ///| pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson] ToJson for (A, B, C, D) with to_json( - self + self, ) { - [self.0.to_json(), self.1.to_json(), self.2.to_json(), self.3.to_json()] + let (a0, a1, a2, a3) = self + [a0, a1, a2, a3] } ///| @@ -39,13 +42,8 @@ pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson] ToJson for D, E, ) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - ] + let (a0, a1, a2, a3, a4) = self + [a0, a1, a2, a3, a4] } ///| @@ -57,312 +55,191 @@ pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson] E, F, ) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - ] + let (a0, a1, a2, a3, a4, a5) = self + [a0, a1, a2, a3, a4, a5] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, +] ToJson for (A, B, C, D, E, F, G) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6) = self + [a0, a1, a2, a3, a4, a5, a6] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, +] ToJson for (A, B, C, D, E, F, G, H) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7) = self + [a0, a1, a2, a3, a4, a5, a6, a7] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson, L : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - self.11.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, + L : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson, L : ToJson, M : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - self.11.to_json(), - self.12.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, + L : ToJson, + M : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson, L : ToJson, M : ToJson, N : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - self.11.to_json(), - self.12.to_json(), - self.13.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, + L : ToJson, + M : ToJson, + N : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson, L : ToJson, M : ToJson, N : ToJson, O : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - self.11.to_json(), - self.12.to_json(), - self.13.to_json(), - self.14.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, + L : ToJson, + M : ToJson, + N : ToJson, + O : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14] } ///| -pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson, G : ToJson, H : ToJson, I : ToJson, J : ToJson, K : ToJson, L : ToJson, M : ToJson, N : ToJson, O : ToJson, P : ToJson] ToJson for ( - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, -) with to_json(self) { - [ - self.0.to_json(), - self.1.to_json(), - self.2.to_json(), - self.3.to_json(), - self.4.to_json(), - self.5.to_json(), - self.6.to_json(), - self.7.to_json(), - self.8.to_json(), - self.9.to_json(), - self.10.to_json(), - self.11.to_json(), - self.12.to_json(), - self.13.to_json(), - self.14.to_json(), - self.15.to_json(), - ] +pub impl[ + A : ToJson, + B : ToJson, + C : ToJson, + D : ToJson, + E : ToJson, + F : ToJson, + G : ToJson, + H : ToJson, + I : ToJson, + J : ToJson, + K : ToJson, + L : ToJson, + M : ToJson, + N : ToJson, + O : ToJson, + P : ToJson, +] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) with to_json(self) { + let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) = self + [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15] } diff --git a/bundled-core/builtin/uninitialized_array.mbt b/bundled-core/builtin/uninitialized_array.mbt index 64218af..72069f4 100644 --- a/bundled-core/builtin/uninitialized_array.mbt +++ b/bundled-core/builtin/uninitialized_array.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -type UninitializedArray[T] FixedArray[UnsafeMaybeUninit[T]] +struct UninitializedArray[T](FixedArray[UnsafeMaybeUninit[T]]) ///| /// Creates an uninitialized array of the specified size. @@ -36,7 +36,7 @@ pub fn[T] UninitializedArray::make(size : Int) -> UninitializedArray[T] = "%fixe /// Returns the element at the specified index. pub fn[T] UninitializedArray::op_get( self : UninitializedArray[T], - index : Int + index : Int, ) -> T = "%fixedarray.get" ///| @@ -50,7 +50,7 @@ pub fn[T] UninitializedArray::op_get( pub fn[T] UninitializedArray::op_set( self : UninitializedArray[T], index : Int, - value : T + value : T, ) = "%fixedarray.set" ///| @@ -70,8 +70,8 @@ pub fn[T] UninitializedArray::op_set( /// than `end`. pub fn[T] UninitializedArray::op_as_view( self : UninitializedArray[T], - start~ : Int = 0, - end? : Int + start? : Int = 0, + end? : Int, ) -> ArrayView[T] { let len = self.length() let end = match end { @@ -81,7 +81,7 @@ pub fn[T] UninitializedArray::op_as_view( guard start >= 0 && start <= end && end <= len else { abort("View start index out of bounds") } - { buf: self, start, len: end - start } + ArrayView::make(self, start, end - start) } ///| @@ -104,17 +104,17 @@ pub fn[T] UninitializedArray::unsafe_blit( dst_offset : Int, src : UninitializedArray[T], src_offset : Int, - len : Int + len : Int, ) -> Unit { - FixedArray::unsafe_blit(dst.inner(), dst_offset, src.inner(), src_offset, len) + FixedArray::unsafe_blit(dst.0, dst_offset, src.0, src_offset, len) } ///| test "op_as_view with valid_range" { let arr : UninitializedArray[Int] = UninitializedArray::make(5) let view = arr[1:4] - inspect(view.start, content="1") - inspect(view.len, content="3") + inspect(view.start(), content="1") + inspect(view.len(), content="3") } ///| @@ -128,3 +128,17 @@ test "panic op_as_view with invalid_end" { let arr : UninitializedArray[Int] = UninitializedArray::make(5) ignore(arr[2:10]) } + +///| +#intrinsic("%fixedarray.fill") +#cfg(not(target="js")) +fn[T] UninitializedArray::unchecked_fill( + self : UninitializedArray[T], + start : Int, + value : T, + len : Int, +) -> Unit { + for i in start..<(start + len) { + self[i] = value + } +} diff --git a/bundled-core/builtin/unit.mbt b/bundled-core/builtin/unit.mbt index 63ad869..3fd377b 100644 --- a/bundled-core/builtin/unit.mbt +++ b/bundled-core/builtin/unit.mbt @@ -13,7 +13,6 @@ // limitations under the License. ///| -pub impl Eq for Unit with op_equal(self : Unit, _other : Unit) -> Bool { - let _ = self +pub impl Eq for Unit with equal(_ : Unit, _ : Unit) -> Bool { true } diff --git a/bundled-core/byte/byte_test.mbt b/bundled-core/byte/byte_test.mbt index 8a58a76..6501aab 100644 --- a/bundled-core/byte/byte_test.mbt +++ b/bundled-core/byte/byte_test.mbt @@ -14,8 +14,8 @@ ///| test "byte to uint" { - assert_eq(b'\xFF'.to_uint(), 255) - assert_eq(b'\x00'.to_uint(), 0) + inspect(b'\xFF'.to_uint(), content="255") + inspect(b'\x00'.to_uint(), content="0") } ///| diff --git a/bundled-core/byte/byte.mbti b/bundled-core/byte/pkg.generated.mbti similarity index 77% rename from bundled-core/byte/byte.mbti rename to bundled-core/byte/pkg.generated.mbti index 9e238c7..5ecd4d0 100644 --- a/bundled-core/byte/byte.mbti +++ b/bundled-core/byte/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/byte" // Values @@ -5,6 +6,8 @@ let max_value : Byte let min_value : Byte +// Errors + // Types and methods fn Byte::popcnt(Byte) -> Int fn Byte::to_uint64(Byte) -> UInt64 diff --git a/bundled-core/bytes/README.mbt.md b/bundled-core/bytes/README.mbt.md index d9108e9..8a7185f 100644 --- a/bundled-core/bytes/README.mbt.md +++ b/bundled-core/bytes/README.mbt.md @@ -13,9 +13,9 @@ test "bytes creation" { let bytes1 = @bytes.from_array(arr) inspect( bytes1, - content= + content=( #|b"\x68\x65\x6c\x6c\x6f" - , + ), ) // Create from fixed array @@ -23,27 +23,27 @@ test "bytes creation" { let bytes2 = @bytes.of(fixed) inspect( bytes2, - content= + content=( #|b"\x61\x61\x61" - , + ), ) // Create empty bytes let empty = @bytes.default() inspect( empty, - content= + content=( #|b"" - , + ), ) // Create from iterator let iter_bytes = @bytes.from_iter(arr.iter()) inspect( iter_bytes, - content= + content=( #|b"\x68\x65\x6c\x6c\x6f" - , + ), ) } ``` @@ -127,9 +127,9 @@ test "bytes operations" { let combined = b1 + b2 inspect( combined, - content= + content=( #|b"\x61\x62\x63\x64" - , + ), ) // Comparison diff --git a/bundled-core/bytes/bitstring.mbt b/bundled-core/bytes/bitstring.mbt new file mode 100644 index 0000000..eda5fd5 --- /dev/null +++ b/bundled-core/bytes/bitstring.mbt @@ -0,0 +1,342 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// offset and len are all in bits + +///| +/// Extract a single bit. +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_bit(bs : View, offset : Int, _len : Int) -> UInt { + let byte_index = offset >> 3 + let bit_mask = (1 << (7 - (offset & 7))).to_byte() + // TODO: branchless for performance + if (bs.unsafe_get(byte_index) & bit_mask) != 0 { + 1 + } else { + 0 + } +} + +///| +/// Extract [2..8] bits. +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_byte(bs : View, offset : Int, len : Int) -> UInt { + let byte_index = offset >> 3 + if (offset & 7) == 0 { + // byte-aligned case + let byte = bs.unsafe_get(byte_index) + (byte >> (8 - len)).to_uint() + } else if (offset & 7) + len <= 8 { + // All bits are within the same byte - no need to read next byte + let byte = bs.unsafe_get(byte_index).to_uint() + let shift = 8 - ((offset & 7) + len) + let mask = (1U << len) - 1 + (byte >> shift) & mask + } else { + // extract 16 bits at [byte_index, byte_index + 1] + let b0 = bs.unsafe_get(byte_index).to_uint() + let b1 = bs.unsafe_get(byte_index + 1).to_uint() + let data = (b0 << 8) | b1 + // mask off the top bits + let bit_mask = (1U << (16 - (offset & 7))) - 1 + let data = data & bit_mask + let shift = 16 - ((offset & 7) + len) + data >> shift + } +} + +///| +/// Extract [9..32] bits in little-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 2 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length +/// +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_uint_le(bs : View, offset : Int, len : Int) -> UInt { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8) + match bytes_needed { + 2 => { + let b1 = bs.unsafe_extract_byte(offset + 8, len - 8) + (b1 << 8) | b0 + } + 3 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, len - 16) + (b2 << 16) | (b1 << 8) | b0 + } + 4 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, 8) + let b3 = bs.unsafe_extract_byte(offset + 24, len - 24) + (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + _ => abort("Invalid byte count for int32 extraction") + } +} + +///| +/// Extract [9..32] bits in big-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 2 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length +/// +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_uint_be(bs : View, offset : Int, len : Int) -> UInt { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8) + match bytes_needed { + 2 => { + let b1 = bs.unsafe_extract_byte(offset + 8, len - 8) + let shift = 16 - len + let data = (b0 << 8) | (b1 << shift) + data >> shift + } + 3 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, len - 16) + let shift = 24 - len + let data = (b0 << 16) | (b1 << 8) | (b2 << shift) + data >> shift + } + 4 => { + let b1 = bs.unsafe_extract_byte(offset + 8, 8) + let b2 = bs.unsafe_extract_byte(offset + 16, 8) + let b3 = bs.unsafe_extract_byte(offset + 24, len - 24) + let shift = 32 - len + let data = (b0 << 24) | (b1 << 16) | (b2 << 8) | (b3 << shift) + data >> shift + } + _ => abort("Invalid byte count for int32 extraction") + } +} + +///| +/// Extract [33..64] bits in little-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 5 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length (5-8 bytes) +/// - For bit lengths < 33, use unsafe_extract_int_le instead +/// +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_uint64_le( + bs : View, + offset : Int, + len : Int, +) -> UInt64 { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8).to_uint64() + let b1 = bs.unsafe_extract_byte(offset + 8, 8).to_uint64() + let b2 = bs.unsafe_extract_byte(offset + 16, 8).to_uint64() + let b3 = bs.unsafe_extract_byte(offset + 24, 8).to_uint64() + match bytes_needed { + 5 => { + let b4 = bs.unsafe_extract_byte(offset + 32, len - 32).to_uint64() + (b4 << 32) | (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + 6 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, len - 40).to_uint64() + (b5 << 40) | (b4 << 32) | (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 + } + 7 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, len - 48).to_uint64() + (b6 << 48) | + (b5 << 40) | + (b4 << 32) | + (b3 << 24) | + (b2 << 16) | + (b1 << 8) | + b0 + } + 8 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, 8).to_uint64() + let b7 = bs.unsafe_extract_byte(offset + 56, len - 56).to_uint64() + (b7 << 56) | + (b6 << 48) | + (b5 << 40) | + (b4 << 32) | + (b3 << 24) | + (b2 << 16) | + (b1 << 8) | + b0 + } + _ => abort("Invalid byte count for int64 extraction") + } +} + +///| +/// Extract [33..64] bits in big-endian byte order. +/// +/// # Invariants +/// - It's guaranteed to have at least 5 bytes available for extraction +/// - Only reads the necessary number of bytes based on the bit length (5-8 bytes) +/// - For bit lengths < 33, use unsafe_extract_int_be instead +/// +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_uint64_be( + bs : View, + offset : Int, + len : Int, +) -> UInt64 { + let bytes_needed = (len + 7) / 8 + // TODO: add fast path for aligned case + // non-aligned case: extract bytes using unsafe_extract_byte + let b0 = bs.unsafe_extract_byte(offset, 8).to_uint64() + let b1 = bs.unsafe_extract_byte(offset + 8, 8).to_uint64() + let b2 = bs.unsafe_extract_byte(offset + 16, 8).to_uint64() + let b3 = bs.unsafe_extract_byte(offset + 24, 8).to_uint64() + match bytes_needed { + 5 => { + let b4 = bs.unsafe_extract_byte(offset + 32, len - 32).to_uint64() + let shift = 40 - len + let data = (b0 << 32) | + (b1 << 24) | + (b2 << 16) | + (b3 << 8) | + (b4 << shift) + data >> shift + } + 6 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, len - 40).to_uint64() + let shift = 48 - len + let data = (b0 << 40) | + (b1 << 32) | + (b2 << 24) | + (b3 << 16) | + (b4 << 8) | + (b5 << shift) + data >> shift + } + 7 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, len - 48).to_uint64() + let shift = 56 - len + let data = (b0 << 48) | + (b1 << 40) | + (b2 << 32) | + (b3 << 24) | + (b4 << 16) | + (b5 << 8) | + (b6 << shift) + data >> shift + } + 8 => { + let b4 = bs.unsafe_extract_byte(offset + 32, 8).to_uint64() + let b5 = bs.unsafe_extract_byte(offset + 40, 8).to_uint64() + let b6 = bs.unsafe_extract_byte(offset + 48, 8).to_uint64() + let b7 = bs.unsafe_extract_byte(offset + 56, len - 56).to_uint64() + let shift = 64 - len + let data = (b0 << 56) | + (b1 << 48) | + (b2 << 40) | + (b3 << 32) | + (b4 << 24) | + (b5 << 16) | + (b6 << 8) | + (b7 << shift) + data >> shift + } + _ => abort("Invalid byte count for int64 extraction") + } +} + +///| +/// Extract a subview from a view. `offset` and `len` are in bits and must be +/// aligned to bytes. +#internal(experimental, "subject to breaking change without notice") +pub fn View::unsafe_extract_bytesview( + bs : View, + offset : Int, + len : Int, +) -> View { + View::make(bs.bytes(), bs.start() + (offset >> 3), len >> 3) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_bit(bs : Bytes, offset : Int, len : Int) -> UInt { + bs[:].unsafe_extract_bit(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_byte(bs : Bytes, offset : Int, len : Int) -> UInt { + bs[:].unsafe_extract_byte(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_uint_le( + bs : Bytes, + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_uint_le(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_uint_be( + bs : Bytes, + offset : Int, + len : Int, +) -> UInt { + bs[:].unsafe_extract_uint_be(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_uint64_le( + bs : Bytes, + offset : Int, + len : Int, +) -> UInt64 { + bs[:].unsafe_extract_uint64_le(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_uint64_be( + bs : Bytes, + offset : Int, + len : Int, +) -> UInt64 { + bs[:].unsafe_extract_uint64_be(offset, len) +} + +///| +#internal(experimental, "subject to breaking change without notice") +pub fn Bytes::unsafe_extract_bytesview( + bs : Bytes, + offset : Int, + len : Int, +) -> View { + bs[:].unsafe_extract_bytesview(offset, len) +} diff --git a/bundled-core/bytes/bitstring_pcap_test.mbt b/bundled-core/bytes/bitstring_pcap_test.mbt new file mode 100644 index 0000000..aa49e16 --- /dev/null +++ b/bundled-core/bytes/bitstring_pcap_test.mbt @@ -0,0 +1,118 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +let data : Bytes = b"\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x01\x00\x00\x00\xBB\x0A\x64\x42\x6B\x54\x0D\x00\x4A\x00\x00\x00\x4A\x00\x00\x00\x00\xA0\xC5\x8F\xE3\xC7\x00\x0C\x76\x1C\x1B\x97\x08\x00\x45\x00\x00\x3C\x92\xA6\x40\x00\x40\x06\xFA\x91\xC0\xA8\x01\x21\xCC\xB2\x1F\x08\xDB\x2D\x02\x34\x67\xA5\x08\x98\x00\x00\x00\x00\x00\xA0\xC2\x16\xD0\x94\xBF\x00\x00\x02\x04\x05\xB4\x04\x02\x08\x0A\x06\x54\x9A\x3E\x00\x00\x00\x00\x01\x03\x03\x02" + +///| +fn pcap_test(data : @bytes.View) -> Unit raise { + match data { + [ + u32(0xa1b2c3d4 | 0xa1b23c4d | 0xd4c3b2a1 | 0x4d3cb2a1 as magic), + // major version + u16le(2), + // minor version + u16le(4), + // time zone + u32(_), + // unused + u32(0), + // snaplen + u32(_), + // network + u32(_), + .. packet, + ] => { + assert_eq(magic, 0xd4c3b2a1) + pcap_packet_test(packet) + } + _ => fail("Not a valid pcap file") + } +} + +///| +fn pcap_packet_test(packet : @bytes.View) -> Unit raise { + match packet { + [u32(_), u32(_), u32le(incl_len), u32le(_orig_len), .. eth] => { + let incl_len = incl_len.reinterpret_as_int() + pcap_eth_test(eth[:incl_len]) + } + _ => fail("Not a valid packet descriptor") + } +} + +///| +fn pcap_eth_test(eth : @bytes.View) -> Unit raise { + match eth { + [ + u8(0x00), + u8(0xA0), + u8(0xC5), + u8(0x8F), + u8(0xE3), + // destination MAC + u8(0xC7), + u8(0x00), + u8(0x0C), + u8(0x76), + u8(0x1C), + u8(0x1B), + // source MAC + u8(0x97), + // ethertype + u16be(0x0800), + .. ipv4, + ] => pcap_ipv4_test(ipv4) + _ => fail("Not a valid ethernet frame") + } +} + +///| +fn pcap_ipv4_test(ipv4 : @bytes.View) -> Unit raise { + match ipv4 { + [ + u4(4), + u4(5), + // dscn/ecn + u8(0), + u16be(60), + // ident + u16be(0x92A6), + // flags + u3(0x02), + // fragment offset + u13be(0), + // ttl + u8(64), + u8(0x06), + // checksum + u16be(0xFA91), + u8(0xC0), + u8(0xA8), + u8(0x01), + u8(0x21), + u8(0xCC), + u8(0xB2), + u8(0x1F), + u8(0x08), + .., + ] => () + _ => fail("Not a valid ipv4 layer") + } +} + +///| +test "pcap_test" { + inspect(try? pcap_test(data[:]), content="Ok(())") +} diff --git a/bundled-core/bytes/bitstring_test.mbt b/bundled-core/bytes/bitstring_test.mbt new file mode 100644 index 0000000..675a0a9 --- /dev/null +++ b/bundled-core/bytes/bitstring_test.mbt @@ -0,0 +1,1885 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ============================================================================ +// Bitstring Extraction Tests +// +// This file contains comprehensive tests for bitstring operations in the bytes module. +// The tests are organized by function and cover various edge cases, bit alignments, +// and data patterns to ensure robust operation. +// ============================================================================ + +// ============================================================================ +// 1. Single Bit Extraction Tests +// ============================================================================ + +///| +test "extract_bit - basic bit patterns" { + // Test with alternating bit pattern: 0xAA = 10101010, 0x55 = 01010101 + let bytes = b"\xAA\x55\xFF\x00" + let view = bytes[:] + + // First byte (0xAA = 10101010) + inspect(view.unsafe_extract_bit(0, 1), content="1") // MSB + inspect(view.unsafe_extract_bit(1, 1), content="0") + inspect(view.unsafe_extract_bit(2, 1), content="1") + inspect(view.unsafe_extract_bit(3, 1), content="0") + inspect(view.unsafe_extract_bit(4, 1), content="1") + inspect(view.unsafe_extract_bit(5, 1), content="0") + inspect(view.unsafe_extract_bit(6, 1), content="1") + inspect(view.unsafe_extract_bit(7, 1), content="0") // LSB + + // Second byte (0x55 = 01010101) + inspect(view.unsafe_extract_bit(8, 1), content="0") // MSB + inspect(view.unsafe_extract_bit(9, 1), content="1") + inspect(view.unsafe_extract_bit(10, 1), content="0") + inspect(view.unsafe_extract_bit(11, 1), content="1") + inspect(view.unsafe_extract_bit(12, 1), content="0") + inspect(view.unsafe_extract_bit(13, 1), content="1") + inspect(view.unsafe_extract_bit(14, 1), content="0") + inspect(view.unsafe_extract_bit(15, 1), content="1") // LSB +} + +///| +test "extract_bit - edge patterns" { + let bytes = b"\xFF\x00\x80\x01" + let view = bytes[:] + + // All ones byte (0xFF) + for i = 0; i < 8; i = i + 1 { + inspect(view.unsafe_extract_bit(i, 1), content="1") + } + + // All zeros byte (0x00) + for i = 8; i < 16; i = i + 1 { + inspect(view.unsafe_extract_bit(i, 1), content="0") + } + + // Single high bit (0x80 = 10000000) + inspect(view.unsafe_extract_bit(16, 1), content="1") + for i = 17; i < 24; i = i + 1 { + inspect(view.unsafe_extract_bit(i, 1), content="0") + } + + // Single low bit (0x01 = 00000001) + for i = 24; i < 31; i = i + 1 { + inspect(view.unsafe_extract_bit(i, 1), content="0") + } + inspect(view.unsafe_extract_bit(31, 1), content="1") +} + +// ============================================================================ +// 2. Byte Extraction Tests (2-8 bits) +// ============================================================================ + +///| +test "extract_byte - byte aligned full bytes" { + let bytes = b"\x12\x34\x56\x78\x9A\xBC\xDE\xF0" + let view = bytes[:] + + // Test extracting full bytes at byte boundaries + inspect(view.unsafe_extract_byte(0, 8), content="18") + inspect(view.unsafe_extract_byte(8, 8), content="52") + inspect(view.unsafe_extract_byte(16, 8), content="86") + inspect(view.unsafe_extract_byte(24, 8), content="120") + inspect(view.unsafe_extract_byte(32, 8), content="154") + inspect(view.unsafe_extract_byte(40, 8), content="188") + inspect(view.unsafe_extract_byte(48, 8), content="222") + inspect(view.unsafe_extract_byte(56, 8), content="240") +} + +///| +test "extract_byte - partial bits from byte aligned positions" { + let bytes = b"\xFF\x00\xAA\x55" + let view = bytes[:] + + // Extract 2-7 bits from 0xFF (11111111) + inspect(view.unsafe_extract_byte(0, 2), content="3") // 11 -> 3 + inspect(view.unsafe_extract_byte(0, 3), content="7") // 111 -> 7 + inspect(view.unsafe_extract_byte(0, 4), content="15") // 1111 -> 15 + inspect(view.unsafe_extract_byte(0, 5), content="31") // 11111 -> 31 + inspect(view.unsafe_extract_byte(0, 6), content="63") // 111111 -> 63 + inspect(view.unsafe_extract_byte(0, 7), content="127") // 1111111 -> 127 + + // Extract 2-7 bits from 0x00 (00000000) + for len = 2; len <= 7; len = len + 1 { + inspect(view.unsafe_extract_byte(8, len), content="0") + } +} + +///| +test "extract_byte - non-aligned positions" { + let bytes = b"\xF0\x0F\xCC\x33" // 11110000 00001111 11001100 00110011 + let view = bytes[:] + + // Extract from middle of first byte + inspect(view.unsafe_extract_byte(2, 4), content="12") // 1100 from 11110000 + inspect(view.unsafe_extract_byte(4, 4), content="0") // 0000 from 11110000 + + // Cross byte boundaries + inspect(view.unsafe_extract_byte(6, 4), content="0") // 00 from byte 0 + 00 from byte 1 + inspect(view.unsafe_extract_byte(7, 2), content="0") // 0 from byte 0 + 0 from byte 1 + inspect(view.unsafe_extract_byte(7, 6), content="1") // 0 from byte 0 + 00001 from byte 1 + + // Extract from middle positions spanning bytes + inspect(view.unsafe_extract_byte(10, 4), content="3") // 1111 from 00001111 + inspect(view.unsafe_extract_byte(14, 4), content="15") // 11 from byte 1 + 11 from byte 2 +} + +///| +test "extract_byte - bit patterns and edge cases" { + let bytes = b"\x81\x42\x24\x18\x99\x66\xA5\x5A" + let view = bytes[:] + + // Test various lengths from different starting positions + inspect(view.unsafe_extract_byte(1, 3), content="0") // 3 bits starting from bit 1 + inspect(view.unsafe_extract_byte(5, 5), content="5") // 5 bits crossing byte boundary + inspect(view.unsafe_extract_byte(9, 7), content="66") // 7 bits from middle positions + inspect(view.unsafe_extract_byte(17, 6), content="18") // 6 bits from byte 2 + + // Test maximum span (8 bits) from non-aligned positions + inspect(view.unsafe_extract_byte(3, 8), content="10") // 8 bits starting from bit 3 + inspect(view.unsafe_extract_byte(5, 8), content="40") // 8 bits starting from bit 5 +} + +// ============================================================================ +// 3. Integer Extraction Tests - Little Endian (9-32 bits) +// ============================================================================ + +///| +test "extract_int_le" { + let s = b"\xA5\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint_le(0, 9).to_be_bytes(), + content=( + #|b"\x00\x00\x01\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 10).to_be_bytes(), + content=( + #|b"\x00\x00\x02\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 11).to_be_bytes(), + content=( + #|b"\x00\x00\x05\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 12).to_be_bytes(), + content=( + #|b"\x00\x00\x0b\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 13).to_be_bytes(), + content=( + #|b"\x00\x00\x16\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 14).to_be_bytes(), + content=( + #|b"\x00\x00\x2d\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 15).to_be_bytes(), + content=( + #|b"\x00\x00\x5b\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 16).to_be_bytes(), + content=( + #|b"\x00\x00\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 17).to_be_bytes(), + content=( + #|b"\x00\x01\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 18).to_be_bytes(), + content=( + #|b"\x00\x03\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 19).to_be_bytes(), + content=( + #|b"\x00\x06\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 20).to_be_bytes(), + content=( + #|b"\x00\x0c\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 21).to_be_bytes(), + content=( + #|b"\x00\x18\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 22).to_be_bytes(), + content=( + #|b"\x00\x31\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 23).to_be_bytes(), + content=( + #|b"\x00\x63\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 24).to_be_bytes(), + content=( + #|b"\x00\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 25).to_be_bytes(), + content=( + #|b"\x01\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 26).to_be_bytes(), + content=( + #|b"\x03\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 27).to_be_bytes(), + content=( + #|b"\x06\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 28).to_be_bytes(), + content=( + #|b"\x0d\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 29).to_be_bytes(), + content=( + #|b"\x1b\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 30).to_be_bytes(), + content=( + #|b"\x36\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 31).to_be_bytes(), + content=( + #|b"\x6c\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(0, 32).to_be_bytes(), + content=( + #|b"\xd8\xc7\xb6\xa5" + ), + ) +} + +///| +test "extract_int_le" { + let s = b"\xA5\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint_le(0, 32).to_be_bytes(), + content=( + #|b"\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint_le(1, 31).to_be_bytes(), + content=( + #|b"\x58\x8f\x6d\x4b" + ), + ) + inspect( + s.unsafe_extract_uint_le(2, 30).to_be_bytes(), + content=( + #|b"\x18\x1f\xdb\x96" + ), + ) + inspect( + s.unsafe_extract_uint_le(3, 29).to_be_bytes(), + content=( + #|b"\x18\x3e\xb6\x2d" + ), + ) + inspect( + s.unsafe_extract_uint_le(4, 28).to_be_bytes(), + content=( + #|b"\x08\x7d\x6c\x5b" + ), + ) + inspect( + s.unsafe_extract_uint_le(5, 27).to_be_bytes(), + content=( + #|b"\x00\xfb\xd8\xb6" + ), + ) + inspect( + s.unsafe_extract_uint_le(6, 26).to_be_bytes(), + content=( + #|b"\x00\xf6\xb1\x6d" + ), + ) + inspect( + s.unsafe_extract_uint_le(7, 25).to_be_bytes(), + content=( + #|b"\x00\xec\x63\xdb" + ), + ) + inspect( + s.unsafe_extract_uint_le(8, 24).to_be_bytes(), + content=( + #|b"\x00\xd8\xc7\xb6" + ), + ) + inspect( + s.unsafe_extract_uint_le(9, 23).to_be_bytes(), + content=( + #|b"\x00\x58\x8f\x6d" + ), + ) + inspect( + s.unsafe_extract_uint_le(10, 22).to_be_bytes(), + content=( + #|b"\x00\x18\x1f\xdb" + ), + ) + inspect( + s.unsafe_extract_uint_le(11, 21).to_be_bytes(), + content=( + #|b"\x00\x18\x3e\xb6" + ), + ) + inspect( + s.unsafe_extract_uint_le(12, 20).to_be_bytes(), + content=( + #|b"\x00\x08\x7d\x6c" + ), + ) + inspect( + s.unsafe_extract_uint_le(13, 19).to_be_bytes(), + content=( + #|b"\x00\x00\xfb\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_le(14, 18).to_be_bytes(), + content=( + #|b"\x00\x00\xf6\xb1" + ), + ) + inspect( + s.unsafe_extract_uint_le(15, 17).to_be_bytes(), + content=( + #|b"\x00\x00\xec\x63" + ), + ) + inspect( + s.unsafe_extract_uint_le(16, 16).to_be_bytes(), + content=( + #|b"\x00\x00\xd8\xc7" + ), + ) + inspect( + s.unsafe_extract_uint_le(17, 15).to_be_bytes(), + content=( + #|b"\x00\x00\x58\x8f" + ), + ) + inspect( + s.unsafe_extract_uint_le(18, 14).to_be_bytes(), + content=( + #|b"\x00\x00\x18\x1f" + ), + ) + inspect( + s.unsafe_extract_uint_le(19, 13).to_be_bytes(), + content=( + #|b"\x00\x00\x18\x3e" + ), + ) + inspect( + s.unsafe_extract_uint_le(20, 12).to_be_bytes(), + content=( + #|b"\x00\x00\x08\x7d" + ), + ) + inspect( + s.unsafe_extract_uint_le(21, 11).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xfb" + ), + ) + inspect( + s.unsafe_extract_uint_le(22, 10).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xf6" + ), + ) + inspect( + s.unsafe_extract_uint_le(23, 9).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xec" + ), + ) +} + +///| +test "extract_int_le" { + let s = b"\xA5\xB6\xC7\xD8\xA5\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint_le(1, 32).to_be_bytes(), + content=( + #|b"\xb1\x8f\x6d\x4b" + ), + ) + inspect( + s.unsafe_extract_uint_le(2, 32).to_be_bytes(), + content=( + #|b"\x62\x1f\xdb\x96" + ), + ) + inspect( + s.unsafe_extract_uint_le(3, 32).to_be_bytes(), + content=( + #|b"\xc5\x3e\xb6\x2d" + ), + ) + inspect( + s.unsafe_extract_uint_le(4, 32).to_be_bytes(), + content=( + #|b"\x8a\x7d\x6c\x5b" + ), + ) + inspect( + s.unsafe_extract_uint_le(5, 32).to_be_bytes(), + content=( + #|b"\x14\xfb\xd8\xb6" + ), + ) + inspect( + s.unsafe_extract_uint_le(6, 32).to_be_bytes(), + content=( + #|b"\x29\xf6\xb1\x6d" + ), + ) + inspect( + s.unsafe_extract_uint_le(7, 32).to_be_bytes(), + content=( + #|b"\x52\xec\x63\xdb" + ), + ) + inspect( + s.unsafe_extract_uint_le(8, 32).to_be_bytes(), + content=( + #|b"\xa5\xd8\xc7\xb6" + ), + ) +} + +///| +test "extract_int_le_path_consistent" { + let s1 = b"\xBA\xAA\xAA\xAA"[:] + let i1 = s1.unsafe_extract_uint_le(4, 28) + let s2 = b"\xAA\xAA\xAA\xAB"[:] + let i2 = s2.unsafe_extract_uint_le(0, 28) + inspect(i1, content="178956970") + inspect(i2, content="178956970") +} + +// ============================================================================ +// 4. Integer Extraction Tests - Big Endian (9-32 bits) +// ============================================================================ + +///| +test "extract_int_be" { + let s = b"\xA5\xB6\xC7\xD8"[:] + inspect( + (s.unsafe_extract_uint_be(0, 9) << 23).to_be_bytes(), + content=( + #|b"\xa5\x80\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 10) << 22).to_be_bytes(), + content=( + #|b"\xa5\x80\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 11) << 21).to_be_bytes(), + content=( + #|b"\xa5\xa0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 12) << 20).to_be_bytes(), + content=( + #|b"\xa5\xb0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 13) << 19).to_be_bytes(), + content=( + #|b"\xa5\xb0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 14) << 18).to_be_bytes(), + content=( + #|b"\xa5\xb4\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 15) << 17).to_be_bytes(), + content=( + #|b"\xa5\xb6\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 16) << 16).to_be_bytes(), + content=( + #|b"\xa5\xb6\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 17) << 15).to_be_bytes(), + content=( + #|b"\xa5\xb6\x80\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 18) << 14).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 19) << 13).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 20) << 12).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 21) << 11).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 22) << 10).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc4\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 23) << 9).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc6\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 24) << 8).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\x00" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 25) << 7).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\x80" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 26) << 6).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xc0" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 27) << 5).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xc0" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 28) << 4).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd0" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 29) << 3).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 30) << 2).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 31) << 1).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8" + ), + ) + inspect( + (s.unsafe_extract_uint_be(0, 32) << 0).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8" + ), + ) +} + +///| +test "extract_int_be" { + let s = b"\xA5\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint_be(0, 32).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(1, 31).to_be_bytes(), + content=( + #|b"\x25\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(2, 30).to_be_bytes(), + content=( + #|b"\x25\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(3, 29).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(4, 28).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(5, 27).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(6, 26).to_be_bytes(), + content=( + #|b"\x01\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(7, 25).to_be_bytes(), + content=( + #|b"\x01\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(8, 24).to_be_bytes(), + content=( + #|b"\x00\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(9, 23).to_be_bytes(), + content=( + #|b"\x00\x36\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(10, 22).to_be_bytes(), + content=( + #|b"\x00\x36\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(11, 21).to_be_bytes(), + content=( + #|b"\x00\x16\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(12, 20).to_be_bytes(), + content=( + #|b"\x00\x06\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(13, 19).to_be_bytes(), + content=( + #|b"\x00\x06\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(14, 18).to_be_bytes(), + content=( + #|b"\x00\x02\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(15, 17).to_be_bytes(), + content=( + #|b"\x00\x00\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(16, 16).to_be_bytes(), + content=( + #|b"\x00\x00\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(17, 15).to_be_bytes(), + content=( + #|b"\x00\x00\x47\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(18, 14).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(19, 13).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(20, 12).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(21, 11).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(22, 10).to_be_bytes(), + content=( + #|b"\x00\x00\x03\xd8" + ), + ) + inspect( + s.unsafe_extract_uint_be(23, 9).to_be_bytes(), + content=( + #|b"\x00\x00\x01\xd8" + ), + ) +} + +///| +test "extract_int_be" { + let s = b"\xA5\xB6\xC7\xD8\xA5\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint_be(1, 32).to_be_bytes(), + content=( + #|b"\x4b\x6d\x8f\xb1" + ), + ) + inspect( + s.unsafe_extract_uint_be(2, 32).to_be_bytes(), + content=( + #|b"\x96\xdb\x1f\x62" + ), + ) + inspect( + s.unsafe_extract_uint_be(3, 32).to_be_bytes(), + content=( + #|b"\x2d\xb6\x3e\xc5" + ), + ) + inspect( + s.unsafe_extract_uint_be(4, 32).to_be_bytes(), + content=( + #|b"\x5b\x6c\x7d\x8a" + ), + ) + inspect( + s.unsafe_extract_uint_be(5, 32).to_be_bytes(), + content=( + #|b"\xb6\xd8\xfb\x14" + ), + ) + inspect( + s.unsafe_extract_uint_be(6, 32).to_be_bytes(), + content=( + #|b"\x6d\xb1\xf6\x29" + ), + ) + inspect( + s.unsafe_extract_uint_be(7, 32).to_be_bytes(), + content=( + #|b"\xdb\x63\xec\x52" + ), + ) + inspect( + s.unsafe_extract_uint_be(8, 32).to_be_bytes(), + content=( + #|b"\xb6\xc7\xd8\xa5" + ), + ) +} + +///| +test "extract_int_be_path_consistent" { + let s1 = b"\xBA\xAA\xAA\xAA"[:] + let i1 = s1.unsafe_extract_uint_be(4, 28) + let s2 = b"\xAA\xAA\xAA\xAB"[:] + let i2 = s2.unsafe_extract_uint_be(0, 28) + inspect(i1, content="178956970") + inspect(i2, content="178956970") +} + +// ============================================================================ +// 5. Int64 Extraction Tests - Little Endian (33-64 bits) +// ============================================================================ + +///| +test "extract_int64_le" { + let s = b"\xA5\xB6\xC7\xD8\x5A\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint64_le(0, 33).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 34).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x01\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 35).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x02\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 36).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x05\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 37).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x0b\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 38).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x16\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 39).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x2d\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 40).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 41).to_be_bytes(), + content=( + #|b"\x00\x00\x01\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 42).to_be_bytes(), + content=( + #|b"\x00\x00\x02\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 43).to_be_bytes(), + content=( + #|b"\x00\x00\x05\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 44).to_be_bytes(), + content=( + #|b"\x00\x00\x0b\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 45).to_be_bytes(), + content=( + #|b"\x00\x00\x16\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 46).to_be_bytes(), + content=( + #|b"\x00\x00\x2d\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 47).to_be_bytes(), + content=( + #|b"\x00\x00\x5b\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 48).to_be_bytes(), + content=( + #|b"\x00\x00\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 49).to_be_bytes(), + content=( + #|b"\x00\x01\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 50).to_be_bytes(), + content=( + #|b"\x00\x03\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 51).to_be_bytes(), + content=( + #|b"\x00\x06\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 52).to_be_bytes(), + content=( + #|b"\x00\x0c\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 53).to_be_bytes(), + content=( + #|b"\x00\x18\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 54).to_be_bytes(), + content=( + #|b"\x00\x31\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 55).to_be_bytes(), + content=( + #|b"\x00\x63\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 56).to_be_bytes(), + content=( + #|b"\x00\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 57).to_be_bytes(), + content=( + #|b"\x01\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 58).to_be_bytes(), + content=( + #|b"\x03\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 59).to_be_bytes(), + content=( + #|b"\x06\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 60).to_be_bytes(), + content=( + #|b"\x0d\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 61).to_be_bytes(), + content=( + #|b"\x1b\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 62).to_be_bytes(), + content=( + #|b"\x36\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 63).to_be_bytes(), + content=( + #|b"\x6c\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(0, 64).to_be_bytes(), + content=( + #|b"\xd8\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) +} + +///| +test "extract_int64_le" { + let s = b"\xA5\xB6\xC7\xD8\x5A\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint64_le(0, 64).to_be_bytes(), + content=( + #|b"\xd8\xc7\xb6\x5a\xd8\xc7\xb6\xa5" + ), + ) + inspect( + s.unsafe_extract_uint64_le(1, 63).to_be_bytes(), + content=( + #|b"\x58\x8f\x6d\xb5\xb0\x8f\x6d\x4b" + ), + ) + inspect( + s.unsafe_extract_uint64_le(2, 62).to_be_bytes(), + content=( + #|b"\x18\x1f\xdb\x6a\x61\x1f\xdb\x96" + ), + ) + inspect( + s.unsafe_extract_uint64_le(3, 61).to_be_bytes(), + content=( + #|b"\x18\x3e\xb6\xd5\xc2\x3e\xb6\x2d" + ), + ) + inspect( + s.unsafe_extract_uint64_le(4, 60).to_be_bytes(), + content=( + #|b"\x08\x7d\x6c\xab\x85\x7d\x6c\x5b" + ), + ) + inspect( + s.unsafe_extract_uint64_le(5, 59).to_be_bytes(), + content=( + #|b"\x00\xfb\xd8\x56\x0b\xfb\xd8\xb6" + ), + ) + inspect( + s.unsafe_extract_uint64_le(6, 58).to_be_bytes(), + content=( + #|b"\x00\xf6\xb1\xad\x16\xf6\xb1\x6d" + ), + ) + inspect( + s.unsafe_extract_uint64_le(7, 57).to_be_bytes(), + content=( + #|b"\x00\xec\x63\x5b\x2d\xec\x63\xdb" + ), + ) + inspect( + s.unsafe_extract_uint64_le(8, 56).to_be_bytes(), + content=( + #|b"\x00\xd8\xc7\xb6\x5a\xd8\xc7\xb6" + ), + ) + inspect( + s.unsafe_extract_uint64_le(9, 55).to_be_bytes(), + content=( + #|b"\x00\x58\x8f\x6d\xb5\xb0\x8f\x6d" + ), + ) + inspect( + s.unsafe_extract_uint64_le(10, 54).to_be_bytes(), + content=( + #|b"\x00\x18\x1f\xdb\x6a\x61\x1f\xdb" + ), + ) + inspect( + s.unsafe_extract_uint64_le(11, 53).to_be_bytes(), + content=( + #|b"\x00\x18\x3e\xb6\xd5\xc2\x3e\xb6" + ), + ) + inspect( + s.unsafe_extract_uint64_le(12, 52).to_be_bytes(), + content=( + #|b"\x00\x08\x7d\x6c\xab\x85\x7d\x6c" + ), + ) + inspect( + s.unsafe_extract_uint64_le(13, 51).to_be_bytes(), + content=( + #|b"\x00\x00\xfb\xd8\x56\x0b\xfb\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_le(14, 50).to_be_bytes(), + content=( + #|b"\x00\x00\xf6\xb1\xad\x16\xf6\xb1" + ), + ) + inspect( + s.unsafe_extract_uint64_le(15, 49).to_be_bytes(), + content=( + #|b"\x00\x00\xec\x63\x5b\x2d\xec\x63" + ), + ) + inspect( + s.unsafe_extract_uint64_le(16, 48).to_be_bytes(), + content=( + #|b"\x00\x00\xd8\xc7\xb6\x5a\xd8\xc7" + ), + ) + inspect( + s.unsafe_extract_uint64_le(17, 47).to_be_bytes(), + content=( + #|b"\x00\x00\x58\x8f\x6d\xb5\xb0\x8f" + ), + ) + inspect( + s.unsafe_extract_uint64_le(18, 46).to_be_bytes(), + content=( + #|b"\x00\x00\x18\x1f\xdb\x6a\x61\x1f" + ), + ) + inspect( + s.unsafe_extract_uint64_le(19, 45).to_be_bytes(), + content=( + #|b"\x00\x00\x18\x3e\xb6\xd5\xc2\x3e" + ), + ) + inspect( + s.unsafe_extract_uint64_le(20, 44).to_be_bytes(), + content=( + #|b"\x00\x00\x08\x7d\x6c\xab\x85\x7d" + ), + ) + inspect( + s.unsafe_extract_uint64_le(21, 43).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xfb\xd8\x56\x0b\xfb" + ), + ) + inspect( + s.unsafe_extract_uint64_le(22, 42).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xf6\xb1\xad\x16\xf6" + ), + ) + inspect( + s.unsafe_extract_uint64_le(23, 41).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xec\x63\x5b\x2d\xec" + ), + ) + inspect( + s.unsafe_extract_uint64_le(24, 40).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xd8\xc7\xb6\x5a\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_le(25, 39).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x58\x8f\x6d\xb5\xb0" + ), + ) + inspect( + s.unsafe_extract_uint64_le(26, 38).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x18\x1f\xdb\x6a\x61" + ), + ) + inspect( + s.unsafe_extract_uint64_le(27, 37).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x18\x3e\xb6\xd5\xc2" + ), + ) + inspect( + s.unsafe_extract_uint64_le(28, 36).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x08\x7d\x6c\xab\x85" + ), + ) + inspect( + s.unsafe_extract_uint64_le(29, 35).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\xfb\xd8\x56\x0b" + ), + ) + inspect( + s.unsafe_extract_uint64_le(30, 34).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\xf6\xb1\xad\x16" + ), + ) + inspect( + s.unsafe_extract_uint64_le(31, 33).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\xec\x63\x5b\x2d" + ), + ) +} + +///| +test "extract_int64_le path_consistent" { + let s1 = b"\xBA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"[:] + let i1 = s1.unsafe_extract_uint64_le(4, 60) + let s2 = b"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"[:] + let i2 = s2.unsafe_extract_uint64_le(0, 60) + inspect(i1, content="768614336404564650") + inspect(i2, content="768614336404564650") +} + +// ============================================================================ +// 6. Int64 Extraction Tests - Big Endian (33-64 bits) +// ============================================================================ + +///| +test "extract_int64_be" { + let s = b"\xA5\xB6\xC7\xD8\x5A\xB6\xC7\xD8"[:] + inspect( + (s.unsafe_extract_uint64_be(0, 33) << 31).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x00\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 34) << 30).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x40\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 35) << 29).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x40\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 36) << 28).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x50\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 37) << 27).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x58\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 38) << 26).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x58\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 39) << 25).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 40) << 24).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\x00\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 41) << 23).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\x80\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 42) << 22).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\x80\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 43) << 21).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xa0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 44) << 20).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 45) << 19).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb0\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 46) << 18).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb4\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 47) << 17).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 48) << 16).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\x00\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 49) << 15).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\x80\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 50) << 14).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 51) << 13).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 52) << 12).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 53) << 11).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc0\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 54) << 10).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc4\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 55) << 9).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc6\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 56) << 8).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\x00" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 57) << 7).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\x80" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 58) << 6).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xc0" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 59) << 5).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xc0" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 60) << 4).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd0" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 61) << 3).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 62) << 2).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + (s.unsafe_extract_uint64_be(0, 63) << 1).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(0, 64).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) +} + +///| +test "extract_int64_be" { + let s = b"\xA5\xB6\xC7\xD8\x5A\xB6\xC7\xD8"[:] + inspect( + s.unsafe_extract_uint64_be(0, 64).to_be_bytes(), + content=( + #|b"\xa5\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(1, 63).to_be_bytes(), + content=( + #|b"\x25\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(2, 62).to_be_bytes(), + content=( + #|b"\x25\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(3, 61).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(4, 60).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(5, 59).to_be_bytes(), + content=( + #|b"\x05\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(6, 58).to_be_bytes(), + content=( + #|b"\x01\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(7, 57).to_be_bytes(), + content=( + #|b"\x01\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(8, 56).to_be_bytes(), + content=( + #|b"\x00\xb6\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(9, 55).to_be_bytes(), + content=( + #|b"\x00\x36\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(10, 54).to_be_bytes(), + content=( + #|b"\x00\x36\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(11, 53).to_be_bytes(), + content=( + #|b"\x00\x16\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(12, 52).to_be_bytes(), + content=( + #|b"\x00\x06\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(13, 51).to_be_bytes(), + content=( + #|b"\x00\x06\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(14, 50).to_be_bytes(), + content=( + #|b"\x00\x02\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(15, 49).to_be_bytes(), + content=( + #|b"\x00\x00\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(16, 48).to_be_bytes(), + content=( + #|b"\x00\x00\xc7\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(17, 47).to_be_bytes(), + content=( + #|b"\x00\x00\x47\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(18, 46).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(19, 45).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(20, 44).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(21, 43).to_be_bytes(), + content=( + #|b"\x00\x00\x07\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(22, 42).to_be_bytes(), + content=( + #|b"\x00\x00\x03\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(23, 41).to_be_bytes(), + content=( + #|b"\x00\x00\x01\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(24, 40).to_be_bytes(), + content=( + #|b"\x00\x00\x00\xd8\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(25, 39).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x58\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(26, 38).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x18\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(27, 37).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x18\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(28, 36).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x08\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(29, 35).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(30, 34).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\x5a\xb6\xc7\xd8" + ), + ) + inspect( + s.unsafe_extract_uint64_be(31, 33).to_be_bytes(), + content=( + #|b"\x00\x00\x00\x00\x5a\xb6\xc7\xd8" + ), + ) +} + +///| +test "extract_int64_be path_consistent" { + let s1 = b"\xBA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"[:] + let i1 = s1.unsafe_extract_uint64_be(4, 60) + let s2 = b"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"[:] + let i2 = s2.unsafe_extract_uint64_be(0, 60) + inspect(i1, content="768614336404564650") + inspect(i2, content="768614336404564650") +} + +// ============================================================================ +// 7. BytesView Extraction Tests +// ============================================================================ + +///| +test "extract_bytesview - basic functionality" { + let bytes = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C" + let view = bytes[:] + + // Extract subviews at byte boundaries + let view1 = view.unsafe_extract_bytesview(0, 32) // First 4 bytes + let view2 = view.unsafe_extract_bytesview(32, 32) // Next 4 bytes + let view3 = view.unsafe_extract_bytesview(64, 32) // Last 4 bytes + + // Verify by extracting bytes from each view + inspect(view1.unsafe_extract_byte(0, 8), content="1") + inspect(view1.unsafe_extract_byte(8, 8), content="2") + inspect(view1.unsafe_extract_byte(16, 8), content="3") + inspect(view1.unsafe_extract_byte(24, 8), content="4") + inspect(view2.unsafe_extract_byte(0, 8), content="5") + inspect(view2.unsafe_extract_byte(8, 8), content="6") + inspect(view2.unsafe_extract_byte(16, 8), content="7") + inspect(view2.unsafe_extract_byte(24, 8), content="8") + inspect(view3.unsafe_extract_byte(0, 8), content="9") + inspect(view3.unsafe_extract_byte(8, 8), content="10") + inspect(view3.unsafe_extract_byte(16, 8), content="11") + inspect(view3.unsafe_extract_byte(24, 8), content="12") +} + +///| +test "extract_bytesview - various sizes" { + let bytes = b"\xFF\xEE\xDD\xCC\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00" + let view = bytes[:] + + // Test different view sizes + let small_view = view.unsafe_extract_bytesview(0, 8) // 1 byte + let medium_view = view.unsafe_extract_bytesview(8, 32) // 4 bytes + let large_view = view.unsafe_extract_bytesview(40, 48) // 6 bytes + + // Verify contents + inspect(small_view.unsafe_extract_byte(0, 8), content="255") + inspect(medium_view.unsafe_extract_byte(0, 8), content="238") + inspect(medium_view.unsafe_extract_byte(24, 8), content="187") + inspect(large_view.unsafe_extract_byte(0, 8), content="170") + inspect(large_view.unsafe_extract_byte(40, 8), content="85") +} + +///| +test "extract_bytesview - nested extractions" { + let bytes = b"\x12\x34\x56\x78\x9A\xBC\xDE\xF0\x11\x22\x33\x44" + let view = bytes[:] + + // Extract a subview, then extract from that subview + let subview = view.unsafe_extract_bytesview(16, 64) // Extract middle 8 bytes + let sub_subview = subview.unsafe_extract_bytesview(16, 32) // Extract 4 bytes from middle + + // Verify the nested extraction + inspect(sub_subview.unsafe_extract_byte(0, 8), content="154") + inspect(sub_subview.unsafe_extract_byte(8, 8), content="188") + inspect(sub_subview.unsafe_extract_byte(16, 8), content="222") + inspect(sub_subview.unsafe_extract_byte(24, 8), content="240") +} + +// ============================================================================ +// 8. Cross-Function Integration Tests +// ============================================================================ + +///| +test "integration - complex bit manipulations" { + let bytes = b"\xAB\xCD\xEF\x12\x34\x56\x78\x9A\xBC\xDE\xF0\x11" + let view = bytes[:] + + // Combine different extraction methods + let bit = view.unsafe_extract_bit(5, 1) + let byte_val = view.unsafe_extract_byte(6, 6) + let int_val = view.unsafe_extract_uint_le(12, 20) + let int64_val = view.unsafe_extract_uint64_be(32, 40) + + // Verify each extraction independently + inspect(bit, content="0") // 6th bit of 0xAB (10101011) + inspect(byte_val, content="60") // 6 bits starting from bit 6 + + // For int and int64 values, shift to make them more readable + inspect( + (int_val << (32 - 20)).to_le_bytes(), + content=( + #|b"\x00\xe0\x1d\x2f" + ), + ) + inspect( + (int64_val << (64 - 40)).to_le_bytes(), + content=( + #|b"\x00\x00\x00\xbc\x9a\x78\x56\x34" + ), + ) +} + +///| +test "integration - boundary stress test" { + // Create a pattern that helps identify bit alignment issues + let bytes = b"\xF0\x0F\xF0\x0F\xF0\x0F\xF0\x0F\xF0\x0F\xF0\x0F" + let view = bytes[:] + + // Test extractions that cross multiple boundaries + for offset = 1; offset <= 7; offset = offset + 1 { + // Extract a bit + let _bit = view.unsafe_extract_bit(offset, 1) + + // Extract a byte spanning boundaries + let _byte_val = view.unsafe_extract_byte(offset, 8) + + // Extract an int spanning boundaries + let _int_val = view.unsafe_extract_uint_le(offset, 16) + + // Extract an int64 spanning boundaries + let _int64_val = view.unsafe_extract_uint64_be(offset, 48) + + } + + // If we get here without crashes, the boundary handling is working + inspect(true, content="true") +} + +///| +test "integration - pattern verification" { + // Use a known pattern to verify correctness across all functions + let bytes = b"\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA" // Alternating pattern + let view = bytes[:] + + // Verify the pattern is preserved through different extraction methods + + // Check that alternating pattern is maintained in bits + for i = 0; i < 16; i = i + 1 { + let bit = view.unsafe_extract_bit(i, 1) + let expected = if i % 2 == 0 { false } else { true } // 0x55 = 01010101 + if i < 8 { + inspect(bit, content=if expected { "1" } else { "0" }) + } + } + + // Check that pattern is maintained in byte extractions + let byte1 = view.unsafe_extract_byte(0, 8) + let byte2 = view.unsafe_extract_byte(8, 8) + inspect(byte1, content="85") + inspect(byte2, content="170") + + // Check that pattern is maintained in larger extractions + let int_val = view.unsafe_extract_uint_be(0, 16) + inspect( + int_val.to_be_bytes(), + content=( + #|b"\x00\x00\x55\xaa" + ), + ) +} + +///| +test "misc" { + let s = b"\xFF\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10"[:] + inspect(s.unsafe_extract_byte(0, 7), content="127") + inspect(s.unsafe_extract_uint_le(7, 15), content="129") + inspect(s.unsafe_extract_uint_le(22, 23), content="2097601") + inspect(s.unsafe_extract_uint64_le(45, 40), content="279726645696") + inspect(s.unsafe_extract_byte(85, 3), content="3") + inspect(s.unsafe_extract_byte(88, 8), content="12") + inspect( + s.unsafe_extract_bytesview(96, 32), + content=( + #|b"\x0d\x0e\x0f\x10" + ), + ) + inspect(s.unsafe_extract_byte(0, 7), content="127") + inspect(s.unsafe_extract_uint_be(7, 15), content="16512") + inspect(s.unsafe_extract_uint_be(22, 23), content="6324384") + inspect(s.unsafe_extract_uint64_be(45, 40), content="828408668481") + inspect(s.unsafe_extract_byte(85, 3), content="3") + inspect(s.unsafe_extract_byte(88, 8), content="12") + inspect( + s.unsafe_extract_bytesview(96, 32), + content=( + #|b"\x0d\x0e\x0f\x10" + ), + ) +} diff --git a/bundled-core/bytes/bytes.mbt b/bundled-core/bytes/bytes.mbt index 3c34890..06852f8 100644 --- a/bundled-core/bytes/bytes.mbt +++ b/bundled-core/bytes/bytes.mbt @@ -26,20 +26,18 @@ /// ```moonbit /// let arr = [b'h', b'i'] /// let bytes = @bytes.from_array(arr) -/// inspect(bytes, content= -/// #|b"\x68\x69" +/// inspect( +/// bytes, +/// content=( +/// #|b"\x68\x69" +/// ), /// ) /// ``` +#as_free_fn pub fn Bytes::from_array(arr : Array[Byte]) -> Bytes { Bytes::makei(arr.length(), i => arr[i]) } -///| -/// same as `Bytes::from_array` -pub fn from_array(arr : Array[Byte]) -> Bytes { - Bytes::makei(arr.length(), i => arr[i]) -} - ///| /// Creates a new bytes sequence from a fixed-size array of bytes with an /// optional length parameter. @@ -59,10 +57,14 @@ pub fn from_array(arr : Array[Byte]) -> Bytes { /// ```moonbit /// let arr : FixedArray[Byte] = [b'h', b'e', b'l', b'l', b'o'] /// let bytes = @bytes.from_fixedarray(arr, len=3) -/// inspect(bytes, content= -/// #|b"\x68\x65\x6c" +/// inspect( +/// bytes, +/// content=( +/// #|b"\x68\x65\x6c" +/// ), /// ) /// ``` +#as_free_fn pub fn Bytes::from_fixedarray(arr : FixedArray[Byte], len? : Int) -> Bytes { let len = match len { None => arr.length() @@ -71,12 +73,6 @@ pub fn Bytes::from_fixedarray(arr : FixedArray[Byte], len? : Int) -> Bytes { Bytes::makei(len, i => arr[i]) } -///| -/// same as `Bytes::from_fixedarray` -pub fn from_fixedarray(arr : FixedArray[Byte], len? : Int) -> Bytes { - Bytes::from_fixedarray(arr, len?) -} - ///| /// Converts a bytes sequence into a fixed-size array of bytes. If an optional /// length is provided, the resulting array will have exactly that length, @@ -126,18 +122,16 @@ pub fn to_fixedarray(self : Bytes, len? : Int) -> FixedArray[Byte] { /// ```moonbit /// let iter = Iter::singleton(b'h') /// let bytes = @bytes.from_iter(iter) -/// inspect(bytes, content= -/// #|b"\x68" +/// inspect( +/// bytes, +/// content=( +/// #|b"\x68" +/// ), /// ) /// ``` +#as_free_fn pub fn Bytes::from_iter(iter : Iter[Byte]) -> Bytes { - from_array(iter.collect()) -} - -///| -/// same as `Bytes::from_iter` -pub fn from_iter(iter : Iter[Byte]) -> Bytes { - from_array(iter.collect()) + Bytes::from_array(iter.collect()) } ///| @@ -155,21 +149,19 @@ pub fn from_iter(iter : Iter[Byte]) -> Bytes { /// ```moonbit /// let arr : FixedArray[Byte] = [b'h', b'e', b'l', b'l', b'o'] /// let bytes = @bytes.of(arr) -/// inspect(bytes, content= -/// #|b"\x68\x65\x6c\x6c\x6f" +/// inspect( +/// bytes, +/// content=( +/// #|b"\x68\x65\x6c\x6c\x6f" +/// ), /// ) /// ``` /// TODO: marked as intrinsic, inline if it is constant +#as_free_fn pub fn Bytes::of(arr : FixedArray[Byte]) -> Bytes { Bytes::makei(arr.length(), i => arr[i]) } -///| -/// same as `Bytes::of` -pub fn of(arr : FixedArray[Byte]) -> Bytes { - Bytes::makei(arr.length(), i => arr[i]) -} - ///| /// Converts a bytes sequence into an array of bytes. /// @@ -214,7 +206,7 @@ pub fn to_array(self : Bytes) -> Array[Byte] { /// ``` pub fn iter(self : Bytes) -> Iter[Byte] { Iter::new(yield_ => for i in 0.. Iter[Byte] { /// inspect(keys, content="[0, 1, 2, 3, 4]") pub fn iter2(self : Bytes) -> Iter2[Int, Byte] { Iter2::new(yield_ => for i in 0.. Bytes { + b"" +} + ///| /// Retrieves a byte from the view at the specified index. /// @@ -286,12 +284,6 @@ pub fn get(self : Bytes, index : Int) -> Byte? { Some(self[index]) } -///| -/// same as `Bytes::default` -pub fn default() -> Bytes { - b"" -} - ///| /// Reinterpret the byte sequence as Bytes. fn unsafe_to_bytes(array : FixedArray[Byte]) -> Bytes = "%identity" @@ -304,7 +296,7 @@ fn unsafe_to_bytes(array : FixedArray[Byte]) -> Bytes = "%identity" /// * `self` : The first bytes sequence. /// * `other` : The second bytes sequence. /// TODO: marked as intrinsic, inline if it is constant -pub impl Add for Bytes with op_add(self : Bytes, other : Bytes) -> Bytes { +pub impl Add for Bytes with add(self : Bytes, other : Bytes) -> Bytes { let len_self = self.length() let len_other = other.length() let rv : FixedArray[Byte] = FixedArray::make(len_self + len_other, 0) diff --git a/bundled-core/bytes/bytes_pattern_test.mbt b/bundled-core/bytes/bytes_pattern_test.mbt index 9ffe10a..e818dbb 100644 --- a/bundled-core/bytes/bytes_pattern_test.mbt +++ b/bundled-core/bytes/bytes_pattern_test.mbt @@ -79,23 +79,23 @@ pub fn decode_utf8(bytes : Bytes) -> String { test "decode_utf8_basic" { // Test ASCII characters let ascii = b"Hello, World!" - assert_eq(decode_utf8(ascii), "Hello, World!") + inspect(decode_utf8(ascii), content="Hello, World!") // Test 2-byte sequences (Latin, Greek, Cyrillic, etc.) let two_byte = b"\xC3\xA9\xC3\xB1\xC3\xBC" // รฉ รฑ รผ - assert_eq(decode_utf8(two_byte), "รฉรฑรผ") + inspect(decode_utf8(two_byte), content="รฉรฑรผ") // Test 3-byte sequences (Chinese, Japanese, etc.) let three_byte = b"\xE4\xBD\xA0\xE5\xA5\xBD" // ไฝ ๅฅฝ - assert_eq(decode_utf8(three_byte), "ไฝ ๅฅฝ") + inspect(decode_utf8(three_byte), content="ไฝ ๅฅฝ") // Test 4-byte sequences (Emoji, etc.) let four_byte = b"\xF0\x9F\x98\x80\xF0\x9F\x98\x81" // ๐Ÿ˜€๐Ÿ˜ - assert_eq(decode_utf8(four_byte), "๐Ÿ˜€๐Ÿ˜") + inspect(decode_utf8(four_byte), content="๐Ÿ˜€๐Ÿ˜") // Test mixed byte sequences let mixed = b"Hello \xE4\xB8\x96\xE7\x95\x8C!" // Hello ไธ–็•Œ! - assert_eq(decode_utf8(mixed), "Hello ไธ–็•Œ!") + inspect(decode_utf8(mixed), content="Hello ไธ–็•Œ!") } ///| @@ -115,12 +115,12 @@ test "decode_utf8_invalid" { // Mixed valid and invalid let mixed_valid_invalid = b"A\xC3B\xE4\xBD\xA0" - assert_eq(decode_utf8(mixed_valid_invalid), "ABไฝ ") + inspect(decode_utf8(mixed_valid_invalid), content="ABไฝ ") } ///| /// Test function that demonstrates decoding a longer text with mixed character types test "decode_utf8_long_text" { let text = b"UTF-8 \xE6\xB5\x8B\xE8\xAF\x95 (Test): \xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82 \xF0\x9F\x8C\x8D" - assert_eq(decode_utf8(text), "UTF-8 ๆต‹่ฏ• (Test): ะŸั€ะธะฒะตั‚ ๐ŸŒ") + inspect(decode_utf8(text), content="UTF-8 ๆต‹่ฏ• (Test): ะŸั€ะธะฒะตั‚ ๐ŸŒ") } diff --git a/bundled-core/bytes/bytes_test.mbt b/bundled-core/bytes/bytes_test.mbt index c004a4e..b459091 100644 --- a/bundled-core/bytes/bytes_test.mbt +++ b/bundled-core/bytes/bytes_test.mbt @@ -21,9 +21,9 @@ test "bytes literal" { let b : Bytes = "ไฝ ๅฅฝ" inspect( b + "utf8" + HELLO, - content= + content=( #|b"\xe4\xbd\xa0\xe5\xa5\xbd\x75\x74\x66\x38\xe4\xbd\xa0\xe5\xa5\xbd" - , + ), ) // match ("ไฝ ๅฅฝ,utf8" : Bytes) { // // [_,_,_, .. rest] => inspect(rest, content="utf8") @@ -32,9 +32,9 @@ test "bytes literal" { // } inspect( b"\x41\x42\x43", - content= + content=( #|b"\x41\x42\x43" - , + ), ) } @@ -43,9 +43,9 @@ test "from_array" { let b = @bytes.of([b'\x41', b'\x00', b'\x42', b'\x00', b'\x43', b'\x00']) inspect( b, - content= + content=( #|b"\x41\x00\x42\x00\x43\x00" - , + ), ) } @@ -54,9 +54,9 @@ test "from_array literal" { let b = @bytes.of([65, 0, 66, 0, 67, 0]) inspect( b, - content= + content=( #|b"\x41\x00\x42\x00\x43\x00" - , + ), ) } @@ -180,9 +180,9 @@ test "Bytes::of with different byte values" { let bytes = Bytes::of(arr) inspect( bytes, - content= + content=( #|b"\x61\x62\x63" - , + ), ) } @@ -192,9 +192,9 @@ test "Bytes::from_iter with multiple elements" { let bytes = Bytes::from_iter(iter) inspect( bytes, - content= + content=( #|b"\x61\x62\x63" - , + ), ) } diff --git a/bundled-core/bytes/feature_pipe_test.mbt b/bundled-core/bytes/feature_pipe_test.mbt index 47beba2..d003f7e 100644 --- a/bundled-core/bytes/feature_pipe_test.mbt +++ b/bundled-core/bytes/feature_pipe_test.mbt @@ -23,14 +23,14 @@ test "feature pipe" { inspect(v, content="5") inspect( b, - content= + content=( #|b"\x68\x65\x6c\x6c\x6f" - , + ), ) inspect( u, - content= + content=( #|b"\x68\x65\x6c\x6c\x6f" - , + ), ) } diff --git a/bundled-core/bytes/find_test.mbt b/bundled-core/bytes/find_test.mbt new file mode 100644 index 0000000..51352a9 --- /dev/null +++ b/bundled-core/bytes/find_test.mbt @@ -0,0 +1,43 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "find" { + inspect(b"hello".find("o"), content="Some(4)") + inspect(b"hello".find("l"), content="Some(2)") + inspect(b"hello".find("hello"), content="Some(0)") + inspect(b"hello".find("h"), content="Some(0)") + inspect(b"hello".find(""), content="Some(0)") + inspect(b"hello".find("world"), content="None") + inspect(b"".find(""), content="Some(0)") + inspect(b"".find("a"), content="None") + inspect(b"hello hello".find("hello"), content="Some(0)") + inspect(b"aaa".find("aa"), content="Some(0)") + inspect(b"aabaabaaa".find("aaa"), content="Some(6)") +} + +///| +test "rev_find" { + inspect(b"hello".rev_find("o"), content="Some(4)") + inspect(b"hello".rev_find("l"), content="Some(3)") + inspect(b"hello".rev_find("hello"), content="Some(0)") + inspect(b"hello".rev_find("h"), content="Some(0)") + inspect(b"hello".rev_find(""), content="Some(5)") + inspect(b"hello".rev_find("world"), content="None") + inspect(b"".rev_find(""), content="Some(0)") + inspect(b"".rev_find("a"), content="None") + inspect(b"hello hello".rev_find("hello"), content="Some(6)") + inspect(b"aaa".rev_find("aa"), content="Some(1)") + inspect(b"aabaaabaa".find("aaa"), content="Some(3)") +} diff --git a/bundled-core/bytes/methods.mbt b/bundled-core/bytes/methods.mbt new file mode 100644 index 0000000..76a2cc8 --- /dev/null +++ b/bundled-core/bytes/methods.mbt @@ -0,0 +1,63 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// Returns the offset of the first occurrence of the given +/// bytes substring. If the substring is not found, `None` is returned. +pub fn View::find(target : View, pattern : View) -> Int? { + // TODO: more efficient algorithm + let target_len = target.length() + let pattern_len = pattern.length() + for i in 0..=(target_len - pattern_len) { + for j in 0.. Int? { + target[:].find(pattern) +} + +///| +/// Returns the offset of the last occurrence of the given +/// bytes substring. If the substring is not found, `None` is returned. +pub fn View::rev_find(target : View, pattern : View) -> Int? { + // TODO: more efficient algorithm + let target_len = target.length() + let pattern_len = pattern.length() + for i = target_len - pattern_len; i >= 0; i = i - 1 { + for j in 0.. Int? { + target[:].rev_find(pattern) +} diff --git a/bundled-core/bytes/moon.pkg.json b/bundled-core/bytes/moon.pkg.json index a8e08e3..96b4a8f 100644 --- a/bundled-core/bytes/moon.pkg.json +++ b/bundled-core/bytes/moon.pkg.json @@ -2,7 +2,11 @@ "import": ["moonbitlang/core/builtin"], "test-import": [ "moonbitlang/core/array", - "moonbitlang/core/double", - "moonbitlang/core/test" + "moonbitlang/core/double", + "moonbitlang/core/uint", + "moonbitlang/core/uint64", + "moonbitlang/core/test", + "moonbitlang/core/quickcheck", + "moonbitlang/core/error" ] } diff --git a/bundled-core/bytes/bytes.mbti b/bundled-core/bytes/pkg.generated.mbti similarity index 53% rename from bundled-core/bytes/bytes.mbti rename to bundled-core/bytes/pkg.generated.mbti index 63e9a46..644b1c1 100644 --- a/bundled-core/bytes/bytes.mbti +++ b/bundled-core/bytes/pkg.generated.mbti @@ -1,24 +1,21 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/bytes" // Values fn default() -> Bytes -fn from_array(Array[Byte]) -> Bytes - -fn from_fixedarray(FixedArray[Byte], len? : Int) -> Bytes - -fn from_iter(Iter[Byte]) -> Bytes - -fn of(FixedArray[Byte]) -> Bytes +// Errors // Types and methods type View fn View::data(Self) -> Bytes +fn View::find(Self, Self) -> Int? fn View::get(Self, Int) -> Byte? fn View::iter(Self) -> Iter[Byte] fn View::length(Self) -> Int -fn View::op_as_view(Self, start~ : Int = .., end? : Int) -> Self +fn View::op_as_view(Self, start? : Int, end? : Int) -> Self fn View::op_get(Self, Int) -> Byte +fn View::rev_find(Self, Self) -> Int? fn View::start_offset(Self) -> Int fn View::to_bytes(Self) -> Bytes fn View::to_double_be(Self) -> Double @@ -33,21 +30,42 @@ fn View::to_uint64_be(Self) -> UInt64 fn View::to_uint64_le(Self) -> UInt64 fn View::to_uint_be(Self) -> UInt fn View::to_uint_le(Self) -> UInt +fn View::unsafe_extract_bit(Self, Int, Int) -> UInt +fn View::unsafe_extract_byte(Self, Int, Int) -> UInt +fn View::unsafe_extract_bytesview(Self, Int, Int) -> Self +fn View::unsafe_extract_uint64_be(Self, Int, Int) -> UInt64 +fn View::unsafe_extract_uint64_le(Self, Int, Int) -> UInt64 +fn View::unsafe_extract_uint_be(Self, Int, Int) -> UInt +fn View::unsafe_extract_uint_le(Self, Int, Int) -> UInt fn View::unsafe_get(Self, Int) -> Byte impl Compare for View impl Eq for View +impl Hash for View impl Show for View +fn Bytes::find(Bytes, View) -> Int? +#as_free_fn fn Bytes::from_array(Array[Byte]) -> Bytes +#as_free_fn fn Bytes::from_fixedarray(FixedArray[Byte], len? : Int) -> Bytes +#as_free_fn fn Bytes::from_iter(Iter[Byte]) -> Bytes fn Bytes::get(Bytes, Int) -> Byte? fn Bytes::iter(Bytes) -> Iter[Byte] fn Bytes::iter2(Bytes) -> Iter2[Int, Byte] +#as_free_fn fn Bytes::of(FixedArray[Byte]) -> Bytes -fn Bytes::op_as_view(Bytes, start~ : Int = .., end? : Int) -> View +fn Bytes::op_as_view(Bytes, start? : Int, end? : Int) -> View +fn Bytes::rev_find(Bytes, View) -> Int? fn Bytes::to_array(Bytes) -> Array[Byte] fn Bytes::to_fixedarray(Bytes, len? : Int) -> FixedArray[Byte] +fn Bytes::unsafe_extract_bit(Bytes, Int, Int) -> UInt +fn Bytes::unsafe_extract_byte(Bytes, Int, Int) -> UInt +fn Bytes::unsafe_extract_bytesview(Bytes, Int, Int) -> View +fn Bytes::unsafe_extract_uint64_be(Bytes, Int, Int) -> UInt64 +fn Bytes::unsafe_extract_uint64_le(Bytes, Int, Int) -> UInt64 +fn Bytes::unsafe_extract_uint_be(Bytes, Int, Int) -> UInt +fn Bytes::unsafe_extract_uint_le(Bytes, Int, Int) -> UInt impl Add for Bytes impl Default for Bytes impl Hash for Bytes diff --git a/bundled-core/bytes/view.mbt b/bundled-core/bytes/view.mbt index 5552dd1..42819dd 100644 --- a/bundled-core/bytes/view.mbt +++ b/bundled-core/bytes/view.mbt @@ -20,11 +20,12 @@ /// ```mbt /// let bs = b"\x00\x01\x02\x03\x04\x05" /// let bv = bs[1:4] -/// assert_eq(bv.length(), 3) +/// inspect(bv.length(), content="3") /// assert_eq(bv[0], b'\x01') /// assert_eq(bv[1], b'\x02') /// assert_eq(bv[2], b'\x03') /// ``` +#builtin.valtype type View ///| @@ -33,9 +34,6 @@ fn View::bytes(self : View) -> Bytes = "%bytesview.bytes" ///| fn View::start(self : View) -> Int = "%bytesview.start" -///| -fn View::len(self : View) -> Int = "%bytesview.len" - ///| fn View::make(b : Bytes, start : Int, len : Int) -> View = "%bytesview.make" @@ -59,6 +57,9 @@ pub fn View::length(self : View) -> Int { self.len() } +///| +fn View::len(self : View) -> Int = "%bytesview.len" + ///| /// Retrieves a byte from the view at the specified index. /// @@ -77,9 +78,9 @@ pub fn View::length(self : View) -> Int { /// inspect(view[1], content="b'\\x03'") /// ``` pub fn View::op_get(self : View, index : Int) -> Byte { - guard index >= 0 && index < self.len() else { + guard index >= 0 && index < self.length() else { abort( - "index out of bounds: the len is from 0 to \{self.len()} but the index is \{index}", + "index out of bounds: the len is from 0 to \{self.length()} but the index is \{index}", ) } self.bytes()[self.start() + index] @@ -108,7 +109,7 @@ pub fn View::op_get(self : View, index : Int) -> Byte { /// inspect(result, content="None") /// ``` pub fn View::get(self : View, index : Int) -> Byte? { - guard index >= 0 && index < self.len() else { None } + guard index >= 0 && index < self.length() else { None } Some(self.bytes().unsafe_get(self.start() + index)) } @@ -148,12 +149,12 @@ pub fn View::unsafe_get(self : View, index : Int) -> Byte { /// ```mbt /// let bs = b"\x00\x01\x02\x03\x04\x05" /// let bv = bs[1:4] -/// assert_eq(bv.length(), 3) +/// inspect(bv.length(), content="3") /// assert_eq(bv[0], b'\x01') /// assert_eq(bv[1], b'\x02') /// assert_eq(bv[2], b'\x03') /// ``` -pub fn Bytes::op_as_view(self : Bytes, start~ : Int = 0, end? : Int) -> View { +pub fn Bytes::op_as_view(self : Bytes, start? : Int = 0, end? : Int) -> View { let len = self.length() let end = match end { None => len @@ -174,10 +175,10 @@ pub fn Bytes::op_as_view(self : Bytes, start~ : Int = 0, end? : Int) -> View { /// ```mbt /// let bv = b"\x00\x01\x02\x03\x04\x05"[:] /// let bv2 = bv[1:4] -/// assert_eq(bv2.length(), 3) +/// inspect(bv2.length(), content="3") /// assert_eq(bv2[1], b'\x02') /// ``` -pub fn View::op_as_view(self : View, start~ : Int = 0, end? : Int) -> View { +pub fn View::op_as_view(self : View, start? : Int = 0, end? : Int) -> View { let len = self.length() let end = match end { None => len @@ -199,10 +200,10 @@ pub fn View::op_as_view(self : View, start~ : Int = 0, end? : Int) -> View { /// let bv = b"\x00\x01\x02\x03\x04\x05"[:] /// let mut sum = 0 /// bv.iter().each((x) => { sum = sum + x.to_int() }) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` pub fn View::iter(self : View) -> Iter[Byte] { - Iter::new(yield_ => for i in 0.. for i in 0.. Bool { - guard self.len() == other.len() else { return false } - for i in 0.. Bool { + guard self.length() == other.length() else { return false } + for i in 0.. Bool { /// inspect(bytes[1:5].compare(bytes[2:5]), content="1") // bcab > cab /// ``` pub impl Compare for View with compare(self, other) -> Int { - let self_len = self.len() - let other_len = other.len() + let self_len = self.length() + let other_len = other.length() let cmp = self_len.compare(other_len) guard cmp == 0 else { return cmp } for i in 0.. Int { ///| pub fn View::to_bytes(self : View) -> Bytes { - if self.len() == 0 && self.len() == self.bytes().length() { + if self.length() == 0 && self.length() == self.bytes().length() { return self.bytes() } - let bytes = FixedArray::make(self.len(), (0 : Byte)) - bytes.blit_from_bytes(0, self.bytes(), self.start_offset(), self.len()) + let bytes = FixedArray::make(self.length(), (0 : Byte)) + bytes.blit_from_bytes(0, self.bytes(), self.start_offset(), self.length()) unsafe_to_bytes(bytes) } + +///| +pub impl Hash for View with hash_combine(self : View, hasher : Hasher) { + for i in 0.. Int { + xxhash32(self.data(), 0, offset=self.start_offset(), len=self.length()) +} diff --git a/bundled-core/bytes/view_test.mbt b/bundled-core/bytes/view_test.mbt index ee1c005..532d81f 100644 --- a/bundled-core/bytes/view_test.mbt +++ b/bundled-core/bytes/view_test.mbt @@ -105,93 +105,93 @@ test "negative index1" { let bv = bs[-1:] inspect( bv, - content= + content=( #|b"\x03" - , + ), ) let bv = bs[-2:] inspect( bv, - content= + content=( #|b"\x02\x03" - , + ), ) let bv = bs[-3:] inspect( bv, - content= + content=( #|b"\x01\x02\x03" - , + ), ) let bv = bs[:-1] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[:-2] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[:-3] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:-3] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:-2] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[-3:-1] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[-3:0] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:1] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[-3:2] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[-3:3] inspect( bv, - content= + content=( #|b"\x01\x02\x03" - , + ), ) } @@ -201,93 +201,93 @@ test "negative index2" { let bv = bs[-1:] inspect( bv, - content= + content=( #|b"\x03" - , + ), ) let bv = bs[-2:] inspect( bv, - content= + content=( #|b"\x02\x03" - , + ), ) let bv = bs[-3:] inspect( bv, - content= + content=( #|b"\x01\x02\x03" - , + ), ) let bv = bs[:-1] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[:-2] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[:-3] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:-3] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:-2] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[-3:-1] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[-3:0] inspect( bv, - content= + content=( #|b"" - , + ), ) let bv = bs[-3:1] inspect( bv, - content= + content=( #|b"\x01" - , + ), ) let bv = bs[-3:2] inspect( bv, - content= + content=( #|b"\x01\x02" - , + ), ) let bv = bs[-3:3] inspect( bv, - content= + content=( #|b"\x01\x02\x03" - , + ), ) } @@ -403,7 +403,7 @@ test "panic to_float_be/short_input" { // Test that trying to read from a view that's too short causes a panic let bytes = b"\x40\x49" let view = bytes[:] - ignore(View::to_float_be(view)) // Should panic due to insufficient bytes + ignore(@bytes.View::to_float_be(view)) // Should panic due to insufficient bytes } ///| @@ -493,24 +493,47 @@ test "View::to_bytes" { let view = bytes[1:4] inspect( view.to_bytes(), - content= + content=( #|b"\x62\x63\x61" - , + ), ) let bytes = b"abcabc" let view = bytes[:] inspect( view.to_bytes(), - content= + content=( #|b"\x61\x62\x63\x61\x62\x63" - , + ), ) let bytes = b"abcabc" let view = bytes[0:0] inspect( view.to_bytes(), - content= + content=( #|b"" - , + ), ) } + +///| +test "impl Hash for View" { + let b0 : Bytes = "12345678" + let b1 : Bytes = "56781234" + assert_eq(b0[0:4].hash(), b1[4:8].hash()) + assert_eq(b0[4:8].hash(), b1[0:4].hash()) + assert_not_eq(b0[0:4].hash(), b1[0:4].hash()) + assert_not_eq(b0[4:8].hash(), b1[4:8].hash()) +} + +///| +/// This test checks the invariance that the hash of a bytes and its view are the same. +/// +/// This is necessary because we may want to use a bytes view as a key in a hash map, +/// and we need to ensure that the hash remains consistent with the original bytes. +test "bytes view hash invariant" { + let bytes : Array[Bytes] = @quickcheck.samples(20) + for b in bytes { + let view = b[:] + assert_eq(view.hash(), b.hash()) + } +} diff --git a/bundled-core/bytes/xxhash.mbt b/bundled-core/bytes/xxhash.mbt index ab4547f..4e5837b 100644 --- a/bundled-core/bytes/xxhash.mbt +++ b/bundled-core/bytes/xxhash.mbt @@ -30,15 +30,19 @@ let gPRIME4 = 0x27D4EB2F let gPRIME5 = 0x165667B1 ///| -fn xxhash32(input : Bytes, seed : Int) -> Int { - let len = input.length() +fn xxhash32( + input : Bytes, + offset? : Int = 0, + len? : Int = input.length(), + seed : Int, +) -> Int { let h = (if len >= 16 { - h16bytes(input, 0, len, seed) + h16bytes(input, offset, len, seed) } else { seed + gPRIME5 }) + len - finalize(h, input, len & -16, len & 0xF) + finalize(h, input, offset + (len & -16), len & 0xF) } ///| @@ -118,7 +122,7 @@ fn _h16bytes( v1 : Int, v2 : Int, v3 : Int, - v4 : Int + v4 : Int, ) -> Int { if remain >= 16 { _h16bytes( @@ -147,3 +151,11 @@ fn h16bytes(input : Bytes, cur : Int, len : Int, seed : Int) -> Int { seed - gPRIME1, ) } + +///| +test "Bytes hash_combine" { + let data : Bytes = [1, 2, 3, 4] + let hasher = Hasher::new() + data.hash_combine(hasher) + assert_true(hasher.finalize() != 0) +} diff --git a/bundled-core/char/char.mbt b/bundled-core/char/char.mbt index c60bced..cd701f3 100644 --- a/bundled-core/char/char.mbt +++ b/bundled-core/char/char.mbt @@ -22,63 +22,72 @@ pub impl Hash for Char with hash_combine(self, hasher) -> Unit { hasher.combine_char(self) } -///| Checks if the value is within the ASCII range. -pub fn is_ascii(self : Char) -> Bool { +///| +/// Checks if the value is within the ASCII range. +pub fn Char::is_ascii(self : Self) -> Bool { self is ('\u{00}'..='\u{7F}') } -///| Checks if the value is an ASCII alphabetic character: +///| +/// Checks if the value is an ASCII alphabetic character: /// - U+0041 'A' ..= U+005A 'Z' /// - U+0061 'a' ..= U+007A 'z' -pub fn is_ascii_alphabetic(self : Char) -> Bool { +pub fn Char::is_ascii_alphabetic(self : Self) -> Bool { self is ('A'..='Z' | 'a'..='z') } -///| Checks if the value is an ASCII control character: +///| +/// Checks if the value is an ASCII control character: /// U+0000 NUL ..= U+001F UNIT SEPARATOR, or U+007F DELETE. /// Note that most ASCII whitespace characters are control characters, but SPACE is not. -pub fn is_ascii_control(self : Char) -> Bool { +pub fn Char::is_ascii_control(self : Self) -> Bool { self is ('\u{00}'..='\u{1F}' | '\u{7F}') } -///| Checks if the value is an ASCII decimal digit: +///| +/// Checks if the value is an ASCII decimal digit: /// U+0030 '0' ..= U+0039 '9' -pub fn is_ascii_digit(self : Char) -> Bool { +pub fn Char::is_ascii_digit(self : Self) -> Bool { self is ('0'..='9') } -///| Checks if the value is an ASCII graphic character: +///| +/// Checks if the value is an ASCII graphic character: /// U+0021 '!' ..= U+007E '~' -pub fn is_ascii_graphic(self : Char) -> Bool { +pub fn Char::is_ascii_graphic(self : Self) -> Bool { self is ('\u{21}'..='\u{7E}') } -///| Checks if the value is an ASCII hexadecimal digit: +///| +/// Checks if the value is an ASCII hexadecimal digit: /// - U+0030 '0' ..= U+0039 '9' /// - U+0041 'A' ..= U+0046 'F' /// - U+0061 'a' ..= U+0066 'f' -pub fn is_ascii_hexdigit(self : Char) -> Bool { +pub fn Char::is_ascii_hexdigit(self : Self) -> Bool { self is ('0'..='9' | 'A'..='F' | 'a'..='f') } -///| Checks if the value is an ASCII lowercase character: +///| +/// Checks if the value is an ASCII lowercase character: /// U+0061 'a' ..= U+007A 'z'. -pub fn is_ascii_lowercase(self : Char) -> Bool { +pub fn Char::is_ascii_lowercase(self : Self) -> Bool { self is ('a'..='z') } -///| Checks if the value is an ASCII octal digit: +///| +/// Checks if the value is an ASCII octal digit: /// U+0030 '0' ..= U+0037 '7' -pub fn is_ascii_octdigit(self : Char) -> Bool { +pub fn Char::is_ascii_octdigit(self : Self) -> Bool { self is ('0'..='7') } -///| Checks if the value is an ASCII punctuation character: +///| +/// Checks if the value is an ASCII punctuation character: /// - U+0021 ..= U+002F ! " # $ % & ' ( ) * + , - . / /// - U+003A ..= U+0040 : ; < = > ? @ /// - U+005B ..= U+0060 [ \ ] ^ _ ` /// - U+007B ..= U+007E { | } ~ -pub fn is_ascii_punctuation(self : Char) -> Bool { +pub fn Char::is_ascii_punctuation(self : Self) -> Bool { self is ('\u{21}'..='\u{2F}' | '\u{3A}'..='\u{40}' @@ -86,28 +95,31 @@ pub fn is_ascii_punctuation(self : Char) -> Bool { | '\u{7B}'..='\u{7E}') } -///| Checks if the value is an ASCII uppercase character: +///| +/// Checks if the value is an ASCII uppercase character: /// U+0041 'A' ..= U+005A 'Z' -pub fn is_ascii_uppercase(self : Char) -> Bool { +pub fn Char::is_ascii_uppercase(self : Self) -> Bool { self is ('A'..='Z') } -///| Checks if the value is an ASCII whitespace character: -/// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, U+000C FORM FEED, or U+000D CARRIAGE RETURN. -pub fn is_ascii_whitespace(self : Char) -> Bool { - self is ('\u{20}' | '\u{09}' | '\u{0A}' | '\u{0C}' | '\u{0D}') +///| +/// Checks if the value is an ASCII whitespace character: +/// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, U+000B VERTICAL TAB, U+000C FORM FEED, or U+000D CARRIAGE RETURN. +pub fn Char::is_ascii_whitespace(self : Self) -> Bool { + self is ('\u{20}' | '\u{09}' | '\u{0A}' | '\u{0B}' | '\u{0C}' | '\u{0D}') } -///| Returns true if this char has the general category for control codes. -pub fn is_control(self : Char) -> Bool { +///| +/// Returns true if this char has the general category for control codes. +pub fn Char::is_control(self : Self) -> Bool { self is ('\u0000'..='\u001F' | '\u007F'..='\u009F') } -///| +///| /// Checks if a char is a digit in the given radix (range from 2 to 36). /// /// panic if the radix is invalid. -pub fn is_digit(self : Char, radix : UInt) -> Bool { +pub fn Char::is_digit(self : Self, radix : UInt) -> Bool { let v = self.to_uint() match radix { 2..=10 => v >= 48 && v <= radix + 47 @@ -119,8 +131,9 @@ pub fn is_digit(self : Char, radix : UInt) -> Bool { } } -///| Returns true if this char has the White_Space property. -pub fn is_whitespace(self : Char) -> Bool { +///| +/// Returns true if this char has the White_Space property. +pub fn Char::is_whitespace(self : Self) -> Bool { self is ('\u0009'..='\u000D' | '\u0020' @@ -135,8 +148,9 @@ pub fn is_whitespace(self : Char) -> Bool { | '\u3000') } -///| Returns true if this char has one of the general categories for numbers. -pub fn is_numeric(self : Char) -> Bool { +///| +/// Returns true if this char has one of the general categories for numbers. +pub fn Char::is_numeric(self : Self) -> Bool { self is ('\u0030'..='\u0039' | '\u00B2' @@ -283,7 +297,8 @@ pub fn is_numeric(self : Char) -> Bool { | '\u{1FBF0}'..='\u{1FBF9}') } -///| Returns true if this character is printable (visible when displayed). +///| +/// Returns true if this character is printable (visible when displayed). /// Aligns with Unicode standard categories for printable characters. /// Characters are considered printable if they are: /// - Letters (L*) @@ -299,7 +314,7 @@ pub fn is_numeric(self : Char) -> Bool { /// - Private use (Co) /// - Unassigned (Cn) /// - Surrogates (Cs) -pub fn is_printable(self : Char) -> Bool { +pub fn Char::is_printable(self : Self) -> Bool { // Check for control characters (Cc) if self.is_control() { return false @@ -337,7 +352,7 @@ pub fn is_printable(self : Char) -> Bool { return false } // Surrogate (Cs) - if self >= 0xD800 && self <= 0xDFFF { + if self.is_surrogate() { return false } @@ -371,32 +386,36 @@ pub fn is_printable(self : Char) -> Bool { true } -///| Makes a copy of the value in its ASCII lower case equivalent. +///| +/// Makes a copy of the value in its ASCII lower case equivalent. /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', /// but non-ASCII letters are unchanged. -pub fn to_ascii_lowercase(self : Char) -> Char { +pub fn Char::to_ascii_lowercase(self : Self) -> Char { if self.is_ascii_uppercase() { return (self.to_int() + 32).unsafe_to_char() } self } -///| Makes a copy of the value in its ASCII upper case equivalent. +///| +/// Makes a copy of the value in its ASCII upper case equivalent. /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', /// but non-ASCII letters are unchanged. -pub fn to_ascii_uppercase(self : Char) -> Char { +pub fn Char::to_ascii_uppercase(self : Self) -> Char { if self.is_ascii_lowercase() { return (self.to_int() - 32).unsafe_to_char() } self } -///| Convert Char to String +///| +/// Convert Char to String pub impl Show for Char with to_string(self : Char) -> String { char_to_string(self) } -///| TODO: support attributes for impl +///| +/// TODO: support attributes for impl #intrinsic("%char.to_string") fn char_to_string(char : Char) -> String { [char] @@ -421,7 +440,7 @@ pub impl Show for Char with output(self, logger) { '\t' => logger.write_string("\\t") ' '..='~' => logger.write_char(self) _ => - if not(self.is_printable()) { + if !self.is_printable() { let code = self.to_int() let hex_len = if code <= 0xFF { 2 @@ -463,3 +482,57 @@ pub impl Show for Char with output(self, logger) { pub impl ToJson for Char with to_json(self : Char) -> Json { Json::string(self.to_string()) } + +///| +/// Returns the number of UTF-16 code units required to encode this character. +/// +/// Parameters: +/// +/// * `self` : The character to analyze. +/// +/// Returns the number of UTF-16 code units (1 or 2) needed to represent this +/// character. +/// Note surrogate pairs are counted as 2, it should not happen in general since +/// surrogate pair is Int instead of Char. +/// Example: +/// +/// ```moonbit +/// inspect('A'.utf16_len(), content="1") +/// inspect('๐ŸŒŸ'.utf16_len(), content="2") +/// ``` +/// +pub fn Char::utf16_len(self : Self) -> Int { + let code = self.to_int() + if code <= 0xFFFF { + 1 + } else { + 2 + } +} + +///| +/// Returns true if this character is in the Basic Multilingual Plane (BMP). +/// +/// The BMP (Basic Multilingual Plane) contains 65,536 code points (U+0000 to U+FFFF) +/// distributed as follows: +/// +/// - **~57,022 actual Unicode character positions** (87% of BMP) +/// - **2,048 surrogate code points** (U+D800-U+DFFF) - reserved for UTF-16 encoding +/// - High surrogates: U+D800-U+DBFF (1,024 code points) +/// - Low surrogates: U+DC00-U+DFFF (1,024 code points) +/// - **6,400 private use area** (U+E000-U+F8FF) - for custom characters +/// - **66 permanent noncharacters** (including U+FFFE, U+FFFF, U+FDD0-U+FDEF) +/// +/// Note: Surrogate code points are not actual characters but encoding mechanisms +/// for representing characters outside the BMP in UTF-16. +/// +/// Example: +/// +/// ```moonbit +/// inspect('A'.is_bmp(), content="true") +/// inspect('๐ŸŒŸ'.is_bmp(), content="false") +/// ``` +/// +pub fn Char::is_bmp(self : Self) -> Bool { + self.to_int() <= 0xFFFF +} diff --git a/bundled-core/char/char_coverage_test.mbt b/bundled-core/char/char_coverage_test.mbt new file mode 100644 index 0000000..4c7c6cb --- /dev/null +++ b/bundled-core/char/char_coverage_test.mbt @@ -0,0 +1,121 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "character classification edge cases" { + // Test ASCII range edge cases + inspect('\u{7F}'.is_ascii(), content="true") // DEL character + inspect('\u{80}'.is_ascii(), content="false") // First non-ASCII + + // Test control characters + inspect('\u{00}'.is_ascii_control(), content="true") // NUL + inspect('\u{1F}'.is_ascii_control(), content="true") // Unit separator + inspect(' '.is_ascii_control(), content="false") // SPACE is not control + + // Test hexadecimal digits + inspect('A'.is_ascii_hexdigit(), content="true") + inspect('F'.is_ascii_hexdigit(), content="true") + inspect('a'.is_ascii_hexdigit(), content="true") + inspect('f'.is_ascii_hexdigit(), content="true") + inspect('G'.is_ascii_hexdigit(), content="false") + inspect('g'.is_ascii_hexdigit(), content="false") +} + +///| +test "digit validation with different radixes" { + // Test edge cases for is_digit function + inspect('0'.is_digit(2), content="true") + inspect('1'.is_digit(2), content="true") + inspect('2'.is_digit(2), content="false") // Invalid in binary + inspect('9'.is_digit(10), content="true") + inspect('A'.is_digit(16), content="true") + inspect('Z'.is_digit(36), content="true") + inspect('a'.is_digit(16), content="true") + inspect('z'.is_digit(36), content="true") +} + +///| +test "unicode whitespace characters" { + // Test various Unicode whitespace characters + inspect('\u{0009}'.is_whitespace(), content="true") // TAB + inspect('\u{000A}'.is_whitespace(), content="true") // LF + inspect('\u{000B}'.is_whitespace(), content="true") // VT + inspect('\u{000C}'.is_whitespace(), content="true") // FF + inspect('\u{000D}'.is_whitespace(), content="true") // CR + inspect('\u{0020}'.is_whitespace(), content="true") // SPACE + inspect('\u{0085}'.is_whitespace(), content="true") // NEL + inspect('\u{00A0}'.is_whitespace(), content="true") // NBSP + inspect('\u{1680}'.is_whitespace(), content="true") // Ogham space mark + inspect('\u{2000}'.is_whitespace(), content="true") // En quad + inspect('\u{2028}'.is_whitespace(), content="true") // Line separator + inspect('\u{2029}'.is_whitespace(), content="true") // Paragraph separator + inspect('\u{3000}'.is_whitespace(), content="true") // CJK space +} + +///| +test "numeric character testing" { + // Test various numeric characters from different scripts + inspect('0'.is_numeric(), content="true") + inspect('9'.is_numeric(), content="true") + inspect('\u{00B2}'.is_numeric(), content="true") // Superscript 2 + inspect('\u{00B3}'.is_numeric(), content="true") // Superscript 3 + inspect('\u{0660}'.is_numeric(), content="true") // Arabic-Indic digit zero + inspect('\u{0966}'.is_numeric(), content="true") // Devanagari digit zero + inspect('\u{FF10}'.is_numeric(), content="true") // Fullwidth digit zero +} + +///| +test "printable character testing" { + // Test printable vs non-printable characters + inspect('A'.is_printable(), content="true") + inspect(' '.is_printable(), content="true") // Space is printable + inspect('\u{0009}'.is_printable(), content="false") // TAB is not printable + inspect('\u{000A}'.is_printable(), content="false") // LF is not printable + inspect('\u{007F}'.is_printable(), content="false") // DEL is not printable + + // Test some Unicode characters + inspect('ฮฑ'.is_printable(), content="true") // Greek alpha + inspect('ไธญ'.is_printable(), content="true") // Chinese character + inspect('๐Ÿ™‚'.is_printable(), content="true") // Emoji +} + +///| +test "case conversion" { + // Test ASCII case conversion + inspect('A'.to_ascii_lowercase(), content="a") + inspect('Z'.to_ascii_lowercase(), content="z") + inspect('a'.to_ascii_lowercase(), content="a") // Already lowercase + inspect('1'.to_ascii_lowercase(), content="1") // Non-letter unchanged + inspect('a'.to_ascii_uppercase(), content="A") + inspect('z'.to_ascii_uppercase(), content="Z") + inspect('A'.to_ascii_uppercase(), content="A") // Already uppercase + inspect('1'.to_ascii_uppercase(), content="1") // Non-letter unchanged + + // Test non-ASCII characters remain unchanged + inspect('รฑ'.to_ascii_lowercase(), content="รฑ") + inspect('รฑ'.to_ascii_uppercase(), content="รฑ") +} + +///| +test "punctuation character testing" { + // Test various punctuation characters + inspect('!'.is_ascii_punctuation(), content="true") + inspect('@'.is_ascii_punctuation(), content="true") + inspect('['.is_ascii_punctuation(), content="true") + inspect('`'.is_ascii_punctuation(), content="true") + inspect('{'.is_ascii_punctuation(), content="true") + inspect('~'.is_ascii_punctuation(), content="true") + inspect('A'.is_ascii_punctuation(), content="false") + inspect('0'.is_ascii_punctuation(), content="false") +} diff --git a/bundled-core/char/char_test.mbt b/bundled-core/char/char_test.mbt index dd6538d..f2e1e49 100644 --- a/bundled-core/char/char_test.mbt +++ b/bundled-core/char/char_test.mbt @@ -14,7 +14,7 @@ ///| test "to_string" { - assert_eq('a'.to_string(), "a") + inspect('a'.to_string(), content="a") } ///| @@ -25,14 +25,14 @@ test "show output" { buf.to_string() } - assert_eq(repr('a'), "'a'") - assert_eq(repr('\''), "'\\''") - assert_eq(repr('"'), "'\"'") - assert_eq(repr('\\'), "'\\\\'") - assert_eq(repr('\n'), "'\\n'") - assert_eq(repr('\r'), "'\\r'") - assert_eq(repr('\b'), "'\\b'") - assert_eq(repr('\t'), "'\\t'") + inspect(repr('a'), content="'a'") + inspect(repr('\''), content="'\\''") + inspect(repr('"'), content="'\"'") + inspect(repr('\\'), content="'\\\\'") + inspect(repr('\n'), content="'\\n'") + inspect(repr('\r'), content="'\\r'") + inspect(repr('\b'), content="'\\b'") + inspect(repr('\t'), content="'\\t'") assert_eq(repr((0).unsafe_to_char()), "'\\u{00}'") } @@ -246,7 +246,11 @@ test "is_ascii_whitespace" { let zero = '0' let percent = '%' let space = ' ' + let tab = '\t' let lf = '\n' + let vt = '\u{0B}' // vertical tab + let ff = '\u{0C}' // form feed + let cr = '\r' let esc = '\u{1B}' assert_false(uppercase_a.is_ascii_whitespace()) assert_false(uppercase_g.is_ascii_whitespace()) @@ -255,7 +259,11 @@ test "is_ascii_whitespace" { assert_false(zero.is_ascii_whitespace()) assert_false(percent.is_ascii_whitespace()) assert_true(space.is_ascii_whitespace()) + assert_true(tab.is_ascii_whitespace()) assert_true(lf.is_ascii_whitespace()) + assert_true(vt.is_ascii_whitespace()) + assert_true(ff.is_ascii_whitespace()) + assert_true(cr.is_ascii_whitespace()) assert_false(esc.is_ascii_whitespace()) } @@ -267,21 +275,21 @@ test "is_control" { ///| test "is_digit" { - assert_eq(true, 'a'.is_digit(11)) - assert_eq(true, 'a'.is_digit(12)) - assert_eq(true, 'a'.is_digit(13)) - assert_eq(true, 'a'.is_digit(14)) - assert_eq(true, 'a'.is_digit(15)) - assert_eq(true, 'a'.is_digit(16)) - assert_eq(true, 'A'.is_digit(11)) - assert_eq(true, 'B'.is_digit(12)) - assert_eq(true, 'C'.is_digit(13)) - assert_eq(true, 'D'.is_digit(14)) - assert_eq(true, 'E'.is_digit(15)) - assert_eq(true, 'F'.is_digit(16)) - assert_eq(false, 'A'.is_digit(8)) - assert_eq(true, '1'.is_digit(2)) - assert_eq(true, 'z'.is_digit(36)) + inspect('a'.is_digit(11), content="true") + inspect('a'.is_digit(12), content="true") + inspect('a'.is_digit(13), content="true") + inspect('a'.is_digit(14), content="true") + inspect('a'.is_digit(15), content="true") + inspect('a'.is_digit(16), content="true") + inspect('A'.is_digit(11), content="true") + inspect('B'.is_digit(12), content="true") + inspect('C'.is_digit(13), content="true") + inspect('D'.is_digit(14), content="true") + inspect('E'.is_digit(15), content="true") + inspect('F'.is_digit(16), content="true") + inspect('A'.is_digit(8), content="false") + inspect('1'.is_digit(2), content="true") + inspect('z'.is_digit(36), content="true") } ///| @@ -378,7 +386,7 @@ test "is_printable" { assert_false(paragraph_separator.is_printable()) assert_false(byte_order_mark.is_printable()) inspect( - ['\t', '\n', '\r', '\b', '\\'].map(is_printable), + ['\t', '\n', '\r', '\b', '\\'].map(Char::is_printable), content="[false, false, false, false, true]", ) } @@ -402,3 +410,125 @@ test "to ascii lowercase" { assert_eq('_', '_'.to_ascii_lowercase()) assert_eq('โค', 'โค'.to_ascii_lowercase()) } + +///| +test "Hash implementation edge cases" { + // Test that hash function works for various characters + let hash_a = 'a'.hash() + let hash_b = 'b'.hash() + assert_true(hash_a >= 0) + assert_true(hash_b >= 0) + assert_false(hash_a == hash_b) // Different characters should have different hashes + + // Test with null character + let null_hash = '\u{0000}'.hash() + assert_true(null_hash >= 0) + + // Test with high Unicode characters + let unicode_hash = '๐Ÿš€'.hash() + assert_true(unicode_hash >= 0) +} + +///| +test "ToJson implementation" { + // Test that to_json works without crashing for various characters + let json_a = 'a'.to_json() + let json_quote = '"'.to_json() + // let json_backslash = '\\'.to_json() + let json_newline = '\n'.to_json() + let json_tab = '\t'.to_json() + let json_null = '\u{0000}'.to_json() + let json_unicode = '๐Ÿš€'.to_json() + + // Basic validation that JSON objects were created + assert_true(json_a != json_quote) // Different characters produce different JSON + assert_true(json_newline != json_tab) + assert_true(json_null != json_unicode) +} + +///| +test "is_digit boundary cases with maximum radix" { + // Test maximum valid radix (36) + assert_true('z'.is_digit(36)) + assert_true('Z'.is_digit(36)) + assert_false('z'.is_digit(35)) + + // Test minimum valid radix (2) + assert_true('0'.is_digit(2)) + assert_true('1'.is_digit(2)) + assert_false('2'.is_digit(2)) + + // Test digits at boundaries + assert_true('9'.is_digit(10)) + assert_false('9'.is_digit(9)) + assert_true('a'.is_digit(11)) + assert_false('a'.is_digit(10)) +} + +///| +test "ASCII boundaries and edge characters" { + // Test the exact boundary of ASCII range + assert_true('\u{007F}'.is_ascii()) // Last ASCII character (DEL) + assert_false('\u{0080}'.is_ascii()) // First non-ASCII character + + // Test control character boundaries + assert_true('\u{001F}'.is_ascii_control()) // Last in first control range + assert_false('\u{0020}'.is_ascii_control()) // SPACE (not control) + assert_true('\u{007F}'.is_ascii_control()) // DEL character + assert_false('\u{0080}'.is_ascii_control()) // Beyond ASCII + + // Test graphic character boundaries + assert_true('\u{0021}'.is_ascii_graphic()) // First graphic character (!) + assert_false('\u{0020}'.is_ascii_graphic()) // SPACE (not graphic) + assert_true('\u{007E}'.is_ascii_graphic()) // Last graphic character (~) + assert_false('\u{007F}'.is_ascii_graphic()) // DEL (not graphic) +} + +///| +test "Unicode edge cases for numeric detection" { + // Test various Unicode numeric characters + assert_true('\u{00BC}'.is_numeric()) // ยผ (vulgar fraction one quarter) + assert_true('\u{2160}'.is_numeric()) // โ…  (Roman numeral one) + assert_true('\u{1D7CE}'.is_numeric()) // Mathematical monospace digit zero + + // Test non-numeric characters that might be confused with numbers + assert_false('O'.is_numeric()) // Letter O (not zero) + assert_false('l'.is_numeric()) // Letter l (not one) + assert_false('I'.is_numeric()) // Letter I (not Roman numeral) +} + +///| +test "Whitespace edge cases" { + // Test all ASCII whitespace characters + assert_true('\u{0009}'.is_ascii_whitespace()) // TAB + assert_true('\u{000A}'.is_ascii_whitespace()) // LF + assert_true('\u{000B}'.is_ascii_whitespace()) // VT + assert_true('\u{000C}'.is_ascii_whitespace()) // FF + assert_true('\u{000D}'.is_ascii_whitespace()) // CR + assert_true('\u{0020}'.is_ascii_whitespace()) // SPACE + + // Test characters that are not ASCII whitespace + assert_false('\u{00A0}'.is_ascii_whitespace()) // NBSP (Unicode whitespace but not ASCII) + assert_false('\u{0085}'.is_ascii_whitespace()) // NEL (not ASCII) + + // But they should be Unicode whitespace + assert_true('\u{00A0}'.is_whitespace()) // NBSP + assert_true('\u{0085}'.is_whitespace()) // NEL +} + +///| +test "Case conversion with non-letters" { + // Test symbols and numbers remain unchanged + assert_eq('0'.to_ascii_lowercase(), '0') + assert_eq('9'.to_ascii_uppercase(), '9') + assert_eq('!'.to_ascii_lowercase(), '!') + assert_eq('@'.to_ascii_uppercase(), '@') + assert_eq(' '.to_ascii_lowercase(), ' ') + assert_eq(' '.to_ascii_uppercase(), ' ') + + // Test boundary letters + assert_eq('A'.to_ascii_lowercase(), 'a') + assert_eq('Z'.to_ascii_lowercase(), 'z') + assert_eq('a'.to_ascii_uppercase(), 'A') + assert_eq('z'.to_ascii_uppercase(), 'Z') +} diff --git a/bundled-core/char/char.mbti b/bundled-core/char/pkg.generated.mbti similarity index 87% rename from bundled-core/char/char.mbti rename to bundled-core/char/pkg.generated.mbti index f652b84..b5f231b 100644 --- a/bundled-core/char/char.mbti +++ b/bundled-core/char/pkg.generated.mbti @@ -1,7 +1,10 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/char" // Values +// Errors + // Types and methods fn Char::is_ascii(Char) -> Bool fn Char::is_ascii_alphabetic(Char) -> Bool @@ -14,6 +17,7 @@ fn Char::is_ascii_octdigit(Char) -> Bool fn Char::is_ascii_punctuation(Char) -> Bool fn Char::is_ascii_uppercase(Char) -> Bool fn Char::is_ascii_whitespace(Char) -> Bool +fn Char::is_bmp(Char) -> Bool fn Char::is_control(Char) -> Bool fn Char::is_digit(Char, UInt) -> Bool fn Char::is_numeric(Char) -> Bool @@ -21,6 +25,7 @@ fn Char::is_printable(Char) -> Bool fn Char::is_whitespace(Char) -> Bool fn Char::to_ascii_lowercase(Char) -> Char fn Char::to_ascii_uppercase(Char) -> Char +fn Char::utf16_len(Char) -> Int impl Hash for Char impl Show for Char impl ToJson for Char diff --git a/bundled-core/cmp/README.mbt.md b/bundled-core/cmp/README.mbt.md index 1269a82..78710d9 100644 --- a/bundled-core/cmp/README.mbt.md +++ b/bundled-core/cmp/README.mbt.md @@ -11,10 +11,6 @@ test "generic comparison" { // Works with numbers inspect(@cmp.maximum(3, 4), content="4") inspect(@cmp.minimum(3, 4), content="3") - - // Works with floating point - inspect(@cmp.maximum(3.14, 2.718), content="3.14") - inspect(@cmp.minimum(3.14, 2.718), content="2.718") } ``` diff --git a/bundled-core/cmp/cmp.mbt b/bundled-core/cmp/cmp.mbt index ab898ce..4d46cfc 100644 --- a/bundled-core/cmp/cmp.mbt +++ b/bundled-core/cmp/cmp.mbt @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| Returns the element that gives the maximum value from the specified function. +///| +/// Returns the element that gives the maximum value from the specified function. /// /// Returns the second argument if the comparison determines them to be equal. /// /// # Examples /// /// ```mbt +/// inspect(@cmp.maximum_by_key(1, -2, @int.abs), content="-2") /// inspect(@cmp.maximum_by_key(-2, 1, @int.abs), content="-2") /// inspect(@cmp.maximum_by_key(-2, 2, @int.abs), content="2") /// ``` @@ -30,13 +32,15 @@ pub fn[T, K : Compare] maximum_by_key(x : T, y : T, f : (T) -> K) -> T { } } -///| Returns the element that gives the minimum value from the specified function. +///| +/// Returns the element that gives the minimum value from the specified function. /// /// Returns the first argument if the comparison determines them to be equal. /// /// # Examples /// /// ```mbt +/// inspect(@cmp.minimum_by_key(1, -2, @int.abs), content="1") /// inspect(@cmp.minimum_by_key(-2, 1, @int.abs), content="1") /// inspect(@cmp.minimum_by_key(-2, 2, @int.abs), content="-2") /// ``` @@ -56,8 +60,13 @@ pub fn[T, K : Compare] minimum_by_key(x : T, y : T, f : (T) -> K) -> T { /// # Examples /// /// ```mbt -/// assert_eq(@cmp.maximum(1, 2), 2) -/// assert_eq(@cmp.maximum(2, 2), 2) +/// inspect(@cmp.maximum(1, 2), content="2") +/// inspect(@cmp.maximum(2, 1), content="2") +/// +/// let fst = [] +/// let snd = [] +/// @cmp.maximum(fst, snd).push(0) +/// inspect(snd, content="[0]") /// ``` pub fn[T : Compare] maximum(x : T, y : T) -> T { if x > y { @@ -75,8 +84,13 @@ pub fn[T : Compare] maximum(x : T, y : T) -> T { /// # Examples /// /// ```mbt -/// assert_eq(@cmp.minimum(1, 2), 1) -/// assert_eq(@cmp.minimum(2, 2), 2) +/// inspect(@cmp.minimum(1, 2), content="1") +/// inspect(@cmp.minimum(2, 1), content="1") +/// +/// let fst = [] +/// let snd = [] +/// @cmp.minimum(fst, snd).push(0) +/// inspect(fst, content="[0]") /// ``` pub fn[T : Compare] minimum(x : T, y : T) -> T { if x > y { diff --git a/bundled-core/cmp/cmp_test.mbt b/bundled-core/cmp/cmp_test.mbt deleted file mode 100644 index 9dd85b3..0000000 --- a/bundled-core/cmp/cmp_test.mbt +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -test "maximum_by_key" { - assert_eq(@cmp.maximum_by_key(1, -2, @int.abs), -2) - assert_eq(@cmp.maximum_by_key(-2, 1, @int.abs), -2) - assert_eq(@cmp.maximum_by_key(-2, 2, @int.abs), 2) -} - -///| -test "minimum_by_key" { - assert_eq(@cmp.minimum_by_key(1, -2, @int.abs), 1) - assert_eq(@cmp.minimum_by_key(-2, 1, @int.abs), 1) - assert_eq(@cmp.minimum_by_key(-2, 2, @int.abs), -2) -} - -///| -test "maximum.value" { - // assert_eq(@math.maximum(1, 2), 2) - inspect(1 |> maximum(2), content="2") - // assert_eq(@math.maximum(2, 1), 2) - inspect(2 |> maximum(1), content="2") - // assert_eq(@math.maximum(2, 2), 2) - inspect(2 |> maximum(2), content="2") -} - -///| -test "maximum.ref" { - let v1 = 1 - let v2 = 2 - let x1 = T::{ x: v1 } - let x2 = T::{ x: v2 } - - // We need another value that equals to x2 by value but not reference - let x2t = T::{ x: v2 } - @test.is_not(x2, x2t) - @test.same_object(@cmp.maximum(x1, x2), x2) - @test.same_object(@cmp.maximum(x2, x1), x2) - @test.same_object(@cmp.maximum(x2, x2t), x2t) - @test.same_object(@cmp.maximum(x2t, x2), x2) -} - -///| -test "minimum.value" { - assert_eq(@cmp.minimum(1, 2), 1) - assert_eq(@cmp.minimum(2, 1), 1) - assert_eq(@cmp.minimum(2, 2), 2) -} - -///| -test "minimum.ref" { - let v1 = 1 - let v2 = 2 - let x1 = T::{ x: v1 } - let x2 = T::{ x: v2 } - - // We need another value that equals to x2 by value but not reference - let x2t = T::{ x: v2 } - @test.is_not(x2, x2t) - @test.same_object(@cmp.minimum(x1, x2), x1) - @test.same_object(@cmp.minimum(x2, x1), x1) - @test.same_object(@cmp.minimum(x2, x2t), x2) - @test.same_object(@cmp.minimum(x2t, x2), x2t) -} - -///| -// For testing purposes -priv struct T { - x : Int -} derive(Show, Eq, Compare) diff --git a/bundled-core/cmp/moon.pkg.json b/bundled-core/cmp/moon.pkg.json index 4a7e1e0..3d6f330 100644 --- a/bundled-core/cmp/moon.pkg.json +++ b/bundled-core/cmp/moon.pkg.json @@ -3,8 +3,6 @@ "moonbitlang/core/builtin" ], "test-import": [ - "moonbitlang/core/int", - "moonbitlang/core/double", - "moonbitlang/core/test" + "moonbitlang/core/int" ] } \ No newline at end of file diff --git a/bundled-core/cmp/cmp.mbti b/bundled-core/cmp/pkg.generated.mbti similarity index 82% rename from bundled-core/cmp/cmp.mbti rename to bundled-core/cmp/pkg.generated.mbti index a47ee3d..26daaf6 100644 --- a/bundled-core/cmp/cmp.mbti +++ b/bundled-core/cmp/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/cmp" // Values @@ -9,6 +10,8 @@ fn[T : Compare] minimum(T, T) -> T fn[T, K : Compare] minimum_by_key(T, T, (T) -> K) -> T +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/coverage/README.mbt.md b/bundled-core/coverage/README.mbt.md new file mode 100644 index 0000000..e1409a3 --- /dev/null +++ b/bundled-core/coverage/README.mbt.md @@ -0,0 +1,333 @@ +# Coverage Package Documentation + +This package provides code coverage tracking utilities for MoonBit programs. It includes tools for measuring which parts of your code are executed during testing and generating coverage reports. + +## Coverage Counter + +The core component for tracking code execution: + +```moonbit +test "coverage counter basics" { + // Create a coverage counter for tracking 5 code points + let counter = CoverageCounter::new(5) + + // Initially all counters should be zero + inspect(counter.to_string(), content="[0, 0, 0, 0, 0]") + + // Increment specific tracking points + counter.incr(0) // First code point executed once + counter.incr(2) // Third code point executed once + counter.incr(0) // First code point executed again + + // Check the updated counters + inspect(counter.to_string(), content="[2, 0, 1, 0, 0]") +} +``` + +## Tracking Code Execution + +Use coverage counters to track which code paths are executed: + +```moonbit +test "tracking execution paths" { + let counter = CoverageCounter::new(3) + + fn conditional_function(x : Int, coverage : CoverageCounter) -> String { + if x > 0 { + coverage.incr(0) // Positive path + "positive" + } else if x < 0 { + coverage.incr(1) // Negative path + "negative" + } else { + coverage.incr(2) // Zero path + "zero" + } + } + + // Test different paths + let result1 = conditional_function(5, counter) + inspect(result1, content="positive") + + let result2 = conditional_function(-3, counter) + inspect(result2, content="negative") + + let result3 = conditional_function(0, counter) + inspect(result3, content="zero") + + // All paths should have been executed once + inspect(counter.to_string(), content="[1, 1, 1]") +} +``` + +## Loop Coverage Tracking + +Track coverage in loops and iterations: + +```moonbit +test "loop coverage" { + let counter = CoverageCounter::new(2) + + fn process_array(arr : Array[Int], coverage : CoverageCounter) -> Int { + let mut sum = 0 + for x in arr { + if x % 2 == 0 { + coverage.incr(0) // Even number processing + sum = sum + x + } else { + coverage.incr(1) // Odd number processing + sum = sum + x * 2 + } + } + sum + } + + let test_data = [1, 2, 3, 4, 5] // Mix of even and odd + let result = process_array(test_data, counter) + + // Should have processed both even and odd numbers + inspect(result, content="24") // 1*2 + 2 + 3*2 + 4 + 5*2 = 2 + 2 + 6 + 4 + 10 = 24 + + // Both branches should have been executed + let coverage_str = counter.to_string() + inspect(coverage_str.length() > 5, content="true") // Should show execution counts +} +``` + +## Function Coverage + +Track coverage across different functions: + +```moonbit +test "function coverage" { + let counter = CoverageCounter::new(4) + + fn math_operations(a : Int, b : Int, op : String, coverage : CoverageCounter) -> Int { + match op { + "add" => { + coverage.incr(0) + a + b + } + "sub" => { + coverage.incr(1) + a - b + } + "mul" => { + coverage.incr(2) + a * b + } + _ => { + coverage.incr(3) + 0 // Unknown operation + } + } + } + + // Test different operations + let add_result = math_operations(10, 5, "add", counter) + inspect(add_result, content="15") + + let sub_result = math_operations(10, 5, "sub", counter) + inspect(sub_result, content="5") + + let unknown_result = math_operations(10, 5, "unknown", counter) + inspect(unknown_result, content="0") + + // Check that three of four branches were executed + let final_coverage = counter.to_string() + inspect(final_coverage, content="[1, 1, 0, 1]") // add, sub, not mul, unknown +} +``` + +## Coverage Analysis + +Analyze coverage data to understand code execution: + +```moonbit +test "coverage analysis" { + let counter = CoverageCounter::new(6) + + fn complex_function(input : Int, coverage : CoverageCounter) -> String { + coverage.incr(0) // Function entry + + if input < 0 { + coverage.incr(1) // Negative branch + return "negative" + } + + coverage.incr(2) // Non-negative path + + if input == 0 { + coverage.incr(3) // Zero branch + return "zero" + } + + coverage.incr(4) // Positive path + + if input > 100 { + coverage.incr(5) // Large number branch + "large" + } else { + "small" + } + } + + // Test various inputs + let result1 = complex_function(-5, counter) + inspect(result1, content="negative") + + let result2 = complex_function(0, counter) + inspect(result2, content="zero") + + let result3 = complex_function(50, counter) + inspect(result3, content="small") + + // Analyze coverage: which paths were taken + let coverage = counter.to_string() + // Should show: [3, 1, 2, 1, 1, 0] - entry(3), negative(1), non-negative(2), zero(1), positive(1), large(0) + inspect(coverage.length() > 10, content="true") +} +``` + +## Integration with Testing + +Coverage tracking integrates with MoonBit's testing system: + +```moonbit +test "testing integration" { + // In real usage, coverage counters are typically generated automatically + // by the compiler for coverage analysis + + fn test_function_with_coverage() -> Bool { + // This would normally have auto-generated coverage tracking + let counter = CoverageCounter::new(2) + + fn helper(condition : Bool, cov : CoverageCounter) -> String { + if condition { + cov.incr(0) + "true_branch" + } else { + cov.incr(1) + "false_branch" + } + } + + // Test both branches + let result1 = helper(true, counter) + let result2 = helper(false, counter) + + result1 == "true_branch" && result2 == "false_branch" + } + + let test_passed = test_function_with_coverage() + inspect(test_passed, content="true") +} +``` + +## Coverage Reporting + +Generate and analyze coverage reports: + +```moonbit +test "coverage reporting" { + let counter = CoverageCounter::new(3) + + // Simulate some code execution + counter.incr(0) // Line 1 executed + counter.incr(0) // Line 1 executed again + counter.incr(2) // Line 3 executed + // Line 2 (index 1) never executed + + let report = counter.to_string() + inspect(report, content="[2, 0, 1]") + + // In real usage, you might analyze this data: + fn analyze_coverage(_coverage_str : String) -> (Int, Int) { + // This would parse the coverage data and return (covered, total) + // For demonstration, we'll return mock values + (2, 3) // 2 out of 3 lines covered + } + + let (covered, total) = analyze_coverage(report) + inspect(covered, content="2") + inspect(total, content="3") +} +``` + +## Best Practices + +### 1. Automatic Coverage Generation + +In real applications, coverage tracking is typically generated automatically: + +```moonbit +// This is conceptual - actual coverage is compiler-generated +fn example_function(x : Int) -> String { + // Compiler automatically inserts: coverage.incr(0) + if x > 0 { + // Compiler automatically inserts: coverage.incr(1) + "positive" + } else { + // Compiler automatically inserts: coverage.incr(2) + "non-positive" + } + // Compiler automatically inserts: coverage.incr(3) +} + +test "automatic coverage concept" { + let result = example_function(5) + inspect(result, content="positive") +} +``` + +### 2. Coverage-Driven Testing + +Use coverage information to improve test quality: + +```moonbit +test "coverage driven testing" { + // Write tests to ensure all code paths are covered + fn multi_branch_function(a : Int, b : Int) -> String { + if a > b { + "greater" + } else if a < b { + "less" + } else { + "equal" + } + } + + // Test all branches + inspect(multi_branch_function(5, 3), content="greater") + inspect(multi_branch_function(2, 7), content="less") + inspect(multi_branch_function(4, 4), content="equal") + + // This ensures 100% branch coverage +} +``` + +## Integration with Build System + +Coverage tracking integrates with MoonBit's build tools: + +- Use `moon test` to run tests with coverage tracking +- Use `moon coverage analyze` to generate coverage reports +- Coverage data helps identify untested code paths +- Supports both line coverage and branch coverage analysis + +## Performance Considerations + +- Coverage tracking adds minimal runtime overhead +- Counters use efficient fixed arrays for storage +- Coverage instrumentation is typically removed in release builds +- Use coverage data to optimize test suite performance + +## Common Use Cases + +1. **Test Quality Assessment**: Ensure comprehensive test coverage +2. **Dead Code Detection**: Find unused code paths +3. **Regression Testing**: Verify that tests exercise the same code paths +4. **Performance Analysis**: Identify frequently executed code for optimization +5. **Code Review**: Understand which parts of code are well-tested + +The coverage package provides essential tools for maintaining high-quality, well-tested MoonBit code through comprehensive coverage analysis. diff --git a/bundled-core/coverage/coverage.mbt b/bundled-core/coverage/coverage.mbt index 133e653..cf1218c 100644 --- a/bundled-core/coverage/coverage.mbt +++ b/bundled-core/coverage/coverage.mbt @@ -16,16 +16,14 @@ /// The `CoverageCounter` structure is used for keeping track of the number of /// times each chunk of code is executed. It's not very useful outside of /// generated code. -struct CoverageCounter { - counter : FixedArray[UInt] -} +struct CoverageCounter(FixedArray[UInt]) ///| /// Create a new coverage counter with the given size. /// #coverage.skip pub fn CoverageCounter::new(size : Int) -> CoverageCounter { - { counter: FixedArray::make(size, 0) } + CoverageCounter(FixedArray::make(size, 0)) } ///| @@ -33,17 +31,19 @@ pub fn CoverageCounter::new(size : Int) -> CoverageCounter { /// #coverage.skip pub fn CoverageCounter::incr(self : CoverageCounter, idx : Int) -> Unit { - self.counter.unsafe_set(idx, self.counter.unsafe_get(idx) + 1) + let CoverageCounter(self) = self + self.unsafe_set(idx, self.unsafe_get(idx) + 1) } ///| pub impl Show for CoverageCounter with output(self, logger) { + let CoverageCounter(self) = self logger.write_char('[') - for i in 0.. Unit { @@ -106,8 +106,8 @@ pub fn end() -> Unit { let sbuf = StringBuffer::StringBuffer([]) coverage_log(counters.val, sbuf) let line_buf = StringBuilder::new() - for i in 0.. Unit { ///| fn coverage_reset(counters : MList[(String, CoverageCounter)]) -> Unit { loop counters { - MCons((_, counter), xs) => { - for i in 0.. { + for i in 0.. Unit { ///| fn coverage_log( counters : MList[(String, CoverageCounter)], - io : &Output + io : &Output, ) -> Unit { let print = x => io.output(x) let println = x => { @@ -197,11 +197,11 @@ test "log" { coverage_log(counters, buf) inspect( buf, - content= + content=( #|{ "foo/foo": [1, 0] #|, "foo/bar": [0, 1] #|} - , + ), ) } @@ -225,22 +225,22 @@ test "reset" { coverage_log(counters, buf) inspect( buf, - content= + content=( #|{ "foo/foo": [1, 0] #|, "foo/bar": [0, 1] #|} - , + ), ) coverage_reset(counters) let buf = StringBuilder::new(size_hint=1024) coverage_log(counters, buf) inspect( buf, - content= + content=( #|{ "foo/foo": [0, 0] #|, "foo/bar": [0, 0] #|} - , + ), ) } diff --git a/bundled-core/coverage/coverage_test.mbt b/bundled-core/coverage/coverage_test.mbt new file mode 100644 index 0000000..4e36a8a --- /dev/null +++ b/bundled-core/coverage/coverage_test.mbt @@ -0,0 +1,13 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/bundled-core/coverage/moon.pkg.json b/bundled-core/coverage/moon.pkg.json index 99d997c..fbe8c11 100644 --- a/bundled-core/coverage/moon.pkg.json +++ b/bundled-core/coverage/moon.pkg.json @@ -1,5 +1,4 @@ { - "import": [ - "moonbitlang/core/builtin" - ] + "import": ["moonbitlang/core/builtin"], + "alert-list": "-test_import_all" } diff --git a/bundled-core/coverage/coverage.mbti b/bundled-core/coverage/pkg.generated.mbti similarity index 83% rename from bundled-core/coverage/coverage.mbti rename to bundled-core/coverage/pkg.generated.mbti index 0d02276..b59c631 100644 --- a/bundled-core/coverage/coverage.mbti +++ b/bundled-core/coverage/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/coverage" // Values @@ -5,6 +6,8 @@ fn end() -> Unit fn track(String, CoverageCounter) -> Unit +// Errors + // Types and methods type CoverageCounter fn CoverageCounter::incr(Self, Int) -> Unit diff --git a/bundled-core/deque/README.mbt.md b/bundled-core/deque/README.mbt.md index 6c5f410..1dc8a6d 100644 --- a/bundled-core/deque/README.mbt.md +++ b/bundled-core/deque/README.mbt.md @@ -10,7 +10,7 @@ You can create a deque manually via the `new()` or construct it using the `of()` ```moonbit test { - let _dv : @deque.T[Int] = @deque.new() + let _dv : @deque.Deque[Int] = @deque.new() let _dv = @deque.of([1, 2, 3, 4, 5]) } ``` @@ -19,7 +19,7 @@ If you want to set the length at creation time to minimize expansion consumption ```moonbit test { - let _dv: @deque.T[Int] = @deque.new(capacity=10) + let _dv: @deque.Deque[Int] = @deque.new(capacity=10) } ``` @@ -38,7 +38,7 @@ Similarly, you can use the `is_empty` to determine whether the queue is empty. ```moonbit test { - let dv : @deque.T[Int] = @deque.new() + let dv : @deque.Deque[Int] = @deque.new() assert_eq(dv.is_empty(), true) } ``` diff --git a/bundled-core/deque/deprecated.mbt b/bundled-core/deque/deprecated.mbt index 7ed7641..0c8101c 100644 --- a/bundled-core/deque/deprecated.mbt +++ b/bundled-core/deque/deprecated.mbt @@ -15,20 +15,20 @@ ///| #deprecated("Use `unsafe_pop_front` instead") #coverage.skip -pub fn[A] pop_front_exn(self : T[A]) -> Unit { +pub fn[A] pop_front_exn(self : Deque[A]) -> Unit { self.unsafe_pop_front() } ///| #deprecated("Use `unsafe_pop_back` instead") #coverage.skip -pub fn[A] pop_back_exn(self : T[A]) -> Unit { +pub fn[A] pop_back_exn(self : Deque[A]) -> Unit { self.unsafe_pop_back() } ///| #deprecated("Use `@deque.retain_map` instead") -pub fn[A] filter_map_inplace(self : T[A], f : (A) -> A?) -> Unit { +pub fn[A] filter_map_inplace(self : Deque[A], f : (A) -> A?) -> Unit { self.retain_map(f) } diff --git a/bundled-core/deque/deque.mbt b/bundled-core/deque/deque.mbt index 7faf361..91197d8 100644 --- a/bundled-core/deque/deque.mbt +++ b/bundled-core/deque/deque.mbt @@ -29,16 +29,17 @@ fn[T] set_null(buffer : UninitializedArray[T], index : Int) = "%fixedarray.set_n ///# Example /// /// ```moonbit -/// let dq : @deque.T[Int] = @deque.new() +/// let dq : @deque.Deque[Int] = @deque.new() /// inspect(dq.length(), content="0") /// inspect(dq.capacity(), content="0") /// -/// let dq : @deque.T[Int] = @deque.new(capacity=10) +/// let dq : @deque.Deque[Int] = @deque.new(capacity=10) /// inspect(dq.length(), content="0") /// inspect(dq.capacity(), content="10") /// ``` -pub fn[A] new(capacity~ : Int = 0) -> T[A] { - T::{ buf: UninitializedArray::make(capacity), len: 0, head: 0, tail: 0 } +#as_free_fn +pub fn[A] Deque::new(capacity? : Int = 0) -> Deque[A] { + Deque::{ buf: UninitializedArray::make(capacity), len: 0, head: 0, tail: 0 } } ///| @@ -58,7 +59,7 @@ pub fn[A] new(capacity~ : Int = 0) -> T[A] { /// inspect(dq, content="@deque.of([1, 2, 3])") /// ``` /// -pub impl[A : Show] Show for T[A] with output(self, logger) { +pub impl[A : Show] Show for Deque[A] with output(self, logger) { logger.write_iter(self.iter(), prefix="@deque.of([", suffix="])") } @@ -79,8 +80,9 @@ pub impl[A : Show] Show for T[A] with output(self, logger) { /// let dq = @deque.from_array(arr) /// inspect(dq, content="@deque.of([1, 2, 3, 4, 5])") /// ``` -pub fn[A] from_array(arr : Array[A]) -> T[A] { - let deq = T::{ +#as_free_fn +pub fn[A] Deque::from_array(arr : Array[A]) -> Deque[A] { + let deq = Deque::{ buf: UninitializedArray::make(arr.length()), len: arr.length(), head: 0, @@ -111,9 +113,9 @@ pub fn[A] from_array(arr : Array[A]) -> T[A] { /// let copied = dq.copy() /// inspect(copied, content="@deque.of([1, 2, 3, 4, 5])") /// ``` -pub fn[A] copy(self : T[A]) -> T[A] { +pub fn[A] copy(self : Deque[A]) -> Deque[A] { let len = self.len - let deq = T::{ + let deq = Deque::{ buf: UninitializedArray::make(len), len, head: 0, @@ -125,6 +127,55 @@ pub fn[A] copy(self : T[A]) -> T[A] { deq } +///| +/// Appends all elements from one deque to the end of another deque. The elements +/// are added in-place, modifying the original deque. +/// +/// Parameters: +/// +/// * `self` : The deque to append to. +/// * `other` : The deque whose elements will be appended. +/// +/// Example: +/// +/// ```moonbit +/// let v1 = @deque.of([1, 2, 3]) +/// let v2 = @deque.of([4, 5, 6]) +/// v1.append(v2) +/// inspect(v1, content="@deque.of([1, 2, 3, 4, 5, 6])") +/// let v1 = @deque.of([1, 2, 3]) +/// let v2 = @deque.of([]) +/// v1.append(v2) +/// inspect(v1, content="@deque.of([1, 2, 3])") +/// ``` +pub fn[A] append(self : Deque[A], other : Deque[A]) -> Unit { + guard other.len != 0 else { return } + let space = if self.buf.length() != 0 { + (self.head - self.tail - 1 + self.buf.length()) % self.buf.length() + } else { + 0 + } + if space < other.len { + let new_cap = if self.len + other.len > self.buf.length() * 2 { + self.len + other.len + } else { + self.buf.length() * 2 + } + let new_buf = UninitializedArray::make(new_cap) + for i, x in self { + new_buf[i] = x + } + self.buf = new_buf + self.head = 0 + self.tail = self.len - 1 + } + for _, y in other { + self.tail = (self.tail + 1) % self.buf.length() + self.buf[self.tail] = y + self.len += 1 + } +} + ///| /// Creates a new deque from a fixed array, preserving the order of elements. /// @@ -140,8 +191,9 @@ pub fn[A] copy(self : T[A]) -> T[A] { /// let dq = @deque.of([1, 2, 3]) /// inspect(dq, content="@deque.of([1, 2, 3])") /// ``` -pub fn[A] of(arr : FixedArray[A]) -> T[A] { - let deq = T::{ +#as_free_fn +pub fn[A] Deque::of(arr : FixedArray[A]) -> Deque[A] { + let deq = Deque::{ buf: UninitializedArray::make(arr.length()), len: arr.length(), head: 0, @@ -170,7 +222,7 @@ pub fn[A] of(arr : FixedArray[A]) -> T[A] { /// dq.push_back(4) /// inspect(dq.length(), content="4") /// ``` -pub fn[A] length(self : T[A]) -> Int { +pub fn[A] length(self : Deque[A]) -> Int { self.len } @@ -192,13 +244,13 @@ pub fn[A] length(self : T[A]) -> Int { /// dq.push_back(2) /// inspect(dq.capacity(), content="10") /// ``` -pub fn[A] capacity(self : T[A]) -> Int { +pub fn[A] capacity(self : Deque[A]) -> Int { self.buf.length() } ///| /// Reallocate the deque with a new capacity. -fn[A] realloc(self : T[A]) -> Unit { +fn[A] realloc(self : Deque[A]) -> Unit { let old_cap = self.len let new_cap = if old_cap == 0 { 8 } else { old_cap * 2 } let new_buf = UninitializedArray::make(new_cap) @@ -234,7 +286,7 @@ fn[A] realloc(self : T[A]) -> Unit { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// assert_eq(dv.front(), Some(1)) /// ``` -pub fn[A] front(self : T[A]) -> A? { +pub fn[A] front(self : Deque[A]) -> A? { if self.len == 0 { None } else { @@ -250,7 +302,7 @@ pub fn[A] front(self : T[A]) -> A? { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// assert_eq(dv.back(), Some(5)) /// ``` -pub fn[A] back(self : T[A]) -> A? { +pub fn[A] back(self : Deque[A]) -> A? { if self.len == 0 { None } else { @@ -269,7 +321,7 @@ pub fn[A] back(self : T[A]) -> A? { /// dv.push_front(0) /// assert_eq(dv.front(), Some(0)) /// ``` -pub fn[A] push_front(self : T[A], value : A) -> Unit { +pub fn[A] push_front(self : Deque[A], value : A) -> Unit { if self.len == self.buf.length() { self.realloc() } @@ -291,7 +343,7 @@ pub fn[A] push_front(self : T[A], value : A) -> Unit { /// dv.push_back(6) /// assert_eq(dv.back(), Some(6)) /// ``` -pub fn[A] push_back(self : T[A], value : A) -> Unit { +pub fn[A] push_back(self : Deque[A], value : A) -> Unit { if self.len == self.buf.length() { self.realloc() } @@ -312,7 +364,7 @@ pub fn[A] push_back(self : T[A], value : A) -> Unit { /// assert_eq(dv.front(), Some(2)) /// ``` #internal(unsafe, "Panic if the deque is empty.") -pub fn[A] unsafe_pop_front(self : T[A]) -> Unit { +pub fn[A] unsafe_pop_front(self : Deque[A]) -> Unit { match self.len { 0 => abort("The deque is empty!") 1 => { @@ -360,7 +412,7 @@ pub fn[A] unsafe_pop_front(self : T[A]) -> Unit { /// assert_eq(dv.back(), Some(4)) /// ``` #internal(unsafe, "Panic if the deque is empty.") -pub fn[A] unsafe_pop_back(self : T[A]) -> Unit { +pub fn[A] unsafe_pop_back(self : Deque[A]) -> Unit { match self.len { 0 => abort("The deque is empty!") 1 => { @@ -408,7 +460,7 @@ pub fn[A] unsafe_pop_back(self : T[A]) -> Unit { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// assert_eq(dv.pop_front(), Some(1)) /// ``` -pub fn[A] pop_front(self : T[A]) -> A? { +pub fn[A] pop_front(self : Deque[A]) -> A? { match self.len { 0 => None 1 => { @@ -439,7 +491,7 @@ pub fn[A] pop_front(self : T[A]) -> A? { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// assert_eq(dv.pop_back(), Some(5)) /// ``` -pub fn[A] pop_back(self : T[A]) -> A? { +pub fn[A] pop_back(self : Deque[A]) -> A? { match self.len { 0 => None 1 => { @@ -470,9 +522,9 @@ pub fn[A] pop_back(self : T[A]) -> A? { /// # Example /// ```mbt /// let dv = @deque.of([1, 2, 3, 4, 5]) -/// assert_eq(dv[2], 3) +/// inspect(dv[2], content="3") /// ``` -pub fn[A] op_get(self : T[A], index : Int) -> A { +pub fn[A] op_get(self : Deque[A], index : Int) -> A { if index < 0 || index >= self.len { let len = self.len abort( @@ -495,9 +547,9 @@ pub fn[A] op_get(self : T[A], index : Int) -> A { /// ```mbt /// let dv = @deque.of([1, 2, 3, 4, 5]) /// dv[2] = 1 -/// assert_eq(dv[2], 1) +/// inspect(dv[2], content="1") /// ``` -pub fn[A] op_set(self : T[A], index : Int, value : A) -> Unit { +pub fn[A] op_set(self : Deque[A], index : Int, value : A) -> Unit { if index < 0 || index >= self.len { let len = self.len abort( @@ -535,7 +587,7 @@ pub fn[A] op_set(self : T[A], index : Int, value : A) -> Unit { /// inspect(v1.length(), content="5") /// inspect(v2.length(), content="0") /// ``` -pub fn[A] T::as_views(self : T[A]) -> (@array.View[A], @array.View[A]) { +pub fn[A] Deque::as_views(self : Deque[A]) -> (@array.View[A], @array.View[A]) { guard self.len != 0 else { ([][:], [][:]) } let { buf, head, len, .. } = self let cap = buf.length() @@ -567,7 +619,7 @@ pub fn[A] T::as_views(self : T[A]) -> (@array.View[A], @array.View[A]) { /// inspect(dq1 == dq2, content="true") /// inspect(dq1 == dq3, content="false") /// ``` -pub impl[A : Eq] Eq for T[A] with op_equal(self, other) { +pub impl[A : Eq] Eq for Deque[A] with equal(self, other) { if self.len != other.len { return false } @@ -587,9 +639,9 @@ pub impl[A : Eq] Eq for T[A] with op_equal(self, other) { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// let mut sum = 0 /// dv.each((x) => {sum = sum + x}) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` -pub fn[A] each(self : T[A], f : (A) -> Unit) -> Unit { +pub fn[A] each(self : Deque[A], f : (A) -> Unit) -> Unit { for v in self { f(v) } @@ -603,9 +655,9 @@ pub fn[A] each(self : T[A], f : (A) -> Unit) -> Unit { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// let mut idx_sum = 0 /// dv.eachi((i, _x) => {idx_sum = idx_sum + i}) -/// assert_eq(idx_sum, 10) +/// inspect(idx_sum, content="10") /// ``` -pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { +pub fn[A] eachi(self : Deque[A], f : (Int, A) -> Unit) -> Unit { for i, v in self { f(i, v) } @@ -619,9 +671,9 @@ pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// let mut sum = 0 /// dv.rev_each((x) => {sum = sum + x}) -/// assert_eq(sum, 15) +/// inspect(sum, content="15") /// ``` -pub fn[A] rev_each(self : T[A], f : (A) -> Unit) -> Unit { +pub fn[A] rev_each(self : Deque[A], f : (A) -> Unit) -> Unit { for v in self.rev_iter() { f(v) } @@ -635,9 +687,9 @@ pub fn[A] rev_each(self : T[A], f : (A) -> Unit) -> Unit { /// let dv = @deque.of([1, 2, 3, 4, 5]) /// let mut idx_sum = 0 /// dv.rev_eachi((i, _x) => {idx_sum = idx_sum + i}) -/// assert_eq(idx_sum, 10) +/// inspect(idx_sum, content="10") /// ``` -pub fn[A] rev_eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { +pub fn[A] rev_eachi(self : Deque[A], f : (Int, A) -> Unit) -> Unit { for i, v in self.rev_iter2() { f(i, v) } @@ -652,9 +704,9 @@ pub fn[A] rev_eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { /// ```mbt /// let dv = @deque.of([1, 2, 3, 4, 5]) /// dv.clear() -/// assert_eq(dv.length(), 0) +/// inspect(dv.length(), content="0") /// ``` -pub fn[A] clear(self : T[A]) -> Unit { +pub fn[A] clear(self : Deque[A]) -> Unit { let { head, buf, len, .. } = self let cap = buf.length() let head_len = cap - head @@ -684,15 +736,16 @@ pub fn[A] clear(self : T[A]) -> Unit { /// let dv2 = dv.map((x) => {x + 1}) /// assert_eq(dv2, @deque.of([4, 5, 6])) /// ``` -pub fn[A, U] map(self : T[A], f : (A) -> U) -> T[U] { +pub fn[A, U] map(self : Deque[A], f : (A) -> U) -> Deque[U] { if self.len == 0 { new() } else { let buf : UninitializedArray[U] = UninitializedArray::make(self.len) for i in 0.. U) -> T[U] { /// let dv2 = dv.mapi((i, x) => {x + i}) // @deque.of([3, 5, 7]) /// assert_eq(dv2, @deque.of([3, 5, 7])) /// ``` -pub fn[A, U] mapi(self : T[A], f : (Int, A) -> U) -> T[U] { +pub fn[A, U] mapi(self : Deque[A], f : (Int, A) -> U) -> Deque[U] { if self.len == 0 { new() } else { let buf : UninitializedArray[U] = UninitializedArray::make(self.len) for i in 0.. U) -> T[U] { /// # Example /// ```mbt /// let dv = @deque.new() -/// assert_eq(dv.is_empty(), true) +/// inspect(dv.is_empty(), content="true") /// dv.push_back(1) -/// assert_eq(dv.is_empty(), false) +/// inspect(dv.is_empty(), content="false") /// ``` -pub fn[A] is_empty(self : T[A]) -> Bool { +pub fn[A] is_empty(self : Deque[A]) -> Bool { self.len == 0 } @@ -749,9 +803,10 @@ pub fn[A] is_empty(self : T[A]) -> Bool { /// inspect(dq.search(2), content="Some(1)") /// inspect(dq.search(4), content="None") /// ``` -pub fn[A : Eq] search(self : T[A], value : A) -> Int? { +pub fn[A : Eq] search(self : Deque[A], value : A) -> Int? { for i in 0.. Int? { /// inspect(dq.contains(3), content="true") /// inspect(dq.contains(6), content="false") /// ``` -pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { +pub fn[A : Eq] contains(self : Deque[A], value : A) -> Bool { self.iter().contains(value) } @@ -789,9 +844,9 @@ pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { /// ```mbt /// let dv = @deque.of([1]) /// dv.reserve_capacity(10) -/// assert_eq(dv.capacity(), 10) +/// inspect(dv.capacity(), content="10") /// ``` -pub fn[A] reserve_capacity(self : T[A], capacity : Int) -> Unit { +pub fn[A] reserve_capacity(self : Deque[A], capacity : Int) -> Unit { if self.capacity() >= capacity { return } @@ -799,11 +854,10 @@ pub fn[A] reserve_capacity(self : T[A], capacity : Int) -> Unit { let { buf, len, head, .. } = self self.buf = new_buf self.head = 0 - self.tail = 0 + self.tail = if len == 0 { 0 } else { len - 1 } for i in 0.. Unit { /// dv.push_back(1) /// dv.push_back(2) /// dv.push_back(3) -/// assert_eq(dv.capacity(), 10) +/// inspect(dv.capacity(), content="10") /// dv.shrink_to_fit() -/// assert_eq(dv.capacity(), 3) +/// inspect(dv.capacity(), content="3") /// ``` -pub fn[A] shrink_to_fit(self : T[A]) -> Unit { +pub fn[A] shrink_to_fit(self : Deque[A]) -> Unit { if self.capacity() <= self.length() { return } let { buf, len, head, .. } = self self.buf = UninitializedArray::make(len) self.head = 0 - self.tail = 0 + self.tail = if len == 0 { 0 } else { len - 1 } for i in 0.. Unit { /// Shortens the deque in-place, keeping the first `len` elements and dropping /// the rest. /// -/// If `len` is greater than or equal to the deque's current length or negative, +/// If `len` is greater than or equal to the deque's current length or negative, /// this has no effect /// /// Parameters: @@ -854,7 +908,7 @@ pub fn[A] shrink_to_fit(self : T[A]) -> Unit { /// dq.truncate(3) /// inspect(dq, content="@deque.of([1, 2, 3])") /// ``` -pub fn[A] truncate(self : T[A], len : Int) -> Unit { +pub fn[A] truncate(self : Deque[A], len : Int) -> Unit { guard len >= 0 && len < self.len else { return } if len == 0 { self.clear() @@ -905,8 +959,8 @@ pub fn[A] truncate(self : T[A], len : Int) -> Unit { /// dq.retain_map((x) => { if x % 2 == 0 { Some(x * 2) } else { None } }) /// inspect(dq, content="@deque.of([4, 8])") /// ``` -pub fn[A] retain_map(self : T[A], f : (A) -> A?) -> Unit { - guard not(self.is_empty()) else { return } +pub fn[A] retain_map(self : Deque[A], f : (A) -> A?) -> Unit { + guard !self.is_empty() else { return } let { head, buf, .. } = self let cap = buf.length() let head_len = cap - head @@ -962,8 +1016,8 @@ pub fn[A] retain_map(self : T[A], f : (A) -> A?) -> Unit { /// dq.retain((x) => { x % 2 == 0 }) /// inspect(dq, content="@deque.of([2, 4])") /// ``` -pub fn[A] retain(self : T[A], f : (A) -> Bool) -> Unit { - guard not(self.is_empty()) else { return } +pub fn[A] retain(self : Deque[A], f : (A) -> Bool) -> Unit { + guard !self.is_empty() else { return } let { head, buf, .. } = self let cap = buf.length() let head_len = cap - head @@ -1013,9 +1067,9 @@ pub fn[A] retain(self : T[A], f : (A) -> Bool) -> Unit { /// dq.iter().each((x) => { sum = sum + x }) /// inspect(sum, content="15") /// ``` -pub fn[A] iter(self : T[A]) -> Iter[A] { +pub fn[A] iter(self : Deque[A]) -> Iter[A] { Iter::new(yield_ => { - guard not(self.is_empty()) else { IterContinue } + guard !self.is_empty() else { IterContinue } let { head, buf, len, .. } = self let cap = buf.length() let head_len = cap - head @@ -1055,9 +1109,9 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { /// dq.iter2().each((i, x) => { sum = sum + i * x }) /// inspect(sum, content="80") // 0*10 + 1*20 + 2*30 = 80 /// ``` -pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { +pub fn[A] iter2(self : Deque[A]) -> Iter2[Int, A] { Iter2::new(yield_ => { - guard not(self.is_empty()) else { IterContinue } + guard !self.is_empty() else { IterContinue } let { head, buf, len, .. } = self let cap = buf.length() let head_len = cap - head @@ -1099,9 +1153,9 @@ pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { /// dq.rev_iter().each((x) => { sum = sum * 10 + x }) /// inspect(sum, content="321") /// ``` -pub fn[A] rev_iter(self : T[A]) -> Iter[A] { +pub fn[A] rev_iter(self : Deque[A]) -> Iter[A] { Iter::new(yield_ => { - guard not(self.is_empty()) else { IterContinue } + guard !self.is_empty() else { IterContinue } let { head, buf, len, .. } = self let cap = buf.length() let head_len = cap - head @@ -1141,9 +1195,9 @@ pub fn[A] rev_iter(self : T[A]) -> Iter[A] { /// dq.rev_iter2().each((i, x) => { s = s + "\{i}:\{x} " }) /// inspect(s, content="0:3 1:2 2:1 ") /// ``` -pub fn[A] rev_iter2(self : T[A]) -> Iter2[Int, A] { +pub fn[A] rev_iter2(self : Deque[A]) -> Iter2[Int, A] { Iter2::new(yield_ => { - guard not(self.is_empty()) else { IterContinue } + guard !self.is_empty() else { IterContinue } let { head, buf, len, .. } = self let cap = buf.length() let head_len = cap - head @@ -1184,7 +1238,8 @@ pub fn[A] rev_iter2(self : T[A]) -> Iter2[Int, A] { /// let dq = @deque.from_iter(arr.iter()) /// inspect(dq, content="@deque.of([1, 2, 3, 4, 5])") /// ``` -pub fn[A] from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A] Deque::from_iter(iter : Iter[A]) -> Deque[A] { let dq = new() iter.each(e => dq.push_back(e)) dq @@ -1208,7 +1263,7 @@ pub fn[A] from_iter(iter : Iter[A]) -> T[A] { /// inspect(arr, content="[1, 2, 3, 4, 5]") /// ``` /// -pub fn[A] to_array(self : T[A]) -> Array[A] { +pub fn[A] to_array(self : Deque[A]) -> Array[A] { let len = self.length() if len == 0 { [] @@ -1222,7 +1277,7 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { } ///| -/// Joins the elements of a string deque into a single string, +/// Joins the elements of a string deque into a single string, /// separated by the specified separator. /// /// Parameters: @@ -1230,7 +1285,7 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { /// * `self` : The deque of strings to join. /// * `separator` : The separator to insert between elements (as a string view). /// -/// Returns the concatenated string. +/// Returns the concatenated string. /// - If the deque is empty, returns an empty string. /// - Efficiently pre-allocates memory based on calculated size hint. /// - Handles empty separators efficiently by direct concatenation. @@ -1241,11 +1296,11 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { /// let deque = @deque.of(["a","b","c"]) /// let s1 = deque.join("") /// inspect(s1, content="abc") -/// +/// /// let s2 = deque.join(",") /// inspect(s2, content="a,b,c") /// ``` -pub fn T::join(self : T[String], separator : @string.View) -> String { +pub fn Deque::join(self : Deque[String], separator : @string.View) -> String { let str = separator.to_string() self.iter().join(str) } @@ -1265,8 +1320,8 @@ pub fn T::join(self : T[String], separator : @string.View) -> String { /// inspect(json, content="Array([Number(1), Number(2), Number(3)])") /// ``` /// -pub impl[A : ToJson] ToJson for T[A] with to_json(self : T[A]) -> Json { - let res = Array::make(self.length(), Json::null()) +pub impl[A : ToJson] ToJson for Deque[A] with to_json(self : Deque[A]) -> Json { + let res = Array::make(self.length(), null) for i, x in self { res[i] = x.to_json() } @@ -1291,19 +1346,22 @@ pub impl[A : ToJson] ToJson for T[A] with to_json(self : T[A]) -> Json { /// Example: /// /// ```moonbit -/// let json = @json.parse("[1, 2, 3]") -/// let dq : @deque.T[Int] = @json.from_json(json) -/// inspect(dq, content="@deque.of([1, 2, 3])") +/// let json = @json.parse("[1, 2, 3]") +/// let dq : @deque.Deque[Int] = @json.from_json(json) +/// inspect(dq, content="@deque.of([1, 2, 3])") /// ``` /// -pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) { +pub impl[A : @json.FromJson] @json.FromJson for Deque[A] with from_json( + json, + path, +) { guard json is Array(arr) else { raise @json.JsonDecodeError((path, "Deque::from_json: expected array")) } let len = arr.length() let buf = UninitializedArray::make(len) let head = 0 - let tail = len + let tail = if len == 0 { 0 } else { len - 1 } for i, x in arr { buf[i] = @json.FromJson::from_json(x, path.add_index(i)) } @@ -1318,7 +1376,7 @@ pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) /// /// * `self` : The high-dimensional deque to flatten. /// -/// Returns a new lower-dimensional deque containing all elements +/// Returns a new lower-dimensional deque containing all elements /// from inner deques in sequence. /// /// Note: @@ -1332,12 +1390,12 @@ pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) /// let deque_test = deque.flatten() /// inspect(deque_test, content="@deque.of([1, 2, 3, 4, 5, 6, 7, 8])") /// ``` -pub fn[A] T::flatten(self : T[T[A]]) -> T[A] { +pub fn[A] Deque::flatten(self : Deque[Deque[A]]) -> Deque[A] { let mut len = 0 for deque in self { len += deque.length() } - let target = T::{ + let target = Deque::{ buf: UninitializedArray::make(len), len, head: 0, @@ -1377,7 +1435,7 @@ pub fn[A] T::flatten(self : T[T[A]]) -> T[A] { /// inspect(deque_test, content="@deque.of([3, 4, 5, 6])") /// inspect(deque, content="@deque.of([1, 2, 7, 8, 9])") /// ``` -pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] { +pub fn[A] Deque::drain(self : Deque[A], start~ : Int, len? : Int) -> Deque[A] { let len = match len { Some(l) => if l > self.len { self.len } else { l } None => self.len - start @@ -1385,7 +1443,7 @@ pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] { if len == 0 { return new() } - let deque = T::{ + let deque = Deque::{ buf: UninitializedArray::make(len), len, head: 0, @@ -1448,27 +1506,15 @@ pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] { /// ```moonbit /// let dq = @deque.of([1, 3, 5, 7, 9]) /// -/// -/// let find_3 = dq.binary_search_by(x => { -/// if x < 3 { -/// -1 -/// } else if x > 3 { -/// 1 -/// } else { -/// 0 -/// } +/// +/// let find_3 = dq.binary_search_by((x) => { +/// x.compare(3) /// }) /// inspect(find_3, content="Ok(1)") /// -/// -/// let find_4 = dq.binary_search_by(x => { -/// if x < 4 { -/// -1 -/// } else if x > 4 { -/// 1 -/// } else { -/// 0 -/// } +/// +/// let find_4 = dq.binary_search_by((x) => { +/// x.compare(4) /// }) /// inspect(find_4, content="Err(2)") /// ``` @@ -1483,42 +1529,41 @@ pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] { /// * Handles the deque's ring buffer structure internally /// * For empty deques, returns `Err(0)` #locals(cmp) -pub fn[A] binary_search_by(self : T[A], cmp : (A) -> Int) -> Result[Int, Int] { +pub fn[A] binary_search_by( + self : Deque[A], + cmp : (A) -> Int, +) -> Result[Int, Int] { let len = self.len - let mut i = 0 - let mut j = len - while i < j { + + // Functional loop with two evolving bounds `i` (inclusive) and `j` (exclusive). + // `continue new_i, new_j` updates the pair for the next iteration, eliminating + // the need for mutable variables. + for i = 0, j = len; i < j; { let h = i + (j - i) / 2 - match self.get(h) { - Some(element) => - match cmp(element) { - _..<0 => i = h + 1 // Continue searching right - 1..<_ => j = h // Continue searching left - _ => { // Found match, find leftmost - j = h - while i < j { - let mid = i + (j - i) / 2 - match self.get(mid) { - Some(v) => - match cmp(v) { - 0 => j = mid - _ => i = mid + 1 - } - None => break - } - } - return Ok(i) - } - } - None => return Err(i) + let ord = cmp(self[h]) + if ord < 0 { + // Search the right half + continue h + 1, j + } else { + // ord == 0 (match) or ord > 0 (too large): keep searching left half to + // guarantee we land on the left-most occurrence. + continue i, h + } + } else { + // When the loop finishes, `i == j`. If the deque is non-empty and the + // element at `i` matches, we found the left-most index; otherwise `i` is + // the correct insertion point. + if i < len && cmp(self[i]) == 0 { + Ok(i) + } else { + Err(i) } } - Err(i) } ///| /// Safe element access with bounds checking -pub fn[A] get(self : T[A], index : Int) -> A? { +pub fn[A] get(self : Deque[A], index : Int) -> A? { if index >= 0 && index < self.len { let physical_index = (self.head + index) % self.buf.length() Some(self.buf[physical_index]) @@ -1555,6 +1600,166 @@ pub fn[A] get(self : T[A], index : Int) -> A? { /// * Assumes the deque is sorted in ascending order /// * For multiple matches, returns the leftmost matching position /// * Returns an insertion point that maintains the sort order when no match is found -pub fn[A : Compare] binary_search(self : T[A], value : A) -> Result[Int, Int] { +pub fn[A : Compare] binary_search( + self : Deque[A], + value : A, +) -> Result[Int, Int] { self.binary_search_by(x => x.compare(value)) } + +///| +/// Compares two deques based on shortlex order. +/// +/// First compares the lengths of the deques. If they differ, returns -1 if the +/// first deque is shorter, 1 if it's longer. If the lengths are equal, compares +/// elements pairwise until a difference is found or all elements have been +/// compared. +/// +/// Parameters: +/// +/// * `self` : The first deque to compare. +/// * `other` : The second deque to compare. +/// +/// Returns an integer that indicates the relative order: +/// +/// * A negative value if `self` is less than `other` +/// * Zero if `self` equals `other` +/// * A positive value if `self` is greater than `other` +/// +/// Example: +/// +/// ```moonbit +/// let dq1 = @deque.of([1, 2, 3]) +/// let dq2 = @deque.of([1, 2, 4]) +/// let dq3 = @deque.of([1, 2]) +/// inspect(dq1.compare(dq2), content="-1") // dq1 < dq2 +/// inspect(dq2.compare(dq1), content="1") // dq2 > dq1 +/// inspect(dq1.compare(dq3), content="1") // dq1 > dq3 (longer) +/// inspect(dq1.compare(dq1), content="0") // dq1 = dq1 +/// ``` +pub impl[A : Compare] Compare for Deque[A] with compare(self, other) { + let len_self = self.length() + let len_other = other.length() + let cmp = len_self.compare(len_other) + guard cmp is 0 else { return cmp } + for i in 0.. Unit { + guard self.len > 0 else { return } + let cap = self.buf.length() + let mut left = self.head + let mut right = self.tail + for _ in 0..<(self.len / 2) { + let temp = self.buf[left] + self.buf[left] = self.buf[right] + self.buf[right] = temp + left = (left + 1) % cap + right = (right - 1 + cap) % cap + } +} + +///| +/// Creates a new deque with elements in reversed order. +/// +/// Parameters: +/// +/// * `self` : The deque to be reversed. +/// +/// Returns a new deque containing the same elements as the input deque but in +/// reverse order. The original deque remains unchanged. +/// +/// Example: +/// +/// ```moonbit +/// let dq = @deque.of([1, 2, 3, 4, 5]) +/// inspect(dq.rev(), content="@deque.of([5, 4, 3, 2, 1])") +/// inspect(dq, content="@deque.of([1, 2, 3, 4, 5])") // original deque unchanged +/// ``` +pub fn[A] Deque::rev(self : Deque[A]) -> Deque[A] { + let len = self.len + let new_buf = UninitializedArray::make(len) + // Copy elements in reverse order + for i in 0.. Int, +) -> Unit { + let n = self.len + let buf_length = self.buf.length() + for i = n - 1; i > 0; i = i - 1 { + let j = rand(i + 1) + // Calculate circular buffer positions + let i_pos = (self.head + i) % buf_length + let j_pos = (self.head + j) % buf_length + // Swap elements + let tmp = self.buf[i_pos] + self.buf[i_pos] = self.buf[j_pos] + self.buf[j_pos] = tmp + } +} + +///| +/// Shuffle the deque using Knuth shuffle (Fisher-Yates algorithm) +/// +/// Returns a new shuffled deque without modifying the original deque. +/// +/// To use this function, you need to provide a rand function, which takes an integer as its upper bound +/// and returns an integer. +/// *rand n* is expected to return a uniformly distributed integer between 0 and n - 1 +pub fn[A] shuffle(self : Deque[A], rand~ : (Int) -> Int) -> Deque[A] { + // Create a copy of the original deque + let new_deque = self.copy() + // Shuffle the copy in place + new_deque.shuffle_in_place(rand~) + // Return the shuffled copy + new_deque +} diff --git a/bundled-core/deque/deque.mbti b/bundled-core/deque/deque.mbti deleted file mode 100644 index be0ecbe..0000000 --- a/bundled-core/deque/deque.mbti +++ /dev/null @@ -1,73 +0,0 @@ -package "moonbitlang/core/deque" - -import( - "moonbitlang/core/json" - "moonbitlang/core/string" -) - -// Values -fn[A] from_array(Array[A]) -> T[A] - -fn[A] from_iter(Iter[A]) -> T[A] - -fn[A] new(capacity~ : Int = ..) -> T[A] - -fn[A] of(FixedArray[A]) -> T[A] - -// Types and methods -type T[A] -fn[A] T::as_views(Self[A]) -> (ArrayView[A], ArrayView[A]) -fn[A] T::back(Self[A]) -> A? -fn[A : Compare] T::binary_search(Self[A], A) -> Result[Int, Int] -fn[A] T::binary_search_by(Self[A], (A) -> Int) -> Result[Int, Int] -fn[A] T::capacity(Self[A]) -> Int -fn[A] T::clear(Self[A]) -> Unit -fn[A : Eq] T::contains(Self[A], A) -> Bool -fn[A] T::copy(Self[A]) -> Self[A] -fn[A] T::drain(Self[A], start~ : Int, len? : Int) -> Self[A] -fn[A] T::each(Self[A], (A) -> Unit) -> Unit -fn[A] T::eachi(Self[A], (Int, A) -> Unit) -> Unit -#deprecated -fn[A] T::filter_map_inplace(Self[A], (A) -> A?) -> Unit -fn[A] T::flatten(Self[Self[A]]) -> Self[A] -fn[A] T::front(Self[A]) -> A? -fn[A] T::get(Self[A], Int) -> A? -fn[A] T::is_empty(Self[A]) -> Bool -fn[A] T::iter(Self[A]) -> Iter[A] -fn[A] T::iter2(Self[A]) -> Iter2[Int, A] -fn T::join(Self[String], @string.StringView) -> String -fn[A] T::length(Self[A]) -> Int -fn[A, U] T::map(Self[A], (A) -> U) -> Self[U] -fn[A, U] T::mapi(Self[A], (Int, A) -> U) -> Self[U] -fn[A] T::op_get(Self[A], Int) -> A -fn[A] T::op_set(Self[A], Int, A) -> Unit -fn[A] T::pop_back(Self[A]) -> A? -#deprecated -fn[A] T::pop_back_exn(Self[A]) -> Unit -fn[A] T::pop_front(Self[A]) -> A? -#deprecated -fn[A] T::pop_front_exn(Self[A]) -> Unit -fn[A] T::push_back(Self[A], A) -> Unit -fn[A] T::push_front(Self[A], A) -> Unit -fn[A] T::reserve_capacity(Self[A], Int) -> Unit -fn[A] T::retain(Self[A], (A) -> Bool) -> Unit -fn[A] T::retain_map(Self[A], (A) -> A?) -> Unit -fn[A] T::rev_each(Self[A], (A) -> Unit) -> Unit -fn[A] T::rev_eachi(Self[A], (Int, A) -> Unit) -> Unit -fn[A] T::rev_iter(Self[A]) -> Iter[A] -fn[A] T::rev_iter2(Self[A]) -> Iter2[Int, A] -fn[A : Eq] T::search(Self[A], A) -> Int? -fn[A] T::shrink_to_fit(Self[A]) -> Unit -fn[A] T::to_array(Self[A]) -> Array[A] -fn[A] T::truncate(Self[A], Int) -> Unit -fn[A] T::unsafe_pop_back(Self[A]) -> Unit -fn[A] T::unsafe_pop_front(Self[A]) -> Unit -impl[A : Eq] Eq for T[A] -impl[A : Show] Show for T[A] -impl[A : ToJson] ToJson for T[A] -impl[A : @json.FromJson] @json.FromJson for T[A] - -// Type aliases - -// Traits - diff --git a/bundled-core/deque/deque_test.mbt b/bundled-core/deque/deque_test.mbt index e3cade6..3d24fd9 100644 --- a/bundled-core/deque/deque_test.mbt +++ b/bundled-core/deque/deque_test.mbt @@ -39,9 +39,9 @@ test "reserve_capacity" { dv.pop_front() |> ignore() // 4, 5, 1 dv.reserve_capacity(10) assert_true(dv.capacity() == 10) - assert_eq(dv[0], 4) - assert_eq(dv[1], 5) - assert_eq(dv[2], 1) + inspect(dv[0], content="4") + inspect(dv[1], content="5") + inspect(dv[2], content="1") } ///| @@ -61,9 +61,9 @@ test "shrink_to_fit" { assert_true(dv.capacity() == 5) dv.shrink_to_fit() assert_true(dv.capacity() == 3) - assert_eq(dv[0], 4) - assert_eq(dv[1], 5) - assert_eq(dv[2], 1) + inspect(dv[0], content="4") + inspect(dv[1], content="5") + inspect(dv[2], content="1") } ///| @@ -88,14 +88,14 @@ test "iter" { assert_eq(i, dv.length()) inspect( buf, - content= + content=( #|1 #|2 #|3 #|4 #|5 #| - , + ), ) inspect( iter.take(4).filter(x => x % 2 == 0).fold((a, x) => a + x, init=0), @@ -219,9 +219,30 @@ test "from_array" { inspect(@deque.of([1, 2, 3]), content="@deque.of([1, 2, 3])") } +///| +test "from_json_tail_bug" { + let json = @json.parse("[1, 2, 3]") + let dq : @deque.Deque[Int] = @json.from_json(json) + assert_eq(dq.length(), 3) + assert_eq(dq.front(), Some(1)) + assert_eq(dq[0], 1) + assert_eq(dq[1], 2) + assert_eq(dq[2], 3) + + // Will panic under the bug (tail == len). Should be Some(3) when fixed. + assert_eq(dq.back(), Some(3)) + dq.push_back(4) + assert_eq(dq.back(), Some(4)) + let json = @json.parse("[]") + let dq : @deque.Deque[Int] = @json.from_json(json) + assert_eq(dq.back(), None) + dq.push_back(1) + assert_eq(dq.back(), Some(1)) +} + ///| test "copy" { - inspect((@deque.new() : @deque.T[Int]).copy(), content="@deque.of([])") + inspect((@deque.new() : @deque.Deque[Int]).copy(), content="@deque.of([])") let deq = @deque.of([1, 2, 3]) let deq1 = deq.copy() deq1[0] = 111 @@ -233,7 +254,7 @@ test "copy" { test "op_set" { let dv = @deque.of([1, 2, 3, 4, 5]) dv[1] = 3 - assert_eq(dv[1], 3) + inspect(dv[1], content="3") } ///| @@ -255,17 +276,17 @@ test "op_equal" { test "clear" { let dv = @deque.of([3, 4, 5]) dv.clear() - assert_eq(dv.length(), 0) - assert_eq(dv.capacity(), 3) + inspect(dv.length(), content="0") + inspect(dv.capacity(), content="3") } ///| test "map" { let dv = @deque.of([3, 4, 5]) let dvp = dv.map(x => x + 1) - assert_eq(dvp[0], 4) - assert_eq(dvp[1], 5) - assert_eq(dvp[2], 6) + inspect(dvp[0], content="4") + inspect(dvp[1], content="5") + inspect(dvp[2], content="6") let dve = @deque.of(([] : FixedArray[Int])) inspect(dve.map(x => x), content="@deque.of([])") } @@ -274,19 +295,19 @@ test "map" { test "push_and_pop" { let dv = @deque.of([1, 2, 3, 4, 5]) assert_eq(dv.pop_back(), Some(5)) - assert_eq(dv.length(), 4) + inspect(dv.length(), content="4") assert_eq(dv.back(), Some(4)) assert_eq(dv.pop_front(), Some(1)) - assert_eq(dv.length(), 3) + inspect(dv.length(), content="3") assert_eq(dv.front(), Some(2)) dv.push_front(5) dv.push_front(6) dv.push_back(7) dv.push_back(8) - assert_eq(dv.length(), 7) + inspect(dv.length(), content="7") assert_eq(dv.pop_front(), Some(6)) assert_eq(dv.front(), Some(5)) - assert_eq(dv.length(), 6) + inspect(dv.length(), content="6") assert_eq(dv.pop_back(), Some(8)) assert_eq(dv.back(), Some(7)) let dv = @deque.of([1]) @@ -306,9 +327,9 @@ test "is_empty" { ///| test "of" { let d = @deque.of([1, 2, 3]) - assert_eq(d[0], 1) - assert_eq(d[1], 2) - assert_eq(d[2], 3) + inspect(d[0], content="1") + inspect(d[1], content="2") + inspect(d[2], content="3") } ///| @@ -316,21 +337,21 @@ test "front_and_back" { let dv = @deque.of([1, 2, 3, 4, 5]) assert_eq(dv.back(), Some(5)) assert_eq(dv.front(), Some(1)) - let dv : @deque.T[Int] = @deque.new() + let dv : @deque.Deque[Int] = @deque.new() assert_eq(dv.back(), None) assert_eq(dv.front(), None) } ///| test "new_with_capacity" { - let d : @deque.T[Int] = @deque.new(capacity=0) - assert_eq(d.capacity(), 0) + let d : @deque.Deque[Int] = @deque.new(capacity=0) + inspect(d.capacity(), content="0") } ///| test "new_with_capacity_non_zero" { - let d : @deque.T[Int] = @deque.new(capacity=10) - assert_eq(d.capacity(), 10) + let d : @deque.Deque[Int] = @deque.new(capacity=10) + inspect(d.capacity(), content="10") } ///| @@ -401,7 +422,7 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let dq : @deque.T[Int] = @deque.from_iter(Iter::empty()) + let dq : @deque.Deque[Int] = @deque.from_iter(Iter::empty()) inspect(dq, content="@deque.of([])") } @@ -409,7 +430,7 @@ test "from_iter empty iter" { test "from_array with single element" { let arr = [42] let deque = @deque.from_array(arr) - assert_eq(deque.length(), 1) + inspect(deque.length(), content="1") assert_eq(deque.front(), Some(42)) assert_eq(deque.back(), Some(42)) } @@ -519,15 +540,15 @@ test "deque push_front when empty" { let d = @deque.of(["a", "b"]) inspect( d.pop_back(), - content= + content=( #|Some("b") - , + ), ) inspect( d.pop_back(), - content= + content=( #|Some("a") - , + ), ) d.push_front("a") inspect(d, content="@deque.of([\"a\"])") @@ -550,7 +571,7 @@ test "deque push_back when empty" { ///| test "deque as_views" { // Test empty deque - let dq1 : @deque.T[Int] = @deque.new() + let dq1 : @deque.Deque[Int] = @deque.new() @json.inspect(dq1.as_views(), content=[[], []]) // Test contiguous case (head_len >= len) @@ -574,27 +595,38 @@ test "test_get" { tester.push_back(1) tester.push_back(2) tester.push_back(35) - assert_eq(tester.length(), 3) - assert_eq(tester[1], 2) - assert_eq(tester[2], 35) - assert_eq(tester[0], 1) + inspect(tester.length(), content="3") + inspect(tester[1], content="2") + inspect(tester[2], content="35") + inspect(tester[0], content="1") let _ = tester.pop_front() - assert_eq(tester.length(), 2) - assert_eq(tester[0], 2) - assert_eq(tester[1], 35) + inspect(tester.length(), content="2") + inspect(tester[0], content="2") + inspect(tester[1], content="35") } ///| test "test_reserve_capacity" { - let tester : @deque.T[Int] = @deque.new(capacity=1) - assert_eq(tester.capacity(), 1) + let tester : @deque.Deque[Int] = @deque.new(capacity=1) + inspect(tester.capacity(), content="1") tester.reserve_capacity(2) - assert_eq(tester.capacity(), 2) + inspect(tester.capacity(), content="2") tester.reserve_capacity(1) // reserving won't shrink the buffer - assert_eq(tester.capacity(), 2) + inspect(tester.capacity(), content="2") tester.reserve_capacity(35) - assert_eq(tester.capacity(), 35) + inspect(tester.capacity(), content="35") + let dq = @deque.of([1, 2, 3]) + dq.reserve_capacity(8) + // reproduce the bug in reserve_capacity with wrong tail calculation + assert_eq(dq.back(), Some(3)) + dq.push_back(4) + assert_eq(dq.length(), 4) + assert_eq(dq[0], 1) + assert_eq(dq[1], 2) + assert_eq(dq[2], 3) + assert_eq(dq[3], 4) + @json.inspect(dq.to_array(), content=[1, 2, 3, 4]) } ///| @@ -792,7 +824,7 @@ test "deque guard iter2 coverage improvement" { ///| test "deque truncate" { // Empty deque - let dq : @deque.T[Int] = @deque.new() + let dq : @deque.Deque[Int] = @deque.new() dq.truncate(3) @json.inspect(dq, content=[]) @@ -838,7 +870,7 @@ test "deque retain" { let is_even = x => x % 2 == 0 // Empty deque - let dq : @deque.T[Int] = @deque.new() + let dq : @deque.Deque[Int] = @deque.new() dq.retain(is_even) @json.inspect(dq, content=[]) @@ -876,7 +908,7 @@ test "deque retain_map" { let inc_if_even = inc_if(is_even) // Empty deque - let dq : @deque.T[Int] = @deque.new() + let dq : @deque.Deque[Int] = @deque.new() dq.retain_map(inc_if_even) @json.inspect(dq, content=[]) @@ -920,7 +952,7 @@ test "deque retain_map" { ///| test "@deque.to_array/empty" { - let dq : T[Int] = @deque.new() + let dq : @deque.Deque[Int] = @deque.new() let arr = dq.to_array() @json.inspect(arr, content=[]) } @@ -947,7 +979,7 @@ test "@deque.to_array/wrapped_around" { ///| test "pop_front when wrapped around" { - let dq = of([1, 2, 3, 4, 5]) + let dq = @deque.of([1, 2, 3, 4, 5]) dq.push_front(6) inspect(dq.as_views(), content="([6], [1, 2, 3, 4, 5])") for _ in 0.. Int { + (upper * 123 + 456) % (upper + 1) // More varied results + } + + // Shuffle in place + @deque.Deque::shuffle_in_place(dq, rand~) + + // Verify properties of shuffle: + assert_eq(dq.length(), original.length()) + assert_eq(dq.to_array().sort(), original.to_array().sort()) + assert_true(dq != original) + + // Test non-in-place version + let shuffled = @deque.Deque::shuffle(original, rand~) + assert_eq(shuffled.length(), original.length()) + assert_eq(shuffled.to_array().sort(), original.to_array().sort()) + assert_true(shuffled != original) +} + +///| +test "deque map/mapi/search with wrapped head should keep logical order" { + let dq = @deque.new(capacity=4) + dq.push_back(1) + dq.push_back(2) + dq.push_back(3) + dq.push_back(4) + let _ = dq.pop_front() + let _ = dq.pop_front() + dq.push_back(5) + dq.push_back(6) + assert_eq(dq[0], 3) + assert_eq(dq[1], 4) + assert_eq(dq[2], 5) + assert_eq(dq[3], 6) + let mapped = dq.map(x => x + 10) + assert_eq(mapped, @deque.of([13, 14, 15, 16])) + let mapped = dq.mapi((x, i) => x + i) + assert_eq(mapped, @deque.of([3, 5, 7, 9])) + assert_eq(dq.search(3), Some(0)) + assert_eq(dq.search(4), Some(1)) + assert_eq(dq.search(5), Some(2)) + assert_eq(dq.search(6), Some(3)) +} + +///| +test "append_simple" { + // Test basic append functionality + let d1 = @deque.of([1, 2, 3]) + let d2 = @deque.of([4, 5, 6]) + d1.append(d2) + assert_eq(d1.to_array(), [1, 2, 3, 4, 5, 6]) + inspect(d1.as_views(), content="([1, 2, 3, 4, 5, 6], [])") + assert_eq(d1.length(), 6) + assert_eq(d2.to_array(), [4, 5, 6]) + inspect(d2.as_views(), content="([4, 5, 6], [])") + assert_eq(d2.length(), 3) +} + +///| +test "append_wrap_around" { + // Test append when the deque is wrapped around + let d1 = @deque.of([]) + d1.push_back(3) + d1.push_back(4) + d1.push_back(5) + d1.push_front(2) + d1.push_front(1) + let d2 = @deque.of([6, 7, 8]) + d1.append(d2) + assert_eq(d1.to_array(), [1, 2, 3, 4, 5, 6, 7, 8]) + inspect(d1.as_views(), content="([1, 2], [3, 4, 5, 6, 7, 8])") + assert_eq(d1.length(), 8) +} + +///| +test "append_empty" { + // Test appending an empty deque + let d1 = @deque.of([1, 2, 3]) + let d2 = @deque.of([]) + d1.append(d2) + assert_eq(d1.to_array(), [1, 2, 3]) + inspect(d1.as_views(), content="([1, 2, 3], [])") + assert_eq(d1.length(), 3) +} + +///| +test "append_into_empty" { + // Test appending into an empty deque + let d1 = @deque.of([]) + let d2 = @deque.of([1, 2, 3]) + d1.append(d2) + assert_eq(d1.to_array(), [1, 2, 3]) + inspect(d1.as_views(), content="([1, 2, 3], [])") + assert_eq(d1.length(), 3) +} + +///| +test "append_multiple_times" { + // Test multiple append operations + let d1 = @deque.of([1]) + let d2 = @deque.of([2]) + let d3 = @deque.of([3, 4]) + let d4 = @deque.of([5, 6, 7]) + d1.append(d2) + d1.append(d3) + d1.append(d4) + assert_eq(d1.to_array(), [1, 2, 3, 4, 5, 6, 7]) + inspect(d1.as_views(), content="([1, 2, 3, 4, 5, 6, 7], [])") + assert_eq(d1.length(), 7) +} + +///| +test "append_self" { + // Test appending a deque to itself (should create a copy) + let d1 = @deque.of([1, 2, 3]) + let old_len = d1.length() + d1.append(d1.copy()) + assert_eq(d1.to_array(), [1, 2, 3, 1, 2, 3]) + inspect(d1.as_views(), content="([1, 2, 3, 1, 2, 3], [])") + assert_eq(d1.length(), old_len * 2) +} + +///| +test "append_tail_position" { + /// Test that tail is correctly updated after append + let d1 = @deque.of([1, 2, 3]) + let d2 = @deque.of([4]) + d1.append(d2) + d1.push_back(5) + assert_eq(d1.to_array(), [1, 2, 3, 4, 5]) + inspect(d1.as_views(), content="([1, 2, 3, 4, 5], [])") +} + +///| +test "append_growth_strategy" { + /// Test the behavior of capacity growth after flattening + let d1 = @deque.of([]) + let d2 = @deque.of([]) + for i = 0; i < 100; i = i + 1 { + d2.push_back(i) + } + let old_cap = d1.capacity() + d1.append(d2) + assert_eq(d1.length(), 100) + assert_true(d1.capacity() >= old_cap * 2 || d1.capacity() >= 100) +} + +///| +/// Test large append to trigger buffer reallocation +test "append_large_realloc" { + let d1 = @deque.of([1]) + let d2 : @deque.Deque[Int] = @deque.of([]) + for i = 0; i < 1000; i = i + 1 { + d2.push_back(i) + } + d1.append(d2) + assert_eq(d1.length(), 1001) + assert_eq(d1.to_array()[0], 1) + assert_eq(d1.to_array()[1000], 999) +} + +///| +/// Test append empty after multiple pushes +test "append_empty_after_pushes" { + let d1 = @deque.of([]) + for i = 0; i < 10; i = i + 1 { + d1.push_back(i) + } + let d2 = @deque.of([]) + d1.append(d2) + assert_eq(d1.length(), 10) + assert_eq(d1.to_array(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + inspect(d1.as_views(), content="([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [])") +} + +///| +test "append_folded_but_has_space" { + let d1 = @deque.of([1, 2, 3, 4, 5, 6, 7, 8]) + inspect(d1.as_views(), content="([1, 2, 3, 4, 5, 6, 7, 8], [])") + d1 + ..unsafe_pop_front() + ..unsafe_pop_front() + ..unsafe_pop_front() + ..unsafe_pop_back() + inspect(d1.as_views(), content="([4, 5, 6, 7], [])") + d1.append(@deque.of([9, 10, 11])) + inspect(d1.as_views(), content="([4, 5, 6, 7, 9], [10, 11])") +} diff --git a/bundled-core/deque/deque_wbtest.mbt b/bundled-core/deque/deque_wbtest.mbt index 15ac6df..968d5e7 100644 --- a/bundled-core/deque/deque_wbtest.mbt +++ b/bundled-core/deque/deque_wbtest.mbt @@ -28,6 +28,6 @@ test "unsafe_pop_front after many push_front" { test "truncate zero" { let dq = of([1, 2, 3]) dq.truncate(0) - assert_eq(dq.len, 0) + inspect(dq.len, content="0") assert_eq(dq.head, dq.tail) } diff --git a/bundled-core/deque/pkg.generated.mbti b/bundled-core/deque/pkg.generated.mbti new file mode 100644 index 0000000..a7111cb --- /dev/null +++ b/bundled-core/deque/pkg.generated.mbti @@ -0,0 +1,84 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/deque" + +import( + "moonbitlang/core/json" + "moonbitlang/core/string" +) + +// Values + +// Errors + +// Types and methods +type Deque[A] +fn[A] Deque::append(Self[A], Self[A]) -> Unit +fn[A] Deque::as_views(Self[A]) -> (ArrayView[A], ArrayView[A]) +fn[A] Deque::back(Self[A]) -> A? +fn[A : Compare] Deque::binary_search(Self[A], A) -> Result[Int, Int] +fn[A] Deque::binary_search_by(Self[A], (A) -> Int) -> Result[Int, Int] +fn[A] Deque::capacity(Self[A]) -> Int +fn[A] Deque::clear(Self[A]) -> Unit +fn[A : Eq] Deque::contains(Self[A], A) -> Bool +fn[A] Deque::copy(Self[A]) -> Self[A] +fn[A] Deque::drain(Self[A], start~ : Int, len? : Int) -> Self[A] +fn[A] Deque::each(Self[A], (A) -> Unit) -> Unit +fn[A] Deque::eachi(Self[A], (Int, A) -> Unit) -> Unit +#deprecated +fn[A] Deque::filter_map_inplace(Self[A], (A) -> A?) -> Unit +fn[A] Deque::flatten(Self[Self[A]]) -> Self[A] +#as_free_fn +fn[A] Deque::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A] Deque::from_iter(Iter[A]) -> Self[A] +fn[A] Deque::front(Self[A]) -> A? +fn[A] Deque::get(Self[A], Int) -> A? +fn[A] Deque::is_empty(Self[A]) -> Bool +fn[A] Deque::iter(Self[A]) -> Iter[A] +fn[A] Deque::iter2(Self[A]) -> Iter2[Int, A] +fn Deque::join(Self[String], @string.View) -> String +fn[A] Deque::length(Self[A]) -> Int +fn[A, U] Deque::map(Self[A], (A) -> U) -> Self[U] +fn[A, U] Deque::mapi(Self[A], (Int, A) -> U) -> Self[U] +#as_free_fn +fn[A] Deque::new(capacity? : Int) -> Self[A] +#as_free_fn +fn[A] Deque::of(FixedArray[A]) -> Self[A] +fn[A] Deque::op_get(Self[A], Int) -> A +fn[A] Deque::op_set(Self[A], Int, A) -> Unit +fn[A] Deque::pop_back(Self[A]) -> A? +#deprecated +fn[A] Deque::pop_back_exn(Self[A]) -> Unit +fn[A] Deque::pop_front(Self[A]) -> A? +#deprecated +fn[A] Deque::pop_front_exn(Self[A]) -> Unit +fn[A] Deque::push_back(Self[A], A) -> Unit +fn[A] Deque::push_front(Self[A], A) -> Unit +fn[A] Deque::reserve_capacity(Self[A], Int) -> Unit +fn[A] Deque::retain(Self[A], (A) -> Bool) -> Unit +fn[A] Deque::retain_map(Self[A], (A) -> A?) -> Unit +fn[A] Deque::rev(Self[A]) -> Self[A] +fn[A] Deque::rev_each(Self[A], (A) -> Unit) -> Unit +fn[A] Deque::rev_eachi(Self[A], (Int, A) -> Unit) -> Unit +fn[A] Deque::rev_inplace(Self[A]) -> Unit +fn[A] Deque::rev_iter(Self[A]) -> Iter[A] +fn[A] Deque::rev_iter2(Self[A]) -> Iter2[Int, A] +fn[A : Eq] Deque::search(Self[A], A) -> Int? +fn[A] Deque::shrink_to_fit(Self[A]) -> Unit +fn[A] Deque::shuffle(Self[A], rand~ : (Int) -> Int) -> Self[A] +fn[A] Deque::shuffle_in_place(Self[A], rand~ : (Int) -> Int) -> Unit +fn[A] Deque::to_array(Self[A]) -> Array[A] +fn[A] Deque::truncate(Self[A], Int) -> Unit +fn[A] Deque::unsafe_pop_back(Self[A]) -> Unit +fn[A] Deque::unsafe_pop_front(Self[A]) -> Unit +impl[A : Compare] Compare for Deque[A] +impl[A : Eq] Eq for Deque[A] +impl[A : Show] Show for Deque[A] +impl[A : ToJson] ToJson for Deque[A] +impl[A : @json.FromJson] @json.FromJson for Deque[A] + +// Type aliases +pub typealias Deque as T + +// Traits + diff --git a/bundled-core/deque/types.mbt b/bundled-core/deque/types.mbt index 7d7e509..05837e6 100644 --- a/bundled-core/deque/types.mbt +++ b/bundled-core/deque/types.mbt @@ -12,11 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // head and tail point to non-empty slots. When the buffer is empty, head and tail points to the same slot. -struct T[A] { + +///| +struct Deque[A] { mut buf : UninitializedArray[A] mut len : Int mut head : Int mut tail : Int } + +///| +#deprecated("Use `Deque` instead of `T`") +pub typealias Deque as T diff --git a/bundled-core/double/README.mbt.md b/bundled-core/double/README.mbt.md index 84c3b02..a74518b 100644 --- a/bundled-core/double/README.mbt.md +++ b/bundled-core/double/README.mbt.md @@ -80,15 +80,15 @@ test "binary representation" { // Different byte orders should produce different results inspect( num.to_be_bytes(), - content= + content=( #|b"\x3f\xf0\x00\x00\x00\x00\x00\x00" - , + ), ) inspect( num.to_le_bytes(), - content= + content=( #|b"\x00\x00\x00\x00\x00\x00\xf0\x3f" - , + ), ) } ``` diff --git a/bundled-core/double/cbrt_js.mbt b/bundled-core/double/cbrt_js.mbt index 1c30980..4e36a8a 100644 --- a/bundled-core/double/cbrt_js.mbt +++ b/bundled-core/double/cbrt_js.mbt @@ -11,8 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `@math.cbrt` instead") -#coverage.skip -pub fn Double::cbrt(self : Double) -> Double = "Math" "cbrt" diff --git a/bundled-core/double/cbrt_nonjs.mbt b/bundled-core/double/cbrt_nonjs.mbt index 226c540..edd10f7 100644 --- a/bundled-core/double/cbrt_nonjs.mbt +++ b/bundled-core/double/cbrt_nonjs.mbt @@ -23,44 +23,3 @@ // is preserved. // ==================================================== // - -///| -#deprecated("use `@math.cbrt` instead") -#coverage.skip -pub fn Double::cbrt(self : Double) -> Double { - if self.is_inf() || self.is_nan() || self == 0.0 { - return self - } - let b1 : UInt = 715094163 // B1 = (682-0.03306235651)*2**20 - let b2 : UInt = 696219795 // B2 = (664-0.03306235651)*2**20 - let c = 5.42857142857142815906e-01 // 19/35 = 0x3FE15F15, 0xF15F15F1 - let d = -7.05306122448979611050e-01 // -864/1225 = 0xBFE691DE, 0x2532C834 - let e = 1.41428571428571436819e+00 // 99/70 = 0x3FF6A0EA, 0x0EA0EA0F - let f = 1.60714285714285720630e+00 // 45/28 = 0x3FF9B6DB, 0x6DB6DB6E - let g = 3.57142857142857150787e-01 // 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 - let hx = get_high_word(self).reinterpret_as_int() - let sign = if self < 0.0 { true } else { false } - let self = abs(self) - let t = if hx < 0x00100000 { - let t : UInt64 = 0x43500000_00000000 - let t : Double = t.reinterpret_as_double() - let t = t * self - set_high_word(0, get_high_word(t) / 3 + b2) - } else { - set_high_word(0, hx.reinterpret_as_uint() / 3 + b1) - } - let r = t * t / self - let s = c + r * t - let t = t * (g + f / (s + e + d / s)) - let t = set_high_word(0, get_high_word(t) + 0x00000001) - let s = t * t - let r = self / s - let w = t + t - let r = (r - t) / (w + r) - let t = t + t * r - if sign { - -t - } else { - t - } -} diff --git a/bundled-core/double/double.mbt b/bundled-core/double/double.mbt index 6fbf0b9..bf28389 100644 --- a/bundled-core/double/double.mbt +++ b/bundled-core/double/double.mbt @@ -74,6 +74,7 @@ pub fn from_int(i : Int) -> Double { /// inspect(3.14.abs(), content="3.14") /// inspect(0.0.abs(), content="0") /// ``` +#as_free_fn pub fn Double::abs(self : Double) -> Double = "%f64.abs" ///| @@ -91,7 +92,8 @@ pub fn signum(self : Double) -> Double { } } -///| Checks whether a double-precision floating-point number represents a "Not a +///| +/// Checks whether a double-precision floating-point number represents a "Not a /// Number" (NaN) value. /// /// Parameters: @@ -103,7 +105,7 @@ pub fn signum(self : Double) -> Double { /// Example: /// /// ```moonbit -/// inspect(not_a_number.is_nan(), content="true") +/// inspect(@double.not_a_number.is_nan(), content="true") /// inspect(42.0.is_nan(), content="false") /// inspect((0.0 / 0.0).is_nan(), content="true") /// ``` @@ -126,8 +128,8 @@ pub fn is_nan(self : Double) -> Bool { /// Example: /// /// ```moonbit -/// inspect(infinity.is_inf(), content="true") -/// inspect(neg_infinity.is_inf(), content="true") +/// inspect(@double.infinity.is_inf(), content="true") +/// inspect(@double.neg_infinity.is_inf(), content="true") /// inspect(42.0.is_inf(), content="false") /// ``` pub fn is_inf(self : Double) -> Bool { @@ -146,8 +148,8 @@ pub fn is_inf(self : Double) -> Bool { /// Example: /// /// ```moonbit -/// inspect(infinity.is_pos_inf(), content="true") -/// inspect(neg_infinity.is_pos_inf(), content="false") +/// inspect(@double.infinity.is_pos_inf(), content="true") +/// inspect(@double.neg_infinity.is_pos_inf(), content="false") /// inspect(42.0.is_pos_inf(), content="false") /// ``` pub fn is_pos_inf(self : Double) -> Bool { @@ -262,7 +264,7 @@ pub impl Hash for Double with hash_combine(self, hasher) { /// inspect(42.0.to_string(), content="42") /// inspect(3.14159.to_string(), content="3.14159") /// inspect((-0.0).to_string(), content="0") -/// inspect(not_a_number.to_string(), content="NaN") +/// inspect(@double.not_a_number.to_string(), content="NaN") /// ``` /// #intrinsic("%f64.to_string") @@ -301,13 +303,14 @@ pub impl Show for Double with output(self, logger) { /// let y = 1.000000001 /// inspect(x.is_close(y), content="false") /// inspect(x.is_close(y, relative_tolerance=1.0e-10), content="false") -/// inspect(infinity.is_close(infinity), content="true") +/// inspect(@double.infinity.is_close(@double.infinity), content="true") /// ``` +#as_free_fn pub fn Double::is_close( self : Self, other : Self, - relative_tolerance~ : Self = 1.0e-09, - absolute_tolerance~ : Self = 0.0 + relative_tolerance? : Self = 1.0e-09, + absolute_tolerance? : Self = 0.0, ) -> Bool { if relative_tolerance < 0.0 || absolute_tolerance < 0.0 { abort("Tolerances must be non-negative") @@ -341,9 +344,12 @@ pub fn Double::is_close( /// /// ```moonbit /// let d = 1.0 -/// inspect(d.to_be_bytes(), content= -/// #|b"\x3f\xf0\x00\x00\x00\x00\x00\x00" -/// ) +/// inspect( +/// d.to_be_bytes(), +/// content=( +/// #|b"\x3f\xf0\x00\x00\x00\x00\x00\x00" +/// ), +/// ) /// ``` pub fn to_be_bytes(self : Double) -> Bytes { self.reinterpret_as_uint64().to_be_bytes() @@ -364,9 +370,12 @@ pub fn to_be_bytes(self : Double) -> Bytes { /// /// ```moonbit /// let d = 1.0 -/// inspect(d.to_le_bytes(), content= -/// #|b"\x00\x00\x00\x00\x00\x00\xf0\x3f" -/// ) +/// inspect( +/// d.to_le_bytes(), +/// content=( +/// #|b"\x00\x00\x00\x00\x00\x00\xf0\x3f" +/// ), +/// ) /// ``` pub fn to_le_bytes(self : Double) -> Bytes { self.reinterpret_as_uint64().to_le_bytes() diff --git a/bundled-core/double/double.mbti b/bundled-core/double/double.mbti deleted file mode 100644 index f8b3e50..0000000 --- a/bundled-core/double/double.mbti +++ /dev/null @@ -1,168 +0,0 @@ -package "moonbitlang/core/double" - -// Values -fn abs(Double) -> Double - -#deprecated -fn acos(Double) -> Double - -#deprecated -fn acosh(Double) -> Double - -#deprecated -fn asin(Double) -> Double - -#deprecated -fn asinh(Double) -> Double - -#deprecated -fn atan(Double) -> Double - -#deprecated -fn atan2(Double, Double) -> Double - -#deprecated -fn atanh(Double) -> Double - -#deprecated -fn cbrt(Double) -> Double - -fn ceil(Double) -> Double - -#deprecated -fn cos(Double) -> Double - -#deprecated -fn cosh(Double) -> Double - -#deprecated -fn exp(Double) -> Double - -#deprecated -fn expm1(Double) -> Double - -fn floor(Double) -> Double - -fn from_int(Int) -> Double - -#deprecated -fn hypot(Double, Double) -> Double - -let infinity : Double - -fn is_close(Double, Double, relative_tolerance~ : Double = .., absolute_tolerance~ : Double = ..) -> Bool - -#deprecated -fn ln(Double) -> Double - -#deprecated -fn ln_1p(Double) -> Double - -#deprecated -fn log10(Double) -> Double - -#deprecated -fn log2(Double) -> Double - -let max_value : Double - -let min_positive : Double - -let min_value : Double - -let neg_infinity : Double - -let not_a_number : Double - -fn pow(Double, Double) -> Double - -fn round(Double) -> Double - -#deprecated -fn sin(Double) -> Double - -#deprecated -fn sinh(Double) -> Double - -#deprecated -fn tan(Double) -> Double - -#deprecated -fn tanh(Double) -> Double - -fn trunc(Double) -> Double - -// Types and methods -fn Double::abs(Double) -> Double -#deprecated -fn Double::acos(Double) -> Double -#deprecated -fn Double::acosh(Double) -> Double -#deprecated -fn Double::asin(Double) -> Double -#deprecated -fn Double::asinh(Double) -> Double -#deprecated -fn Double::atan(Double) -> Double -#deprecated -fn Double::atan2(Double, Double) -> Double -#deprecated -fn Double::atanh(Double) -> Double -#deprecated -fn Double::cbrt(Double) -> Double -fn Double::ceil(Double) -> Double -#deprecated -fn Double::cos(Double) -> Double -#deprecated -fn Double::cosh(Double) -> Double -#deprecated -fn Double::exp(Double) -> Double -#deprecated -fn Double::expm1(Double) -> Double -fn Double::floor(Double) -> Double -fn Double::from_int(Int) -> Double -#deprecated -fn Double::hypot(Double, Double) -> Double -#deprecated -fn Double::inf(Int) -> Double -fn Double::is_close(Double, Double, relative_tolerance~ : Double = .., absolute_tolerance~ : Double = ..) -> Bool -fn Double::is_inf(Double) -> Bool -fn Double::is_nan(Double) -> Bool -fn Double::is_neg_inf(Double) -> Bool -fn Double::is_pos_inf(Double) -> Bool -#deprecated -fn Double::ln(Double) -> Double -#deprecated -fn Double::ln_1p(Double) -> Double -#deprecated -fn Double::log10(Double) -> Double -#deprecated -fn Double::log2(Double) -> Double -#deprecated -fn Double::min_normal() -> Double -#deprecated -fn Double::nan() -> Double -fn Double::pow(Double, Double) -> Double -fn Double::round(Double) -> Double -fn Double::signum(Double) -> Double -#deprecated -fn Double::sin(Double) -> Double -#deprecated -fn Double::sinh(Double) -> Double -#deprecated -fn Double::tan(Double) -> Double -#deprecated -fn Double::tanh(Double) -> Double -fn Double::to_be_bytes(Double) -> Bytes -fn Double::to_le_bytes(Double) -> Bytes -fn Double::to_string(Double) -> String -fn Double::to_uint(Double) -> UInt -fn Double::trunc(Double) -> Double -impl Hash for Double -impl Mod for Double -impl Show for Double - -// Type aliases - -// Traits - diff --git a/bundled-core/double/exp_js.mbt b/bundled-core/double/exp_js.mbt index 96ebc3b..4e36a8a 100644 --- a/bundled-core/double/exp_js.mbt +++ b/bundled-core/double/exp_js.mbt @@ -11,13 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `Double::exp` instead") -#coverage.skip -pub fn Double::exp(self : Double) -> Double = "Math" "exp" - -///| -#deprecated("use `Double::expm1` instead") -#coverage.skip -pub fn Double::expm1(self : Double) -> Double = "Math" "expm1" diff --git a/bundled-core/double/exp_nonjs.mbt b/bundled-core/double/exp_nonjs.mbt index 75bc540..eb0aee1 100644 --- a/bundled-core/double/exp_nonjs.mbt +++ b/bundled-core/double/exp_nonjs.mbt @@ -23,215 +23,3 @@ // is preserved. // ==================================================== // - -///| -#deprecated("use `@math.exp` instead") -#coverage.skip -pub fn Double::exp(self : Double) -> Double { - fn get_high_word(x : Double) -> UInt { - (x.reinterpret_as_uint64() >> 32).to_uint() - } - - fn get_low_word(x : Double) -> UInt { - x.reinterpret_as_uint64().to_uint() - } - - fn insert_words(ix0 : UInt64, ix1 : UInt64) -> Double { - let mut bits : UInt64 = 0 - bits = bits | (ix0 << 32) - bits = bits | ix1 - bits.reinterpret_as_double() - } - - let mut x = self - let one = 1.0 - let halF = [0.5, -0.5] - let o_threshold = 7.09782712893383973096e+02 - let u_threshold = -7.45133219101941108420e+02 - let ln2HI = [6.93147180369123816490e-01, -6.93147180369123816490e-01] - let ln2LO = [1.90821492927058770002e-10, -1.90821492927058770002e-10] - let invln2 = 1.44269504088896338700e+00 - let p1 = 1.66666666666666019037e-01 - let p2 = -2.77777777770155933842e-03 - let p3 = 6.61375632143793436117e-05 - let p4 = -1.65339022054652515390e-06 - let p5 = 4.13813679705723846039e-08 - let e = 2.718281828459045 - let mut hi = 0.0 - let mut lo = 0.0 - let huge = 1.0e+300 - let twom1000 = 9.33263618503218878990e-302 - let two1023 = 8.988465674311579539e307 - let mut k : Int = 0 - let mut hx : UInt = get_high_word(self) - let xsb : Int = ((hx >> 31) & 1).reinterpret_as_int() - hx = hx & 0x7FFFFFFF - if hx >= 0x40862E42 { - if hx >= 0x7FF00000 { - let lx : UInt = get_low_word(self) - if ((hx & 0xFFFFF) | lx) != 0 { - return self + self - } else if xsb == 0 { - return self - } else { - return 0.0 - } - } - if self > o_threshold { - return huge * huge - } - if self < u_threshold { - return twom1000 * twom1000 - } - } - if hx > 0x3FD62E42 { - if hx < 0x3FF0A2B2 { - if self == 1.0 { - return e - } - hi = self - ln2HI[xsb] - lo = ln2LO[xsb] - k = 1 - xsb - xsb - } else { - k = (invln2 * self + halF[xsb]).to_int() - let t = k.to_double() - hi = self - t * ln2HI[0] - lo = t * ln2LO[0] - } - x = hi - lo - } else if hx < 0x3E300000 { - if huge + x > one { - return one + x - } - } else { - k = 0 - } - let t = x * x - let twopk = if k >= -1021 { - insert_words( - (0x3FF00000 + (k.reinterpret_as_uint() << 20).reinterpret_as_int()) - .to_int64() - .reinterpret_as_uint64(), - 0, - ) - } else { - insert_words( - 0x3FF00000UL + ((k + 1000).reinterpret_as_uint() << 20).to_uint64(), - 0, - ) - } - let c = x - t * (p1 + t * (p2 + t * (p3 + t * (p4 + t * p5)))) - if k == 0 { - return one - (x * c / (c - 2.0) - x) - } - let y = one - (lo - x * c / (2.0 - c) - hi) - if k >= -1021 { - if k == 1024 { - return y * 2.0 * two1023 - } else { - return y * twopk - } - } else { - return y * twopk * twom1000 - } -} - -///| -#deprecated("use `@math.expm1` instead") -#coverage.skip -pub fn Double::expm1(self : Double) -> Double { - if self.is_nan() { - return not_a_number - } - let o_threshold = 7.09782712893383973096e+02 - if self > o_threshold { - return infinity - } - if self.is_inf() { - return -1.0 - } - let huge = 1.0e+300 - let tiny = 1.0e-300 - let ln2_hi = 6.93147180369123816490e-01 - let ln2_lo = 1.90821492927058770002e-10 - let invln2 = 1.44269504088896338700e+00 - let q1 = -3.33333333333331316428e-02 - let q2 = 1.58730158725481460165e-03 - let q3 = -7.93650757867487942473e-05 - let q4 = 4.00821782732936239552e-06 - let q5 = -2.01099218183624371326e-07 - let mut x = self - let mut hx = get_high_word(x) - let xsb : Int = (hx & 0x80000000).reinterpret_as_int() - let mut y : Double = if xsb == 0 { x } else { -x } - hx = hx & 0x7fffffff - if hx >= 0x4043687A { - if xsb != 0 { - if x + tiny < 0.0 { - return tiny - 1.0 - } - } - } - let mut hi = 0.0 - let mut lo = 0.0 - let mut k = 0 - let mut c = 0.0 - let mut t = 0.0 - if hx > 0x3fd62e42 { - if hx < 0x3FF0A2B2 { - hi = if xsb == 0 { x - ln2_hi } else { x + ln2_hi } - lo = if xsb == 0 { ln2_lo } else { -ln2_lo } - k = if xsb == 0 { 1 } else { -1 } - } else { - k = (invln2 * x + (if xsb == 0 { 0.5 } else { -0.5 })).to_int() - t = k.to_double() - hi = x - t * ln2_hi - lo = t * ln2_lo - } - x = hi - lo - c = hi - x - lo - } else if hx < 0x3c900000 { - t = huge + x - return x - (t - (huge + x)) - } else { - k = 0 - } - let hfx : Double = 0.5 * x - let hxs : Double = x * hfx - let r1 : Double = 1.0 + - hxs * (q1 + hxs * (q2 + hxs * (q3 + hxs * (q4 + hxs * q5)))) - let t : Double = 3.0 - r1 * hfx - let e : Double = hxs * ((r1 - t) / (6.0 - x * t)) - if k == 0 { - return x - (x * e - hxs) - } else { - let e : Double = x * (e - c) - c - let e : Double = e - hxs - if k == -1 { - return 0.5 * (x - e) - 0.5 - } - if k == 1 { - return if x < -0.25 { - -2.0 * (e - (x + 0.5)) - } else { - 1.0 + 2.0 * (x - e) - } - } - if k <= -2 || k > 56 { - y = 1.0 - (e - x) - y = set_high_word(y, get_high_word(y) + (k << 20).reinterpret_as_uint()) - return y - 1.0 - } - let mut t : Double = 1.0 - if k < 20 { - t = set_high_word(0, (0x3ff00000 - (0x200000 >> k)).reinterpret_as_uint()) - y = t - (e - x) - y = set_high_word(y, get_high_word(y) + (k << 20).reinterpret_as_uint()) - } else { - t = set_high_word(0, ((0x3ff - k) << 20).reinterpret_as_uint()) - y = x - (e + t) + 1.0 - y = set_high_word(y, get_high_word(y) + (k << 20).reinterpret_as_uint()) - } - } - y -} diff --git a/bundled-core/double/hyperbolic_js.mbt b/bundled-core/double/hyperbolic_js.mbt index fb238bb..4e36a8a 100644 --- a/bundled-core/double/hyperbolic_js.mbt +++ b/bundled-core/double/hyperbolic_js.mbt @@ -11,33 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `@math.sinh` instead") -#coverage.skip -pub fn Double::sinh(self : Double) -> Double = "Math" "sinh" - -///| -#deprecated("use `@math.cosh` instead") -#coverage.skip -pub fn Double::cosh(self : Double) -> Double = "Math" "cosh" - -///| -#deprecated("use `@math.tanh` instead") -#coverage.skip -pub fn Double::tanh(self : Double) -> Double = "Math" "tanh" - -///| -#deprecated("use `@math.asinh` instead") -#coverage.skip -pub fn Double::asinh(self : Double) -> Double = "Math" "asinh" - -///| -#deprecated("use `@math.acosh` instead") -#coverage.skip -pub fn Double::acosh(self : Double) -> Double = "Math" "acosh" - -///| -#deprecated("use `@math.atanh` instead") -#coverage.skip -pub fn Double::atanh(self : Double) -> Double = "Math" "atanh" diff --git a/bundled-core/double/hyperbolic_nonjs.mbt b/bundled-core/double/hyperbolic_nonjs.mbt index ed400f8..7614af9 100644 --- a/bundled-core/double/hyperbolic_nonjs.mbt +++ b/bundled-core/double/hyperbolic_nonjs.mbt @@ -28,195 +28,3 @@ // is preserved. // ==================================================== // - -///| -#deprecated("use `@math.sinh` instead") -#coverage.skip -pub fn Double::sinh(self : Double) -> Double { - if self.is_nan() || self.is_inf() { - return self - } - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - let abs_x = self.abs() - let shuge = 1.0e307 - let h = if self < 0.0 { -0.5 } else { 0.5 } - if ix < 0x40360000 { - if ix < 0x3e300000 { - if shuge + self > 1.0 { - return self - } - } - let t = abs_x.expm1() - if ix < 0x3ff00000 { - return h * (2.0 * t - t * t / (t + 1.0)) - } - return h * (t + t / (t + 1.0)) - } - if ix < 0x40862E42 { - return h * abs_x.exp() - } - if abs_x.reinterpret_as_uint64() < 0x408633ce8fb9f87d { - let w = exp(0.5 * abs_x) - let t = h * w - return t * w - } - self * shuge -} - -///| -#deprecated("use `@math.cosh` instead") -#coverage.skip -pub fn Double::cosh(self : Double) -> Double { - if self.is_nan() { - return self - } - if self.is_inf() { - return infinity - } - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - if ix < 0x3fd62e43 { - let t = self.abs().expm1() - let w = 1.0 + t - if ix < 0x3c800000 { - return w - } - return 1.0 + t * t / (w + w) - } - if ix < 0x40360000 { - let t = self.abs().exp() - return 0.5 * t + 0.5 / t - } - if ix < 0x40862E42 { - return 0.5 * self.abs().exp() - } - let lx = get_low_word(self).reinterpret_as_int() - if ix < 0x408633ce || (ix == 0x408633ce && lx <= 0x8fb9f87d) { - let w = exp(0.5 * self.abs()) - let t = 0.5 * w - return t * w - } - infinity -} - -///| -#deprecated("use `@math.tanh` instead") -#coverage.skip -pub fn Double::tanh(self : Double) -> Double { - if self.is_nan() { - return self - } - if self.is_pos_inf() { - return 1.0 - } - if self.is_neg_inf() { - return -1.0 - } - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - let tiny = 1.0e-300 - let z = if ix < 0x40360000 { - if ix < 0x3c800000 { - self * (1.0 + self) - } else if ix >= 0x3ff00000 { - let t = (2.0 * self.abs()).expm1() - 1.0 - 2.0 / (t + 2.0) - } else { - let t = (-2.0 * self.abs()).expm1() - -t / (t + 2.0) - } - } else { - 1.0 - tiny - } - if self >= 0.0 { - z - } else { - -z - } -} - -///| -#deprecated("use `@math.asinh` instead") -#coverage.skip -pub fn Double::asinh(self : Double) -> Double { - if self.is_nan() || self.is_inf() { - return self - } - let one : Double = 1.0 - let ln2 : Double = 6.93147180559945286227e-01 - let huge : Double = 1.0e300 - let hx = get_high_word(self).reinterpret_as_int() - let ix = hx & 0x7fffffff - if ix < 0x3e300000 { - if huge + self > one { - return self - } - } - let w : Double = if ix > 0x41b00000 { - self.abs().ln() + ln2 - } else if ix > 0x40000000 { - let t = self.abs() - (2.0 * t + one / ((self * self + one).sqrt() + t)).ln() - } else { - let t = self * self - (self.abs() + t / (one + (one + t).sqrt())).ln_1p() - } - if hx > 0 { - w - } else { - -w - } -} - -///| -#deprecated("use `@math.acosh` instead") -#coverage.skip -pub fn Double::acosh(self : Double) -> Double { - let one = 1.0 - let hx = get_high_word(self).reinterpret_as_int() - if self < 1.0 || self.is_nan() { - return not_a_number - } else if self == 1.0 { - return 0.0 - } else if self.is_pos_inf() { - return infinity - } else if hx >= 0x41b00000 { - return self.ln() + ln2 - } else if hx > 0x40000000 { - let t = self * self - return (2.0 * self - one / (self + (t - one).sqrt())).ln() - } else { - let t = self - one - return (t + (2.0 * t + t * t).sqrt()).ln_1p() - } -} - -///| -#deprecated("use `@math.atanh` instead") -#coverage.skip -pub fn Double::atanh(self : Double) -> Double { - let hx : Int = get_high_word(self).reinterpret_as_int() - let ix = hx & 0x7fffffff - if self.abs() > 1.0 { - return not_a_number - } - if self == 1.0 { - return infinity - } - if self == -1.0 { - return neg_infinity - } - if ix < 0x3e300000 && 1.0e300 + self > 0.0 { - return self - } - let self = self.abs() - let t = if self <= 0.5 { - let t = self + self - 0.5 * (t + t * self / (1.0 - self)).ln_1p() - } else { - 0.5 * ((self + self) / (1.0 - self)).ln_1p() - } - if hx >= 0 { - t - } else { - -t - } -} diff --git a/bundled-core/double/hypot_js.mbt b/bundled-core/double/hypot_js.mbt index 36cb13f..4e36a8a 100644 --- a/bundled-core/double/hypot_js.mbt +++ b/bundled-core/double/hypot_js.mbt @@ -11,8 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `@math.hypot` instead") -#coverage.skip -pub fn Double::hypot(self : Double, y : Double) -> Double = "Math" "hypot" diff --git a/bundled-core/double/hypot_nonjs.mbt b/bundled-core/double/hypot_nonjs.mbt index f2fee1f..a803de3 100644 --- a/bundled-core/double/hypot_nonjs.mbt +++ b/bundled-core/double/hypot_nonjs.mbt @@ -19,24 +19,3 @@ // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // The algorithm has been adapted and implemented independently in this code. - -///| -#deprecated("use `@math.hypot` instead") -#coverage.skip -pub fn Double::hypot(self : Double, y : Double) -> Double { - if self.is_nan() || y.is_nan() { - return not_a_number - } - if self.is_inf() || y.is_inf() { - return infinity - } - let x = self.abs() - let y = y.abs() - let double_epsilon : Double = 0x0.0000000000001P-1022 - let (x, y) = if y > x { (y, x) } else { (x, y) } - if x * double_epsilon >= y { - return x - } - let r = y / x - x * (1.0 + r * r).sqrt() -} diff --git a/bundled-core/double/internal/ryu/README.mbt.md b/bundled-core/double/internal/ryu/README.mbt.md new file mode 100644 index 0000000..08e6929 --- /dev/null +++ b/bundled-core/double/internal/ryu/README.mbt.md @@ -0,0 +1,157 @@ +# Double Internal Ryu Package Documentation + +This package provides the Ryu algorithm implementation for fast and accurate double-precision floating-point number to string conversion. Ryu is an optimized algorithm that produces the shortest decimal representation of floating-point numbers. + +## Overview + +The Ryu algorithm is used internally by the `double` package to convert floating-point numbers to their string representations efficiently and accurately. This is an internal implementation package that most users won't interact with directly. + +## Core Function + +The main function provided by this package: + +```moonbit +test "ryu conversion" { + // The @ryu.ryu_to_string function converts doubles to strings + let result = @ryu.ryu_to_string(123.456) + inspect(result.length() > 0, content="true") + + // Test with various double values + let zero_result = @ryu.ryu_to_string(0.0) + inspect(zero_result, content="0") + + let negative_result = @ryu.ryu_to_string(-42.5) + inspect(negative_result.length() > 0, content="true") + + // Test with large values + let large_result = @ryu.ryu_to_string(12300000000.0) // 1.23e10 + inspect(large_result.length() > 0, content="true") +} +``` + +## Algorithm Properties + +The Ryu algorithm provides several important properties: + +### Accuracy +```moonbit +test "accuracy properties" { + // Ryu produces the shortest decimal representation + let precise_value = 0.1 + let result = @ryu.ryu_to_string(precise_value) + inspect(result.length() > 0, content="true") + + // Test with values that have exact representations + let exact_value = 0.5 + let exact_result = @ryu.ryu_to_string(exact_value) + inspect(exact_result, content="0.5") + + // Test with powers of 2 (should be exact) + let power_of_two = 8.0 + let power_result = @ryu.ryu_to_string(power_of_two) + inspect(power_result, content="8") +} +``` + +### Edge Cases +```moonbit +test "edge cases" { + // Test special values + let inf_result = @ryu.ryu_to_string(1.0 / 0.0) // Infinity + inspect(inf_result.length() > 0, content="true") + + let neg_inf_result = @ryu.ryu_to_string(-1.0 / 0.0) // Negative infinity + inspect(neg_inf_result.length() > 0, content="true") + + // Test very small values + let tiny_result = @ryu.ryu_to_string(0.0000000001) // Very small + inspect(tiny_result.length() > 0, content="true") + + // Test very large values + let huge_result = @ryu.ryu_to_string(1000000000000.0) // Large number + inspect(huge_result.length() > 0, content="true") +} +``` + +## Performance Characteristics + +The Ryu algorithm is optimized for: + +1. **Speed**: Faster than traditional algorithms like Dragon4 +2. **Accuracy**: Always produces the shortest decimal representation +3. **Determinism**: Same input always produces same output +4. **Memory efficiency**: Uses minimal temporary storage + +```moonbit +test "performance demonstration" { + // Ryu is designed to be fast for common values + let common_values = [0.0, 1.0, -1.0, 0.5, 0.25, 0.125] + + for value in common_values { + let result = @ryu.ryu_to_string(value) + inspect(result.length() > 0, content="true") + } + + // Also efficient for complex values + let complex_values = [3.141592653589793, 2.718281828459045, 1.4142135623730951] + + for value in complex_values { + let result = @ryu.ryu_to_string(value) + inspect(result.length() > 0, content="true") + } +} +``` + +## Internal Implementation Details + +This package implements the core Ryu algorithm with: + +- **Lookup tables**: Pre-computed powers of 5 and 10 +- **Bit manipulation**: Efficient operations on floating-point representation +- **Optimized arithmetic**: 128-bit multiplication and division routines +- **Special case handling**: NaN, infinity, and zero values + +## Usage Context + +This package is used internally by: + +- `double` package for `Double::to_string()` implementation +- JSON serialization of floating-point numbers +- String formatting operations involving doubles +- Any operation that needs to display floating-point numbers + +## Technical Background + +The Ryu algorithm works by: + +1. **Extracting** the IEEE 754 representation of the double +2. **Computing** the exact decimal representation using integer arithmetic +3. **Optimizing** the output to the shortest possible form +4. **Formatting** the result according to ECMAScript standards + +## Compliance + +This implementation: + +- Follows the ECMAScript number-to-string specification +- Produces output compatible with JavaScript's `Number.prototype.toString()` +- Handles all IEEE 754 double-precision values correctly +- Maintains round-trip accuracy for string-to-number-to-string conversions + +## Alternative Approaches + +Before Ryu, common approaches included: + +- **Dragon4**: Accurate but slower +- **Grisu**: Fast but not always shortest representation +- **sprintf-style**: Simple but potentially inaccurate + +Ryu provides the best combination of speed and accuracy. + +## References + +- Original Ryu paper: "Ryu: fast float-to-string conversion" by Ulf Adams +- ECMAScript specification for number-to-string conversion +- IEEE 754 standard for floating-point arithmetic + +This package provides the foundation for accurate and efficient floating-point string conversion in MoonBit's double package. diff --git a/bundled-core/double/internal/ryu/common.mbt b/bundled-core/double/internal/ryu/common.mbt index f4d318f..0e66195 100644 --- a/bundled-core/double/internal/ryu/common.mbt +++ b/bundled-core/double/internal/ryu/common.mbt @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. + +///| fn pow5bits(e : Int) -> Int { ((e * 1217359).reinterpret_as_uint() >> 19).reinterpret_as_int() + 1 } @@ -30,14 +31,16 @@ fn copy_special_str(sign : Bool, exponent : Bool, mantissa : Bool) -> String { return s + "0.0" } -///| // Returns floor(log_10(5^e)); requires 0 <= e <= 2620. + +///| fn log10Pow5(e : Int) -> Int { ((e * 732923).reinterpret_as_uint() >> 20).reinterpret_as_int() } -///| // Returns floor(log_10(2^e)); requires 0 <= e <= 1650. + +///| fn log10Pow2(e : Int) -> Int { ((e * 78913).reinterpret_as_uint() >> 18).reinterpret_as_int() } diff --git a/bundled-core/double/internal/ryu/ryu.mbti b/bundled-core/double/internal/ryu/pkg.generated.mbti similarity index 71% rename from bundled-core/double/internal/ryu/ryu.mbti rename to bundled-core/double/internal/ryu/pkg.generated.mbti index 0f4f607..6798542 100644 --- a/bundled-core/double/internal/ryu/ryu.mbti +++ b/bundled-core/double/internal/ryu/pkg.generated.mbti @@ -1,8 +1,11 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/double/internal/ryu" // Values fn ryu_to_string(Double) -> String +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/double/internal/ryu/ryu.mbt b/bundled-core/double/internal/ryu/ryu.mbt index 3739bb7..02e0733 100644 --- a/bundled-core/double/internal/ryu/ryu.mbt +++ b/bundled-core/double/internal/ryu/ryu.mbt @@ -86,7 +86,7 @@ fn mulShiftAll64( m : UInt64, mul : (UInt64, UInt64), j : Int, - mmShift : Bool + mmShift : Bool, ) -> (UInt64, UInt64, UInt64) { let m = m << 1 let (lo, tmp) = umul128(m, mul.0) @@ -120,7 +120,7 @@ fn mulShiftAll64( let gPOW5_TABLE_SIZE = 26 ///| -let gDOUBLE_POW5_INV_SPLIT2 : Array[UInt64] = [ +let gDOUBLE_POW5_INV_SPLIT2 : FixedArray[UInt64] = [ 1, 2305843009213693952, 5955668970331000884, 1784059615882449851, 8982663654677661702, 1380349269358112757, 7286864317269821294, 2135987035920910082, 7005857020398200553, 1652639921975621497, 17965325103354776697, 1278668206209430417, 8928596168509315048, @@ -132,14 +132,14 @@ let gDOUBLE_POW5_INV_SPLIT2 : Array[UInt64] = [ ] ///| -let gPOW5_INV_OFFSETS : Array[UInt] = [ +let gPOW5_INV_OFFSETS : FixedArray[UInt] = [ 0x54544554, 0x04055545, 0x10041000, 0x00400414, 0x40010000, 0x41155555, 0x00000454, 0x00010044, 0x40000000, 0x44000041, 0x50454450, 0x55550054, 0x51655554, 0x40004000, 0x01000001, 0x00010500, 0x51515411, 0x05555554, 0x00000000, ] ///| -let gDOUBLE_POW5_SPLIT2 : Array[UInt64] = [ +let gDOUBLE_POW5_SPLIT2 : FixedArray[UInt64] = [ 0, 1152921504606846976, 0, 1490116119384765625, 1032610780636961552, 1925929944387235853, 7910200175544436838, 1244603055572228341, 16941905809032713930, 1608611746708759036, 13024893955298202172, 2079081953128979843, 6607496772837067824, 1343575221513417750, @@ -149,14 +149,14 @@ let gDOUBLE_POW5_SPLIT2 : Array[UInt64] = [ ] ///| -let gPOW5_OFFSETS : Array[UInt] = [ +let gPOW5_OFFSETS : FixedArray[UInt] = [ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40000000, 0x59695995, 0x55545555, 0x56555515, 0x41150504, 0x40555410, 0x44555145, 0x44504540, 0x45555550, 0x40004000, 0x96440440, 0x55565565, 0x54454045, 0x40154151, 0x55559155, 0x51405555, 0x00000105, ] ///| -let gDOUBLE_POW5_TABLE : Array[UInt64] = [ +let gDOUBLE_POW5_TABLE : FixedArray[UInt64] = [ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, 6103515625, 30517578125, 152587890625, 762939453125, 3814697265625, 19073486328125, 95367431640625, 476837158203125, 2384185791015625, 11920928955078125, @@ -288,8 +288,9 @@ fn decimal_length17(v : UInt64) -> Int { return 1 } -///| // A floating decimal representing m * 10^e. + +///| priv struct FloatingDecimal64 { mantissa : UInt64 // Decimal exponent's range is -324 to 308 @@ -355,12 +356,11 @@ fn d2d(ieeeMantissa : UInt64, ieeeExponent : UInt) -> FloatingDecimal64 { // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q // <=> true && pow5Factor(mm) >= q, since e2 >= q. vmIsTrailingZeros = multipleOfPowerOf5( - mv - 1UL - mmShift.to_int64().reinterpret_as_uint64(), + mv - 1UL - mmShift.to_uint64(), q, ) } else { - vp = vp - - multipleOfPowerOf5(mv + 2UL, q).to_int64().reinterpret_as_uint64() + vp = vp - multipleOfPowerOf5(mv + 2UL, q).to_uint64() } } } else { @@ -435,8 +435,7 @@ fn d2d(ieeeMantissa : UInt64, ieeeExponent : UInt) -> FloatingDecimal64 { lastRemovedDigit = 4 } output = vr + - ((vr == vm && (not(even) || not(vmIsTrailingZeros))) || - lastRemovedDigit >= 5) + ((vr == vm && (!even || !vmIsTrailingZeros)) || lastRemovedDigit >= 5) .to_int64() .reinterpret_as_uint64() } else { @@ -471,7 +470,7 @@ fn d2d(ieeeMantissa : UInt64, ieeeExponent : UInt) -> FloatingDecimal64 { vm = vmDiv10 removed = removed + 1 } - output = vr + (vr == vm || roundUp).to_int64().reinterpret_as_uint64() + output = vr + (vr == vm || roundUp).to_uint64() } let exp : Int = e10 + removed let fd : FloatingDecimal64 = { mantissa: output, exponent: exp } @@ -490,7 +489,7 @@ fn to_chars(v : FloatingDecimal64, sign : Bool) -> String { let mut output = v.mantissa let olength = decimal_length17(output) let mut exp : Int = v.exponent + olength - 1 - let scientificNotation = not(exp >= -6 && exp < 21) + let scientificNotation = !(exp >= -6 && exp < 21) if scientificNotation { // Print the decimal digits. for i in 0..<(olength - 1) { @@ -587,7 +586,7 @@ fn to_chars(v : FloatingDecimal64, sign : Bool) -> String { ///| fn d2d_small_int( ieeeMantissa : UInt64, - ieeeExponent : Int + ieeeExponent : Int, ) -> FloatingDecimal64? { let m2 : UInt64 = (1UL << gDOUBLE_MANTISSA_BITS) | ieeeMantissa let e2 : Int = ieeeExponent - gDOUBLE_BIAS - gDOUBLE_MANTISSA_BITS @@ -658,7 +657,7 @@ test "double/ryu.mbt:205" { if sum < high0 { high1 = high1 + 1 } - assert_eq(high1, 222222222) + inspect(high1, content="222222222") } ///| @@ -678,7 +677,7 @@ test "double/ryu.mbt:230" { ///| test "double/ryu.mbt:252" { - assert_eq(gDOUBLE_POW5_BITCOUNT, 125) + inspect(gDOUBLE_POW5_BITCOUNT, content="125") } ///| @@ -713,5 +712,5 @@ test "double/ryu.mbt:230" { ///| test "double/ryu.mbt:252" { - assert_eq(gDOUBLE_POW5_BITCOUNT, 125) + inspect(gDOUBLE_POW5_BITCOUNT, content="125") } diff --git a/bundled-core/double/internal/ryu/ryu_test.mbt b/bundled-core/double/internal/ryu/ryu_test.mbt index 4890aec..4aadc00 100644 --- a/bundled-core/double/internal/ryu/ryu_test.mbt +++ b/bundled-core/double/internal/ryu/ryu_test.mbt @@ -14,15 +14,15 @@ ///| test "Basic" { - assert_eq(@ryu.ryu_to_string(0.0), "0") - assert_eq(@ryu.ryu_to_string(-0.0), "0") - assert_eq(@ryu.ryu_to_string(1.e0), "1") - assert_eq(@ryu.ryu_to_string(-1.e0), "-1") - assert_eq(@ryu.ryu_to_string(0.0 / 0.0), "NaN") - assert_eq(@ryu.ryu_to_string(1.0 / 0.0), "Infinity") - assert_eq(@ryu.ryu_to_string(-1.0 / 0.0), "-Infinity") - assert_eq(@ryu.ryu_to_string(3.1415926), "3.1415926") - assert_eq(@ryu.ryu_to_string(1234000000000000.0), "1234000000000000") + inspect(@ryu.ryu_to_string(0.0), content="0") + inspect(@ryu.ryu_to_string(-0.0), content="0") + inspect(@ryu.ryu_to_string(1.e0), content="1") + inspect(@ryu.ryu_to_string(-1.e0), content="-1") + inspect(@ryu.ryu_to_string(0.0 / 0.0), content="NaN") + inspect(@ryu.ryu_to_string(1.0 / 0.0), content="Infinity") + inspect(@ryu.ryu_to_string(-1.0 / 0.0), content="-Infinity") + inspect(@ryu.ryu_to_string(3.1415926), content="3.1415926") + inspect(@ryu.ryu_to_string(1234000000000000.0), content="1234000000000000") } ///| @@ -48,24 +48,54 @@ test "Min and Max" { ///| test "Lots of Trailing Zeros" { - assert_eq(@ryu.ryu_to_string(2.98023223876953125e-8), "2.9802322387695312e-8") + inspect( + @ryu.ryu_to_string(2.98023223876953125e-8), + content="2.9802322387695312e-8", + ) } ///| test "Regression" { - assert_eq(@ryu.ryu_to_string(-2.109808898695963e16), "-21098088986959630") - assert_eq(@ryu.ryu_to_string(4.940656e-318), "4.940656e-318") - assert_eq(@ryu.ryu_to_string(1.18575755e-316), "1.18575755e-316") - assert_eq(@ryu.ryu_to_string(2.989102097996e-312), "2.989102097996e-312") - assert_eq(@ryu.ryu_to_string(9.0608011534336e15), "9060801153433600") - assert_eq(@ryu.ryu_to_string(4.708356024711512e18), "4708356024711512000") - assert_eq(@ryu.ryu_to_string(9.409340012568248e18), "9409340012568248000") - assert_eq(@ryu.ryu_to_string(1.2345678), "1.2345678") - assert_eq(@ryu.ryu_to_string(1.8531501765868567e21), "1.8531501765868567e+21") - assert_eq(@ryu.ryu_to_string(-3.347727380279489e33), "-3.347727380279489e+33") - assert_eq(@ryu.ryu_to_string(1.9430376160308388e16), "19430376160308388") - assert_eq(@ryu.ryu_to_string(-6.9741824662760956e19), "-69741824662760956000") - assert_eq(@ryu.ryu_to_string(4.3816050601147837e18), "4381605060114783700") + inspect( + @ryu.ryu_to_string(-2.109808898695963e16), + content="-21098088986959630", + ) + inspect(@ryu.ryu_to_string(4.940656e-318), content="4.940656e-318") + inspect(@ryu.ryu_to_string(1.18575755e-316), content="1.18575755e-316") + inspect( + @ryu.ryu_to_string(2.989102097996e-312), + content="2.989102097996e-312", + ) + inspect(@ryu.ryu_to_string(9.0608011534336e15), content="9060801153433600") + inspect( + @ryu.ryu_to_string(4.708356024711512e18), + content="4708356024711512000", + ) + inspect( + @ryu.ryu_to_string(9.409340012568248e18), + content="9409340012568248000", + ) + inspect(@ryu.ryu_to_string(1.2345678), content="1.2345678") + inspect( + @ryu.ryu_to_string(1.8531501765868567e21), + content="1.8531501765868567e+21", + ) + inspect( + @ryu.ryu_to_string(-3.347727380279489e33), + content="-3.347727380279489e+33", + ) + inspect( + @ryu.ryu_to_string(1.9430376160308388e16), + content="19430376160308388", + ) + inspect( + @ryu.ryu_to_string(-6.9741824662760956e19), + content="-69741824662760956000", + ) + inspect( + @ryu.ryu_to_string(4.3816050601147837e18), + content="4381605060114783700", + ) } ///| @@ -86,32 +116,32 @@ test "Looks Like Pow5" { ///| test "Output Length" { - assert_eq(@ryu.ryu_to_string(1.0), "1") - assert_eq(@ryu.ryu_to_string(1.2), "1.2") - assert_eq(@ryu.ryu_to_string(1.23), "1.23") - assert_eq(@ryu.ryu_to_string(1.234), "1.234") - assert_eq(@ryu.ryu_to_string(1.2345), "1.2345") - assert_eq(@ryu.ryu_to_string(1.23456), "1.23456") - assert_eq(@ryu.ryu_to_string(1.234567), "1.234567") - assert_eq(@ryu.ryu_to_string(1.2345678), "1.2345678") - assert_eq(@ryu.ryu_to_string(1.23456789), "1.23456789") - assert_eq(@ryu.ryu_to_string(1.234567895), "1.234567895") - assert_eq(@ryu.ryu_to_string(1.2345678901), "1.2345678901") - assert_eq(@ryu.ryu_to_string(1.23456789012), "1.23456789012") - assert_eq(@ryu.ryu_to_string(1.234567890123), "1.234567890123") - assert_eq(@ryu.ryu_to_string(1.2345678901234), "1.2345678901234") - assert_eq(@ryu.ryu_to_string(1.23456789012345), "1.23456789012345") - assert_eq(@ryu.ryu_to_string(1.234567890123456), "1.234567890123456") - assert_eq(@ryu.ryu_to_string(1.2345678901234567), "1.2345678901234567") + inspect(@ryu.ryu_to_string(1.0), content="1") + inspect(@ryu.ryu_to_string(1.2), content="1.2") + inspect(@ryu.ryu_to_string(1.23), content="1.23") + inspect(@ryu.ryu_to_string(1.234), content="1.234") + inspect(@ryu.ryu_to_string(1.2345), content="1.2345") + inspect(@ryu.ryu_to_string(1.23456), content="1.23456") + inspect(@ryu.ryu_to_string(1.234567), content="1.234567") + inspect(@ryu.ryu_to_string(1.2345678), content="1.2345678") + inspect(@ryu.ryu_to_string(1.23456789), content="1.23456789") + inspect(@ryu.ryu_to_string(1.234567895), content="1.234567895") + inspect(@ryu.ryu_to_string(1.2345678901), content="1.2345678901") + inspect(@ryu.ryu_to_string(1.23456789012), content="1.23456789012") + inspect(@ryu.ryu_to_string(1.234567890123), content="1.234567890123") + inspect(@ryu.ryu_to_string(1.2345678901234), content="1.2345678901234") + inspect(@ryu.ryu_to_string(1.23456789012345), content="1.23456789012345") + inspect(@ryu.ryu_to_string(1.234567890123456), content="1.234567890123456") + inspect(@ryu.ryu_to_string(1.2345678901234567), content="1.2345678901234567") } ///| test "32-bit chunking" { - assert_eq(@ryu.ryu_to_string(4.294967294), "4.294967294") - assert_eq(@ryu.ryu_to_string(4.294967295), "4.294967295") - assert_eq(@ryu.ryu_to_string(4.294967296), "4.294967296") - assert_eq(@ryu.ryu_to_string(4.294967297), "4.294967297") - assert_eq(@ryu.ryu_to_string(4.294967298), "4.294967298") + inspect(@ryu.ryu_to_string(4.294967294), content="4.294967294") + inspect(@ryu.ryu_to_string(4.294967295), content="4.294967295") + inspect(@ryu.ryu_to_string(4.294967296), content="4.294967296") + inspect(@ryu.ryu_to_string(4.294967297), content="4.294967297") + inspect(@ryu.ryu_to_string(4.294967298), content="4.294967298") } ///| @@ -157,137 +187,141 @@ test "Min Max Shift" { ///| test "Small Integers" { // 2^53-1 - assert_eq(@ryu.ryu_to_string(9007199254740991.0), "9007199254740991") + inspect(@ryu.ryu_to_string(9007199254740991.0), content="9007199254740991") // 2^53 - assert_eq(@ryu.ryu_to_string(9007199254740992.0), "9007199254740992") - assert_eq(@ryu.ryu_to_string(1.0e+0), "1") - assert_eq(@ryu.ryu_to_string(1.2e+1), "12") - assert_eq(@ryu.ryu_to_string(1.23e+2), "123") - assert_eq(@ryu.ryu_to_string(1.234e+3), "1234") - assert_eq(@ryu.ryu_to_string(1.2345e+4), "12345") - assert_eq(@ryu.ryu_to_string(1.23456e+5), "123456") - assert_eq(@ryu.ryu_to_string(1.234567e+6), "1234567") - assert_eq(@ryu.ryu_to_string(1.2345678e+7), "12345678") - assert_eq(@ryu.ryu_to_string(1.23456789e+8), "123456789") - assert_eq(@ryu.ryu_to_string(1.23456789e+9), "1234567890") - assert_eq(@ryu.ryu_to_string(1.234567895e+9), "1234567895") - assert_eq(@ryu.ryu_to_string(1.2345678901e+10), "12345678901") - assert_eq(@ryu.ryu_to_string(1.23456789012e+11), "123456789012") - assert_eq(@ryu.ryu_to_string(1.234567890123e+12), "1234567890123") - assert_eq(@ryu.ryu_to_string(1.2345678901234e+13), "12345678901234") - assert_eq(@ryu.ryu_to_string(1.23456789012345e+14), "123456789012345") - assert_eq(@ryu.ryu_to_string(1.234567890123456e+15), "1234567890123456") + inspect(@ryu.ryu_to_string(9007199254740992.0), content="9007199254740992") + inspect(@ryu.ryu_to_string(1.0e+0), content="1") + inspect(@ryu.ryu_to_string(1.2e+1), content="12") + inspect(@ryu.ryu_to_string(1.23e+2), content="123") + inspect(@ryu.ryu_to_string(1.234e+3), content="1234") + inspect(@ryu.ryu_to_string(1.2345e+4), content="12345") + inspect(@ryu.ryu_to_string(1.23456e+5), content="123456") + inspect(@ryu.ryu_to_string(1.234567e+6), content="1234567") + inspect(@ryu.ryu_to_string(1.2345678e+7), content="12345678") + inspect(@ryu.ryu_to_string(1.23456789e+8), content="123456789") + inspect(@ryu.ryu_to_string(1.23456789e+9), content="1234567890") + inspect(@ryu.ryu_to_string(1.234567895e+9), content="1234567895") + inspect(@ryu.ryu_to_string(1.2345678901e+10), content="12345678901") + inspect(@ryu.ryu_to_string(1.23456789012e+11), content="123456789012") + inspect(@ryu.ryu_to_string(1.234567890123e+12), content="1234567890123") + inspect(@ryu.ryu_to_string(1.2345678901234e+13), content="12345678901234") + inspect(@ryu.ryu_to_string(1.23456789012345e+14), content="123456789012345") + inspect(@ryu.ryu_to_string(1.234567890123456e+15), content="1234567890123456") } ///| test "10^i" { - assert_eq(@ryu.ryu_to_string(1.0e-15), "1e-15") - assert_eq(@ryu.ryu_to_string(1.0e-14), "1e-14") - assert_eq(@ryu.ryu_to_string(1.0e-13), "1e-13") - assert_eq(@ryu.ryu_to_string(1.0e-12), "1e-12") - assert_eq(@ryu.ryu_to_string(1.0e-11), "1e-11") - assert_eq(@ryu.ryu_to_string(1.0e-10), "1e-10") - assert_eq(@ryu.ryu_to_string(1.0e-9), "1e-9") - assert_eq(@ryu.ryu_to_string(1.0e-8), "1e-8") - assert_eq(@ryu.ryu_to_string(1.0e-7), "1e-7") - assert_eq(@ryu.ryu_to_string(1.0e-6), "0.000001") - assert_eq(@ryu.ryu_to_string(1.0e-5), "0.00001") - assert_eq(@ryu.ryu_to_string(1.0e-4), "0.0001") - assert_eq(@ryu.ryu_to_string(1.0e-3), "0.001") - assert_eq(@ryu.ryu_to_string(1.0e-2), "0.01") - assert_eq(@ryu.ryu_to_string(1.0e-1), "0.1") - assert_eq(@ryu.ryu_to_string(1.0e+0), "1") - assert_eq(@ryu.ryu_to_string(1.0e+1), "10") - assert_eq(@ryu.ryu_to_string(1.0e+2), "100") - assert_eq(@ryu.ryu_to_string(1.0e+3), "1000") - assert_eq(@ryu.ryu_to_string(1.0e+4), "10000") - assert_eq(@ryu.ryu_to_string(1.0e+5), "100000") - assert_eq(@ryu.ryu_to_string(1.0e+6), "1000000") - assert_eq(@ryu.ryu_to_string(1.0e+7), "10000000") - assert_eq(@ryu.ryu_to_string(1.0e+8), "100000000") - assert_eq(@ryu.ryu_to_string(1.0e+9), "1000000000") - assert_eq(@ryu.ryu_to_string(1.0e+10), "10000000000") - assert_eq(@ryu.ryu_to_string(1.0e+11), "100000000000") - assert_eq(@ryu.ryu_to_string(1.0e+12), "1000000000000") - assert_eq(@ryu.ryu_to_string(1.0e+13), "10000000000000") - assert_eq(@ryu.ryu_to_string(1.0e+14), "100000000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15), "1000000000000000") + inspect(@ryu.ryu_to_string(1.0e-15), content="1e-15") + inspect(@ryu.ryu_to_string(1.0e-14), content="1e-14") + inspect(@ryu.ryu_to_string(1.0e-13), content="1e-13") + inspect(@ryu.ryu_to_string(1.0e-12), content="1e-12") + inspect(@ryu.ryu_to_string(1.0e-11), content="1e-11") + inspect(@ryu.ryu_to_string(1.0e-10), content="1e-10") + inspect(@ryu.ryu_to_string(1.0e-9), content="1e-9") + inspect(@ryu.ryu_to_string(1.0e-8), content="1e-8") + inspect(@ryu.ryu_to_string(1.0e-7), content="1e-7") + inspect(@ryu.ryu_to_string(1.0e-6), content="0.000001") + inspect(@ryu.ryu_to_string(1.0e-5), content="0.00001") + inspect(@ryu.ryu_to_string(1.0e-4), content="0.0001") + inspect(@ryu.ryu_to_string(1.0e-3), content="0.001") + inspect(@ryu.ryu_to_string(1.0e-2), content="0.01") + inspect(@ryu.ryu_to_string(1.0e-1), content="0.1") + inspect(@ryu.ryu_to_string(1.0e+0), content="1") + inspect(@ryu.ryu_to_string(1.0e+1), content="10") + inspect(@ryu.ryu_to_string(1.0e+2), content="100") + inspect(@ryu.ryu_to_string(1.0e+3), content="1000") + inspect(@ryu.ryu_to_string(1.0e+4), content="10000") + inspect(@ryu.ryu_to_string(1.0e+5), content="100000") + inspect(@ryu.ryu_to_string(1.0e+6), content="1000000") + inspect(@ryu.ryu_to_string(1.0e+7), content="10000000") + inspect(@ryu.ryu_to_string(1.0e+8), content="100000000") + inspect(@ryu.ryu_to_string(1.0e+9), content="1000000000") + inspect(@ryu.ryu_to_string(1.0e+10), content="10000000000") + inspect(@ryu.ryu_to_string(1.0e+11), content="100000000000") + inspect(@ryu.ryu_to_string(1.0e+12), content="1000000000000") + inspect(@ryu.ryu_to_string(1.0e+13), content="10000000000000") + inspect(@ryu.ryu_to_string(1.0e+14), content="100000000000000") + inspect(@ryu.ryu_to_string(1.0e+15), content="1000000000000000") } ///| test "10^15 + 10^i" { - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+0), "1000000000000001") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+1), "1000000000000010") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+2), "1000000000000100") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+3), "1000000000001000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+4), "1000000000010000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+5), "1000000000100000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+6), "1000000001000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+7), "1000000010000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+8), "1000000100000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+9), "1000001000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+10), "1000010000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+11), "1000100000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+12), "1001000000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+13), "1010000000000000") - assert_eq(@ryu.ryu_to_string(1.0e+15 + 1.0e+14), "1100000000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+0), content="1000000000000001") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+1), content="1000000000000010") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+2), content="1000000000000100") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+3), content="1000000000001000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+4), content="1000000000010000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+5), content="1000000000100000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+6), content="1000000001000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+7), content="1000000010000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+8), content="1000000100000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+9), content="1000001000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+10), content="1000010000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+11), content="1000100000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+12), content="1001000000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+13), content="1010000000000000") + inspect(@ryu.ryu_to_string(1.0e+15 + 1.0e+14), content="1100000000000000") } ///| test "Largest power of 2 <= 10^(i+1)" { - assert_eq(@ryu.ryu_to_string(8.0), "8") - assert_eq(@ryu.ryu_to_string(64.0), "64") - assert_eq(@ryu.ryu_to_string(512.0), "512") - assert_eq(@ryu.ryu_to_string(8192.0), "8192") - assert_eq(@ryu.ryu_to_string(65536.0), "65536") - assert_eq(@ryu.ryu_to_string(524288.0), "524288") - assert_eq(@ryu.ryu_to_string(8388608.0), "8388608") - assert_eq(@ryu.ryu_to_string(67108864.0), "67108864") - assert_eq(@ryu.ryu_to_string(536870912.0), "536870912") - assert_eq(@ryu.ryu_to_string(8589934592.0), "8589934592") - assert_eq(@ryu.ryu_to_string(68719476736.0), "68719476736") - assert_eq(@ryu.ryu_to_string(549755813888.0), "549755813888") - assert_eq(@ryu.ryu_to_string(8796093022208.0), "8796093022208") - assert_eq(@ryu.ryu_to_string(70368744177664.0), "70368744177664") - assert_eq(@ryu.ryu_to_string(562949953421312.0), "562949953421312") - assert_eq(@ryu.ryu_to_string(9007199254740992.0), "9007199254740992") + inspect(@ryu.ryu_to_string(8.0), content="8") + inspect(@ryu.ryu_to_string(64.0), content="64") + inspect(@ryu.ryu_to_string(512.0), content="512") + inspect(@ryu.ryu_to_string(8192.0), content="8192") + inspect(@ryu.ryu_to_string(65536.0), content="65536") + inspect(@ryu.ryu_to_string(524288.0), content="524288") + inspect(@ryu.ryu_to_string(8388608.0), content="8388608") + inspect(@ryu.ryu_to_string(67108864.0), content="67108864") + inspect(@ryu.ryu_to_string(536870912.0), content="536870912") + inspect(@ryu.ryu_to_string(8589934592.0), content="8589934592") + inspect(@ryu.ryu_to_string(68719476736.0), content="68719476736") + inspect(@ryu.ryu_to_string(549755813888.0), content="549755813888") + inspect(@ryu.ryu_to_string(8796093022208.0), content="8796093022208") + inspect(@ryu.ryu_to_string(70368744177664.0), content="70368744177664") + inspect(@ryu.ryu_to_string(562949953421312.0), content="562949953421312") + inspect(@ryu.ryu_to_string(9007199254740992.0), content="9007199254740992") } ///| test "1000 * (Largest power of 2 <= 10^(i+1))" { - assert_eq(@ryu.ryu_to_string(8.0e+3), "8000") - assert_eq(@ryu.ryu_to_string(64.0e+3), "64000") - assert_eq(@ryu.ryu_to_string(512.0e+3), "512000") - assert_eq(@ryu.ryu_to_string(8192.0e+3), "8192000") - assert_eq(@ryu.ryu_to_string(65536.0e+3), "65536000") - assert_eq(@ryu.ryu_to_string(524288.0e+3), "524288000") - assert_eq(@ryu.ryu_to_string(8388608.0e+3), "8388608000") - assert_eq(@ryu.ryu_to_string(67108864.0e+3), "67108864000") - assert_eq(@ryu.ryu_to_string(536870912.0e+3), "536870912000") - assert_eq(@ryu.ryu_to_string(8589934592.0e+3), "8589934592000") - assert_eq(@ryu.ryu_to_string(68719476736.0e+3), "68719476736000") - assert_eq(@ryu.ryu_to_string(549755813888.0e+3), "549755813888000") - assert_eq(@ryu.ryu_to_string(8796093022208.0e+3), "8796093022208000") + inspect(@ryu.ryu_to_string(8.0e+3), content="8000") + inspect(@ryu.ryu_to_string(64.0e+3), content="64000") + inspect(@ryu.ryu_to_string(512.0e+3), content="512000") + inspect(@ryu.ryu_to_string(8192.0e+3), content="8192000") + inspect(@ryu.ryu_to_string(65536.0e+3), content="65536000") + inspect(@ryu.ryu_to_string(524288.0e+3), content="524288000") + inspect(@ryu.ryu_to_string(8388608.0e+3), content="8388608000") + inspect(@ryu.ryu_to_string(67108864.0e+3), content="67108864000") + inspect(@ryu.ryu_to_string(536870912.0e+3), content="536870912000") + inspect(@ryu.ryu_to_string(8589934592.0e+3), content="8589934592000") + inspect(@ryu.ryu_to_string(68719476736.0e+3), content="68719476736000") + inspect(@ryu.ryu_to_string(549755813888.0e+3), content="549755813888000") + inspect(@ryu.ryu_to_string(8796093022208.0e+3), content="8796093022208000") } ///| test "boundary conditions" { // x = 1.0e7 - assert_eq(@ryu.ryu_to_string(1.0e7), "10000000") + inspect(@ryu.ryu_to_string(1.0e7), content="10000000") // x < 1.0e7 - assert_eq(@ryu.ryu_to_string(9999999.999999998), "9999999.999999998") + inspect(@ryu.ryu_to_string(9999999.999999998), content="9999999.999999998") // x = 1.0e-3 - assert_eq(@ryu.ryu_to_string(0.001), "0.001") + inspect(@ryu.ryu_to_string(0.001), content="0.001") // x < 1.0e-3 - assert_eq(@ryu.ryu_to_string(0.0009999999999999998), "0.0009999999999999998") + inspect( + @ryu.ryu_to_string(0.0009999999999999998), + content="0.0009999999999999998", + ) } -///| // For testing purposes + +///| fn ieee_parts_to_double( sign : Bool, ieeeExponent : Int, - ieeeMantissa : Int64 + ieeeMantissa : Int64, ) -> Double { ((sign.to_int64() << 63) | (ieeeExponent.to_int64() << 52) | ieeeMantissa).reinterpret_as_double() } @@ -312,8 +346,8 @@ test "double/ryu.mbt:403" { } else { vp = vp - 1L } - assert_eq(vrIsTrailingZeros, true) - assert_eq(vmIsTrailingZeros, true) + inspect(vrIsTrailingZeros, content="true") + inspect(vmIsTrailingZeros, content="true") assert_eq(vp, 10L) } @@ -337,7 +371,7 @@ test "double/ryu.mbt:403" { } else { vp = vp - 1L } - assert_eq(vrIsTrailingZeros, true) - assert_eq(vmIsTrailingZeros, true) + inspect(vrIsTrailingZeros, content="true") + inspect(vmIsTrailingZeros, content="true") assert_eq(vp, 10L) } diff --git a/bundled-core/double/log_js.mbt b/bundled-core/double/log_js.mbt index 9521fb5..4e36a8a 100644 --- a/bundled-core/double/log_js.mbt +++ b/bundled-core/double/log_js.mbt @@ -11,23 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `@math.ln` instead") -#converage.skip -pub fn Double::ln(self : Double) -> Double = "Math" "log" - -///| -#deprecated("use `@math.log2` instead") -#converage.skip -pub fn Double::log2(self : Double) -> Double = "Math" "log2" - -///| -#deprecated("use `@math.log10` instead") -#converage.skip -pub fn Double::log10(self : Double) -> Double = "Math" "log10" - -///| -#deprecated("use `@math.ln_1p` instead") -#converage.skip -pub fn Double::ln_1p(self : Double) -> Double = "Math" "log1p" diff --git a/bundled-core/double/log_nonjs.mbt b/bundled-core/double/log_nonjs.mbt index 63f1fe9..4e36a8a 100644 --- a/bundled-core/double/log_nonjs.mbt +++ b/bundled-core/double/log_nonjs.mbt @@ -11,232 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -// ported from https://github.com/golang/go/blob/master/src/math/log.go - -// -// origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c -// ==================================================== -// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. -// -// Developed at SunSoft, a Sun Microsystems, Inc. business. -// Permission to use, copy, modify, and distribute this -// software is freely granted, provided that this notice -// is preserved. -// ==================================================== -// - -///| -let sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 - -///| -let ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 - -///| -let ln2_hi = 6.93147180369123816490e-01 // 3fe62e42 fee00000 - -///| -let ln2_lo = 1.90821492927058770002e-10 // 3dea39ef 35793c76 - -///| -let l1 = 6.666666666666735130e-01 // 3FE55555 55555593 - -///| -let l2 = 3.999999999940941908e-01 // 3FD99999 9997FA04 - -///| -let l3 = 2.857142874366239149e-01 // 3FD24924 94229359 - -///| -let l4 = 2.222219843214978396e-01 // 3FCC71C5 1D8E78AF - -///| -let l5 = 1.818357216161805012e-01 // 3FC74664 96CB03DE - -///| -let l6 = 1.531383769920937332e-01 // 3FC39A09 D078C69F - -///| -let l7 = 1.479819860511658591e-01 // 3FC2F112 DF3E5244 - -///| -fn normalize(f : Double) -> (Double, Int) { - if f.abs() < min_positive { - return (f * (1L << 52).to_double(), -52) - } - (f, 0) -} - -///| -fn frexp(f : Double) -> (Double, Int) { - if f == 0.0 || f.is_inf() || f.is_nan() { - return (f, 0) - } - let (norm_f, exp) = normalize(f) - let u = norm_f.reinterpret_as_uint64() - let exp = exp + ((u >> 52) & 0x7FF).to_int() - 1022 - let frac = ((u & (0x7FFUL << 52).lnot()) | (1022UL << 52)).reinterpret_as_double() - return (frac, exp) -} - -///| -#deprecated("use `@math.ln` instead") -#coverage.skip -pub fn Double::ln(self : Double) -> Double { - if self < 0.0 { - return not_a_number - } else if self.is_nan() || self.is_inf() { - return self - } else if self == 0.0 { - return neg_infinity - } - let (f1, ki) = frexp(self) - let (f, k) = if f1 < sqrt2 / 2.0 { - (f1 * 2.0 - 1.0, (ki - 1).to_double()) - } else { - (f1 - 1.0, ki.to_double()) - } - let s = f / (2.0 + f) - let s2 = s * s - let s4 = s2 * s2 - let t1 = s2 * (l1 + s4 * (l3 + s4 * (l5 + s4 * l7))) - let t2 = s4 * (l2 + s4 * (l4 + s4 * l6)) - let r = t1 + t2 - let hfsq = 0.5 * f * f - k * ln2_hi - (hfsq - (s * (hfsq + r) + k * ln2_lo) - f) -} - -///| -#deprecated("use `@math.log2` instead") -#coverage.skip -pub fn Double::log2(self : Double) -> Double { - let (f, e) = frexp(self) - if f == 0.5 { - return e.to_double() - 1.0 - } - ln(f) / ln2 + e.to_double() -} - -///| -#deprecated("use `@math.log10` instead") -#coverage.skip -pub fn Double::log10(self : Double) -> Double { - if self < 0.0 { - return not_a_number - } else if self.is_nan() || self.is_inf() { - return self - } else if self == 0.0 { - return neg_infinity - } - let ivln10 = 4.34294481903251816668e-01 - let log10_2hi = 3.01029995663611771306e-01 - let log10_2lo = 3.69423907715893078616e-13 - let (f, e) = frexp(self) - let (f, e) = if e >= 1 { - (f * 2.0, (e - 1).to_double()) - } else { - (f, e.to_double()) - } - let z = e * log10_2lo + ivln10 * f.ln() - z + e * log10_2hi -} - -///| -#deprecated("use `@math.ln_1p` instead") -#coverage.skip -pub fn Double::ln_1p(self : Double) -> Double { - if self < -1.0 || self.is_nan() { - return not_a_number - } - if self == -1.0 { - return neg_infinity - } - if self.is_inf() { - return infinity - } - let ln2_hi = 6.93147180369123816490e-01 - let ln2_lo = 1.90821492927058770002e-10 - let two54 = 1.80143985094819840000e+16 - let lp1 = 6.666666666666735130e-01 - let lp2 = 3.999999999940941908e-01 - let lp3 = 2.857142874366239149e-01 - let lp4 = 2.222219843214978396e-01 - let lp5 = 1.818357216161805012e-01 - let lp6 = 1.531383769920937332e-01 - let zero = 0.0 - let lp7 = 1.479819860511658591e-01 - let hx = get_high_word(self).reinterpret_as_int() - let ax = hx & 0x7fffffff - let mut f = 0.0 - let mut c = 0.0 - let mut s = 0.0 - let mut z = 0.0 - let mut r = 0.0 - let mut u = 0.0 - let mut hu = 0 - let mut k = 1 - if hx < 0x3FDA827A { - if ax < 0x3e200000 { - if two54 + self > zero && ax < 0x3c900000 { - return self - } else { - return self - self * self * 0.5 - } - } - if hx > 0 || hx <= 0xbfd2bec3 { - k = 0 - f = self - hu = 1 - } - } - if k != 0 { - if hx < 0x43400000 { - u = 1.0 + self - hu = get_high_word(u).reinterpret_as_int() - k = (hu >> 20) - 1023 - c = if k > 0 { 1.0 - (u - self) } else { self - (u - 1.0) } - c /= u - } else { - u = self - hu = get_high_word(u).reinterpret_as_int() - k = (hu >> 20) - 1023 - c = 0.0 - } - hu = hu & 0x000fffff - if hu < 0x6a09e { - u = set_high_word(u, hu.reinterpret_as_uint() | 0x3ff00000) - } else { - k += 1 - u = set_high_word(u, hu.reinterpret_as_uint() | 0x3fe00000) - hu = (0x00100000 - hu) >> 2 - } - f = u - 1.0 - } - let hfsq = 0.5 * f * f - if hu == 0 { - if f == zero { - if k == 0 { - return zero - } else { - c += k.to_double() * ln2_lo - return k.to_double() * ln2_hi + c - } - } - r = hfsq * (1.0 - 0.66666666666666666 * f) - if k == 0 { - return f - r - } else { - return k.to_double() * ln2_hi - (r - (k.to_double() * ln2_lo + c) - f) - } - } - s = f / (2.0 + f) - z = s * s - r = z * - (lp1 + z * (lp2 + z * (lp3 + z * (lp4 + z * (lp5 + z * (lp6 + z * lp7)))))) - if k == 0 { - return f - (hfsq - s * (hfsq + r)) - } else { - return k.to_double() * ln2_hi - - (hfsq - (s * (hfsq + r) + (k.to_double() * ln2_lo + c)) - f) - } -} diff --git a/bundled-core/double/math_functions.mbt b/bundled-core/double/math_functions.mbt index 27d0b02..4e36a8a 100644 --- a/bundled-core/double/math_functions.mbt +++ b/bundled-core/double/math_functions.mbt @@ -11,35 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -pub fnalias Double::( - sin, - cos, - tan, - asin, - acos, - atan, - atan2, - exp, - expm1, - pow, - ln, - log2, - log10, - ln_1p, - sinh, - cosh, - tanh, - asinh, - acosh, - atanh, - hypot, - cbrt, - is_close, - trunc, - floor, - ceil, - round, - abs -) diff --git a/bundled-core/double/mod_js.mbt b/bundled-core/double/mod_js.mbt index bfd2442..38219d9 100644 --- a/bundled-core/double/mod_js.mbt +++ b/bundled-core/double/mod_js.mbt @@ -31,13 +31,13 @@ /// ```moonbit /// inspect(5.0.op_mod(3.0), content="2") /// inspect((-5.0).op_mod(3.0), content="-2") -/// inspect(5.0.op_mod(not_a_number), content="NaN") -/// inspect(infinity.op_mod(3.0), content="NaN") +/// inspect(5.0.op_mod(@double.not_a_number), content="NaN") +/// inspect(@double.infinity.op_mod(3.0), content="NaN") /// ``` extern "js" fn mod_ffi(self : Double, other : Double) -> Double = #| (a, b) => (a % b) ///| -pub impl Mod for Double with op_mod(self, other) { +pub impl Mod for Double with mod(self, other) { self.mod_ffi(other) } diff --git a/bundled-core/double/mod_nonjs.mbt b/bundled-core/double/mod_nonjs.mbt index b2f2ff0..c0e7ba3 100644 --- a/bundled-core/double/mod_nonjs.mbt +++ b/bundled-core/double/mod_nonjs.mbt @@ -31,10 +31,10 @@ /// ```moonbit /// inspect(5.0.op_mod(3.0), content="2") /// inspect((-5.0).op_mod(3.0), content="-2") -/// inspect(5.0.op_mod(not_a_number), content="NaN") -/// inspect(infinity.op_mod(3.0), content="NaN") +/// inspect(5.0.op_mod(@double.not_a_number), content="NaN") +/// inspect(@double.infinity.op_mod(3.0), content="NaN") /// ``` -pub impl Mod for Double with op_mod(self : Double, other : Double) -> Double { +pub impl Mod for Double with mod(self : Double, other : Double) -> Double { let x = self let y = other let mut uint64_x = x.reinterpret_as_uint64() diff --git a/bundled-core/double/moon.pkg.json b/bundled-core/double/moon.pkg.json index 17ab64d..a72fdce 100644 --- a/bundled-core/double/moon.pkg.json +++ b/bundled-core/double/moon.pkg.json @@ -28,10 +28,5 @@ "hypot_js.mbt" : ["js"], "hypot_nonjs.mbt" : ["not", "js"], "scalbn.mbt" : ["not", "js"] - }, - "test-import": [ - "moonbitlang/core/test", - "moonbitlang/core/bench" - ], - "warn-list": "-29" + } } diff --git a/bundled-core/double/pkg.generated.mbti b/bundled-core/double/pkg.generated.mbti new file mode 100644 index 0000000..ae0ef08 --- /dev/null +++ b/bundled-core/double/pkg.generated.mbti @@ -0,0 +1,59 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/double" + +// Values +fn from_int(Int) -> Double + +let infinity : Double + +let max_value : Double + +let min_positive : Double + +let min_value : Double + +let neg_infinity : Double + +let not_a_number : Double + +// Errors + +// Types and methods +#as_free_fn +fn Double::abs(Double) -> Double +#as_free_fn +fn Double::ceil(Double) -> Double +#as_free_fn +fn Double::floor(Double) -> Double +fn Double::from_int(Int) -> Double +#deprecated +fn Double::inf(Int) -> Double +#as_free_fn +fn Double::is_close(Double, Double, relative_tolerance? : Double, absolute_tolerance? : Double) -> Bool +fn Double::is_inf(Double) -> Bool +fn Double::is_nan(Double) -> Bool +fn Double::is_neg_inf(Double) -> Bool +fn Double::is_pos_inf(Double) -> Bool +#deprecated +fn Double::min_normal() -> Double +#deprecated +fn Double::nan() -> Double +#as_free_fn +fn Double::pow(Double, Double) -> Double +#as_free_fn +fn Double::round(Double) -> Double +fn Double::signum(Double) -> Double +fn Double::to_be_bytes(Double) -> Bytes +fn Double::to_le_bytes(Double) -> Bytes +fn Double::to_string(Double) -> String +fn Double::to_uint(Double) -> UInt +#as_free_fn +fn Double::trunc(Double) -> Double +impl Hash for Double +impl Mod for Double +impl Show for Double + +// Type aliases + +// Traits + diff --git a/bundled-core/double/pow_js.mbt b/bundled-core/double/pow_js.mbt index 52cdf3b..797ab8b 100644 --- a/bundled-core/double/pow_js.mbt +++ b/bundled-core/double/pow_js.mbt @@ -33,6 +33,7 @@ /// inspect(x.pow(0.0), content="1") /// inspect((-1.0).pow(2.0), content="1") /// inspect(0.0.pow(0.0), content="1") -/// inspect(infinity.pow(-1.0), content="0") +/// inspect(@double.infinity.pow(-1.0), content="0") /// ``` +#as_free_fn pub fn Double::pow(self : Double, other : Double) -> Double = "Math" "pow" diff --git a/bundled-core/double/pow_nonjs.mbt b/bundled-core/double/pow_nonjs.mbt index a301a8c..5b22bab 100644 --- a/bundled-core/double/pow_nonjs.mbt +++ b/bundled-core/double/pow_nonjs.mbt @@ -128,8 +128,9 @@ const POW_ivln2_l = 1.92596299112661746887e-08 /// inspect(x.pow(0.0), content="1") /// inspect((-1.0).pow(2.0), content="1") /// inspect(0.0.pow(0.0), content="1") -/// inspect(infinity.pow(-1.0), content="0") +/// inspect(@double.infinity.pow(-1.0), content="0") /// ``` +#as_free_fn pub fn Double::pow(self : Double, other : Double) -> Double { fn set_low_word(d : Double, v : UInt) -> Double { let bits : UInt64 = d.reinterpret_as_uint64() diff --git a/bundled-core/double/pow_test.mbt b/bundled-core/double/pow_test.mbt index aa21ca4..e34c9de 100644 --- a/bundled-core/double/pow_test.mbt +++ b/bundled-core/double/pow_test.mbt @@ -13,52 +13,963 @@ // limitations under the License. ///| -test "pow" (it : @test.T) { - let value = [ - 0.0, - -0.0, - 0x0000000000000001UL.reinterpret_as_double(), - -0x0000000000000001UL.reinterpret_as_double(), - 0x0000000000000002UL.reinterpret_as_double(), - -0x0000000000000002UL.reinterpret_as_double(), - 0x0000000000000003UL.reinterpret_as_double(), - -0x0000000000000003UL.reinterpret_as_double(), - 1.0e-300, - -1.0e-300, - 1.0e-10, - -1.0e-10, - 0.5, - -0.5, - 1.0, - -1.0, - 1.224744871, - -1.224744871, - 1.732, - -1.732, - 2.0, - -2.0, - 2.718281828459045, - -2.718281828459045, - 1024.0, - -1024.0, - 1075.0, - -1075.0, - 2048.0, - -2048.0, - 2147483647.0, - -2147483647.0, - 1.0e10, - -1.0e10, - 1.0e300, - -1.0e300, - @double.infinity, - @double.neg_infinity, - @double.not_a_number, +test "pow" { + let test_cases = [ + (1.1, 1.1, 1.1105342410545758), + (1.1, 2.2, 1.2332863005546628), + (1.1, 3.3, 1.3696066657894779), + (1.1, 4.4, 1.5209950991358059), + (1.1, 5.5, 1.6891171380665115), + (1.1, 1.0e-5, 1.0000009531022522), + (1.1, 2.1e-6, 1.0000002001513977), + (1.1, 3.2e-7, 1.000000030499258), + (1.1, 4.3e-8, 1.0000000040983377), + (1.1, 5.4e-9, 1.000000000514675), + (1.1, 1.2e5, @double.infinity), + (1.1, 2.3e6, @double.infinity), + (1.1, 3.4e7, @double.infinity), + (1.1, 4.5e8, @double.infinity), + (1.1, 5.6e9, @double.infinity), + (1.1, -1.3, 0.8834653263771346), + (1.1, -2.4, 0.795531820376998), + (1.1, -3.5, 0.7163505554061548), + (1.1, -4.6, 0.6450503990997164), + (1.1, -5.7, 0.5808469250683971), + (1.1, -1.3e-7, 0.9999999876096767), + (1.1, -2.4e-8, 0.9999999977125557), + (1.1, -3.5e-9, 0.9999999996664144), + (1.1, -4.6e-10, 0.9999999999561573), + (1.1, -5.7e-11, 0.9999999999945673), + (1.1, -1.7e5, 0), + (1.1, -2.8e6, 0), + (1.1, -3.9e7, 0), + (1.1, -4.0e8, 0), + (1.1, -5.1e9, 0), + (1.1, @double.infinity, @double.infinity), + (1.1, @double.not_a_number, @double.not_a_number), + (1.1, @double.neg_infinity, 0), + (2.2, 1.1, 2.3804822576003546), + (2.2, 2.2, 5.66669577875008), + (2.2, 3.3, 13.489468760533386), + (2.2, 4.4, 32.111441048903984), + (2.2, 5.5, 76.44071568289563), + (2.2, 1.0e-5, 1.000007884604687), + (2.2, 2.1e-6, 1.0000016557618276), + (2.2, 3.2e-7, 1.0000002523063871), + (2.2, 4.3e-8, 1.000000033903667), + (2.2, 5.4e-9, 1.0000000042576698), + (2.2, 1.2e5, @double.infinity), + (2.2, 2.3e6, @double.infinity), + (2.2, 3.4e7, @double.infinity), + (2.2, 4.5e8, @double.infinity), + (2.2, 5.6e9, @double.infinity), + (2.2, -1.3, 0.35879841422373565), + (2.2, -2.4, 0.15072509491644878), + (2.2, -3.5, 0.0633170419293052), + (2.2, -4.6, 0.026598409514352772), + (2.2, -5.7, 0.011173538231352035), + (2.2, -1.3e-7, 0.9999998975005484), + (2.2, -2.4e-8, 0.9999999810770235), + (2.2, -3.5e-9, 0.9999999972403992), + (2.2, -4.6e-10, 0.9999999996373096), + (2.2, -5.7e-11, 0.999999999955058), + (2.2, -1.7e5, 0), + (2.2, -2.8e6, 0), + (2.2, -3.9e7, 0), + (2.2, -4.0e8, 0), + (2.2, -5.1e9, 0), + (2.2, @double.infinity, @double.infinity), + (2.2, @double.not_a_number, @double.not_a_number), + (2.2, @double.neg_infinity, 0), + (3.3, 1.1, 3.718479005997499), + (3.3, 2.2, 13.827086118044146), + (3.3, 3.3, 51.415729444066585), + (3.3, 4.4, 191.18831051580915), + (3.3, 5.5, 710.9297188451668), + (3.3, 1.0e-5, 1.0000119392959574), + (3.3, 2.1e-6, 1.000002507240327), + (3.3, 3.2e-7, 1.000000382055263), + (3.3, 4.3e-8, 1.0000000513386675), + (3.3, 5.4e-9, 1.0000000064471815), + (3.3, 1.2e5, @double.infinity), + (3.3, 2.3e6, @double.infinity), + (3.3, 3.4e7, @double.infinity), + (3.3, 4.5e8, @double.infinity), + (3.3, 5.6e9, @double.infinity), + (3.3, -1.3, 0.21180288829407456), + (3.3, -2.4, 0.05695954930832198), + (3.3, -3.5, 0.015317969851773392), + (3.3, -4.6, 0.0041194181349598034), + (3.3, -5.7, 0.0011078234214353857), + (3.3, -1.3e-7, 0.9999998447900912), + (3.3, -2.4e-8, 0.9999999713458612), + (3.3, -3.5e-9, 0.9999999958212714), + (3.3, -4.6e-10, 0.9999999994507957), + (3.3, -5.7e-11, 0.9999999999319464), + (3.3, -1.7e5, 0), + (3.3, -2.8e6, 0), + (3.3, -3.9e7, 0), + (3.3, -4.0e8, 0), + (3.3, -5.1e9, 0), + (3.3, @double.infinity, @double.infinity), + (3.3, @double.not_a_number, @double.not_a_number), + (3.3, @double.neg_infinity, 0), + (4.4, 1.1, 5.102675423469089), + (4.4, 2.2, 26.037296477275444), + (4.4, 3.3, 132.8598728281716), + (4.4, 4.4, 677.9408078455402), + (4.4, 5.5, 3459.3118987602156), + (4.4, 1.0e-5, 1.0000148161551674), + (4.4, 2.1e-6, 1.0000031113743764), + (4.4, 3.2e-7, 1.0000004741135655), + (4.4, 4.3e-8, 1.0000000637089972), + (4.4, 5.4e-9, 1.0000000080006646), + (4.4, 1.2e5, @double.infinity), + (4.4, 2.3e6, @double.infinity), + (4.4, 3.4e7, @double.infinity), + (4.4, 4.5e8, @double.infinity), + (4.4, 5.6e9, @double.infinity), + (4.4, -1.3, 0.14571743588102323), + (4.4, -2.4, 0.0285570654192142), + (4.4, -3.5, 0.005596488714110584), + (4.4, -4.6, 0.0010967753677551722), + (4.4, -5.7, 0.00021494123704413892), + (4.4, -1.3e-7, 0.9999998073914282), + (4.4, -2.4e-8, 0.9999999644414916), + (4.4, -3.5e-9, 0.9999999948143842), + (4.4, -4.6e-10, 0.999999999318462), + (4.4, -5.7e-11, 0.9999999999155486), + (4.4, -1.7e5, 0), + (4.4, -2.8e6, 0), + (4.4, -3.9e7, 0), + (4.4, -4.0e8, 0), + (4.4, -5.1e9, 0), + (4.4, @double.infinity, @double.infinity), + (4.4, @double.not_a_number, @double.not_a_number), + (4.4, @double.neg_infinity, 0), + (5.5, 1.1, 6.522272782452906), + (5.5, 2.2, 42.54004224872598), + (5.5, 3.3, 277.457759723262), + (5.5, 4.4, 1809.655194523391), + (5.5, 5.5, 11803.064820864423), + (5.5, 1.0e-5, 1.0000170476262316), + (5.5, 2.1e-6, 1.0000035799774019), + (5.5, 3.2e-7, 1.0000005455195382), + (5.5, 4.3e-8, 1.0000000733041707), + (5.5, 5.4e-9, 1.0000000092056398), + (5.5, 1.2e5, @double.infinity), + (5.5, 2.3e6, @double.infinity), + (5.5, 3.4e7, @double.infinity), + (5.5, 4.5e8, @double.infinity), + (5.5, 5.6e9, @double.infinity), + (5.5, -1.3, 0.10902560458273548), + (5.5, -2.4, 0.016715891564065034), + (5.5, -3.5, 0.002562893660172627), + (5.5, -4.6, 0.00039294487453325625), + (5.5, -5.7, 0.00006024661764997149), + (5.5, -1.3e-7, 0.9999997783827725), + (5.5, -2.4e-8, 0.9999999590860467), + (5.5, -3.5e-9, 0.9999999940333817), + (5.5, -4.6e-10, 0.9999999992158158), + (5.5, -5.7e-11, 0.9999999999028294), + (5.5, -1.7e5, 0), + (5.5, -2.8e6, 0), + (5.5, -3.9e7, 0), + (5.5, -4.0e8, 0), + (5.5, -5.1e9, 0), + (5.5, @double.infinity, @double.infinity), + (5.5, @double.not_a_number, @double.not_a_number), + (5.5, @double.neg_infinity, 0), + (1.0e-5, 1.1, 0.0000031622776601683762), + (1.0e-5, 2.2, 9.999999999999982e-12), + (1.0e-5, 3.3, 3.162277660168387e-17), + (1.0e-5, 4.4, 9.999999999999963e-23), + (1.0e-5, 5.5, 3.1622776601683807e-28), + (1.0e-5, 1.0e-5, 0.9998848773724686), + (1.0e-5, 2.1e-6, 0.9999758231487883), + (1.0e-5, 3.2e-7, 0.9999963158706376), + (1.0e-5, 4.3e-8, 0.9999995049443275), + (1.0e-5, 5.4e-9, 0.9999999378302045), + (1.0e-5, 1.2e5, 0), + (1.0e-5, 2.3e6, 0), + (1.0e-5, 3.4e7, 0), + (1.0e-5, 4.5e8, 0), + (1.0e-5, 5.6e9, 0), + (1.0e-5, -1.3, 3162277.6601683805), + (1.0e-5, -2.4, 999999999999.9988), + (1.0e-5, -3.5, 316227766016837800), + (1.0e-5, -4.6, 9.999999999999956e+22), + (1.0e-5, -5.7, 3.1622776601683843e+28), + (1.0e-5, -1.3e-7, 1.0000014966814306), + (1.0e-5, -2.4e-8, 1.0000002763102493), + (1.0e-5, -3.5e-9, 1.00000004029524), + (1.0e-5, -4.6e-10, 1.0000000052959457), + (1.0e-5, -5.7e-11, 1.0000000006562368), + (1.0e-5, -1.7e5, @double.infinity), + (1.0e-5, -2.8e6, @double.infinity), + (1.0e-5, -3.9e7, @double.infinity), + (1.0e-5, -4.0e8, @double.infinity), + (1.0e-5, -5.1e9, @double.infinity), + (1.0e-5, @double.infinity, 0), + (1.0e-5, @double.not_a_number, @double.not_a_number), + (1.0e-5, @double.neg_infinity, @double.infinity), + (2.1e-6, 1.1, 5.68121498915637e-7), + (2.1e-6, 2.2, 3.227620375301501e-13), + (2.1e-6, 3.3, 1.83368052554695e-19), + (2.1e-6, 4.4, 1.0417533287061401e-25), + (2.1e-6, 5.5, 5.9184246260489e-32), + (2.1e-6, 1.0e-5, 0.9998692728134111), + (2.1e-6, 2.1e-6, 0.9999725458731237), + (2.1e-6, 3.2e-7, 0.9999958164653228), + (2.1e-6, 4.3e-8, 0.9999994378365098), + (2.1e-6, 5.4e-9, 0.9999999294027071), + (2.1e-6, 1.2e5, 0), + (2.1e-6, 2.3e6, 0), + (2.1e-6, 3.4e7, 0), + (2.1e-6, 4.5e8, 0), + (2.1e-6, 5.6e9, 0), + (2.1e-6, -1.3, 24049990.92567991), + (2.1e-6, -2.4, 42332478125865.01), + (2.1e-6, -3.5, 74513071951447410000), + (2.1e-6, -4.6, 1.3115693050459929e+26), + (2.1e-6, -5.7, 2.3086070630126982e+32), + (2.1e-6, -1.3e-7, 1.000001699565962), + (2.1e-6, -2.4e-8, 1.0000003137658064), + (2.1e-6, -3.5e-9, 1.0000000457575073), + (2.1e-6, -4.6e-10, 1.0000000060138436), + (2.1e-6, -5.7e-11, 1.0000000007451937), + (2.1e-6, -1.7e5, @double.infinity), + (2.1e-6, -2.8e6, @double.infinity), + (2.1e-6, -3.9e7, @double.infinity), + (2.1e-6, -4.0e8, @double.infinity), + (2.1e-6, -5.1e9, @double.infinity), + (2.1e-6, @double.infinity, 0), + (2.1e-6, @double.not_a_number, @double.not_a_number), + (2.1e-6, @double.neg_infinity, @double.infinity), + (3.2e-7, 1.1, 7.172407832612147e-8), + (3.2e-7, 2.2, 5.144343411731606e-15), + (3.2e-7, 3.3, 3.6897328979950713e-22), + (3.2e-7, 4.4, 2.646426913782639e-29), + (3.2e-7, 5.5, 1.8981253124850313e-36), + (3.2e-7, 1.0e-5, 0.9998504617335499), + (3.2e-7, 2.1e-6, 0.9999685951089775), + (3.2e-7, 3.2e-7, 0.9999952144291017), + (3.2e-7, 4.3e-8, 0.9999993569375786), + (3.2e-7, 5.4e-9, 0.9999999192433011), + (3.2e-7, 1.2e5, 0), + (3.2e-7, 2.3e6, 0), + (3.2e-7, 3.4e7, 0), + (3.2e-7, 4.5e8, 0), + (3.2e-7, 5.6e9, 0), + (3.2e-7, -1.3, 277526863.95170367), + (3.2e-7, -2.4, 3869368145657010), + (3.2e-7, -3.5, 5.3947966093944355e+22), + (3.2e-7, -4.6, 7.521597677233097e+29), + (3.2e-7, -5.7, 1.048685163026184e+37), + (3.2e-7, -1.3e-7, 1.0000019441447192), + (3.2e-7, -2.4e-8, 1.0000003589187405), + (3.2e-7, -3.5e-9, 1.0000000523423083), + (3.2e-7, -4.6e-10, 1.0000000068792747), + (3.2e-7, -5.7e-11, 1.000000000852432), + (3.2e-7, -1.7e5, @double.infinity), + (3.2e-7, -2.8e6, @double.infinity), + (3.2e-7, -3.9e7, @double.infinity), + (3.2e-7, -4.0e8, @double.infinity), + (3.2e-7, -5.1e9, @double.infinity), + (3.2e-7, @double.infinity, 0), + (3.2e-7, @double.not_a_number, @double.not_a_number), + (3.2e-7, @double.neg_infinity, @double.infinity), + (4.3e-8, 1.1, 7.885246986931752e-9), + (4.3e-8, 2.2, 6.217712004491629e-17), + (4.3e-8, 3.3, 4.902819484902738e-25), + (4.3e-8, 4.4, 3.865994257079931e-33), + (4.3e-8, 5.5, 3.048431956713522e-41), + (4.3e-8, 1.0e-5, 0.9998303937275578), + (4.3e-8, 2.1e-6, 0.9999643802963821), + (4.3e-8, 3.2e-7, 0.9999945721537), + (4.3e-8, 4.3e-8, 0.99999927063144), + (4.3e-8, 5.4e-9, 0.9999999084048493), + (4.3e-8, 1.2e5, 0), + (4.3e-8, 2.3e6, 0), + (4.3e-8, 3.4e7, 0), + (4.3e-8, 4.5e8, 0), + (4.3e-8, 5.6e9, 0), + (4.3e-8, -1.3, 3771299362.935219), + (4.3e-8, -2.4, 478272826353491800), + (4.3e-8, -3.5, 6.065413387128329e+25), + (4.3e-8, -4.6, 7.692103236817457e+33), + (4.3e-8, -5.7, 9.755056816312366e+41), + (4.3e-8, -1.3e-7, 1.000002205070975), + (4.3e-8, -2.4e-8, 1.0000004070896602), + (4.3e-8, -3.5e-9, 1.0000000593672318), + (4.3e-8, -4.6e-10, 1.0000000078025502), + (4.3e-8, -5.7e-11, 1.0000000009668377), + (4.3e-8, -1.7e5, @double.infinity), + (4.3e-8, -2.8e6, @double.infinity), + (4.3e-8, -3.9e7, @double.infinity), + (4.3e-8, -4.0e8, @double.infinity), + (4.3e-8, -5.1e9, @double.infinity), + (4.3e-8, @double.infinity, 0), + (4.3e-8, @double.not_a_number, @double.not_a_number), + (4.3e-8, @double.neg_infinity, @double.infinity), + (5.4e-9, 1.1, 8.046983962849195e-10), + (5.4e-9, 2.2, 6.475395089835214e-19), + (5.4e-9, 3.3, 5.210740044101683e-28), + (5.4e-9, 4.4, 4.1930741569462e-37), + (5.4e-9, 5.5, 3.3741600495983767e-46), + (5.4e-9, 1.0e-5, 0.9998096494501315), + (5.4e-9, 2.1e-6, 0.9999600233786313), + (5.4e-9, 3.2e-7, 0.9999939082211523), + (5.4e-9, 4.3e-8, 0.9999991814150591), + (5.4e-9, 5.4e-9, 0.9999998972009241), + (5.4e-9, 1.2e5, 0), + (5.4e-9, 2.3e6, 0), + (5.4e-9, 3.4e7, 0), + (5.4e-9, 4.5e8, 0), + (5.4e-9, 5.6e9, 0), + (5.4e-9, -1.3, 55961340909.73872), + (5.4e-9, -2.4, 69543248958985440000), + (5.4e-9, -3.5, 8.64215080830884e+28), + (5.4e-9, -4.6, 1.0739614802523902e+38), + (5.4e-9, -5.7, 1.3346136704268286e+47), + (5.4e-9, -1.3e-7, 1.0000024747957572), + (5.4e-9, -2.4e-8, 1.0000004568849097), + (5.4e-9, -3.5e-9, 1.0000000666290363), + (5.4e-9, -4.6e-10, 1.0000000087569587), + (5.4e-9, -5.7e-11, 1.0000000010851013), + (5.4e-9, -1.7e5, @double.infinity), + (5.4e-9, -2.8e6, @double.infinity), + (5.4e-9, -3.9e7, @double.infinity), + (5.4e-9, -4.0e8, @double.infinity), + (5.4e-9, -5.1e9, @double.infinity), + (5.4e-9, @double.infinity, 0), + (5.4e-9, @double.not_a_number, @double.not_a_number), + (5.4e-9, @double.neg_infinity, @double.infinity), + (1.2e5, 1.1, 386455.39155829826), + (1.2e5, 2.2, 149347769664.47763), + (1.2e5, 3.3, 57716250804043944), + (1.2e5, 4.4, 2.2304756303753866e+22), + (1.2e5, 5.5, 8.619793330979577e+27), + (1.2e5, 1.0e-5, 1.0001169593094243), + (1.2e5, 2.1e-6, 1.0000245603203455), + (1.2e5, 3.2e-7, 1.00000374248605), + (1.2e5, 4.3e-8, 1.0000005028957484), + (1.2e5, 5.4e-9, 1.000000063154336), + (1.2e5, 1.2e5, @double.infinity), + (1.2e5, 2.3e6, @double.infinity), + (1.2e5, 3.4e7, @double.infinity), + (1.2e5, 4.5e8, @double.infinity), + (1.2e5, 5.6e9, @double.infinity), + (1.2e5, -1.3, 2.4949645549379676e-7), + (1.2e5, -2.4, 6.456022116492064e-13), + (1.2e5, -3.5, 1.6705736955718338e-18), + (1.2e5, -4.6, 4.322811201664475e-24), + (1.2e5, -5.7, 1.1185796073988373e-29), + (1.2e5, -1.3e-7, 0.999998479619043), + (1.2e5, -2.4e-8, 0.9999997193141109), + (1.2e5, -3.5e-9, 0.9999999590666363), + (1.2e5, -4.6e-10, 0.9999999946201864), + (1.2e5, -5.7e-11, 0.9999999993333709), + (1.2e5, -1.7e5, 0), + (1.2e5, -2.8e6, 0), + (1.2e5, -3.9e7, 0), + (1.2e5, -4.0e8, 0), + (1.2e5, -5.1e9, 0), + (1.2e5, @double.infinity, @double.infinity), + (1.2e5, @double.not_a_number, @double.not_a_number), + (1.2e5, @double.neg_infinity, 0), + (2.3e6, 1.1, 9951776.630147615), + (2.3e6, 2.2, 99037858096352.22), + (2.3e6, 3.3, 985602641703147300000), + (2.3e6, 4.4, 9.808497336313198e+27), + (2.3e6, 5.5, 9.761197456838618e+34), + (2.3e6, 1.0e-5, 1.000146494926143), + (2.3e6, 2.1e-6, 1.0000307621544753), + (2.3e6, 3.2e-7, 1.0000046875052842), + (2.3e6, 4.3e-8, 1.0000006298822446), + (2.3e6, 5.4e-9, 1.0000000791014694), + (2.3e6, 1.2e5, @double.infinity), + (2.3e6, 2.3e6, @double.infinity), + (2.3e6, 3.4e7, @double.infinity), + (2.3e6, 4.5e8, @double.infinity), + (2.3e6, 5.6e9, @double.infinity), + (2.3e6, -1.3, 5.3672745751358e-9), + (2.3e6, -2.4, 5.393282802265032e-16), + (2.3e6, -3.5, 5.419417057580233e-23), + (2.3e6, -4.6, 5.44567795177682e-30), + (2.3e6, -5.7, 5.4720660985092965e-37), + (2.3e6, -1.3e-7, 0.9999980957072546), + (2.3e6, -2.4e-8, 0.9999996484379895), + (2.3e6, -3.5e-9, 0.9999999487305324), + (2.3e6, -4.6e-10, 0.999999993261727), + (2.3e6, -5.7e-11, 0.9999999991650401), + (2.3e6, -1.7e5, 0), + (2.3e6, -2.8e6, 0), + (2.3e6, -3.9e7, 0), + (2.3e6, -4.0e8, 0), + (2.3e6, -5.1e9, 0), + (2.3e6, @double.infinity, @double.infinity), + (2.3e6, @double.not_a_number, @double.not_a_number), + (2.3e6, @double.neg_infinity, 0), + (3.4e7, 1.1, 192586929.35759524), + (3.4e7, 2.2, 37089725359387384), + (3.4e7, 3.3, 7.142996317680891e+24), + (3.4e7, 4.4, 1.3756477272347834e+33), + (3.4e7, 5.5, 2.6493177166589964e+41), + (3.4e7, 1.0e-5, 1.0001734337487198), + (3.4e7, 2.1e-6, 1.0000364185924142), + (3.4e7, 3.2e-7, 1.0000055494141444), + (3.4e7, 4.3e-8, 1.0000007457007345), + (3.4e7, 5.4e-9, 1.0000000936461082), + (3.4e7, 1.2e5, @double.infinity), + (3.4e7, 2.3e6, @double.infinity), + (3.4e7, 3.4e7, @double.infinity), + (3.4e7, 4.5e8, @double.infinity), + (3.4e7, 5.6e9, @double.infinity), + (3.4e7, -1.3, 1.6183684669395333e-10), + (3.4e7, -2.4, 8.403314141504143e-19), + (3.4e7, -3.5, 4.363387572321108e-27), + (3.4e7, -4.6, 2.265671708290861e-35), + (3.4e7, -5.7, 1.1764410574738154e-43), + (3.4e7, -1.3e-7, 0.9999977455593005), + (3.4e7, -2.4e-8, 0.9999995837951806), + (3.4e7, -3.5e-9, 0.9999999393034531), + (3.4e7, -4.6e-10, 0.9999999920227394), + (3.4e7, -5.7e-11, 0.9999999990115134), + (3.4e7, -1.7e5, 0), + (3.4e7, -2.8e6, 0), + (3.4e7, -3.9e7, 0), + (3.4e7, -4.0e8, 0), + (3.4e7, -5.1e9, 0), + (3.4e7, @double.infinity, @double.infinity), + (3.4e7, @double.not_a_number, @double.not_a_number), + (3.4e7, @double.neg_infinity, 0), + (4.5e8, 1.1, 3300150646.401208), + (4.5e8, 2.2, 10890994288942311000), + (4.5e8, 3.3, 3.5941921842604514e+28), + (4.5e8, 4.4, 1.1861375660177403e+38), + (4.5e8, 5.5, 3.914432655214167e+47), + (4.5e8, 1.0e-5, 1.000199267432525), + (4.5e8, 2.1e-6, 1.000041842867484), + (4.5e8, 3.2e-7, 1.0000063759429312), + (4.5e8, 4.3e-8, 1.000000856764967), + (4.5e8, 5.4e-9, 1.0000001075936997), + (4.5e8, 1.2e5, @double.infinity), + (4.5e8, 2.3e6, @double.infinity), + (4.5e8, 3.4e7, @double.infinity), + (4.5e8, 4.5e8, @double.infinity), + (4.5e8, 5.6e9, @double.infinity), + (4.5e8, -1.3, 5.634089375820785e-12), + (4.5e8, -2.4, 1.7072218754512757e-21), + (4.5e8, -3.5, 5.173163465470856e-31), + (4.5e8, -4.6, 1.5675537330734282e-40), + (4.5e8, -5.7, 4.749945990443864e-50), + (4.5e8, -1.3e-7, 0.9999974097847963), + (4.5e8, -2.4e-8, 0.999999521805919), + (4.5e8, -3.5e-9, 0.999999930263349), + (4.5e8, -4.6e-10, 0.9999999908346113), + (4.5e8, -5.7e-11, 0.9999999988642888), + (4.5e8, -1.7e5, 0), + (4.5e8, -2.8e6, 0), + (4.5e8, -3.9e7, 0), + (4.5e8, -4.0e8, 0), + (4.5e8, -5.1e9, 0), + (4.5e8, @double.infinity, @double.infinity), + (4.5e8, @double.not_a_number, @double.not_a_number), + (4.5e8, @double.neg_infinity, 0), + (5.6e9, 1.1, 52845356216.05751), + (5.6e9, 2.2, 2.792631673602008e+21), + (5.6e9, 3.3, 1.4757761557174151e+32), + (5.6e9, 4.4, 7.798791664405154e+42), + (5.6e9, 5.5, 4.121299235603064e+53), + (5.6e9, 1.0e-5, 1.0002244855174505), + (5.6e9, 2.1e-6, 1.000047137779063), + (5.6e9, 3.2e-7, 1.000007182756175), + (5.6e9, 4.3e-8, 1.0000009651798605), + (5.6e9, 5.4e-9, 1.0000001212085825), + (5.6e9, 1.2e5, @double.infinity), + (5.6e9, 2.3e6, @double.infinity), + (5.6e9, 3.4e7, @double.infinity), + (5.6e9, 4.5e8, @double.infinity), + (5.6e9, 5.6e9, @double.infinity), + (5.6e9, -1.3, 2.1249835131503997e-13), + (5.6e9, -2.4, 4.021135754033797e-24), + (5.6e9, -3.5, 7.609250919973817e-35), + (5.6e9, -4.6, 1.4399090979467666e-45), + (5.6e9, -5.7, 2.7247599430680413e-56), + (5.6e9, -1.3e-7, 0.9999970820200408), + (5.6e9, -2.4e-8, 0.9999994612953667), + (5.6e9, -3.5e-9, 0.9999999214388896), + (5.6e9, -4.6e-10, 0.9999999896748252), + (5.6e9, -5.7e-11, 0.9999999987205761), + (5.6e9, -1.7e5, 0), + (5.6e9, -2.8e6, 0), + (5.6e9, -3.9e7, 0), + (5.6e9, -4.0e8, 0), + (5.6e9, -5.1e9, 0), + (5.6e9, @double.infinity, @double.infinity), + (5.6e9, @double.not_a_number, @double.not_a_number), + (5.6e9, @double.neg_infinity, 0), + (-1.3, 1.1, @double.not_a_number), + (-1.3, 2.2, @double.not_a_number), + (-1.3, 3.3, @double.not_a_number), + (-1.3, 4.4, @double.not_a_number), + (-1.3, 5.5, @double.not_a_number), + (-1.3, 1.0e-5, @double.not_a_number), + (-1.3, -4.6e-10, @double.not_a_number), + (-1.3, -5.7e-11, @double.not_a_number), + (-1.3, -1.7e5, 0), + (-1.3, -2.8e6, 0), + (-1.3, -3.9e7, 0), + (-1.3, -4.0e8, 0), + (-1.3, -5.1e9, 0), + (-1.3, @double.infinity, @double.infinity), + (-1.3, @double.not_a_number, @double.not_a_number), + (-1.3, @double.neg_infinity, 0), + (-2.4, 1.1, @double.not_a_number), + (-2.4, 2.2, @double.not_a_number), + (-2.4, -1.3e-7, @double.not_a_number), + (-2.4, -2.4e-8, @double.not_a_number), + (-2.4, -3.5e-9, @double.not_a_number), + (-2.4, -4.6e-10, @double.not_a_number), + (-2.4, -5.7e-11, @double.not_a_number), + (-2.4, -1.7e5, 0), + (-2.4, -2.8e6, 0), + (-2.4, -3.9e7, 0), + (-2.4, -4.0e8, 0), + (-2.4, -5.1e9, 0), + (-2.4, @double.infinity, @double.infinity), + (-2.4, @double.not_a_number, @double.not_a_number), + (-2.4, @double.neg_infinity, 0), + (-3.5, 1.1, @double.not_a_number), + (-3.5, 2.2, @double.not_a_number), + (-3.5, 3.3, @double.not_a_number), + (-3.5, 4.4, @double.not_a_number), + (-3.5, 5.5, @double.not_a_number), + (-3.5, 1.0e-5, @double.not_a_number), + (-3.5, 2.1e-6, @double.not_a_number), + (-3.5, 3.2e-7, @double.not_a_number), + (-3.5, 4.3e-8, @double.not_a_number), + (-3.5, 5.4e-9, @double.not_a_number), + (-3.5, 1.2e5, @double.infinity), + (-3.5, -4.6e-10, @double.not_a_number), + (-3.5, -5.7e-11, @double.not_a_number), + (-3.5, -1.7e5, 0), + (-3.5, -2.8e6, 0), + (-3.5, -3.9e7, 0), + (-3.5, -4.0e8, 0), + (-3.5, -5.1e9, 0), + (-3.5, @double.infinity, @double.infinity), + (-3.5, @double.not_a_number, @double.not_a_number), + (-3.5, @double.neg_infinity, 0), + (-4.6, 1.1, @double.not_a_number), + (-4.6, 2.2, @double.not_a_number), + (-4.6, 3.3, @double.not_a_number), + (-4.6, -4.6, @double.not_a_number), + (-4.6, -5.7, @double.not_a_number), + (-4.6, -1.3e-7, @double.not_a_number), + (-4.6, -2.4e-8, @double.not_a_number), + (-4.6, -3.5e-9, @double.not_a_number), + (-4.6, -4.6e-10, @double.not_a_number), + (-4.6, -5.7e-11, @double.not_a_number), + (-4.6, -1.7e5, 0), + (-4.6, -2.8e6, 0), + (-4.6, -3.9e7, 0), + (-4.6, -4.0e8, 0), + (-4.6, -5.1e9, 0), + (-4.6, @double.infinity, @double.infinity), + (-4.6, @double.not_a_number, @double.not_a_number), + (-4.6, @double.neg_infinity, 0), + (-5.7, 1.1, @double.not_a_number), + (-5.7, 2.2, @double.not_a_number), + (-5.7, 3.3, @double.not_a_number), + (-5.7, 4.4, @double.not_a_number), + (-5.7, 5.5, @double.not_a_number), + (-5.7, 1.0e-5, @double.not_a_number), + (-5.7, 2.1e-6, @double.not_a_number), + (-5.7, 3.2e-7, @double.not_a_number), + (-5.7, 4.3e-8, @double.not_a_number), + (-5.7, 5.4e-9, @double.not_a_number), + (-5.7, 1.2e5, @double.infinity), + (-5.7, 5.6e9, @double.infinity), + (-5.7, -1.3, @double.not_a_number), + (-5.7, -2.4, @double.not_a_number), + (-5.7, -3.5, @double.not_a_number), + (-5.7, -4.6e-10, @double.not_a_number), + (-5.7, -5.7e-11, @double.not_a_number), + (-5.7, -1.7e5, 0), + (-5.7, -2.8e6, 0), + (-5.7, -3.9e7, 0), + (-5.7, -4.0e8, 0), + (-5.7, -5.1e9, 0), + (-5.7, @double.infinity, @double.infinity), + (-5.7, @double.not_a_number, @double.not_a_number), + (-5.7, @double.neg_infinity, 0), + (-1.3e-7, 1.1, @double.not_a_number), + (-1.3e-7, 2.2, @double.not_a_number), + (-1.3e-7, 3.3, @double.not_a_number), + (-1.3e-7, 4.4, @double.not_a_number), + (-1.3e-7, 5.5, @double.not_a_number), + (-1.3e-7, 1.0e-5, @double.not_a_number), + (-1.3e-7, 2.1e-6, @double.not_a_number), + (-1.3e-7, 3.2e-7, @double.not_a_number), + (-1.3e-7, 4.3e-8, @double.not_a_number), + (-1.3e-7, 5.4e-9, @double.not_a_number), + (-1.3e-7, 1.2e5, 0), + (-1.3e-7, 2.3e6, 0), + (-1.3e-7, 3.4e7, 0), + (-1.3e-7, 4.5e8, 0), + (-1.3e-7, 5.6e9, 0), + (-1.3e-7, -1.3, @double.not_a_number), + (-1.3e-7, -2.4, @double.not_a_number), + (-1.3e-7, -3.5, @double.not_a_number), + (-1.3e-7, -1.3e-7, @double.not_a_number), + (-1.3e-7, -2.4e-8, @double.not_a_number), + (-1.3e-7, -4.6e-10, @double.not_a_number), + (-1.3e-7, -5.7e-11, @double.not_a_number), + (-1.3e-7, -1.7e5, @double.infinity), + (-1.3e-7, -2.8e6, @double.infinity), + (-1.3e-7, -3.9e7, @double.infinity), + (-1.3e-7, -4.0e8, @double.infinity), + (-1.3e-7, -5.1e9, @double.infinity), + (-1.3e-7, @double.infinity, 0), + (-1.3e-7, @double.not_a_number, @double.not_a_number), + (-1.3e-7, @double.neg_infinity, @double.infinity), + (-2.4e-8, 1.1, @double.not_a_number), + (-2.4e-8, 2.2, @double.not_a_number), + (-2.4e-8, 3.3, @double.not_a_number), + (-2.4e-8, 4.3e-8, @double.not_a_number), + (-2.4e-8, 5.4e-9, @double.not_a_number), + (-2.4e-8, 1.2e5, 0), + (-2.4e-8, 2.3e6, 0), + (-2.4e-8, 3.4e7, 0), + (-2.4e-8, 4.5e8, 0), + (-2.4e-8, 5.6e9, 0), + (-2.4e-8, -1.3, @double.not_a_number), + (-2.4e-8, -2.4, @double.not_a_number), + (-2.4e-8, -3.5, @double.not_a_number), + (-2.4e-8, -4.6, @double.not_a_number), + (-2.4e-8, -4.0e8, @double.infinity), + (-2.4e-8, -5.1e9, @double.infinity), + (-2.4e-8, @double.infinity, 0), + (-2.4e-8, @double.not_a_number, @double.not_a_number), + (-2.4e-8, @double.neg_infinity, @double.infinity), + (-3.5e-9, 1.1, @double.not_a_number), + (-3.5e-9, 4.3e-8, @double.not_a_number), + (-3.5e-9, 5.4e-9, @double.not_a_number), + (-3.5e-9, 1.2e5, 0), + (-3.5e-9, 2.3e6, 0), + (-3.5e-9, 3.4e7, 0), + (-3.5e-9, 4.5e8, 0), + (-3.5e-9, 5.6e9, 0), + (-3.5e-9, -1.3, @double.not_a_number), + (-3.5e-9, -2.4, @double.not_a_number), + (-3.5e-9, -3.5, @double.not_a_number), + (-3.5e-9, -4.6, @double.not_a_number), + (-3.5e-9, -5.7, @double.not_a_number), + (-3.5e-9, -1.3e-7, @double.not_a_number), + (-3.5e-9, -2.4e-8, @double.not_a_number), + (-3.5e-9, -3.5e-9, @double.not_a_number), + (-3.5e-9, -4.6e-10, @double.not_a_number), + (-3.5e-9, -5.7e-11, @double.not_a_number), + (-3.5e-9, -1.7e5, @double.infinity), + (-3.5e-9, -2.8e6, @double.infinity), + (-3.5e-9, -3.9e7, @double.infinity), + (-3.5e-9, -4.0e8, @double.infinity), + (-3.5e-9, -5.1e9, @double.infinity), + (-3.5e-9, @double.infinity, 0), + (-3.5e-9, @double.not_a_number, @double.not_a_number), + (-3.5e-9, @double.neg_infinity, @double.infinity), + (-4.6e-10, 1.1, @double.not_a_number), + (-4.6e-10, 2.2, @double.not_a_number), + (-4.6e-10, 3.3, @double.not_a_number), + (-4.6e-10, 4.4, @double.not_a_number), + (-4.6e-10, 5.5, @double.not_a_number), + (-4.6e-10, 1.0e-5, @double.not_a_number), + (-4.6e-10, 2.1e-6, @double.not_a_number), + (-4.6e-10, 3.2e-7, @double.not_a_number), + (-4.6e-10, 4.3e-8, @double.not_a_number), + (-4.6e-10, 5.4e-9, @double.not_a_number), + (-4.6e-10, 1.2e5, 0), + (-4.6e-10, 2.3e6, 0), + (-4.6e-10, 3.4e7, 0), + (-4.6e-10, 4.5e8, 0), + (-4.6e-10, 5.6e9, 0), + (-4.6e-10, -1.3, @double.not_a_number), + (-4.6e-10, -2.4, @double.not_a_number), + (-4.6e-10, -3.5, @double.not_a_number), + (-4.6e-10, -4.6, @double.not_a_number), + (-4.6e-10, -5.7, @double.not_a_number), + (-4.6e-10, -1.3e-7, @double.not_a_number), + (-4.6e-10, -2.4e-8, @double.not_a_number), + (-4.6e-10, -3.5e-9, @double.not_a_number), + (-4.6e-10, -4.6e-10, @double.not_a_number), + (-4.6e-10, -5.7e-11, @double.not_a_number), + (-4.6e-10, -1.7e5, @double.infinity), + (-4.6e-10, -2.8e6, @double.infinity), + (-4.6e-10, -3.9e7, @double.infinity), + (-4.6e-10, -4.0e8, @double.infinity), + (-4.6e-10, -5.1e9, @double.infinity), + (-4.6e-10, @double.infinity, 0), + (-4.6e-10, @double.not_a_number, @double.not_a_number), + (-4.6e-10, @double.neg_infinity, @double.infinity), + (-5.7e-11, 1.1, @double.not_a_number), + (-5.7e-11, 2.2, @double.not_a_number), + (-5.7e-11, 3.3, @double.not_a_number), + (-5.7e-11, 4.4, @double.not_a_number), + (-5.7e-11, 5.5, @double.not_a_number), + (-5.7e-11, 1.0e-5, @double.not_a_number), + (-5.7e-11, 2.1e-6, @double.not_a_number), + (-5.7e-11, 3.2e-7, @double.not_a_number), + (-5.7e-11, 4.3e-8, @double.not_a_number), + (-5.7e-11, 5.4e-9, @double.not_a_number), + (-5.7e-11, 1.2e5, 0), + (-5.7e-11, 2.3e6, 0), + (-5.7e-11, 3.4e7, 0), + (-5.7e-11, 4.5e8, 0), + (-5.7e-11, 5.6e9, 0), + (-5.7e-11, -1.3, @double.not_a_number), + (-5.7e-11, -2.4, @double.not_a_number), + (-5.7e-11, -4.6e-10, @double.not_a_number), + (-5.7e-11, -5.7e-11, @double.not_a_number), + (-5.7e-11, -1.7e5, @double.infinity), + (-5.7e-11, -2.8e6, @double.infinity), + (-5.7e-11, -3.9e7, @double.infinity), + (-5.7e-11, -4.0e8, @double.infinity), + (-5.7e-11, -5.1e9, @double.infinity), + (-5.7e-11, @double.infinity, 0), + (-5.7e-11, @double.not_a_number, @double.not_a_number), + (-5.7e-11, @double.neg_infinity, @double.infinity), + (-1.7e5, 1.1, @double.not_a_number), + (-1.7e5, 2.2, @double.not_a_number), + (-1.7e5, 3.3, @double.not_a_number), + (-1.7e5, 4.4, @double.not_a_number), + (-1.7e5, 5.5, @double.not_a_number), + (-1.7e5, 1.0e-5, @double.not_a_number), + (-1.7e5, 2.1e-6, @double.not_a_number), + (-1.7e5, 5.4e-9, @double.not_a_number), + (-1.7e5, 1.2e5, @double.infinity), + (-1.7e5, 4.5e8, @double.infinity), + (-1.7e5, 5.6e9, @double.infinity), + (-1.7e5, -1.3, @double.not_a_number), + (-1.7e5, -2.4, @double.not_a_number), + (-1.7e5, -2.4e-8, @double.not_a_number), + (-1.7e5, -3.5e-9, @double.not_a_number), + (-1.7e5, -4.6e-10, @double.not_a_number), + (-1.7e5, -5.7e-11, @double.not_a_number), + (-1.7e5, -1.7e5, 0), + (-1.7e5, -2.8e6, 0), + (-1.7e5, -3.9e7, 0), + (-1.7e5, -4.0e8, 0), + (-1.7e5, -5.1e9, 0), + (-1.7e5, @double.infinity, @double.infinity), + (-1.7e5, @double.not_a_number, @double.not_a_number), + (-1.7e5, @double.neg_infinity, 0), + (-2.8e6, 1.1, @double.not_a_number), + (-2.8e6, 2.2, @double.not_a_number), + (-2.8e6, 3.3, @double.not_a_number), + (-2.8e6, 1.0e-5, @double.not_a_number), + (-2.8e6, 2.1e-6, @double.not_a_number), + (-2.8e6, 5.4e-9, @double.not_a_number), + (-2.8e6, 1.2e5, @double.infinity), + (-2.8e6, 2.3e6, @double.infinity), + (-2.8e6, 3.4e7, @double.infinity), + (-2.8e6, 4.5e8, @double.infinity), + (-2.8e6, 5.6e9, @double.infinity), + (-2.8e6, -1.3, @double.not_a_number), + (-2.8e6, -2.4, @double.not_a_number), + (-2.8e6, -3.5, @double.not_a_number), + (-2.8e6, -4.6, @double.not_a_number), + (-2.8e6, -5.7, @double.not_a_number), + (-2.8e6, -1.3e-7, @double.not_a_number), + (-2.8e6, -2.4e-8, @double.not_a_number), + (-2.8e6, -3.5e-9, @double.not_a_number), + (-2.8e6, -4.6e-10, @double.not_a_number), + (-2.8e6, -5.7e-11, @double.not_a_number), + (-2.8e6, -1.7e5, 0), + (-2.8e6, -2.8e6, 0), + (-2.8e6, -3.9e7, 0), + (-2.8e6, -4.0e8, 0), + (-2.8e6, -5.1e9, 0), + (-2.8e6, @double.infinity, @double.infinity), + (-2.8e6, @double.not_a_number, @double.not_a_number), + (-2.8e6, @double.neg_infinity, 0), + (-3.9e7, 1.1, @double.not_a_number), + (-3.9e7, 2.2, @double.not_a_number), + (-3.9e7, 3.3, @double.not_a_number), + (-3.9e7, 4.4, @double.not_a_number), + (-3.9e7, 5.5, @double.not_a_number), + (-3.9e7, 1.0e-5, @double.not_a_number), + (-3.9e7, 2.1e-6, @double.not_a_number), + (-3.9e7, 3.2e-7, @double.not_a_number), + (-3.9e7, 4.3e-8, @double.not_a_number), + (-3.9e7, 5.4e-9, @double.not_a_number), + (-3.9e7, 1.2e5, @double.infinity), + (-3.9e7, 2.3e6, @double.infinity), + (-3.9e7, 4.5e8, @double.infinity), + (-3.9e7, 5.6e9, @double.infinity), + (-3.9e7, -1.3, @double.not_a_number), + (-3.9e7, -5.7, @double.not_a_number), + (-3.9e7, -1.3e-7, @double.not_a_number), + (-3.9e7, -2.4e-8, @double.not_a_number), + (-3.9e7, -3.5e-9, @double.not_a_number), + (-3.9e7, -4.6e-10, @double.not_a_number), + (-3.9e7, -5.7e-11, @double.not_a_number), + (-3.9e7, -1.7e5, 0), + (-3.9e7, -2.8e6, 0), + (-3.9e7, -3.9e7, 0), + (-3.9e7, -4.0e8, 0), + (-3.9e7, -5.1e9, 0), + (-3.9e7, @double.infinity, @double.infinity), + (-3.9e7, @double.not_a_number, @double.not_a_number), + (-3.9e7, @double.neg_infinity, 0), + (-4.0e8, 1.1, @double.not_a_number), + (-4.0e8, 2.2, @double.not_a_number), + (-4.0e8, 3.3, @double.not_a_number), + (-4.0e8, 4.4, @double.not_a_number), + (-4.0e8, 5.5, @double.not_a_number), + (-4.0e8, 1.0e-5, @double.not_a_number), + (-4.0e8, 2.1e-6, @double.not_a_number), + (-4.0e8, 3.2e-7, @double.not_a_number), + (-4.0e8, 4.3e-8, @double.not_a_number), + (-4.0e8, 5.4e-9, @double.not_a_number), + (-4.0e8, 1.2e5, @double.infinity), + (-4.0e8, 4.5e8, @double.infinity), + (-4.0e8, 5.6e9, @double.infinity), + (-4.0e8, -1.3, @double.not_a_number), + (-4.0e8, -2.4, @double.not_a_number), + (-4.0e8, -1.3e-7, @double.not_a_number), + (-4.0e8, -2.4e-8, @double.not_a_number), + (-4.0e8, -3.5e-9, @double.not_a_number), + (-4.0e8, -4.6e-10, @double.not_a_number), + (-4.0e8, -5.7e-11, @double.not_a_number), + (-4.0e8, -1.7e5, 0), + (-4.0e8, -2.8e6, 0), + (-4.0e8, -3.9e7, 0), + (-4.0e8, -4.0e8, 0), + (-4.0e8, -5.1e9, 0), + (-4.0e8, @double.infinity, @double.infinity), + (-4.0e8, @double.not_a_number, @double.not_a_number), + (-4.0e8, @double.neg_infinity, 0), + (-5.1e9, 1.1, @double.not_a_number), + (-5.1e9, 2.2, @double.not_a_number), + (-5.1e9, 3.3, @double.not_a_number), + (-5.1e9, 4.4, @double.not_a_number), + (-5.1e9, 5.5, @double.not_a_number), + (-5.1e9, 1.0e-5, @double.not_a_number), + (-5.1e9, 2.1e-6, @double.not_a_number), + (-5.1e9, 3.2e-7, @double.not_a_number), + (-5.1e9, 4.3e-8, @double.not_a_number), + (-5.1e9, 5.4e-9, @double.not_a_number), + (-5.1e9, 1.2e5, @double.infinity), + (-5.1e9, 2.3e6, @double.infinity), + (-5.1e9, 3.4e7, @double.infinity), + (-5.1e9, 4.5e8, @double.infinity), + (-5.1e9, 5.6e9, @double.infinity), + (-5.1e9, -1.3, @double.not_a_number), + (-5.1e9, -2.4, @double.not_a_number), + (-5.1e9, -3.5, @double.not_a_number), + (-5.1e9, -4.6, @double.not_a_number), + (-5.1e9, -5.7, @double.not_a_number), + (-5.1e9, -1.3e-7, @double.not_a_number), + (-5.1e9, -2.4e-8, @double.not_a_number), + (-5.1e9, -3.5e-9, @double.not_a_number), + (-5.1e9, -4.6e-10, @double.not_a_number), + (-5.1e9, -5.7e-11, @double.not_a_number), + (-5.1e9, -1.7e5, 0), + (-5.1e9, -2.8e6, 0), + (-5.1e9, -3.9e7, 0), + (-5.1e9, -4.0e8, 0), + (-5.1e9, -5.1e9, 0), + (-5.1e9, @double.infinity, @double.infinity), + (-5.1e9, @double.not_a_number, @double.not_a_number), + (-5.1e9, @double.neg_infinity, 0), + (@double.infinity, 1.1, @double.infinity), + (@double.infinity, 2.2, @double.infinity), + (@double.infinity, 3.3, @double.infinity), + (@double.infinity, 4.4, @double.infinity), + (@double.infinity, 5.5, @double.infinity), + (@double.infinity, 1.0e-5, @double.infinity), + (@double.infinity, 2.1e-6, @double.infinity), + (@double.infinity, 3.2e-7, @double.infinity), + (@double.infinity, 4.3e-8, @double.infinity), + (@double.infinity, 5.4e-9, @double.infinity), + (@double.infinity, 1.2e5, @double.infinity), + (@double.infinity, 2.3e6, @double.infinity), + (@double.infinity, 3.4e7, @double.infinity), + (@double.infinity, 4.5e8, @double.infinity), + (@double.infinity, 5.6e9, @double.infinity), + (@double.infinity, -1.3, 0), + (@double.infinity, -2.4, 0), + (@double.infinity, -3.5, 0), + (@double.infinity, -4.6, 0), + (@double.infinity, -5.7, 0), + (@double.infinity, -1.3e-7, 0), + (@double.infinity, -2.4e-8, 0), + (@double.infinity, -3.5e-9, 0), + (@double.infinity, -4.6e-10, 0), + (@double.infinity, -5.7e-11, 0), + (@double.infinity, -1.7e5, 0), + (@double.infinity, -2.8e6, 0), + (@double.infinity, -3.9e7, 0), + (@double.infinity, -4.0e8, 0), + (@double.infinity, -5.1e9, 0), + (@double.infinity, @double.infinity, @double.infinity), + (@double.infinity, @double.not_a_number, @double.not_a_number), + (@double.infinity, @double.neg_infinity, 0), + (@double.not_a_number, 1.1, @double.not_a_number), + (@double.not_a_number, 2.2, @double.not_a_number), + (@double.not_a_number, 3.3, @double.not_a_number), + (@double.not_a_number, -4.0e8, @double.not_a_number), + (@double.not_a_number, -5.1e9, @double.not_a_number), + (@double.not_a_number, @double.infinity, @double.not_a_number), + (@double.not_a_number, @double.not_a_number, @double.not_a_number), + (@double.not_a_number, @double.neg_infinity, @double.not_a_number), + (@double.neg_infinity, 1.1, @double.infinity), + (@double.neg_infinity, 2.3e6, @double.infinity), + (@double.neg_infinity, 3.4e7, @double.infinity), + (@double.neg_infinity, 4.5e8, @double.infinity), + (@double.neg_infinity, 5.6e9, @double.infinity), + (@double.neg_infinity, -1.3, 0), + (@double.neg_infinity, -2.4, 0), + (@double.neg_infinity, -3.5, 0), + (@double.neg_infinity, -4.6, 0), + (@double.neg_infinity, -5.7, 0), + (@double.neg_infinity, -1.3e-7, 0), + (@double.neg_infinity, -2.4e-8, 0), + (@double.neg_infinity, -3.5e-9, 0), + (@double.neg_infinity, -4.6e-10, 0), + (@double.neg_infinity, -5.7e-11, 0), + (@double.neg_infinity, -1.7e5, 0), + (@double.neg_infinity, -2.8e6, 0), + (@double.neg_infinity, -3.9e7, 0), + (@double.neg_infinity, -4.0e8, 0), + (@double.neg_infinity, -5.1e9, 0), + (@double.neg_infinity, @double.infinity, @double.infinity), + (@double.neg_infinity, @double.not_a_number, @double.not_a_number), + (@double.neg_infinity, @double.neg_infinity, 0), ] - for x in value { - for y in value { - it.writeln("\{x} ** \{y} == \{@double.pow(x, y)}") + fn is_accept(actucal : Double, expect : Double) -> Bool { + if expect.is_nan() { + return actucal.is_nan() } + if expect.is_pos_inf() { + return actucal.is_pos_inf() + } + if expect.is_neg_inf() { + return actucal.is_neg_inf() + } + let actual = actucal.reinterpret_as_int64() + let expected = expect.reinterpret_as_int64() + (actual - expected).abs() <= 2 + } + + for case in test_cases { + let (a, b, expect) = case + let actual = a.pow(b) + assert_true( + is_accept(actual, expect), + msg="Test failed: \{a} ** \{b} = \{actual}, expected \{expect}", + ) } - it.snapshot(filename="pow") } diff --git a/bundled-core/double/round.mbt b/bundled-core/double/round.mbt index 5190801..b723e11 100644 --- a/bundled-core/double/round.mbt +++ b/bundled-core/double/round.mbt @@ -41,6 +41,7 @@ let frac_bits = 52 /// inspect((-3.7).trunc(), content="-3") /// inspect(0.0.trunc(), content="0") /// ``` +#as_free_fn pub fn Double::trunc(self : Double) -> Double { let u64 = self.reinterpret_as_uint64() let biased_exp = ((u64 >> frac_bits) & ((0x1UL << exp_bits) - 1)).to_int() @@ -70,6 +71,7 @@ pub fn Double::trunc(self : Double) -> Double { /// inspect((-3.7).ceil(), content="-3") /// inspect(42.0.ceil(), content="42") /// ``` +#as_free_fn pub fn Double::ceil(self : Double) -> Double { let trunced = self.trunc() if self > trunced { @@ -96,6 +98,7 @@ pub fn Double::ceil(self : Double) -> Double { /// inspect((-3.7).floor(), content="-4") /// inspect(0.0.floor(), content="0") /// ``` +#as_free_fn pub fn Double::floor(self : Double) -> Double { let trunced = self.trunc() if self < trunced { @@ -124,6 +127,7 @@ pub fn Double::floor(self : Double) -> Double { /// inspect(3.5.round(), content="4") /// inspect((-3.5).round(), content="-3") /// ``` +#as_free_fn pub fn Double::round(self : Double) -> Double { floor(self + 0.5) } diff --git a/bundled-core/double/round_js.mbt b/bundled-core/double/round_js.mbt index dccd749..8060411 100644 --- a/bundled-core/double/round_js.mbt +++ b/bundled-core/double/round_js.mbt @@ -29,6 +29,7 @@ /// inspect((-3.7).trunc(), content="-3") /// inspect(0.0.trunc(), content="0") /// ``` +#as_free_fn pub fn Double::trunc(self : Double) -> Double = "Math" "trunc" ///| @@ -47,6 +48,7 @@ pub fn Double::trunc(self : Double) -> Double = "Math" "trunc" /// inspect((-3.7).ceil(), content="-3") /// inspect(42.0.ceil(), content="42") /// ``` +#as_free_fn pub fn Double::ceil(self : Double) -> Double = "Math" "ceil" ///| @@ -66,6 +68,7 @@ pub fn Double::ceil(self : Double) -> Double = "Math" "ceil" /// inspect((-3.7).floor(), content="-4") /// inspect(0.0.floor(), content="0") /// ``` +#as_free_fn pub fn Double::floor(self : Double) -> Double = "Math" "floor" ///| @@ -87,4 +90,5 @@ pub fn Double::floor(self : Double) -> Double = "Math" "floor" /// inspect(3.5.round(), content="4") /// inspect((-3.5).round(), content="-3") /// ``` +#as_free_fn pub fn Double::round(self : Double) -> Double = "Math" "round" diff --git a/bundled-core/double/round_wasm.mbt b/bundled-core/double/round_wasm.mbt index 584856e..e407b71 100644 --- a/bundled-core/double/round_wasm.mbt +++ b/bundled-core/double/round_wasm.mbt @@ -29,6 +29,7 @@ /// inspect((-3.7).trunc(), content="-3") /// inspect(0.0.trunc(), content="0") /// ``` +#as_free_fn pub fn Double::trunc(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.trunc (local.get $d)))" ///| @@ -47,6 +48,7 @@ pub fn Double::trunc(self : Double) -> Double = "(func (param $d f64) (result f6 /// inspect((-3.7).ceil(), content="-3") /// inspect(42.0.ceil(), content="42") /// ``` +#as_free_fn pub fn Double::ceil(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.ceil (local.get $d)))" ///| @@ -66,6 +68,7 @@ pub fn Double::ceil(self : Double) -> Double = "(func (param $d f64) (result f64 /// inspect((-3.7).floor(), content="-4") /// inspect(0.0.floor(), content="0") /// ``` +#as_free_fn pub fn Double::floor(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.floor (local.get $d)))" ///| @@ -87,6 +90,7 @@ pub fn Double::floor(self : Double) -> Double = "(func (param $d f64) (result f6 /// inspect(3.5.round(), content="4") /// inspect((-3.5).round(), content="-3") /// ``` +#as_free_fn pub fn Double::round(self : Double) -> Double { floor(self + 0.5) } diff --git a/bundled-core/double/trig_js.mbt b/bundled-core/double/trig_js.mbt index 2ae386d..4e36a8a 100644 --- a/bundled-core/double/trig_js.mbt +++ b/bundled-core/double/trig_js.mbt @@ -11,38 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("use `@math.sin` instead") -#coverage.skip -pub fn Double::sin(self : Double) -> Double = "Math" "sin" - -///| -#deprecated("use `@math.cos` instead") -#coverage.skip -pub fn Double::cos(self : Double) -> Double = "Math" "cos" - -///| -#deprecated("use `@math.tan` instead") -#coverage.skip -pub fn Double::tan(self : Double) -> Double = "Math" "tan" - -///| -#deprecated("use `@math.asin` instead") -#coverage.skip -pub fn Double::asin(self : Double) -> Double = "Math" "asin" - -///| -#deprecated("use `@math.acos` instead") -#coverage.skip -pub fn Double::acos(self : Double) -> Double = "Math" "acos" - -///| -#deprecated("use `@math.atan` instead") -#coverage.skip -pub fn Double::atan(self : Double) -> Double = "Math" "atan" - -///| -#deprecated("use `@math.atan2` instead") -#coverage.skip -pub fn Double::atan2(self : Double, x : Double) -> Double = "Math" "atan2" diff --git a/bundled-core/double/trig_nonjs.mbt b/bundled-core/double/trig_nonjs.mbt index b7bf9c2..94a2ea6 100644 --- a/bundled-core/double/trig_nonjs.mbt +++ b/bundled-core/double/trig_nonjs.mbt @@ -32,7 +32,7 @@ // ///| -let two_over_pi : Array[Int] = [ +let two_over_pi : FixedArray[Int] = [ 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, @@ -45,7 +45,7 @@ let two_over_pi : Array[Int] = [ ] ///| -let pi_over_2 : Array[Double] = [ +let pi_over_2 : FixedArray[Double] = [ 1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000 */ 7.54978941586159635335e-08, // 0x3E74442D, 0x00000000 */ 5.39030252995776476554e-15, // 0x3CF84698, 0x80000000 */ @@ -56,45 +56,6 @@ let pi_over_2 : Array[Double] = [ 2.16741683877804819444e-51, // 0x3569F31D, 0x00000000 */ ] -///| -let npio2_hw : Array[Int] = [ - 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, 0x4025FDBB, - 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, 0x40346B9C, 0x4035FDBB, - 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A, 0x403DD85A, 0x403F6A7A, 0x40407E4C, - 0x4041475C, 0x4042106C, 0x4042D97C, 0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, - 0x4046C6CB, 0x40478FDB, 0x404858EB, 0x404921FB, -] - -///| -const PI_OVER_4 : Double = 0.785398163397448309616 - -///| -const PIO2_1 : Double = 1.5707963267341256e+00 // 0x3FF921FB, 0x54400000 */ - -///| -const PIO2_1T : Double = 6.0771005065061922e-11 // 0x3DD0B461, 0x1A600000 */ - -///| -const PIO2_2 : Double = 6.0771005063039660e-11 // 0x3DD0B461, 0x1A600000 */ - -///| -const PIO2_2T : Double = 2.0222662487959506e-21 // 0x3BA3198A, 0x2E037073 */ - -///| -const PIO2_3 : Double = 2.0222662487111665e-21 // 0x3BA3198A, 0x2E037073 */ - -///| -const PIO2_3T : Double = 8.4784276603688996e-32 // 0x397B839A, 0x252049C1 */ - -///| -const INV_PIO2 : Double = 6.3661977236758138e-01 // 0x3FE45F30, 0x6DC9C883 */ - -///| -const HALF : Double = 0.5 - -///| -const TWO24 : Double = 16777216.0 // 0x41700000, 0x00000000 */ - ///| fn set_low_word(d : Double, v : UInt) -> Double { let bits : UInt64 = d.reinterpret_as_uint64() @@ -103,14 +64,6 @@ fn set_low_word(d : Double, v : UInt) -> Double { bits.reinterpret_as_double() } -///| -fn set_high_word(d : Double, v : UInt) -> Double { - let bits : UInt64 = d.reinterpret_as_uint64() - let bits = bits & 0x0000_0000_FFFF_FFFF - let bits = bits | (v.to_uint64() << 32) - bits.reinterpret_as_double() -} - ///| fn get_high_word(x : Double) -> UInt { (x.reinterpret_as_uint64() >> 32).to_uint() @@ -121,128 +74,13 @@ fn get_low_word(x : Double) -> UInt { x.reinterpret_as_uint64().to_uint() } -///| -fn rem_pio2(x : Double, y : Array[Double]) -> Int { - let hx = get_high_word(x).reinterpret_as_int() - let ix : Int = hx & 0x7fffffff - let mut z = 0.0 - if ix <= 0x3fe921fb { - // |x| <= pi/4, no reduction needed - y[0] = x - y[1] = 0.0 - return 0 - } - if ix < 0x4002d97c { - // |x| < 3pi/4, special case with n = +-1 - if hx > 0 { - z = x - PIO2_1 - if ix != 0x3ff921fb { - // 33+53 bit pi is good enough - y[0] = z - PIO2_1T - y[1] = z - y[0] - PIO2_1T - } else { - // Near pi/2, use 33+33+53 bit pi - z = z - PIO2_2 - y[0] = z - PIO2_2T - y[1] = z - y[0] - PIO2_2T - } - return 1 - } else { - // Negative x - z = x + PIO2_1 - if ix != 0x3ff921fb { - // 33+53 bit pi is good enough - y[0] = z + PIO2_1T - y[1] = z - y[0] + PIO2_1T - } else { - // Near pi/2, use 33+33+53 bit pi - let z = z + PIO2_2 - y[0] = z + PIO2_2T - y[1] = z - y[0] + PIO2_2T - } - return -1 - } - } - if ix <= 0x413921fb { - // |x| <= 2^19 * (pi/2), medium size - let t = x.abs() - let n = (t * INV_PIO2 + HALF).to_int() - let fn_ = n.to_double() - let mut r = t - fn_ * PIO2_1 - let mut w = fn_ * PIO2_1T - if n < 32 && ix != npio2_hw[n - 1] { - y[0] = r - w - } else { - let j = ix >> 20 - y[0] = r - w - let i = j - ((get_high_word(y[0]) >> 20).reinterpret_as_int() & 0x7ff) - if i > 16 { - // 2nd iteration needed, good to 118 bits - let t = r - w = fn_ * PIO2_2 - r = t - w - w = fn_ * PIO2_2T - (t - r - w) - y[0] = r - w - let i = j - ((get_high_word(y[0]) >> 20).reinterpret_as_int() & 0x7ff) - if i > 49 { - // 3rd iteration needed, 151 bits accuracy - let t = r - w = fn_ * PIO2_3 - r = t - w - w = fn_ * PIO2_3T - (t - r - w) - y[0] = r - w - } - } - } - y[1] = r - y[0] - w - if hx > 0 { - return n - } else { - y[0] = -y[0] - y[1] = -y[1] - return -n - } - } - - // All other (large) arguments - if ix >= 0x7ff00000 { - // x is inf or NaN - y[0] = x - x - y[1] = y[0] - return 0 - } - - // Set z = scalbn(|x|, ilogb(x) - 23) - z = set_low_word(z, get_low_word(x)) - let e0 = (ix >> 20) - 1046 // e0 = ilogb(z) - 23 - z = set_high_word(z, (ix - (e0 << 20)).reinterpret_as_uint()) - let tx = [0.0, 0.0, 0.0] - for i in 0..<2 { - tx[i] = z.to_int().to_double() - z = (z - tx[i]) * TWO24 - } - tx[2] = z - let mut nx = 3 - while tx[nx - 1] == 0.0 { - nx -= 1 - } - let n = __kernel_rem_pio2(tx, y, e0, nx, 2) - if hx > 0 { - n - } else { - y[0] = -y[0] - y[1] = -y[1] - -n - } -} - ///| fn __kernel_rem_pio2( x : Array[Double], y : Array[Double], e0 : Int, nx : Int, - prec : Int + prec : Int, ) -> Int { let init_jk = [2, 3, 4, 6] let two24 : Double = 16777216.0 // 0x41700000, 0x00000000 @@ -663,334 +501,3 @@ fn __kernal_tan(x : Double, y : Double, iy : Int) -> Double { t + a * (s + t * v) } } - -///| -#deprecated("use `@math.sin` instead") -#coverage.skip -pub fn Double::sin(self : Double) -> Double { - if self.is_inf() || self.is_nan() { - return not_a_number - } - let y = [0.0, 0.0] - let z = 0.0 - if self.abs() <= PI_OVER_4 { - return __kernel_sin(self, z, 0) - } else { - let n = rem_pio2(self, y) - match n & 3 { - 0 => __kernel_sin(y[0], y[1], 1) - 1 => __kernel_cos(y[0], y[1]) - 2 => -__kernel_sin(y[0], y[1], 1) - _ => -__kernel_cos(y[0], y[1]) - } - } -} - -///| -#deprecated("use `@math.cos` instead") -#coverage.skip -pub fn Double::cos(self : Double) -> Double { - if self.is_inf() || self.is_nan() { - return not_a_number - } - let y = [0.0, 0.0] - let z = 0.0 - if self.abs() <= PI_OVER_4 { - return __kernel_cos(self, z) - } else { - let n = rem_pio2(self, y) - match n & 3 { - 0 => __kernel_cos(y[0], y[1]) - 1 => -__kernel_sin(y[0], y[1], 1) - 2 => -__kernel_cos(y[0], y[1]) - _ => __kernel_sin(y[0], y[1], 1) - } - } -} - -///| -#deprecated("use `@math.tan` instead") -#coverage.skip -pub fn Double::tan(self : Double) -> Double { - if self.is_inf() || self.is_nan() { - return not_a_number - } - let y = Array::make(2, 0.0) - let z = 0.0 - if self.abs() <= PI_OVER_4 { - __kernal_tan(self, z, 1) - } else { - let n = rem_pio2(self, y) - __kernal_tan(y[0], y[1], 1 - ((n & 1) << 1)) - } -} - -///| -#deprecated("use `@math.asin` instead") -#coverage.skip -pub fn Double::asin(self : Double) -> Double { - let huge = 1.0e+300 - let pio4_hi = 7.85398163397448278999e-01 - let pio2_hi = 1.57079632679489655800 - let pio2_lo = 6.12323399573676603587e-17 - let ps0 = 1.66666666666666657415e-01 - let ps1 = -3.25565818622400915405e-01 - let ps2 = 2.01212532134862925881e-01 - let ps3 = -4.00555345006794114027e-02 - let ps4 = 7.91534994289814532176e-04 - let ps5 = 3.47933107596021167570e-05 - let qs1 = -2.40339491173441421878e+00 - let qs2 = 2.02094576023350569471e+00 - let qs3 = -6.88283971605453293030e-01 - let qs4 = 7.70381505559019352791e-02 - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - let absx = self.abs() - if absx >= 1.0 { - if absx == 1.0 { - return self * pio2_hi + self * pio2_lo - } else { - return not_a_number - } - } else if absx < 0.5 { - if ix < 0x3e400000 { - if huge + self > 1.0 { - return self - } - } else { - let t = self * self - let p = t * - (ps0 + t * (ps1 + t * (ps2 + t * (ps3 + t * (ps4 + t * ps5))))) - let q = 1.0 + t * (qs1 + t * (qs2 + t * (qs3 + t * qs4))) - let w = p / q - return self + self * w - } - } - let w = 1.0 - absx - let t = w * 0.5 - let p = t * (ps0 + t * (ps1 + t * (ps2 + t * (ps3 + t * (ps4 + t * ps5))))) - let q = 1.0 + t * (qs1 + t * (qs2 + t * (qs3 + t * qs4))) - let s = t.sqrt() - if ix >= 0x3FEF3333 { - let w = p / q - let t = pio2_hi - (2.0 * (s + s * w) - pio2_lo) - return if self > 0.0 { t } else { -t } - } else { - let mut w = s - w = set_low_word(w, 0) - let c = (t - w * w) / (s + w) - let r = p / q - let p = 2.0 * s * r - (pio2_lo - 2.0 * c) - let q = pio4_hi - 2.0 * w - let t = pio4_hi - (p - q) - return if self > 0.0 { t } else { -t } - } -} - -///| -#deprecated("use `@math.acos` instead") -#coverage.skip -pub fn Double::acos(self : Double) -> Double { - let one : Double = 1.0 - let pi : Double = 3.14159265358979311600 - let pio2_hi : Double = 1.57079632679489655800 - let pio2_lo : Double = 6.12323399573676603587e-17 - let ps0 : Double = 1.66666666666666657415e-01 - let ps1 : Double = -3.25565818622400915405e-01 - let ps2 : Double = 2.01212532134862925881e-01 - let ps3 : Double = -4.00555345006794114027e-02 - let ps4 : Double = 7.91534994289814532176e-04 - let ps5 : Double = 3.47933107596021167570e-05 - let qs1 : Double = -2.40339491173441421878e+00 - let qs2 : Double = 2.02094576023350569471e+00 - let qs3 : Double = -6.88283971605453293030e-01 - let qs4 : Double = 7.70381505559019352791e-02 - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - let absx = self.abs() - if absx >= 1.0 { - if absx == 1.0 { - if self > 0 { - return 0.0 - } else { - return pi + 2.0 * pio2_lo - } - } - return not_a_number - } - if absx < 0.5 { - if ix <= 0x3c600000 { - return pio2_hi + pio2_lo - } - let z = self * self - let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5))))) - let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4))) - let r = p / q - pio2_hi - (self - (pio2_lo - self * r)) - } else if self < 0 { - let z = (one + self) * 0.5 - let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5))))) - let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4))) - let s = z.sqrt() - let r = p / q - let w = r * s - pio2_lo - pi - 2.0 * (s + w) - } else { - let z = (one - self) * 0.5 - let s = z.sqrt() - let df = s - let c = (z - df * df) / (s + df) - let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5))))) - let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4))) - let r = p / q - let w = r * s + c - 2.0 * (df + w) - } -} - -///| -#deprecated("use `@math.atan` instead") -#coverage.skip -pub fn Double::atan(self : Double) -> Double { - if self.is_nan() || self == 0.0 { - return self - } - let atan_hi = [ - 4.63647609000806093515e-01, 7.85398163397448278999e-01, 9.82793723247329054082e-01, - 1.57079632679489655800e+00, - ] - let atan_lo = [ - 2.26987774529616870924e-17, 3.06161699786838301793e-17, 1.39033110312309984516e-17, - 6.12323399573676603587e-17, - ] - let a_t = [ - 3.33333333333329318027e-01, -1.99999999998764832476e-01, 1.42857142725034663711e-01, - -1.11111104054623557880e-01, 9.09088713343650656196e-02, -7.69187620504482999495e-02, - 6.66107313738753120669e-02, -5.83357013379057348645e-02, 4.97687799461593236017e-02, - -3.65315727442169155270e-02, 1.62858201153657823623e-02, - ] - let one = 1.0 - let huge = 1.0e300 - let ix = get_high_word(self).reinterpret_as_int() & 0x7fffffff - let mut id = 0 - let mut z = 0.0 - let mut w = 0.0 - let mut self = self - let x_is_neg = self < 0.0 - if ix >= 0x44100000 { - if self > 0 { - return atan_hi[3] + atan_lo[3] - } else { - return -atan_hi[3] - atan_lo[3] - } - } - if ix < 0x3fdc0000 { - if ix < 0x3e200000 { - if huge + self > one { - return self - } - } - id = -1 - } else { - self = self.abs() - if ix < 0x3ff30000 { - if ix < 0x3fe60000 { - id = 0 - self = (2.0 * self - one) / (2.0 + self) - } else { - id = 1 - self = (self - one) / (self + one) - } - } else if ix < 0x40038000 { - id = 2 - self = (self - 1.5) / (one + 1.5 * self) - } else { - id = 3 - self = -1.0 / self - } - } - z = self * self - w = z * z - let s1 = z * - ( - a_t[0] + - w * (a_t[2] + w * (a_t[4] + w * (a_t[6] + w * (a_t[8] + w * a_t[10])))) - ) - let s2 = w * - (a_t[1] + w * (a_t[3] + w * (a_t[5] + w * (a_t[7] + w * a_t[9])))) - if id < 0 { - self - self * (s1 + s2) - } else { - z = atan_hi[id] - (self * (s1 + s2) - atan_lo[id] - self) - if x_is_neg { - -z - } else { - z - } - } -} - -///| -#deprecated("use `@math.atan2` instead") -#coverage.skip -pub fn Double::atan2(self : Double, x : Double) -> Double { - if x.is_nan() || self.is_nan() { - return not_a_number - } - let tiny = 1.0e-300 - let zero = 0.0 - let pi_o_4 = 7.8539816339744827900E-01 - let pi_o_2 = 1.5707963267948965580E+00 - let pi = 3.1415926535897931160E+00 - let pi_lo = 1.2246467991473531772E-16 - let hx = get_high_word(x).reinterpret_as_int() - let hy = get_high_word(self).reinterpret_as_int() - let ix = hx & 0x7fffffff - let iy = hy & 0x7fffffff - if x == 1.0 { - return self.atan() - } - let m = ((hy >> 31) & 1) | ((hx >> 30) & 2) - if self == 0 { - match m { - 0 | 1 => return self - 2 => return pi + tiny - _ => return -pi - tiny - } - } - if x == 0 { - return if hy < 0 { -pi_o_2 - tiny } else { pi_o_2 + tiny } - } - if x.is_inf() { - if self.is_inf() { - match m { - 0 => return pi_o_4 + tiny - 1 => return -pi_o_4 - tiny - 2 => return 3.0 * pi_o_4 + tiny - _ => return -3.0 * pi_o_4 - tiny - } - } else { - match m { - 0 => return zero - 1 => return -zero - 2 => return pi + tiny - _ => return -pi - tiny - } - } - } - if self.is_inf() { - return if hy < 0 { -pi_o_2 - tiny } else { pi_o_2 + tiny } - } - let k = (iy - ix) >> 20 - let z = if k > 60 { - pi_o_2 + 0.5 * pi_lo - } else if hx < 0 && k < -60 { - 0.0 - } else { - (self / x).abs().atan() - } - match m { - 0 => z - 1 => -z - 2 => pi - (z - pi_lo) - _ => z - pi_lo - pi - } -} diff --git a/bundled-core/encoding/utf8/decode.mbt b/bundled-core/encoding/utf8/decode.mbt new file mode 100644 index 0000000..ce2dc1a --- /dev/null +++ b/bundled-core/encoding/utf8/decode.mbt @@ -0,0 +1,174 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +pub suberror Malformed @bytes.View + +///| +/// The Unicode Replacement Character, which is used to replace invalid or unrecognized sequences during lossy decoding. +/// https://unicode.org/charts/nameslist/n_FFF0.html +const U_REP = '\u{FFFD}' + +///| +pub fn decode( + bytes : @bytes.View, + ignore_bom? : Bool = false, +) -> String raise Malformed { + let bytes = if ignore_bom && bytes is [.. "\xef\xbb\xbf", .. rest] { + rest + } else { + bytes + } + let builder = StringBuilder::new(size_hint=bytes.length()) + loop bytes { + [] => () + [0..=0x7F as b, .. rest] => { + builder.write_char(b.to_int().unsafe_to_char()) + continue rest + } + [0xC2..=0xDF as b0, 0x80..=0xBF as b1, .. rest] => { + let ch = ((b0.to_int() & 0x1F) << 6) | (b1.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + [0xE0 as b0, 0xA0..=0xBF as b1, 0x80..=0xBF as b2, .. rest] + | [0xE1..=0xEC as b0, 0x80..=0xBF as b1, 0x80..=0xBF as b2, .. rest] + | [0xED as b0, 0x80..=0x9F as b1, 0x80..=0xBF as b2, .. rest] + | [0xEE..=0xEF as b0, 0x80..=0xBF as b1, 0x80..=0xBF as b2, .. rest] => { + let ch = ((b0.to_int() & 0x0F) << 12) | + ((b1.to_int() & 0x3F) << 6) | + (b2.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + [ + 0xF0 as b0, + 0x90..=0xBF as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] + | [ + 0xF1..=0xF3 as b0, + 0x80..=0xBF as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] + | [ + 0xF4 as b0, + 0x80..=0x8F as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] => { + let ch = ((b0.to_int() & 0x07) << 18) | + ((b1.to_int() & 0x3F) << 12) | + ((b2.to_int() & 0x3F) << 6) | + (b3.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + _ as bytes => raise Malformed(bytes) + } + builder.to_string() +} + +///| +/// +/// References : +/// - https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G66453 +/// - https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-5/#G40630 +pub fn decode_lossy(bytes : @bytes.View, ignore_bom? : Bool = false) -> String { + let bytes = if ignore_bom && bytes is [.. "\xef\xbb\xbf", .. rest] { + rest + } else { + bytes + } + let builder = StringBuilder::new(size_hint=bytes.length()) + loop bytes { + [] => () + [0..=0x7F as b, .. rest] => { + builder.write_char(b.to_int().unsafe_to_char()) + continue rest + } + [0xC2..=0xDF as b0, 0x80..=0xBF as b1, .. rest] => { + let ch = ((b0.to_int() & 0x1F) << 6) | (b1.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + [0xE0 as b0, 0xA0..=0xBF as b1, 0x80..=0xBF as b2, .. rest] + | [0xE1..=0xEC as b0, 0x80..=0xBF as b1, 0x80..=0xBF as b2, .. rest] + | [0xED as b0, 0x80..=0x9F as b1, 0x80..=0xBF as b2, .. rest] + | [0xEE..=0xEF as b0, 0x80..=0xBF as b1, 0x80..=0xBF as b2, .. rest] => { + let ch = ((b0.to_int() & 0x0F) << 12) | + ((b1.to_int() & 0x3F) << 6) | + (b2.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + [0xE0, 0xA0..=0xBF, .. rest] + | [0xE1..=0xEC, 0x80..=0xBF, .. rest] + | [0xED, 0x80..=0x9F, .. rest] + | [0xEE..=0xEF, 0x80..=0xBF, .. rest] => { + builder.write_char(U_REP) + continue rest + } + [ + 0xF0 as b0, + 0x90..=0xBF as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] + | [ + 0xF1..=0xF3 as b0, + 0x80..=0xBF as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] + | [ + 0xF4 as b0, + 0x80..=0x8F as b1, + 0x80..=0xBF as b2, + 0x80..=0xBF as b3, + .. rest, + ] => { + let ch = ((b0.to_int() & 0x07) << 18) | + ((b1.to_int() & 0x3F) << 12) | + ((b2.to_int() & 0x3F) << 6) | + (b3.to_int() & 0x3F) + builder.write_char(ch.unsafe_to_char()) + continue rest + } + [0xF0, 0x90..=0xBF, 0x80..=0xBF, .. rest] + | [0xF1..=0xF3, 0x80..=0xBF, 0x80..=0xBF, .. rest] + | [0xF4, 0x80..=0x8F, 0x80..=0xBF, .. rest] => { + builder.write_char(U_REP) + continue rest + } + [0xF0, 0x90..=0xBF, .. rest] + | [0xF1..=0xF3, 0x80..=0xBF, .. rest] + | [0xF4, 0x80..=0x8F, .. rest] => { + builder.write_char(U_REP) + continue rest + } + [_, .. rest] => { + builder.write_char(U_REP) + continue rest + } + } + builder.to_string() +} diff --git a/bundled-core/encoding/utf8/decode_test.mbt b/bundled-core/encoding/utf8/decode_test.mbt new file mode 100644 index 0000000..204b263 --- /dev/null +++ b/bundled-core/encoding/utf8/decode_test.mbt @@ -0,0 +1,61 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// Reference : https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G66453 +test "U+FFFD" { + let table_3_8 = b"\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41" + inspect(@utf8.decode_lossy(table_3_8), content="๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝA") + let table_3_9 = b"\xed\xa0\x80\xed\xbf\xbf\xed\xaf\x41" + inspect(@utf8.decode_lossy(table_3_9), content="๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝA") + let table_3_10 = b"\xf4\x91\x92\x93\xff\x41\x80\xbf\x42" + inspect(@utf8.decode_lossy(table_3_10), content="๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝA๏ฟฝ๏ฟฝB") + let table_3_11 = b"\xe1\x80\xe2\xf0\x91\x92\xf1\xbf\x41" + inspect(@utf8.decode_lossy(table_3_11), content="๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝA") +} + +///| +test "decoding UTF8 encoded data to String" { + let buf = @buffer.new(size_hint=10) + buf.write_bytes(b"abc") + buf.write_bytes(b"\xe4\xbd\xa0") // ไฝ  + buf.write_bytes(b"\xe5\xa5\xbd") // ๅฅฝ + buf.write_bytes(b"\xf0\x9f\x91\x80") // ๐Ÿ‘€ + let chars = @utf8.decode(buf.to_bytes()) + inspect(chars, content="abcไฝ ๅฅฝ๐Ÿ‘€") +} + +///| +test "decoding UTF8 invalid data to String" { + let table_3_8 = b"\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41" + try { + let _ = @utf8.decode(table_3_8) + panic() + } catch { + Malformed(e) => + inspect( + e, + content=( + #|b"\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41" + ), + ) + } +} + +///| +test "decoding UTF8 with bom" { + let text = b"\xef\xbb\xbf\x61\x62\x63\xe4\xbd\xa0\xe5\xa5\xbd\xf0\x9f\x91\x80" + inspect(try! @utf8.decode(text), content="๏ปฟabcไฝ ๅฅฝ๐Ÿ‘€") + inspect(try! @utf8.decode(text, ignore_bom=true), content="abcไฝ ๅฅฝ๐Ÿ‘€") +} diff --git a/bundled-core/encoding/utf8/encode.mbt b/bundled-core/encoding/utf8/encode.mbt new file mode 100644 index 0000000..9478817 --- /dev/null +++ b/bundled-core/encoding/utf8/encode.mbt @@ -0,0 +1,29 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +const U_BOM : Char = '\u{FEFF}' + +///| +/// Encodes a string into a UTF-8 byte array. +/// +/// Panics if the string contains any invalid char (out of 0..=10FFFF) +pub fn encode(str : @string.View, bom? : Bool = false) -> Bytes { + let buffer = @buffer.new(size_hint=str.length() * 4) + if bom is true { + buffer.write_char_utf8(U_BOM) + } + buffer.write_string_utf8(str) + buffer.to_bytes() +} diff --git a/bundled-core/encoding/utf8/encode_test.mbt b/bundled-core/encoding/utf8/encode_test.mbt new file mode 100644 index 0000000..36a1a95 --- /dev/null +++ b/bundled-core/encoding/utf8/encode_test.mbt @@ -0,0 +1,32 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "encode" { + let s = "abcไฝ ๅฅฝ๐Ÿ‘€" + let encoded = @utf8.encode(s) + inspect( + encoded, + content=( + #|b"\x61\x62\x63\xe4\xbd\xa0\xe5\xa5\xbd\xf0\x9f\x91\x80" + ), + ) + let encoded_with_bom = @utf8.encode(s, bom=true) + inspect( + encoded_with_bom, + content=( + #|b"\xef\xbb\xbf\x61\x62\x63\xe4\xbd\xa0\xe5\xa5\xbd\xf0\x9f\x91\x80" + ), + ) +} diff --git a/bundled-core/encoding/utf8/moon.pkg.json b/bundled-core/encoding/utf8/moon.pkg.json new file mode 100644 index 0000000..c2a72e1 --- /dev/null +++ b/bundled-core/encoding/utf8/moon.pkg.json @@ -0,0 +1,8 @@ +{ + "import": [ + "moonbitlang/core/buffer", + "moonbitlang/core/string", + "moonbitlang/core/bytes", + "moonbitlang/core/builtin" + ] +} \ No newline at end of file diff --git a/bundled-core/encoding/utf8/pkg.generated.mbti b/bundled-core/encoding/utf8/pkg.generated.mbti new file mode 100644 index 0000000..b869e26 --- /dev/null +++ b/bundled-core/encoding/utf8/pkg.generated.mbti @@ -0,0 +1,24 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/encoding/utf8" + +import( + "moonbitlang/core/bytes" + "moonbitlang/core/string" +) + +// Values +fn decode(@bytes.View, ignore_bom? : Bool) -> String raise Malformed + +fn decode_lossy(@bytes.View, ignore_bom? : Bool) -> String + +fn encode(@string.View, bom? : Bool) -> Bytes + +// Errors +pub suberror Malformed @bytes.View + +// Types and methods + +// Type aliases + +// Traits + diff --git a/bundled-core/env/README.mbt.md b/bundled-core/env/README.mbt.md new file mode 100644 index 0000000..8d4552e --- /dev/null +++ b/bundled-core/env/README.mbt.md @@ -0,0 +1,289 @@ +# Env Package Documentation + +This package provides utilities for interacting with the runtime environment, including access to command line arguments, current time, and working directory information. + +## Command Line Arguments + +Access command line arguments passed to your program: + +```moonbit +test "command line arguments" { + let arguments = @env.args() + + // The arguments array contains program arguments + // In a test environment, this will typically be empty or contain test runner args + inspect(arguments.length() >= 0, content="true") + + // Example of how you might process arguments in a real program: + fn process_args(args : Array[String]) -> String { + if args.length() == 0 { + "No arguments provided" + } else { + "First argument: " + args[0] + } + } + + let result = process_args(arguments) + inspect(result.length() > 0, content="true") +} +``` + +## Current Time + +Get the current time in milliseconds since Unix epoch: + +```moonbit +test "current time" { + let timestamp = @env.now() + + // Timestamp should be a reasonable value (after year 2020) + let year_2020_ms = 1577836800000UL // Jan 1, 2020 in milliseconds + inspect(timestamp > year_2020_ms, content="true") + + // Demonstrate time-based operations + fn format_timestamp(ts : UInt64) -> String { + "Timestamp: " + ts.to_string() + } + + let formatted = format_timestamp(timestamp) + inspect(formatted.length() > 10, content="true") // Should contain timestamp data +} +``` + +## Working Directory + +Get the current working directory: + +```moonbit +test "working directory" { + let cwd = @env.current_dir() + + match cwd { + Some(path) => { + // We have a current directory + inspect(path.length() > 0, content="true") + inspect(path.length() > 1, content="true") // Should be a meaningful path + } + None => { + // Current directory unavailable (some platforms/environments) + inspect(true, content="true") // This is also valid + } + } +} +``` + +## Practical Usage Examples + +### Command Line Tool Pattern + +```moonbit +test "command line tool pattern" { + fn parse_command(args : Array[String]) -> Result[String, String] { + if args.length() < 2 { + Err("Usage: program [args...]") + } else { + match args[1] { + "help" => Ok("Showing help information") + "version" => Ok("Version 1.0.0") + "status" => Ok("System is running") + cmd => Err("Unknown command: " + cmd) + } + } + } + + // Test with mock arguments + let test_args = ["program", "help"] + let result = parse_command(test_args) + inspect(result, content="Ok(\"Showing help information\")") + + let invalid_result = parse_command(["program", "invalid"]) + match invalid_result { + Ok(_) => inspect(false, content="true") + Err(msg) => inspect(msg.length() > 10, content="true") // Should have error message + } +} +``` + +### Configuration Loading + +```moonbit +test "configuration loading" { + fn load_config_path() -> String { + match @env.current_dir() { + Some(cwd) => cwd + "/config.json" + None => "./config.json" // Fallback + } + } + + let config_path = load_config_path() + inspect(config_path.length() > 10, content="true") // Should have path with config.json +} +``` + +### Logging with Timestamps + +```moonbit +test "logging with timestamps" { + fn log_message(level : String, message : String) -> String { + let timestamp = @env.now() + "[" + timestamp.to_string() + "] " + level + ": " + message + } + + let log_entry = log_message("INFO", "Application started") + inspect(log_entry.length() > 20, content="true") // Should have timestamp and message + inspect(log_entry.length() > 10, content="true") // Should have substantial content +} +``` + +### File Path Operations + +```moonbit +test "file path operations" { + fn resolve_relative_path(relative : String) -> String { + match @env.current_dir() { + Some(base) => base + "/" + relative + None => relative + } + } + + let resolved = resolve_relative_path("data/input.txt") + inspect(resolved.length() > 10, content="true") // Should have resolved path +} +``` + +## Platform Differences + +The env package behaves differently across platforms: + +### JavaScript Environment +- `args()` returns arguments from the JavaScript environment +- `@env.now()` uses `Date.@env.now()` +- `@env.current_dir()` may return `None` in browser environments + +### WebAssembly Environment +- `args()` behavior depends on the WASM host +- `@env.now()` provides millisecond precision timing +- `@env.current_dir()` availability depends on host capabilities + +### Native Environment +- `args()` returns actual command line arguments +- `@env.now()` provides system time +- `@env.current_dir()` uses system calls to get working directory + +## Error Handling + +Handle cases where environment information is unavailable: + +```moonbit +test "error handling" { + fn safe_get_cwd() -> String { + match @env.current_dir() { + Some(path) => path + None => { + // Fallback when current directory is unavailable + "." + } + } + } + + let safe_cwd = safe_get_cwd() + inspect(safe_cwd.length() > 0, content="true") + + fn validate_args(args : Array[String], min_count : Int) -> Result[Unit, String] { + if args.length() < min_count { + Err("Insufficient arguments: expected at least " + min_count.to_string()) + } else { + Ok(()) + } + } + + let validation = validate_args(["prog"], 2) + match validation { + Ok(_) => inspect(false, content="true") + Err(msg) => inspect(msg.length() > 10, content="true") // Should have error message + } +} +``` + +## Best Practices + +### 1. Handle Missing Environment Data Gracefully + +```moonbit +test "graceful handling" { + fn get_work_dir() -> String { + match @env.current_dir() { + Some(dir) => dir + None => "~" // Fallback to home directory symbol + } + } + + let work_dir = get_work_dir() + inspect(work_dir.length() > 0, content="true") +} +``` + +### 2. Validate Command Line Arguments + +```moonbit +test "argument validation" { + fn validate_and_parse_args(args : Array[String]) -> Result[(String, Array[String]), String] { + if args.length() == 0 { + Err("No program name available") + } else if args.length() == 1 { + Ok((args[0], [])) // Program name only, no arguments + } else { + let program = args[0] + let arguments = Array::new() + for i in 1.. { + inspect(prog, content="myprogram") + inspect(args.length(), content="2") + } + Err(_) => inspect(false, content="true") + } +} +``` + +### 3. Use Timestamps for Unique Identifiers + +```moonbit +test "unique identifiers" { + fn generate_unique_id(prefix : String) -> String { + prefix + "_" + @env.now().to_string() + } + + let id1 = generate_unique_id("task") + let id2 = generate_unique_id("task") + + inspect(id1.length() > 10, content="true") // Should have task prefix and timestamp + inspect(id2.length() > 10, content="true") // Should have task prefix and timestamp + // IDs should be different (though they might be the same in fast tests) +} +``` + +## Common Use Cases + +1. **Command Line Tools**: Parse arguments and provide help/usage information +2. **Configuration Management**: Load config files relative to current directory +3. **Logging Systems**: Add timestamps to log entries +4. **File Processing**: Resolve relative file paths +5. **Debugging**: Include environment information in error reports +6. **Build Tools**: Determine working directory for relative path operations + +## Performance Considerations + +- `args()` is typically called once at program startup +- `@env.now()` is lightweight but avoid calling in tight loops if high precision isn't needed +- `@env.current_dir()` may involve system calls, so cache the result if used frequently +- Environment functions are generally fast but platform-dependent + +The env package provides essential runtime environment access for building robust MoonBit applications that interact with their execution environment. diff --git a/bundled-core/env/env_native.mbt b/bundled-core/env/env_native.mbt index 4e9bd2a..dff26b0 100644 --- a/bundled-core/env/env_native.mbt +++ b/bundled-core/env/env_native.mbt @@ -82,6 +82,7 @@ fn now_internal() -> UInt64 { } ///| +#borrow(ptr) extern "c" fn getcwd(ptr : Bytes, size : Int) -> Int = "getcwd" ///| diff --git a/bundled-core/env/env_wasm.mbt b/bundled-core/env/env_wasm.mbt index 9c9a004..728b32e 100644 --- a/bundled-core/env/env_wasm.mbt +++ b/bundled-core/env/env_wasm.mbt @@ -23,7 +23,8 @@ priv type XExternString ///| fn begin_read_string(s : XExternString) -> XStringReadHandle = "__moonbit_fs_unstable" "begin_read_string" -///| Read one char from the string, returns -1 if the end of the string is reached. +///| +/// Read one char from the string, returns -1 if the end of the string is reached. /// The number returned is the unicode codepoint of the character. fn string_read_char(handle : XStringReadHandle) -> Int = "__moonbit_fs_unstable" "string_read_char" diff --git a/bundled-core/env/env.mbti b/bundled-core/env/pkg.generated.mbti similarity index 74% rename from bundled-core/env/env.mbti rename to bundled-core/env/pkg.generated.mbti index 80631eb..4970050 100644 --- a/bundled-core/env/env.mbti +++ b/bundled-core/env/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/env" // Values @@ -7,6 +8,8 @@ fn current_dir() -> String? fn now() -> UInt64 +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/error/README.mbt.md b/bundled-core/error/README.mbt.md new file mode 100644 index 0000000..d0edd1d --- /dev/null +++ b/bundled-core/error/README.mbt.md @@ -0,0 +1,210 @@ +# Error Package Documentation + +This package provides utilities for working with MoonBit's error handling system, including implementations of `Show` and `ToJson` traits for the built-in `Error` type. + +## Basic Error Usage + +MoonBit uses a structured error system with `raise` and `try` constructs: + +```moonbit +test "basic error handling" { + fn divide(a : Int, b : Int) -> Int raise { + if b == 0 { + raise Failure("Division by zero") + } else { + a / b + } + } + + // Successful operation + let result1 = try! divide(10, 2) + inspect(result1, content="5") + + // Handle error with try? + let result2 = try? divide(10, 0) + inspect(result2, content="Err(Failure(\"Division by zero\"))") +} +``` + +## Custom Error Types + +Define custom error types using `suberror`: + +```moonbit +suberror ValidationError String +suberror NetworkError String + +test "custom errors" { + fn validate_email(email : String) -> String raise ValidationError { + if email.length() > 5 { // Simple validation without string methods + email + } else { + raise ValidationError("Invalid email format") + } + } + + fn fetch_data(url : String) -> String raise NetworkError { + if url.length() > 10 { // Simple validation + "data" + } else { + raise NetworkError("Invalid URL") + } + } + + // Test validation error + let email_result = try? validate_email("short") + match email_result { + Ok(_) => inspect(false, content="true") + Err(_) => inspect(true, content="true") + } + + // Test network error + let data_result = try? fetch_data("short") + match data_result { + Ok(_) => inspect(false, content="true") + Err(_) => inspect(true, content="true") + } +} +``` + +## Error Display and JSON Conversion + +The error package provides `Show` and `ToJson` implementations: + +```moonbit +suberror MyError Int derive(ToJson(style="flat")) + +test "error display and json" { + let error : Error = MyError(42) + + // Error can be displayed as string + let error_string = error.to_string() + inspect(error_string.length() > 0, content="true") + + // Error can be converted to JSON + let error_json = error.to_json() + inspect(error_json, content="Array([String(\"MyError\"), Number(42)])") +} +``` + +## Error Propagation and Handling + +Handle errors at different levels of your application: + +```moonbit +suberror ParseError String +suberror FileError String + +test "error propagation" { + fn parse_number(s : String) -> Int raise ParseError { + if s == "42" { + 42 + } else { + raise ParseError("Invalid number: " + s) + } + } + + fn read_and_parse(content : String) -> Int raise { + try parse_number(content) catch { + ParseError(msg) => raise FileError("Parse failed: " + msg) + } + } + + // Success case + let result1 = try! read_and_parse("42") + inspect(result1, content="42") + + // Error propagation + let result2 = try? read_and_parse("invalid") + match result2 { + Ok(_) => inspect(false, content="true") + Err(_) => inspect(true, content="true") + } +} +``` + +## Resource Management with Finally + +Use `protect` functions for resource cleanup: + +```moonbit +suberror ResourceError String + +test "resource management" { + fn risky_operation() -> String raise ResourceError { + raise ResourceError("Something went wrong") + } + + // Simple resource management pattern + fn use_resource() -> String raise { + // Acquire resource (simulated) + try { + risky_operation() + } catch { + ResourceError(_) => { + // Cleanup happens here + raise Failure("Operation failed after cleanup") + } + } + } + + let result = try? use_resource() + match result { + Ok(_) => inspect(false, content="true") + Err(_) => inspect(true, content="true") + } +} +``` + +## Error Composition + +Combine multiple error-producing operations: + +```moonbit +suberror ConfigError String +suberror DatabaseError String + +test "error composition" { + fn load_config() -> String raise ConfigError { + if true { "config_data" } else { raise ConfigError("Config not found") } + } + + fn connect_database(config : String) -> String raise DatabaseError { + if config == "config_data" { + "connected" + } else { + raise DatabaseError("Invalid config") + } + } + + fn initialize_app() -> String raise { + let config = try load_config() catch { + ConfigError(msg) => raise Failure("Config error: " + msg) + } + + let db = try connect_database(config) catch { + DatabaseError(msg) => raise Failure("Database error: " + msg) + } + + "App initialized with " + db + } + + let app_result = try! initialize_app() + inspect(app_result, content="App initialized with connected") +} +``` + +## Best Practices + +1. **Use specific error types**: Create custom `suberror` types for different error categories +2. **Provide meaningful messages**: Include context and actionable information in error messages +3. **Handle errors at appropriate levels**: Don't catch errors too early; let them propagate to where they can be properly handled +4. **Use `try!` for operations that should not fail**: This will panic if an error occurs, making failures visible during development +5. **Use `try?` for recoverable errors**: This returns a `Result` type that can be pattern matched +6. **Implement proper cleanup**: Use the `protect` pattern or similar constructs for resource management + +## Performance Notes + +- Error handling in MoonBit is zero-cost when no errors occur +- Error propagation is efficient and doesn't require heap allocation for the error path +- Custom error types with `derive(ToJson)` automatically generate efficient JSON serialization diff --git a/bundled-core/error/error.mbt b/bundled-core/error/error.mbt index 17d785c..dad3512 100644 --- a/bundled-core/error/error.mbt +++ b/bundled-core/error/error.mbt @@ -28,8 +28,11 @@ fn Error::to_string(self : Error) -> String = "%error.to_string" /// /// ```moonbit /// let error : Error = Failure("test error") -/// inspect(error, content= -/// #|Failure("test error") +/// inspect( +/// error, +/// content=( +/// #|Failure("test error") +/// ), /// ) /// ``` pub impl Show for Error with output(self, logger) { diff --git a/bundled-core/error/error_test.mbt b/bundled-core/error/error_test.mbt index b669211..d6caf47 100644 --- a/bundled-core/error/error_test.mbt +++ b/bundled-core/error/error_test.mbt @@ -25,12 +25,7 @@ test "show error" { } } - inspect( - try? f(true), - content= - #|Ok("ok") - , - ) + inspect(try! f(true), content="ok") inspect( try? f(false), content="Err(moonbitlang/core/error_blackbox_test.IntError.IntError)", @@ -54,7 +49,7 @@ test "show error" { /// here `protect` raise the `work` error fn[A, E : Error] protect( finalize~ : () -> Unit raise?, - work : () -> A raise E + work : () -> A raise E, ) -> A raise E { try work() catch { e => { @@ -63,7 +58,7 @@ fn[A, E : Error] protect( } raise e } - } else { + } noraise { x => { finalize() catch { _ => () @@ -85,7 +80,7 @@ fn[A] protect2(finalize~ : () -> Unit raise?, work : () -> A raise) -> A raise { finalize() raise e } - } else { + } noraise { x => { finalize() x @@ -102,9 +97,9 @@ test "protect" { }) inspect( x, - content= + content=( #|Err(Failure("error")) - , + ), ) } @@ -117,9 +112,9 @@ test "protect & finally raise error" { }) inspect( x, - content= + content=( #|Err(Failure("error")) - , + ), ) } @@ -132,9 +127,9 @@ test "protect2" { }) inspect( x, - content= + content=( #|Err(Failure("error")) - , + ), ) } @@ -147,14 +142,14 @@ test "protect2 & finally raise error" { }) inspect( x, - content= + content=( #|Err(Failure("finally error")) - , + ), ) } ///| -suberror ErrWithToJson Int derive(ToJson) +suberror ErrWithToJson Int derive(ToJson(style="flat")) ///| suberror ErrWithoutToJson Int @@ -174,14 +169,14 @@ test "error to json" { inspect( j(ErrWithToJson(42)), - content= - #|Object({"$tag": String("ErrWithToJson"), "0": Number(42)}) - , + content=( + #|Array([String("ErrWithToJson"), Number(42)]) + ), ) inspect( j(ErrWithoutToJson(42)), - content= + content=( #|String("moonbitlang/core/error_blackbox_test.ErrWithoutToJson.ErrWithoutToJson") - , + ), ) } diff --git a/bundled-core/error/error.mbti b/bundled-core/error/pkg.generated.mbti similarity index 70% rename from bundled-core/error/error.mbti rename to bundled-core/error/pkg.generated.mbti index c467fa6..aa33cdd 100644 --- a/bundled-core/error/error.mbti +++ b/bundled-core/error/pkg.generated.mbti @@ -1,7 +1,10 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/error" // Values +// Errors + // Types and methods impl Show for Error impl ToJson for Error diff --git a/bundled-core/float/deprecated.mbt b/bundled-core/float/deprecated.mbt deleted file mode 100644 index 3e03e8c..0000000 --- a/bundled-core/float/deprecated.mbt +++ /dev/null @@ -1,767 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -#deprecated("use `@math.cbtrf` instead") -#coverage.skip -pub fn Float::cbrt(self : Float) -> Float { - let b1 : UInt = 709958130 // B1 = (127-127.0/3-0.03306235651)*2**23 */ - let b2 : UInt = 642849266 // B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ - let mut ui : UInt = self.reinterpret_as_uint() - let mut hx : UInt = ui & 0x7fffffff - if hx >= 0x7f800000 { - // cbrt(NaN,INF) is itx - return self + self - } - - // rough cbrt to 5 bits - if hx < 0x00800000 { - // zero or subnormal? - if hx == 0 { - return self - } // cbrt(+-0) is itx - ui = (self * (0x1.0p24 : Float)).reinterpret_as_uint() - hx = ui & 0x7fffffff - hx = hx / 3 + b2 - } else { - hx = hx / 3 + b1 - } - ui = ui & 0x80000000 - ui = ui | hx - - // - // First step Newton iteration (solving t*t-x/t == 0) to 16 bits. In - // double precision so that its terms can be arranged for efficiency - // without causing overflow or underflow. - // - let dx = self.to_double() - let t = ui.reinterpret_as_float().to_double() - let r = t * t * t - let t = t * (dx + dx + r) / (dx + r + r) - - // - // Second step Newton iteration to 47 bits. In double precision for - // efficiency and accuracy. - // - let r = t * t * t - let t = t * (dx + dx + r) / (dx + r + r) - - // rounding to 24 bits is perfect in round-to-nearest mode - t.to_float() -} - -///| -#deprecated("use `@math.hypotf` instead") -#coverage.skip -pub fn Float::hypot(self : Float, y : Float) -> Float { - let epsilon : Float = 1.1920928955078125e-7 - let x = self.abs() - let y = y.abs() - if self.is_inf() || y.is_inf() { - return infinity - } - let (x, y) = if y > x { (y, x) } else { (x, y) } - if x * epsilon >= y { - return x - } - let rat = y / x - x * (rat * rat + 1.0).sqrt() -} - -///| -#deprecated("use `@math.expf` instead") -#coverage.skip -pub fn Float::exp(self : Float) -> Float { - let xd = self.to_double() - let abstop = top12(self) & 0x7ff - if abstop >= top12(88.0) { - if self.reinterpret_as_int().reinterpret_as_uint() == - neg_infinity.reinterpret_as_int().reinterpret_as_uint() { - return 0.0 - } - if abstop >= top12(infinity) { - return self + self - } - if self > 0x1.62e42ep6 { - return __math_oflowf(0) - } - if self < -0x1.9fe368p6 { - return __math_uflowf(0) - } - } - let z = exp2f_data.invln2_scaled * xd - let kd = z + exp2f_data.shift - let ki = kd.reinterpret_as_uint64() - let kd = kd - exp2f_data.shift - let r = z - kd - let t = exp2f_data.tab[(ki % expf_n).to_int()] - let t = t + (ki << (52 - exp2f_table_bits)) - let s = t.reinterpret_as_double() - let z = exp2f_data.poly_scaled[0] * r + exp2f_data.poly_scaled[1] - let r2 = r * r - let y = exp2f_data.poly_scaled[2] * r + 1 - let y = z * r2 + y - let y = y * s - y.to_float() -} - -///| -#deprecated("use `@math.expm1f` instead") -#coverage.skip -pub fn Float::expm1(self : Float) -> Float { - let float_ln2_hi : Float = 6.9314575195e-01 // 0x3f317200 - let float_ln2_lo : Float = 1.4286067653e-06 // 0x35bfbe8e - let inv_ln2 : Float = 1.4426950216e+00 // 0x3fb8aa3b - let mut x = self - let q1 : Float = -3.3333212137e-2 // -0x888868.0p-28 - let q2 : Float = 1.5807170421e-3 // 0xcf3010.0p-33 - let mut hx = x.reinterpret_as_uint() - let sign = hx >> 31 != 0 - hx = hx & 0x7fffffff - - // filter out huge and non-finite argument - if hx >= 0x4195b844 { - // if |x|>=27*ln2 - if hx > 0x7f800000 { - // NaN - return x - } - if sign { - return -1.0 - } - if hx > 0x42b17217 { - x *= (0x1.0p127 : Float) - return x - } - } - let mut k : Int = 0 - let mut hi : Float = 0 - let mut lo : Float = 0 - let mut c : Float = 0 - // argument reduction - if hx > 0x3eb17218 { - // if |x| > 0.5 ln2 - if hx < 0x3F851592 { - // and |x| < 1.5 ln2 - if not(sign) { - hi = x - float_ln2_hi - lo = float_ln2_lo - k = 1 - } else { - hi = x + float_ln2_hi - lo = -float_ln2_lo - k = -1 - } - } else { - k = (inv_ln2 * x + (if sign { -0.5 } else { 0.5 })).to_int() - let t = k.to_float() - hi = x - t * float_ln2_hi // t*ln2_hi is exact here - lo = t * float_ln2_lo - } - x = hi - lo - c = hi - x - lo - } else if hx < 0x33000000 { - // when |x|<2**-25, return x - //if hx < 0x00800000 { - // force_eval(x * x); - //} - return x - } else { - k = 0 - } - - // x is now in primary range - let hfx = (0.5 : Float) * x - let hxs = x * hfx - let r1 = (1.0 : Float) + hxs * (q1 + hxs * q2) - let t = (3.0 : Float) - r1 * hfx - let mut e = hxs * ((r1 - t) / ((6.0 : Float) - x * t)) - if k == 0 { - // c is 0 - return x - (x * e - hxs) - } - e = x * (e - c) - c - e -= hxs - // exp(x) ~ 2^k (x_reduced - e + 1) - if k == -1 { - return (0.5 : Float) * (x - e) - 0.5 - } - if k == 1 { - if x < -0.25 { - return -(2.0 : Float) * (e - (x + 0.5)) - } - return (1.0 : Float) + (2.0 : Float) * (x - e) - } - let twopk = ((0x7f + k) << 23).reinterpret_as_float() // 2^k - if not(k is (0..=56)) { - // suffice to return exp(x)-1 - let mut y = x - e + 1.0 - if k == 128 { - y = y * 2.0 * (0x1.0p127 : Float) - } else { - y = y * twopk - } - return y - 1.0 - } - let uf = ((0x7f - k) << 23).reinterpret_as_float() // 2^-k - if k < 23 { - (x - e + ((1.0 : Float) - uf)) * twopk - } else { - (x - (e + uf) + 1.0) * twopk - } -} - -///| -#deprecated("use `@math.sinh` instead") -#coverage.skip -pub fn Float::sinh(self : Float) -> Float { - let x = self - let mut h : Float = 0.5 - let mut ix = x.reinterpret_as_uint() - if ix >> 31 != 0 { - h = -h - } - // |x| - ix = ix & 0x7fffffff - let absx = ix.reinterpret_as_float() - let w = ix - - // |self| < log(FLT_MAX) - if w < 0x42b17217 { - let t = absx.expm1() - if w < 0x3f800000 { - if w < 0x3f800000U - (12U << 23) { - return x - } - return h * ((2.0 : Float) * t - t * t / (t + 1.0)) - } - return h * (t + t / (t + 1.0)) - } - - // |self| > logf(FLT_MAX) or nan - h * k_expo2f(absx) * 2.0 -} - -///| -#deprecated("use `@math.cosh` instead") -#coverage.skip -pub fn Float::cosh(self : Float) -> Float { - let mut x = self - let mut ix = x.reinterpret_as_uint() - ix = ix & 0x7fffffff - x = ix.reinterpret_as_float() - let w = ix - - // |x| < log(2) - if w < 0x3f317217 { - if w < 0x3f800000U - (12U << 23) { - return 1.0 - } - let t = x.expm1() - return (1.0 : Float) + t * t / ((2.0 : Float) * (t + 1.0)) - } - - // |x| < log(FLT_MAX) - if w < 0x42b17217 { - let t = x.exp() - return (t + (1.0 : Float) / t) * 0.5 - } - - // |x| > log(FLT_MAX) or nan - k_expo2f(x) -} - -///| -#deprecated("use `@math.tanh` instead") -#coverage.skip -pub fn Float::tanh(self : Float) -> Float { - let x = self - let mut ix = x.reinterpret_as_uint() - let sign = ix >> 31 != 0 - ix = ix & 0x7fffffff - let x = ix.reinterpret_as_float() - let w = ix - let tt = if w > 0x3f0c9f54 { - // |x| > log(3)/2 ~= 0.5493 or nan - if w > 0x41200000 { - // |x| > 10 - (1.0 : Float) + (0.0 : Float) / x - } else { - let t = (x * 2.0).expm1() - (1.0 : Float) - (2.0 : Float) / (t + 2.0) - } - } else if w > 0x3e82c578 { - // |x| > log(5/3)/2 ~= 0.2554 - let t = (x * 2.0).expm1() - t / (t + 2.0) - } else if w >= 0x00800000 { - // |x| >= 0x1p-126 - let t = (x * -2.0).expm1() - -t / (t + 2.0) - } else { - // |x| is subnormal - x - } - if sign { - -tt - } else { - tt - } -} - -///| -#deprecated("use `@math.asinh` instead") -#coverage.skip -pub fn Float::asinh(self : Float) -> Float { - let x = self - let u = x.reinterpret_as_uint() - let i = u & 0x7fffffff - let sign = u >> 31 != 0 - let ln2 : Float = 0.693147180559945309417232121458176568 - let x = i.reinterpret_as_float() - let x = if i >= 0x3f800000U + (12U << 23) { - // |x| >= 0x1p12 or inf or nan - x.ln() + ln2 - } else if i >= 0x3f800000U + (1U << 23) { - // |x| >= 2 - (x * 2.0 + (1.0 : Float) / ((x * x + 1.0).sqrt() + x)).ln() - } else if i >= 0x3f800000U - (12U << 23) { - // |x| >= 0x1p-12, up to 1.6ulp error in [0.125,0.5] - (x + x * x / ((x * x + 1.0).sqrt() + 1.0)).ln_1p() - } else { - // |x| < 0x1p-12, raise inexact if x!=0 - // x + 0x1.0p120 - x - } - if sign { - -x - } else { - x - } -} - -///| -#deprecated("use `@math.acosh` instead") -#coverage.skip -pub fn Float::acosh(self : Float) -> Float { - let x = self - let ln2 : Float = 693147180559945309417232121458176568 - let u = x.reinterpret_as_uint() - let a = u & 0x7fffffffU - if a < 0x3f800000U + (1U << 23) { - // |x| < 2, invalid if x < 1 or nan - // up to 2ulp error in [1,1.125] - return (x - 1.0 + ((x - 1.0) * (x - 1.0) + (2.0 : Float) * (x - 1.0)).sqrt()).ln_1p() - } - if a < 0x3f800000U + (12U << 23) { - // |x| < 0x1p12 - return (x * 2.0 - (1.0 : Float) / (x + (x * x - 1.0).sqrt())).ln() - } - // x >= 0x1p12 - return x.ln() + ln2 -} - -///| -#deprecated("use `@math.atanh` instead") -#coverage.skip -pub fn Float::atanh(self : Float) -> Float { - let x = self - let u = x.reinterpret_as_uint() - let sign = u >> 31 != 0 - let u = u & 0x7fffffff - let x = u.reinterpret_as_float() - let x = if u < 0x3f800000U - (1U << 23) { - if u < 0x3f800000U - (32U << 23) { - x - } else { - // |x| < 0.5, up to 1.7ulp error - (x * 2.0 + x * 2.0 * x / ((1.0 : Float) - x)).ln_1p() * 0.5 - } - } else { - // avoid overflow - (x / ((1.0 : Float) - x) * 2.0).ln_1p() * 0.5 - } - if sign { - -x - } else { - x - } -} - -///| -#deprecated("use `@math.lnf` instead") -#coverage.skip -pub fn Float::ln(self : Float) -> Float { - let mut ix : UInt = self.reinterpret_as_int().reinterpret_as_uint() - if ix == 0x3f800000U { - return 0.0 - } - if ix - 0x00800000U >= 0x7f800000U - 0x00800000U { - if ix * 2 == 0 { - return neg_infinity - } - if ix == 0x7f800000U { - return self - } - if (ix & 0x80000000U) != 0 || ix * 2 >= 0xff000000U { - return not_a_number - } - ix = (self * 0x1.0p23).reinterpret_as_int().reinterpret_as_uint() - ix -= (23 << 23).reinterpret_as_uint() - } - let tmp = ix - logf_off - let i = ((tmp >> (23 - logf_table_bits)) % logf_n).reinterpret_as_int() - let k = tmp.reinterpret_as_int() >> 23 - let iz = ix - (tmp & 0xff800000U) - let invc = logf_data.invc[i] - let logc = logf_data.logc[i] - let z = iz.reinterpret_as_int().reinterpret_as_float().to_double() - let r = z * invc - 1 - let y0 = logc + k.to_double() * logf_data.ln2 - let r2 = r * r - let y = logf_data.poly[1] * r + logf_data.poly[2] - let y = logf_data.poly[0] * r2 + y - let y = y * r2 + (y0 + r) - y.to_float() -} - -///| -#deprecated("use `@math.ln_1pf` instead") -#coverage.skip -pub fn Float::ln_1p(self : Float) -> Float { - let lg1_f : Float = 0.66666662693 - let lg2_f : Float = 0.40000972152 - let lg3_f : Float = 0.28498786688 - let lg4_f : Float = 0.24279078841 - let float_ln2_hi : Float = 6.9314575195e-01 // 0x3f317200 - let float_ln2_lo : Float = 1.4286067653e-06 // 0x35bfbe8e - let mut ui : UInt = self.reinterpret_as_uint() - let mut f : Float = 0 - let mut c : Float = 0 - let mut iu : UInt = 0 - let one : Float = 1.0 - let mut k = 1 - if ui < 0x3ed413d0 || ui >> 31 > 0 { - if ui >= 0xbf800000 { - if self == -1.0 { - return self / 0.0 - } - return (self - self) / 0.0 - } - if ui << 1 < 0x33800000U << 1 { - return self - } - if ui <= 0xbe95f619 { - k = 0 - c = 0.0 - f = self - } - } else if ui >= 0x7f800000 { - return self - } - if k > 0 { - ui = (one + self).reinterpret_as_uint() - iu = ui - iu += 0x3f800000U - 0x3f3504f3U - k = (iu >> 23).reinterpret_as_int() - 0x7f - if k < 25 { - let fui = ui.reinterpret_as_float() - c = if k >= 2 { one - (fui - self) } else { self - (fui - 1.0) } - c /= ui.reinterpret_as_float() - } else { - c = 0.0 - } - iu = (iu & 0x007fffff) + 0x3f3504f3 - ui = iu - f = ui.reinterpret_as_float() - 1.0 - } - let s = f / (f + 2.0) - let z = s * s - let w = z * z - let t1 = w * (lg2_f + w * lg4_f) - let t2 = z * (lg1_f + w * lg3_f) - let r = t2 + t1 - let hfsq = f * f * 0.5 - let dk = k.to_float() - s * (hfsq + r) + (dk * float_ln2_lo + c) - hfsq + f + dk * float_ln2_hi -} - -///| -#deprecated("use `@math.sinf` instead") -#coverage.skip -pub fn Float::sin(self : Float) -> Float { - let x = self - if x.is_nan() || x.is_inf() { - return not_a_number - } - if x == 0.0 { - return x - } - let (x, q) = trig_reduce(x, SIN_SWITCHOVER) - sin_cos_core(x, q) -} - -///| -#deprecated("use `@math.cosf` instead") -#coverage.skip -pub fn Float::cos(self : Float) -> Float { - let x = self - if x.is_nan() || x.is_inf() { - return not_a_number - } - if x == 0.0 { - return 1.0 - } - let (x, q) = trig_reduce(x, COS_SWITCHOVER) - sin_cos_core(x, q + 1) -} - -///| -#deprecated("use `@math.tanf` instead") -#coverage.skip -pub fn Float::tan(self : Float) -> Float { - let x = self - if x.is_nan() || x.is_inf() { - return not_a_number - } - if x == 0.0 { - return x - } - let (x, q) = trig_reduce(x, COS_SWITCHOVER) - tanf_poly(x, (q & 1) != 0) -} - -///| -#deprecated("use `@math.asinf` instead") -#coverage.skip -pub fn Float::asin(self : Float) -> Float { - let x = self - let x1p120 = 0x3870000000000000UL.reinterpret_as_double() - let pio2 : Double = 1.570796326794896558e+00 - - // coefficients for R(x^2) - let ps0 : Float = 1.6666586697e-01 - let ps1 : Float = -4.2743422091e-02 - let ps2 : Float = -8.6563630030e-03 - let qs2 : Float = -7.0662963390e-01 - fn r(z : Float) -> Float { - let p = z * (ps0 + z * (ps1 + z * ps2)) - let q = z * qs2 + 1.0 - p / q - } - - let hx = x.reinterpret_as_uint() - let ix = hx & 0x7fffffff - if ix >= 0x3f800000 { - if ix == 0x3f800000 { - return (x.to_double() * pio2 + x1p120).to_float() - } - return not_a_number // asin(|x|>1) is NaN - } - if ix < 0x3f000000 { - if ix is (0x00800000..=0x39800000) { - return x - } - return x + x * r(x * x) - } - let z = ((1.0 : Float) - x.abs()) * 0.5 - let s = z.to_double().sqrt() - let x = (pio2 - 2.0 * (s + s * r(z).to_double())).to_float() - if hx >> 31 != 0 { - -x - } else { - x - } -} - -///| -#deprecated("use `@math.acosf` instead") -#coverage.skip -pub fn Float::acos(self : Float) -> Float { - let x = self - let pio2_hi : Float = 1.5707962513 - let pio2_lo : Float = 7.5497894159e-08 - let ps0 : Float = 1.6666586697e-01 - let ps1 : Float = -4.2743422091e-02 - let ps2 : Float = -8.6563630030e-03 - let qs1 : Float = -7.0662963390e-01 - let one : Float = 1.0 - let two : Float = 2.0 - fn r(z : Float) -> Float { - let p = z * (ps0 + z * (ps1 + z * ps2)) - let q = z * qs1 + 1.0 - p / q - } - - let hx = x.reinterpret_as_int() - let ix = hx & 0x7fffffff - if ix >= 0x3f800000 { - if ix == 0x3f800000 { - if hx >> 31 != 0 { - return two * pio2_hi + 0x1.0p-120 - } - return 0.0 - } - return not_a_number - } - if ix < 0x3f000000 { - if ix <= 0x32800000 { - return pio2_hi + 0x1.0p-120 - } - return pio2_hi - (x - (pio2_lo - x * r(x * x))) - } - if hx >> 31 != 0 { - let z = (x + 1.0) * 0.5 - let s = z.sqrt() - let w = r(z) * s - pio2_lo - return two * (pio2_hi - (s + w)) - } - let z = (one - x) * 0.5 - let s = z.sqrt() - let df = s - let c = (z - df * df) / (s + df) - let w = r(z) * s + c - two * (df + w) -} - -///| -#deprecated("use `@math.atanf` instead") -#coverage.skip -pub fn Float::atan(self : Float) -> Float { - let x = self - let atanhi : Array[Float] = [ - 4.6364760399e-01, 7.8539812565e-01, 9.8279368877e-01, 1.5707962513e+00, - ] - let atanlo : Array[Float] = [ - 5.0121582440e-09, 3.7748947079e-08, 3.4473217170e-08, 7.5497894159e-08, - ] - let a_t : Array[Float] = [ - 3.3333328366e-01, -1.9999158382e-01, 1.4253635705e-01, -1.0648017377e-01, 6.1687607318e-02, - ] - let ix = x.reinterpret_as_int() - let sign = ix >> 31 - let ix = ix & 0x7fffffff - let mut id = 0 - let mut x = x - let one : Float = 1.0 - let two : Float = 2.0 - if ix >= 0x4c800000 { - if x.is_nan() { - return x - } - let z = atanhi[3] + 0x1.0p-120 - let z = if sign != 0 { -z } else { z } - return z - } - if ix < 0x3ee00000 { - if ix < 0x39800000 { - return x - } - id = -1 - } else { - x = x.abs() - if ix < 0x3f980000 { - if ix < 0x3f300000 { - id = 0 - x = (two * x - one) / (two + x) - } else { - id = 1 - x = (x - one) / (x + one) - } - } else if ix < 0x401c0000 { - id = 2 - x = (x - 1.5) / (one + x * 1.5) - } else { - id = 3 - x = -one / x - } - } - let z = x * x - let w = z * z - let s1 = z * (a_t[0] + w * (a_t[2] + w * a_t[4])) - let s2 = w * (a_t[1] + w * a_t[3]) - if id < 0 { - return x - x * (s1 + s2) - } - let z = atanhi[id] - (x * (s1 + s2) - atanlo[id] - x) - if sign != 0 { - -z - } else { - z - } -} - -///| -#deprecated("use `@math.atan2f` instead") -#coverage.skip -pub fn Float::atan2(self : Float, other : Float) -> Float { - let (y, x) = (self, other) - if x.is_nan() || y.is_nan() { - return not_a_number - } - let pi : Float = 3.1415927410e+00 - let pi_lo : Float = -8.7422776573e-08 - let zero : Float = 0.0 - let ix = x.reinterpret_as_uint() - let iy = y.reinterpret_as_uint() - if ix == 0x3f800000 { - return y.atan() - } - let m = ((iy >> 31) & 1) | ((ix >> 30) & 2) - let ix = ix & 0x7fffffff - let iy = iy & 0x7fffffff - if iy == 0 { - match m { - 0 | 1 => return y - 2 => return pi - _ => return -pi - } - } - if ix == 0 { - let res = if (m & 1) != 0 { -pi / 2 } else { pi / 2 } - return res - } - if ix == 0x7f800000 { - if iy == 0x7f800000 { - match m { - 0 => return pi / 4 - 1 => return -pi / 4 - 2 => return pi * 3.0 / 4 - _ => return -pi * 3.0 / 4 - } - } else { - match m { - 0 => return 0.0 - 1 => return -0.0 - 2 => return pi - _ => return -pi - } - } - } - if ix + (26U << 23) < iy || iy == 0x7f800000 { - let res = if (m & 1) != 0 { -pi / 2 } else { pi / 2 } - return res - } - let z = if (m & 2) != 0 && iy + (26U << 23) < ix { - zero - } else { - (y / x).atan() - } - match m { - 0 => z - 1 => -z - 2 => pi - (z - pi_lo) - _ => z - pi_lo - pi - } -} diff --git a/bundled-core/float/exp.mbt b/bundled-core/float/exp.mbt index 9e49276..4e36a8a 100644 --- a/bundled-core/float/exp.mbt +++ b/bundled-core/float/exp.mbt @@ -11,61 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -fn top12(x : Float) -> UInt { - x.reinterpret_as_int().reinterpret_as_uint() >> 20 -} - -///| -fn __math_xflowf(sign : UInt, y : Float) -> Float { - return (if sign != 0 { -y } else { y }) * y -} - -///| -fn __math_oflowf(sign : UInt) -> Float { - return __math_xflowf(sign, 0x1.0p97) -} - -///| -fn __math_uflowf(sign : UInt) -> Float { - return __math_xflowf(sign, 0x1.0p-95) -} - -///| -let exp2f_table_bits = 5 - -///| -priv struct Exp2fData { - tab : FixedArray[UInt64] - shift : Double - invln2_scaled : Double - poly_scaled : FixedArray[Double] -} - -///| -let expf_n : UInt64 = (1 << exp2f_table_bits).to_int64().reinterpret_as_uint64() - -///| -let exp2f_data_n : Double = (1 << exp2f_table_bits).to_double() - -///| -let exp2f_data : Exp2fData = { - tab: [ - 0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51, - 0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1, - 0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d, - 0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585, - 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13, - 0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d, - 0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069, - 0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540, - ], - shift: 0x1.8p+52, - invln2_scaled: 0x1.71547652b82fep+0 * exp2f_data_n, - poly_scaled: [ - 0x1.c6af84b912394p-5 / exp2f_data_n / exp2f_data_n / exp2f_data_n, - 0x1.ebfce50fac4f3p-3 / exp2f_data_n / exp2f_data_n, - 0x1.62e42ff0c52d6p-1 / exp2f_data_n, - ], -} diff --git a/bundled-core/float/float.mbt b/bundled-core/float/float.mbt index 9ecc75f..4aef776 100644 --- a/bundled-core/float/float.mbt +++ b/bundled-core/float/float.mbt @@ -111,6 +111,7 @@ pub let min_positive : Float = (0x00800000).reinterpret_as_float() /// inspect((-0.0 : Float).abs(), content="0") /// inspect(@float.not_a_number.abs().is_nan(), content="true") /// ``` +#as_free_fn pub fn Float::abs(self : Float) -> Float = "%f32.abs" ///| @@ -218,9 +219,12 @@ pub impl Hash for Float with hash_combine(self, hasher) { /// /// ```moonbit /// let x : Float = 1.0 -/// inspect(x.to_be_bytes(), content= -/// #|b"\x3f\x80\x00\x00" -/// ) +/// inspect( +/// x.to_be_bytes(), +/// content=( +/// #|b"\x3f\x80\x00\x00" +/// ), +/// ) /// ``` pub fn to_be_bytes(self : Float) -> Bytes { self.reinterpret_as_uint().to_be_bytes() @@ -355,13 +359,13 @@ pub fn is_nan(self : Float) -> Bool { /// let y = 1.000000001 /// inspect(x.is_close(y), content="false") /// inspect(x.is_close(y, relative_tolerance=1.0e-10), content="false") -/// inspect(infinity.is_close(infinity), content="true") +/// inspect(@float.infinity.is_close(@float.infinity), content="true") /// ``` pub fn Float::is_close( self : Self, other : Self, - relative_tolerance~ : Self = 1.0e-09, - absolute_tolerance~ : Self = 0.0 + relative_tolerance? : Self = 1.0e-09, + absolute_tolerance? : Self = 0.0, ) -> Bool { if relative_tolerance < 0.0 || absolute_tolerance < 0.0 { abort("Tolerances must be non-negative") diff --git a/bundled-core/float/float.mbti b/bundled-core/float/float.mbti deleted file mode 100644 index 8aa18ae..0000000 --- a/bundled-core/float/float.mbti +++ /dev/null @@ -1,148 +0,0 @@ -package "moonbitlang/core/float" - -// Values -fn abs(Float) -> Float - -#deprecated -fn acos(Float) -> Float - -#deprecated -fn acosh(Float) -> Float - -#deprecated -fn asin(Float) -> Float - -#deprecated -fn asinh(Float) -> Float - -#deprecated -fn atan(Float) -> Float - -#deprecated -fn atan2(Float, Float) -> Float - -#deprecated -fn atanh(Float) -> Float - -#deprecated -fn cbrt(Float) -> Float - -fn ceil(Float) -> Float - -#deprecated -fn cos(Float) -> Float - -#deprecated -fn cosh(Float) -> Float - -fn default() -> Float - -#deprecated -fn exp(Float) -> Float - -#deprecated -fn expm1(Float) -> Float - -fn floor(Float) -> Float - -#deprecated -fn hypot(Float, Float) -> Float - -let infinity : Float - -#deprecated -fn ln(Float) -> Float - -#deprecated -fn ln_1p(Float) -> Float - -let max_value : Float - -let min_positive : Float - -let min_value : Float - -let neg_infinity : Float - -let not_a_number : Float - -fn pow(Float, Float) -> Float - -fn round(Float) -> Float - -#deprecated -fn sin(Float) -> Float - -#deprecated -fn sinh(Float) -> Float - -#deprecated -fn tan(Float) -> Float - -#deprecated -fn tanh(Float) -> Float - -fn trunc(Float) -> Float - -// Types and methods -fn Float::abs(Float) -> Float -#deprecated -fn Float::acos(Float) -> Float -#deprecated -fn Float::acosh(Float) -> Float -#deprecated -fn Float::asin(Float) -> Float -#deprecated -fn Float::asinh(Float) -> Float -#deprecated -fn Float::atan(Float) -> Float -#deprecated -fn Float::atan2(Float, Float) -> Float -#deprecated -fn Float::atanh(Float) -> Float -#deprecated -fn Float::cbrt(Float) -> Float -fn Float::ceil(Float) -> Float -#deprecated -fn Float::cos(Float) -> Float -#deprecated -fn Float::cosh(Float) -> Float -#deprecated -fn Float::exp(Float) -> Float -#deprecated -fn Float::expm1(Float) -> Float -fn Float::floor(Float) -> Float -#deprecated -fn Float::hypot(Float, Float) -> Float -fn Float::is_close(Float, Float, relative_tolerance~ : Float = .., absolute_tolerance~ : Float = ..) -> Bool -fn Float::is_inf(Float) -> Bool -fn Float::is_nan(Float) -> Bool -fn Float::is_neg_inf(Float) -> Bool -fn Float::is_pos_inf(Float) -> Bool -#deprecated -fn Float::ln(Float) -> Float -#deprecated -fn Float::ln_1p(Float) -> Float -fn Float::pow(Float, Float) -> Float -fn Float::round(Float) -> Float -#deprecated -fn Float::sin(Float) -> Float -#deprecated -fn Float::sinh(Float) -> Float -#deprecated -fn Float::tan(Float) -> Float -#deprecated -fn Float::tanh(Float) -> Float -fn Float::to_be_bytes(Float) -> Bytes -fn Float::to_int(Float) -> Int -fn Float::to_le_bytes(Float) -> Bytes -fn Float::trunc(Float) -> Float -impl Default for Float -impl Hash for Float -impl Mod for Float -impl Show for Float - -// Type aliases - -// Traits - diff --git a/bundled-core/float/float_test.mbt b/bundled-core/float/float_test.mbt index 201d0b9..b680915 100644 --- a/bundled-core/float/float_test.mbt +++ b/bundled-core/float/float_test.mbt @@ -146,14 +146,14 @@ test "Float::to_be_bytes with various values" { test "to_le_bytes for zero" { inspect( (0.0 : Float).to_le_bytes(), - content= + content=( #|b"\x00\x00\x00\x00" - , + ), ) inspect( (-0.0 : Float).to_le_bytes(), - content= + content=( #|b"\x00\x00\x00\x80" - , + ), ) } diff --git a/bundled-core/float/hyperbolic.mbt b/bundled-core/float/hyperbolic.mbt index 52894ae..4e36a8a 100644 --- a/bundled-core/float/hyperbolic.mbt +++ b/bundled-core/float/hyperbolic.mbt @@ -11,13 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -fn k_expo2f(x : Float) -> Float { - let k = 235 - let k_ln2 = (0x4322e3bc).reinterpret_as_float() - // note that k is odd and scale*scale overflows */ - let scale = ((0x7f + k / 2) << 23).reinterpret_as_float() - // exp(x - k ln2) * 2**(k-1) */ - (x - k_ln2).exp() * scale * scale -} diff --git a/bundled-core/float/log.mbt b/bundled-core/float/log.mbt index 75cd7b2..4e36a8a 100644 --- a/bundled-core/float/log.mbt +++ b/bundled-core/float/log.mbt @@ -11,38 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -let logf_off = 0x3f330000U - -///| -let logf_table_bits = 4 - -///| -let logf_n : UInt = 1U << logf_table_bits - -///| -priv struct LogfData { - invc : Array[Double] - logc : Array[Double] - ln2 : Double - poly : Array[Double] -} - -///| -let logf_data : LogfData = { - invc: [ - 0x1.661ec79f8f3bep+0, 0x1.571ed4aaf883dp+0, 0x1.49539f0f010bp+0, 0x1.3c995b0b80385p+0, - 0x1.30d190c8864a5p+0, 0x1.25e227b0b8eap+0, 0x1.1bb4a4a1a343fp+0, 0x1.12358f08ae5bap+0, - 0x1.0953f419900a7p+0, 0x1.0p+0, 0x1.e608cfd9a47acp-1, 0x1.ca4b31f026aap-1, 0x1.b2036576afce6p-1, - 0x1.9c2d163a1aa2dp-1, 0x1.886e6037841edp-1, 0x1.767dcf5534862p-1, - ], - logc: [ - -0x1.57bf7808caadep-2, -0x1.2bef0a7c06ddbp-2, -0x1.01eae7f513a67p-2, -0x1.b31d8a68224e9p-3, - -0x1.6574f0ac07758p-3, -0x1.1aa2bc79c81p-3, -0x1.a4e76ce8c0e5ep-4, -0x1.1973c5a611cccp-4, - -0x1.252f438e10c1ep-5, 0x0.0p+0, 0x1.aa5aa5df25984p-5, 0x1.c5e53aa362eb4p-4, - 0x1.526e57720db08p-3, 0x1.bc2860d22477p-3, 0x1.1058bc8a07ee1p-2, 0x1.4043057b6ee09p-2, - ], - ln2: 0x1.62e42fefa39efp-1, - poly: [-0x1.00ea348b88334p-2, 0x1.5575b0be00b6ap-2, -0x1.ffffef20a4123p-2], -} diff --git a/bundled-core/float/math_functions.mbt b/bundled-core/float/math_functions.mbt index ee32e8c..4e36a8a 100644 --- a/bundled-core/float/math_functions.mbt +++ b/bundled-core/float/math_functions.mbt @@ -11,32 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -pub fnalias Float::( - sin, - cos, - tan, - asin, - acos, - atan, - atan2, - exp, - expm1, - pow, - ln, - ln_1p, - sinh, - cosh, - tanh, - asinh, - acosh, - atanh, - hypot, - cbrt, - trunc, - ceil, - floor, - round, - abs -) diff --git a/bundled-core/float/mod.mbt b/bundled-core/float/mod.mbt index 7189083..fa279f0 100644 --- a/bundled-core/float/mod.mbt +++ b/bundled-core/float/mod.mbt @@ -28,6 +28,6 @@ /// inspect((5.7 : Float).op_mod(2.0), content="1.6999998092651367") /// inspect((-5.7 : Float).op_mod(2.0), content="-1.6999998092651367") /// ``` -pub impl Mod for Float with op_mod(self : Float, other : Float) -> Float { +pub impl Mod for Float with mod(self : Float, other : Float) -> Float { (self.to_double() % other.to_double()).to_float() } diff --git a/bundled-core/float/pkg.generated.mbti b/bundled-core/float/pkg.generated.mbti new file mode 100644 index 0000000..cfa9b86 --- /dev/null +++ b/bundled-core/float/pkg.generated.mbti @@ -0,0 +1,50 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/float" + +// Values +fn default() -> Float + +let infinity : Float + +let max_value : Float + +let min_positive : Float + +let min_value : Float + +let neg_infinity : Float + +let not_a_number : Float + +// Errors + +// Types and methods +#as_free_fn +fn Float::abs(Float) -> Float +#as_free_fn +fn Float::ceil(Float) -> Float +#as_free_fn +fn Float::floor(Float) -> Float +fn Float::is_close(Float, Float, relative_tolerance? : Float, absolute_tolerance? : Float) -> Bool +fn Float::is_inf(Float) -> Bool +fn Float::is_nan(Float) -> Bool +fn Float::is_neg_inf(Float) -> Bool +fn Float::is_pos_inf(Float) -> Bool +#as_free_fn +fn Float::pow(Float, Float) -> Float +#as_free_fn +fn Float::round(Float) -> Float +fn Float::to_be_bytes(Float) -> Bytes +fn Float::to_int(Float) -> Int +fn Float::to_le_bytes(Float) -> Bytes +#as_free_fn +fn Float::trunc(Float) -> Float +impl Default for Float +impl Hash for Float +impl Mod for Float +impl Show for Float + +// Type aliases + +// Traits + diff --git a/bundled-core/float/pow.mbt b/bundled-core/float/pow.mbt index 4a492b8..a9b09c6 100644 --- a/bundled-core/float/pow.mbt +++ b/bundled-core/float/pow.mbt @@ -30,6 +30,7 @@ /// inspect((4.0 : Float).pow(0.5), content="2") /// inspect((1.0 : Float).pow(-1.0), content="1") /// ``` +#as_free_fn pub fn Float::pow(self : Float, other : Float) -> Float { self.to_double().pow(other.to_double()).to_float() } diff --git a/bundled-core/float/round.mbt b/bundled-core/float/round.mbt index eedefbe..a48567a 100644 --- a/bundled-core/float/round.mbt +++ b/bundled-core/float/round.mbt @@ -46,6 +46,7 @@ let frac_bits = 23 /// inspect((-3.7 : Float).trunc(), content="-3") /// inspect((0.2 : Float).trunc(), content="0") /// ``` +#as_free_fn pub fn Float::trunc(self : Float) -> Float { let u32 = self.reinterpret_as_uint() let biased_exp = ((u32 >> frac_bits) & ((0x1U << exp_bits) - 1)).reinterpret_as_int() @@ -76,6 +77,7 @@ pub fn Float::trunc(self : Float) -> Float { /// inspect((1.0 : Float).ceil(), content="1") /// inspect((-1.5 : Float).ceil(), content="-1") /// ``` +#as_free_fn pub fn Float::ceil(self : Float) -> Float { let trunced = self.trunc() if self > trunced { @@ -103,6 +105,7 @@ pub fn Float::ceil(self : Float) -> Float { /// inspect((-1.7 : Float).floor(), content="-2") /// inspect((2.0 : Float).floor(), content="2") /// ``` +#as_free_fn pub fn Float::floor(self : Float) -> Float { let trunced = self.trunc() if self < trunced { @@ -129,6 +132,7 @@ pub fn Float::floor(self : Float) -> Float { /// inspect((2.5 : Float).round(), content="3") /// inspect((-2.5 : Float).round(), content="-2") /// ``` +#as_free_fn pub fn Float::round(self : Float) -> Float { floor(self + 0.5) } diff --git a/bundled-core/float/round_js.mbt b/bundled-core/float/round_js.mbt index 6d88aeb..77e8fd5 100644 --- a/bundled-core/float/round_js.mbt +++ b/bundled-core/float/round_js.mbt @@ -29,6 +29,7 @@ /// inspect((3.7 : Float).trunc(), content="3") /// inspect((-3.7 : Float).trunc(), content="-3") /// ``` +#as_free_fn pub fn Float::trunc(self : Float) -> Float = "Math" "trunc" ///| @@ -47,6 +48,7 @@ pub fn Float::trunc(self : Float) -> Float = "Math" "trunc" /// inspect((1.5 : Float).ceil(), content="2") /// inspect((-1.5 : Float).ceil(), content="-1") /// ``` +#as_free_fn pub fn Float::ceil(self : Float) -> Float = "Math" "ceil" ///| @@ -66,6 +68,7 @@ pub fn Float::ceil(self : Float) -> Float = "Math" "ceil" /// inspect((3.7 : Float).floor(), content="3") /// inspect((-3.7 : Float).floor(), content="-4") /// ``` +#as_free_fn pub fn Float::floor(self : Float) -> Float = "Math" "floor" ///| @@ -86,4 +89,5 @@ pub fn Float::floor(self : Float) -> Float = "Math" "floor" /// inspect((1.6 : Float).round(), content="2") /// inspect((-1.5 : Float).round(), content="-1") /// ``` +#as_free_fn pub fn Float::round(self : Float) -> Float = "Math" "round" diff --git a/bundled-core/float/round_wasm.mbt b/bundled-core/float/round_wasm.mbt index 9020745..fdfeaf1 100644 --- a/bundled-core/float/round_wasm.mbt +++ b/bundled-core/float/round_wasm.mbt @@ -33,6 +33,7 @@ /// inspect((-1.9 : Float).trunc(), content="-1") /// inspect((0.1 : Float).trunc(), content="0") /// ``` +#as_free_fn pub fn Float::trunc(self : Float) -> Float = "(func (param $f f32) (result f32) (f32.trunc (local.get $f)))" ///| @@ -52,6 +53,7 @@ pub fn Float::trunc(self : Float) -> Float = "(func (param $f f32) (result f32) /// inspect((1.0 : Float).ceil(), content="1") /// inspect((-1.4 : Float).ceil(), content="-1") /// ``` +#as_free_fn pub fn Float::ceil(self : Float) -> Float = "(func (param $f f32) (result f32) (f32.ceil (local.get $f)))" ///| @@ -71,6 +73,7 @@ pub fn Float::ceil(self : Float) -> Float = "(func (param $f f32) (result f32) ( /// inspect((3.7 : Float).floor(), content="3") /// inspect((-3.7 : Float).floor(), content="-4") /// ``` +#as_free_fn pub fn Float::floor(self : Float) -> Float = "(func (param $f f32) (result f32) (f32.floor (local.get $f)))" ///| @@ -92,6 +95,7 @@ pub fn Float::floor(self : Float) -> Float = "(func (param $f f32) (result f32) /// inspect((1.6 : Float).round(), content="2") /// inspect((-1.5 : Float).round(), content="-1") /// ``` +#as_free_fn pub fn Float::round(self : Float) -> Float { floor(self + 0.5) } diff --git a/bundled-core/float/trig.mbt b/bundled-core/float/trig.mbt index 845cd23..4e36a8a 100644 --- a/bundled-core/float/trig.mbt +++ b/bundled-core/float/trig.mbt @@ -11,154 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -const SIN_SWITCHOVER : Float = 201.15625 - -///| -const COS_SWITCHOVER : Float = 142.90625 - -///| -fn mulh(a : UInt, b : UInt) -> UInt { - let a = a.to_uint64() - let b = b.to_uint64() - let res = a * b - (res >> 32).to_uint() -} - -///| -fn mul(a : UInt, b : UInt) -> (UInt, UInt) { - let a = a.to_uint64() - let b = b.to_uint64() - let res = a * b - ((res >> 32).to_uint(), res.to_uint()) -} - -///| -fn trig_reduce(x : Float, switch_over : Float) -> (Float, Int) { - if x.abs() <= switch_over { - let mut j : Float = 0.0 - let mut r : Float = 0.0 - j = x * (0x3f22f983).reinterpret_as_float() + - (0x4b40_0000).reinterpret_as_float() - j = (j.reinterpret_as_int() - 0x4b40_0000).to_float() - r = x - j * (0x3fc90f80).reinterpret_as_float() - r = r - j * (0x37354440).reinterpret_as_float() - r = r - j * (0x2c34611a).reinterpret_as_float() - return (r, j.to_int()) - } - let xispos = x > 0.0 - let mut exp : Int = ((x.reinterpret_as_int() >> 23) & 0xff) - 126 - let ix = ((x.reinterpret_as_uint() & 0x007fffff) << 8) | 0x80000000 - let ind = exp >> 5 - exp = exp & 0x1f - let two_over_pi : Array[UInt] = [ - 0x00000000, 0x28be60db, 0x9391054a, 0x7f09d5f4, 0x7d4d3770, 0x36d8a566, 0x4f10e410, - 0000000000, - ] - let mut hi = two_over_pi[ind] - let mut mi = two_over_pi[ind + 1] - let mut lo = two_over_pi[ind + 2] - let tp = two_over_pi[ind + 3] - if exp > 0 { - hi = (hi << exp) | (mi >> (32 - exp)) - mi = (mi << exp) | (lo >> (32 - exp)) - lo = (lo << exp) | (tp >> (32 - exp)) - } - let phi = 0U - let (h, l) = mul(ix, lo) - let plo = phi + l - let phi = h + (if plo < l { 1 } else { 0 }) - let (h, l) = mul(ix, mi) - let mut plo = phi + l - let phi = h + (if plo < l { 1 } else { 0 }) - let l = ix * hi - let mut phi = phi + l - let mut q : Int = (phi >> 30).reinterpret_as_int() - phi = phi & 0x3fffffff - if (phi & 0x2000_0000) != 0 { - phi = phi - 0x4000_0000 - q = q + 1 - } - let s : UInt = phi & 0x8000_0000 - if phi >= 0x8000_0000 { - phi = phi.lnot() - plo = 0U - plo - //phi += (plo == 0).to_uint() - phi += if plo == 0 { 1 } else { 0 } - } - exp = 0 - while phi < 0x8000_0000 { - phi = (phi << 1) | (plo >> 31) - plo = plo << 1 - exp = exp - 1 - } - phi = mulh(phi, 0xc90f_daa2) - if phi < 0x8000_0000 { - phi = phi << 1 - exp = exp - 1 - } - let mut r = s + - ((exp + 128) << 23).reinterpret_as_uint() + - (phi >> 8) + - (if (phi & 0xff) > 0x7e { 1 } else { 0 }) - if not(xispos) { - r = r ^ 0x8000_0000 - q = -q - } - let r = r.reinterpret_as_float() - return (r, q) -} - -///| -fn sinf_poly(x : Float) -> Float { - let s = x * x - let mut r = (0x3640_5000).reinterpret_as_float() - r = r * s - (0x3950_3486).reinterpret_as_float() - r = r * s + (0x3c08_88c1).reinterpret_as_float() - r = r * s - (0x3e2a_aaab).reinterpret_as_float() - let t = x * s - r = r * t + x - r -} - -///| -fn cosf_poly(x : Float) -> Float { - let s = x * x - let mut r = (0x37cd_4000).reinterpret_as_float() - r = r * s - (0x3ab6_077d).reinterpret_as_float() - r = r * s + (0x3d2a_aaa8).reinterpret_as_float() - r = r * s - (0x3f00_0000).reinterpret_as_float() - r = r * s + (0x3f80_0000).reinterpret_as_float() - r -} - -///| -fn sin_cos_core(x : Float, q : Int) -> Float { - let mut r = if (q & 1) != 0 { cosf_poly(x) } else { sinf_poly(x) } - if (q & 2) != 0 { - r = -r - } - r -} - -///| -fn tanf_poly(x : Float, odd : Bool) -> Float { - let x = x.to_double() - let coef : FixedArray[Double] = [ - 0.333331395030791399758, // 0x15554d3418c99f.0p-54 */ - 0.133392002712976742718, // 0x1112fd38999f72.0p-55 */ - 0.0533812378445670393523, // 0x1b54c91d865afe.0p-57 */ - 0.0245283181166547278873, // 0x191df3908c33ce.0p-58 */ - 0.00297435743359967304927, // 0x185dadfcecf44e.0p-61 */ - 0.00946564784943673166728, // 0x1362b9bf971bcd.0p-59 */ - ] - let z = x * x - let mut r = coef[4] + z * coef[5] - let t = coef[2] + z * coef[3] - let w = z * z - let s = z * x - let u = coef[0] + z * coef[1] - r = x + s * u + s * w * (t + w * r) - (if odd { -1.0 / r } else { r }).to_float() -} diff --git a/bundled-core/hashmap/README.mbt.md b/bundled-core/hashmap/README.mbt.md index b066f3a..0d5022e 100644 --- a/bundled-core/hashmap/README.mbt.md +++ b/bundled-core/hashmap/README.mbt.md @@ -10,7 +10,7 @@ You can create an empty map using `new()` or construct it using `from_array()`. ```moonbit test { - let _map2 : @hashmap.T[String, Int] = @hashmap.new() + let _map2 : @hashmap.HashMap[String, Int] = @hashmap.new() } ``` @@ -20,7 +20,7 @@ You can use `set()` to add a key-value pair to the map, and use `get()` to get a ```moonbit test { - let map : @hashmap.T[String, Int] = @hashmap.new() + let map : @hashmap.HashMap[String, Int] = @hashmap.new() map.set("a", 1) assert_eq(map.get("a"), Some(1)) assert_eq(map.get_or_default("a", 0), 1) @@ -70,7 +70,7 @@ Similarly, you can use `is_empty()` to check whether the map is empty. ```moonbit test { - let map: @hashmap.T[String, Int] = @hashmap.new() + let map: @hashmap.HashMap[String, Int] = @hashmap.new() assert_eq(map.is_empty(), true) } ``` diff --git a/bundled-core/hashmap/deprecated.mbt b/bundled-core/hashmap/deprecated.mbt index 9aa1fcb..4e36a8a 100644 --- a/bundled-core/hashmap/deprecated.mbt +++ b/bundled-core/hashmap/deprecated.mbt @@ -11,9 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("Use `get` instead. `op_get` will return `V` instead of `Option[V]` in the future.") -pub fn[K : Hash + Eq, V] op_get(self : T[K, V], key : K) -> V? { - self.get(key) -} diff --git a/bundled-core/hashmap/hashmap.mbt b/bundled-core/hashmap/hashmap.mbt index 6d912f0..83be15b 100644 --- a/bundled-core/hashmap/hashmap.mbt +++ b/bundled-core/hashmap/hashmap.mbt @@ -12,31 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -fn power_2_above(x : Int, n : Int) -> Int { - for i = x { - if i >= n { - break i - } - let next = i << 1 - if next < 0 { - // overflow happened - break i - } - continue next - } -} - -///| -test "power_2_above" { - inspect(power_2_above(1, 15), content="16") - inspect(power_2_above(1, 16), content="16") - inspect(power_2_above(1, 17), content="32") - inspect(power_2_above(1, 32), content="32") - inspect(power_2_above(128, 33), content="128") - inspect(power_2_above(1, 2147483647), content="1073741824") -} - ///| /// Creates a new empty hash map with the specified initial capacity. The actual /// capacity will be rounded up to the next power of 2 that is greater than or @@ -53,12 +28,13 @@ test "power_2_above" { /// Example: /// /// ```moonbit -/// let map : @hashmap.T[String, Int] = @hashmap.new(capacity=16) +/// let map : @hashmap.HashMap[String, Int] = @hashmap.new(capacity=16) /// inspect(map.capacity(), content="16") /// inspect(map.is_empty(), content="true") /// ``` -pub fn[K, V] new(capacity~ : Int = 8) -> T[K, V] { - let capacity = power_2_above(8, capacity) +#as_free_fn +pub fn[K, V] HashMap::new(capacity? : Int = 8) -> HashMap[K, V] { + let capacity = capacity.next_power_of_two() { size: 0, capacity, @@ -87,7 +63,10 @@ pub fn[K, V] new(capacity~ : Int = 8) -> T[K, V] { /// inspect(map.get(1), content="Some(\"ONE\")") /// inspect(map.get(2), content="Some(\"two\")") /// ``` -pub fn[K : Hash + Eq, V] from_array(arr : Array[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Hash + Eq, V] HashMap::from_array( + arr : Array[(K, V)], +) -> HashMap[K, V] { let m = new(capacity=arr.length()) arr.each(e => m.set(e.0, e.1)) m @@ -107,47 +86,70 @@ pub fn[K : Hash + Eq, V] from_array(arr : Array[(K, V)]) -> T[K, V] { /// Example: /// /// ```moonbit -/// let map : @hashmap.T[String, Int] = @hashmap.new() +/// let map : @hashmap.HashMap[String, Int] = @hashmap.new() /// map.set("key", 42) /// inspect(map.get("key"), content="Some(42)") /// map.set("key", 24) // update existing key /// inspect(map.get("key"), content="Some(24)") /// ``` -pub fn[K : Hash + Eq, V] set(self : T[K, V], key : K, value : V) -> Unit { +pub fn[K : Hash + Eq, V] set(self : HashMap[K, V], key : K, value : V) -> Unit { self.set_with_hash(key, value, key.hash()) } ///| fn[K : Eq, V] set_with_hash( - self : T[K, V], + self : HashMap[K, V], key : K, value : V, - hash : Int + hash : Int, ) -> Unit { if self.size >= self.capacity / 2 { self.grow() } - for idx = hash & self.capacity_mask, entry = { psl: 0, hash, key, value } { + let (idx, psl) = for psl = 0, idx = hash & self.capacity_mask { + match self.entries[idx] { + None => break (idx, psl) + Some(curr_entry) => { + if curr_entry.hash == hash && curr_entry.key == key { + curr_entry.value = value + return + } + if psl > curr_entry.psl { + self.push_away(idx, curr_entry) + break (idx, psl) + } + continue psl + 1, (idx + 1) & self.capacity_mask + } + } + } + let entry = { psl, key, value, hash } + self.entries[idx] = Some(entry) + self.size += 1 +} + +///| +fn[K, V] HashMap::push_away( + self : HashMap[K, V], + idx : Int, + entry : Entry[K, V], +) -> Unit { + for psl = entry.psl + 1, idx = (idx + 1) & self.capacity_mask, entry = entry { match self.entries[idx] { None => { + entry.psl = psl self.entries[idx] = Some(entry) - self.size += 1 break } - Some(curr_entry) => { - if curr_entry.hash == entry.hash && curr_entry.key == entry.key { - curr_entry.value = entry.value - break - } - let curr_entry = if entry.psl > curr_entry.psl { + Some(curr_entry) => + if psl > curr_entry.psl { + entry.psl = psl self.entries[idx] = Some(entry) - curr_entry + continue curr_entry.psl + 1, + (idx + 1) & self.capacity_mask, + curr_entry } else { - entry + continue psl + 1, (idx + 1) & self.capacity_mask, entry } - curr_entry.psl += 1 - continue (idx + 1) & self.capacity_mask, curr_entry - } } } } @@ -168,11 +170,15 @@ fn[K : Eq, V] set_with_hash( /// Example: /// /// ```moonbit -/// let map : @hashmap.T[String, Int] = @hashmap.new() +/// let map : @hashmap.HashMap[String, Int] = @hashmap.new() /// map["key"] = 42 /// inspect(map.get("key"), content="Some(42)") /// ``` -pub fn[K : Hash + Eq, V] op_set(self : T[K, V], key : K, value : V) -> Unit { +pub fn[K : Hash + Eq, V] op_set( + self : HashMap[K, V], + key : K, + value : V, +) -> Unit { self.set(key, value) } @@ -193,7 +199,7 @@ pub fn[K : Hash + Eq, V] op_set(self : T[K, V], key : K, value : V) -> Unit { /// inspect(map.get("key"), content="Some(42)") /// inspect(map.get("nonexistent"), content="None") /// ``` -pub fn[K : Hash + Eq, V] get(self : T[K, V], key : K) -> V? { +pub fn[K : Hash + Eq, V] get(self : HashMap[K, V], key : K) -> V? { // self.get_with_hash(key, key.hash()) let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { @@ -209,39 +215,32 @@ pub fn[K : Hash + Eq, V] get(self : T[K, V], key : K) -> V? { } ///| -fn[K : Eq, V] get_with_hash(self : T[K, V], key : K, hash : Int) -> V? { - for i = 0, idx = hash & self.capacity_mask { - guard self.entries[idx] is Some(entry) else { break None } - if entry.hash == hash && entry.key == key { - break Some(entry.value) - } - if i > entry.psl { - break None - } - continue i + 1, (idx + 1) & self.capacity_mask - } -} - -///| -/// Retrieves a value from the hash map using the index operator syntax. This -/// method is automatically called when using the square bracket notation -/// `map[key]`. +/// Retrieves the value associated with a given key in the hash map. /// /// Parameters: /// -/// * `map` : The hash map to retrieve the value from. -/// * `key` : The key to look up in the map. Must implement both `Hash` and `Eq` -/// traits. +/// * `self` : The hash map to search in. +/// * `key` : The key to look up in the map. /// -/// Returns `Some(value)` if the key exists in the map, `None` otherwise. +/// Returns `value` if the key exists in the map, panic otherwise. /// /// Example: /// /// ```moonbit /// let map = @hashmap.of([("key", 42)]) -/// inspect(map.get("key"), content="Some(42)") -/// inspect(map.get("nonexistent"), content="None") +/// inspect(map["key"], content="42") /// ``` +pub fn[K : Hash + Eq, V] op_get(self : HashMap[K, V], key : K) -> V { + let hash = key.hash() + for i = 0, idx = hash & self.capacity_mask { + guard self.entries[idx] is Some(entry) + if entry.hash == hash && entry.key == key { + break entry.value + } + guard entry.psl <= i + continue i + 1, (idx + 1) & self.capacity_mask + } +} ///| /// Gets the value associated with the given key. If the key doesn't exist in the @@ -261,25 +260,49 @@ fn[K : Eq, V] get_with_hash(self : T[K, V], key : K, hash : Int) -> V? { /// Example: /// /// ```moonbit -/// let map : @hashmap.T[String, Int] = @hashmap.new() +/// let map : @hashmap.HashMap[String, Int] = @hashmap.new() /// let value = map.get_or_init("key", () => { 42 }) /// inspect(value, content="42") /// inspect(map.get("key"), content="Some(42)") /// ``` pub fn[K : Hash + Eq, V] get_or_init( - self : T[K, V], + self : HashMap[K, V], key : K, - init : () -> V + init : () -> V, ) -> V { let hash = key.hash() - match self.get_with_hash(key, hash) { - Some(v) => v - None => { - let v = init() - self.set_with_hash(key, v, hash) - v + let (idx, psl, new_value, push_away) = for psl = 0, idx = hash & + self.capacity_mask { + match self.entries[idx] { + Some(entry) => { + if entry.hash == hash && entry.key == key { + return entry.value + } + if psl > entry.psl { + let new_value = init() + break (idx, psl, new_value, Some(entry)) + } + continue psl + 1, (idx + 1) & self.capacity_mask + } + None => { + let new_value = init() + break (idx, psl, new_value, None) + } } } + if self.size >= self.capacity / 2 { + // Slow path, we need to resize + self.grow() + self.set_with_hash(key, new_value, hash) + } else { + if push_away is Some(entry) { + self.push_away(idx, entry) + } + let entry = { psl, hash, key, value: new_value } + self.entries[idx] = Some(entry) + self.size += 1 + } + new_value } ///| @@ -303,9 +326,9 @@ pub fn[K : Hash + Eq, V] get_or_init( /// inspect(map.get_or_default("c", 0), content="0") /// ``` pub fn[K : Hash + Eq, V] get_or_default( - self : T[K, V], + self : HashMap[K, V], key : K, - default : V + default : V, ) -> V { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { @@ -337,7 +360,7 @@ pub fn[K : Hash + Eq, V] get_or_default( /// inspect(map.contains("a"), content="true") /// inspect(map.contains("c"), content="false") /// ``` -pub fn[K : Hash + Eq, V] contains(self : T[K, V], key : K) -> Bool { +pub fn[K : Hash + Eq, V] contains(self : HashMap[K, V], key : K) -> Bool { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { guard self.entries[idx] is Some(entry) else { return false } @@ -352,10 +375,31 @@ pub fn[K : Hash + Eq, V] contains(self : T[K, V], key : K) -> Bool { } ///| +/// Checks if a map contains a specific key-value pair. +/// +/// Parameters: +/// +/// * `map` : A map of type `@hashmap.HashMap[K, V]` to search in. +/// * `key` : The key to look up in the map. +/// * `value` : The value to be compared with the value associated with the key. +/// +/// Returns `true` if the map contains the specified key and its associated value +/// equals the given value, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// +/// let map = @hashmap.new() +/// map..set("a", 1)..set("b", 2) +/// inspect(map.contains_kv("a", 1), content="true") +/// inspect(map.contains_kv("a", 2), content="false") +/// inspect(map.contains_kv("c", 3), content="false") +/// ``` pub fn[K : Hash + Eq, V : Eq] contains_kv( - self : T[K, V], + self : HashMap[K, V], key : K, - value : V + value : V, ) -> Bool { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { @@ -389,13 +433,20 @@ pub fn[K : Hash + Eq, V : Eq] contains_kv( /// inspect(map.get("a"), content="None") /// inspect(map.size(), content="1") /// ``` -pub fn[K : Hash + Eq, V] remove(self : T[K, V], key : K) -> Unit { - let hash = key.hash() +pub fn[K : Hash + Eq, V] remove(self : HashMap[K, V], key : K) -> Unit { + self.remove_with_hash(key, key.hash()) +} + +///| +fn[K : Eq, V] remove_with_hash( + self : HashMap[K, V], + key : K, + hash : Int, +) -> Unit { for i = 0, idx = hash & self.capacity_mask { match self.entries[idx] { Some(entry) => { if entry.hash == hash && entry.key == key { - self.entries[idx] = None self.shift_back(idx) self.size -= 1 break @@ -411,24 +462,20 @@ pub fn[K : Hash + Eq, V] remove(self : T[K, V], key : K) -> Unit { } ///| -fn[K, V] shift_back(self : T[K, V], start_index : Int) -> Unit { - for prev = start_index, curr = (start_index + 1) & self.capacity_mask { - match self.entries[curr] { - Some({ psl, hash, key, value }) => { - if psl == 0 { - break - } - self.entries[prev] = Some({ psl: psl - 1, hash, key, value }) - self.entries[curr] = None - continue curr, (curr + 1) & self.capacity_mask - } - None => break +fn[K, V] shift_back(self : HashMap[K, V], idx : Int) -> Unit { + let next = (idx + 1) & self.capacity_mask + match self.entries[next] { + None | Some({ psl: 0, .. }) => self.entries[idx] = None + Some(entry) => { + entry.psl -= 1 + self.entries[idx] = Some(entry) + self.shift_back(next) } } } ///| -fn[K : Eq, V] grow(self : T[K, V]) -> Unit { +fn[K : Eq, V] grow(self : HashMap[K, V]) -> Unit { let old_entries = self.entries let new_capacity = self.capacity << 1 self.entries = FixedArray::make(new_capacity, None) @@ -461,7 +508,8 @@ fn[K : Eq, V] grow(self : T[K, V]) -> Unit { /// inspect(map.get(1), content="Some(\"one\")") /// inspect(map.get(2), content="Some(\"two\")") /// ``` -pub fn[K : Eq + Hash, V] of(arr : FixedArray[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Eq + Hash, V] HashMap::of(arr : FixedArray[(K, V)]) -> HashMap[K, V] { let m = new(capacity=arr.length()) arr.each(e => m.set(e.0, e.1)) m @@ -489,10 +537,10 @@ test "of" { /// Example: /// /// ```moonbit -/// let samples : Array[@hashmap.T[Int, String]] = @quickcheck.samples(5) +/// let samples : Array[@hashmap.HashMap[Int, String]] = @quickcheck.samples(5) /// inspect(samples.length(), content="5") /// ``` -pub impl[K : @quickcheck.Arbitrary + Hash + Eq, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[ +pub impl[K : @quickcheck.Arbitrary + Hash + Eq, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashMap[ K, V, ] with arbitrary(size, rs) { @@ -505,7 +553,56 @@ pub impl[K : @quickcheck.Arbitrary + Hash + Eq, V : @quickcheck.Arbitrary] @quic } ///| -priv type MyString String derive(Eq) +pub impl[K, V] Default for HashMap[K, V] with default() { + new() +} + +///| +/// Applies a function to each key-value pair in the map and +/// returns a new map with the results, using the original keys. +pub fn[K, V, V2] HashMap::map( + self : HashMap[K, V], + f : (K, V) -> V2, +) -> HashMap[K, V2] { + let other = { + capacity: self.capacity, + entries: FixedArray::make(self.capacity, None), + size: self.size, + capacity_mask: self.capacity_mask, + } + if self.size == 0 { + return other + } + for i in 0.. HashMap[K, V] { + let other = { + capacity: self.capacity, + entries: FixedArray::make(self.capacity, None), + size: self.size, + capacity_mask: self.capacity_mask, + } + if self.size == 0 { + return other + } + for i in 0.. 1), content="1") inspect(m.get("a"), content="Some(1)") inspect(m.get_or_init("a", () => 2), content="1") diff --git a/bundled-core/hashmap/hashmap.mbti b/bundled-core/hashmap/hashmap.mbti deleted file mode 100644 index b58f4f7..0000000 --- a/bundled-core/hashmap/hashmap.mbti +++ /dev/null @@ -1,45 +0,0 @@ -package "moonbitlang/core/hashmap" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[K : Hash + Eq, V] from_array(Array[(K, V)]) -> T[K, V] - -fn[K : Hash + Eq, V] from_iter(Iter[(K, V)]) -> T[K, V] - -fn[K, V] new(capacity~ : Int = ..) -> T[K, V] - -fn[K : Eq + Hash, V] of(FixedArray[(K, V)]) -> T[K, V] - -// Types and methods -type T[K, V] -fn[K, V] T::capacity(Self[K, V]) -> Int -fn[K, V] T::clear(Self[K, V]) -> Unit -fn[K : Hash + Eq, V] T::contains(Self[K, V], K) -> Bool -fn[K : Hash + Eq, V : Eq] T::contains_kv(Self[K, V], K, V) -> Bool -fn[K, V] T::each(Self[K, V], (K, V) -> Unit) -> Unit -fn[K, V] T::eachi(Self[K, V], (Int, K, V) -> Unit) -> Unit -fn[K : Hash + Eq, V] T::get(Self[K, V], K) -> V? -fn[K : Hash + Eq, V] T::get_or_default(Self[K, V], K, V) -> V -fn[K : Hash + Eq, V] T::get_or_init(Self[K, V], K, () -> V) -> V -fn[K, V] T::is_empty(Self[K, V]) -> Bool -fn[K, V] T::iter(Self[K, V]) -> Iter[(K, V)] -fn[K, V] T::iter2(Self[K, V]) -> Iter2[K, V] -#deprecated -fn[K : Hash + Eq, V] T::op_get(Self[K, V], K) -> V? -fn[K : Hash + Eq, V] T::op_set(Self[K, V], K, V) -> Unit -fn[K : Hash + Eq, V] T::remove(Self[K, V], K) -> Unit -fn[K : Hash + Eq, V] T::set(Self[K, V], K, V) -> Unit -fn[K, V] T::size(Self[K, V]) -> Int -fn[K, V] T::to_array(Self[K, V]) -> Array[(K, V)] -impl[K : Hash + Eq, V : Eq] Eq for T[K, V] -impl[K : Show, V : Show] Show for T[K, V] -impl[K : Show, V : ToJson] ToJson for T[K, V] -impl[K : @quickcheck.Arbitrary + Hash + Eq, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K, V] - -// Type aliases - -// Traits - diff --git a/bundled-core/hashmap/hashmap_test.mbt b/bundled-core/hashmap/hashmap_test.mbt index af135c5..3b6e27a 100644 --- a/bundled-core/hashmap/hashmap_test.mbt +++ b/bundled-core/hashmap/hashmap_test.mbt @@ -14,9 +14,9 @@ ///| test "new" { - let m : @hashmap.T[Int, Int] = @hashmap.new() + let m : @hashmap.HashMap[Int, Int] = @hashmap.new() inspect(m.capacity(), content="8") - assert_eq(m.size(), 0) + inspect(m.size(), content="0") } ///| @@ -29,6 +29,9 @@ test "get" { inspect(m.get("b"), content="Some(2)") inspect(m.get("c"), content="Some(3)") inspect(m.get("d"), content="None") + + // pattern + guard m is { "a": 1, "b": 2, "c": 3, "d"? : None, .. } } ///| @@ -37,10 +40,30 @@ test "get_or_default" { m.set("a", 1) m.set("b", 2) m.set("c", 3) - assert_eq(m.get_or_default("a", 42), 1) - assert_eq(m.get_or_default("b", 42), 2) - assert_eq(m.get_or_default("c", 42), 3) - assert_eq(m.get_or_default("d", 42), 42) + inspect(m.get_or_default("a", 42), content="1") + inspect(m.get_or_default("b", 42), content="2") + inspect(m.get_or_default("c", 42), content="3") + inspect(m.get_or_default("d", 42), content="42") +} + +///| +test "get_or_init" { + let m : @hashmap.HashMap[String, Array[Int]] = @hashmap.new() + m.get_or_init("a", () => Array::new()).push(1) + m.get_or_init("b", () => Array::new()).push(2) + m.get_or_init("a", () => Array::new()).push(3) + assert_eq(m.get("a"), Some([1, 3])) + assert_eq(m.get("b"), Some([2])) + assert_eq(m.size(), 2) +} + +///| +test "get_or_init full" { + let m = @hashmap.new(capacity=2) + m.get_or_init("a", () => 0) |> ignore + m.get_or_init("b", () => 0) |> ignore + m.get_or_init("c", () => 0) |> ignore + assert_eq(m.size(), 3) } ///| @@ -57,9 +80,16 @@ test "op_get" { let m = @hashmap.new() m.set("a", 1) m.set("b", 2) - assert_eq(m.get("a"), Some(1)) - assert_eq(m.get("b"), Some(2)) - assert_eq(m.get("c"), None) + assert_eq(m["a"], 1) + assert_eq(m["b"], 2) +} + +///| +test "panic op_get" { + let m = @hashmap.new() + m.set("a", 1) + m.set("b", 2) + m["c"] |> ignore } ///| @@ -76,8 +106,8 @@ test "set_update" { test "contains" { let m = @hashmap.new() m.set("a", 1) - assert_eq(m.contains("a"), true) - assert_eq(m.contains("b"), false) + inspect(m.contains("a"), content="true") + inspect(m.contains("b"), content="false") } ///| @@ -91,19 +121,19 @@ test "from_array" { ///| test "size" { let m = @hashmap.new() - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.set("a", 1) - assert_eq(m.size(), 1) + inspect(m.size(), content="1") } ///| test "is_empty" { let m = @hashmap.new() - assert_eq(m.is_empty(), true) + inspect(m.is_empty(), content="true") m.set("a", 1) - assert_eq(m.is_empty(), false) + inspect(m.is_empty(), content="false") m.remove("a") - assert_eq(m.is_empty(), true) + inspect(m.is_empty(), content="true") } ///| @@ -111,7 +141,7 @@ test "iter" { let m = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) let mut sum = 0 m.each((_k, v) => sum += v) - assert_eq(sum, 6) + inspect(sum, content="6") } ///| @@ -123,8 +153,8 @@ test "iteri" { s += i.to_string() sum += v }) - assert_eq(s, "012") - assert_eq(sum, 6) + inspect(s, content="012") + inspect(sum, content="6") } ///| @@ -164,9 +194,9 @@ test "to_array" { let map = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) inspect( map.to_array(), - content= + content=( #|[(2, "two"), (1, "one"), (3, "three")] - , + ), ) } @@ -184,7 +214,7 @@ test "remove_nonexistent_key" { m.set("a", 1) m.set("b", 2) m.remove("c") - assert_eq(m.size(), 2) + inspect(m.size(), content="2") } ///| @@ -201,7 +231,7 @@ test "remove_nonexistent_key" { m.set("a", 1) m.set("b", 2) m.remove("c") - assert_eq(m.size(), 2) + inspect(m.size(), content="2") } ///| @@ -218,7 +248,7 @@ test "remove_nonexistent_key" { m.set("a", 1) m.set("b", 2) m.remove("c") - assert_eq(m.size(), 2) + inspect(m.size(), content="2") } ///| @@ -235,7 +265,7 @@ test "remove_nonexistent_key" { m.set("a", 1) m.set("b", 2) m.remove("c") - assert_eq(m.size(), 2) + inspect(m.size(), content="2") } ///| @@ -310,13 +340,13 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let map : @hashmap.T[Int, Int] = @hashmap.from_iter(Iter::empty()) + let map : @hashmap.HashMap[Int, Int] = @hashmap.from_iter(Iter::empty()) inspect(map, content="HashMap::of([])") } ///| test "@hashmap.contains/empty" { - let map : @hashmap.T[Int, String] = @hashmap.new() + let map : @hashmap.HashMap[Int, String] = @hashmap.new() inspect(map.contains(42), content="false") } @@ -364,3 +394,51 @@ test "@hashmap.eq" { let map2 = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) inspect(map1 == map2, content="true") } + +///| +test "@hashmap.map" { + let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) + let v = map.map((k, v) => k + v.to_string()) + inspect( + v, + content=( + #|HashMap::of([("a", "a1"), ("c", "c3"), ("b", "b2")]) + ), + ) + map["d"] = 10 + map["e"] = 20 + map.remove("c") + let v = map.map((k, v) => k + v.to_string()) + inspect( + v, + content=( + #|HashMap::of([("e", "e20"), ("a", "a1"), ("b", "b2"), ("d", "d10")]) + ), + ) + let v : @hashmap.HashMap[String, String] = @hashmap.new().map((k, v) => k + v) + inspect(v, content="HashMap::of([])") +} + +///| +test "@hashmap.copy" { + let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) + let copy = map.copy() + inspect( + copy, + content=( + #|HashMap::of([("a", 1), ("c", 3), ("b", 2)]) + ), + ) + map["d"] = 10 + map["e"] = 20 + map.remove("c") + let copy = map.copy() + inspect( + copy, + content=( + #|HashMap::of([("e", 20), ("a", 1), ("b", 2), ("d", 10)]) + ), + ) + let copy : @hashmap.HashMap[String, String] = @hashmap.new().copy() + inspect(copy, content="HashMap::of([])") +} diff --git a/bundled-core/hashmap/json.mbt b/bundled-core/hashmap/json.mbt index 8f1ee99..da8112d 100644 --- a/bundled-core/hashmap/json.mbt +++ b/bundled-core/hashmap/json.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -pub impl[K : Show, V : ToJson] ToJson for T[K, V] with to_json(self) { +pub impl[K : Show, V : ToJson] ToJson for HashMap[K, V] with to_json(self) { let object = Map::new(capacity=self.capacity) for k, v in self { object[k.to_string()] = v.to_json() diff --git a/bundled-core/hashmap/moon.pkg.json b/bundled-core/hashmap/moon.pkg.json index f86caf6..0ef05bb 100644 --- a/bundled-core/hashmap/moon.pkg.json +++ b/bundled-core/hashmap/moon.pkg.json @@ -4,7 +4,8 @@ "moonbitlang/core/test", "moonbitlang/core/array", "moonbitlang/core/tuple", - "moonbitlang/core/quickcheck" + "moonbitlang/core/quickcheck", + "moonbitlang/core/int" ], "test-import": ["moonbitlang/core/string", "moonbitlang/core/json"] } diff --git a/bundled-core/hashmap/pattern_test.mbt b/bundled-core/hashmap/pattern_test.mbt index f45ce7d..4f4e1a0 100644 --- a/bundled-core/hashmap/pattern_test.mbt +++ b/bundled-core/hashmap/pattern_test.mbt @@ -22,8 +22,8 @@ test "pattern" { let { "name"? : name, "age"? : age, "is_human"? : is_human, .. } = m inspect( (name, age, is_human), - content= + content=( #|(Some("John Doe"), Some("43"), Some("true")) - , + ), ) } diff --git a/bundled-core/hashmap/pkg.generated.mbti b/bundled-core/hashmap/pkg.generated.mbti new file mode 100644 index 0000000..41fd4ce --- /dev/null +++ b/bundled-core/hashmap/pkg.generated.mbti @@ -0,0 +1,55 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/hashmap" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type HashMap[K, V] +fn[K, V] HashMap::capacity(Self[K, V]) -> Int +fn[K, V] HashMap::clear(Self[K, V]) -> Unit +fn[K : Hash + Eq, V] HashMap::contains(Self[K, V], K) -> Bool +fn[K : Hash + Eq, V : Eq] HashMap::contains_kv(Self[K, V], K, V) -> Bool +fn[K, V] HashMap::copy(Self[K, V]) -> Self[K, V] +fn[K, V] HashMap::each(Self[K, V], (K, V) -> Unit raise?) -> Unit raise? +fn[K, V] HashMap::eachi(Self[K, V], (Int, K, V) -> Unit raise?) -> Unit raise? +#as_free_fn +fn[K : Hash + Eq, V] HashMap::from_array(Array[(K, V)]) -> Self[K, V] +#as_free_fn +fn[K : Hash + Eq, V] HashMap::from_iter(Iter[(K, V)]) -> Self[K, V] +fn[K : Hash + Eq, V] HashMap::get(Self[K, V], K) -> V? +fn[K : Hash + Eq, V] HashMap::get_or_default(Self[K, V], K, V) -> V +fn[K : Hash + Eq, V] HashMap::get_or_init(Self[K, V], K, () -> V) -> V +fn[K, V] HashMap::is_empty(Self[K, V]) -> Bool +fn[K, V] HashMap::iter(Self[K, V]) -> Iter[(K, V)] +fn[K, V] HashMap::iter2(Self[K, V]) -> Iter2[K, V] +fn[K, V] HashMap::keys(Self[K, V]) -> Iter[K] +fn[K, V, V2] HashMap::map(Self[K, V], (K, V) -> V2) -> Self[K, V2] +#as_free_fn +fn[K, V] HashMap::new(capacity? : Int) -> Self[K, V] +#as_free_fn +fn[K : Eq + Hash, V] HashMap::of(FixedArray[(K, V)]) -> Self[K, V] +fn[K : Hash + Eq, V] HashMap::op_get(Self[K, V], K) -> V +fn[K : Hash + Eq, V] HashMap::op_set(Self[K, V], K, V) -> Unit +fn[K : Hash + Eq, V] HashMap::remove(Self[K, V], K) -> Unit +fn[K, V] HashMap::retain(Self[K, V], (K, V) -> Bool) -> Unit +fn[K : Hash + Eq, V] HashMap::set(Self[K, V], K, V) -> Unit +fn[K, V] HashMap::size(Self[K, V]) -> Int +fn[K, V] HashMap::to_array(Self[K, V]) -> Array[(K, V)] +fn[K, V] HashMap::values(Self[K, V]) -> Iter[V] +impl[K, V] Default for HashMap[K, V] +impl[K : Hash + Eq, V : Eq] Eq for HashMap[K, V] +impl[K : Show, V : Show] Show for HashMap[K, V] +impl[K : Show, V : ToJson] ToJson for HashMap[K, V] +impl[K : @quickcheck.Arbitrary + Hash + Eq, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashMap[K, V] + +// Type aliases +pub typealias HashMap as T + +// Traits + diff --git a/bundled-core/hashmap/types.mbt b/bundled-core/hashmap/types.mbt index 5998973..7cc7842 100644 --- a/bundled-core/hashmap/types.mbt +++ b/bundled-core/hashmap/types.mbt @@ -38,7 +38,7 @@ priv struct Entry[K, V] { /// map.set(3, "updated") /// assert_eq(map.get(3), Some("updated")) /// ``` -struct T[K, V] { +struct HashMap[K, V] { mut entries : FixedArray[Entry[K, V]?] mut capacity : Int mut capacity_mask : Int // capacity_mask = capacity - 1, used to find idx @@ -49,9 +49,9 @@ struct T[K, V] { } ///| -pub impl[K : Hash + Eq, V : Eq] Eq for T[K, V] with op_equal( - self : T[K, V], - that : T[K, V] +pub impl[K : Hash + Eq, V : Eq] Eq for HashMap[K, V] with equal( + self : HashMap[K, V], + that : HashMap[K, V], ) -> Bool { guard self.size == that.size else { return false } for k, v in self { @@ -60,3 +60,7 @@ pub impl[K : Hash + Eq, V : Eq] Eq for T[K, V] with op_equal( true } } + +///| +#deprecated("Use `HashMap` instead of `T`") +pub typealias HashMap as T diff --git a/bundled-core/hashmap/types_test.mbt b/bundled-core/hashmap/types_test.mbt new file mode 100644 index 0000000..71b2bbc --- /dev/null +++ b/bundled-core/hashmap/types_test.mbt @@ -0,0 +1,107 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "HashMap equality - identical maps" { + let map1 = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) + let map2 = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) + assert_eq(map1, map2) + assert_true(map1 == map2) +} + +///| +test "HashMap equality - same content different order" { + let map1 = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) + let map2 = @hashmap.of([(3, "three"), (1, "one"), (2, "two")]) + assert_eq(map1, map2) + assert_true(map1 == map2) +} + +///| +test "HashMap equality - empty maps" { + let map1 : @hashmap.HashMap[Int, String] = @hashmap.new() + let map2 : @hashmap.HashMap[Int, String] = @hashmap.new() + assert_eq(map1, map2) + assert_true(map1 == map2) +} + +///| +test "HashMap equality - different sizes" { + let map1 = @hashmap.of([(1, "one"), (2, "two")]) + let map2 = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) + assert_false(map1 == map2) + assert_false(map2 == map1) +} + +///| +test "HashMap equality - same size different keys" { + let map1 = @hashmap.of([(1, "one"), (2, "two")]) + let map2 = @hashmap.of([(1, "one"), (3, "three")]) + assert_false(map1 == map2) + assert_false(map2 == map1) +} + +///| +test "HashMap equality - same size different values" { + let map1 = @hashmap.of([(1, "one"), (2, "two")]) + let map2 = @hashmap.of([(1, "one"), (2, "different")]) + assert_false(map1 == map2) + assert_false(map2 == map1) +} + +///| +test "HashMap equality - one empty one non-empty" { + let empty_map : @hashmap.HashMap[Int, String] = @hashmap.new() + let non_empty_map = @hashmap.of([(1, "one")]) + assert_false(empty_map == non_empty_map) + assert_false(non_empty_map == empty_map) +} + +///| +test "HashMap equality - self equality" { + let map = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) + assert_eq(map, map) + assert_true(map == map) +} + +///| +test "HashMap equality - mixed types" { + let map1 = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) + let map2 = @hashmap.of([("c", 3), ("a", 1), ("b", 2)]) + assert_eq(map1, map2) + assert_true(map1 == map2) + let map3 = @hashmap.of([("a", 1), ("b", 2), ("d", 4)]) + assert_false(map1 == map3) +} + +///| +test "HashMap equality - after modifications" { + let map1 = @hashmap.of([(1, "one"), (2, "two")]) + let map2 = @hashmap.of([(1, "one")]) + + // Initially not equal + assert_false(map1 == map2) + + // Add element to map2 + map2.set(2, "two") + assert_true(map1 == map2) + + // Modify value in map1 + map1.set(1, "ONE") + assert_false(map1 == map2) + + // Make them equal again + map2.set(1, "ONE") + assert_true(map1 == map2) +} diff --git a/bundled-core/hashmap/utils.mbt b/bundled-core/hashmap/utils.mbt index 7131df8..8adef9f 100644 --- a/bundled-core/hashmap/utils.mbt +++ b/bundled-core/hashmap/utils.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -fn[K : Show, V : Show] debug_entries(self : T[K, V]) -> String { +fn[K : Show, V : Show] debug_entries(self : HashMap[K, V]) -> String { for s = "", i = 0; i < self.entries.length(); { let s = if i > 0 { s + "," } else { s } match self.entries[i] { @@ -43,7 +43,7 @@ fn[K : Show, V : Show] debug_entries(self : T[K, V]) -> String { /// inspect(map.size(), content="0") /// inspect(map.get("a"), content="None") /// ``` -pub fn[K, V] clear(self : T[K, V]) -> Unit { +pub fn[K, V] clear(self : HashMap[K, V]) -> Unit { self.entries.fill(None) self.size = 0 } @@ -67,7 +67,7 @@ pub fn[K, V] clear(self : T[K, V]) -> Unit { /// inspect(pairs.contains((1, "one")), content="true") /// inspect(pairs.contains((2, "two")), content="true") /// ``` -pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { +pub fn[K, V] iter(self : HashMap[K, V]) -> Iter[(K, V)] { Iter::new(yield_ => for entry in self.entries { if entry is Some({ key, value, .. }) { guard yield_((key, value)) is IterContinue else { break IterEnd } @@ -97,7 +97,7 @@ pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { /// map.iter2().each((k, _) => { sum = sum + k }) /// inspect(sum, content="3") /// ``` -pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { +pub fn[K, V] iter2(self : HashMap[K, V]) -> Iter2[K, V] { Iter2::new(yield_ => for entry in self.entries { if entry is Some({ key, value, .. }) { guard yield_(key, value) is IterContinue else { break IterEnd } @@ -125,7 +125,7 @@ pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { /// let arr = [(1, "one"), (2, "two")] /// let iter = Iter::new((yield_) => { /// for pair in arr { -/// if yield_(pair) == IterEnd { +/// if yield_(pair) is IterEnd { /// break IterEnd /// } /// } else { @@ -136,7 +136,10 @@ pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { /// inspect(map.get(1), content="Some(\"one\")") /// inspect(map.get(2), content="Some(\"two\")") /// ``` -pub fn[K : Hash + Eq, V] from_iter(iter : Iter[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Hash + Eq, V] HashMap::from_iter( + iter : Iter[(K, V)], +) -> HashMap[K, V] { let m = new() iter.each(e => m[e.0] = e.1) m @@ -157,11 +160,14 @@ pub fn[K : Hash + Eq, V] from_iter(iter : Iter[(K, V)]) -> T[K, V] { /// ```moonbit /// let map = @hashmap.of([(1, "one"), (2, "two")]) /// let arr = map.to_array() -/// inspect(arr, content= -/// #|[(2, "two"), (1, "one")] -/// ) +/// inspect( +/// arr, +/// content=( +/// #|[(2, "two"), (1, "one")] +/// ), +/// ) /// ``` -pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { +pub fn[K, V] to_array(self : HashMap[K, V]) -> Array[(K, V)] { let mut i = 0 let res = while i < self.capacity { if self.entries[i] is Some({ key, value, .. }) { @@ -172,7 +178,7 @@ pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { } else { [] } - if not(res.is_empty()) { + if !res.is_empty() { let mut res_idx = 1 while res_idx < res.length() && i < self.capacity { if self.entries[i] is Some({ key, value, .. }) { @@ -200,7 +206,7 @@ pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { /// let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) /// inspect(map.size(), content="3") /// ``` -pub fn[K, V] size(self : T[K, V]) -> Int { +pub fn[K, V] size(self : HashMap[K, V]) -> Int { self.size } @@ -219,10 +225,10 @@ pub fn[K, V] size(self : T[K, V]) -> Int { /// Example: /// /// ```moonbit -/// let map : @hashmap.T[Int, String] = @hashmap.new(capacity=16) +/// let map : @hashmap.HashMap[Int, String] = @hashmap.new(capacity=16) /// inspect(map.capacity(), content="16") /// ``` -pub fn[K, V] capacity(self : T[K, V]) -> Int { +pub fn[K, V] capacity(self : HashMap[K, V]) -> Int { self.capacity } @@ -239,12 +245,12 @@ pub fn[K, V] capacity(self : T[K, V]) -> Int { /// Example: /// /// ```moonbit -/// let map : @hashmap.T[String, Int] = @hashmap.new() +/// let map : @hashmap.HashMap[String, Int] = @hashmap.new() /// inspect(map.is_empty(), content="true") /// map.set("key", 42) /// inspect(map.is_empty(), content="false") /// ``` -pub fn[K, V] is_empty(self : T[K, V]) -> Bool { +pub fn[K, V] is_empty(self : HashMap[K, V]) -> Bool { self.size == 0 } @@ -266,7 +272,11 @@ pub fn[K, V] is_empty(self : T[K, V]) -> Bool { /// map.each((k, v) => { result = result + "\{k}:\{v}," }) /// inspect(result, content="2:two,1:one,") /// ``` -pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit) -> Unit { +#locals(f) +pub fn[K, V] each( + self : HashMap[K, V], + f : (K, V) -> Unit raise?, +) -> Unit raise? { for i in 0.. Unit) -> Unit { /// // "b" is at index 1 /// inspect(result, content="2") /// ``` -pub fn[K, V] eachi(self : T[K, V], f : (Int, K, V) -> Unit) -> Unit { +#locals(f) +pub fn[K, V] eachi( + self : HashMap[K, V], + f : (Int, K, V) -> Unit raise?, +) -> Unit raise? { for i = 0, idx = 0; i < self.capacity; { match self.entries[i] { Some({ key, value, .. }) => { @@ -320,11 +334,14 @@ pub fn[K, V] eachi(self : T[K, V], f : (Int, K, V) -> Unit) -> Unit { /// /// ```moonbit /// let map = @hashmap.of([(1, "one"), (2, "two")]) -/// inspect(map, content= -/// #|HashMap::of([(2, "two"), (1, "one")]) +/// inspect( +/// map, +/// content=( +/// #|HashMap::of([(2, "two"), (1, "one")]) +/// ), /// ) /// ``` -pub impl[K : Show, V : Show] Show for T[K, V] with output(self, logger) { +pub impl[K : Show, V : Show] Show for HashMap[K, V] with output(self, logger) { logger.write_string("HashMap::of([") self.eachi((i, k, v) => { if i > 0 { @@ -339,3 +356,102 @@ pub impl[K : Show, V : Show] Show for T[K, V] with output(self, logger) { }) logger.write_string("])") } + +///| +/// Returns an iterator over all keys in the hash map. +/// +/// Parameters: +/// +/// * `self` : The hash map to iterate over. +/// +/// Returns an iterator that yields each key in the hash map in unspecified order. +/// The keys are yielded in the same order as they appear in the internal storage. +/// +/// Example: +/// +/// ```moonbit +/// let map = @hashmap.of([(1, "one"), (2, "two"), (3, "three")]) +/// let keys = map.keys().to_array() +/// inspect(keys.length(), content="3") +/// inspect(keys.contains(1), content="true") +/// inspect(keys.contains(2), content="true") +/// inspect(keys.contains(3), content="true") +/// ``` +pub fn[K, V] HashMap::keys(self : HashMap[K, V]) -> Iter[K] { + Iter::new(yield_ => for entry in self.entries { + if entry is Some({ key, .. }) { + guard yield_(key) is IterContinue else { break IterEnd } + } + } else { + IterContinue + }) +} + +///| +/// Returns an iterator over all values in the hash map. +/// +/// Parameters: +/// +/// * `self` : The hash map to iterate over. +/// +/// Returns an iterator that yields each value in the hash map in unspecified order. +/// The values are yielded in the same order as they appear in the internal storage. +/// +/// Example: +/// +/// ```moonbit +/// let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) +/// let values = map.values().to_array() +/// inspect(values.length(), content="3") +/// inspect(values.contains(1), content="true") +/// inspect(values.contains(2), content="true") +/// inspect(values.contains(3), content="true") +/// ``` +pub fn[K, V] HashMap::values(self : HashMap[K, V]) -> Iter[V] { + Iter::new(yield_ => for entry in self.entries { + if entry is Some({ value, .. }) { + guard yield_(value) is IterContinue else { break IterEnd } + } + } else { + IterContinue + }) +} + +///| +/// Retains only the key-value pairs that satisfy the given predicate function. +/// This method modifies the hash map in-place, removing all entries for which +/// the predicate returns `false`. +/// +/// Parameters: +/// +/// * `self` : The hash map to be filtered. +/// * `predicate` : A function that takes a key and value as arguments and returns +/// `true` if the key-value pair should be kept, `false` if it should be removed. +/// +/// Example: +/// +/// ```moonbit +/// let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3), ("d", 4)]) +/// map.retain((_k, v) => v % 2 == 0) // Keep only even values +/// inspect(map.size(), content="2") +/// inspect(map.get("a"), content="None") +/// inspect(map.get("b"), content="Some(2)") +/// inspect(map.get("c"), content="None") +/// inspect(map.get("d"), content="Some(4)") +/// ``` +#locals(f) +pub fn[K, V] retain(self : HashMap[K, V], f : (K, V) -> Bool) -> Unit { + let last = self.capacity - 1 + while self.entries[last] is Some(entry) && !f(entry.key, entry.value) { + self.shift_back(last) + self.size -= 1 + } + for i = last - 1; i >= 0; i = i - 1 { + if self.entries[i] is Some(entry) { + if !f(entry.key, entry.value) { + self.shift_back(i) + self.size -= 1 + } + } + } +} diff --git a/bundled-core/hashmap/utils_test.mbt b/bundled-core/hashmap/utils_test.mbt index 27c42c7..ae66794 100644 --- a/bundled-core/hashmap/utils_test.mbt +++ b/bundled-core/hashmap/utils_test.mbt @@ -15,7 +15,7 @@ ///| test "capacity" { let m = @hashmap.new() - assert_eq(m.capacity(), 8) + inspect(m.capacity(), content="8") m.set("a", 1) m.set("b", 2) m.set("c", 3) @@ -24,5 +24,143 @@ test "capacity" { m.set("f", 6) m.set("g", 7) m.set("h", 8) - assert_eq(m.capacity(), 16) + inspect(m.capacity(), content="16") +} + +///| +test "T::retain - keep even values" { + let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)]) + map.retain((_k, v) => v % 2 == 0) + inspect(map.size(), content="2") + inspect(map.get("a"), content="None") + inspect(map.get("b"), content="Some(2)") + inspect(map.get("c"), content="None") + inspect(map.get("d"), content="Some(4)") + inspect(map.get("e"), content="None") +} + +///| +test "T::retain - keep all" { + let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) + map.retain((_k, _v) => true) + inspect(map.size(), content="3") + inspect(map.get("a"), content="Some(1)") + inspect(map.get("b"), content="Some(2)") + inspect(map.get("c"), content="Some(3)") +} + +///| +test "T::retain - remove all" { + let map = @hashmap.of([("a", 1), ("b", 2), ("c", 3)]) + map.retain((_k, _v) => false) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") + inspect(map.get("a"), content="None") + inspect(map.get("b"), content="None") + inspect(map.get("c"), content="None") +} + +///| +test "T::retain - empty map" { + let map : @hashmap.HashMap[String, Int] = @hashmap.new() + map.retain((_k, _v) => true) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") +} + +///| +test "T::retain - key-based filtering" { + let map = @hashmap.of([ + ("apple", 5), + ("banana", 6), + ("cherry", 5), + ("date", 4), + ]) + map.retain((k, _v) => k.length() >= 6) // Keep keys with 6+ characters + inspect(map.size(), content="2") + inspect(map.get("apple"), content="None") // 5 chars + inspect(map.get("banana"), content="Some(6)") // 6 chars + inspect(map.get("cherry"), content="Some(5)") // 6 chars + inspect(map.get("date"), content="None") // 4 chars +} + +///| +test "T::retain - filter by both key and value" { + let map = @hashmap.of([("a1", 1), ("b2", 2), ("c3", 3), ("d4", 4), ("e5", 5)]) + map.retain((k, v) => k.contains("2") || v > 3) // Keep keys containing "2" or values > 3 + inspect(map.size(), content="3") + inspect(map.get("a1"), content="None") + inspect(map.get("b2"), content="Some(2)") // key contains "2" + inspect(map.get("c3"), content="None") + inspect(map.get("d4"), content="Some(4)") // value > 3 + inspect(map.get("e5"), content="Some(5)") // value > 3 +} + +///| +test "T::retain - single element map" { + let map = @hashmap.of([("only", 42)]) + + // Test keeping the element + map.retain((_k, v) => v > 30) + inspect(map.size(), content="1") + inspect(map.get("only"), content="Some(42)") + + // Test removing the element + map.retain((_k, v) => v < 30) + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") +} + +///| +test "T::retain - large map with complex predicate" { + let map = @hashmap.new() + for i = 0; i < 100; i = i + 1 { + map.set("key" + i.to_string(), i) + } + + // Keep only elements where value is divisible by 6 (both divisible by 2 and 3) + map.retain((_k, v) => v % 6 == 0) + inspect(map.size(), content="17") // 0, 6, 12, 18, ..., 96 (17 numbers) + inspect(map.get("key0"), content="Some(0)") + inspect(map.get("key6"), content="Some(6)") + inspect(map.get("key12"), content="Some(12)") + inspect(map.get("key1"), content="None") + inspect(map.get("key2"), content="None") + inspect(map.get("key3"), content="None") +} + +///| +test "T::retain - with collisions" { + // Create keys that likely hash to same buckets + let map = @hashmap.new() + let keys = ["a", "aa", "aaa", "aaaa", "aaaaa"] + for i = 0; i < keys.length(); i = i + 1 { + map.set(keys[i], i) + } + + // Keep only keys with odd length + map.retain((k, _v) => k.length() % 2 == 1) + inspect(map.size(), content="3") + inspect(map.get("a"), content="Some(0)") // length 1 (odd) + inspect(map.get("aa"), content="None") // length 2 (even) + inspect(map.get("aaa"), content="Some(2)") // length 3 (odd) + inspect(map.get("aaaa"), content="None") // length 4 (even) + inspect(map.get("aaaaa"), content="Some(4)") // length 5 (odd) +} + +///| +test "T::retain - preserve map integrity after retain" { + let map = @hashmap.of([("x", 1), ("y", 2), ("z", 3)]) + map.retain((_k, v) => v != 2) // Remove middle element + + // Test that map operations still work correctly + map.set("w", 4) + inspect(map.get("w"), content="Some(4)") + inspect(map.size(), content="3") + map.remove("x") + inspect(map.get("x"), content="None") + inspect(map.size(), content="2") + map.clear() + inspect(map.size(), content="0") + inspect(map.is_empty(), content="true") } diff --git a/bundled-core/hashset/README.mbt.md b/bundled-core/hashset/README.mbt.md index a6478c8..2243687 100644 --- a/bundled-core/hashset/README.mbt.md +++ b/bundled-core/hashset/README.mbt.md @@ -11,7 +11,7 @@ You can create an empty set using `new()` or construct it using `from_array()`. ```moonbit test { let _set1 = @hashset.of([1, 2, 3, 4, 5]) - let _set2 : @hashset.T[String] = @hashset.new() + let _set2 : @hashset.HashSet[String] = @hashset.new() } ``` @@ -21,7 +21,7 @@ You can use `insert()` to add a key to the set, and `contains()` to check whethe ```moonbit test { - let set : @hashset.T[String] = @hashset.new() + let set : @hashset.HashSet[String] = @hashset.new() set.add("a") assert_eq(set.contains("a"), true) } @@ -55,7 +55,7 @@ Similarly, you can use `is_empty()` to check whether the set is empty. ```moonbit test { - let set : @hashset.T[Int] = @hashset.new() + let set : @hashset.HashSet[Int] = @hashset.new() assert_eq(set.is_empty(), true) } ``` @@ -94,7 +94,7 @@ You can use `union()`, `intersection()`, `difference()` and `symmetric_differenc test { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) - fn to_sorted_array(set : @hashset.T[String]) { + fn to_sorted_array(set : @hashset.HashSet[String]) { let arr = set.to_array() arr.sort() arr diff --git a/bundled-core/hashset/deprecated.mbt b/bundled-core/hashset/deprecated.mbt index b94146c..844f3d3 100644 --- a/bundled-core/hashset/deprecated.mbt +++ b/bundled-core/hashset/deprecated.mbt @@ -15,6 +15,6 @@ ///| #deprecated("Use `add` instead.") #coverage.skip -pub fn[K : Hash + Eq] insert(self : T[K], key : K) -> Unit { +pub fn[K : Hash + Eq] insert(self : HashSet[K], key : K) -> Unit { self.add(key) } diff --git a/bundled-core/hashset/hashset.mbt b/bundled-core/hashset/hashset.mbt index 27ea246..7a76364 100644 --- a/bundled-core/hashset/hashset.mbt +++ b/bundled-core/hashset/hashset.mbt @@ -12,16 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // Default initial capacity + +///| let default_init_capacity = 8 ///| /// Create new hash set. -pub fn[K] new(capacity~ : Int = default_init_capacity) -> T[K] { +#as_free_fn +pub fn[K] HashSet::new(capacity? : Int = default_init_capacity) -> HashSet[K] { { size: 0, capacity, + capacity_mask: capacity - 1, grow_at: calc_grow_threshold(capacity), entries: FixedArray::make(capacity, None), } @@ -29,14 +32,16 @@ pub fn[K] new(capacity~ : Int = default_init_capacity) -> T[K] { ///| /// Create new hash set from array. -pub fn[K : Hash + Eq] from_array(arr : Array[K]) -> T[K] { +#as_free_fn +pub fn[K : Hash + Eq] HashSet::from_array(arr : Array[K]) -> HashSet[K] { let m = new() arr.each(e => m.add(e)) m } ///| -pub fn[K : Hash + Eq] of(arr : FixedArray[K]) -> T[K] { +#as_free_fn +pub fn[K : Hash + Eq] HashSet::of(arr : FixedArray[K]) -> HashSet[K] { let m = new() arr.each(e => m.add(e)) m @@ -44,111 +49,208 @@ pub fn[K : Hash + Eq] of(arr : FixedArray[K]) -> T[K] { ///| /// Insert a key into hash set. - -///| -/// Insert a key into hash set. -pub fn[K : Hash + Eq] add(self : T[K], key : K) -> Unit { - if self.capacity == 0 || self.size >= self.grow_at { +/// +/// Parameters: +/// +/// * `self` : The hash set to modify. +/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits. +/// +/// Example: +/// +/// ```moonbit +/// let set : @hashset.HashSet[String] = @hashset.new() +/// set.add("key") +/// inspect(set.contains("key"), content="true") +/// set.add("key") // no effect since it already exists +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] add(self : HashSet[K], key : K) -> Unit { + self.add_with_hash(key, key.hash()) +} + +///| +fn[K : Eq] add_with_hash(self : HashSet[K], key : K, hash : Int) -> Unit { + if self.size >= self.grow_at { self.grow() } - let hash = key.hash() - let entry = { psl: 0, hash, key } - loop (0, self.index(hash), entry) { - (i, idx, entry) => { - if i == self.capacity { - panic() - } - match self.entries[idx] { - None => { - self.entries[idx] = Some(entry) - self.size += 1 - break + let (idx, psl) = for psl = 0, idx = abs(hash) & self.capacity_mask { + match self.entries[idx] { + None => break (idx, psl) + Some(curr_entry) => { + if curr_entry.hash == hash && curr_entry.key == key { + return } - Some(curr_entry) => { - if curr_entry.hash == entry.hash && curr_entry.key == entry.key { - self.entries[idx] = Some(entry) - break - } - if entry.psl > curr_entry.psl { - self.entries[idx] = Some(entry) - curr_entry.psl += 1 - continue (i + 1, self.next_index(idx), curr_entry) - } - entry.psl += 1 - continue (i + 1, self.next_index(idx), entry) + if psl > curr_entry.psl { + self.push_away(idx, curr_entry) + break (idx, psl) } + continue psl + 1, (idx + 1) & self.capacity_mask } } } + let entry = { psl, key, hash } + self.set_entry(entry, idx) + self.size += 1 } ///| -/// Check if the hash set contains a key. -pub fn[K : Hash + Eq] contains(self : T[K], key : K) -> Bool { - let hash = key.hash() - for i = 0, idx = self.index(hash) - i < self.capacity - i = i + 1, idx = self.next_index(idx) { +fn[K] push_away(self : HashSet[K], idx : Int, entry : Entry[K]) -> Unit { + for psl = entry.psl + 1, idx = (idx + 1) & self.capacity_mask, entry = entry { match self.entries[idx] { - Some(entry) => { - if entry.hash == hash && entry.key == key { - return true - } - if i > entry.psl { - return false - } + None => { + entry.psl = psl + self.set_entry(entry, idx) + break } - None => return false + Some(curr_entry) => + if psl > curr_entry.psl { + entry.psl = psl + self.set_entry(entry, idx) + continue curr_entry.psl + 1, + (idx + 1) & self.capacity_mask, + curr_entry + } else { + continue psl + 1, (idx + 1) & self.capacity_mask, entry + } + } + } +} + +///| +#inline +fn[K] set_entry(self : HashSet[K], entry : Entry[K], new_idx : Int) -> Unit { + self.entries[new_idx] = Some(entry) +} + +///| +/// Check if the hash set contains a key. +pub fn[K : Hash + Eq] contains(self : HashSet[K], key : K) -> Bool { + // inline lookup to avoid unnecessary allocations + let hash = key.hash() + for i = 0, idx = abs(hash) & self.capacity_mask { + guard self.entries[idx] is Some(entry) else { break false } + if entry.hash == hash && entry.key == key { + break true } + if i > entry.psl { + break false + } + continue i + 1, (idx + 1) & self.capacity_mask } - false } ///| -/// Remove a key from hash set. -pub fn[K : Hash + Eq] remove(self : T[K], key : K) -> Unit { +/// Remove a key from hash set. If the key exists in the set, removes it +/// and adjusts the probe sequence length (PSL) of subsequent entries to +/// maintain the Robin Hood hashing invariant. If the key does not exist, +/// the set remains unchanged. +/// +/// Parameters: +/// +/// * `self` : The hash set to remove the key from. +/// * `key` : The key to remove from the set. +/// +/// Example: +/// +/// ```moonbit +/// let set = @hashset.of(["a", "b"]) +/// set.remove("a") +/// inspect(set.contains("a"), content="false") +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] remove(self : HashSet[K], key : K) -> Unit { let hash = key.hash() - for i = 0, idx = self.index(hash) - i < self.capacity - i = i + 1, idx = self.next_index(idx) { - if self.entries[idx] is Some(entry) && - entry.hash == hash && - entry.key == key { - self.entries[idx] = None + for i = 0, idx = abs(hash) & self.capacity_mask { + guard self.entries[idx] is Some(entry) else { break } + if entry.hash == hash && entry.key == key { self.shift_back(idx) self.size -= 1 break } + if i > entry.psl { + break + } + continue i + 1, (idx + 1) & self.capacity_mask + } +} + +///| +fn[K] shift_back(self : HashSet[K], idx : Int) -> Unit { + let next = (idx + 1) & self.capacity_mask + match self.entries[next] { + None | Some({ psl: 0, .. }) => self.entries[idx] = None + Some(entry) => { + entry.psl -= 1 + self.set_entry(entry, idx) + self.shift_back(next) + } + } +} + +///| +fn[K : Eq] grow(self : HashSet[K]) -> Unit { + // handle zero capacity + if self.capacity == 0 { + self.capacity = default_init_capacity + self.capacity_mask = self.capacity - 1 + self.grow_at = calc_grow_threshold(self.capacity) + self.size = 0 + self.entries = FixedArray::make(self.capacity, None) + return + } + let old_entries = self.entries + self.entries = FixedArray::make(self.capacity * 2, None) + self.capacity = self.capacity * 2 + self.capacity_mask = self.capacity - 1 + self.grow_at = calc_grow_threshold(self.capacity) + self.size = 0 + for i in 0.. Int { +pub fn[K] size(self : HashSet[K]) -> Int { self.size } ///| /// Get the capacity of the set. -pub fn[K] capacity(self : T[K]) -> Int { +pub fn[K] capacity(self : HashSet[K]) -> Int { self.capacity } ///| /// Check if the hash set is empty. -pub fn[K] is_empty(self : T[K]) -> Bool { +pub fn[K] is_empty(self : HashSet[K]) -> Bool { self.size == 0 } ///| /// Iterate over all keys of the set. -pub fn[K] each(self : T[K], f : (K) -> Unit) -> Unit { - self.eachi((_i, k) => f(k)) +#locals(f) +pub fn[K] each(self : HashSet[K], f : (K) -> Unit raise?) -> Unit raise? { + for entry in self.entries { + if entry is Some({ key, .. }) { + f(key) + } + } } ///| /// Iterate over all keys of the set, with index. -pub fn[K] eachi(self : T[K], f : (Int, K) -> Unit) -> Unit { +#locals(f) +pub fn[K] eachi(self : HashSet[K], f : (Int, K) -> Unit raise?) -> Unit raise? { let mut idx = 0 for i in 0.. Unit) -> Unit { ///| /// Clears the set, removing all keys. Keeps the allocated space. -pub fn[K] clear(self : T[K]) -> Unit { - for i in 0.. Unit { + self.entries.fill(None) self.size = 0 } +///| +/// Returns the iterator of the hash set. +pub fn[K] iter(self : HashSet[K]) -> Iter[K] { + Iter::new(yield_ => for entry in self.entries { + if entry is Some({ key, .. }) { + guard yield_(key) is IterContinue else { break IterEnd } + } + } else { + IterContinue + }) +} + +///| +/// Converts the hash set to an array. +pub fn[K] to_array(self : HashSet[K]) -> Array[K] { + let arr = Array::new(capacity=self.size) + for entry in self.entries { + if entry is Some({ key, .. }) { + arr.push(key) + } + } + arr +} + +///| +#as_free_fn +pub fn[K : Hash + Eq] HashSet::from_iter(iter : Iter[K]) -> HashSet[K] { + let s = new() + iter.each(e => s.add(e)) + s +} + ///| /// Union of two hash sets. -pub fn[K : Hash + Eq] union(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Hash + Eq] union( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { let m = new() self.each(k => m.add(k)) other.each(k => m.add(k)) @@ -178,7 +313,10 @@ pub fn[K : Hash + Eq] union(self : T[K], other : T[K]) -> T[K] { ///| /// Intersection of two hash sets. -pub fn[K : Hash + Eq] intersection(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Hash + Eq] intersection( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { let m = new() self.each(k => if other.contains(k) { m.add(k) }) m @@ -186,34 +324,43 @@ pub fn[K : Hash + Eq] intersection(self : T[K], other : T[K]) -> T[K] { ///| /// Difference of two hash sets. -pub fn[K : Hash + Eq] difference(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Hash + Eq] difference( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { let m = new() - self.each(k => if not(other.contains(k)) { m.add(k) }) + self.each(k => if !other.contains(k) { m.add(k) }) m } ///| /// Symmetric difference of two hash sets. -pub fn[K : Hash + Eq] symmetric_difference(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Hash + Eq] symmetric_difference( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { let m = new() - self.each(k => if not(other.contains(k)) { m.add(k) }) - other.each(k => if not(self.contains(k)) { m.add(k) }) + self.each(k => if !other.contains(k) { m.add(k) }) + other.each(k => if !self.contains(k) { m.add(k) }) m } ///| /// Check if two sets have no common elements. -pub fn[K : Hash + Eq] is_disjoint(self : T[K], other : T[K]) -> Bool { +pub fn[K : Hash + Eq] is_disjoint( + self : HashSet[K], + other : HashSet[K], +) -> Bool { if self.size() <= other.size() { - self.iter().all(k => not(other.contains(k))) + self.iter().all(k => !other.contains(k)) } else { - other.iter().all(k => not(self.contains(k))) + other.iter().all(k => !self.contains(k)) } } ///| /// Check if the current set is a subset of another set. -pub fn[K : Hash + Eq] is_subset(self : T[K], other : T[K]) -> Bool { +pub fn[K : Hash + Eq] is_subset(self : HashSet[K], other : HashSet[K]) -> Bool { if self.size() <= other.size() { self.iter().all(k => other.contains(k)) } else { @@ -223,106 +370,44 @@ pub fn[K : Hash + Eq] is_subset(self : T[K], other : T[K]) -> Bool { ///| /// Check if the current set is a superset of another set. -pub fn[K : Hash + Eq] is_superset(self : T[K], other : T[K]) -> Bool { +pub fn[K : Hash + Eq] is_superset( + self : HashSet[K], + other : HashSet[K], +) -> Bool { other.is_subset(self) } ///| /// Intersection of two hash sets. -pub impl[K : Hash + Eq] BitAnd for T[K] with land(self, other) { +pub impl[K : Hash + Eq] BitAnd for HashSet[K] with land(self, other) { self.intersection(other) } ///| /// Union of two hash sets. -pub impl[K : Hash + Eq] BitOr for T[K] with lor(self, other) { +pub impl[K : Hash + Eq] BitOr for HashSet[K] with lor(self, other) { self.union(other) } ///| /// Symmetric difference of two hash sets. -pub impl[K : Hash + Eq] BitXOr for T[K] with lxor(self, other) { +pub impl[K : Hash + Eq] BitXOr for HashSet[K] with lxor(self, other) { self.symmetric_difference(other) } ///| /// Difference of two hash sets. -pub impl[K : Hash + Eq] Sub for T[K] with op_sub(self, other) { +pub impl[K : Hash + Eq] Sub for HashSet[K] with sub(self, other) { self.difference(other) } ///| -pub fn[K] iter(self : T[K]) -> Iter[K] { - Iter::new(yield_ => for entry in self.entries { - if entry is Some({ key, .. }) { - guard yield_(key) is IterContinue else { break IterEnd } - } - } else { - IterContinue - }) -} - -///| -pub fn[K : Hash + Eq] from_iter(iter : Iter[K]) -> T[K] { - let s = new() - iter.each(e => s.add(e)) - s -} - -///| -pub impl[X : @quickcheck.Arbitrary + Eq + Hash] @quickcheck.Arbitrary for T[X] with arbitrary( - size, - rs -) { +pub impl[X : @quickcheck.Arbitrary + Eq + Hash] @quickcheck.Arbitrary for HashSet[ + X, +] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_iter } -///| -fn[K] shift_back(self : T[K], start_index : Int) -> Unit { - for i = 0, prev = start_index, curr = self.next_index(start_index) - i < self.entries.length() - i = i + 1, prev = curr, curr = self.next_index(curr) { - match self.entries[curr] { - Some(entry) => { - if entry.psl == 0 { - break - } - entry.psl -= 1 - self.entries[prev] = Some(entry) - self.entries[curr] = None - } - None => break - } - } -} - -///| -fn[K : Hash + Eq] grow(self : T[K]) -> Unit { - // handle zero capacity - if self.capacity == 0 { - self.capacity = default_init_capacity - self.grow_at = calc_grow_threshold(self.capacity) - self.size = 0 - self.entries = FixedArray::make(self.capacity, None) - return - } - let old_entries = self.entries - self.entries = FixedArray::make(self.capacity * 2, None) - self.capacity = self.capacity * 2 - self.grow_at = calc_grow_threshold(self.capacity) - self.size = 0 - for i in 0.. Int { - abs(hash) & (self.capacity - 1) -} - ///| fn abs(n : Int) -> Int { if n < 0 { @@ -332,18 +417,13 @@ fn abs(n : Int) -> Int { } } -///| -fn[K] next_index(self : T[K], index : Int) -> Int { - (index + 1) & (self.capacity - 1) -} - ///| fn calc_grow_threshold(capacity : Int) -> Int { capacity * 13 / 16 } ///| -fn[K : Show] debug_entries(self : T[K]) -> String { +fn[K : Show] debug_entries(self : HashSet[K]) -> String { let mut s = "" for i in 0.. 0 { @@ -358,12 +438,7 @@ fn[K : Show] debug_entries(self : T[K]) -> String { } ///| -pub impl[K : Show] Show for T[K] with output(self, logger) { - logger.write_iter(self.iter(), prefix="@hashset.of([", suffix="])") -} - -///| -priv type MyString String derive(Eq) +priv struct MyString(String) derive(Eq) ///| impl Hash for MyString with hash(self) { @@ -385,7 +460,7 @@ impl Show for MyString with output(self, logger) { ///| test "set" { - let m : T[MyString] = new() + let m : HashSet[MyString] = new() m.add("a") m.add("b") m.add("bc") @@ -393,7 +468,7 @@ test "set" { m.add("cd") m.add("c") m.add("d") - assert_eq(m.size, 7) + inspect(m.size, content="7") assert_eq( m.debug_entries(), "_,(0,a),(1,b),(2,c),(3,d),(3,bc),(4,cd),(4,abc),_,_,_,_,_,_,_,_", @@ -402,7 +477,7 @@ test "set" { ///| test "remove" { - let m : T[MyString] = new() + let m : HashSet[MyString] = new() fn i(s) { MyString::MyString(s) } @@ -414,13 +489,16 @@ test "remove" { m.add("abc" |> i) m.add("abcdef" |> i) m.remove("ab" |> i) - assert_eq(m.size(), 5) - assert_eq(m.debug_entries(), "_,(0,a),(0,bc),(1,cd),(1,abc),_,(0,abcdef),_") + inspect(m.size(), content="5") + inspect( + m.debug_entries(), + content="_,(0,a),(0,bc),(1,cd),(1,abc),_,(0,abcdef),_", + ) } ///| test "remove_unexist_key" { - let m : T[MyString] = new() + let m : HashSet[MyString] = new() fn i(s) { MyString::MyString(s) } @@ -429,13 +507,13 @@ test "remove_unexist_key" { m.add("ab" |> i) m.add("abc" |> i) m.remove("d" |> i) - assert_eq(m.size(), 3) - assert_eq(m.debug_entries(), "_,(0,a),(0,ab),(0,abc),_,_,_,_") + inspect(m.size(), content="3") + inspect(m.debug_entries(), content="_,(0,a),(0,ab),(0,abc),_,_,_,_") } ///| test "grow" { - let m : T[MyString] = new() + let m : HashSet[MyString] = new() fn i(s) { MyString::MyString(s) } @@ -446,16 +524,16 @@ test "grow" { m.add("Java" |> i) m.add("Scala" |> i) m.add("Julia" |> i) - assert_eq(m.size, 6) - assert_eq(m.capacity, 8) + inspect(m.size, content="6") + inspect(m.capacity, content="8") m.add("Cobol" |> i) - assert_eq(m.size, 7) - assert_eq(m.capacity, 16) + inspect(m.size, content="7") + inspect(m.capacity, content="16") m.add("Python" |> i) m.add("Haskell" |> i) m.add("Rescript" |> i) - assert_eq(m.size, 10) - assert_eq(m.capacity, 16) + inspect(m.size, content="10") + inspect(m.capacity, content="16") assert_eq( m.debug_entries(), "_,(0,C),(0,Go),(0,C++),(0,Java),(0,Scala),(1,Julia),(2,Cobol),(2,Python),(2,Haskell),(2,Rescript),_,_,_,_,_", @@ -464,36 +542,97 @@ test "grow" { ///| test "clear" { - let m : T[MyString] = new() + let m : HashSet[MyString] = new() m.clear() - assert_eq(m.size(), 0) - assert_eq(m.capacity(), 8) + inspect(m.size(), content="0") + inspect(m.capacity(), content="8") for i in 0.. Array[K] { - let mut i = 0 - let res = while i < self.capacity { - if self.entries[i] is Some({ key, .. }) { - i += 1 - break Array::make(self.size, key) - } - i += 1 - } else { - [] +/// Insert a key into the hash set and returns whether the key was successfully added. +/// +/// Parameters: +/// +/// * `self` : The hash set to modify. +/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits. +/// +/// Returns `true` if the key was successfully added (i.e., it wasn't already present), +/// `false` if the key already existed in the set. +/// +/// Example: +/// +/// ```moonbit +/// let set : @hashset.HashSet[String] = @hashset.new() +/// inspect(set.add_and_check("key"), content="true") // First insertion +/// inspect(set.add_and_check("key"), content="false") // Already exists +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] add_and_check(self : HashSet[K], key : K) -> Bool { + let old_size = self.size + self.add(key) + self.size > old_size +} + +///| +/// Remove a key from the hash set and returns whether the key was successfully removed. +/// +/// Parameters: +/// +/// * `self` : The hash set to modify. +/// * `key` : The key to remove. Must implement `Hash` and `Eq` traits. +/// +/// Returns `true` if the key was successfully removed (i.e., it was present), +/// `false` if the key didn't exist in the set. +/// +/// Example: +/// +/// ```moonbit +/// let set = @hashset.of(["a", "b"]) +/// inspect(set.remove_and_check("a"), content="true") // Successfully removed +/// inspect(set.remove_and_check("a"), content="false") // Already removed +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] remove_and_check(self : HashSet[K], key : K) -> Bool { + let old_size = self.size + self.remove(key) + self.size < old_size +} + +///| +/// Copy the set, creating a new set with the same keys. +pub fn[K] copy(self : HashSet[K]) -> HashSet[K] { + let other = { + capacity: self.capacity, + entries: FixedArray::make(self.capacity, None), + size: self.size, + capacity_mask: self.capacity_mask, + grow_at: self.grow_at, } - if not(res.is_empty()) { - let mut res_idx = 1 - while res_idx < res.length() && i < self.capacity { - if self.entries[i] is Some({ key, .. }) { - res[res_idx] = key - res_idx += 1 - } - i += 1 + for i in 0.. T[K] - -fn[K : Hash + Eq] from_iter(Iter[K]) -> T[K] - -fn[K] new(capacity~ : Int = ..) -> T[K] - -fn[K : Hash + Eq] of(FixedArray[K]) -> T[K] - -// Types and methods -type T[K] -fn[K : Hash + Eq] T::add(Self[K], K) -> Unit -fn[K] T::capacity(Self[K]) -> Int -fn[K] T::clear(Self[K]) -> Unit -fn[K : Hash + Eq] T::contains(Self[K], K) -> Bool -fn[K : Hash + Eq] T::difference(Self[K], Self[K]) -> Self[K] -fn[K] T::each(Self[K], (K) -> Unit) -> Unit -fn[K] T::eachi(Self[K], (Int, K) -> Unit) -> Unit -#deprecated -fn[K : Hash + Eq] T::insert(Self[K], K) -> Unit -fn[K : Hash + Eq] T::intersection(Self[K], Self[K]) -> Self[K] -fn[K : Hash + Eq] T::is_disjoint(Self[K], Self[K]) -> Bool -fn[K] T::is_empty(Self[K]) -> Bool -fn[K : Hash + Eq] T::is_subset(Self[K], Self[K]) -> Bool -fn[K : Hash + Eq] T::is_superset(Self[K], Self[K]) -> Bool -fn[K] T::iter(Self[K]) -> Iter[K] -fn[K : Hash + Eq] T::remove(Self[K], K) -> Unit -fn[K] T::size(Self[K]) -> Int -fn[K : Hash + Eq] T::symmetric_difference(Self[K], Self[K]) -> Self[K] -fn[K] T::to_array(Self[K]) -> Array[K] -fn[K : Hash + Eq] T::union(Self[K], Self[K]) -> Self[K] -impl[K : Hash + Eq] BitAnd for T[K] -impl[K : Hash + Eq] BitOr for T[K] -impl[K : Hash + Eq] BitXOr for T[K] -impl[K : Show] Show for T[K] -impl[K : Hash + Eq] Sub for T[K] -impl[X : @quickcheck.Arbitrary + Eq + Hash] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/hashset/hashset_coverage_test.mbt b/bundled-core/hashset/hashset_coverage_test.mbt new file mode 100644 index 0000000..1467d59 --- /dev/null +++ b/bundled-core/hashset/hashset_coverage_test.mbt @@ -0,0 +1,89 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "is_disjoint with different sizes" { + let set1 = @hashset.new() + let set2 = @hashset.new() + + // Add fewer elements to set1 + set1.add(1) + set1.add(2) + + // Add more elements to set2, no overlap + set2.add(3) + set2.add(4) + set2.add(5) + set2.add(6) + inspect(set1.is_disjoint(set2), content="true") + // This should trigger the else branch where other.size() < self.size() + inspect(set2.is_disjoint(set1), content="true") +} + +///| +test "is_disjoint with overlap" { + let set1 = @hashset.new() + let set2 = @hashset.new() + set1.add(1) + set1.add(2) + set2.add(2) + set2.add(3) + set2.add(4) + inspect(set1.is_disjoint(set2), content="false") + inspect(set2.is_disjoint(set1), content="false") +} + +///| +test "is_subset with different sizes" { + let subset = @hashset.new() + let superset = @hashset.new() + subset.add(1) + subset.add(2) + superset.add(1) + superset.add(2) + superset.add(3) + superset.add(4) + inspect(subset.is_subset(superset), content="true") + // This should trigger the else branch where self.size() > other.size() + inspect(superset.is_subset(subset), content="false") +} + +///| +test "contains with probe sequence length comparison" { + let set = @hashset.new() + + // Create a scenario that might trigger PSL comparison + // Add multiple elements to create collisions and probe sequences + for i in 0..<50 { + set.add(i) + } + + // Test containment + inspect(set.contains(25), content="true") + inspect(set.contains(100), content="false") // Not in set +} + +///| +test "large set operations" { + // Create a large enough set to test growth scenarios + let set = @hashset.new() + + // Add many elements to trigger multiple grows + for i in 0..<1000 { + set.add(i) + } + inspect(set.size(), content="1000") + inspect(set.contains(500), content="true") + inspect(set.contains(1500), content="false") +} diff --git a/bundled-core/hashset/hashset_test.mbt b/bundled-core/hashset/hashset_test.mbt index b268315..6c6a697 100644 --- a/bundled-core/hashset/hashset_test.mbt +++ b/bundled-core/hashset/hashset_test.mbt @@ -17,29 +17,65 @@ let default_init_capacity = 8 ///| test "new_with_capacity_then_add " { - let set : @hashset.T[(String, String)] = @hashset.new(capacity=20) + let set : @hashset.HashSet[(String, String)] = @hashset.new(capacity=20) set.add(("None", "Hash")) inspect( set, - content= + content=( #|@hashset.of([("None", "Hash")]) - , + ), ) } ///| -test "doc" { - let set = @hashset.of([3, 8, 1]) - set.add(3) - set.add(4) - inspect(set, content="@hashset.of([3, 4, 1, 8])") +test "to_array" { + let v = @hashset.of([1, 2, 3, 4]) + let arr = v.to_array() + arr.sort() + inspect(arr, content="[1, 2, 3, 4]") +} + +///| +test "add_and_check" { + let set : @hashset.HashSet[String] = @hashset.new() + inspect(set.add_and_check("key"), content="true") // First insertion + inspect(set.add_and_check("key"), content="false") // Already exists + inspect(set.size(), content="1") +} + +///| +test "remove_and_check" { + let set = @hashset.of(["a", "b"]) + inspect(set.remove_and_check("a"), content="true") // Successfully removed + inspect(set.remove_and_check("a"), content="false") // Already removed + inspect(set.size(), content="1") +} + +///| +test "copy" { + let set = @hashset.of(["a", "b", "c"]) + let copied = set.copy() + inspect(copied.size(), content="3") + inspect(copied.contains("a"), content="true") + inspect(copied.contains("b"), content="true") + inspect(copied.contains("c"), content="true") + // Modify original, copy should be unaffected + set.remove("a") + inspect(set.size(), content="2") + inspect(copied.size(), content="3") +} + +///| +test "to_json" { + let set = @hashset.of([1, 2, 3]) + @json.inspect(set, content=[3, 1, 2]) } ///| test "new" { - let m : @hashset.T[Int] = @hashset.new() + let m : @hashset.HashSet[Int] = @hashset.new() assert_eq(m.capacity(), default_init_capacity) - assert_eq(m.size(), 0) + inspect(m.size(), content="0") } ///| @@ -66,19 +102,19 @@ test "from_array" { ///| test "size" { let m = @hashset.new() - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.add("a") - assert_eq(m.size(), 1) + inspect(m.size(), content="1") } ///| test "is_empty" { let m = @hashset.new() - assert_eq(m.is_empty(), true) + inspect(m.is_empty(), content="true") m.add("a") - assert_eq(m.is_empty(), false) + inspect(m.is_empty(), content="false") m.remove("a") - assert_eq(m.is_empty(), true) + inspect(m.is_empty(), content="true") } ///| @@ -107,7 +143,7 @@ test "union" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1.union(m2) - assert_eq(m.size(), 4) + inspect(m.size(), content="4") assert_true(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -119,7 +155,7 @@ test "intersection" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1.intersection(m2) - assert_eq(m.size(), 2) + inspect(m.size(), content="2") assert_false(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -131,7 +167,7 @@ test "difference" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1.difference(m2) - assert_eq(m.size(), 1) + inspect(m.size(), content="1") assert_true(m.contains("a")) assert_false(m.contains("b")) assert_false(m.contains("c")) @@ -143,7 +179,7 @@ test "symmetric_difference" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1.symmetric_difference(m2) - assert_eq(m.size(), 2) + inspect(m.size(), content="2") assert_true(m.contains("a")) assert_false(m.contains("b")) assert_false(m.contains("c")) @@ -154,30 +190,30 @@ test "symmetric_difference" { test "is_disjoint" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) - assert_eq(m1.is_disjoint(m2), false) + inspect(m1.is_disjoint(m2), content="false") let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["e", "f", "g"]) - assert_eq(m1.is_disjoint(m2), true) + inspect(m1.is_disjoint(m2), content="true") } ///| test "is_subset" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) - assert_eq(m1.is_subset(m2), false) + inspect(m1.is_subset(m2), content="false") let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["a", "b", "c", "d"]) - assert_eq(m1.is_subset(m2), true) + inspect(m1.is_subset(m2), content="true") } ///| test "is_superset" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) - assert_eq(m1.is_superset(m2), false) + inspect(m1.is_superset(m2), content="false") let m1 = @hashset.of(["a", "b", "c", "d"]) let m2 = @hashset.of(["a", "b", "c"]) - assert_eq(m1.is_superset(m2), true) + inspect(m1.is_superset(m2), content="true") } ///| @@ -185,7 +221,7 @@ test "land" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1 & m2 - assert_eq(m.size(), 2) + inspect(m.size(), content="2") assert_false(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -197,7 +233,7 @@ test "lor" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1 | m2 - assert_eq(m.size(), 4) + inspect(m.size(), content="4") assert_true(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -209,7 +245,7 @@ test "lxor" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1 ^ m2 - assert_eq(m.size(), 2) + inspect(m.size(), content="2") assert_true(m.contains("a")) assert_false(m.contains("b")) assert_false(m.contains("c")) @@ -221,7 +257,7 @@ test "op_sub" { let m1 = @hashset.of(["a", "b", "c"]) let m2 = @hashset.of(["b", "c", "d"]) let m = m1 - m2 - assert_eq(m.size(), 1) + inspect(m.size(), content="1") assert_true(m.contains("a")) assert_false(m.contains("b")) assert_false(m.contains("c")) @@ -255,8 +291,8 @@ test "insert_and_grow" { for i in 0..<10 { m.add(i.to_string()) } - assert_eq(m.size(), 10) - assert_eq(m.capacity(), 16) + inspect(m.size(), content="10") + inspect(m.capacity(), content="16") } ///| @@ -277,9 +313,9 @@ test "remove_and_shift_back" { test "capacity_and_size" { let m = @hashset.new() assert_eq(m.capacity(), default_init_capacity) - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.add("a") - assert_eq(m.size(), 1) + inspect(m.size(), content="1") } ///| @@ -288,9 +324,9 @@ test "clear_and_reinsert" { m.add("a") m.add("b") m.clear() - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.add("c") - assert_eq(m.size(), 1) + inspect(m.size(), content="1") assert_true(m.contains("c")) } @@ -300,8 +336,8 @@ test "insert_and_grow" { for i in 0..<10 { m.add(i.to_string()) } - assert_eq(m.size(), 10) - assert_eq(m.capacity(), 16) + inspect(m.size(), content="10") + inspect(m.capacity(), content="16") } ///| @@ -322,9 +358,9 @@ test "remove_and_shift_back" { test "capacity_and_size" { let m = @hashset.new() assert_eq(m.capacity(), default_init_capacity) - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.add("a") - assert_eq(m.size(), 1) + inspect(m.size(), content="1") } ///| @@ -333,9 +369,9 @@ test "clear_and_reinsert" { m.add("a") m.add("b") m.clear() - assert_eq(m.size(), 0) + inspect(m.size(), content="0") m.add("c") - assert_eq(m.size(), 1) + inspect(m.size(), content="1") assert_true(m.contains("c")) } @@ -354,13 +390,13 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let map : @hashset.T[Int] = @hashset.from_iter(Iter::empty()) + let map : @hashset.HashSet[Int] = @hashset.from_iter(Iter::empty()) inspect(map, content="@hashset.of([])") } ///| test "hashset arbitrary" { - let samples : Array[@hashset.T[Int]] = @quickcheck.samples(20) + let samples : Array[@hashset.HashSet[Int]] = @quickcheck.samples(20) inspect( samples[5:10], content="[@hashset.of([]), @hashset.of([]), @hashset.of([0]), @hashset.of([0]), @hashset.of([0, 3, 1, 2])]", @@ -373,7 +409,7 @@ test "hashset arbitrary" { ///| test "@hashset.to_array/empty" { - let set : @hashset.T[Int] = @hashset.new() + let set : @hashset.HashSet[Int] = @hashset.new() inspect(set.to_array(), content="[]") } diff --git a/bundled-core/hashset/moon.pkg.json b/bundled-core/hashset/moon.pkg.json index c47fcfd..8360cac 100644 --- a/bundled-core/hashset/moon.pkg.json +++ b/bundled-core/hashset/moon.pkg.json @@ -5,5 +5,9 @@ "moonbitlang/core/array", "moonbitlang/core/quickcheck" ], - "test-import": ["moonbitlang/core/string", "moonbitlang/core/int"] + "test-import": [ + "moonbitlang/core/string", + "moonbitlang/core/int", + "moonbitlang/core/json" + ] } diff --git a/bundled-core/hashset/pkg.generated.mbti b/bundled-core/hashset/pkg.generated.mbti new file mode 100644 index 0000000..ee9d29a --- /dev/null +++ b/bundled-core/hashset/pkg.generated.mbti @@ -0,0 +1,58 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/hashset" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type HashSet[K] +fn[K : Hash + Eq] HashSet::add(Self[K], K) -> Unit +fn[K : Hash + Eq] HashSet::add_and_check(Self[K], K) -> Bool +fn[K] HashSet::capacity(Self[K]) -> Int +fn[K] HashSet::clear(Self[K]) -> Unit +fn[K : Hash + Eq] HashSet::contains(Self[K], K) -> Bool +fn[K] HashSet::copy(Self[K]) -> Self[K] +fn[K : Hash + Eq] HashSet::difference(Self[K], Self[K]) -> Self[K] +fn[K] HashSet::each(Self[K], (K) -> Unit raise?) -> Unit raise? +fn[K] HashSet::eachi(Self[K], (Int, K) -> Unit raise?) -> Unit raise? +#as_free_fn +fn[K : Hash + Eq] HashSet::from_array(Array[K]) -> Self[K] +#as_free_fn +fn[K : Hash + Eq] HashSet::from_iter(Iter[K]) -> Self[K] +#deprecated +fn[K : Hash + Eq] HashSet::insert(Self[K], K) -> Unit +fn[K : Hash + Eq] HashSet::intersection(Self[K], Self[K]) -> Self[K] +fn[K : Hash + Eq] HashSet::is_disjoint(Self[K], Self[K]) -> Bool +fn[K] HashSet::is_empty(Self[K]) -> Bool +fn[K : Hash + Eq] HashSet::is_subset(Self[K], Self[K]) -> Bool +fn[K : Hash + Eq] HashSet::is_superset(Self[K], Self[K]) -> Bool +fn[K] HashSet::iter(Self[K]) -> Iter[K] +#as_free_fn +fn[K] HashSet::new(capacity? : Int) -> Self[K] +#as_free_fn +fn[K : Hash + Eq] HashSet::of(FixedArray[K]) -> Self[K] +fn[K : Hash + Eq] HashSet::remove(Self[K], K) -> Unit +fn[K : Hash + Eq] HashSet::remove_and_check(Self[K], K) -> Bool +fn[K] HashSet::size(Self[K]) -> Int +fn[K : Hash + Eq] HashSet::symmetric_difference(Self[K], Self[K]) -> Self[K] +fn[K] HashSet::to_array(Self[K]) -> Array[K] +fn[K : Hash + Eq] HashSet::union(Self[K], Self[K]) -> Self[K] +impl[K : Hash + Eq] BitAnd for HashSet[K] +impl[K : Hash + Eq] BitOr for HashSet[K] +impl[K : Hash + Eq] BitXOr for HashSet[K] +impl[K] Default for HashSet[K] +impl[K : Show] Show for HashSet[K] +impl[K : Hash + Eq] Sub for HashSet[K] +impl[X : ToJson] ToJson for HashSet[X] +impl[X : @quickcheck.Arbitrary + Eq + Hash] @quickcheck.Arbitrary for HashSet[X] + +// Type aliases +pub typealias HashSet as T + +// Traits + diff --git a/bundled-core/hashset/types.mbt b/bundled-core/hashset/types.mbt index b5fd35c..4740c56 100644 --- a/bundled-core/hashset/types.mbt +++ b/bundled-core/hashset/types.mbt @@ -35,9 +35,14 @@ priv struct Entry[K] { /// set.add((4, "four")) /// assert_eq(set.contains((4, "four")), true) /// ``` -struct T[K] { +struct HashSet[K] { mut entries : FixedArray[Entry[K]?] mut size : Int // active key count mut capacity : Int // current capacity + mut capacity_mask : Int // capacity_mask = capacity - 1, used to find idx mut grow_at : Int // threshold that triggers grow } + +///| +#deprecated("Use `HashSet` instead of `T`") +pub typealias HashSet as T diff --git a/bundled-core/immut/array/array.mbt b/bundled-core/immut/array/array.mbt index d9fea3a..102a73d 100644 --- a/bundled-core/immut/array/array.mbt +++ b/bundled-core/immut/array/array.mbt @@ -18,13 +18,15 @@ ///| /// Return a new empty array -pub fn[A] new() -> T[A] { +#as_free_fn +pub fn[A] T::new() -> T[A] { { tree: Tree::empty(), size: 0, shift: 0 } } ///| /// Create a persistent array with a given length and value. -pub fn[A] make(len : Int, value : A) -> T[A] { +#as_free_fn +pub fn[A] T::make(len : Int, value : A) -> T[A] { let quot = len / branching_factor let rem = len % branching_factor let leaves = if rem == 0 { @@ -45,7 +47,8 @@ pub fn[A] make(len : Int, value : A) -> T[A] { ///| /// Create a persistent array with a given length and a function to generate values. -pub fn[A] makei(len : Int, f : (Int) -> A raise?) -> T[A] raise? { +#as_free_fn +pub fn[A] T::makei(len : Int, f : (Int) -> A raise?) -> T[A] raise? { let quot = len / branching_factor let rem = len % branching_factor let leaves = if rem == 0 { @@ -70,7 +73,8 @@ pub fn[A] makei(len : Int, f : (Int) -> A raise?) -> T[A] raise? { ///| /// Convert a FixedArray to an @immut/array. -pub fn[A] of(arr : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A] T::of(arr : FixedArray[A]) -> T[A] { makei(arr.length(), i => arr[i]) } @@ -108,12 +112,14 @@ pub fn[A] copy(self : T[A]) -> T[A] { /// let v = @array.of([1, 2, 3]) /// assert_eq(v, @array.from_array([1, 2, 3])) /// ``` -pub fn[A] from_array(arr : Array[A]) -> T[A] { +#as_free_fn +pub fn[A] T::from_array(arr : Array[A]) -> T[A] { makei(arr.length(), i => arr[i]) } ///| -pub fn[A] from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A] T::from_iter(iter : Iter[A]) -> T[A] { let mut buf : FixedArray[A] = [] let mut index = 0 let leaves = [] @@ -180,7 +186,7 @@ pub fn[A] length(self : T[A]) -> Int { /// # Examples /// ```mbt /// let v = @array.of([1, 2, 3, 4, 5]) -/// assert_eq(v[0], 1) +/// inspect(v[0], content="1") /// ``` pub fn[A] op_get(self : T[A], index : Int) -> A { if index == 0 { @@ -271,7 +277,7 @@ pub fn[A] concat(self : T[A], other : T[A]) -> T[A] { ///| /// Concat two arrays. -pub impl[A] Add for T[A] with op_add(self, other) { +pub impl[A] Add for T[A] with add(self, other) { self.concat(other) } @@ -336,7 +342,7 @@ pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B raise?) -> B raise? { pub fn[A, B] rev_fold( self : T[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { self.tree.rev_fold(init, f) } @@ -368,7 +374,7 @@ pub fn[A] fold_left(self : T[A], f : (A, A) -> A raise?, init~ : A) -> A raise? pub fn[A] fold_right( self : T[A], f : (A, A) -> A raise?, - init~ : A + init~ : A, ) -> A raise? { self.rev_fold(init~, f) } @@ -392,7 +398,7 @@ pub fn[A, B] map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] with arbitrary( size, - rs + rs, ) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } @@ -405,7 +411,7 @@ pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { } ///| -pub impl[A : Eq] Eq for T[A] with op_equal(self, other) { +pub impl[A : Eq] Eq for T[A] with equal(self, other) { self.size == other.size && self.tree == other.tree } @@ -415,7 +421,7 @@ pub impl[A : Show] Show for T[A] with output(self, logger) { } ///| -/// Compares two arrays lexicographically. +/// Compares two arrays based on shortlex order. /// /// First compares the lengths of the arrays. If they differ, returns -1 if the /// first array is shorter, 1 if it's longer. If the lengths are equal, compares @@ -439,10 +445,10 @@ pub impl[A : Show] Show for T[A] with output(self, logger) { /// let arr1 = @array.of([1, 2, 3]) /// let arr2 = @array.of([1, 2, 4]) /// let arr3 = @array.of([1, 2]) -/// assert_eq(arr1.compare(arr2), -1) // arr1 < arr2 -/// assert_eq(arr1.compare(arr3), 1) // arr2 > arr1 -/// assert_eq(arr3.compare(arr1), -1) // arr1 > arr3 (longer) -/// assert_eq(arr1.compare(arr1), 0) // arr1 = arr1 +/// inspect(arr1.compare(arr2), content="-1") // arr1 < arr2 +/// inspect(arr1.compare(arr3), content="1") // arr2 > arr1 +/// inspect(arr3.compare(arr1), content="-1") // arr1 > arr3 (longer) +/// inspect(arr1.compare(arr1), content="0") // arr1 = arr1 /// ``` pub impl[A : Compare] Compare for T[A] with compare(self, other) { let len_self = self.length() @@ -464,7 +470,7 @@ pub impl[A : Compare] Compare for T[A] with compare(self, other) { ///| fn[A] from_leaves( leaves : @core/array.View[FixedArray[A]], - cap : Int + cap : Int, ) -> Tree[A] { if cap == branching_factor { Leaf(leaves[0]) diff --git a/bundled-core/immut/array/array_test.mbt b/bundled-core/immut/array/array_test.mbt index 04cba9e..705555b 100644 --- a/bundled-core/immut/array/array_test.mbt +++ b/bundled-core/immut/array/array_test.mbt @@ -169,9 +169,9 @@ test "map" { inspect(v.map(e => e * 2), content="@immut/array.of([2, 4, 6, 8, 10])") inspect( v.map(e => e.to_string()), - content= + content=( #|@immut/array.of(["1", "2", "3", "4", "5"]) - , + ), ) inspect( v.map(e => e % 2 == 0), @@ -259,11 +259,11 @@ test "compare" { let arr2 = @array.of([1, 2, 4]) let arr3 = @array.of([1, 2]) let empty : @array.T[Int] = @array.new() - assert_eq(arr1.compare(arr2), -1) - assert_eq(arr1.compare(arr3), 1) - assert_eq(arr3.compare(arr1), -1) - assert_eq(arr1.compare(arr1), 0) - assert_eq(empty.compare(empty), 0) + inspect(arr1.compare(arr2), content="-1") + inspect(arr1.compare(arr3), content="1") + inspect(arr3.compare(arr1), content="-1") + inspect(arr1.compare(arr1), content="0") + inspect(empty.compare(empty), content="0") } ///| diff --git a/bundled-core/immut/array/deprecated.mbt b/bundled-core/immut/array/deprecated.mbt deleted file mode 100644 index 57c1b37..0000000 --- a/bundled-core/immut/array/deprecated.mbt +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -#deprecated("use `@immut/array.new` instead") -#coverage.skip -pub fn[A] T::new() -> T[A] { - new() -} - -///| -#deprecated("use `@immut/array.from_iter` instead") -#coverage.skip -pub fn[A] T::from_iter(iter : Iter[A]) -> T[A] { - from_iter(iter) -} - -///| -#deprecated("use `@immut/array.from_array` instead") -#coverage.skip -pub fn[A] T::from_array(arr : Array[A]) -> T[A] { - from_array(arr) -} - -///| -#deprecated("use `@immut/array.make` instead") -#coverage.skip -pub fn[A] T::make(len : Int, value : A) -> T[A] { - make(len, value) -} - -///| -#deprecated("use `@immut/array.makei` instead") -#coverage.skip -pub fn[A] T::makei(len : Int, f : (Int) -> A) -> T[A] { - makei(len, f) -} - -///| -#deprecated("use `@immut/array.of` instead") -#coverage.skip -pub fn[A] T::of(arr : FixedArray[A]) -> T[A] { - of(arr) -} diff --git a/bundled-core/immut/array/array.mbti b/bundled-core/immut/array/pkg.generated.mbti similarity index 81% rename from bundled-core/immut/array/array.mbti rename to bundled-core/immut/array/pkg.generated.mbti index f4f4737..a7217ae 100644 --- a/bundled-core/immut/array/array.mbti +++ b/bundled-core/immut/array/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/immut/array" import( @@ -5,17 +6,8 @@ import( ) // Values -fn[A] from_array(Array[A]) -> T[A] -fn[A] from_iter(Iter[A]) -> T[A] - -fn[A] make(Int, A) -> T[A] - -fn[A] makei(Int, (Int) -> A raise?) -> T[A] raise? - -fn[A] new() -> T[A] - -fn[A] of(FixedArray[A]) -> T[A] +// Errors // Types and methods type T[A] @@ -29,22 +21,22 @@ fn[A, B] T::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? fn[A] T::fold_left(Self[A], (A, A) -> A raise?, init~ : A) -> A raise? #deprecated fn[A] T::fold_right(Self[A], (A, A) -> A raise?, init~ : A) -> A raise? -#deprecated +#as_free_fn fn[A] T::from_array(Array[A]) -> Self[A] -#deprecated +#as_free_fn fn[A] T::from_iter(Iter[A]) -> Self[A] fn[A] T::get(Self[A], Int) -> A? fn[A] T::is_empty(Self[A]) -> Bool fn[A] T::iter(Self[A]) -> Iter[A] fn[A] T::length(Self[A]) -> Int -#deprecated +#as_free_fn fn[A] T::make(Int, A) -> Self[A] -#deprecated -fn[A] T::makei(Int, (Int) -> A) -> Self[A] +#as_free_fn +fn[A] T::makei(Int, (Int) -> A raise?) -> Self[A] raise? fn[A, B] T::map(Self[A], (A) -> B raise?) -> Self[B] raise? -#deprecated +#as_free_fn fn[A] T::new() -> Self[A] -#deprecated +#as_free_fn fn[A] T::of(FixedArray[A]) -> Self[A] fn[A] T::op_get(Self[A], Int) -> A fn[A] T::push(Self[A], A) -> Self[A] diff --git a/bundled-core/immut/array/tree.mbt b/bundled-core/immut/array/tree.mbt index f9273e5..9dce9b8 100644 --- a/bundled-core/immut/array/tree.mbt +++ b/bundled-core/immut/array/tree.mbt @@ -300,7 +300,7 @@ fn[A] Tree::eachi( self : Tree[A], f : (Int, A) -> Unit raise?, shift : Int, - start : Int + start : Int, ) -> Unit raise? { match self { Empty => () @@ -332,7 +332,7 @@ fn[A] Tree::eachi( fn[A, B] Tree::fold( self : Tree[A], acc : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { match self { Empty => acc @@ -346,7 +346,7 @@ fn[A, B] Tree::fold( fn[A, B] Tree::rev_fold( self : Tree[A], acc : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { match self { Empty => acc @@ -382,7 +382,7 @@ fn[A] Tree::concat( left_shift : Int, right : Tree[A], right_shift : Int, - top : Bool + top : Bool, ) -> (Tree[A], Int) { if left_shift > right_shift { let (c, c_shift) = Tree::concat( @@ -455,7 +455,7 @@ fn[A] rebalance( center : Tree[A], right : Tree[A], shift : Int, - top : Bool + top : Bool, ) -> (Tree[A], Int) { // Suppose H = shift / num_bits let t = tri_merge(left, center, right) // t is a list of trees of (H-1) height @@ -465,7 +465,7 @@ fn[A] rebalance( if nc_len <= branching_factor { // All nodes can be accommodated in a single node let node = Node(new_t, compute_sizes(new_t, shift - num_bits)) // node of H height - if not(top) { + if !top { return (Node(FixedArray::from_array([node]), None), shift + num_bits) // return (H+1) height node, add another layer to align with the case at the end of the thisfunction } else { @@ -509,9 +509,9 @@ fn[A] rebalance( fn[A] tri_merge( left : Tree[A], center : Tree[A], - right : Tree[A] + right : Tree[A], ) -> FixedArray[Tree[A]] { - if left.is_leaf() || not(center.is_node()) || right.is_leaf() { + if left.is_leaf() || !center.is_node() || right.is_leaf() { abort("Unreachable: input to merge is invalid") } fn get_children(self : Tree[A]) -> FixedArray[Tree[A]] { @@ -588,7 +588,7 @@ fn[A] redis( old_t : FixedArray[Tree[A]], node_counts : FixedArray[Int], node_nums : Int, - shift : Int + shift : Int, ) -> FixedArray[Tree[A]] { let old_len = old_t.length() let new_t = FixedArray::make(node_nums, Empty) @@ -683,7 +683,7 @@ fn[A] redis( /// Given a list of trees as `children` with heights of (`shift` / `num_bits`), compute the sizes array of the subtrees. fn[A] compute_sizes( children : FixedArray[Tree[A]], - shift : Int + shift : Int, ) -> FixedArray[Int]? { let len = children.length() let sizes = FixedArray::make(len, 0) @@ -746,20 +746,20 @@ test "Show for Tree" { inspect((Empty : Tree[Int]), content="Empty") inspect( Node([Leaf([4, 2])], Some([2])), - content= + content=( #|Node( #| Leaf(4, 2) #|) #| - , + ), ) inspect( Node([Empty, Leaf([42])], Some([0, 1])), - content= + content=( #|Node( #| Empty Leaf(42) #|) #| - , + ), ) } diff --git a/bundled-core/immut/array/tree_utils_wbtest.mbt b/bundled-core/immut/array/tree_utils_wbtest.mbt new file mode 100644 index 0000000..5a80259 --- /dev/null +++ b/bundled-core/immut/array/tree_utils_wbtest.mbt @@ -0,0 +1,117 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "Tree::is_node" { + let empty_tree : Tree[Int] = Empty + let leaf_tree = Leaf(FixedArray::from_array([1, 2, 3])) + let node_tree : Tree[Int] = Node(FixedArray::from_array([Empty]), None) + inspect(empty_tree.is_node(), content="false") + inspect(leaf_tree.is_node(), content="false") + inspect(node_tree.is_node(), content="true") +} + +///| +test "Tree::is_leaf" { + let empty_tree : Tree[Int] = Empty + let leaf_tree = Leaf(FixedArray::from_array([1, 2, 3])) + let node_tree : Tree[Int] = Node(FixedArray::from_array([Empty]), None) + inspect(empty_tree.is_leaf(), content="false") + inspect(leaf_tree.is_leaf(), content="true") + inspect(node_tree.is_leaf(), content="false") +} + +///| +test "Tree::left_child and Tree::right_child" { + let child1 = Leaf(FixedArray::from_array([1, 2])) + let child2 = Leaf(FixedArray::from_array([3, 4])) + let child3 = Leaf(FixedArray::from_array([5, 6])) + let node_tree = Node(FixedArray::from_array([child1, child2, child3]), None) + + // left_child should return first child + let left = node_tree.left_child() + inspect(left.is_leaf(), content="true") + + // right_child should return last child + let right = node_tree.right_child() + inspect(right.is_leaf(), content="true") +} + +///| +test "Tree::leaf_elements" { + let elements = FixedArray::from_array([1, 2, 3, 4, 5]) + let leaf_tree = Leaf(elements) + let result = leaf_tree.leaf_elements() + inspect(result.length(), content="5") + inspect(result[0], content="1") + inspect(result[4], content="5") +} + +///| +test "Tree::node_children" { + let child1 = Leaf(FixedArray::from_array([1])) + let child2 = Leaf(FixedArray::from_array([2])) + let node_tree = Node(FixedArray::from_array([child1, child2]), None) + let children = node_tree.node_children() + inspect(children.length(), content="2") + inspect(children[0].is_leaf(), content="true") + inspect(children[1].is_leaf(), content="true") +} + +///| +test "Tree::local_size" { + let empty_tree : Tree[Int] = Empty + let leaf_tree = Leaf(FixedArray::from_array([1, 2, 3])) + let node_tree : Tree[Int] = Node( + FixedArray::from_array([Empty, Empty, Empty]), + None, + ) + inspect(empty_tree.local_size(), content="0") + inspect(leaf_tree.local_size(), content="3") + inspect(node_tree.local_size(), content="3") +} + +///| +test "Tree::size for Empty" { + let empty_tree : Tree[Int] = Empty + inspect(empty_tree.size(5), content="0") +} + +///| +test "Tree::size for Leaf" { + let leaf_tree = Leaf(FixedArray::from_array([10, 20, 30])) + inspect(leaf_tree.size(5), content="3") +} + +///| +test "Tree::size for Node with sizes" { + let sizes = FixedArray::from_array([5, 10]) + let child1 = Leaf(FixedArray::from_array([1])) + let child2 = Leaf(FixedArray::from_array([2])) + let node_tree = Node(FixedArray::from_array([child1, child2]), Some(sizes)) + inspect(node_tree.size(5), content="10") +} + +///| +test "Tree::size for Node without sizes" { + let child1 = Leaf(FixedArray::from_array([1, 2])) + let child2 = Leaf(FixedArray::from_array([3, 4, 5])) + let node_tree = Node(FixedArray::from_array([child1, child2]), None) + + // This will compute size based on the formula + // The calculation: (len_1 << shift) + children[len_1].size(shift - num_bits) + // With shift=6 and num_bits=5 (typical for immutable arrays) + let result = node_tree.size(6) + inspect(result >= 3, content="true") // At least the size of the last leaf +} diff --git a/bundled-core/immut/array/utils.mbt b/bundled-core/immut/array/utils.mbt index e953c05..5a59ce0 100644 --- a/bundled-core/immut/array/utils.mbt +++ b/bundled-core/immut/array/utils.mbt @@ -65,7 +65,7 @@ fn get_branch_index(sizes : FixedArray[Int], index : Int) -> Int { lo } -///| +///| /// Copy the sizes array. fn copy_sizes(sizes : FixedArray[Int]?) -> FixedArray[Int]? { match sizes { diff --git a/bundled-core/immut/array/utils_wbtest.mbt b/bundled-core/immut/array/utils_wbtest.mbt index c35a768..3f14fb5 100644 --- a/bundled-core/immut/array/utils_wbtest.mbt +++ b/bundled-core/immut/array/utils_wbtest.mbt @@ -136,7 +136,7 @@ fn random_array(n : Int) -> Array[Int] { } ///| -type Random Ref[UInt64] +struct Random(Ref[UInt64]) ///| fn int(self : Random, limit~ : Int) -> Int { diff --git a/bundled-core/immut/array/utils_wbtest2.mbt b/bundled-core/immut/array/utils_wbtest2.mbt new file mode 100644 index 0000000..f536b6e --- /dev/null +++ b/bundled-core/immut/array/utils_wbtest2.mbt @@ -0,0 +1,91 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "immutable_set" { + let arr = FixedArray::from_array([1, 2, 3, 4, 5]) + let new_arr = immutable_set(arr, 2, 10) + inspect(new_arr[2], content="10") + inspect(arr[2], content="3") // Original array unchanged + inspect(new_arr.length(), content="5") +} + +///| +test "immutable_push" { + let arr = FixedArray::from_array([1, 2, 3]) + let new_arr = immutable_push(arr, 4) + inspect(new_arr.length(), content="4") + inspect(new_arr[3], content="4") + inspect(arr.length(), content="3") // Original array unchanged +} + +///| +test "shr_as_uint" { + inspect(shr_as_uint(16, 2), content="4") + inspect(shr_as_uint(8, 1), content="4") + inspect(shr_as_uint(-1, 1), content="2147483647") // Handle negative numbers as unsigned +} + +///| +test "radix_indexing" { + // Assuming bitmask is 31 (0x1F) for 5 bits + inspect(radix_indexing(35, 5), content="1") // 35 >> 5 = 1, 1 & 31 = 1 + inspect(radix_indexing(100, 5), content="3") // 100 >> 5 = 3, 3 & 31 = 3 + inspect(radix_indexing(7, 5), content="0") // 7 >> 5 = 0, 0 & 31 = 0 +} + +///| +test "get_branch_index" { + let sizes = FixedArray::from_array([3, 6, 10, 15]) + inspect(get_branch_index(sizes, 2), content="0") // Index 2 is in first branch (contains indexes 0-2) + inspect(get_branch_index(sizes, 5), content="1") // Index 5 is in second branch (contains indexes 3-5) + inspect(get_branch_index(sizes, 8), content="2") // Index 8 is in third branch (contains indexes 6-9) +} + +///| +test "copy_sizes with Some" { + let original = FixedArray::from_array([1, 2, 3]) + let sizes = Some(original) + let copied = copy_sizes(sizes) + match copied { + Some(copied_arr) => { + inspect(copied_arr.length(), content="3") + inspect(copied_arr[0], content="1") + inspect(copied_arr[1], content="2") + inspect(copied_arr[2], content="3") + // Verify it's a copy by modifying original + original[0] = 100 + inspect(copied_arr[0], content="1") // Should still be 1 + } + None => inspect("Should not be None", content="error") + } +} + +///| +test "copy_sizes with None" { + let sizes : FixedArray[Int]? = None + let copied = copy_sizes(sizes) + match copied { + None => inspect("None copied correctly", content="None copied correctly") + Some(_) => inspect("Should be None", content="error") + } +} + +///| +test "min function" { + inspect(min(5, 3), content="3") + inspect(min(1, 10), content="1") + inspect(min(-5, 0), content="-5") + inspect(min(42, 42), content="42") +} diff --git a/bundled-core/immut/hashmap/HAMT.mbt b/bundled-core/immut/hashmap/HAMT.mbt index 07206ce..4436444 100644 --- a/bundled-core/immut/hashmap/HAMT.mbt +++ b/bundled-core/immut/hashmap/HAMT.mbt @@ -28,57 +28,57 @@ // - ///| -// The number of bits consumed at every [Branch] node -let segment_length : Int = 5 - -///| -let segment_mask : Int = 0b11111 - -///| -pub fn[K, V] new() -> T[K, V] { - Empty +#as_free_fn +pub fn[K, V] HashMap::new() -> HashMap[K, V] { + None } ///| /// Create a map with a single key-value pair. -pub fn[K, V] singleton(key : K, value : V) -> T[K, V] { - Leaf(key, value) +#as_free_fn +pub fn[K : Hash, V] HashMap::singleton(key : K, value : V) -> HashMap[K, V] { + Some(Flat(key, value, @path.of(key))) } ///| /// Check if the map contains a key. -pub fn[K : Eq + Hash, V] contains(self : T[K, V], key : K) -> Bool { - match self.get(key) { - Some(_) => true - None => false - } +pub fn[K : Eq + Hash, V] contains(self : HashMap[K, V], key : K) -> Bool { + self.get(key) is Some(_) } ///| /// Lookup a key from a hash map -#deprecated("Use `get()` instead") -#coverage.skip -pub fn[K : Eq + Hash, V] find(self : T[K, V], key : K) -> V? { - self.get(key) +#alias(find, deprecated) +pub fn[K : Eq + Hash, V] get(self : HashMap[K, V], key : K) -> V? { + match self.0 { + None => None + Some(node) => node.get_with_path(key, @path.of(key)) + } } ///| -/// Lookup a key from a hash map -pub fn[K : Eq + Hash, V] get(self : T[K, V], key : K) -> V? { - loop (self, key.hash()) { - (Empty, _) => None - (Leaf(key1, value), _) => if key == key1 { Some(value) } else { None } - (Collision(bucket), _) => bucket.find(key) - // get the first segment (lower 5 bits) of the hash value - // inline the hot path of Sparse_array::op_get - (Branch(children), hash) => { - let idx = hash & segment_mask - if children.elem_info.has(idx) { - let child = children.data[children.elem_info.index_of(idx)] - continue ( - child, - (hash.reinterpret_as_uint() >> segment_length).reinterpret_as_int(), - ) +fn[K : Eq, V] Node::get_with_path( + self : Node[K, V], + key : K, + path : Path, +) -> V? { + loop (self, path) { + (Leaf(key1, value1, bucket), _) => + if key == key1 { + Some(value1) + } else { + bucket.lookup(key) + } + (Flat(key1, value1, path1), path) => + if path == path1 && key == key1 { + Some(value1) + } else { + None + } + (Branch(children), path) => { + let idx = path.idx() + if children[idx] is Some(child) { + continue (child, path.next()) } None } @@ -87,62 +87,72 @@ pub fn[K : Eq + Hash, V] get(self : T[K, V], key : K) -> V? { ///| #deprecated("Use `get` instead. `op_get` will return `V` instead of `Option[V]` in the future.") -pub fn[K : Eq + Hash, V] op_get(self : T[K, V], key : K) -> V? { +pub fn[K : Eq + Hash, V] op_get(self : HashMap[K, V], key : K) -> V? { self.get(key) } ///| -fn[K : Eq, V] add_with_hash( - self : T[K, V], - key : K, - depth : Int, - hash : Int, - value : V -) -> T[K, V] { - // make sure leaf nodes always appear at the bottom of the tree - fn make_leaf(depth : Int, key : K, hash : Int, value : V) { - if depth >= 32 { - T::Leaf(key, value) +/// require: key1 != key2, path1 and path2 has the same length +fn[K, V] join_2( + key1 : K, + value1 : V, + path1 : Path, + key2 : K, + value2 : V, + path2 : Path, +) -> Node[K, V] { + let idx1 = path1.idx() + let idx2 = path2.idx() + if idx1 == idx2 { + let node = if path1.is_last() { + Leaf(key2, value2, @list.singleton((key1, value1))) } else { - let idx = hash & segment_mask - let child = make_leaf( - depth + segment_length, - key, - (hash.reinterpret_as_uint() >> segment_length).reinterpret_as_int(), - value, - ) - T::Branch(@sparse_array.singleton(idx, child)) + join_2(key1, value1, path1.next(), key2, value2, path2.next()) } + Branch(@sparse_array.singleton(idx1, node)) + } else { + let (node1, node2) = if path1.is_last() { + (Leaf(key1, value1, @list.empty()), Leaf(key2, value2, @list.empty())) + } else { + (Flat(key1, value1, path1.next()), Flat(key2, value2, path2.next())) + } + Branch(@sparse_array.doubleton(idx1, node1, idx2, node2)) } +} +///| +fn[K : Eq, V] add_with_path( + self : Node[K, V], + key : K, + value : V, + path : Path, +) -> Node[K, V] { match self { - Empty => make_leaf(depth, key, hash, value) - Leaf(key1, value1) => + Leaf(key1, value1, bucket) => if key == key1 { - Leaf(key, value) + Leaf(key, value, bucket) + } else { + let new_bucket = match bucket.find_index(kv => kv.0 == key) { + None => bucket + Some(index) => bucket.remove_at(index) + } + Leaf(key, value, new_bucket.add((key1, value1))) + } + Flat(key1, value1, path1) => + if path == path1 && key == key1 { + Flat(key1, value, path1) } else { - Collision(More(key, value, JustOne(key1, value1))) + join_2(key1, value1, path1, key, value, path) } - Collision(bucket) => Collision(bucket.add(key, value)) Branch(children) => { - let idx = hash & segment_mask + let idx = path.idx() match children[idx] { Some(child) => { - let child = child.add_with_hash( - key, - depth + segment_length, - (hash.reinterpret_as_uint() >> segment_length).reinterpret_as_int(), - value, - ) + let child = child.add_with_path(key, value, path.next()) Branch(children.replace(idx, child)) } None => { - let child = make_leaf( - depth + segment_length, - key, - (hash.reinterpret_as_uint() >> segment_length).reinterpret_as_int(), - value, - ) + let child = Flat(key, value, path.next()) Branch(children.add(idx, child)) } } @@ -152,36 +162,61 @@ fn[K : Eq, V] add_with_hash( ///| /// Filter values that satisfy the predicate -pub fn[K : Eq + Hash, V] filter( - self : T[K, V], - pred : (V) -> Bool raise? -) -> T[K, V] raise? { - match self { - Empty => Empty - Leaf(k, v) => if pred(v) { Leaf(k, v) } else { Empty } - Collision(bucket) => - match bucket.filter(pred) { - Some(JustOne(k, v)) => Leaf(k, v) - Some(b) => Collision(b) - None => Empty +#deprecated("Use `filter_with_key` instead. `filter` will accept `(K, V) -> Bool` in the future.") +#coverage.skip +pub fn[K, V] filter( + self : HashMap[K, V], + pred : (V) -> Bool raise?, +) -> HashMap[K, V] raise? { + self.filter_with_key((_, v) => pred(v)) +} + +///| +/// Filter entries that satisfy the predicate +pub fn[K, V] filter_with_key( + self : HashMap[K, V], + pred : (K, V) -> Bool raise?, +) -> HashMap[K, V] raise? { + fn go(node) raise? { + match node { + Leaf(key1, value1, bucket) => { + let new_bucket = bucket.filter(kv => pred(kv.0, kv.1)) + if pred(key1, value1) { + Some(Leaf(key1, value1, new_bucket)) + } else { + match new_bucket { + Empty => None + More((k1, v1), tail~) => Some(Leaf(k1, v1, tail)) + } + } } - Branch(children) => { - let mut result = Empty - children.each(child => child - .filter(pred) - .iter() - .each(kv => result = result.add(kv.0, kv.1))) - result + Flat(key1, value1, _) => + if pred(key1, value1) { + Some(node) + } else { + None + } + Branch(children) => + match children.filter(go) { + None => None + Some(new_children) => Some(Branch(new_children)) + } } } + + match self.0 { + None => None + Some(node) => go(node) + } } ///| /// Fold the values in the map +#deprecated("Use `fold_with_key` instead. `fold` will accept `(A, K, V) -> A` in the future.") pub fn[K, V, A] fold( - self : T[K, V], + self : HashMap[K, V], init~ : A, - f : (A, V) -> A raise? + f : (A, V) -> A raise?, ) -> A raise? { self.fold_with_key((acc, _k, v) => f(acc, v), init~) } @@ -189,108 +224,129 @@ pub fn[K, V, A] fold( ///| /// Fold the values in the map with key /// TODO: can not mark `f` as `#locals(f)` because -/// it will be shadowed by the `f` in the `fold_with_key` function +/// it will be shadowed by the `f` in the `@list.T::fold` function /// TO make it more useful in the future, we may need propagate pub fn[K, V, A] fold_with_key( - self : T[K, V], + self : HashMap[K, V], init~ : A, - f : (A, K, V) -> A raise? + f : (A, K, V) -> A raise?, ) -> A raise? { - loop (@list.singleton((self, 0)), init) { - (Empty, acc) => acc - (More((node, index), tail~), acc) => - match node { - Empty => continue (tail, acc) - Leaf(k, v) => continue (tail, f(acc, k, v)) - Collision(bucket) => continue (tail, bucket.foldl_with_key(init=acc, f)) - Branch(children) => - if index < children.data.length() { - let child = children.data.unsafe_get(index) - continue (tail.add((node, index + 1)).add((child, 0)), acc) - } else { - continue (tail, acc) - } - } + fn go(acc, node) raise? { + match node { + Leaf(k, v, bucket) => + bucket.fold(init=f(acc, k, v), (acc, kv) => f(acc, kv.0, kv.1)) + Flat(k, v, _) => f(acc, k, v) + Branch(children) => children.data.fold(init=acc, go) + } + } + + match self.0 { + None => init + Some(node) => go(init, node) } } ///| /// Maps over the values in the map -pub fn[K : Eq + Hash, V, A] map( - self : T[K, V], - f : (V) -> A raise? -) -> T[K, A] raise? { +#deprecated("Use `map_with_key` instead. `map` will accept `(K, V) -> A` in the future.") +#coverage.skip +pub fn[K, V, A] map( + self : HashMap[K, V], + f : (V) -> A raise?, +) -> HashMap[K, A] raise? { self.map_with_key((_k, v) => f(v)) } ///| /// Maps over the key-value pairs in the map -pub fn[K : Eq + Hash, V, A] map_with_key( - self : T[K, V], - f : (K, V) -> A raise? -) -> T[K, A] raise? { - fn go(m : T[K, V]) -> T[K, A] raise? { +pub fn[K, V, A] map_with_key( + self : HashMap[K, V], + f : (K, V) -> A raise?, +) -> HashMap[K, A] raise? { + fn go(m : Node[K, V]) -> Node[K, A] raise? { match m { - Empty => Empty - Leaf(k, v) => Leaf(k, f(k, v)) - Collision(bucket) => Collision(bucket.map_with_key(f)) - Branch(children) => { - let result = [] - children.each(child => child - .map_with_key(f) - .iter() - .each(kv => result.push(kv))) - from_array(result) - } + Leaf(k, v, bucket) => + Leaf(k, f(k, v), bucket.map(kv => (kv.0, f(kv.0, kv.1)))) + Flat(k, v, path) => Flat(k, f(k, v), path) + Branch(children) => Branch(children.map(go)) } } - go(self) + match self.0 { + None => None + Some(node) => Some(go(node)) + } } ///| /// Add a key-value pair to the hashmap. /// /// If a pair with the same key already exists, the old one is replaced -pub fn[K : Eq + Hash, V] add(self : T[K, V], key : K, value : V) -> T[K, V] { - self.add_with_hash(key, 0, key.hash(), value) +pub fn[K : Eq + Hash, V] add( + self : HashMap[K, V], + key : K, + value : V, +) -> HashMap[K, V] { + match self.0 { + None => Some(Flat(key, value, @path.of(key))) + Some(node) => Some(node.add_with_path(key, value, @path.of(key))) + } } ///| /// Remove an element from a map -pub fn[K : Eq + Hash, V] remove(self : T[K, V], key : K) -> T[K, V] { - self.remove_with_hash(key, 0, key.hash()) +pub fn[K : Eq + Hash, V] remove(self : HashMap[K, V], key : K) -> HashMap[K, V] { + match self.0 { + None => None + Some(node) => node.remove_with_path(key, @path.of(key)) + } } ///| -fn[K : Eq, V] remove_with_hash( - self : T[K, V], +fn[K : Eq, V] remove_with_path( + self : Node[K, V], key : K, - depth : Int, - hash : Int -) -> T[K, V] { + path : Path, +) -> Node[K, V]? { match self { - Empty => self - Leaf(old_key, _) => if key == old_key { Empty } else { self } - Collision(bucket) => - match bucket.remove(key) { - None => Empty - Some(JustOne(k, v)) => Leaf(k, v) - Some(new_bucket) => Collision(new_bucket) + Leaf(key1, value1, bucket) => + if key1 == key { + match bucket { + @list.Empty => None + More((key2, value2), tail~) => Some(Leaf(key2, value2, tail)) + } + } else if bucket.find_index(kv => kv.0 == key) is Some(index) { + Some(Leaf(key1, value1, bucket.remove_at(index))) + } else { + Some(self) + } + Flat(key1, _, path1) => + if path == path1 && key == key1 { + None + } else { + Some(self) } Branch(children) => { - let idx = hash & segment_mask + let idx = path.idx() match children[idx] { - None => self + None => Some(self) Some(child) => { - let new_child = child.remove_with_hash( - key, - depth + segment_length, - (hash.reinterpret_as_uint() >> segment_length).reinterpret_as_int(), - ) - match (children.size(), new_child) { - (1, Empty) => Empty - (_, _) => Branch(children.replace(idx, new_child)) + let new_child = child.remove_with_path(key, path.next()) + let new_children = match (children.size(), new_child) { + (1, None) => return None + (_, None) => children.remove(idx) + (_, Some(new_child)) => children.replace(idx, new_child) + } + match new_children.data { + [Flat(key1, value1, path1)] => + Some( + Flat( + key1, + value1, + path1.push(new_children.elem_info.first_idx()), + ), + ) + _ => Some(Branch(new_children)) } } } @@ -302,148 +358,321 @@ fn[K : Eq, V] remove_with_hash( /// Calculate the size of a map. /// /// WARNING: this operation is `O(N)` in map size -pub fn[K, V] size(self : T[K, V]) -> Int { - match self { - Empty => 0 - Leaf(_) => 1 - Collision(bucket) => bucket.size() - Branch(children) => - for i = 0, total_size = 0 { - if i < children.data.length() { - continue i + 1, total_size + children.data[i].size() +pub fn[K, V] size(self : HashMap[K, V]) -> Int { + fn node_size(node) { + match node { + Leaf(_, _, bucket) => 1 + bucket.length() + Flat(_) => 1 + Branch(children) => + for i = 0, total_size = 0; i < children.data.length(); { + continue i + 1, total_size + node_size(children.data[i]) } else { - break total_size + total_size } - } + } + } + + match self.0 { + None => 0 + Some(node) => node_size(node) } } ///| /// Union two hashmaps, right-hand side element is prioritized -pub fn[K : Eq + Hash, V] T::union(self : T[K, V], other : T[K, V]) -> T[K, V] { - other.iter().fold(init=self, (m, kv) => m.add(kv.0, kv.1)) +pub fn[K : Eq, V] HashMap::union( + self : HashMap[K, V], + other : HashMap[K, V], +) -> HashMap[K, V] { + fn go(node1 : Node[_], node2) { + match (node1, node2) { + (_, Flat(key2, value2, path2)) => node1.add_with_path(key2, value2, path2) + (Flat(key1, value1, path1), _) => + match node2.get_with_path(key1, path1) { + Some(_) => node2 + None => node2.add_with_path(key1, value1, path1) + } + (Branch(children1), Branch(children2)) => + Branch(children1.union(children2, go)) + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + match kvs1.filter(kv => kvs2.lookup(kv.0) is None) { + Empty => node2 + More(head, tail~) => Leaf(key2, value2, bucket2 + tail.add(head)) + } + } + _ => abort("Unreachable") + } + } + + match (self.0, other.0) { + (None, x) | (x, None) => x + (Some(a), Some(b)) => Some(go(a, b)) + } } ///| /// Union two hashmaps with a function -pub fn[K : Eq + Hash, V] T::union_with( - self : T[K, V], - other : T[K, V], - f : (K, V, V) -> V raise? -) -> T[K, V] raise? { - match (self, other) { - (_, Empty) => self - (Empty, _) => other - (_, Leaf(k, v)) => - match self.get(k) { - Some(v1) => self.add(k, f(k, v1, v)) - None => self.add(k, v) +pub fn[K : Eq, V] HashMap::union_with( + self : HashMap[K, V], + other : HashMap[K, V], + f : (K, V, V) -> V raise?, +) -> HashMap[K, V] raise? { + fn go(node1 : Node[_], node2) raise? { + match (node1, node2) { + (_, Flat(key2, value2, path2)) => { + let new_value = match node1.get_with_path(key2, path2) { + Some(value1) => f(key2, value1, value2) + None => value2 + } + node1.add_with_path(key2, new_value, path2) + } + (Flat(key1, value1, path1), _) => { + let new_value = match node2.get_with_path(key1, path1) { + Some(value2) => f(key1, value1, value2) + None => value1 + } + node2.add_with_path(key1, new_value, path1) } - (Leaf(k, v), _) => - match other.get(k) { - Some(v2) => other.add(k, f(k, v, v2)) - None => other.add(k, v) + (Branch(children1), Branch(children2)) => + Branch(children1.union(children2, go)) + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + kvs1.union_with(kvs2, f) } - (Branch(sa1), Branch(sa2)) => - Branch(sa1.union(sa2, (m1, m2) => m1.union_with(m2, f))) - (_, _) => - self - .iter() - .fold(init=other, (m, kv) => match m.get(kv.0) { - Some(v2) => m.add(kv.0, f(kv.0, kv.1, v2)) - None => m.add(kv.0, kv.1) - }) + _ => abort("Unreachable") + } + } + + match (self.0, other.0) { + (None, x) | (x, None) => x + (Some(a), Some(b)) => Some(go(a, b)) } } +///| +fn[K : Eq, V] @list.List::union_with( + self : Self[(K, V)], + other : Self[(K, V)], + f : (K, V, V) -> V raise?, +) -> Node[K, V] raise? { + let res = self.to_array() + for kv2 in other { + for i, kv1 in res { + if kv1.0 == kv2.0 { + res[i] = (kv1.0, f(kv1.0, kv1.1, kv2.1)) + break + } + } else { + res.push(kv2) + } + } + guard @list.from_array(res) is More((k, v), tail~) + Leaf(k, v, tail) +} + ///| /// Intersect two hashmaps, right-hand side element is prioritized -pub fn[K : Eq + Hash, V] T::intersection( - self : T[K, V], - other : T[K, V] -) -> T[K, V] { - self - .iter() - .fold(init=Empty, (m, kv) => if other.get(kv.0) is Some(v2) { - m.add(kv.0, v2) - } else { - m - }) +pub fn[K : Eq, V] HashMap::intersection( + self : HashMap[K, V], + other : HashMap[K, V], +) -> HashMap[K, V] { + fn go(node1 : Node[_], node2) { + match (node1, node2) { + (_, Flat(key2, _, path2)) => + match node1.get_with_path(key2, path2) { + Some(_) => Some(node2) + None => None + } + (Flat(key1, _, path1), _) => + match node2.get_with_path(key1, path1) { + Some(value2) => Some(Flat(key1, value2, path1)) + None => None + } + (Branch(children1), Branch(children2)) => + match children1.intersection(children2, go) { + None => None + Some({ data: [Flat(key, value, path)], elem_info }) => + Some(Flat(key, value, path.push(elem_info.first_idx()))) + Some(children) => Some(Branch(children)) + } + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + match kvs2.filter(kv => kvs1.lookup(kv.0) is Some(_)) { + Empty => None + More(head, tail~) => Some(Leaf(head.0, head.1, tail)) + } + } + _ => abort("Unreachable") + } + } + + match (self.0, other.0) { + (None, _) | (_, None) => None + (Some(a), Some(b)) => go(a, b) + } } ///| /// Intersection two hashmaps with a function -pub fn[K : Eq + Hash, V] T::intersection_with( - self : T[K, V], - other : T[K, V], - f : (K, V, V) -> V raise? -) -> T[K, V] raise? { - self - .iter() - .fold(init=Empty, (m, kv) => match other.get(kv.0) { - Some(v2) => m.add(kv.0, f(kv.0, kv.1, v2)) - None => m - }) +pub fn[K : Eq, V] HashMap::intersection_with( + self : HashMap[K, V], + other : HashMap[K, V], + f : (K, V, V) -> V raise?, +) -> HashMap[K, V] raise? { + fn go(node1 : Node[_], node2) raise? { + match (node1, node2) { + (_, Flat(key2, value2, path2)) => + match node1.get_with_path(key2, path2) { + Some(value1) => Some(Flat(key2, f(key2, value1, value2), path2)) + None => None + } + (Flat(key1, value1, path1), _) => + match node2.get_with_path(key1, path1) { + Some(value2) => Some(Flat(key1, f(key1, value1, value2), path1)) + None => None + } + (Branch(children1), Branch(children2)) => + match children1.intersection(children2, go) { + None => None + Some({ data: [Flat(key, value, path)], elem_info }) => + Some(Flat(key, value, path.push(elem_info.first_idx()))) + Some(children) => Some(Branch(children)) + } + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + kvs1.intersection_with(kvs2, f) + } + _ => abort("Unreachable") + } + } + + match (self.0, other.0) { + (None, _) | (_, None) => None + (Some(a), Some(b)) => go(a, b) + } +} + +///| +fn[K : Eq, V] @list.List::intersection_with( + self : Self[(K, V)], + other : Self[(K, V)], + f : (K, V, V) -> V raise?, +) -> Node[K, V]? raise? { + let res = [] + for kv1 in self { + for kv2 in other { + if kv1.0 == kv2.0 { + res.push((kv1.0, f(kv1.0, kv1.1, kv2.1))) + break + } + } + } + match @list.from_array(res) { + Empty => None + More((k, v), tail~) => Some(Leaf(k, v, tail)) + } } ///| /// Difference of two hashmaps: elements in `self` but not in `other` -pub fn[K : Eq + Hash, V] T::difference( - self : T[K, V], - other : T[K, V] -) -> T[K, V] { - self - .iter() - .fold(init=Empty, (m, kv) => if other.get(kv.0) is None { - m.add(kv.0, kv.1) - } else { - m - }) +pub fn[K : Eq, V] HashMap::difference( + self : HashMap[K, V], + other : HashMap[K, V], +) -> HashMap[K, V] { + fn go(node1 : Node[_], node2) { + match (node1, node2) { + (node, Flat(k, _, path)) => node.remove_with_path(k, path) + (Flat(key, _, path), _) => + match node2.get_with_path(key, path) { + Some(_) => None + None => Some(node1) + } + (Branch(children1), Branch(children2)) => + match children1.difference(children2, go) { + None => None + Some({ data: [Flat(key, value, path)], elem_info }) => + Some(Flat(key, value, path.push(elem_info.first_idx()))) + Some(children) => Some(Branch(children)) + } + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + match kvs1.filter(kv => not(kvs2.lookup(kv.0) is Some(_))) { + Empty => None + More(head, tail~) => Some(Leaf(head.0, head.1, tail)) + } + } + _ => abort("Unreachable") + } + } + + match (self.0, other.0) { + (None, _) => None + (_, None) => self + (Some(a), Some(b)) => go(a, b) + } } ///| /// Iterate through the elements in a hash map -pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit raise?) -> Unit raise? { - match self { - Empty => () - Leaf(k, v) => f(k, v) - Collision(bucket) => bucket.each(f) - Branch(children) => children.each(child => child.each(f)) +pub fn[K, V] each( + self : HashMap[K, V], + f : (K, V) -> Unit raise?, +) -> Unit raise? { + fn go(node) raise? { + match node { + Leaf(k, v, bucket) => { + f(k, v) + bucket.each(kv => f(kv.0, kv.1)) + } + Flat(k, v, _) => f(k, v) + Branch(children) => children.each(go) + } + } + + match self.0 { + None => () + Some(node) => go(node) } } ///| /// Returns all keys of the map -pub fn[K, V] keys(self : T[K, V]) -> Iter[K] { +pub fn[K, V] keys(self : HashMap[K, V]) -> Iter[K] { self.iter().map(p => p.0) } ///| /// Returns all values of the map -pub fn[K, V] values(self : T[K, V]) -> Iter[V] { +#alias(elems, deprecated="Use `values` instead") +pub fn[K, V] values(self : HashMap[K, V]) -> Iter[V] { self.iter().map(p => p.1) } -///| -#deprecated("Use `values` instead") -#coverage.skip -pub fn[K, V] elems(self : T[K, V]) -> Iter[V] { - self.values() -} - ///| /// Converted to Iter -pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { - match self { - Empty => Iter::empty() - Leaf(k, v) => Iter::singleton((k, v)) - Collision(bucket) => bucket.iter() - Branch(children) => children.data.iter().flat_map(map => map.iter()) +pub fn[K, V] iter(self : HashMap[K, V]) -> Iter[(K, V)] { + fn go(node) -> Iter[(K, V)] { + match node { + Leaf(k, v, bucket) => Iter::singleton((k, v)) + bucket.iter() + Flat(k, v, _) => Iter::singleton((k, v)) + Branch(children) => children.data.iter().flat_map(go) + } + } + + match self.0 { + None => Iter::empty() + Some(node) => go(node) } } ///| -pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { +pub fn[K, V] iter2(self : HashMap[K, V]) -> Iter2[K, V] { Iter2::new(yield_ => for kv in self { guard yield_(kv.0, kv.1) is IterContinue else { break IterEnd } } else { @@ -452,17 +681,23 @@ pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { } ///| -pub fn[K : Eq + Hash, V] from_iter(iter : Iter[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Eq + Hash, V] HashMap::from_iter( + iter : Iter[(K, V)], +) -> HashMap[K, V] { iter.fold(init=new(), (m, e) => m.add(e.0, e.1)) } ///| -pub impl[K : Show, V : Show] Show for T[K, V] with output(self, logger) { +pub impl[K : Show, V : Show] Show for HashMap[K, V] with output(self, logger) { logger.write_iter(self.iter(), prefix="@immut/hashmap.of([", suffix="])") } ///| -pub fn[K : Eq + Hash, V] from_array(arr : Array[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Eq + Hash, V] HashMap::from_array( + arr : Array[(K, V)], +) -> HashMap[K, V] { loop (arr.length(), new()) { (0, map) => map (n, map) => { @@ -474,14 +709,14 @@ pub fn[K : Eq + Hash, V] from_array(arr : Array[(K, V)]) -> T[K, V] { ///| /// Convert to an array of key-value pairs. -pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { +pub fn[K, V] to_array(self : HashMap[K, V]) -> Array[(K, V)] { let arr = Array::new(capacity=self.size()) self.each((k, v) => arr.push((k, v))) arr } ///| -pub impl[K : Eq + Hash + @quickcheck.Arbitrary, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[ +pub impl[K : Eq + Hash + @quickcheck.Arbitrary, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashMap[ K, V, ] with arbitrary(size, rs) { @@ -489,7 +724,8 @@ pub impl[K : Eq + Hash + @quickcheck.Arbitrary, V : @quickcheck.Arbitrary] @quic } ///| -pub fn[K : Eq + Hash, V] of(arr : FixedArray[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Eq + Hash, V] HashMap::of(arr : FixedArray[(K, V)]) -> HashMap[K, V] { loop (arr.length(), new()) { (0, map) => map (n, map) => { @@ -500,15 +736,25 @@ pub fn[K : Eq + Hash, V] of(arr : FixedArray[(K, V)]) -> T[K, V] { } ///| -pub impl[K : Eq + Hash, V : Eq] Eq for T[K, V] with op_equal(self, other) { - guard self.size() == other.size() else { return false } - for kv in self { - guard other.get(kv.0) is Some(v) && v == kv.1 else { return false } +impl[K : Eq, V : Eq] Eq for Node[K, V] with equal(self, other) { + match (self, other) { + (Flat(key1, value1, path1), Flat(key2, value2, path2)) => + path1 == path2 && key1 == key2 && value1 == value2 + (Branch(children1), Branch(children2)) => children1 == children2 + (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { + guard bucket1.length() == bucket2.length() else { return false } + let kvs1 = bucket1.add((key1, value1)) + let kvs2 = bucket2.add((key2, value2)) + kvs1.all(kv => kvs2.lookup(kv.0) is Some(v) && kv.1 == v) + } + _ => false } - true } ///| -pub impl[K : Hash, V : Hash] Hash for T[K, V] with hash_combine(self, hasher) { - self.each((k, v) => hasher..combine(k)..combine(v)) +pub impl[K : Hash, V : Hash] Hash for HashMap[K, V] with hash_combine( + self, + hasher, +) { + hasher.combine(self.fold_with_key(init=0, (acc, k, v) => acc ^ (k, v).hash())) } diff --git a/bundled-core/immut/hashmap/HAMT_test.mbt b/bundled-core/immut/hashmap/HAMT_test.mbt index d4af2da..6d08015 100644 --- a/bundled-core/immut/hashmap/HAMT_test.mbt +++ b/bundled-core/immut/hashmap/HAMT_test.mbt @@ -67,18 +67,18 @@ test "HAMT::remove" { test "keys" { let m = @hashmap.of([(1, "one"), (2, "two")]) let keys = m.keys() - assert_eq(keys.contains(1), true) - assert_eq(keys.contains(2), true) - assert_eq(keys.contains(3), false) + inspect(keys.contains(1), content="true") + inspect(keys.contains(2), content="true") + inspect(keys.contains(3), content="false") } ///| test "HAMT::values" { let m = @hashmap.of([(1, "one"), (2, "two")]) let values = m.values() - assert_eq(values.contains("one"), true) - assert_eq(values.contains("two"), true) - assert_eq(values.contains("three"), false) + inspect(values.contains("one"), content="true") + inspect(values.contains("two"), content="true") + inspect(values.contains("three"), content="false") } ///| @@ -115,21 +115,21 @@ test "HAMT::from_array" { let map = @hashmap.of([(1, "1"), (2, "2"), (42, "42")]) inspect( (1, map.get(1)), - content= + content=( #|(1, Some("1")) - , + ), ) inspect( (2, map.get(2)), - content= + content=( #|(2, Some("2")) - , + ), ) inspect( (42, map.get(42)), - content= + content=( #|(42, Some("42")) - , + ), ) inspect((43, map.get(43)), content="(43, None)") } @@ -138,7 +138,7 @@ test "HAMT::from_array" { test "to_array" { let m = @hashmap.of([(1, "one"), (2, "two")]) let arr = m.to_array() - assert_eq(arr.length(), 2) + inspect(arr.length(), content="2") assert_eq(arr.contains((1, "one")), true) assert_eq(arr.contains((2, "two")), true) assert_eq(arr.contains((3, "three")), false) @@ -162,65 +162,77 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @hashmap.T[Int, Int] = @hashmap.from_iter(Iter::empty()) + let pq : @hashmap.HashMap[Int, Int] = @hashmap.from_iter(Iter::empty()) inspect(pq, content="@immut/hashmap.of([])") } ///| test "eq for boundary cases" { // Test with empty maps - let empty_map1 : @hashmap.T[Int, Int] = @hashmap.new() - let empty_map2 : @hashmap.T[Int, Int] = @hashmap.new() + let empty_map1 : @hashmap.HashMap[Int, Int] = @hashmap.new() + let empty_map2 : @hashmap.HashMap[Int, Int] = @hashmap.new() inspect(empty_map1 == empty_map2, content="true") // Test with one empty map and one non-empty map - let non_empty_map : @hashmap.T[Int, Int] = @hashmap.of([(1, 1)]) + let non_empty_map : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1)]) inspect(empty_map1 == non_empty_map, content="false") inspect(non_empty_map == empty_map1, content="false") // Test with maps of different sizes - let larger_map : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (2, 2)]) + let larger_map : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (2, 2)]) inspect(non_empty_map == larger_map, content="false") } ///| test "eq for random cases" { // Test with maps containing different keys - let map1 : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (2, 2)]) - let map2 : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (3, 3)]) + let map1 : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (2, 2)]) + let map2 : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (3, 3)]) inspect(map1 == map2, content="false") // Test with maps containing same keys but different values - let map3 : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (2, 3)]) + let map3 : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (2, 3)]) inspect(map1 == map3, content="false") // Test with maps containing same keys and values but in different order - let map4 : @hashmap.T[Int, Int] = @hashmap.of([(2, 2), (1, 1)]) + let map4 : @hashmap.HashMap[Int, Int] = @hashmap.of([(2, 2), (1, 1)]) inspect(map1 == map4, content="true") // Test with maps containing same keys and values but with different hash collisions - let map5 : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (11, 11)]) - let map6 : @hashmap.T[Int, Int] = @hashmap.of([(1, 1), (11, 11)]) + let map5 : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (11, 11)]) + let map6 : @hashmap.HashMap[Int, Int] = @hashmap.of([(1, 1), (11, 11)]) inspect(map5 == map6, content="true") // Test with maps containing same keys and values but with different hash collisions and different order - let map7 : @hashmap.T[Int, Int] = @hashmap.of([(11, 11), (1, 1)]) + let map7 : @hashmap.HashMap[Int, Int] = @hashmap.of([(11, 11), (1, 1)]) inspect(map5 == map7, content="true") } ///| test "eq for random cases with different types" { // Test with maps containing different types - let map8 : @hashmap.T[String, Int] = @hashmap.of([("one", 1), ("two", 2)]) - let map9 : @hashmap.T[String, Int] = @hashmap.of([("one", 1), ("two", 2)]) + let map8 : @hashmap.HashMap[String, Int] = @hashmap.of([ + ("one", 1), + ("two", 2), + ]) + let map9 : @hashmap.HashMap[String, Int] = @hashmap.of([ + ("one", 1), + ("two", 2), + ]) inspect(map8 == map9, content="true") // Test with maps containing same keys but different values of different types - let map10 : @hashmap.T[String, Int] = @hashmap.of([("one", 1), ("two", 3)]) + let map10 : @hashmap.HashMap[String, Int] = @hashmap.of([ + ("one", 1), + ("two", 3), + ]) inspect(map8 == map10, content="false") // Test with maps containing different keys of different types - let map11 : @hashmap.T[String, Int] = @hashmap.of([("one", 1), ("three", 3)]) + let map11 : @hashmap.HashMap[String, Int] = @hashmap.of([ + ("one", 1), + ("three", 3), + ]) inspect(map8 == map11, content="false") } @@ -279,46 +291,46 @@ test "union 2 hashmaps, without conflict" { ///| test "HAMT::contains" { let map = @hashmap.of([(1, "one")]) - assert_eq(map.contains(1), true) - assert_eq(map.contains(2), false) + inspect(map.contains(1), content="true") + inspect(map.contains(2), content="false") let map2 = map.add(2, "two") - assert_eq(map2.contains(2), true) + inspect(map2.contains(2), content="true") let map3 = map.remove(2) - assert_eq(map3.contains(2), false) + inspect(map3.contains(2), content="false") } ///| test "filter with simple predicate" { let map = @hashmap.of([(1, 1), (2, 2), (3, 3), (4, 4)]) - let only_even = map.filter(v => v % 2 == 0) - assert_eq(only_even.contains(1), false) - assert_eq(only_even.contains(2), true) - assert_eq(only_even.contains(3), false) - assert_eq(only_even.contains(4), true) + let only_even = map.filter_with_key((_, v) => v % 2 == 0) + inspect(only_even.contains(1), content="false") + inspect(only_even.contains(2), content="true") + inspect(only_even.contains(3), content="false") + inspect(only_even.contains(4), content="true") } ///| test "filter with all elements matching" { let map = @hashmap.of([(1, 1), (2, 2)]) - let filtered = map.filter(v => v > 0) - assert_eq(filtered.contains(1), true) - assert_eq(filtered.contains(2), true) + let filtered = map.filter_with_key((_, v) => v > 0) + inspect(filtered.contains(1), content="true") + inspect(filtered.contains(2), content="true") } ///| test "filter with no elements matching" { let map = @hashmap.of([(1, 1), (2, 2), (3, 3)]) - let filtered = map.filter(v => v > 10) + let filtered = map.filter_with_key((_, v) => v > 10) assert_eq(filtered.get(1), None) assert_eq(filtered.get(2), None) assert_eq(filtered.get(3), None) - assert_eq(filtered.size(), 0) + inspect(filtered.size(), content="0") } ///| test "filter with collision" { let map = @hashmap.of([(1, 10), (2, 20)]).add(1, 30) - let filtered = map.filter(v => v == 30) + let filtered = map.filter_with_key((_, v) => v == 30) assert_eq(filtered.get(1), Some(30)) assert_eq(filtered.get(2), None) } @@ -329,7 +341,7 @@ test "filter with branch nodes" { (10, map) => map (i, map) => continue (i + 1, map.add(i, i * 10)) } - let filtered = map.filter(v => v % 20 == 0) + let filtered = map.filter_with_key((_, v) => v % 20 == 0) for i in 0..<10 { if i * 10 % 20 == 0 { assert_eq(filtered.get(i), Some(i * 10)) @@ -344,21 +356,21 @@ test "HAMT::fold_with_key" { let map = @hashmap.of([(1, "a"), (2, "b")]) let result = map.fold_with_key(init="", (acc, k, v) => acc + "\{k}:\{v}, ") // order of elements is not guaranteed, so we check for substrings - assert_eq(result.contains("1:a"), true) - assert_eq(result.contains("2:b"), true) + inspect(result.contains("1:a"), content="true") + inspect(result.contains("2:b"), content="true") } ///| test "HAMT::fold" { let map = @hashmap.of([(1, 10), (2, 20), (3, 30)]) - let result = map.fold(init=0, (acc, v) => acc + v) - assert_eq(result, 60) + let result = map.fold_with_key(init=0, (acc, _, v) => acc + v) + inspect(result, content="60") } ///| test "HAMT::map" { let map = @hashmap.of([(1, 10), (2, 20)]) - let mapped = map.map(v => v * 2) + let mapped = map.map_with_key((_, v) => v * 2) assert_eq(mapped.get(1), Some(20)) assert_eq(mapped.get(2), Some(40)) assert_eq(mapped.get(3), None) @@ -368,15 +380,15 @@ test "HAMT::map" { test "HAMT::map with overflow" { let max = 2147483647 // Int.max_value let map = @hashmap.of([(1, max)]) - let mapped = map.map(v => v + 1) + let mapped = map.map_with_key((_, v) => v + 1) assert_eq(mapped.get(1), Some(-2147483648)) } ///| test "HAMT::map_with_key empty" { - let map : @hashmap.T[Int, Int] = @hashmap.new() + let map : @hashmap.HashMap[Int, Int] = @hashmap.new() let mapped = map.map_with_key((_k, v) => v) - assert_eq(mapped.size(), 0) + inspect(mapped.size(), content="0") } ///| @@ -384,13 +396,13 @@ test "HAMT::map_with_key leaf" { let map = @hashmap.singleton(42, 100) // Leaf let mapped = map.map_with_key((k, v) => k + v) assert_eq(mapped.get(42), Some(142)) - assert_eq(mapped.size(), 1) + inspect(mapped.size(), content="1") } ///| test "HAMT::map_with_key collision" { let map = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) // Collision - let mapped = map.map_with_key((k, v) => k.inner() + ":" + v.to_string()) + let mapped = map.map_with_key((k, v) => k.0 + ":" + v.to_string()) assert_eq(mapped.get(MyString("a")), Some("a:1")) assert_eq(mapped.get(MyString("b")), Some("b:2")) assert_eq(mapped.get(MyString("c")), None) @@ -412,13 +424,13 @@ test "HAMT::map_with_key branch" { ///| test "HAMT::singleton" { let map = @hashmap.singleton(1, "one") - assert_eq(map.size(), 1) + inspect(map.size(), content="1") assert_eq(map.get(1), Some("one")) assert_eq(map.get(2), None) } ///| -type MyString String +struct MyString(String) ///| impl Hash for MyString with hash_combine(_self, hasher) { @@ -426,8 +438,8 @@ impl Hash for MyString with hash_combine(_self, hasher) { } ///| -impl Eq for MyString with op_equal(self, other) { - self.inner() == other.inner() +impl Eq for MyString with equal(self, other) { + self.0 == other.0 } ///| @@ -457,23 +469,23 @@ test "add_with_hash collision" { ///| test "empty filtering" { - let map : @hashmap.T[Int, Int] = @hashmap.of([]) - let _filtered = map.filter(v => v > 2) - assert_eq(map.size(), 0) + let map : @hashmap.HashMap[Int, Int] = @hashmap.of([]) + let _filtered = map.filter_with_key((_, v) => v > 2) + inspect(map.size(), content="0") } ///| test "filter with collision - all removed" { let map = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) - let filtered = map.filter(v => v > 2) - assert_eq(filtered.size(), 0) + let filtered = map.filter_with_key((_, v) => v > 2) + inspect(filtered.size(), content="0") } ///| test "filter with collision - one left" { let map = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) - let filtered = map.filter(v => v == 1) - assert_eq(filtered.size(), 1) + let filtered = map.filter_with_key((_, v) => v == 1) + inspect(filtered.size(), content="1") } ///| @@ -483,35 +495,35 @@ test "filter with collision - more than one left" { (MyString("b"), 2), (MyString("c"), 3), ]) - let filtered = map.filter(v => v > 1) - assert_eq(filtered.size(), 2) + let filtered = map.filter_with_key((_, v) => v > 1) + inspect(filtered.size(), content="2") } ///| test "HAMT::fold_with_key on empty" { - let map : @hashmap.T[Int, Int] = @hashmap.new() + let map : @hashmap.HashMap[Int, Int] = @hashmap.new() let result = map.fold_with_key(init=0, (acc, _k, _v) => acc + 1) - assert_eq(result, 0) + inspect(result, content="0") } ///| test "HAMT::fold_with_key on collision" { let map = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) let result = map.fold_with_key(init=0, (acc, _k, v) => acc + v) - assert_eq(result, 3) + inspect(result, content="3") } ///| test "HAMT::remove_with_hash collision" { let map = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) let map1 = map.remove(MyString("c")) - assert_eq(map1.size(), 2) + inspect(map1.size(), content="2") let map2 = map.remove(MyString("a")) assert_eq(map2.get(MyString("a")), None) assert_eq(map2.get(MyString("b")), Some(2)) - assert_eq(map2.size(), 1) + inspect(map2.size(), content="1") let map3 = map2.remove(MyString("b")) - assert_eq(map3.size(), 0) + inspect(map3.size(), content="0") } ///| @@ -538,7 +550,7 @@ test "HAMT::union with branch" { let m2 = @hashmap.of([(4, 4), (5, 5), (6, 6)]) let u = m1.union(m2) for i in 1..=6 { - assert_eq(u.contains(i), true) + inspect(u.contains(i), content="true") } } @@ -672,23 +684,34 @@ test "HAMT::each" { let empty = @hashmap.new() let mut sum = 0 empty.each((k : Int, v : Int) => sum = sum + k + v) - assert_eq(sum, 0) + inspect(sum, content="0") let leaf = @hashmap.singleton(1, 10) let mut sum_leaf = 0 leaf.each((k, v) => sum_leaf = sum_leaf + k + v) - assert_eq(sum_leaf, 11) + inspect(sum_leaf, content="11") let collision = @hashmap.of([(MyString("a"), 1), (MyString("b"), 2)]) let mut sum_collision = 0 collision.each((_k : MyString, v : Int) => sum_collision = sum_collision + v) - assert_eq(sum_collision, 3) + inspect(sum_collision, content="3") let branch = @hashmap.of([(1, 1), (2, 2), (3, 3), (4, 4)]) let mut sum_branch = 0 branch.each((_k, v) => sum_branch = sum_branch + v) - assert_eq(sum_branch, 10) + inspect(sum_branch, content="10") } ///| test "arbitary" { - let m : Array[@hashmap.T[Int, Int]] = @quickcheck.samples(10) - assert_eq(m.length(), 10) + let m : Array[@hashmap.HashMap[Int, Int]] = @quickcheck.samples(10) + inspect(m.length(), content="10") +} + +///| +test "equal imply hash equal when collision" { + let s1 = MyString("1") + let s2 = MyString("2") + inspect(s1.hash() == s2.hash(), content="true") // collisions + let ma = @hashmap.new().add(s1, 1).add(s2, 2) + let mb = @hashmap.new().add(s2, 2).add(s1, 1) + inspect(ma == mb, content="true") + inspect(ma.hash() == mb.hash(), content="true") } diff --git a/bundled-core/immut/hashmap/bucket.mbt b/bundled-core/immut/hashmap/bucket.mbt deleted file mode 100644 index 1528643..0000000 --- a/bundled-core/immut/hashmap/bucket.mbt +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -/// Lookup a key from the bucket -fn[K : Eq, V] Bucket::find(self : Bucket[K, V], key : K) -> V? { - match self { - JustOne(key1, value) => if key == key1 { Some(value) } else { None } - More(key1, value, rest) => - if key == key1 { - Some(value) - } else { - rest.find(key) - } - } -} - -///| -/// Add a new key-value pair to a bucket. -/// Replace the old entry if one with the same key already exists. -fn[K : Eq, V] Bucket::add( - self : Bucket[K, V], - key : K, - value : V -) -> Bucket[K, V] { - match self { - JustOne(key1, _) => - if key == key1 { - JustOne(key, value) - } else { - More(key, value, self) - } - More(key1, value1, rest) => - if key == key1 { - More(key, value, rest) - } else { - More(key1, value1, rest.add(key, value)) - } - } -} - -///| -/// Remove a key from a bucket -fn[K : Eq, V] Bucket::remove(self : Bucket[K, V], key : K) -> Bucket[K, V]? { - match self { - JustOne(old_key, _) => if key == old_key { None } else { Some(self) } - More(old_key, old_value, rest) => - if key == old_key { - Some(rest) - } else { - match rest.remove(key) { - None => Some(JustOne(old_key, old_value)) - Some(rest) => Some(More(old_key, old_value, rest)) - } - } - } -} - -///| -/// Get the size of a bucket -fn[K, V] Bucket::size(self : Bucket[K, V]) -> Int { - loop (self, 1) { - (JustOne(_), acc) => acc - (More(_, _, rest), acc) => continue (rest, acc + 1) - } -} - -///| -/// Iterate through elements of a bucket -fn[K, V] Bucket::each( - self : Bucket[K, V], - f : (K, V) -> Unit raise? -) -> Unit raise? { - loop self { - JustOne(k, v) => f(k, v) - More(k, v, rest) => { - f(k, v) - continue rest - } - } -} - -///| -fn[K, V] Bucket::filter( - self : Bucket[K, V], - f : (V) -> Bool raise? -) -> Bucket[K, V]? raise? { - match self { - JustOne(k, v) => if f(v) { Some(JustOne(k, v)) } else { None } - More(k, v, rest) => { - let filtered = rest.filter(f) - if f(v) { - match filtered { - None => Some(JustOne(k, v)) - Some(rest) => Some(More(k, v, rest)) - } - } else { - filtered - } - } - } -} - -///| -test "Bucket::filter" { - let bucket = More(1, 1, More(2, 2, JustOne(3, 3))) - let filtered = bucket.filter(v => v == 2) - assert_eq(filtered.unwrap().find(1), None) - assert_eq(filtered.unwrap().find(2), Some(2)) -} - -///| -fn[K, V] Bucket::iter(self : Bucket[K, V]) -> Iter[(K, V)] { - Iter::new(f => loop self { - JustOne(k, v) => f((k, v)) - More(k, v, rest) => - if f((k, v)) == IterContinue { - continue rest - } else { - IterEnd - } - }) -} - -///| -test "Bucket" { - let b0 : Bucket[_] = JustOne(0, 0) - inspect((b0.find(0), b0.find(1), b0.size()), content="(Some(0), None, 1)") - let b1 = b0.add(1, 1) - inspect((b1.find(0), b1.find(1), b1.size()), content="(Some(0), Some(1), 2)") - let b2 = b1.add(0, 2) - inspect((b2.find(0), b2.find(1), b2.size()), content="(Some(2), Some(1), 2)") - let b3 = b2.remove(0) - { - let b1 = b3.unwrap() - inspect((b1.find(0), b1.find(1)), content="(None, Some(1))") - } - let b4 = b2.remove(1) - let b1 = b4.unwrap() // b4 ? --> return Option instead of Result - inspect((b1.find(0), b1.find(1)), content="(Some(2), None)") -} - -///| -test "Bucket::iter" { - let b : Bucket[_] = More(0, 0, More(1, 1, JustOne(31, 31))) - let buf = StringBuilder::new(size_hint=0) - let mut is_first = true - b.each((k, v) => { - if is_first { - is_first = false - } else { - buf.write_string(", ") - } - buf.write_string("\{k} => \{v}") - }) - inspect(buf.to_string(), content="0 => 0, 1 => 1, 31 => 31") -} - -///| -test "Bucket::iter" { - let b : Bucket[_] = More(0, 0, More(1, 1, JustOne(31, 31))) - let buf = StringBuilder::new(size_hint=0) - let mut is_first = true - b - .iter() - .each(kv => { - // weird syntax conventions that - // braces needed here - if is_first { - is_first = false - } else { - buf.write_string(", ") - } - buf.write_string("\{kv.0} => \{kv.1}") - }) - inspect(buf.to_string(), content="0 => 0, 1 => 1, 31 => 31") -} - -///| -/// Fold over the bucket from left to right, applying a function to each key-value pair. -/// -fn[K, V, A] Bucket::foldl_with_key( - self : Bucket[K, V], - f : (A, K, V) -> A raise?, - init~ : A -) -> A raise? { - match self { - JustOne(k, v) => f(init, k, v) - More(k, v, rest) => rest.foldl_with_key(init=f(init, k, v), f) - } -} - -///| -test "foldl_with_key" { - let bucket = More("a", "a", More("b", "b", JustOne("c", "c"))) - let result = bucket.foldl_with_key(init="", (init, _k, v) => init + v) - assert_eq(result, "abc") -} - -///| -fn[K, V, A] Bucket::map_with_key( - self : Bucket[K, V], - f : (K, V) -> A raise? -) -> Bucket[K, A] raise? { - match self { - JustOne(k, v) => JustOne(k, f(k, v)) - More(k, v, rest) => More(k, f(k, v), rest.map_with_key(f)) - } -} - -///| -test "Bucket::map_with_key" { - let bucket = More(1, 10, More(2, 20, JustOne(3, 30))) - let mapped = bucket.map_with_key((k, v) => "\{k}:\{v}") - assert_eq(mapped.find(1), Some("1:10")) - assert_eq(mapped.find(2), Some("2:20")) - assert_eq(mapped.find(3), Some("3:30")) -} - -///| -fn[K, V, A] Bucket::map( - self : Bucket[K, V], - f : (V) -> A raise? -) -> Bucket[K, A] raise? { - self.map_with_key((_k, v) => f(v)) -} - -///| -test "Bucket::map" { - let bucket = More(1, 10, More(2, 20, JustOne(3, 30))) - let mapped = bucket.map(v => v * 2) - assert_eq(mapped.find(1), Some(20)) - assert_eq(mapped.find(2), Some(40)) - assert_eq(mapped.find(3), Some(60)) -} diff --git a/bundled-core/immut/hashmap/hashmap.mbti b/bundled-core/immut/hashmap/hashmap.mbti deleted file mode 100644 index 5f9d1ee..0000000 --- a/bundled-core/immut/hashmap/hashmap.mbti +++ /dev/null @@ -1,55 +0,0 @@ -package "moonbitlang/core/immut/hashmap" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[K : Eq + Hash, V] from_array(Array[(K, V)]) -> T[K, V] - -fn[K : Eq + Hash, V] from_iter(Iter[(K, V)]) -> T[K, V] - -fn[K, V] new() -> T[K, V] - -fn[K : Eq + Hash, V] of(FixedArray[(K, V)]) -> T[K, V] - -fn[K, V] singleton(K, V) -> T[K, V] - -// Types and methods -type T[K, V] -fn[K : Eq + Hash, V] T::add(Self[K, V], K, V) -> Self[K, V] -fn[K : Eq + Hash, V] T::contains(Self[K, V], K) -> Bool -fn[K : Eq + Hash, V] T::difference(Self[K, V], Self[K, V]) -> Self[K, V] -fn[K, V] T::each(Self[K, V], (K, V) -> Unit raise?) -> Unit raise? -#deprecated -fn[K, V] T::elems(Self[K, V]) -> Iter[V] -fn[K : Eq + Hash, V] T::filter(Self[K, V], (V) -> Bool raise?) -> Self[K, V] raise? -#deprecated -fn[K : Eq + Hash, V] T::find(Self[K, V], K) -> V? -fn[K, V, A] T::fold(Self[K, V], init~ : A, (A, V) -> A raise?) -> A raise? -fn[K, V, A] T::fold_with_key(Self[K, V], init~ : A, (A, K, V) -> A raise?) -> A raise? -fn[K : Eq + Hash, V] T::get(Self[K, V], K) -> V? -fn[K : Eq + Hash, V] T::intersection(Self[K, V], Self[K, V]) -> Self[K, V] -fn[K : Eq + Hash, V] T::intersection_with(Self[K, V], Self[K, V], (K, V, V) -> V raise?) -> Self[K, V] raise? -fn[K, V] T::iter(Self[K, V]) -> Iter[(K, V)] -fn[K, V] T::iter2(Self[K, V]) -> Iter2[K, V] -fn[K, V] T::keys(Self[K, V]) -> Iter[K] -fn[K : Eq + Hash, V, A] T::map(Self[K, V], (V) -> A raise?) -> Self[K, A] raise? -fn[K : Eq + Hash, V, A] T::map_with_key(Self[K, V], (K, V) -> A raise?) -> Self[K, A] raise? -#deprecated -fn[K : Eq + Hash, V] T::op_get(Self[K, V], K) -> V? -fn[K : Eq + Hash, V] T::remove(Self[K, V], K) -> Self[K, V] -fn[K, V] T::size(Self[K, V]) -> Int -fn[K, V] T::to_array(Self[K, V]) -> Array[(K, V)] -fn[K : Eq + Hash, V] T::union(Self[K, V], Self[K, V]) -> Self[K, V] -fn[K : Eq + Hash, V] T::union_with(Self[K, V], Self[K, V], (K, V, V) -> V raise?) -> Self[K, V] raise? -fn[K, V] T::values(Self[K, V]) -> Iter[V] -impl[K : Eq + Hash, V : Eq] Eq for T[K, V] -impl[K : Hash, V : Hash] Hash for T[K, V] -impl[K : Show, V : Show] Show for T[K, V] -impl[K : Eq + Hash + @quickcheck.Arbitrary, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K, V] - -// Type aliases - -// Traits - diff --git a/bundled-core/immut/hashmap/moon.pkg.json b/bundled-core/immut/hashmap/moon.pkg.json index 88e4619..15101b9 100644 --- a/bundled-core/immut/hashmap/moon.pkg.json +++ b/bundled-core/immut/hashmap/moon.pkg.json @@ -5,6 +5,7 @@ "moonbitlang/core/tuple", "moonbitlang/core/quickcheck", "moonbitlang/core/immut/internal/sparse_array", + "moonbitlang/core/immut/internal/path", "moonbitlang/core/list" ], "test-import": [ diff --git a/bundled-core/immut/hashmap/pattern_test.mbt b/bundled-core/immut/hashmap/pattern_test.mbt index f45ce7d..4f4e1a0 100644 --- a/bundled-core/immut/hashmap/pattern_test.mbt +++ b/bundled-core/immut/hashmap/pattern_test.mbt @@ -22,8 +22,8 @@ test "pattern" { let { "name"? : name, "age"? : age, "is_human"? : is_human, .. } = m inspect( (name, age, is_human), - content= + content=( #|(Some("John Doe"), Some("43"), Some("true")) - , + ), ) } diff --git a/bundled-core/immut/hashmap/pkg.generated.mbti b/bundled-core/immut/hashmap/pkg.generated.mbti new file mode 100644 index 0000000..4c4cb8e --- /dev/null +++ b/bundled-core/immut/hashmap/pkg.generated.mbti @@ -0,0 +1,62 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/immut/hashmap" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type HashMap[K, V] +fn[K : Eq + Hash, V] HashMap::add(Self[K, V], K, V) -> Self[K, V] +fn[K : Eq + Hash, V] HashMap::contains(Self[K, V], K) -> Bool +fn[K : Eq, V] HashMap::difference(Self[K, V], Self[K, V]) -> Self[K, V] +fn[K, V] HashMap::each(Self[K, V], (K, V) -> Unit raise?) -> Unit raise? +#deprecated +fn[K, V] HashMap::filter(Self[K, V], (V) -> Bool raise?) -> Self[K, V] raise? +fn[K, V] HashMap::filter_with_key(Self[K, V], (K, V) -> Bool raise?) -> Self[K, V] raise? +#deprecated +fn[K, V, A] HashMap::fold(Self[K, V], init~ : A, (A, V) -> A raise?) -> A raise? +fn[K, V, A] HashMap::fold_with_key(Self[K, V], init~ : A, (A, K, V) -> A raise?) -> A raise? +#as_free_fn +fn[K : Eq + Hash, V] HashMap::from_array(Array[(K, V)]) -> Self[K, V] +#as_free_fn +fn[K : Eq + Hash, V] HashMap::from_iter(Iter[(K, V)]) -> Self[K, V] +#alias(find, deprecated) +fn[K : Eq + Hash, V] HashMap::get(Self[K, V], K) -> V? +fn[K : Eq, V] HashMap::intersection(Self[K, V], Self[K, V]) -> Self[K, V] +fn[K : Eq, V] HashMap::intersection_with(Self[K, V], Self[K, V], (K, V, V) -> V raise?) -> Self[K, V] raise? +fn[K, V] HashMap::iter(Self[K, V]) -> Iter[(K, V)] +fn[K, V] HashMap::iter2(Self[K, V]) -> Iter2[K, V] +fn[K, V] HashMap::keys(Self[K, V]) -> Iter[K] +#deprecated +fn[K, V, A] HashMap::map(Self[K, V], (V) -> A raise?) -> Self[K, A] raise? +fn[K, V, A] HashMap::map_with_key(Self[K, V], (K, V) -> A raise?) -> Self[K, A] raise? +#as_free_fn +fn[K, V] HashMap::new() -> Self[K, V] +#as_free_fn +fn[K : Eq + Hash, V] HashMap::of(FixedArray[(K, V)]) -> Self[K, V] +#deprecated +fn[K : Eq + Hash, V] HashMap::op_get(Self[K, V], K) -> V? +fn[K : Eq + Hash, V] HashMap::remove(Self[K, V], K) -> Self[K, V] +#as_free_fn +fn[K : Hash, V] HashMap::singleton(K, V) -> Self[K, V] +fn[K, V] HashMap::size(Self[K, V]) -> Int +fn[K, V] HashMap::to_array(Self[K, V]) -> Array[(K, V)] +fn[K : Eq, V] HashMap::union(Self[K, V], Self[K, V]) -> Self[K, V] +fn[K : Eq, V] HashMap::union_with(Self[K, V], Self[K, V], (K, V, V) -> V raise?) -> Self[K, V] raise? +#alias(elems, deprecated) +fn[K, V] HashMap::values(Self[K, V]) -> Iter[V] +impl[K : Eq, V : Eq] Eq for HashMap[K, V] +impl[K : Hash, V : Hash] Hash for HashMap[K, V] +impl[K : Show, V : Show] Show for HashMap[K, V] +impl[K : Eq + Hash + @quickcheck.Arbitrary, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashMap[K, V] + +// Type aliases +pub typealias HashMap as T + +// Traits + diff --git a/bundled-core/immut/hashmap/types.mbt b/bundled-core/immut/hashmap/types.mbt index 9154dc8..7867e0a 100644 --- a/bundled-core/immut/hashmap/types.mbt +++ b/bundled-core/immut/hashmap/types.mbt @@ -13,18 +13,20 @@ // limitations under the License. ///| -/// A bucket is a non-empty linked list of key-value pair, -/// used to resolve hash collision in HAMT -priv enum Bucket[K, V] { - JustOne(K, V) // must be non-empty - More(K, V, Bucket[K, V]) -} +typealias @path.Path ///| -/// An immutable hash-map data structure -enum T[K, V] { - Empty - Leaf(K, V) // optimize for the case of no collision - Collision(Bucket[K, V]) // use a list of buckets to resolve collision - Branch(@sparse_array.SparseArray[T[K, V]]) +/// An non-empty immutable hash set data structure +priv enum Node[K, V] { + Flat(K, V, Path) + Leaf(K, V, @list.List[(K, V)]) // use a list of buckets to resolve collision + /// number of all its leaf > 1. If equals 1, it should be represented as `Flat` + Branch(@sparse_array.SparseArray[Node[K, V]]) } + +///| +struct HashMap[K, V](Node[K, V]?) derive(Eq) + +///| +#deprecated("Use `HashMap` instead of `T`") +pub typealias HashMap as T diff --git a/bundled-core/immut/hashset/HAMT.mbt b/bundled-core/immut/hashset/HAMT.mbt index e433181..3fbc381 100644 --- a/bundled-core/immut/hashset/HAMT.mbt +++ b/bundled-core/immut/hashset/HAMT.mbt @@ -28,14 +28,15 @@ // - ///| -pub fn[A] new() -> T[A] { +#as_free_fn +pub fn[A] HashSet::new() -> HashSet[A] { None } ///| /// Lookup a value from the hash set -pub fn[A : Eq + Hash] contains(self : T[A], key : A) -> Bool { - self.inner() is Some(node) && node.contains(key, @path.of(key)) +pub fn[A : Eq + Hash] contains(self : HashSet[A], key : A) -> Bool { + self.0 is Some(node) && node.contains(key, @path.of(key)) } ///| @@ -108,8 +109,8 @@ fn[A : Eq] add_with_path(self : Node[A], key : A, path : Path) -> Node[A] { ///| /// Add a key to the hashset. -pub fn[A : Eq + Hash] add(self : T[A], key : A) -> T[A] { - match self.inner() { +pub fn[A : Eq + Hash] add(self : HashSet[A], key : A) -> HashSet[A] { + match self.0 { None => Some(Flat(key, @path.of(key))) Some(node) => Some(node.add_with_path(key, @path.of(key))) } @@ -117,8 +118,8 @@ pub fn[A : Eq + Hash] add(self : T[A], key : A) -> T[A] { ///| /// Remove an element from a set -pub fn[A : Eq + Hash] remove(self : T[A], key : A) -> T[A] { - match self.inner() { +pub fn[A : Eq + Hash] remove(self : HashSet[A], key : A) -> HashSet[A] { + match self.0 { None => None Some(node) => node.remove_with_path(key, @path.of(key)) } @@ -170,7 +171,7 @@ fn[A : Eq] remove_with_path(self : Node[A], key : A, path : Path) -> Node[A]? { /// Calculate the size of a set. /// /// WARNING: this operation is `O(N)` in set size -pub fn[A] size(self : T[A]) -> Int { +pub fn[A] size(self : HashSet[A]) -> Int { fn node_size(node) { match node { Leaf(_, bucket) => 1 + bucket.length() @@ -184,7 +185,7 @@ pub fn[A] size(self : T[A]) -> Int { } } - match self.inner() { + match self.0 { None => 0 Some(node) => node_size(node) } @@ -192,7 +193,10 @@ pub fn[A] size(self : T[A]) -> Int { ///| /// Union two hashsets -pub fn[K : Eq] T::union(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Eq] HashSet::union( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { fn go(node1, node2) { match (node1, node2) { (node, Flat(key, path)) | (Flat(key, path), node) => @@ -202,7 +206,7 @@ pub fn[K : Eq] T::union(self : T[K], other : T[K]) -> T[K] { (Leaf(key1, bucket1), Leaf(key2, bucket2)) => { let keys1 = bucket1.add(key1) let keys2 = bucket2.add(key2) - match keys1.filter(k => not(keys2.contains(k))) { + match keys1.filter(k => !keys2.contains(k)) { Empty => node2 More(head, tail~) => Leaf(key2, bucket2 + tail.add(head)) } @@ -211,7 +215,7 @@ pub fn[K : Eq] T::union(self : T[K], other : T[K]) -> T[K] { } } - match (self.inner(), other.inner()) { + match (self.0, other.0) { (None, x) | (x, None) => x (Some(a), Some(b)) => Some(go(a, b)) } @@ -219,7 +223,10 @@ pub fn[K : Eq] T::union(self : T[K], other : T[K]) -> T[K] { ///| /// Intersect two hashsets -pub fn[K : Eq] T::intersection(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Eq] HashSet::intersection( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { fn go(node1, node2) { match (node1, node2) { (node, Flat(key, path) as flat) | (Flat(key, path) as flat, node) => @@ -247,7 +254,7 @@ pub fn[K : Eq] T::intersection(self : T[K], other : T[K]) -> T[K] { } } - match (self.inner(), other.inner()) { + match (self.0, other.0) { (None, _) | (_, None) => None (Some(a), Some(b)) => go(a, b) } @@ -255,7 +262,10 @@ pub fn[K : Eq] T::intersection(self : T[K], other : T[K]) -> T[K] { ///| /// Difference of two hashsets: elements in `self` but not in `other` -pub fn[K : Eq] T::difference(self : T[K], other : T[K]) -> T[K] { +pub fn[K : Eq] HashSet::difference( + self : HashSet[K], + other : HashSet[K], +) -> HashSet[K] { fn go(node1 : Node[_], node2) { match (node1, node2) { (node, Flat(k, path)) => node.remove_with_path(k, path) @@ -275,7 +285,7 @@ pub fn[K : Eq] T::difference(self : T[K], other : T[K]) -> T[K] { (Leaf(key1, bucket1), Leaf(key2, bucket2)) => { let keys1 = bucket1.add(key1) let keys2 = bucket2.add(key2) - match keys1.filter(k => not(keys2.contains(k))) { + match keys1.filter(k => !keys2.contains(k)) { Empty => None More(head, tail~) => Some(Leaf(head, tail)) } @@ -284,7 +294,7 @@ pub fn[K : Eq] T::difference(self : T[K], other : T[K]) -> T[K] { } } - match (self.inner(), other.inner()) { + match (self.0, other.0) { (None, _) => None (_, None) => self (Some(a), Some(b)) => go(a, b) @@ -293,13 +303,13 @@ pub fn[K : Eq] T::difference(self : T[K], other : T[K]) -> T[K] { ///| /// Returns true if the hash set is empty. -pub fn[A] is_empty(self : T[A]) -> Bool { - self.inner() is None +pub fn[A] is_empty(self : HashSet[A]) -> Bool { + self.0 is None } ///| /// Iterate through the elements in a hash set -pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { +pub fn[A] each(self : HashSet[A], f : (A) -> Unit raise?) -> Unit raise? { fn go(node) raise? { match node { Leaf(k, bucket) => { @@ -311,7 +321,7 @@ pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { } } - match self.inner() { + match self.0 { None => () Some(node) => go(node) } @@ -319,7 +329,7 @@ pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { ///| /// Converted to Iter -pub fn[A] iter(self : T[A]) -> Iter[A] { +pub fn[A] iter(self : HashSet[A]) -> Iter[A] { fn go(node) -> Iter[A] { match node { Leaf(k, bucket) => Iter::singleton(k) + bucket.iter() @@ -328,24 +338,26 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { } } - match self.inner() { + match self.0 { None => Iter::empty() Some(node) => go(node) } } ///| -pub fn[A : Eq + Hash] from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A : Eq + Hash] HashSet::from_iter(iter : Iter[A]) -> HashSet[A] { iter.fold(init=new(), (s, e) => s.add(e)) } ///| -pub impl[A : Show] Show for T[A] with output(self, logger) { +pub impl[A : Show] Show for HashSet[A] with output(self, logger) { logger.write_iter(self.iter(), prefix="@immut/hashset.of([", suffix="])") } ///| -pub fn[A : Eq + Hash] from_array(arr : Array[A]) -> T[A] { +#as_free_fn +pub fn[A : Eq + Hash] HashSet::from_array(arr : Array[A]) -> HashSet[A] { loop (arr.length(), new()) { (0, set) => set (n, set) => { @@ -356,7 +368,8 @@ pub fn[A : Eq + Hash] from_array(arr : Array[A]) -> T[A] { } ///| -pub fn[A : Eq + Hash] of(arr : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A : Eq + Hash] HashSet::of(arr : FixedArray[A]) -> HashSet[A] { loop (arr.length(), new()) { (0, set) => set (n, set) => { @@ -367,12 +380,12 @@ pub fn[A : Eq + Hash] of(arr : FixedArray[A]) -> T[A] { } ///| -pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { +pub impl[A : Hash] Hash for HashSet[A] with hash_combine(self, hasher) { hasher.combine(self.iter().fold(init=0, (x, y) => x ^ y.hash())) } ///| -impl[A : Eq] Eq for Node[A] with op_equal(self, other) { +impl[A : Eq] Eq for Node[A] with equal(self, other) { match (self, other) { (Leaf(x, xs), Leaf(y, ys)) => xs.length() == ys.length() && @@ -388,10 +401,9 @@ impl[A : Eq] Eq for Node[A] with op_equal(self, other) { } ///| -pub impl[K : Eq + Hash + @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K] with arbitrary( - size, - rs -) { +pub impl[K : Eq + Hash + @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashSet[ + K, +] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } diff --git a/bundled-core/immut/hashset/HAMT_test.mbt b/bundled-core/immut/hashset/HAMT_test.mbt index 960c55f..f8b7f24 100644 --- a/bundled-core/immut/hashset/HAMT_test.mbt +++ b/bundled-core/immut/hashset/HAMT_test.mbt @@ -88,23 +88,23 @@ test "@hashset.is_empty" { ///| test "@hashset.size" { let set = @hashset.new() - assert_eq(set.size(), 0) + inspect(set.size(), content="0") let set = set.add(1) - assert_eq(set.size(), 1) + inspect(set.size(), content="1") let set = set.add(2) - assert_eq(set.size(), 2) + inspect(set.size(), content="2") let set = set.remove(1) - assert_eq(set.size(), 1) + inspect(set.size(), content="1") } ///| test "@hashset.is_empty" { let set = @hashset.new() - assert_eq(set.is_empty(), true) + inspect(set.is_empty(), content="true") let set = set.add(1) - assert_eq(set.is_empty(), false) + inspect(set.is_empty(), content="false") let set = set.remove(1) - assert_eq(set.is_empty(), true) + inspect(set.is_empty(), content="true") } ///| @@ -140,7 +140,7 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @hashset.T[Int] = @hashset.from_iter(Iter::empty()) + let pq : @hashset.HashSet[Int] = @hashset.from_iter(Iter::empty()) inspect(pq, content="@immut/hashset.of([])") } @@ -187,15 +187,15 @@ test "hashset equality" { let set1 = @hashset.of([1, 2]) let set2 = @hashset.of([2, 1]) inspect(set1 == set2, content="true") - let empty1 : @hashset.T[Unit] = @hashset.new() - let empty2 : @hashset.T[Unit] = @hashset.new() + let empty1 : @hashset.HashSet[Unit] = @hashset.new() + let empty2 : @hashset.HashSet[Unit] = @hashset.new() inspect(empty1 == empty2, content="true") } ///| test "arbitrary implementation for hashset" { // Use @quickcheck.gen to generate an arbitrary hashset of Int - let set : @hashset.T[Int] = @quickcheck.gen() + let set : @hashset.HashSet[Int] = @quickcheck.gen() // Make sure the generated set is valid by converting it to array and back let arr = set.iter().to_array() let set2 = @hashset.from_array(arr) @@ -247,7 +247,7 @@ test "HAMT::union with branch" { } ///| -type MyString String +struct MyString(String) ///| impl Hash for MyString with hash_combine(_self, hasher) { @@ -255,31 +255,31 @@ impl Hash for MyString with hash_combine(_self, hasher) { } ///| -impl Eq for MyString with op_equal(self, other) { - self.inner() == other.inner() +impl Eq for MyString with equal(self, other) { + self.0 == other.0 } ///| impl Show for MyString with to_string(self) { - self.inner() + self.0 } ///| impl Show for MyString with output(self, logger) { - logger.write_string(self.inner()) + logger.write_string(self.0) } ///| test "HAMT::remove_with_hash collision" { let set = @hashset.of([MyString("a"), MyString("b")]) let set1 = set.remove(MyString("c")) - assert_eq(set1.size(), 2) + inspect(set1.size(), content="2") let set2 = set.remove(MyString("a")) assert_eq(set2.contains(MyString("a")), false) assert_eq(set2.contains(MyString("b")), true) - assert_eq(set2.size(), 1) + inspect(set2.size(), content="1") let set3 = set2.remove(MyString("b")) - assert_eq(set3.size(), 0) + inspect(set3.size(), content="0") } ///| @@ -301,8 +301,8 @@ test "HAMT::union all cases" { assert_eq(empty.union(leaf).contains(1), true) assert_eq(leaf.union(branch).contains(2), true) let u1 = branch.union(branch) - assert_eq(u1.contains(1), true) - assert_eq(u1.contains(2), true) + inspect(u1.contains(1), content="true") + inspect(u1.contains(2), content="true") let collision2 = @hashset.of([MyString("b"), MyString("c")]) let u2 = collision.union(collision2) assert_eq(u2.contains(MyString("a")), true) @@ -315,9 +315,9 @@ test "HAMT::union leaf to non-overlapping map" { let leaf = @hashset.of([42]) let other = @hashset.of([1, 2]) let u = leaf.union(other) - assert_eq(u.contains(42), true) - assert_eq(u.contains(1), true) - assert_eq(u.contains(2), true) + inspect(u.contains(42), content="true") + inspect(u.contains(1), content="true") + inspect(u.contains(2), content="true") } ///| diff --git a/bundled-core/immut/hashset/README.mbt.md b/bundled-core/immut/hashset/README.mbt.md new file mode 100644 index 0000000..e9e4437 --- /dev/null +++ b/bundled-core/immut/hashset/README.mbt.md @@ -0,0 +1,311 @@ +# Immutable HashSet Package Documentation + +This package provides an immutable hash-based set implementation using Hash Array Mapped Trie (HAMT) data structure. Unlike the mutable `Set` type, this provides persistent data structures that create new versions when modified while sharing structure efficiently. + +## Creating Immutable Sets + +Create immutable sets using various methods: + +```moonbit +test "creating immutable sets" { + // Empty set + let empty : @hashset.HashSet[Int] = @hashset.new() + inspect(empty.size(), content="0") + inspect(empty.is_empty(), content="true") + + // From array + let from_array_result = @hashset.from_array([1, 2, 3, 2, 1]) // Duplicates removed + inspect(from_array_result.size(), content="3") + + // From fixed array + let from_fixed = @hashset.of([10, 20, 30]) + inspect(from_fixed.size(), content="3") + + // From iterator + let from_iter = @hashset.from_iter([40, 50, 60].iter()) + inspect(from_iter.size(), content="3") +} +``` + +## Immutable Operations + +All operations return new sets without modifying the original: + +```moonbit +test "immutable operations" { + let original = @hashset.from_array([1, 2, 3]) + + // Add element - returns new set + let with_four = original.add(4) + inspect(original.size(), content="3") // Original unchanged + inspect(with_four.size(), content="4") // New set has additional element + inspect(with_four.contains(4), content="true") + + // Remove element - returns new set + let without_two = original.remove(2) + inspect(original.size(), content="3") // Original unchanged + inspect(without_two.size(), content="2") // New set missing element + inspect(without_two.contains(2), content="false") + + // Original set remains unmodified + inspect(original.contains(2), content="true") +} +``` + +## Set Operations + +Perform mathematical set operations immutably: + +```moonbit +test "set operations" { + let set1 = @hashset.from_array([1, 2, 3, 4]) + let set2 = @hashset.from_array([3, 4, 5, 6]) + + // Union - all elements from both sets + let union_set = set1.union(set2) + inspect(union_set.size(), content="6") // [1, 2, 3, 4, 5, 6] + + // Intersection - common elements only + let intersection_set = set1.intersection(set2) + inspect(intersection_set.size(), content="2") // [3, 4] + + // Difference - elements in first but not second + let difference_set = set1.difference(set2) + inspect(difference_set.size(), content="2") // [1, 2] + + // All original sets remain unchanged + inspect(set1.size(), content="4") + inspect(set2.size(), content="4") +} +``` + +## Membership and Queries + +Test membership and query the set: + +```moonbit +test "membership and queries" { + let numbers = @hashset.from_array([10, 20, 30, 40, 50]) + + // Membership testing + inspect(numbers.contains(30), content="true") + inspect(numbers.contains(35), content="false") + + // Size and emptiness + inspect(numbers.size(), content="5") + inspect(numbers.is_empty(), content="false") + + // Iterate over elements + let mut sum = 0 + numbers.each(fn(x) { sum = sum + x }) + inspect(sum, content="150") // 10+20+30+40+50 +} +``` + +## Structural Sharing + +Immutable sets share structure efficiently: + +```moonbit +test "structural sharing" { + let base_set = @hashset.from_array([1, 2, 3, 4, 5]) + + // Adding elements creates new sets that share structure + let set_with_six = base_set.add(6) + let set_with_seven = base_set.add(7) + let set_with_eight = base_set.add(8) + + // All sets share the common structure [1, 2, 3, 4, 5] + inspect(base_set.size(), content="5") + inspect(set_with_six.size(), content="6") + inspect(set_with_seven.size(), content="6") + inspect(set_with_eight.size(), content="6") + + // Each retains the base elements + inspect(set_with_six.contains(3), content="true") + inspect(set_with_seven.contains(3), content="true") + inspect(set_with_eight.contains(3), content="true") +} +``` + +## Filtering and Transformation + +Transform sets while maintaining immutability: + +```moonbit +test "filtering and transformation" { + let numbers = @hashset.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + + // Create filtered sets manually (no built-in filter/map) + let evens = @hashset.from_array([2, 4, 6, 8, 10]) + inspect(evens.size(), content="5") + + let doubled = @hashset.from_array([2, 4, 6, 8, 10, 12, 14, 16, 18, 20]) + inspect(doubled.size(), content="10") + inspect(doubled.contains(6), content="true") // 3 * 2 = 6 + + // Original set unchanged + inspect(numbers.size(), content="10") + inspect(numbers.contains(3), content="true") +} +``` + +## Combining Sets + +Build complex sets from simpler ones: + +```moonbit +test "combining sets" { + let small_primes = @hashset.from_array([2, 3, 5, 7]) + let small_evens = @hashset.from_array([2, 4, 6, 8]) + let small_odds = @hashset.from_array([1, 3, 5, 7, 9]) + + // Combine multiple sets + let all_small = small_primes.union(small_evens).union(small_odds) + inspect(all_small.size(), content="9") // [1, 2, 3, 4, 5, 6, 7, 8, 9] + + // Find intersection of primes and odds + let odd_primes = small_primes.intersection(small_odds) + inspect(odd_primes.size(), content="3") // [3, 5, 7] + + // All original sets remain unchanged + inspect(small_primes.size(), content="4") + inspect(small_evens.size(), content="4") + inspect(small_odds.size(), content="5") +} +``` + +## Comparison with Mutable Sets + +Key differences from mutable sets: + +```moonbit +test "immutable vs mutable comparison" { + // Immutable set - creates new instances + let immut_set = @hashset.from_array([1, 2, 3]) + let immut_with_four = immut_set.add(4) + + // Both sets exist independently + inspect(immut_set.contains(4), content="false") // Original doesn't have 4 + inspect(immut_with_four.contains(4), content="true") // New one has 4 + + // This demonstrates the immutable nature - both sets exist + inspect(immut_set.size(), content="3") + inspect(immut_with_four.size(), content="4") +} +``` + +## Advanced Operations + +More complex set operations: + +```moonbit +test "advanced operations" { + let set1 = @hashset.from_array([1, 2, 3, 4, 5]) + let set2 = @hashset.from_array([4, 5, 6, 7, 8]) + + // Symmetric difference (elements in either but not both) + let sym_diff = set1.difference(set2).union(set2.difference(set1)) + inspect(sym_diff.size(), content="6") // [1, 2, 3, 6, 7, 8] + + // Test intersection + let intersection = set1.intersection(set2) + inspect(intersection.size(), content="2") // [4, 5] + + // Test difference + let diff = set1.difference(set2) + inspect(diff.size(), content="3") // [1, 2, 3] +} +``` + +## Performance Benefits + +Immutable sets provide several performance advantages: + +```moonbit +test "performance benefits" { + let base = @hashset.from_array([1, 2, 3, 4, 5]) + + // Multiple derived sets share structure + let derived1 = base.add(6) + let derived2 = base.add(7) + let derived3 = base.remove(1) + + // Efficient operations due to structural sharing + inspect(derived1.size(), content="6") + inspect(derived2.size(), content="6") + inspect(derived3.size(), content="4") + + // Union of derived sets is efficient + let combined = derived1.union(derived2) + inspect(combined.size(), content="7") // [1, 2, 3, 4, 5, 6, 7] +} +``` + +## Use Cases + +Immutable sets are particularly useful for: + +1. **Functional programming**: Pure functions that don't modify data +2. **Concurrent programming**: Safe sharing between threads +3. **Undo/redo systems**: Keep history of set states +4. **Caching**: Cache intermediate results without fear of modification +5. **Configuration management**: Immutable configuration sets + +## Best Practices + +### 1. Prefer Immutable for Functional Code + +```moonbit +test "functional programming style" { + fn process_numbers(_numbers : @hashset.HashSet[Int]) -> @hashset.HashSet[Int] { + // Manually create processed set (no built-in filter/map) + let positive_squares = @hashset.from_array([1, 4, 9]) // Squares of 1, 2, 3 + positive_squares.add(1) // Add the number 1 (though 1 already exists) + } + + let input = @hashset.from_array([-2, -1, 0, 1, 2, 3]) + let result = process_numbers(input) + + // Input unchanged, result is new set + inspect(input.size(), content="6") + inspect(result.contains(1), content="true") // Has 1 + inspect(result.contains(4), content="true") // Has 4 + inspect(result.contains(9), content="true") // Has 9 +} +``` + +### 2. Use for Configuration and State + +```moonbit +test "configuration usage" { + let base_config = @hashset.from_array(["feature1", "feature2", "feature3"]) + + fn enable_feature(config : @hashset.HashSet[String], feature : String) -> @hashset.HashSet[String] { + config.add(feature) + } + + fn disable_feature(config : @hashset.HashSet[String], feature : String) -> @hashset.HashSet[String] { + config.remove(feature) + } + + // Create different configurations + let dev_config = enable_feature(base_config, "debug_mode") + let prod_config = disable_feature(base_config, "feature3") + + inspect(base_config.size(), content="3") // Base unchanged + inspect(dev_config.size(), content="4") // Has debug_mode + inspect(prod_config.size(), content="2") // Missing feature3 +} +``` + +## Memory Efficiency + +The HAMT structure provides: + +- **Logarithmic depth**: O(log n) operations +- **Structural sharing**: Common subtrees shared between versions +- **Compact representation**: Efficient memory usage +- **Cache-friendly access patterns**: Good locality of reference + +The immutable hashset package provides efficient, thread-safe, and functionally pure set operations for MoonBit applications requiring persistent data structures. diff --git a/bundled-core/immut/hashset/hashset.mbti b/bundled-core/immut/hashset/hashset.mbti deleted file mode 100644 index 0380cf2..0000000 --- a/bundled-core/immut/hashset/hashset.mbti +++ /dev/null @@ -1,36 +0,0 @@ -package "moonbitlang/core/immut/hashset" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[A : Eq + Hash] from_array(Array[A]) -> T[A] - -fn[A : Eq + Hash] from_iter(Iter[A]) -> T[A] - -fn[A] new() -> T[A] - -fn[A : Eq + Hash] of(FixedArray[A]) -> T[A] - -// Types and methods -type T[A] -fn[A : Eq + Hash] T::add(Self[A], A) -> Self[A] -fn[A : Eq + Hash] T::contains(Self[A], A) -> Bool -fn[K : Eq] T::difference(Self[K], Self[K]) -> Self[K] -fn[A] T::each(Self[A], (A) -> Unit raise?) -> Unit raise? -fn[K : Eq] T::intersection(Self[K], Self[K]) -> Self[K] -fn[A] T::is_empty(Self[A]) -> Bool -fn[A] T::iter(Self[A]) -> Iter[A] -fn[A : Eq + Hash] T::remove(Self[A], A) -> Self[A] -fn[A] T::size(Self[A]) -> Int -fn[K : Eq] T::union(Self[K], Self[K]) -> Self[K] -impl[A : Eq] Eq for T[A] -impl[A : Hash] Hash for T[A] -impl[A : Show] Show for T[A] -impl[K : Eq + Hash + @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K] - -// Type aliases - -// Traits - diff --git a/bundled-core/immut/hashset/pkg.generated.mbti b/bundled-core/immut/hashset/pkg.generated.mbti new file mode 100644 index 0000000..630a690 --- /dev/null +++ b/bundled-core/immut/hashset/pkg.generated.mbti @@ -0,0 +1,41 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/immut/hashset" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type HashSet[A] +fn[A : Eq + Hash] HashSet::add(Self[A], A) -> Self[A] +fn[A : Eq + Hash] HashSet::contains(Self[A], A) -> Bool +fn[K : Eq] HashSet::difference(Self[K], Self[K]) -> Self[K] +fn[A] HashSet::each(Self[A], (A) -> Unit raise?) -> Unit raise? +#as_free_fn +fn[A : Eq + Hash] HashSet::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A : Eq + Hash] HashSet::from_iter(Iter[A]) -> Self[A] +fn[K : Eq] HashSet::intersection(Self[K], Self[K]) -> Self[K] +fn[A] HashSet::is_empty(Self[A]) -> Bool +fn[A] HashSet::iter(Self[A]) -> Iter[A] +#as_free_fn +fn[A] HashSet::new() -> Self[A] +#as_free_fn +fn[A : Eq + Hash] HashSet::of(FixedArray[A]) -> Self[A] +fn[A : Eq + Hash] HashSet::remove(Self[A], A) -> Self[A] +fn[A] HashSet::size(Self[A]) -> Int +fn[K : Eq] HashSet::union(Self[K], Self[K]) -> Self[K] +impl[A : Eq] Eq for HashSet[A] +impl[A : Hash] Hash for HashSet[A] +impl[A : Show] Show for HashSet[A] +impl[K : Eq + Hash + @quickcheck.Arbitrary] @quickcheck.Arbitrary for HashSet[K] + +// Type aliases +pub typealias HashSet as T + +// Traits + diff --git a/bundled-core/immut/hashset/types.mbt b/bundled-core/immut/hashset/types.mbt index 6bb81f6..056ecd9 100644 --- a/bundled-core/immut/hashset/types.mbt +++ b/bundled-core/immut/hashset/types.mbt @@ -19,10 +19,14 @@ typealias @path.Path /// An non-empty immutable hash set data structure priv enum Node[A] { Flat(A, Path) - Leaf(A, @list.T[A]) // use a list of buckets to resolve collision + Leaf(A, @list.List[A]) // use a list of buckets to resolve collision /// number of all its leaf > 1. If equals 1, it should be represented as `Flat` Branch(@sparse_array.SparseArray[Node[A]]) } ///| -type T[A] Node[A]? derive(Eq) +struct HashSet[A](Node[A]?) derive(Eq) + +///| +#deprecated("Use `Set` instead of `T`") +pub typealias HashSet as T diff --git a/bundled-core/immut/internal/path/README.mbt.md b/bundled-core/immut/internal/path/README.mbt.md new file mode 100644 index 0000000..5b316eb --- /dev/null +++ b/bundled-core/immut/internal/path/README.mbt.md @@ -0,0 +1,94 @@ +# Immutable Internal Path Package Documentation + +This package provides internal path utilities for immutable data structures, specifically for navigating Hash Array Mapped Trie (HAMT) structures. It is used internally by immutable collections to track positions within the tree structure. + +## Path Structure + +The `Path` type represents a position in a HAMT structure: + +```moonbit +test "path basics" { + // Create a path from a hashable value + let path = @path.of(42) + + // Check if this is the last level + let is_last = path.is_last() + inspect(is_last, content="false") // Single level path + + // Get the index at current level + let idx = path.idx() + inspect(idx >= 0, content="true") // Should be valid index +} +``` + +## Path Navigation + +Navigate through HAMT tree levels: + +```moonbit +test "path navigation" { + let initial_path = @path.of("test_key") + + // Move to next level in the tree + let next_path = initial_path.next() + + // Paths should be different + inspect(initial_path == next_path, content="false") + + // Check indices at different levels + let initial_idx = initial_path.idx() + let next_idx = next_path.idx() + + inspect(initial_idx >= 0, content="true") + inspect(next_idx >= 0, content="true") +} +``` + +## Path Construction + +Build paths for tree navigation: + +```moonbit +test "path construction" { + let base_path = @path.of(12345) + + // Push additional level information + let extended_path = base_path.push(7) + + // Extended path should be different + inspect(base_path == extended_path, content="false") + + // Check properties + let base_idx = base_path.idx() + let extended_idx = extended_path.idx() + + inspect(base_idx >= 0, content="true") + inspect(extended_idx >= 0, content="true") +} +``` + +## Internal Usage + +This package is used internally by: + +1. **HAMT implementations**: Navigate tree structures efficiently +2. **Immutable collections**: Track insertion and lookup paths +3. **Hash table operations**: Determine bucket positions +4. **Tree balancing**: Manage tree depth and structure + +## Technical Details + +The Path type: +- Encodes tree navigation information compactly +- Uses bit manipulation for efficient path operations +- Supports up to 32 levels of tree depth +- Provides O(1) path operations + +## Performance Characteristics + +- **Path creation**: O(1) from hash values +- **Navigation**: O(1) to move between levels +- **Index calculation**: O(1) bit operations +- **Memory usage**: Single UInt per path (very compact) + +This is an internal implementation detail used by immutable data structures and is not intended for direct use by application developers. diff --git a/bundled-core/immut/internal/path/path.mbt b/bundled-core/immut/internal/path/path.mbt index d88b716..9ad0570 100644 --- a/bundled-core/immut/internal/path/path.mbt +++ b/bundled-core/immut/internal/path/path.mbt @@ -23,7 +23,7 @@ /// /// For example, `Path::of(x) = 0b11_10000_10101_00100_11111_01010` /// represents segments as [0b_10000, 0b_10101, 0b_00100, 0b_11111, 0b_01010] -pub(all) type Path UInt derive(Eq) +pub(all) struct Path(UInt) derive(Eq) ///| const SEGMENT_LENGTH : Int = 5 diff --git a/bundled-core/immut/internal/path/path.mbti b/bundled-core/immut/internal/path/pkg.generated.mbti similarity index 78% rename from bundled-core/immut/internal/path/path.mbti rename to bundled-core/immut/internal/path/pkg.generated.mbti index 7486c83..b5267d1 100644 --- a/bundled-core/immut/internal/path/path.mbti +++ b/bundled-core/immut/internal/path/pkg.generated.mbti @@ -1,10 +1,13 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/immut/internal/path" // Values fn[A : Hash] of(A) -> Path +// Errors + // Types and methods -pub(all) type Path UInt +pub(all) struct Path(UInt) fn Path::idx(Self) -> Int fn Path::inner(Self) -> UInt fn Path::is_last(Self) -> Bool diff --git a/bundled-core/immut/internal/sparse_array/README.mbt.md b/bundled-core/immut/internal/sparse_array/README.mbt.md new file mode 100644 index 0000000..7d3e0f4 --- /dev/null +++ b/bundled-core/immut/internal/sparse_array/README.mbt.md @@ -0,0 +1,214 @@ +# Immutable Internal Sparse Array Package Documentation + +This package provides sparse array and bitset utilities used internally by immutable data structures. It implements efficient storage for arrays with gaps and bitset operations for tracking element presence. + +## Sparse Array Basics + +Create and manipulate sparse arrays: + +```moonbit +test "sparse array basics" { + // Empty sparse array + let empty_array : @sparse_array.SparseArray[String] = @sparse_array.empty() + inspect(empty_array.size(), content="0") + + // Singleton sparse array + let single = @sparse_array.singleton(5, "value") + inspect(single.size(), content="1") + + // Check if element exists + match single[5] { + Some(val) => inspect(val, content="value") + None => inspect(false, content="true") + } + + // Doubleton (two elements) + let double = @sparse_array.doubleton(2, "first", 7, "second") + inspect(double.size(), content="2") +} +``` + +## Sparse Array Operations + +Add, remove, and modify elements: + +```moonbit +test "sparse array operations" { + let arr = @sparse_array.singleton(3, 100) + + // Add new element + let with_new = arr.add(8, 200) + inspect(with_new.size(), content="2") + + // Replace existing element + let replaced = with_new.replace(3, 150) + inspect(replaced.size(), content="2") + + // Remove element + let removed = replaced.remove(8) + inspect(removed.size(), content="1") + + // Check final value + match removed[3] { + Some(val) => inspect(val, content="150") + None => inspect(false, content="true") + } +} +``` + +## Bitset Operations + +Work with compact bitsets for tracking presence: + +```moonbit +test "bitset operations" { + // Note: Bitset constructor is internal, so we demonstrate concepts + // In real usage, bitsets are created by sparse array operations + + let sparse = @sparse_array.singleton(3, "test") + inspect(sparse.size(), content="1") + + // Add more elements to create internal bitsets + let with_more = sparse.add(7, "another").add(15, "third") + inspect(with_more.size(), content="3") + + // Access elements by index + match with_more[3] { + Some(val) => inspect(val, content="test") + None => inspect(false, content="true") + } + + // Remove element + let removed = with_more.remove(7) + inspect(removed.size(), content="2") +} +``` + +## Sparse Array Set Operations + +Perform set-like operations on sparse arrays: + +```moonbit +test "sparse array set operations" { + let arr1 = @sparse_array.doubleton(1, "a", 3, "c") + let arr2 = @sparse_array.doubleton(3, "C", 5, "e") + + // Intersection - keep common indices + let intersection = arr1.intersection(arr2, fn(val1, val2) { Some(val1 + val2) }) + match intersection { + Some(result) => inspect(result.size(), content="1") // Only index 3 is common + None => inspect(false, content="true") + } + + // Difference - remove common elements + let difference = arr1.difference(arr2, fn(_val1, _val2) { None }) + match difference { + Some(result) => inspect(result.size(), content="1") // Only index 1 remains + None => inspect(false, content="true") + } +} +``` + +## Sparse Array Transformations + +Transform sparse arrays while maintaining efficiency: + +```moonbit +test "sparse array transformations" { + let numbers = @sparse_array.doubleton(1, 10, 5, 50) + + // Map values to new type + let doubled = numbers.map(fn(x) { x * 2 }) + inspect(doubled.size(), content="2") + match doubled[1] { + Some(val) => inspect(val, content="20") + None => inspect(false, content="true") + } + + // Filter elements (keeping only those > 30) + let filtered = numbers.filter(fn(x) { if x > 30 { Some(x) } else { None } }) + match filtered { + Some(f) => inspect(f.size(), content="1") // Only 50 remains + None => inspect(false, content="true") + } +} +``` + +## Sparse Array Combinations + +Combine multiple sparse arrays: + +```moonbit +test "sparse array combinations" { + let arr1 = @sparse_array.doubleton(1, "a", 3, "c") + let arr2 = @sparse_array.doubleton(2, "b", 3, "C") // Overlaps at index 3 + + // Union with conflict resolution + let combined = arr1.union(arr2, fn(old_val, new_val) { old_val + new_val }) + inspect(combined.size(), content="3") + + // Check combined values + match combined[3] { + Some(val) => inspect(val, content="cC") // Combined "c" + "C" + None => inspect(false, content="true") + } +} +``` + +## Advanced Sparse Array Operations + +Work with sparse arrays efficiently: + +```moonbit +test "advanced sparse operations" { + let numbers = @sparse_array.doubleton(2, 20, 5, 50) + + // Add more elements + let extended = numbers.add(10, 100).add(15, 150) + inspect(extended.size(), content="4") + + // Access specific elements + match extended[10] { + Some(val) => inspect(val, content="100") + None => inspect(false, content="true") + } + + // Check non-existent element + match extended[7] { + Some(_) => inspect(false, content="true") + None => inspect(true, content="true") // Should be None + } +} +``` + +## Internal Usage Context + +These utilities are used by: + +1. **HAMT (Hash Array Mapped Trie)**: Efficient immutable hash tables +2. **Immutable vectors**: Sparse storage for large vectors +3. **Immutable sets**: Compact representation of set membership +4. **Tree structures**: Efficient navigation and storage + +## Performance Characteristics + +### Sparse Arrays +- **Access**: O(1) for existing elements +- **Insertion**: O(1) amortized +- **Space**: Only stores non-empty elements +- **Cache efficiency**: Compact storage layout + +### Bitsets +- **Set/clear**: O(1) bit operations +- **Union/intersection**: O(1) bitwise operations +- **Size counting**: O(1) with population count +- **Memory**: 1 bit per possible position + +## Design Principles + +1. **Immutability**: All operations return new instances +2. **Structural sharing**: Efficient memory usage +3. **Bit-level efficiency**: Compact representation +4. **Zero-cost abstractions**: No runtime overhead + +This package provides the low-level building blocks for efficient immutable data structures in the MoonBit standard library. diff --git a/bundled-core/immut/internal/sparse_array/bitset.mbt b/bundled-core/immut/internal/sparse_array/bitset.mbt index 0f9a7e2..76cdadc 100644 --- a/bundled-core/immut/internal/sparse_array/bitset.mbt +++ b/bundled-core/immut/internal/sparse_array/bitset.mbt @@ -14,7 +14,7 @@ ///| /// a simple bit set to store a set of integers less than 32 -pub(all) type Bitset UInt derive(Eq) +pub(all) struct Bitset(UInt) derive(Eq) ///| let empty_bitset : Bitset = Bitset(0) @@ -22,40 +22,40 @@ let empty_bitset : Bitset = Bitset(0) ///| /// Check if the given index is present in the bitset. pub fn Bitset::has(self : Bitset, idx : Int) -> Bool { - (self.inner() & (1U << idx)) != 0 + (self.0 & (1U << idx)) != 0 } ///| /// Get the index of the bit in the bitset. pub fn Bitset::index_of(self : Bitset, idx : Int) -> Int { - (self.inner() & ((1U << idx) - 1)).popcnt() + (self.0 & ((1U << idx) - 1)).popcnt() } ///| pub fn Bitset::first_idx(self : Bitset) -> Int { - self.inner().ctz() + self.0.ctz() } ///| pub fn Bitset::union(self : Bitset, other : Bitset) -> Bitset { - Bitset(self.inner() | other.inner()) + Bitset(self.0 | other.0) } ///| pub fn Bitset::intersection(self : Bitset, other : Bitset) -> Bitset { - Bitset(self.inner() & other.inner()) + Bitset(self.0 & other.0) } ///| /// Add a new index to the bitset. pub fn Bitset::add(self : Bitset, idx : Int) -> Bitset { - Bitset(self.inner() | (1U << idx)) + Bitset(self.0 | (1U << idx)) } ///| /// Remove an index from the bitset. pub fn Bitset::remove(self : Bitset, idx : Int) -> Bitset { - Bitset(self.inner() ^ (1U << idx)) + Bitset(self.0 ^ (1U << idx)) } ///| diff --git a/bundled-core/immut/internal/sparse_array/sparse_array.mbti b/bundled-core/immut/internal/sparse_array/pkg.generated.mbti similarity index 84% rename from bundled-core/immut/internal/sparse_array/sparse_array.mbti rename to bundled-core/immut/internal/sparse_array/pkg.generated.mbti index dc488b0..b35ba91 100644 --- a/bundled-core/immut/internal/sparse_array/sparse_array.mbti +++ b/bundled-core/immut/internal/sparse_array/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/immut/internal/sparse_array" // Values @@ -7,8 +8,10 @@ fn[X] empty() -> SparseArray[X] fn[X] singleton(Int, X) -> SparseArray[X] +// Errors + // Types and methods -pub(all) type Bitset UInt +pub(all) struct Bitset(UInt) fn Bitset::add(Self, Int) -> Self fn Bitset::first_idx(Self) -> Int fn Bitset::has(Self, Int) -> Bool @@ -27,7 +30,9 @@ pub(all) struct SparseArray[X] { fn[X] SparseArray::add(Self[X], Int, X) -> Self[X] fn[X] SparseArray::difference(Self[X], Self[X], (X, X) -> X?) -> Self[X]? fn[X] SparseArray::each(Self[X], (X) -> Unit raise?) -> Unit raise? +fn[X] SparseArray::filter(Self[X], (X) -> X? raise?) -> Self[X]? raise? fn[X] SparseArray::intersection(Self[X], Self[X], (X, X) -> X? raise?) -> Self[X]? raise? +fn[X, Y] SparseArray::map(Self[X], (X) -> Y raise?) -> Self[Y] raise? fn[X] SparseArray::op_get(Self[X], Int) -> X? fn[X] SparseArray::remove(Self[X], Int) -> Self[X] fn[X] SparseArray::replace(Self[X], Int, X) -> Self[X] diff --git a/bundled-core/immut/internal/sparse_array/sparse_array.mbt b/bundled-core/immut/internal/sparse_array/sparse_array.mbt index 4f757ea..b75e3b5 100644 --- a/bundled-core/immut/internal/sparse_array/sparse_array.mbt +++ b/bundled-core/immut/internal/sparse_array/sparse_array.mbt @@ -50,7 +50,7 @@ pub fn[X] doubleton( idx1 : Int, value1 : X, idx2 : Int, - value2 : X + value2 : X, ) -> SparseArray[X] { { elem_info: empty_bitset.add(idx1).add(idx2), @@ -109,7 +109,7 @@ pub fn[X] remove(self : SparseArray[X], idx : Int) -> SparseArray[X] { pub fn[X] SparseArray::union( self : SparseArray[X], other : SparseArray[X], - f : (X, X) -> X raise? + f : (X, X) -> X raise?, ) -> SparseArray[X] raise? { let union_elem_info = self.elem_info.union(other.elem_info) let data = FixedArray::make(union_elem_info.size(), self.data[0]) @@ -131,7 +131,7 @@ pub fn[X] SparseArray::union( ///| fn[A] FixedArray::copy_prefix( self : FixedArray[A], - len~ : Int + len~ : Int, ) -> FixedArray[A] { let res = FixedArray::make(len, self[0]) FixedArray::unsafe_blit(res, 0, self, 0, len) @@ -143,7 +143,7 @@ fn[A] FixedArray::copy_prefix( pub fn[X] SparseArray::intersection( self : SparseArray[X], other : SparseArray[X], - f : (X, X) -> X? raise? + f : (X, X) -> X? raise?, ) -> SparseArray[X]? raise? { let inter_elem_info = self.elem_info.intersection(other.elem_info) guard inter_elem_info != 0 else { return None } @@ -173,10 +173,9 @@ pub fn[X] SparseArray::intersection( pub fn[X] SparseArray::difference( self : SparseArray[X], other : SparseArray[X], - f : (X, X) -> X? + f : (X, X) -> X?, ) -> SparseArray[X]? { let self_elem_info = self.elem_info - guard self_elem_info != empty_bitset else { return None } let data = FixedArray::make(self_elem_info.size(), self.data[0]) for rest = self_elem_info, index = 0, elem_info = self_elem_info; rest != empty_bitset; { @@ -207,11 +206,48 @@ pub fn[X] SparseArray::difference( } ///| +pub fn[X, Y] SparseArray::map( + self : SparseArray[X], + f : (X) -> Y raise?, +) -> SparseArray[Y] raise? { + { elem_info: self.elem_info, data: self.data.map(f) } +} + +///| +pub fn[X] SparseArray::filter( + self : SparseArray[X], + pred : (X) -> X? raise?, +) -> SparseArray[X]? raise? { + let self_elem_info = self.elem_info + let data = FixedArray::make(self_elem_info.size(), self.data[0]) + for rest = self_elem_info, index = 0, elem_info = self_elem_info; rest != + empty_bitset; { + let idx = rest.first_idx() + match pred(self.unsafe_get(idx)) { + None => continue rest.remove(idx), index, elem_info.remove(idx) + Some(v) => { + data[index] = v + continue rest.remove(idx), index + 1, elem_info + } + } + } else { + if elem_info == empty_bitset { + None + } else if elem_info == self_elem_info { + Some({ elem_info, data }) + } else { + Some({ elem_info, data: data.copy_prefix(len=index) }) + } + } +} + // replace an existing element in the sparse array. + +///| pub fn[X] replace( self : SparseArray[X], idx : Int, - value : X + value : X, ) -> SparseArray[X] { let new_data = self.data.copy() new_data[self.elem_info.index_of(idx)] = value diff --git a/bundled-core/immut/list/deprecated.mbt b/bundled-core/immut/list/deprecated.mbt index d92c057..af257fe 100644 --- a/bundled-core/immut/list/deprecated.mbt +++ b/bundled-core/immut/list/deprecated.mbt @@ -16,7 +16,7 @@ #deprecated("use `@immut/list.from_json` instead") #coverage.skip pub fn[A : @json.FromJson] T::from_json( - json : Json + json : Json, ) -> T[A] raise @json.JsonDecodeError { @json.from_json(json) } diff --git a/bundled-core/immut/list/list.mbt b/bundled-core/immut/list/list.mbt index 80396ae..feacd97 100644 --- a/bundled-core/immut/list/list.mbt +++ b/bundled-core/immut/list/list.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#deprecated("use `@list` instead") pub fn[A] add(self : T[A], head : A) -> T[A] { Cons(head, self) } @@ -34,6 +35,7 @@ pub impl[A : ToJson] ToJson for T[A] with to_json(self) { } ///| +#deprecated("use `@list` instead") pub fn[A : ToJson] to_json(self : T[A]) -> Json { ToJson::to_json(self) } @@ -51,8 +53,9 @@ pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) } ///| +#deprecated("use `@list` instead") pub fn[A : @json.FromJson] from_json( - json : Json + json : Json, ) -> T[A] raise @json.JsonDecodeError { @json.from_json(json) } @@ -66,6 +69,7 @@ pub fn[A : @json.FromJson] from_json( /// let ls = @list.of([1, 2, 3, 4, 5]) /// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5])) /// ``` +#deprecated("use `@list` instead") pub fn[A] from_array(arr : Array[A]) -> T[A] { for i = arr.length() - 1, list = Nil; i >= 0; { continue i - 1, Cons(arr[i], list) @@ -76,6 +80,7 @@ pub fn[A] from_array(arr : Array[A]) -> T[A] { ///| /// Get the length of the list. +#deprecated("use `@list` instead") pub fn[A] length(self : T[A]) -> Int { loop (self, 0) { (Nil, len) => len @@ -93,6 +98,7 @@ pub fn[A] length(self : T[A]) -> Int { /// @list.of([1, 2, 3, 4, 5]).each((x) => { arr.push(x) }) /// assert_eq(arr, [1, 2, 3, 4, 5]) /// ``` +#deprecated("use `@list` instead") pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { loop self { Nil => () @@ -113,6 +119,7 @@ pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { /// @list.of([1, 2, 3, 4, 5]).eachi((i, x) => { arr.push("(\{i},\{x})") }) /// assert_eq(arr, ["(0,1)", "(1,2)", "(2,3)", "(3,4)", "(4,5)"]) /// ``` +#deprecated("use `@list` instead") pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit raise?) -> Unit raise? { loop (self, 0) { (Nil, _) => () @@ -131,6 +138,7 @@ pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit raise?) -> Unit raise? { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).map((x) => { x * 2}), @list.of([2, 4, 6, 8, 10])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] map(self : T[A], f : (A) -> B) -> T[B] { match self { Nil => Nil @@ -140,6 +148,7 @@ pub fn[A, B] map(self : T[A], f : (A) -> B) -> T[B] { ///| /// Maps the list with index. +#deprecated("use `@list` instead") pub fn[A, B] mapi(self : T[A], f : (Int, A) -> B raise?) -> T[B] raise? { fn go(xs : T[A], i : Int, f : (Int, A) -> B raise?) -> T[B] raise? { match xs { @@ -160,6 +169,7 @@ pub fn[A, B] mapi(self : T[A], f : (Int, A) -> B raise?) -> T[B] raise? { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).rev_map((x) => { x * 2 }), @list.of([10, 8, 6, 4, 2])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] rev_map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { loop (Nil, self) { (acc, Nil) => acc @@ -169,6 +179,7 @@ pub fn[A, B] rev_map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { ///| /// Convert list to array. +#deprecated("use `@list` instead") pub fn[A] to_array(self : T[A]) -> Array[A] { match self { Nil => [] @@ -194,6 +205,7 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).filter((x) => { x % 2 == 0}), @list.of([2, 4])) /// ``` +#deprecated("use `@list` instead") pub fn[A] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { match self { Nil => Nil @@ -208,6 +220,7 @@ pub fn[A] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { ///| /// Test if all elements of the list satisfy the predicate. +#deprecated("use `@list` instead") pub fn[A] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { loop self { Nil => true @@ -217,6 +230,7 @@ pub fn[A] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { ///| /// Test if any element of the list satisfies the predicate. +#deprecated("use `@list` instead") pub fn[A] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { match self { Nil => false @@ -232,6 +246,7 @@ pub fn[A] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).tail(), @list.of([2, 3, 4, 5])) /// ``` +#deprecated("use `@list` instead") pub fn[A] tail(self : T[A]) -> T[A] { match self { Nil => Nil @@ -242,6 +257,7 @@ pub fn[A] tail(self : T[A]) -> T[A] { ///| /// Get first element of the list. #internal(unsafe, "Panic if the list is empty") +#deprecated("use `@list` instead") pub fn[A] unsafe_head(self : T[A]) -> A { match self { Nil => abort("head of empty list") @@ -250,8 +266,8 @@ pub fn[A] unsafe_head(self : T[A]) -> A { } ///| -#deprecated("Use `unsafe_head` instead") #coverage.skip +#deprecated("use `@list` instead") pub fn[A] head_exn(self : T[A]) -> A { self.unsafe_head() } @@ -264,6 +280,7 @@ pub fn[A] head_exn(self : T[A]) -> A { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).head(), Some(1)) /// ``` +#deprecated("use `@list` instead") pub fn[A] head(self : T[A]) -> A? { match self { Nil => None @@ -273,6 +290,7 @@ pub fn[A] head(self : T[A]) -> A? { ///| #internal(unsafe, "Panic if the list is empty") +#deprecated("use `@list` instead") pub fn[A] unsafe_last(self : T[A]) -> A { loop self { Nil => abort("last of empty list") @@ -289,6 +307,7 @@ pub fn[A] unsafe_last(self : T[A]) -> A { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).last(), Some(5)) /// ``` +#deprecated("use `@list` instead") pub fn[A] last(self : T[A]) -> A? { loop self { Nil => None @@ -306,6 +325,7 @@ pub fn[A] last(self : T[A]) -> A? { /// let ls = @list.of([1, 2, 3, 4, 5]).concat(@list.of([6, 7, 8, 9, 10])) /// assert_eq(ls, @list.of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) /// ``` +#deprecated("use `@list` instead") pub fn[A] concat(self : T[A], other : T[A]) -> T[A] { match self { Nil => other @@ -322,6 +342,7 @@ pub fn[A] concat(self : T[A], other : T[A]) -> T[A] { /// let ls = @list.of([1, 2, 3, 4, 5]).rev_concat(@list.of([6, 7, 8, 9, 10])) /// assert_eq(ls, @list.of([5, 4, 3, 2, 1, 6, 7, 8, 9, 10])) /// ``` +#deprecated("use `@list` instead") pub fn[A] rev_concat(self : T[A], other : T[A]) -> T[A] { loop (self, other) { (Nil, other) => other @@ -337,6 +358,7 @@ pub fn[A] rev_concat(self : T[A], other : T[A]) -> T[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).rev(), @list.of([5, 4, 3, 2, 1])) /// ``` +#deprecated("use `@list` instead") pub fn[A] rev(self : T[A]) -> T[A] { self.rev_concat(Nil) } @@ -348,8 +370,9 @@ pub fn[A] rev(self : T[A]) -> T[A] { /// /// ```mbt /// let r = @list.of([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => { acc + x }) -/// assert_eq(r, 15) +/// inspect(r, content="15") /// ``` +#deprecated("use `@list` instead") pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B raise?) -> B raise? { match self { Nil => init @@ -363,12 +386,13 @@ pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B raise?) -> B raise? { /// # Example /// ```mbt /// let r = @list.of([1, 2, 3, 4, 5]).rev_fold((x, acc) => { x + acc }, init=0) -/// assert_eq(r, 15) +/// inspect(r, content="15") /// ``` +#deprecated("use `@list` instead") pub fn[A, B] rev_fold( self : T[A], init~ : B, - f : (A, B) -> B raise? + f : (A, B) -> B raise?, ) -> B raise? { let xs = self.to_array() let mut acc = init @@ -385,25 +409,25 @@ pub fn[A, B] rev_fold( /// /// ```mbt /// let r = @list.of([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => { acc + x }) -/// assert_eq(r, 15) +/// inspect(r, content="15") /// ``` -#deprecated("Use `fold` instead") #coverage.skip +#deprecated("use `@list` instead") pub fn[A, B] fold_left( self : T[A], f : (B, A) -> B raise?, - init~ : B + init~ : B, ) -> B raise? { self.fold(init~, f) } ///| -#deprecated("Use `rev_fold` instead") #coverage.skip +#deprecated("use `@list.rev_fold` instead") pub fn[A, B] fold_right( self : T[A], f : (A, B) -> B raise?, - init~ : B + init~ : B, ) -> B raise? { match self { Nil => init @@ -413,10 +437,11 @@ pub fn[A, B] fold_right( ///| /// Fold the list from left with index. +#deprecated("use `@list` instead") pub fn[A, B] foldi( self : T[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { fn go(xs : T[A], i : Int, f : (Int, B, A) -> B raise?, acc : B) -> B raise? { match xs { @@ -430,10 +455,11 @@ pub fn[A, B] foldi( ///| /// Fold the list from right with index. +#deprecated("use `@list` instead") pub fn[A, B] rev_foldi( self : T[A], init~ : B, - f : (Int, A, B) -> B raise? + f : (Int, A, B) -> B raise?, ) -> B raise? { fn go(xs : T[A], i : Int, f : (Int, A, B) -> B raise?, acc : B) -> B raise? { match xs { @@ -447,12 +473,11 @@ pub fn[A, B] rev_foldi( ///| /// Fold the list from left with index. -#deprecated("Use `foldi` instead") -#coverage.skip +#deprecated("use `@list.foldi` instead") pub fn[A, B] fold_lefti( self : T[A], f : (Int, B, A) -> B raise?, - init~ : B + init~ : B, ) -> B raise? { fn go(xs : T[A], i : Int, f : (Int, B, A) -> B raise?, acc : B) -> B raise? { match xs { @@ -466,12 +491,11 @@ pub fn[A, B] fold_lefti( ///| /// Fold the list from right with index. -#deprecated("Use `rev_foldi` instead") -#coverage.skip +#deprecated("use `@list.rev_foldi` instead") pub fn[A, B] fold_righti( self : T[A], f : (Int, A, B) -> B raise?, - init~ : B + init~ : B, ) -> B raise? { fn go(xs : T[A], i : Int, f : (Int, A, B) -> B raise?, acc : B) -> B raise? { match xs { @@ -493,6 +517,7 @@ pub fn[A, B] fold_righti( /// let r = @list.of([1, 2, 3, 4, 5]).zip(@list.of([6, 7, 8, 9, 10])) /// assert_eq(r, Some(@list.from_array([(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]))) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] zip(self : T[A], other : T[B]) -> T[(A, B)]? { let mut acc = Nil let res = loop (self, other) { @@ -518,8 +543,7 @@ pub fn[A, B] zip(self : T[A], other : T[B]) -> T[(A, B)]? { /// let r = ls.flat_map((x) => { @list.from_array([x, x * 2]) }) /// assert_eq(r, @list.from_array([1, 2, 2, 4, 3, 6])) /// ``` -#deprecated("Use `flat_map` instead") -#coverage.skip +#deprecated("use `@list` instead") pub fn[A, B] concat_map(self : T[A], f : (A) -> T[B]) -> T[B] { self.flat_map(f) } @@ -536,6 +560,7 @@ pub fn[A, B] concat_map(self : T[A], f : (A) -> T[B]) -> T[B] { /// let r = ls.flat_map((x) => { @list.from_array([x, x * 2]) }) /// assert_eq(r, @list.from_array([1, 2, 2, 4, 3, 6])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] flat_map(self : T[A], f : (A) -> T[B] raise?) -> T[B] raise? { match self { Nil => Nil @@ -553,6 +578,7 @@ pub fn[A, B] flat_map(self : T[A], f : (A) -> T[B] raise?) -> T[B] raise? { /// let r = ls.filter_map((x) => { if (x >= 3) { Some(x) } else { None } }) /// assert_eq(r, @list.of([4, 6, 3])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] filter_map(self : T[A], f : (A) -> B?) -> T[B] { loop (Nil, self) { (acc, Nil) => acc.rev() @@ -566,6 +592,7 @@ pub fn[A, B] filter_map(self : T[A], f : (A) -> B?) -> T[B] { ///| #internal(unsafe, "Panic if the index is out of bounds") +#deprecated("use `@list` instead") pub fn[A] unsafe_nth(self : T[A], n : Int) -> A { loop (self, n) { (Nil, _) => abort("nth: index out of bounds") @@ -575,14 +602,14 @@ pub fn[A] unsafe_nth(self : T[A], n : Int) -> A { } ///| -#deprecated("Use `unsafe_nth` instead") -#coverage.skip +#deprecated("use `@list` instead") pub fn[A] nth_exn(self : T[A], n : Int) -> A { self.unsafe_nth(n) } ///| /// Get nth element of the list or None if the index is out of bounds +#deprecated("use `@list` instead") pub fn[A] nth(self : T[A], n : Int) -> A? { loop (self, n) { (Nil, _) => None @@ -599,6 +626,7 @@ pub fn[A] nth(self : T[A], n : Int) -> A? { /// ```mbt /// assert_eq(@list.repeat(5, 1), @list.from_array([1, 1, 1, 1, 1])) /// ``` +#deprecated("use `@list` instead") pub fn[A] repeat(n : Int, x : A) -> T[A] { if n == 0 { Nil @@ -616,6 +644,7 @@ pub fn[A] repeat(n : Int, x : A) -> T[A] { /// let ls = (@list.from_array(["1", "2", "3", "4", "5"])).intersperse("|") /// assert_eq(ls, @list.from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"])) /// ``` +#deprecated("use `@list` instead") pub fn[A] intersperse(self : T[A], separator : A) -> T[A] { match self { Nil => Nil @@ -626,6 +655,7 @@ pub fn[A] intersperse(self : T[A], separator : A) -> T[A] { ///| /// Check if the list is empty. +#deprecated("use `@list` instead") pub fn[A] is_empty(self : T[A]) -> Bool { self is Nil } @@ -640,6 +670,7 @@ pub fn[A] is_empty(self : T[A]) -> Bool { /// assert_eq(a, @list.from_array([1, 3, 5])) /// assert_eq(b, @list.from_array([2, 4, 6])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] unzip(self : T[(A, B)]) -> (T[A], T[B]) { let mut xs = Nil let mut ys = Nil @@ -663,6 +694,7 @@ pub fn[A, B] unzip(self : T[(A, B)]) -> (T[A], T[B]) { /// let ls = (@list.from_array([@list.from_array([1,2,3]), @list.from_array([4,5,6]), @list.from_array([7,8,9])])).flatten() /// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9])) /// ``` +#deprecated("use `@list` instead") pub fn[A] flatten(self : T[T[A]]) -> T[A] { match self { Nil => Nil @@ -672,6 +704,7 @@ pub fn[A] flatten(self : T[T[A]]) -> T[A] { ///| #internal(unsafe, "Panic if the list is empty") +#deprecated("use `@list` instead") pub fn[A : Compare] unsafe_maximum(self : T[A]) -> A { match self { Nil => abort("maximum: empty list") @@ -690,6 +723,7 @@ pub fn[A : Compare] unsafe_maximum(self : T[A]) -> A { ///| /// Get maximum element of the list. /// Returns None if the list is empty. +#deprecated("use `@list` instead") pub fn[A : Compare] maximum(self : T[A]) -> A? { match self { Nil => None @@ -704,6 +738,7 @@ pub fn[A : Compare] maximum(self : T[A]) -> A? { ///| #internal(unsafe, "Panic if the list is empty") +#deprecated("use `@list` instead") pub fn[A : Compare] unsafe_minimum(self : T[A]) -> A { match self { Nil => abort("minimum: empty list") @@ -721,6 +756,7 @@ pub fn[A : Compare] unsafe_minimum(self : T[A]) -> A { ///| /// Get minimum element of the list. +#deprecated("use `@list` instead") pub fn[A : Compare] minimum(self : T[A]) -> A? { match self { Nil => None @@ -742,6 +778,7 @@ pub fn[A : Compare] minimum(self : T[A]) -> A? { /// let ls = (@list.from_array([1,123,52,3,6,0,-6,-76])).sort() /// assert_eq(ls, @list.from_array([-76, -6, 0, 1, 3, 6, 52, 123])) /// ``` +#deprecated("use `@list` instead") pub fn[A : Compare] sort(self : T[A]) -> T[A] { match self { Nil => Nil @@ -757,12 +794,13 @@ pub fn[A : Compare] sort(self : T[A]) -> T[A] { /// Concatenate two lists. /// /// `a + b` equal to `a.concat(b)` -pub impl[A] Add for T[A] with op_add(self, other) { +pub impl[A] Add for T[A] with add(self, other) { self.concat(other) } ///| /// Check if the list contains the value. +#deprecated("use `@list` instead") pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { loop self { Nil => false @@ -779,6 +817,7 @@ pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { /// let r = @list.unfold(init=0, i => if i == 3 { None } else { Some((i, i + 1)) }) /// assert_eq(r, @list.from_array([0, 1, 2])) /// ``` +#deprecated("use `@list` instead") pub fn[A, S] unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { match f(init) { Some((element, new_state)) => Cons(element, unfold(init=new_state, f)) @@ -797,6 +836,7 @@ pub fn[A, S] unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { /// let r = ls.take(3) /// assert_eq(r, @list.of([1, 2, 3])) /// ``` +#deprecated("use `@list` instead") pub fn[A] take(self : T[A], n : Int) -> T[A] { fn go(n, t) { match (n, t) { @@ -824,6 +864,7 @@ pub fn[A] take(self : T[A], n : Int) -> T[A] { /// let r = ls.drop(3) /// assert_eq(r, @list.of([4, 5])) /// ``` +#deprecated("use `@list` instead") pub fn[A] drop(self : T[A], n : Int) -> T[A] { if n <= 0 { self @@ -846,6 +887,7 @@ pub fn[A] drop(self : T[A], n : Int) -> T[A] { /// let r = ls.take_while((x) => { x < 3 }) /// assert_eq(r, @list.of([1, 2])) /// ``` +#deprecated("use `@list` instead") pub fn[A] take_while(self : T[A], p : (A) -> Bool) -> T[A] { match self { Nil => Nil @@ -863,6 +905,7 @@ pub fn[A] take_while(self : T[A], p : (A) -> Bool) -> T[A] { /// let r = ls.drop_while((x) => { x < 3 }) /// assert_eq(r, @list.of([3, 4])) /// ``` +#deprecated("use `@list` instead") pub fn[A] drop_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { loop self { Nil => Nil @@ -880,10 +923,11 @@ pub fn[A] drop_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { /// let r = ls.scan_left((acc, x) => { acc + x }, init=0) /// assert_eq(r, @list.of([0, 1, 3, 6, 10, 15])) /// ``` +#deprecated("use `@list` instead") pub fn[A, E] scan_left( self : T[A], f : (E, A) -> E raise?, - init~ : E + init~ : E, ) -> T[E] raise? { Cons( init, @@ -905,10 +949,11 @@ pub fn[A, E] scan_left( /// let r = ls.scan_right((x, acc) => { acc + x }, init=0) /// assert_eq(r, @list.of([15, 14, 12, 9, 5, 0])) /// ``` +#deprecated("use `@list` instead") pub fn[A, B] scan_right( self : T[A], f : (A, B) -> B raise?, - init~ : B + init~ : B, ) -> T[B] raise? { match self { Nil => Cons(init, Nil) @@ -928,6 +973,7 @@ pub fn[A, B] scan_right( /// let ls = @list.from_array([(1, "a"), (2, "b"), (3, "c")]) /// assert_eq(ls.lookup(3), Some("c")) /// ``` +#deprecated("use `@list` instead") pub fn[A : Eq, B] lookup(self : T[(A, B)], v : A) -> B? { loop self { Nil => None @@ -944,6 +990,7 @@ pub fn[A : Eq, B] lookup(self : T[(A, B)], v : A) -> B? { /// assert_eq(@list.of([1, 3, 5, 8]).find((element) => { element % 2 == 0}), Some(8)) /// assert_eq(@list.of([1, 3, 5]).find((element) => { element % 2 == 0}), None) /// ``` +#deprecated("use `@list` instead") pub fn[A] find(self : T[A], f : (A) -> Bool raise?) -> A? raise? { loop self { Nil => None @@ -965,6 +1012,7 @@ pub fn[A] find(self : T[A], f : (A) -> Bool raise?) -> A? raise? { /// assert_eq(@list.of([1, 3, 5, 8]).findi((element, index) => { (element % 2 == 0) && (index == 3) }), Some(8)) /// assert_eq(@list.of([1, 3, 8, 5]).findi((element, index) => { (element % 2 == 0) && (index == 3) }), None) /// ``` +#deprecated("use `@list` instead") pub fn[A] findi(self : T[A], f : (A, Int) -> Bool raise?) -> A? raise? { loop (self, 0) { (list, index) => @@ -988,6 +1036,7 @@ pub fn[A] findi(self : T[A], f : (A, Int) -> Bool raise?) -> A? raise? { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).remove_at(2), @list.of([1, 2, 4, 5])) /// ``` +#deprecated("use `@list` instead") pub fn[A] remove_at(self : T[A], index : Int) -> T[A] { match (index, self) { (0, Cons(_, tail)) => tail @@ -1005,6 +1054,7 @@ pub fn[A] remove_at(self : T[A], index : Int) -> T[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).remove(3), @list.of([1, 2, 4, 5])) /// ``` +#deprecated("use `@list` instead") pub fn[A : Eq] remove(self : T[A], elem : A) -> T[A] { match self { Nil => Nil @@ -1025,6 +1075,7 @@ pub fn[A : Eq] remove(self : T[A], elem : A) -> T[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).is_prefix(@list.of([1, 2, 3])), true) /// ``` +#deprecated("use `@list` instead") pub fn[A : Eq] is_prefix(self : T[A], prefix : T[A]) -> Bool { loop (self, prefix) { (_, Nil) => true @@ -1046,6 +1097,7 @@ pub fn[A : Eq] is_prefix(self : T[A], prefix : T[A]) -> Bool { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).is_suffix(@list.of([3, 4, 5])), true) /// ``` +#deprecated("use `@list` instead") pub fn[A : Eq] is_suffix(self : T[A], suffix : T[A]) -> Bool { self.rev().is_prefix(suffix.rev()) } @@ -1063,6 +1115,7 @@ pub fn[A : Eq] is_suffix(self : T[A], suffix : T[A]) -> Bool { /// let r = ls.intercalate(@list.of([0])) /// assert_eq(r, @list.of([1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9])) /// ``` +#deprecated("use `@list` instead") pub fn[A] intercalate(self : T[T[A]], sep : T[A]) -> T[A] { self.intersperse(sep).flatten() } @@ -1074,16 +1127,18 @@ pub impl[X] Default for T[X] with default() { ///| /// The empty list +#deprecated("use `@list` instead") pub fn[X] default() -> T[X] { Nil } ///| +#deprecated("use `@list` instead") pub fn[A] iter(self : T[A]) -> Iter[A] { Iter::new(yield_ => loop self { Nil => IterContinue Cons(head, tail) => { - if yield_(head) == IterEnd { + if yield_(head) is IterEnd { break IterEnd } continue tail @@ -1092,11 +1147,12 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { } ///| +#deprecated("use `@list` instead") pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { Iter2::new(yield_ => loop (self, 0) { (Nil, _) => IterEnd (Cons(head, tail), i) => { - if yield_(i, head) == IterEnd { + if yield_(i, head) is IterEnd { break IterEnd } continue (tail, i + 1) @@ -1108,16 +1164,19 @@ pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { /// Convert the iterator into a list. Preserves order of elements. /// This function is tail-recursive, but consumes 2*n memory. /// If the order of elements is not important, use `from_iter_rev` instead. +#deprecated("use `@list` instead") pub fn[A] from_iter(iter : Iter[A]) -> T[A] { iter.fold(init=Nil, (acc, e) => Cons(e, acc)).rev() } ///| +#deprecated("use `@list` instead") pub fn[A] from_iter_rev(iter : Iter[A]) -> T[A] { iter.fold(init=Nil, (acc, e) => Cons(e, acc)) } ///| +#deprecated("use `@list` instead") pub fn[A] of(arr : FixedArray[A]) -> T[A] { for i = arr.length() - 1, list = Nil; i >= 0; { continue i - 1, Cons(arr[i], list) @@ -1129,12 +1188,13 @@ pub fn[A] of(arr : FixedArray[A]) -> T[A] { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] with arbitrary( size, - rs + rs, ) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } ///| +#deprecated("use `@list` instead") pub fn[A] singleton(x : A) -> T[A] { Cons(x, Nil) } @@ -1147,7 +1207,7 @@ pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { } ///| -/// Compares two lists lexicographically. +/// Compares two lists based on shortlex order. /// /// First compares elements pairwise until a difference is found. /// If lists have different lengths and all shared elements are equal, @@ -1170,10 +1230,10 @@ pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { /// let list1 = @list.of([1, 2, 3]) /// let list2 = @list.of([1, 2, 4]) /// let list3 = @list.of([1, 2]) -/// assert_eq(list1.compare(list2), -1) // list1 < list2 -/// assert_eq(list1.compare(list3), 1) // list2 > list1 -/// assert_eq(list3.compare(list1), -1) // list1 > list3 (longer) -/// assert_eq(list1.compare(list1), 0) // list1 = list1 +/// inspect(list1.compare(list2), content="-1") // list1 < list2 +/// inspect(list1.compare(list3), content="1") // list2 > list1 +/// inspect(list3.compare(list1), content="-1") // list1 > list3 (longer) +/// inspect(list1.compare(list1), content="0") // list1 = list1 /// ``` pub impl[A : Compare] Compare for T[A] with compare(self, other) { loop (self, other) { diff --git a/bundled-core/immut/list/list_test.mbt b/bundled-core/immut/list/list_test.mbt index c1a3499..81a23e2 100644 --- a/bundled-core/immut/list/list_test.mbt +++ b/bundled-core/immut/list/list_test.mbt @@ -262,9 +262,9 @@ test "intersperse" { let el : @list.T[String] = Nil inspect( ls.intersperse("|"), - content= + content=( #|@list.of(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) - , + ), ) inspect(el.intersperse("|"), content="@list.of([])") } @@ -401,9 +401,9 @@ test "lookup" { inspect(el.lookup(1), content="None") inspect( ls.lookup(3), - content= + content=( #|Some("c") - , + ), ) inspect(ls.lookup(4), content="None") } @@ -447,15 +447,15 @@ test "remove_at" { inspect(ls.remove_at(0), content="@list.of([2, 3, 4, 5])") inspect( @list.of(["a", "b", "c", "d", "e"]).remove_at(2), - content= + content=( #|@list.of(["a", "b", "d", "e"]) - , + ), ) inspect( @list.of(["a", "b", "c", "d", "e"]).remove_at(5), - content= + content=( #|@list.of(["a", "b", "c", "d", "e"]) - , + ), ) } @@ -464,15 +464,15 @@ test "remove" { inspect(@list.of([1, 2, 3, 4, 5]).remove(3), content="@list.of([1, 2, 4, 5])") inspect( @list.of(["a", "b", "c", "d", "e"]).remove("c"), - content= + content=( #|@list.of(["a", "b", "d", "e"]) - , + ), ) inspect( @list.of(["a", "b", "c", "d", "e"]).remove("f"), - content= + content=( #|@list.of(["a", "b", "c", "d", "e"]) - , + ), ) } @@ -641,22 +641,22 @@ test "op_add" { } ///| -test "from_iter multiple elements iter" { +test "@list.from_iter multiple elements iter" { inspect(@list.from_iter([1, 2, 3].iter()), content="@list.of([1, 2, 3])") } ///| -test "from_iter_rev multiple elements iter" { +test "@list.from_iter_rev multiple elements iter" { inspect(@list.from_iter_rev([1, 2, 3].iter()), content="@list.of([3, 2, 1])") } ///| -test "from_iter single element iter" { +test "@list.from_iter single element iter" { inspect(@list.from_iter([1].iter()), content="@list.of([1])") } ///| -test "from_iter empty iter" { +test "@list.from_iter empty iter" { let pq : @list.T[Int] = @list.from_iter(Iter::empty()) inspect(pq, content="@list.of([])") } @@ -676,7 +676,7 @@ test "hash" { ///| test "stackover flow" { let iter = Int::until(0, 1 << 20) - let lst = from_iter(iter) + let lst = @list.from_iter(iter) inspect( lst.fold(init=0.0, (acc, x) => acc + x.to_double()), content="549755289600", @@ -697,9 +697,9 @@ test "compare" { let list2 = @list.of([1, 2, 4]) let list3 = @list.of([1, 2]) let empty : @list.T[Int] = @list.Nil - assert_eq(list1.compare(list2), -1) - assert_eq(list1.compare(list3), 1) - assert_eq(list3.compare(list1), -1) - assert_eq(list1.compare(list1), 0) - assert_eq(empty.compare(empty), 0) + inspect(list1.compare(list2), content="-1") + inspect(list1.compare(list3), content="1") + inspect(list3.compare(list1), content="-1") + inspect(list1.compare(list1), content="0") + inspect(empty.compare(empty), content="0") } diff --git a/bundled-core/immut/list/moon.pkg.json b/bundled-core/immut/list/moon.pkg.json index 84cad1e..ec7de9e 100644 --- a/bundled-core/immut/list/moon.pkg.json +++ b/bundled-core/immut/list/moon.pkg.json @@ -9,6 +9,7 @@ "test-import": [ "moonbitlang/core/double" ], + "alert-list": "-deprecated", "targets": { "panic_test.mbt": ["not", "native", "llvm"] } diff --git a/bundled-core/immut/list/list.mbti b/bundled-core/immut/list/pkg.generated.mbti similarity index 84% rename from bundled-core/immut/list/list.mbti rename to bundled-core/immut/list/pkg.generated.mbti index 0cd2022..946a149 100644 --- a/bundled-core/immut/list/list.mbti +++ b/bundled-core/immut/list/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/immut/list" import( @@ -6,50 +7,77 @@ import( ) // Values +#deprecated fn[X] default() -> T[X] +#deprecated fn[A] from_array(Array[A]) -> T[A] +#deprecated fn[A] from_iter(Iter[A]) -> T[A] +#deprecated fn[A] from_iter_rev(Iter[A]) -> T[A] +#deprecated fn[A : @json.FromJson] from_json(Json) -> T[A] raise @json.JsonDecodeError +#deprecated fn[A] of(FixedArray[A]) -> T[A] +#deprecated fn[A] repeat(Int, A) -> T[A] +#deprecated fn[A] singleton(A) -> T[A] +#deprecated fn[A, S] unfold((S) -> (A, S)? raise?, init~ : S) -> T[A] raise? +// Errors + // Types and methods pub(all) enum T[A] { Nil Cons(A, T[A]) } +#deprecated fn[A] T::add(Self[A], A) -> Self[A] +#deprecated fn[A] T::all(Self[A], (A) -> Bool raise?) -> Bool raise? +#deprecated fn[A] T::any(Self[A], (A) -> Bool raise?) -> Bool raise? +#deprecated fn[A] T::concat(Self[A], Self[A]) -> Self[A] #deprecated fn[A, B] T::concat_map(Self[A], (A) -> Self[B]) -> Self[B] +#deprecated fn[A : Eq] T::contains(Self[A], A) -> Bool #deprecated fn[X] T::default() -> Self[X] +#deprecated fn[A] T::drop(Self[A], Int) -> Self[A] +#deprecated fn[A] T::drop_while(Self[A], (A) -> Bool raise?) -> Self[A] raise? +#deprecated fn[A] T::each(Self[A], (A) -> Unit raise?) -> Unit raise? +#deprecated fn[A] T::eachi(Self[A], (Int, A) -> Unit raise?) -> Unit raise? #deprecated fn[A : Eq] T::equal(Self[A], Self[A]) -> Bool +#deprecated fn[A] T::filter(Self[A], (A) -> Bool raise?) -> Self[A] raise? +#deprecated fn[A, B] T::filter_map(Self[A], (A) -> B?) -> Self[B] +#deprecated fn[A] T::find(Self[A], (A) -> Bool raise?) -> A? raise? +#deprecated fn[A] T::findi(Self[A], (A, Int) -> Bool raise?) -> A? raise? +#deprecated fn[A, B] T::flat_map(Self[A], (A) -> Self[B] raise?) -> Self[B] raise? +#deprecated fn[A] T::flatten(Self[Self[A]]) -> Self[A] +#deprecated fn[A, B] T::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? #deprecated fn[A, B] T::fold_left(Self[A], (B, A) -> B raise?, init~ : B) -> B raise? @@ -59,6 +87,7 @@ fn[A, B] T::fold_lefti(Self[A], (Int, B, A) -> B raise?, init~ : B) -> B raise? fn[A, B] T::fold_right(Self[A], (A, B) -> B raise?, init~ : B) -> B raise? #deprecated fn[A, B] T::fold_righti(Self[A], (Int, A, B) -> B raise?, init~ : B) -> B raise? +#deprecated fn[A, B] T::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? #deprecated fn[A] T::from_array(Array[A]) -> Self[A] @@ -66,51 +95,89 @@ fn[A] T::from_array(Array[A]) -> Self[A] fn[A] T::from_iter(Iter[A]) -> Self[A] #deprecated fn[A : @json.FromJson] T::from_json(Json) -> Self[A] raise @json.JsonDecodeError +#deprecated fn[A] T::head(Self[A]) -> A? #deprecated fn[A] T::head_exn(Self[A]) -> A #deprecated fn[A] T::init_(Self[A]) -> Self[A] +#deprecated fn[A] T::intercalate(Self[Self[A]], Self[A]) -> Self[A] +#deprecated fn[A] T::intersperse(Self[A], A) -> Self[A] +#deprecated fn[A] T::is_empty(Self[A]) -> Bool +#deprecated fn[A : Eq] T::is_prefix(Self[A], Self[A]) -> Bool +#deprecated fn[A : Eq] T::is_suffix(Self[A], Self[A]) -> Bool +#deprecated fn[A] T::iter(Self[A]) -> Iter[A] +#deprecated fn[A] T::iter2(Self[A]) -> Iter2[Int, A] +#deprecated fn[A] T::last(Self[A]) -> A? +#deprecated fn[A] T::length(Self[A]) -> Int +#deprecated fn[A : Eq, B] T::lookup(Self[(A, B)], A) -> B? +#deprecated fn[A, B] T::map(Self[A], (A) -> B) -> Self[B] +#deprecated fn[A, B] T::mapi(Self[A], (Int, A) -> B raise?) -> Self[B] raise? +#deprecated fn[A : Compare] T::maximum(Self[A]) -> A? +#deprecated fn[A : Compare] T::minimum(Self[A]) -> A? +#deprecated fn[A] T::nth(Self[A], Int) -> A? #deprecated fn[A] T::nth_exn(Self[A], Int) -> A #deprecated fn[A] T::of(FixedArray[A]) -> Self[A] +#deprecated fn[A : Eq] T::remove(Self[A], A) -> Self[A] +#deprecated fn[A] T::remove_at(Self[A], Int) -> Self[A] +#deprecated fn[A] T::rev(Self[A]) -> Self[A] +#deprecated fn[A] T::rev_concat(Self[A], Self[A]) -> Self[A] +#deprecated fn[A, B] T::rev_fold(Self[A], init~ : B, (A, B) -> B raise?) -> B raise? +#deprecated fn[A, B] T::rev_foldi(Self[A], init~ : B, (Int, A, B) -> B raise?) -> B raise? +#deprecated fn[A, B] T::rev_map(Self[A], (A) -> B raise?) -> Self[B] raise? +#deprecated fn[A, E] T::scan_left(Self[A], (E, A) -> E raise?, init~ : E) -> Self[E] raise? +#deprecated fn[A, B] T::scan_right(Self[A], (A, B) -> B raise?, init~ : B) -> Self[B] raise? +#deprecated fn[A : Compare] T::sort(Self[A]) -> Self[A] +#deprecated fn[A] T::tail(Self[A]) -> Self[A] +#deprecated fn[A] T::take(Self[A], Int) -> Self[A] +#deprecated fn[A] T::take_while(Self[A], (A) -> Bool) -> Self[A] +#deprecated fn[A] T::to_array(Self[A]) -> Array[A] +#deprecated fn[A : ToJson] T::to_json(Self[A]) -> Json +#deprecated fn[A] T::unsafe_head(Self[A]) -> A +#deprecated fn[A] T::unsafe_last(Self[A]) -> A +#deprecated fn[A : Compare] T::unsafe_maximum(Self[A]) -> A +#deprecated fn[A : Compare] T::unsafe_minimum(Self[A]) -> A +#deprecated fn[A] T::unsafe_nth(Self[A], Int) -> A +#deprecated fn[A, B] T::unzip(Self[(A, B)]) -> (Self[A], Self[B]) +#deprecated fn[A, B] T::zip(Self[A], Self[B]) -> Self[(A, B)]? impl[A] Add for T[A] impl[A : Compare] Compare for T[A] diff --git a/bundled-core/immut/list/types.mbt b/bundled-core/immut/list/types.mbt index 2c50df3..66d0b82 100644 --- a/bundled-core/immut/list/types.mbt +++ b/bundled-core/immut/list/types.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#deprecated("use `@list` instead") pub(all) enum T[A] { Nil Cons(A, T[A]) diff --git a/bundled-core/immut/priority_queue/README.mbt.md b/bundled-core/immut/priority_queue/README.mbt.md index 54c7071..ee4611f 100644 --- a/bundled-core/immut/priority_queue/README.mbt.md +++ b/bundled-core/immut/priority_queue/README.mbt.md @@ -10,7 +10,7 @@ You can use `new()` or `of()` to create an immutable priority queue. ```moonbit test { - let _queue1 : @priority_queue.T[Int] = @priority_queue.new() + let _queue1 : @priority_queue.PriorityQueue[Int] = @priority_queue.new() let _queue2 = @priority_queue.of([1, 2, 3]) } @@ -34,7 +34,7 @@ You can use the `is_empty` to determine whether the immutable priority queue is ```moonbit test { - let pq : @priority_queue.T[Int] = @priority_queue.new() + let pq : @priority_queue.PriorityQueue[Int] = @priority_queue.new() assert_eq(pq.is_empty(), true) } ``` @@ -57,7 +57,7 @@ You can use `push()` to add elements to the immutable priority queue and get a n ```moonbit test { - let pq : @priority_queue.T[Int] = @priority_queue.new() + let pq : @priority_queue.PriorityQueue[Int] = @priority_queue.new() assert_eq(pq.push(1).peek(), Some(1)) } ``` diff --git a/bundled-core/immut/priority_queue/deprecated.mbt b/bundled-core/immut/priority_queue/deprecated.mbt index 625dfd8..041e7b0 100644 --- a/bundled-core/immut/priority_queue/deprecated.mbt +++ b/bundled-core/immut/priority_queue/deprecated.mbt @@ -15,6 +15,6 @@ ///| #deprecated("Use `unsafe_pop` instead") #coverage.skip -pub fn[A : Compare] pop_exn(self : T[A]) -> T[A] { +pub fn[A : Compare] pop_exn(self : PriorityQueue[A]) -> PriorityQueue[A] { self.unsafe_pop() } diff --git a/bundled-core/immut/priority_queue/pkg.generated.mbti b/bundled-core/immut/priority_queue/pkg.generated.mbti new file mode 100644 index 0000000..4f323ee --- /dev/null +++ b/bundled-core/immut/priority_queue/pkg.generated.mbti @@ -0,0 +1,43 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/immut/priority_queue" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type PriorityQueue[A] +#as_free_fn +fn[A : Compare] PriorityQueue::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A : Compare] PriorityQueue::from_iter(Iter[A]) -> Self[A] +fn[A] PriorityQueue::is_empty(Self[A]) -> Bool +fn[A : Compare] PriorityQueue::iter(Self[A]) -> Iter[A] +fn[A] PriorityQueue::length(Self[A]) -> Int +#as_free_fn +fn[A] PriorityQueue::new() -> Self[A] +#as_free_fn +fn[A : Compare] PriorityQueue::of(FixedArray[A]) -> Self[A] +fn[A] PriorityQueue::peek(Self[A]) -> A? +fn[A : Compare] PriorityQueue::pop(Self[A]) -> Self[A]? +#deprecated +fn[A : Compare] PriorityQueue::pop_exn(Self[A]) -> Self[A] +fn[A : Compare] PriorityQueue::push(Self[A], A) -> Self[A] +fn[A : Compare] PriorityQueue::to_array(Self[A]) -> Array[A] +fn[A : Compare] PriorityQueue::unsafe_pop(Self[A]) -> Self[A] +impl[A : Compare] Compare for PriorityQueue[A] +impl[A : Compare] Eq for PriorityQueue[A] +impl[A : Hash + Compare] Hash for PriorityQueue[A] +impl[A : Show + Compare] Show for PriorityQueue[A] +impl[A : ToJson + Compare] ToJson for PriorityQueue[A] +impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for PriorityQueue[X] + +// Type aliases +pub typealias PriorityQueue as T + +// Traits + diff --git a/bundled-core/immut/priority_queue/priority_queue.mbt b/bundled-core/immut/priority_queue/priority_queue.mbt index 19c1829..39bead9 100644 --- a/bundled-core/immut/priority_queue/priority_queue.mbt +++ b/bundled-core/immut/priority_queue/priority_queue.mbt @@ -20,7 +20,8 @@ /// let queue = @priority_queue.new() /// assert_eq(queue.push(1).length(), 1) /// ``` -pub fn[A] new() -> T[A] { +#as_free_fn +pub fn[A] PriorityQueue::new() -> PriorityQueue[A] { { node: Empty, size: 0 } } @@ -30,9 +31,12 @@ pub fn[A] new() -> T[A] { /// # Example /// ```mbt /// let queue = @priority_queue.of([1, 2, 3, 4, 5]) -/// assert_eq(queue.length(), 5) +/// inspect(queue.length(), content="5") /// ``` -pub fn[A : Compare] from_array(array : Array[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] PriorityQueue::from_array( + array : Array[A], +) -> PriorityQueue[A] { let mut pq = new() for i in 0.. T[A] { } ///| -pub fn[A : Compare] to_array(self : T[A]) -> Array[A] { +pub fn[A : Compare] to_array(self : PriorityQueue[A]) -> Array[A] { let arr : Array[A] = [] - fn go(x : Node[A]) { - match x { - Empty => return + let stack : Array[Node[A]] = [self.node] + while stack.pop() is Some(node) { + match node { + Empty => () Leaf(a) => arr.push(a) Branch(a, left=l, right=r) => { arr.push(a) - go(l) - go(r) + stack.push(l) + stack.push(r) } } } - - go(self.node) - arr.sort_by((x, y) => y.compare(x)) + arr.sort() + arr.rev_inplace() arr } ///| -pub fn[A : Compare] iter(self : T[A]) -> Iter[A] { +pub fn[A : Compare] iter(self : PriorityQueue[A]) -> Iter[A] { Iter::new(yield_ => { let arr = self.to_array() for i in 0.. Iter[A] { } ///| -pub fn[A : Compare] T::from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] PriorityQueue::from_iter( + iter : Iter[A], +) -> PriorityQueue[A] { iter.fold(init=new(), (s, e) => s.push(e)) } ///| -priv type Path Int +priv struct Path(Int) ///| /// require: size >= 2 @@ -91,12 +98,12 @@ fn path(size : Int) -> Path { ///| fn is_left(self : Path) -> Bool { - (self.inner() & 1) == 0 + (self.0 & 1) == 0 } ///| fn next(self : Path) -> Path { - Path(self.inner() >> 1) + Path(self.0 >> 1) } ///| @@ -108,7 +115,7 @@ fn next(self : Path) -> Path { /// let first = queue.pop() /// assert_eq(first, Some(@priority_queue.of([1, 2, 3]))) /// ``` -pub fn[A : Compare] pop(self : T[A]) -> T[A]? { +pub fn[A : Compare] pop(self : PriorityQueue[A]) -> PriorityQueue[A]? { match self.node { Empty => None Leaf(_) => Some({ node: Empty, size: 0 }) @@ -173,7 +180,7 @@ fn[A : Compare] change_and_down(self : Node[A], value : A) -> Node[A] { /// assert_eq(first, @priority_queue.of([1, 2, 3])) /// ``` #internal(unsafe, "Panics if the queue is empty.") -pub fn[A : Compare] unsafe_pop(self : T[A]) -> T[A] { +pub fn[A : Compare] unsafe_pop(self : PriorityQueue[A]) -> PriorityQueue[A] { match self.node { Empty => abort("Priority queue is empty!") Leaf(_) => { node: Empty, size: 0 } @@ -184,14 +191,18 @@ pub fn[A : Compare] unsafe_pop(self : T[A]) -> T[A] { } } -///| Adds a value to the immutable priority queue. +///| +/// Adds a value to the immutable priority queue. /// /// # Example /// ```mbt /// let queue = @priority_queue.new() /// assert_eq(queue.push(1).length(), 1) /// ``` -pub fn[A : Compare] push(self : T[A], value : A) -> T[A] { +pub fn[A : Compare] push( + self : PriorityQueue[A], + value : A, +) -> PriorityQueue[A] { match self.node { Empty => { node: Leaf(value), size: 1 } Leaf(_) | Branch(_) => { @@ -228,7 +239,7 @@ fn[A : Compare] Node::push(self : Node[A], value : A, path : Path) -> Node[A] { /// let queue = @priority_queue.of([1, 2, 3, 4]) /// assert_eq(queue.peek(), Some(4)) /// ``` -pub fn[A] peek(self : T[A]) -> A? { +pub fn[A] peek(self : PriorityQueue[A]) -> A? { match self.node { Empty => None Leaf(a) => Some(a) @@ -242,10 +253,10 @@ pub fn[A] peek(self : T[A]) -> A? { /// # Example /// ```mbt /// let queue = @priority_queue.new() -/// assert_eq(queue.is_empty(), true) +/// inspect(queue.is_empty(), content="true") /// assert_eq(queue.push(1).is_empty(), false) /// ``` -pub fn[A] is_empty(self : T[A]) -> Bool { +pub fn[A] is_empty(self : PriorityQueue[A]) -> Bool { self.node is Empty } @@ -255,15 +266,16 @@ pub fn[A] is_empty(self : T[A]) -> Bool { /// # Example /// ```mbt /// let queue = @priority_queue.new() -/// assert_eq(queue.length(), 0) +/// inspect(queue.length(), content="0") /// assert_eq(queue.push(1).length(), 1) /// ``` -pub fn[A] length(self : T[A]) -> Int { +pub fn[A] length(self : PriorityQueue[A]) -> Int { self.size } ///| -pub fn[A : Compare] of(arr : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] PriorityQueue::of(arr : FixedArray[A]) -> PriorityQueue[A] { let mut pq = new() for i in 0.. T[A] { } ///| -pub impl[A : Show + Compare] Show for T[A] with output(self, logger) { +pub impl[A : Show + Compare] Show for PriorityQueue[A] with output(self, logger) { logger.write_iter( self.iter(), prefix="@immut/priority_queue.of([", @@ -281,27 +293,29 @@ pub impl[A : Show + Compare] Show for T[A] with output(self, logger) { } ///| -pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] with arbitrary( - size, - rs -) { +pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for PriorityQueue[ + X, +] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } ///| -pub impl[A : Compare] Eq for T[A] with op_equal(self, other) { +pub impl[A : Compare] Eq for PriorityQueue[A] with equal(self, other) { self.length() == other.length() && self.to_array() == other.to_array() } ///| -pub impl[A : Hash + Compare] Hash for T[A] with hash_combine(self, hasher) { +pub impl[A : Hash + Compare] Hash for PriorityQueue[A] with hash_combine( + self, + hasher, +) { for e in self { hasher.combine(e) } } ///| -/// Compare two priority queues lexicographically by comparing their sorted contents. +/// Compare two priority queues based on shortlex order by comparing their sorted contents. /// /// Parameters: /// @@ -320,12 +334,12 @@ pub impl[A : Hash + Compare] Hash for T[A] with hash_combine(self, hasher) { /// let pq1 = @priority_queue.of([1, 2, 3]) /// let pq2 = @priority_queue.of([1, 2, 4]) /// let pq3 = @priority_queue.of([1, 2]) -/// assert_eq(pq1.compare(pq2), -1) // pq1 < pq2 -/// assert_eq(pq1.compare(pq3), 1) // pq2 > pq1 -/// assert_eq(pq3.compare(pq1), -1) // pq1 > pq3 (longer) -/// assert_eq(pq1.compare(pq1), 0) // pq1 = pq1 +/// inspect(pq1.compare(pq2), content="-1") // pq1 < pq2 +/// inspect(pq1.compare(pq3), content="1") // pq2 > pq1 +/// inspect(pq3.compare(pq1), content="-1") // pq1 > pq3 (longer) +/// inspect(pq1.compare(pq1), content="0") // pq1 = pq1 /// ``` -pub impl[A : Compare] Compare for T[A] with compare(self, other) { +pub impl[A : Compare] Compare for PriorityQueue[A] with compare(self, other) { let len_cmp = self.length().compare(other.length()) if len_cmp != 0 { return len_cmp diff --git a/bundled-core/immut/priority_queue/priority_queue.mbti b/bundled-core/immut/priority_queue/priority_queue.mbti deleted file mode 100644 index 5634692..0000000 --- a/bundled-core/immut/priority_queue/priority_queue.mbti +++ /dev/null @@ -1,37 +0,0 @@ -package "moonbitlang/core/immut/priority_queue" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[A : Compare] from_array(Array[A]) -> T[A] - -fn[A] new() -> T[A] - -fn[A : Compare] of(FixedArray[A]) -> T[A] - -// Types and methods -type T[A] -fn[A : Compare] T::from_iter(Iter[A]) -> Self[A] -fn[A] T::is_empty(Self[A]) -> Bool -fn[A : Compare] T::iter(Self[A]) -> Iter[A] -fn[A] T::length(Self[A]) -> Int -fn[A] T::peek(Self[A]) -> A? -fn[A : Compare] T::pop(Self[A]) -> Self[A]? -#deprecated -fn[A : Compare] T::pop_exn(Self[A]) -> Self[A] -fn[A : Compare] T::push(Self[A], A) -> Self[A] -fn[A : Compare] T::to_array(Self[A]) -> Array[A] -fn[A : Compare] T::unsafe_pop(Self[A]) -> Self[A] -impl[A : Compare] Compare for T[A] -impl[A : Compare] Eq for T[A] -impl[A : Hash + Compare] Hash for T[A] -impl[A : Show + Compare] Show for T[A] -impl[A : ToJson + Compare] ToJson for T[A] -impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/immut/priority_queue/priority_queue_test.mbt b/bundled-core/immut/priority_queue/priority_queue_test.mbt index 4110327..9ffb51a 100644 --- a/bundled-core/immut/priority_queue/priority_queue_test.mbt +++ b/bundled-core/immut/priority_queue/priority_queue_test.mbt @@ -30,7 +30,7 @@ test "to_array" { inspect(v.to_array(), content="[4, 3, 2, 1]") inspect(v.push(0).to_array(), content="[4, 3, 2, 1, 0]") inspect( - (@priority_queue.new() : @priority_queue.T[Int]).to_array(), + (@priority_queue.new() : @priority_queue.PriorityQueue[Int]).to_array(), content="[]", ) } @@ -57,7 +57,7 @@ test "pop" { }, content="Some(3)", ) - let empty : @priority_queue.T[Int] = @priority_queue.of([]) + let empty : @priority_queue.PriorityQueue[Int] = @priority_queue.of([]) inspect( match empty.pop() { Some(q) => q.peek() @@ -117,7 +117,7 @@ test "length" { ///| test "from_iter multiple elements iter" { inspect( - @priority_queue.T::from_iter([1, 2, 3].iter()), + @priority_queue.PriorityQueue::from_iter([1, 2, 3].iter()), content="@immut/priority_queue.of([3, 2, 1])", ) } @@ -125,14 +125,16 @@ test "from_iter multiple elements iter" { ///| test "from_iter single element iter" { inspect( - @priority_queue.T::from_iter([1].iter()), + @priority_queue.PriorityQueue::from_iter([1].iter()), content="@immut/priority_queue.of([1])", ) } ///| test "from_iter empty iter" { - let pq : @priority_queue.T[Int] = @priority_queue.T::from_iter(Iter::empty()) + let pq : @priority_queue.PriorityQueue[Int] = @priority_queue.PriorityQueue::from_iter( + Iter::empty(), + ) inspect(pq, content="@immut/priority_queue.of([])") } @@ -143,7 +145,7 @@ test "hash" { inspect(pq1.hash() == pq2.hash(), content="true") let pq3 = @priority_queue.of([5, 4, 3, 2, 1]) inspect(pq1.hash() == pq3.hash(), content="true") - let pq4 : @priority_queue.T[Int] = @priority_queue.new() + let pq4 : @priority_queue.PriorityQueue[Int] = @priority_queue.new() inspect(pq1.hash() == pq4.hash(), content="false") inspect(pq4.hash() == pq4.hash(), content="true") let pq5 = @priority_queue.of([1, 2, 3, 4, 6]) @@ -158,7 +160,7 @@ test "equal" { inspect(pq1 == pq2, content="true") let pq3 = @priority_queue.of([5, 4, 3, 2, 1]) inspect(pq1 == pq3, content="true") - let pq4 : @priority_queue.T[Int] = @priority_queue.new() + let pq4 : @priority_queue.PriorityQueue[Int] = @priority_queue.new() inspect(pq1 == pq4, content="false") let pq5 = @priority_queue.of([1, 2, 3]) inspect(pq1 == pq5, content="false") @@ -173,17 +175,17 @@ test "complex" { let arr = (0).until(1000, inclusive=true).collect() arr.shuffle_in_place(rand~) - let mut pq = new() + let mut pq = @priority_queue.new() for i in 0..=1000 { pq = pq.push(arr[i]) } - assert_eq(pq.length(), 1001) + inspect(pq.length(), content="1001") for i in 0..=1000 { assert_eq(pq.peek(), Some(1000 - i)) pq = pq.pop().unwrap() } inspect(pq.iter(), content="[]") - assert_eq(pq.length(), 0) + inspect(pq.length(), content="0") } ///| @@ -191,12 +193,12 @@ test "compare" { let pq1 = @priority_queue.of([1, 2, 3]) let pq2 = @priority_queue.of([1, 2, 4]) let pq3 = @priority_queue.of([1, 2]) - let empty : @priority_queue.T[Int] = @priority_queue.new() - assert_eq(pq1.compare(pq2), -1) - assert_eq(pq1.compare(pq3), 1) - assert_eq(pq3.compare(pq1), -1) - assert_eq(pq1.compare(pq1), 0) - assert_eq(empty.compare(empty), 0) + let empty : @priority_queue.PriorityQueue[Int] = @priority_queue.new() + inspect(pq1.compare(pq2), content="-1") + inspect(pq1.compare(pq3), content="1") + inspect(pq3.compare(pq1), content="-1") + inspect(pq1.compare(pq1), content="0") + inspect(empty.compare(empty), content="0") } ///| diff --git a/bundled-core/immut/priority_queue/types.mbt b/bundled-core/immut/priority_queue/types.mbt index 01b8056..b369282 100644 --- a/bundled-core/immut/priority_queue/types.mbt +++ b/bundled-core/immut/priority_queue/types.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -struct T[A] { +struct PriorityQueue[A] { node : Node[A] size : Int } @@ -26,7 +26,9 @@ priv enum Node[A] { } ///| -pub impl[A : ToJson + Compare] ToJson for T[A] with to_json(self : T[A]) { +pub impl[A : ToJson + Compare] ToJson for PriorityQueue[A] with to_json( + self : PriorityQueue[A], +) { // note here iter requires Compare // This requires some investigation // CR: this syntax feels less intuitive @@ -36,3 +38,7 @@ pub impl[A : ToJson + Compare] ToJson for T[A] with to_json(self : T[A]) { } Json::array(output) } + +///| +#deprecated("Use `PriorityQueue` instead of `T`") +pub typealias PriorityQueue as T diff --git a/bundled-core/immut/sorted_map/README.mbt.md b/bundled-core/immut/sorted_map/README.mbt.md index b2e68f5..317caec 100644 --- a/bundled-core/immut/sorted_map/README.mbt.md +++ b/bundled-core/immut/sorted_map/README.mbt.md @@ -1,4 +1,3 @@ - # Immutable Map An immutable tree map based on size balanced tree. @@ -7,11 +6,12 @@ An immutable tree map based on size balanced tree. ## Create -You can create an empty map using `new()` or construct it with a single key-value pair using `singleton()`. +You can create an empty map using `new()` or construct it with a single +key-value pair using `singleton()`. ```moonbit test { - let map1 : @sorted_map.T[String, Int] = @sorted_map.new() + let map1 : @sorted_map.SortedMap[String, Int] = @sorted_map.new() let map2 = @sorted_map.singleton("a", 1) assert_eq(map1.size(), 0) assert_eq(map2.size(), 1) @@ -24,17 +24,18 @@ Also, you can construct it from an array using `of()` or `from_array()`. test { let map = @sorted_map.of([("a", 1), ("b", 2), ("c", 3)]) assert_eq(map.values().collect(), [1, 2, 3]) - assert_eq(map.keys(), ["a", "b", "c"]) + assert_eq(map.keys_as_iter().collect(), ["a", "b", "c"]) } ``` ## Insert & Lookup -You can use `add()` to add a key-value pair to the map and create a new map. Or use `lookup()` to get the value associated with a key. +You can use `add()` to add a key-value pair to the map and create a new map. Or +use `lookup()` to get the value associated with a key. ```moonbit test { - let map : @sorted_map.T[String,Int] = @sorted_map.new() + let map : @sorted_map.SortedMap[String,Int] = @sorted_map.new() let map = map.add("a", 1) assert_eq(map.get("a"), Some(1)) } @@ -79,7 +80,7 @@ Similarly, you can use `is_empty()` to check whether the map is empty. ```moonbit test { - let map : @sorted_map.T[String, Int] = @sorted_map.new() + let map : @sorted_map.SortedMap[String, Int] = @sorted_map.new() assert_eq(map.is_empty(), true) } ``` @@ -100,41 +101,47 @@ test { } ``` -Use `map()` or `map_with_key()` to map a function over all values. +Use `map_with_key()` to map a function over all values. ```moonbit test { let map = @sorted_map.of([("a", 1), ("b", 2), ("c", 3)]) - let map = map.map((v) => { v + 1 }) + let map = map.map_with_key((_, v) => { v + 1 }) assert_eq(map.values().collect(), [2, 3, 4]) let map = map.map_with_key((_k, v) => { v + 1 }) assert_eq(map.values().collect(), [3, 4, 5]) } ``` -Use `fold()` or `foldl_with_key()` to fold the values in the map. The default order of fold is Pre-order. -Similarly, you can use `foldr_with_key()` to do a Post-order fold. +Use `foldl_with_key()` to fold the values in the map. The default order of fold +is Pre-order. Similarly, you can use `rev_fold()` to do a Post-order fold. ```moonbit test { let map = @sorted_map.of([("a", 1), ("b", 2), ("c", 3)]) - assert_eq(map.fold((acc, v) => { acc + v }, init=0), 6) // 6 - assert_eq(map.foldl_with_key((acc, k, v) => { acc + k + v.to_string() }, init=""), "a1b2c3") // "a1b2c3" - assert_eq(map.foldr_with_key((acc, k, v) => { acc + k + v.to_string() }, init=""), "c3b2a1") // "c3b2a1" + assert_eq(map.foldl_with_key((acc, _, v) => acc + v, init=0), 6) // 6 + assert_eq( + map.foldl_with_key((acc, k, v) => acc + k + v.to_string(), init=""), + "a1b2c3", + ) // "a1b2c3" + assert_eq( + map.rev_fold((acc, k, v) => acc + k + v.to_string(), init=""), + "c3b2a1", + ) // "c3b2a1" } ``` -Use `filter()` or `filter_with_key()` to filter all keys/values that satisfy the predicate. +Use `filter_with_key()` to filter all keys/values that satisfy the predicate. ```moonbit test { let map = @sorted_map.of([("a", 1), ("b", 2), ("c", 3)]) - let map = map.filter((v) => { v > 1 }) + let map = map.filter_with_key((_, v) => { v > 1 }) assert_eq(map.values().collect(), [2, 3]) - assert_eq(map.keys(), ["b", "c"]) + assert_eq(map.keys_as_iter().collect(), ["b", "c"]) let map = map.filter_with_key((k, v) => { k > "a" && v > 1 }) assert_eq(map.values().collect(), [2, 3]) - assert_eq(map.keys(), ["b", "c"]) + assert_eq(map.keys_as_iter().collect(), ["b", "c"]) } ``` @@ -155,8 +162,7 @@ Use `keys()` to get all keys of the map in ascending order. ```moonbit test { let map = @sorted_map.of([("a", 1), ("b", 2), ("c", 3)]) - let keys = map.keys() // ["a", "b", "c"] - assert_eq(keys, ["a", "b", "c"]) + let keys = map.keys_as_iter() // ["a", "b", "c"] + assert_eq(keys.collect(), ["a", "b", "c"]) } ``` - diff --git a/bundled-core/immut/sorted_map/deprecated.mbt b/bundled-core/immut/sorted_map/deprecated.mbt deleted file mode 100644 index 53ffdba..0000000 --- a/bundled-core/immut/sorted_map/deprecated.mbt +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -/// Create a new map with a key-value pair inserted. -/// O(log n). -/// -#deprecated("Use `add` instead") -#coverage.skip -pub fn[K : Compare, V] insert(self : T[K, V], key : K, value : V) -> T[K, V] { - self.add(key, value) -} - -///| -#deprecated("use `@immut/sorted_map.new` instead") -#coverage.skip -pub fn[K, V] T::new() -> T[K, V] { - Empty -} - -///| -#deprecated("use `@immut/sorted_map.singleton` instead") -#coverage.skip -pub fn[K, V] T::singleton(key : K, value : V) -> T[K, V] { - singleton(key, value) -} - -///| -#deprecated("use `@immut/sorted_map.from_array` instead") -#coverage.skip -pub fn[K : Compare, V] T::from_array(array : Array[(K, V)]) -> T[K, V] { - from_array(array) -} - -///| -#deprecated("use `@immut/sorted_map.from_iter` instead") -#coverage.skip -pub fn[K : Compare, V] T::from_iter(iter : Iter[(K, V)]) -> T[K, V] { - from_iter(iter) -} - -///| -#deprecated("use `@immut/sorted_map.of` instead") -#coverage.skip -pub fn[K : Compare, V] T::of(array : FixedArray[(K, V)]) -> T[K, V] { - of(array) -} - -///| -#deprecated("use `@immut/sorted_map.from_json` instead") -#coverage.skip -pub fn[V : @json.FromJson] T::from_json( - json : Json -) -> T[String, V] raise @json.JsonDecodeError { - @json.from_json(json) -} diff --git a/bundled-core/immut/sorted_map/inorder_iterator.mbt b/bundled-core/immut/sorted_map/inorder_iterator.mbt index bf7dbdc..f5a1ab3 100644 --- a/bundled-core/immut/sorted_map/inorder_iterator.mbt +++ b/bundled-core/immut/sorted_map/inorder_iterator.mbt @@ -13,10 +13,10 @@ // limitations under the License. ///| -priv type InorderIterator[K, V] Array[T[K, V]] +priv struct InorderIterator[K, V](Array[SortedMap[K, V]]) ///| -fn[K, V] InorderIterator::new(root : T[K, V]) -> InorderIterator[K, V] { +fn[K, V] InorderIterator::new(root : SortedMap[K, V]) -> InorderIterator[K, V] { let it = InorderIterator([]) it.move_left(root) it @@ -25,7 +25,7 @@ fn[K, V] InorderIterator::new(root : T[K, V]) -> InorderIterator[K, V] { ///| fn[K, V] InorderIterator::move_left( self : InorderIterator[K, V], - node : T[K, V] + node : SortedMap[K, V], ) -> Unit { loop node { Empty => () diff --git a/bundled-core/immut/sorted_map/map.mbt b/bundled-core/immut/sorted_map/map.mbt index 60eae32..d370186 100644 --- a/bundled-core/immut/sorted_map/map.mbt +++ b/bundled-core/immut/sorted_map/map.mbt @@ -16,7 +16,12 @@ /// Create a new map with a key-value pair inserted. /// O(log n). /// -pub fn[K : Compare, V] add(self : T[K, V], key : K, value : V) -> T[K, V] { +#alias(insert, deprecated) +pub fn[K : Compare, V] add( + self : SortedMap[K, V], + key : K, + value : V, +) -> SortedMap[K, V] { match self { Empty => singleton(key, value) Tree(k, value=v, l, r, ..) => { @@ -33,7 +38,7 @@ pub fn[K : Compare, V] add(self : T[K, V], key : K, value : V) -> T[K, V] { } ///| -fn[K : Compare, V] split_max(self : T[K, V]) -> (K, V, T[K, V]) { +fn[K : Compare, V] split_max(self : SortedMap[K, V]) -> (K, V, SortedMap[K, V]) { match self { Tree(k, value=v, l, Empty, ..) => (k, v, l) Tree(k, value=v, l, r, ..) => { @@ -45,7 +50,7 @@ fn[K : Compare, V] split_max(self : T[K, V]) -> (K, V, T[K, V]) { } ///| -fn[K : Compare, V] split_min(self : T[K, V]) -> (K, V, T[K, V]) { +fn[K : Compare, V] split_min(self : SortedMap[K, V]) -> (K, V, SortedMap[K, V]) { match self { Tree(k, value=v, Empty, r, ..) => (k, v, r) Tree(k, value=v, l, r, ..) => { @@ -57,7 +62,10 @@ fn[K : Compare, V] split_min(self : T[K, V]) -> (K, V, T[K, V]) { } ///| -fn[K : Compare, V] glue(l : T[K, V], r : T[K, V]) -> T[K, V] { +fn[K : Compare, V] glue( + l : SortedMap[K, V], + r : SortedMap[K, V], +) -> SortedMap[K, V] { match (l, r) { (Empty, r) => r (l, Empty) => l @@ -75,7 +83,10 @@ fn[K : Compare, V] glue(l : T[K, V], r : T[K, V]) -> T[K, V] { ///| /// Create a new map with a key-value pair removed. O(log n). /// If the key is not a member or map, the original map is returned. -pub fn[K : Compare, V] remove(self : T[K, V], key : K) -> T[K, V] { +pub fn[K : Compare, V] remove( + self : SortedMap[K, V], + key : K, +) -> SortedMap[K, V] { match self { Empty => Empty Tree(k, value=v, l, r, ..) => { @@ -93,19 +104,21 @@ pub fn[K : Compare, V] remove(self : T[K, V], key : K) -> T[K, V] { ///| /// Filter values that satisfy the predicate +#deprecated("Use `filter_with_key` instead. `filter` will accept `(K, V) -> Bool` in the future.") +#coverage.skip pub fn[K : Compare, V] filter( - self : T[K, V], - pred : (V) -> Bool raise? -) -> T[K, V] raise? { + self : SortedMap[K, V], + pred : (V) -> Bool raise?, +) -> SortedMap[K, V] raise? { self.filter_with_key((_k, v) => pred(v)) } ///| /// Filter key-value pairs that satisfy the predicate pub fn[K : Compare, V] filter_with_key( - self : T[K, V], - pred : (K, V) -> Bool raise? -) -> T[K, V] raise? { + self : SortedMap[K, V], + pred : (K, V) -> Bool raise?, +) -> SortedMap[K, V] raise? { match self { Empty => Empty Tree(k, value=v, l, r, ..) => @@ -119,7 +132,7 @@ pub fn[K : Compare, V] filter_with_key( ///| /// Convert to an array of key-value pairs. -pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { +pub fn[K, V] to_array(self : SortedMap[K, V]) -> Array[(K, V)] { let arr = [] self.each((k, v) => arr.push((k, v))) arr @@ -130,7 +143,12 @@ pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { let ratio = 5 ///| -fn[K, V] balance(key : K, value : V, l : T[K, V], r : T[K, V]) -> T[K, V] { +fn[K, V] balance( + key : K, + value : V, + l : SortedMap[K, V], + r : SortedMap[K, V], +) -> SortedMap[K, V] { // 1 2 // / \ / \ // x 2 ---> 1 z @@ -233,7 +251,7 @@ test "remove" { m3.debug_tree(), content="(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))", ) - let e : T[Int, Int] = Empty + let e : SortedMap[Int, Int] = Empty inspect(e.remove(1).debug_tree(), content="_") } @@ -244,9 +262,9 @@ test "contains" { m.debug_tree(), content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", ) - assert_eq(m.contains(8), true) - assert_eq(m.contains(2), true) - assert_eq(m.contains(4), false) + inspect(m.contains(8), content="true") + inspect(m.contains(2), content="true") + inspect(m.contains(4), content="false") } ///| @@ -273,20 +291,20 @@ test "map_with_key" { test "filter" { let m = of([(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")]) let fm = m.filter(v => v.length() > 3) - assert_eq(fm.debug_tree(), "(3,three,(0,zero,_,_),(8,eight,_,_))") + inspect(fm.debug_tree(), content="(3,three,(0,zero,_,_),(8,eight,_,_))") } ///| test "filter_with_key" { let m = of([(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")]) let fm = m.filter_with_key((k, v) => k > 3 && v.length() > 3) - assert_eq(fm.debug_tree(), "(8,eight,_,_)") + inspect(fm.debug_tree(), content="(8,eight,_,_)") } ///| test "singleton" { let m = singleton(3, "three") - assert_eq(m.debug_tree(), "(3,three,_,_)") + inspect(m.debug_tree(), content="(3,three,_,_)") } ///| @@ -308,27 +326,27 @@ test "remove" { content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", ) let m = m.remove(1).remove(3) - assert_eq(m.debug_tree(), "(2,two,(0,zero,_,_),(8,eight,_,_))") + inspect(m.debug_tree(), content="(2,two,(0,zero,_,_),(8,eight,_,_))") } ///| test "filter" { let m = of([(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")]) let fm = m.filter(v => v.length() > 3) - assert_eq(fm.debug_tree(), "(3,three,(0,zero,_,_),(8,eight,_,_))") + inspect(fm.debug_tree(), content="(3,three,(0,zero,_,_),(8,eight,_,_))") } ///| test "filter_with_key" { let m = of([(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")]) let fm = m.filter_with_key((k, v) => k > 3 && v.length() > 3) - assert_eq(fm.debug_tree(), "(8,eight,_,_)") + inspect(fm.debug_tree(), content="(8,eight,_,_)") } ///| test "empty" { - let m : T[Int, Int] = new() - assert_eq(m.debug_tree(), "_") + let m : SortedMap[Int, Int] = new() + inspect(m.debug_tree(), content="_") } ///| @@ -339,9 +357,12 @@ test "split_max" { "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", ) let (k, v, r) = m.split_max() - assert_eq(k, 8) - assert_eq(v, "eight") - assert_eq(r.debug_tree(), "(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))") + inspect(k, content="8") + inspect(v, content="eight") + inspect( + r.debug_tree(), + content="(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))", + ) } ///| @@ -352,9 +373,12 @@ test "split_min" { "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", ) let (k, v, r) = m.split_min() - assert_eq(k, 0) - assert_eq(v, "zero") - assert_eq(r.debug_tree(), "(3,three,(1,one,_,(2,two,_,_)),(8,eight,_,_))") + inspect(k, content="0") + inspect(v, content="zero") + inspect( + r.debug_tree(), + content="(3,three,(1,one,_,(2,two,_,_)),(8,eight,_,_))", + ) } ///| @@ -369,7 +393,10 @@ test "glue" { _ => abort("unreachable") } let m = glue(l, r) - assert_eq(m.debug_tree(), "(2,two,(1,one,(0,zero,_,_),_),(8,eight,_,_))") + inspect( + m.debug_tree(), + content="(2,two,(1,one,(0,zero,_,_),_),(8,eight,_,_))", + ) } ///| diff --git a/bundled-core/immut/sorted_map/map_test.mbt b/bundled-core/immut/sorted_map/map_test.mbt index a76ce41..2d022f6 100644 --- a/bundled-core/immut/sorted_map/map_test.mbt +++ b/bundled-core/immut/sorted_map/map_test.mbt @@ -35,17 +35,17 @@ test "size" { (2, "two"), (0, "zero"), ]) - assert_eq(m.size(), 5) + inspect(m.size(), content="5") let m = m.remove(1).remove(3) - assert_eq(m.size(), 3) + inspect(m.size(), content="3") } ///| test "is_empty" { - let m : @sorted_map.T[Int, Int] = @sorted_map.new() - assert_eq(m.is_empty(), true) + let m : @sorted_map.SortedMap[Int, Int] = @sorted_map.new() + inspect(m.is_empty(), content="true") let m = m.add(1, 1) - assert_eq(m.is_empty(), false) + inspect(m.is_empty(), content="false") } ///| @@ -59,7 +59,7 @@ test "iter" { ]) let mut s = "" m.each((k, v) => s = s + "(\{k},\{v})") - assert_eq(s, "(0,zero)(1,one)(2,two)(3,three)(8,eight)") + inspect(s, content="(0,zero)(1,one)(2,two)(3,three)(8,eight)") } ///| @@ -73,13 +73,13 @@ test "iteri" { ]) let mut s = "" m.eachi((i, k, v) => s = s + "(\{i},\{k},\{v})") - assert_eq(s, "(0,0,zero)(1,1,one)(2,2,two)(3,3,three)(4,8,eight)") + inspect(s, content="(0,0,zero)(1,1,one)(2,2,two)(3,3,three)(4,8,eight)") } ///| test "fold" { let m = @sorted_map.of([("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)]) - assert_eq(m.fold((acc, v) => acc + v, init=0), 15) + assert_eq(m.foldl_with_key((acc, _, v) => acc + v, init=0), 15) } ///| @@ -109,7 +109,7 @@ test "to_array" { ///| test "keys" { let m = @sorted_map.of([(1, "one"), (2, "two"), (3, "three")]) - assert_eq(m.keys(), [1, 2, 3]) + assert_eq(m.keys_as_iter().collect(), [1, 2, 3]) } ///| @@ -135,13 +135,13 @@ test "op_equal" { (0, "zero"), ]) let m3 = @sorted_map.of([(3, "three"), (8, "eight"), (1, "one"), (2, "two")]) - assert_eq(m1 == m2, true) - assert_eq(m1 == m3, false) + inspect(m1 == m2, content="true") + inspect(m1 == m3, content="false") } ///| test "compare" { - let xss : Array[@sorted_map.T[Int, Int]] = @quickcheck.samples(5) + let xss : Array[@sorted_map.SortedMap[Int, Int]] = @quickcheck.samples(5) for xs in xss { for ys in xss { assert_eq(xs.compare(ys), xs.to_array().compare(ys.to_array())) @@ -194,8 +194,8 @@ test "op_equal" { (0, "zero"), ]) let m3 = @sorted_map.of([(3, "three"), (8, "eight"), (1, "one"), (2, "two")]) - assert_eq(m1 == m2, true) - assert_eq(m1 == m3, false) + inspect(m1 == m2, content="true") + inspect(m1 == m3, content="false") } ///| @@ -225,7 +225,9 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @sorted_map.T[Int, Int] = @sorted_map.from_iter(Iter::empty()) + let pq : @sorted_map.SortedMap[Int, Int] = @sorted_map.from_iter( + Iter::empty(), + ) inspect(pq, content="@immut/sorted_map.of([])") } diff --git a/bundled-core/immut/sorted_map/panic_wbtest.mbt b/bundled-core/immut/sorted_map/panic_wbtest.mbt index 1f22e79..609a39f 100644 --- a/bundled-core/immut/sorted_map/panic_wbtest.mbt +++ b/bundled-core/immut/sorted_map/panic_wbtest.mbt @@ -14,10 +14,10 @@ ///| test "panic split_max with empty tree" { - (Empty : T[Int, Int]).split_max() |> ignore + (Empty : SortedMap[Int, Int]).split_max() |> ignore } ///| test "panic split_min with empty tree" { - (Empty : T[Int, Int]).split_min() |> ignore + (Empty : SortedMap[Int, Int]).split_min() |> ignore } diff --git a/bundled-core/immut/sorted_map/pkg.generated.mbti b/bundled-core/immut/sorted_map/pkg.generated.mbti new file mode 100644 index 0000000..793b5a7 --- /dev/null +++ b/bundled-core/immut/sorted_map/pkg.generated.mbti @@ -0,0 +1,75 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/immut/sorted_map" + +import( + "moonbitlang/core/json" + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type SortedMap[K, V] +#alias(insert, deprecated) +fn[K : Compare, V] SortedMap::add(Self[K, V], K, V) -> Self[K, V] +fn[K : Compare, V] SortedMap::contains(Self[K, V], K) -> Bool +fn[K, V] SortedMap::each(Self[K, V], (K, V) -> Unit) -> Unit +fn[K, V] SortedMap::eachi(Self[K, V], (Int, K, V) -> Unit) -> Unit +#deprecated +fn[K, V] SortedMap::elems(Self[K, V]) -> Array[V] +#deprecated +fn[K, V] SortedMap::empty() -> Self[K, V] +#deprecated +fn[K : Compare, V] SortedMap::filter(Self[K, V], (V) -> Bool raise?) -> Self[K, V] raise? +fn[K : Compare, V] SortedMap::filter_with_key(Self[K, V], (K, V) -> Bool raise?) -> Self[K, V] raise? +#deprecated +fn[K, V, A] SortedMap::fold(Self[K, V], init~ : A, (A, V) -> A) -> A +fn[K, V, A] SortedMap::foldl_with_key(Self[K, V], (A, K, V) -> A, init~ : A) -> A +#as_free_fn +fn[K : Compare, V] SortedMap::from_array(Array[(K, V)]) -> Self[K, V] +#as_free_fn +fn[K : Compare, V] SortedMap::from_iter(Iter[(K, V)]) -> Self[K, V] +#as_free_fn +fn[V : @json.FromJson] SortedMap::from_json(Json) -> Self[String, V] raise @json.JsonDecodeError +fn[K : Compare, V] SortedMap::get(Self[K, V], K) -> V? +fn[K, V] SortedMap::is_empty(Self[K, V]) -> Bool +fn[K, V] SortedMap::iter(Self[K, V]) -> Iter[(K, V)] +fn[K, V] SortedMap::iter2(Self[K, V]) -> Iter2[K, V] +#deprecated +fn[K, V] SortedMap::keys(Self[K, V]) -> Array[K] +fn[K, V] SortedMap::keys_as_iter(Self[K, V]) -> Iter[K] +#deprecated +fn[K : Compare, V] SortedMap::lookup(Self[K, V], K) -> V? +#deprecated +fn[K, X, Y] SortedMap::map(Self[K, X], (X) -> Y) -> Self[K, Y] +fn[K, X, Y] SortedMap::map_with_key(Self[K, X], (K, X) -> Y) -> Self[K, Y] +#as_free_fn +fn[K, V] SortedMap::new() -> Self[K, V] +#as_free_fn +fn[K : Compare, V] SortedMap::of(FixedArray[(K, V)]) -> Self[K, V] +fn[K : Compare, V] SortedMap::op_get(Self[K, V], K) -> V +fn[K : Compare, V] SortedMap::remove(Self[K, V], K) -> Self[K, V] +#alias(foldr_with_key) +fn[K, V, A] SortedMap::rev_fold(Self[K, V], (A, K, V) -> A, init~ : A) -> A +#as_free_fn +fn[K, V] SortedMap::singleton(K, V) -> Self[K, V] +fn[K, V] SortedMap::size(Self[K, V]) -> Int +fn[K, V] SortedMap::to_array(Self[K, V]) -> Array[(K, V)] +fn[K : Show, V : ToJson] SortedMap::to_json(Self[K, V]) -> Json +fn[K, V] SortedMap::values(Self[K, V]) -> Iter[V] +impl[K : Compare, V : Compare] Compare for SortedMap[K, V] +impl[K, V] Default for SortedMap[K, V] +impl[K : Eq, V : Eq] Eq for SortedMap[K, V] +impl[K : Hash, V : Hash] Hash for SortedMap[K, V] +impl[K : Show, V : Show] Show for SortedMap[K, V] +impl[K : Show, V : ToJson] ToJson for SortedMap[K, V] +impl[V : @json.FromJson] @json.FromJson for SortedMap[String, V] +impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for SortedMap[K, V] + +// Type aliases +pub typealias SortedMap as T + +// Traits + diff --git a/bundled-core/immut/sorted_map/sorted_map.mbti b/bundled-core/immut/sorted_map/sorted_map.mbti deleted file mode 100644 index 36f281a..0000000 --- a/bundled-core/immut/sorted_map/sorted_map.mbti +++ /dev/null @@ -1,78 +0,0 @@ -package "moonbitlang/core/immut/sorted_map" - -import( - "moonbitlang/core/json" - "moonbitlang/core/quickcheck" -) - -// Values -fn[K : Compare, V] from_array(Array[(K, V)]) -> T[K, V] - -fn[K : Compare, V] from_iter(Iter[(K, V)]) -> T[K, V] - -fn[V : @json.FromJson] from_json(Json) -> T[String, V] raise @json.JsonDecodeError - -fn[K, V] new() -> T[K, V] - -fn[K : Compare, V] of(FixedArray[(K, V)]) -> T[K, V] - -fn[K, V] singleton(K, V) -> T[K, V] - -// Types and methods -type T[K, V] -fn[K : Compare, V] T::add(Self[K, V], K, V) -> Self[K, V] -fn[K : Compare, V] T::contains(Self[K, V], K) -> Bool -fn[K, V] T::each(Self[K, V], (K, V) -> Unit) -> Unit -fn[K, V] T::eachi(Self[K, V], (Int, K, V) -> Unit) -> Unit -#deprecated -fn[K, V] T::elems(Self[K, V]) -> Array[V] -#deprecated -fn[K, V] T::empty() -> Self[K, V] -fn[K : Compare, V] T::filter(Self[K, V], (V) -> Bool raise?) -> Self[K, V] raise? -fn[K : Compare, V] T::filter_with_key(Self[K, V], (K, V) -> Bool raise?) -> Self[K, V] raise? -fn[K, V, A] T::fold(Self[K, V], init~ : A, (A, V) -> A) -> A -fn[K, V, A] T::foldl_with_key(Self[K, V], (A, K, V) -> A, init~ : A) -> A -fn[K, V, A] T::foldr_with_key(Self[K, V], (A, K, V) -> A, init~ : A) -> A -#deprecated -fn[K : Compare, V] T::from_array(Array[(K, V)]) -> Self[K, V] -#deprecated -fn[K : Compare, V] T::from_iter(Iter[(K, V)]) -> Self[K, V] -#deprecated -fn[V : @json.FromJson] T::from_json(Json) -> Self[String, V] raise @json.JsonDecodeError -fn[K : Compare, V] T::get(Self[K, V], K) -> V? -#deprecated -fn[K : Compare, V] T::insert(Self[K, V], K, V) -> Self[K, V] -fn[K, V] T::is_empty(Self[K, V]) -> Bool -fn[K, V] T::iter(Self[K, V]) -> Iter[(K, V)] -fn[K, V] T::iter2(Self[K, V]) -> Iter2[K, V] -fn[K, V] T::keys(Self[K, V]) -> Array[K] -#deprecated -fn[K : Compare, V] T::lookup(Self[K, V], K) -> V? -fn[K, X, Y] T::map(Self[K, X], (X) -> Y) -> Self[K, Y] -fn[K, X, Y] T::map_with_key(Self[K, X], (K, X) -> Y) -> Self[K, Y] -#deprecated -fn[K, V] T::new() -> Self[K, V] -#deprecated -fn[K : Compare, V] T::of(FixedArray[(K, V)]) -> Self[K, V] -#deprecated -fn[K : Compare, V] T::op_get(Self[K, V], K) -> V? -fn[K : Compare, V] T::remove(Self[K, V], K) -> Self[K, V] -#deprecated -fn[K, V] T::singleton(K, V) -> Self[K, V] -fn[K, V] T::size(Self[K, V]) -> Int -fn[K, V] T::to_array(Self[K, V]) -> Array[(K, V)] -fn[K : Show, V : ToJson] T::to_json(Self[K, V]) -> Json -fn[K, V] T::values(Self[K, V]) -> Iter[V] -impl[K : Compare, V : Compare] Compare for T[K, V] -impl[K, V] Default for T[K, V] -impl[K : Eq, V : Eq] Eq for T[K, V] -impl[K : Hash, V : Hash] Hash for T[K, V] -impl[K : Show, V : Show] Show for T[K, V] -impl[K : Show, V : ToJson] ToJson for T[K, V] -impl[V : @json.FromJson] @json.FromJson for T[String, V] -impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K, V] - -// Type aliases - -// Traits - diff --git a/bundled-core/immut/sorted_map/traits_impl.mbt b/bundled-core/immut/sorted_map/traits_impl.mbt index 6be567f..5d19285 100644 --- a/bundled-core/immut/sorted_map/traits_impl.mbt +++ b/bundled-core/immut/sorted_map/traits_impl.mbt @@ -13,12 +13,12 @@ // limitations under the License. ///| -pub impl[K, V] Default for T[K, V] with default() { +pub impl[K, V] Default for SortedMap[K, V] with default() { new() } ///| -pub impl[K : Eq, V : Eq] Eq for T[K, V] with op_equal(self, other) -> Bool { +pub impl[K : Eq, V : Eq] Eq for SortedMap[K, V] with equal(self, other) -> Bool { let iter = InorderIterator::new(self) let iter1 = InorderIterator::new(other) loop (iter.next(), iter1.next()) { @@ -32,7 +32,10 @@ pub impl[K : Eq, V : Eq] Eq for T[K, V] with op_equal(self, other) -> Bool { } ///| -pub impl[K : Compare, V : Compare] Compare for T[K, V] with compare(self, other) -> Int { +pub impl[K : Compare, V : Compare] Compare for SortedMap[K, V] with compare( + self, + other, +) -> Int { let iter = InorderIterator::new(self) let iter1 = InorderIterator::new(other) loop (iter.next(), iter1.next()) { @@ -48,7 +51,7 @@ pub impl[K : Compare, V : Compare] Compare for T[K, V] with compare(self, other) } ///| -pub impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[ +pub impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for SortedMap[ K, V, ] with arbitrary(size, rs) { @@ -56,19 +59,22 @@ pub impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickc } ///| -pub impl[K : Hash, V : Hash] Hash for T[K, V] with hash_combine(self, hasher) { +pub impl[K : Hash, V : Hash] Hash for SortedMap[K, V] with hash_combine( + self, + hasher, +) { for e in self { hasher..combine(e.0)..combine(e.1) } } ///| -pub impl[K : Show, V : Show] Show for T[K, V] with output(self, logger) { +pub impl[K : Show, V : Show] Show for SortedMap[K, V] with output(self, logger) { logger.write_iter(self.iter(), prefix="@immut/sorted_map.of([", suffix="])") } ///| -pub impl[K : Show, V : ToJson] ToJson for T[K, V] with to_json(self) { +pub impl[K : Show, V : ToJson] ToJson for SortedMap[K, V] with to_json(self) { let capacity = self.size() guard capacity != 0 else { return Json::object(Map::new()) } let jsons = Map::new(capacity~) @@ -77,9 +83,9 @@ pub impl[K : Show, V : ToJson] ToJson for T[K, V] with to_json(self) { } ///| -pub impl[V : @json.FromJson] @json.FromJson for T[String, V] with from_json( +pub impl[V : @json.FromJson] @json.FromJson for SortedMap[String, V] with from_json( json, - path + path, ) { guard json is Object(obj) else { raise @json.JsonDecodeError( @@ -92,3 +98,11 @@ pub impl[V : @json.FromJson] @json.FromJson for T[String, V] with from_json( } map } + +///| +#as_free_fn +pub fn[V : @json.FromJson] SortedMap::from_json( + json : Json, +) -> SortedMap[String, V] raise @json.JsonDecodeError { + @json.from_json(json) +} diff --git a/bundled-core/immut/sorted_map/types.mbt b/bundled-core/immut/sorted_map/types.mbt index 964fe0f..e93b9b3 100644 --- a/bundled-core/immut/sorted_map/types.mbt +++ b/bundled-core/immut/sorted_map/types.mbt @@ -32,7 +32,11 @@ /// assert_eq(map3.get(3), None) /// assert_eq(map3.get(2), Some("updated")) /// ``` -enum T[K, V] { +enum SortedMap[K, V] { Empty - Tree(K, value~ : V, size~ : Int, T[K, V], T[K, V]) + Tree(K, value~ : V, size~ : Int, SortedMap[K, V], SortedMap[K, V]) } + +///| +#deprecated("Use `SortedMap` instead of `T`") +pub typealias SortedMap as T diff --git a/bundled-core/immut/sorted_map/utils.mbt b/bundled-core/immut/sorted_map/utils.mbt index 5856c30..acd9f20 100644 --- a/bundled-core/immut/sorted_map/utils.mbt +++ b/bundled-core/immut/sorted_map/utils.mbt @@ -12,28 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| Create an empty map. +///| +/// Create an empty map. #deprecated("Use `new()` instead") #coverage.skip -pub fn[K, V] T::empty() -> T[K, V] { +pub fn[K, V] SortedMap::empty() -> SortedMap[K, V] { Empty } -///| Create an empty map. -pub fn[K, V] new() -> T[K, V] { +///| +/// Create an empty map. +#as_free_fn +pub fn[K, V] SortedMap::new() -> SortedMap[K, V] { Empty } ///| /// Create a map with a single key-value pair. -pub fn[K, V] singleton(key : K, value : V) -> T[K, V] { +#as_free_fn +pub fn[K, V] SortedMap::singleton(key : K, value : V) -> SortedMap[K, V] { Tree(key, value~, size=1, Empty, Empty) } ///| /// Check if the map contains a key. /// O(log n). -pub fn[K : Compare, V] contains(self : T[K, V], key : K) -> Bool { +pub fn[K : Compare, V] contains(self : SortedMap[K, V], key : K) -> Bool { loop self { Empty => false Tree(k, l, r, ..) => { @@ -51,7 +55,7 @@ pub fn[K : Compare, V] contains(self : T[K, V], key : K) -> Bool { ///| /// Get the number of key-value pairs in the map. -pub fn[K, V] size(self : T[K, V]) -> Int { +pub fn[K, V] size(self : SortedMap[K, V]) -> Int { match self { Empty => 0 Tree(_) as t => t.size @@ -59,12 +63,17 @@ pub fn[K, V] size(self : T[K, V]) -> Int { } ///| -pub fn[K, V] is_empty(self : T[K, V]) -> Bool { +pub fn[K, V] is_empty(self : SortedMap[K, V]) -> Bool { self.size() == 0 } ///| -fn[K, V] make_tree(key : K, value : V, l : T[K, V], r : T[K, V]) -> T[K, V] { +fn[K, V] make_tree( + key : K, + value : V, + l : SortedMap[K, V], + r : SortedMap[K, V], +) -> SortedMap[K, V] { let size = l.size() + r.size() + 1 Tree(key, value~, size~, l, r) } @@ -74,14 +83,14 @@ fn[K, V] make_tree(key : K, value : V, l : T[K, V], r : T[K, V]) -> T[K, V] { /// O(log n). #deprecated("Use `get` instead") #coverage.skip -pub fn[K : Compare, V] lookup(self : T[K, V], key : K) -> V? { +pub fn[K : Compare, V] lookup(self : SortedMap[K, V], key : K) -> V? { self.get(key) } ///| /// Get the value associated with a key. /// O(log n). -pub fn[K : Compare, V] get(self : T[K, V], key : K) -> V? { +pub fn[K : Compare, V] get(self : SortedMap[K, V], key : K) -> V? { loop self { Empty => None Tree(k, value~, l, r, ..) => { @@ -98,14 +107,27 @@ pub fn[K : Compare, V] get(self : T[K, V], key : K) -> V? { } ///| -#deprecated("Use `get` instead. `op_get` will return `V` instead of `Option[V]` in the future.") -pub fn[K : Compare, V] op_get(self : T[K, V], key : K) -> V? { - self.get(key) +/// Get the value associated with a key. +/// O(log n). +pub fn[K : Compare, V] op_get(self : SortedMap[K, V], key : K) -> V { + loop self { + Empty => panic() + Tree(k, value~, l, r, ..) => { + let c = key.compare(k) + if c == 0 { + value + } else if c < 0 { + continue l + } else { + continue r + } + } + } } ///| /// Iterate over the key-value pairs in the map. -pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit) -> Unit { +pub fn[K, V] each(self : SortedMap[K, V], f : (K, V) -> Unit) -> Unit { match self { Empty => () Tree(k, value~, l, r, ..) => { @@ -118,8 +140,8 @@ pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit) -> Unit { ///| /// Iterate over the key-value pairs with index. -pub fn[K, V] eachi(self : T[K, V], f : (Int, K, V) -> Unit) -> Unit { - fn do_eachi(m : T[K, V], f, i) { +pub fn[K, V] eachi(self : SortedMap[K, V], f : (Int, K, V) -> Unit) -> Unit { + fn do_eachi(m : SortedMap[K, V], f, i) { match m { Empty => () Tree(k, value~, l, r, ..) => { @@ -135,7 +157,9 @@ pub fn[K, V] eachi(self : T[K, V], f : (Int, K, V) -> Unit) -> Unit { ///| /// Ts over the values in the map. -pub fn[K, X, Y] map(self : T[K, X], f : (X) -> Y) -> T[K, Y] { +#deprecated("Use `map_with_key` instead. `map` will accept `(K, X) -> Y` in the future.") +#coverage.skip +pub fn[K, X, Y] map(self : SortedMap[K, X], f : (X) -> Y) -> SortedMap[K, Y] { match self { Empty => Empty Tree(k, value~, size~, l, r) => @@ -145,7 +169,10 @@ pub fn[K, X, Y] map(self : T[K, X], f : (X) -> Y) -> T[K, Y] { ///| /// Maps over the key-value pairs in the map. -pub fn[K, X, Y] map_with_key(self : T[K, X], f : (K, X) -> Y) -> T[K, Y] { +pub fn[K, X, Y] map_with_key( + self : SortedMap[K, X], + f : (K, X) -> Y, +) -> SortedMap[K, Y] { match self { Empty => Empty Tree(k, value~, l, r, size~) => @@ -156,19 +183,21 @@ pub fn[K, X, Y] map_with_key(self : T[K, X], f : (K, X) -> Y) -> T[K, Y] { ///| /// Fold the values in the map. /// O(n). -pub fn[K, V, A] fold(self : T[K, V], init~ : A, f : (A, V) -> A) -> A { +#deprecated("Use `foldl_with_key` instead. `fold` will accept `(A, K, V) -> A` in the future.") +pub fn[K, V, A] fold(self : SortedMap[K, V], init~ : A, f : (A, V) -> A) -> A { self.foldl_with_key((acc, _k, v) => f(acc, v), init~) } ///| /// Post-order fold. /// O(n). -pub fn[K, V, A] foldr_with_key( - self : T[K, V], +#alias(foldr_with_key) +pub fn[K, V, A] rev_fold( + self : SortedMap[K, V], f : (A, K, V) -> A, - init~ : A + init~ : A, ) -> A { - fn go(m : T[K, V], acc) { + fn go(m : SortedMap[K, V], acc) { match m { Empty => acc Tree(k, value~, l, r, ..) => go(l, f(go(r, acc), k, value)) @@ -182,11 +211,11 @@ pub fn[K, V, A] foldr_with_key( /// Pre-order fold. /// O(n). pub fn[K, V, A] foldl_with_key( - self : T[K, V], + self : SortedMap[K, V], f : (A, K, V) -> A, - init~ : A + init~ : A, ) -> A { - fn go(m : T[K, V], acc) { + fn go(m : SortedMap[K, V], acc) { match m { Empty => acc Tree(k, value~, l, r, ..) => go(r, f(go(l, acc), k, value)) @@ -197,7 +226,7 @@ pub fn[K, V, A] foldl_with_key( } ///| -fn[K : Show, V : Show] debug_tree(self : T[K, V]) -> String { +fn[K : Show, V : Show] debug_tree(self : SortedMap[K, V]) -> String { match self { Empty => "_" Tree(k, value~, l, r, ..) => { @@ -211,7 +240,10 @@ fn[K : Show, V : Show] debug_tree(self : T[K, V]) -> String { ///| /// Build a map from an array of key-value pairs. /// O(n*log n). -pub fn[K : Compare, V] from_array(array : Array[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Compare, V] SortedMap::from_array( + array : Array[(K, V)], +) -> SortedMap[K, V] { for i = 0, mp = Empty; i < array.length(); { let (k, v) = array[i] continue i + 1, mp.add(k, v) @@ -221,7 +253,7 @@ pub fn[K : Compare, V] from_array(array : Array[(K, V)]) -> T[K, V] { } ///| -pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { +pub fn[K, V] iter(self : SortedMap[K, V]) -> Iter[(K, V)] { Iter::new(yield_ => { fn go(t) { match t { @@ -242,7 +274,7 @@ pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { } ///| -pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { +pub fn[K, V] iter2(self : SortedMap[K, V]) -> Iter2[K, V] { Iter2::new(yield_ => { fn go(t) { match t { @@ -263,31 +295,45 @@ pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { } ///| -pub fn[K : Compare, V] from_iter(iter : Iter[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Compare, V] SortedMap::from_iter( + iter : Iter[(K, V)], +) -> SortedMap[K, V] { iter.fold(init=new(), (m, e) => m.add(e.0, e.1)) } ///| /// Return all keys of the map in ascending order. -pub fn[K, V] keys(self : T[K, V]) -> Array[K] { +#deprecated("Use `keys_as_iter` instead. `keys` will return `Iter[K]` instead of `Array[K]` in the future.") +#coverage.skip +pub fn[K, V] keys(self : SortedMap[K, V]) -> Array[K] { self.iter().map(p => p.0).collect() } +///| +/// Return all keys of the map in ascending order. +pub fn[K, V] keys_as_iter(self : SortedMap[K, V]) -> Iter[K] { + self.iter().map(p => p.0) +} + ///| /// Return all elements of the map in the ascending order of their keys. -pub fn[K, V] values(self : T[K, V]) -> Iter[V] { +pub fn[K, V] values(self : SortedMap[K, V]) -> Iter[V] { self.iter().map(p => p.1) } ///| #deprecated("Use `values` instead") #coverage.skip -pub fn[K, V] elems(self : T[K, V]) -> Array[V] { +pub fn[K, V] elems(self : SortedMap[K, V]) -> Array[V] { self.values().collect() } ///| -pub fn[K : Compare, V] of(array : FixedArray[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Compare, V] SortedMap::of( + array : FixedArray[(K, V)], +) -> SortedMap[K, V] { for i = 0, mp = Empty; i < array.length(); { let (k, v) = array[i] continue i + 1, mp.add(k, v) @@ -297,13 +343,6 @@ pub fn[K : Compare, V] of(array : FixedArray[(K, V)]) -> T[K, V] { } ///| -pub fn[K : Show, V : ToJson] to_json(self : T[K, V]) -> Json { +pub fn[K : Show, V : ToJson] to_json(self : SortedMap[K, V]) -> Json { ToJson::to_json(self) } - -///| -pub fn[V : @json.FromJson] from_json( - json : Json -) -> T[String, V] raise @json.JsonDecodeError { - @json.from_json(json) -} diff --git a/bundled-core/immut/sorted_map/utils_test.mbt b/bundled-core/immut/sorted_map/utils_test.mbt index 1cad45c..2a6c000 100644 --- a/bundled-core/immut/sorted_map/utils_test.mbt +++ b/bundled-core/immut/sorted_map/utils_test.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -test "op_get with existing key" { +test "get with existing key" { let map = @sorted_map.of([ (3, "three"), (8, "eight"), @@ -25,7 +25,7 @@ test "op_get with existing key" { } ///| -test "op_get with non-existing key" { +test "get with non-existing key" { let map = @sorted_map.of([ (3, "three"), (8, "eight"), @@ -37,7 +37,20 @@ test "op_get with non-existing key" { } ///| -test "op_get after insertion" { +test "get with pattern" { + let map = @sorted_map.of([ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ]) + guard map + is { 3: "three", 8: "eight", 1: "one", 2: "two", 0: "zero", 4? : None, .. } +} + +///| +test "get after insertion" { let map = @sorted_map.of([ (3, "three"), (8, "eight"), @@ -50,7 +63,7 @@ test "op_get after insertion" { } ///| -test "op_get after removal" { +test "get after removal" { let map = @sorted_map.of([ (3, "three"), (8, "eight"), @@ -62,6 +75,56 @@ test "op_get after removal" { assert_eq(map.get(3), None) } +///| +test "op_get with existing key" { + let map = @sorted_map.of([ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ]) + assert_eq(map[3], "three") +} + +///| +test "panic op_get with non-existing key" { + let map = @sorted_map.of([ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ]) + map[4] |> ignore +} + +///| +test "op_get after insertion" { + let map = @sorted_map.of([ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ]) + let map = map.add(4, "four") + assert_eq(map[4], "four") +} + +///| +test "panic op_get after removal" { + let map = @sorted_map.of([ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ]) + let map = map.remove(3) + map[3] |> ignore +} + ///| test "iter" { let map = [(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] @@ -79,7 +142,7 @@ test "iter" { } inspect( buf, - content= + content=( #|0: "zero" #|1: "one" #|2: "two" @@ -91,16 +154,22 @@ test "iter" { #|(3, "three") #|(8, "eight") #| - , + ), ) } ///| -test "op_get with empty map" { - let map : @sorted_map.T[Int, String] = @sorted_map.new() +test "get with empty map" { + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() assert_eq(map.get(3), None) } +///| +test "panic op_get with empty map" { + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() + map[3] |> ignore +} + ///| test "to_json with non-empty map" { let map = [(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] @@ -116,13 +185,14 @@ test "to_json with non-empty map" { ///| test "to_json with empty map" { - let map : @sorted_map.T[Int, String] = @sorted_map.new() + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() @json.inspect(map.to_json(), content={}) } ///| test "from_json" { - for xs in (@quickcheck.samples(20) : Array[@sorted_map.T[String, Int]]) { + for + xs in (@quickcheck.samples(20) : Array[@sorted_map.SortedMap[String, Int]]) { assert_eq(xs, @json.from_json(xs.to_json())) } } diff --git a/bundled-core/immut/sorted_set/README.mbt.md b/bundled-core/immut/sorted_set/README.mbt.md index 75e36af..62401bf 100644 --- a/bundled-core/immut/sorted_set/README.mbt.md +++ b/bundled-core/immut/sorted_set/README.mbt.md @@ -12,7 +12,7 @@ You can create an empty ImmutableSet with a value separately through the followi ```moonbit test { - let _set1 : @sorted_set.T[Int] = @sorted_set.new() + let _set1 : @sorted_set.SortedSet[Int] = @sorted_set.new() let _set2 = @sorted_set.singleton(1) let _set4 = @sorted_set.from_array([1]) let _set5= @sorted_set.of([1]) @@ -161,7 +161,7 @@ test { ```moonbit test { - let set1 : @sorted_set.T[Int] = @sorted_set.of([]) + let set1 : @sorted_set.SortedSet[Int] = @sorted_set.of([]) assert_eq(set1.is_empty(), true) let set2 = @sorted_set.of([1]) assert_eq(set2.is_empty(), false) diff --git a/bundled-core/immut/sorted_set/deprecated.mbt b/bundled-core/immut/sorted_set/deprecated.mbt deleted file mode 100644 index aaca710..0000000 --- a/bundled-core/immut/sorted_set/deprecated.mbt +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -#deprecated("use `@immut/sorted_set.new` instead") -#coverage.skip -pub fn[A] T::new() -> T[A] { - Empty -} - -///| -#deprecated("use `immut/sorted_set.singleton` instead") -#coverage.skip -pub fn[A] T::singleton(value : A) -> T[A] { - singleton(value) -} - -///| -#deprecated("use `@immut/sorted_set.from_array` instead") -#coverage.skip -pub fn[A : Compare] T::from_array(array : Array[A]) -> T[A] { - from_array(array) -} - -///| -#deprecated("use `@immut/sorted_set.from_json` instead") -#coverage.skip -pub fn[A : @json.FromJson + Compare] T::from_json( - json : Json -) -> T[A] raise @json.JsonDecodeError { - @json.from_json(json) -} - -///| -#deprecated("use `@immut/sorted_set.of` instead") -#coverage.skip -pub fn[A : Compare] T::of(array : FixedArray[A]) -> T[A] { - of(array) -} diff --git a/bundled-core/immut/sorted_set/generic.mbt b/bundled-core/immut/sorted_set/generic.mbt index b13a55e..4f227b8 100644 --- a/bundled-core/immut/sorted_set/generic.mbt +++ b/bundled-core/immut/sorted_set/generic.mbt @@ -17,7 +17,7 @@ // All operations over sets are purely applicative (no side-effects). ///| -pub fn[A] iter(self : T[A]) -> Iter[A] { +pub fn[A] iter(self : SortedSet[A]) -> Iter[A] { Iter::new(yield_ => { fn go(t) { match t { @@ -38,23 +38,18 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { } ///| -pub fn[A : Compare] from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] SortedSet::from_iter(iter : Iter[A]) -> SortedSet[A] { iter.fold(init=new(), (s, e) => s.add(e)) } -///| -#deprecated("use `@immut/sorted_set.from_iter` instead") -pub fn[A : Compare] T::from_iter(iter : Iter[A]) -> T[A] { - from_iter(iter) -} - ///| test { @json.inspect(of([2, 7, 1, 2, 3, 4, 5]), content=[1, 2, 3, 4, 5, 7]) } ///| -pub impl[A : Eq] Eq for T[A] with op_equal(self, other) -> Bool { +pub impl[A : Eq] Eq for SortedSet[A] with equal(self, other) -> Bool { // There's no `Iter::zip` (https://github.com/moonbitlang/core/issues/994#issuecomment-2350935193), // so we have to use the manual implementation below: let iter = InorderIterator::new(self) @@ -70,7 +65,7 @@ pub impl[A : Eq] Eq for T[A] with op_equal(self, other) -> Bool { } ///| -pub impl[A : Compare] Compare for T[A] with compare(self, other) -> Int { +pub impl[A : Compare] Compare for SortedSet[A] with compare(self, other) -> Int { let iter = InorderIterator::new(self) let iter1 = InorderIterator::new(other) loop (iter.next(), iter1.next()) { @@ -86,10 +81,10 @@ pub impl[A : Compare] Compare for T[A] with compare(self, other) -> Int { } ///| -priv type InorderIterator[A] Array[T[A]] +priv struct InorderIterator[A](Array[SortedSet[A]]) ///| -fn[A] InorderIterator::new(root : T[A]) -> InorderIterator[A] { +fn[A] InorderIterator::new(root : SortedSet[A]) -> InorderIterator[A] { let it = InorderIterator([]) it.move_left(root) it @@ -98,7 +93,7 @@ fn[A] InorderIterator::new(root : T[A]) -> InorderIterator[A] { ///| fn[A] InorderIterator::move_left( self : InorderIterator[A], - node : T[A] + node : SortedSet[A], ) -> Unit { loop node { Empty => () diff --git a/bundled-core/immut/sorted_set/generic_test.mbt b/bundled-core/immut/sorted_set/generic_test.mbt index ee5a421..a449ad8 100644 --- a/bundled-core/immut/sorted_set/generic_test.mbt +++ b/bundled-core/immut/sorted_set/generic_test.mbt @@ -38,12 +38,12 @@ test "iter early termination - value" { count = count + 1 IterContinue }) - assert_eq(count, 1) + inspect(count, content="1") } ///| test "iter terminates on right" { - let set = of([1, 2, 3]) + let set = @sorted_set.of([1, 2, 3]) let result = set.iter().take(2).to_array() inspect(result, content="[1, 2]") } diff --git a/bundled-core/immut/sorted_set/immutable_set.mbt b/bundled-core/immut/sorted_set/immutable_set.mbt index c40a6e5..c951b95 100644 --- a/bundled-core/immut/sorted_set/immutable_set.mbt +++ b/bundled-core/immut/sorted_set/immutable_set.mbt @@ -16,26 +16,30 @@ // The types stored in set need to implement the Compare trait. // All operations over sets are purely applicative (no side-effects). -///| // Construct a empty ImmutableSet. -pub fn[A] new() -> T[A] { + +///| +#as_free_fn +pub fn[A] SortedSet::new() -> SortedSet[A] { Empty } ///| -pub impl[A] Default for T[A] with default() { +pub impl[A] Default for SortedSet[A] with default() { Empty } ///| /// Returns the one-value ImmutableSet containing only `value`. -pub fn[A] singleton(value : A) -> T[A] { +#as_free_fn +pub fn[A] SortedSet::singleton(value : A) -> SortedSet[A] { Node(left=Empty, value~, right=Empty, size=1) } ///| /// Initialize an ImmutableSet[T] from a Array[T] -pub fn[A : Compare] from_array(array : Array[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] SortedSet::from_array(array : Array[A]) -> SortedSet[A] { for i = array.length() - 1, set = Empty; i >= 0; { continue i - 1, set.add(array[i]) } else { @@ -45,9 +49,9 @@ pub fn[A : Compare] from_array(array : Array[A]) -> T[A] { ///| /// Convert ImmutableSet[T] to Array[T], the result must be ordered. -pub fn[A] to_array(self : T[A]) -> Array[A] { +pub fn[A] to_array(self : SortedSet[A]) -> Array[A] { let arr = [] - fn aux(set : T[A]) { + fn aux(set : SortedSet[A]) { match set { Empty => () Node(left~, value~, right~, ..) => { @@ -72,7 +76,7 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { /// ```mbt /// assert_eq(@sorted_set.of([3, 4, 5]).remove_min(), @sorted_set.of([4, 5])) /// ``` -pub fn[A : Compare] remove_min(self : T[A]) -> T[A] { +pub fn[A : Compare] remove_min(self : SortedSet[A]) -> SortedSet[A] { match self { Empty => abort("remove_min: empty ImmutableSet") Node(left~, right~, value~, ..) => @@ -92,7 +96,7 @@ pub fn[A : Compare] remove_min(self : T[A]) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([6, 3, 8, 1]).add(5), @sorted_set.of([1, 3, 5, 6, 8])) /// ``` -pub fn[A : Compare] add(self : T[A], value : A) -> T[A] { +pub fn[A : Compare] add(self : SortedSet[A], value : A) -> SortedSet[A] { match self { Empty => Node(left=Empty, value~, right=Empty, size=1) Node(left~, right~, value=node_value, ..) => { @@ -126,7 +130,7 @@ pub fn[A : Compare] add(self : T[A], value : A) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([3, 8, 1]).remove(8), @sorted_set.of([1, 3])) /// ``` -pub fn[A : Compare] remove(self : T[A], value : A) -> T[A] { +pub fn[A : Compare] remove(self : SortedSet[A], value : A) -> SortedSet[A] { match self { Empty => Empty Node(left~, right~, value=node_value, ..) => { @@ -161,7 +165,7 @@ pub fn[A : Compare] remove(self : T[A], value : A) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).min(), 1) /// ``` -pub fn[A : Compare] min(self : T[A]) -> A { +pub fn[A : Compare] min(self : SortedSet[A]) -> A { match self { Empty => abort("min: there are no values in sorted_set.") Node(left~, value~, ..) => if left is Empty { value } else { left.min() } @@ -171,7 +175,7 @@ pub fn[A : Compare] min(self : T[A]) -> A { ///| /// Returns the smallest value in the sorted_set. /// But returns None when the value does not exist. -pub fn[A : Compare] min_option(self : T[A]) -> A? { +pub fn[A : Compare] min_option(self : SortedSet[A]) -> A? { match self { Empty => None Node(left~, value~, ..) => @@ -192,7 +196,7 @@ pub fn[A : Compare] min_option(self : T[A]) -> A? { /// ```mbt /// assert_eq(@sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).max(), 9) /// ``` -pub fn[A : Compare] max(self : T[A]) -> A { +pub fn[A : Compare] max(self : SortedSet[A]) -> A { match self { Empty => abort("max: there are no values in ImmutableSet.") Node(right~, value~, ..) => if right is Empty { value } else { right.max() } @@ -202,7 +206,7 @@ pub fn[A : Compare] max(self : T[A]) -> A { ///| /// Returns the largest value in the ImmutableSet. /// But returns None when the value does not exist. -pub fn[A : Compare] max_option(self : T[A]) -> A? { +pub fn[A : Compare] max_option(self : SortedSet[A]) -> A? { match self { Empty => None Node(right~, value~, ..) => @@ -223,11 +227,14 @@ pub fn[A : Compare] max_option(self : T[A]) -> A? { /// /// ```mbt /// let (left, present, right) = @sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).split(5) -/// assert_eq(present, true) +/// inspect(present, content="true") /// assert_eq(left, @sorted_set.of([1, 2, 3, 4])) /// assert_eq(right, @sorted_set.of([6, 7, 8, 9])) /// ``` -pub fn[A : Compare] split(self : T[A], divide : A) -> (T[A], Bool, T[A]) { +pub fn[A : Compare] split( + self : SortedSet[A], + divide : A, +) -> (SortedSet[A], Bool, SortedSet[A]) { match self { Empty => (Empty, false, Empty) Node(left~, right~, value~, ..) => { @@ -247,13 +254,13 @@ pub fn[A : Compare] split(self : T[A], divide : A) -> (T[A], Bool, T[A]) { ///| /// Returns true if sorted_set is empty -pub fn[A] is_empty(self : T[A]) -> Bool { +pub fn[A] is_empty(self : SortedSet[A]) -> Bool { self is Empty } ///| /// Return true if value contain in sorted_set -pub fn[A : Compare] contains(self : T[A], value : A) -> Bool { +pub fn[A : Compare] contains(self : SortedSet[A], value : A) -> Bool { match self { Empty => false Node(left~, right~, value=node_value, ..) => { @@ -272,7 +279,10 @@ pub fn[A : Compare] contains(self : T[A], value : A) -> Bool { /// ```mbt /// assert_eq(@sorted_set.of([3, 4, 5]).union(@sorted_set.of([4, 5, 6])), @sorted_set.of([3, 4, 5, 6])) /// ``` -pub fn[A : Compare] union(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] union( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) => other (_, Empty) => self @@ -303,14 +313,17 @@ pub fn[A : Compare] union(self : T[A], other : T[A]) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([3, 4, 5]) + (@sorted_set.of([4, 5, 6])), @sorted_set.of([3, 4, 5, 6])) /// ``` -pub impl[A : Compare] Add for T[A] with op_add(self, other) { +pub impl[A : Compare] Add for SortedSet[A] with add(self, other) { return self.union(other) } ///| #deprecated("Use `intersection` instead") #coverage.skip -pub fn[A : Compare] inter(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] inter( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { self.intersection(other) } @@ -322,7 +335,10 @@ pub fn[A : Compare] inter(self : T[A], other : T[A]) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([3, 4, 5]).intersection(@sorted_set.of([4, 5, 6])), @sorted_set.of([4, 5])) /// ``` -pub fn[A : Compare] intersection(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] intersection( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) | (_, Empty) => Empty (Node(left=l1, value=v1, right=r1, ..), _) => @@ -343,7 +359,10 @@ pub fn[A : Compare] intersection(self : T[A], other : T[A]) -> T[A] { /// ``` #deprecated("Use `difference` instead") #coverage.skip -pub fn[A : Compare] diff(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] diff( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { self.difference(other) } @@ -355,7 +374,10 @@ pub fn[A : Compare] diff(self : T[A], other : T[A]) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([1, 2, 3]).difference(@sorted_set.of([4, 5, 1])), @sorted_set.of([2, 3])) /// ``` -pub fn[A : Compare] difference(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] difference( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) => Empty (_, Empty) => self @@ -389,7 +411,10 @@ pub fn[A : Compare] difference(self : T[A], other : T[A]) -> T[A] { /// content="@immut/sorted_set.of([1, 2, 5, 6])", /// ) /// ``` -pub fn[A : Compare] symmetric_difference(self : T[A], other : T[A]) -> T[A] { +pub fn[A : Compare] symmetric_difference( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) => other (_, Empty) => self @@ -411,7 +436,7 @@ pub fn[A : Compare] symmetric_difference(self : T[A], other : T[A]) -> T[A] { /// ```mbt /// assert_eq(@sorted_set.of([1, 2, 3]).subset(@sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1])), true) /// ``` -pub fn[A : Compare] subset(self : T[A], other : T[A]) -> Bool { +pub fn[A : Compare] subset(self : SortedSet[A], other : SortedSet[A]) -> Bool { match (self, other) { (Empty, _) => true (_, Empty) => false @@ -441,7 +466,7 @@ pub fn[A : Compare] subset(self : T[A], other : T[A]) -> Bool { /// ```mbt /// assert_eq(@sorted_set.of([1, 2, 3]).disjoint(@sorted_set.of([4, 5, 6])), true) /// ``` -pub fn[A : Compare] disjoint(self : T[A], other : T[A]) -> Bool { +pub fn[A : Compare] disjoint(self : SortedSet[A], other : SortedSet[A]) -> Bool { match (self, other) { (Empty, _) | (_, Empty) => true (Node(left=l1, value=v1, right=r1, ..), _) => @@ -466,7 +491,7 @@ pub fn[A : Compare] disjoint(self : T[A], other : T[A]) -> Bool { /// @sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).each((x) => { arr.push(x) }) /// assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8, 9]) /// ``` -pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { +pub fn[A] each(self : SortedSet[A], f : (A) -> Unit raise?) -> Unit raise? { match self { Empty => () Node(left~, value~, right~, ..) => { @@ -486,9 +511,9 @@ pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { /// assert_eq(@sorted_set.of([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => { acc + x }), 15) /// ``` pub fn[A : Compare, B] fold( - self : T[A], + self : SortedSet[A], init~ : B, - f : (B, A) -> B raise? + f : (B, A) -> B raise?, ) -> B raise? { match self { Empty => init @@ -506,9 +531,9 @@ pub fn[A : Compare, B] fold( /// assert_eq(@sorted_set.of([1, 2, 3]).map((x) => { x * 2}), @sorted_set.of([2, 4, 6])) /// ``` pub fn[A : Compare, B : Compare] map( - self : T[A], - f : (A) -> B raise? -) -> T[B] raise? { + self : SortedSet[A], + f : (A) -> B raise?, +) -> SortedSet[B] raise? { match self { Empty => Empty Node(left~, value~, right~, ..) => @@ -524,7 +549,10 @@ pub fn[A : Compare, B : Compare] map( /// ```mbt /// assert_eq(@sorted_set.of([2, 4, 6]).all((v) => { v % 2 == 0}), true) /// ``` -pub fn[A : Compare] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { +pub fn[A : Compare] all( + self : SortedSet[A], + f : (A) -> Bool raise?, +) -> Bool raise? { match self { Empty => true Node(left~, value~, right~, ..) => f(value) && left.all(f) && right.all(f) @@ -539,7 +567,10 @@ pub fn[A : Compare] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { /// ```mbt /// assert_eq(@sorted_set.of([1, 4, 3]).any((v) => { v % 2 == 0}), true) /// ``` -pub fn[A : Compare] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { +pub fn[A : Compare] any( + self : SortedSet[A], + f : (A) -> Bool raise?, +) -> Bool raise? { match self { Empty => false Node(left~, value~, right~, ..) => f(value) || left.any(f) || right.any(f) @@ -554,7 +585,10 @@ pub fn[A : Compare] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { /// ```mbt /// assert_eq(@sorted_set.of([1, 2, 3, 4, 5, 6]).filter((v) => { v % 2 == 0}), @sorted_set.of([2, 4, 6])) /// ``` -pub fn[A : Compare] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { +pub fn[A : Compare] filter( + self : SortedSet[A], + f : (A) -> Bool raise?, +) -> SortedSet[A] raise? { match self { Empty => Empty Node(left~, value~, right~, ..) => { @@ -575,12 +609,12 @@ pub fn[A : Compare] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { } ///| -pub impl[A : Show] Show for T[A] with output(self, logger) { +pub impl[A : Show] Show for SortedSet[A] with output(self, logger) { logger.write_iter(self.iter(), prefix="@immut/sorted_set.of([", suffix="])") } ///| -pub impl[A : ToJson] ToJson for T[A] with to_json(self) { +pub impl[A : ToJson] ToJson for SortedSet[A] with to_json(self) { let capacity = self.iter().count() guard capacity != 0 else { return Json::array([]) } let jsons = Array::new(capacity~) @@ -589,14 +623,14 @@ pub impl[A : ToJson] ToJson for T[A] with to_json(self) { } ///| -pub fn[A : ToJson] to_json(self : T[A]) -> Json { +pub fn[A : ToJson] to_json(self : SortedSet[A]) -> Json { ToJson::to_json(self) } ///| -pub impl[A : @json.FromJson + Compare] @json.FromJson for T[A] with from_json( +pub impl[A : @json.FromJson + Compare] @json.FromJson for SortedSet[A] with from_json( json, - path + path, ) { guard json is Array(arr) else { raise @json.JsonDecodeError( @@ -611,17 +645,17 @@ pub impl[A : @json.FromJson + Compare] @json.FromJson for T[A] with from_json( } ///| -pub fn[A : @json.FromJson + Compare] from_json( - json : Json -) -> T[A] raise @json.JsonDecodeError { +#as_free_fn +pub fn[A : @json.FromJson + Compare] SortedSet::from_json( + json : Json, +) -> SortedSet[A] raise @json.JsonDecodeError { @json.from_json(json) } ///| -pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] with arbitrary( - size, - rs -) { +pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for SortedSet[ + X, +] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } @@ -634,7 +668,7 @@ const BALANCE_RATIO = 5 ///| priv enum SplitBis[A] { Found - NotFound(T[A], () -> T[A]) + NotFound(SortedSet[A], () -> SortedSet[A]) } ///| @@ -648,7 +682,7 @@ impl[T] Show for SplitBis[T] with output(self, logger) { ///| /// Same as split, but compute the left and right only if the pivot value is not in the ImmutableSet. /// The right is computed on demand. -fn[A : Compare] split_bis(self : T[A], value : A) -> SplitBis[A] { +fn[A : Compare] split_bis(self : SortedSet[A], value : A) -> SplitBis[A] { match self { Empty => NotFound(Empty, () => Empty) Node(left~, value=node_value, right~, ..) => { @@ -672,7 +706,7 @@ fn[A : Compare] split_bis(self : T[A], value : A) -> SplitBis[A] { ///| /// Get the height of set. -pub fn[A] size(self : T[A]) -> Int { +pub fn[A] size(self : SortedSet[A]) -> Int { match self { Empty => 0 Node(size~, ..) => size @@ -681,13 +715,21 @@ pub fn[A] size(self : T[A]) -> Int { ///| /// Creates a new node. -fn[A] create(left : T[A], value : A, right : T[A]) -> T[A] { +fn[A] create( + left : SortedSet[A], + value : A, + right : SortedSet[A], +) -> SortedSet[A] { Node(left~, right~, value~, size=left.size() + right.size() + 1) } ///| /// Same as create, but performs one step of rebalancing if necessary. -fn[A] balance(left : T[A], value : A, right : T[A]) -> T[A] { +fn[A] balance( + left : SortedSet[A], + value : A, + right : SortedSet[A], +) -> SortedSet[A] { let left_size = left.size() let right_size = right.size() if left_size + right_size < 2 { @@ -726,7 +768,7 @@ fn[A] balance(left : T[A], value : A, right : T[A]) -> T[A] { } ///| -fn[A : Compare] add_min_value(self : T[A], value : A) -> T[A] { +fn[A : Compare] add_min_value(self : SortedSet[A], value : A) -> SortedSet[A] { match self { Empty => singleton(value) Node(left~, value=node_value, right~, ..) => @@ -735,7 +777,7 @@ fn[A : Compare] add_min_value(self : T[A], value : A) -> T[A] { } ///| -fn[A : Compare] add_max_value(self : T[A], value : A) -> T[A] { +fn[A : Compare] add_max_value(self : SortedSet[A], value : A) -> SortedSet[A] { match self { Empty => singleton(value) Node(left~, value=node_value, right~, ..) => @@ -745,7 +787,11 @@ fn[A : Compare] add_max_value(self : T[A], value : A) -> T[A] { ///| /// Same as create and balance, but no assumptions are made on the relative heights of left and right. -fn[A : Compare] join(left : T[A], value : A, right : T[A]) -> T[A] { +fn[A : Compare] join( + left : SortedSet[A], + value : A, + right : SortedSet[A], +) -> SortedSet[A] { match (left, right) { (Empty, _) => right.add_min_value(value) (_, Empty) => left.add_max_value(value) @@ -764,7 +810,11 @@ fn[A : Compare] join(left : T[A], value : A, right : T[A]) -> T[A] { } ///| -fn[A : Compare] try_join(left : T[A], value : A, right : T[A]) -> T[A] { +fn[A : Compare] try_join( + left : SortedSet[A], + value : A, + right : SortedSet[A], +) -> SortedSet[A] { if (left == Empty || left.max().compare(value) < 0) && (right == Empty || value.compare(right.min()) < 0) { join(left, value, right) @@ -776,7 +826,10 @@ fn[A : Compare] try_join(left : T[A], value : A, right : T[A]) -> T[A] { ///| /// Merge two ImmutableSet[T] into one. /// All values of left must precede the values of r. -fn[A : Compare] merge(self : T[A], other : T[A]) -> T[A] { +fn[A : Compare] merge( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) => other (_, Empty) => self @@ -786,7 +839,10 @@ fn[A : Compare] merge(self : T[A], other : T[A]) -> T[A] { ///| /// Same as merge, but no assumption on the heights of self and other. -fn[A : Compare] concat(self : T[A], other : T[A]) -> T[A] { +fn[A : Compare] concat( + self : SortedSet[A], + other : SortedSet[A], +) -> SortedSet[A] { match (self, other) { (Empty, _) => other (_, Empty) => self @@ -795,7 +851,8 @@ fn[A : Compare] concat(self : T[A], other : T[A]) -> T[A] { } ///| -pub fn[A : Compare] of(array : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] SortedSet::of(array : FixedArray[A]) -> SortedSet[A] { for i = array.length() - 1, set = Empty; i >= 0; { continue i - 1, set.add(array[i]) } else { @@ -804,7 +861,7 @@ pub fn[A : Compare] of(array : FixedArray[A]) -> T[A] { } ///| -pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { +pub impl[A : Hash] Hash for SortedSet[A] with hash_combine(self, hasher) { for t in self { t.hash_combine(hasher) } diff --git a/bundled-core/immut/sorted_set/immutable_set_test.mbt b/bundled-core/immut/sorted_set/immutable_set_test.mbt index 857f359..1757b38 100644 --- a/bundled-core/immut/sorted_set/immutable_set_test.mbt +++ b/bundled-core/immut/sorted_set/immutable_set_test.mbt @@ -18,7 +18,7 @@ ///| test "new" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() inspect(empty, content="@immut/sorted_set.of([])") } @@ -272,7 +272,7 @@ test "min" { ///| test "min_option" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() inspect(empty.min_option(), content="None") inspect( @sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).min_option(), @@ -287,7 +287,7 @@ test "max" { ///| test "max_option" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() inspect(empty.max_option(), content="None") inspect( @sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).max_option(), @@ -297,7 +297,10 @@ test "max_option" { ///| test "is_empty" { - inspect((@sorted_set.of([]) : @sorted_set.T[Int]).is_empty(), content="true") + inspect( + (@sorted_set.of([]) : @sorted_set.SortedSet[Int]).is_empty(), + content="true", + ) inspect(@sorted_set.of([1]).is_empty(), content="false") } @@ -308,14 +311,14 @@ test "to_string" { content="@immut/sorted_set.of([1, 2, 3, 4, 5])", ) inspect( - (@sorted_set.of([]) : @sorted_set.T[Int]), + (@sorted_set.of([]) : @sorted_set.SortedSet[Int]), content="@immut/sorted_set.of([])", ) } ///| test "op_equal" { - let xss : Array[@sorted_set.T[Int]] = @quickcheck.samples(5) + let xss : Array[@sorted_set.SortedSet[Int]] = @quickcheck.samples(5) for xs in xss { for ys in xss { if xs.to_array() == ys.to_array() { @@ -329,7 +332,7 @@ test "op_equal" { ///| test "compare" { - let xss : Array[@sorted_set.T[Int]] = @quickcheck.samples(5) + let xss : Array[@sorted_set.SortedSet[Int]] = @quickcheck.samples(5) for xs in xss { for ys in xss { assert_eq(xs.compare(ys), xs.to_array().compare(ys.to_array())) @@ -340,12 +343,12 @@ test "compare" { ///| test "to_json" { @json.inspect(@sorted_set.of([5, 2, 4, 3, 1]), content=[1, 2, 3, 4, 5]) - @json.inspect((@sorted_set.of([]) : @sorted_set.T[Int]), content=[]) + @json.inspect((@sorted_set.of([]) : @sorted_set.SortedSet[Int]), content=[]) } ///| test "from_json" { - for xs in (@quickcheck.samples(20) : Array[@sorted_set.T[Int]]) { + for xs in (@quickcheck.samples(20) : Array[@sorted_set.SortedSet[Int]]) { assert_eq(xs, @json.from_json(xs.to_json())) } } @@ -355,7 +358,7 @@ test "iter" { let mut s = "" @sorted_set.of([7, 2, 9, 4, 5, 6, 3, 8, 1]).each(x => s += x.to_string()) inspect(s, content="123456789") - let empty : @sorted_set.T[Int] = @sorted_set.of([]) + let empty : @sorted_set.SortedSet[Int] = @sorted_set.of([]) s = "" empty.each(x => s += x.to_string()) inspect(s, content="") @@ -512,7 +515,7 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @sorted_set.T[Int] = @sorted_set.from_iter(Iter::empty()) + let pq : @sorted_set.SortedSet[Int] = @sorted_set.from_iter(Iter::empty()) inspect(pq, content="@immut/sorted_set.of([])") } @@ -529,7 +532,7 @@ test "iter" { ///| test "size" { // Empty set should have size 0 - let empty_set : @sorted_set.T[Int] = @sorted_set.new() + let empty_set : @sorted_set.SortedSet[Int] = @sorted_set.new() inspect(empty_set.size(), content="0") // Single element set should have size 1 diff --git a/bundled-core/immut/sorted_set/panic_test.mbt b/bundled-core/immut/sorted_set/panic_test.mbt index c2b28cb..ff0822a 100644 --- a/bundled-core/immut/sorted_set/panic_test.mbt +++ b/bundled-core/immut/sorted_set/panic_test.mbt @@ -14,18 +14,18 @@ ///| test "panic remove_min on empty set" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() empty.remove_min() |> ignore } ///| test "panic min on empty set" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() empty.min() |> ignore } ///| test "panic max on empty set" { - let empty : @sorted_set.T[Int] = @sorted_set.new() + let empty : @sorted_set.SortedSet[Int] = @sorted_set.new() empty.max() |> ignore } diff --git a/bundled-core/immut/sorted_set/pkg.generated.mbti b/bundled-core/immut/sorted_set/pkg.generated.mbti new file mode 100644 index 0000000..650fd3f --- /dev/null +++ b/bundled-core/immut/sorted_set/pkg.generated.mbti @@ -0,0 +1,71 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/immut/sorted_set" + +import( + "moonbitlang/core/json" + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type SortedSet[A] +fn[A : Compare] SortedSet::add(Self[A], A) -> Self[A] +fn[A : Compare] SortedSet::all(Self[A], (A) -> Bool raise?) -> Bool raise? +fn[A : Compare] SortedSet::any(Self[A], (A) -> Bool raise?) -> Bool raise? +fn[A : Compare] SortedSet::contains(Self[A], A) -> Bool +#deprecated +fn[A : Compare] SortedSet::diff(Self[A], Self[A]) -> Self[A] +fn[A : Compare] SortedSet::difference(Self[A], Self[A]) -> Self[A] +fn[A : Compare] SortedSet::disjoint(Self[A], Self[A]) -> Bool +fn[A] SortedSet::each(Self[A], (A) -> Unit raise?) -> Unit raise? +fn[A : Compare] SortedSet::filter(Self[A], (A) -> Bool raise?) -> Self[A] raise? +fn[A : Compare, B] SortedSet::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? +#as_free_fn +fn[A : Compare] SortedSet::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A : Compare] SortedSet::from_iter(Iter[A]) -> Self[A] +#as_free_fn +fn[A : @json.FromJson + Compare] SortedSet::from_json(Json) -> Self[A] raise @json.JsonDecodeError +#deprecated +fn[A : Compare] SortedSet::inter(Self[A], Self[A]) -> Self[A] +fn[A : Compare] SortedSet::intersection(Self[A], Self[A]) -> Self[A] +fn[A] SortedSet::is_empty(Self[A]) -> Bool +fn[A] SortedSet::iter(Self[A]) -> Iter[A] +fn[A : Compare, B : Compare] SortedSet::map(Self[A], (A) -> B raise?) -> Self[B] raise? +fn[A : Compare] SortedSet::max(Self[A]) -> A +fn[A : Compare] SortedSet::max_option(Self[A]) -> A? +fn[A : Compare] SortedSet::min(Self[A]) -> A +fn[A : Compare] SortedSet::min_option(Self[A]) -> A? +#as_free_fn +fn[A] SortedSet::new() -> Self[A] +#as_free_fn +fn[A : Compare] SortedSet::of(FixedArray[A]) -> Self[A] +fn[A : Compare] SortedSet::remove(Self[A], A) -> Self[A] +fn[A : Compare] SortedSet::remove_min(Self[A]) -> Self[A] +#as_free_fn +fn[A] SortedSet::singleton(A) -> Self[A] +fn[A] SortedSet::size(Self[A]) -> Int +fn[A : Compare] SortedSet::split(Self[A], A) -> (Self[A], Bool, Self[A]) +fn[A : Compare] SortedSet::subset(Self[A], Self[A]) -> Bool +fn[A : Compare] SortedSet::symmetric_difference(Self[A], Self[A]) -> Self[A] +fn[A] SortedSet::to_array(Self[A]) -> Array[A] +fn[A : ToJson] SortedSet::to_json(Self[A]) -> Json +fn[A : Compare] SortedSet::union(Self[A], Self[A]) -> Self[A] +impl[A : Compare] Add for SortedSet[A] +impl[A : Compare] Compare for SortedSet[A] +impl[A] Default for SortedSet[A] +impl[A : Eq] Eq for SortedSet[A] +impl[A : Hash] Hash for SortedSet[A] +impl[A : Show] Show for SortedSet[A] +impl[A : ToJson] ToJson for SortedSet[A] +impl[A : @json.FromJson + Compare] @json.FromJson for SortedSet[A] +impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for SortedSet[X] + +// Type aliases +pub typealias SortedSet as T + +// Traits + diff --git a/bundled-core/immut/sorted_set/sorted_set.mbti b/bundled-core/immut/sorted_set/sorted_set.mbti deleted file mode 100644 index 0c6e9a6..0000000 --- a/bundled-core/immut/sorted_set/sorted_set.mbti +++ /dev/null @@ -1,78 +0,0 @@ -package "moonbitlang/core/immut/sorted_set" - -import( - "moonbitlang/core/json" - "moonbitlang/core/quickcheck" -) - -// Values -fn[A : Compare] from_array(Array[A]) -> T[A] - -fn[A : Compare] from_iter(Iter[A]) -> T[A] - -fn[A : @json.FromJson + Compare] from_json(Json) -> T[A] raise @json.JsonDecodeError - -fn[A] new() -> T[A] - -fn[A : Compare] of(FixedArray[A]) -> T[A] - -fn[A] singleton(A) -> T[A] - -// Types and methods -type T[A] -fn[A : Compare] T::add(Self[A], A) -> Self[A] -fn[A : Compare] T::all(Self[A], (A) -> Bool raise?) -> Bool raise? -fn[A : Compare] T::any(Self[A], (A) -> Bool raise?) -> Bool raise? -fn[A : Compare] T::contains(Self[A], A) -> Bool -#deprecated -fn[A : Compare] T::diff(Self[A], Self[A]) -> Self[A] -fn[A : Compare] T::difference(Self[A], Self[A]) -> Self[A] -fn[A : Compare] T::disjoint(Self[A], Self[A]) -> Bool -fn[A] T::each(Self[A], (A) -> Unit raise?) -> Unit raise? -fn[A : Compare] T::filter(Self[A], (A) -> Bool raise?) -> Self[A] raise? -fn[A : Compare, B] T::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? -#deprecated -fn[A : Compare] T::from_array(Array[A]) -> Self[A] -#deprecated -fn[A : Compare] T::from_iter(Iter[A]) -> Self[A] -#deprecated -fn[A : @json.FromJson + Compare] T::from_json(Json) -> Self[A] raise @json.JsonDecodeError -#deprecated -fn[A : Compare] T::inter(Self[A], Self[A]) -> Self[A] -fn[A : Compare] T::intersection(Self[A], Self[A]) -> Self[A] -fn[A] T::is_empty(Self[A]) -> Bool -fn[A] T::iter(Self[A]) -> Iter[A] -fn[A : Compare, B : Compare] T::map(Self[A], (A) -> B raise?) -> Self[B] raise? -fn[A : Compare] T::max(Self[A]) -> A -fn[A : Compare] T::max_option(Self[A]) -> A? -fn[A : Compare] T::min(Self[A]) -> A -fn[A : Compare] T::min_option(Self[A]) -> A? -#deprecated -fn[A] T::new() -> Self[A] -#deprecated -fn[A : Compare] T::of(FixedArray[A]) -> Self[A] -fn[A : Compare] T::remove(Self[A], A) -> Self[A] -fn[A : Compare] T::remove_min(Self[A]) -> Self[A] -#deprecated -fn[A] T::singleton(A) -> Self[A] -fn[A] T::size(Self[A]) -> Int -fn[A : Compare] T::split(Self[A], A) -> (Self[A], Bool, Self[A]) -fn[A : Compare] T::subset(Self[A], Self[A]) -> Bool -fn[A : Compare] T::symmetric_difference(Self[A], Self[A]) -> Self[A] -fn[A] T::to_array(Self[A]) -> Array[A] -fn[A : ToJson] T::to_json(Self[A]) -> Json -fn[A : Compare] T::union(Self[A], Self[A]) -> Self[A] -impl[A : Compare] Add for T[A] -impl[A : Compare] Compare for T[A] -impl[A] Default for T[A] -impl[A : Eq] Eq for T[A] -impl[A : Hash] Hash for T[A] -impl[A : Show] Show for T[A] -impl[A : ToJson] ToJson for T[A] -impl[A : @json.FromJson + Compare] @json.FromJson for T[A] -impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/immut/sorted_set/types.mbt b/bundled-core/immut/sorted_set/types.mbt index f5ad3e4..a6b6660 100644 --- a/bundled-core/immut/sorted_set/types.mbt +++ b/bundled-core/immut/sorted_set/types.mbt @@ -18,7 +18,11 @@ ///| /// ImmutableSets are represented by balanced binary trees (the heights of the children differ by at most 2). -enum T[A] { +enum SortedSet[A] { Empty - Node(left~ : T[A], right~ : T[A], size~ : Int, value~ : A) + Node(left~ : SortedSet[A], right~ : SortedSet[A], size~ : Int, value~ : A) } + +///| +#deprecated("Use `SortedSet` instead of `T`") +pub typealias SortedSet as T diff --git a/bundled-core/int/README.mbt.md b/bundled-core/int/README.mbt.md index 13b9151..332990d 100644 --- a/bundled-core/int/README.mbt.md +++ b/bundled-core/int/README.mbt.md @@ -30,18 +30,18 @@ test "byte conversions" { let be_bytes = num.to_be_bytes() inspect( be_bytes.to_string(), - content= + content=( #|b"\x00\x00\x01\x02" - , + ), ) // Little-endian conversion (least significant byte first) let le_bytes = num.to_le_bytes() inspect( le_bytes.to_string(), - content= + content=( #|b"\x02\x01\x00\x00" - , + ), ) } ``` @@ -62,15 +62,15 @@ test "method syntax" { let le = n.to_le_bytes() inspect( be.to_string(), - content= + content=( #|b"\xff\xff\xff\xd6" - , + ), ) inspect( le.to_string(), - content= + content=( #|b"\xd6\xff\xff\xff" - , + ), ) } ``` diff --git a/bundled-core/int/int.mbt b/bundled-core/int/int.mbt index 6336a8b..b3f82a5 100644 --- a/bundled-core/int/int.mbt +++ b/bundled-core/int/int.mbt @@ -36,6 +36,7 @@ pub let min_value = -2147483648 /// inspect(@int.abs(-42), content="42") /// inspect(@int.abs(0), content="0") /// ``` +#as_free_fn pub fn Int::abs(self : Int) -> Int { if self < 0 { -self @@ -45,14 +46,13 @@ pub fn Int::abs(self : Int) -> Int { } ///| -pub fnalias Int::abs - -///| Converts the Int to a Bytes in big-endian byte order. +/// Converts the Int to a Bytes in big-endian byte order. pub fn to_be_bytes(self : Int) -> Bytes { self.reinterpret_as_uint().to_be_bytes() } -///| Converts the Int to a Bytes in little-endian byte order. +///| +/// Converts the Int to a Bytes in little-endian byte order. pub fn to_le_bytes(self : Int) -> Bytes { self.reinterpret_as_uint().to_le_bytes() } diff --git a/bundled-core/int/int_test.mbt b/bundled-core/int/int_test.mbt index 600b91b..4862d40 100644 --- a/bundled-core/int/int_test.mbt +++ b/bundled-core/int/int_test.mbt @@ -43,9 +43,9 @@ test "hash" { ///| test "abs function coverage" { // Test cases for abs function - assert_eq(Int::abs(5), 5) - assert_eq(Int::abs(-5), 5) - assert_eq(Int::abs(0), 0) + inspect(Int::abs(5), content="5") + inspect(Int::abs(-5), content="5") + inspect(Int::abs(0), content="0") assert_eq(Int::abs(@int.min_value), @int.min_value.abs()) assert_eq(Int::abs(@int.max_value), @int.max_value) } @@ -55,9 +55,9 @@ test "to_le_bytes" { let bytes = (0x12345678).to_le_bytes() inspect( bytes, - content= + content=( #|b"\x78\x56\x34\x12" - , + ), ) } @@ -66,8 +66,8 @@ test "to_be_bytes" { let bytes = (0x12345678).to_be_bytes() inspect( bytes, - content= + content=( #|b"\x12\x34\x56\x78" - , + ), ) } diff --git a/bundled-core/int/int.mbti b/bundled-core/int/pkg.generated.mbti similarity index 76% rename from bundled-core/int/int.mbti rename to bundled-core/int/pkg.generated.mbti index da25074..9c04131 100644 --- a/bundled-core/int/int.mbti +++ b/bundled-core/int/pkg.generated.mbti @@ -1,13 +1,15 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/int" // Values -fn abs(Int) -> Int - let max_value : Int let min_value : Int +// Errors + // Types and methods +#as_free_fn fn Int::abs(Int) -> Int fn Int::to_be_bytes(Int) -> Bytes fn Int::to_le_bytes(Int) -> Bytes diff --git a/bundled-core/int16/int16.mbt b/bundled-core/int16/int16.mbt index 5a43056..4feb963 100644 --- a/bundled-core/int16/int16.mbt +++ b/bundled-core/int16/int16.mbt @@ -19,32 +19,32 @@ pub let max_value : Int16 = 32767 pub let min_value : Int16 = -32768 ///| -pub impl Add for Int16 with op_add(self : Int16, that : Int16) -> Int16 { +pub impl Add for Int16 with add(self : Int16, that : Int16) -> Int16 { (self.to_int() + that.to_int()).to_int16() } ///| -pub impl Sub for Int16 with op_sub(self : Int16, that : Int16) -> Int16 { +pub impl Sub for Int16 with sub(self : Int16, that : Int16) -> Int16 { (self.to_int() - that.to_int()).to_int16() } ///| -pub impl Mul for Int16 with op_mul(self : Int16, that : Int16) -> Int16 { +pub impl Mul for Int16 with mul(self : Int16, that : Int16) -> Int16 { (self.to_int() * that.to_int()).to_int16() } ///| -pub impl Div for Int16 with op_div(self : Int16, that : Int16) -> Int16 { +pub impl Div for Int16 with div(self : Int16, that : Int16) -> Int16 { (self.to_int() / that.to_int()).to_int16() } ///| -pub impl Mod for Int16 with op_mod(self : Int16, that : Int16) -> Int16 { +pub impl Mod for Int16 with mod(self : Int16, that : Int16) -> Int16 { (self.to_int() % that.to_int()).to_int16() } ///| -pub impl Eq for Int16 with op_equal(self, that) { +pub impl Eq for Int16 with equal(self, that) { self.to_int() == that.to_int() } @@ -64,12 +64,12 @@ pub impl Hash for Int16 with hash_combine(self, hasher) { } ///| -pub impl Shl for Int16 with op_shl(self : Int16, that : Int) -> Int16 { +pub impl Shl for Int16 with shl(self : Int16, that : Int) -> Int16 { (self.to_int() << that).to_int16() } ///| -pub impl Shr for Int16 with op_shr(self : Int16, that : Int) -> Int16 { +pub impl Shr for Int16 with shr(self : Int16, that : Int) -> Int16 { (self.to_int() >> that).to_int16() } @@ -89,7 +89,7 @@ pub impl BitXOr for Int16 with lxor(self : Int16, that : Int16) -> Int16 { } ///| -pub impl Neg for Int16 with op_neg(self : Int16) -> Int16 { +pub impl Neg for Int16 with neg(self : Int16) -> Int16 { (-self.to_int()).to_int16() } @@ -111,3 +111,9 @@ pub impl Default for Int16 with default() { pub impl ToJson for Int16 with to_json(self : Int16) -> Json { Json::number(self.to_int().to_double()) } + +///| +/// reinterpret as an unsigned integer with binary complement +pub fn Int16::reinterpret_as_uint16(self : Int16) -> UInt16 { + self.to_int().to_uint16() +} diff --git a/bundled-core/int16/int16_test.mbt b/bundled-core/int16/int16_test.mbt index a3741cc..884a32c 100644 --- a/bundled-core/int16/int16_test.mbt +++ b/bundled-core/int16/int16_test.mbt @@ -426,3 +426,10 @@ test "Int16::to_json" { inspect(Int16::to_json(32767), content="Number(32767)") inspect(Int16::to_json(-32768), content="Number(-32768)") } + +///| +test "Int16::reinterpret_as_uint16" { + inspect((-1 : Int16).reinterpret_as_uint16(), content="65535") + inspect((0 : Int16).reinterpret_as_uint16(), content="0") + inspect((1 : Int16).reinterpret_as_uint16(), content="1") +} diff --git a/bundled-core/int16/moon.pkg.json b/bundled-core/int16/moon.pkg.json index 822f50a..523beeb 100644 --- a/bundled-core/int16/moon.pkg.json +++ b/bundled-core/int16/moon.pkg.json @@ -1,6 +1,4 @@ { - "import": [ - "moonbitlang/core/builtin", - "moonbitlang/core/json" - ] + "import": ["moonbitlang/core/builtin", "moonbitlang/core/json"], + "test-import": ["moonbitlang/core/uint16"] } diff --git a/bundled-core/int16/int16.mbti b/bundled-core/int16/pkg.generated.mbti similarity index 82% rename from bundled-core/int16/int16.mbti rename to bundled-core/int16/pkg.generated.mbti index 11d573e..db4644c 100644 --- a/bundled-core/int16/int16.mbti +++ b/bundled-core/int16/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/int16" // Values @@ -5,8 +6,11 @@ let max_value : Int16 let min_value : Int16 +// Errors + // Types and methods fn Int16::abs(Int16) -> Int16 +fn Int16::reinterpret_as_uint16(Int16) -> UInt16 impl Add for Int16 impl BitAnd for Int16 impl BitOr for Int16 diff --git a/bundled-core/int64/README.mbt.md b/bundled-core/int64/README.mbt.md index 9b0acd0..c8a3ef8 100644 --- a/bundled-core/int64/README.mbt.md +++ b/bundled-core/int64/README.mbt.md @@ -34,15 +34,15 @@ test "binary conversion" { // Convert to String for inspection inspect( be_bytes.to_string(), - content= + content=( #|b"\x00\x00\x00\x00\x00\x00\x01\x02" - , + ), ) inspect( le_bytes.to_string(), - content= + content=( #|b"\x02\x01\x00\x00\x00\x00\x00\x00" - , + ), ) // We can verify they represent the same number but in different byte orders @@ -65,9 +65,9 @@ test "method style" { // Binary conversions as methods inspect( x.to_be_bytes(), - content= + content=( #|b"\xff\xff\xff\xff\xff\xff\xff\xd6" - , + ), ) } ``` diff --git a/bundled-core/int64/int64.mbt b/bundled-core/int64/int64.mbt index 2f8ffb8..78f3459 100644 --- a/bundled-core/int64/int64.mbt +++ b/bundled-core/int64/int64.mbt @@ -58,6 +58,7 @@ pub fn from_int(i : Int) -> Int64 { /// inspect((-42L).abs(), content="42") /// inspect(0L.abs(), content="0") /// ``` +#as_free_fn pub fn Int64::abs(self : Int64) -> Int64 { if self < 0L { -self @@ -67,14 +68,13 @@ pub fn Int64::abs(self : Int64) -> Int64 { } ///| -pub fnalias Int64::abs - -///| Converts the Int64 to a Bytes in big-endian byte order. +/// Converts the Int64 to a Bytes in big-endian byte order. pub fn to_be_bytes(self : Int64) -> Bytes { self.reinterpret_as_uint64().to_be_bytes() } -///| Converts the Int64 to a Bytes in little-endian byte order. +///| +/// Converts the Int64 to a Bytes in little-endian byte order. pub fn to_le_bytes(self : Int64) -> Bytes { self.reinterpret_as_uint64().to_le_bytes() } diff --git a/bundled-core/int64/int64_test.mbt b/bundled-core/int64/int64_test.mbt index b63db47..7818512 100644 --- a/bundled-core/int64/int64_test.mbt +++ b/bundled-core/int64/int64_test.mbt @@ -181,9 +181,9 @@ test "to_be_bytes" { let num = 0x1234567890ABCDEFL inspect( num.to_be_bytes(), - content= + content=( #|b"\x12\x34\x56\x78\x90\xab\xcd\xef" - , + ), ) } @@ -192,8 +192,8 @@ test "to_le_bytes of negative Int64" { let num = -0x1234567890ABCDEFL inspect( num.to_le_bytes(), - content= + content=( #|b"\x11\x32\x54\x6f\x87\xa9\xcb\xed" - , + ), ) } diff --git a/bundled-core/int64/int64.mbti b/bundled-core/int64/pkg.generated.mbti similarity index 82% rename from bundled-core/int64/int64.mbti rename to bundled-core/int64/pkg.generated.mbti index a5d241d..6198218 100644 --- a/bundled-core/int64/int64.mbti +++ b/bundled-core/int64/pkg.generated.mbti @@ -1,15 +1,17 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/int64" // Values -fn abs(Int64) -> Int64 - fn from_int(Int) -> Int64 let max_value : Int64 let min_value : Int64 +// Errors + // Types and methods +#as_free_fn fn Int64::abs(Int64) -> Int64 fn Int64::from_int(Int) -> Int64 fn Int64::to_be_bytes(Int64) -> Bytes diff --git a/bundled-core/int64/xxhash.mbt b/bundled-core/int64/xxhash.mbt index 139f8f5..1f4af68 100644 --- a/bundled-core/int64/xxhash.mbt +++ b/bundled-core/int64/xxhash.mbt @@ -27,9 +27,10 @@ let gPRIME4 = 0x27D4EB2FU ///| let gPRIME5 = 0x165667B1U -///| // https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md#xxh32-algorithm-description // For a more readable version, see bytes/xxhash.mbt + +///| pub impl Hash for Int64 with hash(self) -> Int { let mut input = self.reinterpret_as_uint64() let seed = 0U diff --git a/bundled-core/json/README.mbt.md b/bundled-core/json/README.mbt.md index 0cf19c4..8b0af43 100644 --- a/bundled-core/json/README.mbt.md +++ b/bundled-core/json/README.mbt.md @@ -16,7 +16,7 @@ test "parse and validate jsons" { // Parse JSON string into Json value let json = @json.parse("{\"key\": 42}") catch { - (_ : ParseError) => panic() + (_ : @json.ParseError) => panic() // _ => panic() // redundant, the type checker won't refine further } @@ -46,9 +46,9 @@ test "json object navigation" { let string_opt = json.value("string").unwrap().as_string() inspect( string_opt, - content= + content=( #|Some("hello") - , + ), ) // Access number @@ -108,9 +108,9 @@ test "json decode" { let map : Map[String, Int] = @json.from_json(json_map) inspect( map, - content= + content=( #|{"a": 1, "b": 2} - , + ), ) } ``` @@ -139,7 +139,7 @@ This is particularly true for deeply-nested data structures. ```moonbit test "json inspection" { - let null = Json::null() + let null = null // Simple json values let json_value : Json = { "key": "value", "numbers": [1, 2, 3] } diff --git a/bundled-core/json/derive_json_test.mbt b/bundled-core/json/derive_json_test.mbt index 64f2f08..f3fa6a8 100644 --- a/bundled-core/json/derive_json_test.mbt +++ b/bundled-core/json/derive_json_test.mbt @@ -26,20 +26,21 @@ test { let v0 : Hello[Int, Int] = @json.from_json(vj) inspect( v0, - content= + content=( #|{fieldA: 3, fieldB: 2, data: {"a": 3}} - , + ), ) assert_eq(v0, v) } -///| // struct Hello2 [A, B] { // fieldA : A // fieldB : B // data : Map[B, A] // }derive(ToJson) // Should the deriver generate constraints that [B: Show] + +///| struct Hello3[A, B] { fieldAX : A fieldB : B @@ -52,16 +53,16 @@ test { let j = h.to_json() inspect( j, - content= + content=( #|Object({"fieldA": Number(1), "fieldB": String("hello"), "data": Object({"a": Number(1), "b": Number(2)})}) - , + ), ) let h3 = Hello3::{ fieldAX: 1, fieldB: "hello", data: { 1: 1, 2: 2 } } let j3 = h3.to_json() inspect( j3, - content= + content=( #|Object({"fieldAX": Number(1), "fieldB": String("hello"), "data": Object({"1": Number(1), "2": Number(2)})}) - , + ), ) } diff --git a/bundled-core/json/from_json.mbt b/bundled-core/json/from_json.mbt index f5acf27..fc74936 100644 --- a/bundled-core/json/from_json.mbt +++ b/bundled-core/json/from_json.mbt @@ -13,7 +13,11 @@ // limitations under the License. ///| -pub(all) suberror JsonDecodeError (JsonPath, String) derive(Eq, Show, ToJson) +pub(all) suberror JsonDecodeError (JsonPath, String) derive ( + Eq, + Show, + ToJson(style="flat"), +) ///| /// Trait for types that can be converted from `Json` @@ -24,7 +28,7 @@ pub(open) trait FromJson { ///| pub fn[T : FromJson] from_json( json : Json, - path~ : JsonPath = Root + path? : JsonPath = Root, ) -> T raise JsonDecodeError { FromJson::from_json(json, path) } @@ -45,9 +49,17 @@ pub impl FromJson for Bool with from_json(json, path) { ///| pub impl FromJson for Int with from_json(json, path) { - guard json is Number(n) else { + guard json is Number(n, ..) && + n != @double.infinity && + n != @double.neg_infinity else { decode_error(path, "Int::from_json: expected number") } + // Range check before conversion to avoid silent wrap/truncation + let max_ok = 2147483647.0 + let min_ok = -2147483648.0 + if n > max_ok || n < min_ok { + decode_error(path, "Int::from_json: overflow") + } n.to_int() } @@ -65,9 +77,16 @@ pub impl FromJson for Int64 with from_json(json, path) { ///| pub impl FromJson for UInt with from_json(json, path) { - guard json is Number(n) else { + guard json is Number(n, ..) && + n != @double.infinity && + n != @double.neg_infinity else { decode_error(path, "UInt::from_json: expected number") } + // Range check before conversion to avoid silent wrap/truncation + let max_ok = 4294967295.0 + if n < 0.0 || n > max_ok { + decode_error(path, "UInt::from_json: overflow") + } n.to_uint() } @@ -85,10 +104,13 @@ pub impl FromJson for UInt64 with from_json(json, path) { ///| pub impl FromJson for Double with from_json(json, path) { - guard json is Number(n) else { - decode_error(path, "Double::from_json: expected number") + match json { + String("NaN") => @double.not_a_number + String("Infinity") => @double.infinity + String("-Infinity") => @double.neg_infinity + Number(n, ..) if n != @double.infinity && n != @double.neg_infinity => n + _ => decode_error(path, "Double::from_json: expected number") } - n } ///| @@ -194,7 +216,7 @@ pub impl[T : FromJson] FromJson for T? with from_json(json, path) { ///| pub impl[Ok : FromJson, Err : FromJson] FromJson for Result[Ok, Err] with from_json( json, - path + path, ) { guard json is Object(obj) else { decode_error(path, "Result::from_json: expected object") @@ -220,6 +242,40 @@ pub impl FromJson for Unit with from_json(json, path) { } ///| -pub impl FromJson for JsonValue with from_json(json, _path) { +pub impl FromJson for Json with from_json(json, _path) { json } + +///| +/// Converts a JSON string to `Bytes`. +/// +/// The expected string format consists of: +/// - Hexadecimal byte sequences represented as `\xNN`, where `NN` are two hex digits. +/// - Printable ASCII characters (except `\` and `"`), which are directly converted to their byte values. +/// - Any invalid escape sequence or character will result in a `JsonDecodeError`. +/// +/// Example valid input: `"hello\\x20world"` (where `\\x20` is a space character). +pub impl FromJson for Bytes with from_json(json, path) { + guard json is String(a) else { + decode_error(path, "Bytes::from_json: expected string") + } + let buffer = @buffer.new(size_hint=a.length()) + loop a[:] { + [] => () + [.. "\\x", '0'..='9' | 'a'..='f' as x, '0'..='9' | 'a'..='f' as y, .. rest] => { + let upper = (x.to_int() & 0xF) + (x.to_int() >> 6) * 9 + let lower = (y.to_int() & 0xF) + (y.to_int() >> 6) * 9 + buffer.write_byte(((upper << 4) | lower).to_byte()) + continue rest + } + [' '..='~' as ch, .. rest] => { + guard ch != '\\' && ch != '"' else { + decode_error(path, "Bytes::from_json: invalid escape sequence") + } + buffer.write_byte(ch.to_uint().to_byte()) + continue rest + } + _ => decode_error(path, "Bytes::from_json: invalid byte sequence") + } + buffer.to_bytes() +} diff --git a/bundled-core/json/from_json_test.mbt b/bundled-core/json/from_json_test.mbt index 81457fd..3306dec 100644 --- a/bundled-core/json/from_json_test.mbt +++ b/bundled-core/json/from_json_test.mbt @@ -12,26 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -/// FIXME: remove later, workaround formatter bug -fn temp() -> Unit { - () -} - ///| typealias Map[String, Int] as TestX ///| test { - let _ = temp() let u = { "x": 3, "y": 4 } let j = u.to_json() let h : TestX = @json.from_json(j) inspect( (j, h), - content= + content=( #|(Object({"x": Number(3), "y": Number(4)}), {"x": 3, "y": 4}) - , + ), ) } @@ -41,9 +34,9 @@ test { let v : Result[Array[Int], _] = try? @json.from_json(u) inspect( v, - content= + content=( #|Err(JsonDecodeError(($[1], "Int::from_json: expected number"))) - , + ), ) } @@ -56,9 +49,9 @@ test { let v : Result[Map[String, Map[String, Double]], _] = try? @json.from_json(u) inspect( v, - content= + content=( #|Err(JsonDecodeError(($.y.yy, "Double::from_json: expected number"))) - , + ), ) } @@ -282,9 +275,9 @@ test "tuple failure" { let v : Result[(Int, Int, Int), _] = try? @json.from_json(u.to_json()) inspect( v.unwrap_err(), - content= + content=( #|JsonDecodeError(($, "expected tuple of size 3")) - , + ), ) let u = ((1, 2), (3, 4)) let v : Result[((Int, Int), Int), _] = try? @json.from_json( @@ -292,9 +285,9 @@ test "tuple failure" { ) inspect( v.unwrap_err(), - content= + content=( #|JsonDecodeError(($[1], "Int::from_json: expected number")) - , + ), ) } @@ -307,9 +300,9 @@ test "fixedarray" { let v : FixedArray[String] = @json.from_json(u.to_json()) inspect( v, - content= + content=( #|["123", "456"] - , + ), ) let u = ([(), ()] : FixedArray[Unit]) let v : FixedArray[Unit] = @json.from_json(u.to_json()) @@ -373,24 +366,24 @@ test "jsonvalue" { let u = Json::boolean(false) let v : Json = @json.from_json(u) inspect(v, content="False") - let u = Json::null() + let u = null let v : Json = @json.from_json(u) inspect(v, content="Null") let u = Json::array([Json::number(1), Json::string("str")]) let v : Json = @json.from_json(u) inspect( v, - content= + content=( #|Array([Number(1), String("str")]) - , + ), ) let u = Json::object({ "x": Json::number(1) }) let v : Json = @json.from_json(u) inspect( v, - content= + content=( #|Object({"x": Number(1)}) - , + ), ) } @@ -426,12 +419,51 @@ test "uint roundtrip" { ///| test "uint" { + // valid max let v : UInt = @json.from_json(Json::number(4294967295)) inspect(v, content="4294967295") - let v : UInt = @json.from_json(Json::number(-1)) - inspect(v, content="0") - let v : UInt = @json.from_json(Json::number(4294967296)) - inspect(v, content="4294967295") + // negative should error + let neg_res : Result[UInt, _] = try? @json.from_json(Json::number(-1)) + inspect( + neg_res, + content="Err(JsonDecodeError(($, \"UInt::from_json: overflow\")))", + ) + // overflow should error + let ovf_res : Result[UInt, _] = try? @json.from_json(Json::number(4294967296)) + inspect( + ovf_res, + content="Err(JsonDecodeError(($, \"UInt::from_json: overflow\")))", + ) +} + +///| +test "Int from_json overflow should error" { + let json = @json.parse("2147483648") // INT_MAX + 1 for 32-bit + let res : Result[Int, _] = try? @json.from_json(json) + match res { + Ok(v) => fail("BUG: Int overflow not detected, decoded as \{v}") + Err(_) => () + } +} + +///| +test "Int from_json negative overflow should error" { + let json = @json.parse("-2147483649") // INT_MIN - 1 for 32-bit + let res : Result[Int, _] = try? @json.from_json(json) + match res { + Ok(v) => fail("BUG: Int negative overflow not detected, decoded as \{v}") + Err(_) => () + } +} + +///| +test "UInt from_json overflow should error" { + let json = @json.parse("4294967296") // UINT_MAX + 1 for 32-bit + let res : Result[UInt, _] = try? @json.from_json(json) + match res { + Ok(v) => fail("BUG: UInt overflow not detected, decoded as \{v}") + Err(_) => () + } } ///| @@ -462,9 +494,9 @@ test "unicode" { let u = "\u{1F600}".to_json() inspect( u, - content= + content=( #|String("๐Ÿ˜€") - , + ), ) let v : Char = @json.from_json(u) inspect(v, content="\u{1F600}") @@ -477,9 +509,9 @@ test "unicode" { let v : Result[Char, _] = try? @json.from_json(u) inspect( v, - content= + content=( #|Err(JsonDecodeError(($, "Char::from_json: invalid surrogate pair"))) - , + ), ) } @@ -488,9 +520,9 @@ test "char to/from json roundtrip" { let u = 'a'.to_json() inspect( u, - content= + content=( #|String("a") - , + ), ) let v : Char = @json.from_json(u) inspect(v, content="a") @@ -499,9 +531,9 @@ test "char to/from json roundtrip" { let u = '๐Ÿ˜€'.to_json() inspect( u, - content= + content=( #|String("๐Ÿ˜€") - , + ), ) let v : Char = @json.from_json(u) inspect(v, content="๐Ÿ˜€") @@ -510,9 +542,9 @@ test "char to/from json roundtrip" { let u = 'ไธญ'.to_json() inspect( u, - content= + content=( #|String("ไธญ") - , + ), ) let v : Char = @json.from_json(u) inspect(v, content="ไธญ") @@ -527,7 +559,7 @@ test "string view from json" { ///| test "Bool from_json error handling" { - let json_null = Json::null() + let json_null = null let result : Result[Bool, _] = try? @json.from_json(json_null) inspect( result, @@ -637,9 +669,9 @@ test "BigInt from_json error handling" { let result : Result[BigInt, _] = try? @json.from_json(json_number) inspect( result, - content= + content=( #|Err(JsonDecodeError(($, "BigInt::from_json: expected number in string representation"))) - , + ), ) } @@ -720,3 +752,16 @@ test "Unit from_json error handling" { content="Err(JsonDecodeError(($, \"Unit::from_json: expected null\")))", ) } + +///| +test "Bytes from_json" { + let bytes = b"\x00\x01abcd\"\\" + let json = bytes.to_json() + let result : Result[Bytes, _] = try? @json.from_json(json) + inspect( + result, + content=( + #|Ok(b"\x00\x01\x61\x62\x63\x64\x22\x5c") + ), + ) +} diff --git a/bundled-core/json/from_to_json_test.mbt b/bundled-core/json/from_to_json_test.mbt index ff98804..2d5485b 100644 --- a/bundled-core/json/from_to_json_test.mbt +++ b/bundled-core/json/from_to_json_test.mbt @@ -19,7 +19,7 @@ enum Expr { Mul(Expr, Expr) Div(Expr, Expr) Val(String) -} derive(ToJson, FromJson, Show, Eq) +} derive(ToJson(style="flat"), FromJson(style="flat"), Show, Eq) ///| test { @@ -27,16 +27,16 @@ test { let ej = e.to_json() inspect( ej.stringify(), - content= - #|{"$tag":"Add","0":{"$tag":"Val","0":"x"},"1":{"$tag":"Val","0":"y"}} - , + content=( + #|["Add",["Val","x"],["Val","y"]] + ), ) let e0 : Expr = @json.from_json(ej) inspect( e0, - content= + content=( #|Add(Val("x"), Val("y")) - , + ), ) assert_eq(e0, e) } diff --git a/bundled-core/json/internal_types.mbt b/bundled-core/json/internal_types.mbt index f8bc309..28fb52e 100644 --- a/bundled-core/json/internal_types.mbt +++ b/bundled-core/json/internal_types.mbt @@ -25,7 +25,7 @@ fn ParseContext::make(input : String) -> ParseContext { } ///| -priv type CharClass Array[(Char, Char)] +priv struct CharClass(Array[(Char, Char)]) ///| fn CharClass::of(array : Array[(Char, Char)]) -> CharClass { @@ -55,7 +55,7 @@ priv enum Token { Null True False - Number(Double) + Number(Double, String?) String(String) LBrace RBrace diff --git a/bundled-core/json/json.mbt b/bundled-core/json/json.mbt index 466523e..cf77bba 100644 --- a/bundled-core/json/json.mbt +++ b/bundled-core/json/json.mbt @@ -14,14 +14,14 @@ ///| /// Try to get this element as a Null -pub fn as_null(self : JsonValue) -> Unit? { +pub fn as_null(self : Json) -> Unit? { guard self is Null else { return None } Some(()) } ///| /// Try to get this element as a Boolean -pub fn as_bool(self : JsonValue) -> Bool? { +pub fn as_bool(self : Json) -> Bool? { match self { True => Some(true) False => Some(false) @@ -31,28 +31,28 @@ pub fn as_bool(self : JsonValue) -> Bool? { ///| /// Try to get this element as a Number -pub fn as_number(self : JsonValue) -> Double? { - guard self is Number(n) else { return None } +pub fn as_number(self : Json) -> Double? { + guard self is Number(n, ..) else { return None } Some(n) } ///| /// Try to get this element as a String -pub fn as_string(self : JsonValue) -> String? { +pub fn as_string(self : Json) -> String? { guard self is String(s) else { return None } Some(s) } ///| /// Try to get this element as an Array -pub fn as_array(self : JsonValue) -> Array[JsonValue]? { +pub fn as_array(self : Json) -> Array[Json]? { guard self is Array(arr) else { return None } Some(arr) } ///| /// Try to get this element as a Json Array and get the element at the `index` as a Json Value -pub fn item(self : JsonValue, index : Int) -> JsonValue? { +pub fn item(self : Json, index : Int) -> Json? { match self.as_array() { Some(arr) => arr.get(index) None => None @@ -61,14 +61,14 @@ pub fn item(self : JsonValue, index : Int) -> JsonValue? { ///| /// Try to get this element as an Object -pub fn as_object(self : JsonValue) -> Map[String, JsonValue]? { +pub fn as_object(self : Json) -> Map[String, Json]? { guard self is Object(obj) else { return None } Some(obj) } ///| /// Try to get this element as a Json Object and get the element with the `key` as a Json Value -pub fn value(self : JsonValue, key : String) -> JsonValue? { +pub fn value(self : Json, key : String) -> Json? { match self.as_object() { Some(obj) => obj.get(key) None => None @@ -86,17 +86,18 @@ fn indent_str(level : Int, indent : Int) -> String { ///| pub fn stringify( - self : JsonValue, - escape_slash~ : Bool = false, - indent~ : Int = 0 + self : Json, + escape_slash? : Bool = false, + indent? : Int = 0, ) -> String { - fn stringify_inner(value : JsonValue, level : Int) -> String { + let buf = StringBuilder::new(size_hint=0) + fn stringify_inner(value : Json, level : Int) -> Unit { match value { Object(members) => { if members.is_empty() { - return "{}" + buf.write_string("{}") + return } - let buf = StringBuilder::new(size_hint=0) buf.write_char('{') buf.write_string(indent_str(level + 1, indent)) let mut first = true @@ -116,16 +117,16 @@ pub fn stringify( } else { buf.write_string(": ") } - buf.write_string(stringify_inner(v, level + 1)) + stringify_inner(v, level + 1) } buf.write_string(indent_str(level, indent)) - buf..write_char('}').to_string() + buf.write_char('}') } Array(arr) => { if arr.is_empty() { - return "[]" + buf.write_string("[]") + return } - let buf = StringBuilder::new(size_hint=0) buf.write_char('[') buf.write_string(indent_str(level + 1, indent)) for i, v in arr { @@ -133,27 +134,29 @@ pub fn stringify( buf.write_char(',') buf.write_string(indent_str(level + 1, indent)) } - buf.write_string(stringify_inner(v, level + 1)) + stringify_inner(v, level + 1) } buf.write_string(indent_str(level, indent)) - buf..write_char(']').to_string() + buf.write_char(']') } - String(s) => { - let buf = StringBuilder::new(size_hint=0) + String(s) => buf ..write_char('\"') ..write_string(escape(s, escape_slash~)) ..write_char('\"') - .to_string() - } - Number(n) => n.to_string() - True => "true" - False => "false" - Null => "null" + Number(n, repr~) => + match repr { + None => buf.write_object(n) + Some(r) => buf.write_string(r) + } + True => buf.write_string("true") + False => buf.write_string("false") + Null => buf.write_string("null") } } stringify_inner(self, 0) + buf.to_string() } ///| @@ -200,16 +203,17 @@ fn escape(str : String, escape_slash~ : Bool) -> String { ///| /// Useful for json interpolation -pub impl ToJson for JsonValue with to_json(self) { +pub impl ToJson for Json with to_json(self) { self } ///| +#callsite(autofill(args_loc, loc)) pub fn inspect( obj : &ToJson, content? : Json, - loc~ : SourceLoc = _, - args_loc~ : ArgsLoc = _ + loc~ : SourceLoc, + args_loc~ : ArgsLoc, ) -> Unit raise InspectError { let loc = loc.to_string().escape() let args_loc = args_loc.to_json().escape() diff --git a/bundled-core/json/json_encode_decode_test.mbt b/bundled-core/json/json_encode_decode_test.mbt index 8e7f4f6..5d24d05 100644 --- a/bundled-core/json/json_encode_decode_test.mbt +++ b/bundled-core/json/json_encode_decode_test.mbt @@ -53,13 +53,13 @@ fn of_json(jv : Json) -> AllThree raise DecodeError { let strings_result = [] for n in ints { match n { - Number(n) => ints_result.push(n.to_int()) + Number(n, ..) => ints_result.push(n.to_int()) _ => () // error handling here } } for n in floats { match n { - Number(n) => floats_result.push(n) + Number(n, ..) => floats_result.push(n) _ => () // error handling here } } @@ -85,9 +85,9 @@ test { let json = all_three.to_json() inspect( try? of_json(json), - content= + content=( #|Ok({ints: [1, 2, 3], floats: [1, 2, 3], strings: ["a", "b", "c"]}) - , + ), ) // FIXME: // note try? `body_no_error` is confusing diff --git a/bundled-core/json/json_inspect_test.mbt b/bundled-core/json/json_inspect_test.mbt index fda0088..29732d2 100644 --- a/bundled-core/json/json_inspect_test.mbt +++ b/bundled-core/json/json_inspect_test.mbt @@ -34,7 +34,7 @@ enum Color { Red Green Blue -} derive(ToJson) +} derive(ToJson(style="flat")) ///| struct Line { @@ -47,11 +47,11 @@ struct Line { test "json inspect" { let p1 = { x: 0, y: 0, color: Color::Red } let p2 = { x: 1, y: 2, color: Color::Green } - @json.inspect(p1, content={ "x": 0, "y": 0, "color": { "$tag": "Red" } }) + @json.inspect(p1, content={ "x": 0, "y": 0, "color": "Red" }) let line = { p1, p2, color: Color::Blue } @json.inspect(line, content={ - "p1": { "x": 0, "y": 0, "color": { "$tag": "Red" } }, - "p2": { "x": 1, "y": 2, "color": { "$tag": "Green" } }, - "color": { "$tag": "Blue" }, + "p1": { "x": 0, "y": 0, "color": "Red" }, + "p2": { "x": 1, "y": 2, "color": "Green" }, + "color": "Blue", }) } diff --git a/bundled-core/json/json_test.mbt b/bundled-core/json/json_test.mbt index fcb4d21..3e09d6f 100644 --- a/bundled-core/json/json_test.mbt +++ b/bundled-core/json/json_test.mbt @@ -14,7 +14,7 @@ ///| test "get as null" { - inspect(Json::null().as_null(), content="Some(())") + inspect(null.as_null(), content="Some(())") inspect(Json::boolean(true).as_null(), content="None") inspect(Json::boolean(false).as_null(), content="None") inspect(Json::number(1.0).as_null(), content="None") @@ -28,7 +28,7 @@ test "get as null" { ///| test "get as bool" { - inspect(Json::null().as_bool(), content="None") + inspect(null.as_bool(), content="None") inspect(Json::boolean(true).as_bool(), content="Some(true)") inspect(Json::boolean(false).as_bool(), content="Some(false)") inspect(Json::number(1.0).as_bool(), content="None") @@ -49,7 +49,7 @@ fn _check2(x : String) -> Json raise @json.ParseError { ///| test "get as number" { - inspect(Json::null().as_number(), content="None") + inspect(null.as_number(), content="None") inspect(Json::boolean(false).as_number(), content="None") inspect(Json::boolean(true).as_number(), content="None") inspect(Json::number(1.0).as_number(), content="Some(1)") @@ -68,15 +68,15 @@ test "get as number" { ///| test "get as string" { - inspect(Json::null().as_string(), content="None") + inspect(null.as_string(), content="None") inspect(Json::boolean(true).as_string(), content="None") inspect(Json::boolean(false).as_string(), content="None") inspect(Json::number(1.0).as_string(), content="None") inspect( Json::string("Hello World").as_string(), - content= + content=( #|Some("Hello World") - , + ), ) inspect( Json::array([Json::string("Hello World")]).as_string(), @@ -92,22 +92,22 @@ test "get as string" { ///| test "get as array" { - inspect(Json::null().as_array(), content="None") + inspect(null.as_array(), content="None") inspect(Json::boolean(true).as_array(), content="None") inspect(Json::boolean(false).as_array(), content="None") inspect(Json::number(1.0).as_array(), content="None") inspect(Json::string("Hello World").as_array(), content="None") inspect( Json::array([Json::string("Hello World")]).as_array(), - content= + content=( #|Some([String("Hello World")]) - , + ), ) inspect( Json::array([Json::string("Hello World")]).item(0).bind(Json::as_string), - content= + content=( #|Some("Hello World") - , + ), ) inspect( Json::array([Json::string("Hello World")]).item(1).bind(Json::as_string), @@ -127,7 +127,7 @@ test "get as array" { ///| test "get as object" { - inspect(Json::null().as_object().map(_.to_array()), content="None") + inspect(null.as_object().map(_.to_array()), content="None") inspect(Json::boolean(true).as_object().map(_.to_array()), content="None") inspect(Json::boolean(false).as_object().map(_.to_array()), content="None") inspect(Json::number(1.0).as_object().map(_.to_array()), content="None") @@ -145,9 +145,9 @@ test "get as object" { ) .as_object() .map(_.to_array()), - content= + content=( #|Some([("key", String("key")), ("value", Number(100))]) - , + ), ) inspect( Json::object( @@ -155,9 +155,9 @@ test "get as object" { ) .value("key") .bind(_.as_string()), - content= + content=( #|Some("key") - , + ), ) inspect( Json::object( @@ -188,10 +188,10 @@ test "get as object" { ///| test "deep access" { let json : Json = { - "key": [1.0, true, Json::null(), [], { "key": "value", "value": 100.0 }], + "key": [1.0, true, null, [], { "key": "value", "value": 100.0 }], "int": 12345, "double": 123.45, - "null": Json::null(), + "null": null, "bool": false, "obj": {}, } @@ -207,9 +207,9 @@ test "deep access" { ) inspect( json.value("key").bind(_.as_array()), - content= + content=( #|Some([Number(1), True, Null, Array([]), Object({"key": String("value"), "value": Number(100)})]) - , + ), ) inspect( json.value("key").bind(_.item(3)).bind(_.as_array()), @@ -217,9 +217,9 @@ test "deep access" { ) inspect( json.value("key").bind(_.item(4)).bind(_.as_object()).map(_.to_array()), - content= + content=( #|Some([("key", String("value")), ("value", Number(100))]) - , + ), ) inspect( json.value("obj").bind(_.as_object()).map(_.to_array()), @@ -230,25 +230,25 @@ test "deep access" { ///| test "stringify" { let json : Json = { - "key": [1.0, true, Json::null(), [], { "key": "value", "value": 100.0 }], + "key": [1.0, true, null, [], { "key": "value", "value": 100.0 }], "int": 12345, "double": 123.45, - "null": Json::null(), + "null": null, "bool": false, "obj": {}, } inspect( json.stringify(), - content= + content=( #|{"key":[1,true,null,[],{"key":"value","value":100}],"int":12345,"double":123.45,"null":null,"bool":false,"obj":{}} - , + ), ) // not mut once we have `try except else` syntax // we do come across issues like ParseError not unified with String let newjson = @json.parse(json.stringify()) match json { - { "key": [_, _, _, _, { "value": Number(i), .. }, ..], .. } => + { "key": [_, _, _, _, { "value": Number(i, ..), .. }, ..], .. } => inspect(i, content="100") _ => fail("Failed to match the JSON") } @@ -270,9 +270,9 @@ let json : Json = { test "stringify escape 1 " { inspect( json.stringify(), - content= + content=( #|{"\b":"\"hello\"","\n":"\n\r\t\f","\\":"/","\u001f":"\u0001","actual":"a,\"b\",c","escape":"\u0000"} - , + ), ) } @@ -280,9 +280,9 @@ test "stringify escape 1 " { test "stringify escape 2" { inspect( json.stringify(escape_slash=true), - content= + content=( #|{"\b":"\"hello\"","\n":"\n\r\t\f","\\":"\/","\u001f":"\u0001","actual":"a,\"b\",c","escape":"\u0000"} - , + ), ) } @@ -290,7 +290,7 @@ test "stringify escape 2" { test "stringify escape 3" { inspect( json.stringify(escape_slash=false, indent=2), - content= + content=( #|{ #| "\b": "\"hello\"", #| "\n": "\n\r\t\f", @@ -299,7 +299,7 @@ test "stringify escape 3" { #| "actual": "a,\"b\",c", #| "escape": "\u0000" #|} - , + ), ) } @@ -307,9 +307,9 @@ test "stringify escape 3" { test "stringify escape 4" { inspect( json.stringify(escape_slash=false), - content= + content=( #|{"\b":"\"hello\"","\n":"\n\r\t\f","\\":"/","\u001f":"\u0001","actual":"a,\"b\",c","escape":"\u0000"} - , + ), ) } @@ -340,15 +340,15 @@ test "escape" { let s = "http://example.com/" inspect( Json::string(s).stringify(escape_slash=true), - content= + content=( #|"http:\/\/example.com\/" - , + ), ) inspect( Json::string(s).stringify(), - content= + content=( #|"http://example.com/" - , + ), ) } @@ -379,14 +379,40 @@ test "stringify number" { inspect(nums[7].stringify(), content="9.999999999992234e+29") inspect(nums[8].stringify(), content="-1.7976931348623157e+308") inspect(nums[9].stringify(), content="1.7976931348623157e+308") - inspect(nums[10].stringify(), content="null") - inspect(nums[11].stringify(), content="null") - inspect(nums[12].stringify(), content="null") + inspect( + nums[10].stringify(), + content=( + #|"NaN" + ), + ) + inspect( + nums[11].stringify(), + content=( + #|"Infinity" + ), + ) + inspect( + nums[12].stringify(), + content=( + #|"-Infinity" + ), + ) let err : Array[String] = [] for json in nums { - match (try? @json.parse(json.stringify())) { - Err(e) => err.push(e.to_string()) - Ok(newjson) => assert_eq(newjson, json) + let json = @json.parse(json.stringify()) catch { + e => { + err.push(e.to_string()) + continue + } + } + match json { + Number(_, repr~) as newjson => + if repr is Some(_) { + assert_eq(newjson.stringify(), json.stringify()) + } else { + assert_eq(newjson, json) + } + newjson => assert_eq(newjson, json) } } inspect(err, content="[]") @@ -395,22 +421,22 @@ test "stringify number" { ///| test "stringify with indent" { let json : Json = { - "key": [1.0, true, Json::null(), [], { "key": "value", "value": 100.0 }], + "key": [1.0, true, null, [], { "key": "value", "value": 100.0 }], "int": 12345, "double": 123.45, - "null": Json::null(), + "null": null, "bool": false, "obj": {}, } inspect( json.stringify(indent=0), - content= + content=( #|{"key":[1,true,null,[],{"key":"value","value":100}],"int":12345,"double":123.45,"null":null,"bool":false,"obj":{}} - , + ), ) inspect( json.stringify(indent=1), - content= + content=( #|{ #| "key": [ #| 1, @@ -428,11 +454,11 @@ test "stringify with indent" { #| "bool": false, #| "obj": {} #|} - , + ), ) inspect( json.stringify(indent=2), - content= + content=( #|{ #| "key": [ #| 1, @@ -450,11 +476,11 @@ test "stringify with indent" { #| "bool": false, #| "obj": {} #|} - , + ), ) inspect( json.stringify(indent=4), - content= + content=( #|{ #| "key": [ #| 1, @@ -472,7 +498,7 @@ test "stringify with indent" { #| "bool": false, #| "obj": {} #|} - , + ), ) } diff --git a/bundled-core/json/json_traverse_test.mbt b/bundled-core/json/json_traverse_test.mbt new file mode 100644 index 0000000..f499e98 --- /dev/null +++ b/bundled-core/json/json_traverse_test.mbt @@ -0,0 +1,228 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// pseudo code is as below +// Json = Null | Bool | Num | Str | Arr[List[Json]] | Obj[Map] +// Return Option: None = โ€œdrop this nodeโ€ +// fn prune_loc(v: Json) -> Option { +// match v { +// Null | Bool(_) | Num(_) | Str(_) => Some(v), + +// Arr(xs) => { +// let ys = xs +// .map(prune_loc) +// .filter_map(id); // keep only Some +// Some(Arr(ys)) // arrays are retained even if empty +// } + +// Obj(m) => { +// // 1) drop "loc" +// let m1 = m.remove_key("loc"); + +// // 2) prune children, dropping empties +// let m2 = Map::new(); +// for (k, child) in m1 { +// match prune_loc(child) { +// Some(c2) => m2.insert(k, c2), +// None => {} // drop this key +// } +// } + +// // 3) drop map if empty +// if m2.is_empty() { None } else { Some(Obj(m2)) } +// } +// } +// } +fn Json::prune_loc(self : Json) -> Json? { + match self { + Null | True | False | Number(_, ..) | String(_) => Some(self) + Array(arr) => { + let pruned_arr = Array::new() + for item in arr { + match item.prune_loc() { + Some(pruned_item) => pruned_arr.push(pruned_item) + None => () // drop this item + } + } + Some(Json::array(pruned_arr)) + } + Object(obj) => { + // 1) create a new map without "loc" key + let new_obj = Map::new() + + // 2) iterate through all keys except "loc" + for key, value in obj { + if key != "loc" { + match value.prune_loc() { + Some(pruned_value) => new_obj.set(key, pruned_value) + None => () // drop this key-value pair + } + } + } + + // 3) return None if the object is empty, otherwise return the pruned object + if new_obj.is_empty() { + None + } else { + Some(Json::object(new_obj)) + } + } + } +} + +///| +fn Json::prune_loc_test(self : Json) -> Json { + match self.prune_loc() { + Some(pruned) => pruned + None => "Empty" + } +} + +///| +test "prune_loc - primitive values" { + // Null, True, False, Number, String should be preserved as-is + inspect(null.prune_loc(), content="Some(Null)") + inspect(Json::boolean(true).prune_loc(), content="Some(True)") + inspect(Json::boolean(false).prune_loc(), content="Some(False)") + inspect(Json::number(42.0).prune_loc(), content="Some(Number(42))") + inspect(Json::string("hello").prune_loc(), content="Some(String(\"hello\"))") +} + +///| +test "prune_loc - arrays" { + // Empty array + inspect(Json::array([]).prune_loc(), content="Some(Array([]))") + + // Array with primitives + let arr1 = Json::array([ + null, + Json::boolean(true), + Json::number(123.0), + Json::string("test"), + ]) + @json.inspect( + arr1.prune_loc_test(), + content=[Null, true, 123, "test"], // FIXME: support null + ) + + // Nested arrays + let arr2 : Json = [[1, 2], []] + @json.inspect(arr2.prune_loc(), content=[[[1, 2], []]]) +} + +///| +test "prune_loc - objects without loc" { + // Empty object + inspect(Json::object(Map::new()).prune_loc(), content="None") + + // Object with no "loc" key + let obj1 : Json = { "name": Json::string("John"), "age": Json::number(30.0) } + @json.inspect(obj1.prune_loc(), content=[{ "name": "John", "age": 30 }]) +} + +///| +test "prune_loc - objects with loc key" { + // Object with only "loc" key should be dropped + let obj_only_loc : Json = { + "loc": Json::object({ + "line": Json::number(10.0), + "column": Json::number(5.0), + }), + } + @json.inspect(obj_only_loc.prune_loc(), content=null) // FIXME: None -> Null + + // Object with "loc" and other keys - "loc" should be removed + let obj_with_loc : Json = { + "type": Json::string("function"), + "name": Json::string("myFunc"), + "loc": Json::object({ + "line": Json::number(10.0), + "column": Json::number(5.0), + }), + } + @json.inspect(obj_with_loc.prune_loc(), content=[ + { "type": "function", "name": "myFunc" }, + ]) +} + +///| +test "prune_loc - nested objects" { + // Nested object where inner object becomes empty after pruning + let nested1 : Json = { + "outer": Json::object({ "loc": Json::string("should be removed") }), + "value": Json::number(42.0), + } + @json.inspect(nested1.prune_loc(), content=[{ "value": 42 }]) + + // Nested object where all objects are pruned + let nested2 : Json = { + "inner": Json::object({ "loc": Json::string("remove me") }), + } + @json.inspect( + nested2.prune_loc(), + content=null, // FIXME: None -> Null + ) +} + +///| +test "prune_loc - complex mixed structure" { + let complex : Json = { + "ast": { + "type": "Program", + "body": [ + { + "type": "FunctionDeclaration", + "name": "test", + "loc": { "start": 1.0, "end": 10.0 }, + "params": [], + }, + { + "type": "ReturnStatement", + "argument": 42.0, + "loc": { "start": 11.0, "end": 20.0 }, + }, + ], + "loc": { "start": 0.0, "end": 25.0 }, + }, + } + let result = complex.prune_loc_test() + @json.inspect( + result, + content={ + "ast": { + "type": "Program", + "body": [ + { "type": "FunctionDeclaration", "name": "test", "params": [] }, + { "type": "ReturnStatement", "argument": 42 }, + ], + }, + }, // FIXME: None -> Null + ) +} + +///| +test "prune_loc - array with objects containing loc" { + let arr_with_loc_objects : Json = [ + { "value": 1.0, "loc": "pos1" }, + { + "loc": "pos2", // this object should be dropped + }, + { "value": 3.0 }, + ] + @json.inspect(arr_with_loc_objects.prune_loc_test(), content=[ + { "value": 1 }, + { "value": 3 }, + ]) +} diff --git a/bundled-core/json/lex_main.mbt b/bundled-core/json/lex_main.mbt index e735223..5d5e036 100644 --- a/bundled-core/json/lex_main.mbt +++ b/bundled-core/json/lex_main.mbt @@ -27,7 +27,7 @@ let non_ascii_whitespace : CharClass = CharClass::of([ ///| fn ParseContext::lex_value( ctx : ParseContext, - allow_rbracket~ : Bool + allow_rbracket~ : Bool, ) -> Token raise ParseError { for { match ctx.read_char() { @@ -41,46 +41,46 @@ fn ParseContext::lex_value( ctx.invalid_char(shift=-1) } Some('n') => { - ctx.expect_ascii_char(b'u') - ctx.expect_ascii_char(b'l') - ctx.expect_ascii_char(b'l') + ctx.expect_ascii_char('u') + ctx.expect_ascii_char('l') + ctx.expect_ascii_char('l') return Null } Some('t') => { - ctx.expect_ascii_char(b'r') - ctx.expect_ascii_char(b'u') - ctx.expect_ascii_char(b'e') + ctx.expect_ascii_char('r') + ctx.expect_ascii_char('u') + ctx.expect_ascii_char('e') return True } Some('f') => { - ctx.expect_ascii_char(b'a') - ctx.expect_ascii_char(b'l') - ctx.expect_ascii_char(b's') - ctx.expect_ascii_char(b'e') + ctx.expect_ascii_char('a') + ctx.expect_ascii_char('l') + ctx.expect_ascii_char('s') + ctx.expect_ascii_char('e') return False } Some('-') => match ctx.read_char() { Some('0') => { - let n = ctx.lex_zero(start=ctx.offset - 2) - return Number(n) + let (n, repr) = ctx.lex_zero(start=ctx.offset - 2) + return Number(n, repr) } Some(c2) => { if c2 is ('1'..='9') { - let n = ctx.lex_decimal_integer(start=ctx.offset - 2) - return Number(n) + let (n, repr) = ctx.lex_decimal_integer(start=ctx.offset - 2) + return Number(n, repr) } ctx.invalid_char(shift=-1) } None => raise InvalidEof } Some('0') => { - let n = ctx.lex_zero(start=ctx.offset - 1) - return Number(n) + let (n, repr) = ctx.lex_zero(start=ctx.offset - 1) + return Number(n, repr) } Some('1'..='9') => { - let n = ctx.lex_decimal_integer(start=ctx.offset - 1) - return Number(n) + let (n, repr) = ctx.lex_decimal_integer(start=ctx.offset - 1) + return Number(n, repr) } Some('"') => { let s = ctx.lex_string() @@ -90,7 +90,8 @@ fn ParseContext::lex_value( if c > '\u{7f}' && non_ascii_whitespace.contains(c) { continue } - ctx.invalid_char(shift=-1) + let shift = -c.utf16_len() + ctx.invalid_char(shift~) } None => raise InvalidEof } diff --git a/bundled-core/json/lex_misc.mbt b/bundled-core/json/lex_misc.mbt index 6ef4fea..ded622c 100644 --- a/bundled-core/json/lex_misc.mbt +++ b/bundled-core/json/lex_misc.mbt @@ -33,10 +33,12 @@ fn ParseContext::read_char(ctx : ParseContext) -> Char? { } } -///| low surrogate +///| +/// low surrogate const SURROGATE_LOW_CHAR = 0xD800 -///| high surrogate +///| +/// high surrogate const SURROGATE_HIGH_CHAR = 0xDFFF ///| @@ -45,7 +47,7 @@ const SURROGATE_HIGH_CHAR = 0xDFFF /// otherwise raise an error, when it is an error, the position is unspecified. fn ParseContext::expect_char( ctx : ParseContext, - c : Char + c : Char, ) -> Unit raise ParseError { guard ctx.offset < ctx.end_offset else { raise InvalidEof } let c1 = ctx.input.unsafe_charcode_at(ctx.offset) @@ -79,7 +81,7 @@ fn ParseContext::expect_char( /// fn ParseContext::expect_ascii_char( ctx : ParseContext, - c : Byte + c : Byte, ) -> Unit raise ParseError { guard ctx.offset < ctx.end_offset else { raise InvalidEof } let c1 = ctx.input.unsafe_charcode_at(ctx.offset) @@ -95,7 +97,7 @@ test "expect_char" { ctx.expect_char('a') ctx.expect_char('b') ctx.expect_char('c') - inspect(try? ctx.expect_char('d'), content={ "Err": { "$tag": "InvalidEof" } }) + inspect(try? ctx.expect_char('d'), content={ "Err": "InvalidEof" }) } ///| @@ -108,7 +110,7 @@ test "expect_char with surrogate pair" { ctx.expect_char('c') ctx.expect_char((0x1F600).unsafe_to_char()) ctx.expect_char('c') - inspect(try? ctx.expect_char('d'), content={ "Err": { "$tag": "InvalidEof" } }) + inspect(try? ctx.expect_char('d'), content={ "Err": "InvalidEof" }) } ///| @@ -130,7 +132,7 @@ fn ParseContext::lex_skip_whitespace(ctx : ParseContext) -> Unit { ///| fn ParseContext::lex_after_array_value( - ctx : ParseContext + ctx : ParseContext, ) -> Token raise ParseError { ctx.lex_skip_whitespace() match ctx.read_char() { @@ -143,7 +145,7 @@ fn ParseContext::lex_after_array_value( ///| fn ParseContext::lex_after_property_name( - ctx : ParseContext + ctx : ParseContext, ) -> Unit raise ParseError { ctx.lex_skip_whitespace() match ctx.read_char() { @@ -155,7 +157,7 @@ fn ParseContext::lex_after_property_name( ///| fn ParseContext::lex_after_object_value( - ctx : ParseContext + ctx : ParseContext, ) -> Token raise ParseError { ctx.lex_skip_whitespace() match ctx.read_char() { @@ -170,7 +172,7 @@ fn ParseContext::lex_after_object_value( /// In the context of `{`, try to lex token `}` or a property name, /// otherwise raise an error. fn ParseContext::lex_property_name( - ctx : ParseContext + ctx : ParseContext, ) -> Token raise ParseError { ctx.lex_skip_whitespace() match ctx.read_char() { @@ -189,7 +191,7 @@ fn ParseContext::lex_property_name( /// otherwise raise an error. /// since it is in comma context, `}` is not allowed. fn ParseContext::lex_property_name2( - ctx : ParseContext + ctx : ParseContext, ) -> Token raise ParseError { ctx.lex_skip_whitespace() match ctx.read_char() { diff --git a/bundled-core/json/lex_number.mbt b/bundled-core/json/lex_number.mbt index 22ed6d0..11f55af 100644 --- a/bundled-core/json/lex_number.mbt +++ b/bundled-core/json/lex_number.mbt @@ -15,8 +15,8 @@ ///| fn ParseContext::lex_decimal_integer( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { for { match ctx.read_char() { Some('.') => return ctx.lex_decimal_point(start~) @@ -36,8 +36,8 @@ fn ParseContext::lex_decimal_integer( ///| fn ParseContext::lex_decimal_point( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { match ctx.read_char() { Some(c) => if c >= '0' && c <= '9' { @@ -52,8 +52,8 @@ fn ParseContext::lex_decimal_point( ///| fn ParseContext::lex_decimal_fraction( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { for { match ctx.read_char() { Some('e' | 'E') => return ctx.lex_decimal_exponent(start~) @@ -72,8 +72,8 @@ fn ParseContext::lex_decimal_fraction( ///| fn ParseContext::lex_decimal_exponent( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { match ctx.read_char() { Some('+') | Some('-') => return ctx.lex_decimal_exponent_sign(start~) Some(c) => { @@ -90,8 +90,8 @@ fn ParseContext::lex_decimal_exponent( ///| fn ParseContext::lex_decimal_exponent_sign( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { match ctx.read_char() { Some(c) => { if c >= '0' && c <= '9' { @@ -107,8 +107,8 @@ fn ParseContext::lex_decimal_exponent_sign( ///| fn ParseContext::lex_decimal_exponent_integer( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) { for { match ctx.read_char() { Some(c) => { @@ -126,8 +126,8 @@ fn ParseContext::lex_decimal_exponent_integer( ///| fn ParseContext::lex_zero( ctx : ParseContext, - start~ : Int -) -> Double raise ParseError { + start~ : Int, +) -> (Double, String?) raise ParseError { match ctx.read_char() { Some('.') => ctx.lex_decimal_point(start~) Some('e' | 'E') => ctx.lex_decimal_exponent(start~) @@ -147,10 +147,35 @@ fn ParseContext::lex_zero( fn ParseContext::lex_number_end( ctx : ParseContext, start : Int, - end : Int -) -> Double raise ParseError { - let s = ctx.input.substring(start~, end~) - @strconv.parse_double(s) catch { - _ => raise InvalidNumber(offset_to_position(ctx.input, start), s) + end : Int, +) -> (Double, String?) { + let s = ctx.input.unsafe_substring(start~, end~) + if !s.contains(".") && !s.contains("e") && !s.contains("E") { + // If the string does not contain a decimal point or exponent, it is likely an integer + // We can try to parse it as an integer first + let parsed_int = try? @strconv.parse_int64(s) + match parsed_int { + Ok(i) if i <= 9007199254740991 && i >= -9007199254740991 => + return (i.to_double(), None) + _ => + return if s is ['-', ..] { + (@double.neg_infinity, Some(s)) + } else { + (@double.infinity, Some(s)) + } + } + } else { + let parsed_double = try? @strconv.parse_double(s) + match parsed_double { + // For normal values, return without string representation + Ok(d) => (d, None) + // If parsing fails as a double, treat it as infinity and preserve the string + Err(_) => + if s is ['-', ..] { + (@double.neg_infinity, Some(s)) + } else { + (@double.infinity, Some(s)) + } + } } } diff --git a/bundled-core/json/lex_number_test.mbt b/bundled-core/json/lex_number_test.mbt index 1530dcf..719ddb5 100644 --- a/bundled-core/json/lex_number_test.mbt +++ b/bundled-core/json/lex_number_test.mbt @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| // shadow @json.inspect + +///| fnalias @builtin.inspect ///| shadow @@ -32,14 +33,76 @@ test "lex_zero invalid case" { } ///| -test "invalid number" { +test "parse numbers" { + // Basic float parsing + inspect(@json.parse("123.45"), content="Number(123.45)") + inspect(@json.parse("-123.45"), content="Number(-123.45)") + + // Exponential notation + // Note: The actual format depends on the implementation details of Double.to_string() + let exp_plus = @json.parse("123.45e+10") + inspect(exp_plus, content="Number(1234500000000)") + inspect(exp_plus.stringify(), content="1234500000000") + let exp_minus = @json.parse("123.45e-10") + inspect(exp_minus, content="Number(1.2345e-8)") + inspect(exp_minus.stringify(), content="1.2345e-8") + let exp_upper_plus = @json.parse("123.45E+10") + inspect(exp_upper_plus.stringify(), content="1234500000000") + let exp_upper_minus = @json.parse("123.45E-10") + inspect(exp_upper_minus.stringify(), content="1.2345e-8") + + // Very large number inspect( try? @json.parse("1e999999999"), - content="Err(Invalid number 1e999999999 at line 1, column 0)", + content=( + #|Ok(Number(Infinity, repr=Some("1e999999999"))) + ), + ) +} + +///| +test "parse and stringify large integers" { + // Test integers at Int boundaries + let min_int = "-2147483648" // Int.min_value + let parsed_min = @json.parse("\{min_int}") + inspect(parsed_min, content="Number(-2147483648)") + inspect(parsed_min.stringify(), content="-2147483648") + let max_int = "2147483647" // Int.max_value + let parsed_max = @json.parse("\{max_int}") + inspect(parsed_max, content="Number(2147483647)") + inspect(parsed_max.stringify(), content="2147483647") + + // Test integers beyond safe JavaScript integer precision (ยฑ2^53) + let beyond_js_safe = "9007199254740993" // 2^53 + 1 + let parsed_beyond = @json.parse(beyond_js_safe) + inspect( + parsed_beyond, + content=( + #|Number(Infinity, repr=Some("9007199254740993")) + ), + ) + inspect(parsed_beyond.stringify(), content="9007199254740993") + + // Test very large integers + let very_large = "12345678901234567890123456789" + let parsed_large = @json.parse(very_large) + inspect( + parsed_large, + content=( + #|Number(Infinity, repr=Some("12345678901234567890123456789")) + ), ) + inspect(parsed_large.stringify(), content="12345678901234567890123456789") } ///| -test "parse incomplete exponent sign" { - inspect(try? @json.parse("1e+"), content="Err(Unexpected end of file)") +test "parse and stringify large double" { + let very_large = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003.141592653589793238462643383279" + let parsed_large = @json.parse(very_large) + inspect( + parsed_large, + content=( + #|Number(Infinity, repr=Some("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003.141592653589793238462643383279")) + ), + ) } diff --git a/bundled-core/json/lex_string.mbt b/bundled-core/json/lex_string.mbt index 5bc6848..a8f6504 100644 --- a/bundled-core/json/lex_string.mbt +++ b/bundled-core/json/lex_string.mbt @@ -64,7 +64,7 @@ fn ParseContext::lex_string(ctx : ParseContext) -> String raise ParseError { ///| fn ParseContext::lex_hex_digits( ctx : ParseContext, - n : Int + n : Int, ) -> Int raise ParseError { let mut r = 0 for i in 0.. Bool { } ///| -pub fn parse(input : String) -> JsonValue raise ParseError { +pub fn parse(input : String) -> Json raise ParseError { let ctx = ParseContext::make(input) let val = ctx.parse_value() ctx.lex_skip_whitespace() @@ -35,7 +35,7 @@ pub fn parse(input : String) -> JsonValue raise ParseError { } ///| -fn ParseContext::parse_value(ctx : ParseContext) -> JsonValue raise ParseError { +fn ParseContext::parse_value(ctx : ParseContext) -> Json raise ParseError { let tok = ctx.lex_value(allow_rbracket=false) ctx.parse_value2(tok) } @@ -43,13 +43,13 @@ fn ParseContext::parse_value(ctx : ParseContext) -> JsonValue raise ParseError { ///| fn ParseContext::parse_value2( ctx : ParseContext, - tok : Token -) -> JsonValue raise ParseError { + tok : Token, +) -> Json raise ParseError { match tok { - Null => Json::null() + Null => null True => Json::boolean(true) False => Json::boolean(false) - Number(n) => Json::number(n) + Number(n, repr) => Json::number(n, repr?) String(s) => Json::string(s) LBrace => ctx.parse_object() LBracket => ctx.parse_array() @@ -58,7 +58,7 @@ fn ParseContext::parse_value2( } ///| -fn ParseContext::parse_object(ctx : ParseContext) -> JsonValue raise ParseError { +fn ParseContext::parse_object(ctx : ParseContext) -> Json raise ParseError { let map = Map::new() loop ctx.lex_property_name() { RBrace => Json::object(map) @@ -76,7 +76,7 @@ fn ParseContext::parse_object(ctx : ParseContext) -> JsonValue raise ParseError } ///| -fn ParseContext::parse_array(ctx : ParseContext) -> JsonValue raise ParseError { +fn ParseContext::parse_array(ctx : ParseContext) -> Json raise ParseError { let vec = [] loop ctx.lex_value(allow_rbracket=true) { RBracket => Json::array(vec) diff --git a/bundled-core/json/parse_test.mbt b/bundled-core/json/parse_test.mbt index 43c9aaa..3fcd5f5 100644 --- a/bundled-core/json/parse_test.mbt +++ b/bundled-core/json/parse_test.mbt @@ -13,7 +13,8 @@ // limitations under the License. ///| -fn test_parse(input : String, loc~ : SourceLoc = _) -> Json raise Error { +#callsite(autofill(loc)) +fn test_parse(input : String, loc~ : SourceLoc) -> Json raise Error { @json.parse(input) catch { err => fail("Parse failed, \{loc}, \{err}") } @@ -30,15 +31,15 @@ test "parses empty object" { test "parses object" { inspect( try? @json.parse("{\"a\":1}"), - content= + content=( #|Ok(Object({"a": Number(1)})) - , + ), ) inspect( try? @json.parse("{\"a\":1,\"b\":2}"), - content= + content=( #|Ok(Object({"a": Number(1), "b": Number(2)})) - , + ), ) } @@ -46,9 +47,9 @@ test "parses object" { test "parses multiple properties" { inspect( try? @json.parse("{\"abc\":1,\"def\":2}"), - content= + content=( #|Ok(Object({"abc": Number(1), "def": Number(2)})) - , + ), ) } @@ -56,9 +57,9 @@ test "parses multiple properties" { test "parses nested objects" { inspect( try? @json.parse("{\"a\":{\"b\":2}}"), - content= + content=( #|Ok(Object({"a": Object({"b": Number(2)})})) - , + ), ) } //endregion @@ -101,7 +102,7 @@ test "parses nested arrays" { ///| test "parses nulls" { let json = test_parse("null") - assert_eq(json, Json::null()) + assert_eq(json, null) } //endregion @@ -327,28 +328,30 @@ test "parse unexpected token in array" { ///| test "parse multi-lines json" { let result = try? @json.parse( - #|{ - #| "a":2, - #| "b":3 - #|} - , + ( + #|{ + #| "a":2, + #| "b":3 + #|} + ), ) inspect( result, - content= + content=( #|Ok(Object({"a": Number(2), "b": Number(3)})) - , + ), ) } ///| test "parse multi-lines json error" { let result = try? @json.parse( - #|{ - #| "a":2, - #| "b":a - #|} - , + ( + #|{ + #| "a":2, + #| "b":a + #|} + ), ) inspect(result, content="Err(Invalid character 'a' at line 3, column 6)") } @@ -369,12 +372,24 @@ test "parser error" { ) } -// TODO: fix the error message below -// test "emojoi" { -// inspect( -// // test first char is emoji -// try? @json.parse("\u{1F600}"), - -// content="Err(Invalid character '๏ฟฝ' at line 1, column 1)", -// ) -// } +///| +test "emoji" { + inspect( + // test first char is emoji + try? @json.parse("\u{1F600}"), + content="Err(Invalid character '๐Ÿ˜€' at line 1, column 0)", + ) + inspect( + try? @json.parse("a๐Ÿ˜€"), + content="Err(Invalid character 'a' at line 1, column 0)", + ) + inspect( + try? @json.parse("a"), + content="Err(Invalid character 'a' at line 1, column 0)", + ) + inspect( + // test first char is emoji (standalone emoji) + try? @json.parse("๐Ÿ˜€"), + content="Err(Invalid character '๐Ÿ˜€' at line 1, column 0)", + ) +} diff --git a/bundled-core/json/json.mbti b/bundled-core/json/pkg.generated.mbti similarity index 92% rename from bundled-core/json/json.mbti rename to bundled-core/json/pkg.generated.mbti index 0f7b88c..4cd6da3 100644 --- a/bundled-core/json/json.mbti +++ b/bundled-core/json/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/json" import( @@ -5,27 +6,21 @@ import( ) // Values -fn[T : FromJson] from_json(Json, path~ : JsonPath = ..) -> T raise JsonDecodeError +fn[T : FromJson] from_json(Json, path? : JsonPath) -> T raise JsonDecodeError -fn inspect(&ToJson, content? : Json, loc~ : SourceLoc = _, args_loc~ : ArgsLoc = _) -> Unit raise InspectError +#callsite(autofill(args_loc, loc)) +fn inspect(&ToJson, content? : Json, loc~ : SourceLoc, args_loc~ : ArgsLoc) -> Unit raise InspectError fn parse(String) -> Json raise ParseError fn valid(String) -> Bool -// Types and methods +// Errors pub(all) suberror JsonDecodeError (JsonPath, String) impl Eq for JsonDecodeError impl Show for JsonDecodeError impl ToJson for JsonDecodeError -type JsonPath -fn JsonPath::add_index(Self, Int) -> Self -fn JsonPath::add_key(Self, String) -> Self -impl Eq for JsonPath -impl Show for JsonPath -impl ToJson for JsonPath - pub(all) suberror ParseError { InvalidChar(Position, Char) InvalidEof @@ -36,6 +31,14 @@ impl Eq for ParseError impl Show for ParseError impl ToJson for ParseError +// Types and methods +type JsonPath +fn JsonPath::add_index(Self, Int) -> Self +fn JsonPath::add_key(Self, String) -> Self +impl Eq for JsonPath +impl Show for JsonPath +impl ToJson for JsonPath + pub(all) struct Position { line : Int column : Int @@ -50,7 +53,7 @@ fn Json::as_number(Self) -> Double? fn Json::as_object(Self) -> Map[String, Self]? fn Json::as_string(Self) -> String? fn Json::item(Self, Int) -> Self? -fn Json::stringify(Self, escape_slash~ : Bool = .., indent~ : Int = ..) -> String +fn Json::stringify(Self, escape_slash? : Bool, indent? : Int) -> String fn Json::value(Self, String) -> Self? impl Show for Json impl ToJson for Json @@ -74,10 +77,11 @@ impl FromJson for String impl[T : FromJson] FromJson for T? impl[Ok : FromJson, Err : FromJson] FromJson for Result[Ok, Err] impl[X : FromJson] FromJson for FixedArray[X] +impl FromJson for Bytes impl[X : FromJson] FromJson for Array[X] impl FromJson for Json impl[V : FromJson] FromJson for Map[String, V] -impl FromJson for @string.StringView +impl FromJson for @string.View impl[A : FromJson, B : FromJson] FromJson for (A, B) impl[A : FromJson, B : FromJson, C : FromJson] FromJson for (A, B, C) impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson] FromJson for (A, B, C, D) diff --git a/bundled-core/json/to_json_test.mbt b/bundled-core/json/to_json_test.mbt index 35eac21..4355396 100644 --- a/bundled-core/json/to_json_test.mbt +++ b/bundled-core/json/to_json_test.mbt @@ -32,21 +32,21 @@ test "UInt::to_json" { test "Int64::to_json" { inspect( 42L.to_json(), - content= + content=( #|String("42") - , + ), ) inspect( (-9223372036854775808L).to_json(), - content= + content=( #|String("-9223372036854775808") - , + ), ) inspect( 9223372036854775807L.to_json(), - content= + content=( #|String("9223372036854775807") - , + ), ) } @@ -54,24 +54,39 @@ test "Int64::to_json" { test "UInt64::to_json" { inspect( 42UL.to_json(), - content= + content=( #|String("42") - , + ), ) inspect( 18446744073709551615UL.to_json(), - content= + content=( #|String("18446744073709551615") - , + ), ) } ///| test "Double::to_json" { inspect(42.0.to_json(), content="Number(42)") - inspect(@double.not_a_number.to_json(), content="Null") - inspect(@double.infinity.to_json(), content="Null") - inspect(@double.neg_infinity.to_json(), content="Null") + inspect( + @double.not_a_number.to_json(), + content=( + #|String("NaN") + ), + ) + inspect( + @double.infinity.to_json(), + content=( + #|String("Infinity") + ), + ) + inspect( + @double.neg_infinity.to_json(), + content=( + #|String("-Infinity") + ), + ) } ///| @@ -83,39 +98,39 @@ test "Float::to_json" { test "String::to_json" { inspect( "abc".to_json(), - content= + content=( #|String("abc") - , + ), ) inspect( "a,\"b\",c".to_json(), - content= + content=( #|String("a,\"b\",c") - , + ), ) inspect( "\"".to_json(), - content= + content=( #|String("\"") - , + ), ) inspect( "\u{00}".to_json(), - content= + content=( #|String("\u{00}") - , + ), ) inspect( "\n\r\b\t\\".to_json(), - content= + content=( #|String("\n\r\b\t\\") - , + ), ) inspect( "\u{0c}\u{0d}\u{0f}".to_json(), - content= + content=( #|String("\u{0c}\r\u{0f}") - , + ), ) } @@ -123,15 +138,15 @@ test "String::to_json" { test "Char::to_json" { inspect( 'a'.to_json(), - content= + content=( #|String("a") - , + ), ) inspect( 'ๅญ—'.to_json(), - content= + content=( #|String("ๅญ—") - , + ), ) } @@ -151,9 +166,9 @@ test "Array::to_json" { ) inspect( [[], ["1"], ["1", "2"]].to_json(), - content= + content=( #|Array([Array([]), Array([String("1")]), Array([String("1"), String("2")])]) - , + ), ) } @@ -165,9 +180,9 @@ test "FixedArray::to_json" { ) inspect( [[], ["1"], ["1", "2"]].to_json(), - content= + content=( #|Array([Array([]), Array([String("1")]), Array([String("1"), String("2")])]) - , + ), ) } @@ -175,9 +190,9 @@ test "FixedArray::to_json" { test "Map::to_json" { inspect( { "x": [1], "y": [2] }.to_json(), - content= + content=( #|Object({"x": Array([Number(1)]), "y": Array([Number(2)])}) - , + ), ) } @@ -207,9 +222,9 @@ test "optional field" { ] inspect( opt.to_json().stringify(), - content= + content=( #|[{"x":42,"t":[42]},{},{"t":null}] - , + ), ) } diff --git a/bundled-core/json/tuple_fromjson.mbt b/bundled-core/json/tuple_fromjson.mbt index 10ec130..e1c8c33 100644 --- a/bundled-core/json/tuple_fromjson.mbt +++ b/bundled-core/json/tuple_fromjson.mbt @@ -15,7 +15,7 @@ ///| pub impl[A : FromJson, B : FromJson] FromJson for (A, B) with from_json( json, - path + path, ) { match json { [a, b] => { @@ -30,7 +30,7 @@ pub impl[A : FromJson, B : FromJson] FromJson for (A, B) with from_json( ///| pub impl[A : FromJson, B : FromJson, C : FromJson] FromJson for (A, B, C) with from_json( json, - path + path, ) { match json { [a, b, c] => { @@ -84,14 +84,14 @@ pub impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson, E : FromJson] F } ///| -pub impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson, E : FromJson, F : FromJson] FromJson for ( - A, - B, - C, - D, - E, - F, -) with from_json(json, path) { +pub impl[ + A : FromJson, + B : FromJson, + C : FromJson, + D : FromJson, + E : FromJson, + F : FromJson, +] FromJson for (A, B, C, D, E, F) with from_json(json, path) { match json { [a, b, c, d, e, f] => { let a : A = FromJson::from_json(a, path.add_index(0)) @@ -107,15 +107,15 @@ pub impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson, E : FromJson, F } ///| -pub impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson, E : FromJson, F : FromJson, G : FromJson] FromJson for ( - A, - B, - C, - D, - E, - F, - G, -) with from_json(json, path) { +pub impl[ + A : FromJson, + B : FromJson, + C : FromJson, + D : FromJson, + E : FromJson, + F : FromJson, + G : FromJson, +] FromJson for (A, B, C, D, E, F, G) with from_json(json, path) { match json { [a, b, c, d, e, f, g] => { let a : A = FromJson::from_json(a, path.add_index(0)) @@ -132,16 +132,16 @@ pub impl[A : FromJson, B : FromJson, C : FromJson, D : FromJson, E : FromJson, F } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7) with from_json(json, path) { match json { [x0, x1, x2, x3, x4, x5, x6, x7] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -159,17 +159,17 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8) with from_json(json, path) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -188,18 +188,21 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -219,19 +222,22 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -252,20 +258,23 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson, T11 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, + T11 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -287,21 +296,24 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson, T11 : FromJson, T12 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, + T11 : FromJson, + T12 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -324,22 +336,25 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson, T11 : FromJson, T12 : FromJson, T13 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, + T11 : FromJson, + T12 : FromJson, + T13 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -363,23 +378,26 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson, T11 : FromJson, T12 : FromJson, T13 : FromJson, T14 : FromJson] FromJson for ( - T0, - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, -) with from_json(json, path) { +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, + T11 : FromJson, + T12 : FromJson, + T13 : FromJson, + T14 : FromJson, +] FromJson for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) with from_json( + json, + path, +) { match json { [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14] => { let x0 : T0 = FromJson::from_json(x0, path.add_index(0)) @@ -404,7 +422,24 @@ pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJs } ///| -pub impl[T0 : FromJson, T1 : FromJson, T2 : FromJson, T3 : FromJson, T4 : FromJson, T5 : FromJson, T6 : FromJson, T7 : FromJson, T8 : FromJson, T9 : FromJson, T10 : FromJson, T11 : FromJson, T12 : FromJson, T13 : FromJson, T14 : FromJson, T15 : FromJson] FromJson for ( +pub impl[ + T0 : FromJson, + T1 : FromJson, + T2 : FromJson, + T3 : FromJson, + T4 : FromJson, + T5 : FromJson, + T6 : FromJson, + T7 : FromJson, + T8 : FromJson, + T9 : FromJson, + T10 : FromJson, + T11 : FromJson, + T12 : FromJson, + T13 : FromJson, + T14 : FromJson, + T15 : FromJson, +] FromJson for ( T0, T1, T2, diff --git a/bundled-core/json/types.mbt b/bundled-core/json/types.mbt index ca68fab..257d86c 100644 --- a/bundled-core/json/types.mbt +++ b/bundled-core/json/types.mbt @@ -24,7 +24,7 @@ pub(all) suberror ParseError { InvalidEof InvalidNumber(Position, String) InvalidIdentEscape(Position) -} derive(Eq, ToJson) +} derive(Eq, ToJson(style="flat")) ///| pub impl Show for ParseError with output(self, logger) { @@ -56,14 +56,18 @@ pub impl Show for ParseError with output(self, logger) { } ///| -pub impl Show for JsonValue with output(self, logger) { +pub impl Show for Json with output(self, logger) { match self { Null => logger.write_string("Null") True => logger.write_string("True") False => logger.write_string("False") - Number(n) => { + Number(n, repr~) => { logger.write_string("Number(") Show::output(n, logger) + if repr is Some(_) { + logger.write_string(", repr=") + Show::output(repr, logger) + } logger.write_string(")") } String(s) => { diff --git a/bundled-core/json/types_test.mbt b/bundled-core/json/types_test.mbt index 1c93644..4f183f9 100644 --- a/bundled-core/json/types_test.mbt +++ b/bundled-core/json/types_test.mbt @@ -31,7 +31,7 @@ test "ParseError::to_string coverage" { invalidCharError.to_string(), "Invalid character 'a' at line 1, column 0", ) - assert_eq(invalidEofError.to_string(), "Unexpected end of file") + inspect(invalidEofError.to_string(), content="Unexpected end of file") assert_eq( invalidNumberError.to_string(), "Invalid number 123abc at line 1, column 0", diff --git a/bundled-core/json/utils.mbt b/bundled-core/json/utils.mbt index fd6241b..732bf3b 100644 --- a/bundled-core/json/utils.mbt +++ b/bundled-core/json/utils.mbt @@ -30,12 +30,12 @@ fn offset_to_position(input : String, offset : Int) -> Position { ///| fn[T] ParseContext::invalid_char( ctx : ParseContext, - shift~ : Int = 0 + shift? : Int = 0, ) -> T raise ParseError { let offset = ctx.offset + shift - // FIXME: this should check the surrogate pair + let replacement_char : Char = '\u{fffd}' raise InvalidChar( offset_to_position(ctx.input, offset), - ctx.input.unsafe_charcode_at(offset).unsafe_to_char(), + ctx.input.get_char(offset).unwrap_or(replacement_char), ) } diff --git a/bundled-core/list/README.mbt.md b/bundled-core/list/README.mbt.md index 75128b3..c47cd8b 100644 --- a/bundled-core/list/README.mbt.md +++ b/bundled-core/list/README.mbt.md @@ -48,7 +48,7 @@ You can create an empty list or a list from an array. ```moonbit test { - let empty_list : @list.T[Int] = @list.new() + let empty_list : @list.List[Int] = @list.new() assert_true(empty_list.is_empty()) let list = @list.of([1, 2, 3, 4, 5]) assert_eq(list, @list.of([1, 2, 3, 4, 5])) @@ -87,7 +87,7 @@ Determine if the list is empty. ```moonbit test { - let empty_list : @list.T[Int] = @list.new() + let empty_list : @list.List[Int] = @list.new() assert_eq(empty_list.is_empty(), true) } ``` @@ -262,7 +262,7 @@ test { When accessing elements that might not exist, use pattern matching for safety: ```moonbit -fn safe_head(list : @list.T[Int]) -> Int { +fn safe_head(list : @list.List[Int]) -> Int { match list.head() { Some(value) => value None => 0 // Default value @@ -273,7 +273,7 @@ test { let list = @list.of([1, 2, 3]) assert_eq(safe_head(list), 1) - let empty_list : @list.T[Int] = @list.new() + let empty_list : @list.List[Int] = @list.new() assert_eq(safe_head(empty_list), 0) } ``` diff --git a/bundled-core/list/deprecated.mbt b/bundled-core/list/deprecated.mbt index 3aee91e..8109c4f 100644 --- a/bundled-core/list/deprecated.mbt +++ b/bundled-core/list/deprecated.mbt @@ -14,7 +14,7 @@ ///| #deprecated("use `_.to_array().rev_fold(...)` instead") -pub fn[A, B] rev_fold(self : T[A], init~ : B, f : (B, A) -> B) -> B { +pub fn[A, B] rev_fold(self : List[A], init~ : B, f : (B, A) -> B) -> B { let xs = self.to_array() let mut acc = init for x in xs.rev_iter() { @@ -25,13 +25,13 @@ pub fn[A, B] rev_fold(self : T[A], init~ : B, f : (B, A) -> B) -> B { ///| #deprecated("use `_.rev().foldi(...)` instead") -pub fn[A, B] rev_foldi(self : T[A], init~ : B, f : (Int, B, A) -> B) -> B { +pub fn[A, B] rev_foldi(self : List[A], init~ : B, f : (Int, B, A) -> B) -> B { self.rev().foldi(init~, (i, b, a) => f(i, b, a)) } ///| #deprecated("use `unsafe_tail` instead") -pub fn[A] tail(self : T[A]) -> T[A] { +pub fn[A] tail(self : List[A]) -> List[A] { match self { Empty => Empty More(_, tail~) => tail diff --git a/bundled-core/list/list.mbt b/bundled-core/list/list.mbt index dcfae4e..27f5ca4 100644 --- a/bundled-core/list/list.mbt +++ b/bundled-core/list/list.mbt @@ -13,40 +13,69 @@ // limitations under the License. ///| -/// Creates an empty list -pub fn[A] new() -> T[A] { - Empty -} - -///| -/// Creates an empty list -pub fn[A] empty() -> T[A] { +/// Creates an empty list. +/// +/// # Example +/// +/// ```moonbit +/// let ls : @list.List[Int] = @list.new() +/// assert_eq(ls, @list.empty()) +/// ``` +#alias(empty) +#as_free_fn +#as_free_fn(empty) +pub fn[A] List::new() -> List[A] { Empty } ///| /// Prepend an element to the list and create a new list. -pub fn[A] construct(head : A, tail : T[A]) -> T[A] { +/// +/// This function constructs a new list with the given element as the head +/// and the provided list as the tail. +/// +/// A more familiar name of this function is `cons`. +/// +/// # Example +/// +/// ```moonbit +/// let tail = @list.of([2, 3, 4]) +/// let ls = @list.cons(1, tail) +/// assert_eq(ls, @list.of([1, 2, 3, 4])) +/// ``` +#as_free_fn +#as_free_fn(construct, deprecated="Use cons instead") +pub fn[A] List::cons(head : A, tail : List[A]) -> List[A] { More(head, tail~) } ///| -pub fn[A] prepend(self : T[A], head : A) -> T[A] { - More(head, tail=self) -} - -///| -pub fn[A] add(self : T[A], head : A) -> T[A] { +/// Prepend an element to the front of the list. +/// +/// Creates a new list with the given element added to the beginning. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([2, 3, 4]).prepend(1) +/// assert_eq(ls, @list.of([1, 2, 3, 4])) +/// ``` +#alias(add) +pub fn[A] prepend(self : List[A], head : A) -> List[A] { More(head, tail=self) } ///| -pub impl[A : Show] Show for T[A] with output(xs, logger) { +/// Show implementation for List. +/// Outputs the list in the format @list.of([element1, element2, ...]). +pub impl[A : Show] Show for List[A] with output(xs, logger) { logger.write_iter(xs.iter(), prefix="@list.of([", suffix="])") } ///| -pub impl[A : ToJson] ToJson for T[A] with to_json(self) { +/// ToJson implementation for List. +/// Converts a list to a JSON array. +pub impl[A : ToJson] ToJson for List[A] with to_json(self) { let capacity = self.length() guard capacity != 0 else { return [] } let jsons = Array::new(capacity~) @@ -57,12 +86,26 @@ pub impl[A : ToJson] ToJson for T[A] with to_json(self) { } ///| -pub fn[A : ToJson] to_json(self : T[A]) -> Json { +/// Convert a list to JSON. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3]) +/// let json = ls.to_json() +/// inspect(json, content="Array([Number(1), Number(2), Number(3)])") +/// ``` +pub fn[A : ToJson] to_json(self : List[A]) -> Json { ToJson::to_json(self) } ///| -pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) { +/// FromJson implementation for List. +/// Parses a JSON array into a list. +pub impl[A : @json.FromJson] @json.FromJson for List[A] with from_json( + json, + path, +) { guard json is Array(arr) else { raise @json.JsonDecodeError((path, "@list.from_json: expected array")) } @@ -74,9 +117,13 @@ pub impl[A : @json.FromJson] @json.FromJson for T[A] with from_json(json, path) } ///| -pub fn[A : @json.FromJson] from_json( - json : Json -) -> T[A] raise @json.JsonDecodeError { +/// Parse JSON into a list. +/// +/// Converts a JSON array into a list of the specified type. +#as_free_fn +pub fn[A : @json.FromJson] List::from_json( + json : Json, +) -> List[A] raise @json.JsonDecodeError { @json.from_json(json) } @@ -86,10 +133,11 @@ pub fn[A : @json.FromJson] from_json( /// # Example /// /// ```mbt -/// let ls = @list.of([1, 2, 3, 4, 5]) -/// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5])) +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5])) /// ``` -pub fn[A] from_array(arr : Array[A]) -> T[A] { +#as_free_fn +pub fn[A] List::from_array(arr : Array[A]) -> List[A] { for i = arr.length() - 1, list = Empty; i >= 0; { continue i - 1, More(arr[i], tail=list) } else { @@ -99,7 +147,7 @@ pub fn[A] from_array(arr : Array[A]) -> T[A] { ///| /// Get the length of the list. -pub fn[A] length(self : T[A]) -> Int { +pub fn[A] length(self : List[A]) -> Int { loop (self, 0) { (Empty, len) => len (More(_, tail=rest), acc) => continue (rest, acc + 1) @@ -112,12 +160,12 @@ pub fn[A] length(self : T[A]) -> Int { /// # Example /// /// ```mbt -/// let arr = [] -/// @list.of([1, 2, 3, 4, 5]).each(x => arr.push(x)) -/// assert_eq(arr, [1, 2, 3, 4, 5]) +/// let arr = [] +/// @list.of([1, 2, 3, 4, 5]).each(x => arr.push(x)) +/// assert_eq(arr, [1, 2, 3, 4, 5]) /// ``` #locals(f) -pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { +pub fn[A] each(self : List[A], f : (A) -> Unit raise?) -> Unit raise? { loop self { Empty => () More(head, tail~) => { @@ -137,7 +185,7 @@ pub fn[A] each(self : T[A], f : (A) -> Unit raise?) -> Unit raise? { /// @list.of([1, 2, 3, 4, 5]).eachi((i, x) => arr.push("(\{i},\{x})")) /// assert_eq(arr, ["(0,1)", "(1,2)", "(2,3)", "(3,4)", "(4,5)"]) /// ``` -pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit raise?) -> Unit raise? { +pub fn[A] eachi(self : List[A], f : (Int, A) -> Unit raise?) -> Unit raise? { loop (self, 0) { (Empty, _) => () (More(x, tail=xs), i) => { @@ -153,9 +201,9 @@ pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit raise?) -> Unit raise? { /// # Example /// /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).map(x => x * 2), @list.of([2, 4, 6, 8, 10])) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).map(x => x * 2), @list.of([2, 4, 6, 8, 10])) /// ``` -pub fn[A, B] map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { +pub fn[A, B] map(self : List[A], f : (A) -> B raise?) -> List[B] raise? { match self { Empty => Empty More(hd, tail~) => { @@ -176,7 +224,18 @@ pub fn[A, B] map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { ///| /// Maps the list with index. -pub fn[A, B] mapi(self : T[A], f : (Int, A) -> B raise?) -> T[B] raise? { +/// +/// Applies a function to each element and its index, creating a new list +/// with the results. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([10, 20, 30]) +/// let result = ls.mapi((i, x) => x + i) +/// assert_eq(result, @list.of([10, 21, 32])) +/// ``` +pub fn[A, B] mapi(self : List[A], f : (Int, A) -> B raise?) -> List[B] raise? { match self { Empty => Empty More(hd, tail~) => { @@ -202,9 +261,9 @@ pub fn[A, B] mapi(self : T[A], f : (Int, A) -> B raise?) -> T[B] raise? { /// /// # Example /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).rev_map(x => x * 2), @list.of([10, 8, 6, 4, 2])) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).rev_map(x => x * 2), @list.of([10, 8, 6, 4, 2])) /// ``` -pub fn[A, B] rev_map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { +pub fn[A, B] rev_map(self : List[A], f : (A) -> B raise?) -> List[B] raise? { loop (Empty, self) { (acc, Empty) => acc (acc, More(x, tail=xs)) => continue (More(f(x), tail=acc), xs) @@ -213,7 +272,17 @@ pub fn[A, B] rev_map(self : T[A], f : (A) -> B raise?) -> T[B] raise? { ///| /// Convert list to array. -pub fn[A] to_array(self : T[A]) -> Array[A] { +/// +/// Creates a new array containing all elements from the list in the same order. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// let arr = ls.to_array() +/// assert_eq(arr, [1, 2, 3, 4, 5]) +/// ``` +pub fn[A] to_array(self : List[A]) -> Array[A] { match self { Empty => [] More(x, tail=xs) => { @@ -236,13 +305,13 @@ pub fn[A] to_array(self : T[A]) -> Array[A] { /// # Example /// /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).filter(x => x % 2 == 0), @list.of([2, 4])) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).filter(x => x % 2 == 0), @list.of([2, 4])) /// ``` -pub fn[A] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { +pub fn[A] filter(self : List[A], f : (A) -> Bool raise?) -> List[A] raise? { loop self { Empty => Empty More(head, tail~) => - if not(f(head)) { + if !f(head) { continue tail } else { let dest = More(head, tail=Empty) @@ -266,7 +335,20 @@ pub fn[A] filter(self : T[A], f : (A) -> Bool raise?) -> T[A] raise? { ///| /// Test if all elements of the list satisfy the predicate. -pub fn[A] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { +/// +/// Returns `true` if every element satisfies the predicate, or if the list is empty. +/// Returns `false` as soon as an element that doesn't satisfy the predicate is found. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([2, 4, 6, 8]) +/// assert_eq(ls.all(x => x % 2 == 0), true) +/// +/// let ls2 = @list.of([2, 3, 6, 8]) +/// assert_eq(ls2.all(x => x % 2 == 0), false) +/// ``` +pub fn[A] all(self : List[A], f : (A) -> Bool raise?) -> Bool raise? { loop self { Empty => true More(head, tail~) => if f(head) { continue tail } else { false } @@ -275,7 +357,20 @@ pub fn[A] all(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { ///| /// Test if any element of the list satisfies the predicate. -pub fn[A] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { +/// +/// Returns `true` as soon as an element that satisfies the predicate is found. +/// Returns `false` if no element satisfies the predicate, or if the list is empty. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 3, 5, 6]) +/// assert_eq(ls.any(x => x % 2 == 0), true) +/// +/// let ls2 = @list.of([1, 3, 5, 7]) +/// assert_eq(ls2.any(x => x % 2 == 0), false) +/// ``` +pub fn[A] any(self : List[A], f : (A) -> Bool raise?) -> Bool raise? { loop self { Empty => false More(head, tail~) => if f(head) { true } else { continue tail } @@ -285,7 +380,7 @@ pub fn[A] any(self : T[A], f : (A) -> Bool raise?) -> Bool raise? { ///| /// Get first element of the list. #internal(unsafe, "Panic if the list is empty") -pub fn[A] unsafe_head(self : T[A]) -> A { +pub fn[A] unsafe_head(self : List[A]) -> A { match self { Empty => abort("head of empty list") More(head, tail=_) => head @@ -293,7 +388,23 @@ pub fn[A] unsafe_head(self : T[A]) -> A { } ///| -pub fn[A] unsafe_tail(self : T[A]) -> T[A] { +/// Get the tail (all elements except the first) of the list. +/// +/// **Warning**: This function panics if the list is empty. +/// Use pattern matching or other safe methods for empty lists. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// let tail = ls.unsafe_tail() +/// assert_eq(tail, @list.of([2, 3, 4, 5])) +/// ``` +/// +/// # Panics +/// +/// Panics if the list is empty. +pub fn[A] unsafe_tail(self : List[A]) -> List[A] { match self { Empty => abort("tail of empty list") More(_, tail~) => tail @@ -306,9 +417,9 @@ pub fn[A] unsafe_tail(self : T[A]) -> T[A] { /// # Example /// /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).head(), Some(1)) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).head(), Some(1)) /// ``` -pub fn[A] head(self : T[A]) -> A? { +pub fn[A] head(self : List[A]) -> A? { match self { Empty => None More(head, tail=_) => Some(head) @@ -316,8 +427,23 @@ pub fn[A] head(self : T[A]) -> A? { } ///| +/// Get the last element of the list. +/// +/// **Warning**: This function panics if the list is empty. +/// Use `last()` for a safe alternative that returns `Option`. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls.unsafe_last(), 5) +/// ``` +/// +/// # Panics +/// +/// Panics if the list is empty. #internal(unsafe, "Panic if the list is empty") -pub fn[A] unsafe_last(self : T[A]) -> A { +pub fn[A] unsafe_last(self : List[A]) -> A { loop self { Empty => abort("last of empty list") More(head, tail=Empty) => head @@ -331,9 +457,9 @@ pub fn[A] unsafe_last(self : T[A]) -> A { /// # Example /// /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).last(), Some(5)) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).last(), Some(5)) /// ``` -pub fn[A] last(self : T[A]) -> A? { +pub fn[A] last(self : List[A]) -> A? { loop self { Empty => None More(head, tail=Empty) => Some(head) @@ -347,10 +473,10 @@ pub fn[A] last(self : T[A]) -> A? { /// # Example /// /// ```mbt -/// let ls = @list.of([1, 2, 3, 4, 5]).concat(@list.of([6, 7, 8, 9, 10])) -/// assert_eq(ls, @list.of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) +/// let ls = @list.of([1, 2, 3, 4, 5]).concat(@list.of([6, 7, 8, 9, 10])) +/// assert_eq(ls, @list.of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) /// ``` -pub fn[A] concat(self : T[A], other : T[A]) -> T[A] { +pub fn[A] concat(self : List[A], other : List[A]) -> List[A] { match self { Empty => other More(hd, tail=Empty) => More(hd, tail=other) @@ -376,10 +502,10 @@ pub fn[A] concat(self : T[A], other : T[A]) -> T[A] { /// # Example /// /// ```mbt -/// let ls = @list.of([1, 2, 3, 4, 5]).rev_concat(@list.of([6, 7, 8, 9, 10])) -/// assert_eq(ls, @list.of([5, 4, 3, 2, 1, 6, 7, 8, 9, 10])) +/// let ls = @list.of([1, 2, 3, 4, 5]).rev_concat(@list.of([6, 7, 8, 9, 10])) +/// assert_eq(ls, @list.of([5, 4, 3, 2, 1, 6, 7, 8, 9, 10])) /// ``` -pub fn[A] rev_concat(self : T[A], other : T[A]) -> T[A] { +pub fn[A] rev_concat(self : List[A], other : List[A]) -> List[A] { loop (self, other) { (Empty, other) => other (More(head, tail~), other) => continue (tail, More(head, tail=other)) @@ -392,9 +518,9 @@ pub fn[A] rev_concat(self : T[A], other : T[A]) -> T[A] { /// # Example /// /// ```mbt -/// assert_eq(@list.of([1, 2, 3, 4, 5]).rev(), @list.of([5, 4, 3, 2, 1])) +/// assert_eq(@list.of([1, 2, 3, 4, 5]).rev(), @list.of([5, 4, 3, 2, 1])) /// ``` -pub fn[A] rev(self : T[A]) -> T[A] { +pub fn[A] rev(self : List[A]) -> List[A] { self.rev_concat(Empty) } @@ -404,10 +530,14 @@ pub fn[A] rev(self : T[A]) -> T[A] { /// # Example /// /// ```mbt -/// let r = @list.of([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => acc + x) -/// assert_eq(r, 15) +/// let r = @list.of([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => acc + x) +/// inspect(r, content="15") /// ``` -pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B raise?) -> B raise? { +pub fn[A, B] fold( + self : List[A], + init~ : B, + f : (B, A) -> B raise?, +) -> B raise? { loop (self, init) { (Empty, acc) => acc (More(head, tail~), acc) => continue (tail, f(acc, head)) @@ -416,12 +546,28 @@ pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B raise?) -> B raise? { ///| /// Fold the list from left with index. +/// +/// Similar to `fold`, but the accumulator function also receives the index +/// of the current element. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([10, 20, 30]) +/// let result = ls.foldi(init=0, (i, acc, x) => acc + x * i) +/// inspect(result, content="80") // 0*10 + 1*20 + 2*30 = 80 +/// ``` pub fn[A, B] foldi( - self : T[A], + self : List[A], init~ : B, - f : (Int, B, A) -> B raise? + f : (Int, B, A) -> B raise?, ) -> B raise? { - fn go(xs : T[A], i : Int, f : (Int, B, A) -> B raise?, acc : B) -> B raise? { + fn go( + xs : List[A], + i : Int, + f : (Int, B, A) -> B raise?, + acc : B, + ) -> B raise? { match xs { Empty => acc More(x, tail=xs) => go(xs, i + 1, f, f(i, acc, x)) @@ -432,16 +578,22 @@ pub fn[A, B] foldi( } ///| -/// Zip two lists. -/// If the lists have different lengths, it will return a list with shorter length. +/// Zip two lists together into a list of tuples. +/// +/// Combines elements from two lists pairwise. If the lists have different +/// lengths, the result will have the length of the shorter list. /// /// # Example /// /// ```moonbit -/// let r = @list.zip(@list.of([1, 2, 3, 4, 5]), @list.of([6, 7, 8, 9, 10])) -/// assert_eq(r, @list.from_array([(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)])) +/// let r = @list.zip(@list.of([1, 2, 3, 4, 5]), @list.of([6, 7, 8, 9, 10])) +/// assert_eq(r, @list.from_array([(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)])) +/// +/// let r2 = @list.zip(@list.of([1, 2]), @list.of([6, 7, 8, 9, 10])) +/// assert_eq(r2, @list.from_array([(1, 6), (2, 7)])) /// ``` -pub fn[A, B] T::zip(self : T[A], other : T[B]) -> T[(A, B)] { +#as_free_fn +pub fn[A, B] List::zip(self : List[A], other : List[B]) -> List[(A, B)] { let res = loop (self, other, Empty) { (Empty, _, acc) => break acc (_, Empty, acc) => break acc @@ -459,11 +611,14 @@ pub fn[A, B] T::zip(self : T[A], other : T[B]) -> T[(A, B)] { /// # Example /// /// ```mbt -/// let ls = @list.from_array([1, 2, 3]) -/// let r = ls.flat_map(x => @list.from_array([x, x * 2])) -/// assert_eq(r, @list.from_array([1, 2, 2, 4, 3, 6])) +/// let ls = @list.from_array([1, 2, 3]) +/// let r = ls.flat_map(x => @list.from_array([x, x * 2])) +/// assert_eq(r, @list.from_array([1, 2, 2, 4, 3, 6])) /// ``` -pub fn[A, B] flat_map(self : T[A], f : (A) -> T[B] raise?) -> T[B] raise? { +pub fn[A, B] flat_map( + self : List[A], + f : (A) -> List[B] raise?, +) -> List[B] raise? { loop self { Empty => Empty More(head, tail~) => @@ -507,11 +662,11 @@ pub fn[A, B] flat_map(self : T[A], f : (A) -> T[B] raise?) -> T[B] raise? { /// # Example /// /// ```mbt -/// let ls = @list.of([4, 2, 2, 6, 3, 1]) -/// let r = ls.filter_map(x => if (x >= 3) { Some(x) } else { None }) -/// assert_eq(r, @list.of([4, 6, 3])) +/// let ls = @list.of([4, 2, 2, 6, 3, 1]) +/// let r = ls.filter_map(x => if (x >= 3) { Some(x) } else { None }) +/// assert_eq(r, @list.of([4, 6, 3])) /// ``` -pub fn[A, B] filter_map(self : T[A], f : (A) -> B? raise?) -> T[B] raise? { +pub fn[A, B] filter_map(self : List[A], f : (A) -> B? raise?) -> List[B] raise? { loop self { Empty => Empty More(hd, tail~) => @@ -538,8 +693,23 @@ pub fn[A, B] filter_map(self : T[A], f : (A) -> B? raise?) -> T[B] raise? { } ///| +/// Get the nth element of the list. +/// +/// **Warning**: This function panics if the index is out of bounds. +/// Use `nth()` for a safe alternative that returns `Option`. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls.unsafe_nth(2), 3) +/// ``` +/// +/// # Panics +/// +/// Panics if the index is out of bounds. #internal(unsafe, "Panic if the index is out of bounds") -pub fn[A] unsafe_nth(self : T[A], n : Int) -> A { +pub fn[A] unsafe_nth(self : List[A], n : Int) -> A { loop (self, n) { (Empty, _) => abort("nth: index out of bounds") (More(head, tail=_), 0) => head @@ -548,8 +718,19 @@ pub fn[A] unsafe_nth(self : T[A], n : Int) -> A { } ///| -/// Get nth element of the list or None if the index is out of bounds -pub fn[A] nth(self : T[A], n : Int) -> A? { +/// Get the nth element of the list. +/// +/// Returns `Some(element)` if the index is valid, or `None` if the index +/// is out of bounds. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls.nth(2), Some(3)) +/// assert_eq(ls.nth(10), None) +/// ``` +pub fn[A] nth(self : List[A], n : Int) -> A? { loop (self, n) { (Empty, _) => None (More(head, tail=_), 0) => Some(head) @@ -565,7 +746,8 @@ pub fn[A] nth(self : T[A], n : Int) -> A? { /// ```mbt /// assert_eq(@list.repeat(5, 1), @list.from_array([1, 1, 1, 1, 1])) /// ``` -pub fn[A] repeat(n : Int, x : A) -> T[A] { +#as_free_fn +pub fn[A] List::repeat(n : Int, x : A) -> List[A] { loop (Empty, n) { (acc, n) => if n <= 0 { acc } else { continue (More(x, tail=acc), n - 1) } } @@ -580,7 +762,7 @@ pub fn[A] repeat(n : Int, x : A) -> T[A] { /// let ls = @list.from_array(["1", "2", "3", "4", "5"]).intersperse("|") /// assert_eq(ls, @list.from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"])) /// ``` -pub fn[A] intersperse(self : T[A], separator : A) -> T[A] { +pub fn[A] intersperse(self : List[A], separator : A) -> List[A] { match self { Empty => Empty More(head, tail=Empty) => More(head, tail=Empty) @@ -603,7 +785,19 @@ pub fn[A] intersperse(self : T[A], separator : A) -> T[A] { ///| /// Check if the list is empty. -pub fn[A] is_empty(self : T[A]) -> Bool { +/// +/// Returns `true` if the list contains no elements, `false` otherwise. +/// +/// # Example +/// +/// ```moonbit +/// let empty_list : @list.List[Int] = @list.empty() +/// assert_eq(empty_list.is_empty(), true) +/// +/// let non_empty = @list.of([1, 2, 3]) +/// assert_eq(non_empty.is_empty(), false) +/// ``` +pub fn[A] is_empty(self : List[A]) -> Bool { self is Empty } @@ -613,11 +807,11 @@ pub fn[A] is_empty(self : T[A]) -> Bool { /// # Example /// /// ```mbt -/// let (a,b) = @list.from_array([(1,2),(3,4),(5,6)]).unzip() -/// assert_eq(a, @list.from_array([1, 3, 5])) -/// assert_eq(b, @list.from_array([2, 4, 6])) +/// let (a,b) = @list.from_array([(1,2),(3,4),(5,6)]).unzip() +/// assert_eq(a, @list.from_array([1, 3, 5])) +/// assert_eq(b, @list.from_array([2, 4, 6])) /// ``` -pub fn[A, B] unzip(self : T[(A, B)]) -> (T[A], T[B]) { +pub fn[A, B] unzip(self : List[(A, B)]) -> (List[A], List[B]) { match self { Empty => (Empty, Empty) More((x, y), tail~) => { @@ -643,10 +837,10 @@ pub fn[A, B] unzip(self : T[(A, B)]) -> (T[A], T[B]) { /// # Example /// /// ```mbt -/// let ls = @list.from_array([@list.from_array([1,2,3]), @list.from_array([4,5,6]), @list.from_array([7,8,9])]).flatten() -/// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9])) +/// let ls = @list.from_array([@list.from_array([1,2,3]), @list.from_array([4,5,6]), @list.from_array([7,8,9])]).flatten() +/// assert_eq(ls, @list.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9])) /// ``` -pub fn[A] flatten(self : T[T[A]]) -> T[A] { +pub fn[A] flatten(self : List[List[A]]) -> List[A] { loop self { Empty => Empty More(head, tail~) => @@ -685,8 +879,23 @@ pub fn[A] flatten(self : T[T[A]]) -> T[A] { } ///| +/// Get the maximum element of the list. +/// +/// **Warning**: This function panics if the list is empty. +/// Use `maximum()` for a safe alternative that returns `Option`. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 3, 2, 5, 4]) +/// assert_eq(ls.unsafe_maximum(), 5) +/// ``` +/// +/// # Panics +/// +/// Panics if the list is empty. #internal(unsafe, "Panic if the list is empty") -pub fn[A : Compare] unsafe_maximum(self : T[A]) -> A { +pub fn[A : Compare] unsafe_maximum(self : List[A]) -> A { match self { Empty => abort("maximum: empty list") More(head, tail~) => @@ -699,9 +908,21 @@ pub fn[A : Compare] unsafe_maximum(self : T[A]) -> A { } ///| -/// Get maximum element of the list. -/// Returns None if the list is empty. -pub fn[A : Compare] maximum(self : T[A]) -> A? { +/// Get the maximum element of the list. +/// +/// Returns `Some(element)` with the largest element, or `None` if the list is empty. +/// Elements are compared using the `Compare` trait. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 3, 2, 5, 4]) +/// assert_eq(ls.maximum(), Some(5)) +/// +/// let empty : @list.List[Int] = @list.empty() +/// assert_eq(empty.maximum(), None) +/// ``` +pub fn[A : Compare] maximum(self : List[A]) -> A? { match self { Empty => None More(head, tail~) => @@ -714,10 +935,25 @@ pub fn[A : Compare] maximum(self : T[A]) -> A? { } ///| +/// Get the minimum element of the list. +/// +/// **Warning**: This function panics if the list is empty. +/// Use `minimum()` for a safe alternative that returns `Option`. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 3, 2, 5, 4]) +/// assert_eq(ls.unsafe_minimum(), 1) +/// ``` +/// +/// # Panics +/// +/// Panics if the list is empty. #internal(unsafe, "Panic if the list is empty") -pub fn[A : Compare] unsafe_minimum(self : T[A]) -> A { +pub fn[A : Compare] unsafe_minimum(self : List[A]) -> A { match self { - Empty => abort("maximum: empty list") + Empty => abort("minimum: empty list") More(head, tail~) => loop (tail, head) { (Empty, curr_min) => curr_min @@ -728,8 +964,21 @@ pub fn[A : Compare] unsafe_minimum(self : T[A]) -> A { } ///| -/// Get minimum element of the list. -pub fn[A : Compare] minimum(self : T[A]) -> A? { +/// Get the minimum element of the list. +/// +/// Returns `Some(element)` with the smallest element, or `None` if the list is empty. +/// Elements are compared using the `Compare` trait. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 3, 2, 5, 4]) +/// assert_eq(ls.minimum(), Some(1)) +/// +/// let empty : @list.List[Int] = @list.empty() +/// assert_eq(empty.minimum(), None) +/// ``` +pub fn[A : Compare] minimum(self : List[A]) -> A? { match self { Empty => None More(head, tail~) => @@ -750,23 +999,44 @@ pub fn[A : Compare] minimum(self : T[A]) -> A? { /// let ls = @list.from_array([1,123,52,3,6,0,-6,-76]).sort() /// assert_eq(ls, @list.from_array([-76, -6, 0, 1, 3, 6, 52, 123])) /// ``` -pub fn[A : Compare] sort(self : T[A]) -> T[A] { +pub fn[A : Compare] sort(self : List[A]) -> List[A] { let arr = self.to_array() arr.sort() from_array(arr) } ///| -/// Concatenate two lists. +/// Add implementation for List - concatenates two lists. +/// +/// The `+` operator for lists performs concatenation. +/// `a + b` is equivalent to `a.concat(b)`. /// -/// `a + b` equal to `a.concat(b)` -pub impl[A] Add for T[A] with op_add(self, other) { +/// # Example +/// +/// ```moonbit +/// let a = @list.of([1, 2, 3]) +/// let b = @list.of([4, 5, 6]) +/// let result = a + b +/// assert_eq(result, @list.of([1, 2, 3, 4, 5, 6])) +/// ``` +pub impl[A] Add for List[A] with add(self, other) { self.concat(other) } ///| -/// Check if the list contains the value. -pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { +/// Check if the list contains the specified value. +/// +/// Returns `true` if any element in the list is equal to the given value, +/// `false` otherwise. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls.contains(3), true) +/// assert_eq(ls.contains(6), false) +/// ``` +pub fn[A : Eq] contains(self : List[A], value : A) -> Bool { loop self { Empty => false More(x, tail=xs) => if x == value { true } else { continue xs } @@ -782,7 +1052,11 @@ pub fn[A : Eq] contains(self : T[A], value : A) -> Bool { /// let r = @list.unfold(init=0, i => if i == 3 { None } else { Some((i, i + 1)) }) /// assert_eq(r, @list.from_array([0, 1, 2])) /// ``` -pub fn[A, S] unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { +#as_free_fn +pub fn[A, S] List::unfold( + f : (S) -> (A, S)? raise?, + init~ : S, +) -> List[A] raise? { match f(init) { None => Empty Some((element, new_state)) => { @@ -801,7 +1075,23 @@ pub fn[A, S] unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { } ///| -pub fn[A, S] rev_unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { +/// Produces a list iteratively in reverse order. +/// +/// Similar to `unfold`, but the resulting list will be in reverse order +/// compared to the generation order. This can be more efficient when +/// you don't need to preserve the generation order. +/// +/// # Example +/// +/// ```moonbit +/// let r = @list.rev_unfold(i => if i == 3 { None } else { Some((i, i + 1)) }, init=0) +/// assert_eq(r, @list.from_array([2, 1, 0])) +/// ``` +#as_free_fn +pub fn[A, S] List::rev_unfold( + f : (S) -> (A, S)? raise?, + init~ : S, +) -> List[A] raise? { loop (f(init), Empty) { (None, acc) => acc (Some((x, s)), acc) => continue (f(s), More(x, tail=acc)) @@ -819,7 +1109,7 @@ pub fn[A, S] rev_unfold(f : (S) -> (A, S)? raise?, init~ : S) -> T[A] raise? { /// let r = ls.take(3) /// assert_eq(r, @list.of([1, 2, 3])) /// ``` -pub fn[A] take(self : T[A], n : Int) -> T[A] { +pub fn[A] take(self : List[A], n : Int) -> List[A] { if n <= 0 { Empty } else { @@ -853,7 +1143,7 @@ pub fn[A] take(self : T[A], n : Int) -> T[A] { /// let r = ls.drop(3) /// assert_eq(r, @list.of([4, 5])) /// ``` -pub fn[A] drop(self : T[A], n : Int) -> T[A] { +pub fn[A] drop(self : List[A], n : Int) -> List[A] { if n <= 0 { self } else { @@ -875,7 +1165,7 @@ pub fn[A] drop(self : T[A], n : Int) -> T[A] { /// let r = ls.take_while(x => x < 3) /// assert_eq(r, @list.of([1, 2])) /// ``` -pub fn[A] take_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { +pub fn[A] take_while(self : List[A], p : (A) -> Bool raise?) -> List[A] raise? { match self { Empty => Empty More(head, tail~) => @@ -907,7 +1197,7 @@ pub fn[A] take_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { /// let r = ls.drop_while(x => x < 3) /// assert_eq(r, @list.of([3, 4])) /// ``` -pub fn[A] drop_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { +pub fn[A] drop_while(self : List[A], p : (A) -> Bool raise?) -> List[A] raise? { loop self { Empty => Empty More(x, tail=xs) => if p(x) { continue xs } else { More(x, tail=xs) } @@ -925,10 +1215,10 @@ pub fn[A] drop_while(self : T[A], p : (A) -> Bool raise?) -> T[A] raise? { /// assert_eq(r, @list.of([0, 1, 3, 6, 10, 15])) /// ``` pub fn[A, E] scan_left( - self : T[A], + self : List[A], f : (E, A) -> E raise?, - init~ : E -) -> T[E] raise? { + init~ : E, +) -> List[E] raise? { let dest = More(init, tail=Empty) loop (dest, self, init) { (_, Empty, _) => () @@ -953,10 +1243,10 @@ pub fn[A, E] scan_left( /// assert_eq(r, @list.of([15, 14, 12, 9, 5, 0])) /// ``` pub fn[A, B] scan_right( - self : T[A], + self : List[A], f : (B, A) -> B raise?, - init~ : B -) -> T[B] raise? { + init~ : B, +) -> List[B] raise? { self.rev().scan_left(f, init~).reverse_inplace() } @@ -969,7 +1259,7 @@ pub fn[A, B] scan_right( /// let ls = @list.from_array([(1, "a"), (2, "b"), (3, "c")]) /// assert_eq(ls.lookup(3), Some("c")) /// ``` -pub fn[A : Eq, B] lookup(self : T[(A, B)], v : A) -> B? { +pub fn[A : Eq, B] lookup(self : List[(A, B)], v : A) -> B? { loop self { Empty => None More((x, y), tail=xs) => if x == v { Some(y) } else { continue xs } @@ -985,7 +1275,7 @@ pub fn[A : Eq, B] lookup(self : T[(A, B)], v : A) -> B? { /// assert_eq(@list.of([1, 3, 5, 8]).find(element => element % 2 == 0), Some(8)) /// assert_eq(@list.of([1, 3, 5]).find(element => element % 2 == 0), None) /// ``` -pub fn[A] find(self : T[A], f : (A) -> Bool raise?) -> A? raise? { +pub fn[A] find(self : List[A], f : (A) -> Bool raise?) -> A? raise? { loop self { Empty => None More(element, tail=list) => @@ -1013,12 +1303,15 @@ pub fn[A] find(self : T[A], f : (A) -> Bool raise?) -> A? raise? { /// Example: /// /// ```moonbit -/// let ls = of([1, 2, 3, 4, 5]) +/// let ls = @list.of([1, 2, 3, 4, 5]) /// inspect(ls.find_index(x => x % 2 == 0), content="Some(1)") /// inspect(ls.find_index(x => x > 10), content="None") /// ``` /// -pub fn[A] T::find_index(self : Self[A], f : (A) -> Bool raise?) -> Int? raise? { +pub fn[A] List::find_index( + self : Self[A], + f : (A) -> Bool raise?, +) -> Int? raise? { loop (self, 0) { (Empty, _) => None (More(element, tail=list), idx) => @@ -1039,7 +1332,7 @@ pub fn[A] T::find_index(self : Self[A], f : (A) -> Bool raise?) -> Int? raise? { /// assert_eq(@list.of([1, 3, 5, 8]).findi((element, index) => (element % 2 == 0) && (index == 3)), Some(8)) /// assert_eq(@list.of([1, 3, 8, 5]).findi((element, index) => (element % 2 == 0) && (index == 3)), None) /// ``` -pub fn[A] findi(self : T[A], f : (A, Int) -> Bool raise?) -> A? raise? { +pub fn[A] findi(self : List[A], f : (A, Int) -> Bool raise?) -> A? raise? { loop (self, 0) { (list, index) => match list { @@ -1062,7 +1355,7 @@ pub fn[A] findi(self : T[A], f : (A, Int) -> Bool raise?) -> A? raise? { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).remove_at(2), @list.of([1, 2, 4, 5])) /// ``` -pub fn[A] remove_at(self : T[A], index : Int) -> T[A] { +pub fn[A] remove_at(self : List[A], index : Int) -> List[A] { match (index, self) { (_, Empty) => Empty (_..<0, _) => self @@ -1091,7 +1384,7 @@ pub fn[A] remove_at(self : T[A], index : Int) -> T[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).remove(3), @list.of([1, 2, 4, 5])) /// ``` -pub fn[A : Eq] remove(self : T[A], elem : A) -> T[A] { +pub fn[A : Eq] remove(self : List[A], elem : A) -> List[A] { match self { Empty => Empty More(head, tail~) if head == elem => tail @@ -1121,7 +1414,7 @@ pub fn[A : Eq] remove(self : T[A], elem : A) -> T[A] { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).is_prefix(@list.of([1, 2, 3])), true) /// ``` -pub fn[A : Eq] is_prefix(self : T[A], prefix : T[A]) -> Bool { +pub fn[A : Eq] is_prefix(self : List[A], prefix : List[A]) -> Bool { loop (self, prefix) { (_, Empty) => true (Empty, More(_)) => false @@ -1142,15 +1435,19 @@ pub fn[A : Eq] is_prefix(self : T[A], prefix : T[A]) -> Bool { /// ```mbt /// assert_eq(@list.of([1, 2, 3, 4, 5]).is_suffix(@list.of([3, 4, 5])), true) /// ``` -pub fn[A : Eq] is_suffix(self : T[A], suffix : T[A]) -> Bool { +pub fn[A : Eq] is_suffix(self : List[A], suffix : List[A]) -> Bool { self.rev().is_prefix(suffix.rev()) } ///| -/// Similar to intersperse but with a list of values. +/// Insert separator lists between lists and flatten the result. +/// +/// Similar to `intersperse`, but works with lists of lists. Inserts the +/// separator list between each list in the input, then flattens everything +/// into a single list. /// /// # Example -/// ```mbt +/// ```moonbit /// let ls = @list.of([ /// @list.of([1, 2, 3]), /// @list.of([4, 5, 6]), @@ -1159,27 +1456,49 @@ pub fn[A : Eq] is_suffix(self : T[A], suffix : T[A]) -> Bool { /// let r = ls.intercalate(@list.of([0])) /// assert_eq(r, @list.of([1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9])) /// ``` -pub fn[A] intercalate(self : T[T[A]], sep : T[A]) -> T[A] { +pub fn[A] intercalate(self : List[List[A]], sep : List[A]) -> List[A] { self.intersperse(sep).flatten() } ///| -pub impl[X] Default for T[X] with default() { +/// Default implementation for List. +/// +/// Returns an empty list as the default value. +pub impl[X] Default for List[X] with default() { Empty } ///| -/// The empty list -pub fn[X] default() -> T[X] { +/// Return the default value for a list (empty list). +/// +/// # Example +/// +/// ```moonbit +/// let ls : @list.List[Int] = @list.default() +/// assert_eq(ls.is_empty(), true) +/// ``` +pub fn[X] default() -> List[X] { Empty } ///| -pub fn[A] iter(self : T[A]) -> Iter[A] { +/// Create an iterator over the list elements. +/// +/// Returns an iterator that yields each element of the list in order. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// let iter = ls.iter() +/// let sum = iter.fold(init=0, (acc, x) => acc + x) +/// inspect(sum, content="15") +/// ``` +pub fn[A] iter(self : List[A]) -> Iter[A] { Iter::new(yield_ => loop self { Empty => IterContinue More(head, tail~) => { - if yield_(head) == IterEnd { + if yield_(head) is IterEnd { break IterEnd } continue tail @@ -1188,11 +1507,23 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { } ///| -pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { +/// Create an iterator over the list elements with indices. +/// +/// Returns an iterator that yields pairs of (index, element) for each +/// element in the list. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([10, 20, 30]) +/// let iter = ls.iter2() +/// inspect(iter, content="[(0, 10), (1, 20), (2, 30)]") +/// ``` +pub fn[A] iter2(self : List[A]) -> Iter2[Int, A] { Iter2::new(yield_ => loop (self, 0) { (Empty, _) => IterEnd (More(head, tail~), i) => { - if yield_(i, head) == IterEnd { + if yield_(i, head) is IterEnd { break IterEnd } continue (tail, i + 1) @@ -1201,19 +1532,72 @@ pub fn[A] iter2(self : T[A]) -> Iter2[Int, A] { } ///| -/// Convert the iterator into a list. Preserves order of elements. -/// If the order of elements is not important, use `from_iter_rev` instead. -pub fn[A] from_iter(iter : Iter[A]) -> T[A] { - iter.fold(init=Empty, (acc, e) => More(e, tail=acc)).reverse_inplace() +/// Convert an iterator into a list, preserving order of elements. +/// +/// Creates a list from an iterator, maintaining the same order as the iterator. +/// If order is not important, consider using `from_iter_rev` for better performance. +/// +/// # Example +/// +/// ```moonbit +/// let arr = [1, 2, 3, 4, 5] +/// let iter = arr.iter() +/// let ls = @list.from_iter(iter) +/// assert_eq(ls, @list.of([1, 2, 3, 4, 5])) +/// ``` +#as_free_fn +pub fn[A] List::from_iter(iter : Iter[A]) -> List[A] { + let mut res = Empty + let mut ptr = Empty + for x in iter { + match (res, ptr) { + (Empty, _) => { + res = More(x, tail=Empty) + ptr = res + } + (More(_), More(_) as ptr_) => { + ptr_.tail = More(x, tail=Empty) + ptr = ptr_.tail + } + (_, _) => panic() + } + } + res } ///| -pub fn[A] from_iter_rev(iter : Iter[A]) -> T[A] { +/// Convert an iterator into a list in reverse order. +/// +/// Creates a list from an iterator, but the resulting list will have elements +/// in reverse order compared to the iterator. This is more efficient than +/// `from_iter` when order doesn't matter. +/// +/// # Example +/// +/// ```moonbit +/// let arr = [1, 2, 3, 4, 5] +/// let iter = arr.iter() +/// let ls = @list.from_iter_rev(iter) +/// assert_eq(ls, @list.of([5, 4, 3, 2, 1])) +/// ``` +#as_free_fn +pub fn[A] List::from_iter_rev(iter : Iter[A]) -> List[A] { iter.fold(init=Empty, (acc, e) => More(e, tail=acc)) } ///| -pub fn[A] of(arr : FixedArray[A]) -> T[A] { +/// Create a list from a FixedArray. +/// +/// Converts a FixedArray into a list with the same elements in the same order. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.of([1, 2, 3, 4, 5]) +/// assert_eq(ls.to_array(), [1, 2, 3, 4, 5]) +/// ``` +#as_free_fn +pub fn[A] List::of(arr : FixedArray[A]) -> List[A] { for i = arr.length() - 1, list = Empty; i >= 0; { continue i - 1, More(arr[i], tail=list) } else { @@ -1222,27 +1606,50 @@ pub fn[A] of(arr : FixedArray[A]) -> T[A] { } ///| -pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] with arbitrary( +/// Arbitrary implementation for quickcheck testing. +/// +/// Generates random lists for property-based testing with quickcheck. +pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for List[X] with arbitrary( size, - rs + rs, ) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_array } ///| -pub fn[A] singleton(x : A) -> T[A] { +/// Create a list with a single element. +/// +/// Returns a list containing only the given element. +/// +/// # Example +/// +/// ```moonbit +/// let ls = @list.singleton(42) +/// assert_eq(ls, @list.of([42])) +/// assert_eq(ls.length(), 1) +/// ``` +#as_free_fn +pub fn[A] List::singleton(x : A) -> List[A] { More(x, tail=Empty) } ///| -pub impl[A : Hash] Hash for T[A] with hash_combine(self, hasher) { +/// Hash implementation for List. +/// +/// Computes the hash value for a list by combining the hash values +/// of all its elements. +pub impl[A : Hash] Hash for List[A] with hash_combine(self, hasher) { for e in self { hasher.combine(e) } } ///| -fn[A] reverse_inplace(self : T[A]) -> T[A] { +/// Reverse a list in-place (internal function). +/// +/// This is an internal helper function that efficiently reverses a list +/// by modifying the existing structure rather than creating a completely new one. +fn[A] reverse_inplace(self : List[A]) -> List[A] { match self { Empty | More(_, tail=Empty) => self More(head, tail~) => @@ -1255,6 +1662,3 @@ fn[A] reverse_inplace(self : T[A]) -> T[A] { } } } - -///| -pub fnalias T::zip diff --git a/bundled-core/list/list.mbti b/bundled-core/list/list.mbti deleted file mode 100644 index ba887ee..0000000 --- a/bundled-core/list/list.mbti +++ /dev/null @@ -1,115 +0,0 @@ -package "moonbitlang/core/list" - -import( - "moonbitlang/core/json" - "moonbitlang/core/quickcheck" -) - -// Values -fn[A] construct(A, T[A]) -> T[A] - -fn[X] default() -> T[X] - -fn[A] empty() -> T[A] - -fn[A] from_array(Array[A]) -> T[A] - -fn[A] from_iter(Iter[A]) -> T[A] - -fn[A] from_iter_rev(Iter[A]) -> T[A] - -fn[A : @json.FromJson] from_json(Json) -> T[A] raise @json.JsonDecodeError - -fn[A] new() -> T[A] - -fn[A] of(FixedArray[A]) -> T[A] - -fn[A] repeat(Int, A) -> T[A] - -fn[A, S] rev_unfold((S) -> (A, S)? raise?, init~ : S) -> T[A] raise? - -fn[A] singleton(A) -> T[A] - -fn[A, S] unfold((S) -> (A, S)? raise?, init~ : S) -> T[A] raise? - -fn[A, B] zip(T[A], T[B]) -> T[(A, B)] - -// Types and methods -pub enum T[A] { - Empty - More(A, mut tail~ : T[A]) -} -fn[A] T::add(Self[A], A) -> Self[A] -fn[A] T::all(Self[A], (A) -> Bool raise?) -> Bool raise? -fn[A] T::any(Self[A], (A) -> Bool raise?) -> Bool raise? -fn[A] T::concat(Self[A], Self[A]) -> Self[A] -fn[A : Eq] T::contains(Self[A], A) -> Bool -fn[A] T::drop(Self[A], Int) -> Self[A] -fn[A] T::drop_while(Self[A], (A) -> Bool raise?) -> Self[A] raise? -fn[A] T::each(Self[A], (A) -> Unit raise?) -> Unit raise? -fn[A] T::eachi(Self[A], (Int, A) -> Unit raise?) -> Unit raise? -fn[A] T::filter(Self[A], (A) -> Bool raise?) -> Self[A] raise? -fn[A, B] T::filter_map(Self[A], (A) -> B? raise?) -> Self[B] raise? -fn[A] T::find(Self[A], (A) -> Bool raise?) -> A? raise? -fn[A] T::find_index(Self[A], (A) -> Bool raise?) -> Int? raise? -fn[A] T::findi(Self[A], (A, Int) -> Bool raise?) -> A? raise? -fn[A, B] T::flat_map(Self[A], (A) -> Self[B] raise?) -> Self[B] raise? -fn[A] T::flatten(Self[Self[A]]) -> Self[A] -fn[A, B] T::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? -fn[A, B] T::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? -fn[A] T::head(Self[A]) -> A? -fn[A] T::intercalate(Self[Self[A]], Self[A]) -> Self[A] -fn[A] T::intersperse(Self[A], A) -> Self[A] -fn[A] T::is_empty(Self[A]) -> Bool -fn[A : Eq] T::is_prefix(Self[A], Self[A]) -> Bool -fn[A : Eq] T::is_suffix(Self[A], Self[A]) -> Bool -fn[A] T::iter(Self[A]) -> Iter[A] -fn[A] T::iter2(Self[A]) -> Iter2[Int, A] -fn[A] T::last(Self[A]) -> A? -fn[A] T::length(Self[A]) -> Int -fn[A : Eq, B] T::lookup(Self[(A, B)], A) -> B? -fn[A, B] T::map(Self[A], (A) -> B raise?) -> Self[B] raise? -fn[A, B] T::mapi(Self[A], (Int, A) -> B raise?) -> Self[B] raise? -fn[A : Compare] T::maximum(Self[A]) -> A? -fn[A : Compare] T::minimum(Self[A]) -> A? -fn[A] T::nth(Self[A], Int) -> A? -fn[A] T::prepend(Self[A], A) -> Self[A] -fn[A : Eq] T::remove(Self[A], A) -> Self[A] -fn[A] T::remove_at(Self[A], Int) -> Self[A] -fn[A] T::rev(Self[A]) -> Self[A] -fn[A] T::rev_concat(Self[A], Self[A]) -> Self[A] -#deprecated -fn[A, B] T::rev_fold(Self[A], init~ : B, (B, A) -> B) -> B -#deprecated -fn[A, B] T::rev_foldi(Self[A], init~ : B, (Int, B, A) -> B) -> B -fn[A, B] T::rev_map(Self[A], (A) -> B raise?) -> Self[B] raise? -fn[A, E] T::scan_left(Self[A], (E, A) -> E raise?, init~ : E) -> Self[E] raise? -fn[A, B] T::scan_right(Self[A], (B, A) -> B raise?, init~ : B) -> Self[B] raise? -fn[A : Compare] T::sort(Self[A]) -> Self[A] -#deprecated -fn[A] T::tail(Self[A]) -> Self[A] -fn[A] T::take(Self[A], Int) -> Self[A] -fn[A] T::take_while(Self[A], (A) -> Bool raise?) -> Self[A] raise? -fn[A] T::to_array(Self[A]) -> Array[A] -fn[A : ToJson] T::to_json(Self[A]) -> Json -fn[A] T::unsafe_head(Self[A]) -> A -fn[A] T::unsafe_last(Self[A]) -> A -fn[A : Compare] T::unsafe_maximum(Self[A]) -> A -fn[A : Compare] T::unsafe_minimum(Self[A]) -> A -fn[A] T::unsafe_nth(Self[A], Int) -> A -fn[A] T::unsafe_tail(Self[A]) -> Self[A] -fn[A, B] T::unzip(Self[(A, B)]) -> (Self[A], Self[B]) -fn[A, B] T::zip(Self[A], Self[B]) -> Self[(A, B)] -impl[A] Add for T[A] -impl[X] Default for T[X] -impl[A : Eq] Eq for T[A] -impl[A : Hash] Hash for T[A] -impl[A : Show] Show for T[A] -impl[A : ToJson] ToJson for T[A] -impl[A : @json.FromJson] @json.FromJson for T[A] -impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/list/list_test.mbt b/bundled-core/list/list_test.mbt index 6fbf717..c1f27a2 100644 --- a/bundled-core/list/list_test.mbt +++ b/bundled-core/list/list_test.mbt @@ -15,14 +15,14 @@ ///| test "from_array" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(ls, content="@list.of([1, 2, 3, 4, 5])") inspect(el, content="@list.of([])") } ///| test "pipe" { - inspect(1 |> @list.T::prepend(@list.empty(), _), content="@list.of([1])") + inspect(1 |> @list.List::prepend(@list.empty(), _), content="@list.of([1])") } ///| @@ -61,7 +61,7 @@ test "iteri" { ///| test "map" { let ls = @list.of([1, 2, 3, 4, 5]) - let rs : @list.T[Int] = @list.empty() + let rs : @list.List[Int] = @list.empty() inspect(ls.map(x => x * 2), content="@list.of([2, 4, 6, 8, 10])") inspect(rs.map(x => x * 2), content="@list.of([])") } @@ -69,7 +69,7 @@ test "map" { ///| test "mapi" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(ls.mapi((i, x) => i * x), content="@list.of([0, 2, 6, 12, 20])") inspect(el.mapi((i, x) => i * x), content="@list.of([])") } @@ -77,7 +77,7 @@ test "mapi" { ///| test "rev_map" { let ls = @list.of([1, 2, 3, 4, 5]) - let rs : @list.T[Int] = @list.empty() + let rs : @list.List[Int] = @list.empty() inspect(ls.rev_map(x => x * 2), content="@list.of([10, 8, 6, 4, 2])") inspect(rs.rev_map(x => x * 2), content="@list.of([])") } @@ -85,7 +85,7 @@ test "rev_map" { ///| test "to_array" { let list = @list.of([1, 2, 3, 4, 5]) - let empty : @list.T[Int] = @list.empty() + let empty : @list.List[Int] = @list.empty() let array = list.to_array() let earray = empty.to_array() inspect(array, content="[1, 2, 3, 4, 5]") @@ -95,7 +95,7 @@ test "to_array" { ///| test "filter" { let ls = @list.of([1, 2, 3, 4, 5]) - let rs : @list.T[Int] = @list.empty() + let rs : @list.List[Int] = @list.empty() inspect(ls.filter(x => x % 2 == 0), content="@list.of([2, 4])") inspect(rs.filter(x => x % 2 == 0), content="@list.of([])") } @@ -122,7 +122,7 @@ test "unsafe_tail" { ///| test "panic unsafe_tail" { - let ls : @list.T[Int] = @list.empty() + let ls : @list.List[Int] = @list.empty() inspect(ls.unsafe_tail()) } @@ -135,7 +135,7 @@ test "head_exn" { ///| test "head" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.of([]) + let el : @list.List[Int] = @list.of([]) inspect(ls.head(), content="Some(1)") inspect(el.head(), content="None") } @@ -152,7 +152,7 @@ test "concat" { let rs = @list.of([6, 7, 8, 9, 10]) inspect(ls.concat(@list.empty()), content="@list.of([1, 2, 3, 4, 5])") inspect( - (@list.empty() : @list.T[Int]).concat(rs), + (@list.empty() : @list.List[Int]).concat(rs), content="@list.of([6, 7, 8, 9, 10])", ) inspect(ls.concat(rs), content="@list.of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])") @@ -181,7 +181,7 @@ test "rev" { ///| test "fold" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(el.fold((acc, x) => acc + x, init=0), content="0") inspect(ls.fold((acc, x) => acc + x, init=0), content="15") } @@ -189,7 +189,7 @@ test "fold" { ///| test "rev_fold" { let ls = @list.of(["1", "2", "3", "4", "5"]) - let el : @list.T[String] = @list.empty() + let el : @list.List[String] = @list.empty() inspect(ls.rev().fold((acc, x) => x + acc, init=""), content="12345") inspect(el.rev().fold((acc, x) => x + acc, init="init"), content="init") } @@ -197,7 +197,7 @@ test "rev_fold" { ///| test "foldi" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(ls.foldi((i, acc, x) => acc + i * x, init=0), content="40") inspect(el.foldi((i, acc, x) => acc + i * x, init=0), content="0") } @@ -205,7 +205,7 @@ test "foldi" { ///| test "rev_foldi" { let ls = @list.of([1, 2, 3, 4, 5]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(ls.rev().foldi((i, acc, x) => x * i + acc, init=0), content="20") inspect(el.rev().foldi((i, acc, x) => x * i + acc, init=0), content="0") } @@ -223,7 +223,7 @@ test "zip" { ///| test "flat_map" { let ls = @list.of([1, 2, 3]) - let rs : @list.T[Int] = @list.empty() + let rs : @list.List[Int] = @list.empty() inspect(rs.flat_map(x => @list.of([x, x * 2])), content="@list.of([])") inspect( ls.flat_map(x => @list.of([x, x * 2])), @@ -234,7 +234,7 @@ test "flat_map" { ///| test "filter_map" { let ls = @list.of([4, 2, 2, 6, 3, 1]) - let rs : @list.T[Int] = @list.empty() + let rs : @list.List[Int] = @list.empty() inspect( ls.filter_map(x => if x >= 3 { Some(x) } else { None }), content="@list.of([4, 6, 3])", @@ -268,12 +268,12 @@ test "repeat" { ///| test "intersperse" { let ls = @list.of(["1", "2", "3", "4", "5"]) - let el : @list.T[String] = empty() + let el : @list.List[String] = @list.empty() inspect( ls.intersperse("|"), - content= + content=( #|@list.of(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) - , + ), ) inspect(el.intersperse("|"), content="@list.of([])") } @@ -282,7 +282,7 @@ test "intersperse" { test "is_empty" { let ls = @list.of([1, 2, 3, 4, 5]) inspect(ls.is_empty(), content="false") - inspect((@list.empty() : @list.T[Unit]).is_empty(), content="true") + inspect((@list.empty() : @list.List[Unit]).is_empty(), content="true") } ///| @@ -300,7 +300,7 @@ test "flatten" { @list.of([4, 5, 6]), @list.of([7, 8, 9]), ]) - let el : @list.T[@list.T[Int]] = @list.empty() + let el : @list.List[@list.List[Int]] = @list.empty() inspect(ls.flatten(), content="@list.of([1, 2, 3, 4, 5, 6, 7, 8, 9])") inspect(el.flatten(), content="@list.of([])") } @@ -320,7 +320,7 @@ test "minimum" { ///| test "sort" { let ls = @list.of([1, 123, 52, 3, 6, 0, -6, -76]) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(el.sort(), content="@list.of([])") inspect(ls.sort(), content="@list.of([-76, -6, 0, 1, 3, 6, 52, 123])") } @@ -376,7 +376,7 @@ test "take" { ///| test "drop" { let ls = @list.of([1, 2, 3, 4, 5]).drop(3) - let el : @list.T[Int] = @list.empty() + let el : @list.List[Int] = @list.empty() inspect(ls, content="@list.of([4, 5])") inspect(ls.drop(-10), content="@list.of([4, 5])") inspect(ls.drop(10), content="@list.of([])") @@ -388,7 +388,7 @@ test "drop" { ///| test "take_while" { let ls = @list.of([0, 1, 2, 3, 4]).take_while(x => x < 3) - let el : @list.T[Int] = @list.empty().take_while(_e => true) + let el : @list.List[Int] = @list.empty().take_while(_e => true) inspect(ls, content="@list.of([0, 1, 2])") inspect(el, content="@list.of([])") } @@ -396,7 +396,7 @@ test "take_while" { ///| test "drop_while" { let ls = @list.of([0, 1, 2, 3, 4]).drop_while(x => x < 3) - let el : @list.T[Int] = @list.empty().drop_while(_e => true) + let el : @list.List[Int] = @list.empty().drop_while(_e => true) inspect(ls, content="@list.of([3, 4])") inspect(el, content="@list.of([])") } @@ -424,13 +424,13 @@ test "scan_right" { ///| test "lookup" { let ls = @list.of([(1, "a"), (2, "b"), (3, "c")]) - let el : @list.T[(Int, Int)] = @list.empty() + let el : @list.List[(Int, Int)] = @list.empty() inspect(el.lookup(1), content="None") inspect( ls.lookup(3), - content= + content=( #|Some("c") - , + ), ) inspect(ls.lookup(4), content="None") } @@ -446,7 +446,7 @@ test "find" { content="None", ) inspect( - (@list.empty() : @list.T[Int]).find(element => element % 2 == 0), + (@list.empty() : @list.List[Int]).find(element => element % 2 == 0), content="None", ) } @@ -462,7 +462,7 @@ test "findi" { content="None", ) inspect( - (@list.empty() : @list.T[Int]).findi((element, i) => element % 2 == 0 && + (@list.empty() : @list.List[Int]).findi((element, i) => element % 2 == 0 && i == 3), content="None", ) @@ -475,15 +475,15 @@ test "remove_at" { inspect(ls.remove_at(0), content="@list.of([2, 3, 4, 5])") inspect( @list.of(["a", "b", "c", "d", "e"]).remove_at(2), - content= + content=( #|@list.of(["a", "b", "d", "e"]) - , + ), ) inspect( @list.of(["a", "b", "c", "d", "e"]).remove_at(5), - content= + content=( #|@list.of(["a", "b", "c", "d", "e"]) - , + ), ) } @@ -492,15 +492,15 @@ test "remove" { inspect(@list.of([1, 2, 3, 4, 5]).remove(3), content="@list.of([1, 2, 4, 5])") inspect( @list.of(["a", "b", "c", "d", "e"]).remove("c"), - content= + content=( #|@list.of(["a", "b", "d", "e"]) - , + ), ) inspect( @list.of(["a", "b", "c", "d", "e"]).remove("f"), - content= + content=( #|@list.of(["a", "b", "c", "d", "e"]) - , + ), ) } @@ -543,7 +543,7 @@ test "intercalate" { @list.of([4, 5, 6]), @list.of([7, 8, 9]), ]) - let el : @list.T[@list.T[Int]] = @list.empty() + let el : @list.List[@list.List[Int]] = @list.empty() inspect( ls.intercalate(@list.of([0])), content="@list.of([1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9])", @@ -553,7 +553,7 @@ test "intercalate" { ///| test "default" { - let ls : @list.T[Int] = @list.default() + let ls : @list.List[Int] = @list.default() inspect(ls, content="@list.of([])") } @@ -574,7 +574,7 @@ test "List::output with non-empty list" { ///| test "List::output with empty list" { let buf = StringBuilder::new(size_hint=100) - let list : @list.T[Int] = @list.empty() + let list : @list.List[Int] = @list.empty() Show::output(list, buf) inspect(buf, content="@list.of([])") } @@ -587,13 +587,13 @@ test "List::to_json with non-empty list" { ///| test "List::to_json with empty list" { - let list : @list.T[Int] = empty() + let list : @list.List[Int] = @list.empty() @json.inspect(ToJson::to_json(list), content=[]) } ///| test "List::from_json" { - for xs in (@quickcheck.samples(20) : Array[@list.T[Int]]) { + for xs in (@quickcheck.samples(20) : Array[@list.List[Int]]) { assert_eq(xs, @json.from_json(xs.to_json())) } } @@ -603,18 +603,16 @@ test "to_json/from_json" { let list = @list.of([1, 2, 3, 4, 5]) let json_expected : Json = [1, 2, 3, 4, 5] @json.inspect(list, content=json_expected) - let list2 : @list.T[Int] = @json.from_json(json_expected) + let list2 : @list.List[Int] = @json.from_json(json_expected) @json.inspect(list2, content=json_expected) - let v : Result[@list.T[Int], @json.JsonDecodeError] = try? @json.from_json({ - "a": 1, - }) + let v : Result[@list.List[Int], @json.JsonDecodeError] = try? @json.from_json({ + "a": 1, + }, + ) @json.inspect(v, content={ - "Err": { - "$tag": "JsonDecodeError", - "0": ["$", "@list.from_json: expected array"], - }, + "Err": ["JsonDecodeError", ["$", "@list.from_json: expected array"]], }) - let v2 : @list.T[Int] = @list.from_json([1, 2, 3, 4]) + let v2 : @list.List[Int] = @list.from_json([1, 2, 3, 4]) @json.inspect(v2, content=[1, 2, 3, 4]) } @@ -623,7 +621,7 @@ test "eachi" { let list = @list.of([1, 2, 3, 4, 5]) let mut acc = 0 list.eachi((i, x) => acc += x * i) - assert_eq(acc, 40) + inspect(acc, content="40") } ///| @@ -652,7 +650,7 @@ test "List::zip with lists of equal length" { ///| test "@list.zip with empty list" { inspect( - @list.of([1]).zip((@list.empty() : @list.T[Int])), + @list.of([1]).zip((@list.empty() : @list.List[Int])), content="@list.of([])", ) } @@ -673,7 +671,7 @@ test "List::maximum with non-empty list" { ///| test "@list.maximum with empty list" { - inspect((@list.empty() : @list.T[Int]).maximum(), content="None") + inspect((@list.empty() : @list.List[Int]).maximum(), content="None") } ///| @@ -685,7 +683,7 @@ test "List::minimum with non-empty list" { ///| test "@list.minimum with empty list" { - (@list.empty() : @list.T[Int]).minimum() |> ignore + (@list.empty() : @list.List[Int]).minimum() |> ignore } ///| @@ -694,7 +692,7 @@ test "op_add" { inspect(@list.of([]) + @list.of([1]), content="@list.of([1])") inspect(@list.of([1]) + @list.of([1]), content="@list.of([1, 1])") inspect( - (@list.empty() : @list.T[Int]) + (@list.empty() : @list.T[Int]), + (@list.empty() : @list.List[Int]) + (@list.empty() : @list.List[Int]), content="@list.of([])", ) } @@ -716,7 +714,7 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @list.T[Int] = @list.from_iter(Iter::empty()) + let pq : @list.List[Int] = @list.from_iter(Iter::empty()) inspect(pq, content="@list.of([])") } @@ -727,7 +725,7 @@ test "hash" { inspect(l1.hash() == l2.hash(), content="true") let l3 = @list.of([5, 4, 3, 2, 1]) inspect(l1.hash() == l3.hash(), content="false") - let l4 : @list.T[Int] = @list.of([]) + let l4 : @list.List[Int] = @list.of([]) inspect(l1.hash() == l4.hash(), content="false") inspect(l4.hash() == l4.hash(), content="true") } @@ -798,7 +796,7 @@ test "drop advanced cases" { inspect(l.drop(10), content="@list.of([])") // Drop from empty list - let empty : @list.T[Int] = @list.empty() + let empty : @list.List[Int] = @list.empty() inspect(empty.drop(3), content="@list.of([])") // Drop 0 @@ -817,8 +815,8 @@ test "list manual grouping implementation" { // Check our manual implementation inspect(evens, content="@list.of([2, 4, 6, 8])") inspect(odds, content="@list.of([1, 3, 5, 7, 9])") - assert_eq(evens.length(), 4) - assert_eq(odds.length(), 5) + inspect(evens.length(), content="4") + inspect(odds.length(), content="5") } ///| @@ -858,9 +856,9 @@ test "advanced folding operations" { init=list.head().unwrap(), ) let product = list.fold((acc, x) => acc * x, init=1) - assert_eq(sum, 150) - assert_eq(max, 50) - assert_eq(product, 12000000) + inspect(sum, content="150") + inspect(max, content="50") + inspect(product, content="12000000") } ///| @@ -877,7 +875,7 @@ test "remove_at edge cases" { inspect(l.remove_at(2), content="@list.of([1, 2, 4, 5])") // Remove from empty list - let empty : @list.T[Int] = @list.empty() + let empty : @list.List[Int] = @list.empty() inspect(empty.remove_at(0), content="@list.of([])") // Remove multiple elements @@ -953,7 +951,7 @@ test "intersperse with complex values" { inspect(result.length(), content="5") // Empty list with intersperse - let empty : @list.T[Int] = @list.empty() + let empty : @list.List[Int] = @list.empty() inspect(empty.intersperse(0), content="@list.of([])") // Single element list with intersperse @@ -973,17 +971,17 @@ test "complex list recursion safety" { }) // Verify operations on large list work correctly without stack overflow - assert_eq(large.length(), 10000) + inspect(large.length(), content="10000") assert_eq(large.nth(9999), Some(9999)) assert_eq(large.last(), Some(9999)) // Test operations that traverse the entire list let sum = large.fold((acc, x) => acc + x, init=0) - assert_eq(sum, 49995000) // Sum of numbers 0 to 9999 + inspect(sum, content="49995000") // Sum of numbers 0 to 9999 // Test filter on large list let filtered = large.filter(x => x % 1000 == 0) - assert_eq(filtered.length(), 10) + inspect(filtered.length(), content="10") } ///| @@ -1005,7 +1003,7 @@ test "is_prefix and is_suffix complex cases" { assert_false(l.is_suffix(@list.of([3, 4, 6]))) // Test with empty list - let empty : @list.T[Int] = @list.empty() + let empty : @list.List[Int] = @list.empty() assert_true(empty.is_prefix(@list.of([]))) assert_true(empty.is_suffix(@list.of([]))) assert_true(l.is_prefix(@list.of([]))) diff --git a/bundled-core/list/panic_test.mbt b/bundled-core/list/panic_test.mbt index be540c7..b27b2b4 100644 --- a/bundled-core/list/panic_test.mbt +++ b/bundled-core/list/panic_test.mbt @@ -29,10 +29,23 @@ test "panic @list.exn with empty list" { ///| test "panic @list.unsafe_maximum with empty list" { - inspect((@list.empty() : @list.T[Int]).unsafe_maximum()) + inspect((@list.empty() : @list.List[Int]).unsafe_maximum()) } ///| test "panic @list.unsafe_minimum with empty list" { - inspect((@list.empty() : @list.T[Int]).unsafe_minimum()) + inspect((@list.empty() : @list.List[Int]).unsafe_minimum()) +} + +///| +test "unsafe_head with non-empty list" { + // Test the non-panic path of unsafe_head to cover line 407 + let list = @list.of([1, 2, 3]) + inspect(list.unsafe_head(), content="1") +} + +///| +test "panic unsafe_last with empty list" { + // Test the panic path of unsafe_last to cover line 469 + @list.empty().unsafe_last() } diff --git a/bundled-core/list/pkg.generated.mbti b/bundled-core/list/pkg.generated.mbti new file mode 100644 index 0000000..8ae5e47 --- /dev/null +++ b/bundled-core/list/pkg.generated.mbti @@ -0,0 +1,119 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/list" + +import( + "moonbitlang/core/json" + "moonbitlang/core/quickcheck" +) + +// Values +fn[X] default() -> List[X] + +// Errors + +// Types and methods +pub enum List[A] { + Empty + More(A, mut tail~ : List[A]) +} +fn[A] List::all(Self[A], (A) -> Bool raise?) -> Bool raise? +fn[A] List::any(Self[A], (A) -> Bool raise?) -> Bool raise? +fn[A] List::concat(Self[A], Self[A]) -> Self[A] +#as_free_fn(construct, deprecated) +#as_free_fn +fn[A] List::cons(A, Self[A]) -> Self[A] +fn[A : Eq] List::contains(Self[A], A) -> Bool +fn[A] List::drop(Self[A], Int) -> Self[A] +fn[A] List::drop_while(Self[A], (A) -> Bool raise?) -> Self[A] raise? +fn[A] List::each(Self[A], (A) -> Unit raise?) -> Unit raise? +fn[A] List::eachi(Self[A], (Int, A) -> Unit raise?) -> Unit raise? +fn[A] List::filter(Self[A], (A) -> Bool raise?) -> Self[A] raise? +fn[A, B] List::filter_map(Self[A], (A) -> B? raise?) -> Self[B] raise? +fn[A] List::find(Self[A], (A) -> Bool raise?) -> A? raise? +fn[A] List::find_index(Self[A], (A) -> Bool raise?) -> Int? raise? +fn[A] List::findi(Self[A], (A, Int) -> Bool raise?) -> A? raise? +fn[A, B] List::flat_map(Self[A], (A) -> Self[B] raise?) -> Self[B] raise? +fn[A] List::flatten(Self[Self[A]]) -> Self[A] +fn[A, B] List::fold(Self[A], init~ : B, (B, A) -> B raise?) -> B raise? +fn[A, B] List::foldi(Self[A], init~ : B, (Int, B, A) -> B raise?) -> B raise? +#as_free_fn +fn[A] List::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A] List::from_iter(Iter[A]) -> Self[A] +#as_free_fn +fn[A] List::from_iter_rev(Iter[A]) -> Self[A] +#as_free_fn +fn[A : @json.FromJson] List::from_json(Json) -> Self[A] raise @json.JsonDecodeError +fn[A] List::head(Self[A]) -> A? +fn[A] List::intercalate(Self[Self[A]], Self[A]) -> Self[A] +fn[A] List::intersperse(Self[A], A) -> Self[A] +fn[A] List::is_empty(Self[A]) -> Bool +fn[A : Eq] List::is_prefix(Self[A], Self[A]) -> Bool +fn[A : Eq] List::is_suffix(Self[A], Self[A]) -> Bool +fn[A] List::iter(Self[A]) -> Iter[A] +fn[A] List::iter2(Self[A]) -> Iter2[Int, A] +fn[A] List::last(Self[A]) -> A? +fn[A] List::length(Self[A]) -> Int +fn[A : Eq, B] List::lookup(Self[(A, B)], A) -> B? +fn[A, B] List::map(Self[A], (A) -> B raise?) -> Self[B] raise? +fn[A, B] List::mapi(Self[A], (Int, A) -> B raise?) -> Self[B] raise? +fn[A : Compare] List::maximum(Self[A]) -> A? +fn[A : Compare] List::minimum(Self[A]) -> A? +#alias(empty) +#as_free_fn(empty) +#as_free_fn +fn[A] List::new() -> Self[A] +fn[A] List::nth(Self[A], Int) -> A? +#as_free_fn +fn[A] List::of(FixedArray[A]) -> Self[A] +#alias(add) +fn[A] List::prepend(Self[A], A) -> Self[A] +fn[A : Eq] List::remove(Self[A], A) -> Self[A] +fn[A] List::remove_at(Self[A], Int) -> Self[A] +#as_free_fn +fn[A] List::repeat(Int, A) -> Self[A] +fn[A] List::rev(Self[A]) -> Self[A] +fn[A] List::rev_concat(Self[A], Self[A]) -> Self[A] +#deprecated +fn[A, B] List::rev_fold(Self[A], init~ : B, (B, A) -> B) -> B +#deprecated +fn[A, B] List::rev_foldi(Self[A], init~ : B, (Int, B, A) -> B) -> B +fn[A, B] List::rev_map(Self[A], (A) -> B raise?) -> Self[B] raise? +#as_free_fn +fn[A, S] List::rev_unfold((S) -> (A, S)? raise?, init~ : S) -> Self[A] raise? +fn[A, E] List::scan_left(Self[A], (E, A) -> E raise?, init~ : E) -> Self[E] raise? +fn[A, B] List::scan_right(Self[A], (B, A) -> B raise?, init~ : B) -> Self[B] raise? +#as_free_fn +fn[A] List::singleton(A) -> Self[A] +fn[A : Compare] List::sort(Self[A]) -> Self[A] +#deprecated +fn[A] List::tail(Self[A]) -> Self[A] +fn[A] List::take(Self[A], Int) -> Self[A] +fn[A] List::take_while(Self[A], (A) -> Bool raise?) -> Self[A] raise? +fn[A] List::to_array(Self[A]) -> Array[A] +fn[A : ToJson] List::to_json(Self[A]) -> Json +#as_free_fn +fn[A, S] List::unfold((S) -> (A, S)? raise?, init~ : S) -> Self[A] raise? +fn[A] List::unsafe_head(Self[A]) -> A +fn[A] List::unsafe_last(Self[A]) -> A +fn[A : Compare] List::unsafe_maximum(Self[A]) -> A +fn[A : Compare] List::unsafe_minimum(Self[A]) -> A +fn[A] List::unsafe_nth(Self[A], Int) -> A +fn[A] List::unsafe_tail(Self[A]) -> Self[A] +fn[A, B] List::unzip(Self[(A, B)]) -> (Self[A], Self[B]) +#as_free_fn +fn[A, B] List::zip(Self[A], Self[B]) -> Self[(A, B)] +impl[A] Add for List[A] +impl[X] Default for List[X] +impl[A : Eq] Eq for List[A] +impl[A : Hash] Hash for List[A] +impl[A : Show] Show for List[A] +impl[A : ToJson] ToJson for List[A] +impl[A : @json.FromJson] @json.FromJson for List[A] +impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for List[X] + +// Type aliases +pub typealias List as T + +// Traits + diff --git a/bundled-core/list/types.mbt b/bundled-core/list/types.mbt index d5e7963..6a762f2 100644 --- a/bundled-core/list/types.mbt +++ b/bundled-core/list/types.mbt @@ -13,7 +13,11 @@ // limitations under the License. ///| -pub enum T[A] { +pub enum List[A] { Empty - More(A, mut tail~ : T[A]) + More(A, mut tail~ : List[A]) } derive(Eq) + +///| +#deprecated("Use List instead of T") +pub typealias List as T diff --git a/bundled-core/math/algebraic.mbt b/bundled-core/math/algebraic.mbt index df29c77..9208bb4 100644 --- a/bundled-core/math/algebraic.mbt +++ b/bundled-core/math/algebraic.mbt @@ -61,10 +61,10 @@ pub fn[T : Compare] minimum(x : T, y : T) -> T { /// # Examples /// /// ```mbt -/// assert_eq(@math.cbrtf(1), 1) -/// assert_eq(@math.cbrtf(27), 3) -/// assert_eq(@math.cbrtf(125), 5) -/// assert_eq(@math.cbrtf(1000), 10) +/// inspect(@math.cbrtf(1), content="1") +/// inspect(@math.cbrtf(27), content="3") +/// inspect(@math.cbrtf(125), content="5") +/// inspect(@math.cbrtf(1000), content="10") /// ``` pub fn cbrtf(x : Float) -> Float { let b1 : UInt = 709958130 // B1 = (127-127.0/3-0.03306235651)*2**23 */ @@ -125,9 +125,9 @@ pub fn cbrtf(x : Float) -> Float { /// # Examples /// /// ```mbt -/// assert_eq(@math.hypotf(3, 4), 5) -/// assert_eq(@math.hypotf(5, 12), 13) -/// assert_eq(@math.hypotf(8, 15), 17) +/// inspect(@math.hypotf(3, 4), content="5") +/// inspect(@math.hypotf(5, 12), content="13") +/// inspect(@math.hypotf(8, 15), content="17") /// ``` pub fn hypotf(x : Float, y : Float) -> Float { let epsilon : Float = 1.1920928955078125e-7 diff --git a/bundled-core/math/algebraic_test.mbt b/bundled-core/math/algebraic_test.mbt index 934e0a4..65fa666 100644 --- a/bundled-core/math/algebraic_test.mbt +++ b/bundled-core/math/algebraic_test.mbt @@ -13,36 +13,36 @@ // limitations under the License. ///| -test "cbrtf" { - inspect(cbrtf(0), content="0") - inspect(cbrtf(1), content="1") - inspect(cbrtf(2), content="1.2599210739135742") - inspect(cbrtf(3), content="1.4422495365142822") - inspect(cbrtf(4), content="1.587401032447815") - inspect(cbrtf(5), content="1.7099759578704834") - inspect(cbrtf(-6), content="-1.8171205520629883") - inspect(cbrtf(-7), content="-1.912931203842163") - inspect(cbrtf(-8), content="-2") - inspect(cbrtf(@float.not_a_number), content="NaN") - inspect(cbrtf(@float.infinity), content="Infinity") - inspect(cbrtf(@float.neg_infinity), content="-Infinity") +test "@math.cbrtf" { + inspect(@math.cbrtf(0), content="0") + inspect(@math.cbrtf(1), content="1") + inspect(@math.cbrtf(2), content="1.2599210739135742") + inspect(@math.cbrtf(3), content="1.4422495365142822") + inspect(@math.cbrtf(4), content="1.587401032447815") + inspect(@math.cbrtf(5), content="1.7099759578704834") + inspect(@math.cbrtf(-6), content="-1.8171205520629883") + inspect(@math.cbrtf(-7), content="-1.912931203842163") + inspect(@math.cbrtf(-8), content="-2") + inspect(@math.cbrtf(@float.not_a_number), content="NaN") + inspect(@math.cbrtf(@float.infinity), content="Infinity") + inspect(@math.cbrtf(@float.neg_infinity), content="-Infinity") } ///| -test "hypotf" { - inspect(hypotf(3, 4), content="5") - inspect(hypotf(5, 12.0), content="13") - inspect(hypotf(8, 15.0), content="17") - inspect(hypotf(7, 24.0), content="25") - inspect(hypotf(20, 21.0), content="29") - inspect(hypotf(9, 40.0), content="41") - inspect(hypotf(12, 35.0), content="37") - inspect(hypotf(11, 60.0), content="61") - inspect(hypotf(16, 63.0), content="65") - inspect(hypotf(@float.not_a_number, 1.0), content="NaN") - inspect(hypotf(1, @float.not_a_number), content="NaN") - inspect(hypotf(@float.infinity, 1.0), content="Infinity") - inspect(hypotf(1.0, @float.infinity), content="Infinity") - inspect(hypotf(@float.neg_infinity, 1.0), content="Infinity") - inspect(hypotf(1, @float.neg_infinity), content="Infinity") +test "@math.hypotf" { + inspect(@math.hypotf(3, 4), content="5") + inspect(@math.hypotf(5, 12.0), content="13") + inspect(@math.hypotf(8, 15.0), content="17") + inspect(@math.hypotf(7, 24.0), content="25") + inspect(@math.hypotf(20, 21.0), content="29") + inspect(@math.hypotf(9, 40.0), content="41") + inspect(@math.hypotf(12, 35.0), content="37") + inspect(@math.hypotf(11, 60.0), content="61") + inspect(@math.hypotf(16, 63.0), content="65") + inspect(@math.hypotf(@float.not_a_number, 1.0), content="NaN") + inspect(@math.hypotf(1, @float.not_a_number), content="NaN") + inspect(@math.hypotf(@float.infinity, 1.0), content="Infinity") + inspect(@math.hypotf(1.0, @float.infinity), content="Infinity") + inspect(@math.hypotf(@float.neg_infinity, 1.0), content="Infinity") + inspect(@math.hypotf(1, @float.neg_infinity), content="Infinity") } diff --git a/bundled-core/math/exp.mbt b/bundled-core/math/exp.mbt index 7f306f9..49efc2a 100644 --- a/bundled-core/math/exp.mbt +++ b/bundled-core/math/exp.mbt @@ -26,7 +26,7 @@ ///| fn top12(x : Float) -> UInt { - x.reinterpret_as_int().reinterpret_as_uint() >> 20 + x.reinterpret_as_uint() >> 20 } ///| @@ -56,7 +56,7 @@ priv struct Exp2fData { } ///| -let expf_n : UInt64 = (1 << exp2f_table_bits).to_int64().reinterpret_as_uint64() +let expf_n : UInt64 = (1 << exp2f_table_bits).to_uint64() ///| let exp2f_data_n : Double = (1 << exp2f_table_bits).to_double() @@ -94,16 +94,15 @@ let exp2f_data : Exp2fData = { /// Example: /// /// ```moonbit -/// inspect(expf(0), content="1") -/// inspect(expf(1), content="2.7182817459106445") -/// inspect(expf(@float.neg_infinity), content="0") +/// inspect(@math.expf(0), content="1") +/// inspect(@math.expf(1), content="2.7182817459106445") +/// inspect(@math.expf(@float.neg_infinity), content="0") /// ``` pub fn expf(x : Float) -> Float { let xd = x.to_double() let abstop = top12(x) & 0x7ff if abstop >= top12(88.0) { - if x.reinterpret_as_int().reinterpret_as_uint() == - @float.neg_infinity.reinterpret_as_int().reinterpret_as_uint() { + if x.reinterpret_as_uint() == @float.neg_infinity.reinterpret_as_uint() { return 0.0 } if abstop >= top12(@float.infinity) { @@ -150,12 +149,12 @@ pub fn expf(x : Float) -> Float { /// Example /// /// ```moonbit -/// inspect(expm1f(0), content="0") -/// inspect(expm1f(1), content="1.7182817459106445") -/// inspect(expm1f(-1), content="-0.6321205496788025") -/// inspect(expm1f(@float.not_a_number), content="NaN") -/// inspect(expm1f(@float.infinity), content="Infinity") -/// inspect(expm1f(@float.neg_infinity), content="-1") +/// inspect(@math.expm1f(0), content="0") +/// inspect(@math.expm1f(1), content="1.7182817459106445") +/// inspect(@math.expm1f(-1), content="-0.6321205496788025") +/// inspect(@math.expm1f(@float.not_a_number), content="NaN") +/// inspect(@math.expm1f(@float.infinity), content="Infinity") +/// inspect(@math.expm1f(@float.neg_infinity), content="-1") /// ``` pub fn expm1f(x : Float) -> Float { let float_ln2_hi : Float = 6.9314575195e-01 // 0x3f317200 @@ -192,7 +191,7 @@ pub fn expm1f(x : Float) -> Float { // if |x| > 0.5 ln2 if hx < 0x3F851592 { // and |x| < 1.5 ln2 - if not(sign) { + if !sign { hi = x - float_ln2_hi lo = float_ln2_lo k = 1 @@ -242,7 +241,7 @@ pub fn expm1f(x : Float) -> Float { return (1.0 : Float) + (2.0 : Float) * (x - e) } let twopk = ((0x7f + k) << 23).reinterpret_as_float() // 2^k - if not(k is (0..=56)) { + if !(k is (0..=56)) { // suffice to return exp(x)-1 let mut y = x - e + 1.0 if k == 128 { diff --git a/bundled-core/math/hyperbolic.mbt b/bundled-core/math/hyperbolic.mbt index 693f71b..bdccc93 100644 --- a/bundled-core/math/hyperbolic.mbt +++ b/bundled-core/math/hyperbolic.mbt @@ -39,15 +39,15 @@ fn k_expo2f(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(sinhf(0), content="0") -/// inspect(sinhf(-0), content="0") -/// inspect(sinhf(1), content="1.175201177597046") -/// inspect(sinhf(2), content="3.6268603801727295") -/// inspect(sinhf(1000), content="Infinity") -/// inspect(sinhf(-1000), content="-Infinity") -/// inspect(sinhf(@float.not_a_number), content="NaN") -/// inspect(sinhf(@float.infinity), content="Infinity") -/// inspect(sinhf(@float.neg_infinity), content="-Infinity") +/// inspect(@math.sinhf(0), content="0") +/// inspect(@math.sinhf(-0), content="0") +/// inspect(@math.sinhf(1), content="1.175201177597046") +/// inspect(@math.sinhf(2), content="3.6268603801727295") +/// inspect(@math.sinhf(1000), content="Infinity") +/// inspect(@math.sinhf(-1000), content="-Infinity") +/// inspect(@math.sinhf(@float.not_a_number), content="NaN") +/// inspect(@math.sinhf(@float.infinity), content="Infinity") +/// inspect(@math.sinhf(@float.neg_infinity), content="-Infinity") /// ``` pub fn sinhf(x : Float) -> Float { let mut h : Float = 0.5 @@ -93,15 +93,15 @@ pub fn sinhf(x : Float) -> Float { /// Example /// /// ```moonbit -/// inspect(coshf(0), content="1") -/// inspect(coshf(-0), content="1") -/// inspect(coshf(1), content="1.5430805683135986") -/// inspect(coshf(2), content="3.7621958255767822") -/// inspect(coshf(1000), content="Infinity") -/// inspect(coshf(-1000), content="Infinity") -/// inspect(coshf(@float.not_a_number), content="NaN") -/// inspect(coshf(@float.infinity), content="Infinity") -/// inspect(coshf(@float.neg_infinity), content="Infinity") +/// inspect(@math.coshf(0), content="1") +/// inspect(@math.coshf(-0), content="1") +/// inspect(@math.coshf(1), content="1.5430805683135986") +/// inspect(@math.coshf(2), content="3.7621958255767822") +/// inspect(@math.coshf(1000), content="Infinity") +/// inspect(@math.coshf(-1000), content="Infinity") +/// inspect(@math.coshf(@float.not_a_number), content="NaN") +/// inspect(@math.coshf(@float.infinity), content="Infinity") +/// inspect(@math.coshf(@float.neg_infinity), content="Infinity") /// ``` pub fn coshf(x : Float) -> Float { let mut x = x @@ -208,14 +208,14 @@ pub fn tanhf(x : Float) -> Float { /// Example /// /// ```moonbit -/// inspect(asinhf(0), content="0") -/// inspect(asinhf(-0), content="0") -/// inspect(asinhf(1), content="0.8813735842704773") -/// inspect(asinhf(2), content="1.4436354637145996") -/// inspect(asinhf(1000), content="7.600902557373047") -/// inspect(asinhf(@float.not_a_number), content="NaN") -/// inspect(asinhf(@float.infinity), content="Infinity") -/// inspect(asinhf(@float.neg_infinity), content="-Infinity") +/// inspect(@math.asinhf(0), content="0") +/// inspect(@math.asinhf(-0), content="0") +/// inspect(@math.asinhf(1), content="0.8813735842704773") +/// inspect(@math.asinhf(2), content="1.4436354637145996") +/// inspect(@math.asinhf(1000), content="7.600902557373047") +/// inspect(@math.asinhf(@float.not_a_number), content="NaN") +/// inspect(@math.asinhf(@float.infinity), content="Infinity") +/// inspect(@math.asinhf(@float.neg_infinity), content="-Infinity") /// ``` pub fn asinhf(x : Float) -> Float { let u = x.reinterpret_as_uint() @@ -263,14 +263,14 @@ pub fn asinhf(x : Float) -> Float { /// Example /// /// ```moonbit -/// inspect(acoshf(1), content="0") -/// inspect(acoshf(2), content="1.316957950592041") -/// inspect(acoshf(1000), content="7.600902080535889") -/// inspect(acoshf(-1), content="NaN") -/// inspect(acoshf(-2), content="NaN") -/// inspect(acoshf(@float.not_a_number), content="NaN") -/// inspect(acoshf(@float.infinity), content="Infinity") -/// inspect(acoshf(@float.neg_infinity), content="NaN") +/// inspect(@math.acoshf(1), content="0") +/// inspect(@math.acoshf(2), content="1.316957950592041") +/// inspect(@math.acoshf(1000), content="7.600902080535889") +/// inspect(@math.acoshf(-1), content="NaN") +/// inspect(@math.acoshf(-2), content="NaN") +/// inspect(@math.acoshf(@float.not_a_number), content="NaN") +/// inspect(@math.acoshf(@float.infinity), content="Infinity") +/// inspect(@math.acoshf(@float.neg_infinity), content="NaN") /// ``` pub fn acoshf(x : Float) -> Float { let ln2 : Float = 693147180559945309417232121458176568 @@ -309,16 +309,16 @@ pub fn acoshf(x : Float) -> Float { /// Example /// /// ```moonbit -/// inspect(atanhf(0), content="0") -/// inspect(atanhf(-0), content="0") -/// inspect(atanhf(0.5), content="0.5493061542510986") -/// inspect(atanhf(-0.5), content="-0.5493061542510986") -/// inspect(atanhf(1), content="Infinity") -/// inspect(atanhf(-1), content="-Infinity") -/// inspect(atanhf(1.5), content="NaN") -/// inspect(atanhf(@float.not_a_number), content="NaN") -/// inspect(atanhf(@float.infinity), content="NaN") -/// inspect(atanhf(@float.neg_infinity), content="NaN") +/// inspect(@math.atanhf(0), content="0") +/// inspect(@math.atanhf(-0), content="0") +/// inspect(@math.atanhf(0.5), content="0.5493061542510986") +/// inspect(@math.atanhf(-0.5), content="-0.5493061542510986") +/// inspect(@math.atanhf(1), content="Infinity") +/// inspect(@math.atanhf(-1), content="-Infinity") +/// inspect(@math.atanhf(1.5), content="NaN") +/// inspect(@math.atanhf(@float.not_a_number), content="NaN") +/// inspect(@math.atanhf(@float.infinity), content="NaN") +/// inspect(@math.atanhf(@float.neg_infinity), content="NaN") /// ``` pub fn atanhf(x : Float) -> Float { let u = x.reinterpret_as_uint() diff --git a/bundled-core/math/log.mbt b/bundled-core/math/log.mbt index e1e9ceb..c9cc979 100644 --- a/bundled-core/math/log.mbt +++ b/bundled-core/math/log.mbt @@ -39,10 +39,10 @@ let logf_n : UInt = 1U << logf_table_bits ///| priv struct LogfData { - invc : Array[Double] - logc : Array[Double] + invc : FixedArray[Double] + logc : FixedArray[Double] ln2 : Double - poly : Array[Double] + poly : FixedArray[Double] } ///| @@ -82,12 +82,12 @@ let logf_data : LogfData = { /// Example: /// /// ```moonbit -/// inspect(lnf(1), content="0") -/// inspect(lnf(2.718281828459045), content="0.9999999403953552") -/// inspect(lnf(0), content="-Infinity") +/// inspect(@math.lnf(1), content="0") +/// inspect(@math.lnf(2.718281828459045), content="0.9999999403953552") +/// inspect(@math.lnf(0), content="-Infinity") /// ``` pub fn lnf(x : Float) -> Float { - let mut ix : UInt = x.reinterpret_as_int().reinterpret_as_uint() + let mut ix : UInt = x.reinterpret_as_uint() if ix == 0x3f800000U { return 0.0 } @@ -101,7 +101,7 @@ pub fn lnf(x : Float) -> Float { if (ix & 0x80000000U) != 0 || ix * 2 >= 0xff000000U { return @float.not_a_number } - ix = (x * 0x1.0p23).reinterpret_as_int().reinterpret_as_uint() + ix = (x * 0x1.0p23).reinterpret_as_uint() ix -= (23 << 23).reinterpret_as_uint() } let tmp = ix - logf_off @@ -110,7 +110,7 @@ pub fn lnf(x : Float) -> Float { let iz = ix - (tmp & 0xff800000U) let invc = logf_data.invc[i] let logc = logf_data.logc[i] - let z = iz.reinterpret_as_int().reinterpret_as_float().to_double() + let z = iz.reinterpret_as_float().to_double() let r = z * invc - 1 let y0 = logc + k.to_double() * logf_data.ln2 let r2 = r * r @@ -138,13 +138,13 @@ pub fn lnf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(ln_1pf(0), content="0") -/// inspect(ln_1pf(1), content="0.6931471824645996") -/// inspect(ln_1pf(-1), content="-Infinity") -/// inspect(ln_1pf(-2), content="NaN") -/// inspect(ln_1pf(@float.not_a_number), content="NaN") -/// inspect(ln_1pf(@float.infinity), content="Infinity") -/// inspect(ln_1pf(@float.neg_infinity), content="NaN") +/// inspect(@math.ln_1pf(0), content="0") +/// inspect(@math.ln_1pf(1), content="0.6931471824645996") +/// inspect(@math.ln_1pf(-1), content="-Infinity") +/// inspect(@math.ln_1pf(-2), content="NaN") +/// inspect(@math.ln_1pf(@float.not_a_number), content="NaN") +/// inspect(@math.ln_1pf(@float.infinity), content="Infinity") +/// inspect(@math.ln_1pf(@float.neg_infinity), content="NaN") /// ``` pub fn ln_1pf(x : Float) -> Float { let lg1_f : Float = 0.66666662693 diff --git a/bundled-core/math/log_test.mbt b/bundled-core/math/log_test.mbt index d62bf34..d5c6d18 100644 --- a/bundled-core/math/log_test.mbt +++ b/bundled-core/math/log_test.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -test "lnf Test" { +test "@math.lnf Test" { let data : Array[Float] = [ 1, 2, 3, 4, 5, 6.5, 7.5, 8.5, 9.5, 10.5, 0.25, 1.25, 2.25, 3.25, 4.25, @float.neg_infinity, @float.not_a_number, @float.infinity, @@ -27,7 +27,7 @@ test "lnf Test" { ] for i in 0.. Double fn cbrtf(Float) -> Float +#as_free_fn fn ceil(Double) -> Double fn cos(Double) -> Double @@ -58,13 +60,14 @@ fn expm1(Double) -> Double fn expm1f(Float) -> Float +#as_free_fn fn floor(Double) -> Double fn hypot(Double, Double) -> Double fn hypotf(Float, Float) -> Float -fn is_probable_prime(@bigint.BigInt, @random.Rand, iters~ : Int = ..) -> Bool +fn is_probable_prime(@bigint.BigInt, @random.Rand, iters? : Int) -> Bool fn ln(Double) -> Double @@ -84,6 +87,7 @@ fn[T : Compare] maximum(T, T) -> T #deprecated fn[T : Compare] minimum(T, T) -> T +#deprecated let pi : Double fn pow(Double, Double) -> Double @@ -92,6 +96,7 @@ fn powf(Float, Float) -> Float fn probable_prime(Int, @random.Rand) -> @bigint.BigInt +#as_free_fn fn round(Double) -> Double fn scalbn(Double, Int) -> Double @@ -114,8 +119,11 @@ fn tanh(Double) -> Double fn tanhf(Float) -> Float +#as_free_fn fn trunc(Double) -> Double +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/math/pow_test.mbt b/bundled-core/math/pow_test.mbt index bb09e81..3291cbc 100644 --- a/bundled-core/math/pow_test.mbt +++ b/bundled-core/math/pow_test.mbt @@ -15,132 +15,132 @@ ///| test "scalbn comprehensive" { // Basic tests for scalbnf - inspect(scalbnf(1.5, 2), content="6") - inspect(scalbnf(2.0, -1), content="1") - inspect(scalbnf(3.0, 0), content="3") + inspect(@math.scalbnf(1.5, 2), content="6") + inspect(@math.scalbnf(2.0, -1), content="1") + inspect(@math.scalbnf(3.0, 0), content="3") // Test extreme exponent values - inspect(scalbnf(1.0, 128), content="Infinity") - inspect(scalbnf(1.0, -150), content="0") - inspect(scalbnf(1.0, 254), content="Infinity") - inspect(scalbnf(1.0, -199), content="0") - inspect(scalbnf(1.0, 307), content="Infinity") - inspect(scalbnf(1.0, -296), content="0") + inspect(@math.scalbnf(1.0, 128), content="Infinity") + inspect(@math.scalbnf(1.0, -150), content="0") + inspect(@math.scalbnf(1.0, 254), content="Infinity") + inspect(@math.scalbnf(1.0, -199), content="0") + inspect(@math.scalbnf(1.0, 307), content="Infinity") + inspect(@math.scalbnf(1.0, -296), content="0") // Test with special values - inspect(scalbnf(@float.infinity, 10), content="Infinity") - inspect(scalbnf(@float.neg_infinity, 10), content="-Infinity") - inspect(scalbnf(@float.not_a_number, 10), content="NaN") - inspect(scalbnf(0.0, 10), content="0") - inspect(scalbnf(-0.0, 10), content="0") + inspect(@math.scalbnf(@float.infinity, 10), content="Infinity") + inspect(@math.scalbnf(@float.neg_infinity, 10), content="-Infinity") + inspect(@math.scalbnf(@float.not_a_number, 10), content="NaN") + inspect(@math.scalbnf(0.0, 10), content="0") + inspect(@math.scalbnf(-0.0, 10), content="0") // Test with different float values - inspect(scalbnf(0.5, 1), content="1") - inspect(scalbnf(0.25, 2), content="1") - inspect(scalbnf(0.1, 4), content="1.600000023841858") + inspect(@math.scalbnf(0.5, 1), content="1") + inspect(@math.scalbnf(0.25, 2), content="1") + inspect(@math.scalbnf(0.1, 4), content="1.600000023841858") // Test with negative numbers - inspect(scalbnf(-2.0, 3), content="-16") - inspect(scalbnf(-3.0, -1), content="-1.5") + inspect(@math.scalbnf(-2.0, 3), content="-16") + inspect(@math.scalbnf(-3.0, -1), content="-1.5") // Test with small numbers let small_number = 1.0e-20 let small_float = small_number.to_float() - inspect(scalbnf(small_float, 1), content="1.999999936531045e-20") + inspect(@math.scalbnf(small_float, 1), content="1.999999936531045e-20") } ///| test "pow basic" { - // Basic tests for powf - inspect(powf(1.0, 3.0), content="1") - inspect(powf(-1.0, 3.0), content="-1") - inspect(powf(2.0, 3.0), content="8") - inspect(powf(-2.0, 3.0), content="-8") - inspect(powf(2.0, -3.0), content="0.125") - inspect(powf(-2.0, -3.0), content="-0.125") - inspect(powf(2.0, -3.14), content="0.1134398877620697") - inspect(powf(-2.0, -3.14), content="NaN") + // Basic tests for @math.powf + inspect(@math.powf(1.0, 3.0), content="1") + inspect(@math.powf(-1.0, 3.0), content="-1") + inspect(@math.powf(2.0, 3.0), content="8") + inspect(@math.powf(-2.0, 3.0), content="-8") + inspect(@math.powf(2.0, -3.0), content="0.125") + inspect(@math.powf(-2.0, -3.0), content="-0.125") + inspect(@math.powf(2.0, -3.14), content="0.1134398877620697") + inspect(@math.powf(-2.0, -3.14), content="NaN") // Test with special values - inspect(powf(@float.infinity, 3.0), content="Infinity") - inspect(powf(@float.infinity, -3.0), content="0") - inspect(powf(@float.infinity, 0.0), content="1") - inspect(powf(@float.neg_infinity, 3.0), content="-Infinity") - inspect(powf(@float.neg_infinity, -3.0), content="0") - inspect(powf(@float.neg_infinity, 0.0), content="1") - inspect(powf(@float.not_a_number, 3.0), content="NaN") + inspect(@math.powf(@float.infinity, 3.0), content="Infinity") + inspect(@math.powf(@float.infinity, -3.0), content="0") + inspect(@math.powf(@float.infinity, 0.0), content="1") + inspect(@math.powf(@float.neg_infinity, 3.0), content="-Infinity") + inspect(@math.powf(@float.neg_infinity, -3.0), content="0") + inspect(@math.powf(@float.neg_infinity, 0.0), content="1") + inspect(@math.powf(@float.not_a_number, 3.0), content="NaN") } ///| test "pow special cases" { // Test x^0 for various x values - inspect(powf(0.0, 0.0), content="1") - inspect(powf(1.0, 0.0), content="1") - inspect(powf(-1.0, 0.0), content="1") - inspect(powf(@float.infinity, 0.0), content="1") - inspect(powf(@float.neg_infinity, 0.0), content="1") - inspect(powf(@float.not_a_number, 0.0), content="1") + inspect(@math.powf(0.0, 0.0), content="1") + inspect(@math.powf(1.0, 0.0), content="1") + inspect(@math.powf(-1.0, 0.0), content="1") + inspect(@math.powf(@float.infinity, 0.0), content="1") + inspect(@math.powf(@float.neg_infinity, 0.0), content="1") + inspect(@math.powf(@float.not_a_number, 0.0), content="1") // Test 0^y for various y values - inspect(powf(0.0, 1.0), content="0") - inspect(powf(0.0, 2.0), content="0") - inspect(powf(0.0, -1.0), content="Infinity") - inspect(powf(0.0, -2.0), content="Infinity") - inspect(powf(0.0, 0.5), content="0") - inspect(powf(0.0, -0.5), content="Infinity") + inspect(@math.powf(0.0, 1.0), content="0") + inspect(@math.powf(0.0, 2.0), content="0") + inspect(@math.powf(0.0, -1.0), content="Infinity") + inspect(@math.powf(0.0, -2.0), content="Infinity") + inspect(@math.powf(0.0, 0.5), content="0") + inspect(@math.powf(0.0, -0.5), content="Infinity") // Test negative base with non-integer exponent - inspect(powf(-1.0, 0.5), content="NaN") - inspect(powf(-2.0, 0.5), content="NaN") + inspect(@math.powf(-1.0, 0.5), content="NaN") + inspect(@math.powf(-2.0, 0.5), content="NaN") // Test 1^y for various y values - inspect(powf(1.0, 10.0), content="1") - inspect(powf(1.0, -10.0), content="1") - inspect(powf(1.0, @float.infinity), content="1") - inspect(powf(1.0, @float.neg_infinity), content="1") - inspect(powf(1.0, @float.not_a_number), content="1") + inspect(@math.powf(1.0, 10.0), content="1") + inspect(@math.powf(1.0, -10.0), content="1") + inspect(@math.powf(1.0, @float.infinity), content="1") + inspect(@math.powf(1.0, @float.neg_infinity), content="1") + inspect(@math.powf(1.0, @float.not_a_number), content="1") // Test x^NaN - inspect(powf(2.0, @float.not_a_number), content="NaN") - inspect(powf(-2.0, @float.not_a_number), content="NaN") + inspect(@math.powf(2.0, @float.not_a_number), content="NaN") + inspect(@math.powf(-2.0, @float.not_a_number), content="NaN") // Test NaN^y (except for y=0, tested above) - inspect(powf(@float.not_a_number, 1.0), content="NaN") - inspect(powf(@float.not_a_number, -1.0), content="NaN") + inspect(@math.powf(@float.not_a_number, 1.0), content="NaN") + inspect(@math.powf(@float.not_a_number, -1.0), content="NaN") } ///| test "pow edge cases" { // Test cases with very large exponents - assert_true(powf(1.2, 100.0).is_close(82818304.0)) - assert_true(powf(0.8, 100.0).is_close(2.0370390096946522e-10)) + assert_true(@math.powf(1.2, 100.0).is_close(82818304.0)) + assert_true(@math.powf(0.8, 100.0).is_close(2.0370390096946522e-10)) // Test cases with very small exponents - assert_true(powf(1.2, -100.0).is_close(1.2074625743707657e-8)) - assert_true(powf(0.8, -100.0).is_close(4909086208.0)) + assert_true(@math.powf(1.2, -100.0).is_close(1.2074625743707657e-8)) + assert_true(@math.powf(0.8, -100.0).is_close(4909086208.0)) // Test cases with small base let small_flt = 1.0e-5F - assert_true(powf(small_flt, 2.0).is_close(9.999999439624929e-11)) - assert_true(powf(small_flt, 0.5).is_close(0.003162277629598975)) + assert_true(@math.powf(small_flt, 2.0).is_close(9.999999439624929e-11)) + assert_true(@math.powf(small_flt, 0.5).is_close(0.003162277629598975)) // Test cases with large base let large_flt : Float = 1.0e5 - assert_true(powf(large_flt, 2.0).is_close(10000000000.0)) - assert_true(powf(large_flt, 0.5).is_close(316.2277526855469)) + assert_true(@math.powf(large_flt, 2.0).is_close(10000000000.0)) + assert_true(@math.powf(large_flt, 0.5).is_close(316.2277526855469)) // Test negative exponent with negative base (where exponent is integer) - assert_true(powf(-2.0, -4.0).is_close(0.0625)) - assert_true(powf(-2.0, -5.0).is_close(-0.03125)) + assert_true(@math.powf(-2.0, -4.0).is_close(0.0625)) + assert_true(@math.powf(-2.0, -5.0).is_close(-0.03125)) // Test fractional exponents with positive base - assert_true(powf(4.0, 0.5).is_close(2.0)) - assert_true(powf(9.0, 0.5).is_close(3.0)) - assert_true(powf(16.0, 0.25).is_close(2.0)) + assert_true(@math.powf(4.0, 0.5).is_close(2.0)) + assert_true(@math.powf(9.0, 0.5).is_close(3.0)) + assert_true(@math.powf(16.0, 0.25).is_close(2.0)) // Test powers that should closely approximate known values - assert_true(powf(10.0, 2.0).is_close(100.0)) - assert_true(powf(10.0, -2.0).is_close(0.009999999776482582)) + assert_true(@math.powf(10.0, 2.0).is_close(100.0)) + assert_true(@math.powf(10.0, -2.0).is_close(0.009999999776482582)) } ///| @@ -151,14 +151,14 @@ test "pow with standard functions" { let e_flt : Float = 2.7182817459106445 let x_flt : Float = 2.0 let expected_exp_flt : Float = 7.3890562 - let actual_pow_flt = powf(e_flt, x_flt) + let actual_pow_flt = @math.powf(e_flt, x_flt) assert_true((actual_pow_flt - expected_exp_flt).abs() < 0.01) // Test sqrt(x) vs. pow(x, 0.5) let values_flt : Array[Float] = [4.0, 9.0, 16.0, 25.0] for value in values_flt { let expected_sqrt_flt = value.sqrt() - let actual_pow_flt = powf(value, 0.5) + let actual_pow_flt = @math.powf(value, 0.5) assert_true((actual_pow_flt - expected_sqrt_flt).abs() < 0.01) } @@ -166,12 +166,13 @@ test "pow with standard functions" { let x_test_flt : Float = 3.0 let y_test_flt : Float = 2.0 let z_test_flt : Float = 4.0 - let left_val_flt = powf(x_test_flt, y_test_flt) * powf(x_test_flt, z_test_flt) - let right_val_flt = powf(x_test_flt, y_test_flt + z_test_flt) + let left_val_flt = @math.powf(x_test_flt, y_test_flt) * + @math.powf(x_test_flt, z_test_flt) + let right_val_flt = @math.powf(x_test_flt, y_test_flt + z_test_flt) assert_true((left_val_flt - right_val_flt).abs() < 0.01) // Test pow(x, y)^z = pow(x, y*z) - let left_test_flt = powf(powf(x_test_flt, y_test_flt), z_test_flt) - let right_test_flt = powf(x_test_flt, y_test_flt * z_test_flt) + let left_test_flt = @math.powf(@math.powf(x_test_flt, y_test_flt), z_test_flt) + let right_test_flt = @math.powf(x_test_flt, y_test_flt * z_test_flt) assert_true((left_test_flt - right_test_flt).abs() < 0.01) } diff --git a/bundled-core/math/prime.mbt b/bundled-core/math/prime.mbt index 91ce88b..2fce8ca 100644 --- a/bundled-core/math/prime.mbt +++ b/bundled-core/math/prime.mbt @@ -200,13 +200,13 @@ let small_primes : Array[@bigint.BigInt] = [ /// let rand = @random.Rand::new() /// let prime = BigInt::from_string("17") /// let composite = BigInt::from_string("15") -/// inspect(is_probable_prime(prime, rand), content="true") -/// inspect(is_probable_prime(composite, rand), content="false") +/// inspect(@math.is_probable_prime(prime, rand), content="true") +/// inspect(@math.is_probable_prime(composite, rand), content="false") /// ``` pub fn is_probable_prime( number : @bigint.BigInt, rand : @random.Rand, - iters~ : Int = DEFAULT_PRIME_TEST_ITERS + iters? : Int = DEFAULT_PRIME_TEST_ITERS, ) -> Bool { if iters <= 0 { abort("non-positive iters for probably prime") @@ -242,8 +242,8 @@ pub fn is_probable_prime( /// /// ```moonbit /// let rand = @random.Rand::new() -/// let prime = probable_prime(64, rand) -/// inspect(is_probable_prime(prime, rand), content="true") +/// let prime = @math.probable_prime(64, rand) +/// inspect(@math.is_probable_prime(prime, rand), content="true") /// ``` pub fn probable_prime(bits : Int, rand : @random.Rand) -> @bigint.BigInt { for { @@ -277,7 +277,7 @@ fn trial_divisions(n : @bigint.BigInt) -> Bool { fn miller_rabin_test( w : @bigint.BigInt, iters : Int, - rand : @random.Rand + rand : @random.Rand, ) -> Bool { if w <= 3N { abort("candidate of miller rabin test must larger than 3") diff --git a/bundled-core/math/prime_test.mbt b/bundled-core/math/prime_test.mbt index 212977e..a13889c 100644 --- a/bundled-core/math/prime_test.mbt +++ b/bundled-core/math/prime_test.mbt @@ -13,34 +13,34 @@ // limitations under the License. ///| -test "is_probable_prime" { +test "@math.is_probable_prime" { let r = @random.Rand::new() // small primes - inspect(is_probable_prime(2N, r), content="true") - inspect(is_probable_prime(3N, r), content="true") - inspect(is_probable_prime(7N, r), content="true") - inspect(is_probable_prime(17N, r), content="true") + inspect(@math.is_probable_prime(2N, r), content="true") + inspect(@math.is_probable_prime(3N, r), content="true") + inspect(@math.is_probable_prime(7N, r), content="true") + inspect(@math.is_probable_prime(17N, r), content="true") // https://t5k.org/lists/small - inspect(is_probable_prime(5915587277N, r), content="true") - inspect(is_probable_prime(1500450271N, r), content="true") - inspect(is_probable_prime(48112959837082048697N, r), content="true") - inspect(is_probable_prime(27542476619900900873N, r), content="true") + inspect(@math.is_probable_prime(5915587277N, r), content="true") + inspect(@math.is_probable_prime(1500450271N, r), content="true") + inspect(@math.is_probable_prime(48112959837082048697N, r), content="true") + inspect(@math.is_probable_prime(27542476619900900873N, r), content="true") inspect( - is_probable_prime( + @math.is_probable_prime( 643808006803554439230129854961492699151386107534013432918073439524138264842370630061369715394739134090922937332590384720397133335969549256322620979036686633213903952966175107096769180017646161851573147596390153N, r, ), content="true", ) inspect( - is_probable_prime( + @math.is_probable_prime( 449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163N, r, ), content="true", ) inspect( - is_probable_prime( + @math.is_probable_prime( 5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993N, r, ), @@ -48,44 +48,56 @@ test "is_probable_prime" { ) // small composites - inspect(is_probable_prime(0N, r), content="false") - inspect(is_probable_prime(1N, r), content="false") - inspect(is_probable_prime(93N, r), content="false") - inspect(is_probable_prime(133N, r), content="false") + inspect(@math.is_probable_prime(0N, r), content="false") + inspect(@math.is_probable_prime(1N, r), content="false") + inspect(@math.is_probable_prime(93N, r), content="false") + inspect(@math.is_probable_prime(133N, r), content="false") // big composites inspect( - is_probable_prime( + @math.is_probable_prime( 21284175091214687912771199898307297748211672914763848041968395774954376176754N, r, ), content="false", ) inspect( - is_probable_prime( + @math.is_probable_prime( 6084766654921918907427900243509372380954290099172559290432744450051395395951N, r, ), content="false", ) inspect( - is_probable_prime( + @math.is_probable_prime( 84594350493221918389213352992032324280367711247940675652888030554255915464401N, r, ), content="false", ) // carmichael numbers - inspect(is_probable_prime(561N, r), content="false") - inspect(is_probable_prime(1729N, r), content="false") - inspect(is_probable_prime(41041N, r), content="false") - inspect(is_probable_prime(509033161N, r), content="false") + inspect(@math.is_probable_prime(561N, r), content="false") + inspect(@math.is_probable_prime(1729N, r), content="false") + inspect(@math.is_probable_prime(41041N, r), content="false") + inspect(@math.is_probable_prime(509033161N, r), content="false") } ///| -test "probable_prime" { +test "@math.probable_prime" { let rand = @random.Rand::new() - inspect(is_probable_prime(probable_prime(32, rand), rand), content="true") - inspect(is_probable_prime(probable_prime(64, rand), rand), content="true") - inspect(is_probable_prime(probable_prime(100, rand), rand), content="true") - inspect(is_probable_prime(probable_prime(256, rand), rand), content="true") + inspect( + @math.is_probable_prime(@math.probable_prime(32, rand), rand), + content="true", + ) + inspect( + @math.is_probable_prime(@math.probable_prime(64, rand), rand), + content="true", + ) + inspect( + @math.is_probable_prime(@math.probable_prime(100, rand), rand), + content="true", + ) + inspect( + @math.is_probable_prime(@math.probable_prime(256, rand), rand), + content="true", + ) } diff --git a/bundled-core/math/round.mbt b/bundled-core/math/round.mbt index 0dabfc7..f01ef14 100644 --- a/bundled-core/math/round.mbt +++ b/bundled-core/math/round.mbt @@ -13,4 +13,4 @@ // limitations under the License. ///| -pub fnalias Double::(round, ceil, floor, trunc) +pub fnalias @double.(round, ceil, floor, trunc) diff --git a/bundled-core/math/scalbn.mbt b/bundled-core/math/scalbn.mbt index 58ffe70..7d826f7 100644 --- a/bundled-core/math/scalbn.mbt +++ b/bundled-core/math/scalbn.mbt @@ -58,7 +58,7 @@ pub fn scalbn(x : Double, exp : Int) -> Double { return y * ui.reinterpret_as_double() } -///| +///| /// Calculcates x * 2 **n where x is a single-precision floating number and n is an integer. /// /// Parameters: @@ -71,15 +71,15 @@ pub fn scalbn(x : Double, exp : Int) -> Double { /// Example: /// /// ```moonbit -/// inspect(scalbnf(1.5, 2), content="6") -/// inspect(scalbnf(2, -1), content="1") -/// inspect(scalbnf(3, 0), content="3") -/// inspect(scalbnf(1, 128), content="Infinity") -/// inspect(scalbnf(1, -150), content="0") -/// inspect(scalbnf(1, 254), content="Infinity") -/// inspect(scalbnf(@float.not_a_number, 1), content="NaN") -/// inspect(scalbnf(-2, 1), content="-4") -/// inspect(scalbnf(-2, 128), content="-Infinity") +/// inspect(@math.scalbnf(1.5, 2), content="6") +/// inspect(@math.scalbnf(2, -1), content="1") +/// inspect(@math.scalbnf(3, 0), content="3") +/// inspect(@math.scalbnf(1, 128), content="Infinity") +/// inspect(@math.scalbnf(1, -150), content="0") +/// inspect(@math.scalbnf(1, 254), content="Infinity") +/// inspect(@math.scalbnf(@float.not_a_number, 1), content="NaN") +/// inspect(@math.scalbnf(-2, 1), content="-4") +/// inspect(@math.scalbnf(-2, 128), content="-Infinity") /// ``` pub fn scalbnf(y : Float, exp : Int) -> Float { let mut y = y diff --git a/bundled-core/math/trig.mbt b/bundled-core/math/trig.mbt index 8b975da..4c281ba 100644 --- a/bundled-core/math/trig.mbt +++ b/bundled-core/math/trig.mbt @@ -105,7 +105,7 @@ fn trig_reduce(x : Float, switch_over : Float) -> (Float, Int) { ((exp + 128) << 23).reinterpret_as_uint() + (phi >> 8) + (if (phi & 0xff) > 0x7e { 1 } else { 0 }) - if not(xispos) { + if !xispos { r = r ^ 0x8000_0000 q = -q } @@ -178,12 +178,12 @@ fn tanf_poly(x : Float, odd : Bool) -> Float { /// Example: /// /// ```moonbit -/// inspect(sinf(0), content="0") -/// inspect(sinf(2), content="0.9092974066734314") -/// inspect(sinf(-5), content="0.9589242935180664") -/// inspect(sinf(@float.not_a_number), content="NaN") -/// inspect(sinf(@float.infinity), content="NaN") -/// inspect(sinf(@float.neg_infinity), content="NaN") +/// inspect(@math.sinf(0), content="0") +/// inspect(@math.sinf(2), content="0.9092974066734314") +/// inspect(@math.sinf(-5), content="0.9589242935180664") +/// inspect(@math.sinf(@float.not_a_number), content="NaN") +/// inspect(@math.sinf(@float.infinity), content="NaN") +/// inspect(@math.sinf(@float.neg_infinity), content="NaN") /// ``` pub fn sinf(x : Float) -> Float { if x.is_nan() || x.is_inf() { @@ -208,12 +208,12 @@ pub fn sinf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(cosf(0), content="1") -/// inspect(cosf(2), content="-0.41614681482315063") -/// inspect(cosf(-5), content="0.28366217017173767") -/// inspect(cosf(@float.not_a_number), content="NaN") -/// inspect(cosf(@float.infinity), content="NaN") -/// inspect(cosf(@float.neg_infinity), content="NaN") +/// inspect(@math.cosf(0), content="1") +/// inspect(@math.cosf(2), content="-0.41614681482315063") +/// inspect(@math.cosf(-5), content="0.28366217017173767") +/// inspect(@math.cosf(@float.not_a_number), content="NaN") +/// inspect(@math.cosf(@float.infinity), content="NaN") +/// inspect(@math.cosf(@float.neg_infinity), content="NaN") /// ``` pub fn cosf(x : Float) -> Float { if x.is_nan() || x.is_inf() { @@ -238,12 +238,12 @@ pub fn cosf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(tanf(0), content="0") -/// inspect(tanf(2), content="-2.18503999710083") -/// inspect(tanf(-5), content="3.3805150985717773") -/// inspect(tanf(@float.not_a_number), content="NaN") -/// inspect(tanf(@float.infinity), content="NaN") -/// inspect(tanf(@float.neg_infinity), content="NaN") +/// inspect(@math.tanf(0), content="0") +/// inspect(@math.tanf(2), content="-2.18503999710083") +/// inspect(@math.tanf(-5), content="3.3805150985717773") +/// inspect(@math.tanf(@float.not_a_number), content="NaN") +/// inspect(@math.tanf(@float.infinity), content="NaN") +/// inspect(@math.tanf(@float.neg_infinity), content="NaN") /// ``` pub fn tanf(x : Float) -> Float { if x.is_nan() || x.is_inf() { @@ -271,12 +271,12 @@ pub fn tanf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(asinf(0), content="0") -/// inspect(asinf(1), content="1.5707963705062866") -/// inspect(asinf(-1), content="-1.5707963705062866") -/// inspect(asinf(@float.not_a_number), content="NaN") -/// inspect(asinf(@float.infinity), content="NaN") -/// inspect(asinf(@float.neg_infinity), content="NaN") +/// inspect(@math.asinf(0), content="0") +/// inspect(@math.asinf(1), content="1.5707963705062866") +/// inspect(@math.asinf(-1), content="-1.5707963705062866") +/// inspect(@math.asinf(@float.not_a_number), content="NaN") +/// inspect(@math.asinf(@float.infinity), content="NaN") +/// inspect(@math.asinf(@float.neg_infinity), content="NaN") /// ``` pub fn asinf(x : Float) -> Float { let x1p120 = 0x3870000000000000UL.reinterpret_as_double() @@ -332,12 +332,12 @@ pub fn asinf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(acosf(0), content="1.570796251296997") -/// inspect(acosf(1), content="0") -/// inspect(acosf(-1), content="3.141592502593994") -/// inspect(acosf(@float.not_a_number), content="NaN") -/// inspect(acosf(@float.infinity), content="NaN") -/// inspect(acosf(@float.neg_infinity), content="NaN") +/// inspect(@math.acosf(0), content="1.570796251296997") +/// inspect(@math.acosf(1), content="0") +/// inspect(@math.acosf(-1), content="3.141592502593994") +/// inspect(@math.acosf(@float.not_a_number), content="NaN") +/// inspect(@math.acosf(@float.infinity), content="NaN") +/// inspect(@math.acosf(@float.neg_infinity), content="NaN") /// ``` pub fn acosf(x : Float) -> Float { let pio2_hi : Float = 1.5707962513 @@ -399,12 +399,12 @@ pub fn acosf(x : Float) -> Float { /// * Returns NaN if the input is NaN. /// /// ```moonbit -/// inspect(atanf(0), content="0") -/// inspect(atanf(1), content="0.7853981852531433") -/// inspect(atanf(-1), content="-0.7853981852531433") -/// inspect(atanf(@float.not_a_number), content="NaN") -/// inspect(atanf(@float.infinity), content="1.570796251296997") -/// inspect(atanf(@float.neg_infinity), content="-1.570796251296997") +/// inspect(@math.atanf(0), content="0") +/// inspect(@math.atanf(1), content="0.7853981852531433") +/// inspect(@math.atanf(-1), content="-0.7853981852531433") +/// inspect(@math.atanf(@float.not_a_number), content="NaN") +/// inspect(@math.atanf(@float.infinity), content="1.570796251296997") +/// inspect(@math.atanf(@float.neg_infinity), content="-1.570796251296997") /// ``` pub fn atanf(x : Float) -> Float { let atanhi : Array[Float] = [ @@ -484,19 +484,19 @@ pub fn atanf(x : Float) -> Float { /// Example: /// /// ```moonbit -/// inspect(atan2f(0.0, -1.0), content="3.1415927410125732") -/// inspect(atan2f(1.0, 0.0), content="1.5707963705062866") -/// inspect(atan2f(1.0, 1.0), content="0.7853981852531433") -/// inspect(atan2f(@float.not_a_number, 1.0), content="NaN") -/// inspect(atan2f(1.0, @float.not_a_number), content="NaN") -/// inspect(atan2f(@float.infinity, 1.0), content="1.570796251296997") -/// inspect(atan2f(1.0, @float.infinity), content="0") -/// inspect(atan2f(@float.neg_infinity, 1.0), content="-1.570796251296997") -/// inspect(atan2f(1.0, @float.neg_infinity), content="3.1415927410125732") -/// inspect(atan2f(@float.infinity, @float.infinity), content="0.7853981852531433") -/// inspect(atan2f(@float.neg_infinity, @float.neg_infinity), content="-2.356194496154785") -/// inspect(atan2f(@float.infinity, @float.neg_infinity), content="2.356194496154785") -/// inspect(atan2f(@float.neg_infinity, @float.infinity), content="-0.7853981852531433") +/// inspect(@math.atan2f(0.0, -1.0), content="3.1415927410125732") +/// inspect(@math.atan2f(1.0, 0.0), content="1.5707963705062866") +/// inspect(@math.atan2f(1.0, 1.0), content="0.7853981852531433") +/// inspect(@math.atan2f(@float.not_a_number, 1.0), content="NaN") +/// inspect(@math.atan2f(1.0, @float.not_a_number), content="NaN") +/// inspect(@math.atan2f(@float.infinity, 1.0), content="1.570796251296997") +/// inspect(@math.atan2f(1.0, @float.infinity), content="0") +/// inspect(@math.atan2f(@float.neg_infinity, 1.0), content="-1.570796251296997") +/// inspect(@math.atan2f(1.0, @float.neg_infinity), content="3.1415927410125732") +/// inspect(@math.atan2f(@float.infinity, @float.infinity), content="0.7853981852531433") +/// inspect(@math.atan2f(@float.neg_infinity, @float.neg_infinity), content="-2.356194496154785") +/// inspect(@math.atan2f(@float.infinity, @float.neg_infinity), content="2.356194496154785") +/// inspect(@math.atan2f(@float.neg_infinity, @float.infinity), content="-0.7853981852531433") /// ``` pub fn atan2f(y : Float, x : Float) -> Float { if x.is_nan() || y.is_nan() { diff --git a/bundled-core/math/trig_double_nonjs.mbt b/bundled-core/math/trig_double_nonjs.mbt index 3f38000..26aaef3 100644 --- a/bundled-core/math/trig_double_nonjs.mbt +++ b/bundled-core/math/trig_double_nonjs.mbt @@ -12,88 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -/// Calculates the sine of a number in radians. Handles special cases and edge -/// conditions according to IEEE 754 standards. -/// -/// Parameters: -/// -/// * `x` : The angle in radians for which to calculate the sine. -/// -/// Returns the sine of the angle `x`. -/// -/// Example: -/// -/// ```moonbit -/// inspect(@math.sin(0.0), content="0") -/// inspect(@math.sin(1.570796326794897), content="1") // pi / 2 -/// inspect(@math.sin(2.0), content="0.9092974268256817") -/// inspect(@math.sin(-5.0), content="0.9589242746631385") -/// inspect(@math.sin(31415926535897.9323846), content="0.0012091232715481885") -/// inspect(@math.sin(@double.not_a_number), content="NaN") -/// inspect(@math.sin(@double.infinity), content="NaN") -/// inspect(@math.sin(@double.neg_infinity), content="NaN") -/// ``` -pub fn sin(x : Double) -> Double { - if x.is_inf() || x.is_nan() { - return @double.not_a_number - } - let y = [0.0, 0.0] - let z = 0.0 - if x.abs() <= PI_OVER_4 { - return __kernel_sin(x, z, 0) - } else { - let n = rem_pio2(x, y) - match n & 3 { - 0 => __kernel_sin(y[0], y[1], 1) - 1 => __kernel_cos(y[0], y[1]) - 2 => -__kernel_sin(y[0], y[1], 1) - _ => -__kernel_cos(y[0], y[1]) - } - } -} - -///| -/// Calculates the cosine of a number in radians. Handles special cases and edge -/// conditions according to IEEE 754 standards. -/// -/// Parameters: -/// -/// * `x` : The angle in radians for which to calculate the cosine. -/// -/// Returns the cosine of the angle `x`. -/// -/// Example: -/// -/// ```moonbit -/// inspect(@math.cos(0.0), content="1") -/// inspect(@math.cos(2.5), content="-0.8011436155469337") -/// inspect(@math.cos(-3.141592653589793), content="-1") // -pi -/// inspect(@math.cos(-5.0), content="0.28366218546322625") -/// inspect(@math.cos(31415926535897.9323846), content="0.9999992690101899") -/// inspect(@math.cos(@double.not_a_number), content="NaN") -/// inspect(@math.cos(@double.infinity), content="NaN") -/// inspect(@math.cos(@double.neg_infinity), content="NaN") -/// ``` -pub fn cos(x : Double) -> Double { - if x.is_inf() || x.is_nan() { - return @double.not_a_number - } - let y = [0.0, 0.0] - let z = 0.0 - if x.abs() <= PI_OVER_4 { - return __kernel_cos(x, z) - } else { - let n = rem_pio2(x, y) - match n & 3 { - 0 => __kernel_cos(y[0], y[1]) - 1 => -__kernel_sin(y[0], y[1], 1) - 2 => -__kernel_cos(y[0], y[1]) - _ => __kernel_sin(y[0], y[1], 1) - } - } -} - ///| /// Calculates the tangent of a number in radians. Handles special cases and edge /// conditions according to IEEE 754 standards. @@ -604,7 +522,7 @@ fn __kernel_rem_pio2( y : Array[Double], e0 : Int, nx : Int, - prec : Int + prec : Int, ) -> Int { let init_jk = [2, 3, 4, 6] let two24 : Double = 16777216.0 // 0x41700000, 0x00000000 diff --git a/bundled-core/math/trig_double_sincos_native.mbt b/bundled-core/math/trig_double_sincos_native.mbt new file mode 100644 index 0000000..a84bd0b --- /dev/null +++ b/bundled-core/math/trig_double_sincos_native.mbt @@ -0,0 +1,61 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// Calculates the sine of a number in radians. Handles special cases and edge +/// conditions according to IEEE 754 standards. +/// +/// Parameters: +/// +/// * `x` : The angle in radians for which to calculate the sine. +/// +/// Returns the sine of the angle `x`. +/// +/// Example: +/// +/// ```moonbit +/// inspect(@math.sin(0.0), content="0") +/// inspect(@math.sin(1.570796326794897), content="1") // pi / 2 +/// inspect(@math.sin(2.0), content="0.9092974268256817") +/// inspect(@math.sin(-5.0), content="0.9589242746631385") +/// inspect(@math.sin(31415926535897.9323846), content="0.0012091232715481885") +/// inspect(@math.sin(@double.not_a_number), content="NaN") +/// inspect(@math.sin(@double.infinity), content="NaN") +/// inspect(@math.sin(@double.neg_infinity), content="NaN") +/// ``` +pub extern "C" fn sin(x : Double) -> Double = "sin" + +///| +/// Calculates the cosine of a number in radians. Handles special cases and edge +/// conditions according to IEEE 754 standards. +/// +/// Parameters: +/// +/// * `x` : The angle in radians for which to calculate the cosine. +/// +/// Returns the cosine of the angle `x`. +/// +/// Example: +/// +/// ```moonbit +/// inspect(@math.cos(0.0), content="1") +/// inspect(@math.cos(2.5), content="-0.8011436155469337") +/// inspect(@math.cos(-3.141592653589793), content="-1") // -pi +/// inspect(@math.cos(-5.0), content="0.28366218546322625") +/// inspect(@math.cos(31415926535897.9323846), content="0.9999992690101899") +/// inspect(@math.cos(@double.not_a_number), content="NaN") +/// inspect(@math.cos(@double.infinity), content="NaN") +/// inspect(@math.cos(@double.neg_infinity), content="NaN") +/// ``` +pub extern "C" fn cos(x : Double) -> Double = "cos" diff --git a/bundled-core/math/trig_double_sincos_non_native_or_js.mbt b/bundled-core/math/trig_double_sincos_non_native_or_js.mbt new file mode 100644 index 0000000..4377dbe --- /dev/null +++ b/bundled-core/math/trig_double_sincos_non_native_or_js.mbt @@ -0,0 +1,95 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +/// Calculates the sine of a number in radians. Handles special cases and edge +/// conditions according to IEEE 754 standards. +/// +/// Parameters: +/// +/// * `x` : The angle in radians for which to calculate the sine. +/// +/// Returns the sine of the angle `x`. +/// +/// Example: +/// +/// ```moonbit +/// inspect(@math.sin(0.0), content="0") +/// inspect(@math.sin(1.570796326794897), content="1") // pi / 2 +/// inspect(@math.sin(2.0), content="0.9092974268256817") +/// inspect(@math.sin(-5.0), content="0.9589242746631385") +/// inspect(@math.sin(31415926535897.9323846), content="0.0012091232715481885") +/// inspect(@math.sin(@double.not_a_number), content="NaN") +/// inspect(@math.sin(@double.infinity), content="NaN") +/// inspect(@math.sin(@double.neg_infinity), content="NaN") +/// ``` +pub fn sin(x : Double) -> Double { + if x.is_inf() || x.is_nan() { + return @double.not_a_number + } + let y = [0.0, 0.0] + let z = 0.0 + if x.abs() <= PI_OVER_4 { + return __kernel_sin(x, z, 0) + } else { + let n = rem_pio2(x, y) + match n & 3 { + 0 => __kernel_sin(y[0], y[1], 1) + 1 => __kernel_cos(y[0], y[1]) + 2 => -__kernel_sin(y[0], y[1], 1) + _ => -__kernel_cos(y[0], y[1]) + } + } +} + +///| +/// Calculates the cosine of a number in radians. Handles special cases and edge +/// conditions according to IEEE 754 standards. +/// +/// Parameters: +/// +/// * `x` : The angle in radians for which to calculate the cosine. +/// +/// Returns the cosine of the angle `x`. +/// +/// Example: +/// +/// ```moonbit +/// inspect(@math.cos(0.0), content="1") +/// inspect(@math.cos(2.5), content="-0.8011436155469337") +/// inspect(@math.cos(-3.141592653589793), content="-1") // -pi +/// inspect(@math.cos(-5.0), content="0.28366218546322625") +/// inspect(@math.cos(31415926535897.9323846), content="0.9999992690101899") +/// inspect(@math.cos(@double.not_a_number), content="NaN") +/// inspect(@math.cos(@double.infinity), content="NaN") +/// inspect(@math.cos(@double.neg_infinity), content="NaN") +/// ``` +pub fn cos(x : Double) -> Double { + if x.is_inf() || x.is_nan() { + return @double.not_a_number + } + let y = [0.0, 0.0] + let z = 0.0 + if x.abs() <= PI_OVER_4 { + return __kernel_cos(x, z) + } else { + let n = rem_pio2(x, y) + match n & 3 { + 0 => __kernel_cos(y[0], y[1]) + 1 => -__kernel_sin(y[0], y[1], 1) + 2 => -__kernel_cos(y[0], y[1]) + _ => __kernel_sin(y[0], y[1], 1) + } + } +} diff --git a/bundled-core/math/utils.mbt b/bundled-core/math/utils.mbt index 64f99d2..4222ed4 100644 --- a/bundled-core/math/utils.mbt +++ b/bundled-core/math/utils.mbt @@ -22,7 +22,7 @@ fn abs(x : Double) -> Double { } ///| -let two_over_pi : Array[Int] = [ +let two_over_pi : FixedArray[Int] = [ 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, @@ -35,7 +35,7 @@ let two_over_pi : Array[Int] = [ ] ///| -let pi_over_2 : Array[Double] = [ +let pi_over_2 : FixedArray[Double] = [ 1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000 */ 7.54978941586159635335e-08, // 0x3E74442D, 0x00000000 */ 5.39030252995776476554e-15, // 0x3CF84698, 0x80000000 */ @@ -47,7 +47,7 @@ let pi_over_2 : Array[Double] = [ ] ///| -let npio2_hw : Array[Int] = [ +let npio2_hw : FixedArray[Int] = [ 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, 0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A, 0x403DD85A, 0x403F6A7A, 0x40407E4C, diff --git a/bundled-core/moon.mod.json b/bundled-core/moon.mod.json index 6dd473a..edd740d 100644 --- a/bundled-core/moon.mod.json +++ b/bundled-core/moon.mod.json @@ -4,5 +4,6 @@ "readme": "README.md", "repository": "https://github.com/moonbitlang/core", "license": "Apache-2.0", - "keywords": ["core","standard library"] + "keywords": ["core","standard library"], + "alert-list": "+test_import_all" } diff --git a/bundled-core/option/deprecated.mbt b/bundled-core/option/deprecated.mbt index 034dc84..91836e2 100644 --- a/bundled-core/option/deprecated.mbt +++ b/bundled-core/option/deprecated.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -#deprecated("inline instead for such tiny function") +#deprecated("Use `if condition { Some(value()) } else { None }` instead") pub fn[T] when(condition : Bool, value : () -> T) -> T? { if condition { Some(value()) @@ -23,19 +23,19 @@ pub fn[T] when(condition : Bool, value : () -> T) -> T? { } ///| -#deprecated("inline instead for such tiny function") +#deprecated("Use `if !condition { Some(value()) } else { None }` instead") pub fn[T] unless(condition : Bool, value : () -> T) -> T? { - when(not(condition), value) + when(!condition, value) } ///| -#deprecated("inline instead for such tiny function") +#deprecated("Use `None` instead") pub fn[T] empty() -> T? { None } ///| -#deprecated("inline instead for such tiny function") +#deprecated("Use `Some(value)` instead") pub fn[T] some(value : T) -> T? { Some(value) } @@ -66,3 +66,17 @@ pub fn[T : Default] Option::or_default(self : T?) -> T { Some(t) => t } } + +///| +/// Checks if the option contains a value. +#deprecated("use `x is Some(_)` instead") +pub fn[T] is_some(self : T?) -> Bool { + self is Some(_) +} + +///| +/// Checks if the option is None. +#deprecated("use `x is None` instead") +pub fn[T] is_none(self : T?) -> Bool { + self is None +} diff --git a/bundled-core/option/option.mbt b/bundled-core/option/option.mbt index 21804c1..a89970e 100644 --- a/bundled-core/option/option.mbt +++ b/bundled-core/option/option.mbt @@ -31,7 +31,7 @@ test "some equals to Some" { /// let b = None /// assert_eq(b.map(x => x * 2), None) /// ``` -pub fn[T, U] map(self : T?, f : (T) -> U) -> U? { +pub fn[T, U] map(self : T?, f : (T) -> U raise?) -> U? raise? { match self { Some(t) => Some(f(t)) None => None @@ -56,7 +56,7 @@ test "map" { /// let a = Some(5) /// assert_eq(a.map_or(3, x => x * 2), 10) /// ``` -pub fn[T, U] map_or(self : T?, default : U, f : (T) -> U) -> U { +pub fn[T, U] map_or(self : T?, default : U, f : (T) -> U raise?) -> U raise? { match self { None => default Some(x) => f(x) @@ -80,7 +80,11 @@ test "map_or" { /// let a = Some(5) /// assert_eq(a.map_or_else(() => 3, x => x * 2), 10) /// ``` -pub fn[T, U] map_or_else(self : T?, default : () -> U, f : (T) -> U) -> U { +pub fn[T, U] map_or_else( + self : T?, + default : () -> U raise?, + f : (T) -> U raise?, +) -> U raise? { match self { None => default() Some(x) => f(x) @@ -109,7 +113,7 @@ test "map_or_else" { /// let r2 = b.bind(x => Some(x * 2)) /// assert_eq(r2, None) /// ``` -pub fn[T, U] bind(self : T?, f : (T) -> U?) -> U? { +pub fn[T, U] bind(self : T?, f : (T) -> U? raise?) -> U? raise? { match self { Some(t) => f(t) None => None @@ -178,7 +182,7 @@ test "is_empty" { /// assert_eq(x.filter(x => x > 5), None) /// assert_eq(x.filter(x => x < 5), Some(3)) /// ``` -pub fn[T] filter(self : T?, f : (T) -> Bool) -> T? { +pub fn[T] filter(self : T?, f : (T) -> Bool raise?) -> T? raise? { match self { Some(t) => if f(t) { self } else { None } None => None @@ -212,7 +216,10 @@ test "unwrap_or" { /// Return the contained `Some` value or the provided default. /// /// Default is lazily evaluated -pub fn[T] Option::unwrap_or_else(self : T?, default : () -> T) -> T { +pub fn[T] Option::unwrap_or_else( + self : T?, + default : () -> T raise?, +) -> T raise? { match self { None => default() Some(t) => t @@ -291,10 +298,10 @@ test "iter" { }) inspect( exb, - content= + content=( #|42 #| - , + ), ) exb.reset() let y : Int? = None @@ -334,7 +341,7 @@ test "or error" { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for X? with arbitrary( i, - rs + rs, ) { if rs.next_double() < 0.3 { None diff --git a/bundled-core/option/option_test.mbt b/bundled-core/option/option_test.mbt index 2648140..872d1c5 100644 --- a/bundled-core/option/option_test.mbt +++ b/bundled-core/option/option_test.mbt @@ -50,3 +50,37 @@ test { inspect(test_option_arg(y=2), content="5") inspect(test_option_arg(x=1, y=2), content="3") } + +///| +test "filter with predicate" { + let some_even = Some(4) + let some_odd = Some(3) + let none : Int? = None + assert_eq(some_even.filter(x => x % 2 == 0), Some(4)) + assert_eq(some_odd.filter(x => x % 2 == 0), None) + assert_eq(none.filter(x => x % 2 == 0), None) +} + +///| +test "bind chaining" { + let safe_divide = (x : Int) => if x == 0 { None } else { Some(100 / x) } + let some_value = Some(4) + let result = some_value.bind(x => safe_divide(x)) + assert_eq(result, Some(25)) + let zero_value = Some(0) + let result2 = zero_value.bind(x => safe_divide(x)) + assert_eq(result2, None) + let none_value : Int? = None + let result3 = none_value.bind(x => safe_divide(x)) + assert_eq(result3, None) +} + +///| +test "unwrap_or with default value" { + let x = Some(2) + let y : Int? = None + assert_eq(x.unwrap_or(100), 2) + assert_eq(y.unwrap_or(100), 100) + let none1 : Int? = None + assert_eq(none1.unwrap_or(42), 42) +} diff --git a/bundled-core/option/option.mbti b/bundled-core/option/pkg.generated.mbti similarity index 61% rename from bundled-core/option/option.mbti rename to bundled-core/option/pkg.generated.mbti index 3441ba4..67d7029 100644 --- a/bundled-core/option/option.mbti +++ b/bundled-core/option/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/option" import( @@ -17,15 +18,21 @@ fn[T] unless(Bool, () -> T) -> T? #deprecated fn[T] when(Bool, () -> T) -> T? +// Errors + // Types and methods -fn[T, U] Option::bind(T?, (T) -> U?) -> U? -fn[T] Option::filter(T?, (T) -> Bool) -> T? +fn[T, U] Option::bind(T?, (T) -> U? raise?) -> U? raise? +fn[T] Option::filter(T?, (T) -> Bool raise?) -> T? raise? fn[T] Option::flatten(T??) -> T? fn[T] Option::is_empty(T?) -> Bool +#deprecated +fn[T] Option::is_none(T?) -> Bool +#deprecated +fn[T] Option::is_some(T?) -> Bool fn[T] Option::iter(T?) -> Iter[T] -fn[T, U] Option::map(T?, (T) -> U) -> U? -fn[T, U] Option::map_or(T?, U, (T) -> U) -> U -fn[T, U] Option::map_or_else(T?, () -> U, (T) -> U) -> U +fn[T, U] Option::map(T?, (T) -> U raise?) -> U? raise? +fn[T, U] Option::map_or(T?, U, (T) -> U raise?) -> U raise? +fn[T, U] Option::map_or_else(T?, () -> U raise?, (T) -> U raise?) -> U raise? #deprecated fn[T] Option::or(T?, T) -> T #deprecated @@ -35,7 +42,7 @@ fn[T] Option::or_else(T?, () -> T) -> T fn[T, Err : Error] Option::or_error(T?, Err) -> T raise Err fn[T] Option::unwrap_or(T?, T) -> T fn[T : Default] Option::unwrap_or_default(T?) -> T -fn[T] Option::unwrap_or_else(T?, () -> T) -> T +fn[T] Option::unwrap_or_else(T?, () -> T raise?) -> T raise? impl[X : Compare] Compare for X? impl[X] Default for X? impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for X? diff --git a/bundled-core/prelude/moon.pkg.json b/bundled-core/prelude/moon.pkg.json index e5203a5..dd00d82 100644 --- a/bundled-core/prelude/moon.pkg.json +++ b/bundled-core/prelude/moon.pkg.json @@ -4,5 +4,6 @@ "moonbitlang/core/bigint", "moonbitlang/core/set", "moonbitlang/core/array" - ] + ], + "alert-list": "-test_import_all" } diff --git a/bundled-core/prelude/prelude.mbti b/bundled-core/prelude/pkg.generated.mbti similarity index 72% rename from bundled-core/prelude/prelude.mbti rename to bundled-core/prelude/pkg.generated.mbti index 87a7723..8bcef8e 100644 --- a/bundled-core/prelude/prelude.mbti +++ b/bundled-core/prelude/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/prelude" import( @@ -7,25 +8,34 @@ import( // Values fn[T] abort(String) -> T -fn[T : @builtin.Eq + @builtin.Show] assert_eq(T, T, msg? : String, loc~ : @builtin.SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn[T : @builtin.Eq + @builtin.Show] assert_eq(T, T, msg? : String, loc~ : @builtin.SourceLoc) -> Unit raise -fn assert_false(Bool, msg? : String, loc~ : @builtin.SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn assert_false(Bool, msg? : String, loc~ : @builtin.SourceLoc) -> Unit raise -fn[T : @builtin.Eq + @builtin.Show] assert_not_eq(T, T, msg? : String, loc~ : @builtin.SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn[T : @builtin.Eq + @builtin.Show] assert_not_eq(T, T, msg? : String, loc~ : @builtin.SourceLoc) -> Unit raise -fn assert_true(Bool, msg? : String, loc~ : @builtin.SourceLoc = _) -> Unit raise +#callsite(autofill(loc)) +fn assert_true(Bool, msg? : String, loc~ : @builtin.SourceLoc) -> Unit raise #deprecated -fn[T] dump(T, name? : String, loc~ : @builtin.SourceLoc = _) -> T +#callsite(autofill(loc)) +fn[T] dump(T, name? : String, loc~ : @builtin.SourceLoc) -> T -fn[T] fail(String, loc~ : @builtin.SourceLoc = _) -> T raise @builtin.Failure +#callsite(autofill(loc)) +fn[T] fail(String, loc~ : @builtin.SourceLoc) -> T raise @builtin.Failure fn[T] ignore(T) -> Unit -fn inspect(&@builtin.Show, content~ : String = .., loc~ : @builtin.SourceLoc = _, args_loc~ : @builtin.ArgsLoc = _) -> Unit raise @builtin.InspectError +#callsite(autofill(args_loc, loc)) +fn inspect(&@builtin.Show, content? : String, loc~ : @builtin.SourceLoc, args_loc~ : @builtin.ArgsLoc) -> Unit raise @builtin.InspectError fn not(Bool) -> Bool +let null : @builtin.Json + fn[T] panic() -> T fn[T] physical_equal(T, T) -> Bool @@ -38,6 +48,8 @@ fn[T] tap(T, (T) -> Unit) -> T fn[T, R] then(T, (T) -> R) -> R +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/prelude/prelude.mbt b/bundled-core/prelude/prelude.mbt index b828474..34ef898 100644 --- a/bundled-core/prelude/prelude.mbt +++ b/bundled-core/prelude/prelude.mbt @@ -76,7 +76,7 @@ pub fnalias @builtin.( dump ) -///| +///| /// Applies a function to a value and returns the original value. /// /// # Parameters @@ -90,7 +90,7 @@ pub fnalias @builtin.( /// ```mbt /// let val : Ref[Int] = { val : 1 } /// let x : Int = 5 -/// assert_eq(x |> tap((n) => { val.val += n }), 5) +/// assert_eq(x |> tap(n => val.val += n), 5) /// // This is not allowed /// // x |> (val.val += _) /// assert_eq(val.val, 6) @@ -100,7 +100,7 @@ pub fn[T] tap(value : T, f : (T) -> Unit) -> T { value } -///| +///| /// Applies a function to a value and returns the result of the function. /// /// # Parameters @@ -113,7 +113,7 @@ pub fn[T] tap(value : T, f : (T) -> Unit) -> T { /// # Examples /// ```moonbit /// let x = 5 -/// let result = x |> then((n) => { n * 2 }) +/// let result = x |> then(n => n * 2) /// // This is not allowed /// // x |> (_ * 2) /// assert_eq(result, 10) @@ -121,3 +121,6 @@ pub fn[T] tap(value : T, f : (T) -> Unit) -> T { pub fn[T, R] then(value : T, f : (T) -> R) -> R { f(value) } + +///| +pub let null : Json = @builtin.null diff --git a/bundled-core/priority_queue/priority_queue.mbti b/bundled-core/priority_queue/pkg.generated.mbti similarity index 72% rename from bundled-core/priority_queue/priority_queue.mbti rename to bundled-core/priority_queue/pkg.generated.mbti index 4d42942..b3177f9 100644 --- a/bundled-core/priority_queue/priority_queue.mbti +++ b/bundled-core/priority_queue/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/priority_queue" import( @@ -5,21 +6,24 @@ import( ) // Values -fn[A : Compare] from_array(Array[A]) -> T[A] -fn[K : Compare] from_iter(Iter[K]) -> T[K] - -fn[A] new() -> T[A] - -fn[A : Compare] of(FixedArray[A]) -> T[A] +// Errors // Types and methods type T[A] fn[A] T::clear(Self[A]) -> Unit fn[A] T::copy(Self[A]) -> Self[A] +#as_free_fn +fn[A : Compare] T::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[K : Compare] T::from_iter(Iter[K]) -> Self[K] fn[A] T::is_empty(Self[A]) -> Bool fn[A : Compare] T::iter(Self[A]) -> Iter[A] fn[A] T::length(Self[A]) -> Int +#as_free_fn +fn[A] T::new() -> Self[A] +#as_free_fn +fn[A : Compare] T::of(FixedArray[A]) -> Self[A] fn[A] T::peek(Self[A]) -> A? fn[A : Compare] T::pop(Self[A]) -> A? fn[A : Compare] T::push(Self[A], A) -> Unit diff --git a/bundled-core/priority_queue/priority_queue.mbt b/bundled-core/priority_queue/priority_queue.mbt index 643390e..fd46f5a 100644 --- a/bundled-core/priority_queue/priority_queue.mbt +++ b/bundled-core/priority_queue/priority_queue.mbt @@ -20,7 +20,8 @@ /// let queue : @priority_queue.T[Int] = @priority_queue.new() /// assert_eq(queue.length(), 0) /// ``` -pub fn[A] new() -> T[A] { +#as_free_fn +pub fn[A] T::new() -> T[A] { { len: 0, top: Nil } } @@ -32,7 +33,8 @@ pub fn[A] new() -> T[A] { /// let queue = @priority_queue.of([1, 2, 3, 4, 5]) /// assert_eq(queue.length(), 5) /// ``` -pub fn[A : Compare] from_array(arr : Array[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] T::from_array(arr : Array[A]) -> T[A] { // CR: bad formatting let len = arr.length() for i = 0, acc = Node::Nil { @@ -74,19 +76,19 @@ pub fn[A] copy(self : T[A]) -> T[A] { ///| pub fn[A : Compare] to_array(self : T[A]) -> Array[A] { let arr = Array::new(capacity=self.len) - fn go(x : Node[A]) { - match x { - Cons(_) as x => { - arr.push(x.content) - go(x.sibling) - go(x.child) - } + let stack : Array[Node[A]] = [self.top] + while stack.pop() is Some(node) { + match node { Nil => () + Cons(content~, sibling~, child~) => { + arr.push(content) + stack.push(sibling) + stack.push(child) + } } } - - go(self.top) - arr.sort_by((x, y) => y.compare(x)) + arr.sort() + arr.rev_inplace() arr } @@ -95,7 +97,7 @@ pub fn[A : Compare] iter(self : T[A]) -> Iter[A] { Iter::new(yield_ => { let arr = self.to_array() for i in 0.. T[K] { +#as_free_fn +pub fn[K : Compare] T::from_iter(iter : Iter[K]) -> T[K] { let s = new() iter.each(e => s.push(e)) s @@ -253,7 +256,8 @@ pub fn[A] is_empty(self : T[A]) -> Bool { } ///| -pub fn[A : Compare] of(arr : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A : Compare] T::of(arr : FixedArray[A]) -> T[A] { // CR: bad formatting let len = arr.length() for i = 0, acc = Node::Nil { @@ -284,7 +288,7 @@ pub impl[A : Show + Compare] Show for T[A] with output(self, logger) { ///| pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] with arbitrary( size, - rs + rs, ) { let len : Int = if size == 0 { 0 } else { rs.next_positive_int() % size } for i = 0, acc = Node::Nil { diff --git a/bundled-core/priority_queue/priority_queue_test.mbt b/bundled-core/priority_queue/priority_queue_test.mbt index 405c6ff..a75f3e1 100644 --- a/bundled-core/priority_queue/priority_queue_test.mbt +++ b/bundled-core/priority_queue/priority_queue_test.mbt @@ -173,7 +173,7 @@ test "complex" { } let arr = (0).until(1000, inclusive=true).collect() arr.shuffle_in_place(rand~) - let pq = new() + let pq = @priority_queue.new() for i in 0..=1000 { pq.push(arr[i]) } @@ -187,7 +187,7 @@ test "complex" { ///| test "priority queue large data" { - let pq = new() + let pq = @priority_queue.new() for i in 0..=10000000 { pq.push(-i) } diff --git a/bundled-core/queue/README.mbt.md b/bundled-core/queue/README.mbt.md index 7faad54..c6bf770 100644 --- a/bundled-core/queue/README.mbt.md +++ b/bundled-core/queue/README.mbt.md @@ -8,7 +8,7 @@ Queue is a first in first out (FIFO) data structure, allowing to process their e You can create a queue manually by using the `new` or construct it using the `from_array`. ```moonbit test { - let _queue : @queue.T[Int] = @queue.new() + let _queue : @queue.Queue[Int] = @queue.new() let _queue1 = @queue.of([1,2,3]) } ``` @@ -86,8 +86,8 @@ test { Transfer the elements from one queue to another using the `transfer` method. ```moonbit test { - let dst : @queue.T[Int] = @queue.new() - let src : @queue.T[Int] = @queue.of([5, 6, 7, 8]) + let dst : @queue.Queue[Int] = @queue.new() + let src : @queue.Queue[Int] = @queue.of([5, 6, 7, 8]) src.transfer(dst) } ``` \ No newline at end of file diff --git a/bundled-core/queue/deprecated.mbt b/bundled-core/queue/deprecated.mbt index 1bd94ae..7e96cc7 100644 --- a/bundled-core/queue/deprecated.mbt +++ b/bundled-core/queue/deprecated.mbt @@ -15,13 +15,13 @@ ///| #deprecated("Use `unsafe_peek` instead") #coverage.skip -pub fn[A] peek_exn(self : T[A]) -> A { +pub fn[A] peek_exn(self : Queue[A]) -> A { self.unsafe_peek() } ///| #deprecated("Use `unsafe_pop` instead") #coverage.skip -pub fn[A] pop_exn(self : T[A]) -> A { +pub fn[A] pop_exn(self : Queue[A]) -> A { self.unsafe_pop() } diff --git a/bundled-core/queue/pkg.generated.mbti b/bundled-core/queue/pkg.generated.mbti new file mode 100644 index 0000000..a1eb8f2 --- /dev/null +++ b/bundled-core/queue/pkg.generated.mbti @@ -0,0 +1,47 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/queue" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type Queue[A] +fn[A] Queue::clear(Self[A]) -> Unit +fn[A] Queue::copy(Self[A]) -> Self[A] +fn[A] Queue::each(Self[A], (A) -> Unit) -> Unit +fn[A] Queue::eachi(Self[A], (Int, A) -> Unit) -> Unit +fn[A, B] Queue::fold(Self[A], init~ : B, (B, A) -> B) -> B +#as_free_fn +fn[A] Queue::from_array(Array[A]) -> Self[A] +#as_free_fn +fn[A] Queue::from_iter(Iter[A]) -> Self[A] +fn[A] Queue::is_empty(Self[A]) -> Bool +fn[A] Queue::iter(Self[A]) -> Iter[A] +fn[A] Queue::length(Self[A]) -> Int +#as_free_fn +fn[A] Queue::new() -> Self[A] +#as_free_fn +fn[A] Queue::of(FixedArray[A]) -> Self[A] +fn[A] Queue::peek(Self[A]) -> A? +#deprecated +fn[A] Queue::peek_exn(Self[A]) -> A +fn[A] Queue::pop(Self[A]) -> A? +#deprecated +fn[A] Queue::pop_exn(Self[A]) -> A +fn[A] Queue::push(Self[A], A) -> Unit +fn[A] Queue::transfer(Self[A], Self[A]) -> Unit +fn[A] Queue::unsafe_peek(Self[A]) -> A +fn[A] Queue::unsafe_pop(Self[A]) -> A +impl[A : Show] Show for Queue[A] +impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Queue[X] + +// Type aliases +pub typealias Queue as T + +// Traits + diff --git a/bundled-core/queue/queue.mbt b/bundled-core/queue/queue.mbt index 624b7ad..3109be0 100644 --- a/bundled-core/queue/queue.mbt +++ b/bundled-core/queue/queue.mbt @@ -17,10 +17,11 @@ /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.new() +/// let queue : @queue.Queue[Int] = @queue.new() /// assert_eq(queue.length(), 0) /// ``` -pub fn[A] new() -> T[A] { +#as_free_fn +pub fn[A] Queue::new() -> Queue[A] { { length: 0, first: None, last: None } } @@ -30,10 +31,11 @@ pub fn[A] new() -> T[A] { /// # Example /// ```mbt /// let array = Array::makei(3, (idx) => { idx + 1 }) -/// let queue : @queue.T[Int] = @queue.from_array(array) +/// let queue : @queue.Queue[Int] = @queue.from_array(array) /// assert_eq(queue.length(), 3) /// ``` -pub fn[A] from_array(arr : Array[A]) -> T[A] { +#as_free_fn +pub fn[A] Queue::from_array(arr : Array[A]) -> Queue[A] { guard arr.length() > 0 else { return new() } let length = arr.length() let last = { content: arr[length - 1], next: None } @@ -46,13 +48,13 @@ pub fn[A] from_array(arr : Array[A]) -> T[A] { } ///| -pub impl[A : Show] Show for T[A] with output(self, logger) { +pub impl[A : Show] Show for Queue[A] with output(self, logger) { logger.write_iter(self.iter(), prefix="@queue.of([", suffix="])") } ///| /// Tests if two queues are equal. -impl[A : Eq] Eq for T[A] with op_equal(self, other) { +impl[A : Eq] Eq for Queue[A] with equal(self, other) { self.length == other.length && self.first == other.first } @@ -61,10 +63,10 @@ impl[A : Eq] Eq for T[A] with op_equal(self, other) { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// queue.clear() /// ``` -pub fn[A] clear(self : T[A]) -> Unit { +pub fn[A] clear(self : Queue[A]) -> Unit { self.length = 0 self.first = None self.last = None @@ -75,10 +77,10 @@ pub fn[A] clear(self : T[A]) -> Unit { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.new() +/// let queue : @queue.Queue[Int] = @queue.new() /// assert_eq(queue.length(), 0) /// ``` -pub fn[A] length(self : T[A]) -> Int { +pub fn[A] length(self : Queue[A]) -> Int { self.length } @@ -87,10 +89,10 @@ pub fn[A] length(self : T[A]) -> Int { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.new() +/// let queue : @queue.Queue[Int] = @queue.new() /// assert_true(queue.is_empty()) /// ``` -pub fn[A] is_empty(self : T[A]) -> Bool { +pub fn[A] is_empty(self : Queue[A]) -> Bool { self.length == 0 } @@ -99,10 +101,10 @@ pub fn[A] is_empty(self : T[A]) -> Bool { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.new() +/// let queue : @queue.Queue[Int] = @queue.new() /// queue.push(1) /// ``` -pub fn[A] push(self : T[A], x : A) -> Unit { +pub fn[A] push(self : Queue[A], x : A) -> Unit { let cell = Some({ content: x, next: None }) match self.last { None => { @@ -123,11 +125,11 @@ pub fn[A] push(self : T[A], x : A) -> Unit { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// assert_eq(queue.unsafe_peek(), 1) /// ``` #internal(unsafe, "Panics if the queue is empty.") -pub fn[A] unsafe_peek(self : T[A]) -> A { +pub fn[A] unsafe_peek(self : Queue[A]) -> A { match self.first { None => abort("Queue is empty") Some(first) => first.content @@ -139,10 +141,10 @@ pub fn[A] unsafe_peek(self : T[A]) -> A { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// assert_eq(queue.peek(), Some(1)) /// ``` -pub fn[A] peek(self : T[A]) -> A? { +pub fn[A] peek(self : Queue[A]) -> A? { match self.first { None => None Some(first) => Some(first.content) @@ -154,11 +156,11 @@ pub fn[A] peek(self : T[A]) -> A? { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// assert_eq(queue.unsafe_pop(), 1) /// ``` #internal(unsafe, "Panics if the queue is empty.") -pub fn[A] unsafe_pop(self : T[A]) -> A { +pub fn[A] unsafe_pop(self : Queue[A]) -> A { match self.first { None => abort("Queue is empty") Some({ content, next: None }) => { @@ -178,10 +180,10 @@ pub fn[A] unsafe_pop(self : T[A]) -> A { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// assert_eq(queue.pop(), Some(1)) /// ``` -pub fn[A] pop(self : T[A]) -> A? { +pub fn[A] pop(self : Queue[A]) -> A? { match self.first { None => None Some({ content, next: None }) => { @@ -201,11 +203,11 @@ pub fn[A] pop(self : T[A]) -> A? { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// let mut sum = 0 /// queue.each((x) => { sum = sum + x }) /// ``` -pub fn[A] each(self : T[A], f : (A) -> Unit) -> Unit { +pub fn[A] each(self : Queue[A], f : (A) -> Unit) -> Unit { loop self.first { Some({ content, next }) => { f(content) @@ -220,11 +222,11 @@ pub fn[A] each(self : T[A], f : (A) -> Unit) -> Unit { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// let mut sum = 0 /// queue.eachi((x, i) => { sum = sum + x * i }) /// ``` -pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { +pub fn[A] eachi(self : Queue[A], f : (Int, A) -> Unit) -> Unit { loop (self.first, 0) { (Some({ content, next }), index) => { f(index, content) @@ -239,11 +241,11 @@ pub fn[A] eachi(self : T[A], f : (Int, A) -> Unit) -> Unit { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.new() +/// let queue : @queue.Queue[Int] = @queue.new() /// let sum = queue.fold(init=0, (acc, x) => { acc + x }) /// assert_eq(sum, 0) /// ``` -pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B) -> B { +pub fn[A, B] fold(self : Queue[A], init~ : B, f : (B, A) -> B) -> B { loop (self.first, init) { (None, acc) => acc (Some({ content, next }), acc) => continue (next, f(acc, content)) @@ -255,11 +257,11 @@ pub fn[A, B] fold(self : T[A], init~ : B, f : (B, A) -> B) -> B { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) -/// let queue2 : @queue.T[Int] = queue.copy() +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) +/// let queue2 : @queue.Queue[Int] = queue.copy() /// assert_eq(queue2.length(), 4) /// ``` -pub fn[A] copy(self : T[A]) -> T[A] { +pub fn[A] copy(self : Queue[A]) -> Queue[A] { guard self.first is Some({ content, next }) else { return new() } let first = { content, next: None } let last = loop (first, next) { @@ -280,11 +282,11 @@ pub fn[A] copy(self : T[A]) -> T[A] { /// /// # Example /// ```mbt -/// let dst : @queue.T[Int] = @queue.new() -/// let src : @queue.T[Int] = @queue.of([5, 6, 7, 8]) +/// let dst : @queue.Queue[Int] = @queue.new() +/// let src : @queue.Queue[Int] = @queue.of([5, 6, 7, 8]) /// src.transfer(dst) /// ``` -pub fn[A] transfer(self : T[A], dst : T[A]) -> Unit { +pub fn[A] transfer(self : Queue[A], dst : Queue[A]) -> Unit { if self.length > 0 { match dst.last { None => { @@ -308,14 +310,14 @@ pub fn[A] transfer(self : T[A], dst : T[A]) -> Unit { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([5, 6, 7, 8]) +/// let queue : @queue.Queue[Int] = @queue.of([5, 6, 7, 8]) /// let sum = queue.iter().fold((x, y) => { x + y }, init=0) /// assert_eq(sum, 26) /// ``` -pub fn[A] iter(self : T[A]) -> Iter[A] { +pub fn[A] iter(self : Queue[A]) -> Iter[A] { Iter::new(yield_ => loop self.first { Some({ content, next }) => { - if yield_(content) == IterEnd { + if yield_(content) is IterEnd { break IterEnd } continue next @@ -329,10 +331,11 @@ pub fn[A] iter(self : T[A]) -> Iter[A] { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.from_iter(Iter::empty()) +/// let queue : @queue.Queue[Int] = @queue.from_iter(Iter::empty()) /// assert_eq(queue.length(), 0) /// ``` -pub fn[A] from_iter(iter : Iter[A]) -> T[A] { +#as_free_fn +pub fn[A] Queue::from_iter(iter : Iter[A]) -> Queue[A] { let q = new() iter.each(e => q.push(e)) q @@ -343,10 +346,11 @@ pub fn[A] from_iter(iter : Iter[A]) -> T[A] { /// /// # Example /// ```mbt -/// let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) +/// let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) /// assert_eq(queue.length(), 4) /// ``` -pub fn[A] of(arr : FixedArray[A]) -> T[A] { +#as_free_fn +pub fn[A] Queue::of(arr : FixedArray[A]) -> Queue[A] { guard arr.length() > 0 else { return new() } let length = arr.length() let last = { content: arr[length - 1], next: None } @@ -390,7 +394,7 @@ test "op_equal" { ///| test "push" { - let queue : T[Int] = new() + let queue : Queue[Int] = new() queue.push(1) queue.push(2) queue.push(3) @@ -401,8 +405,8 @@ test "push" { ///| test "copy" { - let queue : T[Int] = of([1, 2, 3, 4]) - let queue2 : T[Int] = queue.copy() + let queue : Queue[Int] = of([1, 2, 3, 4]) + let queue2 : Queue[Int] = queue.copy() assert_eq(queue2.length(), 4) assert_eq(queue2, of([1, 2, 3, 4])) assert_eq(queue.length(), 4) @@ -411,8 +415,8 @@ test "copy" { ///| test "transfer" { - let queue : T[Int] = of([1, 2, 3, 4]) - let queue2 : T[Int] = of([5, 6, 7, 8]) + let queue : Queue[Int] = of([1, 2, 3, 4]) + let queue2 : Queue[Int] = of([5, 6, 7, 8]) queue.transfer(queue2) assert_eq(queue.length(), 0) assert_eq(queue2.length(), 8) @@ -425,9 +429,9 @@ test "cell_equal" { } ///| -pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] with arbitrary( +pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Queue[X] with arbitrary( size, - rs + rs, ) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_iter } diff --git a/bundled-core/queue/queue.mbti b/bundled-core/queue/queue.mbti deleted file mode 100644 index 35086b2..0000000 --- a/bundled-core/queue/queue.mbti +++ /dev/null @@ -1,42 +0,0 @@ -package "moonbitlang/core/queue" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[A] from_array(Array[A]) -> T[A] - -fn[A] from_iter(Iter[A]) -> T[A] - -fn[A] new() -> T[A] - -fn[A] of(FixedArray[A]) -> T[A] - -// Types and methods -type T[A] -fn[A] T::clear(Self[A]) -> Unit -fn[A] T::copy(Self[A]) -> Self[A] -fn[A] T::each(Self[A], (A) -> Unit) -> Unit -fn[A] T::eachi(Self[A], (Int, A) -> Unit) -> Unit -fn[A, B] T::fold(Self[A], init~ : B, (B, A) -> B) -> B -fn[A] T::is_empty(Self[A]) -> Bool -fn[A] T::iter(Self[A]) -> Iter[A] -fn[A] T::length(Self[A]) -> Int -fn[A] T::peek(Self[A]) -> A? -#deprecated -fn[A] T::peek_exn(Self[A]) -> A -fn[A] T::pop(Self[A]) -> A? -#deprecated -fn[A] T::pop_exn(Self[A]) -> A -fn[A] T::push(Self[A], A) -> Unit -fn[A] T::transfer(Self[A], Self[A]) -> Unit -fn[A] T::unsafe_peek(Self[A]) -> A -fn[A] T::unsafe_pop(Self[A]) -> A -impl[A : Show] Show for T[A] -impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/queue/queue_test.mbt b/bundled-core/queue/queue_test.mbt index 75f207e..e3f7569 100644 --- a/bundled-core/queue/queue_test.mbt +++ b/bundled-core/queue/queue_test.mbt @@ -14,7 +14,7 @@ ///| test "new" { - let queue : @queue.T[Int] = @queue.new() + let queue : @queue.Queue[Int] = @queue.new() assert_eq(queue.length(), 0) } @@ -33,7 +33,7 @@ test "from_fixed_array_1" { ///| test "from_fixed_array_2" { - let q : @queue.T[Int] = @queue.of([]) + let q : @queue.Queue[Int] = @queue.of([]) inspect(q, content="@queue.of([])") } @@ -52,18 +52,18 @@ test "from_array_1" { ///| test "from_array_2" { - let q : @queue.T[Int] = @queue.from_array([]) + let q : @queue.Queue[Int] = @queue.from_array([]) inspect(q, content="@queue.of([])") let array = Array::makei(3, idx => idx + 1) - let q : @queue.T[Int] = @queue.from_array(array) + let q : @queue.Queue[Int] = @queue.from_array(array) inspect(q, content="@queue.of([1, 2, 3])") - let q : @queue.T[Int] = @queue.from_array(Array::new(capacity=10)) + let q : @queue.Queue[Int] = @queue.from_array(Array::new(capacity=10)) inspect(q, content="@queue.of([])") } ///| test "to_string" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) inspect(queue, content="@queue.of([1, 2, 3, 4])") queue.push(11) inspect(queue, content="@queue.of([1, 2, 3, 4, 11])") @@ -73,7 +73,7 @@ test "to_string" { ///| test "clear" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) assert_eq(queue.length(), 4) queue.clear() assert_eq(queue.length(), 0) @@ -81,7 +81,7 @@ test "clear" { ///| test "is_empty" { - let queue : @queue.T[Int] = @queue.new() + let queue : @queue.Queue[Int] = @queue.new() assert_true(queue.is_empty()) queue.push(1) queue.push(2) @@ -92,7 +92,7 @@ test "is_empty" { ///| test "unsafe_peek" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) assert_eq(queue.unsafe_peek(), 1) assert_eq(queue.length(), 4) assert_eq(queue.unsafe_pop(), 1) @@ -102,7 +102,7 @@ test "unsafe_peek" { ///| test "peek" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) assert_eq(queue.peek(), Some(1)) queue.clear() assert_eq(queue.peek(), None) @@ -110,7 +110,7 @@ test "peek" { ///| test "unsafe_pop" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) assert_eq(queue.unsafe_pop(), 1) assert_eq(queue.unsafe_pop(), 2) assert_eq(queue.unsafe_pop(), 3) @@ -120,7 +120,7 @@ test "unsafe_pop" { ///| test "pop" { - let queue : @queue.T[Int] = @queue.of([1, 2, 3, 4]) + let queue : @queue.Queue[Int] = @queue.of([1, 2, 3, 4]) assert_eq(queue.pop(), Some(1)) assert_eq(queue.pop(), Some(2)) assert_eq(queue.pop(), Some(3)) @@ -130,7 +130,7 @@ test "pop" { ///| test "iter" { - let queue : @queue.T[Int] = @queue.new() + let queue : @queue.Queue[Int] = @queue.new() let mut sum = 0 queue.each(x => sum = sum + x) assert_eq(sum, 0) @@ -142,7 +142,7 @@ test "iter" { ///| test "iteri" { - let queue : @queue.T[Int] = @queue.new() + let queue : @queue.Queue[Int] = @queue.new() let mut sum = 0 queue.eachi((x, i) => sum = sum + x * i) assert_eq(sum, 0) @@ -154,7 +154,7 @@ test "iteri" { ///| test "fold" { - let queue : @queue.T[Int] = @queue.new() + let queue : @queue.Queue[Int] = @queue.new() let sum = queue.fold(init=0, (acc, x) => acc + x) assert_eq(sum, 0) @queue.of([1, 2, 3, 4]).transfer(queue) @@ -170,7 +170,7 @@ test "fold" { ///| test "length" { - let empty : @queue.T[Unit] = @queue.of([]) + let empty : @queue.Queue[Unit] = @queue.of([]) inspect(empty.length(), content="0") inspect(@queue.of([1, 2, 3]).length(), content="3") } @@ -193,21 +193,21 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @queue.T[Int] = @queue.from_iter(Iter::empty()) + let pq : @queue.Queue[Int] = @queue.from_iter(Iter::empty()) inspect(pq, content="@queue.of([])") } ///| test "to_string" { - let queue : @queue.T[Int] = @queue.from_array([]) + let queue : @queue.Queue[Int] = @queue.from_array([]) assert_eq(queue.to_string(), "@queue.of([])") - let queue : @queue.T[Int] = @queue.from_array([9, 8, 7, 5]) + let queue : @queue.Queue[Int] = @queue.from_array([9, 8, 7, 5]) assert_eq(queue.to_string(), "@queue.of([9, 8, 7, 5])") } ///| test "queue arbitrary" { - let samples : Array[T[Int]] = @quickcheck.samples(20) + let samples : Array[@queue.Queue[Int]] = @quickcheck.samples(20) inspect( samples[1:5], content="[@queue.of([]), @queue.of([]), @queue.of([0]), @queue.of([0])]", diff --git a/bundled-core/queue/types.mbt b/bundled-core/queue/types.mbt index 61949c0..a1470ff 100644 --- a/bundled-core/queue/types.mbt +++ b/bundled-core/queue/types.mbt @@ -18,11 +18,16 @@ priv struct Cons[A] { mut next : Cons[A]? } derive(Eq) -///| // Invariant: // - length == 0 <=> first is None && last is None -struct T[A] { + +///| +struct Queue[A] { mut length : Int mut first : Cons[A]? mut last : Cons[A]? } + +///| +#deprecated("use `Queue` instead of `T`") +pub typealias Queue as T diff --git a/bundled-core/quickcheck/README.mbt.md b/bundled-core/quickcheck/README.mbt.md index 1daad45..6e67b50 100644 --- a/bundled-core/quickcheck/README.mbt.md +++ b/bundled-core/quickcheck/README.mbt.md @@ -30,15 +30,9 @@ test "multiple samples" { let strings : Array[String] = @quickcheck.samples(12) inspect( strings[5:10], - content= - - #|["E\b\u{0f} ", "", "K\u{1f}[", "!@", "xvLxb"] - - - - - - , + content=( + #|["E\b\u{0f} ", "", "K\u{1f}[", "!@", "xvLxb"] + ), ) } ``` @@ -54,15 +48,15 @@ test "builtin types" { inspect(v, content="(true, '#', b'\\x12')") // Numeric types let v : (Int, Int64, UInt, UInt64, Float, Double, BigInt) = @quickcheck.gen() - inspect(v, content="(0, 0, 0, 0, 76806128, 0.33098446695254635, 0)") + inspect(v, content="(0, 0, 0, 0, 0.1430625319480896, 0.33098446695254635, 0)") // Collections let v : (String, Bytes, Iter[Int]) = @quickcheck.gen() inspect( v, - content= + content=( #|("", b"", []) - , + ), ) } ``` @@ -79,8 +73,8 @@ struct Point { impl Arbitrary for Point with arbitrary(size, r0) { let r1 = r0.split() - let y = Arbitrary::arbitrary(size, r1) - { x: Arbitrary::arbitrary(size, r0), y } + let y = @quickcheck.Arbitrary::arbitrary(size, r1) + { x: @quickcheck.Arbitrary::arbitrary(size, r0), y } } test "custom type generation" { diff --git a/bundled-core/quickcheck/arbitrary.mbt b/bundled-core/quickcheck/arbitrary.mbt index fa62d2b..b8b4d3a 100644 --- a/bundled-core/quickcheck/arbitrary.mbt +++ b/bundled-core/quickcheck/arbitrary.mbt @@ -113,7 +113,7 @@ pub impl Arbitrary for String with arbitrary(size, rs) { pub impl[X : Arbitrary] Arbitrary for Iter[X] with arbitrary(size, rs) { let len = if size == 0 { 0 } else { rs.next_positive_int() % size } Iter::new(yield_ => for i in 0.. { @@ -93,12 +93,12 @@ test "arbitrary_iter_break" { ///| test "arbitrary gen bytes" { let rs = @splitmix.RandomState::default().split().split() - let bytes : Bytes = gen(size=10, state=rs) + let bytes : Bytes = @quickcheck.gen(size=10, state=rs) // Check that the length of the generated bytes is within the expected range inspect( bytes, - content= + content=( #|b"\xec\xa9\x61\xbc\x51\x2e\x20\x8b" - , + ), ) } diff --git a/bundled-core/quickcheck/quickcheck.mbti b/bundled-core/quickcheck/pkg.generated.mbti similarity index 92% rename from bundled-core/quickcheck/quickcheck.mbti rename to bundled-core/quickcheck/pkg.generated.mbti index 8f1298b..7eab626 100644 --- a/bundled-core/quickcheck/quickcheck.mbti +++ b/bundled-core/quickcheck/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/quickcheck" import( @@ -9,6 +10,8 @@ fn[T : Arbitrary] gen(size? : Int, state? : @splitmix.RandomState) -> T fn[X : Arbitrary] samples(Int) -> Array[X] +// Errors + // Types and methods // Type aliases diff --git a/bundled-core/quickcheck/splitmix/README.mbt.md b/bundled-core/quickcheck/splitmix/README.mbt.md new file mode 100644 index 0000000..16db070 --- /dev/null +++ b/bundled-core/quickcheck/splitmix/README.mbt.md @@ -0,0 +1,208 @@ +# QuickCheck SplitMix Package Documentation + +This package provides the SplitMix random number generator, which is used internally by the QuickCheck property-based testing framework. SplitMix is a fast, high-quality pseudorandom number generator suitable for testing and simulation. + +## Random State Creation + +Create and initialize random number generators: + +```moonbit +test "random state creation" { + // Create with default seed + let rng1 = @splitmix.new() + inspect(rng1.to_string().length() > 0, content="true") + + // Create with specific seed + let rng2 = @splitmix.new(seed=12345UL) + inspect(rng2.to_string().length() > 0, content="true") + + // Clone existing state + let rng3 = rng2.clone() + inspect(rng3.to_string().length() > 0, content="true") +} +``` + +## Generating Random Numbers + +Generate various types of random numbers: + +```moonbit +test "random number generation" { + let rng = @splitmix.new(seed=42UL) + + // Generate random integers + let int_val = rng.next_int() + inspect(int_val.to_string().length() > 0, content="true") + + // Generate positive integers only + let pos_int = rng.next_positive_int() + inspect(pos_int > 0, content="true") + + // Generate UInt values + let uint_val = rng.next_uint() + inspect(uint_val.to_string().length() > 0, content="true") + + // Generate Int64 values + let int64_val = rng.next_int64() + inspect(int64_val.to_string().length() > 0, content="true") + + // Generate UInt64 values + let uint64_val = rng.next_uint64() + inspect(uint64_val.to_string().length() > 0, content="true") +} +``` + +## Floating-Point Random Numbers + +Generate random floating-point values: + +```moonbit +test "floating point generation" { + let rng = @splitmix.new(seed=123UL) + + // Generate random doubles [0.0, 1.0) + let double_val = rng.next_double() + inspect(double_val >= 0.0, content="true") + inspect(double_val < 1.0, content="true") + + // Generate random floats [0.0, 1.0) + let float_val = rng.next_float() + inspect(float_val >= 0.0, content="true") + inspect(float_val < 1.0, content="true") + + // Generate multiple values + let val1 = rng.next_double() + let val2 = rng.next_double() + + // Should be different (with high probability) + inspect(val1 != val2, content="true") +} +``` + +## Advanced Operations + +Use advanced RNG operations: + +```moonbit +test "advanced operations" { + let rng = @splitmix.new(seed=999UL) + + // Generate two UInt values at once + let (uint1, uint2) = rng.next_two_uint() + inspect(uint1.to_string().length() > 0, content="true") + inspect(uint2.to_string().length() > 0, content="true") + + // Split the generator (for parallel use) + let split_rng = rng.split() + + // Both generators should work independently + let original_val = rng.next_int() + let split_val = split_rng.next_int() + + inspect(original_val.to_string().length() > 0, content="true") + inspect(split_val.to_string().length() > 0, content="true") +} +``` + +## State Management + +Manage random number generator state: + +```moonbit +test "state management" { + let rng = @splitmix.new(seed=555UL) + + // Advance the state manually + rng.next() + + // Generate value after advancing + let after_advance = rng.next_int() + inspect(after_advance.to_string().length() > 0, content="true") + + // Create independent copy + let independent = rng.clone() + + // Both should generate the same sequence from this point + let val1 = rng.next_int() + let val2 = independent.next_int() + + inspect(val1 == val2, content="true") // Should be identical +} +``` + +## Deterministic Testing + +Use seeded generators for reproducible tests: + +```moonbit +test "deterministic testing" { + // Same seed should produce same sequence + let rng1 = @splitmix.new(seed=777UL) + let rng2 = @splitmix.new(seed=777UL) + + // Generate same sequence + let seq1 = [rng1.next_int(), rng1.next_int(), rng1.next_int()] + let seq2 = [rng2.next_int(), rng2.next_int(), rng2.next_int()] + + inspect(seq1[0] == seq2[0], content="true") + inspect(seq1[1] == seq2[1], content="true") + inspect(seq1[2] == seq2[2], content="true") +} +``` + +## Integration with QuickCheck + +This generator is used by QuickCheck for property testing: + +```moonbit +test "quickcheck integration concept" { + // Conceptual usage in property-based testing + fn test_property_with_random_data() -> Bool { + let rng = @splitmix.new() + + // Generate test data + let test_int = rng.next_positive_int() + let test_double = rng.next_double() + + // Test some property + test_int > 0 && test_double >= 0.0 && test_double < 1.0 + } + + let property_holds = test_property_with_random_data() + inspect(property_holds, content="true") +} +``` + +## SplitMix Algorithm Properties + +SplitMix provides: + +1. **High quality**: Passes statistical randomness tests +2. **Fast generation**: Optimized for speed +3. **Splittable**: Can create independent generators +4. **Deterministic**: Same seed produces same sequence +5. **Period**: Very long period before repetition + +## Performance Characteristics + +- **Generation speed**: Very fast (few CPU cycles per number) +- **Memory usage**: Minimal state (single 64-bit value) +- **Quality**: Good statistical properties for testing +- **Splitting**: O(1) to create independent generators + +## Use Cases + +1. **Property-based testing**: Generate random test inputs +2. **Simulation**: Monte Carlo simulations and modeling +3. **Sampling**: Random sampling from data sets +4. **Shuffling**: Randomize array orders +5. **Game development**: Non-cryptographic randomness + +## Best Practices + +1. **Use seeds for reproducibility**: Fixed seeds for debugging +2. **Split for parallelism**: Create independent generators for parallel testing +3. **Not cryptographically secure**: Don't use for security-sensitive applications +4. **Cache generators**: Reuse generator instances when possible + +This package provides the random number generation foundation for QuickCheck's property-based testing capabilities. diff --git a/bundled-core/quickcheck/splitmix/deprecated.mbt b/bundled-core/quickcheck/splitmix/deprecated.mbt index 2feb53f..560c272 100644 --- a/bundled-core/quickcheck/splitmix/deprecated.mbt +++ b/bundled-core/quickcheck/splitmix/deprecated.mbt @@ -14,6 +14,6 @@ ///| #deprecated("use `@splitmix.new` instead") -pub fn RandomState::new(seed~ : UInt64 = 37) -> RandomState { +pub fn RandomState::new(seed? : UInt64 = 37) -> RandomState { new(seed~) } diff --git a/bundled-core/quickcheck/splitmix/splitmix.mbti b/bundled-core/quickcheck/splitmix/pkg.generated.mbti similarity index 82% rename from bundled-core/quickcheck/splitmix/splitmix.mbti rename to bundled-core/quickcheck/splitmix/pkg.generated.mbti index 2ae6ca7..868f4ac 100644 --- a/bundled-core/quickcheck/splitmix/splitmix.mbti +++ b/bundled-core/quickcheck/splitmix/pkg.generated.mbti @@ -1,13 +1,16 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/quickcheck/splitmix" // Values -fn new(seed~ : UInt64 = ..) -> RandomState +fn new(seed? : UInt64) -> RandomState + +// Errors // Types and methods type RandomState fn RandomState::clone(Self) -> Self #deprecated -fn RandomState::new(seed~ : UInt64 = ..) -> Self +fn RandomState::new(seed? : UInt64) -> Self fn RandomState::next(Self) -> Unit fn RandomState::next_double(Self) -> Double fn RandomState::next_float(Self) -> Float diff --git a/bundled-core/quickcheck/splitmix/random.mbt b/bundled-core/quickcheck/splitmix/random.mbt index 1bd9816..66dbed1 100644 --- a/bundled-core/quickcheck/splitmix/random.mbt +++ b/bundled-core/quickcheck/splitmix/random.mbt @@ -29,12 +29,10 @@ let float_ulp : Float = 1.0.to_float() / (1L << 24).to_float() ///| /// Create a new RandomState from an optional seed. -pub fn new(seed~ : UInt64 = 37) -> RandomState { +pub fn new(seed? : UInt64 = 37) -> RandomState { { seed: mix64(seed), gamma: mix_gamma(seed + golden_gamma) } } -///| - ///| /// Clone a RandomState. pub fn clone(self : RandomState) -> RandomState { @@ -96,7 +94,7 @@ pub fn next_positive_int(self : RandomState) -> Int { /// Get the next random number as a float in [0, 1] pub fn next_float(self : RandomState) -> Float { let u = self.next_uint64() - (u >> 11).to_float() * float_ulp + (u >> 40).to_float() * float_ulp } ///| diff --git a/bundled-core/quickcheck/splitmix/random_test.mbt b/bundled-core/quickcheck/splitmix/random_test.mbt index 8202351..5cf8a4b 100644 --- a/bundled-core/quickcheck/splitmix/random_test.mbt +++ b/bundled-core/quickcheck/splitmix/random_test.mbt @@ -44,5 +44,5 @@ test "next_float" { let result = state.next_float() // Since the result is random, we only check if it falls within the expected range [0, 1] inspect(result >= 0.0, content="true") - inspect(result <= 1.0, content="false") + inspect(result <= 1.0, content="true") } diff --git a/bundled-core/random/deprecated.mbt b/bundled-core/random/deprecated.mbt index e9e8b6f..708c09b 100644 --- a/bundled-core/random/deprecated.mbt +++ b/bundled-core/random/deprecated.mbt @@ -14,7 +14,7 @@ ///| #deprecated("Use `Rand::new()` instead") -pub fn new(seed~ : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456") -> Rand { +pub fn new(seed? : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456") -> Rand { if seed.length() != 32 { abort("seed must be 32 bytes long") } diff --git a/bundled-core/random/internal/random_source/random_source.mbti b/bundled-core/random/internal/random_source/pkg.generated.mbti similarity index 80% rename from bundled-core/random/internal/random_source/random_source.mbti rename to bundled-core/random/internal/random_source/pkg.generated.mbti index f6d4150..9c93ffa 100644 --- a/bundled-core/random/internal/random_source/random_source.mbti +++ b/bundled-core/random/internal/random_source/pkg.generated.mbti @@ -1,7 +1,10 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/random/internal/random_source" // Values +// Errors + // Types and methods type ChaCha8 fn ChaCha8::new(Bytes) -> Self diff --git a/bundled-core/random/internal/random_source/random_source_chacha.mbt b/bundled-core/random/internal/random_source/random_source_chacha.mbt index fbd204f..4e8f8f3 100644 --- a/bundled-core/random/internal/random_source/random_source_chacha.mbt +++ b/bundled-core/random/internal/random_source/random_source_chacha.mbt @@ -80,17 +80,26 @@ pub fn ChaCha8::refill(self : ChaCha8) -> Unit { } } +///| +#valtype +priv struct Quadruple { + _0 : UInt + _1 : UInt + _2 : UInt + _3 : UInt +} + ///| fn chacha_block( seed : FixedArray[UInt], buf : FixedArray[UInt], - counter : UInt + counter : UInt, ) -> Unit { - fn qr(t : (UInt, UInt, UInt, UInt)) -> (UInt, UInt, UInt, UInt) { - let a = t.0 - let b = t.1 - let c = t.2 - let d = t.3 + fn qr(t : Quadruple) -> Quadruple { + let a = t._0 + let b = t._1 + let c = t._2 + let d = t._3 let a = a + b let d = d ^ a let d = (d << 16) | (d >> 16) @@ -103,7 +112,7 @@ fn chacha_block( let c = c + d let b = b ^ c let b = (b << 7) | (b >> 25) - return (a, b, c, d) + { _0: a, _1: b, _2: c, _3: d } } setup(seed, buf, counter) @@ -125,46 +134,46 @@ fn chacha_block( let mut b14 = buf[14 * 4 + i] let mut b15 = buf[15 * 4 + i] for round in 0..<4 { - let tb1 = qr((b0, b4, b8, b12)) - b0 = tb1.0 - b4 = tb1.1 - b8 = tb1.2 - b12 = tb1.3 - let tb2 = qr((b1, b5, b9, b13)) - b1 = tb2.0 - b5 = tb2.1 - b9 = tb2.2 - b13 = tb2.3 - let tb3 = qr((b2, b6, b10, b14)) - b2 = tb3.0 - b6 = tb3.1 - b10 = tb3.2 - b14 = tb3.3 - let tb4 = qr((b3, b7, b11, b15)) - b3 = tb4.0 - b7 = tb4.1 - b11 = tb4.2 - b15 = tb4.3 - let tb5 = qr((b0, b5, b10, b15)) - b0 = tb5.0 - b5 = tb5.1 - b10 = tb5.2 - b15 = tb5.3 - let tb6 = qr((b1, b6, b11, b12)) - b1 = tb6.0 - b6 = tb6.1 - b11 = tb6.2 - b12 = tb6.3 - let tb7 = qr((b2, b7, b8, b13)) - b2 = tb7.0 - b7 = tb7.1 - b8 = tb7.2 - b13 = tb7.3 - let tb8 = qr((b3, b4, b9, b14)) - b3 = tb8.0 - b4 = tb8.1 - b9 = tb8.2 - b14 = tb8.3 + let tb1 = qr({ _0: b0, _1: b4, _2: b8, _3: b12 }) + b0 = tb1._0 + b4 = tb1._1 + b8 = tb1._2 + b12 = tb1._3 + let tb2 = qr({ _0: b1, _1: b5, _2: b9, _3: b13 }) + b1 = tb2._0 + b5 = tb2._1 + b9 = tb2._2 + b13 = tb2._3 + let tb3 = qr({ _0: b2, _1: b6, _2: b10, _3: b14 }) + b2 = tb3._0 + b6 = tb3._1 + b10 = tb3._2 + b14 = tb3._3 + let tb4 = qr({ _0: b3, _1: b7, _2: b11, _3: b15 }) + b3 = tb4._0 + b7 = tb4._1 + b11 = tb4._2 + b15 = tb4._3 + let tb5 = qr({ _0: b0, _1: b5, _2: b10, _3: b15 }) + b0 = tb5._0 + b5 = tb5._1 + b10 = tb5._2 + b15 = tb5._3 + let tb6 = qr({ _0: b1, _1: b6, _2: b11, _3: b12 }) + b1 = tb6._0 + b6 = tb6._1 + b11 = tb6._2 + b12 = tb6._3 + let tb7 = qr({ _0: b2, _1: b7, _2: b8, _3: b13 }) + b2 = tb7._0 + b7 = tb7._1 + b8 = tb7._2 + b13 = tb7._3 + let tb8 = qr({ _0: b3, _1: b4, _2: b9, _3: b14 }) + b3 = tb8._0 + b4 = tb8._1 + b9 = tb8._2 + b14 = tb8._3 } buf[0 * 4 + i] = b0 buf[1 * 4 + i] = b1 @@ -189,7 +198,7 @@ fn chacha_block( fn setup( seed : FixedArray[UInt], b32 : FixedArray[UInt], - counter : UInt + counter : UInt, ) -> Unit { b32[0 * 4 + 0] = 0x61707865 b32[0 * 4 + 1] = 0x61707865 diff --git a/bundled-core/random/moon.pkg.json b/bundled-core/random/moon.pkg.json index 3c5f452..f8e8d78 100644 --- a/bundled-core/random/moon.pkg.json +++ b/bundled-core/random/moon.pkg.json @@ -5,5 +5,8 @@ "moonbitlang/core/random/internal/random_source", "moonbitlang/core/bigint" ], - "test-import": ["moonbitlang/core/float"] + "test-import": [ + "moonbitlang/core/float", + "moonbitlang/core/bench" + ] } diff --git a/bundled-core/random/random.mbti b/bundled-core/random/pkg.generated.mbti similarity index 55% rename from bundled-core/random/random.mbti rename to bundled-core/random/pkg.generated.mbti index 2ca442b..3499f16 100644 --- a/bundled-core/random/random.mbti +++ b/bundled-core/random/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/random" import( @@ -6,23 +7,25 @@ import( // Values #deprecated -fn chacha8(seed~ : Bytes = ..) -> &Source +fn chacha8(seed? : Bytes) -> &Source #deprecated -fn new(seed~ : Bytes = ..) -> Rand +fn new(seed? : Bytes) -> Rand + +// Errors // Types and methods type Rand fn Rand::bigint(Self, Int) -> @bigint.BigInt -fn Rand::chacha8(seed~ : Bytes = ..) -> Self +fn Rand::chacha8(seed? : Bytes) -> Self fn Rand::double(Self) -> Double fn Rand::float(Self) -> Float -fn Rand::int(Self, limit~ : Int = ..) -> Int -fn Rand::int64(Self, limit~ : Int64 = ..) -> Int64 +fn Rand::int(Self, limit? : Int) -> Int +fn Rand::int64(Self, limit? : Int64) -> Int64 fn Rand::new(generator? : &Source) -> Self fn Rand::shuffle(Self, Int, (Int, Int) -> Unit) -> Unit -fn Rand::uint(Self, limit~ : UInt = ..) -> UInt -fn Rand::uint64(Self, limit~ : UInt64 = ..) -> UInt64 +fn Rand::uint(Self, limit? : UInt) -> UInt +fn Rand::uint64(Self, limit? : UInt64) -> UInt64 // Type aliases diff --git a/bundled-core/random/random.mbt b/bundled-core/random/random.mbt index d5b7e05..c659d46 100644 --- a/bundled-core/random/random.mbt +++ b/bundled-core/random/random.mbt @@ -15,7 +15,7 @@ ///| /// `Rand` is a pseudo-random number generator (PRNG) that provides various /// methods to generate random numbers of different types. -type Rand &Source +struct Rand(&Source) ///| /// The [Source] trait defines a method to generate random numbers. @@ -37,7 +37,7 @@ impl Source for @random_source.ChaCha8 with next(self : @random_source.ChaCha8) /// Create a new random number generator with [seed]. /// @alert unsafe "Panic if seed is not 32 bytes long" pub fn Rand::chacha8( - seed~ : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456" + seed? : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", ) -> Rand { if seed.length() != 32 { abort("seed must be 32 bytes long") @@ -48,7 +48,7 @@ pub fn Rand::chacha8( ///| /// Create a new random number generator with [seed]. #deprecated("You may use `Rand::chacha8(seed~)` instead of `Rand::new(chacha8(seed~))") -pub fn chacha8(seed~ : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456") -> &Source { +pub fn chacha8(seed? : Bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456") -> &Source { @random_source.ChaCha8::new(seed) } @@ -82,7 +82,7 @@ test "next" { /// /// * `limit` - The upper bound (exclusive) of the random number to be generated (Optional). /// When limit is 0, the range is [0, 2^31). -pub fn int(self : Rand, limit~ : Int = 0) -> Int { +pub fn int(self : Rand, limit? : Int = 0) -> Int { if limit == 0 { // Range [0, 2^31) (self.next() >> 33).to_int() @@ -98,7 +98,7 @@ pub fn int(self : Rand, limit~ : Int = 0) -> Int { /// /// * `limit` - The upper bound (exclusive) of the random number to be generated (Optional). /// When limit is 0, the range is [0, 2^63). -pub fn int64(self : Rand, limit~ : Int64 = 0) -> Int64 { +pub fn int64(self : Rand, limit? : Int64 = 0) -> Int64 { if limit == 0 { // range [0, 2^63) // Create a mask that keeps the lower 63 bits @@ -116,7 +116,7 @@ pub fn int64(self : Rand, limit~ : Int64 = 0) -> Int64 { /// /// * `limit` - The upper bound (exclusive) of the random number to be generated (Optional). /// When limit is 0, the range is [0, 2^32). -pub fn uint(self : Rand, limit~ : UInt = 0) -> UInt { +pub fn uint(self : Rand, limit? : UInt = 0) -> UInt { if limit == 0 { // Range: [0, 2^32) return self.next().to_uint() @@ -142,7 +142,7 @@ test "uint" { /// /// * `limit` - The upper bound (exclusive) of the random number to be generated (Optional). /// When limit is 0, the range is [0, 2^64). -pub fn uint64(self : Rand, limit~ : UInt64 = 0) -> UInt64 { +pub fn uint64(self : Rand, limit? : UInt64 = 0) -> UInt64 { if limit == 0 { // Range: [0, 2^64) return self.next() @@ -150,14 +150,14 @@ pub fn uint64(self : Rand, limit~ : UInt64 = 0) -> UInt64 { // limit is a power of 2, mask to get the unbiased result. return self.next() & (limit - 1) } - umul128(self.next(), limit) - if gUInt128.lo < limit { + let mut r = umul128(self.next(), limit) + if r.lo < limit { let thresh = limit.lnot() % limit - while gUInt128.lo < thresh { - umul128(self.next(), limit) + while r.lo < thresh { + r = umul128(self.next(), limit) } } - gUInt128.hi + r.hi } ///| @@ -206,7 +206,7 @@ pub fn float(self : Rand) -> Float { /// Example: /// /// ```moonbit -/// let rand = Rand::new() +/// let rand = @random.Rand::new() /// let n = rand.bigint(8) // Generate random 8-bit number /// inspect(n.bit_length() <= 8, content="true") /// ``` @@ -236,21 +236,19 @@ test "bigint" { } ///| +#valtype priv struct UInt128 { - mut hi : UInt64 - mut lo : UInt64 + hi : UInt64 + lo : UInt64 } -///| -let gUInt128 : UInt128 = { hi: 0UL, lo: 0UL } - ///| /// [umul128] returns the 128-bit product of x and y: (hi, lo) = x * y /// with the product bits' upper half returned in hi and the lower /// half returned in lo. /// /// This function's execution time does not depend on the inputs. -fn umul128(a : UInt64, b : UInt64) -> Unit { +fn umul128(a : UInt64, b : UInt64) -> UInt128 { let aLo = a & 0xffffffff let aHi = a >> 32 let bLo = b & 0xffffffff @@ -259,36 +257,35 @@ fn umul128(a : UInt64, b : UInt64) -> Unit { let y = aHi * bLo + (x >> 32) let z = aLo * bHi + (y & 0xffffffff) let w = aHi * bHi + (y >> 32) + (z >> 32) - gUInt128.hi = w - gUInt128.lo = a * b + { hi: w, lo: a * b } } ///| test "umul128" { - umul128(0x123456789ABCDEF0, 0xFEDCBA9876543210) - assert_eq(gUInt128.hi, 1305938385386173474UL) - assert_eq(gUInt128.lo, 2552847189736476416UL) + let r = umul128(0x123456789ABCDEF0, 0xFEDCBA9876543210) + assert_eq(r.hi, 1305938385386173474UL) + assert_eq(r.lo, 2552847189736476416UL) } ///| test "umul128: handles small numbers correctly" { - umul128(1UL, 1UL) - assert_eq(gUInt128.hi, 0UL) - assert_eq(gUInt128.lo, 1UL) + let r = umul128(1UL, 1UL) + assert_eq(r.hi, 0UL) + assert_eq(r.lo, 1UL) } ///| test "umul128: handles large numbers correctly" { - umul128(1UL, 0xFFFFFFFFFFFFFFFFUL) - assert_eq(gUInt128.hi, 0UL) - assert_eq(gUInt128.lo, 0xFFFFFFFFFFFFFFFFUL) + let r = umul128(1UL, 0xFFFFFFFFFFFFFFFFUL) + assert_eq(r.hi, 0UL) + assert_eq(r.lo, 0xFFFFFFFFFFFFFFFFUL) } ///| test "umul128: handles zero correctly" { - umul128(0UL, 0UL) - assert_eq(gUInt128.hi, 0UL) - assert_eq(gUInt128.lo, 0UL) + let r = umul128(0UL, 0UL) + assert_eq(r.hi, 0UL) + assert_eq(r.lo, 0UL) } ///| @@ -297,7 +294,7 @@ test "umul128: handles zero correctly" { /// /// # Example /// ```mbt -/// let r = Rand::new() +/// let r = @random.Rand::new() /// let a = [1, 2, 3, 4, 5] /// r.shuffle( /// a.length(), diff --git a/bundled-core/random/random_test.mbt b/bundled-core/random/random_test.mbt index 58aa3fe..d13f1df 100644 --- a/bundled-core/random/random_test.mbt +++ b/bundled-core/random/random_test.mbt @@ -73,3 +73,12 @@ test "uint64 modulo bias handling with large limit" { let result = r.uint64(limit~) inspect(result < limit, content="true") } + +///| +test "bench random" (b : @bench.T) { + let r = @random.Rand::new() + b.bench(() => for i in 0..<1000000 { + let _ = r.uint64(limit=10000000000000000000UL) + + }) +} diff --git a/bundled-core/rational/README.mbt.md b/bundled-core/rational/README.mbt.md index 5513355..6caa33b 100644 --- a/bundled-core/rational/README.mbt.md +++ b/bundled-core/rational/README.mbt.md @@ -1,79 +1,7 @@ -# Rational +# Rational (DEPRECATED) -The `Rational` type represents a rational number, which is a number that can be expressed as a fraction `a/b` where `a` and `b` are integers and `b` is not zero. - -# Usage - -## Arithmetic Operations - -The `Rational` type supports the following arithmetic operations: - -```moonbit -test { - let a = @rational.new(1L, 2L).unwrap() - let b = @rational.new(1L, 3L).unwrap() - assert_eq(a + b, @rational.new(5L, 6L).unwrap()) - assert_eq(a - b, @rational.new(1L, 6L).unwrap()) - assert_eq(a * b, @rational.new(1L, 6L).unwrap()) - assert_eq(a / b, @rational.new(3L, 2L).unwrap()) - assert_eq(a.neg(), @rational.new(-1L, 2L).unwrap()) - assert_eq(a.reciprocal(), @rational.new(2L, 1L).unwrap()) - assert_eq(a.abs(), @rational.new(1L, 2L).unwrap()) -} -``` - -## Comparison Operations - -The `Rational` type supports the following comparison operations: - -```moonbit -test { - let a = @rational.new(1L, 2L).unwrap() - let b = @rational.new(1L, 3L).unwrap() - assert_eq(a == b, false) - assert_eq(a != b, true) - assert_eq(a < b, false) - assert_eq(a <= b, false) - assert_eq(a > b, true) - assert_eq(a >= b, true) - assert_eq(a.compare(b), 1) -} -``` +โš ๏ธ **This module is deprecated. Use `@rational` in module `moonbitlang/x` instead. Note that you need to rename `Rational` to `Rational64`.** -## Integer Operations - -The `Rational` type supports the following integer operations: - -```moonbit -test { - let a = @rational.new(1L, 2L).unwrap() - assert_eq(a.floor(), 0) - assert_eq(a.ceil(), 1) - assert_eq(a.fract().to_string(), "1/2") - assert_eq(a.trunc(), 0) - assert_eq(a.is_integer(), false) -} -``` - -## Double Operations - -The `Rational` type supports the following double operations: - -```moonbit -test { - let a = @rational.new(1L, 2L).unwrap() - assert_eq(a.to_double(), 0.5) - assert_eq(@rational.from_double(0.5).to_string(), "1/2") -} -``` - -## String Operations - -The `Rational` type supports the following string operations: +The `Rational` type represents a rational number, which is a number that can be expressed as a fraction `a/b` where `a` and `b` are integers and `b` is not zero. -```moonbit -test { - let a = @rational.new(1L, 2L).unwrap() - assert_eq(a.to_string(), "1/2") -} -``` \ No newline at end of file +All tests and examples have been removed. Please refer to the new `moonbitlang/x` module for updated [documentation](https://mooncakes.io/docs/moonbitlang/x/rational) and examples. \ No newline at end of file diff --git a/bundled-core/rational/moon.pkg.json b/bundled-core/rational/moon.pkg.json index d7c1900..e9f5440 100644 --- a/bundled-core/rational/moon.pkg.json +++ b/bundled-core/rational/moon.pkg.json @@ -1,10 +1,8 @@ { "import": [ "moonbitlang/core/builtin", - "moonbitlang/core/result", "moonbitlang/core/double", "moonbitlang/core/int64", "moonbitlang/core/quickcheck" - ], - "test-import": ["moonbitlang/core/array"] + ] } diff --git a/bundled-core/rational/rational.mbti b/bundled-core/rational/pkg.generated.mbti similarity index 74% rename from bundled-core/rational/rational.mbti rename to bundled-core/rational/pkg.generated.mbti index cfd8eaf..6d40565 100644 --- a/bundled-core/rational/rational.mbti +++ b/bundled-core/rational/pkg.generated.mbti @@ -1,35 +1,43 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/rational" // Values -fn abs(T) -> T - -fn ceil(T) -> Int64 - -fn floor(T) -> Int64 - +#deprecated fn from_double(Double) -> T raise RationalError -fn neg(T) -> T - +#deprecated fn new(Int64, Int64) -> T? -fn trunc(T) -> Int64 - -// Types and methods +// Errors pub(all) suberror RationalError String impl Eq for RationalError impl Show for RationalError impl ToJson for RationalError +// Types and methods type T +#as_free_fn +#deprecated fn T::abs(Self) -> Self +#as_free_fn +#deprecated fn T::ceil(Self) -> Int64 +#as_free_fn +#deprecated fn T::floor(Self) -> Int64 +#deprecated fn T::fract(Self) -> Self +#deprecated fn T::is_integer(Self) -> Bool +#as_free_fn +#deprecated fn T::neg(Self) -> Self +#deprecated fn T::reciprocal(Self) -> Self +#deprecated fn T::to_double(Self) -> Double +#as_free_fn +#deprecated fn T::trunc(Self) -> Int64 impl Add for T impl Compare for T diff --git a/bundled-core/rational/rational.mbt b/bundled-core/rational/rational.mbt index 8297cf8..73c61c0 100644 --- a/bundled-core/rational/rational.mbt +++ b/bundled-core/rational/rational.mbt @@ -22,6 +22,8 @@ /// Invariants: /// - The denominator is always positive. /// - The numerator and denominator are always coprime. +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip struct T { numerator : Int64 denominator : Int64 @@ -49,34 +51,8 @@ fn gcd(a : Int64, b : Int64) -> Int64 { /// number is automatically reduced to its simplest form with a positive /// denominator. Returns `None` if the denominator is zero. /// -/// Example: -/// -/// ```moonbit -/// // Create 3/4 -/// let r1 = @rational.new(3L, 4L) -/// inspect(r1, content="Some(3/4)") -/// -/// // Create -1/2 (negative numerator) -/// let r2 = @rational.new(-1L, 2L) -/// inspect(r2, content="Some(-1/2)") -/// -/// // Create -1/2 (negative denominator gets normalized) -/// let r3 = @rational.new(1L, -2L) -/// inspect(r3, content="Some(-1/2)") -/// -/// // Create 1/2 (double negatives cancel out) -/// let r4 = @rational.new(-1L, -2L) -/// inspect(r4, content="Some(1/2)") -/// -/// // Automatic reduction to simplest form -/// let r5 = @rational.new(6L, 9L) -/// inspect(r5, content="Some(2/3)") -/// -/// // Division by zero returns None -/// let r6 = @rational.new(1L, 0L) -/// inspect(r6, content="None") -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn new(numerator : Int64, denominator : Int64) -> T? { if denominator == 0L { None @@ -104,7 +80,8 @@ fn new_unchecked(numerator : Int64, denominator : Int64) -> T { ///| /// NOTE: we don't check overflow here, to align with the `op_add` of `Int64`. /// TODO: add a `checked_add` method. -pub impl Add for T with op_add(self : T, other : T) -> T { +#coverage.skip +pub impl Add for T with add(self : T, other : T) -> T { new_unchecked( self.numerator * other.denominator + other.numerator * self.denominator, self.denominator * other.denominator, @@ -121,15 +98,8 @@ pub impl Add for T with op_add(self : T, other : T) -> T { /// /// Returns the difference of the two rational numbers. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(1L, 2L).unwrap() // 1/2 -/// let b = @rational.new(1L, 3L).unwrap() // 1/3 -/// inspect(a - b, content="1/6") // 1/2 - 1/3 = 1/6 -/// ``` -/// -pub impl Sub for T with op_sub(self : T, other : T) -> T { +#coverage.skip +pub impl Sub for T with sub(self : T, other : T) -> T { new_unchecked( self.numerator * other.denominator - other.numerator * self.denominator, self.denominator * other.denominator, @@ -146,16 +116,8 @@ pub impl Sub for T with op_sub(self : T, other : T) -> T { /// /// Returns the product of the two rational numbers. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(1L, 2L).unwrap() // 1/2 -/// let b = @rational.new(2L, 3L).unwrap() // 2/3 -/// let c = a * b // 1/3 -/// inspect(c, content="1/3") -/// ``` -/// -pub impl Mul for T with op_mul(self : T, other : T) -> T { +#coverage.skip +pub impl Mul for T with mul(self : T, other : T) -> T { new_unchecked( self.numerator * other.numerator, self.denominator * other.denominator, @@ -172,21 +134,8 @@ pub impl Mul for T with op_mul(self : T, other : T) -> T { /// /// Returns the quotient of the division as a rational number. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(1L, 2L) // 1/2 -/// let b = @rational.new(2L, 3L) // 2/3 -/// match (a, b) { -/// (Some(x), Some(y)) => { -/// let result = x / y // 3/4 -/// inspect(result, content="3/4") -/// } -/// _ => () -/// } -/// ``` -/// -pub impl Div for T with op_div(self : T, other : T) -> T { +#coverage.skip +pub impl Div for T with div(self : T, other : T) -> T { if other.numerator < 0L { new_unchecked( self.numerator * -other.denominator, @@ -209,18 +158,8 @@ pub impl Div for T with op_div(self : T, other : T) -> T { /// /// Returns a new rational number that is the reciprocal of the input. /// -/// Example: -/// -/// ```moonbit -/// let half = @rational.new(1L, 2L).unwrap() -/// let two = half.reciprocal() -/// inspect(two, content="2") -/// -/// let negative_third = @rational.new(-1L, 3L).unwrap() -/// let negative_three = negative_third.reciprocal() -/// inspect(negative_three, content="-3") -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn reciprocal(self : T) -> T { if self.numerator < 0L { new_unchecked(-self.denominator, -self.numerator) @@ -238,18 +177,9 @@ pub fn reciprocal(self : T) -> T { /// /// Returns a new rational number with the opposite sign. /// -/// Example: -/// -/// ```moonbit -/// let positive = @rational.new(3L, 4L).unwrap() -/// let negative = positive.neg() -/// inspect(negative.to_string(), content="-3/4") -/// -/// let negative_original = @rational.new(-5L, 2L).unwrap() -/// let positive_result = negative_original.neg() -/// inspect(positive_result.to_string(), content="5/2") -/// ``` -/// +#as_free_fn +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn T::neg(self : T) -> T { new_unchecked(-self.numerator, self.denominator) } @@ -263,15 +193,9 @@ pub fn T::neg(self : T) -> T { /// /// Returns a new rational number representing the absolute value of the input. /// -/// Example: -/// -/// ```moonbit -/// let positive = @rational.new(3L, 4L).unwrap() -/// let negative = @rational.new(-3L, 4L).unwrap() -/// inspect(positive.abs(), content="3/4") -/// inspect(negative.abs(), content="3/4") -/// ``` -/// +#as_free_fn +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn T::abs(self : T) -> T { new_unchecked(self.numerator.abs(), self.denominator) } @@ -286,18 +210,8 @@ pub fn T::abs(self : T) -> T { /// /// Returns `true` if the two rational numbers are equal, `false` otherwise. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(1L, 2L).unwrap() // 1/2 -/// let b = @rational.new(2L, 4L).unwrap() // 2/4 = 1/2 -/// inspect(a == b, content="true") -/// -/// let c = @rational.new(1L, 3L).unwrap() // 1/3 -/// inspect(a == c, content="false") -/// ``` -/// -pub impl Eq for T with op_equal(self : T, other : T) -> Bool { +#coverage.skip +pub impl Eq for T with equal(self : T, other : T) -> Bool { self.numerator * other.denominator == other.numerator * self.denominator } @@ -313,22 +227,7 @@ pub impl Eq for T with op_equal(self : T, other : T) -> Bool { /// than `other`, zero if they are equal, and positive if `self` is greater than /// `other`. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(1L, 2L).unwrap() // 1/2 -/// let b = @rational.new(2L, 3L).unwrap() // 2/3 -/// inspect(a.compare(b), content="-1") // 1/2 < 2/3 -/// -/// let c = @rational.new(3L, 4L).unwrap() // 3/4 -/// let d = @rational.new(3L, 4L).unwrap() // 3/4 -/// inspect(c.compare(d), content="0") // 3/4 == 3/4 -/// -/// let e = @rational.new(5L, 6L).unwrap() // 5/6 -/// let f = @rational.new(1L, 2L).unwrap() // 1/2 -/// inspect(e.compare(f), content="1") // 5/6 > 1/2 -/// ``` -/// +#coverage.skip pub impl Compare for T with compare(self : T, other : T) -> Int { let left = self.numerator * other.denominator let right = other.numerator * self.denominator @@ -346,16 +245,8 @@ pub impl Compare for T with compare(self : T, other : T) -> Int { /// Returns the double-precision floating-point approximation of the rational /// number. /// -/// Example: -/// -/// ```moonbit -/// let rational = @rational.new(1L, 2L).unwrap() -/// inspect(rational.to_double(), content="0.5") -/// -/// let negative = @rational.new(-3L, 4L).unwrap() -/// inspect(negative.to_double(), content="-0.75") -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn to_double(self : T) -> Double { // TODO: complete algorithm self.numerator.to_double() / self.denominator.to_double() @@ -378,19 +269,9 @@ fn[T] overflow_error() -> T raise RationalError { /// /// * `RationalError(String)` : Error with a descriptive message. /// -/// Example: -/// -/// ```moonbit -/// let error = RationalError::RationalError("division by zero") -/// inspect( -/// error, -/// content= -/// #|RationalError("division by zero") -/// , -/// ) -/// ``` -/// -pub(all) suberror RationalError String derive(Show, ToJson) +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip +pub(all) suberror RationalError String derive(Show, ToJson(style="flat")) ///| /// Compares two `RationalError` values for equality. @@ -403,19 +284,10 @@ pub(all) suberror RationalError String derive(Show, ToJson) /// Returns `true` if both `RationalError` values contain the same error message, /// `false` otherwise. /// -/// Example: -/// -/// ```moonbit -/// let error1 = @rational.RationalError("division by zero") -/// let error2 = @rational.RationalError("division by zero") -/// let error3 = @rational.RationalError("overflow") -/// inspect(error1 == error2, content="true") -/// inspect(error1 == error3, content="false") -/// ``` -/// -pub impl Eq for RationalError with op_equal( +#coverage.skip +pub impl Eq for RationalError with equal( self : RationalError, - other : RationalError + other : RationalError, ) -> Bool { match (self, other) { (RationalError(e1), RationalError(e2)) => e1 == e2 @@ -436,18 +308,8 @@ pub impl Eq for RationalError with op_equal( /// Throws an error of type `RationalError` if the input is NaN or if the /// conversion would result in integer overflow. /// -/// Example: -/// -/// ```moonbit -/// // Convert 0.5 to rational 1/2 -/// let half = @rational.from_double(0.5) -/// inspect(half, content="1/2") -/// -/// // Convert 0.333... to a close rational approximation -/// let third = @rational.from_double(1.0 / 3.0) -/// inspect(third, content="1/3") -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn from_double(value : Double) -> T raise RationalError { // continued fraction algorithm // Ported from https://github.com/rust-num/num @@ -472,7 +334,7 @@ pub fn from_double(value : Double) -> T raise RationalError { overflow_error() } for i in 0..= -9223372036854775808.0 && q < 9223372036854775808.0) { + if !(q >= -9223372036854775808.0 && q < 9223372036854775808.0) { break // overflow } let a = q.to_int64() @@ -480,7 +342,7 @@ pub fn from_double(value : Double) -> T raise RationalError { let f = q - a_f // Prevent overflow - if not(a == 0L) && + if !(a == 0L) && ( n1 > t_max / a || d1 > t_max / a || @@ -496,7 +358,7 @@ pub fn from_double(value : Double) -> T raise RationalError { n1 = n d1 = d let g = gcd(n1, d1) - if not(g == 0L) { + if !(g == 0L) { n1 = n1 / g d1 = d1 / g } @@ -532,19 +394,9 @@ pub fn from_double(value : Double) -> T raise RationalError { /// /// Returns the ceiling of the rational number as an `Int64`. /// -/// Example: -/// -/// ```moonbit -/// let r1 = @rational.new(3L, 2L).unwrap() // 3/2 = 1.5 -/// inspect(r1.ceil(), content="2") -/// -/// let r2 = @rational.new(-3L, 2L).unwrap() // -3/2 = -1.5 -/// inspect(r2.ceil(), content="-1") -/// -/// let r3 = @rational.new(4L, 2L).unwrap() // 4/2 = 2.0 -/// inspect(r3.ceil(), content="2") -/// ``` -/// +#as_free_fn +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn T::ceil(self : T) -> Int64 { let sign = if self.numerator < 0L { -1L } else { 1L } let quotient = self.numerator / self.denominator @@ -565,19 +417,9 @@ pub fn T::ceil(self : T) -> Int64 { /// /// Returns the largest `Int64` value that is less than or equal to `self`. /// -/// Example: -/// -/// ```moonbit -/// let r1 = @rational.new(7L, 3L).unwrap() // 7/3 โ‰ˆ 2.33 -/// inspect(r1.floor(), content="2") -/// -/// let r2 = @rational.new(-7L, 3L).unwrap() // -7/3 โ‰ˆ -2.33 -/// inspect(r2.floor(), content="-3") -/// -/// let r3 = @rational.new(6L, 3L).unwrap() // 6/3 = 2.0 -/// inspect(r3.floor(), content="2") -/// ``` -/// +#as_free_fn +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn T::floor(self : T) -> Int64 { let sign = if self.numerator < 0L { -1L } else { 1L } let quotient = self.numerator / self.denominator @@ -597,22 +439,9 @@ pub fn T::floor(self : T) -> Int64 { /// /// Returns the integer part of the rational number as an `Int64`. /// -/// Example: -/// -/// ```moonbit -/// let a = @rational.new(7L, 3L).unwrap() // 7/3 = 2.333... -/// inspect(@rational.trunc(a), content="2") -/// -/// let b = @rational.new(-7L, 3L).unwrap() // -7/3 = -2.333... -/// inspect(@rational.trunc(b), content="-2") -/// -/// let c = @rational.new(5L, 2L).unwrap() // 5/2 = 2.5 -/// inspect(@rational.trunc(c), content="2") -/// -/// let d = @rational.new(-5L, 2L).unwrap() // -5/2 = -2.5 -/// inspect(@rational.trunc(d), content="-2") -/// ``` -/// +#as_free_fn +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn T::trunc(self : T) -> Int64 { if self.numerator < 0L { -(-self.numerator / self.denominator) @@ -632,19 +461,8 @@ pub fn T::trunc(self : T) -> Int64 { /// Returns a new rational number representing the fractional part of `self`. /// This is equivalent to `self - self.trunc()`. /// -/// Example: -/// -/// ```moonbit -/// let r1 = @rational.new(7L, 2L).unwrap() // 7/2 = 3.5 -/// inspect(r1.fract(), content="1/2") // fractional part is 0.5 = 1/2 -/// -/// let r2 = @rational.new(-7L, 2L).unwrap() // -7/2 = -3.5 -/// inspect(r2.fract(), content="-1/2") // fractional part is -0.5 = -1/2 -/// -/// let r3 = @rational.new(5L, 1L).unwrap() // 5/1 = 5 -/// inspect(r3.fract(), content="0") // fractional part is 0 -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn fract(self : T) -> T { new_unchecked(self.numerator % self.denominator, self.denominator) } @@ -664,22 +482,7 @@ pub fn fract(self : T) -> T { /// the numerator /// * Otherwise, outputs in the format "numerator/denominator" /// -/// Example: -/// -/// ```moonbit -/// let zero = @rational.new(0, 1).unwrap() -/// inspect(zero, content="0") -/// -/// let integer = @rational.new(5, 1).unwrap() -/// inspect(integer, content="5") -/// -/// let fraction = @rational.new(3, 4).unwrap() -/// inspect(fraction, content="3/4") -/// -/// let negative = @rational.new(-7, 3).unwrap() -/// inspect(negative, content="-7/3") -/// ``` -/// +#coverage.skip pub impl Show for T with output(self, logger) { if self.numerator == 0L { logger.write_char('0') @@ -693,6 +496,7 @@ pub impl Show for T with output(self, logger) { } ///| +#coverage.skip pub impl @quickcheck.Arbitrary for T with arbitrary(size, rs) { let numerator : Int64 = @quickcheck.Arbitrary::arbitrary(size, rs) let denominator : Int64 = { @@ -716,356 +520,10 @@ pub impl @quickcheck.Arbitrary for T with arbitrary(size, rs) { /// Returns `true` if the rational number is an integer (has a denominator of 1), /// `false` otherwise. /// -/// Examples: -/// -/// ```moonbit -/// let half = @rational.new(1L, 2L).unwrap() -/// inspect(half.is_integer(), content="false") -/// -/// let whole = @rational.new(5L, 1L).unwrap() -/// inspect(whole.is_integer(), content="true") -/// -/// let zero = @rational.new(0L, 1L).unwrap() -/// inspect(zero.is_integer(), content="true") -/// ``` -/// +#deprecated("Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64.") +#coverage.skip pub fn is_integer(self : T) -> Bool { self.denominator == 1L } ///| -pub fnalias T::(neg, abs, ceil, floor, trunc) - -///| -test "op_add" { - // 1/2 + 1/3 = 5/6 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 3L) - let c = a + b - assert_eq(c.numerator, 5L) - assert_eq(c.denominator, 6L) - - // 1/2 + 1/2 = 1 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 2L) - let c = a + b - assert_eq(c.numerator, 1L) - assert_eq(c.denominator, 1L) - - // 1/2 + 1/6 = 2/3 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 6L) - let c = a + b - assert_eq(c.numerator, 2L) - - // -1/2 + 1/2 = 0 - let a = new_unchecked(-1L, 2L) - let b = new_unchecked(1L, 2L) - let c = a + b - assert_eq(c.numerator, 0L) - - // -1/2 + -1/2 = -1 - let a = new_unchecked(-1L, 2L) - let b = new_unchecked(-1L, 2L) - let c = a + b - assert_eq(c.numerator, -1L) - assert_eq(c.denominator, 1L) -} - -///| -test "op_sub" { - // 1/2 - 1/3 = 1/6 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 3L) - let c = a - b - assert_eq(c.numerator, 1L) - assert_eq(c.denominator, 6L) - - // 1/2 - 1/2 = 0 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 2L) - let c = a - b - assert_eq(c.numerator, 0L) - - // 1/2 - 1/6 = 1/3 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 6L) - let c = a - b - assert_eq(c.numerator, 1L) - assert_eq(c.denominator, 3L) - - // -1/2 - 1/2 = -1 - let a = new_unchecked(-1L, 2L) - let b = new_unchecked(1L, 2L) - let c = a - b - assert_eq(c.numerator, -1L) - assert_eq(c.denominator, 1L) - - // -1/2 - -1/2 = 0 - let a = new_unchecked(-1L, 2L) - let b = new_unchecked(-1L, 2L) - let c = a - b - assert_eq(c.numerator, 0L) -} - -///| -test "op_mul" { - // 1/2 * 2/3 = 1/3 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(2L, 3L) - let c = a * b - assert_eq(c.numerator, 1L) - assert_eq(c.denominator, 3L) - - // -4/3 * 3/4 = -1 - let a = new_unchecked(-4L, 3L) - let b = new_unchecked(3L, 4L) - let c = a * b - assert_eq(c.numerator, -1L) - assert_eq(c.denominator, 1L) - - // 1024/42 * 0 = 0 - let a = new_unchecked(1024L, 42L) - let b = new_unchecked(0L, 1L) - let c = a * b - assert_eq(c.numerator, 0L) - assert_eq(c.denominator, 1L) -} - -///| -test "op_div" { - // 1/2 / 2/3 = 3/4 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(2L, 3L) - let c = a / b - assert_eq(c.numerator, 3L) - assert_eq(c.denominator, 4L) - - // 1/2 / -2/3 = -3/4 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(-2L, 3L) - let c = a / b - assert_eq(c.numerator, -3L) - assert_eq(c.denominator, 4L) - - // 0 / 1 = 0 - let a = new_unchecked(0L, 1L) - let b = new_unchecked(1L, 1L) - let c = a / b - assert_eq(c.numerator, 0L) - assert_eq(c.denominator, 1L) -} - -///| -test "reciprocal" { - // 1/2 -> 2/1 - let a = new_unchecked(1L, 2L) - let b = a.reciprocal() - assert_eq(b.numerator, 2L) - assert_eq(b.denominator, 1L) - - // -1/2 -> -2/1 - let a = new_unchecked(-1L, 2L) - let b = a.reciprocal() - assert_eq(b.numerator, -2L) - assert_eq(b.denominator, 1L) -} - -///| -test "neg" { - // 1/2 -> -1/2 - let a = new_unchecked(1L, 2L) - let b = neg(a) - assert_eq(b.numerator, -1L) - assert_eq(b.denominator, 2L) - - // -1/2 -> 1/2 - let a = new_unchecked(-1L, 2L) - let b = neg(a) - assert_eq(b.numerator, 1L) - assert_eq(b.denominator, 2L) -} - -///| -test "abs" { - // 1/2 -> 1/2 - let a = new_unchecked(1L, 2L) - let b = abs(a) - assert_eq(b.numerator, 1L) - assert_eq(b.denominator, 2L) - - // -1/2 -> 1/2 - let a = new_unchecked(-1L, 2L) - let b = abs(a) - assert_eq(b.numerator, 1L) - assert_eq(b.denominator, 2L) -} - -///| -test "to_double" { - // 1/2 -> 0.5 - let a = new_unchecked(1L, 2L) - let b = a.to_double() - assert_eq(b, 0.5) - - // -1/2 -> -0.5 - let a = new_unchecked(-1L, 2L) - let b = a.to_double() - assert_eq(b, -0.5) -} - -///| -test "ceil" { - // 1/2 -> 1 - let a = new_unchecked(1L, 2L) - let b = ceil(a) - assert_eq(b, 1L) - - // -1/2 -> 0 - let a = new_unchecked(-1L, 2L) - let b = ceil(a) - assert_eq(b, 0L) - - // -2/2 -> -1 - let a = new_unchecked(-2L, 2L) - let b = ceil(a) - assert_eq(b, -1L) - - // 2/2 -> 1 - let a = new_unchecked(2L, 2L) - let b = ceil(a) - assert_eq(b, 1L) -} - -///| -test "floor" { - // 1/2 -> 0 - let a = new_unchecked(1L, 2L) - let b = floor(a) - assert_eq(b, 0L) - - // -1/2 -> -1 - let a = new_unchecked(-1L, 2L) - let b = floor(a) - assert_eq(b, -1L) - - // -2/2 -> -1 - let a = new_unchecked(-2L, 2L) - let b = floor(a) - assert_eq(b, -1L) - - // 2/2 -> 1 - let a = new_unchecked(2L, 2L) - let b = floor(a) - assert_eq(b, 1L) -} - -///| -test "trunc" { - // 1/2 -> 0 - let a = new_unchecked(1L, 2L) - let b = trunc(a) - assert_eq(b, 0L) - - // -1/2 -> 0 - let a = new_unchecked(-1L, 2L) - let b = trunc(a) - assert_eq(b, 0L) -} - -///| -test "fract" { - // 1/2 -> 1/2 - let a = new_unchecked(1L, 2L) - let b = a.fract() - assert_eq(b.numerator, 1L) - assert_eq(b.denominator, 2L) - - // -1/2 -> -1/2 - let a = new_unchecked(-1L, 2L) - let b = a.fract() - assert_eq(b.numerator, -1L) - assert_eq(b.denominator, 2L) - - // 3/2 -> 1/2 - let a = new_unchecked(3L, 2L) - let b = a.fract() - assert_eq(b.numerator, 1L) - assert_eq(b.denominator, 2L) - - // -3/2 -> -1/2 - let a = new_unchecked(-3L, 2L) - let b = a.fract() - assert_eq(b.numerator, -1L) - assert_eq(b.denominator, 2L) -} - -///| -test "to_string" { - // 1/2 -> "1/2" - let a = new_unchecked(1L, 2L) - let b = a.to_string() - assert_eq(b, "1/2") - - // 4/4 -> "1" - let a = new_unchecked(4L, 4L) - let b = a.to_string() - assert_eq(b, "1") - - // 0/1 -> "0" - let a = new_unchecked(0L, 1L) - let b = a.to_string() - assert_eq(b, "0") -} - -///| -test "is_integer" { - // 1/2 is not an integer - let a = new_unchecked(1L, 2L) - assert_false(a.is_integer()) - - // 1 is an integer - let a = new_unchecked(1L, 1L) - assert_true(a.is_integer()) - - // 0 is an integer - let a = new_unchecked(0L, 1L) - assert_true(a.is_integer()) - - // -1 is an integer - let a = new_unchecked(-1L, 1L) - assert_true(a.is_integer()) -} - -///| -test "eq and compare" { - // 1/2 < 2/3 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(2L, 3L) - assert_eq(a.compare(b), -1) - assert_true(a != b) - - // -1/2 > -2/3 - let a = new_unchecked(-1L, 2L) - let b = new_unchecked(-2L, 3L) - assert_eq(a.compare(b), 1) - assert_true(a != b) - - // 1/2 == 1/2 - let a = new_unchecked(1L, 2L) - let b = new_unchecked(1L, 2L) - assert_eq(a.compare(b), 0) - assert_true(a == b) -} - -///| -test "from_double normal case" { - let result = try? from_double(0.5) - assert_eq(result, Ok(new_unchecked(1L, 2L))) -} - -///| -test "from_double edge case" { - let result = try? from_double(9_223_372_036_800_000_000.0) - assert_eq(result, Ok(new_unchecked(9_223_372_036_800_000_000L, 1L))) -} diff --git a/bundled-core/rational/rational_test.mbt b/bundled-core/rational/rational_test.mbt index 44be9ec..a5d82b0 100644 --- a/bundled-core/rational/rational_test.mbt +++ b/bundled-core/rational/rational_test.mbt @@ -12,98 +12,5 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -test "new" { - // 1/2 - let a = @rational.new(1L, 2L) - inspect(a, content="Some(1/2)") - - // 1/0 - let a = @rational.new(1L, 0L) - inspect(a, content="None") -} - -///| -test "addition" { - let a = @rational.new(1L, 2L).unwrap() - let b = @rational.new(1L, 3L).unwrap() - let result = a + b - inspect(result, content="5/6") -} - -///| -test "subtraction" { - let a = @rational.new(3L, 4L).unwrap() - let b = @rational.new(1L, 4L).unwrap() - let result = a - b - inspect(result, content="1/2") -} - -///| -test "multiplication" { - let a = @rational.new(2L, 3L).unwrap() - let b = @rational.new(3L, 4L).unwrap() - let result = a * b - inspect(result, content="1/2") -} - -///| -test "division" { - let a = @rational.new(1L, 2L).unwrap() - let b = @rational.new(3L, 4L).unwrap() - let result = a / b - inspect(result, content="2/3") -} - -///| -test "from_double overflow" { - let result = try? @rational.from_double(10.0e200) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) - let result = try? @rational.from_double(-10.0e200) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) -} - -///| -test "from_double NaN" { - let result = try? @rational.from_double(@double.not_a_number) - assert_eq( - result, - Err(RationalError("Rational::from_double: cannot convert NaN")), - ) -} - -///| -test "from_double overflow edge case" { - let result = try? @rational.from_double(9_223_372_036_854_775_807.1) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) - let result = try? @rational.from_double(-9_223_372_036_854_775_807.1) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) -} - -///| -test "from_double infinity" { - let result = try? @rational.from_double(@double.infinity) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) - let result = try? @rational.from_double(@double.neg_infinity) - assert_eq(result, Err(RationalError("Rational::from_double: overflow"))) -} - -///| -test "from_double" { - let a : Array[Double] = [ - 0.5, 5, 29.97, -29.97, 63.5, 126.5, 127.0, 127.5, -63.5, -126.5, -127.0, -127.5, - ] - inspect( - a.map(@rational.from_double), - content="[1/2, 5, 2997/100, -2997/100, 127/2, 253/2, 127, 255/2, -127/2, -253/2, -127, -255/2]", - ) -} - -///| -test "rational arbitrary" { - let samples : Array[@rational.T] = @quickcheck.samples(20) - inspect( - samples, - content="[0, 0, 0, 0, 2/3, -2/5, 1, 1/6, 3/2, -1/2, 9/5, 5/3, -2, 7/12, -2/9, -8/15, 0, -5/6, 3/4, -3/7]", - ) -} +// All tests have been removed as the rational module is deprecated. +// Use @rational in module moonbitlang/x instead. Note that you need to rename Rational to Rational64. diff --git a/bundled-core/ref/ref.mbti b/bundled-core/ref/pkg.generated.mbti similarity index 87% rename from bundled-core/ref/ref.mbti rename to bundled-core/ref/pkg.generated.mbti index 8ef0fe6..ea9f6d8 100644 --- a/bundled-core/ref/ref.mbti +++ b/bundled-core/ref/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/ref" import( @@ -7,12 +8,13 @@ import( // Values fn[T] new(T) -> Ref[T] -fn[T] swap(Ref[T], Ref[T]) -> Unit +// Errors // Types and methods fn[T, R] Ref::map(Self[T], (T) -> R raise?) -> Self[R] raise? fn[T] Ref::new(T) -> Self[T] fn[T, R] Ref::protect(Self[T], T, () -> R raise?) -> R raise? +#as_free_fn fn[T] Ref::swap(Self[T], Self[T]) -> Unit fn[T] Ref::update(Self[T], (T) -> T raise?) -> Unit raise? impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Ref[X] diff --git a/bundled-core/ref/ref.mbt b/bundled-core/ref/ref.mbt index bcd8f6d..efbad86 100644 --- a/bundled-core/ref/ref.mbt +++ b/bundled-core/ref/ref.mbt @@ -23,7 +23,8 @@ test "to_string" { inspect(new(3), content="{val: 3}") } -///| same as `Ref::new` +///| +/// same as `Ref::new` pub fn[T] new(x : T) -> Ref[T] { { val: x } } @@ -69,7 +70,7 @@ pub fn[T, R] protect(self : Ref[T], a : T, f : () -> R raise?) -> R raise? { self.val = old raise err } - } else { + } noraise { r => { self.val = old r @@ -89,15 +90,13 @@ pub fn[T, R] protect(self : Ref[T], a : T, f : () -> R raise?) -> R raise? { /// assert_eq(x.val, 2) /// assert_eq(y.val, 1) /// ``` +#as_free_fn pub fn[T] Ref::swap(self : Ref[T], that : Ref[T]) -> Unit { let tmp = self.val self.val = that.val that.val = tmp } -///| -pub fnalias Ref::swap - ///| test "swap" { let x = new(1) @@ -133,7 +132,7 @@ test "incr" { ///| pub impl[X : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Ref[X] with arbitrary( size, - rs + rs, ) { new(X::arbitrary(size, rs)) } diff --git a/bundled-core/ref/ref_test.mbt b/bundled-core/ref/ref_test.mbt index 2bf22c2..e283277 100644 --- a/bundled-core/ref/ref_test.mbt +++ b/bundled-core/ref/ref_test.mbt @@ -84,3 +84,42 @@ test "Ref::new with string" { let r = Ref::new("hello") inspect(r, content="{val: \"hello\"}") } + +///| +test "map with error handling" { + let x = @ref.new(10) + let result = try? x.map(a => { + if a > 5 { + fail("value too large") + } + a * 2 + }) + assert_true(result is Err(_)) + let y = @ref.new(3) + let result2 = try? y.map(a => { + if a > 5 { + fail("value too large") + } + a * 2 + }) + match result2 { + Ok(r) => assert_eq(r.val, 6) + Err(_) => assert_true(false) + } +} + +///| +test "update with error handling" { + let x = @ref.new(5) + let result = try? x.update(a => { + if a == 5 { + fail("cannot update value 5") + } + a + 1 + }) + assert_true(result is Err(_)) + assert_eq(x.val, 5) // value should remain unchanged on error + let y = @ref.new(3) + y.update(a => a + 1) + assert_eq(y.val, 4) +} diff --git a/bundled-core/result/README.mbt.md b/bundled-core/result/README.mbt.md index e113a1d..92f05a6 100644 --- a/bundled-core/result/README.mbt.md +++ b/bundled-core/result/README.mbt.md @@ -28,9 +28,9 @@ You can check the variant of a `Result` using the `is_ok` and `is_err` methods. ```moonbit test { let result: Result[Int, String] = Ok(42) - let is_ok = result.is_ok() + let is_ok = result is Ok(_) assert_eq(is_ok, true) - let is_err = result.is_err() + let is_err = result is Err(_) assert_eq(is_err, false) } ``` diff --git a/bundled-core/result/deprecated.mbt b/bundled-core/result/deprecated.mbt index 24b856e..d86d6ae 100644 --- a/bundled-core/result/deprecated.mbt +++ b/bundled-core/result/deprecated.mbt @@ -13,32 +13,25 @@ // limitations under the License. ///| -#deprecated("use try? instead") -#coverage.skip -pub fn[T, E : Error] wrap0(f~ : () -> T raise E) -> Result[T, E] { - try f() |> Ok catch { - e => Err(e) - } +#deprecated("Use `Ok(value)` instead") +pub fn[T, E] ok(value : T) -> Result[T, E] { + Ok(value) } ///| -#deprecated("use try? instead") -#coverage.skip -pub fn[T, A, E : Error] wrap1(f~ : (A) -> T raise E, a : A) -> Result[T, E] { - try f(a) |> Ok catch { - e => Err(e) - } +#deprecated("Use `Err(value)` instead") +pub fn[T, E] err(value : E) -> Result[T, E] { + Err(value) } ///| -#deprecated("use try? instead") -#coverage.skip -pub fn[T, A, B, E : Error] wrap2( - f~ : (A, B) -> T raise E, - a : A, - b : B -) -> Result[T, E] { - try f(a, b) |> Ok catch { - e => Err(e) - } +#deprecated("use `x is Ok(_)` instead") +pub fn[T, E] is_ok(self : Result[T, E]) -> Bool { + self is Ok(_) +} + +///| +#deprecated("use `x is Err(_)` instead") +pub fn[T, E] is_err(self : Result[T, E]) -> Bool { + self is Err(_) } diff --git a/bundled-core/result/result.mbti b/bundled-core/result/pkg.generated.mbti similarity index 83% rename from bundled-core/result/result.mbti rename to bundled-core/result/pkg.generated.mbti index 1ead1d9..10d248f 100644 --- a/bundled-core/result/result.mbti +++ b/bundled-core/result/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/result" import( @@ -5,24 +6,22 @@ import( ) // Values -fn[T, E] err(E) -> Result[T, E] - -fn[T, E] ok(T) -> Result[T, E] - #deprecated -fn[T, E : Error] wrap0(f~ : () -> T raise E) -> Result[T, E] +fn[T, E] err(E) -> Result[T, E] #deprecated -fn[T, A, E : Error] wrap1(f~ : (A) -> T raise E, A) -> Result[T, E] +fn[T, E] ok(T) -> Result[T, E] -#deprecated -fn[T, A, B, E : Error] wrap2(f~ : (A, B) -> T raise E, A, B) -> Result[T, E] +// Errors // Types and methods fn[T, E, U] Result::bind(Self[T, E], (T) -> Self[U, E]) -> Self[U, E] fn[T, E] Result::flatten(Self[Self[T, E], E]) -> Self[T, E] +#deprecated fn[T, E, V] Result::fold(Self[T, E], (T) -> V, (E) -> V) -> V +#deprecated fn[T, E] Result::is_err(Self[T, E]) -> Bool +#deprecated fn[T, E] Result::is_ok(Self[T, E]) -> Bool fn[T, E, U] Result::map(Self[T, E], (T) -> U) -> Self[U, E] fn[T, E, F] Result::map_err(Self[T, E], (E) -> F) -> Self[T, F] diff --git a/bundled-core/result/result.mbt b/bundled-core/result/result.mbt index a8f27ce..51300d9 100644 --- a/bundled-core/result/result.mbt +++ b/bundled-core/result/result.mbt @@ -66,72 +66,6 @@ test "map_err" { assert_eq(w, Ok(6)) } -///| -/// Create an `Err` of type `E`. -/// -/// # Example -/// -/// ```mbt -/// let x: Result[Int, String] = Err("error") -/// assert_eq(x, Err("error")) -/// ``` -pub fn[T, E] err(value : E) -> Result[T, E] { - Err(value) -} - -///| -test "err" { - let x : Result[Int, String] = err("error") - assert_eq(x, Err("error")) -} - -///| -/// Create an `Ok` of type `T`. -/// -/// # Example -/// -/// ```mbt -/// let x: Result[String, Unit] = Ok("yes") -/// assert_true(x.is_ok()) -/// ``` -pub fn[T, E] ok(value : T) -> Result[T, E] { - Ok(value) -} - -///| -test "ok" { - let x : Result[String, Unit] = ok("yes") - assert_eq(x, Ok("yes")) -} - -///| -/// Check if a `Result` is an `Ok`. -pub fn[T, E] is_ok(self : Result[T, E]) -> Bool { - self is Ok(_) -} - -///| -test "is_ok" { - let x : Result[Int, String] = Ok(6) - let y : Result[Int, String] = Err("error") - assert_eq(x.is_ok(), true) - assert_eq(y.is_ok(), false) -} - -///| -/// Check if a `Result` is an `Err`. -pub fn[T, E] is_err(self : Result[T, E]) -> Bool { - self is Err(_) -} - -///| -test "is_err" { - let x : Result[Int, String] = Ok(6) - let y : Result[Int, String] = Err("error") - assert_eq(x.is_err(), false) - assert_eq(y.is_err(), true) -} - ///| /// Return the inner `Ok` value, if it exists, otherwise return the provided default. /// @@ -228,7 +162,7 @@ test "flatten" { /// ``` pub fn[T, E, U] bind( self : Result[T, E], - g : (T) -> Result[U, E] + g : (T) -> Result[U, E], ) -> Result[U, E] { match self { Ok(value) => g(value) @@ -244,16 +178,7 @@ test "bind" { } ///| -/// Folds a `Result` into a single value. -/// -/// If the `Result` is an `Ok`, the `ok` function is applied to the value. If the `Result` is an `Err`, the `err` function is applied to the value. -/// # Example -/// -/// ```mbt -/// let x = Ok(6) -/// let y = x.fold((v : Int) => { v * 7 }, (_e : String) => { 0 }) -/// assert_eq(y, 42) -/// ``` +#deprecated("use `match result { Ok(value) => ok(value); Err(error) => err(error) }` instead") pub fn[T, E, V] fold(self : Result[T, E], ok : (T) -> V, err : (E) -> V) -> V { match self { Ok(value) => ok(value) @@ -261,16 +186,6 @@ pub fn[T, E, V] fold(self : Result[T, E], ok : (T) -> V, err : (E) -> V) -> V { } } -///| -test "fold" { - let x : Result[Int, String] = Ok(6) - let y = x.fold((v : Int) => v * 7, (_e : String) => 0) - let z : Result[Int, String] = Err("error") - let w = z.fold((v : Int) => v * 7, (_e : String) => 0) - assert_eq(y, 42) - assert_eq(w, 0) -} - ///| /// Converts a `Result` to an `Option`. /// @@ -303,7 +218,7 @@ test "to_option" { ///| pub impl[T : Compare, E : Compare] Compare for Result[T, E] with compare( self : Result[T, E], - other : Result[T, E] + other : Result[T, E], ) -> Int { match (self, other) { (Ok(x), Ok(y)) => x.compare(y) @@ -365,16 +280,16 @@ test "show" { let ok : Result[_, String] = Ok("hello") inspect( ok, - content= + content=( #|Ok("hello") - , + ), ) let err : Result[String, _] = Err("world") inspect( err, - content= + content=( #|Err("world") - , + ), ) } @@ -395,14 +310,12 @@ test "unwrap exn" { Failure(msg) => Err(msg) }) |> inspect( - content= + content=( #|Err("This is serious") - , + ), ) } -///| - ///| pub impl[T : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary] @quickcheck.Arbitrary for Result[ T, diff --git a/bundled-core/result/result_test.mbt b/bundled-core/result/result_test.mbt index 1ff4670..09df1f4 100644 --- a/bundled-core/result/result_test.mbt +++ b/bundled-core/result/result_test.mbt @@ -32,9 +32,9 @@ test "wrap0 with error" { let result = try? f() inspect( result, - content= + content=( #|Err(Failure("error")) - , + ), ) } @@ -44,8 +44,8 @@ test "wrap2 with error result" { let r = try? f(1, 2) inspect( r, - content= + content=( #|Err(Failure("error")) - , + ), ) } diff --git a/bundled-core/set/README.mbt.md b/bundled-core/set/README.mbt.md new file mode 100644 index 0000000..f03fdfc --- /dev/null +++ b/bundled-core/set/README.mbt.md @@ -0,0 +1,305 @@ +# Set Package Documentation + +This package provides a hash-based set data structure that maintains insertion order. The `Set[K]` type stores unique elements and provides efficient membership testing, insertion, and deletion operations. + +## Creating Sets + +There are several ways to create sets: + +```moonbit +test "creating sets" { + // Empty set + let empty_set : @set.Set[Int] = @set.Set::new() + inspect(empty_set.size(), content="0") + inspect(empty_set.is_empty(), content="true") + + // Set with initial capacity + let set_with_capacity : @set.Set[Int] = @set.Set::new(capacity=16) + inspect(set_with_capacity.capacity(), content="16") + + // From array + let from_array = @set.Set::from_array([1, 2, 3, 2, 1]) // Duplicates are removed + inspect(from_array.size(), content="3") + + // From fixed array + let from_fixed = @set.Set::of([10, 20, 30]) + inspect(from_fixed.size(), content="3") + + // From iterator + let from_iter = @set.Set::from_iter([1, 2, 3, 4, 5].iter()) + inspect(from_iter.size(), content="5") +} +``` + +## Basic Operations + +Add, remove, and check membership: + +```moonbit +test "basic operations" { + let set = @set.Set::new() + + // Adding elements + set.add("apple") + set.add("banana") + set.add("cherry") + inspect(set.size(), content="3") + + // Adding duplicate (no effect) + set.add("apple") + inspect(set.size(), content="3") // Still 3 + + // Check membership + inspect(set.contains("apple"), content="true") + inspect(set.contains("orange"), content="false") + + // Remove elements + set.remove("banana") + inspect(set.contains("banana"), content="false") + inspect(set.size(), content="2") + + // Check if addition/removal was successful + let was_added = set.add_and_check("date") + inspect(was_added, content="true") + + let was_added_again = set.add_and_check("date") + inspect(was_added_again, content="false") // Already exists + + let was_removed = set.remove_and_check("cherry") + inspect(was_removed, content="true") + + let was_removed_again = set.remove_and_check("cherry") + inspect(was_removed_again, content="false") // Doesn't exist +} +``` + +## Set Operations + +Perform mathematical set operations: + +```moonbit +test "set operations" { + let set1 = @set.Set::from_array([1, 2, 3, 4]) + let set2 = @set.Set::from_array([3, 4, 5, 6]) + + // Union (all elements from both sets) + let union_set = set1.union(set2) + let union_array = union_set.to_array() + inspect(union_array.length(), content="6") // [1, 2, 3, 4, 5, 6] + + // Alternative union syntax + let union_alt = set1 | set2 + inspect(union_alt.size(), content="6") + + // Intersection (common elements) + let intersection_set = set1.intersection(set2) + let intersection_array = intersection_set.to_array() + inspect(intersection_array.length(), content="2") // [3, 4] + + // Alternative intersection syntax + let intersection_alt = set1 & set2 + inspect(intersection_alt.size(), content="2") + + // Difference (elements in first but not second) + let difference_set = set1.difference(set2) + let difference_array = difference_set.to_array() + inspect(difference_array.length(), content="2") // [1, 2] + + // Alternative difference syntax + let difference_alt = set1 - set2 + inspect(difference_alt.size(), content="2") + + // Symmetric difference (elements in either but not both) + let sym_diff_set = set1.symmetric_difference(set2) + let sym_diff_array = sym_diff_set.to_array() + inspect(sym_diff_array.length(), content="4") // [1, 2, 5, 6] + + // Alternative symmetric difference syntax + let sym_diff_alt = set1 ^ set2 + inspect(sym_diff_alt.size(), content="4") +} +``` + +## Set Relationships + +Test relationships between sets: + +```moonbit +test "set relationships" { + let small_set = @set.Set::from_array([1, 2]) + let large_set = @set.Set::from_array([1, 2, 3, 4]) + let disjoint_set = @set.Set::from_array([5, 6, 7]) + + // Subset testing + inspect(small_set.is_subset(large_set), content="true") + inspect(large_set.is_subset(small_set), content="false") + + // Superset testing + inspect(large_set.is_superset(small_set), content="true") + inspect(small_set.is_superset(large_set), content="false") + + // Disjoint testing (no common elements) + inspect(small_set.is_disjoint(disjoint_set), content="true") + inspect(small_set.is_disjoint(large_set), content="false") + + // Equal sets + let set1 = @set.Set::from_array([1, 2, 3]) + let set2 = @set.Set::from_array([3, 2, 1]) // Order doesn't matter + inspect(set1 == set2, content="true") +} +``` + +## Iteration and Conversion + +Iterate over sets and convert to other types: + +```moonbit +test "iteration and conversion" { + let set = @set.Set::from_array(["first", "second", "third"]) + + // Convert to array (maintains insertion order) + let array = set.to_array() + inspect(array.length(), content="3") + + // Iterate over elements + let mut count = 0 + set.each(fn(_element) { count = count + 1 }) + inspect(count, content="3") + + // Iterate with index + let mut indices_sum = 0 + set.eachi(fn(i, _element) { indices_sum = indices_sum + i }) + inspect(indices_sum, content="3") // 0 + 1 + 2 = 3 + + // Use iterator + let elements = set.iter().collect() + inspect(elements.length(), content="3") + + // Copy a set + let copied_set = set.copy() + inspect(copied_set.size(), content="3") + inspect(copied_set == set, content="true") +} +``` + +## Modifying Sets + +Clear and modify existing sets: + +```moonbit +test "modifying sets" { + let set = @set.Set::from_array([10, 20, 30, 40, 50]) + inspect(set.size(), content="5") + + // Clear all elements + set.clear() + inspect(set.size(), content="0") + inspect(set.is_empty(), content="true") + + // Add elements back + set.add(100) + set.add(200) + inspect(set.size(), content="2") + inspect(set.contains(100), content="true") +} +``` + +## JSON Serialization + +Sets can be serialized to JSON as arrays: + +```moonbit +test "json serialization" { + let set = @set.Set::from_array([1, 2, 3]) + let json = set.to_json() + + // JSON representation is an array + inspect(json, content="Array([Number(1), Number(2), Number(3)])") + + // String set + let string_set = @set.Set::from_array(["a", "b", "c"]) + let string_json = string_set.to_json() + inspect(string_json, content="Array([String(\"a\"), String(\"b\"), String(\"c\")])") +} +``` + +## Working with Different Types + +Sets work with any type that implements `Hash` and `Eq`: + +```moonbit +test "different types" { + // Integer set + let int_set = @set.Set::from_array([1, 2, 3, 4, 5]) + inspect(int_set.contains(3), content="true") + + // String set + let string_set = @set.Set::from_array(["hello", "world", "moonbit"]) + inspect(string_set.contains("world"), content="true") + + // Note: Char and Bool types don't implement Hash in this version + // So we use Int codes for demonstration + let char_codes = @set.Set::from_array([97, 98, 99]) // ASCII codes for 'a', 'b', 'c' + inspect(char_codes.contains(98), content="true") // 'b' = 98 + + // Integer set representing boolean values + let bool_codes = @set.Set::from_array([1, 0, 1]) // 1=true, 0=false + inspect(bool_codes.size(), content="2") // Only 1 and 0 +} +``` + +## Performance Examples + +Demonstrate efficient operations: + +```moonbit +test "performance examples" { + // Large set operations + let large_set = @set.Set::new(capacity=1000) + + // Add many elements + for i in 0..<100 { + large_set.add(i) + } + inspect(large_set.size(), content="100") + + // Fast membership testing + inspect(large_set.contains(50), content="true") + inspect(large_set.contains(150), content="false") + + // Efficient set operations on large sets + let another_set = @set.Set::new() + for i in 50..<150 { + another_set.add(i) + } + + let intersection = large_set.intersection(another_set) + inspect(intersection.size(), content="50") // Elements 50-99 +} +``` + +## Use Cases + +Sets are particularly useful for: + +1. **Removing duplicates**: Convert arrays to sets and back to remove duplicates +2. **Membership testing**: Fast O(1) average-case lookups +3. **Mathematical operations**: Union, intersection, difference operations +4. **Unique collections**: Maintaining collections of unique items +5. **Algorithm implementation**: Graph algorithms, caching, etc. + +## Performance Characteristics + +- **Insertion**: O(1) average case, O(n) worst case +- **Removal**: O(1) average case, O(n) worst case +- **Lookup**: O(1) average case, O(n) worst case +- **Space complexity**: O(n) where n is the number of elements +- **Iteration order**: Maintains insertion order (linked hash set) + +## Best Practices + +1. **Pre-size when possible**: Use `@set.Set::new(capacity=n)` if you know the approximate size +2. **Use appropriate types**: Ensure your key type has good `Hash` and `Eq` implementations +3. **Prefer set operations**: Use built-in union, intersection, etc. instead of manual loops +4. **Check return values**: Use `add_and_check` and `remove_and_check` when you need to know if the operation succeeded +5. **Consider memory usage**: Sets have overhead compared to arrays for small collections diff --git a/bundled-core/set/grow_heuristic.mbt b/bundled-core/set/grow_heuristic.mbt index b6e1bea..3650e77 100644 --- a/bundled-core/set/grow_heuristic.mbt +++ b/bundled-core/set/grow_heuristic.mbt @@ -12,31 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -fn power_2_above(x : Int, n : Int) -> Int { - for i = x { - if i >= n { - break i - } - let next = i << 1 - if next < 0 { - // overflow happened - break i - } - continue next - } -} - -///| -test "power_2_above" { - inspect(power_2_above(1, 15), content="16") - inspect(power_2_above(1, 16), content="16") - inspect(power_2_above(1, 17), content="32") - inspect(power_2_above(1, 32), content="32") - inspect(power_2_above(128, 33), content="128") - inspect(power_2_above(1, 2147483647), content="1073741824") -} - ///| fn calc_grow_threshold(capacity : Int) -> Int { capacity * 13 / 16 diff --git a/bundled-core/set/linked_hash_set.mbt b/bundled-core/set/linked_hash_set.mbt index 92cf3c4..e936c21 100644 --- a/bundled-core/set/linked_hash_set.mbt +++ b/bundled-core/set/linked_hash_set.mbt @@ -15,35 +15,35 @@ // Types ///| -priv struct SEntry[K] { - mut idx : Int +priv struct Entry[K] { + mut prev : Int + mut next : Entry[K]? mut psl : Int hash : Int key : K -} - -///| -impl[K : Eq] Eq for SEntry[K] with op_equal(self, other) { - self.hash == other.hash && self.key == other.key -} - -///| -priv struct SListNode[K] { - mut prev : SEntry[K]? - mut next : SEntry[K]? -} +} derive(Show) ///| /// Mutable linked hash set that maintains the order of insertion, not thread safe. +/// +/// # Example +/// +/// ```mbt +/// let set = @set.Set::of(["three", "eight", "one"]) +/// assert_eq(set.contains("two"), false) +/// assert_eq(set.contains("three"), true) +/// set.add("three") // no effect since it already exists +/// set.add("two") +/// assert_eq(set.contains("two"), true) +/// ``` struct Set[K] { - mut entries : FixedArray[SEntry[K]?] - mut list : FixedArray[SListNode[K]] - mut size : Int - mut capacity : Int - mut capacity_mask : Int - mut grow_at : Int - mut head : SEntry[K]? - mut tail : SEntry[K]? + mut entries : FixedArray[Entry[K]?] + mut size : Int // active keys count + mut capacity : Int // current capacity + mut capacity_mask : Int // capacity_mask = capacity - 1, used to find idx + mut grow_at : Int // threshold that triggers grow + mut head : Entry[K]? // head of linked list + mut tail : Int // tail of linked list } // Implementations @@ -52,22 +52,23 @@ struct Set[K] { /// Create a hash set. /// The capacity of the set will be the smallest power of 2 that is /// greater than or equal to the provided [capacity]. -pub fn[K] Set::new(capacity~ : Int = 8) -> Set[K] { - let capacity = power_2_above(8, capacity) +#as_free_fn +pub fn[K] Set::new(capacity? : Int = 8) -> Set[K] { + let capacity = capacity.next_power_of_two() { size: 0, capacity, capacity_mask: capacity - 1, grow_at: calc_grow_threshold(capacity), entries: FixedArray::make(capacity, None), - list: FixedArray::make(capacity, { prev: None, next: None }), head: None, - tail: None, + tail: -1, } } ///| /// Create a hash set from array. +#as_free_fn pub fn[K : Hash + Eq] Set::from_array(arr : Array[K]) -> Set[K] { let m = Set::new(capacity=arr.length()) arr.each(e => m.add(e)) @@ -77,232 +78,266 @@ pub fn[K : Hash + Eq] Set::from_array(arr : Array[K]) -> Set[K] { ///| /// Insert a key into the hash set. /// +/// Parameters: +/// +/// * `set` : The hash set to modify. +/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits. +/// +/// Example: +/// +/// ```moonbit +/// let set : @set.Set[String] = @set.Set::new() +/// set.add("key") +/// inspect(set.contains("key"), content="true") +/// set.add("key") // no effect since it already exists +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] add(self : Set[K], key : K) -> Unit { + self.add_with_hash(key, key.hash()) +} ///| -/// Insert a key into the hash set.if the key exists return false -pub fn[K : Hash + Eq] add_and_check(self : Set[K], key : K) -> Bool { +fn[K : Eq] add_with_hash(self : Set[K], key : K, hash : Int) -> Unit { if self.size >= self.grow_at { self.grow() } - let hash = key.hash() - let insert_entry = { idx: -1, psl: 0, hash, key } - let list_node : SListNode[K] = { prev: None, next: None } - for i = 0, idx = hash & self.capacity_mask, entry = insert_entry, node = list_node { + let (idx, psl) = for psl = 0, idx = hash & self.capacity_mask { match self.entries[idx] { - None => { - self.entries[idx] = Some(entry) - self.list[idx] = node - entry.idx = idx - self.add_entry_to_tail(insert_entry) - self.size += 1 - break true - } + None => break (idx, psl) Some(curr_entry) => { - let curr_node = self.list[curr_entry.idx] - if curr_entry.hash == entry.hash && curr_entry.key == entry.key { - break false + if curr_entry.hash == hash && curr_entry.key == key { + return } - if entry.psl > curr_entry.psl { - self.entries[idx] = Some(entry) - self.list[idx] = node - entry.idx = idx - curr_entry.psl += 1 - continue i + 1, (idx + 1) & self.capacity_mask, curr_entry, curr_node - } else { - entry.psl += 1 - continue i + 1, (idx + 1) & self.capacity_mask, entry, node + if psl > curr_entry.psl { + self.push_away(idx, curr_entry) + break (idx, psl) } + continue psl + 1, (idx + 1) & self.capacity_mask } } } + let entry = { prev: self.tail, next: None, psl, key, hash } + self.add_entry_to_tail(idx, entry) } ///| -/// Insert a key into the hash set. -pub fn[K : Hash + Eq] add(self : Set[K], key : K) -> Unit { +fn[K] push_away(self : Set[K], idx : Int, entry : Entry[K]) -> Unit { + for psl = entry.psl + 1, idx = (idx + 1) & self.capacity_mask, entry = entry { + match self.entries[idx] { + None => { + entry.psl = psl + self.set_entry(entry, idx) + break + } + Some(curr_entry) => + if psl > curr_entry.psl { + entry.psl = psl + self.set_entry(entry, idx) + continue curr_entry.psl + 1, + (idx + 1) & self.capacity_mask, + curr_entry + } else { + continue psl + 1, (idx + 1) & self.capacity_mask, entry + } + } + } +} + +///| +fn[K] set_entry(self : Set[K], entry : Entry[K], new_idx : Int) -> Unit { + self.entries[new_idx] = Some(entry) + match entry.next { + None => self.tail = new_idx + Some(next) => next.prev = new_idx + } +} + +///| +/// Insert a key into the hash set and returns whether the key was successfully added. +/// +/// Parameters: +/// +/// * `set` : The hash set to modify. +/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits. +/// +/// Returns `true` if the key was successfully added (i.e., it wasn't already present), +/// `false` if the key already existed in the set. +/// +/// Example: +/// +/// ```moonbit +/// let set : @set.Set[String] = @set.Set::new() +/// inspect(set.add_and_check("key"), content="true") // First insertion +/// inspect(set.add_and_check("key"), content="false") // Already exists +/// inspect(set.size(), content="1") +/// ``` +pub fn[K : Hash + Eq] add_and_check(self : Set[K], key : K) -> Bool { if self.size >= self.grow_at { self.grow() } let hash = key.hash() - let insert_entry = { idx: -1, psl: 0, hash, key } - let list_node : SListNode[K] = { prev: None, next: None } - for i = 0, idx = hash & self.capacity_mask, entry = insert_entry, node = list_node { + let (idx, psl, added) = for psl = 0, idx = hash & self.capacity_mask { match self.entries[idx] { - None => { - self.entries[idx] = Some(entry) - self.list[idx] = node - entry.idx = idx - self.add_entry_to_tail(insert_entry) - self.size += 1 - break - } + None => break (idx, psl, true) Some(curr_entry) => { - let curr_node = self.list[curr_entry.idx] - if curr_entry.hash == entry.hash && curr_entry.key == entry.key { - break + if curr_entry.hash == hash && curr_entry.key == key { + break (idx, psl, false) } - if entry.psl > curr_entry.psl { - self.entries[idx] = Some(entry) - self.list[idx] = node - entry.idx = idx - curr_entry.psl += 1 - continue i + 1, (idx + 1) & self.capacity_mask, curr_entry, curr_node - } else { - entry.psl += 1 - continue i + 1, (idx + 1) & self.capacity_mask, entry, node + if psl > curr_entry.psl { + self.push_away(idx, curr_entry) + break (idx, psl, true) } + continue psl + 1, (idx + 1) & self.capacity_mask } } } + if added { + let entry = { prev: self.tail, next: None, psl, key, hash } + self.add_entry_to_tail(idx, entry) + } + added } ///| /// Check if the hash set contains a key. -pub fn[K : Hash + Eq] contains(self : Set[K], key : K) -> Bool { +pub fn[K : Hash + Eq] Set::contains(self : Set[K], key : K) -> Bool { + // inline lookup to avoid unnecessary allocations let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { - match self.entries[idx] { - Some(entry) => { - if entry.hash == hash && entry.key == key { - break true - } - if i > entry.psl { - break false - } - continue i + 1, (idx + 1) & self.capacity_mask - } - None => break false + guard self.entries[idx] is Some(entry) else { break false } + if entry.hash == hash && entry.key == key { + break true } + if i > entry.psl { + break false + } + continue i + 1, (idx + 1) & self.capacity_mask } } ///| -/// Remove a key from hash set. +/// Remove a key from the hash set. If the key exists in the set, removes it +/// and adjusts the probe sequence length (PSL) of subsequent entries to +/// maintain the Robin Hood hashing invariant. If the key does not exist, +/// the set remains unchanged. +/// +/// Parameters: +/// +/// * `self` : The hash set to remove the key from. +/// * `key` : The key to remove from the set. +/// +/// Example: +/// +/// ```moonbit +/// let set = @set.Set::of(["a", "b"]) +/// set.remove("a") +/// inspect(set.contains("a"), content="false") +/// inspect(set.size(), content="1") +/// ``` pub fn[K : Hash + Eq] remove(self : Set[K], key : K) -> Unit { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { - match self.entries[idx] { - Some(entry) => { - if entry.hash == hash && entry.key == key { - self.entries[idx] = None - self.remove_entry(entry) - self.shift_back(idx) - self.size -= 1 - break - } - if i > entry.psl { - break - } - continue i + 1, (idx + 1) & self.capacity_mask - } - None => break + guard self.entries[idx] is Some(entry) else { break } + if entry.hash == hash && entry.key == key { + self.remove_entry(entry) + self.shift_back(idx) + self.size -= 1 + break } + if i > entry.psl { + break + } + continue i + 1, (idx + 1) & self.capacity_mask } } ///| -/// Remove a key from hash set.if the key exists, delete it and return true +/// Remove a key from the hash set and returns whether the key was successfully removed. +/// +/// Parameters: +/// +/// * `set` : The hash set to modify. +/// * `key` : The key to remove. Must implement `Hash` and `Eq` traits. +/// +/// Returns `true` if the key was successfully removed (i.e., it was present), +/// `false` if the key didn't exist in the set. +/// +/// Example: +/// +/// ```moonbit +/// let set = @set.Set::of(["a", "b"]) +/// inspect(set.remove_and_check("a"), content="true") // Successfully removed +/// inspect(set.remove_and_check("a"), content="false") // Already removed +/// inspect(set.size(), content="1") +/// ``` pub fn[K : Hash + Eq] remove_and_check(self : Set[K], key : K) -> Bool { let hash = key.hash() for i = 0, idx = hash & self.capacity_mask { - match self.entries[idx] { - Some(entry) => { - if entry.hash == hash && entry.key == key { - self.entries[idx] = None - self.remove_entry(entry) - self.shift_back(idx) - self.size -= 1 - break true - } - if i > entry.psl { - break false - } - continue i + 1, (idx + 1) & self.capacity_mask - } - None => break false + guard self.entries[idx] is Some(entry) else { break false } + if entry.hash == hash && entry.key == key { + self.remove_entry(entry) + self.shift_back(idx) + self.size -= 1 + break true + } + if i > entry.psl { + break false } + continue i + 1, (idx + 1) & self.capacity_mask } } ///| -fn[K] add_entry_to_tail(self : Set[K], entry : SEntry[K]) -> Unit { +fn[K] add_entry_to_tail(self : Set[K], idx : Int, entry : Entry[K]) -> Unit { match self.tail { - None => { - self.head = Some(entry) - self.tail = Some(entry) - } - Some(tail) => { - self.list[tail.idx].next = Some(entry) - self.list[entry.idx].prev = Some(tail) - self.tail = Some(entry) - } + -1 => self.head = Some(entry) + tail => self.entries[tail].unwrap().next = Some(entry) } + self.tail = idx + self.entries[idx] = Some(entry) + self.size += 1 } ///| -fn[K : Eq] remove_entry(self : Set[K], entry : SEntry[K]) -> Unit { - let node = self.list[entry.idx] - if self.is_empty() { - self.head = None - self.tail = None - } else { - if self.head.unwrap() == entry { - self.head = node.next - } - if self.tail.unwrap() == entry { - self.tail = node.prev - } - if node.prev is Some(prev) { - self.list[prev.idx].next = node.next - } - if node.next is Some(next) { - self.list[next.idx].prev = node.prev - } +fn[K] remove_entry(self : Set[K], entry : Entry[K]) -> Unit { + match entry.prev { + -1 => self.head = entry.next + idx => self.entries[idx].unwrap().next = entry.next + } + match entry.next { + None => self.tail = entry.prev + Some(next) => next.prev = entry.prev } - node.prev = None - node.next = None } ///| -fn[K] shift_back(self : Set[K], start_index : Int) -> Unit { - for prev = start_index, curr = (start_index + 1) & self.capacity_mask { - match (self.entries[curr], self.list[curr]) { - (Some(entry), currNode) => { - if entry.psl == 0 { - break - } - entry.psl -= 1 - entry.idx = prev - self.entries[prev] = Some(entry) - self.entries[curr] = None - self.list[prev].prev = currNode.prev - self.list[prev].next = currNode.next - currNode.prev = None - currNode.next = None - continue curr, (curr + 1) & self.capacity_mask - } - (None, _) => break +fn[K] shift_back(self : Set[K], idx : Int) -> Unit { + let next = (idx + 1) & self.capacity_mask + match self.entries[next] { + None | Some({ psl: 0, .. }) => self.entries[idx] = None + Some(entry) => { + entry.psl -= 1 + self.set_entry(entry, idx) + self.shift_back(next) } } } ///| -fn[K : Hash + Eq] grow(self : Set[K]) -> Unit { +fn[K : Eq] grow(self : Set[K]) -> Unit { let old_head = self.head - let old_list = self.list let new_capacity = self.capacity << 1 self.entries = FixedArray::make(new_capacity, None) - self.list = FixedArray::make(new_capacity, { prev: None, next: None }) self.capacity = new_capacity self.capacity_mask = new_capacity - 1 self.grow_at = calc_grow_threshold(self.capacity) self.size = 0 self.head = None - self.tail = None + self.tail = -1 loop old_head { - Some({ idx, key, .. }) => { - self.add(key) - continue old_list[idx].next + Some({ next, key, hash, .. }) => { + self.add_with_hash(key, hash) + continue next } None => break } @@ -315,21 +350,16 @@ pub impl[K : Show] Show for Set[K] with output(self, logger) { logger.write_string("{") loop (0, self.head) { (_, None) => logger.write_string("}") - (i, Some({ key, idx, .. })) => { + (i, Some({ key, next, .. })) => { if i > 0 { logger.write_string(", ") } logger.write_object(key) - continue (i + 1, self.list[idx].next) + continue (i + 1, next) } } } -///| -pub impl[K] Default for Set[K] with default() { - Set::new() -} - ///| /// Get the number of keys in the set. pub fn[K] size(self : Set[K]) -> Int { @@ -350,11 +380,12 @@ pub fn[K] is_empty(self : Set[K]) -> Bool { ///| /// Iterate over all keys of the set in the order of insertion. +#locals(f) pub fn[K] each(self : Set[K], f : (K) -> Unit raise?) -> Unit raise? { loop self.head { - Some({ key, idx, .. }) => { + Some({ key, next, .. }) => { f(key) - continue self.list[idx].next + continue next } None => break } @@ -362,11 +393,12 @@ pub fn[K] each(self : Set[K], f : (K) -> Unit raise?) -> Unit raise? { ///| /// Iterate over all keys of the set in the order of insertion, with index. +#locals(f) pub fn[K] eachi(self : Set[K], f : (Int, K) -> Unit raise?) -> Unit raise? { loop (0, self.head) { - (i, Some({ key, idx, .. })) => { + (i, Some({ key, next, .. })) => { f(i, key) - continue (i + 1, self.list[idx].next) + continue (i + 1, next) } (_, None) => break } @@ -378,18 +410,16 @@ pub fn[K] clear(self : Set[K]) -> Unit { self.entries.fill(None) self.size = 0 self.head = None - self.tail = None + self.tail = -1 } ///| /// Returns the iterator of the hash set, provide elements in the order of insertion. pub fn[K] iter(self : Set[K]) -> Iter[K] { Iter::new(yield_ => loop self.head { - Some({ key, idx, .. }) => { - if yield_(key) == IterEnd { - break IterEnd - } - continue self.list[idx].next + Some({ key, next, .. }) => { + guard yield_(key) is IterContinue else { break IterEnd } + continue next } None => break IterContinue }) @@ -398,38 +428,32 @@ pub fn[K] iter(self : Set[K]) -> Iter[K] { ///| /// Converts the hash set to an array. pub fn[K] to_array(self : Set[K]) -> Array[K] { - let res = Array::new(capacity=self.size) + let arr = Array::new(capacity=self.size) loop self.head { - Some({ key, idx, .. }) => { - res.push(key) - continue self.list[idx].next + Some({ key, next, .. }) => { + arr.push(key) + continue next } None => break } - res + arr } ///| -pub impl[K : Hash + Eq] Eq for Set[K] with op_equal(self, other) { - if self.size != other.size { - return false - } - loop self.head { - None => true - Some({ key, idx, .. }) => { - if not(other.contains(key)) { - return false - } - continue self.list[idx].next - } +pub impl[K : Hash + Eq] Eq for Set[K] with equal(self, other) { + guard self.size == other.size else { return false } + for k in self { + guard other.contains(k) else { return false } + } else { + true } } ///| +#as_free_fn pub fn[K : Hash + Eq] Set::of(arr : FixedArray[K]) -> Set[K] { let length = arr.length() let m = Set::new(capacity=length) - // arr.iter((e) => { m.set(e.0, e.1) }) for i in 0.. Set[K] { } ///| +#as_free_fn pub fn[K : Hash + Eq] Set::from_iter(iter : Iter[K]) -> Set[K] { let m = Set::new() - iter.each(e => m.add(e)) + for e in iter { + m.add(e) + } m } +///| +pub impl[K] Default for Set[K] with default() { + Set::new() +} + +///| +/// Copy the set, creating a new set with the same keys and order of insertion. +pub fn[K] copy(self : Set[K]) -> Set[K] { + // copy structure + let other = { + capacity: self.capacity, + entries: FixedArray::make(self.capacity, None), + size: self.size, + capacity_mask: self.capacity_mask, + grow_at: self.grow_at, + head: None, + tail: self.tail, + } + if self.size == 0 { + return other + } + guard self.entries[self.tail] is Some(last) + loop (last, self.tail, None) { + ({ prev, psl, hash, key, .. }, idx, next) => { + let new_entry = { prev, next, psl, hash, key } + other.entries[idx] = Some(new_entry) + if prev != -1 { + continue (self.entries[prev].unwrap(), prev, Some(new_entry)) + } else { + other.head = Some(new_entry) + } + } + } + other +} + ///| pub fn[K : Hash + Eq] difference(self : Set[K], other : Set[K]) -> Set[K] { let m = Set::new() - self.each(k => if not(other.contains(k)) { m.add(k) }) + self.each(k => if !other.contains(k) { m.add(k) }) m } ///| pub fn[K : Hash + Eq] symmetric_difference( self : Set[K], - other : Set[K] + other : Set[K], ) -> Set[K] { let m = Set::new() - self.each(k => if not(other.contains(k)) { m.add(k) }) - other.each(k => if not(self.contains(k)) { m.add(k) }) + self.each(k => if !other.contains(k) { m.add(k) }) + other.each(k => if !self.contains(k) { m.add(k) }) m } @@ -485,3 +548,69 @@ pub impl[X : ToJson] ToJson for Set[X] with to_json(self) { } Json::array(res) } + +///| +/// Check if two sets have no common elements. +pub fn[K : Hash + Eq] is_disjoint(self : Set[K], other : Set[K]) -> Bool { + if self.size() <= other.size() { + for k in self { + if other.contains(k) { + return false + } + } + } else { + for k in other { + if self.contains(k) { + return false + } + } + } + true +} + +///| +/// Check if the current set is a subset of another set. +pub fn[K : Hash + Eq] is_subset(self : Set[K], other : Set[K]) -> Bool { + if self.size() <= other.size() { + for k in self { + if !other.contains(k) { + return false + } + } + true + } else { + false + } +} + +///| +/// Check if the current set is a superset of another set. +pub fn[K : Hash + Eq] is_superset(self : Set[K], other : Set[K]) -> Bool { + other.is_subset(self) +} + +///| +/// Intersection of two hash sets. +pub impl[K : Hash + Eq] BitAnd for Set[K] with land(self, other) { + self.intersection(other) +} + +///| +/// Union of two hash sets. +pub impl[K : Hash + Eq] BitOr for Set[K] with lor(self, other) { + self.union(other) +} + +///| +/// Symmetric difference of two hash sets. +pub impl[K : Hash + Eq] BitXOr for Set[K] with lxor(self, other) { + self.symmetric_difference(other) +} + +///| +/// Difference of two hash sets. +pub impl[K : Hash + Eq] Sub for Set[K] with sub(self, other) { + self.difference(other) +} + +///| diff --git a/bundled-core/set/linked_hash_set_test.mbt b/bundled-core/set/linked_hash_set_test.mbt index cff2682..81147e4 100644 --- a/bundled-core/set/linked_hash_set_test.mbt +++ b/bundled-core/set/linked_hash_set_test.mbt @@ -17,14 +17,14 @@ let default_init_capacity = 8 ///| test "new" { - let m : Set[Int] = Set::new() + let m : @set.Set[Int] = @set.Set::new() assert_eq(m.capacity(), default_init_capacity) assert_eq(m.size(), 0) } ///| test "insert" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.add("c") @@ -36,7 +36,7 @@ test "insert" { ///| test "add remove" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.add("c") @@ -45,7 +45,7 @@ test "add remove" { } assert_true(m.add_and_check("test")) assert_false(m.add_and_check("test")) - assert_true(not(m.contains("a")) && m.contains("d")) + assert_true(!m.contains("a") && m.contains("d")) assert_true(m.contains("b")) assert_true(m.contains("c")) assert_true(m.contains("d")) @@ -53,7 +53,7 @@ test "add remove" { ///| test "from_array" { - let m = Set::of(["a", "b", "c"]) + let m = @set.Set::of(["a", "b", "c"]) assert_true(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -62,7 +62,7 @@ test "from_array" { ///| test "size" { - let m = Set::new() + let m = @set.Set::new() assert_eq(m.size(), 0) m.add("a") assert_eq(m.size(), 1) @@ -70,7 +70,7 @@ test "size" { ///| test "is_empty" { - let m = Set::new() + let m = @set.Set::new() assert_eq(m.is_empty(), true) m.add("a") assert_eq(m.is_empty(), false) @@ -80,7 +80,7 @@ test "is_empty" { ///| test "iter" { - let m = Set::of(["a", "b", "c"]) + let m = @set.Set::of(["a", "b", "c"]) let mut sum = "" m.each(k => sum += k) inspect(sum, content="abc") @@ -88,7 +88,7 @@ test "iter" { ///| test "iteri" { - let m = Set::of(["1", "2", "3"]) + let m = @set.Set::of(["1", "2", "3"]) let mut s = "" let mut sum = 0 m.eachi((i, k) => { @@ -101,8 +101,8 @@ test "iteri" { ///| test "union" { - let m1 = Set::of(["a", "b", "c"]) - let m2 = Set::of(["b", "c", "d"]) + let m1 = @set.Set::of(["a", "b", "c"]) + let m2 = @set.Set::of(["b", "c", "d"]) let m = m1.union(m2) assert_eq(m.size(), 4) assert_true(m.contains("a")) @@ -113,8 +113,8 @@ test "union" { ///| test "intersection" { - let m1 = Set::of(["a", "b", "c"]) - let m2 = Set::of(["b", "c", "d"]) + let m1 = @set.Set::of(["a", "b", "c"]) + let m2 = @set.Set::of(["b", "c", "d"]) let m = m1.intersection(m2) assert_eq(m.size(), 2) assert_false(m.contains("a")) @@ -125,8 +125,8 @@ test "intersection" { ///| test "difference" { - let m1 = Set::of(["a", "b", "c"]) - let m2 = Set::of(["b", "c", "d"]) + let m1 = @set.Set::of(["a", "b", "c"]) + let m2 = @set.Set::of(["b", "c", "d"]) let m = m1.difference(m2) assert_eq(m.size(), 1) assert_true(m.contains("a")) @@ -137,8 +137,8 @@ test "difference" { ///| test "symmetric_difference" { - let m1 = Set::of(["a", "b", "c"]) - let m2 = Set::of(["b", "c", "d"]) + let m1 = @set.Set::of(["a", "b", "c"]) + let m2 = @set.Set::of(["b", "c", "d"]) let m = m1.symmetric_difference(m2) assert_eq(m.size(), 2) assert_true(m.contains("a")) @@ -150,7 +150,7 @@ test "symmetric_difference" { ///| test "iter" { let buf = StringBuilder::new(size_hint=20) - let map = Set::of(["a", "b", "c"]) + let map = @set.Set::of(["a", "b", "c"]) map.iter().each(e => buf.write_string("[\{e}]")) inspect(buf, content="[a][b][c]") buf.reset() @@ -161,7 +161,7 @@ test "iter" { ///| test "from_array" { let arr = ["a", "b", "c"] - let m = Set::from_array(arr) + let m = @set.Set::from_array(arr) assert_true(m.contains("a")) assert_true(m.contains("b")) assert_true(m.contains("c")) @@ -170,7 +170,7 @@ test "from_array" { ///| test "insert_and_grow" { - let m = Set::new() + let m = @set.Set::new() for i in 0..<10 { m.add(i.to_string()) } @@ -182,7 +182,7 @@ test "insert_and_grow" { ///| test "array unique via Set" { let v = [1, 2, 3, 4, 5, 3, 2, 4, 5] - let h = Set::from_iter(v.iter()) + let h = @set.Set::from_iter(v.iter()) let v = [..h] @json.inspect([..h], content=[1, 2, 3, 4, 5]) @json.inspect(v, content=[1, 2, 3, 4, 5]) @@ -190,7 +190,7 @@ test "array unique via Set" { ///| test "remove_and_shift_back" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.add("c") @@ -204,7 +204,7 @@ test "remove_and_shift_back" { ///| test "capacity_and_size" { - let m = Set::new() + let m = @set.Set::new() assert_eq(m.capacity(), default_init_capacity) assert_eq(m.size(), 0) m.add("a") @@ -213,7 +213,7 @@ test "capacity_and_size" { ///| test "clear_and_reinsert" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.clear() @@ -225,7 +225,7 @@ test "clear_and_reinsert" { ///| test "insert_and_grow" { - let m = Set::new() + let m = @set.Set::new() for i in 0..<10 { m.add(i.to_string()) } @@ -235,7 +235,7 @@ test "insert_and_grow" { ///| test "remove_and_shift_back" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.add("c") @@ -249,7 +249,7 @@ test "remove_and_shift_back" { ///| test "capacity_and_size" { - let m = Set::new() + let m = @set.Set::new() assert_eq(m.capacity(), default_init_capacity) assert_eq(m.size(), 0) m.add("a") @@ -258,7 +258,7 @@ test "capacity_and_size" { ///| test "clear_and_reinsert" { - let m = Set::new() + let m = @set.Set::new() m.add("a") m.add("b") m.clear() @@ -270,23 +270,23 @@ test "clear_and_reinsert" { ///| test "from_iter multiple elements iter" { - inspect(Set::from_iter([1, 2, 3].iter()), content="{1, 2, 3}") + inspect(@set.Set::from_iter([1, 2, 3].iter()), content="{1, 2, 3}") } ///| test "from_iter single element iter" { - inspect(Set::from_iter([1].iter()), content="{1}") + inspect(@set.Set::from_iter([1].iter()), content="{1}") } ///| test "from_iter empty iter" { - let map : Set[Int] = Set::from_iter(Iter::empty()) + let map : @set.Set[Int] = @set.Set::from_iter(Iter::empty()) inspect(map, content="{}") } ///| test "remove item causes break when psl < i" { - let set = Set::new() + let set = @set.Set::new() set ..add(1) ..add(11) // This goes to a different bucket due to hash collision @@ -296,63 +296,63 @@ test "remove item causes break when psl < i" { ///| test "to_array_empty" { - inspect((Set::new() : Set[Int]).to_array(), content="[]") + inspect((@set.Set::new() : @set.Set[Int]).to_array(), content="[]") } ///| test "to_array_non_empty" { - let set = Set::new() + let set = @set.Set::new() set..add(1)..add(2)..add(3) inspect(set.to_array(), content="[1, 2, 3]") } ///| test "op_equal: different size" { - let set1 = Set::new() + let set1 = @set.Set::new() set1.add(1) - let set2 = Set::new() + let set2 = @set.Set::new() inspect(set1 == set2, content="false") } ///| test "op_equal: different keys" { - let set1 = Set::new() + let set1 = @set.Set::new() set1.add(1) - let set2 = Set::new() + let set2 = @set.Set::new() set2.add(2) inspect(set1 == set2, content="false") } ///| test "op_equal: same sets" { - let set1 = Set::new() + let set1 = @set.Set::new() set1.add(1) - let set2 = Set::new() + let set2 = @set.Set::new() set2.add(1) inspect(set1 == set2, content="true") } ///| test "op_equal: empty sets" { - let set1 : Set[Int] = Set::new() - let set2 : Set[Int] = Set::new() + let set1 : @set.Set[Int] = @set.Set::new() + let set2 : @set.Set[Int] = @set.Set::new() inspect(set1 == set2, content="true") } ///| test "op_equal: same elements different order" { - let set1 = Set::new() + let set1 = @set.Set::new() set1..add(1)..add(2)..add(3) - let set2 = Set::new() + let set2 = @set.Set::new() set2..add(3)..add(2)..add(1) inspect(set1 == set2, content="true") } ///| test "op_equal: subset" { - let set1 = Set::new() + let set1 = @set.Set::new() set1..add(1)..add(2)..add(3) - let set2 = Set::new() + let set2 = @set.Set::new() set2..add(1)..add(2) inspect(set1 == set2, content="false") inspect(set2 == set1, content="false") @@ -360,8 +360,8 @@ test "op_equal: subset" { ///| test "op_equal: large sets" { - let set1 = Set::new() - let set2 = Set::new() + let set1 = @set.Set::new() + let set2 = @set.Set::new() for i in 0..<100 { set1.add(i) set2.add(i) @@ -371,8 +371,8 @@ test "op_equal: large sets" { ///| test "op_equal: large sets with one difference" { - let set1 = Set::new() - let set2 = Set::new() + let set1 = @set.Set::new() + let set2 = @set.Set::new() for i in 0..<100 { set1.add(i) if i != 50 { @@ -386,9 +386,9 @@ test "op_equal: large sets with one difference" { ///| test "op_equal: after removal" { - let set1 = Set::new() + let set1 = @set.Set::new() set1..add(1)..add(2)..add(3) - let set2 = Set::new() + let set2 = @set.Set::new() set2..add(1)..add(2)..add(3) set1.remove(2) inspect(set1 == set2, content="false") @@ -398,9 +398,9 @@ test "op_equal: after removal" { ///| test "op_equal: after clear" { - let set1 = Set::new() + let set1 = @set.Set::new() set1..add(1)..add(2)..add(3) - let set2 = Set::new() + let set2 = @set.Set::new() inspect(set1 == set2, content="false") set1.clear() inspect(set1 == set2, content="true") @@ -408,9 +408,9 @@ test "op_equal: after clear" { ///| test "op_equal: with string elements" { - let set1 = Set::new() + let set1 = @set.Set::new() set1..add("apple")..add("banana")..add("cherry") - let set2 = Set::new() + let set2 = @set.Set::new() set2..add("apple")..add("banana")..add("cherry") inspect(set1 == set2, content="true") set1.remove("banana") @@ -419,7 +419,7 @@ test "op_equal: with string elements" { ///| test "op_equal: reflexivity" { - let set = Set::new() + let set = @set.Set::new() set..add(1)..add(2)..add(3) inspect(set == set, content="true") } @@ -427,14 +427,72 @@ test "op_equal: reflexivity" { ///| test "remove_and_check when key not found in empty slot" { // Try to remove from empty set - inspect(Set::new().remove_and_check(1), content="false") + inspect(@set.Set::new().remove_and_check(1), content="false") } ///| test "trigger grow" { - let set = Set::new(capacity=2) + let set = @set.Set::new(capacity=2) for i in 0..<10 { assert_true(set.add_and_check(i)) } inspect(set, content="{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}") } + +///| +test "is_disjoint" { + let set1 = @set.Set::of(["a", "b"]) + let set2 = @set.Set::of(["c", "d"]) + let set3 = @set.Set::of(["b", "c"]) + inspect(set1.is_disjoint(set2), content="true") + inspect(set1.is_disjoint(set3), content="false") +} + +///| +test "is_subset" { + let set1 = @set.Set::of(["a", "b"]) + let set2 = @set.Set::of(["a", "b", "c"]) + let set3 = @set.Set::of(["a", "d"]) + inspect(set1.is_subset(set2), content="true") + inspect(set1.is_subset(set3), content="false") + inspect(set2.is_subset(set1), content="false") +} + +///| +test "is_superset" { + let set1 = @set.Set::of(["a", "b", "c"]) + let set2 = @set.Set::of(["a", "b"]) + let set3 = @set.Set::of(["a", "d"]) + inspect(set1.is_superset(set2), content="true") + inspect(set1.is_superset(set3), content="false") + inspect(set2.is_superset(set1), content="false") +} + +///| +test "bitwise_operations" { + let set1 = @set.Set::of(["a", "b", "c"]) + let set2 = @set.Set::of(["b", "c", "d"]) + + // Intersection (&) + let intersection = set1 & set2 + inspect(intersection.size(), content="2") + inspect(intersection.contains("b"), content="true") + inspect(intersection.contains("c"), content="true") + + // Union (|) + let union = set1 | set2 + inspect(union.size(), content="4") + inspect(union.contains("a"), content="true") + inspect(union.contains("d"), content="true") + + // Difference (-) + let diff = set1 - set2 + inspect(diff.size(), content="1") + inspect(diff.contains("a"), content="true") + + // Symmetric difference (^) + let sym_diff = set1 ^ set2 + inspect(sym_diff.size(), content="2") + inspect(sym_diff.contains("a"), content="true") + inspect(sym_diff.contains("d"), content="true") +} diff --git a/bundled-core/set/moon.pkg.json b/bundled-core/set/moon.pkg.json index 4e6546a..745f966 100644 --- a/bundled-core/set/moon.pkg.json +++ b/bundled-core/set/moon.pkg.json @@ -1,6 +1,7 @@ { "import": [ - "moonbitlang/core/builtin" + "moonbitlang/core/builtin", + "moonbitlang/core/int" ], "test-import": [ "moonbitlang/core/json", diff --git a/bundled-core/set/set.mbti b/bundled-core/set/pkg.generated.mbti similarity index 72% rename from bundled-core/set/set.mbti rename to bundled-core/set/pkg.generated.mbti index f017bd2..71537d6 100644 --- a/bundled-core/set/set.mbti +++ b/bundled-core/set/pkg.generated.mbti @@ -1,7 +1,10 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/set" // Values +// Errors + // Types and methods type Set[K] fn[K : Hash + Eq] Set::add(Self[K], K) -> Unit @@ -9,17 +12,25 @@ fn[K : Hash + Eq] Set::add_and_check(Self[K], K) -> Bool fn[K] Set::capacity(Self[K]) -> Int fn[K] Set::clear(Self[K]) -> Unit fn[K : Hash + Eq] Set::contains(Self[K], K) -> Bool +fn[K] Set::copy(Self[K]) -> Self[K] fn[K : Hash + Eq] Set::difference(Self[K], Self[K]) -> Self[K] fn[K] Set::each(Self[K], (K) -> Unit raise?) -> Unit raise? fn[K] Set::eachi(Self[K], (Int, K) -> Unit raise?) -> Unit raise? +#as_free_fn fn[K : Hash + Eq] Set::from_array(Array[K]) -> Self[K] +#as_free_fn fn[K : Hash + Eq] Set::from_iter(Iter[K]) -> Self[K] #deprecated fn[K : Hash + Eq] Set::insert(Self[K], K) -> Unit fn[K : Hash + Eq] Set::intersection(Self[K], Self[K]) -> Self[K] +fn[K : Hash + Eq] Set::is_disjoint(Self[K], Self[K]) -> Bool fn[K] Set::is_empty(Self[K]) -> Bool +fn[K : Hash + Eq] Set::is_subset(Self[K], Self[K]) -> Bool +fn[K : Hash + Eq] Set::is_superset(Self[K], Self[K]) -> Bool fn[K] Set::iter(Self[K]) -> Iter[K] -fn[K] Set::new(capacity~ : Int = ..) -> Self[K] +#as_free_fn +fn[K] Set::new(capacity? : Int) -> Self[K] +#as_free_fn fn[K : Hash + Eq] Set::of(FixedArray[K]) -> Self[K] fn[K : Hash + Eq] Set::remove(Self[K], K) -> Unit fn[K : Hash + Eq] Set::remove_and_check(Self[K], K) -> Bool @@ -27,9 +38,13 @@ fn[K] Set::size(Self[K]) -> Int fn[K : Hash + Eq] Set::symmetric_difference(Self[K], Self[K]) -> Self[K] fn[K] Set::to_array(Self[K]) -> Array[K] fn[K : Hash + Eq] Set::union(Self[K], Self[K]) -> Self[K] +impl[K : Hash + Eq] BitAnd for Set[K] +impl[K : Hash + Eq] BitOr for Set[K] +impl[K : Hash + Eq] BitXOr for Set[K] impl[K] Default for Set[K] impl[K : Hash + Eq] Eq for Set[K] impl[K : Show] Show for Set[K] +impl[K : Hash + Eq] Sub for Set[K] impl[X : ToJson] ToJson for Set[X] // Type aliases diff --git a/bundled-core/sorted_map/README.mbt.md b/bundled-core/sorted_map/README.mbt.md index 669705c..b1280f4 100644 --- a/bundled-core/sorted_map/README.mbt.md +++ b/bundled-core/sorted_map/README.mbt.md @@ -23,7 +23,7 @@ You can create an empty SortedMap or a SortedMap from other containers. ```moonbit test { - let _map1 : @sorted_map.T[Int, String] = @sorted_map.new() + let _map1 : @sorted_map.SortedMap[Int, String] = @sorted_map.new() let _map2 = @sorted_map.from_array([(1, "one"), (2, "two"), (3, "three")]) } @@ -133,7 +133,7 @@ Check if the map is empty. ```moonbit test { - let map : @sorted_map.T[Int, String] = @sorted_map.new() + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() assert_eq(map.is_empty(), true) } ``` @@ -155,8 +155,8 @@ Get all keys or values from the map. ```moonbit test { let map = @sorted_map.from_array([(3, "three"), (1, "one"), (2, "two")]) - assert_eq(map.keys(), [1, 2, 3]) - assert_eq(map.values(), ["one", "two", "three"]) + assert_eq(map.keys_as_iter().collect(), [1, 2, 3]) + assert_eq(map.values_as_iter().collect(), ["one", "two", "three"]) } ``` @@ -258,7 +258,7 @@ test { When working with keys that might not exist, prefer using pattern matching for safety: ```moonbit -fn get_score(scores : @sorted_map.T[Int, Int], student_id : Int) -> Int { +fn get_score(scores : @sorted_map.SortedMap[Int, Int], student_id : Int) -> Int { match scores.get(student_id) { Some(score) => score None => @@ -296,7 +296,7 @@ Key properties of the AVL tree implementation: - **@hashmap.T**: Provides O(1) average case lookups but doesn't maintain order; use when order doesn't matter - **@indexmap.T**: Maintains insertion order but not sorted order; use when insertion order matters -- **@sorted_map.T**: Maintains keys in sorted order; use when you need keys to be sorted +- **@sorted_map.SortedMap**: Maintains keys in sorted order; use when you need keys to be sorted Choose SortedMap when you need: - Key-value pairs sorted by key diff --git a/bundled-core/sorted_map/deprecated.mbt b/bundled-core/sorted_map/deprecated.mbt index d622eae..54d1420 100644 --- a/bundled-core/sorted_map/deprecated.mbt +++ b/bundled-core/sorted_map/deprecated.mbt @@ -13,15 +13,19 @@ // limitations under the License. ///| -#deprecated("Use `get` instead. `op_get` will return `V` instead of `Option[V]` in the future.") -pub fn[K : Compare, V] op_get(self : T[K, V], key : K) -> V? { - self.get(key) +#deprecated("Use `keys_as_iter` instead. `keys` will return `Iter[K]` instead of `Array[K]` in the future.") +#coverage.skip +pub fn[K, V] keys(self : SortedMap[K, V]) -> Array[K] { + let keys = Array::new(capacity=self.size) + self.each(fn(k, _v) { keys.push(k) }) + keys } ///| -#deprecated("Use @sorted_map.from_array instead") -pub fn[K : Compare, V] of(entries : Array[(K, V)]) -> T[K, V] { - let map = { root: None, size: 0 } - entries.each(e => map.add(e.0, e.1)) - map +#deprecated("Use `values_as_iter` instead. `values` will return `Iter[V]` instead of `Array[V]` in the future.") +#coverage.skip +pub fn[K, V] values(self : SortedMap[K, V]) -> Array[V] { + let values = Array::new(capacity=self.size) + self.each(fn(_k, v) { values.push(v) }) + values } diff --git a/bundled-core/sorted_map/map.mbt b/bundled-core/sorted_map/map.mbt index 2a38443..3d6b439 100644 --- a/bundled-core/sorted_map/map.mbt +++ b/bundled-core/sorted_map/map.mbt @@ -13,35 +13,52 @@ // limitations under the License. ///| -pub fn[K : Compare, V] op_set(self : T[K, V], key : K, value : V) -> Unit { +pub fn[K : Compare, V] op_set( + self : SortedMap[K, V], + key : K, + value : V, +) -> Unit { self.add(key, value) } ///| - -///| -pub impl[K : Eq, V : Eq] Eq for T[K, V] with op_equal(self, other) { +pub impl[K : Eq, V : Eq] Eq for SortedMap[K, V] with equal(self, other) { self.to_array() == other.to_array() } ///| /// Returns a new sorted map. -pub fn[K, V] new() -> T[K, V] { +#as_free_fn +pub fn[K, V] SortedMap::new() -> SortedMap[K, V] { { root: None, size: 0 } } ///| - -///| Creates a sorted map from a array of key-value pairs. -pub fn[K : Compare, V] from_array(entries : Array[(K, V)]) -> T[K, V] { +/// Creates a sorted map from a array of key-value pairs. +#as_free_fn +pub fn[K : Compare, V] SortedMap::from_array( + entries : Array[(K, V)], +) -> SortedMap[K, V] { let map = { root: None, size: 0 } entries.each(e => map.add(e.0, e.1)) map } +///| +#as_free_fn +pub fn[K : Compare, V] SortedMap::of( + entries : FixedArray[(K, V)], +) -> SortedMap[K, V] { + let map = { root: None, size: 0 } + for i in 0.. Unit { +pub fn[K : Compare, V] add(self : SortedMap[K, V], key : K, value : V) -> Unit { let (new_root, inserted) = add_node(self.root, key, value) if self.root != new_root { self.root = new_root @@ -53,7 +70,7 @@ pub fn[K : Compare, V] add(self : T[K, V], key : K, value : V) -> Unit { ///| /// Removes a key-value pair. -pub fn[K : Compare, V] remove(self : T[K, V], key : K) -> Unit { +pub fn[K : Compare, V] remove(self : SortedMap[K, V], key : K) -> Unit { if self.root is Some(old_root) { let (new_root, deleted) = delete_node(old_root, key) if self.root != new_root { @@ -67,7 +84,7 @@ pub fn[K : Compare, V] remove(self : T[K, V], key : K) -> Unit { ///| /// Gets a value by a key. -pub fn[K : Compare, V] get(self : T[K, V], key : K) -> V? { +pub fn[K : Compare, V] get(self : SortedMap[K, V], key : K) -> V? { loop self.root { Some(node) => { let cmp = key.compare(node.key) @@ -83,9 +100,27 @@ pub fn[K : Compare, V] get(self : T[K, V], key : K) -> V? { } } +///| +/// Gets a value by a key. +pub fn[K : Compare, V] op_get(self : SortedMap[K, V], key : K) -> V { + loop self.root { + Some(node) => { + let cmp = key.compare(node.key) + if cmp == 0 { + break node.value + } else if cmp > 0 { + continue node.right + } else { + continue node.left + } + } + None => panic() + } +} + ///| /// Checks if map contains a key-value pair. -pub fn[K : Compare, V] contains(self : T[K, V], key : K) -> Bool { +pub fn[K : Compare, V] contains(self : SortedMap[K, V], key : K) -> Bool { match self.get(key) { Some(_) => true None => false @@ -94,26 +129,29 @@ pub fn[K : Compare, V] contains(self : T[K, V], key : K) -> Bool { ///| /// Returns true if map is empty. -pub fn[K, V] is_empty(self : T[K, V]) -> Bool { +pub fn[K, V] is_empty(self : SortedMap[K, V]) -> Bool { self.size == 0 } ///| /// Returns the count of key-value pairs in the map. -pub fn[K, V] size(self : T[K, V]) -> Int { +pub fn[K, V] size(self : SortedMap[K, V]) -> Int { self.size } ///| /// Clears the map. -pub fn[K, V] clear(self : T[K, V]) -> Unit { +pub fn[K, V] clear(self : SortedMap[K, V]) -> Unit { self.root = None self.size = 0 } ///| /// Iterates the map. -pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit raise?) -> Unit raise? { +pub fn[K, V] each( + self : SortedMap[K, V], + f : (K, V) -> Unit raise?, +) -> Unit raise? { fn dfs(root : Node[K, V]?) -> Unit raise? { if root is Some(root) { dfs(root.left) @@ -128,8 +166,8 @@ pub fn[K, V] each(self : T[K, V], f : (K, V) -> Unit raise?) -> Unit raise? { ///| /// Iterates the map with index. pub fn[K, V] eachi( - self : T[K, V], - f : (Int, K, V) -> Unit raise? + self : SortedMap[K, V], + f : (Int, K, V) -> Unit raise?, ) -> Unit raise? { let mut i = 0 self.each((k, v) => { @@ -140,30 +178,58 @@ pub fn[K, V] eachi( ///| /// Returns all keys in the map. -pub fn[K, V] keys(self : T[K, V]) -> Array[K] { - let keys = Array::new(capacity=self.size) - self.each((k, _v) => keys.push(k)) - keys +pub fn[K, V] keys_as_iter(self : SortedMap[K, V]) -> Iter[K] { + Iter::new(fn(yield_) { + fn go(x : Node[K, V]?) { + match x { + None => IterContinue + Some({ left, right, key, .. }) => + if go(left) is IterEnd { + IterEnd + } else if yield_(key) is IterEnd { + IterEnd + } else { + go(right) + } + } + } + + go(self.root) + }) } ///| /// Returns all values in the map. -pub fn[K, V] values(self : T[K, V]) -> Array[V] { - let values = Array::new(capacity=self.size) - self.each((_k, v) => values.push(v)) - values +pub fn[K, V] values_as_iter(self : SortedMap[K, V]) -> Iter[V] { + Iter::new(fn(yield_) { + fn go(x : Node[K, V]?) { + match x { + None => IterContinue + Some({ left, right, value, .. }) => + if go(left) is IterEnd { + IterEnd + } else if yield_(value) is IterEnd { + IterEnd + } else { + go(right) + } + } + } + + go(self.root) + }) } ///| /// Converts the map to an array. -pub fn[K, V] to_array(self : T[K, V]) -> Array[(K, V)] { +pub fn[K, V] to_array(self : SortedMap[K, V]) -> Array[(K, V)] { let arr = Array::new(capacity=self.size) self.each((k, v) => arr.push((k, v))) arr } ///| -pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { +pub fn[K, V] iter(self : SortedMap[K, V]) -> Iter[(K, V)] { Iter::new(yield_ => { fn go(x : Node[K, V]?) { match x { @@ -184,7 +250,7 @@ pub fn[K, V] iter(self : T[K, V]) -> Iter[(K, V)] { } ///| -pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { +pub fn[K, V] iter2(self : SortedMap[K, V]) -> Iter2[K, V] { Iter2::new(yield_ => { fn go(x : Node[K, V]?) { match x { @@ -205,22 +271,30 @@ pub fn[K, V] iter2(self : T[K, V]) -> Iter2[K, V] { } ///| -pub fn[K : Compare, V] from_iter(iter : Iter[(K, V)]) -> T[K, V] { +#as_free_fn +pub fn[K : Compare, V] SortedMap::from_iter( + iter : Iter[(K, V)], +) -> SortedMap[K, V] { let m = new() iter.each(e => m[e.0] = e.1) m } ///| -pub impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[ +pub impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for SortedMap[ K, V, ] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_iter } -///|Returns a new array of key-value pairs that are within the specified range [low, high]. -pub fn[K : Compare, V] range(self : T[K, V], low : K, high : K) -> Iter2[K, V] { +///| +///Returns a new array of key-value pairs that are within the specified range [low, high]. +pub fn[K : Compare, V] range( + self : SortedMap[K, V], + low : K, + high : K, +) -> Iter2[K, V] { Iter2::new(yield_ => { fn go(x : Node[K, V]?) { match x { @@ -252,7 +326,7 @@ pub fn[K : Compare, V] range(self : T[K, V], low : K, high : K) -> Iter2[K, V] { ///| fn[K : Compare, V] replace_root_with_min( root : Node[K, V], - node : Node[K, V] + node : Node[K, V], ) -> Node[K, V]? { let (l, r) = (node.left, node.right) match l { @@ -350,7 +424,7 @@ fn[K, V] rotate_rl(n : Node[K, V]) -> Node[K, V] { fn[K : Compare, V] add_node( root : Node[K, V]?, key : K, - value : V + value : V, ) -> (Node[K, V]?, Bool) { match root { None => (Some(new_node(key, value)), true) @@ -376,7 +450,7 @@ fn[K : Compare, V] add_node( ///| fn[K : Compare, V] delete_node( root : Node[K, V], - key : K + key : K, ) -> (Node[K, V]?, Bool) { if key == root.key { let (l, r) = (root.left, root.right) @@ -412,7 +486,7 @@ fn[K : Compare, V] delete_node( ///| test "new" { - let map : T[Int, String] = new() + let map : SortedMap[Int, String] = new() inspect(map.debug_tree(), content="_") inspect(map.size(), content="0") } @@ -563,6 +637,6 @@ test "clear" { } ///| -pub impl[K, V] Default for T[K, V] with default() { +pub impl[K, V] Default for SortedMap[K, V] with default() { new() } diff --git a/bundled-core/sorted_map/map_test.mbt b/bundled-core/sorted_map/map_test.mbt index f28391c..a989a80 100644 --- a/bundled-core/sorted_map/map_test.mbt +++ b/bundled-core/sorted_map/map_test.mbt @@ -31,7 +31,7 @@ test "remove3" { ///| test "remove on empty map" { - let map : @sorted_map.T[Int, String] = @sorted_map.new() + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() map.remove(1) inspect(map.size(), content="0") } @@ -41,23 +41,26 @@ test "get" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) inspect( map.get(1), - content= + content=( #|Some("a") - , + ), ) inspect( map.get(2), - content= + content=( #|Some("b") - , + ), ) inspect( map.get(3), - content= + content=( #|Some("c") - , + ), ) inspect(map.get(4), content="None") + + // pattern + guard map is { 1: "a", 2: "b", 3: "c", 4? : None, .. } } ///| @@ -72,25 +75,15 @@ test "contains" { ///| test "op_get" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) - inspect( - map.get(1), - content= - #|Some("a") - , - ) - inspect( - map.get(2), - content= - #|Some("b") - , - ) - inspect( - map.get(3), - content= - #|Some("c") - , - ) - inspect(map.get(4), content="None") + inspect(map[1], content="a") + inspect(map[2], content="b") + inspect(map[3], content="c") +} + +///| +test "panic op_get" { + let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) + map[4] |> ignore } ///| @@ -105,7 +98,7 @@ test "op_equal" { ///| test "is_empty" { - let map : @sorted_map.T[Int, String] = @sorted_map.new() + let map : @sorted_map.SortedMap[Int, String] = @sorted_map.new() inspect(map.is_empty(), content="true") map[1] = "a" inspect(map.is_empty(), content="false") @@ -136,17 +129,17 @@ test "eachi" { ///| test "keys" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) - inspect(map.keys(), content="[1, 2, 3]") + inspect(map.keys_as_iter(), content="[1, 2, 3]") } ///| test "values" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) inspect( - map.values(), - content= + map.values_as_iter(), + content=( #|["a", "b", "c"] - , + ), ) } @@ -155,9 +148,9 @@ test "to_array" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) inspect( map.to_array(), - content= + content=( #|[(1, "a"), (2, "b"), (3, "c")] - , + ), ) } @@ -166,15 +159,15 @@ test "iter" { let map = @sorted_map.from_array([(3, "c"), (2, "b"), (1, "a")]) inspect( map.iter().collect(), - content= + content=( #|[(1, "a"), (2, "b"), (3, "c")] - , + ), ) inspect( map.iter().take(2).collect(), - content= + content=( #|[(1, "a"), (2, "b")] - , + ), ) } @@ -196,13 +189,15 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @sorted_map.T[Int, Int] = @sorted_map.from_iter(Iter::empty()) + let pq : @sorted_map.SortedMap[Int, Int] = @sorted_map.from_iter( + Iter::empty(), + ) inspect(pq, content="@sorted_map.of([])") } ///| test "arbitrary" { - let map : Array[@sorted_map.T[Int, UInt]] = @quickcheck.samples(20) + let map : Array[@sorted_map.SortedMap[Int, UInt]] = @quickcheck.samples(20) inspect( map[5:10], content="[@sorted_map.of([]), @sorted_map.of([]), @sorted_map.of([(0, 0)]), @sorted_map.of([(0, 0)]), @sorted_map.of([(0, 0)])]", diff --git a/bundled-core/sorted_map/pkg.generated.mbti b/bundled-core/sorted_map/pkg.generated.mbti new file mode 100644 index 0000000..9ed0116 --- /dev/null +++ b/bundled-core/sorted_map/pkg.generated.mbti @@ -0,0 +1,52 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/sorted_map" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type SortedMap[K, V] +fn[K : Compare, V] SortedMap::add(Self[K, V], K, V) -> Unit +fn[K, V] SortedMap::clear(Self[K, V]) -> Unit +fn[K : Compare, V] SortedMap::contains(Self[K, V], K) -> Bool +fn[K, V] SortedMap::each(Self[K, V], (K, V) -> Unit raise?) -> Unit raise? +fn[K, V] SortedMap::eachi(Self[K, V], (Int, K, V) -> Unit raise?) -> Unit raise? +#as_free_fn +fn[K : Compare, V] SortedMap::from_array(Array[(K, V)]) -> Self[K, V] +#as_free_fn +fn[K : Compare, V] SortedMap::from_iter(Iter[(K, V)]) -> Self[K, V] +fn[K : Compare, V] SortedMap::get(Self[K, V], K) -> V? +fn[K, V] SortedMap::is_empty(Self[K, V]) -> Bool +fn[K, V] SortedMap::iter(Self[K, V]) -> Iter[(K, V)] +fn[K, V] SortedMap::iter2(Self[K, V]) -> Iter2[K, V] +#deprecated +fn[K, V] SortedMap::keys(Self[K, V]) -> Array[K] +fn[K, V] SortedMap::keys_as_iter(Self[K, V]) -> Iter[K] +#as_free_fn +fn[K, V] SortedMap::new() -> Self[K, V] +#as_free_fn +fn[K : Compare, V] SortedMap::of(FixedArray[(K, V)]) -> Self[K, V] +fn[K : Compare, V] SortedMap::op_get(Self[K, V], K) -> V +fn[K : Compare, V] SortedMap::op_set(Self[K, V], K, V) -> Unit +fn[K : Compare, V] SortedMap::range(Self[K, V], K, K) -> Iter2[K, V] +fn[K : Compare, V] SortedMap::remove(Self[K, V], K) -> Unit +fn[K, V] SortedMap::size(Self[K, V]) -> Int +fn[K, V] SortedMap::to_array(Self[K, V]) -> Array[(K, V)] +#deprecated +fn[K, V] SortedMap::values(Self[K, V]) -> Array[V] +fn[K, V] SortedMap::values_as_iter(Self[K, V]) -> Iter[V] +impl[K, V] Default for SortedMap[K, V] +impl[K : Eq, V : Eq] Eq for SortedMap[K, V] +impl[K : Show, V : Show] Show for SortedMap[K, V] +impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for SortedMap[K, V] + +// Type aliases +pub typealias SortedMap as T + +// Traits + diff --git a/bundled-core/sorted_map/sorted_map.mbti b/bundled-core/sorted_map/sorted_map.mbti deleted file mode 100644 index e85c0b1..0000000 --- a/bundled-core/sorted_map/sorted_map.mbti +++ /dev/null @@ -1,45 +0,0 @@ -package "moonbitlang/core/sorted_map" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[K : Compare, V] from_array(Array[(K, V)]) -> T[K, V] - -fn[K : Compare, V] from_iter(Iter[(K, V)]) -> T[K, V] - -fn[K, V] new() -> T[K, V] - -#deprecated -fn[K : Compare, V] of(Array[(K, V)]) -> T[K, V] - -// Types and methods -type T[K, V] -fn[K : Compare, V] T::add(Self[K, V], K, V) -> Unit -fn[K, V] T::clear(Self[K, V]) -> Unit -fn[K : Compare, V] T::contains(Self[K, V], K) -> Bool -fn[K, V] T::each(Self[K, V], (K, V) -> Unit raise?) -> Unit raise? -fn[K, V] T::eachi(Self[K, V], (Int, K, V) -> Unit raise?) -> Unit raise? -fn[K : Compare, V] T::get(Self[K, V], K) -> V? -fn[K, V] T::is_empty(Self[K, V]) -> Bool -fn[K, V] T::iter(Self[K, V]) -> Iter[(K, V)] -fn[K, V] T::iter2(Self[K, V]) -> Iter2[K, V] -fn[K, V] T::keys(Self[K, V]) -> Array[K] -#deprecated -fn[K : Compare, V] T::op_get(Self[K, V], K) -> V? -fn[K : Compare, V] T::op_set(Self[K, V], K, V) -> Unit -fn[K : Compare, V] T::range(Self[K, V], K, K) -> Iter2[K, V] -fn[K : Compare, V] T::remove(Self[K, V], K) -> Unit -fn[K, V] T::size(Self[K, V]) -> Int -fn[K, V] T::to_array(Self[K, V]) -> Array[(K, V)] -fn[K, V] T::values(Self[K, V]) -> Array[V] -impl[K, V] Default for T[K, V] -impl[K : Eq, V : Eq] Eq for T[K, V] -impl[K : Show, V : Show] Show for T[K, V] -impl[K : @quickcheck.Arbitrary + Compare, V : @quickcheck.Arbitrary] @quickcheck.Arbitrary for T[K, V] - -// Type aliases - -// Traits - diff --git a/bundled-core/sorted_map/types.mbt b/bundled-core/sorted_map/types.mbt index 46a1a42..eb6f4d3 100644 --- a/bundled-core/sorted_map/types.mbt +++ b/bundled-core/sorted_map/types.mbt @@ -22,7 +22,11 @@ priv struct Node[K, V] { } ///| -struct T[K, V] { +struct SortedMap[K, V] { mut root : Node[K, V]? mut size : Int } + +///| +#deprecated("Use `SortedMap` instead of `T`") +pub typealias SortedMap as T diff --git a/bundled-core/sorted_map/utils.mbt b/bundled-core/sorted_map/utils.mbt index 41605f5..6160f52 100644 --- a/bundled-core/sorted_map/utils.mbt +++ b/bundled-core/sorted_map/utils.mbt @@ -18,7 +18,7 @@ fn[K, V] new_node(key : K, value : V) -> Node[K, V] { } ///| -impl[K : Eq, V] Eq for Node[K, V] with op_equal(self, other) { +impl[K : Eq, V] Eq for Node[K, V] with equal(self, other) { self.key == other.key } @@ -53,7 +53,7 @@ fn[K : Show, V : Show] debug_node(self : Node[K, V]) -> String { } ///| -fn[K : Show, V : Show] debug_tree(self : T[K, V]) -> String { +fn[K : Show, V : Show] debug_tree(self : SortedMap[K, V]) -> String { match self.root { Some(root) => root.debug_node() None => "_" @@ -61,6 +61,6 @@ fn[K : Show, V : Show] debug_tree(self : T[K, V]) -> String { } ///| -pub impl[K : Show, V : Show] Show for T[K, V] with output(self, logger) { +pub impl[K : Show, V : Show] Show for SortedMap[K, V] with output(self, logger) { logger.write_iter(self.iter(), prefix="@sorted_map.of([", suffix="])") } diff --git a/bundled-core/sorted_set/README.mbt.md b/bundled-core/sorted_set/README.mbt.md index 94a711f..ae034de 100644 --- a/bundled-core/sorted_set/README.mbt.md +++ b/bundled-core/sorted_set/README.mbt.md @@ -10,7 +10,7 @@ You can create an empty SortedSet or a SortedSet from other containers. ```moonbit test { - let _set1 : @sorted_set.T[Int] = @sorted_set.new() + let _set1 : @sorted_set.SortedSet[Int] = @sorted_set.new() let _set2 = @sorted_set.singleton(1) let _set3 = @sorted_set.from_array([1]) } @@ -72,7 +72,7 @@ Whether the set is empty. ```moonbit test { - let set : @sorted_set.T[Int] = @sorted_set.new() + let set : @sorted_set.SortedSet[Int] = @sorted_set.new() assert_eq(set.is_empty(), true) } ``` diff --git a/bundled-core/sorted_set/deprecated.mbt b/bundled-core/sorted_set/deprecated.mbt index a08c818..4a79ff5 100644 --- a/bundled-core/sorted_set/deprecated.mbt +++ b/bundled-core/sorted_set/deprecated.mbt @@ -12,30 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -#deprecated("use `@sorted_set.from_iter` instead") -#coverage.skip -pub fn[V : Compare] T::from_iter(iter : Iter[V]) -> T[V] { - from_iter(iter) -} - -///| -#deprecated("Use @sorted_set.from_array instead") -pub fn[V : Compare] of(array : Array[V]) -> T[V] { - let set = new() - for i in 0.. T[V] { +pub fn[V] deep_clone(self : SortedSet[V]) -> SortedSet[V] { self.copy() } @@ -44,9 +27,12 @@ pub fn[V] deep_clone(self : T[V]) -> T[V] { /// #deprecated("Use `difference` instead") #coverage.skip -pub fn[V : Compare] diff(self : T[V], src : T[V]) -> T[V] { +pub fn[V : Compare] diff( + self : SortedSet[V], + src : SortedSet[V], +) -> SortedSet[V] { let ret = new() - self.each(x => if not(src.contains(x)) { ret.add(x) }) + self.each(x => if !src.contains(x) { ret.add(x) }) ret } @@ -54,6 +40,9 @@ pub fn[V : Compare] diff(self : T[V], src : T[V]) -> T[V] { /// Returns the intersection of two sets. #deprecated("Use `intersection` instead") #coverage.skip -pub fn[V : Compare] intersect(self : T[V], src : T[V]) -> T[V] { +pub fn[V : Compare] intersect( + self : SortedSet[V], + src : SortedSet[V], +) -> SortedSet[V] { self.intersection(src) } diff --git a/bundled-core/sorted_set/pkg.generated.mbti b/bundled-core/sorted_set/pkg.generated.mbti new file mode 100644 index 0000000..f4d1a43 --- /dev/null +++ b/bundled-core/sorted_set/pkg.generated.mbti @@ -0,0 +1,56 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/sorted_set" + +import( + "moonbitlang/core/quickcheck" +) + +// Values + +// Errors + +// Types and methods +type SortedSet[V] +fn[V : Compare] SortedSet::add(Self[V], V) -> Unit +fn[V : Compare] SortedSet::contains(Self[V], V) -> Bool +fn[V] SortedSet::copy(Self[V]) -> Self[V] +#deprecated +fn[V] SortedSet::deep_clone(Self[V]) -> Self[V] +#deprecated +fn[V : Compare] SortedSet::diff(Self[V], Self[V]) -> Self[V] +fn[V : Compare] SortedSet::difference(Self[V], Self[V]) -> Self[V] +fn[V : Compare] SortedSet::disjoint(Self[V], Self[V]) -> Bool +fn[V] SortedSet::each(Self[V], (V) -> Unit raise?) -> Unit raise? +fn[V] SortedSet::eachi(Self[V], (Int, V) -> Unit raise?) -> Unit raise? +#as_free_fn +fn[V : Compare] SortedSet::from_array(Array[V]) -> Self[V] +#as_free_fn +fn[V : Compare] SortedSet::from_iter(Iter[V]) -> Self[V] +#deprecated +fn[V : Compare] SortedSet::intersect(Self[V], Self[V]) -> Self[V] +fn[V : Compare] SortedSet::intersection(Self[V], Self[V]) -> Self[V] +fn[V] SortedSet::is_empty(Self[V]) -> Bool +fn[V] SortedSet::iter(Self[V]) -> Iter[V] +#as_free_fn +fn[V] SortedSet::new() -> Self[V] +#as_free_fn +fn[V : Compare] SortedSet::of(FixedArray[V]) -> Self[V] +fn[V : Compare] SortedSet::range(Self[V], V, V) -> Iter[V] +fn[V : Compare] SortedSet::remove(Self[V], V) -> Unit +#as_free_fn +fn[V] SortedSet::singleton(V) -> Self[V] +fn[V] SortedSet::size(Self[V]) -> Int +fn[V : Compare] SortedSet::subset(Self[V], Self[V]) -> Bool +fn[V : Compare] SortedSet::symmetric_difference(Self[V], Self[V]) -> Self[V] +fn[V] SortedSet::to_array(Self[V]) -> Array[V] +fn[V : Compare] SortedSet::union(Self[V], Self[V]) -> Self[V] +impl[K] Default for SortedSet[K] +impl[V : Eq] Eq for SortedSet[V] +impl[V : Show] Show for SortedSet[V] +impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for SortedSet[X] + +// Type aliases +pub typealias SortedSet as T + +// Traits + diff --git a/bundled-core/sorted_set/set.mbt b/bundled-core/sorted_set/set.mbt index 4ecae17..4d04e12 100644 --- a/bundled-core/sorted_set/set.mbt +++ b/bundled-core/sorted_set/set.mbt @@ -16,19 +16,32 @@ ///| /// Construct a empty set. -pub fn[V] new() -> T[V] { +#as_free_fn +pub fn[V] SortedSet::new() -> SortedSet[V] { { root: None, size: 0 } } ///| /// Returns the one-value set containing only `value`. -pub fn[V] singleton(value : V) -> T[V] { +#as_free_fn +pub fn[V] SortedSet::singleton(value : V) -> SortedSet[V] { { root: Some({ value, left: None, right: None, height: 1 }), size: 1 } } ///| /// Initialize an set from an array. -pub fn[V : Compare] from_array(array : Array[V]) -> T[V] { +#as_free_fn +pub fn[V : Compare] SortedSet::from_array(array : Array[V]) -> SortedSet[V] { + let set = new() + for i in 0.. SortedSet[V] { let set = new() for i in 0.. T[V] { /// /// It is just copying the tree structure, not the values. /// -pub fn[V] copy(self : T[V]) -> T[V] { +pub fn[V] copy(self : SortedSet[V]) -> SortedSet[V] { match self.root { None => new() Some(_) => { root: copy_tree(self.root), size: self.size } @@ -64,9 +77,9 @@ fn[V] copy_tree(node : Node[V]?) -> Node[V]? { ///| fn[V] new_node( value : V, - left~ : Node[V]? = None, - right~ : Node[V]? = None, - height~ : Int = 1 + left? : Node[V]? = None, + right? : Node[V]? = None, + height? : Int = 1, ) -> Node[V] { { value, left, right, height } } @@ -75,7 +88,7 @@ fn[V] new_node( fn[V] new_node_update_height( value : V, left~ : Node[V]?, - right~ : Node[V]? + right~ : Node[V]?, ) -> Node[V] { { value, left, right, height: max(height(left), height(right)) + 1 } } @@ -83,7 +96,7 @@ fn[V] new_node_update_height( // Manipulations ///| -pub fn[V : Compare] add(self : T[V], value : V) -> Unit { +pub fn[V : Compare] add(self : SortedSet[V], value : V) -> Unit { let (new_root, inserted) = add_node(self.root, value) if self.root != new_root { self.root = new_root @@ -94,7 +107,7 @@ pub fn[V : Compare] add(self : T[V], value : V) -> Unit { } ///| -pub fn[V : Compare] remove(self : T[V], value : V) -> Unit { +pub fn[V : Compare] remove(self : SortedSet[V], value : V) -> Unit { if self.root is Some(old_root) { let (new_root, deleted) = delete_node(old_root, value) if self.root != new_root { @@ -113,7 +126,7 @@ pub fn[V : Compare] remove(self : T[V], value : V) -> Unit { ///| /// Return if a value is contained in the set. -pub fn[V : Compare] contains(self : T[V], value : V) -> Bool { +pub fn[V : Compare] contains(self : SortedSet[V], value : V) -> Bool { loop (self.root, value) { (None, _) => false (Some(node), value) => { @@ -131,7 +144,10 @@ pub fn[V : Compare] contains(self : T[V], value : V) -> Bool { ///| /// Returns the union of two sets. -pub fn[V : Compare] union(self : T[V], src : T[V]) -> T[V] { +pub fn[V : Compare] union( + self : SortedSet[V], + src : SortedSet[V], +) -> SortedSet[V] { fn aux(a : Node[V]?, b : Node[V]?) -> Node[V]? { match (a, b) { (Some(_), None) => a @@ -245,9 +261,12 @@ fn[V : Compare] join_right(l : Node[V]?, v : V, r : Node[V]?) -> Node[V] { ///| /// Returns the difference of two sets. -pub fn[V : Compare] difference(self : T[V], src : T[V]) -> T[V] { +pub fn[V : Compare] difference( + self : SortedSet[V], + src : SortedSet[V], +) -> SortedSet[V] { let ret = new() - self.each(x => if not(src.contains(x)) { ret.add(x) }) + self.each(x => if !src.contains(x) { ret.add(x) }) ret } @@ -272,7 +291,10 @@ pub fn[V : Compare] difference(self : T[V], src : T[V]) -> T[V] { /// let diff = set1.symmetric_difference(set2) /// inspect(diff, content="@sorted_set.from_array([1, 2, 5, 6])") /// ``` -pub fn[V : Compare] symmetric_difference(self : T[V], other : T[V]) -> T[V] { +pub fn[V : Compare] symmetric_difference( + self : SortedSet[V], + other : SortedSet[V], +) -> SortedSet[V] { // TODO: Optimize this function to avoid creating two intermediate sets. let set1 = self.difference(other) let set2 = other.difference(self) @@ -281,7 +303,10 @@ pub fn[V : Compare] symmetric_difference(self : T[V], other : T[V]) -> T[V] { ///| /// Returns the intersection of two sets. -pub fn[V : Compare] intersection(self : T[V], src : T[V]) -> T[V] { +pub fn[V : Compare] intersection( + self : SortedSet[V], + src : SortedSet[V], +) -> SortedSet[V] { let ret = new() self.each(x => if src.contains(x) { ret.add(x) }) ret @@ -289,15 +314,15 @@ pub fn[V : Compare] intersection(self : T[V], src : T[V]) -> T[V] { ///| /// Returns if a set is a subset of another set. -pub fn[V : Compare] subset(self : T[V], src : T[V]) -> Bool { +pub fn[V : Compare] subset(self : SortedSet[V], src : SortedSet[V]) -> Bool { let mut ret = true - self.each(x => if not(src.contains(x)) { ret = false }) + self.each(x => if !src.contains(x) { ret = false }) ret } ///| /// Returns if two sets are disjoint. -pub fn[V : Compare] disjoint(self : T[V], src : T[V]) -> Bool { +pub fn[V : Compare] disjoint(self : SortedSet[V], src : SortedSet[V]) -> Bool { let mut ret = true self.each(x => if src.contains(x) { ret = false }) ret @@ -306,25 +331,25 @@ pub fn[V : Compare] disjoint(self : T[V], src : T[V]) -> Bool { // General collection operations ///| -pub impl[V : Eq] Eq for T[V] with op_equal(self, other) { +pub impl[V : Eq] Eq for SortedSet[V] with equal(self, other) { self.to_array() == other.to_array() } ///| /// Returns if the set is empty. -pub fn[V] is_empty(self : T[V]) -> Bool { +pub fn[V] is_empty(self : SortedSet[V]) -> Bool { self.root is None } ///| /// Returns the number of elements in the set. -pub fn[V] size(self : T[V]) -> Int { +pub fn[V] size(self : SortedSet[V]) -> Int { self.size } ///| /// Iterates the set. -pub fn[V] each(self : T[V], f : (V) -> Unit raise?) -> Unit raise? { +pub fn[V] each(self : SortedSet[V], f : (V) -> Unit raise?) -> Unit raise? { fn dfs(root : Node[V]?) -> Unit raise? { if root is Some(root) { dfs(root.left) @@ -338,7 +363,10 @@ pub fn[V] each(self : T[V], f : (V) -> Unit raise?) -> Unit raise? { ///| /// Iterates the set with index. -pub fn[V] eachi(self : T[V], f : (Int, V) -> Unit raise?) -> Unit raise? { +pub fn[V] eachi( + self : SortedSet[V], + f : (Int, V) -> Unit raise?, +) -> Unit raise? { let mut i = 0 self.each(v => { f(i, v) @@ -348,7 +376,7 @@ pub fn[V] eachi(self : T[V], f : (Int, V) -> Unit raise?) -> Unit raise? { ///| /// Converts the set to an array. -pub fn[V] to_array(self : T[V]) -> Array[V] { +pub fn[V] to_array(self : SortedSet[V]) -> Array[V] { if self.size == 0 { [] } else { @@ -371,7 +399,7 @@ pub fn[V] to_array(self : T[V]) -> Array[V] { ///| /// Returns a iterator. -pub fn[V] iter(self : T[V]) -> Iter[V] { +pub fn[V] iter(self : SortedSet[V]) -> Iter[V] { Iter::new(yield_ => { fn go(x : Node[V]?) { match x { @@ -392,7 +420,8 @@ pub fn[V] iter(self : T[V]) -> Iter[V] { } ///| -pub fn[V : Compare] from_iter(iter : Iter[V]) -> T[V] { +#as_free_fn +pub fn[V : Compare] SortedSet::from_iter(iter : Iter[V]) -> SortedSet[V] { let s = new() iter.each(e => s.add(e)) s @@ -400,15 +429,14 @@ pub fn[V : Compare] from_iter(iter : Iter[V]) -> T[V] { ///| /// Converts the set to string. -pub impl[V : Show] Show for T[V] with output(self, logger) { +pub impl[V : Show] Show for SortedSet[V] with output(self, logger) { logger.write_iter(self.iter(), prefix="@sorted_set.from_array([", suffix="])") } ///| -pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] with arbitrary( - size, - rs -) { +pub impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for SortedSet[ + X, +] with arbitrary(size, rs) { @quickcheck.Arbitrary::arbitrary(size, rs) |> from_iter } @@ -419,7 +447,7 @@ impl[T : Show] Show for Node[T] with output(self, logger) { } ///| -pub fn[V : Compare] range(self : T[V], low : V, high : V) -> Iter[V] { +pub fn[V : Compare] range(self : SortedSet[V], low : V, high : V) -> Iter[V] { Iter::new(yield_ => { fn go(x : Node[V]?) { match x { @@ -451,7 +479,7 @@ pub fn[V : Compare] range(self : T[V], low : V, high : V) -> Iter[V] { ///| fn[V : Compare] replace_root_with_min( root : Node[V], - node : Node[V] + node : Node[V], ) -> Node[V]? { let (l, r) = (node.left, node.right) match l { @@ -610,7 +638,7 @@ test "copy" { let copied_set = set.copy() inspect(copied_set, content="@sorted_set.from_array([1, 2, 3, 4, 5])") inspect(set.debug_tree() == copied_set.debug_tree(), content="true") - let set : T[Int] = from_array([]) + let set : SortedSet[Int] = from_array([]) let copied_set = set.copy() inspect(copied_set, content="@sorted_set.from_array([])") inspect(set.debug_tree() == copied_set.debug_tree(), content="true") @@ -643,7 +671,7 @@ test "union" { inspect(set3.debug_tree(), content="([2]2,([1]1,_,_),([1]3,_,_))") // Test 4: Union of two empty sets - let set1 : T[Int] = new() + let set1 : SortedSet[Int] = new() let set2 = new() let set3 = set1.union(set2) inspect(set3, content="@sorted_set.from_array([])") @@ -985,6 +1013,6 @@ test "add_and_remove" { } ///| -pub impl[K] Default for T[K] with default() { +pub impl[K] Default for SortedSet[K] with default() { new() } diff --git a/bundled-core/sorted_set/set_test.mbt b/bundled-core/sorted_set/set_test.mbt index 200dcb2..5bfa40b 100644 --- a/bundled-core/sorted_set/set_test.mbt +++ b/bundled-core/sorted_set/set_test.mbt @@ -14,7 +14,7 @@ ///| test "remove on empty set" { - let set : @sorted_set.T[Int] = @sorted_set.new() + let set : @sorted_set.SortedSet[Int] = @sorted_set.new() set.remove(0) inspect(set.size(), content="0") } @@ -75,7 +75,7 @@ test "each" { let result = StringBuilder::new(size_hint=10) set.each(x => result.write_string(x.to_string())) inspect(result.to_string(), content="123456789") - let set : @sorted_set.T[Int] = @sorted_set.new() + let set : @sorted_set.SortedSet[Int] = @sorted_set.new() set.each(_x => abort("Impossible to reach")) } @@ -97,6 +97,32 @@ test "iter" { inspect(set.iter().take(2).collect(), content="[1, 2]") } +///| +test "iter early termination in left subtree" { + // This test should trigger the uncovered line 381 in sorted_set/set.mbt + // We need a tree structure where the iterator terminates while traversing the left subtree + let set = @sorted_set.from_array([5, 3, 7, 1, 4, 6, 8]) + let mut count = 0 + let mut found_target = false + + // Create an iterator that terminates when it finds value 3 + // Since the tree is sorted and 3 is in the left subtree of 5, + // this should cause go(left) to return IterEnd, triggering line 381 + let _ = set + .iter() + .run(x => { + count += 1 + if x == 3 { + found_target = true + IterEnd // This should propagate up and trigger the uncovered line + } else { + IterContinue + } + }) + inspect(found_target, content="true") + inspect(count, content="2") // Should visit 1, then 3 and terminate +} + ///| test "contains" { let set = @sorted_set.from_array([7, 2, 9, 4, 6, 3, 8, 1]) @@ -117,7 +143,7 @@ test "to_array" { content="[1, 2, 3, 4, 5, 6, 7, 8, 9]", ) inspect( - (@sorted_set.from_array([]) : @sorted_set.T[Int]).to_array(), + (@sorted_set.from_array([]) : @sorted_set.SortedSet[Int]).to_array(), content="[]", ) } @@ -129,7 +155,7 @@ test "to_string" { content="@sorted_set.from_array([1, 2, 3, 4, 5])", ) inspect( - (@sorted_set.from_array([]) : @sorted_set.T[Int]), + (@sorted_set.from_array([]) : @sorted_set.SortedSet[Int]), content="@sorted_set.from_array([])", ) } @@ -145,7 +171,7 @@ test "from_array" { ///| test "is_empty" { inspect( - (@sorted_set.from_array([]) : @sorted_set.T[Int]).is_empty(), + (@sorted_set.from_array([]) : @sorted_set.SortedSet[Int]).is_empty(), content="true", ) inspect(@sorted_set.from_array([1]).is_empty(), content="false") @@ -155,7 +181,10 @@ test "is_empty" { test "size" { inspect(@sorted_set.from_array([1, 2, 3, 4, 5]).size(), content="5") inspect(@sorted_set.from_array([1]).size(), content="1") - inspect((@sorted_set.from_array([]) : @sorted_set.T[Int]).size(), content="0") + inspect( + (@sorted_set.from_array([]) : @sorted_set.SortedSet[Int]).size(), + content="0", + ) } ///| @@ -169,7 +198,7 @@ test "singleton" { test "show" { let set = @sorted_set.from_array([1, 2, 3, 4, 5]) inspect(set, content="@sorted_set.from_array([1, 2, 3, 4, 5])") - let set : @sorted_set.T[Int] = @sorted_set.from_array([]) + let set : @sorted_set.SortedSet[Int] = @sorted_set.from_array([]) inspect(set, content="@sorted_set.from_array([])") } @@ -257,7 +286,7 @@ test "from_iter single element iter" { ///| test "from_iter empty iter" { - let pq : @sorted_set.T[Int] = @sorted_set.from_iter(Iter::empty()) + let pq : @sorted_set.SortedSet[Int] = @sorted_set.from_iter(Iter::empty()) inspect(pq, content="@sorted_set.from_array([])") } diff --git a/bundled-core/sorted_set/sorted_set.mbti b/bundled-core/sorted_set/sorted_set.mbti deleted file mode 100644 index 6f3a22d..0000000 --- a/bundled-core/sorted_set/sorted_set.mbti +++ /dev/null @@ -1,54 +0,0 @@ -package "moonbitlang/core/sorted_set" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -fn[V : Compare] from_array(Array[V]) -> T[V] - -fn[V : Compare] from_iter(Iter[V]) -> T[V] - -fn[V] new() -> T[V] - -#deprecated -fn[V : Compare] of(Array[V]) -> T[V] - -fn[V] singleton(V) -> T[V] - -// Types and methods -type T[V] -fn[V : Compare] T::add(Self[V], V) -> Unit -fn[V : Compare] T::contains(Self[V], V) -> Bool -fn[V] T::copy(Self[V]) -> Self[V] -#deprecated -fn[V] T::deep_clone(Self[V]) -> Self[V] -#deprecated -fn[V : Compare] T::diff(Self[V], Self[V]) -> Self[V] -fn[V : Compare] T::difference(Self[V], Self[V]) -> Self[V] -fn[V : Compare] T::disjoint(Self[V], Self[V]) -> Bool -fn[V] T::each(Self[V], (V) -> Unit raise?) -> Unit raise? -fn[V] T::eachi(Self[V], (Int, V) -> Unit raise?) -> Unit raise? -#deprecated -fn[V : Compare] T::from_iter(Iter[V]) -> Self[V] -#deprecated -fn[V : Compare] T::intersect(Self[V], Self[V]) -> Self[V] -fn[V : Compare] T::intersection(Self[V], Self[V]) -> Self[V] -fn[V] T::is_empty(Self[V]) -> Bool -fn[V] T::iter(Self[V]) -> Iter[V] -fn[V : Compare] T::range(Self[V], V, V) -> Iter[V] -fn[V : Compare] T::remove(Self[V], V) -> Unit -fn[V] T::size(Self[V]) -> Int -fn[V : Compare] T::subset(Self[V], Self[V]) -> Bool -fn[V : Compare] T::symmetric_difference(Self[V], Self[V]) -> Self[V] -fn[V] T::to_array(Self[V]) -> Array[V] -fn[V : Compare] T::union(Self[V], Self[V]) -> Self[V] -impl[K] Default for T[K] -impl[V : Eq] Eq for T[V] -impl[V : Show] Show for T[V] -impl[X : @quickcheck.Arbitrary + Compare] @quickcheck.Arbitrary for T[X] - -// Type aliases - -// Traits - diff --git a/bundled-core/sorted_set/types.mbt b/bundled-core/sorted_set/types.mbt index 85e56f2..e7ff338 100644 --- a/bundled-core/sorted_set/types.mbt +++ b/bundled-core/sorted_set/types.mbt @@ -17,7 +17,7 @@ // All operations over sets are purely applicative (no side-effects). ///| -struct T[V] { +struct SortedSet[V] { mut root : Node[V]? mut size : Int } @@ -29,3 +29,7 @@ priv struct Node[V] { mut right : Node[V]? mut height : Int } + +///| +#deprecated("Use `SortedSet` instead of `T`") +pub typealias SortedSet as T diff --git a/bundled-core/sorted_set/utils.mbt b/bundled-core/sorted_set/utils.mbt index 6f956c1..9ca8ec5 100644 --- a/bundled-core/sorted_set/utils.mbt +++ b/bundled-core/sorted_set/utils.mbt @@ -13,7 +13,7 @@ // limitations under the License. ///| -impl[V : Eq] Eq for Node[V] with op_equal(self, other) { +impl[V : Eq] Eq for Node[V] with equal(self, other) { self.value == other.value } @@ -50,7 +50,7 @@ fn[V : Show] debug_node(self : Node[V]) -> String { } ///| -fn[V : Show] debug_tree(self : T[V]) -> String { +fn[V : Show] debug_tree(self : SortedSet[V]) -> String { match self.root { Some(root) => root.debug_node() None => "_" diff --git a/bundled-core/strconv/additional_coverage_test.mbt b/bundled-core/strconv/additional_coverage_test.mbt index 827ea93..93ff77f 100644 --- a/bundled-core/strconv/additional_coverage_test.mbt +++ b/bundled-core/strconv/additional_coverage_test.mbt @@ -24,7 +24,7 @@ test "parse_uint64 overflow check" { let _ = @strconv.parse_uint64(overflow_val) fail("Expected range error for overflow value") } catch { - @strconv.StrConvError(err) => assert_eq(err, "value out of range") + @strconv.StrConvError(err) => inspect(err, content="value out of range") _ => fail("Expected StrConvError but got different error") } @@ -34,7 +34,7 @@ test "parse_uint64 overflow check" { let _ = @strconv.parse_uint64(overflow_hex, base=16) fail("Expected range error for overflow hex value") } catch { - @strconv.StrConvError(err) => assert_eq(err, "value out of range") + @strconv.StrConvError(err) => inspect(err, content="value out of range") _ => fail("Expected StrConvError but got different error") } } diff --git a/bundled-core/strconv/bool.mbt b/bundled-core/strconv/bool.mbt index 4e6041f..5212d95 100644 --- a/bundled-core/strconv/bool.mbt +++ b/bundled-core/strconv/bool.mbt @@ -14,7 +14,7 @@ ///| /// Parse a string and return the represented boolean value or an error. -pub fn parse_bool(str : String) -> Bool raise StrConvError { +pub fn parse_bool(str : @string.View) -> Bool raise StrConvError { match str { "1" | "t" | "T" | "true" | "TRUE" | "True" => true "0" | "f" | "F" | "false" | "FALSE" | "False" => false diff --git a/bundled-core/strconv/decimal.mbt b/bundled-core/strconv/decimal.mbt index 5274b21..a861835 100644 --- a/bundled-core/strconv/decimal.mbt +++ b/bundled-core/strconv/decimal.mbt @@ -43,8 +43,8 @@ fn Decimal::from_int64_priv(v : Int64) -> Decimal { } ///| -fn parse_decimal_priv(str : String) -> Decimal raise StrConvError { - parse_decimal_from_view(str.view()) +fn parse_decimal_priv(str : @string.View) -> Decimal raise StrConvError { + parse_decimal_from_view(str) } ///| @@ -65,7 +65,7 @@ fn parse_decimal_from_view(str : @string.View) -> Decimal raise StrConvError { let rest = loop rest { ['_', .. rest] => continue rest ['.', .. rest] => { - guard not(has_dp) else { syntax_err() } + guard !has_dp else { syntax_err() } has_dp = true d.decimal_point = d.digits_num continue rest @@ -88,7 +88,7 @@ fn parse_decimal_from_view(str : @string.View) -> Decimal raise StrConvError { rest => rest } guard has_digits else { syntax_err() } - if not(has_dp) { + if !has_dp { d.decimal_point = d.digits_num } // read exponent part @@ -190,7 +190,7 @@ fn to_double_priv(self : Decimal) -> Double raise StrConvError { // rounding might have added a bit, shift down. if mantissa == 2L << double_info.mantissa_bits { - mantissa = mantissa << 1 + mantissa = mantissa >> 1 exponent += 1 if exponent - double_info.bias >= (1 << double_info.exponent_bits) - 1 { // overflow @@ -562,17 +562,17 @@ fn Decimal::to_string(self : Decimal) -> String { ///| test "new" { let hpd = Decimal::from_int64_priv(1L) - assert_eq(hpd.digits.length(), 800) - assert_eq(hpd.digits_num, 1) - assert_eq(hpd.decimal_point, 1) - assert_eq(hpd.negative, false) - assert_eq(hpd.truncated, false) + inspect(hpd.digits.length(), content="800") + inspect(hpd.digits_num, content="1") + inspect(hpd.decimal_point, content="1") + inspect(hpd.negative, content="false") + inspect(hpd.truncated, content="false") } ///| test "from_int64" { let hpd = Decimal::from_int64_priv(123456789L) - assert_eq(hpd.to_string(), "123456789") + inspect(hpd.to_string(), content="123456789") } ///| @@ -586,13 +586,13 @@ test "parse_decimal" { test "to_string" { let hpd = Decimal::from_int64_priv(123456789L) hpd.decimal_point = 1 - assert_eq(hpd.to_string(), "1.23456789") + inspect(hpd.to_string(), content="1.23456789") hpd.decimal_point = 0 - assert_eq(hpd.to_string(), "0.123456789") + inspect(hpd.to_string(), content="0.123456789") hpd.decimal_point = -1 - assert_eq(hpd.to_string(), "0.0123456789") + inspect(hpd.to_string(), content="0.0123456789") hpd.decimal_point = 10 - assert_eq(hpd.to_string(), "1234567890") + inspect(hpd.to_string(), content="1234567890") } ///| @@ -654,9 +654,34 @@ test "parse_decimal with large numbers and truncation" { inspect(parse_decimal_priv(s), content=String::make(800, '9')) } +///| +test "rounded_integer overflow when decimal_point > 20" { + // This test should trigger the uncovered line 252 in rounded_integer + // We need to create a decimal that will have decimal_point > 20 after shifting + // but not trigger the early overflow check in to_double_priv + + // Create a decimal manually to bypass the early overflow checks + let decimal = Decimal::new_priv() + decimal.negative = false + decimal.decimal_point = 25 // This is > 20 but < 310 + decimal.digits_num = 1 + decimal.digits[0] = (1 : Int).to_byte() + decimal.truncated = false + + // Call rounded_integer directly to trigger the uncovered line + let result = decimal.rounded_integer() + inspect(result, content="-1") // Should be Int64::max_value +} + ///| test "corner cases" { inspect(try? parse_decimal_priv(".123"), content="Ok(0.123)") inspect(try? parse_decimal_priv("."), content="Err(invalid syntax)") inspect(try? parse_decimal_priv("-"), content="Err(invalid syntax)") } + +///| +test "parse_double mantissa normalization boundary" { + inspect(parse_double("1.9999999999999999"), content="2") + inspect(parse_double("9007199254740991.5"), content="9007199254740992") +} diff --git a/bundled-core/strconv/deprecated.mbt b/bundled-core/strconv/deprecated.mbt index cc1965a..4775aac 100644 --- a/bundled-core/strconv/deprecated.mbt +++ b/bundled-core/strconv/deprecated.mbt @@ -44,14 +44,14 @@ pub fn Decimal::from_int64(v : Int64) -> Decimal { ///| #deprecated("use `@strconv.parse_double` instead") -pub fn parse_decimal(str : String) -> Decimal raise StrConvError { - parse_decimal_from_view(str.view()) +pub fn parse_decimal(str : @string.View) -> Decimal raise StrConvError { + parse_decimal_from_view(str) } ///| #deprecated("use `@strconv.parse_double` instead") -pub fn Decimal::parse_decimal(str : String) -> Decimal raise StrConvError { - parse_decimal_from_view(str.view()) +pub fn Decimal::parse_decimal(str : @string.View) -> Decimal raise StrConvError { + parse_decimal_from_view(str) } ///| diff --git a/bundled-core/strconv/double.mbt b/bundled-core/strconv/double.mbt index 2aa5952..97d7c65 100644 --- a/bundled-core/strconv/double.mbt +++ b/bundled-core/strconv/double.mbt @@ -57,21 +57,21 @@ let max_mantissa_fast_path : UInt64 = 2UL << mantissa_explicit_bits /// /// Examples: /// ```mbt -/// inspect(parse_double("123"), content="123") -/// inspect(parse_double("12.34"), content="12.34") -/// inspect(parse_double(".123"), content="0.123") -/// inspect(parse_double("1e5"), content="100000") -/// inspect(parse_double("1.2e-3"), content="0.0012") -/// inspect(parse_double("1_234.5"), content="1234.5") +/// inspect(@strconv.parse_double("123"), content="123") +/// inspect(@strconv.parse_double("12.34"), content="12.34") +/// inspect(@strconv.parse_double(".123"), content="0.123") +/// inspect(@strconv.parse_double("1e5"), content="100000") +/// inspect(@strconv.parse_double("1.2e-3"), content="0.0012") +/// inspect(@strconv.parse_double("1_234.5"), content="1234.5") /// ``` /// /// An exponent value exp scales the mantissa (significand) by 10^exp. /// For example, "1.23e2" represents 1.23 ร— 10ยฒ = 123. -pub fn parse_double(str : String) -> Double raise StrConvError { +pub fn parse_double(str : @string.View) -> Double raise StrConvError { if str.length() == 0 { syntax_err() } - if not(check_underscore(str)) { + if !check_underscore(str) { syntax_err() } // validate its a number @@ -107,11 +107,11 @@ fn is_fast_path(self : Number) -> Bool { min_exponent_fast_path <= self.exponent && self.exponent <= max_exponent_disguised_fast_path && self.mantissa <= max_mantissa_fast_path && - not(self.many_digits) + !self.many_digits } ///| -let table = [ +let table : FixedArray[Double] = [ 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0, 100000000000000.0, 1000000000000000.0, 10000000000000000.0, 100000000000000000.0, @@ -125,7 +125,7 @@ fn pow10_fast_path(exponent : Int) -> Double { } ///| -let int_pow10 : Array[UInt64] = [ +let int_pow10 : FixedArray[UInt64] = [ 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, 100000000000000UL, 1000000000000000UL, diff --git a/bundled-core/strconv/double_test.mbt b/bundled-core/strconv/double_test.mbt index 16bd8e1..5c47d57 100644 --- a/bundled-core/strconv/double_test.mbt +++ b/bundled-core/strconv/double_test.mbt @@ -37,3 +37,20 @@ test "corner cases" { inspect(try? @strconv.parse_double("."), content="Err(invalid syntax)") inspect(try? @strconv.parse_double("-"), content="Err(invalid syntax)") } + +///| +test "parse_double infinity and NaN with trailing characters should error" { + // These should trigger the uncovered line 84 in parse_double + // parse_inf_nan succeeds but doesn't consume the entire string + inspect(try? @strconv.parse_double("infabc"), content="Err(invalid syntax)") + inspect(try? @strconv.parse_double("nanxyz"), content="Err(invalid syntax)") + inspect( + try? @strconv.parse_double("+infinity123"), + content="Err(invalid syntax)", + ) + inspect( + try? @strconv.parse_double("-inf_extra"), + content="Err(invalid syntax)", + ) + inspect(try? @strconv.parse_double("NaN!"), content="Err(invalid syntax)") +} diff --git a/bundled-core/strconv/int.mbt b/bundled-core/strconv/int.mbt index 13f03bc..1b9415d 100644 --- a/bundled-core/strconv/int.mbt +++ b/bundled-core/strconv/int.mbt @@ -30,7 +30,7 @@ const INT64_MAX = 0x7fffffffffffffffL /// The boolean flag `allow_underscore` is used to check validity of underscores. fn check_and_consume_base( view : @string.View, - base : Int + base : Int, ) -> (Int, @string.View, Bool) raise StrConvError { // if the base is not given, we need to determine it from the prefix if base == 0 { @@ -70,17 +70,20 @@ test { /// These underscores do not affect the value. /// Examples: /// ```mbt -/// inspect(parse_int64("123"), content="123") -/// inspect(parse_int64("0xff", base=0), content="255") -/// inspect(parse_int64("0o10"), content="8") -/// inspect(parse_int64("0b1010"), content="10") -/// inspect(parse_int64("1_234"), content="1234") -/// inspect(parse_int64("-123"), content="-123") -/// inspect(parse_int64("ff", base=16), content="255") -/// inspect(parse_int64("zz", base=36), content="1295") +/// inspect(@strconv.parse_int64("123"), content="123") +/// inspect(@strconv.parse_int64("0xff", base=0), content="255") +/// inspect(@strconv.parse_int64("0o10"), content="8") +/// inspect(@strconv.parse_int64("0b1010"), content="10") +/// inspect(@strconv.parse_int64("1_234"), content="1234") +/// inspect(@strconv.parse_int64("-123"), content="-123") +/// inspect(@strconv.parse_int64("ff", base=16), content="255") +/// inspect(@strconv.parse_int64("zz", base=36), content="1295") /// ``` /// -pub fn parse_int64(str : String, base~ : Int = 0) -> Int64 raise StrConvError { +pub fn parse_int64( + str : @string.View, + base? : Int = 0, +) -> Int64 raise StrConvError { guard str != "" else { syntax_err() } let (neg, rest) = match str.view() { ['+', .. rest] => (false, rest) @@ -132,7 +135,7 @@ pub fn parse_int64(str : String, base~ : Int = 0) -> Int64 raise StrConvError { ///| /// Parse a string in the given base (0, 2 to 36), return a Int number or an error. /// If the `~base` argument is 0, the base will be inferred by the prefix. -pub fn parse_int(str : String, base~ : Int = 0) -> Int raise StrConvError { +pub fn parse_int(str : @string.View, base? : Int = 0) -> Int raise StrConvError { let n = parse_int64(str, base~) if n < INT_MIN.to_int64() || n > INT_MAX.to_int64() { range_err() @@ -140,10 +143,11 @@ pub fn parse_int(str : String, base~ : Int = 0) -> Int raise StrConvError { n.to_int() } -///| // Check whether the underscores are correct. // Underscores must appear only between digits or between a base prefix and a digit. -fn check_underscore(str : String) -> Bool { + +///| +fn check_underscore(str : @string.View) -> Bool { // skip the sign let rest = match str { ['+' | '-', .. rest] => rest @@ -191,8 +195,9 @@ fn check_underscore(str : String) -> Bool { } } -///| // Determine the base of the value. + +///| fn determine_base(s : String) -> Int { match s { ['0', 'x' | 'X', ..] => 16 @@ -204,7 +209,7 @@ fn determine_base(s : String) -> Int { ///| fn overflow_threshold(base : Int, neg : Bool) -> Int64 { - if not(neg) { + if !neg { if base == 10 { INT64_MAX / 10L + 1L } else if base == 16 { @@ -238,13 +243,13 @@ test "check_underscore" { ///| test "determine_base" { - assert_eq(determine_base("1234"), 10) - assert_eq(determine_base("0x1234"), 16) - assert_eq(determine_base("0X1234"), 16) - assert_eq(determine_base("0o1234"), 8) - assert_eq(determine_base("0O1234"), 8) - assert_eq(determine_base("0b1010"), 2) - assert_eq(determine_base("0B1010"), 2) + inspect(determine_base("1234"), content="10") + inspect(determine_base("0x1234"), content="16") + inspect(determine_base("0X1234"), content="16") + inspect(determine_base("0o1234"), content="8") + inspect(determine_base("0O1234"), content="8") + inspect(determine_base("0b1010"), content="2") + inspect(determine_base("0B1010"), content="2") } ///| diff --git a/bundled-core/strconv/number.mbt b/bundled-core/strconv/number.mbt index 7faedc9..92c9d65 100644 --- a/bundled-core/strconv/number.mbt +++ b/bundled-core/strconv/number.mbt @@ -23,44 +23,34 @@ priv struct Number { many_digits : Bool } -///| -fn is_digit(c : Char) -> Bool { - c >= '0' && c <= '9' -} - -///| -fn to_digit(c : Char) -> Int { - c.to_int() - 48 -} - ///| /// Returns the remaining slice, the parsed number, and the number of digits parsed. -fn parse_digits(s : StringSlice, x : UInt64) -> (StringSlice, UInt64, Int) { +fn parse_digits(s : @string.View, x : UInt64) -> (@string.View, UInt64, Int) { s.fold_digits(x, (digit, acc : UInt64) => acc * 10UL + UInt64::extend_uint(digit.reinterpret_as_uint())) } ///| fn try_parse_19digits( - s : StringSlice, - x : UInt64 -) -> (StringSlice, UInt64, Int) { + s : @string.View, + x : UInt64, +) -> (@string.View, UInt64, Int) { let mut x = x - let mut s = s let mut len = 0 - while (x < min_19digit_int && s.first_is_digit()) || s.first_is('_') { - if s.first_is('_') { - s = s.step_1_unchecked() + loop s { + ['0'..='9' as ch, .. rest] if x < min_19digit_int => { + len += 1 + x = x * 10UL + + UInt64::extend_uint((ch.to_int() - '0').reinterpret_as_uint()) // no overflows here + continue rest } - len += 1 - x = x * 10UL + UInt64::extend_uint(to_digit(s[0]).reinterpret_as_uint()) // no overflows here - s = s.step_1_unchecked() + ['_', .. rest] => continue rest + s => return (s, x, len) } - (s, x, len) } ///| -fn parse_scientific(s : StringSlice) -> (StringSlice, Int64)? { +fn parse_scientific(s : @string.View) -> (@string.View, Int64)? { // the first character is 'e'/'E' and scientific mode is enabled let mut s = match s.step(1) { Some(s) => s @@ -68,11 +58,11 @@ fn parse_scientific(s : StringSlice) -> (StringSlice, Int64)? { } let exp_num = 0L let mut neg_exp = false - if s.first_is_either('-', '+') { - neg_exp = s[0] == '-' - s = s.step_1_unchecked() + if s is ['+' | '-' as ch, .. rest] { + neg_exp = ch == '-' + s = rest } - if s.first_is_digit() { + if s is ['0'..='9', ..] { let (s, exp_num, _) = s.fold_digits(exp_num, (digit, exp_num : Int64) => if exp_num < 0x10000L { 10L * exp_num + digit.to_int64() // no overflows here @@ -91,17 +81,17 @@ fn parse_scientific(s : StringSlice) -> (StringSlice, Int64)? { ///| /// Parse the number from the string, returning the number and the length of the parsed string. -fn parse_number(s : String) -> (Number, Int)? { - let mut s = full_slice(s) +fn parse_number(s : @string.View) -> (Number, Int)? { + let mut s = s let start = s // handle optional +/- sign let mut negative = false - if s.first_is('-') { + if s is ['-', .. rest] { negative = true - s = s.step_1_unchecked() - } else if s.first_is('+') { - s = s.step_1_unchecked() + s = rest + } else if s is ['+', .. rest] { + s = rest } if s.is_empty() { return None @@ -116,8 +106,8 @@ fn parse_number(s : String) -> (Number, Int)? { // handle dot with the following digits let mut n_after_dot = 0 let mut exponent = 0L - if s.first_is('.') { - s = s.step_1_unchecked() + if s is ['.', .. rest] { + s = rest // TODO: optimization chance. In the original Rust implementation, // the the digits are stored as consecutive bytes in the string. // It directly reads 8 bytes to `u64`. @@ -134,7 +124,7 @@ fn parse_number(s : String) -> (Number, Int)? { // handle scientific format let exp_number = 0L - if s.first_is_either('e', 'E') { + if s is ['e' | 'E', ..] { let (new_s, exp_number) = match parse_scientific(s) { Some(res) => res None => return None @@ -151,9 +141,9 @@ fn parse_number(s : String) -> (Number, Int)? { n_digits -= 19 let mut many_digits = false let mut p = start - while p.first_is_either('0', '.') { - n_digits -= (p[0].to_int() - 46) / 2 // '0' = b'.' + 2 - p = p.step_1_unchecked() + while p is ['0' | '.' as ch, .. rest] { + n_digits -= (ch.to_int() - 46) / 2 // '0' = b'.' + 2 + p = rest } let mut mantissa = mantissa if n_digits > 0 { @@ -181,45 +171,37 @@ fn parse_number(s : String) -> (Number, Int)? { ///| /// Parse the number from the string, returning the number and the length of the parsed string. -fn parse_inf_nan(s : String) -> (Double, Int)? { - fn parse_inf_rest(s : StringSlice) -> Int { - if s.length() >= 8 && s.subfix_unchecked(3).prefix_eq_ignore_case("inity") { - 8 - } else { - 3 - } +fn parse_inf_nan(s : @string.View) -> (Double, Int)? { + let mut s = s + let mut pos = true + let mut len = 0 + if s is ['-' | '+' as ch, .. rest] { + pos = ch == '+' + s = rest + len += 1 } - - let s = full_slice(s) - if s.length() >= 3 { - if s.prefix_eq_ignore_case("nan") { - return Some((@double.not_a_number, 3)) - } else if s.prefix_eq_ignore_case("inf") { - return Some((@double.infinity, parse_inf_rest(s))) - } else if s.length() >= 4 { - if s.first_is('+') { - let s = s.step_1_unchecked() - if s.prefix_eq_ignore_case("nan") { - return Some((@double.not_a_number, 4)) - } else if s.prefix_eq_ignore_case("inf") { - return Some((@double.infinity, 1 + parse_inf_rest(s))) - } - } else if s.first_is('-') { - let s = s.step_1_unchecked() - if s.prefix_eq_ignore_case("nan") { - return Some((@double.not_a_number, 4)) - } else if s.prefix_eq_ignore_case("inf") { - return Some((@double.neg_infinity, 1 + parse_inf_rest(s))) - } - } + if s is ['n' | 'N', 'a' | 'A', 'n' | 'N', ..] { + Some((@double.not_a_number, len + 3)) + } else if s is ['i' | 'I', 'n' | 'N', 'f' | 'F', .. rest] { + len += 3 + if rest is ['i' | 'I', 'n' | 'N', 'i' | 'I', 't' | 'T', 'y' | 'Y', ..] { + len += 5 } + if pos { + Some((@double.infinity, len)) + } else { + Some((@double.neg_infinity, len)) + } + } else { + None } - None } ///| /// Returns None if the multiplication might overflow (there are some false-negative corner cases). /// Otherwise, returns Some(m), where m = self * b. +/// WARNING: Note this function is only used internally in the strconv module, +/// the current implementation is not completely safe against overflows. fn checked_mul(a : UInt64, b : UInt64) -> UInt64? { if a == 0UL || b == 0UL { return Some(0UL) @@ -230,12 +212,12 @@ fn checked_mul(a : UInt64, b : UInt64) -> UInt64? { if b == 1UL { return Some(a) } - if b < 0UL || a < 0UL { + // Can only multiply by 1 or 0, which is handled above. + if b.clz() == 0 || a.clz() == 0 { return None } - let r : UInt64 = @uint64.max_value / b - let q : UInt64 = 1UL / b - if r + r + q < a { + let quotient : UInt64 = @uint64.max_value / b + if a > quotient { return None } Some(a * b) diff --git a/bundled-core/strconv/number_wbtest.mbt b/bundled-core/strconv/number_wbtest.mbt new file mode 100644 index 0000000..4f3197e --- /dev/null +++ b/bundled-core/strconv/number_wbtest.mbt @@ -0,0 +1,193 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "parse_inf_nan basic cases" { + // Test basic NaN parsing + let result_nan = parse_inf_nan("nan") + match result_nan { + Some((value, length)) => { + inspect(value.is_nan(), content="true") + inspect(length, content="3") + } + None => fail("Expected to parse 'nan'") + } + + // Test basic infinity parsing + let result_inf = parse_inf_nan("inf") + match result_inf { + Some((value, length)) => { + inspect(value.is_inf() && value > 0.0, content="true") + inspect(length, content="3") + } + None => fail("Expected to parse 'inf'") + } +} + +///| +test "parse_inf_nan with signs" { + // Test positive signed NaN + let result_pos_nan = parse_inf_nan("+nan") + match result_pos_nan { + Some((value, length)) => { + inspect(value.is_nan(), content="true") + inspect(length, content="4") + } + None => fail("Expected to parse '+nan'") + } + + // Test negative signed infinity + let result_neg_inf = parse_inf_nan("-inf") + match result_neg_inf { + Some((value, length)) => { + inspect(value.is_inf() && value < 0.0, content="true") + inspect(length, content="4") + } + None => fail("Expected to parse '-inf'") + } +} + +///| +test "parse_inf_nan case insensitive" { + // Test uppercase + let result_uppercase = parse_inf_nan("NAN") + match result_uppercase { + Some((value, length)) => { + inspect(value.is_nan(), content="true") + inspect(length, content="3") + } + None => fail("Expected to parse 'NAN'") + } + + // Test mixed case + let result_mixed = parse_inf_nan("InF") + match result_mixed { + Some((value, length)) => { + inspect(value.is_inf() && value > 0.0, content="true") + inspect(length, content="3") + } + None => fail("Expected to parse 'InF'") + } +} + +///| +test "parse_inf_nan with infinity suffix" { + // Test full infinity word + let result_infinity = parse_inf_nan("infinity") + match result_infinity { + Some((value, length)) => { + inspect(value.is_inf() && value > 0.0, content="true") + inspect(length, content="8") + } + None => fail("Expected to parse 'infinity'") + } + + // Test signed full infinity + let result_neg_infinity = parse_inf_nan("-INFINITY") + match result_neg_infinity { + Some((value, length)) => { + inspect(value.is_inf() && value < 0.0, content="true") + inspect(length, content="9") + } + None => fail("Expected to parse '-INFINITY'") + } +} + +///| +test "parse_inf_nan with trailing strings" { + // Test trailing characters after 'inf' + let result_trailing = parse_inf_nan("infabc") + match result_trailing { + Some((value, length)) => { + inspect(value.is_inf() && value > 0.0, content="true") + inspect(length, content="3") // should only parse 'inf' + } + None => fail("Expected to parse 'inf' but got None") + } + + // Test trailing characters after 'nan' + let result_nan_trailing = parse_inf_nan("nanxyz") + match result_nan_trailing { + Some((value, length)) => { + inspect(value.is_nan(), content="true") + inspect(length, content="3") // should only parse 'nan' + } + None => fail("Expected to parse 'nan' but got None") + } + + // Test trailing characters after signed infinity + let result_signed_trailing = parse_inf_nan("+infinity123") + match result_signed_trailing { + Some((value, length)) => { + inspect(value.is_inf() && value > 0.0, content="true") + inspect(length, content="9") // should only parse '+infinity' + } + None => fail("Expected to parse '+infinity' but got None") + } +} + +///| +test "parse_inf_nan failures" { + // Test invalid input + inspect(parse_inf_nan("hello"), content="None") + inspect(parse_inf_nan(""), content="None") + inspect(parse_inf_nan("in"), content="None") + inspect(parse_inf_nan("na"), content="None") +} + +///| +test "checked_mul basic cases" { + // Test zero multiplication + inspect(checked_mul(0UL, 5UL), content="Some(0)") + inspect(checked_mul(5UL, 0UL), content="Some(0)") + + // Test multiplication by one + inspect(checked_mul(1UL, 42UL), content="Some(42)") + inspect(checked_mul(42UL, 1UL), content="Some(42)") + + // Test normal multiplication + inspect(checked_mul(3UL, 4UL), content="Some(12)") + inspect(checked_mul(10UL, 10UL), content="Some(100)") +} + +///| +test "checked_mul edge cases" { + // Test potential overflow + let large_val = 0xFFFFFFFFFFFFFFFFUL // max uint64 + inspect(checked_mul(large_val, 2UL), content="None") + inspect(checked_mul(2UL, large_val), content="None") + + // Test small multiplication that should work + inspect(checked_mul(2UL, 3UL), content="Some(6)") + + // Test corner case + inspect( + checked_mul(5UL, 3689348814741910323UL), + content="Some(18446744073709551615)", + ) + inspect( + checked_mul(3689348814741910323UL, 5UL), + content="Some(18446744073709551615)", + ) + inspect( + checked_mul(4UL, 4611686018427387903UL), + content="Some(18446744073709551612)", + ) + inspect( + checked_mul(4611686018427387903UL, 4UL), + content="Some(18446744073709551612)", + ) + inspect(checked_mul(4611686018427387904UL, 4UL), content="None") + inspect(checked_mul(4UL, 4611686018427387904UL), content="None") +} diff --git a/bundled-core/strconv/strconv.mbti b/bundled-core/strconv/pkg.generated.mbti similarity index 53% rename from bundled-core/strconv/strconv.mbti rename to bundled-core/strconv/pkg.generated.mbti index 254870f..8fcf878 100644 --- a/bundled-core/strconv/strconv.mbti +++ b/bundled-core/strconv/pkg.generated.mbti @@ -1,22 +1,31 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/strconv" +import( + "moonbitlang/core/string" +) + // Values fn[A : FromStr] parse(String) -> A raise StrConvError -fn parse_bool(String) -> Bool raise StrConvError +fn parse_bool(@string.View) -> Bool raise StrConvError #deprecated -fn parse_decimal(String) -> Decimal raise StrConvError +fn parse_decimal(@string.View) -> Decimal raise StrConvError + +fn parse_double(@string.View) -> Double raise StrConvError -fn parse_double(String) -> Double raise StrConvError +fn parse_int(@string.View, base? : Int) -> Int raise StrConvError -fn parse_int(String, base~ : Int = ..) -> Int raise StrConvError +fn parse_int64(@string.View, base? : Int) -> Int64 raise StrConvError -fn parse_int64(String, base~ : Int = ..) -> Int64 raise StrConvError +fn parse_uint(@string.View, base? : Int) -> UInt raise StrConvError -fn parse_uint(String, base~ : Int = ..) -> UInt raise StrConvError +fn parse_uint64(@string.View, base? : Int) -> UInt64 raise StrConvError -fn parse_uint64(String, base~ : Int = ..) -> UInt64 raise StrConvError +// Errors +pub(all) suberror StrConvError String +impl Show for StrConvError // Types and methods type Decimal @@ -25,16 +34,13 @@ fn Decimal::from_int64(Int64) -> Self #deprecated fn Decimal::new() -> Self #deprecated -fn Decimal::parse_decimal(String) -> Self raise StrConvError +fn Decimal::parse_decimal(@string.View) -> Self raise StrConvError #deprecated fn Decimal::shift(Self, Int) -> Unit #deprecated fn Decimal::to_double(Self) -> Double raise StrConvError impl Show for Decimal -pub(all) suberror StrConvError String -impl Show for StrConvError - // Type aliases // Traits diff --git a/bundled-core/strconv/string_slice.mbt b/bundled-core/strconv/string_slice.mbt deleted file mode 100644 index 43e5d31..0000000 --- a/bundled-core/strconv/string_slice.mbt +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -///| -/// A slice of a string. -/// TODO: make it a monad -priv struct StringSlice { - string : String - start : Int - end : Int -} - -///| -fn slice(s : String, start : Int, end : Int) -> StringSlice { - { string: s, start, end } -} - -///| -fn full_slice(s : String) -> StringSlice { - slice(s, 0, s.length()) -} - -///| -fn subfix_unchecked(self : StringSlice, start : Int) -> StringSlice { - { string: self.string, start: self.start + start, end: self.end } -} - -///| -fn first_is(self : StringSlice, c : Char) -> Bool { - self.length() > 0 && self[0] == c -} - -///| -fn first_is_either(self : StringSlice, c1 : Char, c2 : Char) -> Bool { - self.length() > 0 && (self[0] == c1 || self[0] == c2) -} - -///| -fn first_is_digit(self : StringSlice) -> Bool { - self.length() > 0 && is_digit(self[0]) -} - -///| -/// Step over `step` non-underscore characters. -/// Return None if there are not enough characters. -fn step(self : StringSlice, step : Int) -> StringSlice? { - let c = self - let mut step = step - let mut start = c.start - while start < c.end && step > 0 { - if c.string.unsafe_charcode_at(start) != '_' { - step -= 1 - } - start += 1 - } - if step == 0 { - Some(c.subfix_unchecked(start - c.start)) - } else { - None - } -} - -///| -/// Step over the first character without checking. -fn step_1_unchecked(self : StringSlice) -> StringSlice { - { string: self.string, start: self.start + 1, end: self.end } -} - -///| -/// Unsafe: make sure index is in bounds -fn op_get(self : StringSlice, index : Int) -> Char { - self.string.unsafe_charcode_at(self.start + index).unsafe_to_char() -} - -///| -fn length(self : StringSlice) -> Int { - self.end - self.start -} - -///| -fn is_empty(self : StringSlice) -> Bool { - self.start == self.end -} - -///| -fn prefix_eq_ignore_case(self : StringSlice, s2 : String) -> Bool { - let end = if s2.length() < self.length() { - s2.length() - } else { - self.length() - } - for i in 0.. Char { - if 'A' <= c && c <= 'Z' { - (c.to_int() + 'a'.to_int() - 'A'.to_int()).unsafe_to_char() - } else { - c - } -} - -///| -/// Returns the accumulated value, the slice left, and the number of digits consumed. -/// It ignores underscore and stops when a non-digit character is found. -fn[T] fold_digits( - self : StringSlice, - init : T, - f : (Int, T) -> T -) -> (StringSlice, T, Int) { - let mut ret = init - let mut len = 0 - // let len = self.length() - for i in 0.. Self? { + let mut step = step + let mut str = self + while str is [ch, .. rest] && step > 0 { + if ch != '_' { + step -= 1 + } + str = rest + } + if step == 0 { + Some(str) + } else { + None + } +} + +///| +/// Returns the accumulated value, the slice left, and the number of digits consumed. +/// It ignores underscore and stops when a non-digit character is found. +fn[T] @string.View::fold_digits( + self : Self, + init : T, + f : (Int, T) -> T, +) -> (Self, T, Int) { + let mut ret = init + let mut len = 0 + let mut str = self + while str is [ch, .. rest] { + if ch is ('0'..='9') { + len += 1 + ret = f(ch.to_int() - '0', ret) + } else if ch != '_' { + break + } + str = rest + } + (str, ret, len) +} diff --git a/bundled-core/strconv/traits.mbt b/bundled-core/strconv/traits.mbt index 372ced3..bb5c4fb 100644 --- a/bundled-core/strconv/traits.mbt +++ b/bundled-core/strconv/traits.mbt @@ -61,13 +61,13 @@ pub fn[A : FromStr] parse(str : String) -> A raise StrConvError { ///| test "parse" { let b : Bool = parse("true") - assert_eq(b, true) + inspect(b, content="true") let i : Int = parse("12345") - assert_eq(i, 12345) + inspect(i, content="12345") let i64 : Int64 = parse("9223372036854775807") assert_eq(i64, 9223372036854775807L) let ui : UInt = parse("4294967295") - assert_eq(ui, 4294967295) + inspect(ui, content="4294967295") let ui64 : UInt64 = parse("18446744073709551615") assert_eq(ui64, 18446744073709551615UL) let d : Double = parse("1234.56789") diff --git a/bundled-core/strconv/uint.mbt b/bundled-core/strconv/uint.mbt index 866b39d..1ef5e82 100644 --- a/bundled-core/strconv/uint.mbt +++ b/bundled-core/strconv/uint.mbt @@ -30,16 +30,19 @@ const UINT64_MAX : UInt64 = 0xffffffffffffffffUL /// These underscores do not affect the value. /// Examples: /// ```mbt -/// inspect(parse_uint64("123"), content="123") -/// inspect(parse_uint64("0xff", base=0), content="255") -/// inspect(parse_uint64("0o10"), content="8") -/// inspect(parse_uint64("0b1010"), content="10") -/// inspect(parse_uint64("1_234"), content="1234") -/// inspect(parse_uint64("ff", base=16), content="255") -/// inspect(parse_uint64("zz", base=36), content="1295") +/// inspect(@strconv.parse_uint64("123"), content="123") +/// inspect(@strconv.parse_uint64("0xff", base=0), content="255") +/// inspect(@strconv.parse_uint64("0o10"), content="8") +/// inspect(@strconv.parse_uint64("0b1010"), content="10") +/// inspect(@strconv.parse_uint64("1_234"), content="1234") +/// inspect(@strconv.parse_uint64("ff", base=16), content="255") +/// inspect(@strconv.parse_uint64("zz", base=36), content="1295") /// ``` /// -pub fn parse_uint64(str : String, base~ : Int = 0) -> UInt64 raise StrConvError { +pub fn parse_uint64( + str : @string.View, + base? : Int = 0, +) -> UInt64 raise StrConvError { guard str != "" else { syntax_err() } if str is ['+' | '-', ..] { syntax_err() @@ -85,7 +88,10 @@ pub fn parse_uint64(str : String, base~ : Int = 0) -> UInt64 raise StrConvError ///| /// Parse a string in the given base (0, 2 to 36), return an UInt number or an error. /// If the `~base` argument is 0, the base will be inferred by the prefix. -pub fn parse_uint(str : String, base~ : Int = 0) -> UInt raise StrConvError { +pub fn parse_uint( + str : @string.View, + base? : Int = 0, +) -> UInt raise StrConvError { let n = parse_uint64(str, base~) if n > UINT_MAX.to_uint64() { range_err() diff --git a/bundled-core/string/DESIGN.mbt.md b/bundled-core/string/DESIGN.mbt.md index 8a3630e..1f88501 100644 --- a/bundled-core/string/DESIGN.mbt.md +++ b/bundled-core/string/DESIGN.mbt.md @@ -44,7 +44,7 @@ test "unsafe vs safe" { // Unsafe: May split surrogate pairs let emoji = "๐ŸŽ‰" - let _ = emoji.char_at(1) // Gets second half of surrogate pair + let _ = emoji.get_char(1) // Gets second half of surrogate pair // Safe: Uses iterator for c in "Hello ๐ŸŒ".iter() { // Properly handles both ASCII and Unicode chars @@ -82,16 +82,16 @@ test "unsafe vs safe" { ```moonbit test "view conversion" { - fn process_text(text : View) -> Int { + fn process_text(text : @string.View) -> Int { text.length() } let str = "Hello World" - let view = str.charcodes(start=0, end=5) + let view = str.view(start_offset=0, end_offset=5) // Both work due to implicit conversion - let _len1 = process_text(str) // String implicitly converts to View - let _len2 = process_text(view) + let _ = process_text(str) // String implicitly converts to View + let _ = process_text(view) // Direct View usage } ``` @@ -113,4 +113,4 @@ test "view conversion" { boundaries when iterating or accessing characters. When creating views, it is required to provide valid String with valid offsets. Violating this will result in the replacement character ๏ฟฝ being displayed when inspecting the - view. \ No newline at end of file + view. diff --git a/bundled-core/string/README.mbt.md b/bundled-core/string/README.mbt.md new file mode 100644 index 0000000..40cfea1 --- /dev/null +++ b/bundled-core/string/README.mbt.md @@ -0,0 +1,169 @@ +# String Package Documentation + +This package provides comprehensive string manipulation utilities for MoonBit, including string creation, conversion, searching, and Unicode handling. + +## String Creation and Conversion + +Create strings from various sources: + +```moonbit +test "string creation" { + // From character array + let chars = ['H', 'e', 'l', 'l', 'o'] + let str1 = String::from_array(chars) + inspect(str1, content="Hello") + + // From character iterator + let str2 = String::from_iter(['W', 'o', 'r', 'l', 'd'].iter()) + inspect(str2, content="World") + + // Default empty string + let empty = String::default() + inspect(empty, content="") +} +``` + +## String Iteration + +Iterate over Unicode characters in strings: + +```moonbit +test "string iteration" { + let text = "Hello๐ŸŒ" + + // Forward iteration + let chars = text.iter().collect() + inspect(chars, content="['H', 'e', 'l', 'l', 'o', '๐ŸŒ']") + + // Reverse iteration + let reversed = text.rev_iter().collect() + inspect(reversed, content="['๐ŸŒ', 'o', 'l', 'l', 'e', 'H']") + + // Iteration with indices - demonstrate iter2 functionality + let mut count = 0 + let mut first_char = 'a' + text.iter2().each(fn(idx, char) { + if idx == 0 { first_char = char } + count = count + 1 + }) + inspect(first_char, content="H") + inspect(count, content="6") // 6 Unicode characters +} +``` + +## String Conversion + +Convert strings to other formats: + +```moonbit +test "string conversion" { + let text = "Hello" + + // Convert to character array + let chars = text.to_array() + inspect(chars, content="['H', 'e', 'l', 'l', 'o']") + + // Convert to bytes (UTF-16 LE encoding) + let bytes = text.to_bytes() + inspect(bytes.length(), content="10") // 5 chars * 2 bytes each +} +``` + +## Unicode Handling + +Work with Unicode characters and surrogate pairs: + +```moonbit +test "unicode handling" { + let emoji_text = "Hello๐ŸคฃWorld" + + // Character count vs UTF-16 code unit count + let char_count = emoji_text.iter().count() + let code_unit_count = emoji_text.length() + inspect(char_count, content="11") // Unicode characters + inspect(code_unit_count, content="12") // UTF-16 code units + + // Find character offset + let offset = emoji_text.offset_of_nth_char(5) // Position of emoji + inspect(offset, content="Some(5)") + + // Test character length + let has_11_chars = emoji_text.char_length_eq(11) + inspect(has_11_chars, content="true") +} +``` + +## String Comparison + +Strings are ordered using shortlex order by Unicode code points: + +```moonbit +test "string comparison" { + let result1 = "apple".compare("banana") + inspect(result1, content="-1") // apple < banana + + let result2 = "hello".compare("hello") + inspect(result2, content="0") // equal + + let result3 = "zebra".compare("apple") + inspect(result3, content="1") // zebra > apple +} +``` + +## String Views + +String views provide efficient substring operations without copying: + +```moonbit +test "string views" { + let text = "Hello, World!" + let view = text[:][7:12] // "World" - create view using slice notation + + // Views support similar operations as strings + let chars = view.iter().collect() + inspect(chars, content="['W', 'o', 'r', 'l', 'd']") + + // Convert view back to string + let substring = view.to_string() + inspect(substring, content="World") +} +``` + +## Practical Examples + +Common string manipulation tasks: + +```moonbit +test "practical examples" { + let text = "The quick brown fox" + + // Split into words (using whitespace) - returns Iter[View] + let words = text.split(" ").collect() + inspect(words.length(), content="4") + inspect(words[0].to_string(), content="The") + inspect(words[3].to_string(), content="fox") + + // Join words back together - convert views to strings first + let word_strings = words.map(fn(v) { v.to_string() }) + let mut result = "" + for i, word in word_strings.iter2() { + if i > 0 { result = result + "-" } + result = result + word + } + inspect(result, content="The-quick-brown-fox") + + // Case conversion (works on views) + let upper = text[:].to_upper().to_string() + inspect(upper, content="THE QUICK BROWN FOX") + + let lower = text[:].to_lower().to_string() + inspect(lower, content="the quick brown fox") +} +``` + +## Performance Notes + +- Use `StringBuilder` or `Buffer` for building strings incrementally rather than repeated concatenation +- String views are lightweight and don't copy the underlying data +- Unicode iteration handles surrogate pairs correctly but is slower than UTF-16 code unit iteration +- Character length operations (`char_length_eq`, `char_length_ge`) have O(n) complexity where n is the character count diff --git a/bundled-core/string/additional_coverage_test.mbt b/bundled-core/string/additional_coverage_test.mbt index 65b7de9..a00c2d6 100644 --- a/bundled-core/string/additional_coverage_test.mbt +++ b/bundled-core/string/additional_coverage_test.mbt @@ -52,15 +52,32 @@ test "String::offset_of_nth_char_forward with special case" { } ///| -test "panic String::char_at with out of bounds offset" { - let s = "Hello" +test "View::pad_start and pad_end no padding" { + let v = "abc"[1:3] // "bc" + inspect(v.pad_start(2, 'x'), content="bc") + inspect(v.pad_end(2, 'y'), content="bc") +} + +///| +test "View::compare identical" { + let str = "abcdef" + let v = str.view(start_offset=2, end_offset=4) + inspect(v.compare(v), content="0") +} - // Valid index should work - let c = s.char_at(0) - inspect(c, content="'H'") +///| +test "View::default and hash" { + let v : @string.View = @string.View::default() + inspect(v, content="") + let v1 = "abc".view() + let v2 = "abc".view() + assert_eq(v1.hash(), v2.hash()) +} - // As this would abort, we do a different test - let len = s.length() - let last = s.char_at(len - 1) - inspect(last, content="'o'") +///| +test "get_char invalid surrogate pair" { + let s = String::from_array([(0xD800).unsafe_to_char(), 'A']) + inspect(s.get_char(0), content="None") + let v = s[:] + inspect(v.get_char(0), content="None") } diff --git a/bundled-core/string/deprecated.mbt b/bundled-core/string/deprecated.mbt index 93663ac..4e36a8a 100644 --- a/bundled-core/string/deprecated.mbt +++ b/bundled-core/string/deprecated.mbt @@ -11,238 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -#deprecated("Use `@string.concat` instead") -pub fn String::concat(self : Array[String], separator~ : String = "") -> String { - concat(self, separator~) -} - -///| -type StringIndex Int derive(Show, Eq) - -///| -#deprecated("StringIndex is deprecated, use String::index_of_nth_char instead") -#coverage.skip -pub fn index_at( - self : String, - offset_by : Int, - start~ : StringIndex = 0 -) -> StringIndex? { - self.index_at_(offset_by, start~) -} - -///| -#deprecated("StringIndex is deprecated, use String::index_of_nth_char instead") -#coverage.skip -pub fn index_at_rev( - self : String, - offset_by : Int, - end? : StringIndex -) -> StringIndex? { - self.index_at_rev_(offset_by, end?) -} - -///| -fn index_at_( - self : String, - offset_by : Int, - start~ : StringIndex = 0 -) -> StringIndex? { - if self.offset_of_nth_char(offset_by, start_offset=start.inner()) - is Some(index) { - Some(index) - } else if offset_by == self.char_length(start_offset=start.inner()) { - Some(self.length()) - } else { - None - } -} - -///| -fn index_at_rev_( - self : String, - offset_by : Int, - end? : StringIndex -) -> StringIndex? { - match end { - Some(end) => - if offset_by == 0 { - Some(end) - } else if self.offset_of_nth_char(-offset_by, end_offset=end.inner()) - is Some(index) { - Some(index) - } else { - None - } - None => - if offset_by == 0 { - Some(self.length()) - } else if self.offset_of_nth_char(-offset_by) is Some(index) { - Some(index) - } else { - None - } - } -} - -///| -#deprecated("use str.charcodes(start = str.offset_of_nth_char(i).unwrap(), end = str.offset_of_nth_char(j).unwrap()) to replace str[i:j]") -#coverage.skip -pub fn String::op_as_view(self : String, start~ : Int = 0, end? : Int) -> View { - let str_len = self.length() - let start = if start >= 0 { - self.index_at_(start, start=0).unwrap().inner() - } else { - self.index_at_rev_(-start, end=str_len).unwrap().inner() - } - let end = match end { - Some(end) => - if end >= 0 { - self.index_at_(end, start=0).unwrap().inner() - } else { - self.index_at_rev_(-end, end=str_len).unwrap().inner() - } - None => str_len - } - guard start >= 0 && start <= end && end <= str_len else { - abort("Invalid index for View") - } - { str: self, start, end } -} - -///| -#deprecated("use view.charcodes(start = view.offset_of_nth_char(i).unwrap(), end = view.offset_of_nth_char(j).unwrap()) to replace view[i:j]") -#coverage.skip -pub fn View::op_as_view(self : View, start~ : Int = 0, end? : Int) -> View { - let start = if start >= 0 { - self.str.index_at_(start, start=self.start).unwrap().inner() - } else { - self.str.index_at_rev_(-start, end=self.end).unwrap().inner() - } - let end = match end { - Some(end) => - if end >= 0 { - self.str.index_at_(end, start=self.start).unwrap().inner() - } else { - self.str.index_at_rev_(-end, end=self.end).unwrap().inner() - } - None => self.end - } - guard start >= self.start && - start <= self.end && - end >= self.start && - end <= self.end && - start <= end else { - abort("Invalid index for View") - } - { str: self.str, start, end } -} - -///| -#deprecated("Use `Array::join` instead.") -pub fn concat(strings : Array[String], separator~ : String = "") -> String { - match strings { - [] => "" - [hd, .. tl] => { - let mut size_hint = hd.length() - for s in tl { - size_hint += s.length() + separator.length() - } - size_hint = size_hint << 1 - let buf = StringBuilder::new(size_hint~) - buf.write_string(hd) - if separator == "" { - for s in tl { - buf.write_string(s) - } - } else { - for s in tl { - buf.write_string(separator) - buf.write_string(s) - } - } - buf.to_string() - } - } -} - -///| -#deprecated("Use `s.find(substr)` instead. If the optional argument `from` is not 0, take view from the string first. Please do not use an invalid `from` argument.") -pub fn index_of(self : String, str : String, from~ : Int = 0) -> Int { - if from <= 0 { - if self.find(str.view()) is Some(idx) { - idx - } else { - -1 - } - } else if from > self.length() { - if str.length() == 0 { - self.length() - } else { - -1 - } - } else if self.view(start_offset=from).find(str.view()) is Some(idx) { - idx + from - } else { - -1 - } -} - -///| -/// Returns the last index of the sub string. -#deprecated("Use `s.rev_find(substr)` instead. If the optional argument `from` is not 0, take view from the string first. Please do not use an invalid `from` argument.") -pub fn last_index_of(self : String, str : String, from? : Int) -> Int { - let from = if from is Some(f) { f } else { self.length() } - if from >= self.length() { - if self.rev_find(str.view()) is Some(idx) { - idx - } else { - -1 - } - } else if from < 0 { - if str.length() == 0 { - self.length() - } else { - -1 - } - } else if self.view(end_offset=from).rev_find(str.view()) is Some(idx) { - idx - } else { - -1 - } -} - -///| -/// Returns true if this string starts with a sub string. -#deprecated("Use `s.has_prefix(str)` instead.") -pub fn starts_with(self : String, str : String) -> Bool { - self.has_prefix(str.view()) -} - -///| -/// Returns true if this string ends with a sub string. -#deprecated("Use `s.has_suffix(str)` instead.") -pub fn ends_with(self : String, str : String) -> Bool { - self.has_suffix(str.view()) -} - -///| -/// A `StringView` represents a view of a String that maintains proper Unicode -/// character boundaries. It allows safe access to a substring while handling -/// multi-byte characters correctly. -#deprecated("use @string.View instead") -#builtin.valtype -struct StringView { - // # Fields - // - // - `str`: The source String being viewed - // - `start`: Starting UTF-16 code unit index into the string - // - `end`: Ending UTF-16 code unit index into the string (not included) - // - // `len` is not included because it will make the operation of `op_as_view` - // has complexity O(n) where n is the length of the code points in the view. - str : String - start : Int - end : Int -} diff --git a/bundled-core/string/methods.mbt b/bundled-core/string/methods.mbt index 2f70f2e..4ea8f9f 100644 --- a/bundled-core/string/methods.mbt +++ b/bundled-core/string/methods.mbt @@ -16,36 +16,86 @@ /// Returns the offset (charcode index) of the first occurrence of the given /// substring. If the substring is not found, it returns None. pub fn View::find(self : View, str : View) -> Int? { - let len = self.length() - let sub_len = str.length() - // Handle empty substring case - guard sub_len > 0 else { return Some(0) } - // If substring is longer than string, it can't be found - guard sub_len <= len else { return None } - let max_idx = len - sub_len - let first = str.unsafe_charcode_at(0) + if str.length() <= 4 { + brute_force_find(self, str) + } else { + boyer_moore_horspool_find(self, str) + } + // TODO: When the pattern string is long (>= 256), + // consider using Two-Way algorithm to ensure linear time complexity. +} + +///| +/// Simple brute force string search algorithm +/// Scans the haystack left to right, matching the needle at each position +fn brute_force_find(haystack : View, needle : View) -> Int? { + let haystack_len = haystack.length() + let needle_len = needle.length() + guard needle_len > 0 else { return Some(0) } + guard haystack_len >= needle_len else { return None } + let needle_first = needle.unsafe_charcode_at(0) + let forward_len = haystack_len - needle_len let mut i = 0 - while i <= max_idx { - // find first character - while i < len && self.unsafe_charcode_at(i) != first { + while i <= forward_len { + // Skip positions where first charcode doesn't match + while i <= forward_len && haystack.unsafe_charcode_at(i) != needle_first { i += 1 } - // check the rest - if i <= max_idx { - for j in 1.. Int? { + let haystack_len = haystack.length() + let needle_len = needle.length() + guard needle_len > 0 else { return Some(0) } + guard haystack_len >= needle_len else { return None } + // Build skip table + let skip_table = FixedArray::make(1 << 8, needle_len) + for i in 0..<(needle_len - 1) { + skip_table[needle.unsafe_charcode_at(i) & 0xFF] = needle_len - 1 - i + } + for i = 0 + i <= haystack_len - needle_len + i = i + skip_table[haystack.unsafe_charcode_at(i + needle_len - 1) & 0xFF] { + // Check all charcodes for match at current position + for j in 0..=(needle_len - 1) { + if haystack.unsafe_charcode_at(i + j) != needle.unsafe_charcode_at(j) { + break + } + } else { + return Some(i) + } + } + None +} + +///| +test "boyer_moore_horspool_find edge cases" { + inspect(boyer_moore_horspool_find("abc"[:], ""[:]), content="Some(0)") + inspect(boyer_moore_horspool_find("ab"[:], "abcd"[:]), content="None") +} + +///| +test "boyer_moore_horspool_rev_find edge cases" { + inspect(boyer_moore_horspool_rev_find("abc"[:], ""[:]), content="Some(3)") + inspect(boyer_moore_horspool_rev_find("ab"[:], "abcd"[:]), content="None") +} + ///| /// Returns the offset of the first occurrence of the given substring. If the /// substring is not found, it returns None. @@ -66,6 +116,14 @@ test "find" { inspect("hello hello".find("hello"), content="Some(0)") inspect("aaa".find("aa"), content="Some(0)") inspect("๐Ÿ˜€๐Ÿ˜€".find("๐Ÿ˜€"), content="Some(0)") + inspect( + ("๐Ÿ˜€๐Ÿ˜€aa".repeat(20) + "๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€").find("๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€"), + content="Some(120)", + ) + inspect( + ("๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€" + "๐Ÿ˜€๐Ÿ˜€aa".repeat(20)).find("๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€"), + content="Some(0)", + ) } ///| @@ -105,28 +163,68 @@ test "find_by" { /// Returns the offset of the last occurrence of the given substring. If the /// substring is not found, it returns None. pub fn View::rev_find(self : View, str : View) -> Int? { - let len = self.length() - let sub_len = str.length() - guard sub_len > 0 else { return Some(len) } - guard sub_len <= len else { return None } - let min_idx = sub_len - 1 - let last = str.unsafe_charcode_at(sub_len - 1) - let mut i = len - 1 - while i >= min_idx { - while i >= 0 && self.unsafe_charcode_at(i) != last { + if str.length() <= 4 { + brute_force_rev_find(self, str) + } else { + boyer_moore_horspool_rev_find(self, str) + } + // TODO: When the pattern string is long (>= 256), + // consider using Two-Way algorithm to ensure linear time complexity. +} + +///| +/// Simple brute force string search algorithm +/// Scans the haystack right to left, matching the needle at each position +fn brute_force_rev_find(haystack : View, needle : View) -> Int? { + let haystack_len = haystack.length() + let needle_len = needle.length() + guard needle_len > 0 else { return Some(haystack_len) } + guard haystack_len >= needle_len else { return None } + let needle_first = needle.unsafe_charcode_at(0) + let mut i = haystack_len - needle_len + while i >= 0 { + // Skip positions where first charcode doesn't match + while i >= 0 && haystack.unsafe_charcode_at(i) != needle_first { i -= 1 } - if i >= min_idx { - for j in 1..= 0 { + // Check remaining charcodes for full match + for j in 1.. Int? { + let haystack_len = haystack.length() + let needle_len = needle.length() + guard needle_len > 0 else { return Some(haystack_len) } + guard haystack_len >= needle_len else { return None } + let skip_table = FixedArray::make(1 << 8, needle_len) + for i = needle_len - 1; i > 0; i = i - 1 { + skip_table[needle.unsafe_charcode_at(i) & 0xFF] = i + } + for i = haystack_len - needle_len + i >= 0 + i = i - skip_table[haystack.unsafe_charcode_at(i) & 0xFF] { + // Check all charcodes for match at current position + for j in 0.. Bool { self.rev_find(str) is Some(i) && i == self.length() - str.length() } ///| /// Returns true if the given substring is suffix of this string. +#alias(ends_with, deprecated) pub fn String::has_suffix(self : String, str : View) -> Bool { self[:].has_suffix(str) } @@ -181,12 +289,14 @@ test "has_suffix" { ///| /// Returns true if this string starts with the given substring. +#alias(starts_with, deprecated) pub fn View::has_prefix(self : View, str : View) -> Bool { self.find(str) is Some(i) && i == 0 } ///| /// Returns true if this string starts with the given substring. +#alias(starts_with, deprecated) pub fn String::has_prefix(self : String, str : View) -> Bool { self[:].has_prefix(str) } @@ -206,6 +316,212 @@ test "has_prefix" { inspect("hello๐Ÿ˜€".has_prefix("๐Ÿ˜€"), content="false") } +///| +/// Removes the given suffix from the string if it exists. +/// +/// Returns `Some(prefix)` if the string ends with the given suffix, +/// where `prefix` is the string without the suffix. +/// Returns `None` if the string does not end with the suffix. +/// +/// # Example +/// +/// ```moonbit +/// inspect("hello world".strip_suffix(" world"), content="Some(\"hello\")") +/// inspect("hello world".strip_suffix(" moon"), content="None") +/// inspect("hello".strip_suffix("hello"), content="Some(\"\")") +/// ``` +pub fn strip_suffix(self : String, suffix : View) -> View? { + if self.has_suffix(suffix) { + Some(self.view(end_offset=self.length() - suffix.length())) + } else { + None + } +} + +///| +test "strip_prefix" { + inspect("hello world".strip_prefix("hello "), content="Some(\"world\")") + inspect("hello world".strip_prefix("hi "), content="None") + inspect("hello".strip_prefix("hello"), content="Some(\"\")") + inspect("".strip_prefix(""), content="Some(\"\")") + inspect("".strip_prefix("a"), content="None") + inspect("abc".strip_prefix(""), content="Some(\"abc\")") + inspect("๐Ÿ˜€hello".strip_prefix("๐Ÿ˜€"), content="Some(\"hello\")") + inspect("๐Ÿ˜€๐Ÿ˜ƒhello".strip_prefix("๐Ÿ˜€๐Ÿ˜ƒ"), content="Some(\"hello\")") +} + +///| +test "strip_suffix" { + inspect("hello world".strip_suffix(" world"), content="Some(\"hello\")") + inspect("hello world".strip_suffix(" moon"), content="None") + inspect("hello".strip_suffix("hello"), content="Some(\"\")") + inspect("".strip_suffix(""), content="Some(\"\")") + inspect("".strip_suffix("a"), content="None") + inspect("abc".strip_suffix(""), content="Some(\"abc\")") + inspect("hello๐Ÿ˜€".strip_suffix("๐Ÿ˜€"), content="Some(\"hello\")") + inspect("hello๐Ÿ˜€๐Ÿ˜ƒ".strip_suffix("๐Ÿ˜€๐Ÿ˜ƒ"), content="Some(\"hello\")") +} + +///| +/// Removes the given prefix from the string if it exists. +/// +/// Returns `Some(suffix)` if the string starts with the given prefix, +/// where `suffix` is the string without the prefix. +/// Returns `None` if the string does not start with the prefix. +/// +/// # Example +/// +/// ```moonbit +/// inspect("hello world".strip_prefix("hello "), content="Some(\"world\")") +/// inspect("hello world".strip_prefix("hi "), content="None") +/// inspect("hello".strip_prefix("hello"), content="Some(\"\")") +/// ``` +pub fn strip_prefix(self : String, prefix : View) -> View? { + if self.has_prefix(prefix) { + Some(self.view(start_offset=prefix.length())) + } else { + None + } +} + +///| +/// Removes the given prefix from the view if it exists. +/// +/// Returns `Some(suffix)` if the view starts with the given prefix, +/// where `suffix` is the view without the prefix. +/// Returns `None` if the view does not start with the prefix. +/// +/// # Example +/// +/// ```moonbit +/// let view = "hello world"[:] +/// inspect(view.strip_prefix("hello "), content="Some(\"world\")") +/// inspect(view.strip_prefix("hi "), content="None") +/// inspect(view.strip_prefix("hello world"), content="Some(\"\")") +/// ``` +/// +pub fn View::strip_prefix(self : View, prefix : View) -> View? { + if self.has_prefix(prefix) { + Some(self.view(start_offset=prefix.length())) + } else { + None + } +} + +///| +/// Removes the given suffix from the view if it exists. +/// +/// Returns `Some(prefix)` if the view ends with the given suffix, +/// where `prefix` is the view without the suffix. +/// Returns `None` if the view does not end with the suffix. +/// +/// # Example +/// +/// ```moonbit +/// let view = "hello world"[:] +/// inspect(view.strip_suffix(" world"), content="Some(\"hello\")") +/// inspect(view.strip_suffix(" moon"), content="None") +/// inspect(view.strip_suffix("hello world"), content="Some(\"\")") +/// ``` +pub fn View::strip_suffix(self : View, suffix : View) -> View? { + if self.has_suffix(suffix) { + Some(self.view(end_offset=self.length() - suffix.length())) + } else { + None + } +} + +///| +/// Converts the View into an array of Chars. +/// +/// # Example +/// +/// ```moonbit +/// let view = "Hello๐Ÿคฃxa"[1:-1] +/// let chars = view.to_array() +/// inspect(chars, content="['e', 'l', 'l', 'o', '๐Ÿคฃ', 'x']") +/// ``` +pub fn View::to_array(self : View) -> Array[Char] { + self + .iter() + .fold(init=Array::new(capacity=self.length()), (rv, c) => { + rv.push(c) + rv + }) +} + +///| +/// Converts the View into bytes using UTF-16 little endian format. +/// +/// # Example +/// +/// ```moonbit +/// let view = "Hellox"[1:-1] +/// let bytes = view.to_bytes() +/// inspect(bytes.to_unchecked_string(), content="ello") +/// ``` +pub fn View::to_bytes(self : View) -> Bytes { + let array = FixedArray::make(self.length() * 2, Byte::default()) + array.blit_from_string(0, self.data(), self.start_offset(), self.length()) + array |> unsafe_to_bytes +} + +///| +test "View::strip_prefix" { + let view = "hello world"[:] + inspect(view.strip_prefix("hello "), content="Some(\"world\")") + inspect(view.strip_prefix("hi "), content="None") + inspect(view.strip_prefix("hello world"), content="Some(\"\")") + inspect(view.strip_prefix(""), content="Some(\"hello world\")") + let empty_view = ""[:] + inspect(empty_view.strip_prefix(""), content="Some(\"\")") + inspect(empty_view.strip_prefix("a"), content="None") + let unicode_view = "๐Ÿ˜€hello๐Ÿ˜ƒ"[:] + inspect(unicode_view.strip_prefix("๐Ÿ˜€"), content="Some(\"hello๐Ÿ˜ƒ\")") + inspect(unicode_view.strip_prefix("๐Ÿ˜ƒ"), content="None") +} + +///| +test "View::strip_suffix" { + let view = "hello world"[:] + inspect(view.strip_suffix(" world"), content="Some(\"hello\")") + inspect(view.strip_suffix(" moon"), content="None") + inspect(view.strip_suffix("hello world"), content="Some(\"\")") + inspect(view.strip_suffix(""), content="Some(\"hello world\")") + let empty_view = ""[:] + inspect(empty_view.strip_suffix(""), content="Some(\"\")") + inspect(empty_view.strip_suffix("a"), content="None") + let unicode_view = "๐Ÿ˜€hello๐Ÿ˜ƒ"[:] + inspect(unicode_view.strip_suffix("๐Ÿ˜ƒ"), content="Some(\"๐Ÿ˜€hello\")") + inspect(unicode_view.strip_suffix("๐Ÿ˜€"), content="None") +} + +///| +test "View::to_array" { + let view = "Hello๐Ÿคฃ"[:] + let chars = view.to_array() + assert_eq(chars, ['H', 'e', 'l', 'l', 'o', '๐Ÿคฃ']) + let empty_view = ""[:] + let empty_chars = empty_view.to_array() + assert_eq(empty_chars, []) + let sub_view = "Hello World"[6:11] // "World" + let sub_chars = sub_view.to_array() + assert_eq(sub_chars, ['W', 'o', 'r', 'l', 'd']) +} + +///| +test "View::to_bytes" { + let view = "Hello"[:] + let bytes = view.to_bytes() + assert_eq(bytes.to_unchecked_string(), "Hello") + let unicode_view = "๐Ÿคฃ๐Ÿค”"[:] + let unicode_bytes = unicode_view.to_bytes() + assert_eq(unicode_bytes.to_unchecked_string(), "๐Ÿคฃ๐Ÿค”") + let sub_view = "Hello World"[0:5] // "Hello" + let sub_bytes = sub_view.to_bytes() + assert_eq(sub_bytes.to_unchecked_string(), "Hello") +} + ///| /// Returns true if this string contains the given substring. pub fn View::contains(self : View, str : View) -> Bool { @@ -398,7 +714,7 @@ test "trim" { inspect("abcabc".trim("abc"), content="") } -///| +///| /// Returns the view of the string without the leading and trailing spaces. pub fn View::trim_space(self : View) -> View { self.trim(" \n\r\t") @@ -492,9 +808,9 @@ test "is_blank" { pub fn View::pad_start( self : View, total_width : Int, - padding_char : Char + padding_char : Char, ) -> String { - let len = self.char_length() + let len = self.length() guard len < total_width else { return self.to_string() } let padding = String::make(total_width - len, padding_char) [..padding, ..self] @@ -507,9 +823,12 @@ pub fn View::pad_start( pub fn pad_start( self : String, total_width : Int, - padding_char : Char + padding_char : Char, ) -> String { - self[:].pad_start(total_width, padding_char) + let len = self.length() + guard len < total_width else { return self } + let padding = String::make(total_width - len, padding_char) + [..padding, ..self] } ///| @@ -530,7 +849,7 @@ test "pad_start" { inspect(view.pad_start(5, 'x'), content="xxllo") // Test with Unicode characters - inspect("๐ŸŒŸ".pad_start(3, 'โœจ'), content="โœจโœจ๐ŸŒŸ") + inspect("๐ŸŒŸ".pad_start(3, 'โœจ'), content="โœจ๐ŸŒŸ") // Edge cases inspect("abc".pad_start(0, 'x'), content="abc") // width less than string length @@ -544,9 +863,9 @@ test "pad_start" { pub fn View::pad_end( self : View, total_width : Int, - padding_char : Char + padding_char : Char, ) -> String { - let len = self.char_length() + let len = self.length() guard len < total_width else { return self.to_string() } let padding = String::make(total_width - len, padding_char) [..self, ..padding] @@ -556,8 +875,15 @@ pub fn View::pad_end( /// Returns a new string with `padding_char`s appended to `self` if /// `self.length() < total_width`. The number of unicode characters in /// the returned string is `total_width` if padding is added. -pub fn pad_end(self : String, total_width : Int, padding_char : Char) -> String { - self[:].pad_end(total_width, padding_char) +pub fn String::pad_end( + self : String, + total_width : Int, + padding_char : Char, +) -> String { + let len = self.length() + guard len < total_width else { return self } + let padding = String::make(total_width - len, padding_char) + [..self, ..padding] } ///| @@ -578,7 +904,7 @@ test "pad_end" { inspect(view.pad_end(5, 'x'), content="lloxx") // Test with Unicode characters - inspect("๐ŸŒŸ".pad_end(3, 'โœจ'), content="๐ŸŒŸโœจโœจ") + inspect("๐ŸŒŸ".pad_end(3, 'โœจ'), content="๐ŸŒŸโœจ") // Edge cases inspect("abc".pad_end(0, 'x'), content="abc") // width less than string length @@ -908,6 +1234,25 @@ test "replace_all" { assert_eq("abc".replace_all(old="", new="x"), "xaxbxcx") } +///| +test "String::replace_all boundary cases" { + // These tests should trigger the uncovered line 1187: guard end + old_len <= len else { break } + // This happens when the pattern is found at the very end of the string + + // Pattern at the end of string - should trigger the guard condition + assert_eq("helloworld".replace_all(old="world", new="X"), "helloX") + assert_eq("abcdef".replace_all(old="def", new="XYZ"), "abcXYZ") + + // Multiple patterns where the last one is at the end + assert_eq("abcabc".replace_all(old="abc", new="X"), "XX") + + // Pattern that exactly matches the string length + assert_eq("test".replace_all(old="test", new="done"), "done") + + // Empty replacement at the end + assert_eq("remove_me".replace_all(old="_me", new=""), "remove") +} + ///| test "View::replace_all" { assert_eq("hello"[:].replace_all(old="o", new="a"), "hella") @@ -942,11 +1287,36 @@ test "View::replace_all" { assert_eq("abc"[:].replace_all(old="", new="x"), "xaxbxcx") } +///| +test "View::replace_all boundary cases" { + // These tests should trigger the uncovered line 1141: guard end + old_len <= len else { break } + // This condition triggers when end + old_len > len, meaning we're at the boundary + + // Let me trace through the algorithm more carefully... + // Actually, let me try a different approach - create a scenario where the view length changes + + // Try with overlapping patterns or edge cases + assert_eq("abcabc"[:].replace_all(old="abc", new="X"), "XX") + assert_eq("aaaa"[:].replace_all(old="aa", new="b"), "bb") + + // Pattern at exact end + assert_eq("hello"[:].replace_all(old="lo", new="X"), "helX") + + // Test with empty string edge case + assert_eq("a"[:].replace_all(old="a", new=""), "") + + // Let me try to understand when end + old_len > len could happen... + // Maybe when we have a complex replacement scenario + inspect("Testing boundary condition", content="Testing boundary condition") +} + ///| /// Converts this string to lowercase. pub fn View::to_lower(self : View) -> View { // TODO: deal with non-ascii characters - guard self.find_by(_.is_ascii_uppercase()) is Some(idx) else { return self } + guard self.find_by(x => x.is_ascii_uppercase()) is Some(idx) else { + return self + } let buf = StringBuilder::new(size_hint=self.length()) let head = self.view(end_offset=idx) buf.write_substring(head.data(), head.start_offset(), head.length()) @@ -965,7 +1335,9 @@ pub fn View::to_lower(self : View) -> View { /// Converts this string to lowercase. pub fn to_lower(self : String) -> String { // TODO: deal with non-ascii characters - guard self.find_by(_.is_ascii_uppercase()) is Some(idx) else { return self } + guard self.find_by(x => x.is_ascii_uppercase()) is Some(idx) else { + return self + } let buf = StringBuilder::new(size_hint=self.length()) let head = self.view(end_offset=idx) buf.write_substring(head.data(), head.start_offset(), head.length()) @@ -1054,7 +1426,8 @@ pub fn[A] View::fold(self : View, init~ : A, f : (A, Char) -> A) -> A { rv } -///| Folds the characters of the string into a single value. +///| +/// Folds the characters of the string into a single value. pub fn[A] fold(self : String, init~ : A, f : (A, Char) -> A) -> A { self[:].fold(init~, f) } @@ -1102,3 +1475,125 @@ test "rev_fold" { 111 + 108 + 108 + 101 + 104, ) } + +///| +/// Returns the UTF-16 code unit at the given index. Returns `None` if the index +/// is out of bounds. +pub fn String::get(self : String, idx : Int) -> Int? { + guard idx >= 0 && idx < self.length() else { return None } + Some(self.unsafe_charcode_at(idx)) +} + +///| +/// Returns the UTF-16 code unit at the given index. Returns `None` if the index +/// is out of bounds. +pub fn View::get(self : View, idx : Int) -> Int? { + guard idx >= 0 && idx < self.length() else { return None } + Some(self.unsafe_charcode_at(idx)) +} + +///| +test "String::get supports emoji (surrogate pair)" { + let s = "hello" + inspect(s.get(0), content="Some(104)") + inspect(s.get(4), content="Some(111)") + inspect(s.get(5), content="None") + inspect(s.get(-1), content="None") + let s = "a๐Ÿคฃb" + inspect(s.get(0), content="Some(97)") + inspect(s.get(1), content="Some(55358)") + inspect(s.get(2), content="Some(56611)") + inspect(s.get(3), content="Some(98)") + inspect(s.get(4), content="None") +} + +///| +test "View::get basic cases" { + let v = "hello"[1:-1] + inspect(v.get(0), content="Some(101)") + inspect(v.get(2), content="Some(108)") + inspect(v.get(3), content="None") + inspect(v.get(-1), content="None") + let v = "ab๐Ÿคฃcd"[1:-1] + inspect(v.get(0), content="Some(98)") + inspect(v.get(1), content="Some(55358)") + inspect(v.get(2), content="Some(56611)") +} + +///| +/// Returns the character at the given index. Returns `None` if the index is out +/// of bounds or the index splits a surrogate pair. +pub fn String::get_char(self : String, idx : Int) -> Char? { + guard idx >= 0 && idx < self.length() else { return None } + let c = self.unsafe_charcode_at(idx) + if c.is_leading_surrogate() { + guard idx + 1 < self.length() else { return None } + let next = self.unsafe_charcode_at(idx + 1) + if next.is_trailing_surrogate() { + Some(code_point_of_surrogate_pair(c, next)) + } else { + None + } + } else if c.is_trailing_surrogate() { + None + } else { + Some(c.unsafe_to_char()) + } +} + +///| +/// Returns the character at the given index. Returns `None` if the index is out +/// of bounds or the index splits a surrogate pair. +pub fn View::get_char(self : View, idx : Int) -> Char? { + guard idx >= 0 && idx < self.length() else { return None } + let c = self.unsafe_charcode_at(idx) + if c.is_leading_surrogate() { + guard idx + 1 < self.length() else { return None } + let next = self.unsafe_charcode_at(idx + 1) + if next.is_trailing_surrogate() { + Some(code_point_of_surrogate_pair(c, next)) + } else { + None + } + } else if c.is_trailing_surrogate() { + None + } else { + Some(c.unsafe_to_char()) + } +} + +///| +test "String::get_char basic cases" { + // Basic ASCII characters + let s = "hello" + inspect(s.get_char(0), content="Some('h')") + inspect(s.get_char(1), content="Some('e')") + inspect(s.get_char(4), content="Some('o')") + inspect(s.get_char(5), content="None") + inspect(s.get_char(-1), content="None") + + // Contains emoji (surrogate pair) + let s = "a๐Ÿคฃb" + inspect(s.get_char(0), content="Some('a')") + inspect(s.get_char(1), content="Some('๐Ÿคฃ')") + inspect(s.get_char(2), content="None") // Second half of surrogate pair is not a valid char + inspect(s.get_char(3), content="Some('b')") + inspect(s.get_char(4), content="None") +} + +///| +test "View::get_char basic cases" { + let s = "a๐Ÿคฃb" + let v = s[0:-1] + inspect(v.get_char(0), content="Some('a')") + inspect(v.get_char(1), content="Some('๐Ÿคฃ')") + inspect(v.get_char(2), content="None") + inspect(v.get_char(3), content="None") + inspect(v.get_char(4), content="None") + + // Test substring view + let v2 = s[1:3] // Only contains the emoji surrogate pair + inspect(v2.get_char(0), content="Some('๐Ÿคฃ')") + inspect(v2.get_char(1), content="None") + inspect(v2.get_char(2), content="None") +} diff --git a/bundled-core/string/moon.pkg.json b/bundled-core/string/moon.pkg.json index 4fe660f..24509ea 100644 --- a/bundled-core/string/moon.pkg.json +++ b/bundled-core/string/moon.pkg.json @@ -1,6 +1,10 @@ { "import": ["moonbitlang/core/builtin", "moonbitlang/core/char"], - "test-import": ["moonbitlang/core/list", "moonbitlang/core/json"], + "test-import": [ + "moonbitlang/core/list", + "moonbitlang/core/json", + "moonbitlang/core/quickcheck" + ], "targets": { "panic_test.mbt": ["not", "native", "llvm"] } diff --git a/bundled-core/string/op_as_view_test.mbt b/bundled-core/string/op_as_view_test.mbt index 3b5dbb6..befe764 100644 --- a/bundled-core/string/op_as_view_test.mbt +++ b/bundled-core/string/op_as_view_test.mbt @@ -17,25 +17,25 @@ test "String::op_as_view with positive indices" { let s = "hello world" // Test basic ranges - let view1 = s.charcodes(start=0, end=5) + let view1 = s[0:5] inspect(view1, content="hello") - let view2 = s.charcodes(start=6) + let view2 = s[6:] inspect(view2, content="world") // Test with default start (0) - let view3 = s.charcodes(end=5) + let view3 = s[:5] inspect(view3, content="hello") // Test with default end (length of string) - let view4 = s.charcodes(start=6) + let view4 = s[6:] inspect(view4, content="world") // Test entire string - let view5 = s.charcodes() + let view5 = s[:] inspect(view5, content="hello world") // Test empty range - let view6 = s.charcodes(start=5, end=5) + let view6 = s[5:5] inspect(view6, content="") } @@ -64,21 +64,21 @@ test "View::op_as_view with positive indices" { let view = s[:] // Test basic subviews - let subview1 = view.charcodes(start=0, end=5) + let subview1 = view[0:5] inspect(subview1, content="hello") - let subview2 = view.charcodes(start=6, end=11) + let subview2 = view[6:11] inspect(subview2, content="world") // Test with default start (0) - let subview3 = view.charcodes(end=5) + let subview3 = view[:5] inspect(subview3, content="hello") // Test with default end (length of view) - let subview4 = view.charcodes(start=6) + let subview4 = view[6:] inspect(subview4, content="world") // Test empty range - let subview5 = view.charcodes(start=5, end=5) + let subview5 = view[5:5] inspect(subview5, content="") } @@ -99,11 +99,11 @@ test "View::op_as_view with positive indices" { ///| test "View::op_as_view with nested views" { let s = "hello world" - let view1 = s.charcodes(start=0, end=11) + let view1 = s.view(start_offset=0, end_offset=11) inspect(view1, content="hello world") - let view2 = view1.charcodes(start=0, end=5) + let view2 = view1.view(start_offset=0, end_offset=5) inspect(view2, content="hello") - let view3 = view2.charcodes(start=1, end=4) + let view3 = view2.view(start_offset=1, end_offset=4) inspect(view3, content="ell") // Test with negative indices in nested views diff --git a/bundled-core/string/panic_test.mbt b/bundled-core/string/panic_test.mbt index 5905d5d..4e36a8a 100644 --- a/bundled-core/string/panic_test.mbt +++ b/bundled-core/string/panic_test.mbt @@ -11,23 +11,3 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -///| -test "panic substring_start_index_error" { - "test".substring(start=-1, end=0) |> ignore -} - -///| -test "panic substring_end_index_error" { - "test".substring(start=0, end=-1) |> ignore -} - -///| -test "panic substring_start_end_index_error" { - "test".substring(start=1, end=0) |> ignore -} - -///| -test "panic substring_length_index_error" { - "test".substring(start=0, end=5) |> ignore -} diff --git a/bundled-core/string/pkg.generated.mbti b/bundled-core/string/pkg.generated.mbti new file mode 100644 index 0000000..59bc31f --- /dev/null +++ b/bundled-core/string/pkg.generated.mbti @@ -0,0 +1,130 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/string" + +// Values +fn default() -> String + +// Errors +pub suberror CreatingViewError { + IndexOutOfBounds + InvalidIndex +} +impl Show for CreatingViewError + +// Types and methods +type View +fn View::char_length(Self) -> Int +fn View::char_length_eq(Self, Int) -> Bool +fn View::char_length_ge(Self, Int) -> Bool +fn View::contains(Self, Self) -> Bool +fn View::contains_char(Self, Char) -> Bool +fn View::data(Self) -> String +fn View::find(Self, Self) -> Int? +fn View::find_by(Self, (Char) -> Bool) -> Int? +fn[A] View::fold(Self, init~ : A, (A, Char) -> A) -> A +fn View::from_array(Array[Char]) -> Self +fn View::from_iter(Iter[Char]) -> Self +fn View::get(Self, Int) -> Int? +fn View::get_char(Self, Int) -> Char? +#alias(starts_with, deprecated) +fn View::has_prefix(Self, Self) -> Bool +#alias(ends_with, deprecated) +fn View::has_suffix(Self, Self) -> Bool +fn View::is_blank(Self) -> Bool +fn View::is_empty(Self) -> Bool +fn View::iter(Self) -> Iter[Char] +fn View::iter2(Self) -> Iter2[Int, Char] +fn View::length(Self) -> Int +fn View::make(Int, Char) -> Self +fn View::offset_of_nth_char(Self, Int) -> Int? +fn View::op_get(Self, Int) -> Int +fn View::pad_end(Self, Int, Char) -> String +fn View::pad_start(Self, Int, Char) -> String +fn View::repeat(Self, Int) -> Self +fn View::replace(Self, old~ : Self, new~ : Self) -> Self +fn View::replace_all(Self, old~ : Self, new~ : Self) -> Self +fn View::rev(Self) -> String +fn View::rev_find(Self, Self) -> Int? +fn[A] View::rev_fold(Self, init~ : A, (A, Char) -> A) -> A +fn View::rev_iter(Self) -> Iter[Char] +fn View::split(Self, Self) -> Iter[Self] +fn View::start_offset(Self) -> Int +fn View::strip_prefix(Self, Self) -> Self? +fn View::strip_suffix(Self, Self) -> Self? +#alias(op_as_view) +fn View::sub(Self, start? : Int, end? : Int) -> Self raise CreatingViewError +fn View::to_array(Self) -> Array[Char] +fn View::to_bytes(Self) -> Bytes +fn View::to_lower(Self) -> Self +fn View::to_upper(Self) -> Self +fn View::trim(Self, Self) -> Self +fn View::trim_end(Self, Self) -> Self +fn View::trim_space(Self) -> Self +fn View::trim_start(Self, Self) -> Self +fn View::unsafe_charcode_at(Self, Int) -> Int +fn View::view(Self, start_offset? : Int, end_offset? : Int) -> Self +impl Add for View +impl Compare for View +impl Default for View +impl Eq for View +impl Hash for View +impl Show for View +impl ToJson for View +impl ToStringView for View + +fn String::char_length_eq(String, Int, start_offset? : Int, end_offset? : Int) -> Bool +fn String::char_length_ge(String, Int, start_offset? : Int, end_offset? : Int) -> Bool +fn String::contains(String, View) -> Bool +fn String::contains_char(String, Char) -> Bool +fn String::find(String, View) -> Int? +fn String::find_by(String, (Char) -> Bool) -> Int? +fn[A] String::fold(String, init~ : A, (A, Char) -> A) -> A +#as_free_fn +fn String::from_array(Array[Char]) -> String +#as_free_fn +fn String::from_iter(Iter[Char]) -> String +fn String::get(String, Int) -> Int? +fn String::get_char(String, Int) -> Char? +#alias(starts_with, deprecated) +fn String::has_prefix(String, View) -> Bool +#alias(ends_with, deprecated) +fn String::has_suffix(String, View) -> Bool +fn String::is_blank(String) -> Bool +fn String::is_empty(String) -> Bool +fn String::iter(String) -> Iter[Char] +fn String::iter2(String) -> Iter2[Int, Char] +fn String::offset_of_nth_char(String, Int, start_offset? : Int, end_offset? : Int) -> Int? +fn String::pad_end(String, Int, Char) -> String +fn String::pad_start(String, Int, Char) -> String +fn String::repeat(String, Int) -> String +fn String::replace(String, old~ : View, new~ : View) -> String +fn String::replace_all(String, old~ : View, new~ : View) -> String +fn String::rev(String) -> String +fn String::rev_find(String, View) -> Int? +fn[A] String::rev_fold(String, init~ : A, (A, Char) -> A) -> A +fn String::rev_iter(String) -> Iter[Char] +fn String::split(String, View) -> Iter[View] +fn String::strip_prefix(String, View) -> View? +fn String::strip_suffix(String, View) -> View? +#alias(op_as_view) +fn String::sub(String, start? : Int, end? : Int) -> View raise CreatingViewError +fn String::to_array(String) -> Array[Char] +fn String::to_bytes(String) -> Bytes +fn String::to_lower(String) -> String +fn String::to_upper(String) -> String +fn String::trim(String, View) -> View +fn String::trim_end(String, View) -> View +fn String::trim_space(String) -> View +fn String::trim_start(String, View) -> View +fn String::view(String, start_offset? : Int, end_offset? : Int) -> View +impl Compare for String +impl Default for String + +// Type aliases + +// Traits +pub trait ToStringView { + to_string_view(Self) -> View +} +impl ToStringView for String + diff --git a/bundled-core/string/string.mbt b/bundled-core/string/string.mbt index fbcb5a4..ff13d7f 100644 --- a/bundled-core/string/string.mbt +++ b/bundled-core/string/string.mbt @@ -23,6 +23,7 @@ /// Do not convert large datas to `Array[Char]` and build a string with `String::from_array`. /// /// For efficiency considerations, it's recommended to use `Buffer` instead. +#as_free_fn pub fn String::from_array(chars : Array[Char]) -> String { let buf = StringBuilder::new(size_hint=chars.length() * 4) for c in chars { @@ -31,28 +32,17 @@ pub fn String::from_array(chars : Array[Char]) -> String { buf.to_string() } -///| same as `String::from_array` -pub fn from_array(chars : Array[Char]) -> String { - String::from_array(chars) -} - ///| /// Convert char iterator to string, /// a simple wrapper for `from_array`. +#as_free_fn pub fn String::from_iter(iter : Iter[Char]) -> String { let chars = iter.collect() String::from_array(chars) } ///| -/// same as `String::from_iter` -pub fn from_iter(iter : Iter[Char]) -> String { - let chars = iter.collect() - String::from_array(chars) -} - -///| -/// Strings are ordered lexicographically by their charcodes(code unit). This +/// Strings are ordered based on shortlex order by their charcodes (code units). This /// orders Unicode characters based on their positions in the code charts. This is /// not necessarily the same as "alphabetical" order, which varies by language /// and locale. @@ -124,9 +114,9 @@ pub fn iter(self : String) -> Iter[Char] { let len = self.length() for index in 0.. Iter2[Int, Char] { let len = self.length() for index = 0, n = 0; index < len; index = index + 1, n = n + 1 { let c1 = self.unsafe_charcode_at(index) - if is_leading_surrogate(c1) && index + 1 < len { + if c1.is_leading_surrogate() && index + 1 < len { let c2 = self.unsafe_charcode_at(index + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { let c = code_point_of_surrogate_pair(c1, c2) guard yield_(n, c) is IterContinue else { break IterEnd } continue index + 2, n + 1 @@ -195,9 +185,9 @@ pub fn rev_iter(self : String) -> Iter[Char] { let len = self.length() for index = len - 1; index >= 0; index = index - 1 { let c1 = self.unsafe_charcode_at(index) - if is_trailing_surrogate(c1) && index - 1 >= 0 { + if c1.is_trailing_surrogate() && index - 1 >= 0 { let c2 = self.unsafe_charcode_at(index - 1) - if is_leading_surrogate(c2) { + if c2.is_leading_surrogate() { let c = code_point_of_surrogate_pair(c2, c1) guard yield_(c) is IterContinue else { break IterEnd } continue index - 2 @@ -217,7 +207,7 @@ fn String::offset_of_nth_char_forward( self : String, n : Int, start_offset~ : Int, - end_offset~ : Int + end_offset~ : Int, ) -> Int? { guard start_offset >= 0 && start_offset <= end_offset else { abort("Invalid start index") @@ -227,7 +217,7 @@ fn String::offset_of_nth_char_forward( while utf16_offset < end_offset && char_count < n { let c = self.unsafe_charcode_at(utf16_offset) // check if this is a surrogate pair - if is_leading_surrogate(c) { + if c.is_leading_surrogate() { utf16_offset = utf16_offset + 2 } else { utf16_offset = utf16_offset + 1 @@ -252,7 +242,7 @@ fn String::offset_of_nth_char_backward( self : String, n : Int, start_offset~ : Int, - end_offset~ : Int + end_offset~ : Int, ) -> Int? { let mut char_count = 0 let mut utf16_offset = end_offset @@ -260,7 +250,7 @@ fn String::offset_of_nth_char_backward( // Invariant: utf16_offset always points to the previous character while utf16_offset - 1 >= start_offset && char_count < n { let c = self.unsafe_charcode_at(utf16_offset - 1) - if is_trailing_surrogate(c) { + if c.is_trailing_surrogate() { utf16_offset = utf16_offset - 2 } else { utf16_offset = utf16_offset - 1 @@ -284,8 +274,8 @@ fn String::offset_of_nth_char_backward( pub fn String::offset_of_nth_char( self : String, i : Int, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> Int? { let end_offset = if end_offset is Some(o) { o } else { self.length() } if i >= 0 { @@ -297,17 +287,6 @@ pub fn String::offset_of_nth_char( } } -///| -/// Returns the Unicode character at the given offset. Note this is not the n-th character. -/// -/// This has O(1) complexity. -pub fn String::char_at(self : String, offset : Int) -> Char { - guard offset >= 0 && offset < self.length() else { - abort("offset out of bounds") - } - self.unsafe_char_at(offset) -} - ///| /// Test if the length of the string is equal to the given length. /// @@ -315,17 +294,17 @@ pub fn String::char_at(self : String, offset : Int) -> Char { pub fn String::char_length_eq( self : String, len : Int, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> Bool { let end_offset = if end_offset is Some(o) { o } else { self.length() } for index = start_offset, count = 0 index < end_offset && count < len index = index + 1, count = count + 1 { let c1 = self.unsafe_charcode_at(index) - if is_leading_surrogate(c1) && index + 1 < end_offset { + if c1.is_leading_surrogate() && index + 1 < end_offset { let c2 = self.unsafe_charcode_at(index + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { continue index + 2, count + 1 } else { abort("invalid surrogate pair") @@ -343,17 +322,17 @@ pub fn String::char_length_eq( pub fn String::char_length_ge( self : String, len : Int, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> Bool { let end_offset = if end_offset is Some(o) { o } else { self.length() } for index = start_offset, count = 0 index < end_offset && count < len index = index + 1, count = count + 1 { let c1 = self.unsafe_charcode_at(index) - if is_leading_surrogate(c1) && index + 1 < end_offset { + if c1.is_leading_surrogate() && index + 1 < end_offset { let c2 = self.unsafe_charcode_at(index + 1) - if is_trailing_surrogate(c2) { + if c2.is_trailing_surrogate() { continue index + 2, count + 1 } else { abort("invalid surrogate pair") diff --git a/bundled-core/string/string.mbti b/bundled-core/string/string.mbti deleted file mode 100644 index f73a233..0000000 --- a/bundled-core/string/string.mbti +++ /dev/null @@ -1,131 +0,0 @@ -package "moonbitlang/core/string" - -// Values -#deprecated -fn concat(Array[String], separator~ : String = ..) -> String - -fn default() -> String - -fn from_array(Array[Char]) -> String - -fn from_iter(Iter[Char]) -> String - -// Types and methods -type StringIndex -impl Eq for StringIndex -impl Show for StringIndex - -type StringView -fn StringView::char_at(Self, Int) -> Char -fn StringView::char_length(Self) -> Int -fn StringView::char_length_eq(Self, Int) -> Bool -fn StringView::char_length_ge(Self, Int) -> Bool -fn StringView::charcode_at(Self, Int) -> Int -fn StringView::charcodes(Self, start~ : Int = .., end? : Int) -> Self -fn StringView::contains(Self, Self) -> Bool -fn StringView::contains_char(Self, Char) -> Bool -fn StringView::data(Self) -> String -fn StringView::find(Self, Self) -> Int? -fn StringView::find_by(Self, (Char) -> Bool) -> Int? -fn[A] StringView::fold(Self, init~ : A, (A, Char) -> A) -> A -fn StringView::from_array(Array[Char]) -> Self -fn StringView::from_iter(Iter[Char]) -> Self -fn StringView::has_prefix(Self, Self) -> Bool -fn StringView::has_suffix(Self, Self) -> Bool -fn StringView::is_blank(Self) -> Bool -fn StringView::is_empty(Self) -> Bool -fn StringView::iter(Self) -> Iter[Char] -fn StringView::iter2(Self) -> Iter2[Int, Char] -fn StringView::length(Self) -> Int -fn StringView::make(Int, Char) -> Self -fn StringView::offset_of_nth_char(Self, Int) -> Int? -#deprecated -fn StringView::op_as_view(Self, start~ : Int = .., end? : Int) -> Self -fn StringView::op_get(Self, Int) -> Int -fn StringView::pad_end(Self, Int, Char) -> String -fn StringView::pad_start(Self, Int, Char) -> String -fn StringView::repeat(Self, Int) -> Self -fn StringView::replace(Self, old~ : Self, new~ : Self) -> Self -fn StringView::replace_all(Self, old~ : Self, new~ : Self) -> Self -fn StringView::rev(Self) -> String -fn StringView::rev_find(Self, Self) -> Int? -fn[A] StringView::rev_fold(Self, init~ : A, (A, Char) -> A) -> A -fn StringView::rev_iter(Self) -> Iter[Char] -fn StringView::split(Self, Self) -> Iter[Self] -fn StringView::start_offset(Self) -> Int -fn StringView::to_lower(Self) -> Self -fn StringView::to_upper(Self) -> Self -fn StringView::trim(Self, Self) -> Self -fn StringView::trim_end(Self, Self) -> Self -fn StringView::trim_space(Self) -> Self -fn StringView::trim_start(Self, Self) -> Self -fn StringView::unsafe_charcode_at(Self, Int) -> Int -fn StringView::view(Self, start_offset~ : Int = .., end_offset? : Int) -> Self -impl Compare for StringView -impl Default for StringView -impl Eq for StringView -impl Hash for StringView -impl Show for StringView -impl ToJson for StringView - -fn String::char_at(String, Int) -> Char -fn String::char_length_eq(String, Int, start_offset~ : Int = .., end_offset? : Int) -> Bool -fn String::char_length_ge(String, Int, start_offset~ : Int = .., end_offset? : Int) -> Bool -fn String::charcodes(String, start~ : Int = .., end? : Int) -> StringView -#deprecated -fn String::concat(Array[String], separator~ : String = ..) -> String -fn String::contains(String, StringView) -> Bool -fn String::contains_char(String, Char) -> Bool -#deprecated -fn String::ends_with(String, String) -> Bool -fn String::find(String, StringView) -> Int? -fn String::find_by(String, (Char) -> Bool) -> Int? -fn[A] String::fold(String, init~ : A, (A, Char) -> A) -> A -fn String::from_array(Array[Char]) -> String -fn String::from_iter(Iter[Char]) -> String -fn String::has_prefix(String, StringView) -> Bool -fn String::has_suffix(String, StringView) -> Bool -#deprecated -fn String::index_at(String, Int, start~ : StringIndex = ..) -> StringIndex? -#deprecated -fn String::index_at_rev(String, Int, end? : StringIndex) -> StringIndex? -#deprecated -fn String::index_of(String, String, from~ : Int = ..) -> Int -fn String::is_blank(String) -> Bool -fn String::is_empty(String) -> Bool -fn String::iter(String) -> Iter[Char] -fn String::iter2(String) -> Iter2[Int, Char] -#deprecated -fn String::last_index_of(String, String, from? : Int) -> Int -fn String::offset_of_nth_char(String, Int, start_offset~ : Int = .., end_offset? : Int) -> Int? -#deprecated -fn String::op_as_view(String, start~ : Int = .., end? : Int) -> StringView -fn String::pad_end(String, Int, Char) -> String -fn String::pad_start(String, Int, Char) -> String -fn String::repeat(String, Int) -> String -fn String::replace(String, old~ : StringView, new~ : StringView) -> String -fn String::replace_all(String, old~ : StringView, new~ : StringView) -> String -fn String::rev(String) -> String -fn String::rev_find(String, StringView) -> Int? -fn[A] String::rev_fold(String, init~ : A, (A, Char) -> A) -> A -fn String::rev_iter(String) -> Iter[Char] -fn String::split(String, StringView) -> Iter[StringView] -#deprecated -fn String::starts_with(String, String) -> Bool -fn String::to_array(String) -> Array[Char] -fn String::to_bytes(String) -> Bytes -fn String::to_lower(String) -> String -fn String::to_upper(String) -> String -fn String::trim(String, StringView) -> StringView -fn String::trim_end(String, StringView) -> StringView -fn String::trim_space(String) -> StringView -fn String::trim_start(String, StringView) -> StringView -fn String::view(String, start_offset~ : Int = .., end_offset? : Int) -> StringView -impl Compare for String -impl Default for String - -// Type aliases -pub typealias StringView as View - -// Traits - diff --git a/bundled-core/string/string_like.mbt b/bundled-core/string/string_like.mbt new file mode 100644 index 0000000..a876789 --- /dev/null +++ b/bundled-core/string/string_like.mbt @@ -0,0 +1,28 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +pub trait ToStringView { + to_string_view(Self) -> View +} + +///| +pub impl ToStringView for String with to_string_view(self) -> View { + self[:] +} + +///| +pub impl ToStringView for View with to_string_view(self) -> View { + self +} diff --git a/bundled-core/string/string_test.mbt b/bundled-core/string/string_test.mbt index 1c1164c..362010b 100644 --- a/bundled-core/string/string_test.mbt +++ b/bundled-core/string/string_test.mbt @@ -86,7 +86,7 @@ test "chars" { .each(c => str = str + c.to_int().to_string() + "\n") inspect( str, - content= + content=( #|65 #|128522 #|134071 @@ -96,7 +96,7 @@ test "chars" { #|134071 #|66 #| - , + ), ) inspect( "A๐Ÿ˜Š๐ ฎทBA๐Ÿ˜Š๐ ฎทB".iter().take(2).collect(), @@ -116,7 +116,7 @@ test "rev_iter" { .each(c => str = str + c.to_int().to_string() + "\n") inspect( str, - content= + content=( #|66 #|134071 #|128522 @@ -126,7 +126,7 @@ test "rev_iter" { #|128522 #|65 #| - , + ), ) inspect( "A๐Ÿ˜Š๐ ฎทBA๐Ÿ˜Š๐ ฎทB".rev_iter().take(2).collect(), @@ -146,7 +146,7 @@ test "iter2" { .each((i, c) => str = str + "\{i}: \{c.to_int()} \n") inspect( str, - content= + content=( #|0: 65 #|1: 128522 #|2: 134071 @@ -156,7 +156,7 @@ test "iter2" { #|6: 134071 #|7: 66 #| - , + ), ) } @@ -297,7 +297,7 @@ test "String::index_of basic matching" { test "String::index_of overlapping patterns" { // Testing overlapping patterns inspect("aaa".find("aa"), content="Some(0)") - inspect("aaa".view(start_offset=1).find("aa"), content="Some(0)") + inspect("aaa"[1:].find("aa"), content="Some(0)") } ///| @@ -362,46 +362,46 @@ test "has_suffix" { test "split" { inspect( "a,b,c,d,e".split(","), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) inspect( "abcde".split(""), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) inspect( "a b c d e".split(" "), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) inspect( "abcde".split("x"), - content= + content=( #|["abcde"] - , + ), ) inspect("".split(""), content="[]") inspect( "".split("x"), - content= + content=( #|[""] - , + ), ) inspect( "ไฝ  wow ๅฅฝ wow ๆœˆ wow ๅ…”".split(" wow "), - content= + content=( #|["ไฝ ", "ๅฅฝ", "ๆœˆ", "ๅ…”"] - , + ), ) inspect( "๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘".split("๐Ÿ˜๐Ÿ˜ญ"), - content= + content=( #|["๐Ÿ‘", "๐Ÿ‘", "๐Ÿ‘", "๐Ÿ‘"] - , + ), ) } @@ -467,65 +467,65 @@ test "to_upper" { test "split" { inspect( "a b c d e".split(" "), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) inspect( "abcde".split("x"), - content= + content=( #|["abcde"] - , + ), ) inspect("".split(""), content="[]") inspect( "".split("x"), - content= + content=( #|[""] - , + ), ) inspect( "ไฝ  wow ๅฅฝ wow ๆœˆ wow ๅ…”".split(" wow "), - content= + content=( #|["ไฝ ", "ๅฅฝ", "ๆœˆ", "ๅ…”"] - , + ), ) inspect( "๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘๐Ÿ˜๐Ÿ˜ญ๐Ÿ‘".split("๐Ÿ˜๐Ÿ˜ญ"), - content= + content=( #|["๐Ÿ‘", "๐Ÿ‘", "๐Ÿ‘", "๐Ÿ‘"] - , + ), ) // Additional test cases inspect( "a,b,c,d,e".split(","), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) inspect( "a,,b,,c".split(","), - content= + content=( #|["a", "", "b", "", "c"] - , + ), ) inspect( "a,,b,,c".split(",,"), - content= + content=( #|["a", "b", "c"] - , + ), ) inspect( "a b c d e".split(""), - content= + content=( #|["a", " ", "b", " ", "c", " ", "d", " ", "e"] - , + ), ) inspect( "a b c d e".split(" "), - content= + content=( #|["a", "b", "c", "d", "e"] - , + ), ) } @@ -682,7 +682,7 @@ test "offset_of_nth_char property" { let n = s.char_length() for i in 0.. Bool { - min_leading_surrogate <= c && c <= max_leading_surrogate +fn code_point_of_surrogate_pair(leading : Int, trailing : Int) -> Char { + ((leading - 0xD800) * 0x400 + trailing - 0xDC00 + 0x10000).unsafe_to_char() } ///| -test "is_leading_surrogate" { - inspect(is_leading_surrogate("๐Ÿคฃ".charcode_at(0)), content="true") - inspect(is_leading_surrogate("๐Ÿคฃ".charcode_at(1)), content="false") +test "code_point_of_surrogate_pair" { + let s = "๐Ÿ˜€" + let leading = s.charcode_at(0) + let trailing = s.charcode_at(1) + inspect(code_point_of_surrogate_pair(leading, trailing), content="๐Ÿ˜€") } ///| -fn is_trailing_surrogate(c : Int) -> Bool { - min_trailing_surrogate <= c && c <= max_trailing_surrogate +test "is_leading_surrogate" { + inspect("๐Ÿคฃ".charcode_at(0).is_leading_surrogate(), content="true") + inspect("๐Ÿคฃ".charcode_at(1).is_leading_surrogate(), content="false") } ///| test "is_trailing_surrogate" { - inspect(is_trailing_surrogate("๐Ÿคฃ".charcode_at(0)), content="false") - inspect(is_trailing_surrogate("๐Ÿคฃ".charcode_at(1)), content="true") + inspect("๐Ÿคฃ".charcode_at(0).is_trailing_surrogate(), content="false") + inspect("๐Ÿคฃ".charcode_at(1).is_trailing_surrogate(), content="true") } ///| -fn code_point_of_surrogate_pair(leading : Int, trailing : Int) -> Char { - ((leading - 0xD800) * 0x400 + trailing - 0xDC00 + 0x10000).unsafe_to_char() -} - -///| -test "code_point_of_surrogate_pair" { - let s = "๐Ÿ˜€" - let leading = s.charcode_at(0) - let trailing = s.charcode_at(1) - inspect(code_point_of_surrogate_pair(leading, trailing), content="๐Ÿ˜€") +test "is_surrogate" { + inspect((0xD800).is_surrogate(), content="true") // Leading surrogate + inspect((0xDBFF).is_surrogate(), content="true") // Leading surrogate + inspect((0xDC00).is_surrogate(), content="true") // Trailing surrogate + inspect((0xDFFF).is_surrogate(), content="true") // Trailing surrogate + inspect((0xD7FF).is_surrogate(), content="false") // Just before surrogates + inspect((0xE000).is_surrogate(), content="false") // Just after surrogates + inspect((0x41).is_surrogate(), content="false") // Regular ASCII 'A' } diff --git a/bundled-core/string/view.mbt b/bundled-core/string/view.mbt index a0f8134..c914be3 100644 --- a/bundled-core/string/view.mbt +++ b/bundled-core/string/view.mbt @@ -16,9 +16,25 @@ /// A `@string.View` represents a view of a String that maintains proper Unicode /// character boundaries. It allows safe access to a substring while handling /// multi-byte characters correctly. -pub typealias StringView as View +#builtin.valtype +type View -///| +///| +/// Returns the source string being viewed. +fn View::str(self : View) -> String = "%stringview.str" + +///| +/// Returns the starting UTF-16 code unit index into the string. +fn View::start(self : View) -> Int = "%stringview.start" + +///| +/// Returns the ending UTF-16 code unit index into the string (not included). +fn View::end(self : View) -> Int = "%stringview.end" + +///| +fn View::make_view(str : String, start : Int, end : Int) -> View = "%stringview.make" + +///| /// Returns the charcode(UTF-16 code unit) at the given index. /// /// This method has O(1) complexity. @@ -27,7 +43,7 @@ pub typealias StringView as View /// /// ```mbt /// let str = "Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ" -/// let view = str.charcodes(start = str.offset_of_nth_char(1).unwrap(), end = str.offset_of_nth_char(6).unwrap()) +/// let view = str.view(start_offset = str.offset_of_nth_char(1).unwrap(), end_offset = str.offset_of_nth_char(6).unwrap()) /// inspect(view[0].to_char(), content="Some('e')") /// inspect(view[4], content="55358") /// ``` @@ -35,20 +51,20 @@ pub fn View::op_get(self : View, index : Int) -> Int { guard index >= 0 && index < self.length() else { abort("Index out of bounds") } - self.str.unsafe_charcode_at(self.start + index) + self.str().unsafe_charcode_at(self.start() + index) } ///| /// Returns the original string that is being viewed. pub fn data(self : View) -> String { - self.str + self.str() } ///| /// Returns the starting offset (in UTF-16 code units) of this view into its /// underlying string. pub fn start_offset(self : View) -> Int { - self.start + self.start() } ///| @@ -56,7 +72,7 @@ pub fn start_offset(self : View) -> Int { /// /// This method counts the charcodes(code unit) in the view and has O(1) complexity. pub fn length(self : View) -> Int { - self.end - self.start + self.end() - self.start() } ///| @@ -79,8 +95,8 @@ pub fn length(self : View) -> Int { /// ``` pub fn String::view( self : String, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> View { let end_offset = if end_offset is Some(o) { o } else { self.length() } guard start_offset >= 0 && @@ -88,15 +104,15 @@ pub fn String::view( end_offset <= self.length() else { abort("Invalid index for View") } - { str: self, start: start_offset, end: end_offset } + View::make_view(self, start_offset, end_offset) } ///| /// Returns a new view of the view with the given start and end offsets. pub fn View::view( self : View, - start_offset~ : Int = 0, - end_offset? : Int + start_offset? : Int = 0, + end_offset? : Int, ) -> View { let end_offset = if end_offset is Some(o) { o } else { self.length() } guard start_offset >= 0 && @@ -104,53 +120,11 @@ pub fn View::view( end_offset <= self.length() else { abort("Invalid index for View") } - { - str: self.str, - start: self.start + start_offset, - end: self.start + end_offset, - } -} - -///| -/// Creates a `View` into a `String`. -/// -/// # Example -/// -/// ```mbt -/// let str = "Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ" -/// let view1 = str.charcodes() -/// inspect(view1, content="Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ") -/// let start = str.offset_of_nth_char(1).unwrap() -/// let end = str.offset_of_nth_char(6).unwrap() // the second emoji -/// let view2 = str.charcodes(start~, end~) -/// inspect(view2, content="ello๐Ÿคฃ") -/// ``` -/// -/// This method has O(1) complexity. -pub fn String::charcodes(self : String, start~ : Int = 0, end? : Int) -> View { - self.view(start_offset=start, end_offset?=end) -} - -///| -/// Creates a `View` into a `View`. -/// -/// # Example -/// -/// ```mbt -/// let str = "Hello๐Ÿคฃ๐Ÿคฃ๐Ÿคฃ" -/// let view1 = str.view() -/// let view2 = view1.charcodes(start=1, end=7) // From 2nd to 6th character -/// inspect(view2, content= -/// "ello๐Ÿคฃ" -/// ) -/// ``` -/// -/// This method is similar to `String::charcodes` but operates on an existing view. -/// It allows you to create a sub-view of an existing view with the specified character range. -/// -/// This method has O(1) complexity. -pub fn View::charcodes(self : View, start~ : Int = 0, end? : Int) -> View { - self.view(start_offset=start, end_offset?=end) + View::make_view( + self.str(), + self.start() + start_offset, + self.start() + end_offset, + ) } ///| @@ -158,56 +132,45 @@ pub fn View::charcodes(self : View, start~ : Int = 0, end? : Int) -> View { /// the view. If i is negative, it returns the index of the (n + i)-th character /// where n is the total number of Unicode characters in the view. pub fn View::offset_of_nth_char(self : View, i : Int) -> Int? { - if self.str.offset_of_nth_char( - i, - start_offset=self.start, - end_offset=self.end, - ) + if self + .str() + .offset_of_nth_char(i, start_offset=self.start(), end_offset=self.end()) is Some(index) { - Some(index - self.start) + Some(index - self.start()) } else { None } } -///| -/// Returns the Unicode character at the given index. Note this is not the n-th character. -/// -/// This method has O(1) complexity. -pub fn View::char_at(self : View, index : Int) -> Char { - guard index >= 0 && index < self.length() else { - abort("Index out of bounds") - } - self.str.unsafe_char_at(self.start + index) -} - -///| -/// Returns the charcode(code unit) at the given index. -/// -/// This method has O(1) complexity. -pub fn View::charcode_at(self : View, index : Int) -> Int { - guard index >= 0 && index < self.length() else { - abort("Index out of bounds") - } - self.str.unsafe_charcode_at(self.start + index) -} - ///| /// Returns the charcode(code unit) at the given index without checking if the /// index is within bounds. /// /// This method has O(1) complexity. +/// #Example +/// +/// ```mbt +/// let str = "B๐Ÿคฃ๐ŸคฃC" +/// let view = str[:] +/// inspect(view.unsafe_charcode_at(0), content="66") +/// inspect(view.unsafe_charcode_at(1), content="55358") +/// inspect(view.unsafe_charcode_at(2), content="56611") +/// inspect(view.unsafe_charcode_at(3), content="55358") +/// inspect(view.unsafe_charcode_at(4), content="56611") +/// inspect(view.unsafe_charcode_at(5), content="67") +/// ``` +/// TODO: rename to `unsafe_get` pub fn View::unsafe_charcode_at(self : View, index : Int) -> Int { - self.str.unsafe_charcode_at(self.start + index) + self.str().unsafe_charcode_at(self.start() + index) } -///| +///| /// Returns the number of Unicode characters in this view. /// /// Note this has O(n) complexity where n is the length of the code points in /// the view. pub fn View::char_length(self : View) -> Int { - self.str.char_length(start_offset=self.start, end_offset=self.end) + self.str().char_length(start_offset=self.start(), end_offset=self.end()) } ///| @@ -215,7 +178,9 @@ pub fn View::char_length(self : View) -> Int { /// /// This has O(n) complexity where n is the length in the parameter. pub fn View::char_length_eq(self : View, len : Int) -> Bool { - self.str.char_length_eq(len, start_offset=self.start, end_offset=self.end) + self + .str() + .char_length_eq(len, start_offset=self.start(), end_offset=self.end()) } ///| @@ -223,12 +188,14 @@ pub fn View::char_length_eq(self : View, len : Int) -> Bool { /// /// This has O(n) complexity where n is the length in the parameter. pub fn View::char_length_ge(self : View, len : Int) -> Bool { - self.str.char_length_ge(len, start_offset=self.start, end_offset=self.end) + self + .str() + .char_length_ge(len, start_offset=self.start(), end_offset=self.end()) } ///| pub impl Show for View with output(self, logger) { - let substr = self.str.substring(start=self.start, end=self.end) + let substr = self.str().unsafe_substring(start=self.start(), end=self.end()) String::output(substr, logger) } @@ -239,21 +206,21 @@ pub impl Show for View with output(self, logger) { /// /// ```mbt /// let str = "Hello World" -/// let view = str.charcodes(start = str.offset_of_nth_char(0).unwrap(),end = str.offset_of_nth_char(5).unwrap()) // "Hello" +/// let view = str.view(start_offset = str.offset_of_nth_char(0).unwrap(),end_offset = str.offset_of_nth_char(5).unwrap()) // "Hello" /// inspect(view.to_string(), content="Hello") /// ``` -pub impl Show for StringView with to_string(self) { - self.str.substring(start=self.start, end=self.end) +pub impl Show for View with to_string(self) { + self.str().unsafe_substring(start=self.start(), end=self.end()) } ///| /// Returns an iterator over the Unicode characters in the string view. pub fn View::iter(self : View) -> Iter[Char] { - Iter::new(yield_ => for index in self.start.. for index in self.start().. Iter2[Int, Char] { Iter2::new(yield_ => { let len = self.length() for index = 0, n = 0; index < len; index = index + 1, n = n + 1 { - let c1 = self.str.unsafe_charcode_at(self.start + index) - if is_leading_surrogate(c1) && index + 1 < len { - let c2 = self.str.unsafe_charcode_at(self.start + index + 1) - if is_trailing_surrogate(c2) { + let c1 = self.str().unsafe_charcode_at(self.start() + index) + if c1.is_leading_surrogate() && index + 1 < len { + let c2 = self.str().unsafe_charcode_at(self.start() + index + 1) + if c2.is_trailing_surrogate() { let c = code_point_of_surrogate_pair(c1, c2) guard yield_(n, c) is IterContinue else { break IterEnd } continue index + 2, n + 1 @@ -291,13 +258,13 @@ pub fn View::iter2(self : View) -> Iter2[Int, Char] { ///| /// Returns an iterator over the Unicode characters in the string view in reverse order. pub fn View::rev_iter(self : View) -> Iter[Char] { - Iter::new(yield_ => for index = self.end - 1 - index >= self.start + Iter::new(yield_ => for index = self.end() - 1 + index >= self.start() index = index - 1 { - let c1 = self.str.unsafe_charcode_at(index) - if is_trailing_surrogate(c1) && index - 1 >= 0 { - let c2 = self.str.unsafe_charcode_at(index - 1) - if is_leading_surrogate(c2) { + let c1 = self.str().unsafe_charcode_at(index) + if c1.is_trailing_surrogate() && index - 1 >= 0 { + let c2 = self.str().unsafe_charcode_at(index - 1) + if c2.is_leading_surrogate() { let c = code_point_of_surrogate_pair(c2, c1) guard yield_(c) is IterContinue else { break IterEnd } continue index - 2 @@ -312,15 +279,15 @@ pub fn View::rev_iter(self : View) -> Iter[Char] { ///| /// Compares two views for equality. Returns true only if both views /// have the same length and contain identical characters in the same order. -pub impl Eq for View with op_equal(self, other) { +pub impl Eq for View with equal(self, other) { let len = self.length() guard len == other.length() else { return false } - if physical_equal(self.str, other.str) && self.start == other.start { + if physical_equal(self.str(), other.str()) && self.start() == other.start() { return true } for i in 0.. View { - // todo: remove .view() in new version - String::from_array(chars).view() + let s = String::from_array(chars) + View::make_view(s, 0, s.length()) } ///| /// Convert char iterator to string view. pub fn View::from_iter(iter : Iter[Char]) -> View { - // todo: remove .view() in new version - String::from_iter(iter).view() + let s = String::from_iter(iter) + View::make_view(s, 0, s.length()) } ///| @@ -388,3 +355,166 @@ pub impl Hash for View with hash_combine(self : View, hasher : Hasher) -> Unit { hasher.combine_uint(self.unsafe_charcode_at(i).reinterpret_as_uint()) } } + +///| +pub suberror CreatingViewError { + IndexOutOfBounds + InvalidIndex +} derive(Show) + +///| +/// Creates a view of a string with proper UTF-16 boundary validation. +/// +/// # Parameters +/// +/// - `start` : Starting UTF-16 code unit index (default: 0) +/// - If positive: counts from the beginning of the string +/// - If negative: counts from the end of the string (e.g., -1 means last position) +/// - `end` : Ending UTF-16 code unit index (optional) +/// - If `None`: extends to the end of the string +/// - If positive: counts from the beginning of the string +/// - If negative: counts from the end of the string +/// +/// # Returns +/// +/// - A `View` representing the specified substring range +/// +/// # Errors +/// +/// - `IndexOutOfBounds` : If start or end indices are out of valid range +/// - `InvalidIndex` : If start or end position would split a UTF-16 surrogate pair +/// +/// This prevents creating views that would split surrogate pairs, which would +/// result in invalid Unicode characters. +/// +/// # Performance +/// +/// This function has O(1) complexity as it only performs boundary checks +/// without scanning the string content. +/// +/// # Examples +/// +/// ```mbt +/// let str = "Hello๐ŸคฃWorld" +/// let view1 = str[0:5] +/// inspect( +/// view1, +/// content=( +/// "Hello" +/// ), +/// ) +/// let view2 = try? str[-5:] +/// inspect( +/// view2, +/// content=( +/// #|Ok("World") +/// ), +/// ) +/// let view3 = try? str[:6] +/// inspect(view3, content="Err(InvalidIndex)") +/// ``` +#alias(op_as_view) +pub fn String::sub( + self : String, + start? : Int = 0, + end? : Int, +) -> View raise CreatingViewError { + let len = self.length() + let end = match end { + None => len + Some(end) => if end < 0 { len + end } else { end } + } + let start = if start < 0 { len + start } else { start } + guard start >= 0 && start <= end && end <= len else { raise IndexOutOfBounds } + if start < len && self.unsafe_charcode_at(start).is_trailing_surrogate() { + raise InvalidIndex + } + if end < len && self.unsafe_charcode_at(end).is_trailing_surrogate() { + raise InvalidIndex + } + View::make_view(self, start, end) +} + +///| +/// Creates a subview of an existing view with proper UTF-16 boundary validation. +/// +/// # Parameters +/// +/// - `start` : Starting UTF-16 code unit index relative to this view (default: 0) +/// - If positive: counts from the beginning of this view +/// - If negative: counts from the end of this view +/// - `end` : Ending UTF-16 code unit index relative to this view (optional) +/// - If `None`: extends to the end of this view +/// - If positive: counts from the beginning of this view +/// - If negative: counts from the end of this view +/// +/// # Returns +/// +/// - A `View` representing the specified subrange of this view +/// +/// # Errors +/// +/// - `IndexOutOfBounds` : If start or end indices are out of this view's range +/// - `InvalidIndex` : If start or end position would split a UTF-16 surrogate pair +/// +/// This prevents creating views that would split surrogate pairs, which would +/// result in invalid Unicode characters. +/// +/// # Performance +/// +/// This function has O(1) complexity as it only performs boundary checks +/// without scanning the string content. +/// +/// # Examples +/// +/// ```mbt +/// let str = "Hello๐ŸคฃWorld"[1:-1] // "ello๐ŸคฃWorl" +/// let view1 = str[0:6] +/// inspect(view1, content="ello๐Ÿคฃ") +/// let view2 = str[-2:] +/// inspect(view2, content="rl") +/// let view3 = try? str[:5] +/// inspect(view3, content="Err(InvalidIndex)") +/// ``` +#alias(op_as_view) +pub fn View::sub( + self : View, + start? : Int = 0, + end? : Int, +) -> View raise CreatingViewError { + let str_len = self.str().length() + + // Calculate absolute positions in the original string + let abs_end = match end { + None => self.end() + Some(end) => if end < 0 { self.end() + end } else { self.start() + end } + } + let abs_start = if start < 0 { + self.end() + start + } else { + self.start() + start + } + + // Validate bounds against the original string + guard abs_start >= self.start() && + abs_start <= abs_end && + abs_end <= self.end() else { + raise IndexOutOfBounds + } + + // Check for surrogate pair boundaries + if abs_start < str_len && + self.str().unsafe_charcode_at(abs_start).is_trailing_surrogate() { + raise InvalidIndex + } + if abs_end < str_len && + self.str().unsafe_charcode_at(abs_end).is_trailing_surrogate() { + raise InvalidIndex + } + View::make_view(self.str(), abs_start, abs_end) +} + +///| +pub impl Add for View with add(self, other) { + [..self, ..other] +} diff --git a/bundled-core/string/view_test.mbt b/bundled-core/string/view_test.mbt index c9e07b2..ab45d8c 100644 --- a/bundled-core/string/view_test.mbt +++ b/bundled-core/string/view_test.mbt @@ -429,30 +429,6 @@ test "index_of_nth_char" { inspect(view.offset_of_nth_char(-6), content="None") } -///| -test "char_at" { - let str = "aa๐Ÿ˜ญb๐Ÿ˜‚cc" - let view = str.view(start_offset=1, end_offset=8) - inspect(view.char_at(0), content="a") - inspect(view.char_at(1), content="๐Ÿ˜ญ") - inspect(view.char_at(3), content="b") - inspect(view.char_at(4), content="๐Ÿ˜‚") - inspect(view.char_at(6), content="c") -} - -///| -test "charcode_at" { - let str = "aa๐Ÿ˜ญb๐Ÿ˜‚cc" - let view = str.view(start_offset=1, end_offset=8) - inspect(view.charcode_at(0), content="97") - inspect(view.charcode_at(1), content="55357") - inspect(view.charcode_at(2), content="56877") - inspect(view.charcode_at(3), content="98") - inspect(view.charcode_at(4), content="55357") - inspect(view.charcode_at(5), content="56834") - inspect(view.charcode_at(6), content="99") -} - ///| test "char_length" { let str = "aa๐Ÿ˜ญb๐Ÿ˜‚cc" @@ -646,19 +622,19 @@ test "iter2" { ///| test "from_array" { - let v = View::from_array(['a', '๐Ÿ˜ญ', 'b', '๐Ÿ˜‚', 'c']) + let v = @string.View::from_array(['a', '๐Ÿ˜ญ', 'b', '๐Ÿ˜‚', 'c']) inspect(v, content="a๐Ÿ˜ญb๐Ÿ˜‚c") } ///| test "from_iter" { - let v = View::from_iter(['a', '๐Ÿ˜ญ', 'b', '๐Ÿ˜‚', 'c'].iter()) + let v = @string.View::from_iter(['a', '๐Ÿ˜ญ', 'b', '๐Ÿ˜‚', 'c'].iter()) inspect(v, content="a๐Ÿ˜ญb๐Ÿ˜‚c") } ///| test "make" { - let v = View::make(5, 'a') + let v = @string.View::make(5, 'a') inspect(v, content="aaaaa") } @@ -667,9 +643,9 @@ test "to_json" { let v = "hello".view(start_offset=1, end_offset=4) inspect( v.to_json(), - content= + content=( #|String("ell") - , + ), ) } @@ -681,3 +657,365 @@ test "hash" { assert_eq(a.hash(), c.hash()) assert_not_eq(a.hash(), b.hash()) } + +///| +/// This test checks the invariance that the hash of a string and its view are the same. +/// +/// This is necessary because we may want to use a string view as a key in a hash map, +/// and we need to ensure that the hash remains consistent with the original string. +test "string view hash invariant" { + let strings : Array[String] = @quickcheck.samples(20) + for s in strings { + let view = s[:] + assert_eq(s.hash(), view.hash()) + } +} + +///| +test "@string.View::replace" { + inspect("hello"[:].replace(old="o", new="a"), content="hella") + inspect("hello"[:].replace(old="world", new="x"), content="hello") + inspect("abc"[:].replace(old="", new="x"), content="xabc") +} + +///| +test "@string.View::repeat" { + inspect("ab"[:].repeat(1), content="ab") + inspect("ab"[:].repeat(0), content="") + inspect("ab"[:].repeat(-2), content="") +} + +///| +test "@string.View::split take" { + inspect( + "a,b,c,d"[:].split(",").take(2).map(@string.View::to_string).collect(), + content=( + #|["a", "b"] + ), + ) +} + +///| +test "String::op_as_view basic usage" { + let str = "Hello๐ŸคฃWorld" + + // Basic usage - positive indices + let view1 = str[0:5] + inspect(view1, content="Hello") + + // Negative indexing from end + let view2 = str[-5:] + inspect(view2, content="World") + + // Full string view + let view3 = str[:] + inspect(view3, content="Hello๐ŸคฃWorld") + + // Start from middle + let view4 = str[5:] + inspect(view4, content="๐ŸคฃWorld") + + // End before middle + let view5 = str[:5] + inspect(view5, content="Hello") +} + +///| +test "String::op_as_view error cases" { + let str = "Hello๐ŸคฃWorld" + + // InvalidIndex - splits surrogate pair + let view1 = try? str[:6] + inspect(view1, content="Err(InvalidIndex)") + + // IndexOutOfBounds - beyond string length + let view2 = try? str[0:20] + inspect(view2, content="Err(IndexOutOfBounds)") + + // IndexOutOfBounds - negative index too large + let view3 = try? str[-20:] + inspect(view3, content="Err(IndexOutOfBounds)") + + // IndexOutOfBounds - start > end + let view4 = try? str[5:3] + inspect(view4, content="Err(IndexOutOfBounds)") + + // InvalidIndex - start on trailing surrogate + let view5 = try? str[6:8] + inspect(view5, content="Err(InvalidIndex)") +} + +///| +test "String::op_as_view with surrogate pairs" { + let str = "Hello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚World" + + // View containing complete emoji + let view1 = str[5:7] + inspect(view1, content="๐Ÿคฃ") + + // View containing multiple emojis + let view2 = str[5:11] + inspect(view2, content="๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚") + + // View from middle of emoji sequence + let view3 = str[7:9] + inspect(view3, content="๐Ÿ˜ญ") + + // Invalid - splits first emoji + let view4 = try? str[5:6] + inspect(view4, content="Err(InvalidIndex)") + + // Invalid - splits second emoji + let view5 = try? str[7:8] + inspect(view5, content="Err(InvalidIndex)") +} + +///| +test "@string.View::op_as_view basic usage" { + let str = "Hello๐ŸคฃWorld"[1:-1] // "ello๐ŸคฃWorl" + + // Basic subview creation + let view1 = str[0:6] + inspect(view1, content="ello๐Ÿคฃ") + + // Negative indexing within view + let view2 = str[-2:] + inspect(view2, content="rl") + + // Full view + let view3 = str[:] + inspect(view3, content="ello๐ŸคฃWorl") + + // Start from middle + let view4 = str[4:] + inspect(view4, content="๐ŸคฃWorl") + + // End before middle + let view5 = str[:4] + inspect(view5, content="ello") +} + +///| +test "@string.View::op_as_view error cases" { + let str = "Hello๐ŸคฃWorld"[1:-1] // "ello๐ŸคฃWorl" + + // IndexOutOfBounds - beyond view range + let view1 = try? str[0:15] + inspect(view1, content="Err(IndexOutOfBounds)") + + // IndexOutOfBounds - negative index too large + let view2 = try? str[-20:] + inspect(view2, content="Err(IndexOutOfBounds)") + + // IndexOutOfBounds - start > end + let view3 = try? str[5:3] + inspect(view3, content="Err(IndexOutOfBounds)") + + // InvalidIndex - splits surrogate pair + let view4 = try? str[:5] + inspect(view4, content="Err(InvalidIndex)") + + // IndexOutOfBounds - start beyond view + let view5 = try? str[11:] + inspect(view5, content="Err(IndexOutOfBounds)") +} + +///| +test "@string.View::op_as_view with surrogate pairs" { + let str = "Hello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚World"[1:-1] // "ello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚Worl" + + // View containing complete emoji + let view1 = str[4:6] + inspect(view1, content="๐Ÿคฃ") + + // View containing multiple emojis + let view2 = str[4:10] + inspect(view2, content="๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚") + + // View from middle of emoji sequence + let view3 = str[6:8] + inspect(view3, content="๐Ÿ˜ญ") + + // Invalid - splits first emoji + let view4 = try? str[4:5] + inspect(view4, content="Err(InvalidIndex)") + + // Invalid - splits second emoji + let view5 = try? str[6:7] + inspect(view5, content="Err(InvalidIndex)") +} + +///| +test "nested @string.View::op_as_view operations" { + let str = "Hello๐ŸคฃWorld" + + // Create first view + let view1 = str[1:-1] // "ello๐ŸคฃWorl" + inspect(view1, content="ello๐ŸคฃWorl") + + // Create subview from first view + let view2 = view1[2:6] // "lo๐Ÿคฃ" + inspect(view2, content="lo๐Ÿคฃ") + + // Create subview from second view + let view3 = view2[2:] // "๐Ÿคฃ" + inspect(view3, content="๐Ÿคฃ") + + // Create subview with negative indexing + let view4 = view1[-4:-1] // "Wor" + inspect(view4, content="Wor") +} + +///| +test "@string.View::op_as_view edge cases" { + let str = "Hello๐ŸคฃWorld" + + // Empty view at start + let view1 = str[0:0] + inspect(view1, content="") + + // Empty view at end + let view2 = str[str.length():str.length()] + inspect(view2, content="") + + // Single character view + let view3 = str[0:1] + inspect(view3, content="H") + + // Single emoji view + let view4 = str[5:7] + inspect(view4, content="๐Ÿคฃ") + + // View with only ASCII characters + let view5 = str[0:5] + inspect(view5, content="Hello") +} + +///| +test "@string.View::op_as_view with complex Unicode" { + let str = "Hello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚๐ŸŒWorld" + + // View with multiple emojis + let view1 = str[5:13] + inspect(view1, content="๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚๐ŸŒ") + + // View with mixed content + let view2 = str[4:14] + inspect(view2, content="o๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚๐ŸŒW") + + // View ending with emoji + let view3 = str[-7:] + inspect(view3, content="๐ŸŒWorld") + + // View starting with emoji + let view4 = str[5:] + inspect(view4, content="๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚๐ŸŒWorld") + + // Invalid - splits emoji + let view5 = try? str[5:6] + inspect(view5, content="Err(InvalidIndex)") +} + +///| +test "@string.View::op_as_view performance characteristics" { + let str = "Hello๐ŸคฃWorld" + + // Test that operations are O(1) - multiple operations + let view1 = str[1:-1] + let view2 = view1[1:-1] + let view3 = view2[1:-1] + let view4 = view3[1:-1] + inspect(view4, content="o๐ŸคฃW") + + // Test with longer string + let long_str = "Hello๐ŸคฃWorldHello๐ŸคฃWorldHello๐ŸคฃWorld" + let long_view1 = long_str[5:15] + let long_view2 = long_view1[2:8] + inspect(long_view2, content="WorldH") +} + +///| +test "@string.View::op_as_view with empty and single character strings" { + let empty_str = "" + + // Empty string operations + let view1 = empty_str[:] + inspect(view1, content="") + let view2 = empty_str[0:0] + inspect(view2, content="") + + // Single character string + let single_str = "A" + let view3 = single_str[:] + inspect(view3, content="A") + let view4 = single_str[0:1] + inspect(view4, content="A") + let view5 = single_str[0:0] + inspect(view5, content="") +} + +///| +test "@string.View::op_as_view boundary validation" { + let str = "Hello๐ŸคฃWorld" + + // Test boundary conditions + let view1 = str[0:str.length()] + inspect(view1, content="Hello๐ŸคฃWorld") + let view2 = str[str.length():str.length()] + inspect(view2, content="") + + // Test negative boundary conditions + let view3 = str[-str.length():] + inspect(view3, content="Hello๐ŸคฃWorld") + let view4 = str[-1:] + inspect(view4, content="d") + + // Test invalid boundaries + let view5 = try? str[str.length() + 1:] + inspect(view5, content="Err(IndexOutOfBounds)") + let view6 = try? str[-str.length() - 1:] + inspect(view6, content="Err(IndexOutOfBounds)") +} + +///| +test "StringView add" { + let str = "Hello๐ŸคฃWorld" + let view1 = str[0:5] + let view2 = str[5:] + inspect(view1 + view2, content="Hello๐ŸคฃWorld") + + // more tests + let view3 = str[0:5] + let view4 = str[5:] + inspect(view3 + view4, content="Hello๐ŸคฃWorld") + let view5 = str[0:5] + let view6 = str[5:] + inspect(view5 + view6, content="Hello๐ŸคฃWorld") + let view7 = str[0:5] + let view8 = str[5:] + inspect(view7 + view8, content="Hello๐ŸคฃWorld") +} + +// Added test to ensure @string.View::op_get abort is covered +// Accessing an index beyond the view length should panic +// and cover the "Index out of bounds" branch in @string.View::op_get. + +///| +test "panic view op_get out of bounds" { + let str = "Hello" + let view = str.view(start_offset=0, end_offset=5) + view[5] |> ignore +} + +// Added test to cover start index pointing to trailing surrogate +// in @string.View::op_as_view. Using a view that begins partway through a string +// and attempting to create a subview starting on the trailing surrogate +// of an emoji should raise `InvalidIndex`. + +///| +test "@string.View::op_as_view start on trailing surrogate" { + let base = "Hello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚World" + let view = base[1:-1] // "ello๐Ÿคฃ๐Ÿ˜ญ๐Ÿ˜‚Worl" + let result = try? view[5:] + inspect(result, content="Err(InvalidIndex)") +} diff --git a/bundled-core/target/packages.json b/bundled-core/target/packages.json deleted file mode 100644 index 8a157ef..0000000 --- a/bundled-core/target/packages.json +++ /dev/null @@ -1,8484 +0,0 @@ -{ - "source_dir": "/home/vigoo/projects/moonbit-component-generator/core", - "name": "moonbitlang/core", - "packages": [ - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/random", - "root": "moonbitlang/core", - "rel": "random", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/random/random.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/random/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/random/random_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/random/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/random/internal/random_source", - "alias": "random_source", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/random/internal/random_source" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/float", - "alias": "float", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/float" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/random/random.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/random/internal/random_source", - "root": "moonbitlang/core", - "rel": "random/internal/random_source", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/random/internal/random_source/random_source_chacha.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/bytes", - "alias": "bytes", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bytes" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/random/internal/random_source/random_source.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/hashset", - "root": "moonbitlang/core", - "rel": "hashset", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashset/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashset/hashset.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashset/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashset/hashset_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashset/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/hashset/hashset.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/buffer", - "root": "moonbitlang/core", - "rel": "buffer", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/buffer/buffer.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/buffer/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/buffer/buffer_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/buffer/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bytes", - "alias": "bytes", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bytes" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/buffer/buffer.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck", - "root": "moonbitlang/core", - "rel": "quickcheck", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/arbitrary.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/arbitrary_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - }, - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - }, - { - "path": "moonbitlang/core/float", - "alias": "float", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/float" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/tuple", - "alias": "tuple", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/tuple" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/quickcheck/quickcheck.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix", - "root": "moonbitlang/core", - "rel": "quickcheck/splitmix", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix/random.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix/random_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/tuple", - "root": "moonbitlang/core", - "rel": "tuple", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/tuple/tuple_arbitrary.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/tuple/tuple.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/tuple/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/tuple/tuple_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/tuple/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/tuple/tuple.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/double", - "root": "moonbitlang/core", - "rel": "double", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/double/mod_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/log_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/hyperbolic_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/cbrt_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/exp_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/round_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/double.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/cbrt_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/trig_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/scalbn.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/pow_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/log_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/round_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/hyperbolic_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/mod_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/hypot_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/hypot_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/exp_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/pow_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/to_uint.mbt": { - "backend": [ - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/trig_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/to_uint_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/round.mbt": { - "backend": [ - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/math_functions.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/double/to_uint_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/pow_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/mod_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/double_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/round_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/double/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/int64", - "alias": "int64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int64" - }, - { - "path": "moonbitlang/core/uint64", - "alias": "uint64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint64" - }, - { - "path": "moonbitlang/core/double/internal/ryu", - "alias": "ryu", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double/internal/ryu" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/bench", - "alias": "bench", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bench" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/double/double.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/double/internal/ryu", - "root": "moonbitlang/core", - "rel": "double/internal/ryu", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/double/internal/ryu/common.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/double/internal/ryu/ryu.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/double/internal/ryu/ryu_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bool", - "alias": "bool", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bool" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/double/internal/ryu/ryu.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/option", - "root": "moonbitlang/core", - "rel": "option", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/option/option.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/option/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/option/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/option/option_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/option/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/option/option.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/set", - "root": "moonbitlang/core", - "rel": "set", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/set/grow_heuristic.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/set/linked_hash_set.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/set/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/set/linked_hash_set_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/set/set.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/uint64", - "root": "moonbitlang/core", - "rel": "uint64", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint64/uint64.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint64/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/uint64/uint64.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/array", - "root": "moonbitlang/core", - "rel": "array", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/array/fixedarray_sort_by.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/fixedarray.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/fixedarray_sort.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/array.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/array_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/blit_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/blit.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/array_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/slice.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/sort.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/blit_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/sort_by.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/view.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/array/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/view_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/hash_data_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/array_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/array/fixedarray_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/array/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bytes", - "alias": "bytes", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bytes" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/random", - "alias": "random", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/random" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/array/array.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/bench", - "root": "moonbitlang/core", - "rel": "bench", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/bench/monotonic_clock_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bench/stats.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bench/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bench/monotonic_clock_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bench/bench.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bench/monotonic_clock_native.mbt": { - "backend": [ - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/bench/bench.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/uint", - "root": "moonbitlang/core", - "rel": "uint", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint/uint.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint/uint_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/uint/uint.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/float", - "root": "moonbitlang/core", - "rel": "float", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/float/round_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/exp.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/round_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/float.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/mod.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/pow.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/log.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/hyperbolic.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/round.mbt": { - "backend": [ - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/to_int.mbt": { - "backend": [ - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/to_int_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/math_functions.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/trig.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/float/pow_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/mod_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/round_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/float/float_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/float/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/uint", - "alias": "uint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/float/float.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/math", - "root": "moonbitlang/core", - "rel": "math", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/math/algebraic_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/pow_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/prime.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/hyperbolic_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/trig_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/algebraic.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/exp.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/scalbn.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/log_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/exp_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/pow_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/pow.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/log.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/hyperbolic.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/log_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/exp_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/round.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/trig_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/hyperbolic_double_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/algebraic_double_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/trig.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/math/algebraic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/pow_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/log_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/exp_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/trig_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/prime_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/math/hyperbolic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/math/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/float", - "alias": "float", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/float" - }, - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - }, - { - "path": "moonbitlang/core/random", - "alias": "random", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/random" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/math/math.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/char", - "root": "moonbitlang/core", - "rel": "char", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/char/char.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/char/char_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/char/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/char/char.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/coverage", - "root": "moonbitlang/core", - "rel": "coverage", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/coverage/coverage.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/coverage/coverage.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/result", - "root": "moonbitlang/core", - "rel": "result", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/result/result.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/result/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/result/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/result/result_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/result/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/result/result.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/json", - "root": "moonbitlang/core", - "rel": "json", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/json/json.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/internal_types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_main.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_misc.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/json_path.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_string.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/tuple_fromjson.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_number.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/parse.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/from_json.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/json/internal_types_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_number_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/parse_error_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/from_to_json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/to_json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/json_inspect_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/parse_whitespace_error_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_misc_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/json_encode_decode_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/types_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/from_json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/lex_string_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/parse_error2_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/derive_json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/json/parse_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/json/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/strconv", - "alias": "strconv", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/strconv" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/result", - "alias": "result", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/result" - }, - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - }, - { - "path": "moonbitlang/core/unit", - "alias": "unit", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/unit" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/json/json.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/unit", - "root": "moonbitlang/core", - "rel": "unit", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/unit/unit.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/unit/unit_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/unit/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/unit/unit.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/hashset", - "root": "moonbitlang/core", - "rel": "immut/hashset", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashset/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashset/HAMT.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashset/HAMT_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/immut/internal/sparse_array", - "alias": "sparse_array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/immut/internal/path", - "alias": "path", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/path" - }, - { - "path": "moonbitlang/core/list", - "alias": "list", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/list" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/hashset/hashset.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/array", - "root": "moonbitlang/core", - "rel": "immut/array", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/tree.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/array.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/tree_utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/panic_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/array_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/array_mix_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/utils_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/array_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/array/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/array", - "alias": "core/array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/array/array.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array", - "root": "moonbitlang/core", - "rel": "immut/internal/sparse_array", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array/sparse_array.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array/bitset.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array/sparse_array_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/path", - "root": "moonbitlang/core", - "rel": "immut/internal/path", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/path/path.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/internal/path/path.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap", - "root": "moonbitlang/core", - "rel": "immut/hashmap", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/HAMT.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/bucket.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/HAMT_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/pattern_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/hashmap/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/tuple", - "alias": "tuple", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/tuple" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/immut/internal/sparse_array", - "alias": "sparse_array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/immut/internal/sparse_array" - }, - { - "path": "moonbitlang/core/list", - "alias": "list", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/list" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/hashmap/hashmap.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set", - "root": "moonbitlang/core", - "rel": "immut/sorted_set", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/immutable_set.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/generic.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/generic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/immutable_set_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_set/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/sorted_set/sorted_set.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue", - "root": "moonbitlang/core", - "rel": "immut/priority_queue", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/priority_queue.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/priority_queue_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/priority_queue/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/random", - "alias": "random", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/random" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/priority_queue/priority_queue.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/list", - "root": "moonbitlang/core", - "rel": "immut/list", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/list.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/dps_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/list_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/list/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/list/list.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map", - "root": "moonbitlang/core", - "rel": "immut/sorted_map", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/traits_impl.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/inorder_iterator.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/map.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/panic_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/utils_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/map_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/immut/sorted_map/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/tuple", - "alias": "tuple", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/tuple" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/immut/sorted_map/sorted_map.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/hashmap", - "root": "moonbitlang/core", - "rel": "hashmap", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/json.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/hashmap.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/utils_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/hashmap_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/pattern_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/hashmap/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/tuple", - "alias": "tuple", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/tuple" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/hashmap/hashmap.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/string", - "root": "moonbitlang/core", - "rel": "string", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/string/string.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/methods.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/view.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/string/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/view_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/string_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/additional_coverage_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/op_as_view_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/string/string_unicode_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/string/DESIGN.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/list", - "alias": "list", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/list" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/string/string.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/sorted_set", - "root": "moonbitlang/core", - "rel": "sorted_set", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/set.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/set_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_set/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/sorted_set/sorted_set.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/builtin", - "root": "moonbitlang/core", - "rel": "builtin", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/builtin/json.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/operators.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int64_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/stringbuilder.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/arraycore_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/output.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/iter2.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/autoloc.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/failure.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/op.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/hasher.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/string.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/uninitialized_array.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/arrayview.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/fixedarray.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/fixedarray_block.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/int64_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/stringbuilder_buffer.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/array.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/uint64.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/linked_hash_map.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int.mbt": { - "backend": [ - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/stringbuilder_concat.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_show.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/console.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/array_block.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/show.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int64_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/intrinsics.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_compare.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/int64_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/bytes.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/arraycore_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_hash.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/result.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/byte.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/traits.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/option.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/to_string.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_to_json.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/unit.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_eq.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/iter.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int64_native.mbt": { - "backend": [ - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/assert.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/builtin/panic_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/array_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/linked_hash_map_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/builtin/int64_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_eq_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/show_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/iter2_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_to_json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int64_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/double_to_int_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/char_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/linked_hash_map_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/unit_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/assert_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/result_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/panic_nonjs_test.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/string_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_show_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/option_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/traits_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/json_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/existensial_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/hasher_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/bytes_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/fixedarray_block_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/arrayview_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/feature_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/stringbuilder_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/array_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_hash_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/iter_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/guard_feature_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/fixedarray_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/repr_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/array_nonjs_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/byte_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/tuple_compare_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/string_overloading_feature_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/builtin/intrinsics_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/builtin/LinkedHashMap.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/abort", - "alias": "abort", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/abort" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/bytes", - "alias": "bytes", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bytes" - }, - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/uint64", - "alias": "uint64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint64" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/int64", - "alias": "int64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int64" - }, - { - "path": "moonbitlang/core/uint", - "alias": "uint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/builtin/builtin.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/int", - "root": "moonbitlang/core", - "rel": "int", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/int/int.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int/int_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/uint", - "alias": "uint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/int/int.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/ref", - "root": "moonbitlang/core", - "rel": "ref", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/ref/ref.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/ref/ref_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/ref/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/ref/ref.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/deque", - "root": "moonbitlang/core", - "rel": "deque", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/deque/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/deque/deque.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/deque/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/deque/deque_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/deque/deque_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/deque/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/deque/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/deque/deque.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/byte", - "root": "moonbitlang/core", - "rel": "byte", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/byte/byte.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/byte/byte_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/byte/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/byte/byte.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/int16", - "root": "moonbitlang/core", - "rel": "int16", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/int16/int16.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int16/int16_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int16/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/int16/int16.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/uint16", - "root": "moonbitlang/core", - "rel": "uint16", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint16/uint16.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint16/uint16_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/uint16/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/uint16/uint16.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/int64", - "root": "moonbitlang/core", - "rel": "int64", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/int64/int64.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/int64/xxhash.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int64/int64_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/int64/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bytes", - "alias": "bytes", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bytes" - }, - { - "path": "moonbitlang/core/uint", - "alias": "uint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint" - }, - { - "path": "moonbitlang/core/uint64", - "alias": "uint64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint64" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/int64/int64.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/queue", - "root": "moonbitlang/core", - "rel": "queue", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/queue/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/queue/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/queue/queue.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/queue/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/queue/queue_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/queue/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/queue/queue.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/priority_queue", - "root": "moonbitlang/core", - "rel": "priority_queue", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/priority_queue/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/priority_queue/priority_queue.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/priority_queue/priority_queue_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/priority_queue/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/priority_queue/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/random", - "alias": "random", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/random" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/priority_queue/priority_queue.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/rational", - "root": "moonbitlang/core", - "rel": "rational", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/rational/rational.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/rational/rational_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/rational/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/result", - "alias": "result", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/result" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/int64", - "alias": "int64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int64" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/rational/rational.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/list", - "root": "moonbitlang/core", - "rel": "list", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/list/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/list/list.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/list/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/list/panic_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/list/list_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/list/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/list/list.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/env", - "root": "moonbitlang/core", - "rel": "env", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/env/env_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/env/env_wasm.mbt": { - "backend": [ - "Wasm", - "WasmGC" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/env/env_native.mbt": { - "backend": [ - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/env/env.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/env/env_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/env/env.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/bigint", - "root": "moonbitlang/core", - "rel": "bigint", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_js.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bigint/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_nonjs.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "wbtest-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_js_wbtest.mbt": { - "backend": [ - "Js" - ], - "optlevel": [ - "Release", - "Debug" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_nonjs_wbtest.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Native", - "LLVM" - ], - "optlevel": [ - "Release", - "Debug" - ] - } - }, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bigint/bigint_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/uint", - "alias": "uint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - }, - { - "path": "moonbitlang/core/quickcheck/splitmix", - "alias": "splitmix", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck/splitmix" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/bigint/bigint.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/bytes", - "root": "moonbitlang/core", - "rel": "bytes", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/bytes/xxhash.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/bytes.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/view.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bytes/bytes_pattern_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/feature_pipe_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/view_pattern_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/view_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/bytes/bytes_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bytes/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/bytes/bytes.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/sorted_map", - "root": "moonbitlang/core", - "rel": "sorted_map", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/utils.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/map.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/map_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/sorted_map/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/option", - "alias": "option", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/option" - }, - { - "path": "moonbitlang/core/tuple", - "alias": "tuple", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/tuple" - }, - { - "path": "moonbitlang/core/quickcheck", - "alias": "quickcheck", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/quickcheck" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/sorted_map/sorted_map.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/bool", - "root": "moonbitlang/core", - "rel": "bool", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/bool/bool.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bool/bool_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/bool/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/bool/bool.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/cmp", - "root": "moonbitlang/core", - "rel": "cmp", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/cmp/cmp.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/cmp/cmp_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/cmp/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/int", - "alias": "int", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/int" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/test", - "alias": "test", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/test" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/cmp/cmp.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/abort", - "root": "moonbitlang/core", - "rel": "abort", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/abort/abort.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/abort/abort.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/test", - "root": "moonbitlang/core", - "rel": "test", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/test/test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/test/types.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/test/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/test/test_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bench", - "alias": "bench", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bench" - }, - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/test/test.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/prelude", - "root": "moonbitlang/core", - "rel": "prelude", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/prelude/prelude.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/prelude/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": {}, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/bigint", - "alias": "bigint", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/bigint" - }, - { - "path": "moonbitlang/core/set", - "alias": "set", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/set" - }, - { - "path": "moonbitlang/core/array", - "alias": "array", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/array" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/prelude/prelude.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/strconv", - "root": "moonbitlang/core", - "rel": "strconv", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/strconv/string_slice.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/errors.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/number.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/double.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/uint.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/int.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/decimal.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/traits.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/deprecated.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/bool.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/strconv/number_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/additional_coverage_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/double_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/int_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - }, - "/home/vigoo/projects/moonbit-component-generator/core/strconv/uint_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": { - "/home/vigoo/projects/moonbit-component-generator/core/strconv/README.mbt.md": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - }, - { - "path": "moonbitlang/core/double", - "alias": "double", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/double" - }, - { - "path": "moonbitlang/core/uint64", - "alias": "uint64", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/uint64" - }, - { - "path": "moonbitlang/core/string", - "alias": "string", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/string" - }, - { - "path": "moonbitlang/core/char", - "alias": "char", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/char" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/strconv/strconv.mi" - }, - { - "is-main": false, - "is-third-party": false, - "root-path": "/home/vigoo/projects/moonbit-component-generator/core/error", - "root": "moonbitlang/core", - "rel": "error", - "files": { - "/home/vigoo/projects/moonbit-component-generator/core/error/error.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "wbtest-files": {}, - "test-files": { - "/home/vigoo/projects/moonbit-component-generator/core/error/error_test.mbt": { - "backend": [ - "Wasm", - "WasmGC", - "Js", - "Native", - "LLVM" - ], - "optlevel": [ - "Debug", - "Release" - ] - } - }, - "mbt-md-files": {}, - "deps": [ - { - "path": "moonbitlang/core/builtin", - "alias": "builtin", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/builtin" - } - ], - "wbtest-deps": [], - "test-deps": [ - { - "path": "moonbitlang/core/json", - "alias": "json", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/json" - }, - { - "path": "moonbitlang/core/prelude", - "alias": "prelude", - "fspath": "/home/vigoo/projects/moonbit-component-generator/core/prelude" - } - ], - "artifact": "/home/vigoo/projects/moonbit-component-generator/core/target/wasm/release/bundle/error/error.mi" - } - ], - "deps": [], - "backend": "wasm", - "opt_level": "release", - "source": null -} \ No newline at end of file diff --git a/bundled-core/target/.moon-lock b/bundled-core/target/wasm/release/build/.moon-lock similarity index 100% rename from bundled-core/target/.moon-lock rename to bundled-core/target/wasm/release/build/.moon-lock diff --git a/bundled-core/target/wasm/release/build/moon.db b/bundled-core/target/wasm/release/build/moon.db new file mode 100644 index 0000000..d5d981c Binary files /dev/null and b/bundled-core/target/wasm/release/build/moon.db differ diff --git a/bundled-core/target/wasm/release/bundle/abort/abort.core b/bundled-core/target/wasm/release/bundle/abort/abort.core deleted file mode 100644 index 5b85497..0000000 Binary files a/bundled-core/target/wasm/release/bundle/abort/abort.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/abort/abort.mi b/bundled-core/target/wasm/release/bundle/abort/abort.mi deleted file mode 100644 index 298c5d7..0000000 Binary files a/bundled-core/target/wasm/release/bundle/abort/abort.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/array/array.core b/bundled-core/target/wasm/release/bundle/array/array.core deleted file mode 100644 index a6acdee..0000000 Binary files a/bundled-core/target/wasm/release/bundle/array/array.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/array/array.mi b/bundled-core/target/wasm/release/bundle/array/array.mi deleted file mode 100644 index ee210b0..0000000 Binary files a/bundled-core/target/wasm/release/bundle/array/array.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bench/bench.core b/bundled-core/target/wasm/release/bundle/bench/bench.core deleted file mode 100644 index 0718a67..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bench/bench.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bench/bench.mi b/bundled-core/target/wasm/release/bundle/bench/bench.mi deleted file mode 100644 index 7f246db..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bench/bench.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bigint/bigint.core b/bundled-core/target/wasm/release/bundle/bigint/bigint.core deleted file mode 100644 index 8210770..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bigint/bigint.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bigint/bigint.mi b/bundled-core/target/wasm/release/bundle/bigint/bigint.mi deleted file mode 100644 index 0505f07..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bigint/bigint.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bool/bool.core b/bundled-core/target/wasm/release/bundle/bool/bool.core deleted file mode 100644 index 1fb3de1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bool/bool.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bool/bool.mi b/bundled-core/target/wasm/release/bundle/bool/bool.mi deleted file mode 100644 index 8d28282..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bool/bool.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/buffer/buffer.core b/bundled-core/target/wasm/release/bundle/buffer/buffer.core deleted file mode 100644 index 4b0eb46..0000000 Binary files a/bundled-core/target/wasm/release/bundle/buffer/buffer.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/buffer/buffer.mi b/bundled-core/target/wasm/release/bundle/buffer/buffer.mi deleted file mode 100644 index ac345ae..0000000 Binary files a/bundled-core/target/wasm/release/bundle/buffer/buffer.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/build.moon_db b/bundled-core/target/wasm/release/bundle/build.moon_db deleted file mode 100644 index c1c13be..0000000 Binary files a/bundled-core/target/wasm/release/bundle/build.moon_db and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/builtin/builtin.core b/bundled-core/target/wasm/release/bundle/builtin/builtin.core deleted file mode 100644 index 8a64d0c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/builtin/builtin.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/builtin/builtin.mi b/bundled-core/target/wasm/release/bundle/builtin/builtin.mi deleted file mode 100644 index 22e416a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/builtin/builtin.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bundle.output b/bundled-core/target/wasm/release/bundle/bundle.output deleted file mode 100644 index e69de29..0000000 diff --git a/bundled-core/target/wasm/release/bundle/byte/byte.core b/bundled-core/target/wasm/release/bundle/byte/byte.core deleted file mode 100644 index be848fd..0000000 Binary files a/bundled-core/target/wasm/release/bundle/byte/byte.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/byte/byte.mi b/bundled-core/target/wasm/release/bundle/byte/byte.mi deleted file mode 100644 index 464218c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/byte/byte.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bytes/bytes.core b/bundled-core/target/wasm/release/bundle/bytes/bytes.core deleted file mode 100644 index edb2611..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bytes/bytes.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/bytes/bytes.mi b/bundled-core/target/wasm/release/bundle/bytes/bytes.mi deleted file mode 100644 index c610410..0000000 Binary files a/bundled-core/target/wasm/release/bundle/bytes/bytes.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/char/char.core b/bundled-core/target/wasm/release/bundle/char/char.core deleted file mode 100644 index b07e15f..0000000 Binary files a/bundled-core/target/wasm/release/bundle/char/char.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/char/char.mi b/bundled-core/target/wasm/release/bundle/char/char.mi deleted file mode 100644 index e97de0a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/char/char.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/cmp/cmp.core b/bundled-core/target/wasm/release/bundle/cmp/cmp.core deleted file mode 100644 index 6a26486..0000000 Binary files a/bundled-core/target/wasm/release/bundle/cmp/cmp.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/cmp/cmp.mi b/bundled-core/target/wasm/release/bundle/cmp/cmp.mi deleted file mode 100644 index 2bc40dd..0000000 Binary files a/bundled-core/target/wasm/release/bundle/cmp/cmp.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/core.core b/bundled-core/target/wasm/release/bundle/core.core deleted file mode 100644 index a779565..0000000 Binary files a/bundled-core/target/wasm/release/bundle/core.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/coverage/coverage.core b/bundled-core/target/wasm/release/bundle/coverage/coverage.core deleted file mode 100644 index e29b0b2..0000000 Binary files a/bundled-core/target/wasm/release/bundle/coverage/coverage.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/coverage/coverage.mi b/bundled-core/target/wasm/release/bundle/coverage/coverage.mi deleted file mode 100644 index 8c648b2..0000000 Binary files a/bundled-core/target/wasm/release/bundle/coverage/coverage.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/deque/deque.core b/bundled-core/target/wasm/release/bundle/deque/deque.core deleted file mode 100644 index df85883..0000000 Binary files a/bundled-core/target/wasm/release/bundle/deque/deque.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/deque/deque.mi b/bundled-core/target/wasm/release/bundle/deque/deque.mi deleted file mode 100644 index 3c5fd61..0000000 Binary files a/bundled-core/target/wasm/release/bundle/deque/deque.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/double/double.core b/bundled-core/target/wasm/release/bundle/double/double.core deleted file mode 100644 index b3ad3ae..0000000 Binary files a/bundled-core/target/wasm/release/bundle/double/double.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/double/double.mi b/bundled-core/target/wasm/release/bundle/double/double.mi deleted file mode 100644 index dcc4b85..0000000 Binary files a/bundled-core/target/wasm/release/bundle/double/double.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.core b/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.core deleted file mode 100644 index f18f949..0000000 Binary files a/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.mi b/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.mi deleted file mode 100644 index 17a36b3..0000000 Binary files a/bundled-core/target/wasm/release/bundle/double/internal/ryu/ryu.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/env/env.core b/bundled-core/target/wasm/release/bundle/env/env.core deleted file mode 100644 index ca88845..0000000 Binary files a/bundled-core/target/wasm/release/bundle/env/env.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/env/env.mi b/bundled-core/target/wasm/release/bundle/env/env.mi deleted file mode 100644 index 85a1520..0000000 Binary files a/bundled-core/target/wasm/release/bundle/env/env.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/error/error.core b/bundled-core/target/wasm/release/bundle/error/error.core deleted file mode 100644 index 455486e..0000000 Binary files a/bundled-core/target/wasm/release/bundle/error/error.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/error/error.mi b/bundled-core/target/wasm/release/bundle/error/error.mi deleted file mode 100644 index 98c01c3..0000000 Binary files a/bundled-core/target/wasm/release/bundle/error/error.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/float/float.core b/bundled-core/target/wasm/release/bundle/float/float.core deleted file mode 100644 index b75e9a5..0000000 Binary files a/bundled-core/target/wasm/release/bundle/float/float.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/float/float.mi b/bundled-core/target/wasm/release/bundle/float/float.mi deleted file mode 100644 index e016530..0000000 Binary files a/bundled-core/target/wasm/release/bundle/float/float.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/hashmap/hashmap.core b/bundled-core/target/wasm/release/bundle/hashmap/hashmap.core deleted file mode 100644 index fa2d189..0000000 Binary files a/bundled-core/target/wasm/release/bundle/hashmap/hashmap.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/hashmap/hashmap.mi b/bundled-core/target/wasm/release/bundle/hashmap/hashmap.mi deleted file mode 100644 index f8ff8bb..0000000 Binary files a/bundled-core/target/wasm/release/bundle/hashmap/hashmap.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/hashset/hashset.core b/bundled-core/target/wasm/release/bundle/hashset/hashset.core deleted file mode 100644 index 440415f..0000000 Binary files a/bundled-core/target/wasm/release/bundle/hashset/hashset.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/hashset/hashset.mi b/bundled-core/target/wasm/release/bundle/hashset/hashset.mi deleted file mode 100644 index cd5190c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/hashset/hashset.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/array/array.core b/bundled-core/target/wasm/release/bundle/immut/array/array.core deleted file mode 100644 index 305c07a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/array/array.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/array/array.mi b/bundled-core/target/wasm/release/bundle/immut/array/array.mi deleted file mode 100644 index fd72059..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/array/array.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.core b/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.core deleted file mode 100644 index 81f7c6a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.mi b/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.mi deleted file mode 100644 index 794be82..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/hashmap/hashmap.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.core b/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.core deleted file mode 100644 index f4b501f..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.mi b/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.mi deleted file mode 100644 index 36e4616..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/hashset/hashset.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/internal/path/path.core b/bundled-core/target/wasm/release/bundle/immut/internal/path/path.core deleted file mode 100644 index bdb534d..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/internal/path/path.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/internal/path/path.mi b/bundled-core/target/wasm/release/bundle/immut/internal/path/path.mi deleted file mode 100644 index 29dc976..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/internal/path/path.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.core b/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.core deleted file mode 100644 index 7ff9759..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.mi b/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.mi deleted file mode 100644 index d3fa2e3..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/internal/sparse_array/sparse_array.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/list/list.core b/bundled-core/target/wasm/release/bundle/immut/list/list.core deleted file mode 100644 index 4ba0c24..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/list/list.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/list/list.mi b/bundled-core/target/wasm/release/bundle/immut/list/list.mi deleted file mode 100644 index f3d8a1c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/list/list.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.core b/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.core deleted file mode 100644 index 7232452..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.mi b/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.mi deleted file mode 100644 index 395dd4b..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/priority_queue/priority_queue.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.core b/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.core deleted file mode 100644 index 586e58b..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.mi b/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.mi deleted file mode 100644 index b5d60a1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/sorted_map/sorted_map.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.core b/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.core deleted file mode 100644 index af160bc..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.mi b/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.mi deleted file mode 100644 index b6d2d6e..0000000 Binary files a/bundled-core/target/wasm/release/bundle/immut/sorted_set/sorted_set.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int/int.core b/bundled-core/target/wasm/release/bundle/int/int.core deleted file mode 100644 index ecd2fb5..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int/int.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int/int.mi b/bundled-core/target/wasm/release/bundle/int/int.mi deleted file mode 100644 index a62219a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int/int.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int16/int16.core b/bundled-core/target/wasm/release/bundle/int16/int16.core deleted file mode 100644 index e24a66b..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int16/int16.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int16/int16.mi b/bundled-core/target/wasm/release/bundle/int16/int16.mi deleted file mode 100644 index 9649390..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int16/int16.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int64/int64.core b/bundled-core/target/wasm/release/bundle/int64/int64.core deleted file mode 100644 index ff9d4a0..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int64/int64.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/int64/int64.mi b/bundled-core/target/wasm/release/bundle/int64/int64.mi deleted file mode 100644 index 5f1ead0..0000000 Binary files a/bundled-core/target/wasm/release/bundle/int64/int64.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/json/json.core b/bundled-core/target/wasm/release/bundle/json/json.core deleted file mode 100644 index 4dcb776..0000000 Binary files a/bundled-core/target/wasm/release/bundle/json/json.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/json/json.mi b/bundled-core/target/wasm/release/bundle/json/json.mi deleted file mode 100644 index af60a6c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/json/json.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/list/list.core b/bundled-core/target/wasm/release/bundle/list/list.core deleted file mode 100644 index 3285975..0000000 Binary files a/bundled-core/target/wasm/release/bundle/list/list.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/list/list.mi b/bundled-core/target/wasm/release/bundle/list/list.mi deleted file mode 100644 index 06123c9..0000000 Binary files a/bundled-core/target/wasm/release/bundle/list/list.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/math/math.core b/bundled-core/target/wasm/release/bundle/math/math.core deleted file mode 100644 index c8e39e1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/math/math.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/math/math.mi b/bundled-core/target/wasm/release/bundle/math/math.mi deleted file mode 100644 index ae53811..0000000 Binary files a/bundled-core/target/wasm/release/bundle/math/math.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/moon.db b/bundled-core/target/wasm/release/bundle/moon.db index c787d6e..9b7c983 100644 Binary files a/bundled-core/target/wasm/release/bundle/moon.db and b/bundled-core/target/wasm/release/bundle/moon.db differ diff --git a/bundled-core/target/wasm/release/bundle/option/option.core b/bundled-core/target/wasm/release/bundle/option/option.core deleted file mode 100644 index 50f9cb2..0000000 Binary files a/bundled-core/target/wasm/release/bundle/option/option.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/option/option.mi b/bundled-core/target/wasm/release/bundle/option/option.mi deleted file mode 100644 index ed622f3..0000000 Binary files a/bundled-core/target/wasm/release/bundle/option/option.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/prelude/prelude.core b/bundled-core/target/wasm/release/bundle/prelude/prelude.core deleted file mode 100644 index be44d09..0000000 Binary files a/bundled-core/target/wasm/release/bundle/prelude/prelude.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/prelude/prelude.mi b/bundled-core/target/wasm/release/bundle/prelude/prelude.mi deleted file mode 100644 index eaeb0aa..0000000 Binary files a/bundled-core/target/wasm/release/bundle/prelude/prelude.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.core b/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.core deleted file mode 100644 index 52fd878..0000000 Binary files a/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.mi b/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.mi deleted file mode 100644 index 635eee9..0000000 Binary files a/bundled-core/target/wasm/release/bundle/priority_queue/priority_queue.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/queue/queue.core b/bundled-core/target/wasm/release/bundle/queue/queue.core deleted file mode 100644 index 02a337a..0000000 Binary files a/bundled-core/target/wasm/release/bundle/queue/queue.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/queue/queue.mi b/bundled-core/target/wasm/release/bundle/queue/queue.mi deleted file mode 100644 index 2048452..0000000 Binary files a/bundled-core/target/wasm/release/bundle/queue/queue.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.core b/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.core deleted file mode 100644 index 7ff767d..0000000 Binary files a/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.mi b/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.mi deleted file mode 100644 index 2714d87..0000000 Binary files a/bundled-core/target/wasm/release/bundle/quickcheck/quickcheck.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.core b/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.core deleted file mode 100644 index 96dcb34..0000000 Binary files a/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.mi b/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.mi deleted file mode 100644 index b2f0295..0000000 Binary files a/bundled-core/target/wasm/release/bundle/quickcheck/splitmix/splitmix.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.core b/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.core deleted file mode 100644 index 8a6d90b..0000000 Binary files a/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.mi b/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.mi deleted file mode 100644 index a9cedab..0000000 Binary files a/bundled-core/target/wasm/release/bundle/random/internal/random_source/random_source.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/random/random.core b/bundled-core/target/wasm/release/bundle/random/random.core deleted file mode 100644 index f079fc2..0000000 Binary files a/bundled-core/target/wasm/release/bundle/random/random.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/random/random.mi b/bundled-core/target/wasm/release/bundle/random/random.mi deleted file mode 100644 index 5eefc26..0000000 Binary files a/bundled-core/target/wasm/release/bundle/random/random.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/rational/rational.core b/bundled-core/target/wasm/release/bundle/rational/rational.core deleted file mode 100644 index 17f2c4c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/rational/rational.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/rational/rational.mi b/bundled-core/target/wasm/release/bundle/rational/rational.mi deleted file mode 100644 index 9ba8fc1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/rational/rational.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/ref/ref.core b/bundled-core/target/wasm/release/bundle/ref/ref.core deleted file mode 100644 index 18b20d1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/ref/ref.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/ref/ref.mi b/bundled-core/target/wasm/release/bundle/ref/ref.mi deleted file mode 100644 index 5f38bc7..0000000 Binary files a/bundled-core/target/wasm/release/bundle/ref/ref.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/result/result.core b/bundled-core/target/wasm/release/bundle/result/result.core deleted file mode 100644 index d2814b0..0000000 Binary files a/bundled-core/target/wasm/release/bundle/result/result.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/result/result.mi b/bundled-core/target/wasm/release/bundle/result/result.mi deleted file mode 100644 index a507ddb..0000000 Binary files a/bundled-core/target/wasm/release/bundle/result/result.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/set/set.core b/bundled-core/target/wasm/release/bundle/set/set.core deleted file mode 100644 index 5273a98..0000000 Binary files a/bundled-core/target/wasm/release/bundle/set/set.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/set/set.mi b/bundled-core/target/wasm/release/bundle/set/set.mi deleted file mode 100644 index 4de0b26..0000000 Binary files a/bundled-core/target/wasm/release/bundle/set/set.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.core b/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.core deleted file mode 100644 index 6bec13d..0000000 Binary files a/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.mi b/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.mi deleted file mode 100644 index 20babd3..0000000 Binary files a/bundled-core/target/wasm/release/bundle/sorted_map/sorted_map.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.core b/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.core deleted file mode 100644 index 14a2a03..0000000 Binary files a/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.mi b/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.mi deleted file mode 100644 index 8d435d8..0000000 Binary files a/bundled-core/target/wasm/release/bundle/sorted_set/sorted_set.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/strconv/strconv.core b/bundled-core/target/wasm/release/bundle/strconv/strconv.core deleted file mode 100644 index c78ec0d..0000000 Binary files a/bundled-core/target/wasm/release/bundle/strconv/strconv.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/strconv/strconv.mi b/bundled-core/target/wasm/release/bundle/strconv/strconv.mi deleted file mode 100644 index 50f1756..0000000 Binary files a/bundled-core/target/wasm/release/bundle/strconv/strconv.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/string/string.core b/bundled-core/target/wasm/release/bundle/string/string.core deleted file mode 100644 index 44315cb..0000000 Binary files a/bundled-core/target/wasm/release/bundle/string/string.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/string/string.mi b/bundled-core/target/wasm/release/bundle/string/string.mi deleted file mode 100644 index d937dcc..0000000 Binary files a/bundled-core/target/wasm/release/bundle/string/string.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/test/test.core b/bundled-core/target/wasm/release/bundle/test/test.core deleted file mode 100644 index f82d30c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/test/test.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/test/test.mi b/bundled-core/target/wasm/release/bundle/test/test.mi deleted file mode 100644 index 17f09e9..0000000 Binary files a/bundled-core/target/wasm/release/bundle/test/test.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/tuple/tuple.core b/bundled-core/target/wasm/release/bundle/tuple/tuple.core deleted file mode 100644 index 6111a7c..0000000 Binary files a/bundled-core/target/wasm/release/bundle/tuple/tuple.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/tuple/tuple.mi b/bundled-core/target/wasm/release/bundle/tuple/tuple.mi deleted file mode 100644 index 63f2238..0000000 Binary files a/bundled-core/target/wasm/release/bundle/tuple/tuple.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint/uint.core b/bundled-core/target/wasm/release/bundle/uint/uint.core deleted file mode 100644 index c1d46be..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint/uint.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint/uint.mi b/bundled-core/target/wasm/release/bundle/uint/uint.mi deleted file mode 100644 index 6ba5b98..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint/uint.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint16/uint16.core b/bundled-core/target/wasm/release/bundle/uint16/uint16.core deleted file mode 100644 index 2e31ed5..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint16/uint16.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint16/uint16.mi b/bundled-core/target/wasm/release/bundle/uint16/uint16.mi deleted file mode 100644 index 41e22af..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint16/uint16.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint64/uint64.core b/bundled-core/target/wasm/release/bundle/uint64/uint64.core deleted file mode 100644 index 1f031f1..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint64/uint64.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/uint64/uint64.mi b/bundled-core/target/wasm/release/bundle/uint64/uint64.mi deleted file mode 100644 index f3e2e52..0000000 Binary files a/bundled-core/target/wasm/release/bundle/uint64/uint64.mi and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/unit/unit.core b/bundled-core/target/wasm/release/bundle/unit/unit.core deleted file mode 100644 index 8e8db97..0000000 Binary files a/bundled-core/target/wasm/release/bundle/unit/unit.core and /dev/null differ diff --git a/bundled-core/target/wasm/release/bundle/unit/unit.mi b/bundled-core/target/wasm/release/bundle/unit/unit.mi deleted file mode 100644 index d1aebd5..0000000 Binary files a/bundled-core/target/wasm/release/bundle/unit/unit.mi and /dev/null differ diff --git a/bundled-core/test/README.mbt.md b/bundled-core/test/README.mbt.md new file mode 100644 index 0000000..9d41d9b --- /dev/null +++ b/bundled-core/test/README.mbt.md @@ -0,0 +1,309 @@ +# Test Package Documentation + +This package provides testing utilities and assertion functions for MoonBit programs. It includes functions for comparing values, checking object identity, and creating structured test outputs with snapshot testing capabilities. + +## Basic Test Structure + +MoonBit tests are written using the `test` keyword: + +```moonbit +test "basic test example" { + let result = 2 + 2 + inspect(result, content="4") + + // Test passes if no errors are raised +} +``` + +## Assertion Functions + +### Object Identity Testing + +Test whether two values refer to the same object in memory: + +```moonbit +test "object identity" { + let str1 = "hello" + let _str2 = "hello" + let str3 = str1 + + // Same object reference + @test.same_object(str1, str3) // Passes - same reference + + // Different objects (even if equal values) + + // @test.is_not(str1, str2) + // May or may not pass - different string objects + // depend on how compiler optimization works + // here we interned + + // Arrays and object identity + let arr1 = [1, 2, 3] + let _arr2 = [1, 2, 3] + let arr3 = arr1 + @test.same_object(arr1, arr3) // Passes - same array reference @test.is_not(arr1, arr2) // Passes - different array objects +} +``` + +### Failure Testing + +Explicitly fail tests with custom messages: + +```moonbit +test "conditional failure" { + let value = 10 + + if value < 0 { + @test.fail("Value should not be negative: \{value}") + } + + // Test continues if condition is not met + inspect(value, content="10") +} +``` + +## Test Output and Logging + +Create structured test outputs using the Test type: + +```moonbit +test "test output" { + let t = @test.new("Example Test") + + // Write output to test buffer + t.write("Testing basic functionality: ") + t.writeln("PASS") + + // Write multiple lines + t.writeln("Step 1: Initialize data") + t.writeln("Step 2: Process data") + t.writeln("Step 3: Verify results") + + // The test output is captured for reporting +} +``` + +## Snapshot Testing + +Compare test outputs against saved snapshots: + +```moonbit +test "snapshot testing" { + let t = @test.new("Snapshot Test") + + // Generate some output + t.writeln("Current timestamp: 2024-01-01") + t.writeln("Processing items: [1, 2, 3, 4, 5]") + t.writeln("Result: SUCCESS") + + // Compare against snapshot file + // This will create or update a snapshot file + t.snapshot(filename="test_output") +} +``` + +## Advanced Testing Patterns + +### Testing with Complex Data + +Test functions that work with complex data structures: + +```moonbit +test "complex data testing" { + // Test with arrays + let numbers = [1, 2, 3, 4, 5] + let doubled = numbers.map(fn(x) { x * 2 }) + inspect(doubled, content="[2, 4, 6, 8, 10]") + + // Test with tuples (simpler than custom structs in test examples) + let person_data = ("Alice", 30) + inspect(person_data.0, content="Alice") + inspect(person_data.1, content="30") +} +``` + +### Error Condition Testing + +Test that functions properly handle error conditions: + +```moonbit +test "error handling" { + fn safe_divide(a : Int, b : Int) -> Option[Int] { + if b == 0 { + None + } else { + Some(a / b) + } + } + + // Test normal case + let result = safe_divide(10, 2) + inspect(result, content="Some(5)") + + // Test error case + let error_result = safe_divide(10, 0) + inspect(error_result, content="None") +} +``` + +### Property-Based Testing + +Test properties that should hold for various inputs: + +```moonbit +test "property testing" { + fn is_even(n : Int) -> Bool { + n % 2 == 0 + } + + // Test the property with multiple values + let test_values = [0, 2, 4, 6, 8, 10] + for value in test_values { + if not(is_even(value)) { + @test.fail("Expected \{value} to be even") + } + } + + // Test negative cases + let odd_values = [1, 3, 5, 7, 9] + for value in odd_values { + if is_even(value) { + @test.fail("Expected \{value} to be odd") + } + } +} +``` + +## Test Organization + +### Grouping Related Tests + +Use descriptive test names to group related functionality: + +```moonbit +test "string operations - concatenation" { + let result = "hello" + " " + "world" + inspect(result, content="hello world") +} + +test "string operations - length" { + let text = "MoonBit" + inspect(text.length(), content="7") +} + +test "string operations - substring" { + let text = "Hello, World!" + let sub = text.length() // Just test length instead of substring + inspect(sub, content="13") +} +``` + +### Setup and Teardown Patterns + +Create helper functions for common test setup: + +```moonbit +test "with setup helper" { + fn setup_test_data() -> Array[Int] { + [10, 20, 30, 40, 50] + } + + fn cleanup_test_data(_data : Array[Int]) -> Unit { + // Cleanup logic here + } + + let data = setup_test_data() + + // Perform tests + inspect(data.length(), content="5") + inspect(data[0], content="10") + inspect(data[4], content="50") + + cleanup_test_data(data) +} +``` + +## Testing Best Practices + +### Clear Test Names + +Use descriptive names that explain what is being tested: + +```moonbit +test "user_can_login_with_valid_credentials" { + // Test implementation +} + +test "login_fails_with_invalid_password" { + // Test implementation +} + +test "shopping_cart_calculates_total_correctly" { + // Test implementation +} +``` + +### One Concept Per Test + +Keep tests focused on a single concept: + +```moonbit +// Good - tests one specific behavior +test "array_push_increases_length" { + let arr = Array::new() + let initial_length = arr.length() + + arr.push(42) + + let new_length = arr.length() + inspect(new_length, content="\{initial_length + 1}") +} + +// Good - tests another specific behavior +test "array_push_adds_element_at_end" { + let arr = Array::new() + arr.push(10) + arr.push(20) + + inspect(arr[arr.length() - 1], content="20") +} +``` + +### Use Meaningful Test Data + +Choose test data that makes the test's intent clear: + +```moonbit +test "tax_calculation_for_standard_rate" { + let price = 100 + let tax_rate = 8 // 8% tax as integer percentage + + let calculated_tax = price * tax_rate / 100 + inspect(calculated_tax, content="8") +} +``` + +## Integration with MoonBit Build System + +Tests are automatically discovered and run by the MoonBit build system: + +- Use `moon test` to run all tests +- Use `moon test --update` to update snapshots +- Tests in `*_test.mbt` files are blackbox tests +- Tests in regular `.mbt` files are whitebox tests + +## Common Testing Patterns + +1. **Arrange-Act-Assert**: Set up data, perform operation, verify result +2. **Given-When-Then**: Given some context, when an action occurs, then verify outcome +3. **Red-Green-Refactor**: Write failing test, make it pass, improve code +4. **Test-Driven Development**: Write tests before implementation + +## Performance Considerations + +- Keep tests fast by avoiding expensive operations when possible +- Use setup/teardown functions to share expensive initialization +- Consider using smaller datasets for unit tests +- Save integration tests with large datasets for separate test suites + +The test package provides essential tools for ensuring code quality and correctness in MoonBit applications through comprehensive testing capabilities. diff --git a/bundled-core/test/__snapshot__/test_output b/bundled-core/test/__snapshot__/test_output new file mode 100644 index 0000000..73306ff --- /dev/null +++ b/bundled-core/test/__snapshot__/test_output @@ -0,0 +1,3 @@ +Current timestamp: 2024-01-01 +Processing items: [1, 2, 3, 4, 5] +Result: SUCCESS diff --git a/bundled-core/test/deprecated.mbt b/bundled-core/test/deprecated.mbt index 4b25d46..179023b 100644 --- a/bundled-core/test/deprecated.mbt +++ b/bundled-core/test/deprecated.mbt @@ -13,9 +13,10 @@ // limitations under the License. ///| +#callsite(autofill(loc)) #deprecated("Use built-in `assert_eq` instead") #coverage.skip -pub fn[T : Show + Eq] eq(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { +pub fn[T : Show + Eq] eq(a : T, b : T, loc~ : SourceLoc) -> Unit raise { if a != b { let a = debug_string(a) let b = debug_string(b) @@ -24,10 +25,11 @@ pub fn[T : Show + Eq] eq(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { } ///| +#callsite(autofill(loc)) #deprecated("Use built-in `assert_not_eq` instead") #coverage.skip -pub fn[T : Show + Eq] ne(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { - if not(a != b) { +pub fn[T : Show + Eq] ne(a : T, b : T, loc~ : SourceLoc) -> Unit raise { + if !(a != b) { let a = debug_string(a) let b = debug_string(b) fail("`\{a} != \{b}`", loc~) @@ -35,19 +37,21 @@ pub fn[T : Show + Eq] ne(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { } ///| +#callsite(autofill(loc)) #deprecated("Use built-in `assert_true` instead") #coverage.skip -pub fn is_true(x : Bool, loc~ : SourceLoc = _) -> Unit raise { - if not(x) { +pub fn is_true(x : Bool, loc~ : SourceLoc) -> Unit raise { + if !x { let x = debug_string(x) fail("`\{x}` is not true", loc~) } } ///| +#callsite(autofill(loc)) #deprecated("Use built-in `assert_false` instead") #coverage.skip -pub fn is_false(x : Bool, loc~ : SourceLoc = _) -> Unit raise { +pub fn is_false(x : Bool, loc~ : SourceLoc) -> Unit raise { if x { let x = debug_string(x) fail("`\{x}` is not false", loc~) @@ -59,7 +63,7 @@ pub fn is_false(x : Bool, loc~ : SourceLoc = _) -> Unit raise { pub fn bench( self : T, f : () -> Unit, - count~ : UInt = 10 + count? : UInt = 10, ) -> Unit raise BenchError { ignore(self) let summary = @bench.single_bench(f, count~) diff --git a/bundled-core/test/pkg.generated.mbti b/bundled-core/test/pkg.generated.mbti new file mode 100644 index 0000000..92abd34 --- /dev/null +++ b/bundled-core/test/pkg.generated.mbti @@ -0,0 +1,49 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/test" + +// Values +#deprecated +#callsite(autofill(loc)) +fn[T : Show + Eq] eq(T, T, loc~ : SourceLoc) -> Unit raise + +#callsite(autofill(loc)) +fn[T] fail(String, loc~ : SourceLoc) -> T raise + +#deprecated +#callsite(autofill(loc)) +fn is_false(Bool, loc~ : SourceLoc) -> Unit raise + +#callsite(autofill(loc)) +fn[T : Show] is_not(T, T, loc~ : SourceLoc) -> Unit raise + +#deprecated +#callsite(autofill(loc)) +fn is_true(Bool, loc~ : SourceLoc) -> Unit raise + +#deprecated +#callsite(autofill(loc)) +fn[T : Show + Eq] ne(T, T, loc~ : SourceLoc) -> Unit raise + +fn new(String) -> T + +#callsite(autofill(loc)) +fn[T : Show] same_object(T, T, loc~ : SourceLoc) -> Unit raise + +// Errors + +// Types and methods +pub(all) struct T { + name : String + buffer : StringBuilder +} +#deprecated +fn T::bench(Self, () -> Unit, count? : UInt) -> Unit raise BenchError +#callsite(autofill(args_loc, loc)) +fn T::snapshot(Self, filename~ : String, loc~ : SourceLoc, args_loc~ : ArgsLoc) -> Unit raise SnapshotError +fn T::write(Self, &Show) -> Unit +fn T::writeln(Self, &Show) -> Unit + +// Type aliases + +// Traits + diff --git a/bundled-core/test/test.mbt b/bundled-core/test/test.mbt index 0125b96..1895c3b 100644 --- a/bundled-core/test/test.mbt +++ b/bundled-core/test/test.mbt @@ -19,8 +19,6 @@ fn[T : Show] debug_string(t : T) -> String { buf.to_string() } -///| - ///| /// Assert referential equality of two values. /// @@ -37,8 +35,9 @@ fn[T : Show] debug_string(t : T) -> String { /// @test.same_object(a, a) /// @test.is_not(a, b) /// ``` -pub fn[T : Show] same_object(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { - if not(physical_equal(a, b)) { +#callsite(autofill(loc)) +pub fn[T : Show] same_object(a : T, b : T, loc~ : SourceLoc) -> Unit raise { + if !physical_equal(a, b) { let a = debug_string(a) let b = debug_string(b) fail("`\{a} is \{b}`", loc~) @@ -61,19 +60,21 @@ pub fn[T : Show] same_object(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { /// @test.is_not(a, b) /// @test.same_object(a, a) /// ``` -pub fn[T : Show] is_not(a : T, b : T, loc~ : SourceLoc = _) -> Unit raise { +#callsite(autofill(loc)) +pub fn[T : Show] is_not(a : T, b : T, loc~ : SourceLoc) -> Unit raise { if physical_equal(a, b) { let a = debug_string(a) let b = debug_string(b) - fail("`not(\{a} is \{b})`", loc~) + fail("`!(\{a} is \{b})`", loc~) } } -///| +///| /// Expected to be used in test blocks, don't catch this error. /// /// Produces an error message similar to @builtin.fail. -pub fn[T] fail(msg : String, loc~ : SourceLoc = _) -> T raise { +#callsite(autofill(loc)) +pub fn[T] fail(msg : String, loc~ : SourceLoc) -> T raise { @builtin.fail(msg, loc~) } @@ -85,7 +86,7 @@ pub fn write(self : T, obj : &Show) -> Unit { self.buffer.write_string(obj.to_string()) } -///| +///| /// Write data to snapshot buffer and newline, use `snapshot` to output. /// /// See also `@test.T::write` @@ -98,17 +99,18 @@ pub fn writeln(self : T, obj : &Show) -> Unit { /// Take a snapshot of the current buffer and write to a file. /// /// ```mbt -/// let t = new("test.txt") +/// let t = @test.new("test.txt") /// t.writeln("hello") /// t.snapshot(filename="test.txt") // actual test block end /// ``` /// /// Currently it can only be used once and should be used as the last step. +#callsite(autofill(args_loc, loc)) pub fn snapshot( self : T, filename~ : String, - loc~ : SourceLoc = _, - args_loc~ : ArgsLoc = _ + loc~ : SourceLoc, + args_loc~ : ArgsLoc, ) -> Unit raise SnapshotError { let loc = loc.to_string().escape() let args_loc = args_loc.to_json().escape() diff --git a/bundled-core/test/test.mbti b/bundled-core/test/test.mbti deleted file mode 100644 index a639d69..0000000 --- a/bundled-core/test/test.mbti +++ /dev/null @@ -1,38 +0,0 @@ -package "moonbitlang/core/test" - -// Values -#deprecated -fn[T : Show + Eq] eq(T, T, loc~ : SourceLoc = _) -> Unit raise - -fn[T] fail(String, loc~ : SourceLoc = _) -> T raise - -#deprecated -fn is_false(Bool, loc~ : SourceLoc = _) -> Unit raise - -fn[T : Show] is_not(T, T, loc~ : SourceLoc = _) -> Unit raise - -#deprecated -fn is_true(Bool, loc~ : SourceLoc = _) -> Unit raise - -#deprecated -fn[T : Show + Eq] ne(T, T, loc~ : SourceLoc = _) -> Unit raise - -fn new(String) -> T - -fn[T : Show] same_object(T, T, loc~ : SourceLoc = _) -> Unit raise - -// Types and methods -pub(all) struct T { - name : String - buffer : StringBuilder -} -#deprecated -fn T::bench(Self, () -> Unit, count~ : UInt = ..) -> Unit raise BenchError -fn T::snapshot(Self, filename~ : String, loc~ : SourceLoc = _, args_loc~ : ArgsLoc = _) -> Unit raise SnapshotError -fn T::write(Self, &Show) -> Unit -fn T::writeln(Self, &Show) -> Unit - -// Type aliases - -// Traits - diff --git a/bundled-core/tuple/deprecated.mbt b/bundled-core/tuple/deprecated.mbt index 5a731de..d493917 100644 --- a/bundled-core/tuple/deprecated.mbt +++ b/bundled-core/tuple/deprecated.mbt @@ -53,7 +53,7 @@ pub fn[T, U, V] map_snd(f : (T) -> U, tuple : (V, T)) -> (V, U) { pub fn[T, U, V, W] map_both( f : (T) -> U, g : (V) -> W, - tuple : (T, V) + tuple : (T, V), ) -> (U, W) { (f(tuple.0), g(tuple.1)) } diff --git a/bundled-core/tuple/pkg.generated.mbti b/bundled-core/tuple/pkg.generated.mbti new file mode 100644 index 0000000..f5a98d7 --- /dev/null +++ b/bundled-core/tuple/pkg.generated.mbti @@ -0,0 +1,78 @@ +// Generated using `moon info`, DON'T EDIT IT +package "moonbitlang/core/tuple" + +import( + "moonbitlang/core/quickcheck" +) + +// Values +#deprecated +fn[T, U, V] curry((T, U) -> V) -> (T) -> (U) -> V + +#deprecated +fn[T, U] fst((T, U)) -> T + +#deprecated +fn[T, U, V, W] map_both((T) -> U, (V) -> W, (T, V)) -> (U, W) + +#deprecated +fn[T, U, V] map_fst((T) -> U, (T, V)) -> (U, V) + +#deprecated +fn[T, U, V] map_snd((T) -> U, (V, T)) -> (V, U) + +#deprecated +fn[T, U] pair(T, U) -> (T, U) + +#deprecated +fn[T, U] snd((T, U)) -> U + +#deprecated +fn[T, U] swap((T, U)) -> (U, T) + +#deprecated +fn[T, U, V] uncurry((T) -> (U) -> V) -> (T, U) -> V + +// Errors + +// Types and methods +impl[A : Default, B : Default] Default for (A, B) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B) + +impl[A : Default, B : Default, C : Default] Default for (A, B, C) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C) + +impl[A : Default, B : Default, C : Default, D : Default] Default for (A, B, C, D) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default] Default for (A, B, C, D, E) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default] Default for (A, B, C, D, E, F) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary, F : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E, F) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default] Default for (A, B, C, D, E, F, G) +impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary, F : @quickcheck.Arbitrary, G : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E, F, G) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default] Default for (A, B, C, D, E, F, G, H) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default] Default for (A, B, C, D, E, F, G, H, I) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default] Default for (A, B, C, D, E, F, G, H, I, J) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default] Default for (A, B, C, D, E, F, G, H, I, J, K) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default, L : Default] Default for (A, B, C, D, E, F, G, H, I, J, K, L) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default, L : Default, M : Default] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default, L : Default, M : Default, N : Default] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default, L : Default, M : Default, N : Default, O : Default] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) + +impl[A : Default, B : Default, C : Default, D : Default, E : Default, F : Default, G : Default, H : Default, I : Default, J : Default, K : Default, L : Default, M : Default, N : Default, O : Default, P : Default] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) + +// Type aliases + +// Traits + diff --git a/bundled-core/tuple/tuple.mbti b/bundled-core/tuple/tuple.mbti deleted file mode 100644 index f1129b6..0000000 --- a/bundled-core/tuple/tuple.mbti +++ /dev/null @@ -1,51 +0,0 @@ -package "moonbitlang/core/tuple" - -import( - "moonbitlang/core/quickcheck" -) - -// Values -#deprecated -fn[T, U, V] curry((T, U) -> V) -> (T) -> (U) -> V - -#deprecated -fn[T, U] fst((T, U)) -> T - -#deprecated -fn[T, U, V, W] map_both((T) -> U, (V) -> W, (T, V)) -> (U, W) - -#deprecated -fn[T, U, V] map_fst((T) -> U, (T, V)) -> (U, V) - -#deprecated -fn[T, U, V] map_snd((T) -> U, (V, T)) -> (V, U) - -#deprecated -fn[T, U] pair(T, U) -> (T, U) - -#deprecated -fn[T, U] snd((T, U)) -> U - -#deprecated -fn[T, U] swap((T, U)) -> (U, T) - -#deprecated -fn[T, U, V] uncurry((T) -> (U) -> V) -> (T, U) -> V - -// Types and methods -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B) - -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C) - -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D) - -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E) - -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary, F : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E, F) - -impl[A : @quickcheck.Arbitrary, B : @quickcheck.Arbitrary, C : @quickcheck.Arbitrary, D : @quickcheck.Arbitrary, E : @quickcheck.Arbitrary, F : @quickcheck.Arbitrary, G : @quickcheck.Arbitrary] @quickcheck.Arbitrary for (A, B, C, D, E, F, G) - -// Type aliases - -// Traits - diff --git a/bundled-core/tuple/tuple_arbitrary.mbt b/bundled-core/tuple/tuple_arbitrary.mbt index 51c9310..1c49f5a 100644 --- a/bundled-core/tuple/tuple_arbitrary.mbt +++ b/bundled-core/tuple/tuple_arbitrary.mbt @@ -18,7 +18,7 @@ traitalias @quickcheck.Arbitrary ///| pub impl[A : Arbitrary, B : Arbitrary] Arbitrary for (A, B) with arbitrary( size, - r0 + r0, ) { let r1 = r0.split() (Arbitrary::arbitrary(size, r0), Arbitrary::arbitrary(size, r1)) @@ -27,7 +27,7 @@ pub impl[A : Arbitrary, B : Arbitrary] Arbitrary for (A, B) with arbitrary( ///| pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary] Arbitrary for (A, B, C) with arbitrary( size, - r0 + r0, ) { let r1 = r0.split() let (v1, v2) = Arbitrary::arbitrary(size, r1) @@ -47,42 +47,42 @@ pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary, D : Arbitrary] Arbitrary f } ///| -pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary, D : Arbitrary, E : Arbitrary] Arbitrary for ( - A, - B, - C, - D, - E, -) with arbitrary(size, r0) { +pub impl[ + A : Arbitrary, + B : Arbitrary, + C : Arbitrary, + D : Arbitrary, + E : Arbitrary, +] Arbitrary for (A, B, C, D, E) with arbitrary(size, r0) { let r1 = r0.split() let (v1, v2, v3, v4) = Arbitrary::arbitrary(size, r1) (Arbitrary::arbitrary(size, r0), v1, v2, v3, v4) } ///| -pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary, D : Arbitrary, E : Arbitrary, F : Arbitrary] Arbitrary for ( - A, - B, - C, - D, - E, - F, -) with arbitrary(size, r0) { +pub impl[ + A : Arbitrary, + B : Arbitrary, + C : Arbitrary, + D : Arbitrary, + E : Arbitrary, + F : Arbitrary, +] Arbitrary for (A, B, C, D, E, F) with arbitrary(size, r0) { let r1 = r0.split() let (v1, v2, v3, v4, v5) = Arbitrary::arbitrary(size, r1) (Arbitrary::arbitrary(size, r0), v1, v2, v3, v4, v5) } ///| -pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary, D : Arbitrary, E : Arbitrary, F : Arbitrary, G : Arbitrary] Arbitrary for ( - A, - B, - C, - D, - E, - F, - G, -) with arbitrary(size, r0) { +pub impl[ + A : Arbitrary, + B : Arbitrary, + C : Arbitrary, + D : Arbitrary, + E : Arbitrary, + F : Arbitrary, + G : Arbitrary, +] Arbitrary for (A, B, C, D, E, F, G) with arbitrary(size, r0) { let r1 = r0.split() let (v1, v2, v3, v4, v5, v6) = Arbitrary::arbitrary(size, r1) (Arbitrary::arbitrary(size, r0), v1, v2, v3, v4, v5, v6) diff --git a/bundled-core/tuple/tuple_default.mbt b/bundled-core/tuple/tuple_default.mbt new file mode 100644 index 0000000..f8b265f --- /dev/null +++ b/bundled-core/tuple/tuple_default.mbt @@ -0,0 +1,374 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +pub impl[A : Default, B : Default] Default for (A, B) with default() { + (Default::default(), Default::default()) +} + +///| +pub impl[A : Default, B : Default, C : Default] Default for (A, B, C) with default() { + (Default::default(), Default::default(), Default::default()) +} + +///| +pub impl[A : Default, B : Default, C : Default, D : Default] Default for ( + A, + B, + C, + D, +) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[A : Default, B : Default, C : Default, D : Default, E : Default] Default for ( + A, + B, + C, + D, + E, +) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, +] Default for (A, B, C, D, E, F) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, +] Default for (A, B, C, D, E, F, G) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, +] Default for (A, B, C, D, E, F, G, H) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, +] Default for (A, B, C, D, E, F, G, H, I) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, +] Default for (A, B, C, D, E, F, G, H, I, J) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, + L : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K, L) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, + L : Default, + M : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, + L : Default, + M : Default, + N : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, + L : Default, + M : Default, + N : Default, + O : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} + +///| +pub impl[ + A : Default, + B : Default, + C : Default, + D : Default, + E : Default, + F : Default, + G : Default, + H : Default, + I : Default, + J : Default, + K : Default, + L : Default, + M : Default, + N : Default, + O : Default, + P : Default, +] Default for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) with default() { + ( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) +} diff --git a/bundled-core/tuple/tuple_default_test.mbt b/bundled-core/tuple/tuple_default_test.mbt new file mode 100644 index 0000000..e45bac6 --- /dev/null +++ b/bundled-core/tuple/tuple_default_test.mbt @@ -0,0 +1,48 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "tuple2 default" { + let t : (Int, Bool) = Default::default() + inspect(t, content="(0, false)") +} + +///| +test "tuple5 default" { + let t : (Int, Bool, Int, Int, Int) = Default::default() + inspect(t, content="(0, false, 0, 0, 0)") +} + +///| +test "tuple16 default" { + let t : ( + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + Int, + ) = Default::default() + inspect(t, content="(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)") +} diff --git a/bundled-core/tuple/tuple_test.mbt b/bundled-core/tuple/tuple_test.mbt index 3dcd19b..b535e98 100644 --- a/bundled-core/tuple/tuple_test.mbt +++ b/bundled-core/tuple/tuple_test.mbt @@ -12,63 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -///| -// test "pair" { -// let tuple = @tuple.pair(1, 2) -// assert_eq(tuple, (1, 2)) -// } - -///| -// test "fst" { -// let tuple = (1, 2) -// assert_eq(@tuple.fst(tuple), 1) -// } - -///| -// test "snd" { -// let tuple = (1, 2) -// assert_eq(@tuple.snd(tuple), 2) -// } - -///| -// test "swap" { -// let tuple = (1, 2) -// let swapped = @tuple.swap(tuple) -// let swapped_2 = @tuple.swap(swapped) -// assert_eq(swapped, (2, 1)) -// assert_eq(swapped_2, tuple) -// } - -///| -// test "curry" { -// let add = (x : Int, y : Int) => { x + y } -// let curried_add = @tuple.curry(add) -// assert_eq(curried_add(1)(2), add(1, 2)) -// } - -///| -// test "uncurry" { -// let add = (x : Int) => { fn(y : Int) -> Int { x + y } } -// let uncurried_add = @tuple.uncurry(add) -// assert_eq(uncurried_add(1, 2), add(1)(2)) -// } - -///| -// test "id_1" { -// let add = (x : Int, y : Int) => { x + y } -// let curried_add = @tuple.curry(add) -// let uncurried_add = @tuple.uncurry(curried_add) -// assert_eq(uncurried_add(1, 2), add(1, 2)) -// } - -///| -// test "id_2" { -// let add = (x : Int) => { fn(y : Int) -> Int { x + y } } -// let uncurried_add = @tuple.uncurry(add) -// let curried_add = @tuple.curry(uncurried_add) -// assert_eq(curried_add(1)(2), add(1)(2)) -// } - ///| test "eq" { inspect((1, 2) == (1, 2), content="true") @@ -196,9 +139,9 @@ test "show" { inspect(tuple4, content="(1, 2, 3, \"hello\")") inspect( tuple5, - content= + content=( #|([1], "2", 3, [4], 5) - , + ), ) } @@ -211,21 +154,21 @@ test "to_string" { inspect(tuple2, content="(1, 2)") inspect( tuple3, - content= + content=( #|("a", "b", "c") - , + ), ) inspect( tuple4, - content= + content=( #|(1, 2, 3, "hello") - , + ), ) inspect( tuple5, - content= + content=( #|([1], "2", 3, [4], 5) - , + ), ) inspect((1, 2, 3, 4, 5, 6), content="(1, 2, 3, 4, 5, 6)") inspect((1, 2, 3, 4, 5, 6, 7), content="(1, 2, 3, 4, 5, 6, 7)") @@ -236,45 +179,45 @@ test "arbitrary" { let t : Array[(Int, String)] = @quickcheck.samples(5) inspect( t, - content= + content=( #|[(0, ""), (0, ""), (0, ""), (-2, "=l"), (2, "m")] - , + ), ) let t : Array[(Int, String, UInt)] = @quickcheck.samples(5) inspect( t, - content= + content=( #|[(0, "", 0), (0, "", 0), (0, "", 1), (-2, "@>", 0), (2, "\u{1a}", 0)] - , + ), ) let t : Array[(Int, String, UInt, Byte)] = @quickcheck.samples(5) inspect( t, - content= + content=( #|[(0, "", 0, b'\x2A'), (0, "", 0, b'\x77'), (0, "", 1, b'\x0D'), (-2, "@>", 1, b'\x4A'), (2, "\u{1a}", 0, b'\xE5')] - , + ), ) let t : Array[(Int, String, UInt, Byte, Char)] = @quickcheck.samples(5) inspect( t, - content= + content=( #|[(0, "", 0, b'\x89', '%'), (0, "", 0, b'\x3B', '\u{16}'), (0, "", 1, b'\x08', 'l'), (-2, "@>", 1, b'\x23', 't'), (2, "\u{1a}", 0, b'\xB7', 'q')] - , + ), ) let t : Array[(Int, String, UInt, Byte, Char, Bool)] = @quickcheck.samples(5) inspect( t, - content= + content=( #|[(0, "", 0, b'\x89', '3', true), (0, "", 0, b'\x3B', 'x', true), (0, "", 1, b'\x08', 'P', true), (-2, "@>", 1, b'\x23', 'd', false), (2, "\u{1a}", 0, b'\xB7', '\u{1d}', true)] - , + ), ) let t : Array[(Int, String, UInt, Byte, Char, Bool, Unit)] = @quickcheck.samples( 5, ) inspect( t, - content= + content=( #|[(0, "", 0, b'\x89', '3', false, ()), (0, "", 0, b'\x3B', 'x', true, ()), (0, "", 1, b'\x08', 'P', true, ()), (-2, "@>", 1, b'\x23', 'd', true, ()), (2, "\u{1a}", 0, b'\xB7', '\u{1d}', true, ())] - , + ), ) } diff --git a/bundled-core/uint/README.mbt.md b/bundled-core/uint/README.mbt.md index 9e91bac..978624c 100644 --- a/bundled-core/uint/README.mbt.md +++ b/bundled-core/uint/README.mbt.md @@ -29,18 +29,18 @@ test "uint byte conversion" { let be_bytes = num.to_be_bytes() inspect( be_bytes, - content= + content=( #|b"\x00\x00\x01\x02" - , + ), ) // Little-endian bytes (least significant byte first) let le_bytes = num.to_le_bytes() inspect( le_bytes, - content= + content=( #|b"\x02\x01\x00\x00" - , + ), ) } ``` @@ -68,15 +68,15 @@ test "uint methods" { inspect(num.to_int64(), content="1000") inspect( num.to_be_bytes(), - content= + content=( #|b"\x00\x00\x03\xe8" - , + ), ) inspect( num.to_le_bytes(), - content= + content=( #|b"\xe8\x03\x00\x00" - , + ), ) } ``` diff --git a/bundled-core/uint/uint.mbti b/bundled-core/uint/pkg.generated.mbti similarity index 83% rename from bundled-core/uint/uint.mbti rename to bundled-core/uint/pkg.generated.mbti index 21b67c0..bf88dec 100644 --- a/bundled-core/uint/uint.mbti +++ b/bundled-core/uint/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/uint" // Values @@ -7,6 +8,8 @@ let max_value : UInt let min_value : UInt +// Errors + // Types and methods fn UInt::to_be_bytes(UInt) -> Bytes fn UInt::to_int64(UInt) -> Int64 diff --git a/bundled-core/uint/uint.mbt b/bundled-core/uint/uint.mbt index d30f8f8..2d6a8b2 100644 --- a/bundled-core/uint/uint.mbt +++ b/bundled-core/uint/uint.mbt @@ -34,7 +34,8 @@ pub fn default() -> UInt { 0 } -///| Converts the UInt to a Bytes in big-endian byte order. +///| +/// Converts the UInt to a Bytes in big-endian byte order. pub fn to_be_bytes(self : UInt) -> Bytes { [ (self >> 24).to_byte(), @@ -44,7 +45,8 @@ pub fn to_be_bytes(self : UInt) -> Bytes { ] } -///| Converts the UInt to a Bytes in little-endian byte order. +///| +/// Converts the UInt to a Bytes in little-endian byte order. pub fn to_le_bytes(self : UInt) -> Bytes { [ self.to_byte(), diff --git a/bundled-core/uint16/uint16.mbti b/bundled-core/uint16/pkg.generated.mbti similarity index 90% rename from bundled-core/uint16/uint16.mbti rename to bundled-core/uint16/pkg.generated.mbti index 934aab9..4d851a4 100644 --- a/bundled-core/uint16/uint16.mbti +++ b/bundled-core/uint16/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/uint16" // Values @@ -5,6 +6,8 @@ let max_value : UInt16 let min_value : UInt16 +// Errors + // Types and methods fn UInt16::to_uint(UInt16) -> UInt fn UInt16::to_uint64(UInt16) -> UInt64 diff --git a/bundled-core/uint16/uint16.mbt b/bundled-core/uint16/uint16.mbt index 745fc62..4adba14 100644 --- a/bundled-core/uint16/uint16.mbt +++ b/bundled-core/uint16/uint16.mbt @@ -19,32 +19,32 @@ pub let max_value : UInt16 = 65535 pub let min_value : UInt16 = 0 ///| -pub impl Add for UInt16 with op_add(self : UInt16, that : UInt16) -> UInt16 { +pub impl Add for UInt16 with add(self : UInt16, that : UInt16) -> UInt16 { (self.to_int() + that.to_int()).to_uint16() } ///| -pub impl Sub for UInt16 with op_sub(self : UInt16, that : UInt16) -> UInt16 { +pub impl Sub for UInt16 with sub(self : UInt16, that : UInt16) -> UInt16 { (self.to_int() - that.to_int()).to_uint16() } ///| -pub impl Mul for UInt16 with op_mul(self : UInt16, that : UInt16) -> UInt16 { +pub impl Mul for UInt16 with mul(self : UInt16, that : UInt16) -> UInt16 { (self.to_int() * that.to_int()).to_uint16() } ///| -pub impl Div for UInt16 with op_div(self : UInt16, that : UInt16) -> UInt16 { +pub impl Div for UInt16 with div(self : UInt16, that : UInt16) -> UInt16 { (self.to_int() / that.to_int()).to_uint16() } ///| -pub impl Mod for UInt16 with op_mod(self : UInt16, that : UInt16) -> UInt16 { +pub impl Mod for UInt16 with mod(self : UInt16, that : UInt16) -> UInt16 { (self.to_int() % that.to_int()).to_uint16() } ///| -pub impl Eq for UInt16 with op_equal(self, that) { +pub impl Eq for UInt16 with equal(self, that) { self.to_int() == that.to_int() } @@ -64,12 +64,12 @@ pub impl Hash for UInt16 with hash_combine(self, hasher) { } ///| -pub impl Shl for UInt16 with op_shl(self : UInt16, that : Int) -> UInt16 { +pub impl Shl for UInt16 with shl(self : UInt16, that : Int) -> UInt16 { (self.to_int() << that).to_uint16() } ///| -pub impl Shr for UInt16 with op_shr(self : UInt16, that : Int) -> UInt16 { +pub impl Shr for UInt16 with shr(self : UInt16, that : Int) -> UInt16 { (self.to_int() >> that).to_uint16() } diff --git a/bundled-core/uint16/uint16_test.mbt b/bundled-core/uint16/uint16_test.mbt index e9c5002..1ca3249 100644 --- a/bundled-core/uint16/uint16_test.mbt +++ b/bundled-core/uint16/uint16_test.mbt @@ -358,3 +358,16 @@ test "UInt16::to_uint64" { inspect(UInt16::to_uint64(32768), content="32768") inspect(UInt16::to_uint64(12345), content="12345") } + +///| +test "UInt16::hash_combine" { + let hasher = @builtin.Hasher::new() + let value : UInt16 = 42 + value.hash_combine(hasher) + inspect(hasher.finalize(), content="1161967057") +} + +///| +test "UInt16::default" { + inspect(UInt16::default(), content="0") +} diff --git a/bundled-core/uint64/README.mbt.md b/bundled-core/uint64/README.mbt.md index 43bd96d..e28c732 100644 --- a/bundled-core/uint64/README.mbt.md +++ b/bundled-core/uint64/README.mbt.md @@ -103,18 +103,18 @@ test "UInt64 byte conversion" { let be_bytes = (0x123456789ABCDEF0UL).to_be_bytes() inspect( be_bytes, - content= + content=( #|b"\x12\x34\x56\x78\x9a\xbc\xde\xf0" - , + ), ) // Convert to bytes in little-endian order (least significant byte first) let le_bytes = (0x123456789ABCDEF0UL).to_le_bytes() inspect( le_bytes, - content= + content=( #|b"\xf0\xde\xbc\x9a\x78\x56\x34\x12" - , + ), ) } ``` @@ -194,9 +194,9 @@ test "UInt64 hexadecimal literals" { let bytes = value.to_be_bytes() inspect( bytes, - content= + content=( #|b"\x00\x00\x00\x00\xde\xad\xbe\xef" - , + ), ) } ``` diff --git a/bundled-core/uint64/uint64.mbti b/bundled-core/uint64/pkg.generated.mbti similarity index 79% rename from bundled-core/uint64/uint64.mbti rename to bundled-core/uint64/pkg.generated.mbti index f3bffbf..580993d 100644 --- a/bundled-core/uint64/uint64.mbti +++ b/bundled-core/uint64/pkg.generated.mbti @@ -1,3 +1,4 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/uint64" // Values @@ -5,6 +6,8 @@ let max_value : UInt64 let min_value : UInt64 +// Errors + // Types and methods fn UInt64::to_be_bytes(UInt64) -> Bytes fn UInt64::to_le_bytes(UInt64) -> Bytes diff --git a/bundled-core/uint64/uint64.mbt b/bundled-core/uint64/uint64.mbt index 8048784..4422e17 100644 --- a/bundled-core/uint64/uint64.mbt +++ b/bundled-core/uint64/uint64.mbt @@ -18,7 +18,8 @@ pub let min_value : UInt64 = 0UL ///| pub let max_value : UInt64 = 18446744073709551615UL -///| Converts the UInt64 to a Bytes in big-endian byte order. +///| +/// Converts the UInt64 to a Bytes in big-endian byte order. pub fn to_be_bytes(self : UInt64) -> Bytes { [ (self >> 56).to_byte(), @@ -32,7 +33,8 @@ pub fn to_be_bytes(self : UInt64) -> Bytes { ] } -///| Converts the UInt64 to a Bytes in little-endian byte order. +///| +/// Converts the UInt64 to a Bytes in little-endian byte order. pub fn to_le_bytes(self : UInt64) -> Bytes { [ self.to_byte(), @@ -50,21 +52,21 @@ pub fn to_le_bytes(self : UInt64) -> Bytes { test "to_be_bytes" { inspect( max_value.to_be_bytes(), - content= + content=( #|b"\xff\xff\xff\xff\xff\xff\xff\xff" - , + ), ) inspect( min_value.to_be_bytes(), - content= + content=( #|b"\x00\x00\x00\x00\x00\x00\x00\x00" - , + ), ) inspect( 0x123456789ABCDEF0UL.to_be_bytes(), - content= + content=( #|b"\x12\x34\x56\x78\x9a\xbc\xde\xf0" - , + ), ) } @@ -72,20 +74,20 @@ test "to_be_bytes" { test "to_le_bytes" { inspect( max_value.to_le_bytes(), - content= + content=( #|b"\xff\xff\xff\xff\xff\xff\xff\xff" - , + ), ) inspect( min_value.to_le_bytes(), - content= + content=( #|b"\x00\x00\x00\x00\x00\x00\x00\x00" - , + ), ) inspect( 0x123456789ABCDEF0UL.to_le_bytes(), - content= + content=( #|b"\xf0\xde\xbc\x9a\x78\x56\x34\x12" - , + ), ) } diff --git a/bundled-core/unit/README.mbt.md b/bundled-core/unit/README.mbt.md index f618d89..e4e5ba7 100644 --- a/bundled-core/unit/README.mbt.md +++ b/bundled-core/unit/README.mbt.md @@ -1,52 +1,182 @@ # `unit` -This package provides functionality for working with the singleton type `Unit`, which has only one value `()`. `Unit` is commonly used to represent a computation that has side effects but no meaningful return value. +The `unit` package provides functionality for working with the singleton type `Unit`, which represents computations that produce side effects but return no meaningful value. This is a fundamental type in functional programming for operations like I/O, logging, and state modifications. -## Unit Value and Default Constructor +## Understanding Unit Type -The unit type `Unit` has a single value `()` which can also be obtained via the `default()` function: +The `Unit` type has exactly one value: `()`. This might seem trivial, but it serves important purposes in type systems: + +- **Side Effect Indication**: Functions returning `Unit` signal they're called for side effects +- **Placeholder Type**: Used when a type parameter is needed but no meaningful value exists +- **Functional Programming**: Represents "no useful return value" without using `null` or exceptions +- **Interface Consistency**: Maintains uniform function signatures in generic contexts + +## Unit Value Creation + +The unit value can be created in multiple ways: ```moonbit test "unit construction" { + // Direct literal syntax let u1 = () + + // Via default constructor let u2 = @unit.default() - // Any two unit values are equal + fn println(_ : String) {} + // All unit values are identical inspect(u1 == u2, content="true") + + // Common pattern: functions that return unit + fn log_message(msg : String) -> Unit { + // In real code, this would write to a log + println(msg) + () // Explicit unit return + } + + let result = log_message("Hello, world!") + inspect(result, content="()") } ``` -## String Representation +## Working with Side-Effect Functions -Unit values can be converted to strings using either the standalone function or method: +Functions that return `Unit` are typically called for their side effects: + +```moonbit +test "side effect patterns" { + let numbers = [1, 2, 3, 4, 5] + fn println(_ : Int) {} + // Processing for side effects (printing, logging, etc.) + let processing_result = numbers.fold(init=(), fn(_acc, n) { + // Simulated side effect + if n % 2 == 0 { + // Would print or log in real code + println(n) + } + () // Return unit to continue fold + }) + inspect(processing_result, content="()") + + // Using each for side effects (more idiomatic) + numbers.each(fn(n) { + if n % 2 == 0 { + println(n) + } + }) +} +``` + +## String Representation and Debugging + +Unit values have a standard string representation for debugging: ```moonbit test "unit string conversion" { let u = () - // Both ways produce the same result inspect(u.to_string(), content="()") + + // Useful for debugging function results + fn perform_operation() -> Unit { + // Some side effect operation + () + } + + let result = perform_operation() + let debug_msg = "Operation completed: \{result}" + inspect(debug_msg, content="Operation completed: ()") +} +``` + +## Generic Programming with Unit + +Unit is particularly useful in generic contexts where you need to represent "no meaningful value": + +```moonbit +test "generic unit usage" { + // Simulating a function that processes data and optionally returns a result + let items = [1, 2, 3, 4, 5] + + // Process for side effects only + items.each(fn(x) { + // Side effect: processing each item + let processed = x * 2 + assert_true(processed > 0) // Simulated processing validation + }) + + // Unit represents successful completion without meaningful return value + let completion_status = () + inspect(completion_status, content="()") + + // Unit is useful in Result types for operations that succeed but return nothing + let operation_result : Result[Unit, String] = Ok(()) + inspect(operation_result, content="Ok(())") } ``` ## Built-in Trait Implementations -Unit implements several useful traits out of the box: +Unit implements essential traits for seamless integration with MoonBit's type system: ```moonbit test "unit trait implementations" { - // Compare - all unit values compare equal let u1 = () let u2 = () + + // Equality: all unit values are equal + inspect(u1 == u2, content="true") + + // Comparison: all unit values compare as equal inspect(u1.compare(u2), content="0") - - // Hash - all unit values hash to the same value + + // Hashing: consistent hash values let h1 = u1.hash() let h2 = u2.hash() inspect(h1 == h2, content="true") - - // Default - provides the unit value + + // Default instance let u3 = Unit::default() inspect(u3 == u1, content="true") } ``` -As we can see, while simple, `Unit` provides all the essential functionality needed for a proper type in the MoonBit type system. +## Practical Use Cases + +### Result Accumulation + +```moonbit +test "result accumulation" { + // Accumulating side effects without meaningful return values + let operations = [ + fn() { () }, // Operation 1 + fn() { () }, // Operation 2 + fn() { () } // Operation 3 + ] + + let final_result = operations.fold(init=(), fn(acc, operation) { + operation() // Execute the operation + acc // Accumulate unit values + }) + inspect(final_result, content="()") +} +``` + +### Builder Pattern Termination + +```moonbit +test "builder pattern" { + // Simulating a builder pattern where build() returns Unit + let settings = ["debug=true", "timeout=30"] + + // Build operation returns Unit after applying configuration + fn apply_config(config_list : Array[String]) -> Unit { + // In real code: apply configuration settings + let _has_settings = config_list.length() > 0 + () // Unit indicates successful completion + } + + let result = apply_config(settings) + inspect(result, content="()") +} +``` + +The `Unit` type provides essential functionality for representing "no meaningful return value" in a type-safe way, enabling clean functional programming patterns and consistent interfaces across MoonBit code. diff --git a/bundled-core/unit/unit.mbti b/bundled-core/unit/pkg.generated.mbti similarity index 78% rename from bundled-core/unit/unit.mbti rename to bundled-core/unit/pkg.generated.mbti index 0b1d7b8..a295665 100644 --- a/bundled-core/unit/unit.mbti +++ b/bundled-core/unit/pkg.generated.mbti @@ -1,8 +1,11 @@ +// Generated using `moon info`, DON'T EDIT IT package "moonbitlang/core/unit" // Values fn default() -> Unit +// Errors + // Types and methods fn Unit::to_string(Unit) -> String impl Compare for Unit diff --git a/bundled-core/unit/unit.mbt b/bundled-core/unit/unit.mbt index d5b241e..530bf24 100644 --- a/bundled-core/unit/unit.mbt +++ b/bundled-core/unit/unit.mbt @@ -13,20 +13,17 @@ // limitations under the License. ///| -pub fn to_string(self : Unit) -> String { - let _ = self +pub fn Unit::to_string(_ : Self) -> String { "()" } ///| -pub impl Hash for Unit with hash(self) { - let _ = self +pub impl Hash for Unit with hash(_) -> Int { 0 } ///| -pub impl Hash for Unit with hash_combine(self, hasher) -> Unit { - let _ = self +pub impl Hash for Unit with hash_combine(_, hasher) -> Unit { hasher.combine_unit() } @@ -42,6 +39,6 @@ pub fn default() -> Unit { } ///| -pub impl Compare for Unit with compare(_self, _other) { +pub impl Compare for Unit with compare(_, _) { 0 } diff --git a/bundled-core/unit/unit_test.mbt b/bundled-core/unit/unit_test.mbt index d965cbb..f3b92c8 100644 --- a/bundled-core/unit/unit_test.mbt +++ b/bundled-core/unit/unit_test.mbt @@ -30,20 +30,20 @@ test { test { let unit = () assert_eq(@unit.default(), unit) - assert_eq(unit.to_string(), "()") + inspect(unit.to_string(), content="()") } ///| test { let unit1 = () let unit2 = () - assert_eq(unit1.compare(unit2), 0) + inspect(unit1.compare(unit2), content="0") } ///| test { let unit = () - assert_eq(unit.hash(), 0) + inspect(unit.hash(), content="0") } ///| diff --git a/core b/core index 1441211..e973850 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 1441211005ee449d1c32945298622fa1e2587d5d +Subproject commit e973850cc8fcd37aa22f251b220b4ff18dc49077 diff --git a/moonc_wasm/src/moonc/moonc.js.new b/moonc_wasm/src/moonc/moonc.js.new new file mode 100644 index 0000000..7fb923b --- /dev/null +++ b/moonc_wasm/src/moonc/moonc.js.new @@ -0,0 +1,123 @@ +(function(a){typeof +globalThis!=="object"&&(this?b():(a.defineProperty(a.prototype,"_T_",{configurable:true,get:b}),_T_));function +b(){var +b=this||self;b.globalThis=b;delete +a.prototype._T_}}(Object)); +(v=>async a=>{"use strict";const{link:i,src:Q,generated:E}=a,e=globalThis?.process?.versions?.node,M={cos:Math.cos,sin:Math.sin,tan:Math.tan,acos:Math.acos,asin:Math.asin,atan:Math.atan,cosh:Math.cosh,sinh:Math.sinh,tanh:Math.tanh,acosh:Math.acosh,asinh:Math.asinh,atanh:Math.atanh,cbrt:Math.cbrt,exp:Math.exp,expm1:Math.expm1,log:Math.log,log1p:Math.log1p,log2:Math.log2,log10:Math.log10,atan2:Math.atan2,hypot:Math.hypot,pow:Math.pow,fmod:(a,b)=>a%b},u=[Float32Array,Float64Array,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Int32Array,Int32Array,Int32Array,Float32Array,Float64Array,Uint8Array,Uint16Array,Uint8ClampedArray],f=e&&require("node:fs"),b=f?.constants,N=f?[b.O_RDONLY,b.O_WRONLY,b.O_RDWR,b.O_APPEND,b.O_CREAT,b.O_TRUNC,b.O_EXCL,b.O_NONBLOCK,b.O_NOCTTY,b.O_DSYNC,b.O_SYNC]:[];var +c={map:new +WeakMap(),set:new +Set(),finalization:new +FinalizationRegistry(a=>c.set.delete(a))};function +P(a){const +b=new +WeakRef(a);c.map.set(a,b);c.set.add(b);c.finalization.register(a,b,a)}function +T(a){const +b=c.map.get(a);if(b){c.map.delete(a);c.set.delete(b);c.finalization.unregister(a)}}function +B(){return[...c.set].map(a=>a.deref()).filter(a=>a)}var +t;function +L(a){return WebAssembly?.Suspending?new +WebAssembly.Suspending(a):a}function +q(a){return WebAssembly?.promising&&a?WebAssembly.promising(a):a}const +l=new +TextDecoder("utf-8",{ignoreBOM:1}),C=new +TextEncoder();function +F(a,b){b=Math.imul(b,0xcc9e2d51|0);b=b<<15|b>>>17;b=Math.imul(b,0x1b873593);a^=b;a=a<<13|a>>>19;return(a+(a<<2)|0)+(0xe6546b64|0)|0}function +G(a,b){for(var +c=0;ca,from_bool:a=>!!a,get:(a,b)=>a[b],set:(a,b,c)=>a[b]=c,delete:(a,b)=>delete +a[b],instanceof:(a,b)=>a +instanceof +b,typeof:a=>typeof +a,equals:(a,b)=>a==b,strict_equals:(a,b)=>a===b,fun_call:(a,b,c)=>a.apply(b,c),meth_call:(a,b,c)=>a[b].apply(a,c),new_array:a=>new +Array(a),new_obj:()=>({}),new:(a,b)=>new +a(...b),global_this:globalThis,iter_props:(a,b)=>{for(var +c +in +a)if(Object.hasOwn(a,c))b(c)},array_length:a=>a.length,array_get:(a,b)=>a[b],array_set:(a,b,c)=>a[b]=c,read_string:a=>l.decode(new +Uint8Array(h,0,a)),read_string_stream:(a,b)=>l.decode(new +Uint8Array(h,0,a),{stream:b}),append_string:(a,b)=>a+b,write_string:a=>{var +c=0,b=a.length;for(;;){const{read:d,written:e}=C.encodeInto(a.slice(c),O);b-=d;if(!b)return e;z(e);c+=d}},ta_create:(a,b)=>new +u[a](b),ta_normalize:a=>a +instanceof +Uint32Array?new +Int32Array(a.buffer,a.byteOffset,a.length):a,ta_kind:b=>u.findIndex(a=>b +instanceof +a),ta_length:a=>a.length,ta_get_f64:(a,b)=>a[b],ta_get_f32:(a,b)=>a[b],ta_get_i32:(a,b)=>a[b],ta_get_i16:(a,b)=>a[b],ta_get_ui16:(a,b)=>a[b],ta_get_i8:(a,b)=>a[b],ta_get_ui8:(a,b)=>a[b],ta_get16_ui8:(a,b)=>a[b]|a[b+1]<<8,ta_get32_ui8:(a,b)=>a[b]|a[b+1]<<8|a[b+2]<<16|a[b+3]<<24,ta_set_f64:(a,b,c)=>a[b]=c,ta_set_f32:(a,b,c)=>a[b]=c,ta_set_i32:(a,b,c)=>a[b]=c,ta_set_i16:(a,b,c)=>a[b]=c,ta_set_ui16:(a,b,c)=>a[b]=c,ta_set_i8:(a,b,c)=>a[b]=c,ta_set_ui8:(a,b,c)=>a[b]=c,ta_set16_ui8:(a,b,c)=>{a[b]=c;a[b+1]=c>>8},ta_set32_ui8:(a,b,c)=>{a[b]=c;a[b+1]=c>>8;a[b+2]=c>>16;a[b+3]=c>>24},ta_fill:(a,b)=>a.fill(b),ta_blit:(a,b)=>b.set(a),ta_subarray:(a,b,c)=>a.subarray(b,c),ta_set:(a,b,c)=>a.set(b,c),ta_new:a=>new +Uint8Array(a),ta_copy:(a,b,c,d)=>a.copyWithin(b,c,d),ta_bytes:a=>new +Uint8Array(a.buffer,a.byteOffset,a.length*a.BYTES_PER_ELEMENT),ta_blit_from_string:(a,b,c,d,e)=>{for(let +f=0;f{for(let +f=0;ffunction(...a){if(a.length===0)a=[undefined];return d(b,a.length,a,1)},wrap_callback_args:b=>function(...a){return d(b,1,[a],0)},wrap_callback_strict:(c,b)=>function(...a){a.length=c;return d(b,c,a,0)},wrap_callback_unsafe:b=>function(...a){return d(b,a.length,a,2)},wrap_meth_callback:b=>function(...a){a.unshift(this);return d(b,a.length,a,1)},wrap_meth_callback_args:b=>function(...a){return d(b,2,[this,a],0)},wrap_meth_callback_strict:(c,b)=>function(...a){a.length=c;a.unshift(this);return d(b,a.length,a,0)},wrap_meth_callback_unsafe:b=>function(...a){a.unshift(this);return d(b,a.length,a,2)},wrap_fun_arguments:b=>function(...a){return b(a)},format_float:(a,b,c,d)=>{function +j(a,b){if(Math.abs(a)<1.0)return a.toFixed(b);else{var +c=Number.parseInt(a.toString().split("+")[1]);if(c>20){c-=20;a/=Math.pow(10,c);a+=new +Array(c+1).join("0");if(b>0)a=a+"."+new +Array(b+1).join("0");return a}else +return a.toFixed(b)}}switch(b){case +0:var +e=d.toExponential(a),f=e.length;if(e.charAt(f-3)==="e")e=e.slice(0,f-1)+"0"+e.slice(f-1);break;case +1:e=j(d,a);break;case +2:a=a?a:1;e=d.toExponential(a-1);var +i=e.indexOf("e"),h=+e.slice(i+1);if(h<-4||d>=1e21||d.toFixed(0).length>a){var +f=i-1;while(e.charAt(f)==="0")f--;if(e.charAt(f)===".")f--;e=e.slice(0,f+1)+e.slice(i);f=e.length;if(e.charAt(f-3)==="e")e=e.slice(0,f-1)+"0"+e.slice(f-1);break}else{var +g=a;if(h<0){g-=h+1;e=d.toFixed(g)}else +while(e=d.toFixed(g),e.length>a+1)g--;if(g){var +f=e.length-1;while(e.charAt(f)==="0")f--;if(e.charAt(f)===".")f--;e=e.slice(0,f+1)}}break}return c?" "+e:e},gettimeofday:()=>new +Date().getTime()/1000,gmtime:a=>{var +b=new +Date(a*1000),c=b.getTime(),e=new +Date(Date.UTC(b.getUTCFullYear(),0,1)).getTime(),d=Math.floor((c-e)/86400000);return n(b.getUTCSeconds(),b.getUTCMinutes(),b.getUTCHours(),b.getUTCDate(),b.getUTCMonth(),b.getUTCFullYear()-1900,b.getUTCDay(),d,false)},localtime:a=>{var +b=new +Date(a*1000),c=b.getTime(),f=new +Date(b.getFullYear(),0,1).getTime(),d=Math.floor((c-f)/86400000),e=new +Date(b.getFullYear(),0,1),g=new +Date(b.getFullYear(),6,1),h=Math.max(e.getTimezoneOffset(),g.getTimezoneOffset());return n(b.getSeconds(),b.getMinutes(),b.getHours(),b.getDate(),b.getMonth(),b.getFullYear()-1900,b.getDay(),d,b.getTimezoneOffset()new +Date(a,b,c,d,e,f).getTime(),random_seed:()=>crypto.getRandomValues(new +Int32Array(12)),open:(a,d,c)=>f.openSync(a,N.reduce((a,b,c)=>d&1<f.closeSync(a),write:(a,b,c,d,e)=>f?f.writeSync(a,b,c,d,e===null?e:Number(e)):(console[a===2?"error":"log"](typeof +b==="string"?b:l.decode(b.slice(c,c+d))),d),read:(a,b,c,d,e)=>f.readSync(a,b,c,d,e),file_size:a=>f.fstatSync(a,{bigint:true}).size,register_channel:P,unregister_channel:T,channel_list:B,exit:a=>e&&process.exit(a),argv:()=>e?process.argv.slice(1):["a.out"],on_windows:()=>r,getenv:a=>e?process.env[a]:null,system:a=>{var +b=require("node:child_process").spawnSync(a,{shell:true,stdio:"inherit"});if(b.error)throw b.error;return b.signal?255:b.status},isatty:a=>+require("node:tty").isatty(a),time:()=>performance.now(),getcwd:()=>e?process.cwd():"/static",chdir:a=>process.chdir(a),mkdir:(a,b)=>f.mkdirSync(a,b),rmdir:a=>f.rmdirSync(a),unlink:a=>f.unlinkSync(a),readdir:a=>f.readdirSync(a),stat:(a,b)=>k(f.statSync(a),b),lstat:(a,b)=>k(f.lstatSync(a),b),fstat:(a,b)=>k(f.fstatSync(a),b),file_exists:a=>+f.existsSync(a),is_directory:a=>+f.lstatSync(a).isDirectory(),utimes:(a,b,c)=>f.utimesSync(a,b,c),truncate:(a,b)=>f.truncateSync(a,b),ftruncate:(a,b)=>f.ftruncateSync(a,b),rename:(a,b)=>{var +c;if(r&&(c=f.statSync(b,{throwIfNoEntry:false}))&&f.statSync(a,{throwIfNoEntry:false})?.isDirectory())if(c.isDirectory()){if(!b.startsWith(a))try{f.rmdirSync(b)}catch{}}else{var +d=new +Error(`ENOTDIR: not a directory, rename '${a}' -> '${b}'`);throw Object.assign(d,{errno:-20,code:"ENOTDIR",syscall:"rename",path:b})}f.renameSync(a,b)},throw:a=>{throw a},start_fiber:a=>t(a),suspend_fiber:L((c,b)=>new +Promise(a=>c(a,b))),resume_fiber:(a,b)=>a(b),weak_new:a=>new +WeakRef(a),weak_deref:a=>{var +b=a.deref();return b===undefined?null:b},weak_map_new:()=>new +WeakMap(),map_new:()=>new +Map(),map_get:(a,b)=>{var +c=a.get(b);return c===undefined?null:c},map_set:(a,b,c)=>a.set(b,c),map_delete:(a,b)=>a.delete(b),log:a=>console.log(a)},m={test:a=>+(typeof +a==="string"),compare:(a,b)=>ab),hash:G,decodeStringFromUTF8Array:()=>"",encodeStringToUTF8Array:()=>0,fromCharCodeArray:()=>""},g=Object.assign({Math:M,bindings:w,js:v,"wasm:js-string":m,"wasm:text-decoder":m,"wasm:text-encoder":m,env:{}},E),s={builtins:["js-string","text-decoder","text-encoder"]};function +K(a){const +b=require("node:path"),c=b.join(b.dirname(require.main.filename),a);return require("node:fs/promises").readFile(c)}const +p=globalThis?.document?.currentScript?.src;function +D(a){const +b=p?new +URL(a,p):a;return fetch(b)}const +J=e?K:D;async function +I(a){return e?WebAssembly.instantiate(await +a,g,s):WebAssembly.instantiateStreaming(a,g,s)}async function +H(){g.OCaml={};const +c=[];async function +b(a,b){const +f=a[1].constructor!==Array;async function +e(){const +d=J(Q+"/"+a[0]+".wasm");await +Promise.all(f?c:a[1].map(a=>c[a]));const +e=await +I(d);Object.assign(b?g.env:g.OCaml,e.instance.exports)}const +d=e();c.push(d);return d}async function +a(a){for(const +c +of +a)await +b(c)}await +b(i[0],1);if(i.length>1){await +b(i[1]);const +c=new +Array(20).fill(i.slice(2).values()).map(a);await +Promise.all(c)}return{instance:{exports:Object.assign(g.env,g.OCaml)}}}const +U=await +H();var{caml_callback:d,caml_alloc_tm:n,caml_alloc_stat:x,caml_start_fiber:A,caml_handle_uncaught_exception:o,caml_buffer:y,caml_extract_string:z,string_get:R,string_set:S,_initialize:j}=U.instance.exports,h=y?.buffer,O=h&&new +Uint8Array(h,0,h.length);t=q(A);var +j=q(j);if(globalThis.process&&globalThis.process.on)globalThis.process.on("uncaughtException",(a,b)=>o(a));else if(globalThis.addEventListener)globalThis.addEventListener("error",a=>a.error&&o(a.error));await +j()})(function(a){"use strict";return{}}(globalThis))({"link":[["code-50e60af296cda1451a87",0]],"generated":{},"src":"moonc.assets"}); diff --git a/moonc_wasm/src/moonc/moonc.wasm b/moonc_wasm/src/moonc/moonc.wasm index 458c335..0fad98d 100644 Binary files a/moonc_wasm/src/moonc/moonc.wasm and b/moonc_wasm/src/moonc/moonc.wasm differ diff --git a/moonc_wasm/src/moonc/moonc.wasm.old b/moonc_wasm/src/moonc/moonc.wasm.old new file mode 100644 index 0000000..458c335 Binary files /dev/null and b/moonc_wasm/src/moonc/moonc.wasm.old differ