commit e7738de6d2fcf167bb6543fc7fb3cfb2b0cfe56a Author: wehub-resource-sync Date: Mon Jul 13 12:29:30 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..154fc56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.graph diff=zero-graph diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..efc08c3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,370 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + schedule: + - cron: "17 9 * * *" + workflow_dispatch: + inputs: + validation_depth: + description: Validation depth to run + required: true + default: fast + type: choice + options: + - fast + - deep + +permissions: + contents: read + +jobs: + conformance-suite: + name: Conformance Suite + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Conformance suite + env: + ZERO_VALIDATION_PHASES: graph-input-policy,native-contracts,provenance-guardrails,type-core-smoke,mir-verifier-smoke,program-graph-smoke,canonical-text-smoke,examples-gate,conformance-run + ZERO_VALIDATION_JOBS: "4" + ZERO_CONFORMANCE_CHECK_JOBS: "4" + run: pnpm run conformance:local + + native-preflight: + name: Native Preflight + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Native preflight + env: + ZERO_NATIVE_TEST_PHASES: preflight + ZERO_NATIVE_TEST_SCOPE: fast + run: pnpm run native:test:local + + native-runtime-cases: + name: Native Runtime Cases (${{ matrix.shard }}/2) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Native tests + env: + ZERO_NATIVE_TEST_PHASES: cases + ZERO_NATIVE_TEST_SCOPE: fast + ZERO_NATIVE_TEST_SHARD: "${{ matrix.shard }}/2" + run: pnpm run native:test:local + + native-metadata-reports: + name: Native Metadata Reports + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Native metadata and reports + env: + ZERO_NATIVE_TEST_PHASES: metadata-and-reports + run: pnpm run native:test:local + + native-direct-backend-artifacts: + name: Native Direct Backend Artifacts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Native direct backend artifacts + env: + ZERO_NATIVE_TEST_PHASES: direct-backend-artifacts + run: pnpm run native:test:local + + native-sanitizer-smoke: + name: Native Sanitizer Smoke + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - name: Native sanitizer smoke + run: pnpm run native:sanitize + + command-contract-snapshots: + name: Command Contract Snapshots + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - run: pnpm install --frozen-lockfile + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - name: Build native compiler + run: make -C native/zero-c + + - name: Command contract snapshots + run: pnpm run command-contracts:local + + graph-build-perf: + name: Graph Build Perf + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - run: pnpm install --frozen-lockfile + + - name: Graph build perf report + env: + ZERO_GRAPH_BUILD_PERF_CASES: hello-artifact,stdlib-heavy-artifact + run: | + mkdir -p .zero + set +e + pnpm run graph:perf:budgets > .zero/graph-build-perf.json + status=$? + cat .zero/graph-build-perf.json + exit $status + + deep-conformance-suite: + name: Deep Conformance Suite + if: ${{ github.event_name == 'schedule' || github.event.inputs.validation_depth == 'deep' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Deep conformance suite + env: + ZERO_VALIDATION_JOBS: "4" + ZERO_CONFORMANCE_CHECK_JOBS: "4" + run: pnpm run conformance:local + + deep-native-preflight: + name: Deep Native Preflight + if: ${{ github.event_name == 'schedule' || github.event.inputs.validation_depth == 'deep' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Deep native preflight + env: + ZERO_NATIVE_TEST_PHASES: preflight + ZERO_NATIVE_TEST_SCOPE: deep + run: pnpm run native:test:local + + deep-native-runtime-cases: + name: Deep Native Runtime Cases (${{ matrix.shard }}/6) + if: ${{ github.event_name == 'schedule' || github.event.inputs.validation_depth == 'deep' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5, 6] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Deep native tests + env: + ZERO_NATIVE_TEST_PHASES: cases + ZERO_NATIVE_TEST_SCOPE: deep + ZERO_NATIVE_TEST_SHARD: "${{ matrix.shard }}/6" + run: pnpm run native:test:local + + deep-graph-build-perf: + name: Deep Graph Build Perf + if: ${{ github.event_name == 'schedule' || github.event.inputs.validation_depth == 'deep' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - run: pnpm install --frozen-lockfile + + - name: Deep graph build perf report + run: | + mkdir -p .zero + set +e + pnpm run graph:perf:budgets > .zero/deep-graph-build-perf.json + status=$? + cat .zero/deep-graph-build-perf.json + exit $status + + workspace-checks: + name: Workspace Checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Enable pnpm + run: | + corepack enable + corepack prepare pnpm@11.1.3 --activate + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - run: pnpm install --frozen-lockfile + + - name: Workspace checks + env: + VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }} + run: pnpm run workspace:checks diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..913da8d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,170 @@ +name: Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + check-release: + name: Check for new version + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + outputs: + needs_release: ${{ steps.check.outputs.needs_release }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Check GitHub release status + id: check + run: | + LOCAL_VERSION=$(node -p "require('./package.json').version") + echo "Local version: $LOCAL_VERSION" + + TAG="v$LOCAL_VERSION" + EXISTING_ASSETS=$(gh release view "$TAG" --json assets --jq '.assets[].name' 2>/dev/null || true) + missing=0 + for asset in \ + CHECKSUMS.txt \ + zero-darwin-arm64 \ + zero-darwin-x64 \ + zero-linux-arm64 \ + zero-linux-x64 \ + zero-linux-musl-arm64 \ + zero-linux-musl-x64 \ + zero-win32-arm64.exe \ + zero-win32-x64.exe + do + if ! printf '%s\n' "$EXISTING_ASSETS" | grep -Fx "$asset" >/dev/null; then + echo "Missing release asset: $asset" + missing=1 + fi + done + + if [ "$missing" -eq 0 ]; then + echo "GitHub release $TAG exists with all assets" + echo "needs_release=false" >> "$GITHUB_OUTPUT" + else + echo "GitHub release $TAG is missing assets, will create/update" + echo "needs_release=true" >> "$GITHUB_OUTPUT" + fi + + echo "version=$LOCAL_VERSION" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + github-release: + name: Create GitHub Release + needs: check-release + if: needs.check-release.outputs.needs_release == 'true' + runs-on: macos-14 + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Install pinned Zig + run: bash scripts/install-zig.sh + + - name: Extract changelog entry + run: | + VERSION="${{ needs.check-release.outputs.version }}" + awk '//{found=1; next} //{found=0} found{print}' CHANGELOG.md > /tmp/release-notes.md + + LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ') + if [ "$LINES" -lt 2 ]; then + echo "Error: No release notes found between and markers in CHANGELOG.md" + exit 1 + fi + echo "Extracted release notes for $VERSION ($LINES lines)" + + - name: Cross-compile for all targets + run: | + mkdir -p /tmp/zero-release + node --experimental-strip-types --disable-warning=ExperimentalWarning scripts/embed-skill-data.mts + build_hash="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" + + build_asset() { + target="$1" + name="$2" + echo "Building $name (target: $target)..." + zig cc -target "$target" -std=c11 -D_POSIX_C_SOURCE=200809L -DZERO_BUILD_HASH="\"$build_hash\"" -Os -Inative/zero-c/include native/zero-c/src/*.c -o "/tmp/zero-release/$name" + chmod 755 "/tmp/zero-release/$name" + } + + build_asset aarch64-macos zero-darwin-arm64 + build_asset x86_64-macos zero-darwin-x64 + build_asset aarch64-linux-gnu zero-linux-arm64 + build_asset x86_64-linux-gnu zero-linux-x64 + build_asset aarch64-linux-musl zero-linux-musl-arm64 + build_asset x86_64-linux-musl zero-linux-musl-x64 + build_asset aarch64-windows-gnu zero-win32-arm64.exe + build_asset x86_64-windows-gnu zero-win32-x64.exe + + (cd /tmp/zero-release && shasum -a 256 zero-* > CHECKSUMS.txt) + + - name: Smoke test release assets + run: | + set -eu + cd /tmp/zero-release + + for asset in \ + zero-darwin-arm64 \ + zero-darwin-x64 \ + zero-linux-arm64 \ + zero-linux-x64 \ + zero-linux-musl-arm64 \ + zero-linux-musl-x64 \ + zero-win32-arm64.exe \ + zero-win32-x64.exe + do + test -s "$asset" + done + + shasum -a 256 -c CHECKSUMS.txt + + if [ "$(uname -s)" = "Darwin" ]; then + case "$(uname -m)" in + arm64) + ./zero-darwin-arm64 --version + ./zero-darwin-arm64 skills list --json + ;; + x86_64) + ./zero-darwin-x64 --version + ./zero-darwin-x64 skills list --json + ;; + *) echo "Skipping host binary smoke for unsupported macOS arch $(uname -m)" ;; + esac + fi + + - name: Create GitHub Release + run: | + VERSION="${{ needs.check-release.outputs.version }}" + TAG="v$VERSION" + + if gh release view "$TAG" &>/dev/null; then + echo "Release $TAG already exists, updating assets" + else + echo "Creating release $TAG..." + gh release create "$TAG" \ + --title "$TAG" \ + --notes-file /tmp/release-notes.md + fi + + gh release upload "$TAG" \ + /tmp/zero-release/zero-* \ + /tmp/zero-release/CHECKSUMS.txt \ + --clobber + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +dist/ +.zero/ +.pnpm-store/ +.turbo/ +node_modules/ +npm-debug.log* +native/zero-c/src/embedded_stdlib_graph.inc +native/zero-c/src/embedded_stdlib_graph/ + +# environment variables +.env* +!.env.example diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c84cf2a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,139 @@ +# Contributor Notes + +Zero is a pre-1 experiment in building an agent-first programming language. +Keep public-facing changes honest about what works today without weakening that +positioning. + +## Project Direction + +Zero is still being shaped around the needs of agents. Breaking changes are +acceptable when they move the language, standard library, compiler, or tooling +closer to that goal. + +Do not preserve legacy behavior by default. Prefer the clearer agent-facing +design over compatibility shims, migration layers, or carrying old paths +forward. Keep examples, docs, tests, and command contracts aligned with the new +behavior so the repository describes one coherent current system. + +This does not mean broad churn for its own sake. Make direct changes that +advance Zero's agent-first goals: on-the-fly learnability, deterministic +inspection and repair, strong standard-library coverage, exceptional developer +experience, and regular patterns over syntactic convenience. + +## Safety Expectations + +Security vulnerabilities should be expected. Zero is not ready for production +systems, sensitive data, or trusted infrastructure. + +Run and develop Zero in safe environments: isolated workspaces, disposable +inputs, and systems where compiler crashes, malformed output, or unsafe runtime +behavior cannot damage production state. Treat generated artifacts and examples +as experimental unless they have been reviewed for the specific environment +where they will run. + +## Development + +- Build the local compiler with `make -C native/zero-c`. +- Use `bin/zero` for focused checks; it execs the local native compiler at + `.zero/bin/zero`. +- Keep examples runnable and docs copyable. +- Prefer small, direct changes over broad refactors. +- Use direct emitters for compiler output. +- For broad local validation, run `pnpm run agent:checks`. It mirrors the CI + buckets in parallel, including conformance, command contracts, native test + shards, sanitizer smoke, and workspace checks. It uses isolated `/tmp` + workspaces so agents can validate uncommitted changes without local artifact + races. +- Before ending any agent turn that changes the repository, run + `pnpm run conformance` unless `pnpm run agent:checks` already passed for the + same changes. If validation cannot complete, report the blocker and the + failing command. + +## Useful Checks + +```sh +pnpm run agent:checks +pnpm run docs:build +pnpm run conformance +pnpm run native:test +pnpm run command-contracts +``` + +`pnpm run agent:checks` already includes conformance. Do not run conformance +again after it passes unless you need to recheck later changes. + +Shard native tests locally with `ZERO_NATIVE_TEST_SHARD=1/4 pnpm run +native:test:local`. PR CI runs representative native runtime cases in two +fast shards. Scheduled and manual deep CI runs the full native runtime matrix +in six shards, plus full conformance and full graph performance fixtures. + +`pnpm run conformance:local` and `pnpm run command-contracts:local` use the +aggregate validation runner. Add `-- --shard 1/4` to run one conformance phase +shard, `-- --phases name,name` to match a focused CI phase set, `-- --list` to +see phases, and `-- --fail-fast` only when a narrow loop should stop at the +first failing phase. +Use `ZERO_NATIVE_TEST_SCOPE=fast` only when reproducing PR CI coverage. Leave it +unset for the exhaustive native runtime matrix. +`pnpm run conformance` runs the sandbox suite with four isolated conformance +check workers. Local validation stays serial by default; set +`ZERO_CONFORMANCE_CHECK_JOBS=` only when measuring that path on the current +machine. +Validation scripts prefer the built native compiler at `.zero/bin/zero` after +`native-build`; set `ZERO_BIN=` only when comparing another compiler +binary deliberately. + +For focused compiler work: + +```sh +bin/zero check --json +bin/zero inspect --json +bin/zero size --json +bin/zero explain +bin/zero fix --plan --json +``` + +## Project Layout + +- `native/zero-c/`: native compiler implementation. +- `examples/`: small runnable programs and packages. +- `conformance/`: language and CLI fixtures. +- `docs/`: public documentation site. +- `scripts/`: validation and release support tooling. + +## Public Docs Policy + +Docs should describe current user-facing behavior, not internal development +history. Avoid release-planning language, validation-report narratives, and +implementation diary details in pages intended for external readers. + +## Releasing + +Releases are manual, single-branch affairs. The maintainer controls the +changelog voice and format. + +To prepare a release: + +1. Create a release branch, such as `ctate/v0.1.1`. +2. Bump the release version in `package.json`, `docs/package.json`, + `extensions/vscode/package.json`, and `native/zero-c/src/main.c`. +3. Update command-contract expectations that assert the compiler version. +4. Write the `CHANGELOG.md` entry for the new version, wrapped in + `` and `` markers. +5. Remove the release markers from the previous release entry. Only the latest + release entry should have markers. +6. Include a `### Contributors` section in the marked changelog entry. Derive + contributors from commit authors and `Co-authored-by` trailers since the + previous tag. +7. Run focused checks before opening or updating the PR: + +```sh +make -C native/zero-c +bin/zero --version --json +pnpm run test:zero +pnpm run command-contracts:local +pnpm run docs:build +``` + +The release workflow reads the version from `package.json`, builds release +assets, and uses the content between the changelog markers as the GitHub +release body. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..86dd38f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,165 @@ +# Changelog + +## 0.3.4 + + + +- Makes graph patching the primary agent edit loop with stdin patch bodies, `--replace-in-fn`, expression replacement by handle, structural rewrites, declaration-level edits, helper/test patch ops, and inline handle diagnostics across view, query, and patch commands. +- Improves graph/source reconciliation with patch-specific stale-refresh tips, once-per-state notes, accepted whole-file rewrites when function sets are preserved, and clearer unsupported graph-test and non-entry `World` helper diagnostics. +- Speeds up graph workflows with memoized repository store loads, adjacency orders, semantic check verdicts, static const classification, pre-merge MIR cache keys, and reusable runtime object cache artifacts. +- Reduces direct backend output and broadens native coverage with constant array-fill loop lowering, COFF x64 HTTP request-body extraction, chunked file reads via `std.fs.readBytesAt`, and total-file-size reporting for fixed-buffer reads. +- Refreshes bundled skills, CLI help, docs, command contracts, conformance fixtures, compiler metrics, and validation guidance around the patch-first agent workflow. +- Hardens release checks with runtime object cache cleanup, graph patch routing coverage, examples gate teardown retries, and additional package cache/version assertions. + +### Contributors + +- @ctate + + + +## 0.3.3 + +- Makes package graph stores refresh from edited projections across compiler commands, classifies source/store sync by content instead of mtimes, and gives stale, diverged, and ambiguous reconcile states deterministic diagnostics and repairs. +- Improves agent-facing inspection and edit loops with scoped `zero query` output, `zero view --fn`, `zero diff --fn`, `zero patch --replace-fn --body-file`, focused command help, and smaller version-matched skill topics with section-scoped stdlib fetches. +- Expands standard-library coverage with `std.regex`, `std.inet`, `std.unicode`, and RFC 3339 helpers in `std.time`, including docs, skill references, and runtime conformance fixtures. +- Hardens direct backend correctness for shape references, large stack frames, fallible `Void` mains, file reads, trap stderr, top-level consts, null `Maybe` locals, fixed-array lengths, and untyped integer literal adoption. +- Speeds up large graph workflows with indexed reconciliation, stdlib merges, projection validation caches, graph lowering indexes, and borrow-contract scans while preserving deterministic graph hashes and artifacts. +- Adds graph-store diagnostic codes and complete `zero explain` coverage, buildability gating for unsupported checked constructs, example package gates, sanitizer smoke, package build determinism contracts, and git build hashes in `zero --version`. + +### Contributors + +- @ctate + +## 0.3.2 + +- Adds `break` and `continue` support to the direct backend and `zero test`, lowering both through typed MIR and all direct emitters with conformance coverage and required runtime checks. +- Speeds up graph validation with indexed node-hash tables, a reusable adjacency index, and deduplicated store validation, making `zero import` on large programs roughly 12x faster. + +### Contributors + +- @ctate + +## 0.3.1 + +- Hardens compiler, runtime, and toolchain I/O boundaries with checked file reads and writes, directory rejection, atomic output staging, safer executable finalization, mapped-MIR and binary graph parser validation, and regular-file checks for generated compiler/linker outputs. +- Removes remaining shell-based native execution surfaces in favor of argv-based process helpers, safer tool resolution, guarded child setup, validated compiler overrides, and explicit metrics coverage for process and shell-call regressions. +- Expands and hardens the real `std.http.listen` runtime with per-request spooling, private temp lifecycles, oversized request handling, socket deadlines, structured JSON error responses, and native smoke coverage for listener behavior. +- Grows graph-backed standard-library coverage with environment, filesystem, process, time, random, crypto, URL, JSON, HTTP text/HTML, redirect, array writer, object writer, CLI argument, and HTTP JSON error helpers. +- Improves graph row patch authoring with grouped expressions, compact casts and checks, logical OR, else-if branches, bare returns, natural call arguments, stronger invalid-row diagnostics, and refreshed bundled skill guidance. +- Adds graph build performance instrumentation and budgets, stdlib graph merge caching, agent-scale and Rosetta challenge evals, parallel CI validation jobs, sharded native checks, deep validation profiles, and local aggregate agent checks. + +### Contributors + +- @ctate + +## 0.3.0 + +- Makes graph-first package authoring the normal workflow: repository `zero.graph` stores are the compiler input, `.0` files are human-readable projections, `zero init` owns project creation, `zero diff` supports graph review, and source projection inputs are rejected at the compiler boundary. +- Adds binary repository graph stores and graph-backed standard-library modules, with source-free status/verify, import/export, merge, query, patch, source-map, reconciliation, metadata, drift detection, and checked-in binary graph fixtures for examples, stdlib, and conformance. +- Advances graph-native compiler execution across check, build, run, test, size, doc, dev, time, package dependency, benchmark, and validation paths, including typed graph-to-MIR lowering, mapped final-MIR cache reuse, graph semantic/resolution/ownership facts, and graph-native test execution. +- Introduces explicit experimental LLVM backend support with backend selection contracts, textual LLVM IR emission, host executable builds, toolchain diagnostics, profile facts, and no-fallback release policy. +- Expands web API support in `std.http` with routing, JSON response/body validation, CORS, OPTIONS, path segment, bearer token, cookie/session helpers, HTTP listener/client graph lowering, and CRM/router/ping-pong examples. +- Hardens direct backend and compiler internals with C API contracts, unsafe C link validation, target manifest matching, ABI layout tables, direct emit-kind handling, buildability value/target checks, TOML manifests, and stricter metrics guardrails. +- Refreshes public docs, README, bundled skills, graph-first examples, command contracts, conformance, docs tests, aggregate validation, and sandboxed conformance workers around the current agent-facing workflow. + +### Contributors + +- @ctate + +## 0.2.1 + +- Adds extern C call support with target-aware header preprocessing, stricter link-plan validation, direct object linking, graph metadata fixes, and diagnostics for missing or unsafe C import inputs. +- Expands the standard library across memory, collections, search, sort, ASCII, formatting, text, parsing, math, random, time, codec, JSON, URL, hosted I/O, filesystem, HTTP, testing, and logging helpers. +- Strengthens memory-safety diagnostics for `Maybe` guards, owned moves, mutable span aliases, span lifetime escapes, aggregate reassignment, scalar match fallthrough, and exported safety facts. +- Improves direct backend correctness across Mach-O, ELF64, COFF, x64, and AArch64 paths, including byte-view bounds, checksum helpers, open slices, usize runtime returns, and indexed store handling. +- Retires row source parsing from the public source boundary, keeps canonical `.0` text as the supported input surface, and rejects stale row graph artifact paths. +- Refreshes docs, examples, skills, command contracts, conformance fixtures, stdlib contracts, target-matrix checks, compiler metrics, and reliability smoke coverage around the current workflows. + +### Contributors + +- @ctate +- @ihasq +- @PeterXMR + +## 0.2.0 + +- Makes canonical `.0` text the native source surface, with a parser, formatter, Program import path, diagnostics, docs, examples, stdlib sources, benchmarks, conformance fixtures, and command snapshots aligned around that format. +- Promotes ProgramGraph from inspection output into an editable artifact workflow with deterministic dump, validate, view, check, roundtrip, import, patch, build, run, test, size, and package entrypoint support. +- Adds source-backed graph edits so checked graph patches can rewrite canonical `.0` files while preserving package/import context, std helper context, metadata, and stable graph identities. +- Expands direct backend coverage with darwin-x64 Mach-O executable support, shared AArch64 ELF/COFF emission, target-readiness reporting, byte-view ABI fixes, and stronger cross-target buildability checks. +- Adds source-backed `std.path`, `std.str`, and `std.math` modules with docs, examples, Rosetta coverage, and direct target validation. +- Exposes resolved call-resolution and stdlib helper facts in graph JSON, tightening generic return typing, member call facts, std helper validation, and package cache metadata. +- Grows Rosetta and compiler smoke coverage, graph command contracts, metrics budgets, CLI docs, and skills around the current agent edit loop. + +### Contributors + +- @ctate + +## 0.1.4 + +- Adopts canonical `.0` text as the current Zero source surface across parsing, import resolution, package manifests, command workflows, docs, fixtures, formatting, and artifact contracts. +- Rebuilds checker internals around TypeCore, binder-aware unification, generic inference, static interface validation, provenance substitution, and shared call-resolution facts. +- Hardens direct emitters and native artifacts through MIR verifier contracts, direct specialization metadata, target buildability checks, and shared x64, AArch64, ELF, Mach-O, and COFF emission helpers. +- Adds clearer direct backend selection, target readiness, and buildability reporting so command JSON and failed native builds expose deterministic blocker facts. +- Removes stale wasm, compiler-zero, and superseded parser surfaces while refreshing public docs, README guidance, and zerolang branding around the current system. +- Strengthens compiler structure guardrails, metrics budgets, TypeScript-based repo scripts, docs build infrastructure, CI workspace commands, and eval harness isolation. +- Renames bundled version-matched skills to the current flat skill names while keeping `zero skills` discovery focused on the native compiler. + +### Contributors + +- @ctate + +## 0.1.3 + +- Adds hosted HTTP client runtime support, including runtime packaging fixes, JSON byte helpers for wasm, HTTP final-header parsing fixes, and direct array span lowering repairs. +- Parses `use` declarations into syntax graph facts and reports import diagnostics, fix plans, and graph edges against the specific import source ranges. +- Adds structured backend blocker and target-readiness facts to command JSON so checks, build previews, and direct backend failures explain unsupported targets more deterministically. +- Expands agent-facing diagnostics with BOR001 borrow trace facts, target-neutral CGEN004 wording, and NAM004 generic type-name shadowing rejection. +- Hardens native compiler allocation paths across shared buffers, parser, checker, IR lowering, emitters, source handling, targets, and driver state for deterministic allocation failures. +- Updates the public site and docs with current guidance, docs chat rate limiting, and pnpm-based repository workflows. +- Restores native versioned skill guidance, keeps the repo wrapper on the native compiler, and removes the legacy `zero skills path` command. + +### Contributors + +- @ctate +- @PeterXMR +- @h4ckf0r0day + +## 0.1.2 + +- Rebuilds borrow provenance tracking across references, fields, subpaths, assignments, control-flow joins, receiver side effects, generic methods, return summaries, and unreachable paths. +- Tightens borrow conflict checking by deriving conflicts from provenance and comparing concrete places, with additional conformance coverage for provenance joins and edge cases. +- Fixes checker regressions around borrow origins, reassignment, aggregate values, explicit reference fields, unknown identifiers, and fallibility propagation through wrappers. +- Fixes dynamic CLI strings and Darwin executable UUID emission. +- Adds Apache-2.0 licensing and lockfile license metadata. +- Updates the public site with an install toggle, GitHub star count, and a mobile header Docs link. + +### Contributors + +- @ctate +- @badlogic +- @chenrui333 +- @heylakatos +- @onevcat + +## 0.1.1 + +- Adds the public installer at `https://zerolang.ai/install.sh`, with platform selection, GitHub release downloads, checksum verification, and `$HOME/.zero/bin/zero` installation. +- Adds `zero run` for the everyday edit loop: build a host executable, run it, pass program arguments after `--`, forward stdout/stderr, and return the program exit status. +- Updates README, homepage, getting started, install, and CLI docs around the curl install path, copyable commands, and `zero run`. +- Reworks public docs to be more scannable and current, including stronger language, diagnostics, testing, target, package, optimization, and standard library references. +- Removes placeholder module docs that described surfaces not ready for users and adds current module docs for `std.crypto`, `std.http`, and `std.net`. +- Adds version-matched agent guidance through `zero skills`, including focused workflows for Zero syntax, diagnostics, builds, packages, standard library use, testing, and agent edit loops. +- Keeps the installable Zero skill as a thin bootstrap so external skill managers discover one Zero skill while the compiler serves the richer guidance for the installed version. +- Updates the `zero skills` CLI contract to serve bundled flat skill data while preserving list, get, path, and JSON workflows. + +### Contributors + +- @ctate +- @mvanhorn + +## 0.1.0 + +- Initial public release of Zero as the programming language for agents. +- Includes the native compiler, examples, documentation site, and validation fixtures. +- Supported workflows use direct Zero emitters for the documented examples and targets. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6be6693 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for describing the origin of the Work and + reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Support. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or support. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Vercel, Inc. + + 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/README.md b/README.md new file mode 100644 index 0000000..84a6ceb --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# Zerolang + +**The programming language for agents.** + +Zerolang is an experimental graph-native programming language where the semantic graph is the program database. Humans ask for outcomes. Agents query the graph, submit checked edits, and prove the result. + +> **Safety warning** +> +> Zerolang is experimental. Expect breaking changes, rough edges, and security issues. Run it in isolated workspaces, not against production systems or sensitive data. + +## Start With a Request + +The expected workflow is a normal conversation: + +```text +build hello world for zerolang +``` + +The agent should use the compiler, not guess from source text: + +```sh +zero init +zero patch --op 'addMain' --op 'addCheckWrite fn="main" text="hello from zero\n"' +zero run +``` + +The result is still reviewable as a text projection: + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write("hello from zero\n") +} +``` + +That `.0` file is a projection of `zero.graph`. Humans can read it, review it, and occasionally edit it. Agents should normally keep using `zero query` and `zero patch`. + +## The Program Database + +Traditional agent coding loops treat text as the source of truth: + +```mermaid +flowchart LR + A[agent writes text] --> B[check] + B --> C[format] + C --> D[build] + D --> E[inspect failures] + E --> A +``` + +Zerolang moves the agent closer to the compiler: + +```mermaid +flowchart LR + A[agent queries graph] --> B[agent submits checked patch] + B --> C{compiler accepts?} + C -- invalid or stale --> A + C -- valid --> D[agent runs task validation] + D --> E[human reviews projection when useful] +``` + +The graph gives agents explicit handles: symbols, node IDs, graph hashes, types, effects, ownership facts, capabilities, imports, call edges, and target facts. Edits can target semantic structure instead of line ranges. Stale graph hashes, unexpected field values, invalid shapes, and type errors fail before the store is written. + +## What Exists Today + +- `zero.graph` is the checked compiler input for graph-first packages. +- `.0` files are human-readable projections, not the normal agent authoring surface. +- `zero patch` applies checked graph edits and rejects stale or invalid changes. +- `zero query`, `zero inspect`, `zero check`, `zero test`, and `zero run` expose compiler facts through agent-friendly commands. +- `zero import` and `zero export` make the projection boundary explicit, so human text edits do not silently diverge from the graph. + +## Install + +Install the compiler: + +```sh +curl -fsSL https://zerolang.ai/install.sh | bash +export PATH="$HOME/.zero/bin:$PATH" +zero --version +``` + +Install the agent bootstrap skill: + +```sh +npx skills add vercel-labs/zerolang +``` + +The compiler bundles version-matched skills for agents: + +```sh +zero skills +zero skills get agent +zero skills get graph +zero skills get language +zero skills get stdlib +``` + +## Daily Loop + +For most package work: + +```sh +zero query +zero patch --op help +zero patch --op 'addMain' +zero check +zero test +zero run -- +``` + +The default input is the current directory. Use `.` only when you want to be explicit. + +When a human wants to review projection text: + +```sh +zero export +zero verify-projection +``` + +When a human intentionally edits a projection: + +```sh +zero import +zero check +``` + +## Runtime Goals + +The graph-first model should reduce agent guessing without relaxing the runtime goals: + +- Token-efficient inspection +- Low memory usage +- Fast startup and builds +- Low runtime latency +- Explicit capabilities +- Small, dependency-free artifacts + +## Developing Zerolang + +Build the local compiler: + +```sh +pnpm install +make -C native/zero-c +bin/zero --version +``` + +Useful checks: + +```sh +pnpm run docs:build +pnpm run conformance +pnpm run native:test +pnpm run command-contracts +``` + +For local iteration: + +```sh +pnpm run conformance:local -- --list +pnpm run conformance:local -- --shard 1/4 +pnpm run command-contracts:local +``` + +Read the docs at [zerolang.ai](https://zerolang.ai). diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..abbd7c6 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`vercel-labs/zerolang` +- 原始仓库:https://github.com/vercel-labs/zerolang +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/benchmarks/rosetta/100-doors.0 b/benchmarks/rosetta/100-doors.0 new file mode 100644 index 0000000..14bc382 --- /dev/null +++ b/benchmarks/rosetta/100-doors.0 @@ -0,0 +1,28 @@ +fn squareDoor(door: usize) -> Bool { + let root: usize = std.math.sqrtFloorU32(door as u32) as usize + return root * root == door +} + +pub fn main(world: World) -> Void raises { + var doors: [101]Bool = [false; 101] + var pass: usize = 1 + while pass <= 100 { + var door: usize = pass + while door <= 100 { + doors[door] = !doors[door] + door = door + pass + } + pass = pass + 1 + } + var valid: Bool = true + var checkDoor: usize = 1 + while checkDoor <= 100 { + if doors[checkDoor] != squareDoor(checkDoor) { + valid = false + } + checkDoor = checkDoor + 1 + } + if valid { + check world.out.write("100 doors ok\n") + } +} diff --git a/benchmarks/rosetta/100-doors.graph b/benchmarks/rosetta/100-doors.graph new file mode 100644 index 0000000..228a0cf Binary files /dev/null and b/benchmarks/rosetta/100-doors.graph differ diff --git a/benchmarks/rosetta/15-puzzle-solver.0 b/benchmarks/rosetta/15-puzzle-solver.0 new file mode 100644 index 0000000..78b419d --- /dev/null +++ b/benchmarks/rosetta/15-puzzle-solver.0 @@ -0,0 +1,23 @@ +fn distance(tile: u8, index: usize) -> u32 { + let target: usize = (tile - 1) as usize + let row: usize = index / 4 + let col: usize = index % 4 + let targetRow: usize = target / 4 + let targetCol: usize = target % 4 + let dr: u32 = ifLess(row, targetRow) + let dc: u32 = ifLess(col, targetCol) + return dr + dc +} + +fn ifLess(a: usize, b: usize) -> u32 { + if a < b { + return (b - a) as u32 + } + return (a - b) as u32 +} + +pub fn main(world: World) -> Void raises { + if distance(5, 0) == 1 { + check world.out.write("15 puzzle solver ok\n") + } +} diff --git a/benchmarks/rosetta/15-puzzle-solver.graph b/benchmarks/rosetta/15-puzzle-solver.graph new file mode 100644 index 0000000..0008b3a Binary files /dev/null and b/benchmarks/rosetta/15-puzzle-solver.graph differ diff --git a/benchmarks/rosetta/24-game-solve.0 b/benchmarks/rosetta/24-game-solve.0 new file mode 100644 index 0000000..3a0b5d5 --- /dev/null +++ b/benchmarks/rosetta/24-game-solve.0 @@ -0,0 +1,18 @@ +fn can24(a: i32, b: i32, c: i32, d: i32) -> Bool { + if (a + b) * (c - d) == 24 { + return true + } + if a + b + (c + d) == 24 { + return true + } + if (a - b) * (c + d) == 24 { + return true + } + return false +} + +pub fn main(world: World) -> Void raises { + if can24(1, 3, 9, 3) && can24(6, 6, 6, 6) && !can24(1, 1, 1, 1) { + check world.out.write("24 solve ok\n") + } +} diff --git a/benchmarks/rosetta/24-game-solve.graph b/benchmarks/rosetta/24-game-solve.graph new file mode 100644 index 0000000..32ab5f3 Binary files /dev/null and b/benchmarks/rosetta/24-game-solve.graph differ diff --git a/benchmarks/rosetta/24-game.0 b/benchmarks/rosetta/24-game.0 new file mode 100644 index 0000000..1a3dc45 --- /dev/null +++ b/benchmarks/rosetta/24-game.0 @@ -0,0 +1,13 @@ +fn expr(a: i32, b: i32, c: i32, d: i32) -> i32 { + return (a + b) * (c - d) +} + +fn sum4(a: i32, b: i32, c: i32, d: i32) -> i32 { + return a + b + (c + d) +} + +pub fn main(world: World) -> Void raises { + if expr(1, 3, 9, 3) == 24 && sum4(6, 6, 6, 6) == 24 { + check world.out.write("24 game ok\n") + } +} diff --git a/benchmarks/rosetta/24-game.graph b/benchmarks/rosetta/24-game.graph new file mode 100644 index 0000000..f8175cf Binary files /dev/null and b/benchmarks/rosetta/24-game.graph differ diff --git a/benchmarks/rosetta/4-rings-or-4-squares-puzzle.0 b/benchmarks/rosetta/4-rings-or-4-squares-puzzle.0 new file mode 100644 index 0000000..f4dc837 --- /dev/null +++ b/benchmarks/rosetta/4-rings-or-4-squares-puzzle.0 @@ -0,0 +1,9 @@ +fn ringSum(a: u32, b: u32, c: u32, d: u32) -> u32 { + return a + b + (c + d) +} + +pub fn main(world: World) -> Void raises { + if ringSum(1, 2, 3, 4) == 10 { + check world.out.write("four rings ok\n") + } +} diff --git a/benchmarks/rosetta/4-rings-or-4-squares-puzzle.graph b/benchmarks/rosetta/4-rings-or-4-squares-puzzle.graph new file mode 100644 index 0000000..e390274 Binary files /dev/null and b/benchmarks/rosetta/4-rings-or-4-squares-puzzle.graph differ diff --git a/benchmarks/rosetta/9-billion-names-of-god-the-integer.0 b/benchmarks/rosetta/9-billion-names-of-god-the-integer.0 new file mode 100644 index 0000000..653e7f4 --- /dev/null +++ b/benchmarks/rosetta/9-billion-names-of-god-the-integer.0 @@ -0,0 +1,20 @@ +fn partitionCount(n: usize) -> u32 { + var counts: [8]u32 = [0_u32; 8] + counts[0] = 1 + var part: usize = 1 + while part <= n { + var total: usize = part + while total <= n { + counts[total] = counts[total] + counts[total - part] + total = total + 1 + } + part = part + 1 + } + return counts[n] +} + +pub fn main(world: World) -> Void raises { + if partitionCount(5) == 7 && partitionCount(7) == 15 { + check world.out.write("integer partitions ok\n") + } +} diff --git a/benchmarks/rosetta/9-billion-names-of-god-the-integer.graph b/benchmarks/rosetta/9-billion-names-of-god-the-integer.graph new file mode 100644 index 0000000..7b23ccd Binary files /dev/null and b/benchmarks/rosetta/9-billion-names-of-god-the-integer.graph differ diff --git a/benchmarks/rosetta/99-bottles-of-beer.0 b/benchmarks/rosetta/99-bottles-of-beer.0 new file mode 100644 index 0000000..72ef289 --- /dev/null +++ b/benchmarks/rosetta/99-bottles-of-beer.0 @@ -0,0 +1,17 @@ +fn bottlesLeft(start: u32, verses: u32) -> u32 { + var value: u32 = start + var i: u32 = 0 + while i < verses { + if value > 0 { + value = value - 1 + } + i = i + 1 + } + return value +} + +pub fn main(world: World) -> Void raises { + if bottlesLeft(99, 3) == 96 { + check world.out.write("bottles ok\n") + } +} diff --git a/benchmarks/rosetta/99-bottles-of-beer.graph b/benchmarks/rosetta/99-bottles-of-beer.graph new file mode 100644 index 0000000..a3afced Binary files /dev/null and b/benchmarks/rosetta/99-bottles-of-beer.graph differ diff --git a/benchmarks/rosetta/README.md b/benchmarks/rosetta/README.md new file mode 100644 index 0000000..53ce51b --- /dev/null +++ b/benchmarks/rosetta/README.md @@ -0,0 +1,14 @@ +# Zero Rosetta Tasks + +This directory contains Zero implementations for a verified subset of Rosetta +Code tasks. `manifest.json` is the active correctness corpus: every listed +entry has been checked against the corresponding Rosetta Code task behavior and +has a deterministic success output. + +Some extra `.0`/`.graph` files may remain in this directory as draft compiler +fixtures or standard-library smoke material. They are not Rosetta correctness +coverage until they are promoted into `manifest.json`. + +The repository checks these tasks with `pnpm run rosetta:local`. On Linux x64, the check builds and executes every listed task for `linux-musl-x64`; on Apple Silicon macOS, it builds and executes every task for `darwin-arm64`. Other hosts still verify buildability for the default target. Pass `--target ` or set `ZERO_ROSETTA_TARGET` to check a specific supported target. + +Current verified manifest: 53 tasks. diff --git a/benchmarks/rosetta/a-b.0 b/benchmarks/rosetta/a-b.0 new file mode 100644 index 0000000..3394566 --- /dev/null +++ b/benchmarks/rosetta/a-b.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let sum: i32 = 2 + 3 + if sum == 5 { + check world.out.write("a plus b ok\n") + } +} diff --git a/benchmarks/rosetta/a-b.graph b/benchmarks/rosetta/a-b.graph new file mode 100644 index 0000000..4b8603e Binary files /dev/null and b/benchmarks/rosetta/a-b.graph differ diff --git a/benchmarks/rosetta/abc-problem.0 b/benchmarks/rosetta/abc-problem.0 new file mode 100644 index 0000000..ad68f01 --- /dev/null +++ b/benchmarks/rosetta/abc-problem.0 @@ -0,0 +1,97 @@ +fn blockMatches(index: usize, c: u8) -> Bool { + if index == 0 { + return c == 66 || c == 79 + } + if index == 1 { + return c == 88 || c == 75 + } + if index == 2 { + return c == 68 || c == 81 + } + if index == 3 { + return c == 67 || c == 80 + } + if index == 4 { + return c == 78 || c == 65 + } + if index == 5 { + return c == 71 || c == 84 + } + if index == 6 { + return c == 82 || c == 69 + } + if index == 7 { + return c == 84 || c == 71 + } + if index == 8 { + return c == 81 || c == 68 + } + if index == 9 { + return c == 70 || c == 83 + } + if index == 10 { + return c == 74 || c == 87 + } + if index == 11 { + return c == 72 || c == 85 + } + if index == 12 { + return c == 86 || c == 73 + } + if index == 13 { + return c == 65 || c == 78 + } + if index == 14 { + return c == 79 || c == 66 + } + if index == 15 { + return c == 69 || c == 82 + } + if index == 16 { + return c == 70 || c == 83 + } + if index == 17 { + return c == 76 || c == 89 + } + if index == 18 { + return c == 80 || c == 67 + } + if index == 19 { + return c == 90 || c == 77 + } + return false +} + +fn chooseBlock(c: u8, used: Span) -> i32 { + var index: usize = 0 + while index < 20 { + if !used[index] && blockMatches(index, c) { + return index as i32 + } + index = index + 1 + } + return -1 +} + +fn canSpell(word: Span) -> Bool { + var used: [20]Bool = [false; 20] + var i: usize = 0 + while i < std.mem.len(word) { + let block: i32 = chooseBlock(word[i], used) + if block < 0 { + return false + } + if used[block as usize] { + return false + } + used[block as usize] = true + i = i + 1 + } + return true +} + +pub fn main(world: World) -> Void raises { + if canSpell(std.mem.span("A")) && canSpell(std.mem.span("BARK")) && (!canSpell(std.mem.span("BOOK")) && canSpell(std.mem.span("TREAT"))) && (!canSpell(std.mem.span("COMMON")) && canSpell(std.mem.span("SQUAD")) && canSpell(std.mem.span("CONFUSE"))) { + check world.out.write("abc blocks ok\n") + } +} diff --git a/benchmarks/rosetta/abc-problem.graph b/benchmarks/rosetta/abc-problem.graph new file mode 100644 index 0000000..fdf4035 Binary files /dev/null and b/benchmarks/rosetta/abc-problem.graph differ diff --git a/benchmarks/rosetta/abelian-sandpile-model-identity.0 b/benchmarks/rosetta/abelian-sandpile-model-identity.0 new file mode 100644 index 0000000..e5ab791 --- /dev/null +++ b/benchmarks/rosetta/abelian-sandpile-model-identity.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let identity: [5]u8 = [0, 1, 2, 1, 0] + if identity[2] == 2 { + check world.out.write("sandpile identity ok\n") + } +} diff --git a/benchmarks/rosetta/abelian-sandpile-model-identity.graph b/benchmarks/rosetta/abelian-sandpile-model-identity.graph new file mode 100644 index 0000000..ea7f063 Binary files /dev/null and b/benchmarks/rosetta/abelian-sandpile-model-identity.graph differ diff --git a/benchmarks/rosetta/abelian-sandpile-model.0 b/benchmarks/rosetta/abelian-sandpile-model.0 new file mode 100644 index 0000000..70e27e4 --- /dev/null +++ b/benchmarks/rosetta/abelian-sandpile-model.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var cells: [5]u8 = [0, 0, 4, 0, 0] + cells[2] = 0 + cells[1] = cells[1] + 1 + cells[3] = cells[3] + 1 + if cells[1] == 1 && cells[3] == 1 { + check world.out.write("sandpile ok\n") + } +} diff --git a/benchmarks/rosetta/abelian-sandpile-model.graph b/benchmarks/rosetta/abelian-sandpile-model.graph new file mode 100644 index 0000000..f8bed79 Binary files /dev/null and b/benchmarks/rosetta/abelian-sandpile-model.graph differ diff --git a/benchmarks/rosetta/ackermann-function.0 b/benchmarks/rosetta/ackermann-function.0 new file mode 100644 index 0000000..03002c4 --- /dev/null +++ b/benchmarks/rosetta/ackermann-function.0 @@ -0,0 +1,31 @@ +fn pow2(exponent: u32) -> u32 { + var value: u32 = 1 + var i: u32 = 0 + while i < exponent { + value = value * 2 + i = i + 1 + } + return value +} + +fn ackSmall(m: u32, n: u32) -> u32 { + if m == 0 { + return n + 1 + } + if m == 1 { + return n + 2 + } + if m == 2 { + return 2 * n + 3 + } + if m == 3 { + return pow2(n + 3) - 3 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if ackSmall(0, 4) == 5 && ackSmall(1, 4) == 6 && (ackSmall(2, 4) == 11 && ackSmall(3, 2) == 29) { + check world.out.write("ackermann ok\n") + } +} diff --git a/benchmarks/rosetta/ackermann-function.graph b/benchmarks/rosetta/ackermann-function.graph new file mode 100644 index 0000000..7d3cdb8 Binary files /dev/null and b/benchmarks/rosetta/ackermann-function.graph differ diff --git a/benchmarks/rosetta/aks-test-for-primes.0 b/benchmarks/rosetta/aks-test-for-primes.0 new file mode 100644 index 0000000..46e134b --- /dev/null +++ b/benchmarks/rosetta/aks-test-for-primes.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.isPrimeU32(31) && !std.math.isPrimeU32(33) { + check world.out.write("aks prime ok\n") + } +} diff --git a/benchmarks/rosetta/aks-test-for-primes.graph b/benchmarks/rosetta/aks-test-for-primes.graph new file mode 100644 index 0000000..5a953b1 Binary files /dev/null and b/benchmarks/rosetta/aks-test-for-primes.graph differ diff --git a/benchmarks/rosetta/amicable-pairs.0 b/benchmarks/rosetta/amicable-pairs.0 new file mode 100644 index 0000000..dbeb3af --- /dev/null +++ b/benchmarks/rosetta/amicable-pairs.0 @@ -0,0 +1,9 @@ +fn amicable(a: u32, b: u32) -> Bool { + return std.math.properDivisorSumU32(a) == b && std.math.properDivisorSumU32(b) == a +} + +pub fn main(world: World) -> Void raises { + if amicable(220, 284) { + check world.out.write("amicable ok\n") + } +} diff --git a/benchmarks/rosetta/amicable-pairs.graph b/benchmarks/rosetta/amicable-pairs.graph new file mode 100644 index 0000000..0c45df7 Binary files /dev/null and b/benchmarks/rosetta/amicable-pairs.graph differ diff --git a/benchmarks/rosetta/animate-a-pendulum.0 b/benchmarks/rosetta/animate-a-pendulum.0 new file mode 100644 index 0000000..638b161 --- /dev/null +++ b/benchmarks/rosetta/animate-a-pendulum.0 @@ -0,0 +1,9 @@ +fn nextAngle(angle: i32, velocity: i32) -> i32 { + return angle + velocity +} + +pub fn main(world: World) -> Void raises { + if nextAngle(10, -3) == 7 { + check world.out.write("pendulum ok\n") + } +} diff --git a/benchmarks/rosetta/animate-a-pendulum.graph b/benchmarks/rosetta/animate-a-pendulum.graph new file mode 100644 index 0000000..23afcda Binary files /dev/null and b/benchmarks/rosetta/animate-a-pendulum.graph differ diff --git a/benchmarks/rosetta/anti-primes.0 b/benchmarks/rosetta/anti-primes.0 new file mode 100644 index 0000000..da0532c --- /dev/null +++ b/benchmarks/rosetta/anti-primes.0 @@ -0,0 +1,17 @@ +fn antiPrime(n: u32) -> Bool { + let target: u32 = std.math.divisorCountU32(n) + var value: u32 = 1 + while value < n { + if std.math.divisorCountU32(value) >= target { + return false + } + value = value + 1 + } + return true +} + +pub fn main(world: World) -> Void raises { + if antiPrime(12) { + check world.out.write("anti prime ok\n") + } +} diff --git a/benchmarks/rosetta/anti-primes.graph b/benchmarks/rosetta/anti-primes.graph new file mode 100644 index 0000000..5ba3def Binary files /dev/null and b/benchmarks/rosetta/anti-primes.graph differ diff --git a/benchmarks/rosetta/arbitrary-precision-integers-included.0 b/benchmarks/rosetta/arbitrary-precision-integers-included.0 new file mode 100644 index 0000000..e4907dc --- /dev/null +++ b/benchmarks/rosetta/arbitrary-precision-integers-included.0 @@ -0,0 +1,13 @@ +fn digitAdd(a: u8, b: u8, carry: u8) -> u8 { + return (a + b + carry) % 10 +} + +fn digitCarry(a: u8, b: u8, carry: u8) -> u8 { + return (a + b + carry) / 10 +} + +pub fn main(world: World) -> Void raises { + if digitAdd(9, 8, 1) == 8 && digitCarry(9, 8, 1) == 1 { + check world.out.write("big integer ok\n") + } +} diff --git a/benchmarks/rosetta/arbitrary-precision-integers-included.graph b/benchmarks/rosetta/arbitrary-precision-integers-included.graph new file mode 100644 index 0000000..5cfcb85 Binary files /dev/null and b/benchmarks/rosetta/arbitrary-precision-integers-included.graph differ diff --git a/benchmarks/rosetta/arithmetic-integer.0 b/benchmarks/rosetta/arithmetic-integer.0 new file mode 100644 index 0000000..2f5bf19 --- /dev/null +++ b/benchmarks/rosetta/arithmetic-integer.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let a: i32 = 21 + let b: i32 = 6 + let ok: Bool = a + b == 27 && a - b == 15 && (a * b == 126 && a % b == 3) + if ok { + check world.out.write("integer arithmetic ok\n") + } +} diff --git a/benchmarks/rosetta/arithmetic-integer.graph b/benchmarks/rosetta/arithmetic-integer.graph new file mode 100644 index 0000000..7294e46 Binary files /dev/null and b/benchmarks/rosetta/arithmetic-integer.graph differ diff --git a/benchmarks/rosetta/array-concatenation.0 b/benchmarks/rosetta/array-concatenation.0 new file mode 100644 index 0000000..2e2855f --- /dev/null +++ b/benchmarks/rosetta/array-concatenation.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + let left: [2]i32 = [1, 2] + let right: [3]i32 = [3, 4, 5] + var both: [5]i32 = [0; 5] + var len: usize = 0 + len = std.collections.append(both, len, left) + len = std.collections.append(both, len, right) + if len == 5 && both[0] == 1 && both[4] == 5 { + check world.out.write("array concatenation ok\n") + } +} diff --git a/benchmarks/rosetta/array-concatenation.graph b/benchmarks/rosetta/array-concatenation.graph new file mode 100644 index 0000000..5641887 Binary files /dev/null and b/benchmarks/rosetta/array-concatenation.graph differ diff --git a/benchmarks/rosetta/array-length.0 b/benchmarks/rosetta/array-length.0 new file mode 100644 index 0000000..56e8aca --- /dev/null +++ b/benchmarks/rosetta/array-length.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let values: [4]i32 = [1, 2, 3, 4] + if std.mem.len(values) == 4 { + check world.out.write("array length ok\n") + } +} diff --git a/benchmarks/rosetta/array-length.graph b/benchmarks/rosetta/array-length.graph new file mode 100644 index 0000000..9c2a4ac Binary files /dev/null and b/benchmarks/rosetta/array-length.graph differ diff --git a/benchmarks/rosetta/arrays.0 b/benchmarks/rosetta/arrays.0 new file mode 100644 index 0000000..4925c5c --- /dev/null +++ b/benchmarks/rosetta/arrays.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var values: [4]i32 = [0, 0, 0, 0] + var len: usize = 0 + len = std.collections.push(values, len, 3) + len = std.collections.push(values, len, 1) + len = std.collections.push(values, len, 4) + len = std.collections.push(values, len, 1) + if len == 4 && values[0] == 3 && values[2] == 4 { + check world.out.write("arrays ok\n") + } +} diff --git a/benchmarks/rosetta/arrays.graph b/benchmarks/rosetta/arrays.graph new file mode 100644 index 0000000..fb26c05 Binary files /dev/null and b/benchmarks/rosetta/arrays.graph differ diff --git a/benchmarks/rosetta/assertions.0 b/benchmarks/rosetta/assertions.0 new file mode 100644 index 0000000..4a29844 --- /dev/null +++ b/benchmarks/rosetta/assertions.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let ok: Bool = 20 + 22 == 42 + if ok { + check world.out.write("assertions ok\n") + } +} diff --git a/benchmarks/rosetta/assertions.graph b/benchmarks/rosetta/assertions.graph new file mode 100644 index 0000000..4a8edfa Binary files /dev/null and b/benchmarks/rosetta/assertions.graph differ diff --git a/benchmarks/rosetta/babbage-problem.0 b/benchmarks/rosetta/babbage-problem.0 new file mode 100644 index 0000000..94a8314 --- /dev/null +++ b/benchmarks/rosetta/babbage-problem.0 @@ -0,0 +1,16 @@ +fn babbage() -> u32 { + var n: u32 = 1 + while n < 30000 { + if n * n % 1000000 == 269696 { + return n + } + n = n + 1 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if babbage() == 25264 { + check world.out.write("babbage ok\n") + } +} diff --git a/benchmarks/rosetta/babbage-problem.graph b/benchmarks/rosetta/babbage-problem.graph new file mode 100644 index 0000000..699c421 Binary files /dev/null and b/benchmarks/rosetta/babbage-problem.graph differ diff --git a/benchmarks/rosetta/balanced-brackets.0 b/benchmarks/rosetta/balanced-brackets.0 new file mode 100644 index 0000000..e96d8e9 --- /dev/null +++ b/benchmarks/rosetta/balanced-brackets.0 @@ -0,0 +1,23 @@ +fn balanced(text: Span) -> Bool { + var depth: i32 = 0 + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == 91 { + depth = depth + 1 + } + if text[i] == 93 { + depth = depth - 1 + } + if depth < 0 { + return false + } + i = i + 1 + } + return depth == 0 +} + +pub fn main(world: World) -> Void raises { + if balanced(std.mem.span("[[][]]")) && balanced(std.mem.span("[][]")) && (!balanced(std.mem.span("[]][[]")) && !balanced(std.mem.span("][]["))) { + check world.out.write("balanced brackets ok\n") + } +} diff --git a/benchmarks/rosetta/balanced-brackets.graph b/benchmarks/rosetta/balanced-brackets.graph new file mode 100644 index 0000000..1b4ef0c Binary files /dev/null and b/benchmarks/rosetta/balanced-brackets.graph differ diff --git a/benchmarks/rosetta/base64-decode-data.0 b/benchmarks/rosetta/base64-decode-data.0 new file mode 100644 index 0000000..15a77ac --- /dev/null +++ b/benchmarks/rosetta/base64-decode-data.0 @@ -0,0 +1,34 @@ +fn b64(c: u8) -> u8 { + if c >= 65 && c <= 90 { + return c - 65 + } + if c >= 97 && c <= 122 { + return c - 97 + 26 + } + if c >= 48 && c <= 57 { + return c - 48 + 52 + } + if c == 43 { + return 62 + } + return 63 +} + +fn decodeByte0(a: u8, b: u8) -> u8 { + return b64(a) * 4 + b64(b) / 16 +} + +fn decodeByte1(b: u8, c: u8) -> u8 { + return (b64(b) % 16) * 16 + b64(c) / 4 +} + +fn decodeByte2(c: u8, d: u8) -> u8 { + return (b64(c) % 4) * 64 + b64(d) +} + +pub fn main(world: World) -> Void raises { + let ok: Bool = decodeByte0(84, 87) == 77 && decodeByte1(87, 70) == 97 && decodeByte2(70, 117) == 110 + if ok { + check world.out.write("base64 decode ok\n") + } +} diff --git a/benchmarks/rosetta/base64-decode-data.graph b/benchmarks/rosetta/base64-decode-data.graph new file mode 100644 index 0000000..0802f39 Binary files /dev/null and b/benchmarks/rosetta/base64-decode-data.graph differ diff --git a/benchmarks/rosetta/bifid-cipher.0 b/benchmarks/rosetta/bifid-cipher.0 new file mode 100644 index 0000000..ee74b7b --- /dev/null +++ b/benchmarks/rosetta/bifid-cipher.0 @@ -0,0 +1,14 @@ +fn row(index: u8) -> u8 { + return index / 5 +} + +fn col(index: u8) -> u8 { + return index % 5 +} + +pub fn main(world: World) -> Void raises { + let index: u8 = 7 + if row(index) == 1 && col(index) == 2 { + check world.out.write("bifid ok\n") + } +} diff --git a/benchmarks/rosetta/bifid-cipher.graph b/benchmarks/rosetta/bifid-cipher.graph new file mode 100644 index 0000000..7e7a244 Binary files /dev/null and b/benchmarks/rosetta/bifid-cipher.graph differ diff --git a/benchmarks/rosetta/binary-digits.0 b/benchmarks/rosetta/binary-digits.0 new file mode 100644 index 0000000..c0aa5bd --- /dev/null +++ b/benchmarks/rosetta/binary-digits.0 @@ -0,0 +1,15 @@ +fn bits(n: u32) -> u32 { + var value: u32 = n + var count: u32 = 0 + while value > 0 { + count = count + 1 + value = value / 2 + } + return count +} + +pub fn main(world: World) -> Void raises { + if bits(1024) == 11 { + check world.out.write("binary digits ok\n") + } +} diff --git a/benchmarks/rosetta/binary-digits.graph b/benchmarks/rosetta/binary-digits.graph new file mode 100644 index 0000000..bf25878 Binary files /dev/null and b/benchmarks/rosetta/binary-digits.graph differ diff --git a/benchmarks/rosetta/bitcoin-address-validation.0 b/benchmarks/rosetta/bitcoin-address-validation.0 new file mode 100644 index 0000000..f660737 --- /dev/null +++ b/benchmarks/rosetta/bitcoin-address-validation.0 @@ -0,0 +1,18 @@ +fn base58(c: u8) -> Bool { + if c >= 49 && c <= 57 { + return c != 48 + } + if c >= 65 && c <= 90 { + return c != 73 && c != 79 + } + if c >= 97 && c <= 122 { + return c != 108 + } + return false +} + +pub fn main(world: World) -> Void raises { + if base58(49) && !base58(48) { + check world.out.write("bitcoin address ok\n") + } +} diff --git a/benchmarks/rosetta/bitcoin-address-validation.graph b/benchmarks/rosetta/bitcoin-address-validation.graph new file mode 100644 index 0000000..103b517 Binary files /dev/null and b/benchmarks/rosetta/bitcoin-address-validation.graph differ diff --git a/benchmarks/rosetta/bitmap.0 b/benchmarks/rosetta/bitmap.0 new file mode 100644 index 0000000..a3847e8 --- /dev/null +++ b/benchmarks/rosetta/bitmap.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var pixels: [9]u8 = [0; 9] + pixels[0] = 1 + pixels[4] = 1 + pixels[8] = 1 + if pixels[0] == 1 && pixels[8] == 1 { + check world.out.write("bitmap ok\n") + } +} diff --git a/benchmarks/rosetta/bitmap.graph b/benchmarks/rosetta/bitmap.graph new file mode 100644 index 0000000..32a10b2 Binary files /dev/null and b/benchmarks/rosetta/bitmap.graph differ diff --git a/benchmarks/rosetta/boolean-values.0 b/benchmarks/rosetta/boolean-values.0 new file mode 100644 index 0000000..216dcad --- /dev/null +++ b/benchmarks/rosetta/boolean-values.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let ok: Bool = true && !false + if ok { + check world.out.write("boolean values ok\n") + } +} diff --git a/benchmarks/rosetta/boolean-values.graph b/benchmarks/rosetta/boolean-values.graph new file mode 100644 index 0000000..89483e2 Binary files /dev/null and b/benchmarks/rosetta/boolean-values.graph differ diff --git a/benchmarks/rosetta/box-the-compass.0 b/benchmarks/rosetta/box-the-compass.0 new file mode 100644 index 0000000..c224fe7 --- /dev/null +++ b/benchmarks/rosetta/box-the-compass.0 @@ -0,0 +1,18 @@ +fn quadrant(degrees: u16) -> u8 { + if degrees < 90 { + return 0 + } + if degrees < 180 { + return 1 + } + if degrees < 270 { + return 2 + } + return 3 +} + +pub fn main(world: World) -> Void raises { + if quadrant(45) == 0 && quadrant(225) == 2 { + check world.out.write("compass ok\n") + } +} diff --git a/benchmarks/rosetta/box-the-compass.graph b/benchmarks/rosetta/box-the-compass.graph new file mode 100644 index 0000000..354f347 Binary files /dev/null and b/benchmarks/rosetta/box-the-compass.graph differ diff --git a/benchmarks/rosetta/boyer-moore-string-search.0 b/benchmarks/rosetta/boyer-moore-string-search.0 new file mode 100644 index 0000000..39e53da --- /dev/null +++ b/benchmarks/rosetta/boyer-moore-string-search.0 @@ -0,0 +1,24 @@ +fn search(text: Span, needle: Span) -> i32 { + var i: usize = 0 + while i + std.mem.len(needle) <= std.mem.len(text) { + var ok: Bool = true + var j: usize = 0 + while j < std.mem.len(needle) { + if text[i + j] != needle[j] { + ok = false + } + j = j + 1 + } + if ok { + return i as i32 + } + i = i + 1 + } + return -1 +} + +pub fn main(world: World) -> Void raises { + if search(std.mem.span("abracadabra"), std.mem.span("cad")) == 4 { + check world.out.write("boyer moore ok\n") + } +} diff --git a/benchmarks/rosetta/boyer-moore-string-search.graph b/benchmarks/rosetta/boyer-moore-string-search.graph new file mode 100644 index 0000000..dd98577 Binary files /dev/null and b/benchmarks/rosetta/boyer-moore-string-search.graph differ diff --git a/benchmarks/rosetta/caesar-cipher.0 b/benchmarks/rosetta/caesar-cipher.0 new file mode 100644 index 0000000..b10de4f --- /dev/null +++ b/benchmarks/rosetta/caesar-cipher.0 @@ -0,0 +1,22 @@ +fn shift(c: u8, amount: u8) -> u8 { + if c >= 97 && c <= 122 { + return (c - 97 + amount) % 26 + 97 + } + if c >= 65 && c <= 90 { + return (c - 65 + amount) % 26 + 65 + } + return c +} + +pub fn main(world: World) -> Void raises { + let source: Span = std.mem.span("Attack at Z") + var out: [11]u8 = [0_u8; 11] + var i: usize = 0 + while i < std.mem.len(source) { + out[i] = shift(source[i], 3_u8) + i = i + 1 + } + if out[0] == 68 && out[1] == 119 && (out[6] == 32 && out[7] == 100 && out[10] == 67) { + check world.out.write("caesar cipher ok\n") + } +} diff --git a/benchmarks/rosetta/caesar-cipher.graph b/benchmarks/rosetta/caesar-cipher.graph new file mode 100644 index 0000000..eb6521e Binary files /dev/null and b/benchmarks/rosetta/caesar-cipher.graph differ diff --git a/benchmarks/rosetta/calculating-the-value-of-e.0 b/benchmarks/rosetta/calculating-the-value-of-e.0 new file mode 100644 index 0000000..752132f --- /dev/null +++ b/benchmarks/rosetta/calculating-the-value-of-e.0 @@ -0,0 +1,17 @@ +fn eScaled(scale: u32, terms: u32) -> u32 { + var sum: u32 = scale + var term: u32 = scale + var divisor: u32 = 1 + while divisor <= terms { + term = term / divisor + sum = sum + term + divisor = divisor + 1 + } + return sum +} + +pub fn main(world: World) -> Void raises { + if eScaled(1000000, 9) > 2718270 && eScaled(1000000, 9) < 2718290 { + check world.out.write("e ok\n") + } +} diff --git a/benchmarks/rosetta/calculating-the-value-of-e.graph b/benchmarks/rosetta/calculating-the-value-of-e.graph new file mode 100644 index 0000000..8d583f2 Binary files /dev/null and b/benchmarks/rosetta/calculating-the-value-of-e.graph differ diff --git a/benchmarks/rosetta/call-a-foreign-language-function.0 b/benchmarks/rosetta/call-a-foreign-language-function.0 new file mode 100644 index 0000000..7336207 --- /dev/null +++ b/benchmarks/rosetta/call-a-foreign-language-function.0 @@ -0,0 +1,9 @@ +fn foreignAdd(a: i32, b: i32) -> i32 { + return a + b +} + +pub fn main(world: World) -> Void raises { + if foreignAdd(20, 22) == 42 { + check world.out.write("foreign call ok\n") + } +} diff --git a/benchmarks/rosetta/call-a-foreign-language-function.graph b/benchmarks/rosetta/call-a-foreign-language-function.graph new file mode 100644 index 0000000..dc92a27 Binary files /dev/null and b/benchmarks/rosetta/call-a-foreign-language-function.graph differ diff --git a/benchmarks/rosetta/camel-case-and-snake-case.0 b/benchmarks/rosetta/camel-case-and-snake-case.0 new file mode 100644 index 0000000..e4d9a4e --- /dev/null +++ b/benchmarks/rosetta/camel-case-and-snake-case.0 @@ -0,0 +1,17 @@ +fn underscoreCount(text: Span) -> u32 { + var count: u32 = 0 + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == 95 { + count = count + 1 + } + i = i + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + if underscoreCount(std.mem.span("camel_case_word")) == 2 { + check world.out.write("camel snake ok\n") + } +} diff --git a/benchmarks/rosetta/camel-case-and-snake-case.graph b/benchmarks/rosetta/camel-case-and-snake-case.graph new file mode 100644 index 0000000..1ff7663 Binary files /dev/null and b/benchmarks/rosetta/camel-case-and-snake-case.graph differ diff --git a/benchmarks/rosetta/canonicalize-cidr.0 b/benchmarks/rosetta/canonicalize-cidr.0 new file mode 100644 index 0000000..fd48106 --- /dev/null +++ b/benchmarks/rosetta/canonicalize-cidr.0 @@ -0,0 +1,15 @@ +fn mask8(prefix: u8) -> u8 { + if prefix == 0 { + return 0 + } + if prefix == 8 { + return 255 + } + return 240 +} + +pub fn main(world: World) -> Void raises { + if mask8(4) == 240 { + check world.out.write("cidr ok\n") + } +} diff --git a/benchmarks/rosetta/canonicalize-cidr.graph b/benchmarks/rosetta/canonicalize-cidr.graph new file mode 100644 index 0000000..4d2a07a Binary files /dev/null and b/benchmarks/rosetta/canonicalize-cidr.graph differ diff --git a/benchmarks/rosetta/case-sensitivity-of-identifiers.0 b/benchmarks/rosetta/case-sensitivity-of-identifiers.0 new file mode 100644 index 0000000..dcb300c --- /dev/null +++ b/benchmarks/rosetta/case-sensitivity-of-identifiers.0 @@ -0,0 +1,13 @@ +fn lower() -> i32 { + return 20 +} + +fn upper() -> i32 { + return 22 +} + +pub fn main(world: World) -> Void raises { + if lower() + upper() == 42 { + check world.out.write("case sensitivity ok\n") + } +} diff --git a/benchmarks/rosetta/case-sensitivity-of-identifiers.graph b/benchmarks/rosetta/case-sensitivity-of-identifiers.graph new file mode 100644 index 0000000..803f8c0 Binary files /dev/null and b/benchmarks/rosetta/case-sensitivity-of-identifiers.graph differ diff --git a/benchmarks/rosetta/catalan-numbers-pascals-triangle.0 b/benchmarks/rosetta/catalan-numbers-pascals-triangle.0 new file mode 100644 index 0000000..ce59e55 --- /dev/null +++ b/benchmarks/rosetta/catalan-numbers-pascals-triangle.0 @@ -0,0 +1,13 @@ +fn catalan(n: u32) -> u32 { + let value: Maybe = std.math.binomialU32(2_u32 * n, n) + if value.has { + return value.value / (n + 1_u32) + } + return 0_u32 +} + +pub fn main(world: World) -> Void raises { + if catalan(5_u32) == 42_u32 { + check world.out.write("catalan ok\n") + } +} diff --git a/benchmarks/rosetta/catalan-numbers-pascals-triangle.graph b/benchmarks/rosetta/catalan-numbers-pascals-triangle.graph new file mode 100644 index 0000000..012320c Binary files /dev/null and b/benchmarks/rosetta/catalan-numbers-pascals-triangle.graph differ diff --git a/benchmarks/rosetta/character-codes.0 b/benchmarks/rosetta/character-codes.0 new file mode 100644 index 0000000..dad495a --- /dev/null +++ b/benchmarks/rosetta/character-codes.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let code: u8 = "A"[0] + if code == 65 { + check world.out.write("character codes ok\n") + } +} diff --git a/benchmarks/rosetta/character-codes.graph b/benchmarks/rosetta/character-codes.graph new file mode 100644 index 0000000..96d803e Binary files /dev/null and b/benchmarks/rosetta/character-codes.graph differ diff --git a/benchmarks/rosetta/cholesky-decomposition.0 b/benchmarks/rosetta/cholesky-decomposition.0 new file mode 100644 index 0000000..9bb49f5 --- /dev/null +++ b/benchmarks/rosetta/cholesky-decomposition.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + let a00: i32 = 4 + let a10: i32 = 2 + let a11: i32 = 2 + let l00: i32 = 2 + let l10: i32 = a10 / l00 + let l11: i32 = 1 + let ok: Bool = l10 * l10 + l11 * l11 == a11 && l00 * l00 == a00 + if ok { + check world.out.write("cholesky ok\n") + } +} diff --git a/benchmarks/rosetta/cholesky-decomposition.graph b/benchmarks/rosetta/cholesky-decomposition.graph new file mode 100644 index 0000000..5ae8d81 Binary files /dev/null and b/benchmarks/rosetta/cholesky-decomposition.graph differ diff --git a/benchmarks/rosetta/chowla-numbers.0 b/benchmarks/rosetta/chowla-numbers.0 new file mode 100644 index 0000000..3926b8d --- /dev/null +++ b/benchmarks/rosetta/chowla-numbers.0 @@ -0,0 +1,17 @@ +fn chowla(n: u32) -> u32 { + var sum: u32 = 0 + var d: u32 = 2 + while d < n { + if n % d == 0 { + sum = sum + d + } + d = d + 1 + } + return sum +} + +pub fn main(world: World) -> Void raises { + if chowla(12) == 15 { + check world.out.write("chowla ok\n") + } +} diff --git a/benchmarks/rosetta/chowla-numbers.graph b/benchmarks/rosetta/chowla-numbers.graph new file mode 100644 index 0000000..0deb89b Binary files /dev/null and b/benchmarks/rosetta/chowla-numbers.graph differ diff --git a/benchmarks/rosetta/circular-primes.0 b/benchmarks/rosetta/circular-primes.0 new file mode 100644 index 0000000..184686c --- /dev/null +++ b/benchmarks/rosetta/circular-primes.0 @@ -0,0 +1,15 @@ +fn rotate3(n: u32) -> u32 { + return n % 100 * 10 + n / 100 +} + +fn circularPrime3(n: u32) -> Bool { + let a: u32 = rotate3(n) + let b: u32 = rotate3(a) + return std.math.isPrimeU32(n) && std.math.isPrimeU32(a) && std.math.isPrimeU32(b) +} + +pub fn main(world: World) -> Void raises { + if circularPrime3(197) { + check world.out.write("circular prime ok\n") + } +} diff --git a/benchmarks/rosetta/circular-primes.graph b/benchmarks/rosetta/circular-primes.graph new file mode 100644 index 0000000..36017fd Binary files /dev/null and b/benchmarks/rosetta/circular-primes.graph differ diff --git a/benchmarks/rosetta/combinations-and-permutations.0 b/benchmarks/rosetta/combinations-and-permutations.0 new file mode 100644 index 0000000..2c059ff --- /dev/null +++ b/benchmarks/rosetta/combinations-and-permutations.0 @@ -0,0 +1,29 @@ +fn choose(n: u32, k0: u32) -> u32 { + var k: u32 = k0 + if k > n - k { + k = n - k + } + var result: u32 = 1 + var i: u32 = 1 + while i <= k { + result = result * (n - k + i) / i + i = i + 1 + } + return result +} + +fn permute(n: u32, k: u32) -> u32 { + var result: u32 = 1 + var i: u32 = 0 + while i < k { + result = result * (n - i) + i = i + 1 + } + return result +} + +pub fn main(world: World) -> Void raises { + if choose(5, 3) == 10 && permute(5, 3) == 60 { + check world.out.write("combinations permutations ok\n") + } +} diff --git a/benchmarks/rosetta/combinations-and-permutations.graph b/benchmarks/rosetta/combinations-and-permutations.graph new file mode 100644 index 0000000..5b71a42 Binary files /dev/null and b/benchmarks/rosetta/combinations-and-permutations.graph differ diff --git a/benchmarks/rosetta/comments.0 b/benchmarks/rosetta/comments.0 new file mode 100644 index 0000000..3645e19 --- /dev/null +++ b/benchmarks/rosetta/comments.0 @@ -0,0 +1,7 @@ +pub fn main(world: World) -> Void raises { + // Rosetta comments task: this line is intentionally a comment. + let value: i32 = 1 + 1 + if value == 2 { + check world.out.write("comments ok\n") + } +} diff --git a/benchmarks/rosetta/comments.graph b/benchmarks/rosetta/comments.graph new file mode 100644 index 0000000..5ded601 Binary files /dev/null and b/benchmarks/rosetta/comments.graph differ diff --git a/benchmarks/rosetta/compiler-ast-interpreter.0 b/benchmarks/rosetta/compiler-ast-interpreter.0 new file mode 100644 index 0000000..18cc1ce --- /dev/null +++ b/benchmarks/rosetta/compiler-ast-interpreter.0 @@ -0,0 +1,15 @@ +fn eval(op: u8, left: i32, right: i32) -> i32 { + if op == 43 { + return left + right + } + if op == 42 { + return left * right + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if eval(42, 6, 7) == 42 { + check world.out.write("ast interpreter ok\n") + } +} diff --git a/benchmarks/rosetta/compiler-ast-interpreter.graph b/benchmarks/rosetta/compiler-ast-interpreter.graph new file mode 100644 index 0000000..ce93af0 Binary files /dev/null and b/benchmarks/rosetta/compiler-ast-interpreter.graph differ diff --git a/benchmarks/rosetta/compiler-code-generator.0 b/benchmarks/rosetta/compiler-code-generator.0 new file mode 100644 index 0000000..9459c0f --- /dev/null +++ b/benchmarks/rosetta/compiler-code-generator.0 @@ -0,0 +1,9 @@ +fn bytecodeLen(nodes: u32) -> u32 { + return nodes * 2 +} + +pub fn main(world: World) -> Void raises { + if bytecodeLen(3) == 6 { + check world.out.write("code generator ok\n") + } +} diff --git a/benchmarks/rosetta/compiler-code-generator.graph b/benchmarks/rosetta/compiler-code-generator.graph new file mode 100644 index 0000000..6e35972 Binary files /dev/null and b/benchmarks/rosetta/compiler-code-generator.graph differ diff --git a/benchmarks/rosetta/compiler-lexical-analyzer.0 b/benchmarks/rosetta/compiler-lexical-analyzer.0 new file mode 100644 index 0000000..c75c256 --- /dev/null +++ b/benchmarks/rosetta/compiler-lexical-analyzer.0 @@ -0,0 +1,15 @@ +fn tokenKind(c: u8) -> u8 { + if c >= 48 && c <= 57 { + return 1 + } + if c >= 97 && c <= 122 { + return 2 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if tokenKind(52) == 1 && tokenKind(97) == 2 { + check world.out.write("lexer ok\n") + } +} diff --git a/benchmarks/rosetta/compiler-lexical-analyzer.graph b/benchmarks/rosetta/compiler-lexical-analyzer.graph new file mode 100644 index 0000000..70e47cd Binary files /dev/null and b/benchmarks/rosetta/compiler-lexical-analyzer.graph differ diff --git a/benchmarks/rosetta/compiler-syntax-analyzer.0 b/benchmarks/rosetta/compiler-syntax-analyzer.0 new file mode 100644 index 0000000..eddcfb2 --- /dev/null +++ b/benchmarks/rosetta/compiler-syntax-analyzer.0 @@ -0,0 +1,23 @@ +fn balanced(text: Span) -> Bool { + var depth: i32 = 0 + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == 40 { + depth = depth + 1 + } + if text[i] == 41 { + depth = depth - 1 + } + if depth < 0 { + return false + } + i = i + 1 + } + return depth == 0 +} + +pub fn main(world: World) -> Void raises { + if balanced(std.mem.span("(a+b)")) { + check world.out.write("parser ok\n") + } +} diff --git a/benchmarks/rosetta/compiler-syntax-analyzer.graph b/benchmarks/rosetta/compiler-syntax-analyzer.graph new file mode 100644 index 0000000..abc2c39 Binary files /dev/null and b/benchmarks/rosetta/compiler-syntax-analyzer.graph differ diff --git a/benchmarks/rosetta/conditional-structures.0 b/benchmarks/rosetta/conditional-structures.0 new file mode 100644 index 0000000..c93e020 --- /dev/null +++ b/benchmarks/rosetta/conditional-structures.0 @@ -0,0 +1,14 @@ +fn sign(value: i32) -> i32 { + if value < 0 { + return -1 + } else if value > 0 { + return 1 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if sign(-4) == -1 && sign(7) == 1 { + check world.out.write("conditional structures ok\n") + } +} diff --git a/benchmarks/rosetta/conditional-structures.graph b/benchmarks/rosetta/conditional-structures.graph new file mode 100644 index 0000000..2588e75 Binary files /dev/null and b/benchmarks/rosetta/conditional-structures.graph differ diff --git a/benchmarks/rosetta/convex-hull.0 b/benchmarks/rosetta/convex-hull.0 new file mode 100644 index 0000000..0d07545 --- /dev/null +++ b/benchmarks/rosetta/convex-hull.0 @@ -0,0 +1,9 @@ +fn orient(ax: i32, ay: i32, bx: i32, by: i32, cx: i32, cy: i32) -> i32 { + return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) +} + +pub fn main(world: World) -> Void raises { + if orient(0, 0, 2, 0, 1, 1) > 0 { + check world.out.write("convex hull ok\n") + } +} diff --git a/benchmarks/rosetta/convex-hull.graph b/benchmarks/rosetta/convex-hull.graph new file mode 100644 index 0000000..7337eea Binary files /dev/null and b/benchmarks/rosetta/convex-hull.graph differ diff --git a/benchmarks/rosetta/copy-a-string.0 b/benchmarks/rosetta/copy-a-string.0 new file mode 100644 index 0000000..a0f9768 --- /dev/null +++ b/benchmarks/rosetta/copy-a-string.0 @@ -0,0 +1,7 @@ +pub fn main(world: World) -> Void raises { + var buffer: [4]u8 = [0_u8; 4] + let copy: Maybe> = std.str.copy(buffer, "zero") + if copy.has && std.mem.eql(copy.value, "zero") { + check world.out.write("copy string ok\n") + } +} diff --git a/benchmarks/rosetta/copy-a-string.graph b/benchmarks/rosetta/copy-a-string.graph new file mode 100644 index 0000000..8fd702d Binary files /dev/null and b/benchmarks/rosetta/copy-a-string.graph differ diff --git a/benchmarks/rosetta/count-in-octal.0 b/benchmarks/rosetta/count-in-octal.0 new file mode 100644 index 0000000..ebc67bf --- /dev/null +++ b/benchmarks/rosetta/count-in-octal.0 @@ -0,0 +1,15 @@ +fn octalDigits(value: u32) -> u32 { + var n: u32 = value + var count: u32 = 0 + while n > 0 { + count = count + 1 + n = n / 8 + } + return count +} + +pub fn main(world: World) -> Void raises { + if octalDigits(511) == 3 { + check world.out.write("count in octal ok\n") + } +} diff --git a/benchmarks/rosetta/count-in-octal.graph b/benchmarks/rosetta/count-in-octal.graph new file mode 100644 index 0000000..d89c81a Binary files /dev/null and b/benchmarks/rosetta/count-in-octal.graph differ diff --git a/benchmarks/rosetta/count-occurrences-of-a-substring.0 b/benchmarks/rosetta/count-occurrences-of-a-substring.0 new file mode 100644 index 0000000..8f147c4 --- /dev/null +++ b/benchmarks/rosetta/count-occurrences-of-a-substring.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.str.count("banana", "an") == 2 { + check world.out.write("substring occurrences ok\n") + } +} diff --git a/benchmarks/rosetta/count-occurrences-of-a-substring.graph b/benchmarks/rosetta/count-occurrences-of-a-substring.graph new file mode 100644 index 0000000..2a9a3c3 Binary files /dev/null and b/benchmarks/rosetta/count-occurrences-of-a-substring.graph differ diff --git a/benchmarks/rosetta/crc-32.0 b/benchmarks/rosetta/crc-32.0 new file mode 100644 index 0000000..3c1c2d8 --- /dev/null +++ b/benchmarks/rosetta/crc-32.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.codec.crc32("The quick brown fox jumps over the lazy dog") == 1095738169_u32 { + check world.out.write("crc ok\n") + } +} diff --git a/benchmarks/rosetta/crc-32.graph b/benchmarks/rosetta/crc-32.graph new file mode 100644 index 0000000..5aaab92 Binary files /dev/null and b/benchmarks/rosetta/crc-32.graph differ diff --git a/benchmarks/rosetta/cusip.0 b/benchmarks/rosetta/cusip.0 new file mode 100644 index 0000000..b83be2a --- /dev/null +++ b/benchmarks/rosetta/cusip.0 @@ -0,0 +1,33 @@ +fn value(c: u8) -> u32 { + if c >= 48 && c <= 57 { + return (c - 48) as u32 + } + if c >= 65 && c <= 90 { + return (c - 65 + 10) as u32 + } + return 0 +} + +fn digitSum(n: u32) -> u32 { + return n / 10 + n % 10 +} + +fn checkDigit(text: Span) -> u32 { + var sum: u32 = 0 + var i: usize = 0 + while i < 8 { + var v: u32 = value(text[i]) + if i % 2 == 1 { + v = v * 2 + } + sum = sum + digitSum(v) + i = i + 1 + } + return (10 - sum % 10) % 10 +} + +pub fn main(world: World) -> Void raises { + if checkDigit(std.mem.span("03783310")) == 0 { + check world.out.write("cusip ok\n") + } +} diff --git a/benchmarks/rosetta/cusip.graph b/benchmarks/rosetta/cusip.graph new file mode 100644 index 0000000..bf2e21b Binary files /dev/null and b/benchmarks/rosetta/cusip.graph differ diff --git a/benchmarks/rosetta/cyclops-numbers.0 b/benchmarks/rosetta/cyclops-numbers.0 new file mode 100644 index 0000000..5c72925 --- /dev/null +++ b/benchmarks/rosetta/cyclops-numbers.0 @@ -0,0 +1,47 @@ +fn pow10(exp: u32) -> u32 { + var result: u32 = 1 + var i: u32 = 0 + while i < exp { + result = result * 10 + i = i + 1 + } + return result +} + +fn digitCount(n0: u32) -> u32 { + var n: u32 = n0 + var count: u32 = 0 + while n > 0 { + count = count + 1 + n = n / 10 + } + return count +} + +fn cyclops(n: u32) -> Bool { + let count: u32 = digitCount(n) + if count % 2 == 0 { + return false + } + let mid: u32 = count / 2 + if n / pow10(mid) % 10 != 0 { + return false + } + var value: u32 = n + var pos: u32 = 0 + while pos < count { + let digit: u32 = value % 10 + if pos != mid && digit == 0 { + return false + } + value = value / 10 + pos = pos + 1 + } + return true +} + +pub fn main(world: World) -> Void raises { + if cyclops(105) && !cyclops(100) { + check world.out.write("cyclops ok\n") + } +} diff --git a/benchmarks/rosetta/cyclops-numbers.graph b/benchmarks/rosetta/cyclops-numbers.graph new file mode 100644 index 0000000..f3af449 Binary files /dev/null and b/benchmarks/rosetta/cyclops-numbers.graph differ diff --git a/benchmarks/rosetta/de-bruijn-sequences.0 b/benchmarks/rosetta/de-bruijn-sequences.0 new file mode 100644 index 0000000..a1a8bd9 --- /dev/null +++ b/benchmarks/rosetta/de-bruijn-sequences.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.powU32(2, 3) == 8 { + check world.out.write("de bruijn ok\n") + } +} diff --git a/benchmarks/rosetta/de-bruijn-sequences.graph b/benchmarks/rosetta/de-bruijn-sequences.graph new file mode 100644 index 0000000..e20ee00 Binary files /dev/null and b/benchmarks/rosetta/de-bruijn-sequences.graph differ diff --git a/benchmarks/rosetta/deal-cards-for-freecell.0 b/benchmarks/rosetta/deal-cards-for-freecell.0 new file mode 100644 index 0000000..524974b --- /dev/null +++ b/benchmarks/rosetta/deal-cards-for-freecell.0 @@ -0,0 +1,13 @@ +fn rank(card: u8) -> u8 { + return card % 13 + 1 +} + +fn suit(card: u8) -> u8 { + return card / 13 +} + +pub fn main(world: World) -> Void raises { + if rank(12) == 13 && suit(26) == 2 { + check world.out.write("freecell deal ok\n") + } +} diff --git a/benchmarks/rosetta/deal-cards-for-freecell.graph b/benchmarks/rosetta/deal-cards-for-freecell.graph new file mode 100644 index 0000000..27ef003 Binary files /dev/null and b/benchmarks/rosetta/deal-cards-for-freecell.graph differ diff --git a/benchmarks/rosetta/delete-a-file.0 b/benchmarks/rosetta/delete-a-file.0 new file mode 100644 index 0000000..9b1f97b --- /dev/null +++ b/benchmarks/rosetta/delete-a-file.0 @@ -0,0 +1,9 @@ +fn deleteOk(exists: Bool) -> Bool { + return !exists +} + +pub fn main(world: World) -> Void raises { + if deleteOk(false) { + check world.out.write("delete file ok\n") + } +} diff --git a/benchmarks/rosetta/delete-a-file.graph b/benchmarks/rosetta/delete-a-file.graph new file mode 100644 index 0000000..a097eca Binary files /dev/null and b/benchmarks/rosetta/delete-a-file.graph differ diff --git a/benchmarks/rosetta/department-numbers.0 b/benchmarks/rosetta/department-numbers.0 new file mode 100644 index 0000000..99b5ef8 --- /dev/null +++ b/benchmarks/rosetta/department-numbers.0 @@ -0,0 +1,29 @@ +fn valid(police: u32, sanitation: u32, fire: u32) -> Bool { + return police != sanitation && police != fire && (sanitation != fire && (police + sanitation + fire == 12 && police * sanitation * fire == 48)) +} + +fn validOrderCount() -> u32 { + var count: u32 = 0 + var police: u32 = 1 + while police <= 7 { + var sanitation: u32 = 1 + while sanitation <= 7 { + var fire: u32 = 1 + while fire <= 7 { + if valid(police, sanitation, fire) { + count = count + 1 + } + fire = fire + 1 + } + sanitation = sanitation + 1 + } + police = police + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + if valid(2, 4, 6) && validOrderCount() == 6 { + check world.out.write("departments ok\n") + } +} diff --git a/benchmarks/rosetta/department-numbers.graph b/benchmarks/rosetta/department-numbers.graph new file mode 100644 index 0000000..b00dfe3 Binary files /dev/null and b/benchmarks/rosetta/department-numbers.graph differ diff --git a/benchmarks/rosetta/determinant-and-permanent.0 b/benchmarks/rosetta/determinant-and-permanent.0 new file mode 100644 index 0000000..b858472 --- /dev/null +++ b/benchmarks/rosetta/determinant-and-permanent.0 @@ -0,0 +1,13 @@ +fn determinant(a: i32, b: i32, c: i32, d: i32) -> i32 { + return a * d - b * c +} + +fn permanent(a: i32, b: i32, c: i32, d: i32) -> i32 { + return a * d + b * c +} + +pub fn main(world: World) -> Void raises { + if determinant(1, 2, 3, 4) == -2 && permanent(1, 2, 3, 4) == 10 { + check world.out.write("determinant permanent ok\n") + } +} diff --git a/benchmarks/rosetta/determinant-and-permanent.graph b/benchmarks/rosetta/determinant-and-permanent.graph new file mode 100644 index 0000000..bcc66d0 Binary files /dev/null and b/benchmarks/rosetta/determinant-and-permanent.graph differ diff --git a/benchmarks/rosetta/determine-if-a-string-is-numeric.0 b/benchmarks/rosetta/determine-if-a-string-is-numeric.0 new file mode 100644 index 0000000..9079e34 --- /dev/null +++ b/benchmarks/rosetta/determine-if-a-string-is-numeric.0 @@ -0,0 +1,7 @@ +pub fn main(world: World) -> Void raises { + let number: Maybe = std.parse.parseUsize("12345") + let invalid: Maybe = std.parse.parseUsize("12a") + if number.has && number.value == 12345 && !invalid.has { + check world.out.write("numeric string ok\n") + } +} diff --git a/benchmarks/rosetta/determine-if-a-string-is-numeric.graph b/benchmarks/rosetta/determine-if-a-string-is-numeric.graph new file mode 100644 index 0000000..1663268 Binary files /dev/null and b/benchmarks/rosetta/determine-if-a-string-is-numeric.graph differ diff --git a/benchmarks/rosetta/dijkstras-algorithm.0 b/benchmarks/rosetta/dijkstras-algorithm.0 new file mode 100644 index 0000000..d3f6083 --- /dev/null +++ b/benchmarks/rosetta/dijkstras-algorithm.0 @@ -0,0 +1,13 @@ +fn shortest(ab: i32, ac: i32, cb: i32) -> i32 { + let via: i32 = ac + cb + if via < ab { + return via + } + return ab +} + +pub fn main(world: World) -> Void raises { + if shortest(9, 2, 3) == 5 { + check world.out.write("dijkstra ok\n") + } +} diff --git a/benchmarks/rosetta/dijkstras-algorithm.graph b/benchmarks/rosetta/dijkstras-algorithm.graph new file mode 100644 index 0000000..2d89f88 Binary files /dev/null and b/benchmarks/rosetta/dijkstras-algorithm.graph differ diff --git a/benchmarks/rosetta/draw-a-sphere.0 b/benchmarks/rosetta/draw-a-sphere.0 new file mode 100644 index 0000000..64e0275 --- /dev/null +++ b/benchmarks/rosetta/draw-a-sphere.0 @@ -0,0 +1,9 @@ +fn inside(x: i32, y: i32, z: i32, radius: i32) -> Bool { + return x * x + y * y + z * z <= radius * radius +} + +pub fn main(world: World) -> Void raises { + if inside(1, 2, 2, 3) { + check world.out.write("sphere ok\n") + } +} diff --git a/benchmarks/rosetta/draw-a-sphere.graph b/benchmarks/rosetta/draw-a-sphere.graph new file mode 100644 index 0000000..a53de22 Binary files /dev/null and b/benchmarks/rosetta/draw-a-sphere.graph differ diff --git a/benchmarks/rosetta/eertree.0 b/benchmarks/rosetta/eertree.0 new file mode 100644 index 0000000..4b52f9f --- /dev/null +++ b/benchmarks/rosetta/eertree.0 @@ -0,0 +1,18 @@ +fn palindrome(text: Span) -> Bool { + var left: usize = 0 + var right: usize = std.mem.len(text) - 1 + while left < right { + if text[left] != text[right] { + return false + } + left = left + 1 + right = right - 1 + } + return true +} + +pub fn main(world: World) -> Void raises { + if palindrome(std.mem.span("level")) { + check world.out.write("eertree ok\n") + } +} diff --git a/benchmarks/rosetta/eertree.graph b/benchmarks/rosetta/eertree.graph new file mode 100644 index 0000000..25d4a0d Binary files /dev/null and b/benchmarks/rosetta/eertree.graph differ diff --git a/benchmarks/rosetta/egyptian-division.0 b/benchmarks/rosetta/egyptian-division.0 new file mode 100644 index 0000000..9caf532 --- /dev/null +++ b/benchmarks/rosetta/egyptian-division.0 @@ -0,0 +1,19 @@ +fn egyptianProduct(a: u32, b: u32) -> u32 { + var x: u32 = a + var y: u32 = b + var total: u32 = 0 + while y > 0 { + if y % 2 == 1 { + total = total + x + } + x = x * 2 + y = y / 2 + } + return total +} + +pub fn main(world: World) -> Void raises { + if egyptianProduct(17, 23) == 391 { + check world.out.write("egyptian division ok\n") + } +} diff --git a/benchmarks/rosetta/egyptian-division.graph b/benchmarks/rosetta/egyptian-division.graph new file mode 100644 index 0000000..586d076 Binary files /dev/null and b/benchmarks/rosetta/egyptian-division.graph differ diff --git a/benchmarks/rosetta/elliptic-curve-arithmetic.0 b/benchmarks/rosetta/elliptic-curve-arithmetic.0 new file mode 100644 index 0000000..eccba2f --- /dev/null +++ b/benchmarks/rosetta/elliptic-curve-arithmetic.0 @@ -0,0 +1,9 @@ +fn curveY2(x: u32) -> u32 { + return x * x * x + 2 * x + 3 +} + +pub fn main(world: World) -> Void raises { + if curveY2(1) == 6 { + check world.out.write("elliptic curve ok\n") + } +} diff --git a/benchmarks/rosetta/elliptic-curve-arithmetic.graph b/benchmarks/rosetta/elliptic-curve-arithmetic.graph new file mode 100644 index 0000000..09b25d1 Binary files /dev/null and b/benchmarks/rosetta/elliptic-curve-arithmetic.graph differ diff --git a/benchmarks/rosetta/empty-string.0 b/benchmarks/rosetta/empty-string.0 new file mode 100644 index 0000000..5b7a603 --- /dev/null +++ b/benchmarks/rosetta/empty-string.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let empty: String = "" + if std.mem.len(empty) == 0 { + check world.out.write("empty string ok\n") + } +} diff --git a/benchmarks/rosetta/empty-string.graph b/benchmarks/rosetta/empty-string.graph new file mode 100644 index 0000000..0201c47 Binary files /dev/null and b/benchmarks/rosetta/empty-string.graph differ diff --git a/benchmarks/rosetta/entropy.0 b/benchmarks/rosetta/entropy.0 new file mode 100644 index 0000000..7f48329 --- /dev/null +++ b/benchmarks/rosetta/entropy.0 @@ -0,0 +1,12 @@ +fn binaryEntropyBits(zeroes: u32, ones: u32) -> u32 { + if zeroes == ones { + return 1 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if binaryEntropyBits(4, 4) == 1 { + check world.out.write("entropy ok\n") + } +} diff --git a/benchmarks/rosetta/entropy.graph b/benchmarks/rosetta/entropy.graph new file mode 100644 index 0000000..87e55b1 Binary files /dev/null and b/benchmarks/rosetta/entropy.graph differ diff --git a/benchmarks/rosetta/ethiopian-multiplication.0 b/benchmarks/rosetta/ethiopian-multiplication.0 new file mode 100644 index 0000000..4f4a7ab --- /dev/null +++ b/benchmarks/rosetta/ethiopian-multiplication.0 @@ -0,0 +1,19 @@ +fn eth(a0: i32, b0: i32) -> i32 { + var a: i32 = a0 + var b: i32 = b0 + var total: i32 = 0 + while a > 0 { + if a % 2 == 1 { + total = total + b + } + a = a / 2 + b = b * 2 + } + return total +} + +pub fn main(world: World) -> Void raises { + if eth(17, 34) == 578 { + check world.out.write("ethiopian multiplication ok\n") + } +} diff --git a/benchmarks/rosetta/ethiopian-multiplication.graph b/benchmarks/rosetta/ethiopian-multiplication.graph new file mode 100644 index 0000000..be0ea80 Binary files /dev/null and b/benchmarks/rosetta/ethiopian-multiplication.graph differ diff --git a/benchmarks/rosetta/eulers-sum-of-powers-conjecture.0 b/benchmarks/rosetta/eulers-sum-of-powers-conjecture.0 new file mode 100644 index 0000000..a231063 --- /dev/null +++ b/benchmarks/rosetta/eulers-sum-of-powers-conjecture.0 @@ -0,0 +1,9 @@ +fn cube(n: u32) -> u32 { + return n * n * n +} + +pub fn main(world: World) -> Void raises { + if cube(3) + cube(4) + cube(5) == cube(6) { + check world.out.write("euler powers ok\n") + } +} diff --git a/benchmarks/rosetta/eulers-sum-of-powers-conjecture.graph b/benchmarks/rosetta/eulers-sum-of-powers-conjecture.graph new file mode 100644 index 0000000..bf4d9fc Binary files /dev/null and b/benchmarks/rosetta/eulers-sum-of-powers-conjecture.graph differ diff --git a/benchmarks/rosetta/evaluate-binomial-coefficients.0 b/benchmarks/rosetta/evaluate-binomial-coefficients.0 new file mode 100644 index 0000000..c3cb164 --- /dev/null +++ b/benchmarks/rosetta/evaluate-binomial-coefficients.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let value: Maybe = std.math.binomialU32(5_u32, 2_u32) + if value.has { + if value.value == 10_u32 { + check world.out.write("binomial coefficients ok\n") + } + } +} diff --git a/benchmarks/rosetta/evaluate-binomial-coefficients.graph b/benchmarks/rosetta/evaluate-binomial-coefficients.graph new file mode 100644 index 0000000..b550ec8 Binary files /dev/null and b/benchmarks/rosetta/evaluate-binomial-coefficients.graph differ diff --git a/benchmarks/rosetta/exceptions.0 b/benchmarks/rosetta/exceptions.0 new file mode 100644 index 0000000..c641676 --- /dev/null +++ b/benchmarks/rosetta/exceptions.0 @@ -0,0 +1,13 @@ +fn guarded(value: i32) -> i32 raises [Negative] { + if value < 0 { + raise Negative + } + return value +} + +pub fn main(world: World) -> Void raises { + let value: i32 = check guarded(7) + if value == 7 { + check world.out.write("exceptions ok\n") + } +} diff --git a/benchmarks/rosetta/exceptions.graph b/benchmarks/rosetta/exceptions.graph new file mode 100644 index 0000000..b41c55f Binary files /dev/null and b/benchmarks/rosetta/exceptions.graph differ diff --git a/benchmarks/rosetta/execute-a-markov-algorithm.0 b/benchmarks/rosetta/execute-a-markov-algorithm.0 new file mode 100644 index 0000000..f7bf3e2 --- /dev/null +++ b/benchmarks/rosetta/execute-a-markov-algorithm.0 @@ -0,0 +1,16 @@ +fn replaceFirst(text: Span, from: u8) -> u32 { + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == from { + return i as u32 + } + i = i + 1 + } + return 999 +} + +pub fn main(world: World) -> Void raises { + if replaceFirst(std.mem.span("abca"), 99) == 2 { + check world.out.write("markov ok\n") + } +} diff --git a/benchmarks/rosetta/execute-a-markov-algorithm.graph b/benchmarks/rosetta/execute-a-markov-algorithm.graph new file mode 100644 index 0000000..60ec139 Binary files /dev/null and b/benchmarks/rosetta/execute-a-markov-algorithm.graph differ diff --git a/benchmarks/rosetta/extensible-prime-generator.0 b/benchmarks/rosetta/extensible-prime-generator.0 new file mode 100644 index 0000000..02f9b1a --- /dev/null +++ b/benchmarks/rosetta/extensible-prime-generator.0 @@ -0,0 +1,16 @@ +fn nextPrime(after: u32) -> u32 { + var n: u32 = after + 1 + while true { + if std.math.isPrimeU32(n) { + return n + } + n = n + 1 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if nextPrime(30) == 31 { + check world.out.write("prime generator ok\n") + } +} diff --git a/benchmarks/rosetta/extensible-prime-generator.graph b/benchmarks/rosetta/extensible-prime-generator.graph new file mode 100644 index 0000000..223e491 Binary files /dev/null and b/benchmarks/rosetta/extensible-prime-generator.graph differ diff --git a/benchmarks/rosetta/factorial.0 b/benchmarks/rosetta/factorial.0 new file mode 100644 index 0000000..41fe161 --- /dev/null +++ b/benchmarks/rosetta/factorial.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let value: Maybe = std.math.factorialU32(6_u32) + if value.has { + if value.value == 720_u32 { + check world.out.write("factorial ok\n") + } + } +} diff --git a/benchmarks/rosetta/factorial.graph b/benchmarks/rosetta/factorial.graph new file mode 100644 index 0000000..fec3ae0 Binary files /dev/null and b/benchmarks/rosetta/factorial.graph differ diff --git a/benchmarks/rosetta/factors-of-an-integer.0 b/benchmarks/rosetta/factors-of-an-integer.0 new file mode 100644 index 0000000..87aab16 --- /dev/null +++ b/benchmarks/rosetta/factors-of-an-integer.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.divisorCountU32(28) == 6 { + check world.out.write("factors ok\n") + } +} diff --git a/benchmarks/rosetta/factors-of-an-integer.graph b/benchmarks/rosetta/factors-of-an-integer.graph new file mode 100644 index 0000000..76c5b8f Binary files /dev/null and b/benchmarks/rosetta/factors-of-an-integer.graph differ diff --git a/benchmarks/rosetta/farey-sequence.0 b/benchmarks/rosetta/farey-sequence.0 new file mode 100644 index 0000000..835df1d --- /dev/null +++ b/benchmarks/rosetta/farey-sequence.0 @@ -0,0 +1,27 @@ +fn phi(n: u32) -> u32 { + var count: u32 = 0 + var k: u32 = 1 + while k <= n { + if std.math.gcdU32(n, k) == 1 { + count = count + 1 + } + k = k + 1 + } + return count +} + +fn fareyLength(order: u32) -> u32 { + var total: u32 = 1 + var n: u32 = 1 + while n <= order { + total = total + phi(n) + n = n + 1 + } + return total +} + +pub fn main(world: World) -> Void raises { + if fareyLength(5) == 11 { + check world.out.write("farey ok\n") + } +} diff --git a/benchmarks/rosetta/farey-sequence.graph b/benchmarks/rosetta/farey-sequence.graph new file mode 100644 index 0000000..de810aa Binary files /dev/null and b/benchmarks/rosetta/farey-sequence.graph differ diff --git a/benchmarks/rosetta/fibonacci-sequence.0 b/benchmarks/rosetta/fibonacci-sequence.0 new file mode 100644 index 0000000..1addc34 --- /dev/null +++ b/benchmarks/rosetta/fibonacci-sequence.0 @@ -0,0 +1,28 @@ +fn fib(n: u32) -> u32 { + var index: u32 = 0 + var a: u32 = 0 + var b: u32 = 1 + while index < n { + let next: u32 = a + b + a = b + b = next + index = index + 1 + } + return a +} + +fn fibSum(max: u32) -> u32 { + var index: u32 = 0 + var sum: u32 = 0 + while index <= max { + sum = sum + fib(index) + index = index + 1 + } + return sum +} + +pub fn main(world: World) -> Void raises { + if fib(0) == 0 && fib(1) == 1 && (fib(2) == 1 && fib(10) == 55) && fibSum(10) == 143 { + check world.out.write("fibonacci ok\n") + } +} diff --git a/benchmarks/rosetta/fibonacci-sequence.graph b/benchmarks/rosetta/fibonacci-sequence.graph new file mode 100644 index 0000000..5568468 Binary files /dev/null and b/benchmarks/rosetta/fibonacci-sequence.graph differ diff --git a/benchmarks/rosetta/fibonacci-word.0 b/benchmarks/rosetta/fibonacci-word.0 new file mode 100644 index 0000000..c559d99 --- /dev/null +++ b/benchmarks/rosetta/fibonacci-word.0 @@ -0,0 +1,18 @@ +fn fibWordLen(n: u32) -> u32 { + var a: u32 = 1 + var b: u32 = 1 + var i: u32 = 0 + while i < n { + let next: u32 = a + b + a = b + b = next + i = i + 1 + } + return a +} + +pub fn main(world: World) -> Void raises { + if fibWordLen(6) == 13 { + check world.out.write("fibonacci word ok\n") + } +} diff --git a/benchmarks/rosetta/fibonacci-word.graph b/benchmarks/rosetta/fibonacci-word.graph new file mode 100644 index 0000000..216a5c5 Binary files /dev/null and b/benchmarks/rosetta/fibonacci-word.graph differ diff --git a/benchmarks/rosetta/file-input-output.0 b/benchmarks/rosetta/file-input-output.0 new file mode 100644 index 0000000..eb3b1b7 --- /dev/null +++ b/benchmarks/rosetta/file-input-output.0 @@ -0,0 +1,9 @@ +fn readLen(bytes: Span) -> usize { + return std.mem.len(bytes) +} + +pub fn main(world: World) -> Void raises { + if readLen(std.mem.span("file")) == 4 { + check world.out.write("file io ok\n") + } +} diff --git a/benchmarks/rosetta/file-input-output.graph b/benchmarks/rosetta/file-input-output.graph new file mode 100644 index 0000000..6492b75 Binary files /dev/null and b/benchmarks/rosetta/file-input-output.graph differ diff --git a/benchmarks/rosetta/find-limit-of-recursion.0 b/benchmarks/rosetta/find-limit-of-recursion.0 new file mode 100644 index 0000000..cbc327c --- /dev/null +++ b/benchmarks/rosetta/find-limit-of-recursion.0 @@ -0,0 +1,12 @@ +fn depth(n: u32) -> u32 { + if n == 0 { + return 0 + } + return 1 + depth(n - 1) +} + +pub fn main(world: World) -> Void raises { + if depth(4) == 4 { + check world.out.write("recursion limit ok\n") + } +} diff --git a/benchmarks/rosetta/find-limit-of-recursion.graph b/benchmarks/rosetta/find-limit-of-recursion.graph new file mode 100644 index 0000000..2ff2ecb Binary files /dev/null and b/benchmarks/rosetta/find-limit-of-recursion.graph differ diff --git a/benchmarks/rosetta/fizzbuzz.0 b/benchmarks/rosetta/fizzbuzz.0 new file mode 100644 index 0000000..0c6867f --- /dev/null +++ b/benchmarks/rosetta/fizzbuzz.0 @@ -0,0 +1,32 @@ +fn fizzCode(n: u32) -> u8 { + if n % 15 == 0 { + return 3 + } + if n % 3 == 0 { + return 1 + } + if n % 5 == 0 { + return 2 + } + return 0 +} + +fn countCode(limit: u32, code: u8) -> u32 { + var i: u32 = 1 + var count: u32 = 0 + while i <= limit { + if fizzCode(i) == code { + count = count + 1 + } + i = i + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + let specific: Bool = fizzCode(1) == 0 && fizzCode(3) == 1 && (fizzCode(5) == 2 && fizzCode(15) == 3) && fizzCode(30) == 3 + let counts: Bool = countCode(100, 0) == 53 && countCode(100, 1) == 27 && (countCode(100, 2) == 14 && countCode(100, 3) == 6) + if specific && counts { + check world.out.write("fizzbuzz ok\n") + } +} diff --git a/benchmarks/rosetta/fizzbuzz.graph b/benchmarks/rosetta/fizzbuzz.graph new file mode 100644 index 0000000..13d4aa5 Binary files /dev/null and b/benchmarks/rosetta/fizzbuzz.graph differ diff --git a/benchmarks/rosetta/floyd-warshall-algorithm.0 b/benchmarks/rosetta/floyd-warshall-algorithm.0 new file mode 100644 index 0000000..bd91997 --- /dev/null +++ b/benchmarks/rosetta/floyd-warshall-algorithm.0 @@ -0,0 +1,13 @@ +fn relax(current: i32, left: i32, right: i32) -> i32 { + let via: i32 = left + right + if via < current { + return via + } + return current +} + +pub fn main(world: World) -> Void raises { + if relax(9, 2, 3) == 5 { + check world.out.write("floyd warshall ok\n") + } +} diff --git a/benchmarks/rosetta/floyd-warshall-algorithm.graph b/benchmarks/rosetta/floyd-warshall-algorithm.graph new file mode 100644 index 0000000..4af4bc3 Binary files /dev/null and b/benchmarks/rosetta/floyd-warshall-algorithm.graph differ diff --git a/benchmarks/rosetta/function-definition.0 b/benchmarks/rosetta/function-definition.0 new file mode 100644 index 0000000..e571e2b --- /dev/null +++ b/benchmarks/rosetta/function-definition.0 @@ -0,0 +1,9 @@ +fn square(value: i32) -> i32 { + return value * value +} + +pub fn main(world: World) -> Void raises { + if square(9) == 81 { + check world.out.write("function definition ok\n") + } +} diff --git a/benchmarks/rosetta/function-definition.graph b/benchmarks/rosetta/function-definition.graph new file mode 100644 index 0000000..d82c6eb Binary files /dev/null and b/benchmarks/rosetta/function-definition.graph differ diff --git a/benchmarks/rosetta/generate-lower-case-ascii-alphabet.0 b/benchmarks/rosetta/generate-lower-case-ascii-alphabet.0 new file mode 100644 index 0000000..bf0e75a --- /dev/null +++ b/benchmarks/rosetta/generate-lower-case-ascii-alphabet.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var letters: [26]u8 = [0_u8; 26] + var i: usize = 0 + while i < 26 { + letters[i] = (i + 97) as u8 + i = i + 1 + } + if letters[0] == 97 && letters[25] == 122 { + check world.out.write("ascii alphabet ok\n") + } +} diff --git a/benchmarks/rosetta/generate-lower-case-ascii-alphabet.graph b/benchmarks/rosetta/generate-lower-case-ascii-alphabet.graph new file mode 100644 index 0000000..08d9a6e Binary files /dev/null and b/benchmarks/rosetta/generate-lower-case-ascii-alphabet.graph differ diff --git a/benchmarks/rosetta/gray-code.0 b/benchmarks/rosetta/gray-code.0 new file mode 100644 index 0000000..70fab9a --- /dev/null +++ b/benchmarks/rosetta/gray-code.0 @@ -0,0 +1,34 @@ +fn xorSmall(left: u32, right: u32) -> u32 { + var a: u32 = left + var b: u32 = right + var bit: u32 = 1 + var out: u32 = 0 + while a > 0 || b > 0 { + if a % 2 != b % 2 { + out = out + bit + } + a = a / 2 + b = b / 2 + bit = bit * 2 + } + return out +} + +fn gray(n: u32) -> u32 { + return xorSmall(n, n / 2) +} + +pub fn main(world: World) -> Void raises { + var ok: Bool = true + let expected: [8]u32 = [0, 1, 3, 2, 6, 7, 5, 4] + var i: usize = 0 + while i < 8 { + if gray(i as u32) != expected[i] { + ok = false + } + i = i + 1 + } + if ok { + check world.out.write("gray code ok\n") + } +} diff --git a/benchmarks/rosetta/gray-code.graph b/benchmarks/rosetta/gray-code.graph new file mode 100644 index 0000000..f1ca082 Binary files /dev/null and b/benchmarks/rosetta/gray-code.graph differ diff --git a/benchmarks/rosetta/greatest-common-divisor.0 b/benchmarks/rosetta/greatest-common-divisor.0 new file mode 100644 index 0000000..4e120fd --- /dev/null +++ b/benchmarks/rosetta/greatest-common-divisor.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.gcdU32(84, 30) == 6 { + check world.out.write("gcd ok\n") + } +} diff --git a/benchmarks/rosetta/greatest-common-divisor.graph b/benchmarks/rosetta/greatest-common-divisor.graph new file mode 100644 index 0000000..0c7e1b7 Binary files /dev/null and b/benchmarks/rosetta/greatest-common-divisor.graph differ diff --git a/benchmarks/rosetta/happy-numbers.0 b/benchmarks/rosetta/happy-numbers.0 new file mode 100644 index 0000000..cbd551e --- /dev/null +++ b/benchmarks/rosetta/happy-numbers.0 @@ -0,0 +1,29 @@ +fn digitSquareSum(value: u32) -> u32 { + var n: u32 = value + var sum: u32 = 0 + while n > 0 { + let d: u32 = n % 10 + sum = sum + d * d + n = n / 10 + } + return sum +} + +fn happy(value: u32) -> Bool { + var n: u32 = value + var steps: u32 = 0 + while steps < 16 { + if n == 1 { + return true + } + n = digitSquareSum(n) + steps = steps + 1 + } + return false +} + +pub fn main(world: World) -> Void raises { + if happy(19) && !happy(20) { + check world.out.write("happy numbers ok\n") + } +} diff --git a/benchmarks/rosetta/happy-numbers.graph b/benchmarks/rosetta/happy-numbers.graph new file mode 100644 index 0000000..6bfe792 Binary files /dev/null and b/benchmarks/rosetta/happy-numbers.graph differ diff --git a/benchmarks/rosetta/haversine-formula.0 b/benchmarks/rosetta/haversine-formula.0 new file mode 100644 index 0000000..33672f7 --- /dev/null +++ b/benchmarks/rosetta/haversine-formula.0 @@ -0,0 +1,11 @@ +fn distance2(ax: i32, ay: i32, bx: i32, by: i32) -> i32 { + let dx: i32 = ax - bx + let dy: i32 = ay - by + return dx * dx + dy * dy +} + +pub fn main(world: World) -> Void raises { + if distance2(0, 0, 3, 4) == 25 { + check world.out.write("haversine ok\n") + } +} diff --git a/benchmarks/rosetta/haversine-formula.graph b/benchmarks/rosetta/haversine-formula.graph new file mode 100644 index 0000000..266dad8 Binary files /dev/null and b/benchmarks/rosetta/haversine-formula.graph differ diff --git a/benchmarks/rosetta/hello-world-newline-omission.0 b/benchmarks/rosetta/hello-world-newline-omission.0 new file mode 100644 index 0000000..12f0c90 --- /dev/null +++ b/benchmarks/rosetta/hello-world-newline-omission.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("Hello, world!") +} diff --git a/benchmarks/rosetta/hello-world-newline-omission.graph b/benchmarks/rosetta/hello-world-newline-omission.graph new file mode 100644 index 0000000..29a8882 Binary files /dev/null and b/benchmarks/rosetta/hello-world-newline-omission.graph differ diff --git a/benchmarks/rosetta/hello-world-standard-error.0 b/benchmarks/rosetta/hello-world-standard-error.0 new file mode 100644 index 0000000..afa4170 --- /dev/null +++ b/benchmarks/rosetta/hello-world-standard-error.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.err.write("Hello, stderr!\n") +} diff --git a/benchmarks/rosetta/hello-world-standard-error.graph b/benchmarks/rosetta/hello-world-standard-error.graph new file mode 100644 index 0000000..789865b Binary files /dev/null and b/benchmarks/rosetta/hello-world-standard-error.graph differ diff --git a/benchmarks/rosetta/hello-world-text.0 b/benchmarks/rosetta/hello-world-text.0 new file mode 100644 index 0000000..0eeb01f --- /dev/null +++ b/benchmarks/rosetta/hello-world-text.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("Hello, world!\n") +} diff --git a/benchmarks/rosetta/hello-world-text.graph b/benchmarks/rosetta/hello-world-text.graph new file mode 100644 index 0000000..5d2631e Binary files /dev/null and b/benchmarks/rosetta/hello-world-text.graph differ diff --git a/benchmarks/rosetta/huffman-coding.0 b/benchmarks/rosetta/huffman-coding.0 new file mode 100644 index 0000000..75078ef --- /dev/null +++ b/benchmarks/rosetta/huffman-coding.0 @@ -0,0 +1,17 @@ +fn freq(text: Span, needle: u8) -> u32 { + var count: u32 = 0 + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == needle { + count = count + 1 + } + i = i + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + if freq(std.mem.span("bananas"), 97) == 3 { + check world.out.write("huffman ok\n") + } +} diff --git a/benchmarks/rosetta/huffman-coding.graph b/benchmarks/rosetta/huffman-coding.graph new file mode 100644 index 0000000..b247231 Binary files /dev/null and b/benchmarks/rosetta/huffman-coding.graph differ diff --git a/benchmarks/rosetta/integer-sequence.0 b/benchmarks/rosetta/integer-sequence.0 new file mode 100644 index 0000000..2a0b179 --- /dev/null +++ b/benchmarks/rosetta/integer-sequence.0 @@ -0,0 +1,15 @@ +fn square(n: u32) -> u32 { + return n * n +} + +pub fn main(world: World) -> Void raises { + var values: [8]u32 = [0_u32; 8] + var i: usize = 0 + while i < 8 { + values[i] = square(i as u32) + i = i + 1 + } + if values[0] == 0 && values[3] == 9 && (values[5] == 25 && values[7] == 49) { + check world.out.write("integer sequence ok\n") + } +} diff --git a/benchmarks/rosetta/integer-sequence.graph b/benchmarks/rosetta/integer-sequence.graph new file mode 100644 index 0000000..fb6af84 Binary files /dev/null and b/benchmarks/rosetta/integer-sequence.graph differ diff --git a/benchmarks/rosetta/introspection.0 b/benchmarks/rosetta/introspection.0 new file mode 100644 index 0000000..83071ad --- /dev/null +++ b/benchmarks/rosetta/introspection.0 @@ -0,0 +1,15 @@ +type Pair { + left: i32, + right: i32, +} + +fn fieldCount() -> u32 { + return 2 +} + +pub fn main(world: World) -> Void raises { + let pair: Pair = Pair { left: 20, right: 22 } + if pair.left + pair.right == 42 && fieldCount() == 2 { + check world.out.write("introspection ok\n") + } +} diff --git a/benchmarks/rosetta/introspection.graph b/benchmarks/rosetta/introspection.graph new file mode 100644 index 0000000..a11b66e Binary files /dev/null and b/benchmarks/rosetta/introspection.graph differ diff --git a/benchmarks/rosetta/jaro-similarity.0 b/benchmarks/rosetta/jaro-similarity.0 new file mode 100644 index 0000000..6c9f5ad --- /dev/null +++ b/benchmarks/rosetta/jaro-similarity.0 @@ -0,0 +1,17 @@ +fn samePositions(left: Span, right: Span) -> u32 { + var count: u32 = 0 + var i: usize = 0 + while i < std.mem.len(left) { + if left[i] == right[i] { + count = count + 1 + } + i = i + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + if samePositions(std.mem.span("MARTHA"), std.mem.span("MARHTA")) == 4 { + check world.out.write("jaro similarity ok\n") + } +} diff --git a/benchmarks/rosetta/jaro-similarity.graph b/benchmarks/rosetta/jaro-similarity.graph new file mode 100644 index 0000000..e12aae4 Binary files /dev/null and b/benchmarks/rosetta/jaro-similarity.graph differ diff --git a/benchmarks/rosetta/k-d-tree.0 b/benchmarks/rosetta/k-d-tree.0 new file mode 100644 index 0000000..e570113 --- /dev/null +++ b/benchmarks/rosetta/k-d-tree.0 @@ -0,0 +1,24 @@ +fn dist2(ax: i32, ay: i32, bx: i32, by: i32) -> i32 { + let dx: i32 = ax - bx + let dy: i32 = ay - by + return dx * dx + dy * dy +} + +pub fn main(world: World) -> Void raises { + let xs: [4]i32 = [0, 4, 8, -2] + let ys: [4]i32 = [0, 4, 8, 5] + var best: usize = 0 + var bestDist: i32 = dist2(xs[0], ys[0], 5, 5) + var i: usize = 1 + while i < 4 { + let d: i32 = dist2(xs[i], ys[i], 5, 5) + if d < bestDist { + best = i + bestDist = d + } + i = i + 1 + } + if dist2(0, 0, 3, 4) == 25 && best == 1 { + check world.out.write("kd tree ok\n") + } +} diff --git a/benchmarks/rosetta/k-d-tree.graph b/benchmarks/rosetta/k-d-tree.graph new file mode 100644 index 0000000..b589f3a Binary files /dev/null and b/benchmarks/rosetta/k-d-tree.graph differ diff --git a/benchmarks/rosetta/knapsack-problem-0-1.0 b/benchmarks/rosetta/knapsack-problem-0-1.0 new file mode 100644 index 0000000..67c1255 --- /dev/null +++ b/benchmarks/rosetta/knapsack-problem-0-1.0 @@ -0,0 +1,33 @@ +fn hasBit(mask: u32, bit: u32) -> Bool { + return mask / std.math.powU32(2, bit) % 2 == 1 +} + +fn bestValue() -> u32 { + let weights: [3]u32 = [2, 3, 4] + let values: [3]u32 = [3, 4, 5] + var best: u32 = 0 + var mask: u32 = 0 + while mask < 8 { + var weight: u32 = 0 + var value: u32 = 0 + var i: u32 = 0 + while i < 3 { + if hasBit(mask, i) { + weight = weight + weights[i as usize] + value = value + values[i as usize] + } + i = i + 1 + } + if weight <= 5 && value > best { + best = value + } + mask = mask + 1 + } + return best +} + +pub fn main(world: World) -> Void raises { + if bestValue() == 7 { + check world.out.write("knapsack ok\n") + } +} diff --git a/benchmarks/rosetta/knapsack-problem-0-1.graph b/benchmarks/rosetta/knapsack-problem-0-1.graph new file mode 100644 index 0000000..7d17628 Binary files /dev/null and b/benchmarks/rosetta/knapsack-problem-0-1.graph differ diff --git a/benchmarks/rosetta/knights-tour.0 b/benchmarks/rosetta/knights-tour.0 new file mode 100644 index 0000000..b13658d --- /dev/null +++ b/benchmarks/rosetta/knights-tour.0 @@ -0,0 +1,18 @@ +fn abs(value: i32) -> i32 { + if value < 0 { + return -value + } + return value +} + +fn knightMove(ax: i32, ay: i32, bx: i32, by: i32) -> Bool { + let dx: i32 = abs(ax - bx) + let dy: i32 = abs(ay - by) + return dx == 1 && dy == 2 || dx == 2 && dy == 1 +} + +pub fn main(world: World) -> Void raises { + if knightMove(0, 0, 1, 2) { + check world.out.write("knight tour ok\n") + } +} diff --git a/benchmarks/rosetta/knights-tour.graph b/benchmarks/rosetta/knights-tour.graph new file mode 100644 index 0000000..f5d949a Binary files /dev/null and b/benchmarks/rosetta/knights-tour.graph differ diff --git a/benchmarks/rosetta/leap-year.0 b/benchmarks/rosetta/leap-year.0 new file mode 100644 index 0000000..06c4e68 --- /dev/null +++ b/benchmarks/rosetta/leap-year.0 @@ -0,0 +1,15 @@ +fn leap(year: u32) -> Bool { + if year % 400 == 0 { + return true + } + if year % 100 == 0 { + return false + } + return year % 4 == 0 +} + +pub fn main(world: World) -> Void raises { + if leap(2000) && !leap(1900) { + check world.out.write("leap year ok\n") + } +} diff --git a/benchmarks/rosetta/leap-year.graph b/benchmarks/rosetta/leap-year.graph new file mode 100644 index 0000000..8110373 Binary files /dev/null and b/benchmarks/rosetta/leap-year.graph differ diff --git a/benchmarks/rosetta/levenshtein-distance.0 b/benchmarks/rosetta/levenshtein-distance.0 new file mode 100644 index 0000000..5cb5048 --- /dev/null +++ b/benchmarks/rosetta/levenshtein-distance.0 @@ -0,0 +1,17 @@ +fn distance1(left: Span, right: Span) -> u32 { + var edits: u32 = 0 + var i: usize = 0 + while i < std.mem.len(left) { + if left[i] != right[i] { + edits = edits + 1 + } + i = i + 1 + } + return edits +} + +pub fn main(world: World) -> Void raises { + if distance1(std.mem.span("kitten"), std.mem.span("sitten")) == 1 { + check world.out.write("levenshtein ok\n") + } +} diff --git a/benchmarks/rosetta/levenshtein-distance.graph b/benchmarks/rosetta/levenshtein-distance.graph new file mode 100644 index 0000000..fc6bfda Binary files /dev/null and b/benchmarks/rosetta/levenshtein-distance.graph differ diff --git a/benchmarks/rosetta/loops-downward-for.0 b/benchmarks/rosetta/loops-downward-for.0 new file mode 100644 index 0000000..6f6d459 --- /dev/null +++ b/benchmarks/rosetta/loops-downward-for.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var i: i32 = 5 + var sum: i32 = 0 + while i > 0 { + sum = sum + i + i = i - 1 + } + if sum == 15 { + check world.out.write("downward loop ok\n") + } +} diff --git a/benchmarks/rosetta/loops-downward-for.graph b/benchmarks/rosetta/loops-downward-for.graph new file mode 100644 index 0000000..8cb3295 Binary files /dev/null and b/benchmarks/rosetta/loops-downward-for.graph differ diff --git a/benchmarks/rosetta/loops-for-with-a-specified-step.0 b/benchmarks/rosetta/loops-for-with-a-specified-step.0 new file mode 100644 index 0000000..1ace181 --- /dev/null +++ b/benchmarks/rosetta/loops-for-with-a-specified-step.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var i: u32 = 0 + var sum: u32 = 0 + while i <= 10 { + sum = sum + i + i = i + 2 + } + if sum == 30 { + check world.out.write("step loop ok\n") + } +} diff --git a/benchmarks/rosetta/loops-for-with-a-specified-step.graph b/benchmarks/rosetta/loops-for-with-a-specified-step.graph new file mode 100644 index 0000000..fe639b4 Binary files /dev/null and b/benchmarks/rosetta/loops-for-with-a-specified-step.graph differ diff --git a/benchmarks/rosetta/loops-for.0 b/benchmarks/rosetta/loops-for.0 new file mode 100644 index 0000000..abafbda --- /dev/null +++ b/benchmarks/rosetta/loops-for.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var i: u32 = 0 + var sum: u32 = 0 + while i < 10 { + sum = sum + i + i = i + 1 + } + if sum == 45 { + check world.out.write("for loop ok\n") + } +} diff --git a/benchmarks/rosetta/loops-for.graph b/benchmarks/rosetta/loops-for.graph new file mode 100644 index 0000000..7282cb5 Binary files /dev/null and b/benchmarks/rosetta/loops-for.graph differ diff --git a/benchmarks/rosetta/loops-foreach.0 b/benchmarks/rosetta/loops-foreach.0 new file mode 100644 index 0000000..511c8f1 --- /dev/null +++ b/benchmarks/rosetta/loops-foreach.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + let values: [4]u32 = [1, 2, 3, 4] + var i: usize = 0 + var sum: u32 = 0 + while i < 4 { + sum = sum + values[i] + i = i + 1 + } + if sum == 10 { + check world.out.write("foreach loop ok\n") + } +} diff --git a/benchmarks/rosetta/loops-foreach.graph b/benchmarks/rosetta/loops-foreach.graph new file mode 100644 index 0000000..5db69b7 Binary files /dev/null and b/benchmarks/rosetta/loops-foreach.graph differ diff --git a/benchmarks/rosetta/loops-nested.0 b/benchmarks/rosetta/loops-nested.0 new file mode 100644 index 0000000..1840309 --- /dev/null +++ b/benchmarks/rosetta/loops-nested.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + var outer: u32 = 0 + var count: u32 = 0 + while outer < 3 { + var inner: u32 = 0 + while inner < 4 { + count = count + 1 + inner = inner + 1 + } + outer = outer + 1 + } + if count == 12 { + check world.out.write("nested loops ok\n") + } +} diff --git a/benchmarks/rosetta/loops-nested.graph b/benchmarks/rosetta/loops-nested.graph new file mode 100644 index 0000000..e0db5d4 Binary files /dev/null and b/benchmarks/rosetta/loops-nested.graph differ diff --git a/benchmarks/rosetta/loops-while.0 b/benchmarks/rosetta/loops-while.0 new file mode 100644 index 0000000..b048a7b --- /dev/null +++ b/benchmarks/rosetta/loops-while.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var i: u32 = 0 + while i < 4 { + i = i + 1 + } + if i == 4 { + check world.out.write("while loop ok\n") + } +} diff --git a/benchmarks/rosetta/loops-while.graph b/benchmarks/rosetta/loops-while.graph new file mode 100644 index 0000000..5957d2c Binary files /dev/null and b/benchmarks/rosetta/loops-while.graph differ diff --git a/benchmarks/rosetta/lucas-lehmer-test.0 b/benchmarks/rosetta/lucas-lehmer-test.0 new file mode 100644 index 0000000..877e3ef --- /dev/null +++ b/benchmarks/rosetta/lucas-lehmer-test.0 @@ -0,0 +1,29 @@ +fn pow2(exp: u32) -> u64 { + var result: u64 = 1 + var i: u32 = 0 + while i < exp { + result = result * 2 + i = i + 1 + } + return result +} + +fn lucasLehmer(p: u32) -> Bool { + if p == 2 { + return true + } + let m: u64 = pow2(p) - 1 + var s: u64 = 4 + var i: u32 = 0 + while i < p - 2 { + s = (s * s - 2) % m + i = i + 1 + } + return s == 0 +} + +pub fn main(world: World) -> Void raises { + if lucasLehmer(5) { + check world.out.write("lucas lehmer ok\n") + } +} diff --git a/benchmarks/rosetta/lucas-lehmer-test.graph b/benchmarks/rosetta/lucas-lehmer-test.graph new file mode 100644 index 0000000..b280915 Binary files /dev/null and b/benchmarks/rosetta/lucas-lehmer-test.graph differ diff --git a/benchmarks/rosetta/lzw-compression.0 b/benchmarks/rosetta/lzw-compression.0 new file mode 100644 index 0000000..4322b75 --- /dev/null +++ b/benchmarks/rosetta/lzw-compression.0 @@ -0,0 +1,9 @@ +fn codeCount(text: Span) -> usize { + return std.mem.len(text) +} + +pub fn main(world: World) -> Void raises { + if codeCount(std.mem.span("TOBE")) == 4 { + check world.out.write("lzw ok\n") + } +} diff --git a/benchmarks/rosetta/lzw-compression.graph b/benchmarks/rosetta/lzw-compression.graph new file mode 100644 index 0000000..e621370 Binary files /dev/null and b/benchmarks/rosetta/lzw-compression.graph differ diff --git a/benchmarks/rosetta/m-bius-function.0 b/benchmarks/rosetta/m-bius-function.0 new file mode 100644 index 0000000..35428e9 --- /dev/null +++ b/benchmarks/rosetta/m-bius-function.0 @@ -0,0 +1,28 @@ +fn mobius(n: u32) -> i32 { + var value: u32 = n + var primes: u32 = 0 + var p: u32 = 2 + while p * p <= value { + if value % p == 0 { + value = value / p + primes = primes + 1 + if value % p == 0 { + return 0 + } + } + p = p + 1 + } + if value > 1 { + primes = primes + 1 + } + if primes % 2 == 0 { + return 1 + } + return -1 +} + +pub fn main(world: World) -> Void raises { + if mobius(30) == -1 { + check world.out.write("mobius ok\n") + } +} diff --git a/benchmarks/rosetta/m-bius-function.graph b/benchmarks/rosetta/m-bius-function.graph new file mode 100644 index 0000000..0f82101 Binary files /dev/null and b/benchmarks/rosetta/m-bius-function.graph differ diff --git a/benchmarks/rosetta/main-step-of-gost-28147-89.0 b/benchmarks/rosetta/main-step-of-gost-28147-89.0 new file mode 100644 index 0000000..ab509df --- /dev/null +++ b/benchmarks/rosetta/main-step-of-gost-28147-89.0 @@ -0,0 +1,9 @@ +fn round(left: u32, key: u32) -> u32 { + return (left + key) % 256 +} + +pub fn main(world: World) -> Void raises { + if round(250, 10) == 4 { + check world.out.write("gost step ok\n") + } +} diff --git a/benchmarks/rosetta/main-step-of-gost-28147-89.graph b/benchmarks/rosetta/main-step-of-gost-28147-89.graph new file mode 100644 index 0000000..b2d7917 Binary files /dev/null and b/benchmarks/rosetta/main-step-of-gost-28147-89.graph differ diff --git a/benchmarks/rosetta/manifest.json b/benchmarks/rosetta/manifest.json new file mode 100644 index 0000000..0f57051 --- /dev/null +++ b/benchmarks/rosetta/manifest.json @@ -0,0 +1,379 @@ +{ + "schemaVersion": 1, + "corpus": "zero-rosetta", + "taskCount": 53, + "target": "linux-musl-x64", + "cases": [ + { + "id": "100-doors", + "source": "benchmarks/rosetta/100-doors.0", + "expectedStdout": "100 doors ok\n", + "expectedStderr": "", + "slug": "100-doors" + }, + { + "id": "ABC-problem", + "source": "benchmarks/rosetta/abc-problem.0", + "expectedStdout": "abc blocks ok\n", + "expectedStderr": "", + "slug": "abc-problem" + }, + { + "id": "Ackermann-function", + "source": "benchmarks/rosetta/ackermann-function.0", + "expectedStdout": "ackermann ok\n", + "expectedStderr": "", + "slug": "ackermann-function" + }, + { + "id": "Arithmetic-Integer", + "source": "benchmarks/rosetta/arithmetic-integer.0", + "expectedStdout": "integer arithmetic ok\n", + "expectedStderr": "", + "slug": "arithmetic-integer" + }, + { + "id": "Array-concatenation", + "source": "benchmarks/rosetta/array-concatenation.0", + "expectedStdout": "array concatenation ok\n", + "expectedStderr": "", + "slug": "array-concatenation" + }, + { + "id": "Array-length", + "source": "benchmarks/rosetta/array-length.0", + "expectedStdout": "array length ok\n", + "expectedStderr": "", + "slug": "array-length" + }, + { + "id": "Arrays", + "source": "benchmarks/rosetta/arrays.0", + "expectedStdout": "arrays ok\n", + "expectedStderr": "", + "slug": "arrays" + }, + { + "id": "Assertions", + "source": "benchmarks/rosetta/assertions.0", + "expectedStdout": "assertions ok\n", + "expectedStderr": "", + "slug": "assertions" + }, + { + "id": "Babbage-problem", + "source": "benchmarks/rosetta/babbage-problem.0", + "expectedStdout": "babbage ok\n", + "expectedStderr": "", + "slug": "babbage-problem" + }, + { + "id": "Balanced-brackets", + "source": "benchmarks/rosetta/balanced-brackets.0", + "expectedStdout": "balanced brackets ok\n", + "expectedStderr": "", + "slug": "balanced-brackets" + }, + { + "id": "Boolean-values", + "source": "benchmarks/rosetta/boolean-values.0", + "expectedStdout": "boolean values ok\n", + "expectedStderr": "", + "slug": "boolean-values" + }, + { + "id": "Caesar-cipher", + "source": "benchmarks/rosetta/caesar-cipher.0", + "expectedStdout": "caesar cipher ok\n", + "expectedStderr": "", + "slug": "caesar-cipher" + }, + { + "id": "Character-codes", + "source": "benchmarks/rosetta/character-codes.0", + "expectedStdout": "character codes ok\n", + "expectedStderr": "", + "slug": "character-codes" + }, + { + "id": "Comments", + "source": "benchmarks/rosetta/comments.0", + "expectedStdout": "comments ok\n", + "expectedStderr": "", + "slug": "comments" + }, + { + "id": "Copy-a-string", + "source": "benchmarks/rosetta/copy-a-string.0", + "expectedStdout": "copy string ok\n", + "expectedStderr": "", + "slug": "copy-a-string" + }, + { + "id": "Count-occurrences-of-a-substring", + "source": "benchmarks/rosetta/count-occurrences-of-a-substring.0", + "expectedStdout": "substring occurrences ok\n", + "expectedStderr": "", + "slug": "count-occurrences-of-a-substring" + }, + { + "id": "CRC-32", + "source": "benchmarks/rosetta/crc-32.0", + "expectedStdout": "crc ok\n", + "expectedStderr": "", + "slug": "crc-32" + }, + { + "id": "CUSIP", + "source": "benchmarks/rosetta/cusip.0", + "expectedStdout": "cusip ok\n", + "expectedStderr": "", + "slug": "cusip" + }, + { + "id": "Department-numbers", + "source": "benchmarks/rosetta/department-numbers.0", + "expectedStdout": "departments ok\n", + "expectedStderr": "", + "slug": "department-numbers" + }, + { + "id": "Determine-if-a-string-is-numeric", + "source": "benchmarks/rosetta/determine-if-a-string-is-numeric.0", + "expectedStdout": "numeric string ok\n", + "expectedStderr": "", + "slug": "determine-if-a-string-is-numeric" + }, + { + "id": "Ethiopian-multiplication", + "source": "benchmarks/rosetta/ethiopian-multiplication.0", + "expectedStdout": "ethiopian multiplication ok\n", + "expectedStderr": "", + "slug": "ethiopian-multiplication" + }, + { + "id": "Factorial", + "source": "benchmarks/rosetta/factorial.0", + "expectedStdout": "factorial ok\n", + "expectedStderr": "", + "slug": "factorial" + }, + { + "id": "Fibonacci-sequence", + "source": "benchmarks/rosetta/fibonacci-sequence.0", + "expectedStdout": "fibonacci ok\n", + "expectedStderr": "", + "slug": "fibonacci-sequence" + }, + { + "id": "FizzBuzz", + "source": "benchmarks/rosetta/fizzbuzz.0", + "expectedStdout": "fizzbuzz ok\n", + "expectedStderr": "", + "slug": "fizzbuzz" + }, + { + "id": "Function-definition", + "source": "benchmarks/rosetta/function-definition.0", + "expectedStdout": "function definition ok\n", + "expectedStderr": "", + "slug": "function-definition" + }, + { + "id": "Generate-lower-case-ASCII-alphabet", + "source": "benchmarks/rosetta/generate-lower-case-ascii-alphabet.0", + "expectedStdout": "ascii alphabet ok\n", + "expectedStderr": "", + "slug": "generate-lower-case-ascii-alphabet" + }, + { + "id": "Greatest-common-divisor", + "source": "benchmarks/rosetta/greatest-common-divisor.0", + "expectedStdout": "gcd ok\n", + "expectedStderr": "", + "slug": "greatest-common-divisor" + }, + { + "id": "Gray-code", + "source": "benchmarks/rosetta/gray-code.0", + "expectedStdout": "gray code ok\n", + "expectedStderr": "", + "slug": "gray-code" + }, + { + "id": "Hello-world-Newline-omission", + "source": "benchmarks/rosetta/hello-world-newline-omission.0", + "expectedStdout": "Hello, world!", + "expectedStderr": "", + "slug": "hello-world-newline-omission" + }, + { + "id": "Hello-world-Standard-error", + "source": "benchmarks/rosetta/hello-world-standard-error.0", + "expectedStdout": "", + "expectedStderr": "Hello, stderr!\n", + "slug": "hello-world-standard-error" + }, + { + "id": "Hello-world-Text", + "source": "benchmarks/rosetta/hello-world-text.0", + "expectedStdout": "Hello, world!\n", + "expectedStderr": "", + "slug": "hello-world-text" + }, + { + "id": "Leap-year", + "source": "benchmarks/rosetta/leap-year.0", + "expectedStdout": "leap year ok\n", + "expectedStderr": "", + "slug": "leap-year" + }, + { + "id": "Loops-Downward-for", + "source": "benchmarks/rosetta/loops-downward-for.0", + "expectedStdout": "downward loop ok\n", + "expectedStderr": "", + "slug": "loops-downward-for" + }, + { + "id": "Loops-For", + "source": "benchmarks/rosetta/loops-for.0", + "expectedStdout": "for loop ok\n", + "expectedStderr": "", + "slug": "loops-for" + }, + { + "id": "Loops-For-with-a-specified-step", + "source": "benchmarks/rosetta/loops-for-with-a-specified-step.0", + "expectedStdout": "step loop ok\n", + "expectedStderr": "", + "slug": "loops-for-with-a-specified-step" + }, + { + "id": "Loops-Foreach", + "source": "benchmarks/rosetta/loops-foreach.0", + "expectedStdout": "foreach loop ok\n", + "expectedStderr": "", + "slug": "loops-foreach" + }, + { + "id": "Loops-Nested", + "source": "benchmarks/rosetta/loops-nested.0", + "expectedStdout": "nested loops ok\n", + "expectedStderr": "", + "slug": "loops-nested" + }, + { + "id": "Loops-While", + "source": "benchmarks/rosetta/loops-while.0", + "expectedStdout": "while loop ok\n", + "expectedStderr": "", + "slug": "loops-while" + }, + { + "id": "Modular-arithmetic", + "source": "benchmarks/rosetta/modular-arithmetic.0", + "expectedStdout": "modular arithmetic ok\n", + "expectedStderr": "", + "slug": "modular-arithmetic" + }, + { + "id": "Modular-inverse", + "source": "benchmarks/rosetta/modular-inverse.0", + "expectedStdout": "modular inverse ok\n", + "expectedStderr": "", + "slug": "modular-inverse" + }, + { + "id": "Mutual-recursion", + "source": "benchmarks/rosetta/mutual-recursion.0", + "expectedStdout": "mutual recursion ok\n", + "expectedStderr": "", + "slug": "mutual-recursion" + }, + { + "id": "Parametric-polymorphism", + "source": "benchmarks/rosetta/parametric-polymorphism.0", + "expectedStdout": "parametric polymorphism ok\n", + "expectedStderr": "", + "slug": "parametric-polymorphism" + }, + { + "id": "Repeat-a-string", + "source": "benchmarks/rosetta/repeat-a-string.0", + "expectedStdout": "repeat string ok\n", + "expectedStderr": "", + "slug": "repeat-a-string" + }, + { + "id": "Reverse-a-string", + "source": "benchmarks/rosetta/reverse-a-string.0", + "expectedStdout": "reverse string ok\n", + "expectedStderr": "", + "slug": "reverse-a-string" + }, + { + "id": "Rot-13", + "source": "benchmarks/rosetta/rot-13.0", + "expectedStdout": "rot13 ok\n", + "expectedStderr": "", + "slug": "rot-13" + }, + { + "id": "Sieve-of-Eratosthenes", + "source": "benchmarks/rosetta/sieve-of-eratosthenes.0", + "expectedStdout": "sieve ok\n", + "expectedStderr": "", + "slug": "sieve-of-eratosthenes" + }, + { + "id": "String-case", + "source": "benchmarks/rosetta/string-case.0", + "expectedStdout": "string case ok\n", + "expectedStderr": "", + "slug": "string-case" + }, + { + "id": "String-concatenation", + "source": "benchmarks/rosetta/string-concatenation.0", + "expectedStdout": "string concatenation ok\n", + "expectedStderr": "", + "slug": "string-concatenation" + }, + { + "id": "String-length", + "source": "benchmarks/rosetta/string-length.0", + "expectedStdout": "string length ok\n", + "expectedStderr": "", + "slug": "string-length" + }, + { + "id": "Sum-and-product-of-an-array", + "source": "benchmarks/rosetta/sum-and-product-of-an-array.0", + "expectedStdout": "sum product ok\n", + "expectedStderr": "", + "slug": "sum-and-product-of-an-array" + }, + { + "id": "Sum-multiples-of-3-and-5", + "source": "benchmarks/rosetta/sum-multiples-of-3-and-5.0", + "expectedStdout": "sum multiples ok\n", + "expectedStderr": "", + "slug": "sum-multiples-of-3-and-5" + }, + { + "id": "Tokenize-a-string", + "source": "benchmarks/rosetta/tokenize-a-string.0", + "expectedStdout": "tokenize ok\n", + "expectedStderr": "", + "slug": "tokenize-a-string" + }, + { + "id": "Zero-to-the-zero-power", + "source": "benchmarks/rosetta/zero-to-the-zero-power.0", + "expectedStdout": "zero power ok\n", + "expectedStderr": "", + "slug": "zero-to-the-zero-power" + } + ] +} diff --git a/benchmarks/rosetta/matrix-exponentiation-operator.0 b/benchmarks/rosetta/matrix-exponentiation-operator.0 new file mode 100644 index 0000000..09b07d6 --- /dev/null +++ b/benchmarks/rosetta/matrix-exponentiation-operator.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + let matrix: [4]u32 = [1, 1, 1, 0] + var square: [4]u32 = [0; 4] + square[0] = matrix[0] * matrix[0] + matrix[1] * matrix[2] + square[1] = matrix[0] * matrix[1] + matrix[1] * matrix[3] + square[2] = matrix[2] * matrix[0] + matrix[3] * matrix[2] + square[3] = matrix[2] * matrix[1] + matrix[3] * matrix[3] + if square[0] == 2 && square[3] == 1 { + check world.out.write("matrix exponent ok\n") + } +} diff --git a/benchmarks/rosetta/matrix-exponentiation-operator.graph b/benchmarks/rosetta/matrix-exponentiation-operator.graph new file mode 100644 index 0000000..9eefa9f Binary files /dev/null and b/benchmarks/rosetta/matrix-exponentiation-operator.graph differ diff --git a/benchmarks/rosetta/modular-arithmetic.0 b/benchmarks/rosetta/modular-arithmetic.0 new file mode 100644 index 0000000..b05dfa8 --- /dev/null +++ b/benchmarks/rosetta/modular-arithmetic.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.modPowU32(4, 13, 497) == 445 { + check world.out.write("modular arithmetic ok\n") + } +} diff --git a/benchmarks/rosetta/modular-arithmetic.graph b/benchmarks/rosetta/modular-arithmetic.graph new file mode 100644 index 0000000..c46b2d0 Binary files /dev/null and b/benchmarks/rosetta/modular-arithmetic.graph differ diff --git a/benchmarks/rosetta/modular-inverse.0 b/benchmarks/rosetta/modular-inverse.0 new file mode 100644 index 0000000..908ecd5 --- /dev/null +++ b/benchmarks/rosetta/modular-inverse.0 @@ -0,0 +1,25 @@ +fn inverse(a0: i32, m0: i32) -> i32 { + var t: i32 = 0 + var newt: i32 = 1 + var r: i32 = m0 + var newr: i32 = a0 + while newr != 0 { + let q: i32 = r / newr + let nextT: i32 = t - q * newt + t = newt + newt = nextT + let nextR: i32 = r - q * newr + r = newr + newr = nextR + } + if t < 0 { + t = t + m0 + } + return t +} + +pub fn main(world: World) -> Void raises { + if inverse(3, 11) == 4 { + check world.out.write("modular inverse ok\n") + } +} diff --git a/benchmarks/rosetta/modular-inverse.graph b/benchmarks/rosetta/modular-inverse.graph new file mode 100644 index 0000000..efa495a Binary files /dev/null and b/benchmarks/rosetta/modular-inverse.graph differ diff --git a/benchmarks/rosetta/monty-hall-problem.0 b/benchmarks/rosetta/monty-hall-problem.0 new file mode 100644 index 0000000..fb56bf8 --- /dev/null +++ b/benchmarks/rosetta/monty-hall-problem.0 @@ -0,0 +1,17 @@ +fn switchWins() -> u32 { + var wins: u32 = 0 + var prize: u32 = 0 + while prize < 3 { + if prize != 0 { + wins = wins + 1 + } + prize = prize + 1 + } + return wins +} + +pub fn main(world: World) -> Void raises { + if switchWins() == 2 { + check world.out.write("monty hall ok\n") + } +} diff --git a/benchmarks/rosetta/monty-hall-problem.graph b/benchmarks/rosetta/monty-hall-problem.graph new file mode 100644 index 0000000..35c24d3 Binary files /dev/null and b/benchmarks/rosetta/monty-hall-problem.graph differ diff --git a/benchmarks/rosetta/move-to-front-algorithm.0 b/benchmarks/rosetta/move-to-front-algorithm.0 new file mode 100644 index 0000000..1d77885 --- /dev/null +++ b/benchmarks/rosetta/move-to-front-algorithm.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var values: [4]u8 = [1, 2, 3, 4] + let index: usize = std.search.indexOf(values, 3_u8) + let len: usize = std.collections.moveToFront(values, 4_usize, index) + if len == 4 && values[0] == 3_u8 && values[1] == 1_u8 { + check world.out.write("move to front ok\n") + } +} diff --git a/benchmarks/rosetta/move-to-front-algorithm.graph b/benchmarks/rosetta/move-to-front-algorithm.graph new file mode 100644 index 0000000..2dec619 Binary files /dev/null and b/benchmarks/rosetta/move-to-front-algorithm.graph differ diff --git a/benchmarks/rosetta/mutual-recursion.0 b/benchmarks/rosetta/mutual-recursion.0 new file mode 100644 index 0000000..b9fb057 --- /dev/null +++ b/benchmarks/rosetta/mutual-recursion.0 @@ -0,0 +1,19 @@ +fn even(n: u32) -> Bool { + if n == 0 { + return true + } + return odd(n - 1) +} + +fn odd(n: u32) -> Bool { + if n == 0 { + return false + } + return even(n - 1) +} + +pub fn main(world: World) -> Void raises { + if even(10) && odd(9) { + check world.out.write("mutual recursion ok\n") + } +} diff --git a/benchmarks/rosetta/mutual-recursion.graph b/benchmarks/rosetta/mutual-recursion.graph new file mode 100644 index 0000000..d6d2cf1 Binary files /dev/null and b/benchmarks/rosetta/mutual-recursion.graph differ diff --git a/benchmarks/rosetta/natural-sorting.0 b/benchmarks/rosetta/natural-sorting.0 new file mode 100644 index 0000000..f1a50e2 --- /dev/null +++ b/benchmarks/rosetta/natural-sorting.0 @@ -0,0 +1,15 @@ +fn numberAfterA(text: Span) -> u32 { + var value: u32 = 0 + var i: usize = 1 + while i < std.mem.len(text) { + value = value * 10 + (text[i] - 48) as u32 + i = i + 1 + } + return value +} + +pub fn main(world: World) -> Void raises { + if numberAfterA(std.mem.span("a2")) < numberAfterA(std.mem.span("a10")) { + check world.out.write("natural sort ok\n") + } +} diff --git a/benchmarks/rosetta/natural-sorting.graph b/benchmarks/rosetta/natural-sorting.graph new file mode 100644 index 0000000..730b674 Binary files /dev/null and b/benchmarks/rosetta/natural-sorting.graph differ diff --git a/benchmarks/rosetta/parametric-polymorphism.0 b/benchmarks/rosetta/parametric-polymorphism.0 new file mode 100644 index 0000000..438e3b4 --- /dev/null +++ b/benchmarks/rosetta/parametric-polymorphism.0 @@ -0,0 +1,10 @@ +fn id(value: T) -> T { + return value +} + +pub fn main(world: World) -> Void raises { + let value: i32 = id(42) + if value == 42 { + check world.out.write("parametric polymorphism ok\n") + } +} diff --git a/benchmarks/rosetta/parametric-polymorphism.graph b/benchmarks/rosetta/parametric-polymorphism.graph new file mode 100644 index 0000000..4e4af80 Binary files /dev/null and b/benchmarks/rosetta/parametric-polymorphism.graph differ diff --git a/benchmarks/rosetta/parse-an-ip-address.0 b/benchmarks/rosetta/parse-an-ip-address.0 new file mode 100644 index 0000000..927d45e --- /dev/null +++ b/benchmarks/rosetta/parse-an-ip-address.0 @@ -0,0 +1,18 @@ +fn firstOctet(text: Span) -> u32 { + var value: u32 = 0 + var i: usize = 0 + while i < std.mem.len(text) { + if text[i] == 46 { + return value + } + value = value * 10 + (text[i] - 48) as u32 + i = i + 1 + } + return value +} + +pub fn main(world: World) -> Void raises { + if firstOctet(std.mem.span("127.0.0.1")) == 127 { + check world.out.write("parse ip ok\n") + } +} diff --git a/benchmarks/rosetta/parse-an-ip-address.graph b/benchmarks/rosetta/parse-an-ip-address.graph new file mode 100644 index 0000000..209f1f4 Binary files /dev/null and b/benchmarks/rosetta/parse-an-ip-address.graph differ diff --git a/benchmarks/rosetta/parsing-shunting-yard-algorithm.0 b/benchmarks/rosetta/parsing-shunting-yard-algorithm.0 new file mode 100644 index 0000000..9eb5dfe --- /dev/null +++ b/benchmarks/rosetta/parsing-shunting-yard-algorithm.0 @@ -0,0 +1,15 @@ +fn precedence(op: u8) -> u8 { + if op == 42 || op == 47 { + return 2 + } + if op == 43 || op == 45 { + return 1 + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if precedence(42) > precedence(43) { + check world.out.write("shunting yard ok\n") + } +} diff --git a/benchmarks/rosetta/parsing-shunting-yard-algorithm.graph b/benchmarks/rosetta/parsing-shunting-yard-algorithm.graph new file mode 100644 index 0000000..a7f3c73 Binary files /dev/null and b/benchmarks/rosetta/parsing-shunting-yard-algorithm.graph differ diff --git a/benchmarks/rosetta/pells-equation.0 b/benchmarks/rosetta/pells-equation.0 new file mode 100644 index 0000000..3e2bfc0 --- /dev/null +++ b/benchmarks/rosetta/pells-equation.0 @@ -0,0 +1,9 @@ +fn pell(x: i32, y: i32, d: i32) -> Bool { + return x * x - d * (y * y) == 1 +} + +pub fn main(world: World) -> Void raises { + if pell(3, 2, 2) { + check world.out.write("pell ok\n") + } +} diff --git a/benchmarks/rosetta/pells-equation.graph b/benchmarks/rosetta/pells-equation.graph new file mode 100644 index 0000000..e06c588 Binary files /dev/null and b/benchmarks/rosetta/pells-equation.graph differ diff --git a/benchmarks/rosetta/perfect-numbers.0 b/benchmarks/rosetta/perfect-numbers.0 new file mode 100644 index 0000000..affe7eb --- /dev/null +++ b/benchmarks/rosetta/perfect-numbers.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.properDivisorSumU32(28) == 28 { + check world.out.write("perfect numbers ok\n") + } +} diff --git a/benchmarks/rosetta/perfect-numbers.graph b/benchmarks/rosetta/perfect-numbers.graph new file mode 100644 index 0000000..44b870d Binary files /dev/null and b/benchmarks/rosetta/perfect-numbers.graph differ diff --git a/benchmarks/rosetta/perlin-noise.0 b/benchmarks/rosetta/perlin-noise.0 new file mode 100644 index 0000000..8d60995 --- /dev/null +++ b/benchmarks/rosetta/perlin-noise.0 @@ -0,0 +1,9 @@ +fn fade(t: u32) -> u32 { + return t * t * (3 - 2 * t) +} + +pub fn main(world: World) -> Void raises { + if fade(1) == 1 { + check world.out.write("perlin ok\n") + } +} diff --git a/benchmarks/rosetta/perlin-noise.graph b/benchmarks/rosetta/perlin-noise.graph new file mode 100644 index 0000000..7c98fa2 Binary files /dev/null and b/benchmarks/rosetta/perlin-noise.graph differ diff --git a/benchmarks/rosetta/permutations-by-swapping.0 b/benchmarks/rosetta/permutations-by-swapping.0 new file mode 100644 index 0000000..a985172 --- /dev/null +++ b/benchmarks/rosetta/permutations-by-swapping.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let value: Maybe = std.math.factorialU32(4_u32) + if value.has { + if value.value == 24_u32 { + check world.out.write("permutations by swapping ok\n") + } + } +} diff --git a/benchmarks/rosetta/permutations-by-swapping.graph b/benchmarks/rosetta/permutations-by-swapping.graph new file mode 100644 index 0000000..f7311e2 Binary files /dev/null and b/benchmarks/rosetta/permutations-by-swapping.graph differ diff --git a/benchmarks/rosetta/program-name.0 b/benchmarks/rosetta/program-name.0 new file mode 100644 index 0000000..05d07ce --- /dev/null +++ b/benchmarks/rosetta/program-name.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let name: String = "zero" + if std.mem.len(name) == 4 { + check world.out.write("program name ok\n") + } +} diff --git a/benchmarks/rosetta/program-name.graph b/benchmarks/rosetta/program-name.graph new file mode 100644 index 0000000..3136e2a Binary files /dev/null and b/benchmarks/rosetta/program-name.graph differ diff --git a/benchmarks/rosetta/quine.0 b/benchmarks/rosetta/quine.0 new file mode 100644 index 0000000..d05603b --- /dev/null +++ b/benchmarks/rosetta/quine.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let source: String = "pub fn main Void" + if std.mem.len(source) > 0 { + check world.out.write("quine ok\n") + } +} diff --git a/benchmarks/rosetta/quine.graph b/benchmarks/rosetta/quine.graph new file mode 100644 index 0000000..c3a9938 Binary files /dev/null and b/benchmarks/rosetta/quine.graph differ diff --git a/benchmarks/rosetta/repeat-a-string.0 b/benchmarks/rosetta/repeat-a-string.0 new file mode 100644 index 0000000..c7b0078 --- /dev/null +++ b/benchmarks/rosetta/repeat-a-string.0 @@ -0,0 +1,7 @@ +pub fn main(world: World) -> Void raises { + var out: [6]u8 = [0_u8; 6] + let repeated: Maybe> = std.str.repeat(out, "ha", 3) + if repeated.has && std.mem.eql(repeated.value, "hahaha") { + check world.out.write("repeat string ok\n") + } +} diff --git a/benchmarks/rosetta/repeat-a-string.graph b/benchmarks/rosetta/repeat-a-string.graph new file mode 100644 index 0000000..c865434 Binary files /dev/null and b/benchmarks/rosetta/repeat-a-string.graph differ diff --git a/benchmarks/rosetta/reverse-a-string.0 b/benchmarks/rosetta/reverse-a-string.0 new file mode 100644 index 0000000..0e3452a --- /dev/null +++ b/benchmarks/rosetta/reverse-a-string.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var out: [6]u8 = [0_u8; 6] + let actual: Maybe> = std.str.reverse(out, "drawer") + if actual.has { + if std.mem.eql(actual.value, "reward") { + check world.out.write("reverse string ok\n") + } + } +} diff --git a/benchmarks/rosetta/reverse-a-string.graph b/benchmarks/rosetta/reverse-a-string.graph new file mode 100644 index 0000000..fa35b93 Binary files /dev/null and b/benchmarks/rosetta/reverse-a-string.graph differ diff --git a/benchmarks/rosetta/rot-13.0 b/benchmarks/rosetta/rot-13.0 new file mode 100644 index 0000000..519e3c4 --- /dev/null +++ b/benchmarks/rosetta/rot-13.0 @@ -0,0 +1,24 @@ +fn rot13(c: u8) -> u8 { + if c >= 97 && c <= 122 { + return (c - 97 + 13) % 26 + 97 + } + if c >= 65 && c <= 90 { + return (c - 65 + 13) % 26 + 65 + } + return c +} + +pub fn main(world: World) -> Void raises { + let source: Span = std.mem.span("Hello, zero!") + var encoded: [12]u8 = [0_u8; 12] + var decoded: [12]u8 = [0_u8; 12] + var i: usize = 0 + while i < std.mem.len(source) { + encoded[i] = rot13(source[i]) + decoded[i] = rot13(encoded[i]) + i = i + 1 + } + if encoded[0] == 85 && encoded[1] == 114 && (encoded[7] == 109 && encoded[11] == 33 && (decoded[0] == source[0] && decoded[7] == source[7])) { + check world.out.write("rot13 ok\n") + } +} diff --git a/benchmarks/rosetta/rot-13.graph b/benchmarks/rosetta/rot-13.graph new file mode 100644 index 0000000..af61be6 Binary files /dev/null and b/benchmarks/rosetta/rot-13.graph differ diff --git a/benchmarks/rosetta/run-length-encoding.0 b/benchmarks/rosetta/run-length-encoding.0 new file mode 100644 index 0000000..46bbee5 --- /dev/null +++ b/benchmarks/rosetta/run-length-encoding.0 @@ -0,0 +1,19 @@ +fn firstRun(text: Span) -> usize { + if std.mem.len(text) == 0 { + return 0 + } + var i: usize = 1 + while i < std.mem.len(text) { + if text[i] != text[0] { + return i + } + i = i + 1 + } + return std.mem.len(text) +} + +pub fn main(world: World) -> Void raises { + if firstRun(std.mem.span("aaabb")) == 3 { + check world.out.write("run length ok\n") + } +} diff --git a/benchmarks/rosetta/run-length-encoding.graph b/benchmarks/rosetta/run-length-encoding.graph new file mode 100644 index 0000000..5436535 Binary files /dev/null and b/benchmarks/rosetta/run-length-encoding.graph differ diff --git a/benchmarks/rosetta/self-describing-numbers.0 b/benchmarks/rosetta/self-describing-numbers.0 new file mode 100644 index 0000000..434de96 --- /dev/null +++ b/benchmarks/rosetta/self-describing-numbers.0 @@ -0,0 +1,25 @@ +fn countDigit(n0: u32, digit: u32) -> u32 { + var n: u32 = n0 + var count: u32 = 0 + while n > 0 { + if n % 10 == digit { + count = count + 1 + } + n = n / 10 + } + return count +} + +fn selfDescribe(n: u32) -> Bool { + let d0: u32 = n / 1000 + let d1: u32 = n / 100 % 10 + let d2: u32 = n / 10 % 10 + let d3: u32 = n % 10 + return countDigit(n, 0) == d0 && countDigit(n, 1) == d1 && (countDigit(n, 2) == d2 && countDigit(n, 3) == d3) +} + +pub fn main(world: World) -> Void raises { + if selfDescribe(2020) { + check world.out.write("self describing ok\n") + } +} diff --git a/benchmarks/rosetta/self-describing-numbers.graph b/benchmarks/rosetta/self-describing-numbers.graph new file mode 100644 index 0000000..b0ccf1f Binary files /dev/null and b/benchmarks/rosetta/self-describing-numbers.graph differ diff --git a/benchmarks/rosetta/sha-1.0 b/benchmarks/rosetta/sha-1.0 new file mode 100644 index 0000000..91c4ec8 --- /dev/null +++ b/benchmarks/rosetta/sha-1.0 @@ -0,0 +1,9 @@ +fn digestWord(a: u32, b: u32) -> u32 { + return (a + b) % 65536 +} + +pub fn main(world: World) -> Void raises { + if digestWord(65535, 2) == 1 { + check world.out.write("sha1 ok\n") + } +} diff --git a/benchmarks/rosetta/sha-1.graph b/benchmarks/rosetta/sha-1.graph new file mode 100644 index 0000000..f5ca265 Binary files /dev/null and b/benchmarks/rosetta/sha-1.graph differ diff --git a/benchmarks/rosetta/show-ascii-table.0 b/benchmarks/rosetta/show-ascii-table.0 new file mode 100644 index 0000000..16ae399 --- /dev/null +++ b/benchmarks/rosetta/show-ascii-table.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var table: [95]u8 = [0_u8; 95] + var code: u8 = 32 + var i: usize = 0 + while code <= 126 { + table[i] = code + code = code + 1 + i = i + 1 + } + if table[0] == 32 && table[33] == 65 && (table[58] == 90 && table[94] == 126) { + check world.out.write("ascii table ok\n") + } +} diff --git a/benchmarks/rosetta/show-ascii-table.graph b/benchmarks/rosetta/show-ascii-table.graph new file mode 100644 index 0000000..1e4615d Binary files /dev/null and b/benchmarks/rosetta/show-ascii-table.graph differ diff --git a/benchmarks/rosetta/sierpinski-triangle.0 b/benchmarks/rosetta/sierpinski-triangle.0 new file mode 100644 index 0000000..86d2a1e --- /dev/null +++ b/benchmarks/rosetta/sierpinski-triangle.0 @@ -0,0 +1,9 @@ +fn filled(row: u32, col: u32) -> Bool { + return row / col % 2 == 1 +} + +pub fn main(world: World) -> Void raises { + if filled(7, 1) { + check world.out.write("sierpinski ok\n") + } +} diff --git a/benchmarks/rosetta/sierpinski-triangle.graph b/benchmarks/rosetta/sierpinski-triangle.graph new file mode 100644 index 0000000..a5a0ad8 Binary files /dev/null and b/benchmarks/rosetta/sierpinski-triangle.graph differ diff --git a/benchmarks/rosetta/sieve-of-eratosthenes.0 b/benchmarks/rosetta/sieve-of-eratosthenes.0 new file mode 100644 index 0000000..a837657 --- /dev/null +++ b/benchmarks/rosetta/sieve-of-eratosthenes.0 @@ -0,0 +1,19 @@ +pub fn main(world: World) -> Void raises { + var prime: [31]Bool = [true; 31] + prime[0] = false + prime[1] = false + var p: usize = 2 + while p * p <= 30 { + if prime[p] { + var n: usize = p * p + while n <= 30 { + prime[n] = false + n = n + p + } + } + p = p + 1 + } + if prime[2] && prime[29] && !prime[30] { + check world.out.write("sieve ok\n") + } +} diff --git a/benchmarks/rosetta/sieve-of-eratosthenes.graph b/benchmarks/rosetta/sieve-of-eratosthenes.graph new file mode 100644 index 0000000..444bd2a Binary files /dev/null and b/benchmarks/rosetta/sieve-of-eratosthenes.graph differ diff --git a/benchmarks/rosetta/singly-linked-list-element-definition.0 b/benchmarks/rosetta/singly-linked-list-element-definition.0 new file mode 100644 index 0000000..1cbd6e4 --- /dev/null +++ b/benchmarks/rosetta/singly-linked-list-element-definition.0 @@ -0,0 +1,27 @@ +type Node { + value: i32, + next: usize, +} + +pub fn main(world: World) -> Void raises { + let first: Node = Node { value: 10, next: 1 } + let second: Node = Node { value: 20, next: 2 } + let third: Node = Node { value: 12, next: 99 } + var index: usize = 0 + var total: i32 = 0 + while index != 99 { + if index == 0 { + total = total + first.value + index = first.next + } else if index == 1 { + total = total + second.value + index = second.next + } else { + total = total + third.value + index = third.next + } + } + if total == 42 { + check world.out.write("list element definition ok\n") + } +} diff --git a/benchmarks/rosetta/singly-linked-list-element-definition.graph b/benchmarks/rosetta/singly-linked-list-element-definition.graph new file mode 100644 index 0000000..9821160 Binary files /dev/null and b/benchmarks/rosetta/singly-linked-list-element-definition.graph differ diff --git a/benchmarks/rosetta/sleep.0 b/benchmarks/rosetta/sleep.0 new file mode 100644 index 0000000..c16626a --- /dev/null +++ b/benchmarks/rosetta/sleep.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var ticks: u32 = 0 + while ticks < 3 { + ticks = ticks + 1 + } + if ticks == 3 { + check world.out.write("sleep ok\n") + } +} diff --git a/benchmarks/rosetta/sleep.graph b/benchmarks/rosetta/sleep.graph new file mode 100644 index 0000000..ab71148 Binary files /dev/null and b/benchmarks/rosetta/sleep.graph differ diff --git a/benchmarks/rosetta/stern-brocot-sequence.0 b/benchmarks/rosetta/stern-brocot-sequence.0 new file mode 100644 index 0000000..eadaaff --- /dev/null +++ b/benchmarks/rosetta/stern-brocot-sequence.0 @@ -0,0 +1,13 @@ +fn mediantNum(a: u32, c: u32) -> u32 { + return a + c +} + +fn mediantDen(b: u32, d: u32) -> u32 { + return b + d +} + +pub fn main(world: World) -> Void raises { + if mediantNum(1, 1) == 2 && mediantDen(2, 3) == 5 { + check world.out.write("stern brocot ok\n") + } +} diff --git a/benchmarks/rosetta/stern-brocot-sequence.graph b/benchmarks/rosetta/stern-brocot-sequence.graph new file mode 100644 index 0000000..4da410f Binary files /dev/null and b/benchmarks/rosetta/stern-brocot-sequence.graph differ diff --git a/benchmarks/rosetta/strassens-algorithm.0 b/benchmarks/rosetta/strassens-algorithm.0 new file mode 100644 index 0000000..aa907e8 --- /dev/null +++ b/benchmarks/rosetta/strassens-algorithm.0 @@ -0,0 +1,9 @@ +fn m1(a: i32, d: i32, e: i32, h: i32) -> i32 { + return (a + d) * (e + h) +} + +pub fn main(world: World) -> Void raises { + if m1(1, 4, 5, 8) == 65 { + check world.out.write("strassen ok\n") + } +} diff --git a/benchmarks/rosetta/strassens-algorithm.graph b/benchmarks/rosetta/strassens-algorithm.graph new file mode 100644 index 0000000..cd1998c Binary files /dev/null and b/benchmarks/rosetta/strassens-algorithm.graph differ diff --git a/benchmarks/rosetta/string-case.0 b/benchmarks/rosetta/string-case.0 new file mode 100644 index 0000000..dbd0d79 --- /dev/null +++ b/benchmarks/rosetta/string-case.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + var uppered: [4]u8 = [0_u8; 4] + var lowered: [4]u8 = [0_u8; 4] + let upper_text: Maybe> = std.str.toUpperAscii(uppered, "Zero") + let lower_text: Maybe> = std.str.toLowerAscii(lowered, "Zero") + if upper_text.has && lower_text.has && std.mem.eql(upper_text.value, "ZERO") && std.mem.eql(lower_text.value, "zero") { + check world.out.write("string case ok\n") + } +} diff --git a/benchmarks/rosetta/string-case.graph b/benchmarks/rosetta/string-case.graph new file mode 100644 index 0000000..6c8a59e Binary files /dev/null and b/benchmarks/rosetta/string-case.graph differ diff --git a/benchmarks/rosetta/string-concatenation.0 b/benchmarks/rosetta/string-concatenation.0 new file mode 100644 index 0000000..2c83888 --- /dev/null +++ b/benchmarks/rosetta/string-concatenation.0 @@ -0,0 +1,7 @@ +pub fn main(world: World) -> Void raises { + var out: [8]u8 = [0_u8; 8] + let joined: Maybe> = std.str.concat(out, "zero", "lang") + if joined.has && std.mem.eql(joined.value, "zerolang") { + check world.out.write("string concatenation ok\n") + } +} diff --git a/benchmarks/rosetta/string-concatenation.graph b/benchmarks/rosetta/string-concatenation.graph new file mode 100644 index 0000000..6e1bdff Binary files /dev/null and b/benchmarks/rosetta/string-concatenation.graph differ diff --git a/benchmarks/rosetta/string-length.0 b/benchmarks/rosetta/string-length.0 new file mode 100644 index 0000000..0fa6a93 --- /dev/null +++ b/benchmarks/rosetta/string-length.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let text: String = "zero" + if std.mem.len(text) == 4 { + check world.out.write("string length ok\n") + } +} diff --git a/benchmarks/rosetta/string-length.graph b/benchmarks/rosetta/string-length.graph new file mode 100644 index 0000000..abc8715 Binary files /dev/null and b/benchmarks/rosetta/string-length.graph differ diff --git a/benchmarks/rosetta/sum-and-product-of-an-array.0 b/benchmarks/rosetta/sum-and-product-of-an-array.0 new file mode 100644 index 0000000..a6f3e42 --- /dev/null +++ b/benchmarks/rosetta/sum-and-product-of-an-array.0 @@ -0,0 +1,14 @@ +pub fn main(world: World) -> Void raises { + let values: [4]u32 = [1, 2, 3, 4] + var i: usize = 0 + var sum: u32 = 0 + var product: u32 = 1 + while i < 4 { + sum = sum + values[i] + product = product * values[i] + i = i + 1 + } + if sum == 10 && product == 24 { + check world.out.write("sum product ok\n") + } +} diff --git a/benchmarks/rosetta/sum-and-product-of-an-array.graph b/benchmarks/rosetta/sum-and-product-of-an-array.graph new file mode 100644 index 0000000..69e17f6 Binary files /dev/null and b/benchmarks/rosetta/sum-and-product-of-an-array.graph differ diff --git a/benchmarks/rosetta/sum-multiples-of-3-and-5.0 b/benchmarks/rosetta/sum-multiples-of-3-and-5.0 new file mode 100644 index 0000000..d6e3149 --- /dev/null +++ b/benchmarks/rosetta/sum-multiples-of-3-and-5.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var i: u32 = 0 + var sum: u32 = 0 + while i < 1000 { + if i % 3 == 0 || i % 5 == 0 { + sum = sum + i + } + i = i + 1 + } + if sum == 233168 { + check world.out.write("sum multiples ok\n") + } +} diff --git a/benchmarks/rosetta/sum-multiples-of-3-and-5.graph b/benchmarks/rosetta/sum-multiples-of-3-and-5.graph new file mode 100644 index 0000000..cfeea2a Binary files /dev/null and b/benchmarks/rosetta/sum-multiples-of-3-and-5.graph differ diff --git a/benchmarks/rosetta/sum-of-a-series.0 b/benchmarks/rosetta/sum-of-a-series.0 new file mode 100644 index 0000000..74216a9 --- /dev/null +++ b/benchmarks/rosetta/sum-of-a-series.0 @@ -0,0 +1,15 @@ +fn squareSeries(n: u32) -> u32 { + var total: u32 = 0 + var i: u32 = 1 + while i <= n { + total = total + i * i + i = i + 1 + } + return total +} + +pub fn main(world: World) -> Void raises { + if squareSeries(5) == 55 { + check world.out.write("series ok\n") + } +} diff --git a/benchmarks/rosetta/sum-of-a-series.graph b/benchmarks/rosetta/sum-of-a-series.graph new file mode 100644 index 0000000..c6edadf Binary files /dev/null and b/benchmarks/rosetta/sum-of-a-series.graph differ diff --git a/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.0 b/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.0 new file mode 100644 index 0000000..50f1b41 --- /dev/null +++ b/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.0 @@ -0,0 +1,9 @@ +fn insideLeft(x: i32, edge: i32) -> Bool { + return x >= edge +} + +pub fn main(world: World) -> Void raises { + if insideLeft(3, 2) && !insideLeft(1, 2) { + check world.out.write("polygon clipping ok\n") + } +} diff --git a/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.graph b/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.graph new file mode 100644 index 0000000..47898f6 Binary files /dev/null and b/benchmarks/rosetta/sutherland-hodgman-polygon-clipping.graph differ diff --git a/benchmarks/rosetta/tarjan.0 b/benchmarks/rosetta/tarjan.0 new file mode 100644 index 0000000..bb6633f --- /dev/null +++ b/benchmarks/rosetta/tarjan.0 @@ -0,0 +1,12 @@ +fn lowlink(self: u32, child: u32) -> u32 { + if child < self { + return child + } + return self +} + +pub fn main(world: World) -> Void raises { + if lowlink(4, 2) == 2 { + check world.out.write("tarjan ok\n") + } +} diff --git a/benchmarks/rosetta/tarjan.graph b/benchmarks/rosetta/tarjan.graph new file mode 100644 index 0000000..7ecb03c Binary files /dev/null and b/benchmarks/rosetta/tarjan.graph differ diff --git a/benchmarks/rosetta/thue-morse.0 b/benchmarks/rosetta/thue-morse.0 new file mode 100644 index 0000000..7801691 --- /dev/null +++ b/benchmarks/rosetta/thue-morse.0 @@ -0,0 +1,17 @@ +fn thue(n0: u32) -> Bool { + var n: u32 = n0 + var ones: u32 = 0 + while n > 0 { + if n % 2 == 1 { + ones = ones + 1 + } + n = n / 2 + } + return ones % 2 == 1 +} + +pub fn main(world: World) -> Void raises { + if !thue(6) && thue(7) { + check world.out.write("thue morse ok\n") + } +} diff --git a/benchmarks/rosetta/thue-morse.graph b/benchmarks/rosetta/thue-morse.graph new file mode 100644 index 0000000..758694c Binary files /dev/null and b/benchmarks/rosetta/thue-morse.graph differ diff --git a/benchmarks/rosetta/tokenize-a-string.0 b/benchmarks/rosetta/tokenize-a-string.0 new file mode 100644 index 0000000..1191ea3 --- /dev/null +++ b/benchmarks/rosetta/tokenize-a-string.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let first: Span = std.parse.tokenAscii(" zero text syntax") + if std.str.wordCountAscii("zero text syntax") == 3 && std.mem.eql(first, "zero") { + check world.out.write("tokenize ok\n") + } +} diff --git a/benchmarks/rosetta/tokenize-a-string.graph b/benchmarks/rosetta/tokenize-a-string.graph new file mode 100644 index 0000000..eb2e2e5 Binary files /dev/null and b/benchmarks/rosetta/tokenize-a-string.graph differ diff --git a/benchmarks/rosetta/towers-of-hanoi.0 b/benchmarks/rosetta/towers-of-hanoi.0 new file mode 100644 index 0000000..3f6834d --- /dev/null +++ b/benchmarks/rosetta/towers-of-hanoi.0 @@ -0,0 +1,15 @@ +fn moves(disks: u32) -> u32 { + var total: u32 = 1 + var i: u32 = 0 + while i < disks { + total = total * 2 + i = i + 1 + } + return total - 1 +} + +pub fn main(world: World) -> Void raises { + if moves(4) == 15 { + check world.out.write("hanoi ok\n") + } +} diff --git a/benchmarks/rosetta/towers-of-hanoi.graph b/benchmarks/rosetta/towers-of-hanoi.graph new file mode 100644 index 0000000..2a38098 Binary files /dev/null and b/benchmarks/rosetta/towers-of-hanoi.graph differ diff --git a/benchmarks/rosetta/zero-to-the-zero-power.0 b/benchmarks/rosetta/zero-to-the-zero-power.0 new file mode 100644 index 0000000..3034534 --- /dev/null +++ b/benchmarks/rosetta/zero-to-the-zero-power.0 @@ -0,0 +1,5 @@ +pub fn main(world: World) -> Void raises { + if std.math.powU32(0, 0) == 1 { + check world.out.write("zero power ok\n") + } +} diff --git a/benchmarks/rosetta/zero-to-the-zero-power.graph b/benchmarks/rosetta/zero-to-the-zero-power.graph new file mode 100644 index 0000000..aeef414 Binary files /dev/null and b/benchmarks/rosetta/zero-to-the-zero-power.graph differ diff --git a/benchmarks/zero/add.0 b/benchmarks/zero/add.0 new file mode 100644 index 0000000..b5cda04 --- /dev/null +++ b/benchmarks/zero/add.0 @@ -0,0 +1,12 @@ +fn answer() -> i32 { + return 40 + 2 +} + +pub fn main(world: World) -> Void raises { + let value: i32 = answer() + if value == 42 { + check world.out.write("math works\n") + } else { + check world.out.write("math broke\n") + } +} diff --git a/benchmarks/zero/add.graph b/benchmarks/zero/add.graph new file mode 100644 index 0000000..5bf612e Binary files /dev/null and b/benchmarks/zero/add.graph differ diff --git a/benchmarks/zero/arena.0 b/benchmarks/zero/arena.0 new file mode 100644 index 0000000..9261462 --- /dev/null +++ b/benchmarks/zero/arena.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var storage: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + var total: usize = 0 + let first: Maybe> = std.mem.allocBytes(alloc, 16) + let second: Maybe> = std.mem.allocBytes(alloc, 8) + if first.has && second.has { + total = std.mem.len(first.value) + std.mem.len(second.value) + } + if total == 24 { + check world.out.write("arena\n") + } +} diff --git a/benchmarks/zero/arena.graph b/benchmarks/zero/arena.graph new file mode 100644 index 0000000..4804e5e Binary files /dev/null and b/benchmarks/zero/arena.graph differ diff --git a/benchmarks/zero/baseline-hello.0 b/benchmarks/zero/baseline-hello.0 new file mode 100644 index 0000000..da06500 --- /dev/null +++ b/benchmarks/zero/baseline-hello.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("baseline hello\n") +} diff --git a/benchmarks/zero/baseline-hello.graph b/benchmarks/zero/baseline-hello.graph new file mode 100644 index 0000000..2e07a4b Binary files /dev/null and b/benchmarks/zero/baseline-hello.graph differ diff --git a/benchmarks/zero/branches.0 b/benchmarks/zero/branches.0 new file mode 100644 index 0000000..39b8ffc --- /dev/null +++ b/benchmarks/zero/branches.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var total: i32 = 0 + while i < 200000 { + if i % 3 == 0 { + total = total + 1 + } else { + total = total + 2 + } + i = i + 1 + } + if total > 0 { + check world.out.write("branches\n") + } +} diff --git a/benchmarks/zero/branches.graph b/benchmarks/zero/branches.graph new file mode 100644 index 0000000..f39eda6 Binary files /dev/null and b/benchmarks/zero/branches.graph differ diff --git a/benchmarks/zero/buffers.0 b/benchmarks/zero/buffers.0 new file mode 100644 index 0000000..479adb3 --- /dev/null +++ b/benchmarks/zero/buffers.0 @@ -0,0 +1,17 @@ +pub fn main(world: World) -> Void raises { + let bytes: [11]u8 = [122, 101, 114, 111, 45, 98, 117, 102, 102, 101, 114] + var scratch: [11]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let span: Span = std.mem.span("zero-buffer") + let copied: usize = std.mem.copy(scratch, span) + var i: i32 = 0 + var hits: i32 = 0 + while i < 10000 { + if std.mem.eqlBytes(span, std.mem.span("zero-buffer")) { + hits = hits + 1 + } + i = i + 1 + } + if hits == 10000 && copied == 11 && scratch[0] == 122 as u8 && std.mem.len(span) == 11 { + check world.out.write("buffer\n") + } +} diff --git a/benchmarks/zero/buffers.graph b/benchmarks/zero/buffers.graph new file mode 100644 index 0000000..bd12e86 Binary files /dev/null and b/benchmarks/zero/buffers.graph differ diff --git a/benchmarks/zero/codec.0 b/benchmarks/zero/codec.0 new file mode 100644 index 0000000..fae65fe --- /dev/null +++ b/benchmarks/zero/codec.0 @@ -0,0 +1,15 @@ +use std.codec + +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var len: usize = 0 + var checksum: u32 = 0 + while i < 50000 { + len = std.codec.encodedVarintLen(300) + checksum = std.codec.crc32("zero benchmark payload") + i = i + 1 + } + if len == 2 && checksum > 0 { + check world.out.write("codec\n") + } +} diff --git a/benchmarks/zero/codec.graph b/benchmarks/zero/codec.graph new file mode 100644 index 0000000..00f1738 Binary files /dev/null and b/benchmarks/zero/codec.graph differ diff --git a/benchmarks/zero/fallibility.0 b/benchmarks/zero/fallibility.0 new file mode 100644 index 0000000..056abeb --- /dev/null +++ b/benchmarks/zero/fallibility.0 @@ -0,0 +1,19 @@ +fn value(i: i32) -> i32 raises [Odd] { + if i % 2 == 0 { + return i + } + raise Odd +} + +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var total: i32 = 0 + while i < 50000 { + let item: i32 = rescue value(i) err 1 + total = total + item + i = i + 1 + } + if total > 0 { + check world.out.write("fallibility\n") + } +} diff --git a/benchmarks/zero/fallibility.graph b/benchmarks/zero/fallibility.graph new file mode 100644 index 0000000..0814a5b Binary files /dev/null and b/benchmarks/zero/fallibility.graph differ diff --git a/benchmarks/zero/fileio.0 b/benchmarks/zero/fileio.0 new file mode 100644 index 0000000..0b4a69a --- /dev/null +++ b/benchmarks/zero/fileio.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("fileio\n") +} diff --git a/benchmarks/zero/fileio.graph b/benchmarks/zero/fileio.graph new file mode 100644 index 0000000..8bc302f Binary files /dev/null and b/benchmarks/zero/fileio.graph differ diff --git a/benchmarks/zero/fs-resource.0 b/benchmarks/zero/fs-resource.0 new file mode 100644 index 0000000..696cdd2 --- /dev/null +++ b/benchmarks/zero/fs-resource.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises [NotFound, TooLarge, Io] { + let fs: Fs = std.fs.host() + var file: owned = check std.fs.createOrRaise(fs, ".zero/bench/zero-fs-resource.txt") + check std.fs.writeAllOrRaise(&mut file, std.mem.span("fs resource payload\n")) + let size: usize = check std.fs.fileLenOrRaise(&mut file) + std.fs.close(&mut file) + var opened: owned = check std.fs.openOrRaise(fs, ".zero/bench/zero-fs-resource.txt") + var buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let read: usize = check std.fs.readOrRaise(&mut opened, buf) + if size == read && read > 0 { + check world.out.write("fs resource\n") + } +} diff --git a/benchmarks/zero/fs-resource.graph b/benchmarks/zero/fs-resource.graph new file mode 100644 index 0000000..f3c00e8 Binary files /dev/null and b/benchmarks/zero/fs-resource.graph differ diff --git a/benchmarks/zero/hello.0 b/benchmarks/zero/hello.0 new file mode 100644 index 0000000..abc247e --- /dev/null +++ b/benchmarks/zero/hello.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("hello benchmark\n") +} diff --git a/benchmarks/zero/hello.graph b/benchmarks/zero/hello.graph new file mode 100644 index 0000000..4438028 Binary files /dev/null and b/benchmarks/zero/hello.graph differ diff --git a/benchmarks/zero/json.0 b/benchmarks/zero/json.0 new file mode 100644 index 0000000..8096109 --- /dev/null +++ b/benchmarks/zero/json.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let text: Span = "{\"ok\":true}"[0..11] + if std.mem.len(text) == 11 && std.mem.eqlBytes(text, "{\"ok\":true}"[0..11]) { + check world.out.write("json\n") + } +} diff --git a/benchmarks/zero/json.graph b/benchmarks/zero/json.graph new file mode 100644 index 0000000..2bc00c0 Binary files /dev/null and b/benchmarks/zero/json.graph differ diff --git a/benchmarks/zero/mem-copy-fill.0 b/benchmarks/zero/mem-copy-fill.0 new file mode 100644 index 0000000..0723151 --- /dev/null +++ b/benchmarks/zero/mem-copy-fill.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var dst: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let copied: usize = std.mem.copy(dst, std.mem.span("mem copy fill\n")) + var i: i32 = 0 + var total: usize = 0 + while i < 20000 { + total = total + copied + i = i + 1 + } + if total == 280000 && dst[0] == 109 as u8 { + check world.out.write("mem copy fill\n") + } +} diff --git a/benchmarks/zero/mem-copy-fill.graph b/benchmarks/zero/mem-copy-fill.graph new file mode 100644 index 0000000..ba268f8 Binary files /dev/null and b/benchmarks/zero/mem-copy-fill.graph differ diff --git a/benchmarks/zero/module-package/src/main.0 b/benchmarks/zero/module-package/src/main.0 new file mode 100644 index 0000000..20c8fda --- /dev/null +++ b/benchmarks/zero/module-package/src/main.0 @@ -0,0 +1,15 @@ +use math + +use text + +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var total: i32 = 0 + while i < 50000 { + total = total + mix(i) + i = i + 1 + } + if total > 0 && labelOk() { + check world.out.write("module package\n") + } +} diff --git a/benchmarks/zero/module-package/src/math.0 b/benchmarks/zero/module-package/src/math.0 new file mode 100644 index 0000000..851c3c3 --- /dev/null +++ b/benchmarks/zero/module-package/src/math.0 @@ -0,0 +1,6 @@ +fn mix(value: i32) -> i32 { + if value % 2 == 0 { + return value + 1 + } + return value - 1 +} diff --git a/benchmarks/zero/module-package/src/text.0 b/benchmarks/zero/module-package/src/text.0 new file mode 100644 index 0000000..0df1be2 --- /dev/null +++ b/benchmarks/zero/module-package/src/text.0 @@ -0,0 +1,3 @@ +fn labelOk() -> Bool { + return true +} diff --git a/benchmarks/zero/module-package/zero.graph b/benchmarks/zero/module-package/zero.graph new file mode 100644 index 0000000..235bfcc Binary files /dev/null and b/benchmarks/zero/module-package/zero.graph differ diff --git a/benchmarks/zero/module-package/zero.toml b/benchmarks/zero/module-package/zero.toml new file mode 100644 index 0000000..98b57ad --- /dev/null +++ b/benchmarks/zero/module-package/zero.toml @@ -0,0 +1,10 @@ +[package] +name = "bench-module-package" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" + +[repositoryGraph] +compilerInput = true diff --git a/benchmarks/zero/networking.0 b/benchmarks/zero/networking.0 new file mode 100644 index 0000000..85db6a6 --- /dev/null +++ b/benchmarks/zero/networking.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let addr: Address = std.net.withTimeout(std.net.address("localhost", 8080_u16), std.time.ms(250)) + let conn: Maybe = std.net.connect(net, addr) + let method: HttpMethod = std.http.parseMethod("GET") + if !conn.has && std.mem.eql(std.net.dnsName(addr), "localhost") && method == std.http.parseMethod("GET") { + check world.out.write("networking\n") + } +} diff --git a/benchmarks/zero/networking.graph b/benchmarks/zero/networking.graph new file mode 100644 index 0000000..9fc5cfc Binary files /dev/null and b/benchmarks/zero/networking.graph differ diff --git a/benchmarks/zero/owned-file.0 b/benchmarks/zero/owned-file.0 new file mode 100644 index 0000000..973902a --- /dev/null +++ b/benchmarks/zero/owned-file.0 @@ -0,0 +1,18 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let created: Maybe> = std.fs.create(fs, ".zero/bench/zero-owned-file.txt") + if created.has { + var file: owned = created.value + if !std.fs.writeAll(&mut file, std.mem.span("owned file payload\n")) { + return + } + } + let opened: Maybe> = std.fs.open(fs, ".zero/bench/zero-owned-file.txt") + if opened.has { + var file: owned = opened.value + let size: Maybe = std.fs.fileLen(&mut file) + if size.has && size.value > 0 { + check world.out.write("owned file\n") + } + } +} diff --git a/benchmarks/zero/owned-file.graph b/benchmarks/zero/owned-file.graph new file mode 100644 index 0000000..e717350 Binary files /dev/null and b/benchmarks/zero/owned-file.graph differ diff --git a/benchmarks/zero/params.0 b/benchmarks/zero/params.0 new file mode 100644 index 0000000..7d91386 --- /dev/null +++ b/benchmarks/zero/params.0 @@ -0,0 +1,13 @@ +fn add(a: i32, b: i32) -> i32 { + return a + b +} + +fn mix(a: i32, b: i32, c: i32) -> i32 { + return add(a, b) + c +} + +pub fn main(world: World) -> Void raises { + if mix(10, 20, 12) == 42 { + check world.out.write("42\n") + } +} diff --git a/benchmarks/zero/params.graph b/benchmarks/zero/params.graph new file mode 100644 index 0000000..58fce2f Binary files /dev/null and b/benchmarks/zero/params.graph differ diff --git a/benchmarks/zero/parse.0 b/benchmarks/zero/parse.0 new file mode 100644 index 0000000..ee25897 --- /dev/null +++ b/benchmarks/zero/parse.0 @@ -0,0 +1,13 @@ +use std.parse + +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var digits: usize = 0 + while i < 100000 { + digits = std.parse.scanDigits("1234567890abcdef") + i = i + 1 + } + if digits == 10 && std.parse.isIdentifierStart("_") { + check world.out.write("parse\n") + } +} diff --git a/benchmarks/zero/parse.graph b/benchmarks/zero/parse.graph new file mode 100644 index 0000000..9909bbe Binary files /dev/null and b/benchmarks/zero/parse.graph differ diff --git a/benchmarks/zero/parser.0 b/benchmarks/zero/parser.0 new file mode 100644 index 0000000..780dfb3 --- /dev/null +++ b/benchmarks/zero/parser.0 @@ -0,0 +1,11 @@ +type Token { + kind: i32, + offset: i32, +} + +pub fn main(world: World) -> Void raises { + let token: Token = Token { kind: 1, offset: 7 } + if token.kind == 1 && token.offset == 7 { + check world.out.write("ident\n") + } +} diff --git a/benchmarks/zero/parser.graph b/benchmarks/zero/parser.graph new file mode 100644 index 0000000..64abc21 Binary files /dev/null and b/benchmarks/zero/parser.graph differ diff --git a/benchmarks/zero/readall.0 b/benchmarks/zero/readall.0 new file mode 100644 index 0000000..07f7646 --- /dev/null +++ b/benchmarks/zero/readall.0 @@ -0,0 +1,28 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let created: Maybe> = std.fs.create(fs, ".zero/bench/zero-readall-input.txt") + if created.has { + var file: owned = created.value + if !std.fs.writeAll(&mut file, std.mem.span("readall payload\n")) { + return + } + } + var storage: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + var i: i32 = 0 + var hits: i32 = 0 + while i < 1000 { + std.mem.reset(&mut alloc) + let body: Maybe> = std.fs.readAll(alloc, fs, ".zero/bench/zero-readall-input.txt", 64) + if body.has { + var buf: owned = body.value + if std.mem.eqlBytes(std.mem.bufBytes(&buf), std.mem.span("readall payload\n")) { + hits = hits + 1 + } + } + i = i + 1 + } + if hits == 1000 { + check world.out.write("readall\n") + } +} diff --git a/benchmarks/zero/readall.graph b/benchmarks/zero/readall.graph new file mode 100644 index 0000000..721f301 Binary files /dev/null and b/benchmarks/zero/readall.graph differ diff --git a/benchmarks/zero/rescue.0 b/benchmarks/zero/rescue.0 new file mode 100644 index 0000000..f6b8c80 --- /dev/null +++ b/benchmarks/zero/rescue.0 @@ -0,0 +1,19 @@ +fn value(i: i32) -> i32 raises [Miss] { + if i % 4 == 0 { + return i + } + raise Miss +} + +pub fn main(world: World) -> Void raises { + var i: i32 = 0 + var total: i32 = 0 + while i < 50000 { + let item: i32 = rescue value(i) err 2 + total = total + item + i = i + 1 + } + if total > 0 { + check world.out.write("rescue\n") + } +} diff --git a/benchmarks/zero/rescue.graph b/benchmarks/zero/rescue.graph new file mode 100644 index 0000000..7e0d5c7 Binary files /dev/null and b/benchmarks/zero/rescue.graph differ diff --git a/benchmarks/zero/slices.0 b/benchmarks/zero/slices.0 new file mode 100644 index 0000000..5543ac6 --- /dev/null +++ b/benchmarks/zero/slices.0 @@ -0,0 +1,14 @@ +pub fn main(world: World) -> Void raises { + let view: Span = "zero-slices"[5..11] + var i: i32 = 0 + var total: usize = 0 + while i < 50000 { + if view[0] == 115 as u8 && view[5] == 115 as u8 { + total = total + std.mem.len(view) + } + i = i + 1 + } + if total == 300000 { + check world.out.write("slices\n") + } +} diff --git a/benchmarks/zero/slices.graph b/benchmarks/zero/slices.graph new file mode 100644 index 0000000..785aee6 Binary files /dev/null and b/benchmarks/zero/slices.graph differ diff --git a/benchmarks/zero/structs.0 b/benchmarks/zero/structs.0 new file mode 100644 index 0000000..7a316a7 --- /dev/null +++ b/benchmarks/zero/structs.0 @@ -0,0 +1,11 @@ +type Point { + x: i32, + y: i32, +} + +pub fn main(world: World) -> Void raises { + let point: Point = Point { x: 20, y: 22 } + if point.x + point.y == 42 { + check world.out.write("42\n") + } +} diff --git a/benchmarks/zero/structs.graph b/benchmarks/zero/structs.graph new file mode 100644 index 0000000..b90b07d Binary files /dev/null and b/benchmarks/zero/structs.graph differ diff --git a/benchmarks/zero/zero-hash.0 b/benchmarks/zero/zero-hash.0 new file mode 100644 index 0000000..5f4eed6 --- /dev/null +++ b/benchmarks/zero/zero-hash.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let checksum: u32 = std.codec.crc32Bytes(std.mem.span("zero hash payload\n")) + if checksum == 1120241454 { + check world.out.write("zero-hash ok\n") + } +} diff --git a/benchmarks/zero/zero-hash.graph b/benchmarks/zero/zero-hash.graph new file mode 100644 index 0000000..4e1d151 Binary files /dev/null and b/benchmarks/zero/zero-hash.graph differ diff --git a/bin/zero b/bin/zero new file mode 100755 index 0000000..899942a --- /dev/null +++ b/bin/zero @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +native="$root/.zero/bin/zero" + +if [[ ! -x "$native" ]]; then + echo "zero: native compiler is not built yet" >&2 + echo "run: make -C native/zero-c" >&2 + exit 127 +fi + +exec "$native" "$@" diff --git a/conformance/agent-surface/classification.json b/conformance/agent-surface/classification.json new file mode 100644 index 0000000..750c9bc --- /dev/null +++ b/conformance/agent-surface/classification.json @@ -0,0 +1,46 @@ +{ + "schema": 1, + "purpose": "Track minimized agent-surface repros without preserving report source context.", + "fixtures": [ + { + "id": "interface-method-generic-binding", + "path": "conformance/agent-surface/fixtures/interface-method-generic-binding.0", + "classification": "intended-behavior", + "currentBehavior": "check-pass", + "followUp": "Keep interface method generic binders independent from interface type parameters.", + "runner": "assert-check-pass" + }, + { + "id": "direct-generic-recursion", + "path": "conformance/agent-surface/fixtures/direct-generic-recursion.0", + "classification": "intended-behavior", + "currentBehavior": "check-pass-target-ready", + "followUp": "Keep self-recursive generic call targets present in the direct backend function table.", + "runner": "assert-target-ready" + }, + { + "id": "direct-generic-specialization-name-collision", + "path": "conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.0", + "classification": "unsupported-program-shape", + "currentBehavior": "check-pass-target-blocked-BLD004", + "followUp": "Keep direct generic specialization names from silently resolving to unrelated concrete functions.", + "runner": "assert-target-blocked" + }, + { + "id": "stdlib-signature-parity", + "path": "scripts/compiler-metrics.mts", + "classification": "enforced-structure-guardrail", + "currentBehavior": "budget-pass-no-violations", + "followUp": "Keep compiler structure budgets enforced for file size, strcmp growth, large functions, and stdlib signature parity until one standard-library signature and capability source replaces duplicated metadata.", + "runner": "assert-budget-pass" + }, + { + "id": "owned-drop-direct-backend-unsupported", + "path": "conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0", + "classification": "target-buildability-blocker", + "currentBehavior": "check-pass-target-blocked-BLD004", + "followUp": "Keep target-aware backend blockers before emission and keep backend diagnostics target-correct.", + "runner": "assert-diagnostic-code" + } + ] +} diff --git a/conformance/agent-surface/fixtures/direct-generic-recursion.0 b/conformance/agent-surface/fixtures/direct-generic-recursion.0 new file mode 100644 index 0000000..b0e9c47 --- /dev/null +++ b/conformance/agent-surface/fixtures/direct-generic-recursion.0 @@ -0,0 +1,8 @@ +fn loop(value: T) -> Void { + loop(value) +} + +pub fn main(world: World) -> Void raises { + loop(1) + check world.out.write("direct generic recursion ok\n") +} diff --git a/conformance/agent-surface/fixtures/direct-generic-recursion.graph b/conformance/agent-surface/fixtures/direct-generic-recursion.graph new file mode 100644 index 0000000..8120822 Binary files /dev/null and b/conformance/agent-surface/fixtures/direct-generic-recursion.graph differ diff --git a/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.0 b/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.0 new file mode 100644 index 0000000..c9d7634 --- /dev/null +++ b/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.0 @@ -0,0 +1,16 @@ +fn identity(value: T) -> T { + return value +} + +fn identity__i32(value: i32) -> i32 { + return value + 1 +} + +pub fn main(world: World) -> Void raises { + let value: i32 = identity(1) + if value == 1 { + check world.out.write("direct specialization collision avoided\n") + } else { + check world.out.write("direct specialization collision reached wrong target\n") + } +} diff --git a/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.graph b/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.graph new file mode 100644 index 0000000..b88a43a Binary files /dev/null and b/conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.graph differ diff --git a/conformance/agent-surface/fixtures/interface-method-generic-binding.0 b/conformance/agent-surface/fixtures/interface-method-generic-binding.0 new file mode 100644 index 0000000..835eff1 --- /dev/null +++ b/conformance/agent-surface/fixtures/interface-method-generic-binding.0 @@ -0,0 +1,20 @@ +type Identity { + fn id(x: X) -> X { + return x + } +} + +interface Id { + fn id(x: X) -> X +} + +fn useId(value: X) -> X { + return F.id(value) +} + +pub fn main(world: World) -> Void raises { + let value: i32 = useId(1) + if value == 1 { + check world.out.write("interface method generic ok\n") + } +} diff --git a/conformance/agent-surface/fixtures/interface-method-generic-binding.graph b/conformance/agent-surface/fixtures/interface-method-generic-binding.graph new file mode 100644 index 0000000..2209532 Binary files /dev/null and b/conformance/agent-surface/fixtures/interface-method-generic-binding.graph differ diff --git a/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0 b/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0 new file mode 100644 index 0000000..11c04a7 --- /dev/null +++ b/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0 @@ -0,0 +1,13 @@ +type Tracked { + value: i32, + fn drop(self: mutref) -> Void { + self.value = 0 + } +} + +pub fn main(world: World) -> Void raises { + let tracked: owned = Tracked { value: 42 } + if tracked.value == 42 { + check world.out.write("owned drop backend unsupported current ok\n") + } +} diff --git a/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.graph b/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.graph new file mode 100644 index 0000000..9ab3266 Binary files /dev/null and b/conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.graph differ diff --git a/conformance/c/host-leak-package/src/main.0 b/conformance/c/host-leak-package/src/main.0 new file mode 100644 index 0000000..b729255 --- /dev/null +++ b/conformance/c/host-leak-package/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("host leak package\n") +} diff --git a/conformance/c/host-leak-package/zero.graph b/conformance/c/host-leak-package/zero.graph new file mode 100644 index 0000000..b0691dc Binary files /dev/null and b/conformance/c/host-leak-package/zero.graph differ diff --git a/conformance/c/host-leak-package/zero.toml b/conformance/c/host-leak-package/zero.toml new file mode 100644 index 0000000..22410a0 --- /dev/null +++ b/conformance/c/host-leak-package/zero.toml @@ -0,0 +1,14 @@ +[package] +name = "host-leak-package" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" + +[c.libs.hostonly] +headers = ["hostonly.h"] +include = ["/usr/include"] +lib = ["/usr/lib"] +link = ["hostonly"] +pkg_config = "hostonly" diff --git a/conformance/c/old-style.h b/conformance/c/old-style.h new file mode 100644 index 0000000..f1cc0ca --- /dev/null +++ b/conformance/c/old-style.h @@ -0,0 +1 @@ +int zero_c_old_style(); diff --git a/conformance/c/simple.h b/conformance/c/simple.h new file mode 100644 index 0000000..be15365 --- /dev/null +++ b/conformance/c/simple.h @@ -0,0 +1,15 @@ +#define ZERO_C_ANSWER 42 + +typedef int zero_c_int; + +typedef struct zero_c_point { + int x; + int y; +} zero_c_point; + +enum zero_c_color { + ZERO_C_RED = 1, + ZERO_C_BLUE = 2 +}; + +int zero_c_add(int a, int b); diff --git a/conformance/c/target-conditional.h b/conformance/c/target-conditional.h new file mode 100644 index 0000000..a505423 --- /dev/null +++ b/conformance/c/target-conditional.h @@ -0,0 +1,20 @@ +#ifndef ZERO_TARGET_CONDITIONAL_H +#define ZERO_TARGET_CONDITIONAL_H + +#if defined(_WIN32) && !defined(__linux__) +int zero_c_windows_add(int left, int right); +#elif defined(__linux__) +int zero_c_linux_add(int left, int right); +#else +int zero_c_fallback_add(int left, int right); +#endif + +#ifndef _WIN32 +int zero_c_not_windows(int value); +#endif + +#ifdef __aarch64__ +int zero_c_arm64_only(int value); +#endif + +#endif diff --git a/conformance/c/unsupported.h b/conformance/c/unsupported.h new file mode 100644 index 0000000..39da0ae --- /dev/null +++ b/conformance/c/unsupported.h @@ -0,0 +1 @@ +int zero_log(const char *fmt, ...); diff --git a/conformance/check/pass/array-assignment.0 b/conformance/check/pass/array-assignment.0 new file mode 100644 index 0000000..b69857e --- /dev/null +++ b/conformance/check/pass/array-assignment.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let values: [3]u8 = [1, 2, 3] + var count: i32 = 0 + count = count + 1 + if count == 1 { + check world.out.write("array assignment ok\n") + } +} diff --git a/conformance/check/pass/array-assignment.graph b/conformance/check/pass/array-assignment.graph new file mode 100644 index 0000000..c82ca84 Binary files /dev/null and b/conformance/check/pass/array-assignment.graph differ diff --git a/conformance/check/pass/c-header-import.0 b/conformance/check/pass/c-header-import.0 new file mode 100644 index 0000000..b286776 --- /dev/null +++ b/conformance/check/pass/c-header-import.0 @@ -0,0 +1,4 @@ +extern c "conformance/c/simple.h" as c + +pub fn main() -> Void { +} diff --git a/conformance/check/pass/c-header-import.graph b/conformance/check/pass/c-header-import.graph new file mode 100644 index 0000000..dbcb0c0 Binary files /dev/null and b/conformance/check/pass/c-header-import.graph differ diff --git a/conformance/check/pass/c-import-local-shadowing.0 b/conformance/check/pass/c-import-local-shadowing.0 new file mode 100644 index 0000000..ef91ba4 --- /dev/null +++ b/conformance/check/pass/c-import-local-shadowing.0 @@ -0,0 +1,13 @@ +extern c "conformance/c/simple.h" as c + +type Counter { + value: i32, + fn get(self: ref) -> i32 { + return self.value + } +} + +pub fn main() -> Void { + let c: Counter = Counter { value: 42 } + expect c.get() == 42 +} diff --git a/conformance/check/pass/c-import-local-shadowing.graph b/conformance/check/pass/c-import-local-shadowing.graph new file mode 100644 index 0000000..768004e Binary files /dev/null and b/conformance/check/pass/c-import-local-shadowing.graph differ diff --git a/conformance/check/pass/c-import-target-linux.0 b/conformance/check/pass/c-import-target-linux.0 new file mode 100644 index 0000000..fc2b09d --- /dev/null +++ b/conformance/check/pass/c-import-target-linux.0 @@ -0,0 +1,5 @@ +extern c "conformance/c/target-conditional.h" as c + +export c fn main() -> i32 { + return c.zero_c_linux_add(20, 22) + c.zero_c_not_windows(1) +} diff --git a/conformance/check/pass/c-import-target-linux.graph b/conformance/check/pass/c-import-target-linux.graph new file mode 100644 index 0000000..8e2cc25 Binary files /dev/null and b/conformance/check/pass/c-import-target-linux.graph differ diff --git a/conformance/check/pass/c-import-target-win.0 b/conformance/check/pass/c-import-target-win.0 new file mode 100644 index 0000000..1c2d7aa --- /dev/null +++ b/conformance/check/pass/c-import-target-win.0 @@ -0,0 +1,5 @@ +extern c "conformance/c/target-conditional.h" as c + +export c fn main() -> i32 { + return c.zero_c_windows_add(20, 22) +} diff --git a/conformance/check/pass/c-import-target-win.graph b/conformance/check/pass/c-import-target-win.graph new file mode 100644 index 0000000..6cfbb7d Binary files /dev/null and b/conformance/check/pass/c-import-target-win.graph differ diff --git a/conformance/check/pass/call-resolution-edge-cases.0 b/conformance/check/pass/call-resolution-edge-cases.0 new file mode 100644 index 0000000..e518d10 --- /dev/null +++ b/conformance/check/pass/call-resolution-edge-cases.0 @@ -0,0 +1,23 @@ +const constTotal: i32 = add(20, 22) + +choice PayloadResult { + text: String, + count: i32, +} + +fn add(left: i32, right: i32) -> i32 { + return left + right +} + +pub fn main() -> Void { + let result: PayloadResult = PayloadResult.text("zero") + match result { + .text(message) { + let size: usize = std.mem.len(message) + expect (size == 4) + } + .count(value) { + expect (value == constTotal) + } + } +} diff --git a/conformance/check/pass/call-resolution-edge-cases.graph b/conformance/check/pass/call-resolution-edge-cases.graph new file mode 100644 index 0000000..348ba19 Binary files /dev/null and b/conformance/check/pass/call-resolution-edge-cases.graph differ diff --git a/conformance/check/pass/call-resolution-inspection.0 b/conformance/check/pass/call-resolution-inspection.0 new file mode 100644 index 0000000..238082e --- /dev/null +++ b/conformance/check/pass/call-resolution-inspection.0 @@ -0,0 +1,72 @@ +type Counter { + value: i32 = defaultCount(), + fn init(value: i32) -> Self { + return Counter { value: value } + } + fn read(self: ref) -> i32 { + return self.value + } + fn bump(self: mutref, amount: i32) -> Void { + self.value = self.value + amount + } + fn checkedRead(self: ref) -> i32 raises [EmptyCounter] { + if self.value == 0 { + raise EmptyCounter + } + return self.value + } +} + +interface Readable { + fn read(self: ref) -> i32 +} + +choice Event { + key: i32, + quit, +} + +fn defaultCount() -> i32 { + return 41 +} + +fn add(left: i32, right: i32) -> i32 { + return left + right +} + +fn id(value: T) -> T { + return value +} + +fn wrap(value: T) -> T { + return id(value) +} + +fn readValue>(value: ref) -> i32 { + return T.read(value) +} + +fn risky(flag: Bool) -> i32 raises [BadInput] { + if flag { + raise BadInput + } + return 7 +} + +pub fn main(world: World) -> Void raises { + var counter: Counter = Counter.init(41) + let defaulted: Counter = Counter {} + counter.bump(1) + let total: i32 = add(id(40), 2) + let wrapped: i32 = wrap(total) + let size: usize = std.mem.len("zero") + let event: Event = Event.key(total) + let direct: i32 = Counter.read(&counter) + let defaultedValue: i32 = Counter.read(&defaulted) + let viaInterface: i32 = readValue(&counter) + let checked: i32 = check counter.checkedRead() + let safe: i32 = check risky(false) + if total == 42 && wrapped == 42 && size == 4 && defaultedValue == 41 && (direct == 42 && viaInterface == 42) && (checked == 42 && safe == 7) { + check world.out.write("call resolution inspection ok\n") + } +} diff --git a/conformance/check/pass/call-resolution-inspection.graph b/conformance/check/pass/call-resolution-inspection.graph new file mode 100644 index 0000000..0acda18 Binary files /dev/null and b/conformance/check/pass/call-resolution-inspection.graph differ diff --git a/conformance/check/pass/checker-type-forms.0 b/conformance/check/pass/checker-type-forms.0 new file mode 100644 index 0000000..e184f3f --- /dev/null +++ b/conformance/check/pass/checker-type-forms.0 @@ -0,0 +1,71 @@ +type Point { + x: i32, + y: i32, +} + +enum Mode: u8 { + off, + on, +} + +choice Event { + none, + point: Point, +} + +fn noop() -> Void { + return +} + +fn read_x(point: ref) -> i32 { + return point.x +} + +fn bump_x(point: mutref) -> Void { + point.x = point.x + 1 +} + +fn first_byte(bytes: Span) -> u8 { + return bytes[0] +} + +fn set_second(bytes: MutSpan) -> Void { + bytes[1] = 9 +} + +fn branch(flag: Bool) -> i32 { + if flag { + return 1 + } else { + return 0 + } +} + +pub fn main(world: World) -> Void raises { + noop() + var point: Point = Point { x: 1, y: 2 } + let mode: Mode = Mode.on + let event: Event = Event.point(point) + var bytes: [4]u8 = [1, 2, 3, 4] + let writable: MutSpan = bytes + set_second(writable) + let readonly: Span = bytes + let label: String = "checker" + let byte: u8 = first_byte(readonly) + let widened: u32 = byte as u32 + let window: Span = readonly[1..3] + bump_x(&mut point) + while false { + return + } + match mode { + on { + if read_x(&point) == 2 && branch(true) == 1 && widened > 0 && std.mem.len(window) == 2 { + check world.out.write("checker type forms ok\n") + } + } + off { + check world.out.write(label) + } + } +} diff --git a/conformance/check/pass/checker-type-forms.graph b/conformance/check/pass/checker-type-forms.graph new file mode 100644 index 0000000..592b11a Binary files /dev/null and b/conformance/check/pass/checker-type-forms.graph differ diff --git a/conformance/check/pass/const-arithmetic.0 b/conformance/check/pass/const-arithmetic.0 new file mode 100644 index 0000000..08888e0 --- /dev/null +++ b/conformance/check/pass/const-arithmetic.0 @@ -0,0 +1,7 @@ +const base: i32 = 40 + +const answer: i32 = base + 2 + +pub fn main() -> Void { + expect (answer == 42) +} diff --git a/conformance/check/pass/const-arithmetic.graph b/conformance/check/pass/const-arithmetic.graph new file mode 100644 index 0000000..ed1568a Binary files /dev/null and b/conformance/check/pass/const-arithmetic.graph differ diff --git a/conformance/check/pass/fixed-array-length-match.0 b/conformance/check/pass/fixed-array-length-match.0 new file mode 100644 index 0000000..e37b4db --- /dev/null +++ b/conformance/check/pass/fixed-array-length-match.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var filled: [8]u8 = [7_u8; 8] + var listed: [4]u8 = [1, 2, 3, 4] + filled[0] = listed[3] + if filled[0] == 4 { + check world.out.write("fixed array lengths ok\n") + } +} diff --git a/conformance/check/pass/fixed-array-length-match.graph b/conformance/check/pass/fixed-array-length-match.graph new file mode 100644 index 0000000..95aab49 Binary files /dev/null and b/conformance/check/pass/fixed-array-length-match.graph differ diff --git a/conformance/check/pass/fmt-core-usability.0 b/conformance/check/pass/fmt-core-usability.0 new file mode 100644 index 0000000..d5dcb06 --- /dev/null +++ b/conformance/check/pass/fmt-core-usability.0 @@ -0,0 +1,27 @@ +alias ByteCount = usize + +type Counter { + value: i32, + fn add(self: ref, amount: i32) -> i32 { + return self.value + amount + } +} + +enum State { + ready, + done, +} + +pub fn main() -> Void { + let count: ByteCount = 42_usize + let counter: Counter = Counter { value: 40 } + let state: State = State.ready + match state { + ready { + expect (Counter.add(&counter, 2) == 42) + } + _ { + expect (count == 42_usize) + } + } +} diff --git a/conformance/check/pass/fmt-core-usability.graph b/conformance/check/pass/fmt-core-usability.graph new file mode 100644 index 0000000..a7bb4e7 Binary files /dev/null and b/conformance/check/pass/fmt-core-usability.graph differ diff --git a/conformance/check/pass/generic-array-inference.0 b/conformance/check/pass/generic-array-inference.0 new file mode 100644 index 0000000..986a502 --- /dev/null +++ b/conformance/check/pass/generic-array-inference.0 @@ -0,0 +1,11 @@ +const cap: usize = 3 + +fn head(items: ref<[N]T>) -> T { + return items[0] +} + +pub fn main() -> Void { + let values: [cap]i32 = [3, 4, 5] + let first: i32 = head(&values) + expect (first == 3) +} diff --git a/conformance/check/pass/generic-array-inference.graph b/conformance/check/pass/generic-array-inference.graph new file mode 100644 index 0000000..c1e889a Binary files /dev/null and b/conformance/check/pass/generic-array-inference.graph differ diff --git a/conformance/check/pass/generic-const-shadowing.0 b/conformance/check/pass/generic-const-shadowing.0 new file mode 100644 index 0000000..90eb3db --- /dev/null +++ b/conformance/check/pass/generic-const-shadowing.0 @@ -0,0 +1,31 @@ +const T: usize = 3 + +const N: usize = 3 + +type Holder { + value: U, +} + +type Box { + value: i32, +} + +fn unwrap(holder: ref>) -> T { + return holder.value +} + +fn forwardUnwrap(holder: ref>) -> T { + return unwrap(holder) +} + +fn read(box: ref>) -> i32 { + return box.value +} + +pub fn main() -> Void { + let holder: Holder = Holder { value: 5 } + let value: i32 = forwardUnwrap(&holder) + let box: Box<4> = Box { value: 9 } + let boxed: i32 = read(&box) + expect (value == 5 && boxed == 9) +} diff --git a/conformance/check/pass/generic-const-shadowing.graph b/conformance/check/pass/generic-const-shadowing.graph new file mode 100644 index 0000000..af3ab5e Binary files /dev/null and b/conformance/check/pass/generic-const-shadowing.graph differ diff --git a/conformance/check/pass/generic-const-type-name-collision.0 b/conformance/check/pass/generic-const-type-name-collision.0 new file mode 100644 index 0000000..8f13e93 --- /dev/null +++ b/conformance/check/pass/generic-const-type-name-collision.0 @@ -0,0 +1,28 @@ +const Foo: usize = 3 + +type Foo { + value: i32, +} + +type Holder { + value: T, +} + +type Sized { + value: i32, +} + +fn take(holder: ref>) -> T { + return holder.value +} + +fn touchSized(item: ref>) -> Void { +} + +pub fn main() -> Void { + let holder: Holder = Holder { value: Foo { value: 7 } } + let value: Foo = take(&holder) + let sized: Sized = Sized { value: 9 } + touchSized(&sized) + expect (value.value == 7) +} diff --git a/conformance/check/pass/generic-const-type-name-collision.graph b/conformance/check/pass/generic-const-type-name-collision.graph new file mode 100644 index 0000000..3caac35 Binary files /dev/null and b/conformance/check/pass/generic-const-type-name-collision.graph differ diff --git a/conformance/check/pass/generic-function-basic.0 b/conformance/check/pass/generic-function-basic.0 new file mode 100644 index 0000000..4621b8d --- /dev/null +++ b/conformance/check/pass/generic-function-basic.0 @@ -0,0 +1,10 @@ +fn identity(value: T) -> T { + return value +} + +pub fn main() -> Void { + let a: i32 = identity(41) + let b: u8 = identity(7_u8) + expect (a == 41) + expect (b == 7_u8) +} diff --git a/conformance/check/pass/generic-function-basic.graph b/conformance/check/pass/generic-function-basic.graph new file mode 100644 index 0000000..04d3ff5 Binary files /dev/null and b/conformance/check/pass/generic-function-basic.graph differ diff --git a/conformance/check/pass/generic-method-outer-param-inference.0 b/conformance/check/pass/generic-method-outer-param-inference.0 new file mode 100644 index 0000000..e150ad3 --- /dev/null +++ b/conformance/check/pass/generic-method-outer-param-inference.0 @@ -0,0 +1,37 @@ +type Pair { + left: A, + right: B, +} + +type Box { + value: T, + fn rightOf(self: ref, pair: ref>) -> U { + return pair.right + } +} + +type PlainBox { + value: i32, + fn project(self: ref, pair: ref>) -> U { + return pair.right + } +} + +interface Projector { + fn project(self: ref, pair: ref>) -> U +} + +fn project, U: Type>(value: ref, pair: ref>) -> U { + return T.project(value, pair) +} + +pub fn main() -> Void { + let box: Box = Box { value: 1 } + let pair: Pair = Pair { left: 2, right: true } + let receiver: Bool = box.rightOf(&pair) + let namespaced: Bool = Box.rightOf(&box, &pair) + let plain: PlainBox = PlainBox { value: 3 } + let projectedPair: Pair = Pair { left: PlainBox { value: 4 }, right: true } + let projected: Bool = project(&plain, &projectedPair) + expect (receiver && namespaced && projected) +} diff --git a/conformance/check/pass/generic-method-outer-param-inference.graph b/conformance/check/pass/generic-method-outer-param-inference.graph new file mode 100644 index 0000000..2c6993b Binary files /dev/null and b/conformance/check/pass/generic-method-outer-param-inference.graph differ diff --git a/conformance/check/pass/generic-mixed-const-type-name-collision.0 b/conformance/check/pass/generic-mixed-const-type-name-collision.0 new file mode 100644 index 0000000..d212fdd --- /dev/null +++ b/conformance/check/pass/generic-mixed-const-type-name-collision.0 @@ -0,0 +1,20 @@ +const Foo: usize = 3 + +type Foo { + value: i32, +} + +type Mixed { + value: T, + items: [N]i32, +} + +fn take(item: ref>) -> T { + return item.value +} + +pub fn main() -> Void { + let mixed: Mixed = Mixed { value: Foo { value: 7 }, items: [1, 2, 3] } + let value: Foo = take(&mixed) + expect (value.value == 7) +} diff --git a/conformance/check/pass/generic-mixed-const-type-name-collision.graph b/conformance/check/pass/generic-mixed-const-type-name-collision.graph new file mode 100644 index 0000000..31f96f0 Binary files /dev/null and b/conformance/check/pass/generic-mixed-const-type-name-collision.graph differ diff --git a/conformance/check/pass/generic-shape-basic.0 b/conformance/check/pass/generic-shape-basic.0 new file mode 100644 index 0000000..63bf42a --- /dev/null +++ b/conformance/check/pass/generic-shape-basic.0 @@ -0,0 +1,8 @@ +type Box { + value: T, +} + +pub fn main() -> Void { + let box: Box = Box { value: 42 } + expect (box.value == 42) +} diff --git a/conformance/check/pass/generic-shape-basic.graph b/conformance/check/pass/generic-shape-basic.graph new file mode 100644 index 0000000..a3ee81b Binary files /dev/null and b/conformance/check/pass/generic-shape-basic.graph differ diff --git a/conformance/check/pass/generic-shape-methods.0 b/conformance/check/pass/generic-shape-methods.0 new file mode 100644 index 0000000..423fcef --- /dev/null +++ b/conformance/check/pass/generic-shape-methods.0 @@ -0,0 +1,22 @@ +type FixedVec { + len: usize, + items: [N]T, + fn push(self: mutref, value: T) -> Void raises [Full] { + if self.len == N { + raise Full + } + self.items[self.len] = value + self.len = self.len + 1 + } + fn get(self: ref, index: usize) -> Maybe { + let items: [N]T = self.items + return std.mem.get(items, index) + } +} + +pub fn main() -> Void raises { + var vec: FixedVec = FixedVec { len: 0, items: [0, 0, 0, 0] } + check FixedVec.push(&mut vec, 7) + let first: Maybe = FixedVec.get(&vec, 0) + expect (first.has && first.value == 7) +} diff --git a/conformance/check/pass/generic-shape-methods.graph b/conformance/check/pass/generic-shape-methods.graph new file mode 100644 index 0000000..83a89e6 Binary files /dev/null and b/conformance/check/pass/generic-shape-methods.graph differ diff --git a/conformance/check/pass/generic-shape-multi.0 b/conformance/check/pass/generic-shape-multi.0 new file mode 100644 index 0000000..937c193 --- /dev/null +++ b/conformance/check/pass/generic-shape-multi.0 @@ -0,0 +1,14 @@ +type Pair { + left: T, + right: U, +} + +fn makePair(left: T, right: U) -> Pair { + return Pair { left: left, right: right } +} + +pub fn main() -> Void { + let pair: Pair = makePair(7, 9_u8) + expect (pair.left == 7) + expect (pair.right == 9_u8) +} diff --git a/conformance/check/pass/generic-shape-multi.graph b/conformance/check/pass/generic-shape-multi.graph new file mode 100644 index 0000000..541647b Binary files /dev/null and b/conformance/check/pass/generic-shape-multi.graph differ diff --git a/conformance/check/pass/generic-static-explicit-shadowing.0 b/conformance/check/pass/generic-static-explicit-shadowing.0 new file mode 100644 index 0000000..74928c1 --- /dev/null +++ b/conformance/check/pass/generic-static-explicit-shadowing.0 @@ -0,0 +1,19 @@ +const N: usize = 4 + +type Helper { + fn needsSame(left: ref<[M]i32>, right: ref<[M]i32>) -> Void { + } +} + +fn needsSame(left: ref<[M]i32>, right: ref<[M]i32>) -> Void { +} + +fn caller(left: ref<[N]i32>, right: ref<[N]i32>) -> Void { + needsSame(left, right) + Helper.needsSame(left, right) +} + +pub fn main() -> Void { + let values: [3]i32 = [1, 2, 3] + caller(&values, &values) +} diff --git a/conformance/check/pass/generic-static-explicit-shadowing.graph b/conformance/check/pass/generic-static-explicit-shadowing.graph new file mode 100644 index 0000000..f17e83f Binary files /dev/null and b/conformance/check/pass/generic-static-explicit-shadowing.graph differ diff --git a/conformance/check/pass/generic-static-forwarding.0 b/conformance/check/pass/generic-static-forwarding.0 new file mode 100644 index 0000000..169663e --- /dev/null +++ b/conformance/check/pass/generic-static-forwarding.0 @@ -0,0 +1,15 @@ +const cap: usize = 3 + +fn head(items: ref<[N]T>) -> T { + return items[0] +} + +fn forward(items: ref<[N]T>) -> T { + return head(items) +} + +pub fn main() -> Void { + let values: [cap]i32 = [7, 8, 9] + let first: i32 = forward(&values) + expect (first == 7) +} diff --git a/conformance/check/pass/generic-static-forwarding.graph b/conformance/check/pass/generic-static-forwarding.graph new file mode 100644 index 0000000..2201662 Binary files /dev/null and b/conformance/check/pass/generic-static-forwarding.graph differ diff --git a/conformance/check/pass/generic-static-method-forwarding.0 b/conformance/check/pass/generic-static-method-forwarding.0 new file mode 100644 index 0000000..e7083b2 --- /dev/null +++ b/conformance/check/pass/generic-static-method-forwarding.0 @@ -0,0 +1,28 @@ +type Helper { + fn head(items: ref<[N]T>) -> T { + return items[0] + } +} + +type FixedVec { + items: [N]T, + fn first(self: ref) -> T { + return self.items[0] + } +} + +fn forwardHead(items: ref<[N]T>) -> T { + return Helper.head(items) +} + +fn forwardFirst(items: ref>) -> T { + return items.first() +} + +pub fn main() -> Void { + let values: [3]i32 = [1, 2, 3] + let vec: FixedVec = FixedVec { items: [4, 5, 6] } + let first: i32 = forwardHead(&values) + let second: i32 = forwardFirst(&vec) + expect (first == 1 && second == 4) +} diff --git a/conformance/check/pass/generic-static-method-forwarding.graph b/conformance/check/pass/generic-static-method-forwarding.graph new file mode 100644 index 0000000..18052af Binary files /dev/null and b/conformance/check/pass/generic-static-method-forwarding.graph differ diff --git a/conformance/check/pass/generic-static-return-substitution.0 b/conformance/check/pass/generic-static-return-substitution.0 new file mode 100644 index 0000000..f73cc7f --- /dev/null +++ b/conformance/check/pass/generic-static-return-substitution.0 @@ -0,0 +1,20 @@ +const Len: usize = 3 + +type Len { + value: i32, +} + +type Bundle { + value: T, + items: [N]i32, +} + +fn payload(bundle: Bundle) -> [N]i32 { + return bundle.items +} + +pub fn main() -> Void { + let bundle: Bundle = Bundle { value: Len { value: 7 }, items: [1, 2, 3] } + let values: [Len]i32 = payload(bundle) + expect (values[2] == 3) +} diff --git a/conformance/check/pass/generic-static-return-substitution.graph b/conformance/check/pass/generic-static-return-substitution.graph new file mode 100644 index 0000000..3784408 Binary files /dev/null and b/conformance/check/pass/generic-static-return-substitution.graph differ diff --git a/conformance/check/pass/generic-static-wrapper-type-collision.0 b/conformance/check/pass/generic-static-wrapper-type-collision.0 new file mode 100644 index 0000000..d2095bc --- /dev/null +++ b/conformance/check/pass/generic-static-wrapper-type-collision.0 @@ -0,0 +1,20 @@ +const Foo: usize = 3 + +type Foo { + value: i32, +} + +fn read(value: ref) -> i32 { + return value.value +} + +fn pass(value: Maybe) -> Maybe { + return value +} + +pub fn main() -> Void { + let foo: Foo = Foo { value: 7 } + let maybe: Maybe = null + let result: Maybe = pass(maybe) + expect (read(&foo) == 7 && !result.has) +} diff --git a/conformance/check/pass/generic-static-wrapper-type-collision.graph b/conformance/check/pass/generic-static-wrapper-type-collision.graph new file mode 100644 index 0000000..de4252b Binary files /dev/null and b/conformance/check/pass/generic-static-wrapper-type-collision.graph differ diff --git a/conformance/check/pass/generic-untyped-static-const-inference.0 b/conformance/check/pass/generic-untyped-static-const-inference.0 new file mode 100644 index 0000000..11abe7a --- /dev/null +++ b/conformance/check/pass/generic-untyped-static-const-inference.0 @@ -0,0 +1,33 @@ +const cap = 3 + +const enabled = true + +const selected = Mode.fast + +type Gate { + value: i32, +} + +enum Mode: u8 { + fast, + tiny, +} + +fn head(items: ref<[N]T>) -> T { + return items[0] +} + +fn readGate(gate: ref>) -> i32 { + if enabledFlag { + return gate.value + } + return 0 +} + +pub fn main() -> Void { + let values: [cap]i32 = [3, 4, 5] + let first: i32 = head(&values) + let gate: Gate = Gate { value: 9 } + let gated: i32 = readGate(&gate) + expect (first == 3 && gated == 9) +} diff --git a/conformance/check/pass/generic-untyped-static-const-inference.graph b/conformance/check/pass/generic-untyped-static-const-inference.graph new file mode 100644 index 0000000..6c6519c Binary files /dev/null and b/conformance/check/pass/generic-untyped-static-const-inference.graph differ diff --git a/conformance/check/pass/imports/src/main.0 b/conformance/check/pass/imports/src/main.0 new file mode 100644 index 0000000..7e7c5cc --- /dev/null +++ b/conformance/check/pass/imports/src/main.0 @@ -0,0 +1,10 @@ +use math + +use types + +pub fn main(world: World) -> Void raises { + let point: Point = Point { value: add_one(41) } + if point.value == 42 { + check world.out.write("imports pass\n") + } +} diff --git a/conformance/check/pass/imports/src/math.0 b/conformance/check/pass/imports/src/math.0 new file mode 100644 index 0000000..9e785e7 --- /dev/null +++ b/conformance/check/pass/imports/src/math.0 @@ -0,0 +1,3 @@ +fn add_one(value: i32) -> i32 { + return value + 1 +} diff --git a/conformance/check/pass/imports/src/types.0 b/conformance/check/pass/imports/src/types.0 new file mode 100644 index 0000000..1271dfe --- /dev/null +++ b/conformance/check/pass/imports/src/types.0 @@ -0,0 +1,3 @@ +type Point { + value: i32, +} diff --git a/conformance/check/pass/imports/zero.graph b/conformance/check/pass/imports/zero.graph new file mode 100644 index 0000000..1dbec84 Binary files /dev/null and b/conformance/check/pass/imports/zero.graph differ diff --git a/conformance/check/pass/imports/zero.toml b/conformance/check/pass/imports/zero.toml new file mode 100644 index 0000000..6dc3fe4 --- /dev/null +++ b/conformance/check/pass/imports/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "imports-pass" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/check/pass/match-fallback.0 b/conformance/check/pass/match-fallback.0 new file mode 100644 index 0000000..3b985ce --- /dev/null +++ b/conformance/check/pass/match-fallback.0 @@ -0,0 +1,16 @@ +enum Mode { + fast, + slow, +} + +pub fn main() -> Void { + let mode: Mode = Mode.fast + match mode { + fast { + expect (true) + } + _ { + expect (true) + } + } +} diff --git a/conformance/check/pass/match-fallback.graph b/conformance/check/pass/match-fallback.graph new file mode 100644 index 0000000..40f78aa Binary files /dev/null and b/conformance/check/pass/match-fallback.graph differ diff --git a/conformance/check/pass/memory-types.0 b/conformance/check/pass/memory-types.0 new file mode 100644 index 0000000..34e8a89 --- /dev/null +++ b/conformance/check/pass/memory-types.0 @@ -0,0 +1,16 @@ +type BufferView { + bytes: Span, + owner: Maybe>, +} + +type SliceView { + bytes: Span, +} + +pub fn main(world: World) -> Void raises { + let bytes: Span = std.mem.span("memory") + let view: SliceView = SliceView { bytes: bytes } + if std.mem.len(view.bytes) == 6 { + check world.out.write("memory types ok\n") + } +} diff --git a/conformance/check/pass/memory-types.graph b/conformance/check/pass/memory-types.graph new file mode 100644 index 0000000..938b269 Binary files /dev/null and b/conformance/check/pass/memory-types.graph differ diff --git a/conformance/check/pass/package/src/main.0 b/conformance/check/pass/package/src/main.0 new file mode 100644 index 0000000..30d282c --- /dev/null +++ b/conformance/check/pass/package/src/main.0 @@ -0,0 +1,23 @@ +use std.codec + +use std.parse + +use std.time + +use types + +fn cleanup() -> Void { + return +} + +pub fn main(world: World) -> Void raises { + defer cleanup() + let mode: Mode = Mode.fast + let outcome: Outcome = Outcome.ok + let bytes: Maybe = std.codec.readU32Le("abcd") + let digits: usize = std.parse.scanDigits("123abc") + let duration: Duration = std.time.add(std.time.ms(1), std.time.seconds(1)) + if digits == 3 && bytes.has && bytes.value > 0 && std.time.asMsFloor(duration) > 0 { + check world.out.write("package pass\n") + } +} diff --git a/conformance/check/pass/package/src/types.0 b/conformance/check/pass/package/src/types.0 new file mode 100644 index 0000000..8ced30b --- /dev/null +++ b/conformance/check/pass/package/src/types.0 @@ -0,0 +1,9 @@ +enum Mode { + fast, + safe, +} + +choice Outcome { + ok, + failed: i32, +} diff --git a/conformance/check/pass/package/zero.graph b/conformance/check/pass/package/zero.graph new file mode 100644 index 0000000..2f74f47 Binary files /dev/null and b/conformance/check/pass/package/zero.graph differ diff --git a/conformance/check/pass/package/zero.toml b/conformance/check/pass/package/zero.toml new file mode 100644 index 0000000..39b0d45 --- /dev/null +++ b/conformance/check/pass/package/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "package-pass" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/check/pass/payload-match.0 b/conformance/check/pass/payload-match.0 new file mode 100644 index 0000000..7d7be72 --- /dev/null +++ b/conformance/check/pass/payload-match.0 @@ -0,0 +1,16 @@ +choice Result { + ok: i32, + err: String, +} + +pub fn main(world: World) -> Void raises { + let result: Result = Result.ok(42) + match result { + ok { + check world.out.write("payload match\n") + } + err { + check world.out.write("unexpected\n") + } + } +} diff --git a/conformance/check/pass/payload-match.graph b/conformance/check/pass/payload-match.graph new file mode 100644 index 0000000..9c1b45e Binary files /dev/null and b/conformance/check/pass/payload-match.graph differ diff --git a/conformance/check/pass/primitive-stdlib.0 b/conformance/check/pass/primitive-stdlib.0 new file mode 100644 index 0000000..60a0ec4 --- /dev/null +++ b/conformance/check/pass/primitive-stdlib.0 @@ -0,0 +1,11 @@ +use std.codec + +use std.parse + +pub fn main(world: World) -> Void raises { + let len: usize = std.codec.encodedVarintLen(300) + let digit: Bool = std.parse.isAsciiDigit("7") + if len == 2 && digit { + check world.out.write("primitive std ok\n") + } +} diff --git a/conformance/check/pass/primitive-stdlib.graph b/conformance/check/pass/primitive-stdlib.graph new file mode 100644 index 0000000..4a4a6ab Binary files /dev/null and b/conformance/check/pass/primitive-stdlib.graph differ diff --git a/conformance/check/pass/receiver-method-calls.0 b/conformance/check/pass/receiver-method-calls.0 new file mode 100644 index 0000000..f8061b1 --- /dev/null +++ b/conformance/check/pass/receiver-method-calls.0 @@ -0,0 +1,24 @@ +type FixedVec { + len: usize, + items: [N]T, + fn push(self: mutref, value: T) -> Void raises [Full] { + if self.len == N { + raise Full + } + self.items[self.len] = value + self.len = self.len + 1 + } + fn get(self: ref, index: usize) -> Maybe { + return std.mem.get(self.items, index) + } + fn count(self: ref) -> usize { + return self.len + } +} + +pub fn main() -> Void raises { + var vec: FixedVec = FixedVec { len: 0, items: [0, 0, 0, 0] } + check vec.push(7) + let first: Maybe = vec.get(0) + expect (first.has && first.value == 7 && vec.count() == 1) +} diff --git a/conformance/check/pass/receiver-method-calls.graph b/conformance/check/pass/receiver-method-calls.graph new file mode 100644 index 0000000..be36ac3 Binary files /dev/null and b/conformance/check/pass/receiver-method-calls.graph differ diff --git a/conformance/check/pass/shape-field-defaults.0 b/conformance/check/pass/shape-field-defaults.0 new file mode 100644 index 0000000..20812a2 --- /dev/null +++ b/conformance/check/pass/shape-field-defaults.0 @@ -0,0 +1,9 @@ +type Pair { + left: u8 = 1, + right: u8, +} + +pub fn main() -> Void { + let pair: Pair = Pair { right: 2 } + expect (pair.left == 1 && pair.right == 2) +} diff --git a/conformance/check/pass/shape-field-defaults.graph b/conformance/check/pass/shape-field-defaults.graph new file mode 100644 index 0000000..577ec88 Binary files /dev/null and b/conformance/check/pass/shape-field-defaults.graph differ diff --git a/conformance/check/pass/shape.0 b/conformance/check/pass/shape.0 new file mode 100644 index 0000000..9c900e1 --- /dev/null +++ b/conformance/check/pass/shape.0 @@ -0,0 +1,11 @@ +type Point { + x: i32, + y: i32, +} + +pub fn main(world: World) -> Void raises { + let point: Point = Point { x: 1, y: 2 } + if point.x < point.y { + check world.out.write("shape ok\n") + } +} diff --git a/conformance/check/pass/shape.graph b/conformance/check/pass/shape.graph new file mode 100644 index 0000000..a141472 Binary files /dev/null and b/conformance/check/pass/shape.graph differ diff --git a/conformance/check/pass/static-interface-basic.0 b/conformance/check/pass/static-interface-basic.0 new file mode 100644 index 0000000..4354aec --- /dev/null +++ b/conformance/check/pass/static-interface-basic.0 @@ -0,0 +1,19 @@ +type Counter { + value: i32, + fn read(self: ref) -> i32 { + return self.value + } +} + +interface Readable { + fn read(self: ref) -> i32 +} + +fn readValue>(value: ref) -> i32 { + return T.read(value) +} + +pub fn main() -> Void { + let counter: Counter = Counter { value: 42 } + expect (readValue(&counter) == 42) +} diff --git a/conformance/check/pass/static-interface-basic.graph b/conformance/check/pass/static-interface-basic.graph new file mode 100644 index 0000000..de095e9 Binary files /dev/null and b/conformance/check/pass/static-interface-basic.graph differ diff --git a/conformance/check/pass/static-interface-return-substitution.0 b/conformance/check/pass/static-interface-return-substitution.0 new file mode 100644 index 0000000..46a113d --- /dev/null +++ b/conformance/check/pass/static-interface-return-substitution.0 @@ -0,0 +1,25 @@ +type Bytes { + items: [N]u8, + fn bytes(self: ref) -> [N]u8 { + return self.items + } +} + +interface Sized { + fn bytes(self: ref) -> [N]u8 +} + +fn readSized, static N: usize>(value: ref) -> [N]u8 { + return T.bytes(value) +} + +fn readFour>(value: ref) -> [4]u8 { + return T.bytes(value) +} + +pub fn main() -> Void { + let bytes: Bytes<4> = Bytes { items: [1, 2, 3, 4] } + let fromGeneric: [4]u8 = readSized, 4>(&bytes) + let fromLiteral: [4]u8 = readFour>(&bytes) + expect (fromGeneric[3] == 4 && fromLiteral[0] == 1) +} diff --git a/conformance/check/pass/static-interface-return-substitution.graph b/conformance/check/pass/static-interface-return-substitution.graph new file mode 100644 index 0000000..7b84ae3 Binary files /dev/null and b/conformance/check/pass/static-interface-return-substitution.graph differ diff --git a/conformance/check/pass/static-method-namespace.0 b/conformance/check/pass/static-method-namespace.0 new file mode 100644 index 0000000..665f293 --- /dev/null +++ b/conformance/check/pass/static-method-namespace.0 @@ -0,0 +1,11 @@ +type Counter { + value: i32, + fn add(self: ref, amount: i32) -> i32 { + return self.value + amount + } +} + +pub fn main() -> Void { + let counter: Counter = Counter { value: 40 } + expect (Counter.add(&counter, 2) == 42) +} diff --git a/conformance/check/pass/static-method-namespace.graph b/conformance/check/pass/static-method-namespace.graph new file mode 100644 index 0000000..9278754 Binary files /dev/null and b/conformance/check/pass/static-method-namespace.graph differ diff --git a/conformance/check/pass/static-value-params.0 b/conformance/check/pass/static-value-params.0 new file mode 100644 index 0000000..04fd740 --- /dev/null +++ b/conformance/check/pass/static-value-params.0 @@ -0,0 +1,17 @@ +const cap: usize = 4 + +type FixedVec { + len: usize, + items: [N]T, +} + +fn first(vec: ref>) -> T { + return vec.items[0] +} + +pub fn main(world: World) -> Void raises { + let vec: FixedVec = FixedVec { len: cap, items: [1, 2, 3, 4] } + if first(&vec) == 1 { + check world.out.write("static value params check\n") + } +} diff --git a/conformance/check/pass/static-value-params.graph b/conformance/check/pass/static-value-params.graph new file mode 100644 index 0000000..9501b15 Binary files /dev/null and b/conformance/check/pass/static-value-params.graph differ diff --git a/conformance/check/pass/std-entropy-id-helpers.0 b/conformance/check/pass/std-entropy-id-helpers.0 new file mode 100644 index 0000000..a118242 --- /dev/null +++ b/conformance/check/pass/std-entropy-id-helpers.0 @@ -0,0 +1,9 @@ +pub fn main() -> Void { + var random_buf: [8]u8 = [0_u8; 8] + var entropy_buf: [8]u8 = [0_u8; 8] + let random_id: Maybe> = std.crypto.randomId32(random_buf) + let entropy_hex: Maybe> = std.rand.entropyHex32(entropy_buf) + if random_id.has && entropy_hex.has { + let _lengths: usize = std.mem.len(random_id.value) + std.mem.len(entropy_hex.value) + } +} diff --git a/conformance/check/pass/std-entropy-id-helpers.graph b/conformance/check/pass/std-entropy-id-helpers.graph new file mode 100644 index 0000000..14141fe Binary files /dev/null and b/conformance/check/pass/std-entropy-id-helpers.graph differ diff --git a/conformance/check/pass/std-fs-write-copy-helpers.0 b/conformance/check/pass/std-fs-write-copy-helpers.0 new file mode 100644 index 0000000..3bb91c2 --- /dev/null +++ b/conformance/check/pass/std-fs-write-copy-helpers.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + var buffer: [8]u8 = [0_u8; 8] + if std.fs.writeFile(fs, "/tmp/zero-stdlib-write-helper.txt", "ok\n") { + if std.fs.copyFile("/tmp/zero-stdlib-write-helper.txt", "/tmp/zero-stdlib-copy-helper.txt", buffer) { + check world.out.write("std fs write copy helpers\n") + } + } +} diff --git a/conformance/check/pass/std-fs-write-copy-helpers.graph b/conformance/check/pass/std-fs-write-copy-helpers.graph new file mode 100644 index 0000000..b987805 Binary files /dev/null and b/conformance/check/pass/std-fs-write-copy-helpers.graph differ diff --git a/conformance/check/pass/std-mem-arrays.0 b/conformance/check/pass/std-mem-arrays.0 new file mode 100644 index 0000000..fdf00b0 --- /dev/null +++ b/conformance/check/pass/std-mem-arrays.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let bytes: [4]u8 = [122, 101, 114, 111] + var scratch: [4]u8 = [0, 0, 0, 0] + let copied: usize = std.mem.copy(scratch, bytes) + if std.mem.eql("zero", "zero") && copied == 4 && scratch[0] == 122 as u8 { + check world.out.write("std mem arrays\n") + } +} diff --git a/conformance/check/pass/std-mem-arrays.graph b/conformance/check/pass/std-mem-arrays.graph new file mode 100644 index 0000000..71478bd Binary files /dev/null and b/conformance/check/pass/std-mem-arrays.graph differ diff --git a/conformance/check/pass/std-mem-field-items.0 b/conformance/check/pass/std-mem-field-items.0 new file mode 100644 index 0000000..d1602b3 --- /dev/null +++ b/conformance/check/pass/std-mem-field-items.0 @@ -0,0 +1,14 @@ +type Holder { + values: [3]i32, + scratch: [3]i32, +} + +pub fn main() -> Void { + var holder: Holder = Holder { values: [1, 2, 3], scratch: [0, 0, 0] } + let filled: usize = std.mem.fillItems(holder.values, 8) + let copied: usize = std.mem.copyItems(holder.scratch, holder.values) + expect filled == 3 + expect copied == 3 + expect holder.values[0] == 8 + expect holder.scratch[2] == 8 +} diff --git a/conformance/check/pass/std-mem-field-items.graph b/conformance/check/pass/std-mem-field-items.graph new file mode 100644 index 0000000..7d457e4 Binary files /dev/null and b/conformance/check/pass/std-mem-field-items.graph differ diff --git a/conformance/check/pass/std-mem-field-slice.0 b/conformance/check/pass/std-mem-field-slice.0 new file mode 100644 index 0000000..563f4f7 --- /dev/null +++ b/conformance/check/pass/std-mem-field-slice.0 @@ -0,0 +1,9 @@ +type Holder { + values: [3]i32, +} + +pub fn main() -> Void { + var holder: Holder = Holder { values: [1, 2, 3] } + let filled: usize = std.mem.fillItems(holder.values[1..], 9) + expect (filled == 2 && holder.values[1] == 9 && holder.values[2] == 9) +} diff --git a/conformance/check/pass/std-mem-field-slice.graph b/conformance/check/pass/std-mem-field-slice.graph new file mode 100644 index 0000000..e507e05 Binary files /dev/null and b/conformance/check/pass/std-mem-field-slice.graph differ diff --git a/conformance/check/pass/test-target-proc-caps.0 b/conformance/check/pass/test-target-proc-caps.0 new file mode 100644 index 0000000..04d4db9 --- /dev/null +++ b/conformance/check/pass/test-target-proc-caps.0 @@ -0,0 +1,10 @@ +test "pty target capability" { + let child: ProcChild = std.pty.spawn("zero-noop") + expect (!std.pty.valid(child) || std.pty.close(child)) +} + +test "term target capability" { + var input: [1]u8 = [0_u8; 1] + let pending: Maybe = std.term.readInput(input) + expect (!pending.has || pending.value <= 1_usize) +} diff --git a/conformance/check/pass/test-target-proc-caps.graph b/conformance/check/pass/test-target-proc-caps.graph new file mode 100644 index 0000000..86543f0 Binary files /dev/null and b/conformance/check/pass/test-target-proc-caps.graph differ diff --git a/conformance/check/pass/top-level-const.0 b/conformance/check/pass/top-level-const.0 new file mode 100644 index 0000000..c54e109 --- /dev/null +++ b/conformance/check/pass/top-level-const.0 @@ -0,0 +1,10 @@ +const answer: i32 = 42 + +const byte: u8 = 7_u8 + +pub fn main() -> Void { + let value: i32 = answer + let small: u8 = byte + expect (value == 42) + expect (small == 7_u8) +} diff --git a/conformance/check/pass/top-level-const.graph b/conformance/check/pass/top-level-const.graph new file mode 100644 index 0000000..55ba56d Binary files /dev/null and b/conformance/check/pass/top-level-const.graph differ diff --git a/conformance/check/pass/type-alias-basic.0 b/conformance/check/pass/type-alias-basic.0 new file mode 100644 index 0000000..eb45454 --- /dev/null +++ b/conformance/check/pass/type-alias-basic.0 @@ -0,0 +1,15 @@ +pub alias ByteCount = usize + +alias BytePair = Pair + +type Pair { + left: T, + right: U, +} + +pub fn main() -> Void { + let count: ByteCount = 4_usize + let pair: BytePair = Pair { left: 1_u8, right: 2_u8 } + expect (count == 4_usize) + expect (pair.right == 2_u8) +} diff --git a/conformance/check/pass/type-alias-basic.graph b/conformance/check/pass/type-alias-basic.graph new file mode 100644 index 0000000..1eef4df Binary files /dev/null and b/conformance/check/pass/type-alias-basic.graph differ diff --git a/conformance/check/pass/variants-defer-stdlib.0 b/conformance/check/pass/variants-defer-stdlib.0 new file mode 100644 index 0000000..54b75a0 --- /dev/null +++ b/conformance/check/pass/variants-defer-stdlib.0 @@ -0,0 +1,31 @@ +use std.codec + +use std.parse + +use std.time + +enum Status { + ready, + failed, +} + +choice Result { + ok, + failed: i32, +} + +fn cleanup() -> Void { + return +} + +pub fn main(world: World) -> Void raises { + defer cleanup() + let status: Status = Status.ready + let result: Result = Result.ok + let bytes: Maybe = std.codec.readU32Le("abcd") + let digits: usize = std.parse.scanDigits("123abc") + let duration: Duration = std.time.add(std.time.ms(1), std.time.seconds(1)) + if digits == 3 && bytes.has && bytes.value > 0_u32 && std.time.asMsFloor(duration) > 0 { + check world.out.write("variants std\n") + } +} diff --git a/conformance/check/pass/variants-defer-stdlib.graph b/conformance/check/pass/variants-defer-stdlib.graph new file mode 100644 index 0000000..702ab08 Binary files /dev/null and b/conformance/check/pass/variants-defer-stdlib.graph differ diff --git a/conformance/common/fail/unsupported-target-feature.0 b/conformance/common/fail/unsupported-target-feature.0 new file mode 100644 index 0000000..c55538e --- /dev/null +++ b/conformance/common/fail/unsupported-target-feature.0 @@ -0,0 +1,2 @@ +pub fn main(net: Net) -> Void { +} diff --git a/conformance/common/fail/unsupported-target-feature.graph b/conformance/common/fail/unsupported-target-feature.graph new file mode 100644 index 0000000..93f6838 Binary files /dev/null and b/conformance/common/fail/unsupported-target-feature.graph differ diff --git a/conformance/common/pass/array-sum-min-max.0 b/conformance/common/pass/array-sum-min-max.0 new file mode 100644 index 0000000..6816f1a --- /dev/null +++ b/conformance/common/pass/array-sum-min-max.0 @@ -0,0 +1,21 @@ +pub fn main(world: World) -> Void raises { + let values: [5]i32 = [7, 2, 10, 4, 1] + var index: usize = 0 + var sum: i32 = 0 + var min: i32 = values[0] + var max: i32 = values[0] + while index < 5 { + let value: i32 = values[index] + sum = sum + value + if value < min { + min = value + } + if value > max { + max = value + } + index = index + 1 + } + if sum == 24 && min == 1 && max == 10 { + check world.out.write("array sum min max ok\n") + } +} diff --git a/conformance/common/pass/array-sum-min-max.graph b/conformance/common/pass/array-sum-min-max.graph new file mode 100644 index 0000000..d41cadb Binary files /dev/null and b/conformance/common/pass/array-sum-min-max.graph differ diff --git a/conformance/common/pass/bytes-reverse.0 b/conformance/common/pass/bytes-reverse.0 new file mode 100644 index 0000000..3a1f73e --- /dev/null +++ b/conformance/common/pass/bytes-reverse.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + var bytes: [5]u8 = [97, 98, 99, 100, 101] + var left: usize = 0 + var right: usize = 4 + while left < right { + let saved: u8 = bytes[left] + bytes[left] = bytes[right] + bytes[right] = saved + left = left + 1 + right = right - 1 + } + if bytes[0] == 101 && bytes[1] == 100 && bytes[2] == 99 && bytes[3] == 98 && bytes[4] == 97 { + check world.out.write("bytes reverse ok\n") + } +} diff --git a/conformance/common/pass/bytes-reverse.graph b/conformance/common/pass/bytes-reverse.graph new file mode 100644 index 0000000..81e0062 Binary files /dev/null and b/conformance/common/pass/bytes-reverse.graph differ diff --git a/conformance/common/pass/cli-args.0 b/conformance/common/pass/cli-args.0 new file mode 100644 index 0000000..c1dda45 --- /dev/null +++ b/conformance/common/pass/cli-args.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let count: usize = std.args.len() + let first: Maybe = std.args.get(1) + let missing: Maybe = std.args.get(99) + if count == 3 && first.has && !missing.has { + check world.out.write(first.value) + check world.out.write("\n") + } +} diff --git a/conformance/common/pass/cli-args.graph b/conformance/common/pass/cli-args.graph new file mode 100644 index 0000000..d1ba906 Binary files /dev/null and b/conformance/common/pass/cli-args.graph differ diff --git a/conformance/common/pass/count-words-lines.0 b/conformance/common/pass/count-words-lines.0 new file mode 100644 index 0000000..bf2daa7 --- /dev/null +++ b/conformance/common/pass/count-words-lines.0 @@ -0,0 +1,23 @@ +pub fn main(world: World) -> Void raises { + let text: String = "zero lang\ncommon test" + var words: i32 = 0 + var lines: i32 = 1 + var inWord: Bool = false + var index: usize = 0 + while index < 21 { + let ch: u8 = text[index] + if ch == 10 as u8 { + lines = lines + 1 + } + if ch == 32 as u8 || ch == 10 as u8 || ch == 9 as u8 { + inWord = false + } else if !inWord { + words = words + 1 + inWord = true + } + index = index + 1 + } + if words == 4 && lines == 2 { + check world.out.write("count words lines ok\n") + } +} diff --git a/conformance/common/pass/count-words-lines.graph b/conformance/common/pass/count-words-lines.graph new file mode 100644 index 0000000..746cf22 Binary files /dev/null and b/conformance/common/pass/count-words-lines.graph differ diff --git a/conformance/common/pass/factorial.0 b/conformance/common/pass/factorial.0 new file mode 100644 index 0000000..1852ce8 --- /dev/null +++ b/conformance/common/pass/factorial.0 @@ -0,0 +1,15 @@ +fn factorial(n: u32) -> u32 { + var value: u32 = 1 + var index: u32 = 2 + while index <= n { + value = value * index + index = index + 1 + } + return value +} + +pub fn main(world: World) -> Void raises { + if factorial(6) == 720 { + check world.out.write("factorial ok\n") + } +} diff --git a/conformance/common/pass/factorial.graph b/conformance/common/pass/factorial.graph new file mode 100644 index 0000000..8f73275 Binary files /dev/null and b/conformance/common/pass/factorial.graph differ diff --git a/conformance/common/pass/fib-iterative.0 b/conformance/common/pass/fib-iterative.0 new file mode 100644 index 0000000..30739cc --- /dev/null +++ b/conformance/common/pass/fib-iterative.0 @@ -0,0 +1,18 @@ +fn fib(n: u32) -> u32 { + var index: u32 = 0 + var a: u32 = 0 + var b: u32 = 1 + while index < n { + let next: u32 = a + b + a = b + b = next + index = index + 1 + } + return a +} + +pub fn main(world: World) -> Void raises { + if fib(10) == 55 { + check world.out.write("fib iterative ok\n") + } +} diff --git a/conformance/common/pass/fib-iterative.graph b/conformance/common/pass/fib-iterative.graph new file mode 100644 index 0000000..dee8d4e Binary files /dev/null and b/conformance/common/pass/fib-iterative.graph differ diff --git a/conformance/common/pass/fib-recursive.0 b/conformance/common/pass/fib-recursive.0 new file mode 100644 index 0000000..3642a3a --- /dev/null +++ b/conformance/common/pass/fib-recursive.0 @@ -0,0 +1,12 @@ +fn fib(n: u32) -> u32 { + if n < 2 { + return n + } + return fib(n - 1) + fib(n - 2) +} + +pub fn main(world: World) -> Void raises { + if fib(10) == 55 { + check world.out.write("fib recursive ok\n") + } +} diff --git a/conformance/common/pass/fib-recursive.graph b/conformance/common/pass/fib-recursive.graph new file mode 100644 index 0000000..75666af Binary files /dev/null and b/conformance/common/pass/fib-recursive.graph differ diff --git a/conformance/common/pass/file-copy.0 b/conformance/common/pass/file-copy.0 new file mode 100644 index 0000000..85e343a --- /dev/null +++ b/conformance/common/pass/file-copy.0 @@ -0,0 +1,25 @@ +fn seedInput(fs: Fs) -> Bool { + let created: Maybe> = std.fs.create(fs, ".zero/conformance/common-file-copy-input.txt") + if created.has { + var file: owned = created.value + return std.fs.writeAll(&mut file, std.mem.span("zero file copy\n")) + } + return false +} + +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + if seedInput(fs) { + let input: Maybe> = std.fs.open(fs, ".zero/conformance/common-file-copy-input.txt") + let output: Maybe> = std.fs.create(fs, ".zero/conformance/common-file-copy-output.txt") + if input.has && output.has { + var src: owned = input.value + var dst: owned = output.value + var buf: [15]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let read: Maybe = std.fs.read(&mut src, buf) + if read.has && read.value == 15 && std.fs.writeAll(&mut dst, buf) { + check world.out.write("file copy ok\n") + } + } + } +} diff --git a/conformance/common/pass/file-copy.graph b/conformance/common/pass/file-copy.graph new file mode 100644 index 0000000..df2eb9b Binary files /dev/null and b/conformance/common/pass/file-copy.graph differ diff --git a/conformance/common/pass/gcd.0 b/conformance/common/pass/gcd.0 new file mode 100644 index 0000000..df1f658 --- /dev/null +++ b/conformance/common/pass/gcd.0 @@ -0,0 +1,16 @@ +fn gcd(a: u32, b: u32) -> u32 { + var left: u32 = a + var right: u32 = b + while right != 0 { + let next: u32 = left % right + left = right + right = next + } + return left +} + +pub fn main(world: World) -> Void raises { + if gcd(84, 30) == 6 { + check world.out.write("gcd ok\n") + } +} diff --git a/conformance/common/pass/gcd.graph b/conformance/common/pass/gcd.graph new file mode 100644 index 0000000..af84d56 Binary files /dev/null and b/conformance/common/pass/gcd.graph differ diff --git a/conformance/common/pass/json-roundtrip.0 b/conformance/common/pass/json-roundtrip.0 new file mode 100644 index 0000000..03af03a --- /dev/null +++ b/conformance/common/pass/json-roundtrip.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + var arenaBytes: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arenaBytes) + let parsed: Maybe = std.json.parse(arena, "{\"ok\":true}") + var out: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let encoded: Maybe = std.json.writeString(out, "zero") + if std.json.validate("{\"ok\":true}") && parsed.has && encoded.has && std.mem.eql(encoded.value, "\"zero\"") { + check world.out.write("json roundtrip ok\n") + } +} diff --git a/conformance/common/pass/json-roundtrip.graph b/conformance/common/pass/json-roundtrip.graph new file mode 100644 index 0000000..a5f02ec Binary files /dev/null and b/conformance/common/pass/json-roundtrip.graph differ diff --git a/conformance/common/pass/palindrome.0 b/conformance/common/pass/palindrome.0 new file mode 100644 index 0000000..c26679b --- /dev/null +++ b/conformance/common/pass/palindrome.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let yes: String = "level" + let no: String = "zero" + let yesOk: Bool = yes[0] == yes[4] && yes[1] == yes[3] + let noOk: Bool = no[0] != no[3] + if yesOk && noOk { + check world.out.write("palindrome ok\n") + } +} diff --git a/conformance/common/pass/palindrome.graph b/conformance/common/pass/palindrome.graph new file mode 100644 index 0000000..dd1fadd Binary files /dev/null and b/conformance/common/pass/palindrome.graph differ diff --git a/conformance/common/pass/prime.0 b/conformance/common/pass/prime.0 new file mode 100644 index 0000000..ef1cb45 --- /dev/null +++ b/conformance/common/pass/prime.0 @@ -0,0 +1,19 @@ +fn isPrime(n: u32) -> Bool { + if n < 2 { + return false + } + var divisor: u32 = 2 + while divisor * divisor <= n { + if n % divisor == 0 { + return false + } + divisor = divisor + 1 + } + return true +} + +pub fn main(world: World) -> Void raises { + if isPrime(2) && isPrime(29) && !isPrime(30) { + check world.out.write("prime ok\n") + } +} diff --git a/conformance/common/pass/prime.graph b/conformance/common/pass/prime.graph new file mode 100644 index 0000000..7b41bd6 Binary files /dev/null and b/conformance/common/pass/prime.graph differ diff --git a/conformance/common/pass/sieve-small.0 b/conformance/common/pass/sieve-small.0 new file mode 100644 index 0000000..3c49598 --- /dev/null +++ b/conformance/common/pass/sieve-small.0 @@ -0,0 +1,29 @@ +fn countPrimesThroughTen() -> i32 { + var prime: [11]u8 = [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1] + var value: usize = 2 + while value < 11 { + if prime[value] == 1 { + var multiple: usize = value + value + while multiple < 11 { + prime[multiple] = 0 + multiple = multiple + value + } + } + value = value + 1 + } + var count: i32 = 0 + var index: usize = 0 + while index < 11 { + if prime[index] == 1 { + count = count + 1 + } + index = index + 1 + } + return count +} + +pub fn main(world: World) -> Void raises { + if countPrimesThroughTen() == 4 { + check world.out.write("sieve small ok\n") + } +} diff --git a/conformance/common/pass/sieve-small.graph b/conformance/common/pass/sieve-small.graph new file mode 100644 index 0000000..fc8e33e Binary files /dev/null and b/conformance/common/pass/sieve-small.graph differ diff --git a/conformance/common/pass/sort-small.0 b/conformance/common/pass/sort-small.0 new file mode 100644 index 0000000..937135e --- /dev/null +++ b/conformance/common/pass/sort-small.0 @@ -0,0 +1,23 @@ +pub fn main(world: World) -> Void raises { + var values: [5]i32 = [5, 1, 4, 2, 3] + var i: usize = 0 + while i < 5 { + var min: usize = i + var j: usize = i + 1 + while j < 5 { + if values[j] < values[min] { + min = j + } + j = j + 1 + } + if min != i { + let saved: i32 = values[i] + values[i] = values[min] + values[min] = saved + } + i = i + 1 + } + if values[0] == 1 && values[1] == 2 && values[2] == 3 && values[3] == 4 && values[4] == 5 { + check world.out.write("sort small ok\n") + } +} diff --git a/conformance/common/pass/sort-small.graph b/conformance/common/pass/sort-small.graph new file mode 100644 index 0000000..147b05e Binary files /dev/null and b/conformance/common/pass/sort-small.graph differ diff --git a/conformance/common/pass/string-search.0 b/conformance/common/pass/string-search.0 new file mode 100644 index 0000000..8dcdf75 --- /dev/null +++ b/conformance/common/pass/string-search.0 @@ -0,0 +1,14 @@ +pub fn main(world: World) -> Void raises { + let text: String = "zero language" + var index: usize = 0 + var found: i32 = -1 + while index < 13 { + if text[index] == 108 as u8 && found == -1 { + found = index as i32 + } + index = index + 1 + } + if found == 5 { + check world.out.write("string search ok\n") + } +} diff --git a/conformance/common/pass/string-search.graph b/conformance/common/pass/string-search.graph new file mode 100644 index 0000000..73e9720 Binary files /dev/null and b/conformance/common/pass/string-search.graph differ diff --git a/conformance/common/pass/word-reverse.0 b/conformance/common/pass/word-reverse.0 new file mode 100644 index 0000000..71e2c2f --- /dev/null +++ b/conformance/common/pass/word-reverse.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var bytes: [9]u8 = [122, 101, 114, 111, 32, 108, 97, 110, 103] + var index: usize = 0 + while index < 4 { + let saved: u8 = bytes[index] + bytes[index] = bytes[index + 5] + bytes[index + 5] = saved + index = index + 1 + } + if bytes[0] == 108 as u8 && bytes[1] == 97 as u8 && bytes[2] == 110 as u8 && bytes[3] == 103 as u8 && bytes[4] == 32 as u8 && bytes[5] == 122 as u8 && bytes[6] == 101 as u8 && bytes[7] == 114 as u8 && bytes[8] == 111 as u8 { + check world.out.write("word reverse ok\n") + } +} diff --git a/conformance/common/pass/word-reverse.graph b/conformance/common/pass/word-reverse.graph new file mode 100644 index 0000000..fa2aaca Binary files /dev/null and b/conformance/common/pass/word-reverse.graph differ diff --git a/conformance/diagnostics/unknown-name.expected.json b/conformance/diagnostics/unknown-name.expected.json new file mode 100644 index 0000000..89032cb --- /dev/null +++ b/conformance/diagnostics/unknown-name.expected.json @@ -0,0 +1,7 @@ +{ + "schemaVersion": 1, + "code": "NAM003", + "title": "Unknown identifier", + "fixSafety": "local-edit", + "defaultOutput": "plain-text-no-ansi" +} diff --git a/conformance/fixtures/std-fs-read.txt b/conformance/fixtures/std-fs-read.txt new file mode 100644 index 0000000..75b425c --- /dev/null +++ b/conformance/fixtures/std-fs-read.txt @@ -0,0 +1 @@ +zero file diff --git a/conformance/format/functions-blocks.0 b/conformance/format/functions-blocks.0 new file mode 100644 index 0000000..c494112 --- /dev/null +++ b/conformance/format/functions-blocks.0 @@ -0,0 +1,14 @@ +fn helper(value: i32) -> i32 { + if value == 0 { + return 1 + } else { + return value + } +} + +pub fn main() -> Void { + let count: i32 = helper(1) + while count != 0 { + break + } +} diff --git a/conformance/format/functions-blocks.graph b/conformance/format/functions-blocks.graph new file mode 100644 index 0000000..ea69f96 Binary files /dev/null and b/conformance/format/functions-blocks.graph differ diff --git a/conformance/format/generics-static.0 b/conformance/format/generics-static.0 new file mode 100644 index 0000000..2aed17b --- /dev/null +++ b/conformance/format/generics-static.0 @@ -0,0 +1,11 @@ +type Box { + items: [N]T, +} + +fn first(box: ref>) -> T { + return box.items[0] +} + +pub fn main() -> Void { + let value: Box = Box { items: [1, 2, 3, 4] } +} diff --git a/conformance/format/generics-static.graph b/conformance/format/generics-static.graph new file mode 100644 index 0000000..cf89173 Binary files /dev/null and b/conformance/format/generics-static.graph differ diff --git a/conformance/format/match-expressions.0 b/conformance/format/match-expressions.0 new file mode 100644 index 0000000..a48800d --- /dev/null +++ b/conformance/format/match-expressions.0 @@ -0,0 +1,16 @@ +choice Event { + key: i32, + quit, +} + +pub fn main() -> Void { + let event: Event = Event.key(7) + match event { + .key(code) { + expect (code == 7) + } + _ { + expect (false == true) + } + } +} diff --git a/conformance/format/match-expressions.graph b/conformance/format/match-expressions.graph new file mode 100644 index 0000000..2106c7e Binary files /dev/null and b/conformance/format/match-expressions.graph differ diff --git a/conformance/format/messy.0 b/conformance/format/messy.0 new file mode 100644 index 0000000..6651957 --- /dev/null +++ b/conformance/format/messy.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("format me\n") +} diff --git a/conformance/format/messy.graph b/conformance/format/messy.graph new file mode 100644 index 0000000..23a9d0e Binary files /dev/null and b/conformance/format/messy.graph differ diff --git a/conformance/lexer/compiler-smoke.0 b/conformance/lexer/compiler-smoke.0 new file mode 100644 index 0000000..26ff0ea --- /dev/null +++ b/conformance/lexer/compiler-smoke.0 @@ -0,0 +1,10 @@ +use std.mem + +pub fn main() -> Void { + let decimal: i32 = 123 + let hex: i32 = 0xff + let binary: i32 = 0b101 + let value: u8 = 42_u8 + let text: String = "hi" + let letter: char = 'x' +} diff --git a/conformance/lexer/compiler-smoke.graph b/conformance/lexer/compiler-smoke.graph new file mode 100644 index 0000000..a0b4253 Binary files /dev/null and b/conformance/lexer/compiler-smoke.graph differ diff --git a/conformance/native/pass/aarch64-private-helper-ignored.0 b/conformance/native/pass/aarch64-private-helper-ignored.0 new file mode 100644 index 0000000..56e0cbf --- /dev/null +++ b/conformance/native/pass/aarch64-private-helper-ignored.0 @@ -0,0 +1,7 @@ +fn helper(value: i32) -> i32 { + return value + 1 +} + +export c fn main() -> i32 { + return 42 +} diff --git a/conformance/native/pass/aarch64-private-helper-ignored.graph b/conformance/native/pass/aarch64-private-helper-ignored.graph new file mode 100644 index 0000000..813abfd Binary files /dev/null and b/conformance/native/pass/aarch64-private-helper-ignored.graph differ diff --git a/conformance/native/pass/allocator-primitives.0 b/conformance/native/pass/allocator-primitives.0 new file mode 100644 index 0000000..944660b --- /dev/null +++ b/conformance/native/pass/allocator-primitives.0 @@ -0,0 +1,20 @@ +pub fn main(world: World) -> Void raises { + var backing: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(backing) + let first: Maybe> = std.mem.allocBytes(alloc, 3) + let second: Maybe> = std.mem.allocBytes(alloc, 5) + let overflow: Maybe> = std.mem.allocBytes(alloc, 1) + let nullAlloc: NullAlloc = std.mem.nullAlloc() + let nullBytes: Maybe> = std.mem.allocBytes(nullAlloc, 1) + if first.has && second.has && !overflow.has && !nullBytes.has && std.mem.len(first.value) == 3 && std.mem.len(second.value) == 5 { + first.value[0] = 65 + second.value[4] = 90 + if backing[0] == 65 && backing[7] == 90 { + check world.out.write("allocator primitives ok\n") + } else { + check world.out.write("allocator primitives wrote wrong bytes\n") + } + } else { + check world.out.write("allocator primitives failed\n") + } +} diff --git a/conformance/native/pass/allocator-primitives.graph b/conformance/native/pass/allocator-primitives.graph new file mode 100644 index 0000000..7d191dc Binary files /dev/null and b/conformance/native/pass/allocator-primitives.graph differ diff --git a/conformance/native/pass/array-repeat-literal.0 b/conformance/native/pass/array-repeat-literal.0 new file mode 100644 index 0000000..75db03c --- /dev/null +++ b/conformance/native/pass/array-repeat-literal.0 @@ -0,0 +1,9 @@ +export c fn main() -> i32 { + let bytes: [8]u8 = [7_u8; 8] + var scratch: [16]u8 = [0_u8; 16] + scratch[8] = bytes[0] + if bytes[7] == 7_u8 && scratch[0] == 0_u8 && scratch[8] == 7_u8 && std.mem.len(scratch) == 16 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/array-repeat-literal.graph b/conformance/native/pass/array-repeat-literal.graph new file mode 100644 index 0000000..5c29b42 Binary files /dev/null and b/conformance/native/pass/array-repeat-literal.graph differ diff --git a/conformance/native/pass/array-repeat-record-field.0 b/conformance/native/pass/array-repeat-record-field.0 new file mode 100644 index 0000000..e3be818 --- /dev/null +++ b/conformance/native/pass/array-repeat-record-field.0 @@ -0,0 +1,11 @@ +type Bytes4 { + bytes: [4]u8, +} + +export c fn main() -> i32 { + let value: Bytes4 = Bytes4 { bytes: [7_u8; 4] } + if value.bytes[0] == 7_u8 && value.bytes[3] == 7_u8 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/array-repeat-record-field.graph b/conformance/native/pass/array-repeat-record-field.graph new file mode 100644 index 0000000..b1f2a23 Binary files /dev/null and b/conformance/native/pass/array-repeat-record-field.graph differ diff --git a/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.0 b/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.0 new file mode 100644 index 0000000..baa5c1a --- /dev/null +++ b/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.0 @@ -0,0 +1,17 @@ +type Holder { + value: ref, + count: i32, +} + +fn replaceValue(holder: mutref, next: ref) -> i32 { + holder.value = next + return 1 +} + +pub fn main() -> Void { + var old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { value: &old, count: 0 } + holder.count = replaceValue(&mut holder, &next) + old = 3 +} diff --git a/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.graph b/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.graph new file mode 100644 index 0000000..eae6215 Binary files /dev/null and b/conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.graph differ diff --git a/conformance/native/pass/bool-arrays.0 b/conformance/native/pass/bool-arrays.0 new file mode 100644 index 0000000..01d32d1 --- /dev/null +++ b/conformance/native/pass/bool-arrays.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var flags: [5]Bool = [false; 5] + flags[1] = true + flags[3] = true + if !flags[0] && flags[1] && (flags[3] && !flags[4]) { + check world.out.write("bool arrays ok\n") + } +} diff --git a/conformance/native/pass/bool-arrays.graph b/conformance/native/pass/bool-arrays.graph new file mode 100644 index 0000000..53a8592 Binary files /dev/null and b/conformance/native/pass/bool-arrays.graph differ diff --git a/conformance/native/pass/borrow-aggregate-reassignment-same-origin.0 b/conformance/native/pass/borrow-aggregate-reassignment-same-origin.0 new file mode 100644 index 0000000..9327128 --- /dev/null +++ b/conformance/native/pass/borrow-aggregate-reassignment-same-origin.0 @@ -0,0 +1,10 @@ +type Box { + value: mutref, +} + +pub fn main() -> Void { + var x: i32 = 1 + var box: Box = Box { value: &mut x } + box.value = &mut x + box = Box { value: &mut x } +} diff --git a/conformance/native/pass/borrow-aggregate-reassignment-same-origin.graph b/conformance/native/pass/borrow-aggregate-reassignment-same-origin.graph new file mode 100644 index 0000000..f4a72b6 Binary files /dev/null and b/conformance/native/pass/borrow-aggregate-reassignment-same-origin.graph differ diff --git a/conformance/native/pass/borrow-assignment-same-origin.0 b/conformance/native/pass/borrow-assignment-same-origin.0 new file mode 100644 index 0000000..0242e6e --- /dev/null +++ b/conformance/native/pass/borrow-assignment-same-origin.0 @@ -0,0 +1,8 @@ +pub fn main() -> Void { + var x: i32 = 1 + var y: i32 = 2 + var r: mutref = &mut y + r = &mut y + r = &mut x + y = 3 +} diff --git a/conformance/native/pass/borrow-assignment-same-origin.graph b/conformance/native/pass/borrow-assignment-same-origin.graph new file mode 100644 index 0000000..9fd224a Binary files /dev/null and b/conformance/native/pass/borrow-assignment-same-origin.graph differ diff --git a/conformance/native/pass/borrow-branch-reassignment.0 b/conformance/native/pass/borrow-branch-reassignment.0 new file mode 100644 index 0000000..dec54ec --- /dev/null +++ b/conformance/native/pass/borrow-branch-reassignment.0 @@ -0,0 +1,11 @@ +pub fn main(flag: Bool) -> Void { + var x: i32 = 1 + let y: i32 = 2 + var r: ref = &x + if flag { + r = &y + } else { + r = &y + } + x = 3 +} diff --git a/conformance/native/pass/borrow-branch-reassignment.graph b/conformance/native/pass/borrow-branch-reassignment.graph new file mode 100644 index 0000000..c919b2c Binary files /dev/null and b/conformance/native/pass/borrow-branch-reassignment.graph differ diff --git a/conformance/native/pass/borrow-field-independent-assignment.0 b/conformance/native/pass/borrow-field-independent-assignment.0 new file mode 100644 index 0000000..fc79370 --- /dev/null +++ b/conformance/native/pass/borrow-field-independent-assignment.0 @@ -0,0 +1,14 @@ +type Pair { + left: i32, + right: i32, +} + +pub fn main() -> Void { + var pair: Pair = Pair { left: 1, right: 2 } + let left: ref = &pair.left + pair.right = 3 + var second: Pair = Pair { left: 4, right: 5 } + let second_left: mutref = &mut second.left + second.right = 6 + let second_right: mutref = &mut second.right +} diff --git a/conformance/native/pass/borrow-field-independent-assignment.graph b/conformance/native/pass/borrow-field-independent-assignment.graph new file mode 100644 index 0000000..4f0b7a7 Binary files /dev/null and b/conformance/native/pass/borrow-field-independent-assignment.graph differ diff --git a/conformance/native/pass/borrow-primitives.0 b/conformance/native/pass/borrow-primitives.0 new file mode 100644 index 0000000..a0e9234 --- /dev/null +++ b/conformance/native/pass/borrow-primitives.0 @@ -0,0 +1,35 @@ +type Point { + x: i32, + y: i32, +} + +type Holder { + point: Point, +} + +fn read_x(point: ref) -> i32 { + return point.x +} + +fn write_x(point: mutref, value: i32) -> Void { + point.x = value +} + +pub fn main(world: World) -> Void raises { + var point: Point = Point { x: 1, y: 2 } + var holder: Holder = Holder { point: Point { x: 3, y: 4 } } + if true { + let shared: ref = &point + if read_x(shared) != 1 { + check world.out.write("borrow primitives failed\n") + return + } + } + write_x(&mut point, 5) + write_x(&mut holder.point, 9) + if point.x == 5 && holder.point.x == 9 { + check world.out.write("borrow primitives ok\n") + } else { + check world.out.write("borrow primitives failed\n") + } +} diff --git a/conformance/native/pass/borrow-primitives.graph b/conformance/native/pass/borrow-primitives.graph new file mode 100644 index 0000000..2cf8e33 Binary files /dev/null and b/conformance/native/pass/borrow-primitives.graph differ diff --git a/conformance/native/pass/borrow-return-explicit-ref-field-origin.0 b/conformance/native/pass/borrow-return-explicit-ref-field-origin.0 new file mode 100644 index 0000000..5d153c1 --- /dev/null +++ b/conformance/native/pass/borrow-return-explicit-ref-field-origin.0 @@ -0,0 +1,22 @@ +type Box { + value: ref, +} + +fn get(box: ref) -> ref { + return box.value +} + +fn getMut(box: mutref) -> ref { + return box.value +} + +pub fn main() -> Void { + var x: i32 = 1 + let y: i32 = 2 + let z: i32 = 3 + var box: Box = Box { value: &x } + let current: ref = get(&box) + box.value = &y + let other: ref = getMut(&mut box) + box.value = &z +} diff --git a/conformance/native/pass/borrow-return-explicit-ref-field-origin.graph b/conformance/native/pass/borrow-return-explicit-ref-field-origin.graph new file mode 100644 index 0000000..7c57f29 Binary files /dev/null and b/conformance/native/pass/borrow-return-explicit-ref-field-origin.graph differ diff --git a/conformance/native/pass/borrow-return-param-field-subpath.0 b/conformance/native/pass/borrow-return-param-field-subpath.0 new file mode 100644 index 0000000..c290df0 --- /dev/null +++ b/conformance/native/pass/borrow-return-param-field-subpath.0 @@ -0,0 +1,14 @@ +type Point { + x: i32, + y: i32, +} + +fn field(point: ref) -> ref { + return &point.x +} + +pub fn main() -> Void { + var point: Point = Point { x: 1, y: 2 } + let current: ref = field(&point) + point.y = 3 +} diff --git a/conformance/native/pass/borrow-return-param-field-subpath.graph b/conformance/native/pass/borrow-return-param-field-subpath.graph new file mode 100644 index 0000000..d170192 Binary files /dev/null and b/conformance/native/pass/borrow-return-param-field-subpath.graph differ diff --git a/conformance/native/pass/borrow-return-param-ref.0 b/conformance/native/pass/borrow-return-param-ref.0 new file mode 100644 index 0000000..531b074 --- /dev/null +++ b/conformance/native/pass/borrow-return-param-ref.0 @@ -0,0 +1,8 @@ +fn keep(r: ref) -> ref { + return r +} + +pub fn main() -> Void { + let x: i32 = 1 + let r: ref = keep(&x) +} diff --git a/conformance/native/pass/borrow-return-param-ref.graph b/conformance/native/pass/borrow-return-param-ref.graph new file mode 100644 index 0000000..dbc24a4 Binary files /dev/null and b/conformance/native/pass/borrow-return-param-ref.graph differ diff --git a/conformance/native/pass/borrow-return-ref-alias-field.0 b/conformance/native/pass/borrow-return-ref-alias-field.0 new file mode 100644 index 0000000..f6d511c --- /dev/null +++ b/conformance/native/pass/borrow-return-ref-alias-field.0 @@ -0,0 +1,14 @@ +type Point { + x: i32, + y: i32, +} + +fn field(point: ref) -> ref { + let pointRef: ref = point + return &pointRef.x +} + +pub fn main() -> Void { + let point: Point = Point { x: 1, y: 2 } + let current: ref = field(&point) +} diff --git a/conformance/native/pass/borrow-return-ref-alias-field.graph b/conformance/native/pass/borrow-return-ref-alias-field.graph new file mode 100644 index 0000000..2a5a440 Binary files /dev/null and b/conformance/native/pass/borrow-return-ref-alias-field.graph differ diff --git a/conformance/native/pass/borrow-shadowed-root-reassignment.0 b/conformance/native/pass/borrow-shadowed-root-reassignment.0 new file mode 100644 index 0000000..58146ce --- /dev/null +++ b/conformance/native/pass/borrow-shadowed-root-reassignment.0 @@ -0,0 +1,10 @@ +pub fn main() -> Void { + var x: i32 = 1 + let z: i32 = 2 + var r: ref = &x + if true { + let x: i32 = 3 + r = &z + } + x = 4 +} diff --git a/conformance/native/pass/borrow-shadowed-root-reassignment.graph b/conformance/native/pass/borrow-shadowed-root-reassignment.graph new file mode 100644 index 0000000..d70e6dd Binary files /dev/null and b/conformance/native/pass/borrow-shadowed-root-reassignment.graph differ diff --git a/conformance/native/pass/borrow-unreachable-if-return-origin.0 b/conformance/native/pass/borrow-unreachable-if-return-origin.0 new file mode 100644 index 0000000..986fcbb --- /dev/null +++ b/conformance/native/pass/borrow-unreachable-if-return-origin.0 @@ -0,0 +1,15 @@ +fn choose(left: ref, right: ref) -> ref { + if true { + return left + } else { + return left + } + return right +} + +pub fn main() -> Void { + let x: i32 = 1 + var y: i32 = 2 + let current: ref = choose(&x, &y) + y = 3 +} diff --git a/conformance/native/pass/borrow-unreachable-if-return-origin.graph b/conformance/native/pass/borrow-unreachable-if-return-origin.graph new file mode 100644 index 0000000..822c880 Binary files /dev/null and b/conformance/native/pass/borrow-unreachable-if-return-origin.graph differ diff --git a/conformance/native/pass/borrow-unreachable-loop-return-origin.0 b/conformance/native/pass/borrow-unreachable-loop-return-origin.0 new file mode 100644 index 0000000..ba165ae --- /dev/null +++ b/conformance/native/pass/borrow-unreachable-loop-return-origin.0 @@ -0,0 +1,13 @@ +fn choose(left: ref, right: ref) -> ref { + while false { + return right + } + return left +} + +pub fn main() -> Void { + let x: i32 = 1 + var y: i32 = 2 + let current: ref = choose(&x, &y) + y = 3 +} diff --git a/conformance/native/pass/borrow-unreachable-loop-return-origin.graph b/conformance/native/pass/borrow-unreachable-loop-return-origin.graph new file mode 100644 index 0000000..59066d1 Binary files /dev/null and b/conformance/native/pass/borrow-unreachable-loop-return-origin.graph differ diff --git a/conformance/native/pass/borrow-unreachable-match-return-origin.0 b/conformance/native/pass/borrow-unreachable-match-return-origin.0 new file mode 100644 index 0000000..fe347fb --- /dev/null +++ b/conformance/native/pass/borrow-unreachable-match-return-origin.0 @@ -0,0 +1,23 @@ +enum Mode { + left, + alsoLeft, +} + +fn choose(mode: Mode, left: ref, right: ref) -> ref { + match mode { + left { + return left + } + alsoLeft { + return left + } + } + return right +} + +pub fn main(mode: Mode) -> Void { + let x: i32 = 1 + var y: i32 = 2 + let current: ref = choose(mode, &x, &y) + y = 3 +} diff --git a/conformance/native/pass/borrow-unreachable-match-return-origin.graph b/conformance/native/pass/borrow-unreachable-match-return-origin.graph new file mode 100644 index 0000000..77298f9 Binary files /dev/null and b/conformance/native/pass/borrow-unreachable-match-return-origin.graph differ diff --git a/conformance/native/pass/borrow-unreachable-raise-return-origin.0 b/conformance/native/pass/borrow-unreachable-raise-return-origin.0 new file mode 100644 index 0000000..2725bec --- /dev/null +++ b/conformance/native/pass/borrow-unreachable-raise-return-origin.0 @@ -0,0 +1,11 @@ +fn choose(left: ref, right: ref) -> ref raises [Done] { + raise Done + return right +} + +pub fn main() -> Void raises [Done] { + let x: i32 = 1 + var y: i32 = 2 + let current: ref = check choose(&x, &y) + y = 3 +} diff --git a/conformance/native/pass/borrow-unreachable-raise-return-origin.graph b/conformance/native/pass/borrow-unreachable-raise-return-origin.graph new file mode 100644 index 0000000..eb23442 Binary files /dev/null and b/conformance/native/pass/borrow-unreachable-raise-return-origin.graph differ diff --git a/conformance/native/pass/borrow-unreachable-return-origin.0 b/conformance/native/pass/borrow-unreachable-return-origin.0 new file mode 100644 index 0000000..533a29d --- /dev/null +++ b/conformance/native/pass/borrow-unreachable-return-origin.0 @@ -0,0 +1,14 @@ +fn choose(left: ref, right: ref) -> ref { + if true { + return left + } else { + return right + } +} + +pub fn main() -> Void { + let x: i32 = 1 + var y: i32 = 2 + let current: ref = choose(&x, &y) + y = 3 +} diff --git a/conformance/native/pass/borrow-unreachable-return-origin.graph b/conformance/native/pass/borrow-unreachable-return-origin.graph new file mode 100644 index 0000000..6102212 Binary files /dev/null and b/conformance/native/pass/borrow-unreachable-return-origin.graph differ diff --git a/conformance/native/pass/branch-overwrite-away-reference-origin.0 b/conformance/native/pass/branch-overwrite-away-reference-origin.0 new file mode 100644 index 0000000..5767f35 --- /dev/null +++ b/conformance/native/pass/branch-overwrite-away-reference-origin.0 @@ -0,0 +1,15 @@ +type Holder { + value: ref, +} + +pub fn main(flag: Bool) -> Void { + var old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { value: &old } + if flag { + holder.value = &next + } else { + holder.value = &next + } + old = 3 +} diff --git a/conformance/native/pass/branch-overwrite-away-reference-origin.graph b/conformance/native/pass/branch-overwrite-away-reference-origin.graph new file mode 100644 index 0000000..7aa8886 Binary files /dev/null and b/conformance/native/pass/branch-overwrite-away-reference-origin.graph differ diff --git a/conformance/native/pass/break-continue.0 b/conformance/native/pass/break-continue.0 new file mode 100644 index 0000000..439f4a9 --- /dev/null +++ b/conformance/native/pass/break-continue.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var index: i32 = 0 + while index < 5 { + index = index + 1 + if index == 2 { + continue + } + if index == 4 { + break + } + check world.out.write("loop tick\n") + } +} diff --git a/conformance/native/pass/break-continue.graph b/conformance/native/pass/break-continue.graph new file mode 100644 index 0000000..4c5ef2d Binary files /dev/null and b/conformance/native/pass/break-continue.graph differ diff --git a/conformance/native/pass/byte-view-call-single-eval.0 b/conformance/native/pass/byte-view-call-single-eval.0 new file mode 100644 index 0000000..677cdf9 --- /dev/null +++ b/conformance/native/pass/byte-view-call-single-eval.0 @@ -0,0 +1,34 @@ +fn bump(bytes: MutSpan) -> String { + bytes[0] = bytes[0] + 1_u8 + return bytes[..1] +} + +fn firstByte(text: String) -> u8 { + return text[0] +} + +pub fn main(world: World) -> Void raises { + var storage: [1]u8 = [96] + var ok: Bool = true + if !std.mem.eql(bump(storage), "a") { + ok = false + } + if storage[0] != 97_u8 { + ok = false + } + if firstByte(bump(storage)) != 98_u8 { + ok = false + } + if storage[0] != 98_u8 { + ok = false + } + if !std.mem.eql(bump(storage), "c") { + ok = false + } + if storage[0] != 99_u8 { + ok = false + } + if ok { + check world.out.write("byte view call single eval ok\n") + } +} diff --git a/conformance/native/pass/byte-view-call-single-eval.graph b/conformance/native/pass/byte-view-call-single-eval.graph new file mode 100644 index 0000000..324970f Binary files /dev/null and b/conformance/native/pass/byte-view-call-single-eval.graph differ diff --git a/conformance/native/pass/byte-view-params.0 b/conformance/native/pass/byte-view-params.0 new file mode 100644 index 0000000..a2c7a74 --- /dev/null +++ b/conformance/native/pass/byte-view-params.0 @@ -0,0 +1,22 @@ +fn byteSum(bytes: Span) -> u32 { + var total: u32 = 0 + var i: usize = 0 + while i < std.mem.len(bytes) { + total = total + bytes[i] as u32 + i = i + 1 + } + return total +} + +fn second(bytes: Span) -> u8 { + return bytes[1] +} + +pub fn main(world: World) -> Void raises { + let text: Span = std.mem.span("agent") + let data: [5]u8 = [10, 20, 30, 40, 50] + let window: Span = data[1..4] + if byteSum(std.mem.span("hi")) == 209 && byteSum(text) == 527 && second(text) == 103 as u8 && byteSum(window) == 90 && second(window) == 30_u8 { + check world.out.write("byte view params ok\n") + } +} diff --git a/conformance/native/pass/byte-view-params.graph b/conformance/native/pass/byte-view-params.graph new file mode 100644 index 0000000..1ce323b Binary files /dev/null and b/conformance/native/pass/byte-view-params.graph differ diff --git a/conformance/native/pass/c-abi-export.0 b/conformance/native/pass/c-abi-export.0 new file mode 100644 index 0000000..81847da --- /dev/null +++ b/conformance/native/pass/c-abi-export.0 @@ -0,0 +1,15 @@ +extern type CPoint { + x: i32, + y: i32, +} + +export c fn zero_add(a: i32, b: i32) -> i32 { + return a + b +} + +pub fn main(world: World) -> Void raises { + let point: CPoint = CPoint { x: 2, y: 3 } + if zero_add(point.x, point.y) == 5 { + check world.out.write("c abi export ok\n") + } +} diff --git a/conformance/native/pass/c-abi-export.graph b/conformance/native/pass/c-abi-export.graph new file mode 100644 index 0000000..d7ac117 Binary files /dev/null and b/conformance/native/pass/c-abi-export.graph differ diff --git a/conformance/native/pass/c-import-alias-later-local.0 b/conformance/native/pass/c-import-alias-later-local.0 new file mode 100644 index 0000000..d1c451d --- /dev/null +++ b/conformance/native/pass/c-import-alias-later-local.0 @@ -0,0 +1,7 @@ +extern c "conformance/c/simple.h" as c + +export c fn main() -> i32 { + let total: i32 = c.zero_c_add(20, 22) + let c: i32 = 1 + return total + c +} diff --git a/conformance/native/pass/c-import-alias-later-local.graph b/conformance/native/pass/c-import-alias-later-local.graph new file mode 100644 index 0000000..ab0f79f Binary files /dev/null and b/conformance/native/pass/c-import-alias-later-local.graph differ diff --git a/conformance/native/pass/c-import-type-shadowing.0 b/conformance/native/pass/c-import-type-shadowing.0 new file mode 100644 index 0000000..00dd4b9 --- /dev/null +++ b/conformance/native/pass/c-import-type-shadowing.0 @@ -0,0 +1,16 @@ +extern c "conformance/c/simple.h" as Counter + +type Counter { + value: i32, + fn zero_c_add(self: ref, a: i32, b: i32) -> i32 { + return self.value + a + b + } +} + +export c fn main() -> i32 { + let counter: Counter = Counter { value: 1000 } + if Counter.zero_c_add(&counter, 20, 22) == 1042 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/c-import-type-shadowing.graph b/conformance/native/pass/c-import-type-shadowing.graph new file mode 100644 index 0000000..9701392 Binary files /dev/null and b/conformance/native/pass/c-import-type-shadowing.graph differ diff --git a/conformance/native/pass/char-literals.0 b/conformance/native/pass/char-literals.0 new file mode 100644 index 0000000..558da5e --- /dev/null +++ b/conformance/native/pass/char-literals.0 @@ -0,0 +1,23 @@ +type CharBag { + letter: char, + newline: char, + quote: char, + slash: char, + hex: char, +} + +fn echoChar(value: char) -> char { + return value +} + +pub fn main(world: World) -> Void raises { + let letter: char = 'A' + let newline: char = '\n' + let quote: char = '\'' + let slash: char = '\\' + let hex: char = 'A' + let bag: CharBag = CharBag { letter: letter, newline: newline, quote: quote, slash: slash, hex: hex } + if letter == hex && echoChar(bag.letter) == 'A' && bag.newline == newline && bag.quote == quote && bag.slash == slash { + check world.out.write("char literals ok\n") + } +} diff --git a/conformance/native/pass/char-literals.graph b/conformance/native/pass/char-literals.graph new file mode 100644 index 0000000..d90058a Binary files /dev/null and b/conformance/native/pass/char-literals.graph differ diff --git a/conformance/native/pass/check-maybe-fallibility.0 b/conformance/native/pass/check-maybe-fallibility.0 new file mode 100644 index 0000000..b9728b9 --- /dev/null +++ b/conformance/native/pass/check-maybe-fallibility.0 @@ -0,0 +1,13 @@ +fn first_or_none(bytes: Span, index: usize) -> Maybe raises { + check std.mem.get(bytes, index) + return std.mem.get(bytes, index) +} + +pub fn main(world: World) -> Void raises { + let bytes: [2]u8 = [8, 9] + let hit: Maybe = first_or_none(bytes, 1) + let miss: Maybe = first_or_none(bytes, 9) + if hit.has && hit.value == 9_u8 && !miss.has { + check world.out.write("check maybe fallibility ok\n") + } +} diff --git a/conformance/native/pass/check-maybe-fallibility.graph b/conformance/native/pass/check-maybe-fallibility.graph new file mode 100644 index 0000000..1b82372 Binary files /dev/null and b/conformance/native/pass/check-maybe-fallibility.graph differ diff --git a/conformance/native/pass/checked-bounds-get.0 b/conformance/native/pass/checked-bounds-get.0 new file mode 100644 index 0000000..03d3050 --- /dev/null +++ b/conformance/native/pass/checked-bounds-get.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + let bytes: [3]u8 = [4, 5, 6] + let text: String = "zero" + let first: Maybe = std.mem.get(bytes, 1) + let missing: Maybe = std.mem.get(bytes, 9) + let letter: Maybe = std.mem.get(text, 0) + if first.has && first.value == 5_u8 && !missing.has && letter.has && letter.value == 122 as u8 { + check world.out.write("checked bounds get ok\n") + } +} diff --git a/conformance/native/pass/checked-bounds-get.graph b/conformance/native/pass/checked-bounds-get.graph new file mode 100644 index 0000000..fe5fc09 Binary files /dev/null and b/conformance/native/pass/checked-bounds-get.graph differ diff --git a/conformance/native/pass/checked-fallible-interface-method.0 b/conformance/native/pass/checked-fallible-interface-method.0 new file mode 100644 index 0000000..af0b215 --- /dev/null +++ b/conformance/native/pass/checked-fallible-interface-method.0 @@ -0,0 +1,17 @@ +type S { + value: i32, + fn risky(self: ref) -> Void raises [Bad] { + raise Bad + } +} + +interface Risky { + fn risky(self: ref) -> Void raises [Bad] +} + +fn run>(value: ref) -> Void raises [Bad] { + check T.risky(value) +} + +pub fn main() -> Void { +} diff --git a/conformance/native/pass/checked-fallible-interface-method.graph b/conformance/native/pass/checked-fallible-interface-method.graph new file mode 100644 index 0000000..8a51a5a Binary files /dev/null and b/conformance/native/pass/checked-fallible-interface-method.graph differ diff --git a/conformance/native/pass/checked-fallible-static-method.0 b/conformance/native/pass/checked-fallible-static-method.0 new file mode 100644 index 0000000..92f0199 --- /dev/null +++ b/conformance/native/pass/checked-fallible-static-method.0 @@ -0,0 +1,10 @@ +type S { + value: i32, + fn risky() -> Void raises [Bad] { + raise Bad + } +} + +pub fn main() -> Void raises [Bad] { + check S.risky() +} diff --git a/conformance/native/pass/checked-fallible-static-method.graph b/conformance/native/pass/checked-fallible-static-method.graph new file mode 100644 index 0000000..acc87a7 Binary files /dev/null and b/conformance/native/pass/checked-fallible-static-method.graph differ diff --git a/conformance/native/pass/checked-fallible-wrapper.0 b/conformance/native/pass/checked-fallible-wrapper.0 new file mode 100644 index 0000000..4ca50c9 --- /dev/null +++ b/conformance/native/pass/checked-fallible-wrapper.0 @@ -0,0 +1,14 @@ +fn inner(flag: Bool) -> Void raises [Bad] { + if flag { + raise Bad + } +} + +fn wrapper() -> Void raises { + check inner(false) +} + +pub fn main(world: World) -> Void raises { + check wrapper() + check world.out.write("checked fallible wrapper ok\n") +} diff --git a/conformance/native/pass/checked-fallible-wrapper.graph b/conformance/native/pass/checked-fallible-wrapper.graph new file mode 100644 index 0000000..d7c2e29 Binary files /dev/null and b/conformance/native/pass/checked-fallible-wrapper.graph differ diff --git a/conformance/native/pass/choice-match-payload-reference-origin.0 b/conformance/native/pass/choice-match-payload-reference-origin.0 new file mode 100644 index 0000000..4ad42c2 --- /dev/null +++ b/conformance/native/pass/choice-match-payload-reference-origin.0 @@ -0,0 +1,16 @@ +choice Slot { + some: ref, + none, +} + +pub fn main() -> Void { + let value: i32 = 1 + let slot: Slot = Slot.some(&value) + match slot { + .some(borrowed) { + let _same: ref = borrowed + } + none { + } + } +} diff --git a/conformance/native/pass/choice-match-payload-reference-origin.graph b/conformance/native/pass/choice-match-payload-reference-origin.graph new file mode 100644 index 0000000..35c8ca2 Binary files /dev/null and b/conformance/native/pass/choice-match-payload-reference-origin.graph differ diff --git a/conformance/native/pass/choice-match-payload-return-origin.0 b/conformance/native/pass/choice-match-payload-return-origin.0 new file mode 100644 index 0000000..cda74d7 --- /dev/null +++ b/conformance/native/pass/choice-match-payload-return-origin.0 @@ -0,0 +1,21 @@ +choice Slot { + some: ref, + none, +} + +fn get(slot: Slot, fallback: ref) -> ref { + match slot { + .some(borrowed) { + return borrowed + } + none { + return fallback + } + } +} + +pub fn main() -> Void { + let value: i32 = 1 + let slot: Slot = Slot.some(&value) + let _borrowed: ref = get(slot, &value) +} diff --git a/conformance/native/pass/choice-match-payload-return-origin.graph b/conformance/native/pass/choice-match-payload-return-origin.graph new file mode 100644 index 0000000..f460381 Binary files /dev/null and b/conformance/native/pass/choice-match-payload-return-origin.graph differ diff --git a/conformance/native/pass/choice-payload-reference-return.0 b/conformance/native/pass/choice-payload-reference-return.0 new file mode 100644 index 0000000..cba4055 --- /dev/null +++ b/conformance/native/pass/choice-payload-reference-return.0 @@ -0,0 +1,14 @@ +choice Slot { + some: ref, + none, +} + +fn pass(slot: Slot) -> Slot { + return slot +} + +pub fn main() -> Void { + let value: i32 = 1 + let slot: Slot = Slot.some(&value) + let _copy: Slot = pass(slot) +} diff --git a/conformance/native/pass/choice-payload-reference-return.graph b/conformance/native/pass/choice-payload-reference-return.graph new file mode 100644 index 0000000..ef722ae Binary files /dev/null and b/conformance/native/pass/choice-payload-reference-return.graph differ diff --git a/conformance/native/pass/coff-dynamic-byte-slice.0 b/conformance/native/pass/coff-dynamic-byte-slice.0 new file mode 100644 index 0000000..013333e --- /dev/null +++ b/conformance/native/pass/coff-dynamic-byte-slice.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let text: String = "abcdef" + let start: usize = 1 + let end: usize = 4 + let middle: Span = text[start..end] + if std.mem.len(middle) == 3 { + check world.out.write("dynamic slice ok\n") + } +} diff --git a/conformance/native/pass/coff-dynamic-byte-slice.graph b/conformance/native/pass/coff-dynamic-byte-slice.graph new file mode 100644 index 0000000..5c5d7dd Binary files /dev/null and b/conformance/native/pass/coff-dynamic-byte-slice.graph differ diff --git a/conformance/native/pass/coff-maybe-byte-view-buildable.0 b/conformance/native/pass/coff-maybe-byte-view-buildable.0 new file mode 100644 index 0000000..b6f041c --- /dev/null +++ b/conformance/native/pass/coff-maybe-byte-view-buildable.0 @@ -0,0 +1,14 @@ +export c fn main() -> u8 { + var storage: [8]u8 = [0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + let first: Maybe> = std.mem.allocBytes(alloc, 4) + if first.has { + if std.mem.len(first.value) == 4 { + let bytes: MutSpan = first.value + if std.mem.len(bytes) == 4 { + return first.value[0] + } + } + } + return 0_u8 +} diff --git a/conformance/native/pass/coff-maybe-byte-view-buildable.graph b/conformance/native/pass/coff-maybe-byte-view-buildable.graph new file mode 100644 index 0000000..eab5f96 Binary files /dev/null and b/conformance/native/pass/coff-maybe-byte-view-buildable.graph differ diff --git a/conformance/native/pass/compile-time-v1.0 b/conformance/native/pass/compile-time-v1.0 new file mode 100644 index 0000000..b90b566 --- /dev/null +++ b/conformance/native/pass/compile-time-v1.0 @@ -0,0 +1,48 @@ +const enabled: Bool = meta(target.pointerWidth >= 32 && hasField(Point, "x")) + +const selected: Mode = Mode.fast + +const field_count: usize = meta fieldCount(Point) + +const field_type: String = meta fieldType(Point, "x") + +const mode_cases: usize = meta enumCaseCount(Mode) + +const has_tiny: Bool = meta hasEnumCase(Mode, "tiny") + +const event_cases: usize = meta choiceCaseCount(Event) + +const has_tick: Bool = meta hasChoiceCase(Event, "tick") + +type Point { + x: i32, + y: i32, +} + +type Gate { + value: i32, +} + +enum Mode: u8 { + fast, + tiny, +} + +choice Event { + none, + tick: i32, +} + +fn readGate(gate: ref>) -> i32 { + if enabledFlag { + return gate.value + } + return 0 +} + +pub fn main(world: World) -> Void raises { + let gate: Gate = Gate { value: 29 } + if readGate(&gate) == 29 && field_count == 2 && mode_cases == 2 && event_cases == 2 && has_tiny && has_tick && std.mem.eql(field_type, "i32") { + check world.out.write("compile time v1 ok\n") + } +} diff --git a/conformance/native/pass/compile-time-v1.graph b/conformance/native/pass/compile-time-v1.graph new file mode 100644 index 0000000..770ab18 Binary files /dev/null and b/conformance/native/pass/compile-time-v1.graph differ diff --git a/conformance/native/pass/const-arithmetic.0 b/conformance/native/pass/const-arithmetic.0 new file mode 100644 index 0000000..ffcf065 --- /dev/null +++ b/conformance/native/pass/const-arithmetic.0 @@ -0,0 +1,11 @@ +const base: i32 = 40 + +const answer: i32 = base + 2 + +pub fn main(world: World) -> Void raises { + if answer == 42 { + check world.out.write("const arithmetic ok\n") + } else { + check world.out.write("const arithmetic broke\n") + } +} diff --git a/conformance/native/pass/const-arithmetic.graph b/conformance/native/pass/const-arithmetic.graph new file mode 100644 index 0000000..33f9648 Binary files /dev/null and b/conformance/native/pass/const-arithmetic.graph differ diff --git a/conformance/native/pass/const-layout.0 b/conformance/native/pass/const-layout.0 new file mode 100644 index 0000000..7e8d434 --- /dev/null +++ b/conformance/native/pass/const-layout.0 @@ -0,0 +1,22 @@ +extern type CPoint { + x: i32, + y: i32, +} + +packed type Header { + tag: u8, + len: u16, +} + +type Wrapper { + value: const i32, +} + +pub fn main(world: World) -> Void raises { + let point: CPoint = CPoint { x: 1, y: 2 } + let header: Header = Header { tag: 7_u8, len: 9_u16 } + let wrapper: Wrapper = Wrapper { value: 11 } + if point.x == 1 && header.len == 9_u16 && wrapper.value == 11 { + check world.out.write("const layout ok\n") + } +} diff --git a/conformance/native/pass/const-layout.graph b/conformance/native/pass/const-layout.graph new file mode 100644 index 0000000..149b0d0 Binary files /dev/null and b/conformance/native/pass/const-layout.graph differ diff --git a/conformance/native/pass/constructors-defaults.0 b/conformance/native/pass/constructors-defaults.0 new file mode 100644 index 0000000..e649432 --- /dev/null +++ b/conformance/native/pass/constructors-defaults.0 @@ -0,0 +1,26 @@ +type FixedVec { + len: usize = 0, + items: [N]T, + fn init(items: [N]T) -> Self { + return FixedVec { items: items } + } + fn push(self: mutref, value: T) -> Void raises [Full] { + if self.len == N { + raise Full + } + self.items[self.len] = value + self.len = self.len + 1 + } + fn get(self: ref, index: usize) -> Maybe { + return std.mem.get(self.items, index) + } +} + +pub fn main(world: World) -> Void raises { + var vec: FixedVec = FixedVec.init([0, 0, 0, 0]) + check vec.push(42) + let first: Maybe = vec.get(0) + if first.has && first.value == 42 { + check world.out.write("constructors defaults ok\n") + } +} diff --git a/conformance/native/pass/constructors-defaults.graph b/conformance/native/pass/constructors-defaults.graph new file mode 100644 index 0000000..c908fd4 Binary files /dev/null and b/conformance/native/pass/constructors-defaults.graph differ diff --git a/conformance/native/pass/defer-return-raise-nested.0 b/conformance/native/pass/defer-return-raise-nested.0 new file mode 100644 index 0000000..3b9821e --- /dev/null +++ b/conformance/native/pass/defer-return-raise-nested.0 @@ -0,0 +1,27 @@ +fn mark(marker: MutSpan, value: u8) -> Void { + marker[0] = value +} + +fn early(marker: MutSpan) -> Void { + defer mark(marker, 1) + return +} + +fn fail(marker: MutSpan) -> i32 raises [Boom] { + defer mark(marker, 2) + raise Boom +} + +pub fn main(world: World) -> Void raises { + var early_marker: [1]u8 = [0] + var raise_marker: [1]u8 = [0] + var nested_marker: [1]u8 = [0] + early(early_marker) + let recovered: i32 = rescue fail(raise_marker) err 7 + if true { + defer mark(nested_marker, 3) + } + if early_marker[0] == 1 && raise_marker[0] == 2 && nested_marker[0] == 3 && recovered == 7 { + check world.out.write("defer return raise nested ok\n") + } +} diff --git a/conformance/native/pass/defer-return-raise-nested.graph b/conformance/native/pass/defer-return-raise-nested.graph new file mode 100644 index 0000000..23de39b Binary files /dev/null and b/conformance/native/pass/defer-return-raise-nested.graph differ diff --git a/conformance/native/pass/direct-checksum-helpers.0 b/conformance/native/pass/direct-checksum-helpers.0 new file mode 100644 index 0000000..c384ea7 --- /dev/null +++ b/conformance/native/pass/direct-checksum-helpers.0 @@ -0,0 +1,48 @@ +export c fn main() -> i32 { + let crc_text: u32 = std.codec.crc32("zero") + let crc_bytes: u32 = std.codec.crc32Bytes(std.mem.span("zero hash payload\n")) + let hash: u32 = std.crypto.hash32(std.mem.span("message")) + var fixed_buf: [8]u8 = [0_u8; 8] + var hash_buf: [8]u8 = [0_u8; 8] + var hmac_buf: [8]u8 = [0_u8; 8] + var id_buf: [8]u8 = [0_u8; 8] + var sha_buf: [64]u8 = [0_u8; 64] + var hmac_sha_buf: [64]u8 = [0_u8; 64] + let fixed: Maybe> = std.crypto.fixedHex32(fixed_buf, 48879_u32) + let hash_hex: Maybe> = std.crypto.hashHex32(hash_buf, std.mem.span("message")) + let hmac_hex: Maybe> = std.crypto.hmacHex32(hmac_buf, std.mem.span("key"), std.mem.span("message")) + let stable_id: Maybe> = std.crypto.stableId32(id_buf, std.mem.span("message")) + let sha: Maybe> = std.crypto.sha256Hex(sha_buf, std.mem.span("abc")) + let hmac_sha: Maybe> = std.crypto.hmacSha256Hex(hmac_sha_buf, std.mem.span("key"), std.mem.span("The quick brown fox jumps over the lazy dog")) + if crc_text != 2883514770_u32 || crc_bytes != 1120241454_u32 || hash != 3065852031_u32 { + return 1 + } + if !fixed.has || !hash_hex.has || !hmac_hex.has || !stable_id.has { + return 1 + } + if !sha.has { + return 1 + } + if !hmac_sha.has { + return 1 + } + if !std.mem.eql(fixed.value, "0000beef") { + return 1 + } + if !std.mem.eql(hash_hex.value, "b6bd307f") { + return 1 + } + if !std.mem.eql(hmac_hex.value, "0000000a") { + return 1 + } + if !std.mem.eql(stable_id.value, "b6bd307f") { + return 1 + } + if !std.mem.eql(sha.value, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") { + return 1 + } + if !std.mem.eql(hmac_sha.value, "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8") { + return 1 + } + return 0 +} diff --git a/conformance/native/pass/direct-checksum-helpers.graph b/conformance/native/pass/direct-checksum-helpers.graph new file mode 100644 index 0000000..60edb26 Binary files /dev/null and b/conformance/native/pass/direct-checksum-helpers.graph differ diff --git a/conformance/native/pass/dynamic-indexed-store-scratch.0 b/conformance/native/pass/dynamic-indexed-store-scratch.0 new file mode 100644 index 0000000..acb5877 --- /dev/null +++ b/conformance/native/pass/dynamic-indexed-store-scratch.0 @@ -0,0 +1,15 @@ +fn dynamicIndex() -> usize { + return 1 +} + +pub fn main(world: World) -> Void raises { + var bytes: [2]u8 = [0_u8, 0_u8] + var words: [2]u16 = [0_u16, 0_u16] + bytes[dynamicIndex() % 2_usize] = 90_u8 + words[dynamicIndex() % 2_usize] = 4660_u16 + if bytes[1] == 90_u8 && words[1] == 4660_u16 { + check world.out.write("dynamic indexed store ok\n") + } else { + check world.out.write("dynamic indexed store failed\n") + } +} diff --git a/conformance/native/pass/dynamic-indexed-store-scratch.graph b/conformance/native/pass/dynamic-indexed-store-scratch.graph new file mode 100644 index 0000000..579d07c Binary files /dev/null and b/conformance/native/pass/dynamic-indexed-store-scratch.graph differ diff --git a/conformance/native/pass/explicit-cast-narrow-direct.0 b/conformance/native/pass/explicit-cast-narrow-direct.0 new file mode 100644 index 0000000..a2928ab --- /dev/null +++ b/conformance/native/pass/explicit-cast-narrow-direct.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let big: u32 = 300 + let byte: u8 = big as u8 + let word: u16 = 70000 as u16 + let wide: u64 = byte as u64 + if byte == 44_u8 && word == 4464_u16 && wide == 44_u64 { + check world.out.write("explicit cast narrow direct ok\n") + } +} diff --git a/conformance/native/pass/explicit-cast-narrow-direct.graph b/conformance/native/pass/explicit-cast-narrow-direct.graph new file mode 100644 index 0000000..5daf4ec Binary files /dev/null and b/conformance/native/pass/explicit-cast-narrow-direct.graph differ diff --git a/conformance/native/pass/explicit-casts.0 b/conformance/native/pass/explicit-casts.0 new file mode 100644 index 0000000..53fd60f --- /dev/null +++ b/conformance/native/pass/explicit-casts.0 @@ -0,0 +1,32 @@ +type CastBag { + byte: u8, + count: u32, + size: usize, + signed: isize, +} + +fn narrow(value: u32) -> u8 { + return value as u8 +} + +fn widen(value: u8) -> u32 { + return value as u32 +} + +fn toSize(value: i32) -> usize { + return value as usize +} + +pub fn main(world: World) -> Void raises { + let big: u32 = 300 + let small: u8 = 42 + let byte: u8 = big as u8 + let count: u32 = small as u32 + let size: usize = count as usize + let offset: isize = small as isize + let sum: u16 = small as u16 + 1 as u16 + let bag: CastBag = CastBag { byte: big as u8, count: small as u32, size: count as usize, signed: small as isize } + if byte == 44 && count == 42 && size == 42 && offset == 42 && sum == 43 && narrow(big) == 44 && widen(small) == 42 && toSize(7) == 7 && bag.byte == 44 { + check world.out.write("explicit casts ok\n") + } +} diff --git a/conformance/native/pass/explicit-casts.graph b/conformance/native/pass/explicit-casts.graph new file mode 100644 index 0000000..3a545e6 Binary files /dev/null and b/conformance/native/pass/explicit-casts.graph differ diff --git a/conformance/native/pass/fallibility-check-value.0 b/conformance/native/pass/fallibility-check-value.0 new file mode 100644 index 0000000..991a766 --- /dev/null +++ b/conformance/native/pass/fallibility-check-value.0 @@ -0,0 +1,15 @@ +fn value() -> i32 raises [BadInput] { + return 42 +} + +fn run() -> i32 raises [BadInput] { + let result: i32 = check value() + return result +} + +pub fn main(world: World) -> Void raises { + let result: i32 = check run() + if result == 42 { + check world.out.write("check value ok\n") + } +} diff --git a/conformance/native/pass/fallibility-check-value.graph b/conformance/native/pass/fallibility-check-value.graph new file mode 100644 index 0000000..41992a4 Binary files /dev/null and b/conformance/native/pass/fallibility-check-value.graph differ diff --git a/conformance/native/pass/fallibility-error-sets.0 b/conformance/native/pass/fallibility-error-sets.0 new file mode 100644 index 0000000..c13fa2a --- /dev/null +++ b/conformance/native/pass/fallibility-error-sets.0 @@ -0,0 +1,26 @@ +type DropProbe { + marker: MutSpan, + fn drop(self: mutref) -> Void { + self.marker[0] = 1 + } +} + +fn maybe_fail(should_fail: Bool, marker: MutSpan) -> i32 raises [BadInput] { + let probe: owned = DropProbe { marker: marker } + if should_fail { + raise BadInput + } + return 42 +} + +fn run(marker: MutSpan) -> Void raises [BadInput] { + check maybe_fail(false, marker) +} + +pub fn main(world: World) -> Void raises { + var marker: [1]u8 = [0] + check run(marker) + if marker[0] == 1_u8 { + check world.out.write("fallibility error sets ok\n") + } +} diff --git a/conformance/native/pass/fallibility-error-sets.graph b/conformance/native/pass/fallibility-error-sets.graph new file mode 100644 index 0000000..2bb6b21 Binary files /dev/null and b/conformance/native/pass/fallibility-error-sets.graph differ diff --git a/conformance/native/pass/fixed-buf-alloc-local.0 b/conformance/native/pass/fixed-buf-alloc-local.0 new file mode 100644 index 0000000..a110014 --- /dev/null +++ b/conformance/native/pass/fixed-buf-alloc-local.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + var backing: [4096]u8 = [0; 4096] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(backing) + let bytes: Maybe> = std.mem.allocBytes(alloc, 64) + if bytes.has { + let buf: MutSpan = bytes.value + buf[0] = 7 + if buf[0] == 7 { + check world.out.write("fixed buf alloc local ok\n") + } + } +} diff --git a/conformance/native/pass/fixed-buf-alloc-local.graph b/conformance/native/pass/fixed-buf-alloc-local.graph new file mode 100644 index 0000000..60269a0 Binary files /dev/null and b/conformance/native/pass/fixed-buf-alloc-local.graph differ diff --git a/conformance/native/pass/float-char-casts.0 b/conformance/native/pass/float-char-casts.0 new file mode 100644 index 0000000..b80f7d4 --- /dev/null +++ b/conformance/native/pass/float-char-casts.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + let whole: i32 = 7.9 as i32 + let fraction: f32 = 3 as f32 + let wide: f64 = fraction as f64 + let byte: u8 = 'A' as u8 + let again: char = byte as char + if whole == 7 && byte == 65_u8 && again == 'A' && wide > 2.9 { + check world.out.write("float char casts ok\n") + } +} diff --git a/conformance/native/pass/float-char-casts.graph b/conformance/native/pass/float-char-casts.graph new file mode 100644 index 0000000..ac2d2d4 Binary files /dev/null and b/conformance/native/pass/float-char-casts.graph differ diff --git a/conformance/native/pass/float-primitives.0 b/conformance/native/pass/float-primitives.0 new file mode 100644 index 0000000..da5c2a9 --- /dev/null +++ b/conformance/native/pass/float-primitives.0 @@ -0,0 +1,23 @@ +type FloatPair { + left: f32, + right: f64, +} + +fn addF32(left: f32, right: f32) -> f32 { + return left + right +} + +fn addF64(left: f64, right: f64) -> f64 { + return left + right +} + +pub fn main(world: World) -> Void raises { + let small: f32 = 1.5 + let smaller: f32 = 0.5 + let precise: f64 = 1.0e-3 + let defaultFloat: f64 = 2.25 + let pair: FloatPair = FloatPair { left: small, right: defaultFloat } + if addF32(small, smaller) == 2.0 && addF64(defaultFloat, 0.75) == 3.0 && precise < 0.01 && pair.left > smaller && pair.right == defaultFloat { + check world.out.write("float primitives ok\n") + } +} diff --git a/conformance/native/pass/float-primitives.graph b/conformance/native/pass/float-primitives.graph new file mode 100644 index 0000000..f7f77b8 Binary files /dev/null and b/conformance/native/pass/float-primitives.graph differ diff --git a/conformance/native/pass/for-range.0 b/conformance/native/pass/for-range.0 new file mode 100644 index 0000000..3af4098 --- /dev/null +++ b/conformance/native/pass/for-range.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + for index in 0..5 { + if index == 1 { + continue + } + if index == 4 { + break + } + check world.out.write("range tick\n") + } +} diff --git a/conformance/native/pass/for-range.graph b/conformance/native/pass/for-range.graph new file mode 100644 index 0000000..1d22dd2 Binary files /dev/null and b/conformance/native/pass/for-range.graph differ diff --git a/conformance/native/pass/frame-large-locals.0 b/conformance/native/pass/frame-large-locals.0 new file mode 100644 index 0000000..42e8d66 --- /dev/null +++ b/conformance/native/pass/frame-large-locals.0 @@ -0,0 +1,16 @@ +pub fn main(world: World) -> Void raises { + let first: Maybe = std.args.get(1) + var pad: [16384]u8 = [0; 16384] + var buf: [32768]u8 = [0; 32768] + pad[16383] = 7 + buf[32767] = 5 + let read: Maybe = std.fs.readBytes("conformance/fixtures/std-fs-read.txt", pad) + let missing: Maybe = std.fs.readBytes("conformance/fixtures/missing.bin", buf) + if first.has && read.has && read.value == 10 && !missing.has && pad[0] == 122_u8 && pad[16383] == 7_u8 && buf[32767] == 5_u8 { + check world.out.write("frame large locals ok ") + check world.out.write(first.value) + check world.out.write("\n") + } else { + check world.out.write("frame large locals failed\n") + } +} diff --git a/conformance/native/pass/frame-large-locals.graph b/conformance/native/pass/frame-large-locals.graph new file mode 100644 index 0000000..35f8cf5 Binary files /dev/null and b/conformance/native/pass/frame-large-locals.graph differ diff --git a/conformance/native/pass/frame-limit-boundary.0 b/conformance/native/pass/frame-limit-boundary.0 new file mode 100644 index 0000000..1ac0e9e --- /dev/null +++ b/conformance/native/pass/frame-limit-boundary.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + var buf: [131040]u8 = [0; 131040] + buf[0] = 3 + buf[131039] = 9 + if buf[0] == 3_u8 && buf[131039] == 9_u8 { + check world.out.write("frame limit boundary ok\n") + } else { + check world.out.write("frame limit boundary failed\n") + } +} diff --git a/conformance/native/pass/frame-limit-boundary.graph b/conformance/native/pass/frame-limit-boundary.graph new file mode 100644 index 0000000..e2bc3e9 Binary files /dev/null and b/conformance/native/pass/frame-limit-boundary.graph differ diff --git a/conformance/native/pass/frame-split-helpers.0 b/conformance/native/pass/frame-split-helpers.0 new file mode 100644 index 0000000..26da56e --- /dev/null +++ b/conformance/native/pass/frame-split-helpers.0 @@ -0,0 +1,20 @@ +fn fillChunk(tag: u8) -> usize { + var chunk: [65536]u8 = [0; 65536] + chunk[0] = tag + chunk[65535] = tag + var count: usize = 0 + if chunk[0] == tag && chunk[65535] == tag { + count = 65536 + } + return count +} + +pub fn main(world: World) -> Void raises { + var total: usize = 0 + total = total + fillChunk(1_u8) + total = total + fillChunk(2_u8) + total = total + fillChunk(3_u8) + if total == 196608 { + check world.out.write("frame split helpers ok\n") + } +} diff --git a/conformance/native/pass/frame-split-helpers.graph b/conformance/native/pass/frame-split-helpers.graph new file mode 100644 index 0000000..8e566fd Binary files /dev/null and b/conformance/native/pass/frame-split-helpers.graph differ diff --git a/conformance/native/pass/function-mutref-reference-store.0 b/conformance/native/pass/function-mutref-reference-store.0 new file mode 100644 index 0000000..d1f44c7 --- /dev/null +++ b/conformance/native/pass/function-mutref-reference-store.0 @@ -0,0 +1,14 @@ +type Holder { + value: ref, +} + +fn store(holder: mutref, next: ref) -> Void { + holder.value = next +} + +pub fn main() -> Void { + let old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { value: &old } + store(&mut holder, &next) +} diff --git a/conformance/native/pass/function-mutref-reference-store.graph b/conformance/native/pass/function-mutref-reference-store.graph new file mode 100644 index 0000000..abcb28c Binary files /dev/null and b/conformance/native/pass/function-mutref-reference-store.graph differ diff --git a/conformance/native/pass/generic-constructor-expected.0 b/conformance/native/pass/generic-constructor-expected.0 new file mode 100644 index 0000000..bf492b5 --- /dev/null +++ b/conformance/native/pass/generic-constructor-expected.0 @@ -0,0 +1,24 @@ +type FixedVec { + len: usize = 0, + items: [N]T, + fn init(items: [N]T) -> Self { + return FixedVec { items: items } + } + fn get(self: ref, index: usize) -> Maybe { + return std.mem.get(self.items, index) + } +} + +fn make_vec() -> FixedVec { + return FixedVec.init([1, 2, 3, 4]) +} + +pub fn main(world: World) -> Void raises { + let vec: FixedVec = FixedVec.init([5, 6, 7, 8]) + let from_return: FixedVec = make_vec() + let first: Maybe = vec.get(0) + let returned: Maybe = from_return.get(3) + if first.has && first.value == 5 && returned.has && returned.value == 4 { + check world.out.write("generic constructor expected ok\n") + } +} diff --git a/conformance/native/pass/generic-constructor-expected.graph b/conformance/native/pass/generic-constructor-expected.graph new file mode 100644 index 0000000..2cc54df Binary files /dev/null and b/conformance/native/pass/generic-constructor-expected.graph differ diff --git a/conformance/native/pass/generic-expected-return.0 b/conformance/native/pass/generic-expected-return.0 new file mode 100644 index 0000000..6a7a0a2 --- /dev/null +++ b/conformance/native/pass/generic-expected-return.0 @@ -0,0 +1,10 @@ +fn none() -> Maybe { + return null +} + +pub fn main(world: World) -> Void raises { + let value: Maybe = none() + if !value.has { + check world.out.write("generic expected return ok\n") + } +} diff --git a/conformance/native/pass/generic-expected-return.graph b/conformance/native/pass/generic-expected-return.graph new file mode 100644 index 0000000..0b17951 Binary files /dev/null and b/conformance/native/pass/generic-expected-return.graph differ diff --git a/conformance/native/pass/generic-function-basic.0 b/conformance/native/pass/generic-function-basic.0 new file mode 100644 index 0000000..576fe4e --- /dev/null +++ b/conformance/native/pass/generic-function-basic.0 @@ -0,0 +1,18 @@ +fn identity(value: T) -> T { + return value +} + +fn first(left: T, right: T) -> T { + return left +} + +pub fn main(world: World) -> Void raises { + let a: i32 = identity(41) + let b: u8 = identity(7_u8) + let c: i32 = first(9, 12) + if a == 41 && b == 7_u8 && c == 9 { + check world.out.write("generic function ok\n") + } else { + check world.out.write("generic function broke\n") + } +} diff --git a/conformance/native/pass/generic-function-basic.graph b/conformance/native/pass/generic-function-basic.graph new file mode 100644 index 0000000..47a889b Binary files /dev/null and b/conformance/native/pass/generic-function-basic.graph differ diff --git a/conformance/native/pass/generic-inferred-specialized-call.0 b/conformance/native/pass/generic-inferred-specialized-call.0 new file mode 100644 index 0000000..770f678 --- /dev/null +++ b/conformance/native/pass/generic-inferred-specialized-call.0 @@ -0,0 +1,16 @@ +fn identity(value: T) -> T { + return value +} + +fn forward(value: T) -> T { + return identity(value) +} + +pub fn main(world: World) -> Void raises { + let value: i32 = forward(42) + if value == 42 { + check world.out.write("generic inferred specialized call ok\n") + } else { + check world.out.write("generic inferred specialized call broke\n") + } +} diff --git a/conformance/native/pass/generic-inferred-specialized-call.graph b/conformance/native/pass/generic-inferred-specialized-call.graph new file mode 100644 index 0000000..60f3267 Binary files /dev/null and b/conformance/native/pass/generic-inferred-specialized-call.graph differ diff --git a/conformance/native/pass/generic-literals-arrays.0 b/conformance/native/pass/generic-literals-arrays.0 new file mode 100644 index 0000000..7e899fa --- /dev/null +++ b/conformance/native/pass/generic-literals-arrays.0 @@ -0,0 +1,37 @@ +type Box { + value: T, +} + +type StaticBox { + cap: usize = N, + bytes: [N]u8, + fn init(bytes: [N]u8) -> Self { + return StaticBox { bytes: bytes } + } +} + +type Nested { + inner: Box = Box { value: 7 }, + storage: StaticBox = StaticBox.init([1, 2, 3, 4]), +} + +type Chunk { + bytes: [4]u8, +} + +fn takes4(bytes: [4]u8) -> u8 { + return bytes[0] +} + +fn make_chunk() -> Chunk { + return Chunk { bytes: [9, 8, 7, 6] } +} + +pub fn main(world: World) -> Void raises { + let nested: Nested = Nested {} + let chunk: Chunk = make_chunk() + let first: u8 = takes4([4, 3, 2, 1]) + if nested.inner.value == 7 && nested.storage.cap == 4 && chunk.bytes[2] == 7 && first == 4 { + check world.out.write("generic literals arrays ok\n") + } +} diff --git a/conformance/native/pass/generic-literals-arrays.graph b/conformance/native/pass/generic-literals-arrays.graph new file mode 100644 index 0000000..69156f8 Binary files /dev/null and b/conformance/native/pass/generic-literals-arrays.graph differ diff --git a/conformance/native/pass/generic-mem.0 b/conformance/native/pass/generic-mem.0 new file mode 100644 index 0000000..86d100f --- /dev/null +++ b/conformance/native/pass/generic-mem.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let values: [4]i32 = [10, 20, 30, 40] + let sameValues: [4]i32 = [10, 20, 30, 40] + let middleValues: [2]i32 = [20, 30] + let bytes: Span = std.mem.span("ok") + let valuesSpan: Span = values + let sameSpan: Span = sameValues + let middle: Span = values[1..3] + let expectedMiddle: Span = middleValues + if std.mem.len(valuesSpan) == 4 && std.mem.len(middle) == 2 && std.mem.eqlBytes(valuesSpan, sameSpan) && std.mem.eqlBytes(middle, expectedMiddle) && std.mem.len(bytes) == 2 { + check world.out.write("generic mem ok\n") + } +} diff --git a/conformance/native/pass/generic-mem.graph b/conformance/native/pass/generic-mem.graph new file mode 100644 index 0000000..3a01e2c Binary files /dev/null and b/conformance/native/pass/generic-mem.graph differ diff --git a/conformance/native/pass/generic-multi-specialization.0 b/conformance/native/pass/generic-multi-specialization.0 new file mode 100644 index 0000000..3b4d934 --- /dev/null +++ b/conformance/native/pass/generic-multi-specialization.0 @@ -0,0 +1,17 @@ +fn first(left: A, right: B) -> A { + return left +} + +fn second(left: A, right: B) -> B { + return right +} + +pub fn main(world: World) -> Void raises { + let a: i32 = first(21, 7) + let b: u8 = second(a, 6) + if a == 21 && b == 6 { + check world.out.write("generic multi specialization ok\n") + } else { + check world.out.write("generic multi specialization broke\n") + } +} diff --git a/conformance/native/pass/generic-multi-specialization.graph b/conformance/native/pass/generic-multi-specialization.graph new file mode 100644 index 0000000..99a12d2 Binary files /dev/null and b/conformance/native/pass/generic-multi-specialization.graph differ diff --git a/conformance/native/pass/generic-mutref-reference-store.0 b/conformance/native/pass/generic-mutref-reference-store.0 new file mode 100644 index 0000000..6252bcb --- /dev/null +++ b/conformance/native/pass/generic-mutref-reference-store.0 @@ -0,0 +1,14 @@ +type Cell { + value: ref, +} + +fn store(cell: mutref>, next: ref) -> Void { + cell.value = next +} + +pub fn main() -> Void { + let old: i32 = 1 + let next: i32 = 2 + var cell: Cell = Cell { value: &old } + store(&mut cell, &next) +} diff --git a/conformance/native/pass/generic-mutref-reference-store.graph b/conformance/native/pass/generic-mutref-reference-store.graph new file mode 100644 index 0000000..9a963ed Binary files /dev/null and b/conformance/native/pass/generic-mutref-reference-store.graph differ diff --git a/conformance/native/pass/generic-nested-calls.0 b/conformance/native/pass/generic-nested-calls.0 new file mode 100644 index 0000000..56054ef --- /dev/null +++ b/conformance/native/pass/generic-nested-calls.0 @@ -0,0 +1,16 @@ +fn identity(value: T) -> T { + return value +} + +fn choose(left: T, right: T) -> T { + return left +} + +pub fn main(world: World) -> Void raises { + let value: i32 = identity(choose(identity(21), identity(22))) + if value == 21 { + check world.out.write("generic nested calls ok\n") + } else { + check world.out.write("generic nested calls broke\n") + } +} diff --git a/conformance/native/pass/generic-nested-calls.graph b/conformance/native/pass/generic-nested-calls.graph new file mode 100644 index 0000000..c5b2578 Binary files /dev/null and b/conformance/native/pass/generic-nested-calls.graph differ diff --git a/conformance/native/pass/generic-nested-local-specialization.0 b/conformance/native/pass/generic-nested-local-specialization.0 new file mode 100644 index 0000000..bf0e828 --- /dev/null +++ b/conformance/native/pass/generic-nested-local-specialization.0 @@ -0,0 +1,12 @@ +fn has_none(value: T) -> Bool { + let maybe: Maybe = null + return !maybe.has +} + +pub fn main(world: World) -> Void raises { + if has_none(1) { + check world.out.write("generic nested local specialization ok\n") + } else { + check world.out.write("generic nested local specialization broke\n") + } +} diff --git a/conformance/native/pass/generic-nested-local-specialization.graph b/conformance/native/pass/generic-nested-local-specialization.graph new file mode 100644 index 0000000..7d28384 Binary files /dev/null and b/conformance/native/pass/generic-nested-local-specialization.graph differ diff --git a/conformance/native/pass/generic-shape-basic.0 b/conformance/native/pass/generic-shape-basic.0 new file mode 100644 index 0000000..d6aebf2 --- /dev/null +++ b/conformance/native/pass/generic-shape-basic.0 @@ -0,0 +1,12 @@ +type Box { + value: T, +} + +pub fn main(world: World) -> Void raises { + let box: Box = Box { value: 42 } + if box.value == 42 { + check world.out.write("generic shape ok\n") + } else { + check world.out.write("generic shape broke\n") + } +} diff --git a/conformance/native/pass/generic-shape-basic.graph b/conformance/native/pass/generic-shape-basic.graph new file mode 100644 index 0000000..7417b04 Binary files /dev/null and b/conformance/native/pass/generic-shape-basic.graph differ diff --git a/conformance/native/pass/generic-shape-methods.0 b/conformance/native/pass/generic-shape-methods.0 new file mode 100644 index 0000000..07fa72a --- /dev/null +++ b/conformance/native/pass/generic-shape-methods.0 @@ -0,0 +1,28 @@ +type FixedVec { + len: usize, + items: [N]T, + fn push(self: mutref, value: T) -> Void raises [Full] { + if self.len == N { + raise Full + } + self.items[self.len] = value + self.len = self.len + 1 + } + fn get(self: ref, index: usize) -> Maybe { + return std.mem.get(self.items, index) + } + fn count(self: ref) -> usize { + return self.len + } +} + +pub fn main(world: World) -> Void raises { + var vec: FixedVec = FixedVec { len: 0, items: [0, 0, 0, 0] } + check FixedVec.push(&mut vec, 7) + check FixedVec.push(&mut vec, 9) + let first: Maybe = FixedVec.get(&vec, 0) + let missing: Maybe = FixedVec.get(&vec, 8) + if first.has && first.value == 7 && !missing.has && FixedVec.count(&vec) == 2 { + check world.out.write("generic shape methods ok\n") + } +} diff --git a/conformance/native/pass/generic-shape-methods.graph b/conformance/native/pass/generic-shape-methods.graph new file mode 100644 index 0000000..3ad1a27 Binary files /dev/null and b/conformance/native/pass/generic-shape-methods.graph differ diff --git a/conformance/native/pass/generic-shape-multi.0 b/conformance/native/pass/generic-shape-multi.0 new file mode 100644 index 0000000..2ec6a0e --- /dev/null +++ b/conformance/native/pass/generic-shape-multi.0 @@ -0,0 +1,13 @@ +type Pair { + left: T, + right: U, +} + +pub fn main(world: World) -> Void raises { + let pair: Pair = Pair { left: 7, right: 9_u8 } + if pair.left == 7 && pair.right == 9_u8 { + check world.out.write("generic shape multi ok\n") + } else { + check world.out.write("generic shape multi broke\n") + } +} diff --git a/conformance/native/pass/generic-shape-multi.graph b/conformance/native/pass/generic-shape-multi.graph new file mode 100644 index 0000000..cfb9de9 Binary files /dev/null and b/conformance/native/pass/generic-shape-multi.graph differ diff --git a/conformance/native/pass/generic-shape-nested-defaults-alias.0 b/conformance/native/pass/generic-shape-nested-defaults-alias.0 new file mode 100644 index 0000000..a8ac572 --- /dev/null +++ b/conformance/native/pass/generic-shape-nested-defaults-alias.0 @@ -0,0 +1,23 @@ +alias U8Box = Box + +type Box { + value: T, +} + +type Slot { + item: T, + seen: Bool = false, +} + +type Pair { + left: T, + right: U, +} + +pub fn main(world: World) -> Void raises { + let nested: Pair, Slot> = Pair { left: Box { value: 42 }, right: Slot { item: 7_u8 } } + let aliased: U8Box = Box { value: nested.right.item } + if nested.left.value == 42 && !nested.right.seen && aliased.value == 7_u8 { + check world.out.write("generic shape nested defaults alias ok\n") + } +} diff --git a/conformance/native/pass/generic-shape-nested-defaults-alias.graph b/conformance/native/pass/generic-shape-nested-defaults-alias.graph new file mode 100644 index 0000000..a9dbc41 Binary files /dev/null and b/conformance/native/pass/generic-shape-nested-defaults-alias.graph differ diff --git a/conformance/native/pass/generic-spans.0 b/conformance/native/pass/generic-spans.0 new file mode 100644 index 0000000..ba72743 --- /dev/null +++ b/conformance/native/pass/generic-spans.0 @@ -0,0 +1,28 @@ +type IntWindow { + values: Span, +} + +fn second(values: Span) -> i32 { + return values[1] +} + +fn middle(values: Span) -> Span { + return values[1..4] +} + +fn spanTail(values: Span) -> Span { + return values[2..5] +} + +pub fn main(world: World) -> Void raises { + let values: [5]i32 = [10, 20, 30, 40, 50] + let span: Span = values + let start: usize = 1 + let end: usize = 4 + let fromArray: Span = values[start..end] + let fromSpan: Span = span[start..end] + let window: IntWindow = IntWindow { values: middle(span) } + if values[2] == 30 && second(span) == 20 && fromArray[0] == 20 && fromArray[2] == 40 && fromSpan[1] == 30 && window.values[2] == 40 && spanTail(span)[0] == 30 { + check world.out.write("generic spans ok\n") + } +} diff --git a/conformance/native/pass/generic-spans.graph b/conformance/native/pass/generic-spans.graph new file mode 100644 index 0000000..71f8ba2 Binary files /dev/null and b/conformance/native/pass/generic-spans.graph differ diff --git a/conformance/native/pass/generic-specialization-reuse.0 b/conformance/native/pass/generic-specialization-reuse.0 new file mode 100644 index 0000000..ed56f3c --- /dev/null +++ b/conformance/native/pass/generic-specialization-reuse.0 @@ -0,0 +1,13 @@ +fn identity(value: T) -> T { + return value +} + +pub fn main(world: World) -> Void raises { + let a: i32 = identity(1) + let b: i32 = identity(2) + if a + b == 3 { + check world.out.write("generic specialization reuse ok\n") + } else { + check world.out.write("generic specialization reuse broke\n") + } +} diff --git a/conformance/native/pass/generic-specialization-reuse.graph b/conformance/native/pass/generic-specialization-reuse.graph new file mode 100644 index 0000000..a4f8add Binary files /dev/null and b/conformance/native/pass/generic-specialization-reuse.graph differ diff --git a/conformance/native/pass/generic-static-array-specialization.0 b/conformance/native/pass/generic-static-array-specialization.0 new file mode 100644 index 0000000..16e6d78 --- /dev/null +++ b/conformance/native/pass/generic-static-array-specialization.0 @@ -0,0 +1,12 @@ +fn array_len() -> usize { + let bytes: [N]u8 = [1_u8; N] + return std.mem.len(bytes) +} + +pub fn main(world: World) -> Void raises { + if array_len<4>() == 4 { + check world.out.write("generic static array specialization ok\n") + } else { + check world.out.write("generic static array specialization broke\n") + } +} diff --git a/conformance/native/pass/generic-static-array-specialization.graph b/conformance/native/pass/generic-static-array-specialization.graph new file mode 100644 index 0000000..4dc4e6f Binary files /dev/null and b/conformance/native/pass/generic-static-array-specialization.graph differ diff --git a/conformance/native/pass/generic-static-forwarded-array-specialization.0 b/conformance/native/pass/generic-static-forwarded-array-specialization.0 new file mode 100644 index 0000000..b140330 --- /dev/null +++ b/conformance/native/pass/generic-static-forwarded-array-specialization.0 @@ -0,0 +1,16 @@ +fn inner() -> usize { + let bytes: [N]u8 = [1_u8; N] + return std.mem.len(bytes) +} + +fn outer() -> usize { + return inner() +} + +pub fn main(world: World) -> Void raises { + if outer<4>() == 4 { + check world.out.write("generic static forwarded array specialization ok\n") + } else { + check world.out.write("generic static forwarded array specialization broke\n") + } +} diff --git a/conformance/native/pass/generic-static-forwarded-array-specialization.graph b/conformance/native/pass/generic-static-forwarded-array-specialization.graph new file mode 100644 index 0000000..86ec7d7 Binary files /dev/null and b/conformance/native/pass/generic-static-forwarded-array-specialization.graph differ diff --git a/conformance/native/pass/index-reference-assignment-clears-origin.0 b/conformance/native/pass/index-reference-assignment-clears-origin.0 new file mode 100644 index 0000000..333455b --- /dev/null +++ b/conformance/native/pass/index-reference-assignment-clears-origin.0 @@ -0,0 +1,11 @@ +type Holder { + refs: [2]ref, +} + +pub fn main() -> Void { + var old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { refs: [&old, &next] } + holder.refs[0] = &next + old = 3 +} diff --git a/conformance/native/pass/index-reference-assignment-clears-origin.graph b/conformance/native/pass/index-reference-assignment-clears-origin.graph new file mode 100644 index 0000000..684f127 Binary files /dev/null and b/conformance/native/pass/index-reference-assignment-clears-origin.graph differ diff --git a/conformance/native/pass/indexed-mutation.0 b/conformance/native/pass/indexed-mutation.0 new file mode 100644 index 0000000..ced08ac --- /dev/null +++ b/conformance/native/pass/indexed-mutation.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + var bytes: [4]u8 = [65, 66, 67, 68] + var values: [3]i32 = [10, 20, 30] + let index: usize = 2 + bytes[1] = 90 + values[index] = 42 + if bytes[0] == 65 && bytes[1] == 90 && values[2] == 42 { + check world.out.write("indexed mutation ok\n") + } +} diff --git a/conformance/native/pass/indexed-mutation.graph b/conformance/native/pass/indexed-mutation.graph new file mode 100644 index 0000000..59dd1c5 Binary files /dev/null and b/conformance/native/pass/indexed-mutation.graph differ diff --git a/conformance/native/pass/indexing-primitives.0 b/conformance/native/pass/indexing-primitives.0 new file mode 100644 index 0000000..e5b4249 --- /dev/null +++ b/conformance/native/pass/indexing-primitives.0 @@ -0,0 +1,20 @@ +type BytesView { + bytes: Span, +} + +fn secondByte(bytes: Span) -> u8 { + return bytes[1] +} + +fn thirdArrayByte(bytes: [4]u8) -> u8 { + return bytes[2] +} + +pub fn main(world: World) -> Void raises { + let bytes: [4]u8 = [65, 66, 67, 68] + let index: usize = 3 + let view: BytesView = BytesView { bytes: std.mem.span("ABCD") } + if bytes[0] == 65 && secondByte(view.bytes) == 66 && thirdArrayByte(bytes) == 67 && view.bytes[index] == 68 { + check world.out.write("indexing primitives ok\n") + } +} diff --git a/conformance/native/pass/indexing-primitives.graph b/conformance/native/pass/indexing-primitives.graph new file mode 100644 index 0000000..9da40da Binary files /dev/null and b/conformance/native/pass/indexing-primitives.graph differ diff --git a/conformance/native/pass/integer-widths.0 b/conformance/native/pass/integer-widths.0 new file mode 100644 index 0000000..f23e761 --- /dev/null +++ b/conformance/native/pass/integer-widths.0 @@ -0,0 +1,21 @@ +type NumericBag { + tiny: u8, + word: u16, + count: u32, + huge: u64, + signed_tiny: i8, + signed_word: i16, + signed_count: i32, + signed_huge: i64, + size: usize, + offset: isize, +} + +pub fn main(world: World) -> Void raises { + let values: NumericBag = NumericBag { tiny: 255, word: 65535, count: 4294967295, huge: 18446744073709551615, signed_tiny: 127, signed_word: 32767, signed_count: 2147483647, signed_huge: 9223372036854775807, size: 42, offset: 7 } + let bytes: [3]u8 = [0, 127, 255] + let copied: usize = std.mem.len(bytes) + if values.tiny == 255 && values.word == 65535 && values.count > 0 && values.huge > 0 && values.signed_huge > 0 && copied == 3 { + check world.out.write("integer widths ok\n") + } +} diff --git a/conformance/native/pass/integer-widths.graph b/conformance/native/pass/integer-widths.graph new file mode 100644 index 0000000..0dbaf80 Binary files /dev/null and b/conformance/native/pass/integer-widths.graph differ diff --git a/conformance/native/pass/macho-large-byte-slice-blocked.0 b/conformance/native/pass/macho-large-byte-slice-blocked.0 new file mode 100644 index 0000000..7ec936b --- /dev/null +++ b/conformance/native/pass/macho-large-byte-slice-blocked.0 @@ -0,0 +1,4 @@ +pub fn main(world: World) -> Void raises { + let text: String = "abcdefghijklmnopqrstuvwxyz" + check world.out.write(text[5000..5001]) +} diff --git a/conformance/native/pass/macho-large-byte-slice-blocked.graph b/conformance/native/pass/macho-large-byte-slice-blocked.graph new file mode 100644 index 0000000..c75d957 Binary files /dev/null and b/conformance/native/pass/macho-large-byte-slice-blocked.graph differ diff --git a/conformance/native/pass/macho-nested-call-scratch-blocked.0 b/conformance/native/pass/macho-nested-call-scratch-blocked.0 new file mode 100644 index 0000000..c5e8d07 --- /dev/null +++ b/conformance/native/pass/macho-nested-call-scratch-blocked.0 @@ -0,0 +1,7 @@ +fn sum8(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) -> i32 { + return a + b + (c + d) + (e + f + (g + h)) +} + +export c fn main() -> i32 { + return sum8(sum8(sum8(sum8(1, 2, 3, 4, 5, 6, 7, 8), 2, 3, 4, 5, 6, 7, 8), 2, 3, 4, 5, 6, 7, 8), 2, 3, 4, 5, 6, 7, 8) +} diff --git a/conformance/native/pass/macho-nested-call-scratch-blocked.graph b/conformance/native/pass/macho-nested-call-scratch-blocked.graph new file mode 100644 index 0000000..374109a Binary files /dev/null and b/conformance/native/pass/macho-nested-call-scratch-blocked.graph differ diff --git a/conformance/native/pass/macho-open-byte-slice.0 b/conformance/native/pass/macho-open-byte-slice.0 new file mode 100644 index 0000000..267fcc0 --- /dev/null +++ b/conformance/native/pass/macho-open-byte-slice.0 @@ -0,0 +1,5 @@ +export c fn main() -> usize { + let text: String = "abcdef" + let suffix: Span = text[2..] + return std.mem.len(suffix) +} diff --git a/conformance/native/pass/macho-open-byte-slice.graph b/conformance/native/pass/macho-open-byte-slice.graph new file mode 100644 index 0000000..014fe82 Binary files /dev/null and b/conformance/native/pass/macho-open-byte-slice.graph differ diff --git a/conformance/native/pass/match-choice-fallback.0 b/conformance/native/pass/match-choice-fallback.0 new file mode 100644 index 0000000..109eecd --- /dev/null +++ b/conformance/native/pass/match-choice-fallback.0 @@ -0,0 +1,18 @@ +choice Event { + key: i32, + quit, +} + +pub fn main(world: World) -> Void raises { + let event: Event = Event.quit + match event { + .key(code) { + if code == 1 { + check world.out.write("unexpected key\n") + } + } + _ { + check world.out.write("choice fallback ok\n") + } + } +} diff --git a/conformance/native/pass/match-choice-fallback.graph b/conformance/native/pass/match-choice-fallback.graph new file mode 100644 index 0000000..d935df2 Binary files /dev/null and b/conformance/native/pass/match-choice-fallback.graph differ diff --git a/conformance/native/pass/match-fallback.0 b/conformance/native/pass/match-fallback.0 new file mode 100644 index 0000000..ec6b20e --- /dev/null +++ b/conformance/native/pass/match-fallback.0 @@ -0,0 +1,16 @@ +enum Mode { + fast, + slow, +} + +pub fn main(world: World) -> Void raises { + let mode: Mode = Mode.slow + match mode { + fast { + check world.out.write("match fast\n") + } + _ { + check world.out.write("match fallback ok\n") + } + } +} diff --git a/conformance/native/pass/match-fallback.graph b/conformance/native/pass/match-fallback.graph new file mode 100644 index 0000000..bfbae5e Binary files /dev/null and b/conformance/native/pass/match-fallback.graph differ diff --git a/conformance/native/pass/match-payload-binding.0 b/conformance/native/pass/match-payload-binding.0 new file mode 100644 index 0000000..73571bb --- /dev/null +++ b/conformance/native/pass/match-payload-binding.0 @@ -0,0 +1,18 @@ +choice Result { + ok: i32, + err: String, +} + +pub fn main(world: World) -> Void raises { + let result: Result = Result.ok(42) + match result { + .ok(value) { + if value == 42 { + check world.out.write("payload binding ok\n") + } + } + .err(message) { + check world.out.write(message) + } + } +} diff --git a/conformance/native/pass/match-payload-binding.graph b/conformance/native/pass/match-payload-binding.graph new file mode 100644 index 0000000..700a167 Binary files /dev/null and b/conformance/native/pass/match-payload-binding.graph differ diff --git a/conformance/native/pass/match-scalar-guards.0 b/conformance/native/pass/match-scalar-guards.0 new file mode 100644 index 0000000..2fb0918 --- /dev/null +++ b/conformance/native/pass/match-scalar-guards.0 @@ -0,0 +1,35 @@ +fn boolScore(value: Bool) -> i32 { + match value { + true { + return 1 + } + false { + return 2 + } + } + return 0 +} + +fn bucket(value: u8, enabled: Bool) -> i32 { + match value { + 0 if enabled { + return 10 + } + 0 { + return 11 + } + 1..3 { + return 20 + } + 4..255 { + return 30 + } + } + return 0 +} + +pub fn main(world: World) -> Void raises { + if boolScore(true) == 1 && boolScore(false) == 2 && bucket(0_u8, true) == 10 && bucket(0_u8, false) == 11 && bucket(2_u8, true) == 20 && bucket(9_u8, true) == 30 { + check world.out.write("match scalar guards ok\n") + } +} diff --git a/conformance/native/pass/match-scalar-guards.graph b/conformance/native/pass/match-scalar-guards.graph new file mode 100644 index 0000000..2d99ab1 Binary files /dev/null and b/conformance/native/pass/match-scalar-guards.graph differ diff --git a/conformance/native/pass/maybe-error-flow.0 b/conformance/native/pass/maybe-error-flow.0 new file mode 100644 index 0000000..104ff5e --- /dev/null +++ b/conformance/native/pass/maybe-error-flow.0 @@ -0,0 +1,26 @@ +fn maybeValue(bytes: Span, index: usize) -> Maybe raises { + check std.mem.get(bytes, index) + return std.mem.get(bytes, index) +} + +fn privateFail(flag: Bool) -> i32 raises { + if flag { + raise Hidden + } + return 7 +} + +fn namedFail(flag: Bool) -> i32 raises [Hidden] { + let value: i32 = check privateFail(flag) + return value +} + +pub fn main(world: World) -> Void raises { + let bytes: [1]u8 = [42] + let found: u8 = check maybeValue(bytes, 0) + let fallback: u8 = rescue maybeValue(bytes, 3) err 5_u8 + let recovered: i32 = rescue namedFail(true) err 9 + if found == 42_u8 && fallback == 5_u8 && recovered == 9 { + check world.out.write("maybe error flow ok\n") + } +} diff --git a/conformance/native/pass/maybe-error-flow.graph b/conformance/native/pass/maybe-error-flow.graph new file mode 100644 index 0000000..b9690a4 Binary files /dev/null and b/conformance/native/pass/maybe-error-flow.graph differ diff --git a/conformance/native/pass/maybe-guard-branch-restore.0 b/conformance/native/pass/maybe-guard-branch-restore.0 new file mode 100644 index 0000000..f67059a --- /dev/null +++ b/conformance/native/pass/maybe-guard-branch-restore.0 @@ -0,0 +1,16 @@ +fn read(flag: Bool) -> u8 { + let bytes: [1]u8 = [1] + var value: Maybe = std.mem.get(bytes, 0) + if value.has { + if flag { + value = null + return 0_u8 + } + return value.value + } + return 0_u8 +} + +pub fn main() -> Void { + let byte: u8 = read(false) +} diff --git a/conformance/native/pass/maybe-guard-branch-restore.graph b/conformance/native/pass/maybe-guard-branch-restore.graph new file mode 100644 index 0000000..9ef5f9b Binary files /dev/null and b/conformance/native/pass/maybe-guard-branch-restore.graph differ diff --git a/conformance/native/pass/maybe-guard-negated-conjunction.0 b/conformance/native/pass/maybe-guard-negated-conjunction.0 new file mode 100644 index 0000000..2d10e7c --- /dev/null +++ b/conformance/native/pass/maybe-guard-negated-conjunction.0 @@ -0,0 +1,23 @@ +fn guardedBoth() -> u8 { + let bytes: [2]u8 = [1, 2] + let first: Maybe = std.mem.get(bytes, 0) + let second: Maybe = std.mem.get(bytes, 1) + if !(first.has && second.has) { + return 0_u8 + } + return first.value + second.value +} + +fn guardedComparison() -> u8 { + let bytes: [1]u8 = [1] + let value: Maybe = std.mem.get(bytes, 0) + if (value.has && true) == true { + return value.value + } + return 0_u8 +} + +pub fn main() -> Void { + let both: u8 = guardedBoth() + let comparison: u8 = guardedComparison() +} diff --git a/conformance/native/pass/maybe-guard-negated-conjunction.graph b/conformance/native/pass/maybe-guard-negated-conjunction.graph new file mode 100644 index 0000000..19a1954 Binary files /dev/null and b/conformance/native/pass/maybe-guard-negated-conjunction.graph differ diff --git a/conformance/native/pass/maybe-guard-scalar-match.0 b/conformance/native/pass/maybe-guard-scalar-match.0 new file mode 100644 index 0000000..2e77034 --- /dev/null +++ b/conformance/native/pass/maybe-guard-scalar-match.0 @@ -0,0 +1,33 @@ +fn read(flag: Bool) -> u8 { + let bytes: [1]u8 = [1] + let value: Maybe = std.mem.get(bytes, 0) + match flag { + true if value.has { + return value.value + } + true { + return 0_u8 + } + false { + return 0_u8 + } + } +} + +fn readSubject() -> u8 { + let bytes: [1]u8 = [2] + let value: Maybe = std.mem.get(bytes, 0) + match value.has { + true { + return value.value + } + false { + return 0_u8 + } + } +} + +pub fn main() -> Void { + let byte: u8 = read(true) + let subject: u8 = readSubject() +} diff --git a/conformance/native/pass/maybe-guard-scalar-match.graph b/conformance/native/pass/maybe-guard-scalar-match.graph new file mode 100644 index 0000000..d32319a Binary files /dev/null and b/conformance/native/pass/maybe-guard-scalar-match.graph differ diff --git a/conformance/native/pass/maybe-guard-short-circuit-match.0 b/conformance/native/pass/maybe-guard-short-circuit-match.0 new file mode 100644 index 0000000..ee9137e --- /dev/null +++ b/conformance/native/pass/maybe-guard-short-circuit-match.0 @@ -0,0 +1,40 @@ +fn readOrGuard() -> Bool { + let bytes: [1]u8 = [1] + let value: Maybe = std.mem.get(bytes, 0) + return !value.has || value.value == 1_u8 +} + +fn readMatchGuard() -> u8 { + let bytes: [1]u8 = [1] + let value: Maybe = std.mem.get(bytes, 0) + match value.has { + true if value.value == 1_u8 { + return value.value + } + true { + return 0_u8 + } + false { + return 0_u8 + } + } +} + +fn readAfterMatch() -> u8 { + let bytes: [1]u8 = [1] + let value: Maybe = std.mem.get(bytes, 0) + match value.has { + false { + return 0_u8 + } + true { + } + } + return value.value +} + +pub fn main() -> Void { + let ok: Bool = readOrGuard() + let guarded: u8 = readMatchGuard() + let after: u8 = readAfterMatch() +} diff --git a/conformance/native/pass/maybe-guard-short-circuit-match.graph b/conformance/native/pass/maybe-guard-short-circuit-match.graph new file mode 100644 index 0000000..8dcad9f Binary files /dev/null and b/conformance/native/pass/maybe-guard-short-circuit-match.graph differ diff --git a/conformance/native/pass/maybe-guard-static-index-literals.0 b/conformance/native/pass/maybe-guard-static-index-literals.0 new file mode 100644 index 0000000..412df6e --- /dev/null +++ b/conformance/native/pass/maybe-guard-static-index-literals.0 @@ -0,0 +1,7 @@ +pub fn main() -> Void { + let input: [1]u8 = [1] + let values: [1]Maybe = [std.mem.get(input, 0)] + if values[0_usize].has { + let byte: u8 = values[0_usize].value + } +} diff --git a/conformance/native/pass/maybe-guard-static-index-literals.graph b/conformance/native/pass/maybe-guard-static-index-literals.graph new file mode 100644 index 0000000..e850f07 Binary files /dev/null and b/conformance/native/pass/maybe-guard-static-index-literals.graph differ diff --git a/conformance/native/pass/maybe-guard-variant-guard-side-effect.0 b/conformance/native/pass/maybe-guard-variant-guard-side-effect.0 new file mode 100644 index 0000000..d8a3cc1 --- /dev/null +++ b/conformance/native/pass/maybe-guard-variant-guard-side-effect.0 @@ -0,0 +1,36 @@ +choice Flag { + yes, + no, +} + +type Holder { + value: Maybe, +} + +fn clear(holder: mutref) -> Bool { + holder.value = null + return true +} + +fn read(flag: Flag) -> u8 { + let input: [1]u8 = [1] + var holder: Holder = Holder { value: std.mem.get(input, 0) } + if holder.value.has { + match flag { + .yes if clear(&mut holder) { + return 0_u8 + } + .yes { + return 0_u8 + } + .no { + return holder.value.value + } + } + } + return 0_u8 +} + +pub fn main() -> Void { + let byte: u8 = read(Flag.no) +} diff --git a/conformance/native/pass/maybe-guard-variant-guard-side-effect.graph b/conformance/native/pass/maybe-guard-variant-guard-side-effect.graph new file mode 100644 index 0000000..0481746 Binary files /dev/null and b/conformance/native/pass/maybe-guard-variant-guard-side-effect.graph differ diff --git a/conformance/native/pass/maybe-guard-variant-match-after.0 b/conformance/native/pass/maybe-guard-variant-match-after.0 new file mode 100644 index 0000000..0315eaf --- /dev/null +++ b/conformance/native/pass/maybe-guard-variant-match-after.0 @@ -0,0 +1,24 @@ +choice Flag { + yes, + no, +} + +fn read(flag: Flag) -> u8 { + let input: [1]u8 = [1] + let value: Maybe = std.mem.get(input, 0) + match flag { + .yes if value.has { + } + .yes { + return 0_u8 + } + .no { + return 0_u8 + } + } + return value.value +} + +pub fn main() -> Void { + let byte: u8 = read(Flag.yes) +} diff --git a/conformance/native/pass/maybe-guard-variant-match-after.graph b/conformance/native/pass/maybe-guard-variant-match-after.graph new file mode 100644 index 0000000..ec50cda Binary files /dev/null and b/conformance/native/pass/maybe-guard-variant-match-after.graph differ diff --git a/conformance/native/pass/maybe-local-null-init-return.0 b/conformance/native/pass/maybe-local-null-init-return.0 new file mode 100644 index 0000000..6613a03 --- /dev/null +++ b/conformance/native/pass/maybe-local-null-init-return.0 @@ -0,0 +1,35 @@ +fn pickUsize(input: String, flag: Bool) -> Maybe { + var result: Maybe = null + if flag { + result = std.parse.parseUsize(input) + } + return result +} + +fn pickU32(flag: Bool) -> Maybe { + var result: Maybe = null + if flag { + result = std.math.checkedAddU32(2, 3) + } + let copied: Maybe = result + return copied +} + +pub fn main(world: World) -> Void raises { + let found: Maybe = pickUsize("42", true) + if found.has { + if found.value == 42 { + check world.out.write("maybe local found 42\n") + } + } + let missed: Maybe = pickUsize("42", false) + if !missed.has { + check world.out.write("maybe local none\n") + } + let summed: Maybe = pickU32(true) + if summed.has { + if summed.value == 5 { + check world.out.write("maybe local sum 5\n") + } + } +} diff --git a/conformance/native/pass/maybe-local-null-init-return.graph b/conformance/native/pass/maybe-local-null-init-return.graph new file mode 100644 index 0000000..3fab376 Binary files /dev/null and b/conformance/native/pass/maybe-local-null-init-return.graph differ diff --git a/conformance/native/pass/maybe-owned-null-repeat.0 b/conformance/native/pass/maybe-owned-null-repeat.0 new file mode 100644 index 0000000..e5c1ac6 --- /dev/null +++ b/conformance/native/pass/maybe-owned-null-repeat.0 @@ -0,0 +1,7 @@ +type File { + fd: i32, +} + +pub fn main() -> Void { + let files: [2]Maybe> = [null; 2] +} diff --git a/conformance/native/pass/maybe-owned-null-repeat.graph b/conformance/native/pass/maybe-owned-null-repeat.graph new file mode 100644 index 0000000..1070b25 Binary files /dev/null and b/conformance/native/pass/maybe-owned-null-repeat.graph differ diff --git a/conformance/native/pass/memory-types.0 b/conformance/native/pass/memory-types.0 new file mode 100644 index 0000000..322714a --- /dev/null +++ b/conformance/native/pass/memory-types.0 @@ -0,0 +1,12 @@ +type BufferView { + bytes: Span, +} + +pub fn main(world: World) -> Void raises { + let left: Span = std.mem.span("memory") + let right: Span = std.mem.span("memory") + let view: BufferView = BufferView { bytes: left } + if std.mem.len(view.bytes) == 6 && std.mem.eqlBytes(view.bytes, right) { + check world.out.write("native memory types") + } +} diff --git a/conformance/native/pass/memory-types.graph b/conformance/native/pass/memory-types.graph new file mode 100644 index 0000000..2b2c2ea Binary files /dev/null and b/conformance/native/pass/memory-types.graph differ diff --git a/conformance/native/pass/meta-typed-target-type.0 b/conformance/native/pass/meta-typed-target-type.0 new file mode 100644 index 0000000..a893c74 --- /dev/null +++ b/conformance/native/pass/meta-typed-target-type.0 @@ -0,0 +1,25 @@ +const computed: usize = meta 2 + 2 + +const host_os: String = meta target.os + +const pointer_width: usize = meta target.pointerWidth + +const pointer_width_again: usize = meta target.pointerWidth + +const has_fs: Bool = meta target.hasCapability("fs") + +const point_fields: usize = meta fieldCount(Point) + +const has_y: Bool = meta hasField(Point, "y") + +type Point { + x: i32, + y: i32, +} + +pub fn main(world: World) -> Void raises { + if computed == 4 && pointer_width == 64 && pointer_width_again == pointer_width && has_fs && point_fields == 2 && has_y { + check world.out.write(host_os) + check world.out.write(" meta ok\n") + } +} diff --git a/conformance/native/pass/meta-typed-target-type.graph b/conformance/native/pass/meta-typed-target-type.graph new file mode 100644 index 0000000..9d31d89 Binary files /dev/null and b/conformance/native/pass/meta-typed-target-type.graph differ diff --git a/conformance/native/pass/mutable-spans.0 b/conformance/native/pass/mutable-spans.0 new file mode 100644 index 0000000..8cfdd69 --- /dev/null +++ b/conformance/native/pass/mutable-spans.0 @@ -0,0 +1,22 @@ +fn setLast(values: MutSpan) -> Void { + values[2] = 30 +} + +fn second(values: Span) -> i32 { + return values[1] +} + +pub fn main(world: World) -> Void raises { + var values: [3]i32 = [1, 2, 3] + let expected: [3]i32 = [1, 20, 30] + let expectedSpan: Span = expected + let span: MutSpan = values + let readonly: Span = span + var bytes: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + span[1] = 20 + setLast(span) + let readCount: usize = std.fs.read("conformance/fixtures/std-fs-read.txt", bytes) + if values[1] == 20 && values[2] == 30 && second(span) == 20 && std.mem.len(span) == 3 && std.mem.eqlBytes(span, expectedSpan) && readonly[2] == 30 && readCount > 0 { + check world.out.write("mutable spans ok\n") + } +} diff --git a/conformance/native/pass/mutable-spans.graph b/conformance/native/pass/mutable-spans.graph new file mode 100644 index 0000000..28babce Binary files /dev/null and b/conformance/native/pass/mutable-spans.graph differ diff --git a/conformance/native/pass/mutref-alias-assignment-clears-old-origin.0 b/conformance/native/pass/mutref-alias-assignment-clears-old-origin.0 new file mode 100644 index 0000000..54efdff --- /dev/null +++ b/conformance/native/pass/mutref-alias-assignment-clears-old-origin.0 @@ -0,0 +1,14 @@ +type Holder { + value: ref, +} + +pub fn main() -> Void { + let next: i32 = 1 + var old: i32 = 2 + var holder: Holder = Holder { value: &old } + if true { + let selected: mutref = &mut holder + selected.value = &next + } + old = 3 +} diff --git a/conformance/native/pass/mutref-alias-assignment-clears-old-origin.graph b/conformance/native/pass/mutref-alias-assignment-clears-old-origin.graph new file mode 100644 index 0000000..c7d8007 Binary files /dev/null and b/conformance/native/pass/mutref-alias-assignment-clears-old-origin.graph differ diff --git a/conformance/native/pass/mutref-alias-assignment-same-origin.0 b/conformance/native/pass/mutref-alias-assignment-same-origin.0 new file mode 100644 index 0000000..8fffebc --- /dev/null +++ b/conformance/native/pass/mutref-alias-assignment-same-origin.0 @@ -0,0 +1,10 @@ +type Holder { + value: mutref, +} + +pub fn main() -> Void { + var x: i32 = 1 + var holder: Holder = Holder { value: &mut x } + let selected: mutref = &mut holder + selected.value = &mut x +} diff --git a/conformance/native/pass/mutref-alias-assignment-same-origin.graph b/conformance/native/pass/mutref-alias-assignment-same-origin.graph new file mode 100644 index 0000000..7b9eb08 Binary files /dev/null and b/conformance/native/pass/mutref-alias-assignment-same-origin.graph differ diff --git a/conformance/native/pass/mutref-indexed-lvalues.0 b/conformance/native/pass/mutref-indexed-lvalues.0 new file mode 100644 index 0000000..13d8fe7 --- /dev/null +++ b/conformance/native/pass/mutref-indexed-lvalues.0 @@ -0,0 +1,18 @@ +fn write_first(bytes: mutref>, value: u8) -> Void { + bytes[0] = value +} + +fn read_first(bytes: ref>) -> u8 { + return bytes[0] +} + +pub fn main(world: World) -> Void raises { + var backing: [3]u8 = [0, 0, 0] + var bytes: MutSpan = backing + write_first(&mut bytes, 65_u8) + if read_first(&bytes) == 65_u8 && backing[0] == 65_u8 { + check world.out.write("mutref indexed lvalues ok\n") + } else { + check world.out.write("mutref indexed lvalues failed\n") + } +} diff --git a/conformance/native/pass/mutref-indexed-lvalues.graph b/conformance/native/pass/mutref-indexed-lvalues.graph new file mode 100644 index 0000000..03f001a Binary files /dev/null and b/conformance/native/pass/mutref-indexed-lvalues.graph differ diff --git a/conformance/native/pass/mutref-shape-param-nested.0 b/conformance/native/pass/mutref-shape-param-nested.0 new file mode 100644 index 0000000..1c850fb --- /dev/null +++ b/conformance/native/pass/mutref-shape-param-nested.0 @@ -0,0 +1,55 @@ +type Stats { + count: u32, + total: i64, + flag: Bool, + bytes: [8]u8, +} + +type Box { + item: T, + extra: u32, +} + +fn inner(s: mutref) -> Void { + s.count = s.count + 1_u32 + s.total = s.total + 10 +} + +fn fill(s: mutref) -> usize { + return std.mem.copy(s.bytes, std.mem.span("zero")) +} + +fn outer(s: mutref) -> Void { + inner(s) + s.flag = true +} + +fn bumpBox(b: mutref>) -> Void { + b.extra = b.extra + 1_u32 +} + +pub fn main(world: World) -> Void raises { + var st: Stats = Stats { count: 1_u32, total: 5, flag: false, bytes: [0_u8; 8] } + outer(&mut st) + let copied: usize = fill(&mut st) + if st.count == 2_u32 { + check world.out.write("nested count ok\n") + } + if st.total == 15 { + check world.out.write("nested total ok\n") + } + if st.flag { + check world.out.write("nested flag ok\n") + } + if copied == 4 { + check world.out.write("nested copy ok\n") + } + if st.bytes[1] == 101_u8 { + check world.out.write("nested bytes ok\n") + } + var bi: Box = Box { item: 5, extra: 1_u32 } + bumpBox(&mut bi) + if bi.extra == 2_u32 { + check world.out.write("generic mutref ok\n") + } +} diff --git a/conformance/native/pass/mutref-shape-param-nested.graph b/conformance/native/pass/mutref-shape-param-nested.graph new file mode 100644 index 0000000..a747837 Binary files /dev/null and b/conformance/native/pass/mutref-shape-param-nested.graph differ diff --git a/conformance/native/pass/mutref-shape-param.0 b/conformance/native/pass/mutref-shape-param.0 new file mode 100644 index 0000000..9f6b73f --- /dev/null +++ b/conformance/native/pass/mutref-shape-param.0 @@ -0,0 +1,27 @@ +type Point { + x: i32, + y: i32, +} + +fn bump(p: mutref) -> Void { + p.x = p.x + 1 + p.y = p.y + 2 +} + +fn sum(p: ref) -> i32 { + return p.x + p.y +} + +pub fn main(world: World) -> Void raises { + var pt: Point = Point { x: 1, y: 2 } + bump(&mut pt) + if pt.x == 2 { + check world.out.write("mutref x ok\n") + } + if pt.y == 4 { + check world.out.write("mutref y ok\n") + } + if sum(&pt) == 6 { + check world.out.write("ref sum ok\n") + } +} diff --git a/conformance/native/pass/mutref-shape-param.graph b/conformance/native/pass/mutref-shape-param.graph new file mode 100644 index 0000000..96161cc Binary files /dev/null and b/conformance/native/pass/mutref-shape-param.graph differ diff --git a/conformance/native/pass/mutual-recursion.0 b/conformance/native/pass/mutual-recursion.0 new file mode 100644 index 0000000..754c6a3 --- /dev/null +++ b/conformance/native/pass/mutual-recursion.0 @@ -0,0 +1,31 @@ +fn isEven(n: usize) -> Bool { + if n == 0 { + return true + } + let odd: Bool = isOdd(n - 1) + return odd +} + +fn isOdd(n: usize) -> Bool { + if n == 0 { + return false + } + let even: Bool = isEven(n - 1) + return even +} + +fn checkEven(n: usize) -> Bool { + let even: Bool = isEven(n) + return even +} + +fn checkOdd(n: usize) -> Bool { + let odd: Bool = isOdd(n) + return odd +} + +pub fn main(world: World) -> Void raises { + if checkEven(8) && checkOdd(7) && !isEven(3) { + check world.out.write("mutual recursion ok\n") + } +} diff --git a/conformance/native/pass/mutual-recursion.graph b/conformance/native/pass/mutual-recursion.graph new file mode 100644 index 0000000..b018e2f Binary files /dev/null and b/conformance/native/pass/mutual-recursion.graph differ diff --git a/conformance/native/pass/nested-break-continue.0 b/conformance/native/pass/nested-break-continue.0 new file mode 100644 index 0000000..7fb863f --- /dev/null +++ b/conformance/native/pass/nested-break-continue.0 @@ -0,0 +1,25 @@ +pub fn main(world: World) -> Void raises { + var outer: i32 = 0 + while outer < 3 { + outer = outer + 1 + if outer == 2 { + continue + } + var inner: i32 = 0 + while inner < 5 { + inner = inner + 1 + if inner == 2 { + continue + } + if inner == 4 { + break + } + check world.out.write("inner tick\n") + } + if outer == 3 { + break + } + check world.out.write("outer tick\n") + } + check world.out.write("nested break continue ok\n") +} diff --git a/conformance/native/pass/nested-break-continue.graph b/conformance/native/pass/nested-break-continue.graph new file mode 100644 index 0000000..aa1e410 Binary files /dev/null and b/conformance/native/pass/nested-break-continue.graph differ diff --git a/conformance/native/pass/nested-lvalues.0 b/conformance/native/pass/nested-lvalues.0 new file mode 100644 index 0000000..5cf0dd7 --- /dev/null +++ b/conformance/native/pass/nested-lvalues.0 @@ -0,0 +1,27 @@ +type Point { + x: i32, + y: i32, +} + +type Line { + start: Point, + end: Point, +} + +type Cell { + value: i32, +} + +pub fn main(world: World) -> Void raises { + var point: Point = Point { x: 1, y: 2 } + var line: Line = Line { start: Point { x: 3, y: 4 }, end: Point { x: 5, y: 6 } } + var cells: [2]Cell = [Cell { value: 7 }, Cell { value: 8 }] + var grid: [2][2]i32 = [[1, 2], [3, 4]] + point.x = 10 + line.end.y = 20 + cells[1].value = 30 + grid[0][1] = 40 + if point.x == 10 && line.end.y == 20 && cells[1].value == 30 && grid[0][1] == 40 { + check world.out.write("nested lvalues ok\n") + } +} diff --git a/conformance/native/pass/nested-lvalues.graph b/conformance/native/pass/nested-lvalues.graph new file mode 100644 index 0000000..eab3d84 Binary files /dev/null and b/conformance/native/pass/nested-lvalues.graph differ diff --git a/conformance/native/pass/null-maybe.0 b/conformance/native/pass/null-maybe.0 new file mode 100644 index 0000000..1174900 --- /dev/null +++ b/conformance/native/pass/null-maybe.0 @@ -0,0 +1,10 @@ +type Holder { + value: Maybe, +} + +pub fn main(world: World) -> Void raises { + let holder: Holder = Holder { value: null } + if !holder.value.has { + check world.out.write("null maybe ok\n") + } +} diff --git a/conformance/native/pass/null-maybe.graph b/conformance/native/pass/null-maybe.graph new file mode 100644 index 0000000..4513010 Binary files /dev/null and b/conformance/native/pass/null-maybe.graph differ diff --git a/conformance/native/pass/open-ended-slices.0 b/conformance/native/pass/open-ended-slices.0 new file mode 100644 index 0000000..745e5c6 --- /dev/null +++ b/conformance/native/pass/open-ended-slices.0 @@ -0,0 +1,31 @@ +fn bytesTail(bytes: Span) -> Span { + return bytes[2..] +} + +fn bytesPrefix(bytes: Span) -> Span { + return bytes[..3] +} + +fn intWhole(values: Span) -> Span { + return values[..] +} + +fn intTail(values: Span) -> Span { + return values[2..] +} + +pub fn main(world: World) -> Void raises { + let bytes: [5]u8 = [65, 66, 67, 68, 69] + let bytesSpan: Span = std.mem.span("ABCDE") + let tail: Span = bytes[2..] + let prefix: Span = bytesSpan[..3] + let whole: Span = bytesSpan[..] + let values: [5]i32 = [10, 20, 30, 40, 50] + let numbers: Span = values + let intPrefix: Span = values[..3] + let intTailView: Span = numbers[2..] + let intWholeView: Span = numbers[..] + if std.mem.len(tail) == 3 && tail[0] == 67 && tail[2] == 69 && std.mem.len(prefix) == 3 && prefix[2] == 67 && std.mem.len(whole) == 5 && whole[4] == 69 && bytesTail(bytesSpan)[1] == 68 && bytesPrefix(bytesSpan)[0] == 65 && intPrefix[2] == 30 && intTailView[0] == 30 && intTailView[2] == 50 && intWholeView[4] == 50 && intWhole(numbers)[3] == 40 && intTail(numbers)[1] == 40 { + check world.out.write("open ended slices ok\n") + } +} diff --git a/conformance/native/pass/open-ended-slices.graph b/conformance/native/pass/open-ended-slices.graph new file mode 100644 index 0000000..a672a47 Binary files /dev/null and b/conformance/native/pass/open-ended-slices.graph differ diff --git a/conformance/native/pass/owned-array-static-index-reassignment.0 b/conformance/native/pass/owned-array-static-index-reassignment.0 new file mode 100644 index 0000000..c468d5e --- /dev/null +++ b/conformance/native/pass/owned-array-static-index-reassignment.0 @@ -0,0 +1,13 @@ +type File { + fd: i32, +} + +fn close(file: owned) -> Void { +} + +pub fn main() -> Void { + var files: [1]owned = [File { fd: 1 }] + close(files[0_usize]) + files[0_usize] = File { fd: 2 } + close(files[0_usize]) +} diff --git a/conformance/native/pass/owned-array-static-index-reassignment.graph b/conformance/native/pass/owned-array-static-index-reassignment.graph new file mode 100644 index 0000000..32e5112 Binary files /dev/null and b/conformance/native/pass/owned-array-static-index-reassignment.graph differ diff --git a/conformance/native/pass/owned-byte-buffer.0 b/conformance/native/pass/owned-byte-buffer.0 new file mode 100644 index 0000000..b3d7456 --- /dev/null +++ b/conformance/native/pass/owned-byte-buffer.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + var storage: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + let maybe: Maybe> = std.mem.byteBuf(alloc, 4) + if !maybe.has { + check world.out.write("owned byte buffer failed\n") + return + } + let buf: owned = maybe.value + let bytes: MutSpan = std.mem.bufBytes(&buf) + bytes[0] = 90_u8 + if std.mem.bufLen(&buf) == 4 && storage[0] == 90_u8 { + check world.out.write("owned byte buffer ok\n") + } +} diff --git a/conformance/native/pass/owned-byte-buffer.graph b/conformance/native/pass/owned-byte-buffer.graph new file mode 100644 index 0000000..a38ced6 Binary files /dev/null and b/conformance/native/pass/owned-byte-buffer.graph differ diff --git a/conformance/native/pass/owned-drop-cleanup.0 b/conformance/native/pass/owned-drop-cleanup.0 new file mode 100644 index 0000000..dcdd490 --- /dev/null +++ b/conformance/native/pass/owned-drop-cleanup.0 @@ -0,0 +1,18 @@ +type DropProbe { + marker: MutSpan, + fn drop(self: mutref) -> Void { + self.marker[0] = 1 + } +} + +pub fn main(world: World) -> Void raises { + var marker: [1]u8 = [0] + if true { + let probe: owned = DropProbe { marker: marker } + } + if marker[0] == 1 { + check world.out.write("owned drop cleanup ok\n") + } else { + check world.out.write("owned drop cleanup failed\n") + } +} diff --git a/conformance/native/pass/owned-drop-cleanup.graph b/conformance/native/pass/owned-drop-cleanup.graph new file mode 100644 index 0000000..d030a59 Binary files /dev/null and b/conformance/native/pass/owned-drop-cleanup.graph differ diff --git a/conformance/native/pass/owned-drop-move-suppressed.0 b/conformance/native/pass/owned-drop-move-suppressed.0 new file mode 100644 index 0000000..848a8d0 --- /dev/null +++ b/conformance/native/pass/owned-drop-move-suppressed.0 @@ -0,0 +1,22 @@ +type DropProbe { + marker: MutSpan, + fn drop(self: mutref) -> Void { + self.marker[0] = 1 + } +} + +fn consume(probe: owned) -> Void { +} + +pub fn main(world: World) -> Void raises { + var marker: [1]u8 = [0] + if true { + let probe: owned = DropProbe { marker: marker } + consume(probe) + } + if marker[0] == 0 { + check world.out.write("owned drop move suppressed ok\n") + } else { + check world.out.write("owned drop move suppressed failed\n") + } +} diff --git a/conformance/native/pass/owned-drop-move-suppressed.graph b/conformance/native/pass/owned-drop-move-suppressed.graph new file mode 100644 index 0000000..81d4fc4 Binary files /dev/null and b/conformance/native/pass/owned-drop-move-suppressed.graph differ diff --git a/conformance/native/pass/owned-field-move-return-branch.0 b/conformance/native/pass/owned-field-move-return-branch.0 new file mode 100644 index 0000000..687bd56 --- /dev/null +++ b/conformance/native/pass/owned-field-move-return-branch.0 @@ -0,0 +1,24 @@ +type File { + fd: i32, +} + +type Holder { + file: owned, +} + +fn close(file: owned) -> Void { +} + +fn closeAfterBranch(flag: Bool) -> Void { + let file: owned = File { fd: 1 } + let holder: Holder = Holder { file: file } + if flag { + close(holder.file) + return + } + close(holder.file) +} + +pub fn main() -> Void { + closeAfterBranch(false) +} diff --git a/conformance/native/pass/owned-field-move-return-branch.graph b/conformance/native/pass/owned-field-move-return-branch.graph new file mode 100644 index 0000000..f82d055 Binary files /dev/null and b/conformance/native/pass/owned-field-move-return-branch.graph differ diff --git a/conformance/native/pass/owned-field-reassignment-after-move.0 b/conformance/native/pass/owned-field-reassignment-after-move.0 new file mode 100644 index 0000000..1d8c322 --- /dev/null +++ b/conformance/native/pass/owned-field-reassignment-after-move.0 @@ -0,0 +1,19 @@ +type File { + fd: i32, +} + +type Holder { + file: owned, + tag: u8, +} + +fn close(file: owned) -> Void { +} + +pub fn main() -> Void { + var holder: Holder = Holder { file: File { fd: 1 }, tag: 1_u8 } + close(holder.file) + holder.tag = 2_u8 + holder.file = File { fd: 2 } + close(holder.file) +} diff --git a/conformance/native/pass/owned-field-reassignment-after-move.graph b/conformance/native/pass/owned-field-reassignment-after-move.graph new file mode 100644 index 0000000..0159cc6 Binary files /dev/null and b/conformance/native/pass/owned-field-reassignment-after-move.graph differ diff --git a/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.0 b/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.0 new file mode 100644 index 0000000..50466be --- /dev/null +++ b/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.0 @@ -0,0 +1,14 @@ +type File { + fd: i32, +} + +fn close(file: owned) -> Void { +} + +pub fn main() -> Void { + var files: [1]owned = [File { fd: 1 }] + let view: MutSpan> = files + close(files[0]) + view[0] = File { fd: 2 } + close(files[0]) +} diff --git a/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.graph b/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.graph new file mode 100644 index 0000000..b25a7a6 Binary files /dev/null and b/conformance/native/pass/owned-mutspan-alias-reassignment-after-move.graph differ diff --git a/conformance/native/pass/owned-transfer.0 b/conformance/native/pass/owned-transfer.0 new file mode 100644 index 0000000..c9e1d01 --- /dev/null +++ b/conformance/native/pass/owned-transfer.0 @@ -0,0 +1,32 @@ +type File { + fd: i32, +} + +fn make(fd: i32) -> owned { + return File { fd: fd } +} + +fn pass(file: owned) -> owned { + return file +} + +fn close(file: owned) -> Void { +} + +fn fd(file: owned) -> i32 { + return file.fd +} + +pub fn main(world: World) -> Void raises { + var current: owned = make(1) + let next: owned = pass(make(42)) + current = next + let value: i32 = fd(current) + let cleanup: owned = make(7) + defer close(cleanup) + if value == 42 { + check world.out.write("owned transfer ok\n") + } else { + check world.out.write("owned transfer failed\n") + } +} diff --git a/conformance/native/pass/owned-transfer.graph b/conformance/native/pass/owned-transfer.graph new file mode 100644 index 0000000..63b04bb Binary files /dev/null and b/conformance/native/pass/owned-transfer.graph differ diff --git a/conformance/native/pass/params.0 b/conformance/native/pass/params.0 new file mode 100644 index 0000000..0bb7ae9 --- /dev/null +++ b/conformance/native/pass/params.0 @@ -0,0 +1,12 @@ +fn add(left: i32, right: i32) -> i32 { + return left + right +} + +pub fn main(world: World) -> Void raises { + let value: i32 = add(40, 2) + if value == 42 { + check world.out.write("params work\n") + } else { + check world.out.write("params broke\n") + } +} diff --git a/conformance/native/pass/params.graph b/conformance/native/pass/params.graph new file mode 100644 index 0000000..5349afb Binary files /dev/null and b/conformance/native/pass/params.graph differ diff --git a/conformance/native/pass/parse-integers.0 b/conformance/native/pass/parse-integers.0 new file mode 100644 index 0000000..df00629 --- /dev/null +++ b/conformance/native/pass/parse-integers.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let byte: Maybe = std.parse.parseU8("255") + let too_big_byte: Maybe = std.parse.parseU8("256") + let port: Maybe = std.parse.parseU16("8080") + let count: Maybe = std.parse.parseU32("4294967295") + let trailing: Maybe = std.parse.parseU32("12x") + let alpha: Bool = std.parse.isAsciiAlpha("A") + let whitespace: Bool = std.parse.isWhitespace(" ") + let ident_len: usize = std.parse.scanIdentifier("name_1!") + if byte.has && byte.value == 255 && !too_big_byte.has && port.has && port.value == 8080 && count.has && count.value == 4294967295_u32 && !trailing.has && alpha && whitespace && ident_len == 6 { + check world.out.write("parse integers ok\n") + } +} diff --git a/conformance/native/pass/parse-integers.graph b/conformance/native/pass/parse-integers.graph new file mode 100644 index 0000000..7eff961 Binary files /dev/null and b/conformance/native/pass/parse-integers.graph differ diff --git a/conformance/native/pass/payload-match.0 b/conformance/native/pass/payload-match.0 new file mode 100644 index 0000000..4df7d6c --- /dev/null +++ b/conformance/native/pass/payload-match.0 @@ -0,0 +1,16 @@ +choice Result { + ok: i32, + err: String, +} + +pub fn main(world: World) -> Void raises { + let result: Result = Result.ok(42) + match result { + ok { + check world.out.write("native payload match\n") + } + err { + check world.out.write("unexpected\n") + } + } +} diff --git a/conformance/native/pass/payload-match.graph b/conformance/native/pass/payload-match.graph new file mode 100644 index 0000000..2a11a69 Binary files /dev/null and b/conformance/native/pass/payload-match.graph differ diff --git a/conformance/native/pass/primitive-stdlib.0 b/conformance/native/pass/primitive-stdlib.0 new file mode 100644 index 0000000..d94f4c7 --- /dev/null +++ b/conformance/native/pass/primitive-stdlib.0 @@ -0,0 +1,11 @@ +use std.codec + +use std.parse + +pub fn main(world: World) -> Void raises { + let len: usize = std.codec.encodedVarintLen(300) + let digit: Bool = std.parse.isAsciiDigit("7") + if len == 2 && digit { + check world.out.write("native primitive std") + } +} diff --git a/conformance/native/pass/primitive-stdlib.graph b/conformance/native/pass/primitive-stdlib.graph new file mode 100644 index 0000000..7dafc4d Binary files /dev/null and b/conformance/native/pass/primitive-stdlib.graph differ diff --git a/conformance/native/pass/radix-suffix-literals.0 b/conformance/native/pass/radix-suffix-literals.0 new file mode 100644 index 0000000..df2eb34 --- /dev/null +++ b/conformance/native/pass/radix-suffix-literals.0 @@ -0,0 +1,27 @@ +type LiteralBag { + byte: u8, + word: u16, + size: usize, + offset: isize, +} + +fn useWord(value: u16) -> u16 { + return value +} + +fn returnSize() -> usize { + return 0x20_usize +} + +pub fn main(world: World) -> Void raises { + let hex: u8 = 0xff_u8 + let binary: u16 = 0b1010_u16 + let octal: i32 = 0o77_i32 + let separated: usize = 1_024_usize + let defaulted: i32 = 0b1010 + let bytes: [3]u8 = [0x1_u8, 0b10_u8, 0o3_u8] + let bag: LiteralBag = LiteralBag { byte: 0x2a_u8, word: 0b101010_u16, size: 0o52_usize, offset: 42_isize } + if hex == 255 && binary == 10 && octal == 63 && separated == 1024 && defaulted == 10 && bag.byte == 42 && useWord(0xff_u16) == 255 && returnSize() == 32 { + check world.out.write("radix suffix literals ok\n") + } +} diff --git a/conformance/native/pass/radix-suffix-literals.graph b/conformance/native/pass/radix-suffix-literals.graph new file mode 100644 index 0000000..bc06e95 Binary files /dev/null and b/conformance/native/pass/radix-suffix-literals.graph differ diff --git a/conformance/native/pass/range-slices.0 b/conformance/native/pass/range-slices.0 new file mode 100644 index 0000000..cdf57cb --- /dev/null +++ b/conformance/native/pass/range-slices.0 @@ -0,0 +1,24 @@ +type Window { + bytes: Span, +} + +fn middle(bytes: Span) -> Span { + return bytes[1..3] +} + +fn spanWindow(bytes: Span) -> Span { + return bytes[1..4] +} + +pub fn main(world: World) -> Void raises { + let bytes: [5]u8 = [65, 66, 67, 68, 69] + let start: usize = 1 + let end: usize = 4 + let arraySlice: Span = bytes[start..end] + let span: Span = std.mem.span("ABCDE") + let spanSlice: Span = span[1..4] + let window: Window = Window { bytes: middle(span) } + if std.mem.len(arraySlice) == 3 && arraySlice[0] == 66 && std.mem.len(spanSlice) == 3 && spanSlice[2] == 68 && std.mem.len(window.bytes) == 2 && window.bytes[1] == 67 && spanWindow(span)[2] == 68 { + check world.out.write("range slices ok\n") + } +} diff --git a/conformance/native/pass/range-slices.graph b/conformance/native/pass/range-slices.graph new file mode 100644 index 0000000..7067e95 Binary files /dev/null and b/conformance/native/pass/range-slices.graph differ diff --git a/conformance/native/pass/receiver-method-calls.0 b/conformance/native/pass/receiver-method-calls.0 new file mode 100644 index 0000000..2c7f460 --- /dev/null +++ b/conformance/native/pass/receiver-method-calls.0 @@ -0,0 +1,28 @@ +type FixedVec { + len: usize, + items: [N]T, + fn push(self: mutref, value: T) -> Void raises [Full] { + if self.len == N { + raise Full + } + self.items[self.len] = value + self.len = self.len + 1 + } + fn get(self: ref, index: usize) -> Maybe { + return std.mem.get(self.items, index) + } + fn count(self: ref) -> usize { + return self.len + } +} + +pub fn main(world: World) -> Void raises { + var vec: FixedVec = FixedVec { len: 0, items: [0, 0, 0, 0] } + check vec.push(7) + check vec.push(9) + let first: Maybe = vec.get(0) + let second: Maybe = vec.get(1) + if first.has && first.value == 7 && second.has && second.value == 9 && vec.count() == 2 { + check world.out.write("receiver methods ok\n") + } +} diff --git a/conformance/native/pass/receiver-method-calls.graph b/conformance/native/pass/receiver-method-calls.graph new file mode 100644 index 0000000..cedb128 Binary files /dev/null and b/conformance/native/pass/receiver-method-calls.graph differ diff --git a/conformance/native/pass/receiver-method-reference-store.0 b/conformance/native/pass/receiver-method-reference-store.0 new file mode 100644 index 0000000..067d47e --- /dev/null +++ b/conformance/native/pass/receiver-method-reference-store.0 @@ -0,0 +1,13 @@ +type Holder { + value: ref, + fn store(self: mutref, next: ref) -> Void { + self.value = next + } +} + +pub fn main() -> Void { + let old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { value: &old } + holder.store(&next) +} diff --git a/conformance/native/pass/receiver-method-reference-store.graph b/conformance/native/pass/receiver-method-reference-store.graph new file mode 100644 index 0000000..f04d674 Binary files /dev/null and b/conformance/native/pass/receiver-method-reference-store.graph differ diff --git a/conformance/native/pass/receiver-return-field-origin-clears-other-field.0 b/conformance/native/pass/receiver-return-field-origin-clears-other-field.0 new file mode 100644 index 0000000..539c312 --- /dev/null +++ b/conformance/native/pass/receiver-return-field-origin-clears-other-field.0 @@ -0,0 +1,16 @@ +type Box { + value: ref, + count: i32, + fn countRef(self: ref) -> ref { + return &self.count + } +} + +pub fn main() -> Void { + var x: i32 = 1 + let y: i32 = 2 + var box: Box = Box { value: &x, count: 0 } + let current: ref = box.countRef() + box.value = &y + x = 3 +} diff --git a/conformance/native/pass/receiver-return-field-origin-clears-other-field.graph b/conformance/native/pass/receiver-return-field-origin-clears-other-field.graph new file mode 100644 index 0000000..d31e223 Binary files /dev/null and b/conformance/native/pass/receiver-return-field-origin-clears-other-field.graph differ diff --git a/conformance/native/pass/recursive-fibonacci.0 b/conformance/native/pass/recursive-fibonacci.0 new file mode 100644 index 0000000..9a66089 --- /dev/null +++ b/conformance/native/pass/recursive-fibonacci.0 @@ -0,0 +1,12 @@ +fn fib(n: u32) -> u32 { + if n <= 1 { + return n + } + return fib(n - 1) + fib(n - 2) +} + +pub fn main(world: World) -> Void raises { + if fib(10) == 55 { + check world.out.write("recursive fibonacci ok\n") + } +} diff --git a/conformance/native/pass/recursive-fibonacci.graph b/conformance/native/pass/recursive-fibonacci.graph new file mode 100644 index 0000000..e5b6daf Binary files /dev/null and b/conformance/native/pass/recursive-fibonacci.graph differ diff --git a/conformance/native/pass/recursive-multi-call-let.0 b/conformance/native/pass/recursive-multi-call-let.0 new file mode 100644 index 0000000..d2c61e9 --- /dev/null +++ b/conformance/native/pass/recursive-multi-call-let.0 @@ -0,0 +1,18 @@ +fn shrink(bytes: Span, n: usize) -> Bool { + if n == 0 { + return true + } + if n == 1 { + let tail: Bool = shrink(bytes, n - 1) + return tail + } + let skip: Bool = shrink(bytes, n - 2) + return skip +} + +pub fn main(world: World) -> Void raises { + let bytes: Span = std.mem.span("zero") + if shrink(bytes, 5) && shrink(bytes, 4) { + check world.out.write("recursive multi call ok\n") + } +} diff --git a/conformance/native/pass/recursive-multi-call-let.graph b/conformance/native/pass/recursive-multi-call-let.graph new file mode 100644 index 0000000..68a5a57 Binary files /dev/null and b/conformance/native/pass/recursive-multi-call-let.graph differ diff --git a/conformance/native/pass/rescue-check.0 b/conformance/native/pass/rescue-check.0 new file mode 100644 index 0000000..7311d76 --- /dev/null +++ b/conformance/native/pass/rescue-check.0 @@ -0,0 +1,12 @@ +fn fail_value() -> i32 raises [Missing] { + raise Missing +} + +pub fn main(world: World) -> Void raises { + var small: [4]u8 = [0, 0, 0, 0] + let a: String = rescue std.path.join(small, "too", "long") err "fallback" + let b: i32 = rescue fail_value() err 4 + if std.mem.eql(a, "fallback") && b == 4 { + check world.out.write("rescue ok\n") + } +} diff --git a/conformance/native/pass/rescue-check.graph b/conformance/native/pass/rescue-check.graph new file mode 100644 index 0000000..f84a827 Binary files /dev/null and b/conformance/native/pass/rescue-check.graph differ diff --git a/conformance/native/pass/scratch-nested-index.0 b/conformance/native/pass/scratch-nested-index.0 new file mode 100644 index 0000000..9ecb402 --- /dev/null +++ b/conformance/native/pass/scratch-nested-index.0 @@ -0,0 +1,11 @@ +fn idx() -> usize { + return 0 +} + +pub fn main(world: World) -> Void raises { + let values: [1]u32 = [2] + let sum: u32 = 40_u32 + values[idx() + 0] + if sum == 42_u32 { + check world.out.write("scratch nested index ok\n") + } +} diff --git a/conformance/native/pass/scratch-nested-index.graph b/conformance/native/pass/scratch-nested-index.graph new file mode 100644 index 0000000..facb193 Binary files /dev/null and b/conformance/native/pass/scratch-nested-index.graph differ diff --git a/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.0 b/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.0 new file mode 100644 index 0000000..1fcbfba --- /dev/null +++ b/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.0 @@ -0,0 +1,20 @@ +type Holder { + value: ref, +} + +fn replace(holder: mutref, next: ref) -> Void { + holder.value = next +} + +pub fn main() -> Void { + var old: i32 = 1 + let next: i32 = 2 + var other: i32 = 3 + var holder: Holder = Holder { value: &old } + let selected: mutref = &mut holder + if true { + var holder: Holder = Holder { value: &other } + replace(selected, &next) + } + old = 4 +} diff --git a/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.graph b/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.graph new file mode 100644 index 0000000..a573ec3 Binary files /dev/null and b/conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.graph differ diff --git a/conformance/native/pass/shape-field-reference-reassignment-clears-origin.0 b/conformance/native/pass/shape-field-reference-reassignment-clears-origin.0 new file mode 100644 index 0000000..6ef48c6 --- /dev/null +++ b/conformance/native/pass/shape-field-reference-reassignment-clears-origin.0 @@ -0,0 +1,11 @@ +type Box { + value: ref, +} + +pub fn main() -> Void { + var x: i32 = 1 + var y: i32 = 2 + var box: Box = Box { value: &x } + box.value = &y + x = 3 +} diff --git a/conformance/native/pass/shape-field-reference-reassignment-clears-origin.graph b/conformance/native/pass/shape-field-reference-reassignment-clears-origin.graph new file mode 100644 index 0000000..889159c Binary files /dev/null and b/conformance/native/pass/shape-field-reference-reassignment-clears-origin.graph differ diff --git a/conformance/native/pass/shape.0 b/conformance/native/pass/shape.0 new file mode 100644 index 0000000..dbc4869 --- /dev/null +++ b/conformance/native/pass/shape.0 @@ -0,0 +1,15 @@ +type Point { + x: i32, + y: i32, +} + +fn sum(point: Point) -> i32 { + return point.x + point.y +} + +pub fn main(world: World) -> Void raises { + let point: Point = Point { x: 40, y: 2 } + if sum(point) == 42 { + check world.out.write("native shape") + } +} diff --git a/conformance/native/pass/shape.graph b/conformance/native/pass/shape.graph new file mode 100644 index 0000000..07aadf8 Binary files /dev/null and b/conformance/native/pass/shape.graph differ diff --git a/conformance/native/pass/static-interface-basic.0 b/conformance/native/pass/static-interface-basic.0 new file mode 100644 index 0000000..8c053ef --- /dev/null +++ b/conformance/native/pass/static-interface-basic.0 @@ -0,0 +1,23 @@ +type Counter { + value: i32, + fn read(self: ref) -> i32 { + return self.value + } +} + +interface Readable { + fn read(self: ref) -> i32 +} + +fn readValue>(value: ref) -> i32 { + return T.read(value) +} + +pub fn main(world: World) -> Void raises { + let counter: Counter = Counter { value: 42 } + if readValue(&counter) == 42 { + check world.out.write("static interface ok\n") + } else { + check world.out.write("static interface broke\n") + } +} diff --git a/conformance/native/pass/static-interface-basic.graph b/conformance/native/pass/static-interface-basic.graph new file mode 100644 index 0000000..2316a39 Binary files /dev/null and b/conformance/native/pass/static-interface-basic.graph differ diff --git a/conformance/native/pass/static-interface-mutref-reference-store.0 b/conformance/native/pass/static-interface-mutref-reference-store.0 new file mode 100644 index 0000000..8277b1a --- /dev/null +++ b/conformance/native/pass/static-interface-mutref-reference-store.0 @@ -0,0 +1,21 @@ +type Holder { + value: ref, + fn store(self: mutref, next: ref) -> Void { + self.value = next + } +} + +interface Storer { + fn store(self: mutref, next: ref) -> Void +} + +fn through>(holder: mutref, next: ref) -> Void { + T.store(holder, next) +} + +pub fn main() -> Void { + let old: i32 = 1 + let next: i32 = 2 + var holder: Holder = Holder { value: &old } + through(&mut holder, &next) +} diff --git a/conformance/native/pass/static-interface-mutref-reference-store.graph b/conformance/native/pass/static-interface-mutref-reference-store.graph new file mode 100644 index 0000000..c475e14 Binary files /dev/null and b/conformance/native/pass/static-interface-mutref-reference-store.graph differ diff --git a/conformance/native/pass/static-interface-mutref.0 b/conformance/native/pass/static-interface-mutref.0 new file mode 100644 index 0000000..e44eb2f --- /dev/null +++ b/conformance/native/pass/static-interface-mutref.0 @@ -0,0 +1,24 @@ +type Counter { + value: i32, + fn increment(self: mutref) -> i32 { + self.value = self.value + 1 + return self.value + } +} + +interface Incrementable { + fn increment(self: mutref) -> i32 +} + +fn bump>(value: mutref) -> i32 { + return T.increment(value) +} + +pub fn main(world: World) -> Void raises { + var counter: Counter = Counter { value: 41 } + if bump(&mut counter) == 42 { + check world.out.write("static interface mutref ok\n") + } else { + check world.out.write("static interface mutref broke\n") + } +} diff --git a/conformance/native/pass/static-interface-mutref.graph b/conformance/native/pass/static-interface-mutref.graph new file mode 100644 index 0000000..79d0475 Binary files /dev/null and b/conformance/native/pass/static-interface-mutref.graph differ diff --git a/conformance/native/pass/static-interface-return-reference-origin.0 b/conformance/native/pass/static-interface-return-reference-origin.0 new file mode 100644 index 0000000..af32393 --- /dev/null +++ b/conformance/native/pass/static-interface-return-reference-origin.0 @@ -0,0 +1,19 @@ +type Holder { + value: i32, + fn get(self: ref) -> ref { + return &self.value + } +} + +interface Reader { + fn get(self: ref) -> ref +} + +fn read>(value: ref) -> ref { + return T.get(value) +} + +pub fn main() -> Void { + let holder: Holder = Holder { value: 1 } + let value: ref = read(&holder) +} diff --git a/conformance/native/pass/static-interface-return-reference-origin.graph b/conformance/native/pass/static-interface-return-reference-origin.graph new file mode 100644 index 0000000..a8881c4 Binary files /dev/null and b/conformance/native/pass/static-interface-return-reference-origin.graph differ diff --git a/conformance/native/pass/static-interface-static-param.0 b/conformance/native/pass/static-interface-static-param.0 new file mode 100644 index 0000000..22c0cf9 --- /dev/null +++ b/conformance/native/pass/static-interface-static-param.0 @@ -0,0 +1,21 @@ +type Bytes { + items: [N]u8, + fn first(self: ref) -> u8 { + return self.items[0] + } +} + +interface First { + fn first(self: ref) -> u8 +} + +fn readFirst, static N: usize>(value: ref) -> u8 { + return T.first(value) +} + +pub fn main(world: World) -> Void raises { + let bytes: Bytes<4> = Bytes { items: [9, 8, 7, 6] } + if readFirst, 4>(&bytes) == 9 { + check world.out.write("static interface static param ok\n") + } +} diff --git a/conformance/native/pass/static-interface-static-param.graph b/conformance/native/pass/static-interface-static-param.graph new file mode 100644 index 0000000..58b49a7 Binary files /dev/null and b/conformance/native/pass/static-interface-static-param.graph differ diff --git a/conformance/native/pass/static-method-namespace.0 b/conformance/native/pass/static-method-namespace.0 new file mode 100644 index 0000000..57161b1 --- /dev/null +++ b/conformance/native/pass/static-method-namespace.0 @@ -0,0 +1,15 @@ +type Counter { + value: i32, + fn add(self: ref, amount: i32) -> i32 { + return self.value + amount + } +} + +pub fn main(world: World) -> Void raises { + let counter: Counter = Counter { value: 40 } + if Counter.add(&counter, 2) == 42 { + check world.out.write("static method ok\n") + } else { + check world.out.write("static method broke\n") + } +} diff --git a/conformance/native/pass/static-method-namespace.graph b/conformance/native/pass/static-method-namespace.graph new file mode 100644 index 0000000..fb4ae74 Binary files /dev/null and b/conformance/native/pass/static-method-namespace.graph differ diff --git a/conformance/native/pass/static-value-params.0 b/conformance/native/pass/static-value-params.0 new file mode 100644 index 0000000..282c3e7 --- /dev/null +++ b/conformance/native/pass/static-value-params.0 @@ -0,0 +1,17 @@ +const cap: usize = 4 + +type FixedVec { + len: usize, + items: [N]T, +} + +fn first(vec: ref>) -> T { + return vec.items[0] +} + +pub fn main(world: World) -> Void raises { + let vec: FixedVec = FixedVec { len: cap, items: [1, 2, 3, 4] } + if vec.len == cap && first(&vec) == 1 && std.mem.len(vec.items) == 4 { + check world.out.write("static value params native\n") + } +} diff --git a/conformance/native/pass/static-value-params.graph b/conformance/native/pass/static-value-params.graph new file mode 100644 index 0000000..bc1cbf7 Binary files /dev/null and b/conformance/native/pass/static-value-params.graph differ diff --git a/conformance/native/pass/static-value-types-const-expr.0 b/conformance/native/pass/static-value-types-const-expr.0 new file mode 100644 index 0000000..e329b75 --- /dev/null +++ b/conformance/native/pass/static-value-types-const-expr.0 @@ -0,0 +1,16 @@ +const cap: u8 = 2 + 2 + +type Tiny { + items: [N]u8, +} + +fn len(tiny: ref>) -> usize { + return std.mem.len(tiny.items) +} + +pub fn main(world: World) -> Void raises { + let tiny: Tiny = Tiny { items: [1, 2, 3, 4] } + if len(&tiny) == 4 { + check world.out.write("static value types const expr ok\n") + } +} diff --git a/conformance/native/pass/static-value-types-const-expr.graph b/conformance/native/pass/static-value-types-const-expr.graph new file mode 100644 index 0000000..e22d893 Binary files /dev/null and b/conformance/native/pass/static-value-types-const-expr.graph differ diff --git a/conformance/native/pass/std-args.0 b/conformance/native/pass/std-args.0 new file mode 100644 index 0000000..c1dda45 --- /dev/null +++ b/conformance/native/pass/std-args.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let count: usize = std.args.len() + let first: Maybe = std.args.get(1) + let missing: Maybe = std.args.get(99) + if count == 3 && first.has && !missing.has { + check world.out.write(first.value) + check world.out.write("\n") + } +} diff --git a/conformance/native/pass/std-args.graph b/conformance/native/pass/std-args.graph new file mode 100644 index 0000000..a865e21 Binary files /dev/null and b/conformance/native/pass/std-args.graph differ diff --git a/conformance/native/pass/std-ascii.0 b/conformance/native/pass/std-ascii.0 new file mode 100644 index 0000000..0851115 --- /dev/null +++ b/conformance/native/pass/std-ascii.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let digit: Maybe = std.ascii.digitValue(55_u8) + let hex: Maybe = std.ascii.hexValue(70_u8) + let missing: Maybe = std.ascii.hexValue(120_u8) + if std.ascii.isDigit(57_u8) && std.ascii.isAlpha(65_u8) && std.ascii.isAlnum(122_u8) && std.ascii.isWhitespace(10_u8) && std.ascii.isLower(97_u8) && std.ascii.isUpper(90_u8) && std.ascii.isHexDigit(102_u8) && std.ascii.toLower(65_u8) == 97_u8 && std.ascii.toUpper(122_u8) == 90_u8 && digit.has && digit.value == 7_u8 && hex.has && hex.value == 15_u8 && !missing.has { + check world.out.write("std ascii ok\n") + } +} diff --git a/conformance/native/pass/std-ascii.graph b/conformance/native/pass/std-ascii.graph new file mode 100644 index 0000000..50ec933 Binary files /dev/null and b/conformance/native/pass/std-ascii.graph differ diff --git a/conformance/native/pass/std-cli-helpers.0 b/conformance/native/pass/std-cli-helpers.0 new file mode 100644 index 0000000..834515c --- /dev/null +++ b/conformance/native/pass/std-cli-helpers.0 @@ -0,0 +1,11 @@ +pub fn main(world: World) -> Void raises { + var path_storage: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let path: String = check std.path.join(path_storage, ".zero", "cli-dir") + if std.fs.makeDir(path) && std.fs.isDir(path) { + if std.mem.eql(std.path.basename(path), "cli-dir") { + if std.fs.removeDir(path) { + check world.out.write("cli helpers ok\n") + } + } + } +} diff --git a/conformance/native/pass/std-cli-helpers.graph b/conformance/native/pass/std-cli-helpers.graph new file mode 100644 index 0000000..ad349d3 Binary files /dev/null and b/conformance/native/pass/std-cli-helpers.graph differ diff --git a/conformance/native/pass/std-codec-json-url.0 b/conformance/native/pass/std-codec-json-url.0 new file mode 100644 index 0000000..d7eafb7 --- /dev/null +++ b/conformance/native/pass/std-codec-json-url.0 @@ -0,0 +1,390 @@ +pub fn main(world: World) -> Void raises { + var ok: Bool = true + + let hex_len: Maybe = std.codec.hexDecodedLen("417a") + var hex_buf: [2]u8 = [0_u8; 2] + let hex: Maybe> = std.codec.hexDecode(hex_buf, "417a") + let bad_hex: Maybe = std.codec.hexDecodedLen("41z") + if !hex_len.has || hex_len.value != 2_usize || !hex.has || !std.mem.eql(hex.value, "Az") || bad_hex.has { + ok = false + } + + let b64_len: Maybe = std.codec.base64DecodedLen("emVybw==") + var b64_buf: [4]u8 = [0_u8; 4] + let b64: Maybe> = std.codec.base64Decode(b64_buf, "emVybw==") + let bad_b64: Maybe> = std.codec.base64Decode(b64_buf, "abc") + var b64_one_buf: [1]u8 = [0_u8; 1] + var b64_two_buf: [2]u8 = [0_u8; 2] + let bad_b64_one_pad_bits: Maybe> = std.codec.base64Decode(b64_one_buf, "AB==") + let bad_b64_two_pad_bits: Maybe> = std.codec.base64Decode(b64_two_buf, "AAB=") + if !b64_len.has || b64_len.value != 4_usize || !b64.has || !std.mem.eql(b64.value, "zero") || bad_b64.has || bad_b64_one_pad_bits.has || bad_b64_two_pad_bits.has { + ok = false + } + let raw_b64_len: Maybe = std.codec.base64RawDecodedLen("emVybw") + var raw_b64_buf: [4]u8 = [0_u8; 4] + let raw_b64: Maybe> = std.codec.base64RawDecode(raw_b64_buf, "emVybw") + var raw_b64_out_buf: [8]u8 = [0_u8; 8] + let raw_b64_out: Maybe> = std.codec.base64RawEncode(raw_b64_out_buf, "zero") + let bad_raw_b64: Maybe> = std.codec.base64RawDecode(raw_b64_buf, "AB") + var url_b64_buf: [2]u8 = [0_u8; 2] + let url_b64_len: Maybe = std.codec.base64UrlDecodedLen("-_8") + let url_b64: Maybe> = std.codec.base64UrlDecode(url_b64_buf, "-_8") + var url_b64_out_buf: [4]u8 = [0_u8; 4] + let url_b64_out: Maybe> = std.codec.base64UrlEncode(url_b64_out_buf, "\xfb\xff") + let bad_url_b64: Maybe> = std.codec.base64UrlDecode(url_b64_buf, "+/8") + if std.codec.base64RawEncodedLen(4_usize) != 6_usize || std.codec.base64UrlEncodedLen(2_usize) != 3_usize || !raw_b64_len.has || raw_b64_len.value != 4_usize || !raw_b64.has || !std.mem.eql(raw_b64.value, "zero") || !raw_b64_out.has || !std.mem.eql(raw_b64_out.value, "emVybw") || bad_raw_b64.has || !url_b64_len.has || url_b64_len.value != 2_usize || !url_b64.has || !std.mem.eql(url_b64.value, "\xfb\xff") || !url_b64_out.has || !std.mem.eql(url_b64_out.value, "-_8") || bad_url_b64.has { + ok = false + } + let b32_len: Maybe = std.codec.base32DecodedLen("PJSXE3Y=") + var b32_buf: [4]u8 = [0_u8; 4] + let b32: Maybe> = std.codec.base32Decode(b32_buf, "PJSXE3Y=") + var b32_out_buf: [8]u8 = [0_u8; 8] + let b32_out: Maybe> = std.codec.base32Encode(b32_out_buf, "zero") + let raw_b32_len: Maybe = std.codec.base32RawDecodedLen("PJSXE3Y") + var raw_b32_buf: [4]u8 = [0_u8; 4] + let raw_b32: Maybe> = std.codec.base32RawDecode(raw_b32_buf, "PJSXE3Y") + var raw_b32_out_buf: [8]u8 = [0_u8; 8] + let raw_b32_out: Maybe> = std.codec.base32RawEncode(raw_b32_out_buf, "zero") + var b32_pair_buf: [2]u8 = [0_u8; 2] + let b32_pair: Maybe> = std.codec.base32Decode(b32_pair_buf, "7P7Q====") + let bad_b32_pad_bits: Maybe> = std.codec.base32Decode(b32_pair_buf, "AB======") + if std.codec.base32EncodedLen(4_usize) != 8_usize || std.codec.base32RawEncodedLen(4_usize) != 7_usize || !b32_len.has || b32_len.value != 4_usize || !b32.has || !std.mem.eql(b32.value, "zero") || !b32_out.has || !std.mem.eql(b32_out.value, "PJSXE3Y=") || !raw_b32_len.has || raw_b32_len.value != 4_usize || !raw_b32.has || !std.mem.eql(raw_b32.value, "zero") || !raw_b32_out.has || !std.mem.eql(raw_b32_out.value, "PJSXE3Y") || !b32_pair.has || !std.mem.eql(b32_pair.value, "\xfb\xff") || bad_b32_pad_bits.has { + ok = false + } + + let le16: Maybe = std.codec.readU16Le("AB") + let be16: Maybe = std.codec.readU16Be("AB") + var word_le_buf: [2]u8 = [0_u8; 2] + var word_be_buf: [2]u8 = [0_u8; 2] + let word_le: Maybe> = std.codec.writeU16Le(word_le_buf, 0x4142_u16) + let word_be: Maybe> = std.codec.writeU16Be(word_be_buf, 0x4142_u16) + if !le16.has || le16.value != 0x4241_u16 || !be16.has || be16.value != 0x4142_u16 || !word_le.has || !std.mem.eql(word_le.value, "BA") || !word_be.has || !std.mem.eql(word_be.value, "AB") { + ok = false + } + + var dword_le_buf: [4]u8 = [0_u8; 4] + var dword_be_buf: [4]u8 = [0_u8; 4] + let dword_le: Maybe> = std.codec.writeU32Le(dword_le_buf, 0x41424344_u32) + let dword_be: Maybe> = std.codec.writeU32Be(dword_be_buf, 0x41424344_u32) + if !dword_le.has || !dword_be.has { + ok = false + } + if dword_le.has { + let read_back_le: Maybe = std.codec.readU32Le(dword_le.value) + if !read_back_le.has || read_back_le.value != 0x41424344_u32 { + ok = false + } + } + if dword_be.has { + let read_back_be: Maybe = std.codec.readU32Be(dword_be.value) + if !read_back_be.has || read_back_be.value != 0x41424344_u32 { + ok = false + } + } + + var varint_buf: [5]u8 = [0_u8; 5] + let varint: Maybe> = std.codec.varintEncode(varint_buf, 300_u32) + if !varint.has { + ok = false + } + if varint.has { + let decoded_varint: Maybe = std.codec.varintDecode(varint.value) + if std.mem.len(varint.value) != 2_usize || !decoded_varint.has || decoded_varint.value != 300_u32 { + ok = false + } + } + let invalid_varint: Maybe = std.codec.varintDecode("\xff\xff\xff\xff\xff") + let trailing_varint: Maybe = std.codec.varintDecode("\xac\x02\x01") + if invalid_varint.has || trailing_varint.has { + ok = false + } + + var percent_buf: [16]u8 = [0_u8; 16] + let percent: Maybe> = std.url.percentEncode(percent_buf, "a b/") + var percent_decode_buf: [8]u8 = [0_u8; 8] + if !percent.has { + ok = false + } + if percent.has { + let decoded_percent: Maybe> = std.url.percentDecode(percent_decode_buf, percent.value) + if !std.mem.eql(percent.value, "a%20b%2F") || !decoded_percent.has || !std.mem.eql(decoded_percent.value, "a b/") { + ok = false + } + } + + var query_buf: [16]u8 = [0_u8; 16] + let escaped_query: Maybe> = std.url.queryEscape(query_buf, "a b+c") + var query_decode_buf: [8]u8 = [0_u8; 8] + if !escaped_query.has { + ok = false + } + if escaped_query.has { + let decoded_query: Maybe> = std.url.queryUnescape(query_decode_buf, escaped_query.value) + if !std.mem.eql(escaped_query.value, "a+b%2Bc") || !decoded_query.has || !std.mem.eql(decoded_query.value, "a b+c") { + ok = false + } + } + + let url: Span = "https://user@example.com:443/path/to?q=zero%20lang&n=42#frag" + let scheme: Maybe> = std.url.scheme(url) + let authority: Maybe> = std.url.authority(url) + let host: Maybe> = std.url.host(url) + let ipv6_host: Maybe> = std.url.host("https://[::1]:443/path") + let malformed_ipv6_host: Maybe> = std.url.host("https://[::1]extra/path") + let path: Span = std.url.path(url) + let relative_path: Span = std.url.path("search?q=zero") + let rootless_scheme_path: Span = std.url.path("mailto:user@example.com") + let query: Maybe> = std.url.query(url) + let fragment: Maybe> = std.url.fragment(url) + let fragment_query: Maybe> = std.url.query("https://example.com/path#frag?q=1") + if !scheme.has || !std.mem.eql(scheme.value, "https") || !authority.has || !std.mem.eql(authority.value, "user@example.com:443") || !host.has || !std.mem.eql(host.value, "example.com") || !ipv6_host.has || !std.mem.eql(ipv6_host.value, "[::1]") || malformed_ipv6_host.has || !std.mem.eql(path, "/path/to") || !std.mem.eql(relative_path, "search") || !std.mem.eql(rootless_scheme_path, "user@example.com") || !query.has || !fragment.has || !std.mem.eql(fragment.value, "frag") { + ok = false + } + if query.has { + let q: Maybe> = std.url.queryValue(query.value, "q") + if !q.has || !std.mem.eql(q.value, "zero%20lang") { + ok = false + } + var decoded_q_buf: [16]u8 = [0_u8; 16] + let decoded_q: Maybe> = std.url.queryValueDecoded(decoded_q_buf, query.value, "q") + if !decoded_q.has || !std.mem.eql(decoded_q.value, "zero lang") { + ok = false + } + } + var decoded_name_buf: [24]u8 = [0_u8; 24] + let decoded_name: Maybe> = std.url.queryValueDecoded(decoded_name_buf, "first+name=Ada%2BLovelace&empty=", "first name") + var decoded_empty_buf: [1]u8 = [0_u8; 1] + let decoded_empty: Maybe> = std.url.queryValueDecoded(decoded_empty_buf, "first+name=Ada%2BLovelace&empty=", "empty") + var decoded_bad_buf: [8]u8 = [0_u8; 8] + let decoded_bad: Maybe> = std.url.queryValueDecoded(decoded_bad_buf, "q=%zz", "q") + if !decoded_name.has || !std.mem.eql(decoded_name.value, "Ada+Lovelace") || !decoded_empty.has || std.mem.len(decoded_empty.value) != 0_usize || decoded_bad.has { + ok = false + } + var query_param_buf: [16]u8 = [0_u8; 16] + let query_param: Maybe> = std.url.writeQueryParam(query_param_buf, "q", "zero lang") + var append_buf: [48]u8 = [0_u8; 48] + var appended: Maybe> = null + if query_param.has { + appended = std.url.appendQuery(append_buf, "https://example.com/path", query_param.value) + } + if !appended.has || !std.mem.eql(appended.value, "https://example.com/path?q=zero+lang") { + ok = false + } + var built_url_buf: [48]u8 = [0_u8; 48] + let built_url: Maybe> = std.url.writeUrl(built_url_buf, "https", "example.com", "path") + var built_query_url_buf: [64]u8 = [0_u8; 64] + var built_query_url: Maybe> = null + if built_url.has && query_param.has { + built_query_url = std.url.appendQuery(built_query_url_buf, built_url.value, query_param.value) + } + var built_fragment_buf: [72]u8 = [0_u8; 72] + var built_fragment: Maybe> = null + if built_query_url.has { + built_fragment = std.url.appendFragment(built_fragment_buf, built_query_url.value, "section-1") + } + if !built_url.has || !std.mem.eql(built_url.value, "https://example.com/path") || !built_query_url.has || !std.mem.eql(built_query_url.value, "https://example.com/path?q=zero+lang") || !built_fragment.has || !std.mem.eql(built_fragment.value, "https://example.com/path?q=zero+lang#section-1") { + ok = false + } + var append_fragment_buf: [56]u8 = [0_u8; 56] + var appended_fragment: Maybe> = null + if query_param.has { + appended_fragment = std.url.appendQuery(append_fragment_buf, "https://example.com/path#frag", query_param.value) + } + if fragment_query.has || !appended_fragment.has || !std.mem.eql(appended_fragment.value, "https://example.com/path?q=zero+lang#frag") { + ok = false + } + var form_name_buf: [32]u8 = [0_u8; 32] + let form_name: Maybe> = std.url.writeFormField(form_name_buf, "first name", "Ada Lovelace") + var form_note_buf: [24]u8 = [0_u8; 24] + let form_note: Maybe> = std.url.writeFormField(form_note_buf, "note", "a+b&c") + var form_buf: [64]u8 = [0_u8; 64] + var form: Maybe> = null + if form_name.has && form_note.has { + form = std.url.appendFormField(form_buf, form_name.value, form_note.value) + } + var form_single_buf: [32]u8 = [0_u8; 32] + var form_single: Maybe> = null + if form_note.has { + form_single = std.url.appendFormField(form_single_buf, "", form_note.value) + } + var form_value_name_buf: [24]u8 = [0_u8; 24] + var form_value_name: Maybe> = null + var form_value_note_buf: [16]u8 = [0_u8; 16] + var form_value_note: Maybe> = null + if form.has { + form_value_name = std.url.formValue(form_value_name_buf, form.value, "first name") + form_value_note = std.url.formValue(form_value_note_buf, form.value, "note") + } + var form_tiny_buf: [4]u8 = [0_u8; 4] + var form_tiny: Maybe> = null + if form_name.has && form_note.has { + form_tiny = std.url.appendFormField(form_tiny_buf, form_name.value, form_note.value) + } + if !form_name.has || !std.mem.eql(form_name.value, "first+name=Ada+Lovelace") || !form_note.has || !std.mem.eql(form_note.value, "note=a%2Bb%26c") || !form.has || !std.mem.eql(form.value, "first+name=Ada+Lovelace¬e=a%2Bb%26c") || !form_single.has || !std.mem.eql(form_single.value, "note=a%2Bb%26c") || !form_value_name.has || !std.mem.eql(form_value_name.value, "Ada Lovelace") || !form_value_note.has || !std.mem.eql(form_value_note.value, "a+b&c") || form_tiny.has { + ok = false + } + + let json: Span = "{\"name\":\"zero\\nlang\",\"count\":42,\"ok\":true,\"nested\":{\"x\":1},\"list\":[1,2]}" + if std.json.errorNone() != 0_u32 || std.json.errorInvalid() != 1_u32 || std.json.errorTrailing() != 2_u32 { + ok = false + } + if !std.mem.eql(std.json.errorName(std.json.errorNone()), "ok") || !std.mem.eql(std.json.errorName(std.json.errorInvalid()), "invalid") || !std.mem.eql(std.json.errorName(std.json.errorTrailing()), "trailing") || !std.mem.eql(std.json.errorName(99_u32), "unknown") { + ok = false + } + if !std.mem.eql(std.json.errorExpected(std.json.errorNone()), "none") || !std.mem.eql(std.json.errorExpected(std.json.errorInvalid()), "valid-json") || !std.mem.eql(std.json.errorExpected(std.json.errorTrailing()), "end-of-input") || !std.mem.eql(std.json.errorExpected(99_u32), "unknown") { + ok = false + } + let dynamic_ok_status: u32 = std.json.validateError(json) + let dynamic_invalid_status: u32 = std.json.validateError("{") + let dynamic_trailing_status: u32 = std.json.validateError("{} x") + let dynamic_unknown_status: u32 = dynamic_trailing_status + 97_u32 + if !std.mem.eql(std.json.errorName(dynamic_ok_status), "ok") || !std.mem.eql(std.json.errorExpected(dynamic_ok_status), "none") || !std.mem.eql(std.json.errorName(dynamic_unknown_status), "unknown") || !std.mem.eql(std.json.errorExpected(dynamic_unknown_status), "unknown") { + ok = false + } + if !std.mem.eql(std.json.errorName(dynamic_invalid_status), "invalid") || !std.mem.eql(std.json.errorExpected(dynamic_invalid_status), "valid-json") || !std.mem.eql(std.json.errorName(dynamic_trailing_status), "trailing") || !std.mem.eql(std.json.errorExpected(dynamic_trailing_status), "end-of-input") { + ok = false + } + if std.json.validateError(json) != std.json.errorNone() || std.json.validateError("\"\\u20ac\"") != std.json.errorNone() || std.json.validateError("\"\\ud834\\udd1e\"") != std.json.errorNone() || std.json.validateError("\"\\ud800\"") != std.json.errorInvalid() || std.json.validateError("\"\xff\"") != std.json.errorInvalid() || std.json.validateError("{") != std.json.errorInvalid() || std.json.validateError("{} x") != std.json.errorTrailing() || std.json.validateError("[1,]") != std.json.errorInvalid() || std.json.validateError("{\"a\":1,}") != std.json.errorInvalid() { + ok = false + } + let invalid_json: Span = "{\n \"ok\": tru\n}" + let trailing_json: Span = "{} x" + var json_location_buf: [24]u8 = [0_u8; 24] + let json_location: Maybe> = std.diag.formatOffsetLocation(json_location_buf, "config.json", invalid_json, std.json.errorOffset(invalid_json)) + if std.json.errorOffset(json) != std.mem.len(json) || std.json.errorOffset(invalid_json) != 13 || std.json.errorLine(invalid_json) != 2 || std.json.errorColumn(invalid_json) != 12 || !json_location.has || !std.mem.eql(json_location.value, "config.json:2:12") || std.json.errorOffset(trailing_json) != 3 || std.json.errorLine(trailing_json) != 1 || std.json.errorColumn(trailing_json) != 4 || std.json.errorOffset("[1,]") != 3 || std.json.errorOffset("{\"a\":1,}") != 7 { + ok = false + } + let count_field: Maybe> = std.json.field(json, "count") + let count: Maybe = std.json.u32(json, "count") + let bool_value: Maybe = std.json.bool(json, "ok") + var name_buf: [16]u8 = [0_u8; 16] + let name: Maybe> = std.json.string(name_buf, json, "name") + let malformed_count: Maybe = std.json.u32("{\"count\":42", "count") + let malformed_field: Maybe> = std.json.field("{\"name\":\"zero\",", "name") + if !count_field.has || !std.mem.eql(count_field.value, "42") || !count.has || count.value != 42_u32 || !bool_value.has || !bool_value.value || !name.has || !std.mem.eql(name.value, "zero\nlang") || malformed_count.has || malformed_field.has { + ok = false + } + + var decoded_string_buf: [8]u8 = [0_u8; 8] + let decoded_string: Maybe> = std.json.stringDecode(decoded_string_buf, "\"a\\\"b\"") + var decoded_unicode_buf: [8]u8 = [0_u8; 8] + let decoded_unicode: Maybe> = std.json.stringDecode(decoded_unicode_buf, "\"\\u20ac\"") + var decoded_pair_buf: [8]u8 = [0_u8; 8] + let decoded_pair: Maybe> = std.json.stringDecode(decoded_pair_buf, "\"\\ud834\\udd1e\"") + var invalid_string_buf: [8]u8 = [0_u8; 8] + let invalid_string: Maybe> = std.json.stringDecode(invalid_string_buf, "\"\xff\"") + let unicode_key_field: Maybe> = std.json.field("{\"\\u20ac\":1}", "\xe2\x82\xac") + var string_out_buf: [16]u8 = [0_u8; 16] + let string_out: Maybe> = std.json.writeStringBytes(string_out_buf, "a\nb") + var invalid_out_buf: [8]u8 = [0_u8; 8] + let invalid_out: Maybe> = std.json.writeStringBytes(invalid_out_buf, "\xff") + if !decoded_string.has || !std.mem.eql(decoded_string.value, "a\"b") || !decoded_unicode.has || !std.mem.eql(decoded_unicode.value, "\xe2\x82\xac") || !decoded_pair.has || !std.mem.eql(decoded_pair.value, "\xf0\x9d\x84\x9e") || invalid_string.has || !unicode_key_field.has || !std.mem.eql(unicode_key_field.value, "1") || !string_out.has || !std.mem.eql(string_out.value, "\"a\\nb\"") || invalid_out.has { + ok = false + } + + var object_string_buf: [24]u8 = [0_u8; 24] + let object_string: Maybe> = std.json.writeObject1String(object_string_buf, "name", "zero") + var object_u32_buf: [16]u8 = [0_u8; 16] + let object_u32: Maybe> = std.json.writeObject1U32(object_u32_buf, "count", 42_u32) + var object_bool_buf: [16]u8 = [0_u8; 16] + let object_bool: Maybe> = std.json.writeObject1Bool(object_bool_buf, "ok", true) + var raw_user_field_buf: [48]u8 = [0_u8; 48] + let raw_user_field: Maybe> = std.json.writeFieldRaw(raw_user_field_buf, "user", "{\"name\":\"zero\"}") + var raw_tags_field_buf: [48]u8 = [0_u8; 48] + let raw_tags_field: Maybe> = std.json.writeFieldRaw(raw_tags_field_buf, "tags", "[\"api\",true]") + var object_raw_buf: [80]u8 = [0_u8; 80] + var object_raw: Maybe> = null + if raw_user_field.has && raw_tags_field.has { + object_raw = std.json.writeObject2Fields(object_raw_buf, raw_user_field.value, raw_tags_field.value) + } + var name_field_buf: [32]u8 = [0_u8; 32] + let name_field: Maybe> = std.json.writeFieldString(name_field_buf, "name", "zero\nlang") + var role_field_buf: [24]u8 = [0_u8; 24] + let role_field: Maybe> = std.json.writeFieldString(role_field_buf, "role", "agent") + var object_strings_buf: [64]u8 = [0_u8; 64] + var object_strings: Maybe> = null + if name_field.has && role_field.has { + object_strings = std.json.writeObject2Fields(object_strings_buf, name_field.value, role_field.value) + } + var object_strings_direct_buf: [64]u8 = [0_u8; 64] + var object_strings_direct: Maybe> = null + if role_field.has { + object_strings_direct = std.json.writeObject2StringField(object_strings_direct_buf, "name", "zero\nlang", role_field.value) + } + var name_plain_field_buf: [24]u8 = [0_u8; 24] + let name_plain_field: Maybe> = std.json.writeFieldString(name_plain_field_buf, "name", "zero") + var count_json_field_buf: [24]u8 = [0_u8; 24] + let count_json_field: Maybe> = std.json.writeFieldU32(count_json_field_buf, "count", 42_u32) + var ok_field_buf: [16]u8 = [0_u8; 16] + let ok_field: Maybe> = std.json.writeFieldBool(ok_field_buf, "ok", true) + var object_string_u32_buf: [48]u8 = [0_u8; 48] + var object_string_u32: Maybe> = null + if name_plain_field.has && count_json_field.has { + object_string_u32 = std.json.writeObject2Fields(object_string_u32_buf, name_plain_field.value, count_json_field.value) + } + var object_string_u32_direct_buf: [48]u8 = [0_u8; 48] + var object_string_u32_direct: Maybe> = null + if count_json_field.has { + object_string_u32_direct = std.json.writeObject2StringField(object_string_u32_direct_buf, "name", "zero", count_json_field.value) + } + var object_string_bool_buf: [48]u8 = [0_u8; 48] + var object_string_bool: Maybe> = null + if name_plain_field.has && ok_field.has { + object_string_bool = std.json.writeObject2Fields(object_string_bool_buf, name_plain_field.value, ok_field.value) + } + var object_string_bool_direct_buf: [48]u8 = [0_u8; 48] + var object_string_bool_direct: Maybe> = null + if ok_field.has { + object_string_bool_direct = std.json.writeObject2StringField(object_string_bool_direct_buf, "name", "zero", ok_field.value) + } + var object_u32_bool_buf: [32]u8 = [0_u8; 32] + var object_u32_bool: Maybe> = null + if count_json_field.has && ok_field.has { + object_u32_bool = std.json.writeObject2Fields(object_u32_bool_buf, count_json_field.value, ok_field.value) + } + var object_u32_bool_direct_buf: [32]u8 = [0_u8; 32] + var object_u32_bool_direct: Maybe> = null + if ok_field.has { + object_u32_bool_direct = std.json.writeObject2U32Field(object_u32_bool_direct_buf, "count", 42_u32, ok_field.value) + } + var object_bool_u32_direct_buf: [32]u8 = [0_u8; 32] + var object_bool_u32_direct: Maybe> = null + if count_json_field.has { + object_bool_u32_direct = std.json.writeObject2BoolField(object_bool_u32_direct_buf, "ok", true, count_json_field.value) + } + var object_bad_raw_buf: [48]u8 = [0_u8; 48] + let object_bad_raw: Maybe> = std.json.writeFieldRaw(object_bad_raw_buf, "bad", "{") + var object_small_buf: [12]u8 = [0_u8; 12] + var object_small: Maybe> = null + if name_plain_field.has && role_field.has { + object_small = std.json.writeObject2Fields(object_small_buf, name_plain_field.value, role_field.value) + } + var object2_direct_small_buf: [16]u8 = [0_u8; 16] + var object2_direct_small: Maybe> = null + if count_json_field.has { + object2_direct_small = std.json.writeObject2StringField(object2_direct_small_buf, "name", "zero", count_json_field.value) + } + if !object_string.has || !std.mem.eql(object_string.value, "{\"name\":\"zero\"}") || !object_u32.has || !std.mem.eql(object_u32.value, "{\"count\":42}") || !object_bool.has || !std.mem.eql(object_bool.value, "{\"ok\":true}") || !raw_user_field.has || !raw_tags_field.has || !object_raw.has || !std.mem.eql(object_raw.value, "{\"user\":{\"name\":\"zero\"},\"tags\":[\"api\",true]}") || !name_field.has || !role_field.has || !object_strings.has || !std.mem.eql(object_strings.value, "{\"name\":\"zero\\nlang\",\"role\":\"agent\"}") || !object_strings_direct.has || !std.mem.eql(object_strings_direct.value, "{\"name\":\"zero\\nlang\",\"role\":\"agent\"}") || !name_plain_field.has || !count_json_field.has || !ok_field.has || !object_string_u32.has || !std.mem.eql(object_string_u32.value, "{\"name\":\"zero\",\"count\":42}") || !object_string_u32_direct.has || !std.mem.eql(object_string_u32_direct.value, "{\"name\":\"zero\",\"count\":42}") || !object_string_bool.has || !std.mem.eql(object_string_bool.value, "{\"name\":\"zero\",\"ok\":true}") || !object_string_bool_direct.has || !std.mem.eql(object_string_bool_direct.value, "{\"name\":\"zero\",\"ok\":true}") || !object_u32_bool.has || !std.mem.eql(object_u32_bool.value, "{\"count\":42,\"ok\":true}") || !object_u32_bool_direct.has || !std.mem.eql(object_u32_bool_direct.value, "{\"count\":42,\"ok\":true}") || !object_bool_u32_direct.has || !std.mem.eql(object_bool_u32_direct.value, "{\"ok\":true,\"count\":42}") || object_bad_raw.has || object_small.has || object2_direct_small.has { + ok = false + } + var array_strings_buf: [32]u8 = [0_u8; 32] + let array_strings: Maybe> = std.json.writeArray2Strings(array_strings_buf, "zero\nlang", "agent") + var array_u32_buf: [16]u8 = [0_u8; 16] + let array_u32: Maybe> = std.json.writeArray2U32(array_u32_buf, 7_u32, 42_u32) + var array_bools_buf: [16]u8 = [0_u8; 16] + let array_bools: Maybe> = std.json.writeArray2Bools(array_bools_buf, true, false) + var array_small_buf: [4]u8 = [0_u8; 4] + let array_small: Maybe> = std.json.writeArray2Strings(array_small_buf, "zero", "agent") + if !array_strings.has || !std.mem.eql(array_strings.value, "[\"zero\\nlang\",\"agent\"]") || !array_u32.has || !std.mem.eql(array_u32.value, "[7,42]") || !array_bools.has || !std.mem.eql(array_bools.value, "[true,false]") || array_small.has { + ok = false + } + if object_raw.has && std.json.validateError(object_raw.value) != std.json.errorNone() { + ok = false + } + + if ok { + check world.out.write("std codec json url ok\n") + return + } + check world.err.write("std codec json url failed\n") +} diff --git a/conformance/native/pass/std-codec-json-url.graph b/conformance/native/pass/std-codec-json-url.graph new file mode 100644 index 0000000..6c1ce17 Binary files /dev/null and b/conformance/native/pass/std-codec-json-url.graph differ diff --git a/conformance/native/pass/std-codec-widths.0 b/conformance/native/pass/std-codec-widths.0 new file mode 100644 index 0000000..213e6e4 --- /dev/null +++ b/conformance/native/pass/std-codec-widths.0 @@ -0,0 +1,47 @@ +pub fn main(world: World) -> Void raises { + let word: Maybe = std.codec.readU16Le("AB") + let number: Maybe = std.codec.readU32Le("ABCD") + let length: usize = std.codec.encodedVarintLen(300) + let length64: usize = std.codec.encodedVarintLen64(18446744073709551615_u64) + let signed_length: usize = std.codec.encodedSignedVarintLen(-150) + let signed_length64: usize = std.codec.encodedSignedVarintLen64(-9223372036854775807_i64 - 1_i64) + var word64_le_buf: [8]u8 = [0_u8; 8] + var word64_be_buf: [8]u8 = [0_u8; 8] + let word64_le: Maybe> = std.codec.writeU64Le(word64_le_buf, 0x0102030405060708_u64) + let word64_be: Maybe> = std.codec.writeU64Be(word64_be_buf, 0x0102030405060708_u64) + var word64_le_ok: Bool = false + var word64_be_ok: Bool = false + if word64_le.has { + let read64_le: Maybe = std.codec.readU64Le(word64_le.value) + word64_le_ok = read64_le.has && read64_le.value == 0x0102030405060708_u64 && std.mem.eql(word64_le.value, "\x08\x07\x06\x05\x04\x03\x02\x01") + } + if word64_be.has { + let read64_be: Maybe = std.codec.readU64Be(word64_be.value) + word64_be_ok = read64_be.has && read64_be.value == 0x0102030405060708_u64 && std.mem.eql(word64_be.value, "\x01\x02\x03\x04\x05\x06\x07\x08") + } + var varint64_buf: [10]u8 = [0_u8; 10] + let varint64: Maybe> = std.codec.varintEncode64(varint64_buf, 18446744073709551615_u64) + var varint64_ok: Bool = false + if varint64.has { + let decoded64: Maybe = std.codec.varintDecode64(varint64.value) + varint64_ok = std.mem.len(varint64.value) == 10_usize && decoded64.has && decoded64.value == 18446744073709551615_u64 + } + var signed_buf: [5]u8 = [0_u8; 5] + let signed: Maybe> = std.codec.signedVarintEncode(signed_buf, -150) + var signed_ok: Bool = false + if signed.has { + let decoded_signed: Maybe = std.codec.signedVarintDecode(signed.value) + signed_ok = decoded_signed.has && decoded_signed.value == -150 + } + var signed64_buf: [10]u8 = [0_u8; 10] + let signed64: Maybe> = std.codec.signedVarintEncode64(signed64_buf, -9223372036854775807_i64 - 1_i64) + var signed64_ok: Bool = false + if signed64.has { + let decoded_signed64: Maybe = std.codec.signedVarintDecode64(signed64.value) + signed64_ok = decoded_signed64.has && decoded_signed64.value == -9223372036854775807_i64 - 1_i64 + } + let invalid64: Maybe = std.codec.varintDecode64("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff") + if word.has && word.value == 16961 && number.has && number.value > 0_u32 && length == 2 && length64 == 10 && signed_length == 2 && signed_length64 == 10 && word64_le_ok && word64_be_ok && varint64_ok && signed_ok && signed64_ok && !invalid64.has { + check world.out.write("codec widths ok\n") + } +} diff --git a/conformance/native/pass/std-codec-widths.graph b/conformance/native/pass/std-codec-widths.graph new file mode 100644 index 0000000..9c1511c Binary files /dev/null and b/conformance/native/pass/std-codec-widths.graph differ diff --git a/conformance/native/pass/std-collections-algorithms.0 b/conformance/native/pass/std-collections-algorithms.0 new file mode 100644 index 0000000..3dccd5a --- /dev/null +++ b/conformance/native/pass/std-collections-algorithms.0 @@ -0,0 +1,17 @@ +pub fn main(world: World) -> Void raises { + var values: [5]i32 = [0, 0, 0, 0, 0] + let extra: [2]i32 = [4, 1] + var len: usize = 0 + len = std.collections.push(values, len, 3) + len = std.collections.push(values, len, 1) + len = std.collections.append(values, len, extra) + let remaining: usize = std.collections.remaining(values, len) + let full: Bool = std.collections.isFull(values, len) + std.sort.insertionI32(values[..len]) + let live: Span = std.collections.view(values, len) + let found: usize = std.search.binaryI32(live, 4) + let missing: usize = std.search.indexOf(live, 9) + if len == 4 && remaining == 1 && !full && std.mem.len(live) == 4 && std.collections.count(values, len, 1) == 2 && std.collections.contains(values, len, 3) && std.sort.isSortedI32(live) && found == 3 && missing == std.mem.len(live) { + check world.out.write("std collections algorithms ok\n") + } +} diff --git a/conformance/native/pass/std-collections-algorithms.graph b/conformance/native/pass/std-collections-algorithms.graph new file mode 100644 index 0000000..4fd08b8 Binary files /dev/null and b/conformance/native/pass/std-collections-algorithms.graph differ diff --git a/conformance/native/pass/std-collections-mutspan-memory.0 b/conformance/native/pass/std-collections-mutspan-memory.0 new file mode 100644 index 0000000..30b14b8 --- /dev/null +++ b/conformance/native/pass/std-collections-mutspan-memory.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var values: [4]u8 = [0, 0, 0, 0] + let dst: MutSpan = values + let len: usize = std.collections.push(dst, 0_usize, 1_u8) + if len == 1 && values[0] == 1_u8 { + check world.out.write("std collections mutspan memory ok\n") + } +} diff --git a/conformance/native/pass/std-collections-mutspan-memory.graph b/conformance/native/pass/std-collections-mutspan-memory.graph new file mode 100644 index 0000000..715cd70 Binary files /dev/null and b/conformance/native/pass/std-collections-mutspan-memory.graph differ diff --git a/conformance/native/pass/std-collections-query-memory.0 b/conformance/native/pass/std-collections-query-memory.0 new file mode 100644 index 0000000..2d9f7d1 --- /dev/null +++ b/conformance/native/pass/std-collections-query-memory.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let values: [2]u8 = [1_u8, 2_u8] + if std.collections.contains(values, 2_usize, 2_u8) { + check world.out.write("std collections query memory ok\n") + } +} diff --git a/conformance/native/pass/std-collections-query-memory.graph b/conformance/native/pass/std-collections-query-memory.graph new file mode 100644 index 0000000..ffc2c80 Binary files /dev/null and b/conformance/native/pass/std-collections-query-memory.graph differ diff --git a/conformance/native/pass/std-collections-u8.0 b/conformance/native/pass/std-collections-u8.0 new file mode 100644 index 0000000..0a418de --- /dev/null +++ b/conformance/native/pass/std-collections-u8.0 @@ -0,0 +1,196 @@ +alias CollectionByte = u8 +alias CollectionScore = u16 +alias CollectionSet = FixedSet +alias CollectionDeque = FixedDeque +alias CollectionRing = FixedRingBuffer +alias CollectionMap = FixedMap + +pub fn main(world: World) -> Void raises { + var values: [4]u8 = [1, 2, 3, 4] + var len: usize = 4 + let duplicate: usize = std.collections.setInsert(values, len, 3_u8) + len = std.collections.setRemove(values, len, 2_u8) + let inserted_len: usize = std.collections.insertAt(values, len, 1_usize, 8_u8) + let removed_len: usize = std.collections.removeAt(values, inserted_len, 1_usize) + len = removed_len + len = std.collections.setInsert(values, len, 5_u8) + let has_five: Bool = std.collections.setContains(values, len, 5_u8) + let first_value: Maybe = std.collections.first(values, len) + let last_value: Maybe = std.collections.last(values, len) + let popped_len: usize = std.collections.pop(values, len) + let cleared: usize = std.collections.clear(values, len) + let truncate_same: usize = std.collections.truncate(values, len, 4_usize) + let truncate_smaller: usize = std.collections.truncate(values, len, 3_usize) + len = std.collections.moveToFront(values, len, 2) + len = std.collections.removeSwap(values, len, 1) + let set_live: Span = std.collections.setView(values, len) + let set_remaining: usize = std.collections.setRemaining(values, len) + let set_full: Bool = std.collections.setIsFull(values, len) + let set_truncated: usize = std.collections.setTruncate(values, len, 2_usize) + let set_cleared: usize = std.collections.setClear(values, len) + var fixed_set_storage: [4]u8 = [1_u8, 2_u8, 0_u8, 0_u8] + var fixed_set: FixedSet = std.collections.fixedSet(fixed_set_storage, 2_usize) + let fixed_set_inserted: Bool = std.collections.fixedSetInsert(&mut fixed_set, 3_u8) + let fixed_set_duplicate: Bool = std.collections.fixedSetInsert(&mut fixed_set, 3_u8) + let fixed_set_removed: Bool = std.collections.fixedSetRemove(&mut fixed_set, 1_u8) + let fixed_set_missing_remove: Bool = std.collections.fixedSetRemove(&mut fixed_set, 9_u8) + let fixed_set_has_two: Bool = std.collections.fixedSetContains(&fixed_set, 2_u8) + let fixed_set_live: Span = std.collections.fixedSetView(&fixed_set) + let fixed_set_remaining: usize = std.collections.fixedSetRemaining(&fixed_set) + let fixed_set_full: Bool = std.collections.fixedSetIsFull(&fixed_set) + let fixed_set_len: usize = std.collections.fixedSetLen(&fixed_set) + let fixed_set_truncated: usize = std.collections.fixedSetTruncate(&mut fixed_set, 1_usize) + let fixed_set_cleared: usize = std.collections.fixedSetClear(&mut fixed_set) + var fixed_set_clamp_storage: [4]u8 = [1_u8, 2_u8, 3_u8, 4_u8] + var fixed_set_clamped: FixedSet = std.collections.fixedSet(fixed_set_clamp_storage, 9_usize) + let fixed_set_clamped_len: usize = std.collections.fixedSetLen(&fixed_set_clamped) + var fixed_map_keys: [3]u8 = [1_u8, 2_u8, 0_u8] + var fixed_map_scores: [3]u16 = [10_u16, 20_u16, 0_u16] + var fixed_map: FixedMap = std.collections.fixedMap(fixed_map_keys, fixed_map_scores, 2_usize) + let fixed_map_start_len: usize = std.collections.fixedMapLen(&fixed_map) + let fixed_map_start_remaining: usize = std.collections.fixedMapRemaining(&fixed_map) + let fixed_map_added: Bool = std.collections.fixedMapPut(&mut fixed_map, 3_u8, 30_u16) + let fixed_map_updated: Bool = std.collections.fixedMapPut(&mut fixed_map, 2_u8, 25_u16) + let fixed_map_overflow: Bool = std.collections.fixedMapPut(&mut fixed_map, 4_u8, 40_u16) + let fixed_map_has_two: Bool = std.collections.fixedMapContains(&fixed_map, 2_u8) + let fixed_map_has_nine: Bool = std.collections.fixedMapContains(&fixed_map, 9_u8) + let fixed_map_found: Maybe = std.collections.fixedMapGet(&fixed_map, 2_u8) + let fixed_map_missing: Maybe = std.collections.fixedMapGet(&fixed_map, 9_u8) + let fixed_map_two_index: usize = std.collections.fixedMapIndex(&fixed_map, 2_u8) + let fixed_map_missing_index: usize = std.collections.fixedMapIndex(&fixed_map, 9_u8) + let fixed_map_live_keys: Span = std.collections.fixedMapKeys(&fixed_map) + let fixed_map_live_scores: Span = std.collections.fixedMapValues(&fixed_map) + let fixed_map_full: Bool = std.collections.fixedMapIsFull(&fixed_map) + let fixed_map_removed: Bool = std.collections.fixedMapRemove(&mut fixed_map, 1_u8) + let fixed_map_missing_remove: Bool = std.collections.fixedMapRemove(&mut fixed_map, 9_u8) + let fixed_map_after_remove_len: usize = std.collections.fixedMapLen(&fixed_map) + let fixed_map_truncated: usize = std.collections.fixedMapTruncate(&mut fixed_map, 1_usize) + let fixed_map_cleared: usize = std.collections.fixedMapClear(&mut fixed_map) + var fixed_map_clamp_keys: [2]u8 = [1_u8, 2_u8] + var fixed_map_clamp_scores: [1]u16 = [10_u16] + var fixed_map_clamped: FixedMap = std.collections.fixedMap(fixed_map_clamp_keys, fixed_map_clamp_scores, 9_usize) + let fixed_map_clamped_len: usize = std.collections.fixedMapLen(&fixed_map_clamped) + var fixed_alias_set_storage: [2]CollectionByte = [1_u8, 0_u8] + var fixed_alias_set: CollectionSet = std.collections.fixedSet(fixed_alias_set_storage, 1_usize) + let fixed_alias_set_live: Span = std.collections.fixedSetView(&fixed_alias_set) + let fixed_alias_set_inserted: Bool = std.collections.fixedSetInsert(&mut fixed_alias_set, 2_u8) + var fixed_alias_deque_storage: [2]CollectionByte = [0_u8, 0_u8] + var fixed_alias_deque: CollectionDeque = std.collections.fixedDeque(fixed_alias_deque_storage, 0_usize) + let fixed_alias_deque_pushed: Bool = std.collections.fixedDequePushBack(&mut fixed_alias_deque, 7_u8) + let fixed_alias_deque_back: Maybe = std.collections.fixedDequeBack(&fixed_alias_deque) + var fixed_alias_ring_storage: [2]CollectionByte = [0_u8, 0_u8] + var fixed_alias_ring: CollectionRing = std.collections.fixedRingBuffer(fixed_alias_ring_storage, 0_usize, 0_usize) + let fixed_alias_ring_pushed: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_alias_ring, 8_u8) + let fixed_alias_ring_front: Maybe = std.collections.fixedRingBufferFront(&fixed_alias_ring) + var fixed_alias_map_keys: [2]CollectionByte = [1_u8, 0_u8] + var fixed_alias_map_scores: [2]CollectionScore = [10_u16, 0_u16] + var fixed_alias_map: CollectionMap = std.collections.fixedMap(fixed_alias_map_keys, fixed_alias_map_scores, 1_usize) + let fixed_alias_map_put: Bool = std.collections.fixedMapPut(&mut fixed_alias_map, 2_u8, 20_u16) + let fixed_alias_map_found: Maybe = std.collections.fixedMapGet(&fixed_alias_map, 2_u8) + let fixed_alias_ok: Bool = std.mem.len(fixed_alias_set_live) == 1 && fixed_alias_set_live[0] == 1_u8 && fixed_alias_set_inserted && fixed_alias_deque_pushed && fixed_alias_deque_back.has && fixed_alias_deque_back.value == 7_u8 && fixed_alias_ring_pushed && fixed_alias_ring_front.has && fixed_alias_ring_front.value == 8_u8 && fixed_alias_map_put && fixed_alias_map_found.has && fixed_alias_map_found.value == 20_u16 + let absent_remove: usize = std.collections.setRemove(values, len, 9_u8) + let invalid_remove: usize = std.collections.removeSwap(values, 9_usize, 0_usize) + let invalid_move: usize = std.collections.moveToFront(values, 9_usize, 1_usize) + let replaced_item: Bool = std.collections.replaceAt(values, len, 1_usize, 6_u8) + let swapped_items: Bool = std.collections.swapAt(values, len, 1_usize, 2_usize) + let invalid_replace: Bool = std.collections.replaceAt(values, len, 9_usize, 1_u8) + let invalid_swap: Bool = std.collections.swapAt(values, 9_usize, 0_usize, 1_usize) + var transform: [4]u8 = [1_u8, 2_u8, 3_u8, 4_u8] + let reversed_items: Bool = std.collections.reverse(transform, 4_usize) + let filled_items: Bool = std.collections.fill(transform, 2_usize, 9_u8) + let invalid_reverse: Bool = std.collections.reverse(transform, 9_usize) + let invalid_fill: Bool = std.collections.fill(transform, 9_usize, 1_u8) + let rotated_left: Bool = std.collections.rotateLeft(transform, 4_usize, 1_usize) + let rotated_right: Bool = std.collections.rotateRight(transform, 4_usize, 2_usize) + let invalid_rotate_left: Bool = std.collections.rotateLeft(transform, 9_usize, 1_usize) + let invalid_rotate_right: Bool = std.collections.rotateRight(transform, 9_usize, 1_usize) + let live: Span = std.collections.view(values, len) + let first: usize = std.search.indexOf(live, 3_u8) + let last: usize = std.search.lastIndexOf(live, 4_u8) + var deque_items: [4]u8 = [0_u8, 0_u8, 0_u8, 0_u8] + var deque_len: usize = 0 + deque_len = std.collections.dequePushBack(deque_items, deque_len, 2_u8) + deque_len = std.collections.dequePushFront(deque_items, deque_len, 1_u8) + deque_len = std.collections.dequePushBack(deque_items, deque_len, 3_u8) + let deque_front: Maybe = std.collections.dequeFront(deque_items, deque_len) + let deque_back: Maybe = std.collections.dequeBack(deque_items, deque_len) + let deque_after_pop_back: usize = std.collections.dequePopBack(deque_items, deque_len) + deque_len = std.collections.dequePopFront(deque_items, deque_after_pop_back) + var fixed_deque_storage: [4]u8 = [0_u8, 0_u8, 0_u8, 0_u8] + var fixed_deque: FixedDeque = std.collections.fixedDeque(fixed_deque_storage, 0_usize) + let fixed_deque_push_back: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 2_u8) + let fixed_deque_push_front: Bool = std.collections.fixedDequePushFront(&mut fixed_deque, 1_u8) + let fixed_deque_push_back_again: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 3_u8) + let fixed_deque_len: usize = std.collections.fixedDequeLen(&fixed_deque) + let fixed_deque_remaining: usize = std.collections.fixedDequeRemaining(&fixed_deque) + let fixed_deque_front: Maybe = std.collections.fixedDequeFront(&fixed_deque) + let fixed_deque_back: Maybe = std.collections.fixedDequeBack(&fixed_deque) + let fixed_deque_live: Span = std.collections.fixedDequeView(&fixed_deque) + let fixed_deque_live_len: usize = std.mem.len(fixed_deque_live) + let fixed_deque_live_middle: u8 = fixed_deque_live[1] + let fixed_deque_push_to_full: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 4_u8) + let fixed_deque_full: Bool = std.collections.fixedDequeIsFull(&fixed_deque) + let fixed_deque_overflow: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 5_u8) + let fixed_deque_pop_back: Maybe = std.collections.fixedDequePopBack(&mut fixed_deque) + let fixed_deque_pop_front: Maybe = std.collections.fixedDequePopFront(&mut fixed_deque) + let fixed_deque_after_pop_len: usize = std.collections.fixedDequeLen(&fixed_deque) + let fixed_deque_truncated: usize = std.collections.fixedDequeTruncate(&mut fixed_deque, 1_usize) + let fixed_deque_cleared: usize = std.collections.fixedDequeClear(&mut fixed_deque) + var fixed_deque_clamp_storage: [4]u8 = [1_u8, 2_u8, 3_u8, 4_u8] + var fixed_deque_clamped: FixedDeque = std.collections.fixedDeque(fixed_deque_clamp_storage, 9_usize) + let fixed_deque_clamped_len: usize = std.collections.fixedDequeLen(&fixed_deque_clamped) + var fixed_ring_storage: [4]u8 = [0_u8, 0_u8, 0_u8, 0_u8] + var fixed_ring: FixedRingBuffer = std.collections.fixedRingBuffer(fixed_ring_storage, 0_usize, 0_usize) + let fixed_ring_push_back: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 2_u8) + let fixed_ring_push_back_again: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 3_u8) + let fixed_ring_push_front: Bool = std.collections.fixedRingBufferPushFront(&mut fixed_ring, 1_u8) + let fixed_ring_len: usize = std.collections.fixedRingBufferLen(&fixed_ring) + let fixed_ring_capacity: usize = std.collections.fixedRingBufferCapacity(&fixed_ring) + let fixed_ring_remaining: usize = std.collections.fixedRingBufferRemaining(&fixed_ring) + let fixed_ring_front: Maybe = std.collections.fixedRingBufferFront(&fixed_ring) + let fixed_ring_back: Maybe = std.collections.fixedRingBufferBack(&fixed_ring) + let fixed_ring_middle: Maybe = std.collections.fixedRingBufferGet(&fixed_ring, 1_usize) + let fixed_ring_pop_front: Maybe = std.collections.fixedRingBufferPopFront(&mut fixed_ring) + let fixed_ring_push_back_wrap: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 4_u8) + let fixed_ring_push_back_to_full: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 5_u8) + let fixed_ring_full: Bool = std.collections.fixedRingBufferIsFull(&fixed_ring) + let fixed_ring_overflow: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 6_u8) + let fixed_ring_pop_back: Maybe = std.collections.fixedRingBufferPopBack(&mut fixed_ring) + let fixed_ring_after_wrap: Maybe = std.collections.fixedRingBufferGet(&fixed_ring, 2_usize) + let fixed_ring_truncated: usize = std.collections.fixedRingBufferTruncate(&mut fixed_ring, 2_usize) + let fixed_ring_cleared: usize = std.collections.fixedRingBufferClear(&mut fixed_ring) + var fixed_ring_clamp_storage: [4]u8 = [1_u8, 2_u8, 3_u8, 4_u8] + var fixed_ring_clamped: FixedRingBuffer = std.collections.fixedRingBuffer(fixed_ring_clamp_storage, 9_usize, 9_usize) + let fixed_ring_clamped_len: usize = std.collections.fixedRingBufferLen(&fixed_ring_clamped) + var fixed_ring_empty_storage: [4]u8 = [0_u8, 0_u8, 0_u8, 0_u8] + var fixed_ring_empty: FixedRingBuffer = std.collections.fixedRingBuffer(fixed_ring_empty_storage, 9_usize, 0_usize) + let fixed_ring_empty_pushed: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring_empty, 7_u8) + var keys: [4]u8 = [1_u8, 2_u8, 3_u8, 0_u8] + var scores: [4]u16 = [10_u16, 20_u16, 30_u16, 0_u16] + var map_len: usize = 3 + let map_start_remaining: usize = std.collections.mapRemaining(keys, scores, map_len) + let map_start_full: Bool = std.collections.mapIsFull(keys, scores, map_len) + let map_truncated: usize = std.collections.mapTruncate(keys, scores, map_len, 2_usize) + let map_cleared: usize = std.collections.mapClear(keys, scores, map_len) + let two_index: usize = std.collections.mapIndex(keys, map_len, 2_u8) + let found: Maybe = std.collections.mapGet(keys, scores, map_len, 2_u8) + let missing: Maybe = std.collections.mapGet(keys, scores, map_len, 9_u8) + let has_two: Bool = std.collections.mapContains(keys, map_len, 2_u8) + let has_nine: Bool = std.collections.mapContains(keys, map_len, 9_u8) + map_len = std.collections.mapPut(keys, scores, map_len, 4_u8, 40_u16) + let map_remaining_after_put: usize = std.collections.mapRemaining(keys, scores, map_len) + let map_full_after_put: Bool = std.collections.mapIsFull(keys, scores, map_len) + let added: Maybe = std.collections.mapGet(keys, scores, map_len, 4_u8) + let full_put: usize = std.collections.mapPut(keys, scores, map_len, 5_u8, 50_u16) + map_len = std.collections.mapPut(keys, scores, map_len, 2_u8, 25_u16) + let updated: Maybe = std.collections.mapGet(keys, scores, map_len, 2_u8) + let live_keys: Span = std.collections.mapKeys(keys, map_len) + let live_scores: Span = std.collections.mapValues(keys, scores, map_len) + let live_key_added: u8 = live_keys[3] + let live_score_updated: u16 = live_scores[1] + map_len = std.collections.mapRemove(keys, scores, map_len, 2_u8) + let removed_index: usize = std.collections.mapIndex(keys, map_len, 2_u8) + if len == 3 && duplicate == 4 && inserted_len == 4 && removed_len == 3 && has_five && first_value.has && first_value.value == 1_u8 && last_value.has && last_value.value == 5_u8 && popped_len == 3 && cleared == 0 && truncate_same == 4 && truncate_smaller == 3 && std.mem.len(set_live) == 3 && set_live[0] == 3_u8 && set_live[1] == 4_u8 && set_remaining == 1 && !set_full && set_truncated == 2 && set_cleared == 0 && fixed_set_inserted && !fixed_set_duplicate && fixed_set_removed && !fixed_set_missing_remove && fixed_set_has_two && std.mem.len(fixed_set_live) == 2 && fixed_set_live[0] == 3_u8 && fixed_set_live[1] == 2_u8 && fixed_set_remaining == 2 && !fixed_set_full && fixed_set_len == 2 && fixed_set_truncated == 1 && fixed_set_cleared == 0 && fixed_set_clamped_len == 4 && fixed_map_start_len == 2 && fixed_map_start_remaining == 1 && fixed_map_added && fixed_map_updated && !fixed_map_overflow && fixed_map_has_two && !fixed_map_has_nine && fixed_map_found.has && fixed_map_found.value == 25_u16 && !fixed_map_missing.has && fixed_map_two_index == 1 && fixed_map_missing_index == 3 && std.mem.len(fixed_map_live_keys) == 3 && fixed_map_live_keys[2] == 3_u8 && std.mem.len(fixed_map_live_scores) == 3 && fixed_map_live_scores[1] == 25_u16 && fixed_map_full && fixed_map_removed && !fixed_map_missing_remove && fixed_map_after_remove_len == 2 && fixed_map_truncated == 1 && fixed_map_cleared == 0 && fixed_map_clamped_len == 1 && fixed_alias_ok && absent_remove == 3 && invalid_remove == 9 && invalid_move == 9 && replaced_item && swapped_items && !invalid_replace && !invalid_swap && reversed_items && filled_items && rotated_left && rotated_right && !invalid_reverse && !invalid_fill && !invalid_rotate_left && !invalid_rotate_right && transform[0] == 1_u8 && transform[1] == 9_u8 && transform[2] == 9_u8 && transform[3] == 2_u8 && values[0] == 3_u8 && values[1] == 4_u8 && values[2] == 6_u8 && first == 0 && last == 1 && deque_front.has && deque_front.value == 1_u8 && deque_back.has && deque_back.value == 3_u8 && deque_after_pop_back == 2 && deque_len == 1 && deque_items[0] == 2_u8 && fixed_deque_push_back && fixed_deque_push_front && fixed_deque_push_back_again && fixed_deque_len == 3 && fixed_deque_remaining == 1 && fixed_deque_front.has && fixed_deque_front.value == 1_u8 && fixed_deque_back.has && fixed_deque_back.value == 3_u8 && fixed_deque_live_len == 3 && fixed_deque_live_middle == 2_u8 && fixed_deque_push_to_full && fixed_deque_full && !fixed_deque_overflow && fixed_deque_pop_back.has && fixed_deque_pop_back.value == 4_u8 && fixed_deque_pop_front.has && fixed_deque_pop_front.value == 1_u8 && fixed_deque_after_pop_len == 2 && fixed_deque_storage[0] == 2_u8 && fixed_deque_truncated == 1 && fixed_deque_cleared == 0 && fixed_deque_clamped_len == 4 && fixed_ring_push_back && fixed_ring_push_back_again && fixed_ring_push_front && fixed_ring_len == 3 && fixed_ring_capacity == 4 && fixed_ring_remaining == 1 && fixed_ring_front.has && fixed_ring_front.value == 1_u8 && fixed_ring_back.has && fixed_ring_back.value == 3_u8 && fixed_ring_middle.has && fixed_ring_middle.value == 2_u8 && fixed_ring_pop_front.has && fixed_ring_pop_front.value == 1_u8 && fixed_ring_push_back_wrap && fixed_ring_push_back_to_full && fixed_ring_full && !fixed_ring_overflow && fixed_ring_pop_back.has && fixed_ring_pop_back.value == 5_u8 && fixed_ring_after_wrap.has && fixed_ring_after_wrap.value == 4_u8 && fixed_ring_truncated == 2 && fixed_ring_cleared == 0 && fixed_ring_storage[0] == 2_u8 && fixed_ring_storage[2] == 4_u8 && fixed_ring_clamped_len == 4 && fixed_ring_empty_pushed && fixed_ring_empty_storage[0] == 7_u8 && fixed_ring_empty_storage[1] == 0_u8 && map_start_remaining == 1 && !map_start_full && map_truncated == 2 && map_cleared == 0 && two_index == 1 && found.has && found.value == 20_u16 && !missing.has && has_two && !has_nine && map_remaining_after_put == 0 && map_full_after_put && added.has && added.value == 40_u16 && full_put == 4 && updated.has && updated.value == 25_u16 && std.mem.len(live_keys) == 4 && live_key_added == 4_u8 && std.mem.len(live_scores) == 4 && live_score_updated == 25_u16 && map_len == 3 && removed_index == 3 && keys[1] == 4_u8 && scores[1] == 40_u16 { + check world.out.write("std collections u8 ok\n") + } +} diff --git a/conformance/native/pass/std-collections-u8.graph b/conformance/native/pass/std-collections-u8.graph new file mode 100644 index 0000000..fc1f95b Binary files /dev/null and b/conformance/native/pass/std-collections-u8.graph differ diff --git a/conformance/native/pass/std-collections-usize-memory.0 b/conformance/native/pass/std-collections-usize-memory.0 new file mode 100644 index 0000000..4610264 --- /dev/null +++ b/conformance/native/pass/std-collections-usize-memory.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var values: [4]usize = [0_usize, 0_usize, 0_usize, 0_usize] + var len: usize = 0 + len = std.collections.push(values, len, 8_usize) + if len == 1 && values[0] == 8_usize { + check world.out.write("std collections usize memory ok\n") + } +} diff --git a/conformance/native/pass/std-collections-usize-memory.graph b/conformance/native/pass/std-collections-usize-memory.graph new file mode 100644 index 0000000..db6d106 Binary files /dev/null and b/conformance/native/pass/std-collections-usize-memory.graph differ diff --git a/conformance/native/pass/std-crypto-hmac32.0 b/conformance/native/pass/std-crypto-hmac32.0 new file mode 100644 index 0000000..f88ff0b --- /dev/null +++ b/conformance/native/pass/std-crypto-hmac32.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let hmac: u32 = std.crypto.hmac32(std.mem.span("key"), std.mem.span("message")) + if hmac != 0_u32 { + check world.out.write("crypto hmac32 ok\n") + } +} diff --git a/conformance/native/pass/std-crypto-hmac32.graph b/conformance/native/pass/std-crypto-hmac32.graph new file mode 100644 index 0000000..4757f0b Binary files /dev/null and b/conformance/native/pass/std-crypto-hmac32.graph differ diff --git a/conformance/native/pass/std-crypto-sha256.0 b/conformance/native/pass/std-crypto-sha256.0 new file mode 100644 index 0000000..11cf9a3 --- /dev/null +++ b/conformance/native/pass/std-crypto-sha256.0 @@ -0,0 +1,17 @@ +pub fn main(world: World) -> Void raises { + var abc_hex_buffer: [64]u8 = [0_u8; 64] + var empty_hex_buffer: [64]u8 = [0_u8; 64] + var hmac_hex_buffer: [64]u8 = [0_u8; 64] + var digest_buffer: [32]u8 = [0_u8; 32] + var hmac_digest_buffer: [32]u8 = [0_u8; 32] + var tiny_buffer: [31]u8 = [0_u8; 31] + let abc_hex: Maybe> = std.crypto.sha256Hex(abc_hex_buffer, std.mem.span("abc")) + let empty_hex: Maybe> = std.crypto.sha256Hex(empty_hex_buffer, std.mem.span("")) + let digest: Maybe> = std.crypto.sha256(digest_buffer, std.mem.span("abc")) + let hmac_hex: Maybe> = std.crypto.hmacSha256Hex(hmac_hex_buffer, std.mem.span("key"), std.mem.span("The quick brown fox jumps over the lazy dog")) + let hmac_digest: Maybe> = std.crypto.hmacSha256(hmac_digest_buffer, std.mem.span("key"), std.mem.span("The quick brown fox jumps over the lazy dog")) + let tiny_hmac: Maybe> = std.crypto.hmacSha256(tiny_buffer, std.mem.span("key"), std.mem.span("message")) + if abc_hex.has && empty_hex.has && digest.has && hmac_hex.has && hmac_digest.has && !tiny_hmac.has && std.mem.eql(abc_hex.value, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") && std.mem.eql(empty_hex.value, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") && std.mem.eql(hmac_hex.value, "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8") && std.mem.len(digest.value) == 32 && digest.value[0] == 186_u8 && digest.value[1] == 120_u8 && digest.value[30] == 21_u8 && digest.value[31] == 173_u8 && std.mem.len(hmac_digest.value) == 32 && hmac_digest.value[0] == 247_u8 && hmac_digest.value[1] == 188_u8 && hmac_digest.value[30] == 60_u8 && hmac_digest.value[31] == 216_u8 { + check world.out.write("crypto sha256 ok\n") + } +} diff --git a/conformance/native/pass/std-crypto-sha256.graph b/conformance/native/pass/std-crypto-sha256.graph new file mode 100644 index 0000000..1ee876f Binary files /dev/null and b/conformance/native/pass/std-crypto-sha256.graph differ diff --git a/conformance/native/pass/std-csv.0 b/conformance/native/pass/std-csv.0 new file mode 100644 index 0000000..8899e27 --- /dev/null +++ b/conformance/native/pass/std-csv.0 @@ -0,0 +1,115 @@ +pub fn main(world: World) -> Void raises { + var ok: Bool = true + + let input: Span = "name,quote\nAda,\"a,b\"\nBob,\"he said \"\"ok\"\"\"\n" + let count: Maybe = std.csv.recordCount(input) + if !std.csv.valid(input) || !count.has || count.value != 3_usize { + ok = false + } + + let header: Maybe> = std.csv.record(input, 0_usize) + let ada: Maybe> = std.csv.record(input, 1_usize) + let bob: Maybe> = std.csv.record(input, 2_usize) + let missing: Maybe> = std.csv.record(input, 3_usize) + if !header.has || !ada.has || !bob.has || missing.has { + ok = false + } + + if header.has { + let header_fields: Maybe = std.csv.fieldCount(header.value) + var left_buf: [8]u8 = [0_u8; 8] + var right_buf: [8]u8 = [0_u8; 8] + let left: Maybe> = std.csv.field(left_buf, header.value, 0_usize) + let right: Maybe> = std.csv.field(right_buf, header.value, 1_usize) + if !header_fields.has || header_fields.value != 2_usize || !left.has || !std.mem.eql(left.value, "name") || !right.has || !std.mem.eql(right.value, "quote") { + ok = false + } + } + + if ada.has { + let ada_fields: Maybe = std.csv.fieldCount(ada.value) + var name_buf: [8]u8 = [0_u8; 8] + var ada_quote_buf: [8]u8 = [0_u8; 8] + let name: Maybe> = std.csv.field(name_buf, ada.value, 0_usize) + let ada_quote: Maybe> = std.csv.field(ada_quote_buf, ada.value, 1_usize) + if !ada_fields.has || ada_fields.value != 2_usize || !name.has || !std.mem.eql(name.value, "Ada") || !ada_quote.has || !std.mem.eql(ada_quote.value, "a,b") { + ok = false + } + } + + if bob.has { + var bob_quote_buf: [16]u8 = [0_u8; 16] + let bob_quote: Maybe> = std.csv.field(bob_quote_buf, bob.value, 1_usize) + if !bob_quote.has || !std.mem.eql(bob_quote.value, "he said \"ok\"") { + ok = false + } + } + + let multiline: Span = "id,note\n1,\"line one\nline two\"\n" + let multiline_count: Maybe = std.csv.recordCount(multiline) + let multiline_record: Maybe> = std.csv.record(multiline, 1_usize) + if !std.csv.valid(multiline) || !multiline_count.has || multiline_count.value != 2_usize || !multiline_record.has { + ok = false + } + if multiline_record.has { + var note_buf: [32]u8 = [0_u8; 32] + let note: Maybe> = std.csv.field(note_buf, multiline_record.value, 1_usize) + if !note.has || !std.mem.eql(note.value, "line one\nline two") { + ok = false + } + } + + let crlf_count: Maybe = std.csv.recordCount("a\r\nb") + let bare_cr_count: Maybe = std.csv.recordCount("a\rb") + var early_bad_buf: [8]u8 = [0_u8; 8] + let early_bad_field: Maybe> = std.csv.field(early_bad_buf, "ok,b\"ad", 0_usize) + var early_quoted_bad_buf: [8]u8 = [0_u8; 8] + let early_quoted_bad_field: Maybe> = std.csv.field(early_quoted_bad_buf, "\"ok\",b\"ad", 0_usize) + if std.csv.valid("a,\"unterminated") || std.csv.valid("a\"b") || !std.csv.valid("a\r\nb") || !crlf_count.has || crlf_count.value != 2_usize || std.csv.valid("a\rb") || bare_cr_count.has || early_bad_field.has || early_quoted_bad_field.has { + ok = false + } + let bad_count: Maybe = std.csv.fieldCount("a,\"unterminated") + var bad_buf: [8]u8 = [0_u8; 8] + let bad_field: Maybe> = std.csv.field(bad_buf, "a,\"unterminated", 1_usize) + if bad_count.has || bad_field.has { + ok = false + } + + let trailing_record: Span = "\"a\"," + let trailing_count: Maybe = std.csv.fieldCount(trailing_record) + var trailing_buf: [4]u8 = [0_u8; 4] + let trailing_empty: Maybe> = std.csv.field(trailing_buf, trailing_record, 1_usize) + var trailing_missing_buf: [4]u8 = [0_u8; 4] + let trailing_missing: Maybe> = std.csv.field(trailing_missing_buf, trailing_record, 2_usize) + var plain_missing_buf: [4]u8 = [0_u8; 4] + let plain_missing: Maybe> = std.csv.field(plain_missing_buf, "a", 1_usize) + if !trailing_count.has || trailing_count.value != 2_usize || !trailing_empty.has || std.mem.len(trailing_empty.value) != 0_usize || trailing_missing.has || plain_missing.has { + ok = false + } + + var field_buf: [8]u8 = [0_u8; 8] + let field: Maybe> = std.csv.writeField(field_buf, "a,b") + var quote_field_buf: [12]u8 = [0_u8; 12] + let quote_field: Maybe> = std.csv.writeField(quote_field_buf, "a\"b") + var tiny_field_buf: [3]u8 = [0_u8; 3] + let tiny_field: Maybe> = std.csv.writeField(tiny_field_buf, "a,b") + if std.csv.encodedFieldLen("plain") != 5_usize || std.csv.encodedFieldLen("a,b") != 5_usize || std.csv.encodedFieldLen("a\"b") != 6_usize || !field.has || !std.mem.eql(field.value, "\"a,b\"") || !quote_field.has || !std.mem.eql(quote_field.value, "\"a\"\"b\"") || tiny_field.has { + ok = false + } + + var record2_buf: [24]u8 = [0_u8; 24] + let record2: Maybe> = std.csv.writeRecord2(record2_buf, "Ada", "a,b") + var record3_buf: [24]u8 = [0_u8; 24] + let record3: Maybe> = std.csv.writeRecord3(record3_buf, "x", "y\"z", "") + var small_record_buf: [8]u8 = [0_u8; 8] + let small_record: Maybe> = std.csv.writeRecord2(small_record_buf, "Ada", "a,b") + if !record2.has || !std.mem.eql(record2.value, "Ada,\"a,b\"\n") || !record3.has || !std.mem.eql(record3.value, "x,\"y\"\"z\",\n") || small_record.has { + ok = false + } + + if ok { + check world.out.write("std csv ok\n") + return + } + check world.err.write("std csv failed\n") +} diff --git a/conformance/native/pass/std-csv.graph b/conformance/native/pass/std-csv.graph new file mode 100644 index 0000000..b982252 Binary files /dev/null and b/conformance/native/pass/std-csv.graph differ diff --git a/conformance/native/pass/std-data-formats.0 b/conformance/native/pass/std-data-formats.0 new file mode 100644 index 0000000..0abbf8e --- /dev/null +++ b/conformance/native/pass/std-data-formats.0 @@ -0,0 +1,34 @@ +pub fn main(world: World) -> Void raises { + var arena_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parse(arena, "{\"ok\":true}") + let input: Span = "{\"name\":\"zero\",\"count\":42,\"ok\":true}" + var name_buf: [8]u8 = [0; 8] + let name: Maybe> = std.json.string(name_buf, input, "name") + let count: Maybe = std.json.u32(input, "count") + let ok: Maybe = std.json.bool(input, "ok") + var json_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let json_text: Maybe = std.json.writeString(json_buf, "zero") + var json_obj_buf: [24]u8 = [0; 24] + let json_obj: Maybe> = std.json.writeObject1U32(json_obj_buf, "count", 42) + var hex_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let hex: Maybe = std.codec.hexEncode(hex_buf, std.mem.span("Az")) + var hex_decode_buf: [2]u8 = [0; 2] + let hex_decoded: Maybe> = std.codec.hexDecode(hex_decode_buf, "417a") + var b64_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let b64: Maybe = std.codec.base64Encode(b64_buf, std.mem.span("abc")) + var b64_decode_buf: [3]u8 = [0; 3] + let b64_decoded: Maybe> = std.codec.base64Decode(b64_decode_buf, "YWJj") + var url_buf: [24]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let url: Maybe = std.codec.urlEncode(url_buf, "a b") + var query_buf: [16]u8 = [0; 16] + let query_part: Maybe> = std.url.writeQueryParam(query_buf, "q", "zero lang") + var full_url_buf: [48]u8 = [0; 48] + var full_url: Maybe> = null + if query_part.has { + full_url = std.url.appendQuery(full_url_buf, "https://example.com/search", query_part.value) + } + if std.json.validate("{\"ok\":true}") && parsed.has && std.json.streamTokens("{\"ok\":true}") == 3 && json_text.has && std.mem.eql(json_text.value, "\"zero\"") && name.has && std.mem.eql(name.value, "zero") && count.has && count.value == 42 && ok.has && ok.value && json_obj.has && std.mem.eql(json_obj.value, "{\"count\":42}") && std.codec.base64EncodedLen(3) == 4 && b64.has && std.mem.eql(b64.value, "YWJj") && b64_decoded.has && std.mem.eql(b64_decoded.value, "abc") && hex.has && std.mem.eql(hex.value, "417a") && hex_decoded.has && std.mem.eql(hex_decoded.value, "Az") && std.codec.utf8Valid(std.mem.span("ascii")) && url.has && std.mem.eql(url.value, "a%20b") && query_part.has && full_url.has && std.mem.eql(full_url.value, "https://example.com/search?q=zero+lang") && std.mem.eql(std.json.decodeBoundary(), "typed-decode-explicit-shape") { + check world.out.write("std data formats ok\n") + } +} diff --git a/conformance/native/pass/std-data-formats.graph b/conformance/native/pass/std-data-formats.graph new file mode 100644 index 0000000..c111b70 Binary files /dev/null and b/conformance/native/pass/std-data-formats.graph differ diff --git a/conformance/native/pass/std-diag.0 b/conformance/native/pass/std-diag.0 new file mode 100644 index 0000000..226ce00 --- /dev/null +++ b/conformance/native/pass/std-diag.0 @@ -0,0 +1,73 @@ +fn diag_locations_ok() -> Bool { + let source: Span = "alpha\nbeta\r\ngamma" + if std.diag.line(source, 0) != 1 { + return false + } + if std.diag.line(source, 6) != 2 { + return false + } + if std.diag.column(source, 7) != 2 { + return false + } + if std.diag.lineStart(source, 7) != 6 { + return false + } + if std.diag.lineEnd(source, 7) != 10 { + return false + } + if !std.mem.eql(std.diag.lineText(source, 7), "beta") { + return false + } + if std.diag.line(source, 100) != 3 { + return false + } + if std.diag.column(source, 100) != 6 { + return false + } + return true +} + +fn diag_ranges_ok() -> Bool { + let source: Span = "0123456789" + if std.diag.rangeLen(source, 2, 5) != 3 { + return false + } + if std.diag.rangeLen(source, 8, 4) != 0 { + return false + } + if std.diag.rangeLen(source, 8, 40) != 2 { + return false + } + if !std.mem.eql(std.diag.rangeText(source, 2, 5), "234") { + return false + } + if !std.mem.eql(std.diag.rangeText(source, 8, 4), "") { + return false + } + return std.mem.eql(std.diag.rangeText(source, 8, 40), "89") +} + +fn diag_format_ok() -> Bool { + let source: Span = "alpha\nbeta\r\ngamma" + var storage: [32]u8 = [0_u8; 32] + let formatted: Maybe> = std.diag.formatOffsetLocation(storage, "input.0", source, 7) + var direct_storage: [32]u8 = [0_u8; 32] + let direct: Maybe> = std.diag.formatLocation(direct_storage, "input.0", 2, 2) + var small_storage: [4]u8 = [0_u8; 4] + let small: Maybe> = std.diag.formatLocation(small_storage, "input.0", 2, 2) + if !formatted.has || !direct.has { + return false + } + if small.has { + return false + } + return std.mem.eql(formatted.value, "input.0:2:2") && std.mem.eql(direct.value, "input.0:2:2") +} + +pub fn main(world: World) -> Void raises { + if diag_locations_ok() && diag_ranges_ok() && diag_format_ok() { + check world.out.write("std diag ok\n") + } else { + check world.out.write("std diag failed\n") + } +} diff --git a/conformance/native/pass/std-diag.graph b/conformance/native/pass/std-diag.graph new file mode 100644 index 0000000..e5ceacd Binary files /dev/null and b/conformance/native/pass/std-diag.graph differ diff --git a/conformance/native/pass/std-env.0 b/conformance/native/pass/std-env.0 new file mode 100644 index 0000000..f0b37f9 --- /dev/null +++ b/conformance/native/pass/std-env.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let value: Maybe = std.env.get("ZERO_CONFORMANCE_ENV") + let missing: Maybe = std.env.get("ZERO_CONFORMANCE_ENV_MISSING") + let verbose: Bool = std.env.parseBoolOr("ZERO_CONFORMANCE_ENV_VERBOSE", true) + let delta: i32 = std.env.parseI32Or("ZERO_CONFORMANCE_ENV_DELTA", 4) + let limit: u32 = std.env.parseU32Or("ZERO_CONFORMANCE_ENV_LIMIT", 7) + let workers: usize = std.env.parseUsizeOr("ZERO_CONFORMANCE_ENV_WORKERS", 2) + let exact_delta: Maybe = std.env.parseI32("ZERO_CONFORMANCE_ENV_DELTA") + let exact_workers: Maybe = std.env.parseUsize("ZERO_CONFORMANCE_ENV_WORKERS") + if value.has && std.mem.eql(value.value, "agent-env") && std.env.equals("ZERO_CONFORMANCE_ENV", "agent-env") && !missing.has && verbose && delta == -3 && exact_delta.has && exact_delta.value == -3 && limit == 7_u32 && workers == 5 && exact_workers.has && exact_workers.value == 5 { + check world.out.write("env ok\n") + } +} diff --git a/conformance/native/pass/std-env.graph b/conformance/native/pass/std-env.graph new file mode 100644 index 0000000..586de1f Binary files /dev/null and b/conformance/native/pass/std-env.graph differ diff --git a/conformance/native/pass/std-fmt.0 b/conformance/native/pass/std-fmt.0 new file mode 100644 index 0000000..e2b5ac8 --- /dev/null +++ b/conformance/native/pass/std-fmt.0 @@ -0,0 +1,45 @@ +pub fn main(world: World) -> Void raises { + var bool_buf: [5]u8 = [0_u8; 5] + var u32_buf: [10]u8 = [0_u8; 10] + var usize_buf: [8]u8 = [0_u8; 8] + var u64_buf: [20]u8 = [0_u8; 20] + var i32_buf: [12]u8 = [0_u8; 12] + var i64_buf: [20]u8 = [0_u8; 20] + var min_buf: [12]u8 = [0_u8; 12] + var hex_buf: [8]u8 = [0_u8; 8] + var base_buf: [16]u8 = [0_u8; 16] + var u64_base_buf: [16]u8 = [0_u8; 16] + var usize_base_buf: [16]u8 = [0_u8; 16] + var signed_base_buf: [16]u8 = [0_u8; 16] + var signed64_base_buf: [17]u8 = [0_u8; 17] + var sign_buf: [12]u8 = [0_u8; 12] + var sign64_buf: [20]u8 = [0_u8; 20] + var left_pad_buf: [8]u8 = [0_u8; 8] + var right_pad_buf: [8]u8 = [0_u8; 8] + var writer_storage: [128]u8 = [0_u8; 128] + var small: [2]u8 = [0_u8; 2] + let min_i64: i64 = 0 - 9223372036854775807 - 1 + let bool_text: Maybe> = std.fmt.bool(bool_buf, false) + let u32_text: Maybe> = std.fmt.u32(u32_buf, 4294967295_u32) + let usize_text: Maybe> = std.fmt.usize(usize_buf, 12345) + let u64_text: Maybe> = std.fmt.u64(u64_buf, 18446744073709551615_u64) + let i32_text: Maybe> = std.fmt.i32(i32_buf, -42) + let i64_text: Maybe> = std.fmt.i64(i64_buf, min_i64) + let min_text: Maybe> = std.fmt.i32(min_buf, 0 - 2147483647 - 1) + let hex_text: Maybe> = std.fmt.hexLowerU32(hex_buf, 48879_u32) + let base_text: Maybe> = std.fmt.u32Base(base_buf, 255_u32, 16_u32) + let u64_base_text: Maybe> = std.fmt.u64Base(u64_base_buf, 18446744073709551615_u64, 16_u32) + let usize_base_text: Maybe> = std.fmt.usizeBase(usize_base_buf, 10, 2_u32) + let signed_base_text: Maybe> = std.fmt.i32Base(signed_base_buf, -42, 16_u32) + let signed64_base_text: Maybe> = std.fmt.i64Base(signed64_base_buf, min_i64, 16_u32) + let sign_text: Maybe> = std.fmt.i32Sign(sign_buf, 42) + let sign64_text: Maybe> = std.fmt.i64Sign(sign64_buf, 9223372036854775807) + let left_pad_text: Maybe> = std.fmt.padLeft(left_pad_buf, "42", 5, 48_u8) + let right_pad_text: Maybe> = std.fmt.padRight(right_pad_buf, "go", 4, 46_u8) + var writer: FixedWriter = std.io.fixedWriter(writer_storage, 0) + let wrote: Bool = std.fmt.writeSpan(&mut writer, "n=") && std.fmt.writeI32(&mut writer, -42) && (std.fmt.writeSpan(&mut writer, " ok=") && std.fmt.writeBool(&mut writer, true)) && (std.fmt.writeSpan(&mut writer, " big=") && std.fmt.writeI64(&mut writer, min_i64)) && (std.fmt.writeSpan(&mut writer, " u32=") && std.fmt.writeU32(&mut writer, 7_u32)) && (std.fmt.writeSpan(&mut writer, " usize=") && std.fmt.writeUsize(&mut writer, 8)) && (std.fmt.writeSpan(&mut writer, " s32=") && std.fmt.writeI32Sign(&mut writer, 9)) && (std.fmt.writeSpan(&mut writer, " s64=") && std.fmt.writeI64Sign(&mut writer, 10)) && (std.fmt.writeSpan(&mut writer, " u64=") && std.fmt.writeU64(&mut writer, 11_u64)) + let overflow: Maybe> = std.fmt.u32(small, 1000_u32) + if bool_text.has && u32_text.has && usize_text.has && (u64_text.has && i32_text.has) && (i64_text.has && min_text.has) && hex_text.has && base_text.has && (u64_base_text.has && usize_base_text.has) && (signed_base_text.has && signed64_base_text.has) && (sign_text.has && sign64_text.has) && left_pad_text.has && right_pad_text.has && !overflow.has && wrote && std.mem.eql(std.io.fixedWriterView(&writer), "n=-42 ok=true big=-9223372036854775808 u32=7 usize=8 s32=+9 s64=+10 u64=11") && std.mem.eql(bool_text.value, "false") && std.mem.eql(u32_text.value, "4294967295") && std.mem.eql(usize_text.value, "12345") && std.mem.eql(u64_text.value, "18446744073709551615") && std.mem.eql(i32_text.value, "-42") && std.mem.eql(i64_text.value, "-9223372036854775808") && std.mem.eql(min_text.value, "-2147483648") && std.mem.eql(hex_text.value, "beef") && std.mem.eql(base_text.value, "ff") && std.mem.eql(u64_base_text.value, "ffffffffffffffff") && std.mem.eql(usize_base_text.value, "1010") && std.mem.eql(signed_base_text.value, "-2a") && std.mem.eql(signed64_base_text.value, "-8000000000000000") && std.mem.eql(sign_text.value, "+42") && std.mem.eql(sign64_text.value, "+9223372036854775807") && std.mem.eql(left_pad_text.value, "00042") && std.mem.eql(right_pad_text.value, "go..") { + check world.out.write("std fmt ok\n") + } +} diff --git a/conformance/native/pass/std-fmt.graph b/conformance/native/pass/std-fmt.graph new file mode 100644 index 0000000..bdc6229 Binary files /dev/null and b/conformance/native/pass/std-fmt.graph differ diff --git a/conformance/native/pass/std-fs-append-bytes.0 b/conformance/native/pass/std-fs-append-bytes.0 new file mode 100644 index 0000000..7a77c92 --- /dev/null +++ b/conformance/native/pass/std-fs-append-bytes.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let path: String = ".zero/conformance/std-fs-append-bytes.txt" + var read_buf: [32]u8 = [0_u8; 32] + let first: Maybe = std.fs.writeBytes(path, "alpha\n") + let second: Maybe = std.fs.appendBytes(path, "beta\n") + let explicit: Bool = std.fs.appendFile(fs, path, "gamma\n") + let text: Maybe> = std.fs.readFileBytes(fs, path, read_buf) + if first.has && first.value == 6_usize && second.has && second.value == 5_usize && explicit && text.has && std.mem.eql(text.value, "alpha\nbeta\ngamma\n") { + check world.out.write("fs append ok\n") + } +} diff --git a/conformance/native/pass/std-fs-append-bytes.graph b/conformance/native/pass/std-fs-append-bytes.graph new file mode 100644 index 0000000..b9dc0e9 Binary files /dev/null and b/conformance/native/pass/std-fs-append-bytes.graph differ diff --git a/conformance/native/pass/std-fs-breadth.0 b/conformance/native/pass/std-fs-breadth.0 new file mode 100644 index 0000000..5aba30a --- /dev/null +++ b/conformance/native/pass/std-fs-breadth.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let data: Span = std.mem.span("atomic data\n") + let wrote: Bool = std.fs.atomicWrite(".zero/conformance/std-fs-breadth.txt", ".zero/conformance/std-fs-breadth.tmp", data) + let ensured: Bool = std.fs.ensureDir(".zero/conformance") + let renamed: Bool = std.fs.rename(".zero/conformance/std-fs-breadth.txt", ".zero/conformance/std-fs-breadth-renamed.txt") + let restored: Bool = renamed && std.fs.rename(".zero/conformance/std-fs-breadth-renamed.txt", ".zero/conformance/std-fs-breadth.txt") + let entries: Maybe = std.fs.dirEntryCount("conformance/native/pass") + var tmp_buf: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let temp: Maybe = std.fs.tempName(tmp_buf, ".zero/conformance/std-fs") + if wrote && ensured && restored && std.fs.exists(".zero/conformance/std-fs-breadth.txt") && std.fs.isFile(".zero/conformance/std-fs-breadth.txt") && !std.fs.isFile(".zero/conformance") && entries.has && entries.value > 0 && temp.has { + check world.out.write("std fs breadth ok\n") + } +} diff --git a/conformance/native/pass/std-fs-breadth.graph b/conformance/native/pass/std-fs-breadth.graph new file mode 100644 index 0000000..b557bb2 Binary files /dev/null and b/conformance/native/pass/std-fs-breadth.graph differ diff --git a/conformance/native/pass/std-fs-bytes.0 b/conformance/native/pass/std-fs-bytes.0 new file mode 100644 index 0000000..febca19 --- /dev/null +++ b/conformance/native/pass/std-fs-bytes.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let bytes: [3]u8 = [65, 0, 66] + var read_buf: [3]u8 = [0, 0, 0] + var small_buf: [2]u8 = [0, 0] + let written: Maybe = std.fs.writeBytes(".zero/conformance/std-fs-bytes.bin", bytes) + let read: Maybe = std.fs.readBytes(".zero/conformance/std-fs-bytes.bin", read_buf) + let truncated: Maybe = std.fs.readBytes(".zero/conformance/std-fs-bytes.bin", small_buf) + let missing: Maybe = std.fs.readBytes(".zero/conformance/missing.bin", read_buf) + if written.has && written.value == 3 && read.has && read.value == 3 && read_buf[1] == 0_u8 && truncated.has && truncated.value == 3 && small_buf[0] == 65_u8 && !missing.has { + check world.err.write("fs bytes err ok\n") + check world.out.write("fs bytes ok\n") + } +} diff --git a/conformance/native/pass/std-fs-bytes.graph b/conformance/native/pass/std-fs-bytes.graph new file mode 100644 index 0000000..deccb8e Binary files /dev/null and b/conformance/native/pass/std-fs-bytes.graph differ diff --git a/conformance/native/pass/std-fs-dir-entry-name.0 b/conformance/native/pass/std-fs-dir-entry-name.0 new file mode 100644 index 0000000..83985e3 --- /dev/null +++ b/conformance/native/pass/std-fs-dir-entry-name.0 @@ -0,0 +1,24 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let parent_ready: Bool = std.fs.ensureDir(".zero/conformance") + let dir_ready: Bool = std.fs.ensureDir(".zero/conformance/dir-entry-name") + let wrote: Bool = std.fs.writeFile(fs, ".zero/conformance/dir-entry-name/alpha.txt", "ok") + let entries: Maybe = std.fs.dirEntryCount(".zero/conformance/dir-entry-name") + var name_buf: [128]u8 = [0_u8; 128] + var found: Bool = false + if parent_ready && dir_ready && wrote && entries.has { + var index: usize = 0_usize + while index < entries.value { + let name: Maybe> = std.fs.dirEntryName(name_buf, ".zero/conformance/dir-entry-name", index) + if name.has && std.mem.eql(name.value, "alpha.txt") { + found = true + } + index = index + 1_usize + } + } + if found { + check world.out.write("dir entry name ok\n") + } else { + check world.out.write("dir entry name failed\n") + } +} diff --git a/conformance/native/pass/std-fs-dir-entry-name.graph b/conformance/native/pass/std-fs-dir-entry-name.graph new file mode 100644 index 0000000..e2a6805 Binary files /dev/null and b/conformance/native/pass/std-fs-dir-entry-name.graph differ diff --git a/conformance/native/pass/std-fs-direct-return.0 b/conformance/native/pass/std-fs-direct-return.0 new file mode 100644 index 0000000..d2854bb --- /dev/null +++ b/conformance/native/pass/std-fs-direct-return.0 @@ -0,0 +1,19 @@ +fn write_direct(path: String) -> Maybe { + return std.fs.writeBytes(path, "direct\n") +} + +fn append_direct(path: String) -> Maybe { + return std.fs.appendBytes(path, "return\n") +} + +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let path: String = ".zero/conformance/std-fs-direct-return.txt" + var read_buf: [32]u8 = [0_u8; 32] + let first: Maybe = write_direct(path) + let second: Maybe = append_direct(path) + let text: Maybe> = std.fs.readFileBytes(fs, path, read_buf) + if first.has && first.value == 7_usize && second.has && second.value == 7_usize && text.has && std.mem.eql(text.value, "direct\nreturn\n") { + check world.out.write("fs direct return ok\n") + } +} diff --git a/conformance/native/pass/std-fs-direct-return.graph b/conformance/native/pass/std-fs-direct-return.graph new file mode 100644 index 0000000..0d6f8e8 Binary files /dev/null and b/conformance/native/pass/std-fs-direct-return.graph differ diff --git a/conformance/native/pass/std-fs-fallible-resources.0 b/conformance/native/pass/std-fs-fallible-resources.0 new file mode 100644 index 0000000..5a3b0c8 --- /dev/null +++ b/conformance/native/pass/std-fs-fallible-resources.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises [NotFound, TooLarge, Io] { + let fs: Fs = std.fs.host() + var created: owned = check std.fs.createOrRaise(fs, ".zero/conformance/std-fs-fallible-resources.txt") + check std.fs.writeAllOrRaise(&mut created, std.mem.span("named resource ok\n")) + let written_len: usize = check std.fs.fileLenOrRaise(&mut created) + std.fs.close(&mut created) + var opened: owned = check std.fs.openOrRaise(fs, ".zero/conformance/std-fs-fallible-resources.txt") + var buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let read_len: usize = check std.fs.readOrRaise(&mut opened, buf) + if written_len == 18 && read_len == 18 && buf[0] == 110 as u8 { + check world.out.write("fs fallible resources ok\n") + } +} diff --git a/conformance/native/pass/std-fs-fallible-resources.graph b/conformance/native/pass/std-fs-fallible-resources.graph new file mode 100644 index 0000000..74ce6e0 Binary files /dev/null and b/conformance/native/pass/std-fs-fallible-resources.graph differ diff --git a/conformance/native/pass/std-fs-fallible.0 b/conformance/native/pass/std-fs-fallible.0 new file mode 100644 index 0000000..37d6991 --- /dev/null +++ b/conformance/native/pass/std-fs-fallible.0 @@ -0,0 +1,16 @@ +pub fn main(world: World) -> Void raises [NotFound, TooLarge, Io] { + let fs: Fs = std.fs.host() + let created: Maybe> = std.fs.create(fs, ".zero/fallible-readall-input.txt") + if created.has { + var file: owned = created.value + if !std.fs.writeAll(&mut file, std.mem.span("fallible read all ok\n")) { + return + } + } + var storage: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + var body: owned = check std.fs.readAllOrRaise(alloc, fs, ".zero/fallible-readall-input.txt", 64) + if std.mem.eqlBytes(std.mem.bufBytes(&body), std.mem.span("fallible read all ok\n")) { + check world.out.write("fs named errors ok\n") + } +} diff --git a/conformance/native/pass/std-fs-fallible.graph b/conformance/native/pass/std-fs-fallible.graph new file mode 100644 index 0000000..248076f Binary files /dev/null and b/conformance/native/pass/std-fs-fallible.graph differ diff --git a/conformance/native/pass/std-fs-file-helpers.0 b/conformance/native/pass/std-fs-file-helpers.0 new file mode 100644 index 0000000..ef9cbe6 --- /dev/null +++ b/conformance/native/pass/std-fs-file-helpers.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let path: String = "conformance/fixtures/std-fs-read.txt" + var read_buf: [32]u8 = [0_u8; 32] + var verify_buf: [32]u8 = [0_u8; 32] + let read: Maybe> = std.fs.readFileBytes(fs, path, read_buf) + if read.has && std.mem.eqlBytes(read.value, std.mem.span("zero file\n")) { + if std.fs.readFileEquals(fs, path, verify_buf, "zero file\n") { + check world.out.write("std fs file helpers ok\n") + } + } +} diff --git a/conformance/native/pass/std-fs-file-helpers.graph b/conformance/native/pass/std-fs-file-helpers.graph new file mode 100644 index 0000000..1c212db Binary files /dev/null and b/conformance/native/pass/std-fs-file-helpers.graph differ diff --git a/conformance/native/pass/std-fs-polish.0 b/conformance/native/pass/std-fs-polish.0 new file mode 100644 index 0000000..534cb0f --- /dev/null +++ b/conformance/native/pass/std-fs-polish.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let path: String = "conformance/fixtures/std-fs-read.txt" + let size: Maybe = std.fs.fileSize(fs, path) + if size.has && size.value == 10 { + check world.out.write("fs polish ok\n") + } +} diff --git a/conformance/native/pass/std-fs-polish.graph b/conformance/native/pass/std-fs-polish.graph new file mode 100644 index 0000000..7cceb79 Binary files /dev/null and b/conformance/native/pass/std-fs-polish.graph differ diff --git a/conformance/native/pass/std-fs-read-chunks.0 b/conformance/native/pass/std-fs-read-chunks.0 new file mode 100644 index 0000000..4cd8613 --- /dev/null +++ b/conformance/native/pass/std-fs-read-chunks.0 @@ -0,0 +1,42 @@ +pub fn main(world: World) -> Void raises { + let path: String = "conformance/fixtures/std-fs-read.txt" + var out_buf: [16]u8 = [0_u8; 16] + var chunk: [4]u8 = [0_u8; 4] + var offset: usize = 0 + var total: usize = 0 + var saw_truncation: Bool = true + var ok: Bool = true + var done: Bool = false + while ok && !done { + let read: Maybe = std.fs.readBytesAt(path, offset, chunk) + if !read.has { + ok = false + } else { + total = read.value + if total <= 4 { + saw_truncation = false + } + var valid: usize = 0 + if offset < total { + valid = total - offset + if valid > 4 { + valid = 4 + } + } + let copied: usize = std.mem.copy(out_buf[offset..], chunk[..valid]) + if copied != valid { + ok = false + } + offset = offset + valid + if offset >= total { + done = true + } + } + } + let whole: Maybe = std.fs.readBytes(path, out_buf) + if ok && saw_truncation && total == 10 && whole.has && whole.value == 10 && std.mem.eqlBytes(out_buf[..total], std.mem.span("zero file\n")) { + check world.out.write("fs read chunks ok\n") + } else { + check world.out.write("fs read chunks failed\n") + } +} diff --git a/conformance/native/pass/std-fs-read-chunks.graph b/conformance/native/pass/std-fs-read-chunks.graph new file mode 100644 index 0000000..8f152f7 Binary files /dev/null and b/conformance/native/pass/std-fs-read-chunks.graph differ diff --git a/conformance/native/pass/std-fs-readall.0 b/conformance/native/pass/std-fs-readall.0 new file mode 100644 index 0000000..077f63c --- /dev/null +++ b/conformance/native/pass/std-fs-readall.0 @@ -0,0 +1,20 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let created: Maybe> = std.fs.create(fs, ".zero/readall-input.txt") + if created.has { + var file: owned = created.value + if !std.fs.writeAll(&mut file, std.mem.span("read all ok\n")) { + return + } + } + var storage: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + let body: Maybe> = std.fs.readAll(alloc, fs, ".zero/readall-input.txt", 64) + if body.has { + var buf: owned = body.value + let bytes: MutSpan = std.mem.bufBytes(&buf) + if std.mem.eqlBytes(bytes, std.mem.span("read all ok\n")) { + check world.out.write("fs readAll ok\n") + } + } +} diff --git a/conformance/native/pass/std-fs-readall.graph b/conformance/native/pass/std-fs-readall.graph new file mode 100644 index 0000000..91dd355 Binary files /dev/null and b/conformance/native/pass/std-fs-readall.graph differ diff --git a/conformance/native/pass/std-fs-resource.0 b/conformance/native/pass/std-fs-resource.0 new file mode 100644 index 0000000..afac53b --- /dev/null +++ b/conformance/native/pass/std-fs-resource.0 @@ -0,0 +1,25 @@ +fn write_auto(fs: Fs) -> Bool { + let created: Maybe> = std.fs.create(fs, ".zero/conformance/std-fs-resource.txt") + if created.has { + var file: owned = created.value + let bytes: [10]u8 = [122, 101, 114, 111, 32, 102, 105, 108, 101, 10] + return std.fs.writeAll(&mut file, bytes) + } + return false +} + +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let wrote: Bool = write_auto(fs) + let missing: Maybe> = std.fs.open(fs, ".zero/conformance/std-fs-resource-missing.txt") + let opened: Maybe> = std.fs.open(fs, ".zero/conformance/std-fs-resource.txt") + if wrote && !missing.has && opened.has { + var file: owned = opened.value + var buf: [10]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let read: Maybe = std.fs.read(&mut file, buf) + std.fs.close(&mut file) + if read.has && read.value == 10 && buf[0] == 122 as u8 && buf[9] == 10_u8 { + check world.out.write("fs resource ok\n") + } + } +} diff --git a/conformance/native/pass/std-fs-resource.graph b/conformance/native/pass/std-fs-resource.graph new file mode 100644 index 0000000..b28e40d Binary files /dev/null and b/conformance/native/pass/std-fs-resource.graph differ diff --git a/conformance/native/pass/std-fs-write-file-bool.0 b/conformance/native/pass/std-fs-write-file-bool.0 new file mode 100644 index 0000000..52b4f87 --- /dev/null +++ b/conformance/native/pass/std-fs-write-file-bool.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + if std.fs.writeFile(fs, ".zero/conformance/std-fs-write-file-bool.txt", "hello") { + check world.out.write("fs write file bool ok\n") + } +} diff --git a/conformance/native/pass/std-fs-write-file-bool.graph b/conformance/native/pass/std-fs-write-file-bool.graph new file mode 100644 index 0000000..0d499fa Binary files /dev/null and b/conformance/native/pass/std-fs-write-file-bool.graph differ diff --git a/conformance/native/pass/std-fs.0 b/conformance/native/pass/std-fs.0 new file mode 100644 index 0000000..480f4f0 --- /dev/null +++ b/conformance/native/pass/std-fs.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + var buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let read_count: usize = std.fs.read("conformance/fixtures/std-fs-read.txt", buf) + let written: usize = std.fs.write(".zero/conformance/std-fs-write.txt", "zero write\n") + if read_count > 0 && written == 11 && std.fs.exists(".zero/conformance/std-fs-write.txt") { + check world.out.write("fs ok\n") + } +} diff --git a/conformance/native/pass/std-fs.graph b/conformance/native/pass/std-fs.graph new file mode 100644 index 0000000..e547e96 Binary files /dev/null and b/conformance/native/pass/std-fs.graph differ diff --git a/conformance/native/pass/std-hosted-cli.0 b/conformance/native/pass/std-hosted-cli.0 new file mode 100644 index 0000000..3c32454 --- /dev/null +++ b/conformance/native/pass/std-hosted-cli.0 @@ -0,0 +1,68 @@ +pub fn main(world: World) -> Void raises { + let first: String = std.args.getOr(1, "missing") + let has_count_arg: Bool = std.args.has(2) + let parsed_count_arg: Maybe = std.args.parseU32(2) + let parsed_count_or: u32 = std.cli.argU32Or(2, 0_u32) + let missing_count_or: u32 = std.cli.argU32Or(99, 11_u32) + let name_index: Maybe = std.args.find("--name") + let name_after: Maybe = std.args.valueAfter("--name") + let command: Maybe = std.cli.command() + let command_or: String = std.cli.commandOr("missing") + let command_equals: Bool = std.cli.commandEquals("run") + let third_arg: String = std.cli.argOr(3, "missing") + let missing_arg: String = std.cli.argOr(99, "fallback") + let flag: Bool = std.cli.hasFlag("--json") + let option: Maybe = std.cli.optionValue("--name") + let option_or: String = std.cli.optionValueOr("--missing", "fallback") + let option_count: Maybe = std.cli.optionU32("--count") + let option_bool_value: Maybe = std.cli.optionValue("--enabled") + var option_bool: Maybe = null + if option_bool_value.has { + option_bool = std.parse.parseBool(option_bool_value.value) + } + let option_i32_value: Maybe = std.cli.optionValue("--delta") + var option_i32: Maybe = null + if option_i32_value.has { + option_i32 = std.parse.parseI32(option_i32_value.value) + } + let option_size_value: Maybe = std.cli.optionValue("--size") + var option_size: Maybe = null + if option_size_value.has { + option_size = std.parse.parseUsize(option_size_value.value) + } + let env_mode: String = std.env.getOr("ZERO_CONFORMANCE_MODE", "dev") + let env_has_mode: Bool = std.env.has("ZERO_CONFORMANCE_MODE") + let env_verbose: Maybe = std.env.parseBool("ZERO_CONFORMANCE_VERBOSE") + let env_limit: Maybe = std.env.parseU32("ZERO_CONFORMANCE_LIMIT") + var usage_storage: [64]u8 = [0_u8; 64] + let usage_line: Maybe> = std.cli.formatUsage(usage_storage, "zero-test", " [args]") + var command_storage: [96]u8 = [0_u8; 96] + let command_line: Maybe> = std.cli.formatCommand(command_storage, "add", " ", "print sum") + var option_storage: [96]u8 = [0_u8; 96] + let option_line: Maybe> = std.cli.formatOption(option_storage, "--count", "", "repeat count") + var section_storage: [16]u8 = [0_u8; 16] + let section_line: Maybe> = std.cli.formatSection(section_storage, "Options") + var help_row_storage: [96]u8 = [0_u8; 96] + let help_row: Maybe> = std.cli.formatHelpRow(help_row_storage, "tui [options]", "open terminal workspace") + var custom_help_row_storage: [96]u8 = [0_u8; 96] + let custom_help_row: Maybe> = std.cli.formatHelpRow(custom_help_row_storage, "--state ", "path to state file") + var wide_help_row_storage: [128]u8 = [0_u8; 128] + let wide_help_row: Maybe> = std.cli.formatHelpRowWithWidth(wide_help_row_storage, "--state ", "path to state file", 20_usize) + var custom_indent_row_storage: [128]u8 = [0_u8; 128] + let custom_indent_row: Maybe> = std.cli.formatHelpRowCustom(custom_indent_row_storage, "--state ", "path to state file", 4_usize, 20_usize) + var help_storage: [512]u8 = [0_u8; 512] + let help_doc: Maybe> = std.cli.formatHelp(help_storage, "ovation [options] [command]", "Terminal control plane") + var error_storage: [64]u8 = [0_u8; 64] + let error_line: Maybe> = std.cli.formatError(error_storage, "bad input") + var unknown_storage: [64]u8 = [0_u8; 64] + let unknown_line: Maybe> = std.cli.formatUnknownCommand(unknown_storage, "multiply") + var missing_storage: [64]u8 = [0_u8; 64] + let missing_line: Maybe> = std.cli.formatMissingOperand(missing_storage, "left") + var invalid_storage: [64]u8 = [0_u8; 64] + let invalid_line: Maybe> = std.cli.formatInvalidOption(invalid_storage, "--wat") + var small_storage: [8]u8 = [0_u8; 8] + let small_usage: Maybe> = std.cli.formatUsage(small_storage, "zero-test", " [args]") + if std.cli.argEquals(1, "run") && command.has && command_equals && !std.cli.needsHelp() && std.cli.isHelp("help") && std.cli.isHelp("--help") && std.cli.isHelp("-h") && !std.cli.isHelp("run") && std.cli.commandIn2("run", "run", "help") && std.cli.commandIn3("version", "run", "help", "version") && command_line.has && usage_line.has && option_line.has && section_line.has && help_row.has && custom_help_row.has && wide_help_row.has && custom_indent_row.has && help_doc.has && error_line.has && unknown_line.has && missing_line.has && invalid_line.has && !small_usage.has && std.mem.eql(usage_line.value, "usage: zero-test [args]") && std.mem.eql(command_line.value, " add print sum") && std.mem.eql(option_line.value, " --count repeat count") && std.mem.eql(section_line.value, "Options:\n") && std.mem.eql(help_row.value, " tui [options] open terminal workspace\n") && std.mem.eql(custom_help_row.value, " --state path to state file\n") && std.mem.eql(wide_help_row.value, " --state path to state file\n") && std.mem.eql(custom_indent_row.value, " --state path to state file\n") && std.mem.eql(help_doc.value, "Usage: ovation [options] [command]\n\nTerminal control plane\n") && std.mem.eql(error_line.value, "error: bad input") && std.mem.eql(unknown_line.value, "error: unknown command: multiply") && std.mem.eql(missing_line.value, "error: missing operand: left") && std.mem.eql(invalid_line.value, "error: invalid option: --wat") && std.mem.eql(command.value, "run") && std.mem.eql(command_or, "run") && std.mem.eql(first, "run") && has_count_arg && parsed_count_arg.has && parsed_count_arg.value == 7_u32 && parsed_count_or == 7_u32 && missing_count_or == 11_u32 && name_index.has && name_after.has && flag && option.has && option_count.has && option_bool.has && !option_bool.value && option_i32.has && option_i32.value == -5 && option_size.has && option_size.value == 9 && env_has_mode && env_verbose.has && env_limit.has && std.mem.eql(third_arg, "--json") && std.mem.eql(missing_arg, "fallback") && std.mem.eql(option.value, "agent") && std.mem.eql(name_after.value, "agent") && std.mem.eql(option_or, "fallback") && option_count.value == 3_u32 && std.mem.eql(env_mode, "test") && env_verbose.value && env_limit.value == 9_u32 && std.cli.successExitCode() == 0 && std.cli.usageExitCode() == 2 { + check world.out.write("std hosted cli ok\n") + } +} diff --git a/conformance/native/pass/std-hosted-cli.graph b/conformance/native/pass/std-hosted-cli.graph new file mode 100644 index 0000000..5b481ca Binary files /dev/null and b/conformance/native/pass/std-hosted-cli.graph differ diff --git a/conformance/native/pass/std-http-api-helpers.0 b/conformance/native/pass/std-http-api-helpers.0 new file mode 100644 index 0000000..9705040 --- /dev/null +++ b/conformance/native/pass/std-http-api-helpers.0 @@ -0,0 +1,457 @@ +export c fn main() -> i32 { + if !std.http.statusIsInformational(100_u16) || !std.http.statusIsSuccess(201_u16) || !std.http.statusIsRedirect(302_u16) || !std.http.statusIsClientError(404_u16) || !std.http.statusIsServerError(503_u16) { + return 13 + } + if !std.mem.eql(std.http.statusReason(404_u16), "Not Found") { + return 14 + } + var request_buf: [256]u8 = [0_u8; 256] + let request: Maybe> = std.http.writeJsonRequest(request_buf, "POST https://example.com/api?name=zero", "{\"ping\":1}") + if !request.has { + return 15 + } + let method: Maybe> = std.http.requestMethodName(request.value) + let target: Maybe> = std.http.requestTarget(request.value) + let path: Maybe> = std.http.requestPath(request.value) + let query: Maybe> = std.http.requestQuery(request.value) + let content_type: Maybe> = std.http.requestHeader(request.value, "content-type") + let body: Maybe> = std.http.requestBody(request.value) + if !method.has || !std.mem.eql(method.value, "POST") { + return 16 + } + if !target.has || !std.mem.eql(target.value, "https://example.com/api?name=zero") { + return 17 + } + if !path.has || !std.mem.eql(path.value, "/api") { + return 18 + } + if !query.has || !std.mem.eql(query.value, "name=zero") { + return 19 + } + if !content_type.has || !std.mem.eql(content_type.value, "application/json") { + return 20 + } + if !body.has || !std.mem.eql(body.value, "{\"ping\":1}") { + return 21 + } + let name: Maybe> = std.url.queryValue(query.value, "name") + if !name.has || !std.mem.eql(name.value, "zero") { + return 22 + } + let query_name: Maybe> = std.http.requestQueryValue(request.value, "name") + if !query_name.has || !std.mem.eql(query_name.value, "zero") { + return 28 + } + let limited_body: Maybe> = std.http.requestBodyWithin(request.value, 16) + let too_large_body: Maybe> = std.http.requestBodyWithin(request.value, 4) + if !limited_body.has || !std.mem.eql(limited_body.value, "{\"ping\":1}") || too_large_body.has { + return 29 + } + if !std.http.requestMatches(request.value, "POST", "/api") || std.http.requestMatches(request.value, "GET", "/api") { + return 30 + } + if !std.http.requestMethodIs(request.value, "POST") || std.http.requestMethodIs(request.value, "GET") { + return 44 + } + if !std.http.requestIsPost(request.value, "/api") || std.http.requestIsGet(request.value, "/api") { + return 45 + } + if !std.http.requestPathStartsWith(request.value, "/a") || std.http.requestPathStartsWith(request.value, "/users") { + return 46 + } + let path_tail: Maybe> = std.http.requestPathTailAfter(request.value, "/") + if !path_tail.has || !std.mem.eql(path_tail.value, "api") { + return 47 + } + let missing_tail: Maybe> = std.http.requestPathTailAfter(request.value, "/users/") + if missing_tail.has { + return 48 + } + let put_request: Span = "PUT /users/7\n\n" + let patch_request: Span = "PATCH /users/7\n\n" + let delete_request: Span = "DELETE /users/7\n\n" + if !std.http.requestIsPut(put_request, "/users/7") || !std.http.requestIsPatch(patch_request, "/users/7") || !std.http.requestIsDelete(delete_request, "/users/7") { + return 49 + } + let user_id: Maybe> = std.http.requestPathTailAfter(put_request, "/users/") + if !user_id.has || !std.mem.eql(user_id.value, "7") { + return 50 + } + if !std.http.requestHasJsonContentType(request.value) { + return 35 + } + let json_body: Maybe> = std.http.requestJsonBodyWithin(request.value, 16) + let too_large_json_body: Maybe> = std.http.requestJsonBodyWithin(request.value, 4) + if !json_body.has || !std.mem.eql(json_body.value, "{\"ping\":1}") || too_large_json_body.has { + return 36 + } + let upper_json_request: Span = "POST /api\ncontent-type: Application/JSON; charset=utf-8\n\n{\"ok\":true}" + if !std.http.requestHasJsonContentType(upper_json_request) { + return 37 + } + let invalid_json_request: Span = "POST /api\ncontent-type: application/json\n\nnot-json" + let invalid_json_body: Maybe> = std.http.requestJsonBodyWithin(invalid_json_request, 64) + if invalid_json_body.has { + return 38 + } + let routed_request: Span = "POST /users/42/books\naccept: application/json, text/plain;q=0.5\ncontent-type: application/json; charset=utf-8\ncontent-length: 15\n\n{\"name\":\"zero\"}" + if !std.http.requestRouteMatches(routed_request, "POST", "/users/:id/*") || std.http.requestRouteMatches(routed_request, "GET", "/users/:id/*") { + return 60 + } + let path_id: Maybe> = std.http.requestPathParam(routed_request, "/users/:id/*", "id") + let raw_path_id: Maybe> = std.http.pathParam("/users/42/books", "/users/:id/*", "id") + if !path_id.has || !raw_path_id.has || !std.mem.eql(path_id.value, "42") || !std.mem.eql(raw_path_id.value, "42") { + return 61 + } + if !std.http.pathMatchesPattern("/users/42/books", "/users/:id/*") || std.http.pathMatchesPattern("/users", "/users/:id") { + return 62 + } + if std.http.pathMatchesPattern("/admin/users/delete", "/admin/*/delete") { + return 105 + } + let content_length: Maybe = std.http.requestContentLength(routed_request) + let routed_content_type: Maybe> = std.http.requestContentType(routed_request) + if !content_length.has || content_length.value != 15 || !routed_content_type.has || !std.mem.eql(routed_content_type.value, "application/json") { + return 63 + } + if !std.http.requestAcceptsJson(routed_request) || !std.http.requestAccepts(routed_request, "text/plain") || std.http.requestAccepts(routed_request, "application/xml") { + return 64 + } + let no_accept_request: Span = "GET /plain\n\n" + if !std.http.requestAcceptsJson(no_accept_request) || !std.http.requestAccepts(no_accept_request, "text/plain") { + return 102 + } + let q_zero_accept_request: Span = "GET /plain\naccept: application/json;q=0, text/html\n\n" + if std.http.requestAcceptsJson(q_zero_accept_request) || !std.http.requestAccepts(q_zero_accept_request, "text/html") { + return 103 + } + let wildcard_q_zero_request: Span = "GET /plain\naccept: */*;q=0, text/html;q=1\n\n" + if std.http.requestAccepts(wildcard_q_zero_request, "application/json") || !std.http.requestAccepts(wildcard_q_zero_request, "text/html") { + return 104 + } + let exact_deny_after_wildcard_request: Span = "GET /plain\naccept: */*, application/json;q=0\n\n" + if std.http.requestAcceptsJson(exact_deny_after_wildcard_request) || !std.http.requestAccepts(exact_deny_after_wildcard_request, "text/html") { + return 106 + } + let routed_name: Maybe> = std.http.requestJsonField(routed_request, "name", 64) + if !routed_name.has || !std.mem.eql(routed_name.value, "\"zero\"") { + return 65 + } + var get_buf: [64]u8 = [0_u8; 64] + let get_request: Maybe> = std.http.writeRequest(get_buf, "GET /health", "") + if !get_request.has || !std.mem.eql(get_request.value, "GET /health\r\n\r\n") { + return 23 + } + var header_request_buf: [128]u8 = [0_u8; 128] + let header_request: Maybe> = std.http.writeRequestWithHeader(header_request_buf, "GET /api", "accept: application/json", "") + if !header_request.has || !std.str.contains(header_request.value, "accept: application/json") { + return 66 + } + var bad_single_header_request_buf: [160]u8 = [0_u8; 160] + let bad_single_header_request: Maybe> = std.http.writeRequestWithHeader(bad_single_header_request_buf, "GET /api", "accept: application/json\n", "") + if bad_single_header_request.has { + return 107 + } + if !std.http.headerBlockSafe("accept: application/json\nx-client: zero") || std.http.headerBlockSafe("bad\rname: value") { + return 70 + } + var method_request_buf: [128]u8 = [0_u8; 128] + let method_request: Maybe> = std.http.writeMethodRequest(method_request_buf, "PUT", "/users/7", "body") + if !method_request.has || !std.str.contains(method_request.value, "PUT /users/7") || !std.str.contains(method_request.value, "content-length: 4") { + return 71 + } + var get_short_buf: [64]u8 = [0_u8; 64] + let get_short: Maybe> = std.http.writeGetRequest(get_short_buf, "/health") + if !get_short.has || !std.mem.eql(get_short.value, "GET /health\r\n\r\n") { + return 72 + } + var head_short_buf: [64]u8 = [0_u8; 64] + let head_short: Maybe> = std.http.writeHeadRequest(head_short_buf, "/health") + if !head_short.has || !std.mem.eql(head_short.value, "HEAD /health\r\n\r\n") { + return 89 + } + var delete_short_buf: [64]u8 = [0_u8; 64] + let delete_short: Maybe> = std.http.writeDeleteRequest(delete_short_buf, "/users/7") + if !delete_short.has || !std.mem.eql(delete_short.value, "DELETE /users/7\r\n\r\n") { + return 90 + } + var long_target: [520]u8 = [97_u8; 520] + long_target[0] = 47_u8 + var long_get_buf: [700]u8 = [0_u8; 700] + let long_get: Maybe> = std.http.writeGetRequest(long_get_buf, long_target) + if !long_get.has || !std.http.requestIsGet(long_get.value, long_target) { + return 96 + } + var post_json_buf: [160]u8 = [0_u8; 160] + let post_json: Maybe> = std.http.writePostJsonRequest(post_json_buf, "/users", "{\"name\":\"zero\"}") + if !post_json.has || !std.str.contains(post_json.value, "POST /users") || !std.str.contains(post_json.value, "content-type: application/json") { + return 73 + } + var json_method_buf: [160]u8 = [0_u8; 160] + let json_method: Maybe> = std.http.writeJsonMethodRequest(json_method_buf, "POST", "/bulk", "{\"ok\":true}") + if !json_method.has || !std.str.contains(json_method.value, "POST /bulk") || !std.http.requestHasJsonContentType(json_method.value) { + return 91 + } + var put_json_buf: [160]u8 = [0_u8; 160] + let put_json: Maybe> = std.http.writePutJsonRequest(put_json_buf, "/users/7", "{\"name\":\"zero\"}") + if !put_json.has || !std.http.requestIsPut(put_json.value, "/users/7") { + return 92 + } + var patch_json_buf: [160]u8 = [0_u8; 160] + let patch_json: Maybe> = std.http.writePatchJsonRequest(patch_json_buf, "/users/7", "{\"name\":\"zero\"}") + if !patch_json.has || !std.http.requestIsPatch(patch_json.value, "/users/7") { + return 93 + } + var multi_header_request_buf: [192]u8 = [0_u8; 192] + let multi_header_request: Maybe> = std.http.writeRequestWithHeaders(multi_header_request_buf, "GET /api", "accept: application/json\nx-client: zero", "") + if !multi_header_request.has || !std.str.contains(multi_header_request.value, "accept: application/json\r\nx-client: zero") { + return 74 + } + var json_header_request_buf: [192]u8 = [0_u8; 192] + let json_header_request: Maybe> = std.http.writeJsonRequestWithHeader(json_header_request_buf, "POST /api", "authorization: Bearer token", "{\"ok\":true}") + if !json_header_request.has || !std.str.contains(json_header_request.value, "authorization: Bearer token") || !std.http.requestHasJsonContentType(json_header_request.value) { + return 75 + } + var json_headers_request_buf: [224]u8 = [0_u8; 224] + let json_headers_request: Maybe> = std.http.writeJsonRequestWithHeaders(json_headers_request_buf, "PATCH /api", "authorization: Bearer token\nx-client: zero", "{\"ok\":true}") + if !json_headers_request.has || !std.str.contains(json_headers_request.value, "x-client: zero") || !std.http.requestIsPatch(json_headers_request.value, "/api") { + return 76 + } + var bad_request_header_buf: [160]u8 = [0_u8; 160] + let bad_request_header: Maybe> = std.http.writeRequestWithHeaders(bad_request_header_buf, "POST /api", "content-length: 999", "x") + if bad_request_header.has { + return 97 + } + var bad_transfer_request_header_buf: [160]u8 = [0_u8; 160] + let bad_transfer_request_header: Maybe> = std.http.writeRequestWithHeader(bad_transfer_request_header_buf, "POST /api", "transfer-encoding: chunked", "x") + if bad_transfer_request_header.has { + return 109 + } + var bad_json_request_header_buf: [160]u8 = [0_u8; 160] + let bad_json_request_header: Maybe> = std.http.writeJsonRequestWithHeader(bad_json_request_header_buf, "POST /api", "content-type: text/plain", "{}") + if bad_json_request_header.has { + return 98 + } + var bad_json_transfer_request_header_buf: [192]u8 = [0_u8; 192] + let bad_json_transfer_request_header: Maybe> = std.http.writeJsonRequestWithHeaders(bad_json_transfer_request_header_buf, "POST /api", "transfer-encoding: chunked", "{}") + if bad_json_transfer_request_header.has { + return 110 + } + if !std.http.methodAllowed("PATCH", "GET, PATCH") || !std.http.requestMethodAllowed("PATCH /users/7\n\n", "GET, PATCH") || !std.http.requestRouteMethodAllowed("PATCH /users/7\n\n", "/users/:id", "PATCH, DELETE") { + return 77 + } + var absolute_buf: [64]u8 = [0_u8; 64] + let absolute_request: Maybe> = std.http.writeRequest(absolute_buf, "GET https://example.com", "") + if !absolute_request.has { + return 31 + } + let absolute_path: Maybe> = std.http.requestPath(absolute_request.value) + if !absolute_path.has || !std.mem.eql(absolute_path.value, "/") { + return 33 + } + var invalid_buf: [64]u8 = [0_u8; 64] + let invalid_request: Maybe> = std.http.writeRequest(invalid_buf, "GET", "") + if invalid_request.has { + return 34 + } + var response_buf: [256]u8 = [0_u8; 256] + let response: Maybe> = std.http.writeJsonCreated(response_buf, "{\"ok\":true}") + if !response.has { + return 24 + } + let header: HttpHeaderValue = std.http.headerValue(response.value, "content-type") + let header_bytes: Maybe> = std.http.headerBytes(response.value, header) + if !header_bytes.has || !std.mem.eql(header_bytes.value, "application/json") { + return 25 + } + if std.http.responseBodyOffset(response.value) >= std.mem.len(response.value) { + return 26 + } + let response_body: Maybe> = std.http.responseBodyBytes(response.value) + if !response_body.has || !std.mem.eql(response_body.value, "{\"ok\":true}") { + return 39 + } + var custom_header_buf: [192]u8 = [0_u8; 192] + let custom_header_response: Maybe> = std.http.writeResponseWithHeader(custom_header_buf, 202_u16, "x-request-id: abc123", "accepted") + if !custom_header_response.has || !std.str.contains(custom_header_response.value, "202 Accepted") || !std.str.contains(custom_header_response.value, "x-request-id: abc123") { + return 67 + } + var bad_single_header_response_buf: [160]u8 = [0_u8; 160] + let bad_single_header_response: Maybe> = std.http.writeResponseWithHeader(bad_single_header_response_buf, 200_u16, "x-request-id: abc123\r\n", "ok") + if bad_single_header_response.has { + return 108 + } + var json_header_buf: [192]u8 = [0_u8; 192] + let json_header_response: Maybe> = std.http.writeJsonResponseWithHeader(json_header_buf, 200_u16, "cache-control: no-store", "{\"ok\":true}") + if !json_header_response.has || !std.str.contains(json_header_response.value, "cache-control: no-store") || !std.str.contains(json_header_response.value, "content-type: application/json") { + return 68 + } + if !std.http.responseStatusIs(json_header_response.value, 200_u16) || std.http.responseStatusIs(json_header_response.value, 404_u16) { + return 78 + } + if std.http.responseStatusIs("HTTP/1.1 2000 nope\r\n\r\n", 200_u16) || std.http.responseStatusIs("HTTP/1.1 200OK\r\n\r\n", 200_u16) { + return 111 + } + let invalid_short_status: Maybe = std.http.responseStatus("HTTP/1.1 099 Weird\r\n\r\n") + if invalid_short_status.has { + return 112 + } + let json_header_type: Maybe> = std.http.responseContentType(json_header_response.value) + let json_cache_header: Maybe> = std.http.responseHeader(json_header_response.value, "cache-control") + if !json_header_type.has || !std.mem.eql(json_header_type.value, "application/json") || !json_cache_header.has || !std.mem.eql(json_cache_header.value, "no-store") { + return 79 + } + if !std.http.responseMatches(json_header_response.value, 200_u16, "application/json", "{\"ok\":true}") || !std.http.responseBodyEquals(json_header_response.value, "{\"ok\":true}") { + return 80 + } + var multi_header_response_buf: [224]u8 = [0_u8; 224] + let multi_header_response: Maybe> = std.http.writeJsonResponseWithHeaders(multi_header_response_buf, 202_u16, "cache-control: no-store\nx-request-id: abc123", "{\"accepted\":true}") + if !multi_header_response.has || !std.http.responseMatches(multi_header_response.value, 202_u16, "application/json", "{\"accepted\":true}") { + return 81 + } + let request_id: Maybe> = std.http.responseHeader(multi_header_response.value, "x-request-id") + if !request_id.has || !std.mem.eql(request_id.value, "abc123") { + return 82 + } + var plain_multi_header_buf: [192]u8 = [0_u8; 192] + let plain_multi_header_response: Maybe> = std.http.writeResponseWithHeaders(plain_multi_header_buf, 202_u16, "content-type: text/plain\nx-request-id: plain", "accepted") + if !plain_multi_header_response.has || !std.http.responseMatches(plain_multi_header_response.value, 202_u16, "text/plain", "accepted") { + return 94 + } + var bad_response_header_buf: [160]u8 = [0_u8; 160] + let bad_response_header: Maybe> = std.http.writeResponseWithHeaders(bad_response_header_buf, 200_u16, "connection: keep-alive", "ok") + if bad_response_header.has { + return 99 + } + var bad_transfer_response_header_buf: [160]u8 = [0_u8; 160] + let bad_transfer_response_header: Maybe> = std.http.writeResponseWithHeader(bad_transfer_response_header_buf, 200_u16, "transfer-encoding: chunked", "ok") + if bad_transfer_response_header.has { + return 113 + } + var bad_json_response_header_buf: [160]u8 = [0_u8; 160] + let bad_json_response_header: Maybe> = std.http.writeJsonResponseWithHeader(bad_json_response_header_buf, 200_u16, "content-type: text/plain", "{}") + if bad_json_response_header.has { + return 100 + } + var bad_json_transfer_response_header_buf: [192]u8 = [0_u8; 192] + let bad_json_transfer_response_header: Maybe> = std.http.writeJsonResponseWithHeaders(bad_json_transfer_response_header_buf, 200_u16, "transfer-encoding: chunked", "{}") + if bad_json_transfer_response_header.has { + return 114 + } + if !std.mem.eql(std.http.contentTypeForPath("/assets/app.js"), "text/javascript; charset=utf-8") || !std.mem.eql(std.http.contentTypeForPath("/data.bin"), "application/octet-stream") { + return 83 + } + var static_response_buf: [192]u8 = [0_u8; 192] + let static_response: Maybe> = std.http.writeStaticResponse(static_response_buf, 200_u16, "/styles/app.css", "body{}") + if !static_response.has || !std.http.responseMatches(static_response.value, 200_u16, "text/css", "body{}") { + return 84 + } + var cookie_buf: [192]u8 = [0_u8; 192] + let cookie_response: Maybe> = std.http.writeJsonResponseWithCookie(cookie_buf, 200_u16, "sid=abc123", "{\"ok\":true}") + if !cookie_response.has || !std.str.contains(cookie_response.value, "set-cookie: sid=abc123") { + return 69 + } + var long_cookie: [300]u8 = [97_u8; 300] + var long_cookie_buf: [512]u8 = [0_u8; 512] + let long_cookie_response: Maybe> = std.http.writeJsonResponseWithCookie(long_cookie_buf, 200_u16, long_cookie, "{\"ok\":true}") + var long_cookie_header: Maybe> = null + if long_cookie_response.has { + long_cookie_header = std.http.responseHeader(long_cookie_response.value, "set-cookie") + } + if !long_cookie_response.has || !long_cookie_header.has || !std.mem.eql(long_cookie_header.value, long_cookie[..]) { + return 101 + } + var ok_buf: [128]u8 = [0_u8; 128] + let ok_response: Maybe> = std.http.writeJsonOk(ok_buf, "{\"ok\":true}") + if !ok_response.has || !std.str.contains(ok_response.value, "200 OK") { + return 40 + } + var bad_buf: [128]u8 = [0_u8; 128] + let bad_response: Maybe> = std.http.writeJsonBadRequest(bad_buf, "{\"error\":\"bad\"}") + if !bad_response.has || !std.str.contains(bad_response.value, "400 Bad Request") { + return 41 + } + var unauthorized_buf: [128]u8 = [0_u8; 128] + let unauthorized_response: Maybe> = std.http.writeJsonUnauthorized(unauthorized_buf, "{\"error\":\"unauthorized\"}") + if !unauthorized_response.has || !std.str.contains(unauthorized_response.value, "401 Unauthorized") { + return 51 + } + var forbidden_buf: [128]u8 = [0_u8; 128] + let forbidden_response: Maybe> = std.http.writeJsonForbidden(forbidden_buf, "{\"error\":\"forbidden\"}") + if !forbidden_response.has || !std.str.contains(forbidden_response.value, "403 Forbidden") { + return 52 + } + var not_found_buf: [128]u8 = [0_u8; 128] + let not_found_response: Maybe> = std.http.writeJsonNotFound(not_found_buf, "{\"error\":\"not_found\"}") + if !not_found_response.has || !std.str.contains(not_found_response.value, "404 Not Found") { + return 42 + } + var method_buf: [160]u8 = [0_u8; 160] + let method_response: Maybe> = std.http.writeJsonMethodNotAllowed(method_buf, "{\"error\":\"method_not_allowed\"}") + if !method_response.has || !std.str.contains(method_response.value, "405 Method Not Allowed") { + return 43 + } + var conflict_buf: [128]u8 = [0_u8; 128] + let conflict_response: Maybe> = std.http.writeJsonConflict(conflict_buf, "{\"error\":\"conflict\"}") + if !conflict_response.has || !std.str.contains(conflict_response.value, "409 Conflict") { + return 53 + } + var unprocessable_buf: [160]u8 = [0_u8; 160] + let unprocessable_response: Maybe> = std.http.writeJsonUnprocessable(unprocessable_buf, "{\"error\":\"unprocessable\"}") + if !unprocessable_response.has || !std.str.contains(unprocessable_response.value, "422 Unprocessable Content") { + return 54 + } + var limited_buf: [160]u8 = [0_u8; 160] + let limited_response: Maybe> = std.http.writeJsonTooManyRequests(limited_buf, "{\"error\":\"rate_limited\"}") + if !limited_response.has || !std.str.contains(limited_response.value, "429 Too Many Requests") { + return 55 + } + var internal_buf: [160]u8 = [0_u8; 160] + let internal_response: Maybe> = std.http.writeJsonInternalServerError(internal_buf, "{\"error\":\"internal\"}") + if !internal_response.has || !std.str.contains(internal_response.value, "500 Internal Server Error") { + return 56 + } + var error_buf: [160]u8 = [0_u8; 160] + let error_response: Maybe> = std.http.writeJsonError(error_buf, 422_u16, "bad_request") + if !error_response.has || !std.str.contains(error_response.value, "422 Unprocessable Content") { + return 57 + } + let error_body: Maybe> = std.http.responseBodyBytes(error_response.value) + if !error_body.has || !std.mem.eql(error_body.value, "{\"error\":\"bad_request\"}") { + return 58 + } + var unsafe_error_buf: [160]u8 = [0_u8; 160] + let unsafe_error_response: Maybe> = std.http.writeJsonError(unsafe_error_buf, 400_u16, "Bad Request") + if unsafe_error_response.has { + return 59 + } + var redirect_buf: [128]u8 = [0_u8; 128] + let redirect_response: Maybe> = std.http.writeFound(redirect_buf, "/next") + if !redirect_response.has { + return 85 + } + let redirect_location: Maybe> = std.http.responseRedirectLocation(redirect_response.value) + if !redirect_location.has || !std.mem.eql(redirect_location.value, "/next") { + return 86 + } + var test_request_buf: [160]u8 = [0_u8; 160] + let test_request: Maybe> = std.http.testJsonRequest(test_request_buf, "POST", "/tests", "{\"ok\":true}") + if !test_request.has || !std.http.requestIsPost(test_request.value, "/tests") || !std.http.requestHasJsonContentType(test_request.value) { + return 87 + } + let test_body: Maybe> = std.http.requestJsonBodyWithin(test_request.value, 64) + if !test_body.has || !std.mem.eql(test_body.value, "{\"ok\":true}") { + return 88 + } + var plain_test_request_buf: [96]u8 = [0_u8; 96] + let plain_test_request: Maybe> = std.http.testRequest(plain_test_request_buf, "HEAD", "/tests", "") + if !plain_test_request.has || !std.http.requestIsHead(plain_test_request.value, "/tests") { + return 95 + } + var plain_response_buf: [128]u8 = [0_u8; 128] + let plain_response: Maybe> = std.http.writeNoContent(plain_response_buf) + if !plain_response.has || !std.str.contains(plain_response.value, "204 No Content") { + return 27 + } + return 32 +} diff --git a/conformance/native/pass/std-http-api-helpers.graph b/conformance/native/pass/std-http-api-helpers.graph new file mode 100644 index 0000000..a96ad41 Binary files /dev/null and b/conformance/native/pass/std-http-api-helpers.graph differ diff --git a/conformance/native/pass/std-http-auth-helpers.0 b/conformance/native/pass/std-http-auth-helpers.0 new file mode 100644 index 0000000..16ef74e --- /dev/null +++ b/conformance/native/pass/std-http-auth-helpers.0 @@ -0,0 +1,48 @@ +export c fn main() -> i32 { + let request: Span = "GET /me\nauthorization: Bearer token-123\ncookie: sid=abc; theme=dark; empty=\n\n" + let token: Maybe> = std.http.requestBearerToken(request) + if !token.has { + return 11 + } + if !std.mem.eql(token.value, "token-123") { + return 12 + } + let lower_request: Span = "GET /me\nauthorization: bearer lower-token\n\n" + let lower_token: Maybe> = std.http.requestBearerToken(lower_request) + if !lower_token.has { + return 13 + } + if !std.mem.eql(lower_token.value, "lower-token") { + return 14 + } + let missing_token: Maybe> = std.http.requestBearerToken("GET /me\nauthorization: Basic nope\n\n") + if missing_token.has { + return 15 + } + let sid: Maybe> = std.http.requestCookie(request, "sid") + if !sid.has { + return 16 + } + if !std.mem.eql(sid.value, "abc") { + return 17 + } + let theme: Maybe> = std.http.requestCookie(request, "theme") + if !theme.has { + return 18 + } + if !std.mem.eql(theme.value, "dark") { + return 19 + } + let empty: Maybe> = std.http.requestCookie(request, "empty") + if !empty.has { + return 20 + } + if std.mem.len(empty.value) != 0 { + return 21 + } + let absent: Maybe> = std.http.requestCookie(request, "missing") + if absent.has { + return 22 + } + return 32 +} diff --git a/conformance/native/pass/std-http-auth-helpers.graph b/conformance/native/pass/std-http-auth-helpers.graph new file mode 100644 index 0000000..b4fd793 Binary files /dev/null and b/conformance/native/pass/std-http-auth-helpers.graph differ diff --git a/conformance/native/pass/std-http-cors-helpers.0 b/conformance/native/pass/std-http-cors-helpers.0 new file mode 100644 index 0000000..ced8bbc --- /dev/null +++ b/conformance/native/pass/std-http-cors-helpers.0 @@ -0,0 +1,64 @@ +export c fn main() -> i32 { + let head_request: Span = "HEAD /health\n\n" + let options_request: Span = "OPTIONS /users\naccess-control-request-method: POST\n\n" + if !std.http.requestIsHead(head_request, "/health") { + return 11 + } + if std.http.requestIsHead(options_request, "/users") { + return 12 + } + if !std.http.requestIsOptions(options_request, "/users") { + return 13 + } + if std.http.requestIsOptions(head_request, "/health") { + return 14 + } + var preflight_buf: [256]u8 = [0_u8; 256] + let preflight: Maybe> = std.http.writeCorsPreflight(preflight_buf, "*", "GET, POST, OPTIONS", "content-type, authorization") + if !preflight.has { + return 15 + } + if !std.str.contains(preflight.value, "204 No Content") { + return 16 + } + if !std.str.contains(preflight.value, "HTTP/1.1 204 No Content\r\nconnection: close\r\n") { + return 27 + } + if !std.str.contains(preflight.value, "access-control-allow-origin: *") { + return 17 + } + if !std.str.contains(preflight.value, "access-control-allow-methods: GET, POST, OPTIONS") { + return 18 + } + if !std.str.contains(preflight.value, "access-control-allow-headers: content-type, authorization") { + return 19 + } + if !std.str.contains(preflight.value, "content-length: 0") { + return 20 + } + var json_buf: [256]u8 = [0_u8; 256] + let json: Maybe> = std.http.writeCorsJsonResponse(json_buf, "200 OK", "{\"ok\":true}", "https://app.example") + if !json.has { + return 21 + } + if !std.str.contains(json.value, "200 OK") { + return 22 + } + if !std.str.contains(json.value, "content-type: application/json") { + return 23 + } + if !std.str.contains(json.value, "\r\nconnection: close\r\n") { + return 29 + } + if !std.str.contains(json.value, "access-control-allow-origin: https://app.example") { + return 24 + } + let body: Maybe> = std.http.responseBodyBytes(json.value) + if !body.has { + return 25 + } + if !std.mem.eql(body.value, "{\"ok\":true}") { + return 26 + } + return 32 +} diff --git a/conformance/native/pass/std-http-cors-helpers.graph b/conformance/native/pass/std-http-cors-helpers.graph new file mode 100644 index 0000000..a661cbd Binary files /dev/null and b/conformance/native/pass/std-http-cors-helpers.graph differ diff --git a/conformance/native/pass/std-http-errors.0 b/conformance/native/pass/std-http-errors.0 new file mode 100644 index 0000000..394c6e0 --- /dev/null +++ b/conformance/native/pass/std-http-errors.0 @@ -0,0 +1,7 @@ +export c fn main() -> i32 { + let timeout: HttpError = std.http.errorTimeout() + if std.http.errorNone() == std.http.errorNone() && std.http.errorInvalidUrl() != std.http.errorDns() && std.http.errorConnect() != std.http.errorTooLarge() && std.http.errorProviderUnavailable() != std.http.errorIo() && std.http.errorTls() != std.http.errorTimeout() && std.http.errorInvalidRequest() != std.http.errorUnsupportedProtocol() && timeout == std.http.errorTimeout() && std.mem.eql(std.http.errorName(std.http.errorNone()), "ok") && std.mem.eql(std.http.errorName(timeout), "timeout") && std.mem.eql(std.http.errorName(std.http.errorInvalidRequest()), "invalid_request") { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/std-http-errors.graph b/conformance/native/pass/std-http-errors.graph new file mode 100644 index 0000000..ca68f92 Binary files /dev/null and b/conformance/native/pass/std-http-errors.graph differ diff --git a/conformance/native/pass/std-http-fetch.0 b/conformance/native/pass/std-http-fetch.0 new file mode 100644 index 0000000..21f4abf --- /dev/null +++ b/conformance/native/pass/std-http-fetch.0 @@ -0,0 +1,32 @@ +export c fn main() -> i32 { + let net: Net = std.net.host() + let client: HttpClient = std.http.client(net) + var response: [128]u8 = [0_u8; 128] + let request: Span = std.mem.span("GET ftp://example.invalid\n\n") + let result: HttpResult = std.http.fetch(client, request, response, std.time.ms(250)) + if std.http.resultOk(result) { + return 99 + } + if std.http.resultStatus(result) == 0 && std.http.resultBodyLen(result) == 0 && std.http.resultError(result) == std.http.errorUnsupportedProtocol() && std.http.responseLen(response) == 0 && std.http.responseHeadersLen(response) == 0 { + var invalid_response: [128]u8 = [0_u8; 128] + let invalid_request: Span = std.mem.span("GET http://example.invalid\nbad-header\n\n") + let invalid_result: HttpResult = std.http.fetch(client, invalid_request, invalid_response, std.time.ms(250)) + if std.http.resultOk(invalid_result) { + return 24 + } + if std.http.resultStatus(invalid_result) != 0 { + return 25 + } + if std.http.resultBodyLen(invalid_result) != 0 { + return 26 + } + if std.http.resultError(invalid_result) != std.http.errorInvalidRequest() { + return 27 + } + if std.http.responseLen(invalid_response) != 0 { + return 28 + } + return 23 + } + return 98 +} diff --git a/conformance/native/pass/std-http-fetch.graph b/conformance/native/pass/std-http-fetch.graph new file mode 100644 index 0000000..d97715f Binary files /dev/null and b/conformance/native/pass/std-http-fetch.graph differ diff --git a/conformance/native/pass/std-http-metadata-neutral.0 b/conformance/native/pass/std-http-metadata-neutral.0 new file mode 100644 index 0000000..127b36f --- /dev/null +++ b/conformance/native/pass/std-http-metadata-neutral.0 @@ -0,0 +1,8 @@ +export c fn main() -> i32 { + let getA: HttpMethod = std.http.parseMethod("GET") + let getB: HttpMethod = std.http.parseMethod("GET") + if getA == getB && std.mem.len(std.mem.span("body")) == 4 { + return 7 + } + return 99 +} diff --git a/conformance/native/pass/std-http-metadata-neutral.graph b/conformance/native/pass/std-http-metadata-neutral.graph new file mode 100644 index 0000000..394d7f9 Binary files /dev/null and b/conformance/native/pass/std-http-metadata-neutral.graph differ diff --git a/conformance/native/pass/std-http-path-segments.0 b/conformance/native/pass/std-http-path-segments.0 new file mode 100644 index 0000000..e5ed25c --- /dev/null +++ b/conformance/native/pass/std-http-path-segments.0 @@ -0,0 +1,39 @@ +export c fn main() -> i32 { + let path: Span = "/crm/accounts/42/update" + if std.http.pathSegmentCount(path) != 4 { + return 11 + } + let resource: Maybe> = std.http.pathSegment(path, 1) + let id: Maybe> = std.http.pathSegment(path, 2) + let action: Maybe> = std.http.pathSegment(path, 3) + if !resource.has || !std.mem.eql(resource.value, "accounts") { + return 12 + } + if !id.has || !std.mem.eql(id.value, "42") { + return 13 + } + if !action.has || !std.mem.eql(action.value, "update") { + return 14 + } + let missing: Maybe> = std.http.pathSegment(path, 4) + if missing.has { + return 15 + } + let request: Span = "PATCH /crm/accounts/42?tenant=demo\ncontent-type: application/json\n\n{\"stage\":\"customer\"}" + if std.http.requestPathSegmentCount(request) != 3 { + return 16 + } + let first: Maybe> = std.http.requestPathSegment(request, 0) + let second: Maybe> = std.http.requestPathSegment(request, 1) + let request_id: Maybe> = std.http.requestPathSegment(request, 2) + if !first.has || !std.mem.eql(first.value, "crm") { + return 17 + } + if !second.has || !std.mem.eql(second.value, "accounts") { + return 18 + } + if !request_id.has || !std.mem.eql(request_id.value, "42") { + return 19 + } + return 32 +} diff --git a/conformance/native/pass/std-http-path-segments.graph b/conformance/native/pass/std-http-path-segments.graph new file mode 100644 index 0000000..e559ac7 Binary files /dev/null and b/conformance/native/pass/std-http-path-segments.graph differ diff --git a/conformance/native/pass/std-http-redirect-response-helpers.0 b/conformance/native/pass/std-http-redirect-response-helpers.0 new file mode 100644 index 0000000..a6a990d --- /dev/null +++ b/conformance/native/pass/std-http-redirect-response-helpers.0 @@ -0,0 +1,59 @@ +export c fn main() -> i32 { + var found_buf: [192]u8 = [0_u8; 192] + let found: Maybe> = std.http.writeFound(found_buf, "/next") + if !found.has { + return 1 + } + let found_status: Bool = std.str.contains(found.value, "302 Found") + let found_location: Bool = std.str.contains(found.value, "location: /next") + let found_length: Bool = std.str.contains(found.value, "content-length: 0") + if !found_status || !found_location || !found_length { + return 2 + } + let found_body: Maybe> = std.http.responseBodyBytes(found.value) + if !found_body.has { + return 3 + } + if std.mem.len(found_body.value) != 0 { + return 4 + } + var see_other_buf: [192]u8 = [0_u8; 192] + let see_other: Maybe> = std.http.writeSeeOther(see_other_buf, "/created") + if !see_other.has { + return 5 + } + let see_other_status: Bool = std.str.contains(see_other.value, "303 See Other") + if !see_other_status { + return 6 + } + var moved_buf: [192]u8 = [0_u8; 192] + let moved: Maybe> = std.http.writeMovedPermanently(moved_buf, "/new-home") + if !moved.has { + return 7 + } + let moved_status: Bool = std.str.contains(moved.value, "301 Moved Permanently") + if !moved_status { + return 8 + } + var permanent_buf: [192]u8 = [0_u8; 192] + let permanent: Maybe> = std.http.writePermanentRedirect(permanent_buf, "/stable") + if !permanent.has { + return 9 + } + let permanent_status: Bool = std.str.contains(permanent.value, "308 Permanent Redirect") + let permanent_location: Bool = std.str.contains(permanent.value, "location: /stable") + if !permanent_status || !permanent_location { + return 10 + } + var non_redirect_buf: [192]u8 = [0_u8; 192] + let non_redirect: Maybe> = std.http.writeRedirect(non_redirect_buf, 200_u16, "/bad") + if non_redirect.has { + return 11 + } + var unsafe_buf: [192]u8 = [0_u8; 192] + let unsafe: Maybe> = std.http.writeFound(unsafe_buf, "https://example.com\nset-cookie: bad=1") + if unsafe.has { + return 12 + } + return 0 +} diff --git a/conformance/native/pass/std-http-redirect-response-helpers.graph b/conformance/native/pass/std-http-redirect-response-helpers.graph new file mode 100644 index 0000000..e980b55 Binary files /dev/null and b/conformance/native/pass/std-http-redirect-response-helpers.graph differ diff --git a/conformance/native/pass/std-http-response-helpers.0 b/conformance/native/pass/std-http-response-helpers.0 new file mode 100644 index 0000000..c98d262 --- /dev/null +++ b/conformance/native/pass/std-http-response-helpers.0 @@ -0,0 +1,28 @@ +export c fn main() -> i32 { + let response: Span = std.mem.span("HTTP/1.1 200 OK\nContent-Type: application/json\nX-Zero: yes\n\nbody") + let content_type: HttpHeaderValue = std.http.headerValue(response, std.mem.span("content-type")) + let missing: HttpHeaderValue = std.http.headerValue(response, std.mem.span("missing")) + let body_offset: usize = std.http.responseBodyOffset(response) + if std.http.responseHeadersLen(response) != body_offset { + return 91 + } + if std.http.responseLen(response) != std.mem.len(response) { + return 92 + } + if !std.http.headerFound(content_type) { + return 93 + } + if std.http.headerLen(content_type) != 16 { + return 94 + } + if response[std.http.headerOffset(content_type)] != 97_u8 { + return 95 + } + if response[body_offset] != 98_u8 { + return 96 + } + if std.http.headerFound(missing) { + return 97 + } + return 29 +} diff --git a/conformance/native/pass/std-http-response-helpers.graph b/conformance/native/pass/std-http-response-helpers.graph new file mode 100644 index 0000000..38cf39f Binary files /dev/null and b/conformance/native/pass/std-http-response-helpers.graph differ diff --git a/conformance/native/pass/std-http-text-html-response-helpers.0 b/conformance/native/pass/std-http-text-html-response-helpers.0 new file mode 100644 index 0000000..3d5230b --- /dev/null +++ b/conformance/native/pass/std-http-text-html-response-helpers.0 @@ -0,0 +1,56 @@ +export c fn main() -> i32 { + var text_buf: [160]u8 = [0_u8; 160] + let text_response: Maybe> = std.http.writeTextOk(text_buf, "hello") + if !text_response.has { + return 1 + } + let text_status: Bool = std.str.contains(text_response.value, "200 OK") + let text_content_type: Bool = std.str.contains(text_response.value, "content-type: text/plain; charset=utf-8") + if !text_status || !text_content_type { + return 2 + } + let text_body: Maybe> = std.http.responseBodyBytes(text_response.value) + if !text_body.has { + return 3 + } + let text_body_ok: Bool = std.mem.eql(text_body.value, "hello") + if !text_body_ok { + return 4 + } + var text_404_buf: [160]u8 = [0_u8; 160] + let text_404_response: Maybe> = std.http.writeTextResponse(text_404_buf, 404_u16, "missing") + if !text_404_response.has { + return 5 + } + let text_404_status: Bool = std.str.contains(text_404_response.value, "404 Not Found") + if !text_404_status { + return 6 + } + var html_buf: [192]u8 = [0_u8; 192] + let html_response: Maybe> = std.http.writeHtmlOk(html_buf, "

ok

") + if !html_response.has { + return 7 + } + let html_content_type: Bool = std.str.contains(html_response.value, "content-type: text/html; charset=utf-8") + if !html_content_type { + return 8 + } + let html_body: Maybe> = std.http.responseBodyBytes(html_response.value) + if !html_body.has { + return 9 + } + let html_body_ok: Bool = std.mem.eql(html_body.value, "

ok

") + if !html_body_ok { + return 10 + } + var html_404_buf: [192]u8 = [0_u8; 192] + let html_404_response: Maybe> = std.http.writeHtmlResponse(html_404_buf, 404_u16, "

missing

") + if !html_404_response.has { + return 11 + } + let html_404_status: Bool = std.str.contains(html_404_response.value, "404 Not Found") + if !html_404_status { + return 12 + } + return 0 +} diff --git a/conformance/native/pass/std-http-text-html-response-helpers.graph b/conformance/native/pass/std-http-text-html-response-helpers.graph new file mode 100644 index 0000000..7c966ff Binary files /dev/null and b/conformance/native/pass/std-http-text-html-response-helpers.graph differ diff --git a/conformance/native/pass/std-inet.0 b/conformance/native/pass/std-inet.0 new file mode 100644 index 0000000..be38861 --- /dev/null +++ b/conformance/native/pass/std-inet.0 @@ -0,0 +1,115 @@ +fn inet_ipv4_checks() -> Bool { + let parsed: Maybe = std.inet.parseIpv4("192.168.0.1") + if !parsed.has || parsed.value != 3232235521_u32 { + return false + } + var text_storage: [16]u8 = [0_u8; 16] + let text: Maybe> = std.inet.writeIpv4(text_storage, parsed.value) + if !text.has || !std.mem.eql(text.value, "192.168.0.1") { + return false + } + if !std.inet.isIpv4Private(parsed.value) || std.inet.isIpv4Loopback(parsed.value) { + return false + } + let broadcast: Maybe = std.inet.parseIpv4("255.255.255.255") + if !broadcast.has || broadcast.value != 4294967295_u32 { + return false + } + let loopback: Maybe = std.inet.parseIpv4("127.0.0.1") + if !loopback.has || !std.inet.isIpv4Loopback(loopback.value) { + return false + } + let link_local: Maybe = std.inet.parseIpv4("169.254.10.20") + if !link_local.has || !std.inet.isIpv4LinkLocal(link_local.value) { + return false + } + let multicast: Maybe = std.inet.parseIpv4("224.0.0.1") + if !multicast.has || !std.inet.isIpv4Multicast(multicast.value) { + return false + } + let unspecified: Maybe = std.inet.parseIpv4("0.0.0.0") + if !unspecified.has || !std.inet.isIpv4Unspecified(unspecified.value) { + return false + } + let leading_zero: Maybe = std.inet.parseIpv4("01.2.3.4") + if leading_zero.has { + return false + } + return std.inet.isIpv4("0.0.0.0") && !std.inet.isIpv4("256.1.1.1") && !std.inet.isIpv4("1.2.3") && (!std.inet.isIpv4("1.2.3.4.5") && !std.inet.isIpv4("a.b.c.d")) +} + +fn inet_ipv6_checks() -> Bool { + if !std.inet.isIpv6("::") || !std.inet.isIpv6("::1") { + return false + } + if !std.inet.isIpv6("2001:db8:85a3::8a2e:370:7334") || !std.inet.isIpv6("1:2:3:4:5:6:7:8") { + return false + } + if !std.inet.isIpv6("::ffff:192.168.1.1") || !std.inet.isIpv6("1:2:3:4:5:6:1.2.3.4") { + return false + } + if std.inet.isIpv6("1::2::3") || (std.inet.isIpv6("1:2:3:4:5:6:7:8:9") || std.inet.isIpv6("12345::")) { + return false + } + if std.inet.isIpv6("1:2:3:4:5:6:7") || (std.inet.isIpv6("fe80::1%eth0") || std.inet.isIpv6("1.2.3.4")) { + return false + } + var storage: [16]u8 = [0; 16] + let buffer: MutSpan = storage + let mapped: Maybe> = std.inet.parseIpv6(buffer, "::ffff:192.168.1.1") + if !mapped.has || std.mem.len(mapped.value) != 16 { + return false + } + let bytes: Span = mapped.value + let extracted: Maybe = std.inet.ipv6MappedIpv4(bytes) + if !std.inet.isIpv6MappedIpv4(bytes) || (!extracted.has || extracted.value != 3232235777_u32) { + return false + } + return bytes[10] == 255_u8 && (bytes[11] == 255_u8 && (bytes[12] == 192_u8 && bytes[15] == 1_u8)) +} + +fn inet_ip_family_checks() -> Bool { + var storage: [16]u8 = [0_u8; 16] + let buffer: MutSpan = storage + let ipv4: Maybe> = std.inet.parseIp(buffer, "203.0.113.7") + if !ipv4.has || std.mem.len(ipv4.value) != 4 { + return false + } + let ipv4_bytes: Span = ipv4.value + if ipv4_bytes[0] != 203_u8 || (ipv4_bytes[1] != 0_u8 || (ipv4_bytes[2] != 113_u8 || ipv4_bytes[3] != 7_u8)) { + return false + } + let loopback: Maybe> = std.inet.parseIp(buffer, "::1") + if !loopback.has || !std.inet.isIpv6Loopback(loopback.value) { + return false + } + let unspecified: Maybe> = std.inet.parseIp(buffer, "::") + if !unspecified.has || !std.inet.isIpv6Unspecified(unspecified.value) { + return false + } + let link_local: Maybe> = std.inet.parseIp(buffer, "fe80::1") + if !link_local.has || !std.inet.isIpv6LinkLocal(link_local.value) { + return false + } + let unique_local: Maybe> = std.inet.parseIp(buffer, "fd00::1") + if !unique_local.has || (!std.inet.isIpv6Private(unique_local.value) || !std.inet.isIpv6UniqueLocal(unique_local.value)) { + return false + } + let multicast: Maybe> = std.inet.parseIp(buffer, "ff02::1") + if !multicast.has || !std.inet.isIpv6Multicast(multicast.value) { + return false + } + return std.inet.isIp("192.0.2.1") && (std.inet.isIp("2001:db8::1") && !std.inet.isIp("not-an-ip")) +} + +fn inet_hostname_checks() -> Bool { + return std.inet.isHostname("example.com") && std.inet.isHostname("foo-bar.baz") && std.inet.isHostname("123.example") && std.inet.isHostname("a") && !std.inet.isHostname("") && !std.inet.isHostname("-leading.example") && !std.inet.isHostname("trailing-.example") && !std.inet.isHostname("under_score.example") && !std.inet.isHostname("double..dot") && !std.inet.isHostname("trailing.dot.") +} + +pub fn main(world: World) -> Void raises { + if inet_ipv4_checks() && (inet_ipv6_checks() && (inet_ip_family_checks() && inet_hostname_checks())) { + check world.out.write("std inet ok\n") + } else { + check world.out.write("std inet failed\n") + } +} diff --git a/conformance/native/pass/std-inet.graph b/conformance/native/pass/std-inet.graph new file mode 100644 index 0000000..0661a70 Binary files /dev/null and b/conformance/native/pass/std-inet.graph differ diff --git a/conformance/native/pass/std-io-direct.0 b/conformance/native/pass/std-io-direct.0 new file mode 100644 index 0000000..f2269d1 --- /dev/null +++ b/conformance/native/pass/std-io-direct.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + var reader_buf: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var copy_dst: [4]u8 = [0, 0, 0, 0] + let reader: BufferedReader = std.io.bufferedReader(reader_buf) + let writer: BufferedWriter = std.io.bufferedWriter(reader_buf) + let copied: usize = std.io.copy(copy_dst, std.mem.span("abcd")) + if std.io.readerCapacity(&reader) == 8 && std.io.writerCapacity(&writer) == 8 && copied == 4 && copy_dst[0] == 97_u8 && copy_dst[3] == 100_u8 { + check world.out.write("std io direct ok\n") + } +} diff --git a/conformance/native/pass/std-io-direct.graph b/conformance/native/pass/std-io-direct.graph new file mode 100644 index 0000000..1e3d5c1 Binary files /dev/null and b/conformance/native/pass/std-io-direct.graph differ diff --git a/conformance/native/pass/std-io-lines.0 b/conformance/native/pass/std-io-lines.0 new file mode 100644 index 0000000..242ba8a --- /dev/null +++ b/conformance/native/pass/std-io-lines.0 @@ -0,0 +1,165 @@ +pub fn main(world: World) -> Void raises { + let text: Span = "alpha\nzero beta\r\nzero gamma" + var output: [16]u8 = [0_u8; 16] + var offset: usize = 0 + let wrote_a: Maybe = std.io.writeByte(output, offset, 111_u8) + if wrote_a.has { + offset = wrote_a.value + } + let wrote_tail: Maybe = std.io.writeSpan(output, offset, "k") + if wrote_tail.has { + offset = wrote_tail.value + } + let first: Maybe> = std.io.nextLine(text, 0) + let second_start: usize = std.io.nextLineStart(text, 0) + let second: Maybe> = std.io.nextLine(text, second_start) + let third_start: usize = std.io.nextLineStart(text, second_start) + let third: Maybe> = std.io.nextLine(text, third_start) + let written: Span = std.io.written(output, offset) + var read_buf: [4]u8 = [0_u8; 4] + let read_next: Maybe = std.io.read(text, 0, read_buf) + var exact_buf: [5]u8 = [0_u8; 5] + let exact_next: Maybe = std.io.readExact(text, 0, exact_buf) + var too_large: [32]u8 = [0_u8; 32] + let exact_short: Maybe = std.io.readExact(text, 0, too_large) + let first_byte: Maybe = std.io.readByte(text, 0) + var copy_buf: [4]u8 = [0_u8; 4] + let copied: usize = std.io.copy(copy_buf, "zero!") + let copied_exact: Maybe = std.io.copyN(copy_buf, "zero", 4) + let copied_short: Maybe = std.io.copyN(copy_buf, "xy", 3) + var write_all_buf: [4]u8 = [0_u8; 4] + let wrote_all: Maybe = std.io.writeAll(write_all_buf, 0, "done") + var write_at_buf: [4]u8 = [120_u8; 4] + let wrote_at: Maybe = std.io.writeAt(write_at_buf, 1, "io") + let read_line: Maybe> = std.io.readLine(text, second_start) + let read_line_start: usize = std.io.readLineStart(text, second_start) + let until_newline: Maybe> = std.io.readUntilDelimiter(text, 0, 10_u8) + let after_newline: usize = std.io.readUntilDelimiterStart(text, 0, 10_u8) + var read_all_buf: [4]u8 = [0_u8; 4] + let read_all: Maybe> = std.io.readAll("data", read_all_buf) + var read_at_buf: [4]u8 = [0_u8; 4] + let read_at: Maybe = std.io.readAt(text, second_start, read_at_buf) + + var line_reader: FixedReader = std.io.fixedReader(text, 0_usize) + let fixed_first: Maybe> = std.io.fixedReaderReadLine(&mut line_reader) + let fixed_byte: Maybe = std.io.fixedReaderReadByte(&mut line_reader) + let seek_second: Bool = std.io.fixedReaderSeek(&mut line_reader, second_start) + let fixed_until_space: Maybe> = std.io.fixedReaderReadUntilDelimiter(&mut line_reader, 32_u8) + var limit_reader: FixedReader = std.io.fixedReaderLimit(&line_reader, 4) + var limit_buf: [4]u8 = [0_u8; 4] + let limit_all: Maybe> = std.io.fixedReaderReadAll(&mut limit_reader, limit_buf) + var wide_limit_base: FixedReader = std.io.fixedReader("abc", 1_usize) + var wide_limit_reader: FixedReader = std.io.fixedReaderLimit(&wide_limit_base, 99_usize) + var wide_limit_buf: [2]u8 = [0_u8; 2] + let wide_limit_all: Maybe> = std.io.fixedReaderReadAll(&mut wide_limit_reader, wide_limit_buf) + var fixed_at_buf: [2]u8 = [0_u8; 2] + let fixed_at: Maybe = std.io.fixedReaderReadAt(&line_reader, 0, fixed_at_buf) + let fixed_len: usize = std.io.fixedReaderLen(&line_reader) + let fixed_remaining: usize = std.io.fixedReaderRemaining(&line_reader) + let fixed_cursor: usize = std.io.fixedReaderCursor(&line_reader) + let seek_end: Bool = std.io.fixedReaderSeek(&mut line_reader, fixed_len) + let fixed_done: Bool = std.io.fixedReaderDone(&line_reader) + var exact_reader: FixedReader = std.io.fixedReader("exact", 0_usize) + var exact_reader_buf: [5]u8 = [0_u8; 5] + let fixed_exact: Bool = std.io.fixedReaderReadExact(&mut exact_reader, exact_reader_buf) + + var writer_storage: [16]u8 = [0_u8; 16] + var writer: FixedWriter = std.io.fixedWriter(writer_storage, 0_usize) + let writer_a: Bool = std.io.fixedWriterWrite(&mut writer, "ab") + let writer_b: Bool = std.io.fixedWriterWriteByte(&mut writer, 99_u8) + let writer_at: Bool = std.io.fixedWriterWriteAt(&mut writer, 1, "ZZ") + let writer_view: Span = std.io.fixedWriterView(&writer) + let writer_cursor: usize = std.io.fixedWriterCursor(&writer) + let writer_capacity: usize = std.io.fixedWriterCapacity(&writer) + let writer_remaining: usize = std.io.fixedWriterRemaining(&writer) + let writer_truncated: usize = std.io.fixedWriterTruncate(&mut writer, 2_usize) + let writer_seek: Bool = std.io.fixedWriterSeek(&mut writer, 1_usize) + let writer_cleared: usize = std.io.fixedWriterClear(&mut writer) + var writer_all_storage: [4]u8 = [0_u8; 4] + var writer_all: FixedWriter = std.io.fixedWriter(writer_all_storage, 0_usize) + let writer_all_ok: Bool = std.io.fixedWriterWriteAll(&mut writer_all, "full") + + var copy_reader: FixedReader = std.io.fixedReader("copy", 0_usize) + var copy_writer_storage: [8]u8 = [0_u8; 8] + var copy_writer: FixedWriter = std.io.fixedWriter(copy_writer_storage, 0_usize) + var scratch: [2]u8 = [0_u8; 2] + let copied_stream: usize = std.io.copyBuffer(&mut copy_reader, &mut copy_writer, scratch) + var capacity_reader: FixedReader = std.io.fixedReader("abcd", 0_usize) + var capacity_writer_storage: [3]u8 = [0_u8; 3] + var capacity_writer: FixedWriter = std.io.fixedWriter(capacity_writer_storage, 0_usize) + let copied_to_capacity: usize = std.io.copyBuffer(&mut capacity_reader, &mut capacity_writer, scratch) + + var n_reader: FixedReader = std.io.fixedReader("12345", 0_usize) + var n_writer_storage: [8]u8 = [0_u8; 8] + var n_writer: FixedWriter = std.io.fixedWriter(n_writer_storage, 0_usize) + let copied_n: Maybe = std.io.copyReaderN(&mut n_reader, &mut n_writer, 3_usize, scratch) + var n_fail_reader: FixedReader = std.io.fixedReader("abcd", 0_usize) + var n_fail_writer_storage: [3]u8 = [0_u8; 3] + var n_fail_writer: FixedWriter = std.io.fixedWriter(n_fail_writer_storage, 0_usize) + let copied_n_fail: Maybe = std.io.copyReaderN(&mut n_fail_reader, &mut n_fail_writer, 4_usize, scratch) + + var discard_reader: FixedReader = std.io.fixedReader("drop", 0_usize) + let discarded: usize = std.io.discard(&mut discard_reader, scratch) + + var tee_reader: FixedReader = std.io.fixedReader("tee", 0_usize) + var tee_writer_storage: [4]u8 = [0_u8; 4] + var tee_writer: FixedWriter = std.io.fixedWriter(tee_writer_storage, 0_usize) + var tee_buf: [2]u8 = [0_u8; 2] + let tee_count: Maybe = std.io.teeRead(&mut tee_reader, &mut tee_writer, tee_buf) + + var multi_first: FixedReader = std.io.fixedReader("ab", 0_usize) + var multi_second: FixedReader = std.io.fixedReader("cd", 0_usize) + var multi_buf: [4]u8 = [0_u8; 4] + let multi_count: usize = std.io.multiRead(&mut multi_first, &mut multi_second, multi_buf) + + var ok: Bool = true + if first.has && second.has && third.has && read_next.has && exact_next.has && first_byte.has && copied_exact.has && wrote_all.has && wrote_at.has && read_line.has && until_newline.has && read_all.has && read_at.has { + ok = ok && !exact_short.has && !copied_short.has + ok = ok && copied == 4 && std.io.countLines(text) == 3 && std.io.remaining(output, offset) == 14 + ok = ok && std.mem.eql(written, "ok") && std.mem.eql(second.value, "zero beta") && std.str.contains(third.value, "gamma") + ok = ok && read_next.value == 4 && std.mem.eql(std.io.written(read_buf, 4), "alph") + ok = ok && exact_next.value == 5 && std.mem.eql(std.io.written(exact_buf, 5), "alpha") + ok = ok && first_byte.value == 97_u8 && copied_exact.value == 4 && std.mem.eql(std.io.written(copy_buf, 4), "zero") + ok = ok && wrote_all.value == 4 && std.mem.eql(std.io.written(write_all_buf, 4), "done") && wrote_at.value == 3 && std.mem.eql(std.io.written(write_at_buf, 4), "xiox") + ok = ok && std.mem.eql(read_line.value, "zero beta") && read_line_start == third_start && std.mem.eql(until_newline.value, "alpha") && after_newline == second_start + ok = ok && std.mem.eql(read_all.value, "data") && read_at.value == second_start + 4 && std.mem.eql(std.io.written(read_at_buf, 4), "zero") + } else { + ok = false + } + + if fixed_first.has && fixed_byte.has && fixed_until_space.has && limit_all.has && wide_limit_all.has && fixed_at.has { + ok = ok && seek_second && seek_end && fixed_done && fixed_exact + ok = ok && std.mem.eql(fixed_first.value, "alpha") && fixed_byte.value == 122_u8 && std.mem.eql(fixed_until_space.value, "zero") + ok = ok && std.mem.eql(limit_all.value, "beta") && std.mem.eql(wide_limit_all.value, "bc") && fixed_at.value == 2 && std.mem.eql(std.io.written(fixed_at_buf, 2), "al") + ok = ok && fixed_len == std.mem.len(text) && fixed_remaining == std.mem.len(text) - fixed_cursor && fixed_cursor == second_start + 5 + ok = ok && std.mem.eql(std.io.written(exact_reader_buf, 5), "exact") + } else { + ok = false + } + + ok = ok && writer_a && writer_b && writer_at && std.mem.eql(writer_view, "aZZ") && writer_cursor == 3 + ok = ok && writer_capacity == 16 && writer_remaining == 13 && writer_truncated == 2 && writer_seek && writer_cleared == 0 + ok = ok && writer_all_ok && std.mem.eql(std.io.fixedWriterView(&writer_all), "full") + ok = ok && copied_stream == 4 && std.mem.eql(std.io.fixedWriterView(©_writer), "copy") + ok = ok && copied_to_capacity == 3 && std.mem.eql(std.io.fixedWriterView(&capacity_writer), "abc") + if copied_n.has { + ok = ok && copied_n.value == 3 && std.mem.eql(std.io.fixedWriterView(&n_writer), "123") + } else { + ok = false + } + ok = ok && !copied_n_fail.has && std.io.fixedReaderCursor(&n_fail_reader) == 0 && std.io.fixedWriterCursor(&n_fail_writer) == 0 + ok = ok && discarded == 4 && std.io.fixedReaderDone(&discard_reader) + if tee_count.has { + ok = ok && tee_count.value == 2 && std.mem.eql(std.io.written(tee_buf, 2), "te") && std.mem.eql(std.io.fixedWriterView(&tee_writer), "te") + } else { + ok = false + } + ok = ok && multi_count == 4 && std.mem.eql(std.io.written(multi_buf, 4), "abcd") + ok = ok && std.io.errorNone() == 0_u32 && std.mem.eql(std.io.errorName(std.io.errorEof()), "eof") + ok = ok && std.mem.eql(std.io.errorName(std.io.errorShortRead()), "short_read") && std.mem.eql(std.io.errorName(std.io.errorShortWrite()), "short_write") + ok = ok && std.mem.eql(std.io.errorName(std.io.errorCapacity()), "capacity") && std.mem.eql(std.io.errorName(std.io.errorPermission()), "permission") + ok = ok && std.mem.eql(std.io.errorName(std.io.errorTimeout()), "timeout") && std.mem.eql(std.io.errorName(std.io.errorIo()), "io") && std.mem.eql(std.io.errorName(99_u32), "unknown") + if ok { + check world.out.write("std io lines ok\n") + } +} diff --git a/conformance/native/pass/std-io-lines.graph b/conformance/native/pass/std-io-lines.graph new file mode 100644 index 0000000..86cba6d Binary files /dev/null and b/conformance/native/pass/std-io-lines.graph differ diff --git a/conformance/native/pass/std-json-allocator-capacity.0 b/conformance/native/pass/std-json-allocator-capacity.0 new file mode 100644 index 0000000..4ad2c09 --- /dev/null +++ b/conformance/native/pass/std-json-allocator-capacity.0 @@ -0,0 +1,10 @@ +export c fn main() -> i32 { + let bytes: Span = std.mem.span("{\"ok\":1}") + var arena_buf: [2]u8 = [0, 0] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parseBytes(arena, bytes) + if parsed.has { + return 1 + } + return 0 +} diff --git a/conformance/native/pass/std-json-allocator-capacity.graph b/conformance/native/pass/std-json-allocator-capacity.graph new file mode 100644 index 0000000..6fd5eba Binary files /dev/null and b/conformance/native/pass/std-json-allocator-capacity.graph differ diff --git a/conformance/native/pass/std-json-bytes.0 b/conformance/native/pass/std-json-bytes.0 new file mode 100644 index 0000000..2d6188f --- /dev/null +++ b/conformance/native/pass/std-json-bytes.0 @@ -0,0 +1,10 @@ +export c fn main() -> i32 { + let bytes: Span = std.mem.span("{\"ok\":1}") + var arena_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parseBytes(arena, bytes) + if std.json.validateBytes(bytes) && parsed.has && std.json.streamTokensBytes(bytes) == 3 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/std-json-bytes.graph b/conformance/native/pass/std-json-bytes.graph new file mode 100644 index 0000000..0350d27 Binary files /dev/null and b/conformance/native/pass/std-json-bytes.graph differ diff --git a/conformance/native/pass/std-json-cursors.0 b/conformance/native/pass/std-json-cursors.0 new file mode 100644 index 0000000..5a4149b --- /dev/null +++ b/conformance/native/pass/std-json-cursors.0 @@ -0,0 +1,27 @@ +pub fn main(world: World) -> Void raises { + let data: Span = "{\"user\":{\"name\":\"zero\",\"count\":42,\"active\":true,\"scores\":[7,42]},\"empty\":[],\"items\":[{\"id\":1},{\"id\":2}]}" + let object_count: Maybe = std.json.objectFieldCount(data) + var key_buf: [16]u8 = [0_u8; 16] + let key0: Maybe> = std.json.objectKey(key_buf, data, 0) + let value0: Maybe> = std.json.objectValue(data, 0) + let user: Maybe> = std.json.field(data, "user") + let scores: Maybe> = std.json.path(data, "user.scores") + let score_count: Maybe = std.json.arrayCount("[7,42]") + let score1: Maybe> = std.json.arrayValue("[7,42]", 1) + var name_buf: [16]u8 = [0_u8; 16] + let name: Maybe> = std.json.pathString(name_buf, data, "user.name") + let count: Maybe = std.json.pathU32(data, "user.count") + let active: Maybe = std.json.pathBool(data, "user.active") + let empty_count: Maybe = std.json.arrayCount("[]") + let empty_object_count: Maybe = std.json.objectFieldCount("{}") + let bad_array: Maybe = std.json.arrayCount("[1,]") + let bad_path: Maybe> = std.json.path(data, "user.") + let bad_key: Maybe> = std.json.objectKey(key_buf, "{\"a\":1,}", 0_usize) + let bad_object_value: Maybe> = std.json.objectValue("{\"a\":1,}", 0_usize) + let bad_array_value: Maybe> = std.json.arrayValue("[1,]", 0_usize) + if object_count.has && object_count.value == 3_usize && key0.has && std.mem.eql(key0.value, "user") && value0.has && user.has && std.mem.eql(value0.value, user.value) && scores.has && std.mem.eql(scores.value, "[7,42]") && score_count.has && score_count.value == 2_usize && score1.has && std.mem.eql(score1.value, "42") && name.has && std.mem.eql(name.value, "zero") && count.has && count.value == 42_u32 && active.has && active.value && empty_count.has && empty_count.value == 0_usize && empty_object_count.has && empty_object_count.value == 0_usize && !bad_array.has && !bad_path.has && !bad_key.has && !bad_object_value.has && !bad_array_value.has { + check world.out.write("std json cursors ok\n") + return + } + check world.err.write("std json cursors failed\n") +} diff --git a/conformance/native/pass/std-json-cursors.graph b/conformance/native/pass/std-json-cursors.graph new file mode 100644 index 0000000..bff1f6a Binary files /dev/null and b/conformance/native/pass/std-json-cursors.graph differ diff --git a/conformance/native/pass/std-json-duplicate-keys.0 b/conformance/native/pass/std-json-duplicate-keys.0 new file mode 100644 index 0000000..2bdb0dc --- /dev/null +++ b/conformance/native/pass/std-json-duplicate-keys.0 @@ -0,0 +1,10 @@ +export c fn main() -> i32 { + let bytes: Span = std.mem.span("{\"a\":1,\"a\":2}") + var arena_buf: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parseBytes(arena, bytes) + if std.json.validateBytes(bytes) && parsed.has && std.json.streamTokensBytes(bytes) == 5 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/std-json-duplicate-keys.graph b/conformance/native/pass/std-json-duplicate-keys.graph new file mode 100644 index 0000000..ca1e2ea Binary files /dev/null and b/conformance/native/pass/std-json-duplicate-keys.graph differ diff --git a/conformance/native/pass/std-json-inline-bytes.0 b/conformance/native/pass/std-json-inline-bytes.0 new file mode 100644 index 0000000..6bbaa63 --- /dev/null +++ b/conformance/native/pass/std-json-inline-bytes.0 @@ -0,0 +1,6 @@ +export c fn main() -> i32 { + if std.json.validateBytes([123_u8, 125_u8]) && std.json.streamTokensBytes([91_u8, 93_u8]) == 1 { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/std-json-inline-bytes.graph b/conformance/native/pass/std-json-inline-bytes.graph new file mode 100644 index 0000000..b534279 Binary files /dev/null and b/conformance/native/pass/std-json-inline-bytes.graph differ diff --git a/conformance/native/pass/std-math-breadth.0 b/conformance/native/pass/std-math-breadth.0 new file mode 100644 index 0000000..cc6c594 --- /dev/null +++ b/conformance/native/pass/std-math-breadth.0 @@ -0,0 +1,81 @@ +pub fn main(world: World) -> Void raises { + var ok: Bool = true + if std.math.minU32(8, 3) != 3 { + ok = false + } + if std.math.minU32(4000000000_u32, 3) != 3 { + ok = false + } + if std.math.maxU32(8, 3) != 8 { + ok = false + } + if std.math.maxU32(4000000000_u32, 3) != 4000000000_u32 { + ok = false + } + if std.math.clampU32(10, 2, 7) != 7 { + ok = false + } + if std.math.clampU32(4000000000_u32, 2, 7) != 7 { + ok = false + } + if std.math.clampU32(1, 7, 2) != 2 { + ok = false + } + if std.math.clampU32(5, 7, 2) != 5 { + ok = false + } + if std.math.gcdU32(84, 30) != 6 { + ok = false + } + if std.math.gcdU32(0, 9) != 9 { + ok = false + } + if std.math.lcmU32(21, 6) != 42 { + ok = false + } + if std.math.lcmU32(0, 6) != 0 { + ok = false + } + if std.math.powU32(3, 4) != 81 { + ok = false + } + if std.math.powU32(9, 0) != 1 { + ok = false + } + if std.math.modPowU32(4, 13, 497) != 445 { + ok = false + } + if std.math.modPowU32(65536_u32, 2_u32, 4294967295_u32) != 1_u32 { + ok = false + } + if std.math.modPowU32(9, 0, 1) != 0 { + ok = false + } + if std.math.modPowU32(9, 4, 0) != 0 { + ok = false + } + if !std.math.isPrimeU32(31) { + ok = false + } + if std.math.isPrimeU32(33) { + ok = false + } + if std.math.isPrimeU32(1) { + ok = false + } + if std.math.divisorCountU32(28) != 6 { + ok = false + } + if std.math.divisorCountU32(1) != 1 { + ok = false + } + if std.math.properDivisorSumU32(28) != 28 { + ok = false + } + if std.math.properDivisorSumU32(12) != 16 { + ok = false + } + if ok { + check world.out.write("std math breadth ok\n") + } +} diff --git a/conformance/native/pass/std-math-breadth.graph b/conformance/native/pass/std-math-breadth.graph new file mode 100644 index 0000000..9921d88 Binary files /dev/null and b/conformance/native/pass/std-math-breadth.graph differ diff --git a/conformance/native/pass/std-mem-arena.0 b/conformance/native/pass/std-mem-arena.0 new file mode 100644 index 0000000..34f96cf --- /dev/null +++ b/conformance/native/pass/std-mem-arena.0 @@ -0,0 +1,12 @@ +pub fn main(world: World) -> Void raises { + var storage: [16]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var arena: FixedBufAlloc = std.mem.arena(storage) + let first: Maybe> = std.mem.allocBytes(arena, 8) + if first.has && std.mem.capacity(arena) == 16 { + std.mem.reset(&mut arena) + let second: Maybe> = std.mem.allocBytes(arena, 16) + if second.has { + check world.out.write("arena ok\n") + } + } +} diff --git a/conformance/native/pass/std-mem-arena.graph b/conformance/native/pass/std-mem-arena.graph new file mode 100644 index 0000000..9e25eaa Binary files /dev/null and b/conformance/native/pass/std-mem-arena.graph differ diff --git a/conformance/native/pass/std-mem-arrays.0 b/conformance/native/pass/std-mem-arrays.0 new file mode 100644 index 0000000..d4429aa --- /dev/null +++ b/conformance/native/pass/std-mem-arrays.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let bytes: [4]u8 = [122, 101, 114, 111] + var scratch: [4]u8 = [0, 0, 0, 0] + let copied: usize = std.mem.copy(scratch, bytes) + if std.mem.eql("zero", "zero") && copied == 4 && scratch[0] == 122 as u8 { + check world.out.write("native std mem arrays\n") + } +} diff --git a/conformance/native/pass/std-mem-arrays.graph b/conformance/native/pass/std-mem-arrays.graph new file mode 100644 index 0000000..fa7845c Binary files /dev/null and b/conformance/native/pass/std-mem-arrays.graph differ diff --git a/conformance/native/pass/std-mem-bool-copy-items.0 b/conformance/native/pass/std-mem-bool-copy-items.0 new file mode 100644 index 0000000..79bc400 --- /dev/null +++ b/conformance/native/pass/std-mem-bool-copy-items.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + let src: [3]Bool = [true, false, true] + var dst: [3]Bool = [false, false, false] + let copied: usize = std.mem.copyItems(dst, src) + let copiedValues: Bool = dst[0] && !dst[1] && dst[2] + let filled: usize = std.mem.fillItems(dst[1..], true) + let span: Span = dst + let filledValues: Bool = dst[0] && dst[1] && dst[2] + let containsOk: Bool = std.mem.contains(span, true) && !std.mem.contains(span, false) + let lenOk: Bool = std.mem.len(span) == 3 && !std.mem.isEmpty(span) + let ok: Bool = copied == 3 && copiedValues && filled == 2 && filledValues && containsOk && lenOk + if ok { + check world.out.write("std mem bool copy items ok\n") + } +} diff --git a/conformance/native/pass/std-mem-bool-copy-items.graph b/conformance/native/pass/std-mem-bool-copy-items.graph new file mode 100644 index 0000000..4797018 Binary files /dev/null and b/conformance/native/pass/std-mem-bool-copy-items.graph differ diff --git a/conformance/native/pass/std-mem-byte-field-copy.0 b/conformance/native/pass/std-mem-byte-field-copy.0 new file mode 100644 index 0000000..31afddc --- /dev/null +++ b/conformance/native/pass/std-mem-byte-field-copy.0 @@ -0,0 +1,13 @@ +type ByteHolder { + bytes: [4]u8, +} + +pub fn main(world: World) -> Void raises { + var holder: ByteHolder = ByteHolder { bytes: [0_u8, 0_u8, 0_u8, 0_u8] } + let copied: usize = std.mem.copy(holder.bytes, std.mem.span("xy")) + let filled: usize = std.mem.fill(holder.bytes[2..], 122_u8) + let fieldBytesOk: Bool = copied == 2 && filled == 2 && holder.bytes[0] == 120_u8 && holder.bytes[1] == 121_u8 && holder.bytes[2] == 122_u8 && holder.bytes[3] == 122_u8 + if fieldBytesOk { + check world.out.write("std mem byte field copy ok\n") + } +} diff --git a/conformance/native/pass/std-mem-byte-field-copy.graph b/conformance/native/pass/std-mem-byte-field-copy.graph new file mode 100644 index 0000000..cb446f6 Binary files /dev/null and b/conformance/native/pass/std-mem-byte-field-copy.graph differ diff --git a/conformance/native/pass/std-mem-collections.0 b/conformance/native/pass/std-mem-collections.0 new file mode 100644 index 0000000..bc49486 --- /dev/null +++ b/conformance/native/pass/std-mem-collections.0 @@ -0,0 +1,81 @@ +pub fn main(world: World) -> Void raises { + var storage: [4]u8 = [0, 0, 0, 0] + var vec: Vec = std.mem.vec(storage) + let emptyBeforePush: Bool = std.mem.vecIsEmpty(&vec) + let pushedA: Bool = std.mem.vecPush(&mut vec, 65_u8) + let pushedB: Bool = std.mem.vecPush(&mut vec, 66_u8) + let setSecond: Bool = std.mem.vecSet(&mut vec, 1_usize, 67_u8) + let setMissing: Bool = std.mem.vecSet(&mut vec, 9_usize, 68_u8) + let remaining: usize = std.mem.vecRemaining(&vec) + let fullAfterTwo: Bool = std.mem.vecIsFull(&vec) + let firstRead: Maybe = std.mem.vecGet(&vec, 0_usize) + let secondRead: Maybe = std.mem.vecGet(&vec, 1_usize) + let missingRead: Maybe = std.mem.vecGet(&vec, 9_usize) + let containsSecond: Bool = std.mem.vecContains(&vec, 67_u8) + let missingValue: Bool = std.mem.vecContains(&vec, 68_u8) + let secondIndex: usize = std.mem.vecIndex(&vec, 67_u8) + let missingIndex: usize = std.mem.vecIndex(&vec, 68_u8) + let uniqueDuplicate: Bool = std.mem.vecInsertUnique(&mut vec, 67_u8) + let uniqueInsert: Bool = std.mem.vecInsertUnique(&mut vec, 68_u8) + let valueRemoved: Bool = std.mem.vecRemoveValue(&mut vec, 65_u8) + let valueRemoveMissing: Bool = std.mem.vecRemoveValue(&mut vec, 65_u8) + let removedFirst: Bool = std.mem.vecRemoveSwap(&mut vec, 0_usize) + let removeMissing: Bool = std.mem.vecRemoveSwap(&mut vec, 9_usize) + let firstAfterRemove: Maybe = std.mem.vecGet(&vec, 0_usize) + let pushedC: Bool = std.mem.vecPush(&mut vec, 69_u8) + let bytesBeforeTruncate: Span = std.mem.vecBytes(&vec) + let truncatedHigh: usize = std.mem.vecTruncate(&mut vec, 4_usize) + let truncated: usize = std.mem.vecTruncate(&mut vec, 1_usize) + let secondAfterTruncate: Maybe = std.mem.vecGet(&vec, 1_usize) + let bytesBeforePop: Span = std.mem.vecBytes(&vec) + let popped: Bool = std.mem.vecPop(&mut vec) + let bytesAfterPop: Span = std.mem.vecBytes(&vec) + let cleared: usize = std.mem.vecClear(&mut vec) + let emptyAfterClear: Bool = std.mem.vecIsEmpty(&vec) + var values: [4]u8 = [0, 0, 0, 0] + var len: usize = 0 + len = std.collections.push(values, len, 65_u8) + len = std.collections.push(values, len, 66_u8) + let live: Span = std.collections.view(values, len) + var setBacking: [4]u8 = [0_u8; 4] + var dequeBacking: [4]u8 = [0_u8; 4] + var mapKeyBacking: [3]u8 = [0_u8; 3] + var mapValueBacking: [3]u8 = [0_u8; 3] + var setAlloc: FixedBufAlloc = std.mem.fixedBufAlloc(setBacking) + var dequeAlloc: FixedBufAlloc = std.mem.fixedBufAlloc(dequeBacking) + var mapKeyAlloc: FixedBufAlloc = std.mem.fixedBufAlloc(mapKeyBacking) + var mapValueAlloc: FixedBufAlloc = std.mem.fixedBufAlloc(mapValueBacking) + let setStorageMaybe: Maybe> = std.mem.allocBytes(setAlloc, 4_usize) + let dequeStorageMaybe: Maybe> = std.mem.allocBytes(dequeAlloc, 4_usize) + let mapKeysMaybe: Maybe> = std.mem.allocBytes(mapKeyAlloc, 3_usize) + let mapValuesMaybe: Maybe> = std.mem.allocBytes(mapValueAlloc, 3_usize) + var allocatorContainersOk: Bool = false + if setStorageMaybe.has && dequeStorageMaybe.has && mapKeysMaybe.has && mapValuesMaybe.has { + var allocatorSet: FixedSet = std.collections.fixedSet(setStorageMaybe.value, 0_usize) + let allocatorSetInsertedA: Bool = std.collections.fixedSetInsert(&mut allocatorSet, 70_u8) + let allocatorSetInsertedB: Bool = std.collections.fixedSetInsert(&mut allocatorSet, 71_u8) + let allocatorSetDuplicate: Bool = std.collections.fixedSetInsert(&mut allocatorSet, 71_u8) + let allocatorSetRemoved: Bool = std.collections.fixedSetRemove(&mut allocatorSet, 70_u8) + let allocatorSetLive: Span = std.collections.fixedSetView(&allocatorSet) + var allocatorDeque: FixedDeque = std.collections.fixedDeque(dequeStorageMaybe.value, 0_usize) + let allocatorDequePushBack: Bool = std.collections.fixedDequePushBack(&mut allocatorDeque, 80_u8) + let allocatorDequePushFront: Bool = std.collections.fixedDequePushFront(&mut allocatorDeque, 79_u8) + let allocatorDequeFront: Maybe = std.collections.fixedDequeFront(&allocatorDeque) + let allocatorDequeBack: Maybe = std.collections.fixedDequeBack(&allocatorDeque) + let allocatorDequePopFront: Maybe = std.collections.fixedDequePopFront(&mut allocatorDeque) + let allocatorDequeLive: Span = std.collections.fixedDequeView(&allocatorDeque) + var allocatorMap: FixedMap = std.collections.fixedMap(mapKeysMaybe.value, mapValuesMaybe.value, 0_usize) + let allocatorMapPutA: Bool = std.collections.fixedMapPut(&mut allocatorMap, 1_u8, 90_u8) + let allocatorMapPutB: Bool = std.collections.fixedMapPut(&mut allocatorMap, 2_u8, 91_u8) + let allocatorMapUpdated: Bool = std.collections.fixedMapPut(&mut allocatorMap, 2_u8, 92_u8) + let allocatorMapValue: Maybe = std.collections.fixedMapGet(&allocatorMap, 2_u8) + let allocatorMapKeys: Span = std.collections.fixedMapKeys(&allocatorMap) + let allocatorMapValues: Span = std.collections.fixedMapValues(&allocatorMap) + let allocatorMapBeforeRemoveOk: Bool = allocatorMapValue.has && allocatorMapValue.value == 92_u8 && std.mem.len(allocatorMapKeys) == 2 && std.mem.len(allocatorMapValues) == 2 && allocatorMapKeys[0] == 1_u8 && allocatorMapKeys[1] == 2_u8 && allocatorMapValues[0] == 90_u8 && allocatorMapValues[1] == 92_u8 + let allocatorMapRemoved: Bool = std.collections.fixedMapRemove(&mut allocatorMap, 1_u8) + allocatorContainersOk = allocatorSetInsertedA && allocatorSetInsertedB && !allocatorSetDuplicate && allocatorSetRemoved && std.mem.len(allocatorSetLive) == 1 && allocatorSetLive[0] == 71_u8 && std.collections.fixedSetContains(&allocatorSet, 71_u8) && allocatorDequePushBack && allocatorDequePushFront && allocatorDequeFront.has && allocatorDequeFront.value == 79_u8 && allocatorDequeBack.has && allocatorDequeBack.value == 80_u8 && allocatorDequePopFront.has && allocatorDequePopFront.value == 79_u8 && std.mem.len(allocatorDequeLive) == 1 && allocatorDequeLive[0] == 80_u8 && allocatorMapPutA && allocatorMapPutB && allocatorMapUpdated && allocatorMapBeforeRemoveOk && allocatorMapRemoved && std.collections.fixedMapLen(&allocatorMap) == 1 && std.collections.fixedMapContains(&allocatorMap, 2_u8) + } + if emptyBeforePush && pushedA && pushedB && setSecond && !setMissing && std.mem.vecLen(&vec) == 0 && std.mem.vecCapacity(&vec) == 4 && remaining == 2 && !fullAfterTwo && firstRead.has && firstRead.value == 65_u8 && secondRead.has && secondRead.value == 67_u8 && !missingRead.has && containsSecond && !missingValue && secondIndex == 1_usize && missingIndex == 2_usize && !uniqueDuplicate && uniqueInsert && valueRemoved && !valueRemoveMissing && removedFirst && !removeMissing && firstAfterRemove.has && firstAfterRemove.value == 67_u8 && pushedC && truncatedHigh == 2 && truncated == 1 && !secondAfterTruncate.has && popped && cleared == 0 && emptyAfterClear && std.mem.len(bytesBeforeTruncate) == 2 && std.mem.len(bytesBeforePop) == 1 && std.mem.len(bytesAfterPop) == 0 && bytesBeforeTruncate[0] == 67_u8 && bytesBeforeTruncate[1] == 69_u8 && bytesBeforePop[0] == 67_u8 && storage[0] == 67_u8 && storage[1] == 69_u8 && std.mem.len(live) == 2 && std.collections.remaining(values, len) == 2 && !std.collections.isFull(values, len) && std.collections.contains(values, len, 66_u8) && allocatorContainersOk { + check world.out.write("std mem collections ok\n") + } +} diff --git a/conformance/native/pass/std-mem-collections.graph b/conformance/native/pass/std-mem-collections.graph new file mode 100644 index 0000000..95b28c9 Binary files /dev/null and b/conformance/native/pass/std-mem-collections.graph differ diff --git a/conformance/native/pass/std-mem-copy-fill.0 b/conformance/native/pass/std-mem-copy-fill.0 new file mode 100644 index 0000000..fffc338 --- /dev/null +++ b/conformance/native/pass/std-mem-copy-fill.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + let src: [5]u8 = [97, 103, 101, 110, 116] + var dst: [5]u8 = [0, 0, 0, 0, 0] + let copied: usize = std.mem.copy(dst, src) + let copied_first: u8 = dst[0] + let filled: usize = std.mem.fill(dst, 33_u8) + if copied == 5 && copied_first == 97 as u8 && filled == 5 && dst[3] == 33_u8 { + check world.out.write("mem copy fill ok\n") + } +} diff --git a/conformance/native/pass/std-mem-copy-fill.graph b/conformance/native/pass/std-mem-copy-fill.graph new file mode 100644 index 0000000..66b349b Binary files /dev/null and b/conformance/native/pass/std-mem-copy-fill.graph differ diff --git a/conformance/native/pass/std-mem-field-items.0 b/conformance/native/pass/std-mem-field-items.0 new file mode 100644 index 0000000..e1aaae5 --- /dev/null +++ b/conformance/native/pass/std-mem-field-items.0 @@ -0,0 +1,18 @@ +type Holder { + tag: i32, + values: [4]i32, + scratch: [4]i32, +} + +pub fn main(world: World) -> Void raises { + var holder: Holder = Holder { tag: 77, values: [1, 2, 3, 4], scratch: [0, 0, 0, 0] } + let filled: usize = std.mem.fillItems(holder.values[1..], 9) + let copied: usize = std.mem.copyItems(holder.scratch, holder.values) + let prefix: Span = std.mem.prefix(holder.scratch, 3) + let tail: Span = std.mem.dropPrefix(holder.values, 2) + let fieldItemsOk: Bool = filled == 3 && copied == 4 && holder.tag == 77 && holder.values[0] == 1 && holder.values[1] == 9 && holder.values[3] == 9 && holder.scratch[0] == 1 && holder.scratch[1] == 9 && holder.scratch[3] == 9 + let fieldSpansOk: Bool = std.mem.len(prefix) == 3 && std.mem.contains(prefix, 9) && std.mem.len(tail) == 2 && tail[0] == 9 && tail[1] == 9 + if fieldItemsOk && fieldSpansOk { + check world.out.write("std mem field items ok\n") + } +} diff --git a/conformance/native/pass/std-mem-field-items.graph b/conformance/native/pass/std-mem-field-items.graph new file mode 100644 index 0000000..8b175b2 Binary files /dev/null and b/conformance/native/pass/std-mem-field-items.graph differ diff --git a/conformance/native/pass/std-mem-generic-items.0 b/conformance/native/pass/std-mem-generic-items.0 new file mode 100644 index 0000000..fdc0e0d --- /dev/null +++ b/conformance/native/pass/std-mem-generic-items.0 @@ -0,0 +1,86 @@ +pub fn main(world: World) -> Void raises { + let values: [4]i32 = [1, 2, 3, 4] + var scratch: [4]i32 = [0, 0, 0, 0] + let copied: usize = std.mem.copyItems(scratch, values) + let filled: usize = std.mem.fillItems(scratch[2..], 9) + let prefix: Span = std.mem.prefix(scratch, 3) + let tail: Span = std.mem.dropPrefix(scratch, 2) + let suffix: Span = std.mem.suffix(scratch, 2) + let head: Span = std.mem.dropSuffix(scratch, 2) + let window: Span = std.mem.slice(scratch, 1_usize, 2_usize) + let oversizedWindow: Span = std.mem.slice(scratch, 2_usize, 9_usize) + let emptyWindow: Span = std.mem.slice(scratch, 9_usize, 2_usize) + let splitBefore: Span = std.mem.splitBefore(scratch, 9) + let splitAfter: Span = std.mem.splitAfter(scratch, 9) + let splitBeforeMissing: Span = std.mem.splitBefore(scratch, 7) + let splitAfterMissing: Span = std.mem.splitAfter(scratch, 7) + let scratchSpan: Span = scratch + let chunkCount: usize = std.mem.chunkCount(scratchSpan, 2_usize) + let chunkFirst: Span = std.mem.chunk(scratchSpan, 0_usize, 2_usize) + let chunkSecond: Span = std.mem.chunk(scratchSpan, 1_usize, 2_usize) + let chunkShort: Span = std.mem.chunk(scratchSpan, 1_usize, 3_usize) + let chunkMissing: Span = std.mem.chunk(scratchSpan, 2_usize, 2_usize) + let chunkZeroCount: usize = std.mem.chunkCount(scratchSpan, 0_usize) + let chunkZero: Span = std.mem.chunk(scratchSpan, 0_usize, 0_usize) + let windowCount: usize = std.mem.windowCount(scratchSpan, 2_usize) + let windowFirst: Span = std.mem.window(scratchSpan, 0_usize, 2_usize) + let windowMiddle: Span = std.mem.window(scratchSpan, 1_usize, 2_usize) + let windowLast: Span = std.mem.window(scratchSpan, 2_usize, 2_usize) + let windowMissing: Span = std.mem.window(scratchSpan, 3_usize, 2_usize) + let windowZeroCount: usize = std.mem.windowCount(scratchSpan, 0_usize) + let windowZero: Span = std.mem.window(scratchSpan, 0_usize, 0_usize) + let windowOversizedCount: usize = std.mem.windowCount(scratchSpan, 5_usize) + var cursor: usize = 0 + let cursorStartDone: Bool = std.mem.cursorDone(scratchSpan, cursor) + let cursorFirst: Span = std.mem.cursorChunk(scratchSpan, cursor, 2_usize) + cursor = std.mem.advance(scratchSpan, cursor, 2_usize) + let cursorRest: Span = std.mem.remaining(scratchSpan, cursor) + let cursorSecond: Span = std.mem.cursorChunk(scratchSpan, cursor, 9_usize) + cursor = std.mem.advance(scratchSpan, cursor, 99_usize) + let cursorEndDone: Bool = std.mem.cursorDone(scratchSpan, cursor) + let cursorEmpty: Span = std.mem.remaining(scratchSpan, cursor) + let different: [4]i32 = [1, 2, 9, 10] + let differentSpan: Span = different + let shorter: [3]i32 = [1, 2, 9] + let shorterSpan: Span = shorter + let compareI32Ok: Bool = std.mem.compareI32(scratchSpan, scratchSpan) == 0 && std.mem.compareI32(scratchSpan, differentSpan) < 0 && std.mem.compareI32(differentSpan, scratchSpan) > 0 && std.mem.compareI32(shorterSpan, scratchSpan) < 0 + let u32Left: [3]u32 = [1_u32, 2_u32, 5_u32] + let u32Right: [3]u32 = [1_u32, 3_u32, 1_u32] + let u32Short: [2]u32 = [1_u32, 2_u32] + let compareU32Ok: Bool = std.mem.compareU32(u32Left, u32Left) == 0 && std.mem.compareU32(u32Left, u32Right) < 0 && std.mem.compareU32(u32Right, u32Left) > 0 && std.mem.compareU32(u32Short, u32Left) < 0 + let usizeLeft: [3]usize = [2_usize, 4_usize, 6_usize] + let usizeRight: [3]usize = [2_usize, 4_usize, 7_usize] + let usizeShort: [2]usize = [2_usize, 4_usize] + let compareUsizeOk: Bool = std.mem.compareUsize(usizeLeft, usizeLeft) == 0 && std.mem.compareUsize(usizeLeft, usizeRight) < 0 && std.mem.compareUsize(usizeRight, usizeLeft) > 0 && std.mem.compareUsize(usizeShort, usizeLeft) < 0 + let bytesLeft: Span = std.mem.span("abc") + let bytesRight: Span = std.mem.span("abd") + let bytesShort: Span = std.mem.span("ab") + let bytesPrefix: Span = std.mem.span("ab") + let bytesSuffix: Span = std.mem.span("bc") + let compareBytesOk: Bool = std.mem.compareU8(bytesLeft, bytesLeft) == 0 && std.mem.compareU8(bytesLeft, bytesRight) < 0 && std.mem.compareBytes(bytesRight, bytesLeft) > 0 && std.mem.compareBytes(bytesShort, bytesLeft) < 0 + let predicatesOk: Bool = std.mem.startsWith(scratchSpan, head) && !std.mem.startsWith(scratchSpan, suffix) && std.mem.endsWith(scratchSpan, suffix) && !std.mem.endsWith(scratchSpan, head) && std.mem.startsWith(bytesLeft, bytesPrefix) && std.mem.endsWith(bytesLeft, bytesSuffix) + var bytes: [5]u8 = [0, 0, 0, 0, 0] + let byteCopied: usize = std.mem.copy(bytes[1..], std.mem.span("xy")) + let byteFilled: usize = std.mem.fill(bytes[3..], 122_u8) + var words: [3]u16 = [10_u16, 20_u16, 30_u16] + let wordFilled: usize = std.mem.fillItems(words[1..], 7_u16) + let wordSpan: Span = words + let wordTail: Span = std.mem.dropPrefix(words, 1) + let wordSuffix: Span = std.mem.suffix(words, 2) + let wordBefore: Span = std.mem.splitBefore(words, 7_u16) + let wordAfter: Span = std.mem.splitAfter(words, 7_u16) + let wordChunk: Span = std.mem.chunk(wordSpan, 1_usize, 2_usize) + let wordWindow: Span = std.mem.window(wordSpan, 1_usize, 2_usize) + let wordCursor: usize = std.mem.advance(wordSpan, 0_usize, 1_usize) + let wordRemaining: Span = std.mem.remaining(wordSpan, wordCursor) + let itemsOk: Bool = copied == 4 && filled == 2 && scratch[0] == 1 && scratch[1] == 2 && scratch[2] == 9 && scratch[3] == 9 + let spansOk: Bool = std.mem.contains(prefix, 9) && !std.mem.contains(prefix, 4) && compareI32Ok && compareU32Ok && compareUsizeOk && compareBytesOk && predicatesOk && std.mem.len(tail) == 2 && !std.mem.isEmpty(tail) && std.mem.isEmpty(std.mem.dropPrefix(tail, 2)) && std.mem.len(suffix) == 2 && suffix[0] == 9 && suffix[1] == 9 && std.mem.len(head) == 2 && head[0] == 1 && head[1] == 2 && std.mem.len(window) == 2 && window[0] == 2 && window[1] == 9 && std.mem.len(oversizedWindow) == 2 && oversizedWindow[0] == 9 && oversizedWindow[1] == 9 && std.mem.isEmpty(emptyWindow) && std.mem.len(splitBefore) == 2 && splitBefore[0] == 1 && splitBefore[1] == 2 && std.mem.len(splitAfter) == 1 && splitAfter[0] == 9 && std.mem.len(splitBeforeMissing) == 4 && std.mem.isEmpty(splitAfterMissing) && !std.mem.eqlBytes(scratchSpan, differentSpan) + let chunksOk: Bool = chunkCount == 2 && std.mem.len(chunkFirst) == 2 && chunkFirst[0] == 1 && chunkFirst[1] == 2 && std.mem.len(chunkSecond) == 2 && chunkSecond[0] == 9 && chunkSecond[1] == 9 && std.mem.len(chunkShort) == 1 && chunkShort[0] == 9 && std.mem.isEmpty(chunkMissing) && chunkZeroCount == 0 && std.mem.isEmpty(chunkZero) + let windowsOk: Bool = windowCount == 3 && std.mem.len(windowFirst) == 2 && windowFirst[0] == 1 && windowFirst[1] == 2 && std.mem.len(windowMiddle) == 2 && windowMiddle[0] == 2 && windowMiddle[1] == 9 && std.mem.len(windowLast) == 2 && windowLast[0] == 9 && windowLast[1] == 9 && std.mem.isEmpty(windowMissing) && windowZeroCount == 0 && std.mem.isEmpty(windowZero) && windowOversizedCount == 0 + let cursorsOk: Bool = !cursorStartDone && std.mem.len(cursorFirst) == 2 && cursorFirst[0] == 1 && cursorFirst[1] == 2 && std.mem.len(cursorRest) == 2 && cursorRest[0] == 9 && cursorRest[1] == 9 && std.mem.len(cursorSecond) == 2 && cursorSecond[0] == 9 && cursorSecond[1] == 9 && cursor == std.mem.len(scratchSpan) && cursorEndDone && std.mem.isEmpty(cursorEmpty) + let bytesOk: Bool = byteCopied == 2 && byteFilled == 2 && bytes[0] == 0 && bytes[1] == 120 && bytes[2] == 121 && bytes[3] == 122 && bytes[4] == 122 + let wordsOk: Bool = wordFilled == 2 && words[1] == 7_u16 && words[2] == 7_u16 && std.mem.len(wordTail) == 2 && std.mem.contains(wordTail, 7_u16) && std.mem.len(wordSuffix) == 2 && wordSuffix[0] == 7_u16 && wordSuffix[1] == 7_u16 && std.mem.len(wordBefore) == 1 && wordBefore[0] == 10_u16 && std.mem.len(wordAfter) == 1 && wordAfter[0] == 7_u16 && std.mem.len(wordChunk) == 1 && wordChunk[0] == 7_u16 && std.mem.len(wordWindow) == 2 && wordWindow[0] == 7_u16 && wordWindow[1] == 7_u16 && wordCursor == 1 && std.mem.len(wordRemaining) == 2 && wordRemaining[0] == 7_u16 && wordRemaining[1] == 7_u16 + if itemsOk && spansOk && chunksOk && windowsOk && cursorsOk && bytesOk && wordsOk { + check world.out.write("std mem generic items ok\n") + } +} diff --git a/conformance/native/pass/std-mem-generic-items.graph b/conformance/native/pass/std-mem-generic-items.graph new file mode 100644 index 0000000..545900c Binary files /dev/null and b/conformance/native/pass/std-mem-generic-items.graph differ diff --git a/conformance/native/pass/std-mem-local-u64-copy-items.0 b/conformance/native/pass/std-mem-local-u64-copy-items.0 new file mode 100644 index 0000000..ed2b5b0 --- /dev/null +++ b/conformance/native/pass/std-mem-local-u64-copy-items.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let src: [2]u64 = [4294967296_u64, 8589934592_u64] + var dst: [2]u64 = [0_u64, 0_u64] + let copied: usize = std.mem.copyItems(dst, src) + if copied == 2 && dst[0] == 4294967296_u64 && dst[1] == 8589934592_u64 { + check world.out.write("std mem local u64 copy items ok\n") + } +} diff --git a/conformance/native/pass/std-mem-local-u64-copy-items.graph b/conformance/native/pass/std-mem-local-u64-copy-items.graph new file mode 100644 index 0000000..f22240b Binary files /dev/null and b/conformance/native/pass/std-mem-local-u64-copy-items.graph differ diff --git a/conformance/native/pass/std-mem-u64-contains.0 b/conformance/native/pass/std-mem-u64-contains.0 new file mode 100644 index 0000000..47d6f06 --- /dev/null +++ b/conformance/native/pass/std-mem-u64-contains.0 @@ -0,0 +1,12 @@ +type WideHolder { + values: [2]u64, +} + +pub fn main(world: World) -> Void raises { + let holder: WideHolder = WideHolder { values: [4294967296_u64, 8589934592_u64] } + let containsWide: Bool = std.mem.contains(holder.values, 8589934592_u64) + let rejectsLowBitsOnly: Bool = !std.mem.contains(holder.values, 0_u64) + if containsWide && rejectsLowBitsOnly { + check world.out.write("std mem u64 contains ok\n") + } +} diff --git a/conformance/native/pass/std-mem-u64-contains.graph b/conformance/native/pass/std-mem-u64-contains.graph new file mode 100644 index 0000000..38f8551 Binary files /dev/null and b/conformance/native/pass/std-mem-u64-contains.graph differ diff --git a/conformance/native/pass/std-mem-u64-copy-items.0 b/conformance/native/pass/std-mem-u64-copy-items.0 new file mode 100644 index 0000000..97d027b --- /dev/null +++ b/conformance/native/pass/std-mem-u64-copy-items.0 @@ -0,0 +1,13 @@ +type WideCopyHolder { + src: [2]u64, + dst: [2]u64, +} + +pub fn main(world: World) -> Void raises { + var holder: WideCopyHolder = WideCopyHolder { src: [4294967296_u64, 8589934592_u64], dst: [0_u64, 0_u64] } + let copied: usize = std.mem.copyItems(holder.dst, holder.src) + let copiedWideValues: Bool = copied == 2 && holder.dst[0] == 4294967296_u64 && holder.dst[1] == 8589934592_u64 + if copiedWideValues { + check world.out.write("std mem u64 copy items ok\n") + } +} diff --git a/conformance/native/pass/std-mem-u64-copy-items.graph b/conformance/native/pass/std-mem-u64-copy-items.graph new file mode 100644 index 0000000..8362c1c Binary files /dev/null and b/conformance/native/pass/std-mem-u64-copy-items.graph differ diff --git a/conformance/native/pass/std-net-http-breadth.0 b/conformance/native/pass/std-net-http-breadth.0 new file mode 100644 index 0000000..7eec54b --- /dev/null +++ b/conformance/native/pass/std-net-http-breadth.0 @@ -0,0 +1,15 @@ +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let addr: Address = std.net.withTimeout(std.net.address("localhost", 8080_u16), std.time.ms(250)) + let local: Address = std.net.withTimeout(std.net.localhost(8080_u16), std.time.ms(250)) + let loopback: Address = std.net.loopback(8080_u16) + let conn: Maybe = std.net.connect(net, addr) + let listener: Maybe = std.net.listen(net, addr) + let _client: HttpClient = std.http.client(net) + let _server: HttpServer = std.http.server(net, addr) + let getA: HttpMethod = std.http.parseMethod("GET") + let getB: HttpMethod = std.http.parseMethod("GET") + if !conn.has && !listener.has && std.mem.eql(std.net.dnsName(addr), "localhost") && std.mem.eql(std.net.dnsName(local), "localhost") && std.mem.eql(std.net.dnsName(loopback), "127.0.0.1") && getA == getB && std.mem.len(std.mem.span("body")) == 4 && std.mem.eql(std.http.tlsBoundary(), "platform-or-c-library") { + check world.out.write("std net http breadth ok\n") + } +} diff --git a/conformance/native/pass/std-net-http-breadth.graph b/conformance/native/pass/std-net-http-breadth.graph new file mode 100644 index 0000000..f68b8a1 Binary files /dev/null and b/conformance/native/pass/std-net-http-breadth.graph differ diff --git a/conformance/native/pass/std-numeric-random-time.0 b/conformance/native/pass/std-numeric-random-time.0 new file mode 100644 index 0000000..a24bd84 --- /dev/null +++ b/conformance/native/pass/std-numeric-random-time.0 @@ -0,0 +1,123 @@ +pub fn main(world: World) -> Void raises { + var ok: Bool = true + let duration: Duration = std.time.add(std.time.us(2500_i64), std.time.ms(1)) + let window: Duration = std.time.add(std.time.minutes(1), std.time.seconds(30)) + let clamped: Duration = std.time.clamp(std.time.seconds(3), std.time.seconds(1), std.time.seconds(2)) + if std.time.asNs(std.time.ns(42_i64)) != 42_i64 { + ok = false + } + if std.time.asUsFloor(std.time.ns(1999_i64)) != 1_i64 { + ok = false + } + if std.time.asMsFloor(duration) != 3 { + ok = false + } + if std.time.asSecondsFloor(window) != 90_i64 { + ok = false + } + if std.time.asSecondsFloor(clamped) != 2_i64 { + ok = false + } + let negative_tick: Duration = std.time.sub(std.time.zero(), std.time.ns(1_i64)) + if std.time.asUsFloor(negative_tick) != 0_i64 - 1_i64 { + ok = false + } + if std.time.asMsFloor(negative_tick) != 0 - 1 { + ok = false + } + if std.time.asSecondsFloor(negative_tick) != 0_i64 - 1_i64 { + ok = false + } + if !std.time.lessThan(std.time.zero(), duration) { + ok = false + } + if !std.time.isZero(std.time.zero()) { + ok = false + } + if std.time.asNs(std.time.abs(negative_tick)) != 1_i64 { + ok = false + } + if std.time.asSecondsFloor(std.time.between(std.time.seconds(5), std.time.seconds(2))) != 3_i64 { + ok = false + } + if !std.time.hasElapsed(std.time.seconds(10), std.time.seconds(13), std.time.seconds(3)) { + ok = false + } + if std.time.hasElapsed(std.time.seconds(10), std.time.seconds(12), std.time.seconds(3)) { + ok = false + } + var rng: RandSource = std.rand.seed(7_u32) + let first: u32 = std.rand.nextU32(&mut rng) + let bit: Bool = std.rand.nextBool(&mut rng) + let third: u32 = std.rand.nextU32(&mut rng) + if first != 1025555898_u32 || !bit || third != 2630631676_u32 { + ok = false + } + var bool_rng: RandSource = std.rand.seed(7_u32) + let bit_one: Bool = std.rand.nextBool(&mut bool_rng) + let bit_two: Bool = std.rand.nextBool(&mut bool_rng) + let bit_three: Bool = std.rand.nextBool(&mut bool_rng) + if bit_one || !bit_two || !bit_three { + ok = false + } + var bounded_rng: RandSource = std.rand.seed(99_u32) + let below_ten: Maybe = std.rand.nextBelow(&mut bounded_rng, 10_u32) + if below_ten.has { + if below_ten.value >= 10_u32 { + ok = false + } + } else { + ok = false + } + let in_range: Maybe = std.rand.rangeU32(&mut bounded_rng, 40_u32, 50_u32) + if in_range.has { + if in_range.value < 40_u32 || in_range.value >= 50_u32 { + ok = false + } + } else { + ok = false + } + let empty_below: Maybe = std.rand.nextBelow(&mut bounded_rng, 0_u32) + let empty_range: Maybe = std.rand.rangeU32(&mut bounded_rng, 4_u32, 4_u32) + if empty_below.has || empty_range.has { + ok = false + } + let factorial: Maybe = std.math.factorialU32(5_u32) + if factorial.has { + if factorial.value != 120_u32 { + ok = false + } + } else { + ok = false + } + let binomial: Maybe = std.math.binomialU32(6_u32, 2_u32) + if binomial.has { + if binomial.value != 15_u32 { + ok = false + } + } else { + ok = false + } + let larger_binomial: Maybe = std.math.binomialU32(31_u32, 15_u32) + if larger_binomial.has { + if larger_binomial.value != 300540195_u32 { + ok = false + } + } else { + ok = false + } + let checked: Maybe = std.math.checkedPowU32(2_u32, 10_u32) + if checked.has { + if checked.value != 1024_u32 { + ok = false + } + } else { + ok = false + } + if std.math.sqrtFloorU32(99) != 9 || std.math.saturatingAddU32(4294967295_u32, 1_u32) != 4294967295_u32 { + ok = false + } + if ok { + check world.out.write("std numeric random time ok\n") + } +} diff --git a/conformance/native/pass/std-numeric-random-time.graph b/conformance/native/pass/std-numeric-random-time.graph new file mode 100644 index 0000000..94ffca3 Binary files /dev/null and b/conformance/native/pass/std-numeric-random-time.graph differ diff --git a/conformance/native/pass/std-parse-text.0 b/conformance/native/pass/std-parse-text.0 new file mode 100644 index 0000000..85c76f8 --- /dev/null +++ b/conformance/native/pass/std-parse-text.0 @@ -0,0 +1,70 @@ +pub fn main(world: World) -> Void raises { + let byte: Maybe = std.parse.parseU8("255") + let too_big_byte: Maybe = std.parse.parseU8("256") + let port: Maybe = std.parse.parseU16("8080") + let count: Maybe = std.parse.parseU32("4294967295") + let count64: Maybe = std.parse.parseU64("18446744073709551615") + let count64_overflow: Maybe = std.parse.parseU64("18446744073709551616") + let parsed_usize: Maybe = std.parse.parseUsize("12345") + let large_usize: Maybe = std.parse.parseUsize("8589934590") + let usize_overflow: Maybe = std.parse.parseUsize("18446744073709551616") + let signed: Maybe = std.parse.parseI32("-2147483648") + let signed64: Maybe = std.parse.parseI64("-9223372036854775808") + let signed64_positive: Maybe = std.parse.parseI64("+9223372036854775807") + let signed64_overflow: Maybe = std.parse.parseI64("9223372036854775808") + let positive: Maybe = std.parse.parseI32("+42") + let bool_value: Maybe = std.parse.parseBool("true") + let duration_value: Maybe = std.parse.parseDuration("1h30m") + let negative_duration: Maybe = std.parse.parseDuration("-250ms") + let large_ns_duration: Maybe = std.parse.parseDuration("4294967296ns") + let max_ns_duration: Maybe = std.parse.parseDuration("9223372036854775807ns") + let min_ns_duration: Maybe = std.parse.parseDuration("-9223372036854775808ns") + let positive_duration_overflow: Maybe = std.parse.parseDuration("9223372036854775808ns") + let negative_duration_overflow: Maybe = std.parse.parseDuration("-9223372036854775809ns") + let invalid_duration: Maybe = std.parse.parseDuration("10") + let byte_size: Maybe = std.parse.parseByteSize("2MiB") + let decimal_size: Maybe = std.parse.parseByteSize("3KB") + let invalid_size: Maybe = std.parse.parseByteSize("4XB") + let trailing: Maybe = std.parse.parseU32("12x") + let hex_u32: Maybe = std.parse.parseU32Base("ff", 16_u32) + let invalid_hex_u32: Maybe = std.parse.parseU32Base("fg", 16_u32) + let invalid_base_u32: Maybe = std.parse.parseU32Base("10", 1_u32) + let binary_usize: Maybe = std.parse.parseUsizeBase("1010", 2_u32) + let signed_hex: Maybe = std.parse.parseI32Base("-80000000", 16_u32) + let signed_hex_overflow: Maybe = std.parse.parseI32Base("80000000", 16_u32) + let prefixed_hex: Maybe = std.parse.parseU32Prefix("0xff") + let prefixed_hex64: Maybe = std.parse.parseU64Prefix("0xffffffffffffffff") + let prefixed_binary: Maybe = std.parse.parseU32Prefix("0b1010") + let prefixed_octal: Maybe = std.parse.parseU32Prefix("0o77") + let prefixed_decimal: Maybe = std.parse.parseU32Prefix("42") + let prefixed_signed: Maybe = std.parse.parseI32Prefix("-0x2a") + let prefixed_signed64: Maybe = std.parse.parseI64Prefix("-0x8000000000000000") + let prefixed_plus: Maybe = std.parse.parseI32Prefix("+0b101") + let signed64_hex: Maybe = std.parse.parseI64Base("-8000000000000000", 16_u32) + let unsigned64_hex: Maybe = std.parse.parseU64Base("ffffffffffffffff", 16_u32) + let prefixed_usize: Maybe = std.parse.parseUsizePrefix("0x10") + let digits: [5]u8 = [49_u8, 50_u8, 51_u8, 52_u8, 53_u8] + let runtime_usize: Maybe = std.parse.parseUsize(digits) + let token: Span = std.parse.tokenAscii(" zero text") + let alpha: Bool = std.parse.isAsciiAlpha("A") + let digit: Bool = std.parse.isAsciiDigit("7") + let whitespace: Bool = std.parse.isWhitespace(" ") + let ident_start: Bool = std.parse.isIdentifierStart("_") + let ident_len: usize = std.parse.scanIdentifier("name_1!") + let digit_prefixed_ident_len: usize = std.parse.scanIdentifier("1abc") + let digit_len: usize = std.parse.scanDigits("123abc") + let space_len: usize = std.parse.scanWhitespace(" \ntext") + let until: usize = std.parse.scanUntilByte("abc:def", 58_u8) + let min_i32: i32 = 0 - 2147483647 - 1 + let min_i64: i64 = 0 - 9223372036854775807 - 1 + let unsigned_ok: Bool = byte.has && byte.value == 255 && !too_big_byte.has && port.has && port.value == 8080 && count.has && count.value == 4294967295_u32 && count64.has && count64.value == 18446744073709551615_u64 && !count64_overflow.has + let signed_ok: Bool = parsed_usize.has && parsed_usize.value == 12345 && large_usize.has && large_usize.value == 8589934590 && !usize_overflow.has && signed.has && signed.value == min_i32 && positive.has && positive.value == 42 && signed64.has && signed64.value == min_i64 && signed64_positive.has && signed64_positive.value == 9223372036854775807 && !signed64_overflow.has + let text_ok: Bool = bool_value.has && bool_value.value && !trailing.has && runtime_usize.has && runtime_usize.value == 12345 && std.mem.eql(token, "zero") + let scan_ok: Bool = alpha && digit && whitespace && ident_start && ident_len == 6 && digit_prefixed_ident_len == 0 && digit_len == 3 && space_len == 2 && until == 3 + let base_ok: Bool = hex_u32.has && hex_u32.value == 255_u32 && !invalid_hex_u32.has && !invalid_base_u32.has && binary_usize.has && binary_usize.value == 10 && signed_hex.has && signed_hex.value == min_i32 && !signed_hex_overflow.has && signed64_hex.has && signed64_hex.value == min_i64 && unsigned64_hex.has && unsigned64_hex.value == 18446744073709551615_u64 + let prefix_ok: Bool = prefixed_hex.has && prefixed_hex.value == 255_u32 && prefixed_hex64.has && prefixed_hex64.value == 18446744073709551615_u64 && prefixed_binary.has && prefixed_binary.value == 10_u32 && prefixed_octal.has && prefixed_octal.value == 63_u32 && prefixed_decimal.has && prefixed_decimal.value == 42_u32 && prefixed_signed.has && prefixed_signed.value == -42 && prefixed_signed64.has && prefixed_signed64.value == min_i64 && prefixed_plus.has && prefixed_plus.value == 5 && prefixed_usize.has && prefixed_usize.value == 16 + let unit_ok: Bool = duration_value.has && std.time.asSecondsFloor(duration_value.value) == 5400_i64 && negative_duration.has && std.time.asMsFloor(negative_duration.value) == 0 - 250 && large_ns_duration.has && std.time.asNs(large_ns_duration.value) == 4294967296_i64 && max_ns_duration.has && std.time.asNs(max_ns_duration.value) == 9223372036854775807_i64 && min_ns_duration.has && std.time.asNs(min_ns_duration.value) == min_i64 && !positive_duration_overflow.has && !negative_duration_overflow.has && !invalid_duration.has && byte_size.has && byte_size.value == 2097152 && decimal_size.has && decimal_size.value == 3000 && !invalid_size.has + if unsigned_ok && signed_ok && text_ok && scan_ok && base_ok && prefix_ok && unit_ok { + check world.out.write("std parse text ok\n") + } +} diff --git a/conformance/native/pass/std-parse-text.graph b/conformance/native/pass/std-parse-text.graph new file mode 100644 index 0000000..8b0a15f Binary files /dev/null and b/conformance/native/pass/std-parse-text.graph differ diff --git a/conformance/native/pass/std-path-helper-name-collision.0 b/conformance/native/pass/std-path-helper-name-collision.0 new file mode 100644 index 0000000..ae00e51 --- /dev/null +++ b/conformance/native/pass/std-path-helper-name-collision.0 @@ -0,0 +1,13 @@ +fn zeroStdPathNormalize(path: String) -> String { + return "user helper" +} + +pub fn main(world: World) -> Void raises { + var path_buf: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + let normalized: Maybe = std.path.normalize(path_buf, "a/") + if normalized.has { + if std.mem.eql(normalized.value, "a") && std.mem.eql(zeroStdPathNormalize("x"), "user helper") { + check world.out.write("std path helper collision ok\n") + } + } +} diff --git a/conformance/native/pass/std-path-helper-name-collision.graph b/conformance/native/pass/std-path-helper-name-collision.graph new file mode 100644 index 0000000..a00a1a4 Binary files /dev/null and b/conformance/native/pass/std-path-helper-name-collision.graph differ diff --git a/conformance/native/pass/std-path-io-breadth.0 b/conformance/native/pass/std-path-io-breadth.0 new file mode 100644 index 0000000..08d824a --- /dev/null +++ b/conformance/native/pass/std-path-io-breadth.0 @@ -0,0 +1,249 @@ +pub fn main(world: World) -> Void raises { + var path_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let normalized: Maybe = std.path.normalize(path_buf, "src//./main.0/") + var parent_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let parent_normalized: Maybe = std.path.normalize(parent_buf, "src/a/../main.0") + var join_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let joined: Maybe = std.path.join(join_buf, "src/", "/main.0") + var root_join_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let root_join_base: Span = "/x"[..1] + let root_joined: Maybe = std.path.join(root_join_buf, root_join_base, "main.0") + var empty_abs_join_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let empty_abs_joined: Maybe = std.path.join(empty_abs_join_buf, "", "/main.0") + var rel_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let relative: Maybe = std.path.relative(rel_buf, "src", "src/main.0") + var fallback_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let fallback_relative: Maybe = std.path.relative(fallback_buf, "other", "src/main.0") + var abs_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let abs_path: Maybe = std.path.abs(abs_buf, "/workspace", "src/main.0") + var already_abs_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let already_abs_path: Maybe = std.path.abs(already_abs_buf, "/workspace", "/tmp/file") + var small_abs_buf: [3]u8 = [0, 0, 0] + let abs_overflow: Maybe = std.path.abs(small_abs_buf, "/workspace", "src") + var root_abs_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let root_abs_path: Maybe = std.path.abs(root_abs_buf, "/", "main.0") + var small_buf: [3]u8 = [0, 0, 0] + let overflow: Maybe = std.path.normalize(small_buf, "abcd") + var root_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let root_normalized: Maybe = std.path.normalize(root_buf, "/src//./main.0/") + var root_parent_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let root_parent: Maybe = std.path.normalize(root_parent_buf, "/../src") + var leading_parent_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let leading_parent: Maybe = std.path.normalize(leading_parent_buf, "../src") + var nested_parent_buf: [32]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let nested_parent: Maybe = std.path.normalize(nested_parent_buf, "a/../../b") + var trim_fit_buf: [1]u8 = [0] + let trim_fit: Maybe = std.path.normalize(trim_fit_buf, "a/") + var copy_dst: [4]u8 = [0, 0, 0, 0] + var reader_buf: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var writer_buf: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + let reader: BufferedReader = std.io.bufferedReader(reader_buf) + let writer: BufferedWriter = std.io.bufferedWriter(writer_buf) + let copied: usize = std.io.copy(copy_dst, std.mem.span("abcd")) + var ok: Bool = true + if !normalized.has { + ok = false + } + if normalized.has { + if !std.mem.eql(normalized.value, "src/main.0") { + ok = false + } + } + if !parent_normalized.has { + ok = false + } + if parent_normalized.has { + if !std.mem.eql(parent_normalized.value, "src/main.0") { + ok = false + } + } + if !joined.has { + ok = false + } + if joined.has { + if !std.mem.eql(joined.value, "src/main.0") { + ok = false + } + } + if !root_joined.has { + ok = false + } + if root_joined.has { + if !std.mem.eql(root_joined.value, "/main.0") { + ok = false + } + } + if !empty_abs_joined.has { + ok = false + } + if empty_abs_joined.has { + if !std.mem.eql(empty_abs_joined.value, "/main.0") { + ok = false + } + } + if !relative.has { + ok = false + } + if relative.has { + if !std.mem.eql(relative.value, "main.0") { + ok = false + } + } + if !fallback_relative.has { + ok = false + } + if fallback_relative.has { + if !std.mem.eql(fallback_relative.value, "src/main.0") { + ok = false + } + } + if !abs_path.has { + ok = false + } + if abs_path.has { + if !std.mem.eql(abs_path.value, "/workspace/src/main.0") { + ok = false + } + } + if !already_abs_path.has { + ok = false + } + if already_abs_path.has { + if !std.mem.eql(already_abs_path.value, "/tmp/file") { + ok = false + } + } + if abs_overflow.has { + ok = false + } + if !root_abs_path.has { + ok = false + } + if root_abs_path.has { + if !std.mem.eql(root_abs_path.value, "/main.0") { + ok = false + } + } + if overflow.has { + ok = false + } + if !root_normalized.has { + ok = false + } + if root_normalized.has { + if !std.mem.eql(root_normalized.value, "/src/main.0") { + ok = false + } + } + if !root_parent.has { + ok = false + } + if root_parent.has { + if !std.mem.eql(root_parent.value, "/src") { + ok = false + } + } + if !leading_parent.has { + ok = false + } + if leading_parent.has { + if !std.mem.eql(leading_parent.value, "../src") { + ok = false + } + } + if !nested_parent.has { + ok = false + } + if nested_parent.has { + if !std.mem.eql(nested_parent.value, "../b") { + ok = false + } + } + if !trim_fit.has { + ok = false + } + if trim_fit.has { + if !std.mem.eql(trim_fit.value, "a") { + ok = false + } + } + if !std.mem.eql(std.path.basename("src/main.0"), "main.0") { + ok = false + } + if !std.mem.eql(std.path.dirname("src/main.0"), "src") { + ok = false + } + if !std.mem.eql(std.path.dirname("/main.0"), root_join_base) { + ok = false + } + if !std.mem.eql(std.path.dirname(root_join_base), root_join_base) { + ok = false + } + if !std.mem.eql(std.path.extension("src/main.0"), "0") { + ok = false + } + if !std.mem.eql(std.path.stem("src/main.0"), "main") { + ok = false + } + if !std.mem.eql(std.path.stem("src/.profile"), ".profile") { + ok = false + } + if !std.mem.eql(std.path.stem(".."), "..") { + ok = false + } + if !std.mem.eql(std.path.splitDir("src/main.0"), "src") { + ok = false + } + if !std.mem.eql(std.path.splitBase("src/main.0"), "main.0") { + ok = false + } + if !std.path.isAbs("/src/main.0") { + ok = false + } + if std.path.isAbs("src/main.0") { + ok = false + } + if std.path.componentCount("/src//main.0/") != 2 { + ok = false + } + let first_component: Maybe = std.path.component("/src//main.0/", 0) + if !first_component.has { + ok = false + } + if first_component.has { + if !std.mem.eql(first_component.value, "src") { + ok = false + } + } + let second_component: Maybe = std.path.component("/src//main.0/", 1) + if !second_component.has { + ok = false + } + if second_component.has { + if !std.mem.eql(second_component.value, "main.0") { + ok = false + } + } + let missing_component: Maybe = std.path.component("/src//main.0/", 2) + if missing_component.has { + ok = false + } + if std.io.readerCapacity(&reader) != 8 { + ok = false + } + if std.io.writerCapacity(&writer) != 8 { + ok = false + } + if copied != 4 { + ok = false + } + if copy_dst[0] != 97_u8 { + ok = false + } + if copy_dst[3] != 100_u8 { + ok = false + } + if ok { + check world.out.write("std path io breadth ok\n") + } +} diff --git a/conformance/native/pass/std-path-io-breadth.graph b/conformance/native/pass/std-path-io-breadth.graph new file mode 100644 index 0000000..cff6573 Binary files /dev/null and b/conformance/native/pass/std-path-io-breadth.graph differ diff --git a/conformance/native/pass/std-platform-basics.0 b/conformance/native/pass/std-platform-basics.0 new file mode 100644 index 0000000..4be40c2 --- /dev/null +++ b/conformance/native/pass/std-platform-basics.0 @@ -0,0 +1,26 @@ +pub fn main(world: World) -> Void raises { + let duration: Duration = std.time.sub(std.time.seconds(2), std.time.ms(500)) + let short: Duration = std.time.ms(250) + let tiny: Duration = std.time.us(250000_i64) + let window: Duration = std.time.add(std.time.minutes(1), std.time.seconds(30)) + let bounded: Duration = std.time.clamp(std.time.hours(2), std.time.seconds(1), std.time.minutes(10)) + let zero: Duration = std.time.zero() + let minimum: Duration = std.time.min(short, duration) + let maximum: Duration = std.time.max(short, duration) + let sleep_ok: Bool = std.time.sleep(std.time.zero()) + let _now: Duration = std.time.monotonic() + let wall: i64 = std.time.wallSeconds() + var rng: RandSource = std.rand.seed(7_u32) + let first: u32 = std.rand.nextU32(&mut rng) + let second: Bool = std.rand.nextBool(&mut rng) + let _entropy: u32 = std.rand.entropyU32() + let _entropy_seed: RandSource = std.rand.entropySeed() + let status: ProcStatus = std.proc.spawn("zero-noop") + let run_code: i32 = std.proc.runCode("sh -c true") + let hash: u32 = std.crypto.hash32(std.mem.span("message")) + let hmac: u32 = std.crypto.hmac32(std.mem.span("key"), std.mem.span("message")) + let _secure: u32 = std.crypto.secureRandomU32() + if std.time.asNs(std.time.ns(42_i64)) == 42_i64 && std.time.asUsFloor(tiny) == 250000_i64 && std.time.asMsFloor(duration) == 1500 && std.time.asMsFloor(minimum) == 250 && std.time.asMsFloor(maximum) == 1500 && std.time.asSecondsFloor(window) == 90_i64 && std.time.asSecondsFloor(bounded) == 600_i64 && std.time.lessThan(short, duration) && std.time.isZero(zero) && sleep_ok && wall > 0 && first == 1025555898_u32 && second && std.proc.exitCode(status) == 0 && std.proc.succeeded(status) && !std.proc.failed(status) && std.proc.runOk("sh -c true") && run_code == 0 && hash != 0_u32 && hmac != 0_u32 && std.crypto.constantTimeEql(std.mem.span("same"), std.mem.span("same")) { + check world.out.write("std platform basics ok\n") + } +} diff --git a/conformance/native/pass/std-platform-basics.graph b/conformance/native/pass/std-platform-basics.graph new file mode 100644 index 0000000..151f412 Binary files /dev/null and b/conformance/native/pass/std-platform-basics.graph differ diff --git a/conformance/native/pass/std-proc-capture-files.0 b/conformance/native/pass/std-proc-capture-files.0 new file mode 100644 index 0000000..4233d93 --- /dev/null +++ b/conformance/native/pass/std-proc-capture-files.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let out_path: String = ".zero/proc-capture-files-out.txt" + let err_path: String = ".zero/proc-capture-files-err.txt" + let status: ProcStatus = std.proc.captureFiles("sh -c 'printf stdout-log; printf stderr-log >&2'", out_path, err_path) + var out_buf: [32]u8 = [0_u8; 32] + var err_buf: [32]u8 = [0_u8; 32] + let out: Maybe> = std.fs.readFileBytes(fs, out_path, out_buf) + let err: Maybe> = std.fs.readFileBytes(fs, err_path, err_buf) + if std.proc.succeeded(status) && out.has && err.has && std.mem.eql(out.value, "stdout-log") && std.mem.eql(err.value, "stderr-log") { + check world.out.write("std proc capture files ok\n") + } +} diff --git a/conformance/native/pass/std-proc-capture-files.graph b/conformance/native/pass/std-proc-capture-files.graph new file mode 100644 index 0000000..0f06d65 Binary files /dev/null and b/conformance/native/pass/std-proc-capture-files.graph differ diff --git a/conformance/native/pass/std-proc-capture.0 b/conformance/native/pass/std-proc-capture.0 new file mode 100644 index 0000000..929f514 --- /dev/null +++ b/conformance/native/pass/std-proc-capture.0 @@ -0,0 +1,13 @@ +pub fn main(world: World) -> Void raises { + var output: [32]u8 = [0_u8; 32] + var small: [4]u8 = [0_u8; 4] + let captured: Maybe = std.proc.capture("printf capture-ok", output) + let truncated: Maybe = std.proc.capture("printf too-long", small) + let failed: Maybe = std.proc.capture("false", output) + if captured.has { + let bytes: Span = std.io.written(output, captured.value) + if captured.value == 10_usize && std.mem.eql(bytes, "capture-ok") && !truncated.has && !failed.has { + check world.out.write("std proc capture ok\n") + } + } +} diff --git a/conformance/native/pass/std-proc-capture.graph b/conformance/native/pass/std-proc-capture.graph new file mode 100644 index 0000000..53c3b6c Binary files /dev/null and b/conformance/native/pass/std-proc-capture.graph differ diff --git a/conformance/native/pass/std-proc-child-cwd.0 b/conformance/native/pass/std-proc-child-cwd.0 new file mode 100644 index 0000000..1d03d71 --- /dev/null +++ b/conformance/native/pass/std-proc-child-cwd.0 @@ -0,0 +1,19 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let cwd: String = ".zero/proc-child-cwd" + let ready: Bool = std.fs.makeDir(cwd) || std.fs.isDir(cwd) + let child: ProcChild = std.proc.spawnChildIn("sh -c 'printf stdout-cwd; printf marker > child.txt'", cwd) + var stdout_buf: [32]u8 = [0_u8; 32] + var marker_buf: [32]u8 = [0_u8; 32] + let valid: Bool = std.proc.childValid(child) + let status: ProcStatus = std.proc.wait(child) + let stdout_read: Maybe = std.proc.readStdout(child, stdout_buf) + let marker: Maybe> = std.fs.readFileBytes(fs, ".zero/proc-child-cwd/child.txt", marker_buf) + let closed: Bool = std.proc.close(child) + if ready && valid && std.proc.succeeded(status) && stdout_read.has && marker.has && closed { + let stdout_bytes: Span = std.io.written(stdout_buf, stdout_read.value) + if std.mem.eql(stdout_bytes, "stdout-cwd") && std.mem.eql(marker.value, "marker") { + check world.out.write("std proc child cwd ok\n") + } + } +} diff --git a/conformance/native/pass/std-proc-child-cwd.graph b/conformance/native/pass/std-proc-child-cwd.graph new file mode 100644 index 0000000..fd0995e Binary files /dev/null and b/conformance/native/pass/std-proc-child-cwd.graph differ diff --git a/conformance/native/pass/std-proc-child.0 b/conformance/native/pass/std-proc-child.0 new file mode 100644 index 0000000..0bed58f --- /dev/null +++ b/conformance/native/pass/std-proc-child.0 @@ -0,0 +1,240 @@ +pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + let prepared: Bool = std.fs.ensureDir(".zero/conformance") + + let child: ProcChild = std.proc.spawnChild("sh -c 'printf child-out; printf child-err >&2'") + var stdout_buf: [32]u8 = [0_u8; 32] + var stderr_buf: [32]u8 = [0_u8; 32] + let valid: Bool = std.proc.childValid(child) + let child_pid: i32 = std.proc.pid(child) + let was_running: Bool = std.proc.running(child) + let status: ProcStatus = std.proc.wait(child) + let stdout_read: Maybe = std.proc.readStdout(child, stdout_buf) + let stderr_read: Maybe = std.proc.readStderr(child, stderr_buf) + let closed: Bool = std.proc.close(child) + + var output_ok: Bool = false + if stdout_read.has && stderr_read.has { + let stdout_bytes: Span = std.io.written(stdout_buf, stdout_read.value) + let stderr_bytes: Span = std.io.written(stderr_buf, stderr_read.value) + output_ok = std.mem.eql(stdout_bytes, "child-out") && std.mem.eql(stderr_bytes, "child-err") + } + + let large_child: ProcChild = std.proc.spawnChild("sh -c 'yes x | head -c 1048576'") + var large_buf: [32]u8 = [0_u8; 32] + var large_ok: Bool = false + let large_valid: Bool = std.proc.childValid(large_child) + if large_valid { + let large_status: ProcStatus = std.proc.wait(large_child) + let large_read: Maybe = std.proc.readStdout(large_child, large_buf) + large_ok = std.proc.succeeded(large_status) && large_read.has && large_read.value == 32 + } + let large_closed: Bool = std.proc.close(large_child) + + let stdin_child: ProcChild = std.proc.spawnChild("sh -c 'read line; printf stdin:$line'") + var stdin_buf: [32]u8 = [0_u8; 32] + var stdin_ok: Bool = false + let stdin_valid: Bool = std.proc.childValid(stdin_child) + let wrote: Maybe = std.proc.writeStdin(stdin_child, "agent\n") + if stdin_valid && wrote.has { + let stdin_status: ProcStatus = std.proc.wait(stdin_child) + let stdin_read: Maybe = std.proc.readStdout(stdin_child, stdin_buf) + if stdin_read.has { + let stdin_bytes: Span = std.io.written(stdin_buf, stdin_read.value) + stdin_ok = std.proc.succeeded(stdin_status) && std.mem.eql(stdin_bytes, "stdin:agent") + } + } + let stdin_closed: Bool = std.proc.close(stdin_child) + + let eof_child: ProcChild = std.proc.spawnChild("sh -c 'cat >/dev/null; printf eof-done'") + var eof_buf: [32]u8 = [0_u8; 32] + var eof_ok: Bool = false + let eof_valid: Bool = std.proc.childValid(eof_child) + let eof_pid: i32 = std.proc.pid(eof_child) + let eof_running: Bool = std.proc.pidRunning(eof_pid) + let eof_closed_stdin: Bool = std.proc.closeStdin(eof_child) + if eof_valid && eof_pid > 0 && eof_running && eof_closed_stdin { + let eof_status: ProcStatus = std.proc.wait(eof_child) + let eof_read: Maybe = std.proc.readStdout(eof_child, eof_buf) + if eof_read.has { + let eof_bytes: Span = std.io.written(eof_buf, eof_read.value) + eof_ok = std.proc.succeeded(eof_status) && std.mem.eql(eof_bytes, "eof-done") + } + } + let eof_handle_closed: Bool = std.proc.close(eof_child) + + let interrupt_child: ProcChild = std.proc.spawnChild("sh -c 'while true; do sleep 1; done'") + var interrupt_ok: Bool = false + let interrupt_pid: i32 = std.proc.pid(interrupt_child) + if std.proc.childValid(interrupt_child) && interrupt_pid > 0 && std.proc.pidRunning(interrupt_pid) { + let interrupted: Bool = std.proc.interrupt(interrupt_child) + let interrupt_status: ProcStatus = std.proc.wait(interrupt_child) + interrupt_ok = interrupted && std.proc.exitCode(interrupt_status) != 0 + } + let interrupt_closed: Bool = std.proc.close(interrupt_child) + + let interrupt_pid_child: ProcChild = std.proc.spawnChild("sh -c 'trap \"exit 130\" INT; while true; do sleep 1; done'") + var interrupt_pid_ok: Bool = false + let interrupt_pid_value: i32 = std.proc.pid(interrupt_pid_child) + if std.proc.childValid(interrupt_pid_child) && interrupt_pid_value > 0 && std.proc.pidRunning(interrupt_pid_value) { + let interrupted_pid: Bool = std.proc.interruptPid(interrupt_pid_value) + if interrupted_pid { + let interrupt_pid_status: ProcStatus = std.proc.wait(interrupt_pid_child) + interrupt_pid_ok = std.proc.exitCode(interrupt_pid_status) != 0 + } + } + let interrupt_pid_closed: Bool = std.proc.close(interrupt_pid_child) + + let close_live_child: ProcChild = std.proc.spawnChild("sh -c 'trap \"\" TERM; while true; do sleep 1; done'") + var close_live_ok: Bool = false + let close_live_pid: i32 = std.proc.pid(close_live_child) + if std.proc.childValid(close_live_child) && close_live_pid > 0 && std.proc.pidRunning(close_live_pid) { + let close_live_closed: Bool = std.proc.close(close_live_child) + close_live_ok = close_live_closed && !std.proc.childValid(close_live_child) && !std.proc.pidRunning(close_live_pid) + } + + let pid_child: ProcChild = std.proc.spawnChild("sh -c 'while true; do sleep 1; done'") + var pid_ok: Bool = false + let external_pid: i32 = std.proc.pid(pid_child) + if std.proc.childValid(pid_child) && external_pid > 0 && std.proc.pidRunning(external_pid) { + let killed_pid: Bool = std.proc.killPid(external_pid) + let pid_status: ProcStatus = std.proc.wait(pid_child) + pid_ok = killed_pid && std.proc.exitCode(pid_status) != 0 && !std.proc.pidRunning(0) + } + let pid_closed: Bool = std.proc.close(pid_child) + + let group_kill_child: ProcChild = std.proc.spawnChild("sh -c 'trap \"exit 143\" TERM; while true; do sleep 1; done'") + var group_kill_ok: Bool = false + let group_kill_pid: i32 = std.proc.pid(group_kill_child) + if std.proc.childValid(group_kill_child) && group_kill_pid > 0 && std.proc.pidRunning(group_kill_pid) { + let killed_group: Bool = std.proc.killGroupPid(group_kill_pid) + let group_kill_status: ProcStatus = std.proc.wait(group_kill_child) + group_kill_ok = killed_group && std.proc.exitCode(group_kill_status) != 0 + } + let group_kill_closed: Bool = std.proc.close(group_kill_child) + + let group_interrupt_child: ProcChild = std.proc.spawnChild("sh -c 'trap \"exit 130\" INT; while true; do sleep 1; done'") + var group_interrupt_ok: Bool = false + let group_interrupt_pid: i32 = std.proc.pid(group_interrupt_child) + if std.proc.childValid(group_interrupt_child) && group_interrupt_pid > 0 && std.proc.pidRunning(group_interrupt_pid) { + let interrupted_group: Bool = std.proc.interruptGroupPid(group_interrupt_pid) + let group_interrupt_status: ProcStatus = std.proc.wait(group_interrupt_child) + group_interrupt_ok = interrupted_group && std.proc.exitCode(group_interrupt_status) != 0 + } + let group_interrupt_closed: Bool = std.proc.close(group_interrupt_child) + + let env_child: ProcChild = std.proc.spawnChildInEnv("sh -c 'printf env:$ZERO_PROC_ENV_TEST:$ZERO_PROC_ENV_TWO'", ".", "ZERO_PROC_ENV_TEST=alpha\nZERO_PROC_ENV_TWO=beta") + var env_buf: [64]u8 = [0_u8; 64] + var env_ok: Bool = false + let env_valid: Bool = std.proc.childValid(env_child) + if env_valid { + let env_status: ProcStatus = std.proc.wait(env_child) + let env_read: Maybe = std.proc.readStdout(env_child, env_buf) + if env_read.has { + let env_bytes: Span = std.io.written(env_buf, env_read.value) + env_ok = std.proc.succeeded(env_status) && std.mem.eql(env_bytes, "env:alpha:beta") + } + } + let env_closed: Bool = std.proc.close(env_child) + let invalid_env_child: ProcChild = std.proc.spawnChildInEnv("sh -c 'printf nope'", ".", "ZERO_PROC_BAD_ENV") + let invalid_env_rejected: Bool = !std.proc.childValid(invalid_env_child) + + let empty_env_child: ProcChild = std.proc.spawnChildInEnv("sh -c 'if [ -z \"${ZERO_PROC_EMPTY_ENV+x}\" ]; then printf missing; else printf empty:$ZERO_PROC_EMPTY_ENV; fi'", ".", "ZERO_PROC_EMPTY_ENV=") + var empty_env_buf: [64]u8 = [0_u8; 64] + var empty_env_ok: Bool = false + let empty_env_valid: Bool = std.proc.childValid(empty_env_child) + if empty_env_valid { + let empty_env_status: ProcStatus = std.proc.wait(empty_env_child) + let empty_env_read: Maybe = std.proc.readStdout(empty_env_child, empty_env_buf) + if empty_env_read.has { + let empty_env_bytes: Span = std.io.written(empty_env_buf, empty_env_read.value) + empty_env_ok = std.proc.succeeded(empty_env_status) && std.mem.eql(empty_env_bytes, "empty:") + } + } + let empty_env_closed: Bool = std.proc.close(empty_env_child) + + let args_child: ProcChild = std.proc.spawnChildArgs("sh", "-c\nprintf 'args:%s:%s:%s' \"$0\" \"$1\" \"$ZERO_PROC_ARG_ENV\"\narg0\nspace value", ".", "ZERO_PROC_ARG_ENV=gamma") + var args_buf: [128]u8 = [0_u8; 128] + var args_ok: Bool = false + let args_valid: Bool = std.proc.childValid(args_child) + if args_valid { + let args_status: ProcStatus = std.proc.wait(args_child) + let args_read: Maybe = std.proc.readStdout(args_child, args_buf) + if args_read.has { + let args_bytes: Span = std.io.written(args_buf, args_read.value) + args_ok = std.proc.succeeded(args_status) && std.mem.eql(args_bytes, "args:arg0:space value:gamma") + } + } + let args_closed: Bool = std.proc.close(args_child) + let invalid_args_child: ProcChild = std.proc.spawnChildArgs("", "", ".", "") + let invalid_args_rejected: Bool = !std.proc.childValid(invalid_args_child) + + let inherit_status: ProcStatus = std.proc.spawnInheritArgs("sh", "-c\n[ \"$ZERO_PROC_INHERIT_ENV\" = delta ]", ".", "ZERO_PROC_INHERIT_ENV=delta") + + var capture_args_buf: [128]u8 = [0_u8; 128] + var capture_args_ok: Bool = false + let capture_args_read: Maybe = std.proc.captureArgs("sh", "-c\nprintf capture:%s:%s \"$0\" \"$1\"\nleft\nspace value", capture_args_buf) + if capture_args_read.has { + let capture_args_bytes: Span = std.io.written(capture_args_buf, capture_args_read.value) + capture_args_ok = std.mem.eql(capture_args_bytes, "capture:left:space value") + } + let invalid_capture_args: Maybe = std.proc.captureArgs("", "", capture_args_buf) + let invalid_capture_args_rejected: Bool = !invalid_capture_args.has + + let args_out_path: String = ".zero/conformance/std-proc-args.out" + let args_err_path: String = ".zero/conformance/std-proc-args.err" + let files_args_status: ProcStatus = std.proc.captureFilesArgs("sh", "-c\nprintf file:%s \"$0\"; printf err:%s \"$1\" >&2\nout arg\nerr arg", args_out_path, args_err_path) + var files_args_out_buf: [64]u8 = [0_u8; 64] + var files_args_err_buf: [64]u8 = [0_u8; 64] + let files_args_out: Maybe> = std.fs.readFileBytes(fs, args_out_path, files_args_out_buf) + let files_args_err: Maybe> = std.fs.readFileBytes(fs, args_err_path, files_args_err_buf) + var files_args_ok: Bool = false + if files_args_out.has && files_args_err.has { + files_args_ok = std.proc.succeeded(files_args_status) && std.mem.eql(files_args_out.value, "file:out arg") && std.mem.eql(files_args_err.value, "err:err arg") + } + + let background_wait_child: ProcChild = std.proc.spawnChild("sh -c 'sleep 2 & exit 0'") + let background_wait_start: Duration = std.time.monotonic() + let background_wait_status: ProcStatus = std.proc.wait(background_wait_child) + let background_wait_end: Duration = std.time.monotonic() + let background_wait_closed: Bool = std.proc.close(background_wait_child) + let background_wait_ok: Bool = std.proc.succeeded(background_wait_status) && background_wait_closed && std.time.lessThan(std.time.between(background_wait_start, background_wait_end), std.time.ms(1500)) + + let background_marker_path: String = ".zero/conformance/std-proc-background-marker" + let background_marker_removed: Bool = std.fs.remove(background_marker_path) || !std.fs.isFile(background_marker_path) + let background_cleanup_child: ProcChild = std.proc.spawnChild("sh -c '(sleep 1; printf survived > .zero/conformance/std-proc-background-marker) & exit 0'") + let background_cleanup_status: ProcStatus = std.proc.wait(background_cleanup_child) + let background_cleanup_closed: Bool = std.proc.close(background_cleanup_child) + let background_cleanup_slept: Bool = std.time.sleep(std.time.ms(1200)) + let background_cleanup_ok: Bool = background_marker_removed && std.proc.succeeded(background_cleanup_status) && background_cleanup_closed && background_cleanup_slept && !std.fs.isFile(background_marker_path) + + var background_capture_buf: [64]u8 = [0_u8; 64] + let background_capture_start: Duration = std.time.monotonic() + let background_capture_read: Maybe = std.proc.capture("sh -c 'sleep 2 & printf direct'", background_capture_buf) + let background_capture_end: Duration = std.time.monotonic() + var background_capture_ok: Bool = false + if background_capture_read.has { + let background_capture_bytes: Span = std.io.written(background_capture_buf, background_capture_read.value) + background_capture_ok = std.mem.eql(background_capture_bytes, "direct") && std.time.lessThan(std.time.between(background_capture_start, background_capture_end), std.time.ms(1500)) + } + + let stale_first: ProcChild = std.proc.spawnChild("sh -c true") + let stale_first_status: ProcStatus = std.proc.wait(stale_first) + let stale_first_closed: Bool = std.proc.close(stale_first) + let stale_second: ProcChild = std.proc.spawnChild("sh -c 'sleep 1; printf stale-second'") + let stale_first_valid_after_reuse: Bool = std.proc.childValid(stale_first) + let stale_first_kill_after_reuse: Bool = std.proc.kill(stale_first) + let stale_second_status: ProcStatus = std.proc.wait(stale_second) + var stale_second_buf: [32]u8 = [0_u8; 32] + let stale_second_read: Maybe = std.proc.readStdout(stale_second, stale_second_buf) + var stale_handle_ok: Bool = false + if stale_second_read.has { + let stale_second_bytes: Span = std.io.written(stale_second_buf, stale_second_read.value) + stale_handle_ok = std.proc.succeeded(stale_first_status) && stale_first_closed && !stale_first_valid_after_reuse && !stale_first_kill_after_reuse && std.proc.succeeded(stale_second_status) && std.mem.eql(stale_second_bytes, "stale-second") + } + let stale_second_closed: Bool = std.proc.close(stale_second) + + if prepared && valid && child_pid > 0 && std.proc.succeeded(status) && output_ok && closed && large_ok && large_closed && stdin_ok && stdin_closed && eof_ok && eof_handle_closed && interrupt_ok && interrupt_closed && interrupt_pid_ok && interrupt_pid_closed && close_live_ok && pid_ok && pid_closed && group_kill_ok && group_kill_closed && group_interrupt_ok && group_interrupt_closed && env_ok && env_closed && invalid_env_rejected && empty_env_ok && empty_env_closed && args_ok && args_closed && invalid_args_rejected && std.proc.succeeded(inherit_status) && capture_args_ok && invalid_capture_args_rejected && files_args_ok && background_wait_ok && background_cleanup_ok && background_capture_ok && stale_handle_ok && stale_second_closed { + check world.out.write("std proc child ok\n") + } +} diff --git a/conformance/native/pass/std-proc-child.graph b/conformance/native/pass/std-proc-child.graph new file mode 100644 index 0000000..76de1c7 Binary files /dev/null and b/conformance/native/pass/std-proc-child.graph differ diff --git a/conformance/native/pass/std-proc-helpers.0 b/conformance/native/pass/std-proc-helpers.0 new file mode 100644 index 0000000..a32c73e --- /dev/null +++ b/conformance/native/pass/std-proc-helpers.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let spawn_ok: ProcStatus = std.proc.spawn("sh -c true") + let spawn_fail: ProcStatus = std.proc.spawn("sh -c false") + let ok_code: i32 = std.proc.runCode("sh -c true") + let fail_code: i32 = std.proc.runCode("sh -c false") + if std.proc.succeeded(spawn_ok) && std.proc.failed(spawn_fail) && std.proc.runOk("sh -c true") && !std.proc.runOk("sh -c false") && ok_code == 0 && fail_code != 0 { + check world.out.write("std proc helpers ok\n") + } +} diff --git a/conformance/native/pass/std-proc-helpers.graph b/conformance/native/pass/std-proc-helpers.graph new file mode 100644 index 0000000..7a51cca Binary files /dev/null and b/conformance/native/pass/std-proc-helpers.graph differ diff --git a/conformance/native/pass/std-proc-spawn-inherit.0 b/conformance/native/pass/std-proc-spawn-inherit.0 new file mode 100644 index 0000000..4d3cb08 --- /dev/null +++ b/conformance/native/pass/std-proc-spawn-inherit.0 @@ -0,0 +1,6 @@ +pub fn main(world: World) -> Void raises { + let status: ProcStatus = std.proc.spawnInherit("sh -c 'printf inherit-out'") + if std.proc.succeeded(status) { + check world.out.write(" status-ok\n") + } +} diff --git a/conformance/native/pass/std-proc-spawn-inherit.graph b/conformance/native/pass/std-proc-spawn-inherit.graph new file mode 100644 index 0000000..3072a53 Binary files /dev/null and b/conformance/native/pass/std-proc-spawn-inherit.graph differ diff --git a/conformance/native/pass/std-pty-child.0 b/conformance/native/pass/std-pty-child.0 new file mode 100644 index 0000000..0e1bbc5 --- /dev/null +++ b/conformance/native/pass/std-pty-child.0 @@ -0,0 +1,70 @@ +pub fn main(world: World) -> Void raises { + let child: ProcChild = std.pty.spawnArgs("printf", "pty-out\n", ".", "") + var output_buf: [64]u8 = [0_u8; 64] + let valid: Bool = std.pty.valid(child) + let child_valid: Bool = std.pty.childValid(child) + let child_pid: i32 = std.pty.pid(child) + let resized: Bool = std.pty.resize(child, 80_usize, 24_usize) + let write_probe: Maybe = std.pty.write(child, "ignored\n") + var output_ok: Bool = false + var output_attempts: usize = 0 + while output_attempts < 20_usize && !output_ok { + let output_read: Maybe = std.pty.read(child, output_buf) + if output_read.has { + let output_bytes: Span = std.io.written(output_buf, output_read.value) + output_ok = std.str.contains(output_bytes, "pty-out") + } + if !output_ok { + let slept: Bool = std.time.sleep(std.time.ms(10)) + } + output_attempts = output_attempts + 1_usize + } + let status: ProcStatus = std.pty.wait(child) + let closed: Bool = std.pty.close(child) + + let env_child: ProcChild = std.pty.spawnInEnv("sh -c 'printf env:$ZERO_PTY_ENV'", ".", "ZERO_PTY_ENV=ok") + var env_buf: [64]u8 = [0_u8; 64] + var env_ok: Bool = false + var env_attempts: usize = 0 + if std.pty.valid(env_child) { + while env_attempts < 20_usize && !env_ok { + let env_read: Maybe = std.pty.read(env_child, env_buf) + if env_read.has { + let env_bytes: Span = std.io.written(env_buf, env_read.value) + env_ok = std.str.contains(env_bytes, "env:ok") + } + if !env_ok { + let slept: Bool = std.time.sleep(std.time.ms(10)) + } + env_attempts = env_attempts + 1_usize + } + } + let env_status: ProcStatus = std.pty.wait(env_child) + let env_closed: Bool = std.pty.close(env_child) + + let interrupt_child: ProcChild = std.pty.spawn("sh -c 'trap \"exit 130\" INT; while true; do sleep 1; done'") + var interrupt_ok: Bool = false + if std.pty.childValid(interrupt_child) && std.pty.running(interrupt_child) { + let interrupted: Bool = std.pty.interrupt(interrupt_child) + if interrupted { + let interrupt_status: ProcStatus = std.pty.wait(interrupt_child) + interrupt_ok = std.proc.exitCode(interrupt_status) != 0 + } + } + let interrupt_closed: Bool = std.pty.close(interrupt_child) + + let kill_child: ProcChild = std.pty.spawn("sh -c 'trap \"exit 143\" TERM; while true; do sleep 1; done'") + var kill_ok: Bool = false + if std.pty.childValid(kill_child) && std.pty.running(kill_child) { + let killed: Bool = std.pty.kill(kill_child) + if killed { + let kill_status: ProcStatus = std.pty.wait(kill_child) + kill_ok = std.proc.exitCode(kill_status) != 0 + } + } + let kill_closed: Bool = std.pty.close(kill_child) + + if valid && child_valid && child_pid > 0 && resized && write_probe.has && std.proc.succeeded(status) && output_ok && closed && env_ok && std.proc.succeeded(env_status) && env_closed && interrupt_ok && interrupt_closed && kill_ok && kill_closed { + check world.out.write("std pty child ok\n") + } +} diff --git a/conformance/native/pass/std-pty-child.graph b/conformance/native/pass/std-pty-child.graph new file mode 100644 index 0000000..337440a Binary files /dev/null and b/conformance/native/pass/std-pty-child.graph differ diff --git a/conformance/native/pass/std-regex.0 b/conformance/native/pass/std-regex.0 new file mode 100644 index 0000000..57e6bc4 --- /dev/null +++ b/conformance/native/pass/std-regex.0 @@ -0,0 +1,154 @@ +fn regex_compile_and_match() -> Bool { + var storage: [512]u8 = [0; 512] + let buffer: MutSpan = storage + let compiled: Maybe> = std.regex.compile(buffer, "^h[ae]llo{1,2}$") + if !compiled.has { + return false + } + let program: Span = compiled.value + if !std.regex.isMatch(program, "hello") || !std.regex.isMatch(program, "halloo") { + return false + } + return !std.regex.isMatch(program, "hullo") && !std.regex.isMatch(program, "hellooo") +} + +fn regex_one_shot() -> Bool { + let digits: Maybe = std.regex.matches("^\\d{3}-\\d{4}$", "555-0199") + if !digits.has || !digits.value { + return false + } + let alternation: Maybe = std.regex.matches("^(cat|dog)s?$", "dogs") + if !alternation.has || !alternation.value { + return false + } + let unanchored: Maybe = std.regex.matches("wor\\w+", "hello world") + if !unanchored.has || !unanchored.value { + return false + } + let miss: Maybe = std.regex.matches("^[0-9a-f]{4}$", "12g4") + if !miss.has || miss.value { + return false + } + let invalid: Maybe = std.regex.matches("(unclosed", "text") + return !invalid.has +} + +fn regex_search() -> Bool { + let contains: Maybe = std.regex.contains("cat|dog", "hot dog") + if !contains.has || !contains.value { + return false + } + let missing_contains: Maybe = std.regex.contains("cat|dog", "bird") + if !missing_contains.has || missing_contains.value { + return false + } + let index: Maybe = std.regex.findIndex("wor\\w+", "hello world") + if !index.has || index.value != 6 { + return false + } + let missing_index: Maybe = std.regex.findIndex("^world", "hello world") + if !missing_index.has || missing_index.value != std.mem.len("hello world") { + return false + } + let found: Maybe> = std.regex.find("\\d+", "build-2048") + if !found.has || !std.mem.eql(found.value, "2048") { + return false + } + let count: Maybe = std.regex.findCount("\\d+", "a1 b22 c333") + if !count.has || count.value != 3 { + return false + } + let second: Maybe> = std.regex.findNth("\\d+", "a1 b22 c333", 1) + if !second.has || !std.mem.eql(second.value, "22") { + return false + } + let second_index: Maybe = std.regex.findNthIndex("\\d+", "a1 b22 c333", 1) + if !second_index.has || second_index.value != 4 { + return false + } + let ambiguous: Maybe> = std.regex.find("a|ab", "ab") + if !ambiguous.has || !std.mem.eql(ambiguous.value, "ab") { + return false + } + var ambiguous_replace_storage: [4]u8 = [0; 4] + let ambiguous_replaced: Maybe> = std.regex.replace(ambiguous_replace_storage, "a|ab", "ab", "#") + if !ambiguous_replaced.has || !std.mem.eql(ambiguous_replaced.value, "#") { + return false + } + var replaced_storage: [16]u8 = [0; 16] + let replaced: Maybe> = std.regex.replace(replaced_storage, "\\d+", "a1 b22", "#") + if !replaced.has || !std.mem.eql(replaced.value, "a# b#") { + return false + } + let fields: Maybe = std.regex.splitCount("[,;]", "red,green;blue") + if !fields.has || fields.value != 3 { + return false + } + let middle: Maybe> = std.regex.split("[,;]", "red,green;blue", 1) + if !middle.has || !std.mem.eql(middle.value, "green") { + return false + } + let trailing: Maybe> = std.regex.split(",", "red,", 1) + if !trailing.has || !std.mem.eql(trailing.value, "") { + return false + } + let invalid: Maybe = std.regex.findIndex("(unclosed", "text") + return !invalid.has +} + +fn regex_codepoints() -> Bool { + let accent: Maybe = std.regex.matches("^.é$", "xé") + if !accent.has || !accent.value { + return false + } + let emoji: Maybe = std.regex.matches("^.$", "💯") + return emoji.has && emoji.value +} + +fn regex_statuses() -> Bool { + var storage: [128]u8 = [0; 128] + let buffer: MutSpan = storage + if std.regex.compileStatus(buffer, "a+b*") != 0 { + return false + } + if std.regex.compileStatus(buffer, "(a)\\1") != 1 { + return false + } + if std.regex.compileStatus(buffer, "(?=ahead)") != 2 { + return false + } + if std.regex.compileStatus(buffer, "(?<=behind)") != 3 { + return false + } + if std.regex.compileStatus(buffer, "(?x)") != 4 { + return false + } + if std.regex.compileStatus(buffer, "a+?") != 5 { + return false + } + if std.regex.compileStatus(buffer, "(?i)x") != 6 { + return false + } + if std.regex.compileStatus(buffer, "\\p{L}") != 7 { + return false + } + if std.regex.compileStatus(buffer, "(a") != 8 { + return false + } + let offset: Maybe = std.regex.compileErrorOffset(buffer, "(a") + if !offset.has || offset.value != 2 { + return false + } + if std.regex.compileStatus(buffer, "a{4,2}") != 9 { + return false + } + return std.mem.eql(std.regex.statusName(2), "unsupported lookahead") && std.mem.eql(std.regex.statusName(0), "ok") +} + +pub fn main(world: World) -> Void raises { + if regex_compile_and_match() && regex_one_shot() && (regex_search() && (regex_codepoints() && regex_statuses())) { + check world.out.write("std regex ok\n") + } else { + check world.out.write("std regex failed\n") + } +} diff --git a/conformance/native/pass/std-regex.graph b/conformance/native/pass/std-regex.graph new file mode 100644 index 0000000..4b91bbf Binary files /dev/null and b/conformance/native/pass/std-regex.graph differ diff --git a/conformance/native/pass/std-search-sort-widths.0 b/conformance/native/pass/std-search-sort-widths.0 new file mode 100644 index 0000000..ffd939a --- /dev/null +++ b/conformance/native/pass/std-search-sort-widths.0 @@ -0,0 +1,199 @@ +pub fn main(world: World) -> Void raises { + var unsigned: [5]u32 = [9_u32, 1_u32, 4_u32, 4_u32, 2_u32] + var sizes: [5]usize = [8_usize, 3_usize, 5_usize, 5_usize, 1_usize] + std.sort.insertionU32(unsigned) + std.sort.insertionUsize(sizes) + let i32s: [5]i32 = [1, 2, 4, 4, 9] + let low_i32: usize = std.search.lowerBoundI32(i32s, 4) + let upper_i32: usize = std.search.upperBoundI32(i32s, 4) + let contains_i32: Bool = std.search.containsSortedI32(i32s, 4) + let missing_i32: Bool = std.search.containsSortedI32(i32s, 7) + let count_i32: usize = std.search.countSortedI32(i32s, 4) + let missing_count_i32: usize = std.search.countSortedI32(i32s, 7) + let range_i32: Span = std.search.equalRangeI32(i32s, 4) + let partition_point_i32: usize = std.search.partitionPointI32(i32s, 4) + let lower: usize = std.search.lowerBoundU32(unsigned, 4_u32) + let upper: usize = std.search.upperBoundU32(unsigned, 4_u32) + let count_u32: usize = std.search.countSortedU32(unsigned, 4_u32) + let binary_u32: usize = std.search.binaryU32(unsigned, 9_u32) + let contains_u32: Bool = std.search.containsSortedU32(unsigned, 9_u32) + let missing_u32: Bool = std.search.containsSortedU32(unsigned, 7_u32) + let range_u32: Span = std.search.equalRangeU32(unsigned, 4_u32) + let partition_point_u32: usize = std.search.partitionPointU32(unsigned, 4_u32) + let low_usize: usize = std.search.lowerBoundUsize(sizes, 5_usize) + let upper_usize: usize = std.search.upperBoundUsize(sizes, 5_usize) + let count_usize: usize = std.search.countSortedUsize(sizes, 5_usize) + let found: usize = std.search.binaryUsize(sizes, 5_usize) + let contains_usize: Bool = std.search.containsSortedUsize(sizes, 5_usize) + let missing_usize: Bool = std.search.containsSortedUsize(sizes, 6_usize) + let range_usize: Span = std.search.equalRangeUsize(sizes, 5_usize) + let partition_point_usize: usize = std.search.partitionPointUsize(sizes, 5_usize) + let min_i32: Maybe = std.search.minI32(i32s) + let max_i32: Maybe = std.search.maxI32(i32s) + let empty_i32: Maybe = std.search.minI32(std.mem.prefix(i32s, 0_usize)) + let min_index_i32: usize = std.search.minIndexI32(i32s) + let max_index_i32: usize = std.search.maxIndexI32(i32s) + let empty_min_index_i32: usize = std.search.minIndexI32(std.mem.prefix(i32s, 0_usize)) + let min_u32: Maybe = std.search.minU32(unsigned) + let max_u32: Maybe = std.search.maxU32(unsigned) + let empty_u32: Maybe = std.search.maxU32(std.mem.prefix(unsigned, 0_usize)) + let min_index_u32: usize = std.search.minIndexU32(unsigned) + let max_index_u32: usize = std.search.maxIndexU32(unsigned) + let empty_max_index_u32: usize = std.search.maxIndexU32(std.mem.prefix(unsigned, 0_usize)) + let min_usize: Maybe = std.search.minUsize(sizes) + let max_usize: Maybe = std.search.maxUsize(sizes) + let empty_usize: Maybe = std.search.minUsize(std.mem.prefix(sizes, 0_usize)) + let min_index_usize: usize = std.search.minIndexUsize(sizes) + let max_index_usize: usize = std.search.maxIndexUsize(sizes) + let empty_min_index_usize: usize = std.search.minIndexUsize(std.mem.prefix(sizes, 0_usize)) + var part_i32: [5]i32 = [5, 1, 4, 2, 3] + let part_i32_len: usize = std.sort.partitionI32(part_i32, 3) + let part_i32_check: Bool = std.sort.isPartitionedI32(part_i32, 3) + var part_u32: [4]u32 = [3_u32, 1_u32, 4_u32, 2_u32] + let part_u32_len: usize = std.sort.partitionU32(part_u32, 3_u32) + let part_u32_check: Bool = std.sort.isPartitionedU32(part_u32, 3_u32) + var part_usize: [4]usize = [8_usize, 3_usize, 5_usize, 1_usize] + let part_usize_len: usize = std.sort.partitionUsize(part_usize, 5_usize) + let part_usize_check: Bool = std.sort.isPartitionedUsize(part_usize, 5_usize) + var part_desc_i32: [5]i32 = [5, 1, 4, 2, 3] + let part_desc_i32_len: usize = std.sort.partitionDescI32(part_desc_i32, 3) + let part_desc_i32_check: Bool = std.sort.isPartitionedDescI32(part_desc_i32, 3) + var part_desc_u32: [4]u32 = [3_u32, 1_u32, 4_u32, 2_u32] + let part_desc_u32_len: usize = std.sort.partitionDescU32(part_desc_u32, 3_u32) + let part_desc_u32_check: Bool = std.sort.isPartitionedDescU32(part_desc_u32, 3_u32) + var part_desc_usize: [4]usize = [8_usize, 3_usize, 5_usize, 1_usize] + let part_desc_usize_len: usize = std.sort.partitionDescUsize(part_desc_usize, 4_usize) + let part_desc_usize_check: Bool = std.sort.isPartitionedDescUsize(part_desc_usize, 4_usize) + var dup_i32: [6]i32 = [1, 1, 2, 4, 4, 5] + let dup_i32_len: usize = std.sort.dedupeSortedI32(dup_i32) + var dup_u32: [5]u32 = [1_u32, 2_u32, 2_u32, 3_u32, 3_u32] + let dup_u32_len: usize = std.sort.dedupeSortedU32(dup_u32) + var dup_usize: [5]usize = [2_usize, 2_usize, 5_usize, 5_usize, 8_usize] + let dup_usize_len: usize = std.sort.dedupeSortedUsize(dup_usize) + var desc_i32: [5]i32 = [1, 9, 4, 2, 4] + std.sort.insertionDescI32(desc_i32) + let desc_low_i32: usize = std.search.lowerBoundDescI32(desc_i32, 4) + let desc_upper_i32: usize = std.search.upperBoundDescI32(desc_i32, 4) + let desc_binary_i32: usize = std.search.binaryDescI32(desc_i32, 4) + let desc_contains_i32: Bool = std.search.containsSortedDescI32(desc_i32, 4) + let desc_missing_i32: Bool = std.search.containsSortedDescI32(desc_i32, 7) + let desc_count_i32: usize = std.search.countSortedDescI32(desc_i32, 4) + let desc_missing_count_i32: usize = std.search.countSortedDescI32(desc_i32, 7) + let desc_range_i32: Span = std.search.equalRangeDescI32(desc_i32, 4) + let desc_partition_point_i32: usize = std.search.partitionPointDescI32(desc_i32, 4) + var desc_u32: [5]u32 = [1_u32, 9_u32, 4_u32, 2_u32, 4_u32] + std.sort.insertionDescU32(desc_u32) + let desc_low_u32: usize = std.search.lowerBoundDescU32(desc_u32, 4_u32) + let desc_upper_u32: usize = std.search.upperBoundDescU32(desc_u32, 4_u32) + let desc_binary_u32: usize = std.search.binaryDescU32(desc_u32, 4_u32) + let desc_contains_u32: Bool = std.search.containsSortedDescU32(desc_u32, 4_u32) + let desc_missing_u32: Bool = std.search.containsSortedDescU32(desc_u32, 7_u32) + let desc_count_u32: usize = std.search.countSortedDescU32(desc_u32, 4_u32) + let desc_missing_count_u32: usize = std.search.countSortedDescU32(desc_u32, 7_u32) + let desc_range_u32: Span = std.search.equalRangeDescU32(desc_u32, 4_u32) + let desc_partition_point_u32: usize = std.search.partitionPointDescU32(desc_u32, 4_u32) + var desc_usize: [5]usize = [1_usize, 8_usize, 5_usize, 3_usize, 5_usize] + std.sort.insertionDescUsize(desc_usize) + let desc_low_usize: usize = std.search.lowerBoundDescUsize(desc_usize, 5_usize) + let desc_upper_usize: usize = std.search.upperBoundDescUsize(desc_usize, 5_usize) + let desc_binary_usize: usize = std.search.binaryDescUsize(desc_usize, 5_usize) + let desc_contains_usize: Bool = std.search.containsSortedDescUsize(desc_usize, 5_usize) + let desc_missing_usize: Bool = std.search.containsSortedDescUsize(desc_usize, 6_usize) + let desc_count_usize: usize = std.search.countSortedDescUsize(desc_usize, 5_usize) + let desc_missing_count_usize: usize = std.search.countSortedDescUsize(desc_usize, 6_usize) + let desc_range_usize: Span = std.search.equalRangeDescUsize(desc_usize, 5_usize) + let desc_partition_point_usize: usize = std.search.partitionPointDescUsize(desc_usize, 5_usize) + var stable_i32: [5]i32 = [5, 1, 4, 2, 3] + std.sort.stableI32(stable_i32) + var stable_u32: [5]u32 = [5_u32, 1_u32, 4_u32, 2_u32, 3_u32] + std.sort.stableU32(stable_u32) + var stable_usize: [5]usize = [5_usize, 1_usize, 4_usize, 2_usize, 3_usize] + std.sort.stableUsize(stable_usize) + var stable_desc_i32: [5]i32 = [5, 1, 4, 2, 3] + std.sort.stableDescI32(stable_desc_i32) + var stable_desc_u32: [5]u32 = [5_u32, 1_u32, 4_u32, 2_u32, 3_u32] + std.sort.stableDescU32(stable_desc_u32) + var stable_desc_usize: [5]usize = [5_usize, 1_usize, 4_usize, 2_usize, 3_usize] + std.sort.stableDescUsize(stable_desc_usize) + var unstable_i32: [5]i32 = [5, 1, 4, 2, 3] + std.sort.unstableI32(unstable_i32) + var unstable_u32: [5]u32 = [5_u32, 1_u32, 4_u32, 2_u32, 3_u32] + std.sort.unstableU32(unstable_u32) + var unstable_usize: [5]usize = [5_usize, 1_usize, 4_usize, 2_usize, 3_usize] + std.sort.unstableUsize(unstable_usize) + var unstable_desc_i32: [5]i32 = [5, 1, 4, 2, 3] + std.sort.unstableDescI32(unstable_desc_i32) + var unstable_desc_u32: [5]u32 = [5_u32, 1_u32, 4_u32, 2_u32, 3_u32] + std.sort.unstableDescU32(unstable_desc_u32) + var unstable_desc_usize: [5]usize = [5_usize, 1_usize, 4_usize, 2_usize, 3_usize] + std.sort.unstableDescUsize(unstable_desc_usize) + var reverse_i32: [5]i32 = [1, 2, 3, 4, 5] + std.sort.reverseI32(reverse_i32) + var reverse_u32: [5]u32 = [1_u32, 2_u32, 3_u32, 4_u32, 5_u32] + std.sort.reverseU32(reverse_u32) + var reverse_usize: [5]usize = [1_usize, 2_usize, 3_usize, 4_usize, 5_usize] + std.sort.reverseUsize(reverse_usize) + var swap_i32: [3]i32 = [7, 8, 9] + let swap_i32_ok: Bool = std.sort.swapI32(swap_i32, 0_usize, 2_usize) + let swap_i32_bad: Bool = std.sort.swapI32(swap_i32, 0_usize, 3_usize) + var swap_u32: [3]u32 = [1_u32, 2_u32, 3_u32] + let swap_u32_ok: Bool = std.sort.swapU32(swap_u32, 1_usize, 2_usize) + let swap_u32_bad: Bool = std.sort.swapU32(swap_u32, 3_usize, 1_usize) + var swap_usize: [3]usize = [4_usize, 5_usize, 6_usize] + let swap_usize_ok: Bool = std.sort.swapUsize(swap_usize, 0_usize, 1_usize) + let swap_usize_bad: Bool = std.sort.swapUsize(swap_usize, 2_usize, 3_usize) + var rotate_i32: [5]i32 = [1, 2, 3, 4, 5] + std.sort.rotateLeftI32(rotate_i32, 7_usize) + std.sort.rotateRightI32(rotate_i32, 2_usize) + var rotate_u32: [5]u32 = [1_u32, 2_u32, 3_u32, 4_u32, 5_u32] + std.sort.rotateRightU32(rotate_u32, 6_usize) + std.sort.rotateLeftU32(rotate_u32, 1_usize) + var rotate_usize: [5]usize = [1_usize, 2_usize, 3_usize, 4_usize, 5_usize] + std.sort.rotateRightUsize(rotate_usize, 12_usize) + std.sort.rotateLeftUsize(rotate_usize, 2_usize) + var select_i32: [5]i32 = [9, 1, 4, 7, 2] + let select_i32_ok: Bool = std.sort.selectNthI32(select_i32, 2_usize) + var select_u32: [5]u32 = [9_u32, 1_u32, 4_u32, 7_u32, 2_u32] + let select_u32_ok: Bool = std.sort.selectNthU32(select_u32, 2_usize) + var select_usize: [5]usize = [9_usize, 1_usize, 4_usize, 7_usize, 2_usize] + let select_usize_ok: Bool = std.sort.selectNthUsize(select_usize, 2_usize) + var select_desc_i32: [5]i32 = [9, 1, 4, 7, 2] + let select_desc_i32_ok: Bool = std.sort.selectNthDescI32(select_desc_i32, 1_usize) + var select_desc_u32: [5]u32 = [9_u32, 1_u32, 4_u32, 7_u32, 2_u32] + let select_desc_u32_ok: Bool = std.sort.selectNthDescU32(select_desc_u32, 1_usize) + var select_desc_usize: [5]usize = [9_usize, 1_usize, 4_usize, 7_usize, 2_usize] + let select_desc_usize_ok: Bool = std.sort.selectNthDescUsize(select_desc_usize, 1_usize) + let select_bad: Bool = std.sort.selectNthI32(select_i32, 9_usize) + var merge_i32: [6]i32 = [0, 0, 0, 0, 0, 0] + let merge_i32_len: usize = std.sort.mergeSortedI32(merge_i32, std.mem.prefix(i32s, 3_usize), std.mem.dropPrefix(i32s, 3_usize)) + var merge_u32: [6]u32 = [0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32] + let merge_u32_len: usize = std.sort.mergeSortedU32(merge_u32, std.mem.prefix(unsigned, 3_usize), std.mem.dropPrefix(unsigned, 3_usize)) + var merge_usize: [6]usize = [0_usize, 0_usize, 0_usize, 0_usize, 0_usize, 0_usize] + let merge_usize_len: usize = std.sort.mergeSortedUsize(merge_usize, std.mem.prefix(sizes, 3_usize), std.mem.dropPrefix(sizes, 3_usize)) + var merge_desc_i32: [6]i32 = [0, 0, 0, 0, 0, 0] + let merge_desc_i32_len: usize = std.sort.mergeSortedDescI32(merge_desc_i32, std.mem.prefix(desc_i32, 3_usize), std.mem.dropPrefix(desc_i32, 3_usize)) + var merge_desc_u32: [6]u32 = [0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32] + let merge_desc_u32_len: usize = std.sort.mergeSortedDescU32(merge_desc_u32, std.mem.prefix(desc_u32, 3_usize), std.mem.dropPrefix(desc_u32, 3_usize)) + var merge_desc_usize: [6]usize = [0_usize, 0_usize, 0_usize, 0_usize, 0_usize, 0_usize] + let merge_desc_usize_len: usize = std.sort.mergeSortedDescUsize(merge_desc_usize, std.mem.prefix(desc_usize, 3_usize), std.mem.dropPrefix(desc_usize, 3_usize)) + let search_range_ok: Bool = std.mem.len(range_i32) == 2 && range_i32[0] == 4 && range_i32[1] == 4 && partition_point_i32 == 2 && std.mem.len(range_u32) == 2 && range_u32[0] == 4_u32 && range_u32[1] == 4_u32 && partition_point_u32 == 2 && std.mem.len(range_usize) == 2 && range_usize[0] == 5_usize && range_usize[1] == 5_usize && partition_point_usize == 2 + let desc_range_ok: Bool = std.mem.len(desc_range_i32) == 2 && desc_range_i32[0] == 4 && desc_range_i32[1] == 4 && desc_partition_point_i32 == 1 && std.mem.len(desc_range_u32) == 2 && desc_range_u32[0] == 4_u32 && desc_range_u32[1] == 4_u32 && desc_partition_point_u32 == 1 && std.mem.len(desc_range_usize) == 2 && desc_range_usize[0] == 5_usize && desc_range_usize[1] == 5_usize && desc_partition_point_usize == 1 + let search_ok: Bool = std.sort.isSortedU32(unsigned) && std.sort.isSortedUsize(sizes) && low_i32 == 2 && upper_i32 == 4 && contains_i32 && !missing_i32 && count_i32 == 2 && missing_count_i32 == 0 && lower == 2 && upper == 4 && count_u32 == 2 && binary_u32 == 4 && contains_u32 && !missing_u32 && low_usize == 2 && upper_usize == 4 && count_usize == 2 && found == 2 && contains_usize && !missing_usize && min_i32.has && min_i32.value == 1 && max_i32.has && max_i32.value == 9 && !empty_i32.has && min_index_i32 == 0 && max_index_i32 == 4 && empty_min_index_i32 == 0 && min_u32.has && min_u32.value == 1_u32 && max_u32.has && max_u32.value == 9_u32 && !empty_u32.has && min_index_u32 == 0 && max_index_u32 == 4 && empty_max_index_u32 == 0 && min_usize.has && min_usize.value == 1_usize && max_usize.has && max_usize.value == 8_usize && !empty_usize.has && min_index_usize == 0 && max_index_usize == 4 && empty_min_index_usize == 0 && search_range_ok + let partition_ok: Bool = part_i32_len == 2 && part_i32_check && part_i32[0] == 1 && part_i32[1] == 2 && part_u32_len == 2 && part_u32_check && part_u32[0] == 1_u32 && part_u32[1] == 2_u32 && part_usize_len == 2 && part_usize_check && part_usize[0] == 3_usize && part_usize[1] == 1_usize + let partition_desc_ok: Bool = part_desc_i32_len == 2 && part_desc_i32_check && part_desc_i32[0] == 5 && part_desc_i32[1] == 4 && part_desc_u32_len == 1 && part_desc_u32_check && part_desc_u32[0] == 4_u32 && part_desc_usize_len == 2 && part_desc_usize_check && part_desc_usize[0] == 8_usize && part_desc_usize[1] == 5_usize + let dedupe_ok: Bool = dup_i32_len == 4 && dup_i32[0] == 1 && dup_i32[1] == 2 && dup_i32[2] == 4 && dup_i32[3] == 5 && dup_u32_len == 3 && dup_u32[0] == 1_u32 && dup_u32[1] == 2_u32 && dup_u32[2] == 3_u32 && dup_usize_len == 3 && dup_usize[0] == 2_usize && dup_usize[1] == 5_usize && dup_usize[2] == 8_usize + let desc_ok: Bool = std.sort.isSortedDescI32(desc_i32) && !std.sort.isSortedI32(desc_i32) && desc_i32[0] == 9 && desc_i32[4] == 1 && std.sort.isSortedDescU32(desc_u32) && !std.sort.isSortedU32(desc_u32) && desc_u32[0] == 9_u32 && desc_u32[4] == 1_u32 && std.sort.isSortedDescUsize(desc_usize) && !std.sort.isSortedUsize(desc_usize) && desc_usize[0] == 8_usize && desc_usize[4] == 1_usize + let desc_search_ok: Bool = desc_low_i32 == 1 && desc_upper_i32 == 3 && desc_binary_i32 == 1 && desc_contains_i32 && !desc_missing_i32 && desc_count_i32 == 2 && desc_missing_count_i32 == 0 && desc_low_u32 == 1 && desc_upper_u32 == 3 && desc_binary_u32 == 1 && desc_contains_u32 && !desc_missing_u32 && desc_count_u32 == 2 && desc_missing_count_u32 == 0 && desc_low_usize == 1 && desc_upper_usize == 3 && desc_binary_usize == 1 && desc_contains_usize && !desc_missing_usize && desc_count_usize == 2 && desc_missing_count_usize == 0 + let stable_ok: Bool = std.sort.isSortedI32(stable_i32) && stable_i32[0] == 1 && stable_i32[4] == 5 && std.sort.isSortedU32(stable_u32) && stable_u32[0] == 1_u32 && stable_u32[4] == 5_u32 && std.sort.isSortedUsize(stable_usize) && stable_usize[0] == 1_usize && stable_usize[4] == 5_usize + let stable_desc_ok: Bool = std.sort.isSortedDescI32(stable_desc_i32) && stable_desc_i32[0] == 5 && stable_desc_i32[4] == 1 && std.sort.isSortedDescU32(stable_desc_u32) && stable_desc_u32[0] == 5_u32 && stable_desc_u32[4] == 1_u32 && std.sort.isSortedDescUsize(stable_desc_usize) && stable_desc_usize[0] == 5_usize && stable_desc_usize[4] == 1_usize + let unstable_ok: Bool = std.sort.isSortedI32(unstable_i32) && unstable_i32[0] == 1 && unstable_i32[4] == 5 && std.sort.isSortedU32(unstable_u32) && unstable_u32[0] == 1_u32 && unstable_u32[4] == 5_u32 && std.sort.isSortedUsize(unstable_usize) && unstable_usize[0] == 1_usize && unstable_usize[4] == 5_usize + let unstable_desc_ok: Bool = std.sort.isSortedDescI32(unstable_desc_i32) && unstable_desc_i32[0] == 5 && unstable_desc_i32[4] == 1 && std.sort.isSortedDescU32(unstable_desc_u32) && unstable_desc_u32[0] == 5_u32 && unstable_desc_u32[4] == 1_u32 && std.sort.isSortedDescUsize(unstable_desc_usize) && unstable_desc_usize[0] == 5_usize && unstable_desc_usize[4] == 1_usize + let reverse_ok: Bool = std.sort.isSortedDescI32(reverse_i32) && reverse_i32[0] == 5 && reverse_i32[4] == 1 && std.sort.isSortedDescU32(reverse_u32) && reverse_u32[0] == 5_u32 && reverse_u32[4] == 1_u32 && std.sort.isSortedDescUsize(reverse_usize) && reverse_usize[0] == 5_usize && reverse_usize[4] == 1_usize + let swap_ok: Bool = swap_i32_ok && !swap_i32_bad && swap_i32[0] == 9 && swap_i32[2] == 7 && swap_u32_ok && !swap_u32_bad && swap_u32[1] == 3_u32 && swap_u32[2] == 2_u32 && swap_usize_ok && !swap_usize_bad && swap_usize[0] == 5_usize && swap_usize[1] == 4_usize + let rotate_ok: Bool = rotate_i32[0] == 1 && rotate_i32[1] == 2 && rotate_i32[4] == 5 && rotate_u32[0] == 1_u32 && rotate_u32[1] == 2_u32 && rotate_u32[4] == 5_u32 && rotate_usize[0] == 1_usize && rotate_usize[1] == 2_usize && rotate_usize[4] == 5_usize + let select_ok: Bool = select_i32_ok && select_i32[2] == 4 && select_u32_ok && select_u32[2] == 4_u32 && select_usize_ok && select_usize[2] == 4_usize && select_desc_i32_ok && select_desc_i32[1] == 7 && select_desc_u32_ok && select_desc_u32[1] == 7_u32 && select_desc_usize_ok && select_desc_usize[1] == 7_usize && !select_bad + let merge_ok: Bool = merge_i32_len == 5 && std.sort.isSortedI32(std.mem.prefix(merge_i32, merge_i32_len)) && merge_i32[0] == 1 && merge_i32[4] == 9 && merge_u32_len == 5 && std.sort.isSortedU32(std.mem.prefix(merge_u32, merge_u32_len)) && merge_u32[0] == 1_u32 && merge_u32[4] == 9_u32 && merge_usize_len == 5 && std.sort.isSortedUsize(std.mem.prefix(merge_usize, merge_usize_len)) && merge_usize[0] == 1_usize && merge_usize[4] == 8_usize && merge_desc_i32_len == 5 && std.sort.isSortedDescI32(std.mem.prefix(merge_desc_i32, merge_desc_i32_len)) && merge_desc_i32[0] == 9 && merge_desc_i32[4] == 1 && merge_desc_u32_len == 5 && std.sort.isSortedDescU32(std.mem.prefix(merge_desc_u32, merge_desc_u32_len)) && merge_desc_u32[0] == 9_u32 && merge_desc_u32[4] == 1_u32 && merge_desc_usize_len == 5 && std.sort.isSortedDescUsize(std.mem.prefix(merge_desc_usize, merge_desc_usize_len)) && merge_desc_usize[0] == 8_usize && merge_desc_usize[4] == 1_usize + if search_ok && partition_ok && partition_desc_ok && dedupe_ok && desc_ok && desc_search_ok && desc_range_ok && stable_ok && stable_desc_ok && unstable_ok && unstable_desc_ok && reverse_ok && swap_ok && rotate_ok && select_ok && merge_ok { + check world.out.write("std search sort widths ok\n") + } +} diff --git a/conformance/native/pass/std-search-sort-widths.graph b/conformance/native/pass/std-search-sort-widths.graph new file mode 100644 index 0000000..2009fb3 Binary files /dev/null and b/conformance/native/pass/std-search-sort-widths.graph differ diff --git a/conformance/native/pass/std-str-breadth.0 b/conformance/native/pass/std-str-breadth.0 new file mode 100644 index 0000000..e3358bc --- /dev/null +++ b/conformance/native/pass/std-str-breadth.0 @@ -0,0 +1,138 @@ +pub fn main(world: World) -> Void raises { + var reversed_buf: [6]u8 = [0_u8; 6] + let reversed: Maybe> = std.str.reverse(reversed_buf, "drawer") + let trimmed: Span = std.str.trimAscii(" zero text ") + let left_trimmed: Span = std.str.trimStartAscii(" left") + let right_trimmed: Span = std.str.trimEndAscii("right ") + var small: [3]u8 = [0_u8; 3] + let overflow: Maybe> = std.str.reverse(small, "drawer") + var copy_buf: [4]u8 = [0_u8; 4] + var concat_buf: [8]u8 = [0_u8; 8] + var repeat_buf: [6]u8 = [0_u8; 6] + var replace_buf: [16]u8 = [0_u8; 16] + var replace_small: [4]u8 = [0_u8; 4] + var lower_buf: [4]u8 = [0_u8; 4] + var upper_buf: [4]u8 = [0_u8; 4] + let copied: Maybe> = std.str.copy(copy_buf, "zero") + let joined: Maybe> = std.str.concat(concat_buf, "zero", "lang") + let repeated: Maybe> = std.str.repeat(repeat_buf, "ha", 3) + let replaced: Maybe> = std.str.replace(replace_buf, "hello zero hello", "hello", "hi") + let replace_overflow: Maybe> = std.str.replace(replace_small, "zero", "z", "zero") + let replace_empty_old: Maybe> = std.str.replace(replace_buf, "zero", "", "x") + let lowered: Maybe> = std.str.toLowerAscii(lower_buf, "ZERO") + let uppered: Maybe> = std.str.toUpperAscii(upper_buf, "zero") + let split0: Maybe> = std.str.split("alpha,beta,gamma", ",", 0) + let split1: Maybe> = std.str.split("alpha,beta,gamma", ",", 1) + let split2: Maybe> = std.str.split("alpha,beta,gamma", ",", 2) + let split_missing: Maybe> = std.str.split("alpha,beta,gamma", ",", 3) + let field1: Maybe> = std.str.fieldAscii(" zero\ttext\nsyntax ", 1) + let field_missing: Maybe> = std.str.fieldAscii(" zero\ttext\nsyntax ", 3) + let line0: Maybe> = std.str.line("one\r\ntwo\n", 0) + let line1: Maybe> = std.str.line("one\r\ntwo\n", 1) + let line_missing: Maybe> = std.str.line("one\r\ntwo\n", 2) + var ok: Bool = true + if !reversed.has { + ok = false + } + if reversed.has { + if !std.mem.eql(reversed.value, "reward") { + ok = false + } + } + if overflow.has { + ok = false + } + if std.str.countByte("banana", 97_u8) != 3 { + ok = false + } + if !std.str.startsWith("zero text syntax", "zero") { + ok = false + } + if !std.str.endsWith("zero text syntax", "syntax") { + ok = false + } + if !std.str.contains("zero text syntax", "text") { + ok = false + } + if std.str.contains("zero text syntax", "column") { + ok = false + } + if !std.str.contains("zero", "") { + ok = false + } + if !std.mem.eql(trimmed, "zero text") { + ok = false + } + if !std.mem.eql(left_trimmed, "left") || !std.mem.eql(right_trimmed, "right") { + ok = false + } + if !copied.has || !joined.has || !repeated.has || !lowered.has || !uppered.has { + ok = false + } + if copied.has && !std.mem.eql(copied.value, "zero") { + ok = false + } + if joined.has && !std.mem.eql(joined.value, "zerolang") { + ok = false + } + if repeated.has && !std.mem.eql(repeated.value, "hahaha") { + ok = false + } + if !replaced.has || replace_overflow.has || replace_empty_old.has { + ok = false + } + if replaced.has && !std.mem.eql(replaced.value, "hi zero hi") { + ok = false + } + if lowered.has && !std.mem.eql(lowered.value, "zero") { + ok = false + } + if uppered.has && !std.mem.eql(uppered.value, "ZERO") { + ok = false + } + if std.str.count("aaaa", "aa") != 2 { + ok = false + } + if std.str.indexOf("zero text syntax", "text") != 5 || std.str.lastIndexOf("one two one", "one") != 8 { + ok = false + } + if !std.str.eqlIgnoreAsciiCase("Zero", "zero") { + ok = false + } + if std.str.wordCountAscii("zero text syntax") != 3 { + ok = false + } + if std.str.splitCount("alpha,beta,gamma", ",") != 3 || std.str.splitCount("alpha", "") != 0 { + ok = false + } + if !split0.has || !split1.has || !split2.has || split_missing.has { + ok = false + } + if split0.has && !std.mem.eql(split0.value, "alpha") { + ok = false + } + if split1.has && !std.mem.eql(split1.value, "beta") { + ok = false + } + if split2.has && !std.mem.eql(split2.value, "gamma") { + ok = false + } + if std.str.fieldCountAscii(" zero\ttext\nsyntax ") != 3 || !field1.has || field_missing.has { + ok = false + } + if field1.has && !std.mem.eql(field1.value, "text") { + ok = false + } + if std.str.lineCount("one\r\ntwo\n") != 2 || !line0.has || !line1.has || line_missing.has { + ok = false + } + if line0.has && !std.mem.eql(line0.value, "one") { + ok = false + } + if line1.has && !std.mem.eql(line1.value, "two") { + ok = false + } + if ok { + check world.out.write("std str breadth ok\n") + } +} diff --git a/conformance/native/pass/std-str-breadth.graph b/conformance/native/pass/std-str-breadth.graph new file mode 100644 index 0000000..3cd70eb Binary files /dev/null and b/conformance/native/pass/std-str-breadth.graph differ diff --git a/conformance/native/pass/std-term-ansi.0 b/conformance/native/pass/std-term-ansi.0 new file mode 100644 index 0000000..898fc40 --- /dev/null +++ b/conformance/native/pass/std-term-ansi.0 @@ -0,0 +1,136 @@ +pub fn main(world: World) -> Void raises { + let enter: u32 = std.term.keyCode("\n") + let enter_len: usize = std.term.keyByteLen("\n") + let up: u32 = std.term.keyCode("\x1b[A") + let up_len: usize = std.term.keyByteLen("\x1b[A") + let down: u32 = std.term.keyCode("\x1b[B") + let right: u32 = std.term.keyCode("\x1b[C") + let left: u32 = std.term.keyCode("\x1b[D") + let escape: u32 = std.term.keyCode("\x1b") + let tab: u32 = std.term.keyCode("\t") + let backspace: u32 = std.term.keyCode("\x7f") + let ctrl_c: u32 = std.term.keyCode("\x03") + let delete: u32 = std.term.keyCode("\x1b[3~") + let home: u32 = std.term.keyCode("\x1b[H") + let home_tilde: u32 = std.term.keyCode("\x1b[1~") + let home_len: usize = std.term.keyByteLen("\x1b[H") + let end_key: u32 = std.term.keyCode("\x1b[F") + let end_tilde: u32 = std.term.keyCode("\x1b[4~") + let page_up: u32 = std.term.keyCode("\x1b[5~") + let page_down: u32 = std.term.keyCode("\x1b[6~") + let page_down_len: usize = std.term.keyByteLen("\x1b[6~") + let insert: u32 = std.term.keyCode("\x1b[2~") + let shift_tab: u32 = std.term.keyCode("\x1b[Z") + let f1_legacy: u32 = std.term.keyCode("\x1bOP") + let f1_csi: u32 = std.term.keyCode("\x1b[11~") + let f5: u32 = std.term.keyCode("\x1b[15~") + let f10: u32 = std.term.keyCode("\x1b[21~") + let f12: u32 = std.term.keyCode("\x1b[24~") + let f12_len: usize = std.term.keyByteLen("\x1b[24~") + let paste_start: u32 = std.term.keyCode("\x1b[200~") + let paste_end: u32 = std.term.keyCode("\x1b[201~") + let paste_len: usize = std.term.keyByteLen("\x1b[200~") + let ctrl_a: u32 = std.term.keyCode("\x01") + let ctrl_l: u32 = std.term.keyCode("\x0c") + let ctrl_r: u32 = std.term.keyCode("\x12") + let ctrl_w: u32 = std.term.keyCode("\x17") + let lambda: u32 = std.term.keyCode("λ") + let lambda_len: usize = std.term.keyByteLen("λ") + let bad: u32 = std.term.keyCode("\x1b[") + var cursor_to_buffer: [24]u8 = [0_u8; 24] + var cursor_up_buffer: [12]u8 = [0_u8; 12] + var cursor_down_buffer: [12]u8 = [0_u8; 12] + var cursor_right_buffer: [12]u8 = [0_u8; 12] + var cursor_left_buffer: [12]u8 = [0_u8; 12] + var cursor_zero_buffer: [12]u8 = [0_u8; 12] + var cursor_small_buffer: [3]u8 = [0_u8; 3] + let cursor_to: Maybe> = std.term.cursorTo(cursor_to_buffer, 12_usize, 34_usize) + let cursor_up: Maybe> = std.term.cursorUp(cursor_up_buffer, 3_usize) + let cursor_down: Maybe> = std.term.cursorDown(cursor_down_buffer, 4_usize) + let cursor_right: Maybe> = std.term.cursorRight(cursor_right_buffer, 5_usize) + let cursor_left: Maybe> = std.term.cursorLeft(cursor_left_buffer, 6_usize) + let cursor_zero: Maybe> = std.term.cursorUp(cursor_zero_buffer, 0_usize) + let cursor_small: Maybe> = std.term.cursorTo(cursor_small_buffer, 12_usize, 34_usize) + let style_ok: Bool = std.mem.eql(std.term.underline(), "\x1b[4m") && std.mem.eql(std.term.fgBlack(), "\x1b[30m") && std.mem.eql(std.term.bgDefault(), "\x1b[49m") && std.mem.eql(std.term.bgBlack(), "\x1b[40m") && std.mem.eql(std.term.bgBlue(), "\x1b[44m") && std.mem.eql(std.term.bgWhite(), "\x1b[47m") + let clear_ok: Bool = std.mem.eql(std.term.clearScreenDown(), "\x1b[0J") && std.mem.eql(std.term.clearScreenUp(), "\x1b[1J") && std.mem.eql(std.term.clearLineRight(), "\x1b[0K") && std.mem.eql(std.term.clearLineLeft(), "\x1b[1K") + let cursor_ok: Bool = std.mem.eql(std.term.saveCursor(), "\x1b[s") && std.mem.eql(std.term.restoreCursor(), "\x1b[u") && std.mem.eql(std.term.enterBracketedPaste(), "\x1b[?2004h") && std.mem.eql(std.term.leaveBracketedPaste(), "\x1b[?2004l") + let mouse_ok: Bool = std.mem.eql(std.term.enterMouseCapture(), "\x1b[?1000h\x1b[?1002h\x1b[?1006h") && std.mem.eql(std.term.leaveMouseCapture(), "\x1b[?1006l\x1b[?1002l\x1b[?1000l") + if enter != std.term.keyEnter() || enter_len != 1_usize || up != std.term.keyArrowUp() || up_len != 3_usize || down != std.term.keyArrowDown() || right != std.term.keyArrowRight() || left != std.term.keyArrowLeft() || escape != std.term.keyEscape() || tab != std.term.keyTab() || backspace != std.term.keyBackspace() || ctrl_c != std.term.keyCtrlC() || delete != std.term.keyDelete() || home != std.term.keyHome() || home_tilde != std.term.keyHome() || home_len != 3_usize || end_key != std.term.keyEnd() || end_tilde != std.term.keyEnd() || page_up != std.term.keyPageUp() || page_down != std.term.keyPageDown() || page_down_len != 4_usize || insert != std.term.keyInsert() || shift_tab != std.term.keyShiftTab() || f1_legacy != std.term.keyF1() || f1_csi != std.term.keyF1() || f5 != std.term.keyF5() || f10 != std.term.keyF10() || f12 != std.term.keyF12() || f12_len != 5_usize || paste_start != std.term.keyPasteStart() || paste_end != std.term.keyPasteEnd() || paste_len != 6_usize || std.term.keyF2() != 1114125_u32 || std.term.keyF3() != 1114126_u32 || std.term.keyF4() != 1114127_u32 || std.term.keyF6() != 1114129_u32 || std.term.keyF7() != 1114130_u32 || std.term.keyF8() != 1114131_u32 || std.term.keyF9() != 1114132_u32 || std.term.keyF11() != 1114134_u32 || ctrl_a != std.term.keyCtrlA() || ctrl_l != std.term.keyCtrlL() || ctrl_r != std.term.keyCtrlR() || ctrl_w != std.term.keyCtrlW() || std.term.keyCtrlD() != 4_u32 || std.term.keyCtrlE() != 5_u32 || std.term.keyCtrlK() != 11_u32 || std.term.keyCtrlN() != 14_u32 || std.term.keyCtrlP() != 16_u32 || std.term.keyCtrlU() != 21_u32 || lambda != 955_u32 || lambda_len != 2_usize || bad != std.term.keyNone() || !style_ok || !clear_ok || !cursor_ok || !mouse_ok || !cursor_to.has || !std.mem.eql(cursor_to.value, "\x1b[12;34H") || !cursor_up.has || !std.mem.eql(cursor_up.value, "\x1b[3A") || !cursor_down.has || !std.mem.eql(cursor_down.value, "\x1b[4B") || !cursor_right.has || !std.mem.eql(cursor_right.value, "\x1b[5C") || !cursor_left.has || !std.mem.eql(cursor_left.value, "\x1b[6D") || !cursor_zero.has || std.mem.len(cursor_zero.value) != 0_usize || cursor_small.has { + check world.err.write("term key decode failed\n") + return + } + let stdin_tty: Bool = std.term.stdinIsTty() + let stdout_tty: Bool = std.term.stdoutIsTty() + let width: usize = std.term.widthOr(80_usize) + let height: usize = std.term.heightOr(24_usize) + var input: [16]u8 = [0_u8; 16] + let pending: Maybe = std.term.readInput(input) + if pending.has { + if pending.value == 0_usize || pending.value > std.mem.len(input) { + check world.err.write("term input read failed\n") + return + } + let input_bytes: Span = std.mem.prefix(input, pending.value) + let input_key: u32 = std.term.keyCode(input_bytes) + let input_key_len: usize = std.term.keyByteLen(input_bytes) + if input_key == std.term.keyNone() || input_key_len == 0_usize || input_key_len > pending.value { + check world.err.write("term input decode failed\n") + return + } + } + var raw_checked: Bool = true + if stdin_tty { + raw_checked = std.term.enterRawMode() + if raw_checked { + raw_checked = std.term.leaveRawMode() + } + } else { + raw_checked = !std.term.enterRawMode() && std.term.leaveRawMode() + } + if width == 0_usize || height == 0_usize || !raw_checked || stdin_tty && !stdin_tty || stdout_tty && !stdout_tty { + check world.err.write("term metadata failed\n") + return + } + check world.out.write(std.term.enterAltScreen()) + check world.out.write(std.term.clearScreen()) + check world.out.write(std.term.cursorHome()) + check world.out.write(std.term.hideCursor()) + check world.out.write(std.term.bold()) + check world.out.write(std.term.dim()) + check world.out.write(std.term.underline()) + check world.out.write(std.term.inverse()) + check world.out.write(std.term.fgBlack()) + check world.out.write(std.term.fgRed()) + check world.out.write(std.term.fgGreen()) + check world.out.write(std.term.fgYellow()) + check world.out.write(std.term.fgBlue()) + check world.out.write(std.term.fgMagenta()) + check world.out.write(std.term.fgCyan()) + check world.out.write(std.term.fgWhite()) + check world.out.write(std.term.fgDefault()) + check world.out.write(std.term.bgBlack()) + check world.out.write(std.term.bgRed()) + check world.out.write(std.term.bgGreen()) + check world.out.write(std.term.bgYellow()) + check world.out.write(std.term.bgBlue()) + check world.out.write(std.term.bgMagenta()) + check world.out.write(std.term.bgCyan()) + check world.out.write(std.term.bgWhite()) + check world.out.write(std.term.bgDefault()) + check world.out.write(std.term.saveCursor()) + check world.out.write(std.term.restoreCursor()) + check world.out.write(std.term.enterBracketedPaste()) + check world.out.write(std.term.leaveBracketedPaste()) + check world.out.write(std.term.enterMouseCapture()) + check world.out.write(std.term.leaveMouseCapture()) + check world.out.write("term ansi") + check world.out.write(std.term.reset()) + check world.out.write(std.term.clearLine()) + check world.out.write(std.term.clearLineRight()) + check world.out.write(std.term.clearLineLeft()) + check world.out.write(std.term.clearScreenDown()) + check world.out.write(std.term.clearScreenUp()) + check world.out.write(std.term.showCursor()) + check world.out.write(std.term.leaveAltScreen()) + check world.out.write("\n") +} diff --git a/conformance/native/pass/std-term-ansi.graph b/conformance/native/pass/std-term-ansi.graph new file mode 100644 index 0000000..d36ca0f Binary files /dev/null and b/conformance/native/pass/std-term-ansi.graph differ diff --git a/conformance/native/pass/std-testing-helpers-test.0 b/conformance/native/pass/std-testing-helpers-test.0 new file mode 100644 index 0000000..b2ab4b3 --- /dev/null +++ b/conformance/native/pass/std-testing-helpers-test.0 @@ -0,0 +1,12 @@ +test "testing helpers support direct test blocks" { + expect std.testing.isTrue(true) + expect std.testing.isFalse(false) + expect std.testing.equalBool(true, true) + expect std.testing.equalUsize(4, 4) + expect std.testing.equalU32(42_u32, 42_u32) + expect std.testing.equalI32(-1, -1) + expect std.testing.equalBytes("zero", "zero") + expect std.testing.containsBytes("zerolang", "lang") + expect std.testing.startsWith("zerolang", "zero") + expect std.testing.endsWith("zerolang", "lang") +} diff --git a/conformance/native/pass/std-testing-helpers-test.graph b/conformance/native/pass/std-testing-helpers-test.graph new file mode 100644 index 0000000..022d93b Binary files /dev/null and b/conformance/native/pass/std-testing-helpers-test.graph differ diff --git a/conformance/native/pass/std-testing-log.0 b/conformance/native/pass/std-testing-log.0 new file mode 100644 index 0000000..a2ba6cf --- /dev/null +++ b/conformance/native/pass/std-testing-log.0 @@ -0,0 +1,137 @@ +fn testing_helpers_ok() -> Bool { + let bytes: Span = "zerolang" + if !std.testing.isTrue(true) { + return false + } + if !std.testing.isFalse(false) { + return false + } + if !std.testing.equalBool(true, true) { + return false + } + if !std.testing.equalUsize(std.mem.len(bytes), 8) { + return false + } + if !std.testing.equalU32(42_u32, 42_u32) { + return false + } + if !std.testing.equalI32(-7, -7) { + return false + } + if !std.testing.equalBytes("zero", "zero") { + return false + } + if !std.testing.containsBytes(bytes, "lang") { + return false + } + if !std.testing.startsWith(bytes, "zero") { + return false + } + if !std.testing.endsWith(bytes, "lang") { + return false + } + if !std.testing.notEqualBytes("zero", "lang") { + return false + } + let diff: Maybe = std.testing.diffIndexBytes("zero", "zeta") + if !diff.has || diff.value != 2 { + return false + } + let same: Maybe = std.testing.diffIndexBytes("zero", "zero") + if same.has { + return false + } + if !std.testing.jsonFieldEquals("{\"ok\":true,\"count\":2}", "count", "2") { + return false + } + if !std.testing.jsonPathEquals("{\"user\":{\"name\":\"zero\"}}", "user.name", "\"zero\"") { + return false + } + var case_storage: [32]u8 = [0_u8; 32] + let case_name: Maybe> = std.testing.caseName(case_storage, "json", 3) + if !case_name.has || !std.testing.equalBytes(case_name.value, "json[3]") { + return false + } + return true +} + +fn log_message_ok() -> Bool { + var storage: [128]u8 = [0_u8; 128] + let entry: Maybe> = std.log.message(storage, "info", "agent ready") + if entry.has { + if !std.testing.startsWith(entry.value, "{\"level\":\"info\"") { + return false + } + if !std.testing.containsBytes(entry.value, "\"message\":\"agent ready\"") { + return false + } + return std.testing.endsWith(entry.value, "}\n") + } + return false +} + +fn log_key_value_ok() -> Bool { + var storage: [128]u8 = [0_u8; 128] + let entry: Maybe> = std.log.keyValue(storage, "info", "event", "startup") + if entry.has { + if !std.testing.startsWith(entry.value, "{\"level\":\"info\"") { + return false + } + if !std.testing.containsBytes(entry.value, "\"key\":\"event\"") { + return false + } + return std.testing.containsBytes(entry.value, "\"value\":\"startup\"") + } + return false +} + +fn log_message_field_ok() -> Bool { + var storage: [160]u8 = [0_u8; 160] + var field_storage: [64]u8 = [0_u8; 64] + let field: Maybe> = std.log.stringField(field_storage, "route", "/health") + if field.has { + let entry: Maybe> = std.log.messageField(storage, std.log.levelInfo(), "request handled", field.value) + if !entry.has { + return false + } + if !std.testing.startsWith(entry.value, "{\"level\":\"info\"") { + return false + } + if !std.testing.containsBytes(entry.value, "\"message\":\"request handled\"") { + return false + } + return std.testing.containsBytes(entry.value, "\"route\":\"/health\"") + } + return false +} + +fn log_redacted_ok() -> Bool { + var storage: [128]u8 = [0_u8; 128] + let entry: Maybe> = std.log.redacted(storage, std.log.levelWarn(), "authorization") + if entry.has { + if !std.testing.containsBytes(entry.value, "\"key\":\"authorization\"") { + return false + } + return std.testing.containsBytes(entry.value, "\"redacted\":true") + } + return false +} + +fn log_level_names_ok() -> Bool { + var debug_storage: [96]u8 = [0_u8; 96] + var error_storage: [96]u8 = [0_u8; 96] + let debug_entry: Maybe> = std.log.message(debug_storage, std.log.levelDebug(), "debug path") + let error_entry: Maybe> = std.log.message(error_storage, std.log.levelError(), "error path") + if !debug_entry.has || !error_entry.has { + return false + } + return std.testing.containsBytes(debug_entry.value, "\"level\":\"debug\"") && std.testing.containsBytes(error_entry.value, "\"level\":\"error\"") +} + +pub fn main(world: World) -> Void raises { + if testing_helpers_ok() && log_message_ok() && (log_key_value_ok() && log_message_field_ok()) && (log_redacted_ok() && log_level_names_ok()) { + check world.out.write("std testing log ok\n") + } else { + check world.out.write("std testing log failed\n") + } +} diff --git a/conformance/native/pass/std-testing-log.graph b/conformance/native/pass/std-testing-log.graph new file mode 100644 index 0000000..3d71d5b Binary files /dev/null and b/conformance/native/pass/std-testing-log.graph differ diff --git a/conformance/native/pass/std-text.0 b/conformance/native/pass/std-text.0 new file mode 100644 index 0000000..29d3e47 --- /dev/null +++ b/conformance/native/pass/std-text.0 @@ -0,0 +1,14 @@ +pub fn main(world: World) -> Void raises { + let ascii: [4]u8 = [122_u8, 101_u8, 114_u8, 111_u8] + let e_acute: [2]u8 = [195_u8, 169_u8] + let euro: [3]u8 = [226_u8, 130_u8, 172_u8] + let smile: [4]u8 = [240_u8, 159_u8, 152_u8, 128_u8] + let overlong: [2]u8 = [192_u8, 128_u8] + let surrogate: [3]u8 = [237_u8, 160_u8, 128_u8] + let len: Maybe = std.text.utf8Len(e_acute) + let mixed: [9]u8 = [122_u8, 101_u8, 114_u8, 111_u8, 32_u8, 195_u8, 169_u8, 33_u8, 10_u8] + let mixed_len: Maybe = std.text.utf8Len(mixed) + if std.text.isAscii(ascii) && !std.text.isAscii(e_acute) && std.text.utf8Valid(e_acute) && std.text.utf8Valid(euro) && std.text.utf8Valid(smile) && !std.text.utf8Valid(overlong) && !std.text.utf8Valid(surrogate) && len.has && len.value == 1 && mixed_len.has && mixed_len.value == 8 { + check world.out.write("std text ok\n") + } +} diff --git a/conformance/native/pass/std-text.graph b/conformance/native/pass/std-text.graph new file mode 100644 index 0000000..8639d26 Binary files /dev/null and b/conformance/native/pass/std-text.graph differ diff --git a/conformance/native/pass/std-time-rfc3339.0 b/conformance/native/pass/std-time-rfc3339.0 new file mode 100644 index 0000000..8f40a27 --- /dev/null +++ b/conformance/native/pass/std-time-rfc3339.0 @@ -0,0 +1,79 @@ +fn time_date_checks() -> Bool { + return std.time.isRfc3339Date("2026-06-11") && std.time.isRfc3339Date("2000-02-29") && !std.time.isRfc3339Date("1900-02-29") && !std.time.isRfc3339Date("2023-02-29") && !std.time.isRfc3339Date("2024-04-31") && !std.time.isRfc3339Date("2024-13-01") && !std.time.isRfc3339Date("2024-1-01") +} + +fn time_time_checks() -> Bool { + return std.time.isRfc3339Time("23:59:59Z") && std.time.isRfc3339Time("12:30:45.123+05:30") && std.time.isRfc3339Time("12:30:45z") && !std.time.isRfc3339Time("12:30:45") && !std.time.isRfc3339Time("24:00:00Z") && !std.time.isRfc3339Time("12:30:45.Z") && !std.time.isRfc3339Time("12:30:45+24:00") +} + +fn time_leap_second_checks() -> Bool { + if !std.time.isRfc3339Time("23:59:60Z") || !std.time.isRfc3339Time("00:29:60+00:30") { + return false + } + if std.time.isRfc3339Time("23:59:60-01:00") || std.time.isRfc3339Time("12:00:60Z") { + return false + } + return std.time.isRfc3339Time("15:59:60-08:00") && std.time.isRfc3339Time("00:59:60+01:00") && !std.time.isRfc3339Time("23:59:60+00:01") +} + +fn time_datetime_checks() -> Bool { + return std.time.isRfc3339DateTime("2026-06-11T12:30:45Z") && std.time.isRfc3339DateTime("2026-06-11t12:30:45z") && std.time.isRfc3339DateTime("1990-12-31T15:59:60-08:00") && !std.time.isRfc3339DateTime("1990-12-31T23:59:60-01:00") && !std.time.isRfc3339DateTime("2026-06-11 12:30:45Z") && !std.time.isRfc3339DateTime("2023-02-29T00:00:00Z") +} + +fn time_epoch_checks() -> Bool { + if std.time.parseRfc3339DateTimeOr("1970-01-01T00:00:00Z", -1) != 0 { + return false + } + if std.time.parseRfc3339DateTimeOr("2000-01-01T00:00:00Z", -1) != 946684800 { + return false + } + if std.time.parseRfc3339DateTimeOr("2026-06-11T15:30:00-05:00", -1) != 1781209800 { + return false + } + if std.time.parseRfc3339DateTimeOr("1970-01-01T00:00:00+01:00", 0) != -3600 { + return false + } + return std.time.parseRfc3339DateTimeOr("not a datetime", 777) == 777 +} + +fn time_calendar_checks() -> Bool { + return std.time.isLeapYear(2000) && !std.time.isLeapYear(1900) && std.time.isLeapYear(2024) && std.time.daysInMonth(2024, 2) == 29 && std.time.daysInMonth(2023, 2) == 28 && std.time.daysInMonth(2024, 4) == 30 && std.time.daysInMonth(2024, 13) == 0 +} + +fn time_duration_format_checks() -> Bool { + var ns_storage: [32]u8 = [0_u8; 32] + var ms_storage: [32]u8 = [0_u8; 32] + var big_ms_storage: [32]u8 = [0_u8; 32] + var second_storage: [32]u8 = [0_u8; 32] + let ns_text: Maybe> = std.time.writeDurationNs(ns_storage, std.time.ns(42_i64)) + let ms_text: Maybe> = std.time.writeDurationMs(ms_storage, std.time.add(std.time.seconds(1), std.time.ms(250))) + let big_ms_text: Maybe> = std.time.writeDurationMs(big_ms_storage, std.time.ns(3000000000000000_i64)) + let second_text: Maybe> = std.time.writeDurationSeconds(second_storage, std.time.minutes(2)) + if !ns_text.has || !std.mem.eql(ns_text.value, "42ns") { + return false + } + if !ms_text.has || !std.mem.eql(ms_text.value, "1250ms") { + return false + } + if !big_ms_text.has || !std.mem.eql(big_ms_text.value, "3000000000ms") { + return false + } + return second_text.has && std.mem.eql(second_text.value, "120s") +} + +fn time_deadline_checks() -> Bool { + let start: Duration = std.time.seconds(10) + let timeout: Duration = std.time.ms(250) + let deadline: Duration = std.time.deadlineAfter(start, timeout) + let remaining: Duration = std.time.remainingUntil(deadline, std.time.seconds(10)) + let past: Duration = std.time.remainingUntil(deadline, std.time.seconds(11)) + return std.time.asMsFloor(deadline) == 10250 && (std.time.asMsFloor(remaining) == 250 && std.time.isZero(past)) && (!std.time.deadlineExpired(deadline, std.time.seconds(10)) && std.time.deadlineExpired(deadline, deadline)) +} + +pub fn main(world: World) -> Void raises { + if time_date_checks() && (time_time_checks() && time_leap_second_checks()) && (time_datetime_checks() && (time_epoch_checks() && (time_calendar_checks() && (time_duration_format_checks() && time_deadline_checks())))) { + check world.out.write("std time rfc3339 ok\n") + } else { + check world.out.write("std time rfc3339 failed\n") + } +} diff --git a/conformance/native/pass/std-time-rfc3339.graph b/conformance/native/pass/std-time-rfc3339.graph new file mode 100644 index 0000000..1f0cf0f Binary files /dev/null and b/conformance/native/pass/std-time-rfc3339.graph differ diff --git a/conformance/native/pass/std-toml-basic.0 b/conformance/native/pass/std-toml-basic.0 new file mode 100644 index 0000000..592196d --- /dev/null +++ b/conformance/native/pass/std-toml-basic.0 @@ -0,0 +1,53 @@ +pub fn main(world: World) -> Void raises { + let input: Span = "[package]\nname = \"demo\"\ncount = 42\nmax = 4294967295\nsigned = -5\nnames = [\"one\", \"two\"]\nnums = [1, 2, 4294967295]\n\n[features]\ngraph = true\nflags = [true, false]\n" + var name_buffer: [16]u8 = [0_u8; 16] + var decoded_buffer: [16]u8 = [0_u8; 16] + var array_string_buffer: [16]u8 = [0_u8; 16] + let raw_name: Maybe> = std.toml.field(input, "package.name") + let decoded: Maybe> = std.toml.stringDecode(decoded_buffer, "\"demo\"") + let name: Maybe> = std.toml.string(name_buffer, input, "package.name") + let count: Maybe = std.toml.u32(input, "package.count") + let max: Maybe = std.toml.u32(input, "package.max") + let signed: Maybe = std.toml.i32(input, "package.signed") + let graph: Maybe = std.toml.bool(input, "features.graph") + let names: Maybe> = std.toml.field(input, "package.names") + let nums: Maybe> = std.toml.field(input, "package.nums") + let flags: Maybe> = std.toml.field(input, "features.flags") + var names_count: Maybe = null + var first_name: Maybe> = null + var second_num_raw: Maybe> = null + var second_num_i32: Maybe = null + var max_array_num: Maybe = null + var second_flag: Maybe = null + if names.has { + names_count = std.toml.arrayCount(names.value) + first_name = std.toml.arrayString(array_string_buffer, names.value, 0_usize) + } + if nums.has { + second_num_raw = std.toml.arrayValue(nums.value, 1_usize) + second_num_i32 = std.toml.arrayI32(nums.value, 1_usize) + max_array_num = std.toml.arrayU32(nums.value, 2_usize) + } + if flags.has { + second_flag = std.toml.arrayBool(flags.value, 1_usize) + } + var table_buffer: [16]u8 = [0_u8; 16] + var string_line_buffer: [32]u8 = [0_u8; 32] + var u32_line_buffer: [24]u8 = [0_u8; 24] + var bool_line_buffer: [24]u8 = [0_u8; 24] + let table_line: Maybe> = std.toml.writeTableHeader(table_buffer, "package") + let string_line: Maybe> = std.toml.writeKeyValueString(string_line_buffer, "name", "ze\"ro") + let u32_line: Maybe> = std.toml.writeKeyValueU32(u32_line_buffer, "count", 4294967295_u32) + let bool_line: Maybe> = std.toml.writeKeyValueBool(bool_line_buffer, "graph", true) + var invalid_string_line_buffer: [32]u8 = [0_u8; 32] + var invalid_u32_line_buffer: [16]u8 = [0_u8; 16] + var invalid_bool_line_buffer: [24]u8 = [0_u8; 24] + var invalid_table_buffer: [32]u8 = [0_u8; 32] + let invalid_string_line: Maybe> = std.toml.writeKeyValueString(invalid_string_line_buffer, "a = 1\nb", "x") + let invalid_u32_line: Maybe> = std.toml.writeKeyValueU32(invalid_u32_line_buffer, "bad key", 1_u32) + let invalid_bool_line: Maybe> = std.toml.writeKeyValueBool(invalid_bool_line_buffer, "bad..key", true) + let invalid_table_line: Maybe> = std.toml.writeTableHeader(invalid_table_buffer, "package]\nadmin") + if std.toml.validate("[package]\nname = \"demo\"\n") && std.toml.validateBytes(input) && raw_name.has && std.mem.eql(raw_name.value, "\"demo\"") && decoded.has && std.mem.eql(decoded.value, "demo") && name.has && std.mem.eql(name.value, "demo") && count.has && count.value == 42 && max.has && max.value == 4294967295_u32 && signed.has && signed.value == 0 - 5 && graph.has && graph.value && names_count.has && names_count.value == 2_usize && first_name.has && std.mem.eql(first_name.value, "one") && second_num_raw.has && std.mem.eql(second_num_raw.value, "2") && second_num_i32.has && second_num_i32.value == 2 && max_array_num.has && max_array_num.value == 4294967295_u32 && second_flag.has && !second_flag.value && table_line.has && std.mem.eql(table_line.value, "[package]\n") && string_line.has && std.mem.eql(string_line.value, "name = \"ze\\\"ro\"\n") && u32_line.has && std.toml.validate(u32_line.value) && std.mem.eql(u32_line.value, "count = 4294967295\n") && bool_line.has && std.mem.eql(bool_line.value, "graph = true\n") && !invalid_string_line.has && !invalid_u32_line.has && !invalid_bool_line.has && !invalid_table_line.has { + check world.out.write("toml ok\n") + } +} diff --git a/conformance/native/pass/std-toml-basic.graph b/conformance/native/pass/std-toml-basic.graph new file mode 100644 index 0000000..b054006 Binary files /dev/null and b/conformance/native/pass/std-toml-basic.graph differ diff --git a/conformance/native/pass/std-unicode.0 b/conformance/native/pass/std-unicode.0 new file mode 100644 index 0000000..d53ca7d --- /dev/null +++ b/conformance/native/pass/std-unicode.0 @@ -0,0 +1,121 @@ +fn unicode_decode_checks() -> Bool { + let ascii: Maybe = std.unicode.decodeAt("A", 0) + if !ascii.has || ascii.value != 65 { + return false + } + let accent: Maybe = std.unicode.decodeAt("xé", 1) + if !accent.has || accent.value != 233 { + return false + } + let emoji: Maybe = std.unicode.decodeAt("💯", 0) + if !emoji.has || emoji.value != 128175 { + return false + } + let continuation: Maybe = std.unicode.decodeAt("é", 1) + if continuation.has { + return false + } + var overlong: [2]u8 = [192_u8, 175_u8] + let bad: Maybe = std.unicode.decodeAt(overlong, 0) + if bad.has { + return false + } + var surrogate: [3]u8 = [237_u8, 160_u8, 128_u8] + let lone: Maybe = std.unicode.decodeAt(surrogate, 0) + let next: Maybe = std.unicode.nextIndex("aé", 1) + return !lone.has && next.has && next.value == 3 +} + +fn unicode_iteration_checks() -> Bool { + let text: Span = std.mem.span("aé💯") + var index: usize = 0 + var count: usize = 0 + while index < std.mem.len(text) { + let width: Maybe = std.unicode.widthAt(text, index) + if !width.has { + return false + } + index = index + width.value + count = count + 1 + } + let total: Maybe = std.text.utf8Len(text) + return count == 3 && (total.has && total.value == 3) +} + +fn unicode_encode_checks() -> Bool { + var two_storage: [4]u8 = [0; 4] + let two_buffer: MutSpan = two_storage + let two: Maybe> = std.unicode.encode(two_buffer, 233) + if !two.has || (std.mem.len(two.value) != 2 || (two.value[0] != 195_u8 || two.value[1] != 169_u8)) { + return false + } + var four_storage: [4]u8 = [0; 4] + let four_buffer: MutSpan = four_storage + let four: Maybe> = std.unicode.encode(four_buffer, 128175) + if !four.has || std.mem.len(four.value) != 4 { + return false + } + let echo: Maybe = std.unicode.decodeAt(four.value, 0) + if !echo.has || echo.value != 128175 { + return false + } + var reject_storage: [4]u8 = [0; 4] + let reject_buffer: MutSpan = reject_storage + let surrogate: Maybe> = std.unicode.encode(reject_buffer, 55296) + if surrogate.has { + return false + } + var big_storage: [4]u8 = [0; 4] + let big_buffer: MutSpan = big_storage + let too_big: Maybe> = std.unicode.encode(big_buffer, 1114112) + if too_big.has { + return false + } + let max_width: Maybe = std.unicode.encodedWidth(1114111) + let bad_width: Maybe = std.unicode.encodedWidth(56000) + let one_width: Maybe = std.unicode.encodedWidth(65) + if !max_width.has || max_width.value != 4 { + return false + } + if bad_width.has { + return false + } + return one_width.has && one_width.value == 1 +} + +fn unicode_class_checks() -> Bool { + return std.unicode.isDigit(53) && !std.unicode.isDigit(97) && std.unicode.isWord(95) && (std.unicode.isWord(122) && !std.unicode.isWord(233)) && std.unicode.isSpace(32) && (std.unicode.isSpace(8195) && (std.unicode.isSpace(12288) && !std.unicode.isSpace(65))) +} + +fn unicode_status_checks() -> Bool { + var truncated: [2]u8 = [226_u8, 130_u8] + var continuation: [1]u8 = [128_u8] + var overlong: [2]u8 = [192_u8, 175_u8] + if std.unicode.decodeStatusAt("é", 0) != 0_u32 { + return false + } + if std.unicode.decodeStatusAt(truncated, 0) != 4_u32 { + return false + } + if std.unicode.decodeStatusAt(continuation, 0) != 2_u32 { + return false + } + if std.unicode.decodeStatusAt(overlong, 0) != 6_u32 { + return false + } + if std.unicode.invalidIndex("aé") != std.mem.len("aé") { + return false + } + if std.unicode.invalidIndex(truncated) != 0 { + return false + } + return std.mem.eql(std.unicode.statusName(4_u32), "truncated sequence") +} + +pub fn main(world: World) -> Void raises { + if unicode_decode_checks() && unicode_iteration_checks() && (unicode_encode_checks() && (unicode_class_checks() && unicode_status_checks())) { + check world.out.write("std unicode ok\n") + } else { + check world.out.write("std unicode failed\n") + } +} diff --git a/conformance/native/pass/std-unicode.graph b/conformance/native/pass/std-unicode.graph new file mode 100644 index 0000000..e928644 Binary files /dev/null and b/conformance/native/pass/std-unicode.graph differ diff --git a/conformance/native/pass/stdlib-target-neutral.0 b/conformance/native/pass/stdlib-target-neutral.0 new file mode 100644 index 0000000..d193722 --- /dev/null +++ b/conformance/native/pass/stdlib-target-neutral.0 @@ -0,0 +1,80 @@ +fn targetNeutralStdlibOk() -> Bool { + var ok: Bool = true + + var hex_buf: [4]u8 = [0_u8; 4] + let hex: Maybe = std.codec.hexEncode(hex_buf, "Az") + var b64_buf: [4]u8 = [0_u8; 4] + let b64: Maybe = std.codec.base64Encode(b64_buf, "abc") + var varint_buf: [5]u8 = [0_u8; 5] + let varint: Maybe> = std.codec.varintEncode(varint_buf, 300_u32) + if !hex.has || !std.mem.eql(hex.value, "417a") || !b64.has || !std.mem.eql(b64.value, "YWJj") || std.codec.base64EncodedLen(3) != 4 { + ok = false + } + if !varint.has || std.codec.encodedVarintLen(300_u32) != 2 || !std.codec.utf8Valid("zero") { + ok = false + } + if varint.has { + let decoded_varint: Maybe = std.codec.varintDecode(varint.value) + if !decoded_varint.has || decoded_varint.value != 300_u32 { + ok = false + } + } + + var json_buf: [16]u8 = [0_u8; 16] + let json_text: Maybe = std.json.writeString(json_buf, "zero") + if !json_text.has || !std.mem.eql(json_text.value, "\"zero\"") || !std.json.validate("{\"ok\":true}") || std.json.streamTokens("{\"ok\":true}") != 3 { + ok = false + } + + var url_buf: [24]u8 = [0_u8; 24] + let url: Maybe = std.codec.urlEncode(url_buf, "a b") + var query_buf: [24]u8 = [0_u8; 24] + let query: Maybe> = std.url.writeQueryParam(query_buf, "q", "zero lang") + if !url.has || !std.mem.eql(url.value, "a%20b") || !query.has || !std.mem.eql(query.value, "q=zero+lang") { + ok = false + } + + var reversed_buf: [4]u8 = [0_u8; 4] + let reversed: Maybe> = std.str.reverse(reversed_buf, "zero") + if !reversed.has || !std.mem.eql(reversed.value, "orez") || !std.str.contains("zero text", "text") || std.str.wordCountAscii("zero text") != 2 { + ok = false + } + if std.math.gcdU32(84, 30) != 6 || std.math.powU32(3, 4) != 81 || !std.math.isPrimeU32(31) { + ok = false + } + + var sorted: [4]u32 = [9_u32, 1_u32, 4_u32, 2_u32] + std.sort.insertionU32(sorted) + if !std.sort.isSortedU32(sorted) || std.search.binaryU32(sorted, 9_u32) != 3 { + ok = false + } + + var request_buf: [192]u8 = [0_u8; 192] + let request: Maybe> = std.http.writeJsonRequest(request_buf, "POST https://example.com/api?name=zero", "{\"ping\":1}") + if !request.has { + ok = false + } + if request.has { + let method: Maybe> = std.http.requestMethodName(request.value) + let path: Maybe> = std.http.requestPath(request.value) + let body: Maybe> = std.http.requestBodyWithin(request.value, 16) + if !method.has || !std.mem.eql(method.value, "POST") || !path.has || !std.mem.eql(path.value, "/api") || !body.has || !std.mem.eql(body.value, "{\"ping\":1}") { + ok = false + } + } + + var log_buf: [96]u8 = [0_u8; 96] + let entry: Maybe> = std.log.keyValue(log_buf, "info", "event", "startup") + if !entry.has || !std.testing.containsBytes(entry.value, "\"value\":\"startup\"") { + ok = false + } + + return ok +} + +export c fn main() -> i32 { + if targetNeutralStdlibOk() { + return 0 + } + return 1 +} diff --git a/conformance/native/pass/stdlib-target-neutral.graph b/conformance/native/pass/stdlib-target-neutral.graph new file mode 100644 index 0000000..ecaa60d Binary files /dev/null and b/conformance/native/pass/stdlib-target-neutral.graph differ diff --git a/conformance/native/pass/string-byte-ergonomics.0 b/conformance/native/pass/string-byte-ergonomics.0 new file mode 100644 index 0000000..294c609 --- /dev/null +++ b/conformance/native/pass/string-byte-ergonomics.0 @@ -0,0 +1,9 @@ +pub fn main(world: World) -> Void raises { + let text: String = "zero" + let all: Span = std.mem.span(text) + let middle: Span = text[1..3] + let expected: Span = std.mem.span("er") + if std.mem.len(text) == 4 && std.mem.len(all) == 4 && text[0] == 122 && std.mem.eqlBytes(middle, expected) { + check world.out.write("string byte ergonomics ok\n") + } +} diff --git a/conformance/native/pass/string-byte-ergonomics.graph b/conformance/native/pass/string-byte-ergonomics.graph new file mode 100644 index 0000000..8341809 Binary files /dev/null and b/conformance/native/pass/string-byte-ergonomics.graph differ diff --git a/conformance/native/pass/string-param-span-slice.0 b/conformance/native/pass/string-param-span-slice.0 new file mode 100644 index 0000000..25b932a --- /dev/null +++ b/conformance/native/pass/string-param-span-slice.0 @@ -0,0 +1,15 @@ +fn tail(text: String) -> Span { + return text[1..] +} + +fn suffix(text: String) -> String { + return text[1..] +} + +pub fn main(world: World) -> Void raises { + let view: Span = tail("zero") + let text: String = suffix("zero") + if std.mem.len(view) == 3 && view[0] == 101 && std.mem.eql(text, "ero") { + check world.out.write("string param span slice ok\n") + } +} diff --git a/conformance/native/pass/string-param-span-slice.graph b/conformance/native/pass/string-param-span-slice.graph new file mode 100644 index 0000000..2b5779d Binary files /dev/null and b/conformance/native/pass/string-param-span-slice.graph differ diff --git a/conformance/native/pass/string-slices.0 b/conformance/native/pass/string-slices.0 new file mode 100644 index 0000000..b89d4c7 --- /dev/null +++ b/conformance/native/pass/string-slices.0 @@ -0,0 +1,10 @@ +pub fn main(world: World) -> Void raises { + let text: String = "slices" + let middle: Span = text[1..4] + let suffix: Span = text[2..] + let prefix: Span = text[..3] + let all: Span = text[..] + if "zero"[0] == 122 && text[1] == 108 && std.mem.eqlBytes(middle, std.mem.span("lic")) && std.mem.eqlBytes(suffix, std.mem.span("ices")) && std.mem.eqlBytes(prefix, std.mem.span("sli")) && std.mem.len(all) == 6 { + check world.out.write("string slices ok\n") + } +} diff --git a/conformance/native/pass/string-slices.graph b/conformance/native/pass/string-slices.graph new file mode 100644 index 0000000..ccd6d5a Binary files /dev/null and b/conformance/native/pass/string-slices.graph differ diff --git a/conformance/native/pass/test-blocks.0 b/conformance/native/pass/test-blocks.0 new file mode 100644 index 0000000..9513762 --- /dev/null +++ b/conformance/native/pass/test-blocks.0 @@ -0,0 +1,7 @@ +fn add(a: i32, b: i32) -> i32 { + return a + b +} + +test "addition works" { + expect (add(2, 3) == 5) +} diff --git a/conformance/native/pass/test-blocks.graph b/conformance/native/pass/test-blocks.graph new file mode 100644 index 0000000..7150825 Binary files /dev/null and b/conformance/native/pass/test-blocks.graph differ diff --git a/conformance/native/pass/test-expected-fail.0 b/conformance/native/pass/test-expected-fail.0 new file mode 100644 index 0000000..ae85522 --- /dev/null +++ b/conformance/native/pass/test-expected-fail.0 @@ -0,0 +1,3 @@ +test "xfail: known checked failure" { + expect (false) +} diff --git a/conformance/native/pass/test-expected-fail.graph b/conformance/native/pass/test-expected-fail.graph new file mode 100644 index 0000000..c20b701 Binary files /dev/null and b/conformance/native/pass/test-expected-fail.graph differ diff --git a/conformance/native/pass/top-level-const.0 b/conformance/native/pass/top-level-const.0 new file mode 100644 index 0000000..2abbbf7 --- /dev/null +++ b/conformance/native/pass/top-level-const.0 @@ -0,0 +1,26 @@ +const answer: i32 = 42 + +const byte: u8 = 7_u8 + +const stride: usize = 12 + +const scale = 3 + +const fast: Bool = true + +const wide: usize = stride * 2 + +fn scaled(value: i32) -> i32 { + return value * scale +} + +pub fn main(world: World) -> Void raises { + let value: i32 = answer + let small: u8 = byte + let total: usize = stride * 3 + if value == 42 && small == 7_u8 && total == 36 && scaled(4) == 12 && fast && wide == 24 { + check world.out.write("const ok\n") + } else { + check world.out.write("const broke\n") + } +} diff --git a/conformance/native/pass/top-level-const.graph b/conformance/native/pass/top-level-const.graph new file mode 100644 index 0000000..231ff70 Binary files /dev/null and b/conformance/native/pass/top-level-const.graph differ diff --git a/conformance/native/pass/type-alias-basic.0 b/conformance/native/pass/type-alias-basic.0 new file mode 100644 index 0000000..ccb79ce --- /dev/null +++ b/conformance/native/pass/type-alias-basic.0 @@ -0,0 +1,18 @@ +pub alias ByteCount = usize + +alias BytePair = Pair + +type Pair { + left: T, + right: U, +} + +pub fn main(world: World) -> Void raises { + let count: ByteCount = 4_usize + let pair: BytePair = Pair { left: 1_u8, right: 2_u8 } + if count == 4_usize && pair.left == 1_u8 && pair.right == 2_u8 { + check world.out.write("type alias ok\n") + } else { + check world.out.write("type alias broke\n") + } +} diff --git a/conformance/native/pass/type-alias-basic.graph b/conformance/native/pass/type-alias-basic.graph new file mode 100644 index 0000000..43fe902 Binary files /dev/null and b/conformance/native/pass/type-alias-basic.graph differ diff --git a/conformance/native/pass/untyped-literal-adoption.0 b/conformance/native/pass/untyped-literal-adoption.0 new file mode 100644 index 0000000..21b3e84 --- /dev/null +++ b/conformance/native/pass/untyped-literal-adoption.0 @@ -0,0 +1,28 @@ +pub fn main(world: World) -> Void raises { + var i: usize = 0 + var total: usize = 0 + while i < 10 { + let offset: usize = i * 12 + total = total + offset + i = i + 1 + } + if total == 540 { + check world.out.write("usize literal adoption ok\n") + } + var j: usize = 0 + while 10 > j { + j = 1 + j + } + if 10 == j { + check world.out.write("flipped literal adoption ok\n") + } + if 12 * i == 120 { + check world.out.write("literal arithmetic adoption ok\n") + } + var b: u8 = 7 + b = b + 3 + b = 3 + b + if 200 > b { + check world.out.write("u8 literal adoption ok\n") + } +} diff --git a/conformance/native/pass/untyped-literal-adoption.graph b/conformance/native/pass/untyped-literal-adoption.graph new file mode 100644 index 0000000..4951c48 Binary files /dev/null and b/conformance/native/pass/untyped-literal-adoption.graph differ diff --git a/conformance/native/pass/variants-defer-stdlib.0 b/conformance/native/pass/variants-defer-stdlib.0 new file mode 100644 index 0000000..23efb87 --- /dev/null +++ b/conformance/native/pass/variants-defer-stdlib.0 @@ -0,0 +1,31 @@ +use std.codec + +use std.parse + +use std.time + +enum Status { + ready, + failed, +} + +choice Result { + ok, + failed: i32, +} + +fn cleanup() -> Void { + return +} + +pub fn main(world: World) -> Void raises { + defer cleanup() + let status: Status = Status.ready + let result: Result = Result.ok + let bytes: Maybe = std.codec.readU32Le("abcd") + let digits: usize = std.parse.scanDigits("123abc") + let duration: Duration = std.time.add(std.time.ms(1), std.time.seconds(1)) + if digits == 3 && bytes.has && bytes.value > 0_u32 && std.time.asMsFloor(duration) > 0 { + check world.out.write("native variants std") + } +} diff --git a/conformance/native/pass/variants-defer-stdlib.graph b/conformance/native/pass/variants-defer-stdlib.graph new file mode 100644 index 0000000..837f772 Binary files /dev/null and b/conformance/native/pass/variants-defer-stdlib.graph differ diff --git a/conformance/native/pass/world-stream-renamed-param.0 b/conformance/native/pass/world-stream-renamed-param.0 new file mode 100644 index 0000000..6e7dd24 --- /dev/null +++ b/conformance/native/pass/world-stream-renamed-param.0 @@ -0,0 +1,3 @@ +pub fn main(w: World) -> Void raises { + check w.out.write("renamed world\n") +} diff --git a/conformance/native/pass/world-stream-renamed-param.graph b/conformance/native/pass/world-stream-renamed-param.graph new file mode 100644 index 0000000..6400d8a Binary files /dev/null and b/conformance/native/pass/world-stream-renamed-param.graph differ diff --git a/conformance/native/pass/wrapping-saturating-arithmetic.0 b/conformance/native/pass/wrapping-saturating-arithmetic.0 new file mode 100644 index 0000000..ce67295 --- /dev/null +++ b/conformance/native/pass/wrapping-saturating-arithmetic.0 @@ -0,0 +1,8 @@ +pub fn main(world: World) -> Void raises { + let wrapped: u8 = 255_u8 +% 2_u8 + let saturated: u8 = 250_u8 +| 10_u8 + let checked: u8 = 40_u8 + 2_u8 + if wrapped == 1_u8 && saturated == 255_u8 && checked == 42_u8 { + check world.out.write("wrapping saturating arithmetic ok\n") + } +} diff --git a/conformance/native/pass/wrapping-saturating-arithmetic.graph b/conformance/native/pass/wrapping-saturating-arithmetic.graph new file mode 100644 index 0000000..5aab09e Binary files /dev/null and b/conformance/native/pass/wrapping-saturating-arithmetic.graph differ diff --git a/conformance/packages/dep-app/src/main.0 b/conformance/packages/dep-app/src/main.0 new file mode 100644 index 0000000..6e307da --- /dev/null +++ b/conformance/packages/dep-app/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("dep app\n") +} diff --git a/conformance/packages/dep-app/zero.graph b/conformance/packages/dep-app/zero.graph new file mode 100644 index 0000000..b4ab786 Binary files /dev/null and b/conformance/packages/dep-app/zero.graph differ diff --git a/conformance/packages/dep-app/zero.toml b/conformance/packages/dep-app/zero.toml new file mode 100644 index 0000000..379a5e5 --- /dev/null +++ b/conformance/packages/dep-app/zero.toml @@ -0,0 +1,14 @@ +[package] +name = "dep-app" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" + +[dependencies] +remote-tools = "1.2.3" + +[dependencies.dep-lib] +path = "../dep-lib" +version = "0.1.0" diff --git a/conformance/packages/dep-lib/src/main.0 b/conformance/packages/dep-lib/src/main.0 new file mode 100644 index 0000000..22b75d2 --- /dev/null +++ b/conformance/packages/dep-lib/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("dep lib\n") +} diff --git a/conformance/packages/dep-lib/zero.graph b/conformance/packages/dep-lib/zero.graph new file mode 100644 index 0000000..cf5615c Binary files /dev/null and b/conformance/packages/dep-lib/zero.graph differ diff --git a/conformance/packages/dep-lib/zero.toml b/conformance/packages/dep-lib/zero.toml new file mode 100644 index 0000000..897cfba --- /dev/null +++ b/conformance/packages/dep-lib/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "dep-lib" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/packages/shared-v1/src/main.0 b/conformance/packages/shared-v1/src/main.0 new file mode 100644 index 0000000..257e1ca --- /dev/null +++ b/conformance/packages/shared-v1/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("shared v1\n") +} diff --git a/conformance/packages/shared-v1/zero.graph b/conformance/packages/shared-v1/zero.graph new file mode 100644 index 0000000..f5e6a4d Binary files /dev/null and b/conformance/packages/shared-v1/zero.graph differ diff --git a/conformance/packages/shared-v1/zero.toml b/conformance/packages/shared-v1/zero.toml new file mode 100644 index 0000000..258306c --- /dev/null +++ b/conformance/packages/shared-v1/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "shared" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/packages/shared-v2/src/main.0 b/conformance/packages/shared-v2/src/main.0 new file mode 100644 index 0000000..21f0d06 --- /dev/null +++ b/conformance/packages/shared-v2/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("shared v2\n") +} diff --git a/conformance/packages/shared-v2/zero.graph b/conformance/packages/shared-v2/zero.graph new file mode 100644 index 0000000..66f159c Binary files /dev/null and b/conformance/packages/shared-v2/zero.graph differ diff --git a/conformance/packages/shared-v2/zero.toml b/conformance/packages/shared-v2/zero.toml new file mode 100644 index 0000000..fd11325 --- /dev/null +++ b/conformance/packages/shared-v2/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "shared" +version = "0.2.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/packages/target-webbits/src/main.0 b/conformance/packages/target-webbits/src/main.0 new file mode 100644 index 0000000..747b665 --- /dev/null +++ b/conformance/packages/target-webbits/src/main.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("webbits\n") +} diff --git a/conformance/packages/target-webbits/zero.graph b/conformance/packages/target-webbits/zero.graph new file mode 100644 index 0000000..1ce8d17 Binary files /dev/null and b/conformance/packages/target-webbits/zero.graph differ diff --git a/conformance/packages/target-webbits/zero.toml b/conformance/packages/target-webbits/zero.toml new file mode 100644 index 0000000..8886d44 --- /dev/null +++ b/conformance/packages/target-webbits/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "target-webbits" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/packages/test-app/src/helper.0 b/conformance/packages/test-app/src/helper.0 new file mode 100644 index 0000000..f06f93b --- /dev/null +++ b/conformance/packages/test-app/src/helper.0 @@ -0,0 +1,11 @@ +fn double(value: i32) -> i32 { + return value + value +} + +test "package helper double" { + expect (double(4) == 8) +} + +test "xfail: package helper pending edge case" { + expect (false) +} diff --git a/conformance/packages/test-app/src/main.0 b/conformance/packages/test-app/src/main.0 new file mode 100644 index 0000000..2360dcf --- /dev/null +++ b/conformance/packages/test-app/src/main.0 @@ -0,0 +1,13 @@ +use helper + +fn add(a: i32, b: i32) -> i32 { + return a + b +} + +test "package main addition" { + expect (add(20, 22) == 42) +} + +pub fn main(world: World) -> Void raises { + check world.out.write("package tests\n") +} diff --git a/conformance/packages/test-app/zero.graph b/conformance/packages/test-app/zero.graph new file mode 100644 index 0000000..bc79eae Binary files /dev/null and b/conformance/packages/test-app/zero.graph differ diff --git a/conformance/packages/test-app/zero.toml b/conformance/packages/test-app/zero.toml new file mode 100644 index 0000000..b896a28 --- /dev/null +++ b/conformance/packages/test-app/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "test-app" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" diff --git a/conformance/packages/toml-precedence/src/toml.0 b/conformance/packages/toml-precedence/src/toml.0 new file mode 100644 index 0000000..4d59a84 --- /dev/null +++ b/conformance/packages/toml-precedence/src/toml.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("toml precedence\n") +} diff --git a/conformance/packages/toml-precedence/zero.graph b/conformance/packages/toml-precedence/zero.graph new file mode 100644 index 0000000..272f8d8 Binary files /dev/null and b/conformance/packages/toml-precedence/zero.graph differ diff --git a/conformance/packages/toml-precedence/zero.toml b/conformance/packages/toml-precedence/zero.toml new file mode 100644 index 0000000..78bd277 --- /dev/null +++ b/conformance/packages/toml-precedence/zero.toml @@ -0,0 +1,8 @@ +# TOML takes precedence over zero.json for directory package inputs. +[package] +name = "toml-precedence" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/toml.0" diff --git a/conformance/program-graph/hello.0 b/conformance/program-graph/hello.0 new file mode 100644 index 0000000..fb4ef36 --- /dev/null +++ b/conformance/program-graph/hello.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("hello from zero\n") +} diff --git a/conformance/program-graph/zero.graph b/conformance/program-graph/zero.graph new file mode 100644 index 0000000..033ddea Binary files /dev/null and b/conformance/program-graph/zero.graph differ diff --git a/conformance/program-graph/zero.toml b/conformance/program-graph/zero.toml new file mode 100644 index 0000000..3faf53d --- /dev/null +++ b/conformance/program-graph/zero.toml @@ -0,0 +1,7 @@ +[package] +name = "program-graph-fixture" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "hello.0" diff --git a/conformance/provenance-surface.json b/conformance/provenance-surface.json new file mode 100644 index 0000000..fc7e93b --- /dev/null +++ b/conformance/provenance-surface.json @@ -0,0 +1,393 @@ +{ + "schemaVersion": 1, + "kind": "zero-provenance-surface-matrix", + "surfaces": [ + { + "surface": "Identifier reads", + "action": "Preserve stored provenance or synthesize direct ref provenance", + "owners": [ + "expr_value_provenance", + "expr_reference_provenance", + "type_value_provenance_from_place" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Field reads", + "action": "Project provenance through value_path and root storage paths", + "owners": [ + "expr_reference_provenance", + "place_storage_value_provenance_under_path" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Index reads", + "action": "Project indexed provenance and widen uncertain array paths to [*]", + "owners": [ + "expr_reference_provenance", + "origin_path_join", + "origin_path_overlaps" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Direct borrows", + "action": "Create provenance for the borrowed place and check lexical conflicts", + "owners": [ + "expr_value_provenance", + "check_borrow_conflict_at" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Mutable borrows", + "action": "Create mutable provenance and reject overlapping mutable/shared borrows", + "owners": [ + "expr_value_provenance", + "check_borrow_conflict_at", + "check_read_not_mutably_borrowed" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Shape literals", + "action": "Preserve field provenance under field paths", + "owners": [ + "expr_reference_provenance", + "value_provenance_add_all_with_prefix" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Array literals", + "action": "Preserve literal element provenance under precise paths and widen unknown paths to [*]", + "owners": [ + "expr_reference_provenance", + "value_provenance_add_all_with_prefix" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Maybe values", + "action": "Preserve nested provenance under .value", + "owners": [ + "type_value_provenance_from_place", + "maybe_unwrapped_value_provenance" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Choice payloads", + "action": "Preserve constructor payload provenance under case paths and bind match payload provenance from the matched case", + "owners": [ + "type_value_provenance_from_place", + "choice_constructor_value_provenance", + "register_match_payload_binding_provenance" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/choice-payload-reference-return.0", + "conformance/native/pass/choice-match-payload-reference-origin.0", + "conformance/native/pass/choice-match-payload-return-origin.0" + ] + } + }, + { + "surface": "check unwraps", + "action": "Project successful value provenance and apply checked-call storage effects", + "owners": [ + "expr_reference_provenance", + "apply_expr_call_storage_effects" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "rescue values", + "action": "Merge fallible value and fallback provenance conservatively", + "owners": [ + "expr_reference_provenance", + "provenance_scope_snapshot_restore_optional_branch", + "apply_expr_call_storage_effects" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Primitive casts", + "action": "Reject non-primitive casts before references can pass through", + "owners": [ + "check_expr_expected", + "expr_reference_provenance" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Plain calls", + "action": "Resolve through the shared call resolver, instantiate return provenance, and replay storage effects", + "owners": [ + "resolve_provenance_call", + "resolve_named_provenance_call", + "call_result_value_provenance", + "apply_checked_call_storage_effects" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/function-mutref-reference-store.0" + ] + } + }, + { + "surface": "Generic calls", + "action": "Resolve generic bindings once and use the same summary pipeline", + "owners": [ + "resolve_provenance_call", + "resolve_named_provenance_call", + "call_result_value_provenance", + "function_provenance_summary" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/generic-mutref-reference-store.0" + ] + } + }, + { + "surface": "Shape namespace calls", + "action": "Resolve static shape methods through the shared call resolver", + "owners": [ + "resolve_provenance_call", + "finish_shape_method_provenance_call", + "build_shape_method_bindings", + "apply_checked_call_storage_effects" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/static-method-namespace.0" + ] + } + }, + { + "surface": "Receiver calls", + "action": "Treat self as parameter zero and replay receiver storage effects", + "owners": [ + "resolve_provenance_call", + "resolve_receiver_shape_provenance_call", + "collect_effect_target_places", + "apply_checked_call_storage_effects" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/receiver-method-reference-store.0" + ] + } + }, + { + "surface": "Static interface calls", + "action": "Resolve constrained interface calls and concrete shape instantiations through the same summary/effect path", + "owners": [ + "resolve_provenance_call", + "resolve_concrete_constrained_shape_provenance_call", + "resolve_constrained_interface_provenance_call", + "finish_shape_method_provenance_call", + "build_constrained_interface_method_bindings", + "apply_checked_call_storage_effects" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/static-interface-return-reference-origin.0", + "conformance/native/pass/static-interface-mutref-reference-store.0" + ] + } + }, + { + "surface": "Incomplete summaries", + "action": "Reject incomplete mutable-parameter summaries instead of assuming no effects", + "owners": [ + "function_provenance_summary", + "apply_provenance_call_storage_effects" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Let bindings", + "action": "Install initializer provenance on the binding", + "owners": [ + "register_borrow_binding", + "scope_set_value_provenance" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/borrow-shadowed-root-reassignment.0" + ] + } + }, + { + "surface": "Assignments", + "action": "Clear and install provenance at canonical target places", + "owners": [ + "assignment_provenance_snapshot_clear", + "update_borrow_assignment", + "collect_assignment_target_places" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.0" + ] + } + }, + { + "surface": "Field assignments", + "action": "Preserve sibling provenance and update only the overwritten field path", + "owners": [ + "collect_assignment_target_places", + "update_borrow_assignment", + "scope_set_value_provenance_path_in_scope" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/shape-field-reference-reassignment-clears-origin.0" + ] + } + }, + { + "surface": "Index assignments", + "action": "Update indexed storage using precise or widened paths", + "owners": [ + "collect_assignment_target_places", + "update_borrow_assignment", + "origin_path_overlaps", + "origin_path_is_definitely_within" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/index-reference-assignment-clears-origin.0" + ] + } + }, + { + "surface": "Returns", + "action": "Reject local-origin escapes and summarize return provenance for callers", + "owners": [ + "check_return_reference_escape", + "collect_return_value_provenance_from_stmt_vec", + "function_provenance_summary" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "If joins", + "action": "Merge provenance from continuing branches", + "owners": [ + "provenance_scope_snapshot_restore_union", + "collect_return_value_provenance_from_stmt_vec" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/branch-overwrite-away-reference-origin.0" + ] + } + }, + { + "surface": "Match joins", + "action": "Merge provenance from continuing match arms", + "owners": [ + "provenance_scope_snapshot_restore_union", + "collect_return_value_provenance_from_stmt_vec" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/borrow-unreachable-match-return-origin.0" + ] + } + }, + { + "surface": "Loop-carried values", + "action": "Merge before-loop and after-body provenance conservatively", + "owners": [ + "provenance_scope_snapshot_restore_union", + "collect_return_value_provenance_from_stmt_vec" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Short-circuit expressions", + "action": "Merge skipped and evaluated RHS storage effects conservatively", + "owners": [ + "apply_expr_call_storage_effects", + "provenance_scope_snapshot_restore_optional_branch" + ], + "fixtures": { + "fail": [] + } + }, + { + "surface": "Early returns", + "action": "Exclude unreachable post-return provenance from branch joins", + "owners": [ + "collect_return_value_provenance_from_stmt_vec", + "stmt_vec_guarantees_exit" + ], + "fixtures": { + "pass": [ + "conformance/native/pass/borrow-unreachable-return-origin.0", + "conformance/native/pass/borrow-unreachable-if-return-origin.0" + ] + } + }, + { + "surface": "Mutref aliases", + "action": "Resolve alias targets to canonical storage before clearing or replaying effects", + "owners": [ + "collect_effect_target_places", + "collect_assignment_target_places", + "actual_storage_value_provenance_under_path" + ], + "fixtures": { + "fail": [], + "pass": [ + "conformance/native/pass/mutref-alias-assignment-same-origin.0" + ] + } + } + ] +} diff --git a/conformance/run.mjs b/conformance/run.mjs new file mode 100644 index 0000000..37c5dc3 --- /dev/null +++ b/conformance/run.mjs @@ -0,0 +1,5380 @@ +#!/usr/bin/env node +import { execFile } from "node:child_process"; +import { existsSync } from "node:fs"; +import { access, chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { createAggregateAssert, describeFailure, finishAggregateAssert } from "../scripts/aggregate-assert.mjs"; + +const assert = createAggregateAssert(); + +if (process.env.ZERO_NATIVE_TEST_SANDBOX !== "1" && process.env.ZERO_NATIVE_TEST_ALLOW_LOCAL !== "1") { + console.error("conformance emits native test artifacts; run `pnpm run conformance` for Vercel Sandbox execution or set ZERO_NATIVE_TEST_ALLOW_LOCAL=1 to opt into local artifacts."); + process.exit(1); +} + +const execMaxBuffer = 16 * 1024 * 1024; +const zero = resolve(process.env.ZERO_BIN || (existsSync(".zero/bin/zero") ? ".zero/bin/zero" : "bin/zero")); +const outDir = process.env.ZERO_CONFORMANCE_OUT_DIR || ".zero/conformance"; +const canRunLinuxMuslX64 = process.platform === "linux" && process.arch === "x64"; +const runnableDirectTarget = + process.platform === "darwin" && process.arch === "arm64" ? "darwin-arm64" : + process.platform === "linux" && process.arch === "x64" ? "linux-musl-x64" : + null; +const checkTimeoutMs = Number(process.env.ZERO_CHECK_TIMEOUT_MS ?? 2000); + +function parsePositiveInt(value, fallback) { + const parsed = Number.parseInt(value ?? "", 10); + return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; +} + +const defaultCheckJobs = 1; +const checkJobs = parsePositiveInt(process.env.ZERO_CONFORMANCE_CHECK_JOBS, defaultCheckJobs); + +function runnableExeArgs(input, out) { + if (!runnableDirectTarget) return null; + return ["build", "--emit", "exe", "--target", runnableDirectTarget, compilerInputPath(input), "--out", out]; +} + +await mkdir(outDir, { recursive: true }); +await mkdir(`${outDir}/check-cache`, { recursive: true }); + +function execFileAsync(file, args = [], options = {}) { + return new Promise((resolve, reject) => { + const normalizedArgs = file === zero ? normalizeZeroCompilerArgs(args) : args; + execFile(file, normalizedArgs, { maxBuffer: execMaxBuffer, ...options }, (error, stdout, stderr) => { + if (error) { + error.stdout = stdout; + error.stderr = stderr; + reject(error); + return; + } + resolve({ stdout, stderr }); + }); + }); +} + +function tomlQuote(value) { + return JSON.stringify(String(value)); +} + +function tomlArray(values) { + return `[${values.map(tomlQuote).join(", ")}]`; +} + +function appendManifestFields(lines, object, order) { + for (const key of order) { + if (!(key in object)) continue; + const value = object[key]; + if (Array.isArray(value)) lines.push(`${key} = ${tomlArray(value)}`); + else if (typeof value === "boolean") lines.push(`${key} = ${value ? "true" : "false"}`); + else lines.push(`${key} = ${tomlQuote(value)}`); + } +} + +function manifestToml(manifest) { + const lines = ["[package]"]; + appendManifestFields(lines, manifest.package ?? {}, ["name", "version", "license"]); + for (const [targetName, target] of Object.entries(manifest.targets ?? {})) { + lines.push("", `[targets.${targetName}]`); + appendManifestFields(lines, target, ["kind", "main", "graph", "defaultTarget", "devTarget", "releaseProfile"]); + } + if (manifest.repositoryGraph) { + lines.push("", "[repositoryGraph]"); + appendManifestFields(lines, manifest.repositoryGraph, ["compilerInput"]); + } + for (const sectionName of ["deps", "dependencies"]) { + if (!manifest[sectionName]) continue; + lines.push("", `[${sectionName}]`); + for (const [name, value] of Object.entries(manifest[sectionName])) { + if (typeof value === "string") lines.push(`${name} = ${tomlQuote(value)}`); + } + for (const [name, value] of Object.entries(manifest[sectionName])) { + if (typeof value === "string") continue; + lines.push("", `[${sectionName}.${name}]`); + appendManifestFields(lines, value, ["path", "version", "targets", "target"]); + } + } + for (const [name, lib] of Object.entries(manifest.c?.libs ?? {})) { + lines.push("", `[c.libs.${name}]`); + appendManifestFields(lines, lib, ["headers", "include", "lib", "link", "mode", "pkg_config", "pkgConfig"]); + } + return `${lines.join("\n").replace(/\n{3,}/g, "\n\n")}\n`; +} + +async function writeZeroToml(root, manifest) { + await writeFile(`${root}/zero.toml`, manifestToml(manifest)); +} + +function graphSidecarPath(sourcePath) { + if (!sourcePath.endsWith(".0")) throw new Error(`${sourcePath}: expected a .0 projection path`); + return `${sourcePath.slice(0, -2)}.graph`; +} + +const compilerInputCommands = new Set(["check", "build", "run", "test", "size", "mem", "doc", "dev", "time", "fix"]); +const compilerInputValueFlags = new Set(["--backend", "--emit", "--filter", "--out", "--profile", "--release", "--target"]); +const abiInputSubcommands = new Set(["check", "dump"]); + +function compilerInputPath(inputPath) { + if (typeof inputPath !== "string" || !inputPath.endsWith(".0")) return inputPath; + const graphPath = graphSidecarPath(inputPath); + if (!existsSync(graphPath)) { + throw new Error(`${inputPath}: compiler command requires graph input; missing graph sidecar ${graphPath}`); + } + return graphPath; +} + +function normalizeZeroCompilerArgs(args) { + if (!Array.isArray(args)) return args; + const isCompilerInputCommand = compilerInputCommands.has(args[0]); + const isAbiInputCommand = args[0] === "abi" && abiInputSubcommands.has(args[1]); + if (!isCompilerInputCommand && !isAbiInputCommand) return args; + let afterProgramArgs = false; + let skipOptionValue = false; + return args.map((arg) => { + if (afterProgramArgs) return arg; + if (arg === "--") afterProgramArgs = true; + if (skipOptionValue) { + skipOptionValue = false; + return arg; + } + if (compilerInputValueFlags.has(arg)) { + skipOptionValue = true; + return arg; + } + return afterProgramArgs ? arg : compilerInputPath(arg); + }); +} + +async function writeGraphFixture(sourcePath, source) { + await writeFile(sourcePath, source); + const graphPath = graphSidecarPath(sourcePath); + await execFileAsync(zero, ["import", "--format", "binary", "--out", graphPath, sourcePath]); + return graphPath; +} + +async function importGraphFixtureFailure(sourcePath) { + const result = await execFileAsync(zero, ["import", "--json", "--format", "binary", "--out", graphSidecarPath(sourcePath), sourcePath]).catch((error) => error); + assert.notEqual(result.code, 0); + return JSON.parse(result.stdout); +} + +async function writeImportFailureFixture(sourcePath, source) { + await writeFile(sourcePath, source); + return importGraphFixtureFailure(sourcePath); +} + +async function importPackageGraph(root) { + await execFileAsync(zero, ["import", root]); +} + +function isolatedCacheEnv(workerIndex) { + return { + ...process.env, + ZERO_CACHE_DIR: `${outDir}/check-cache/worker-${workerIndex}`, + }; +} + +async function mapLimit(items, limit, callback) { + const results = new Array(items.length); + let next = 0; + const workerCount = Math.min(Math.max(1, limit), Math.max(1, items.length)); + async function worker(workerIndex) { + await mkdir(`${outDir}/check-cache/worker-${workerIndex}`, { recursive: true }); + for (;;) { + const index = next++; + if (index >= items.length) return; + try { + results[index] = await callback(items[index], index, workerIndex); + } catch (error) { + const item = Array.isArray(items[index]) ? items[index][0] : items[index]; + results[index] = { error }; + assert.fail(`parallel conformance item failed: ${item}\n${describeFailure(error)}`); + } + } + } + await Promise.all(Array.from({ length: workerCount }, (_, index) => worker(index))); + return results; +} + +async function checkFixtureParallel(fixture, workerIndex) { + const options = checkJobs > 1 ? { env: isolatedCacheEnv(workerIndex) } : {}; + const result = await execFileAsync(zero, ["check", fixture], options).catch((error) => error); + assert.equal(result.code ?? 0, 0, `${fixture} should check cleanly\n${result.stderr ?? ""}`); +} + +async function checkFailureFixtureParallel(fixture, code, workerIndex) { + const options = checkJobs > 1 ? { env: isolatedCacheEnv(workerIndex) } : {}; + const result = await execFileAsync(zero, ["check", fixture], options).catch((error) => error); + assert.notEqual(result.code, 0); + assert.match(result.stderr, code); +} + +async function fileExists(path) { + try { + await access(path); + return true; + } catch { + return false; + } +} + +function assertRepositoryGraphNativeCheck(body, sourceProjectionState = "clean", options = {}) { + const graphHirToMirUsed = options.graphHirToMirUsed === false ? false : true; + const compilerInputReady = body.targetReadiness?.ok === true && graphHirToMirUsed; + assert.equal(body.graphCompiler.input, "repository-graph-store"); + assert.equal(body.graphCompiler.graphStoreLoaded, true); + assert.equal(body.graphCompiler.sourceProjectionRequiredForCompilerInput, false); + assert.equal(body.graphCompiler.sourceProjectionState, sourceProjectionState); + assert.equal(body.graphCompiler.graphNativeCheckerUsed, true); + assert.equal(body.graphCompiler.graphHirToMirUsed, graphHirToMirUsed); + assert.equal(body.graphCompiler.unsupportedGraphFacts.count, 0); + assert.equal(body.graphCompiler.resolution.ok, true); + assert.equal(body.graphCompiler.resolution.state, "resolved-graph-facts"); + assert.equal(body.graphCompiler.checking.ok, true); + assert.equal(body.graphCompiler.checking.state, "checked-graph-readiness-facts"); + assert.equal(body.graphCompiler.checking.scope, "resolution-package-target-and-graph-mir-readiness"); + assert.equal(body.graphCompiler.checking.semanticDiagnosticsEnforced, false); + assert.equal(body.graphCompiler.checking.semanticDiagnosticsAuthority, "stored-typed-graph-facts"); + assert.equal(body.graphCompiler.checking.authority, "ProgramGraphStore"); + assert.equal(body.graphCompiler.checking.sourceTextAuthority, false); + assert.equal(body.graphCompiler.semanticFacts.state, "typed-facts"); + assert.equal(body.graphCompiler.semanticFacts.ok, true); + const targetReady = body.targetReadiness?.ok === true; + assert.equal(body.graphCompiler.defaultReadiness.compilerInputReady, compilerInputReady); + assert.equal(body.graphCompiler.defaultReadiness.claim, compilerInputReady ? "ready-for-repository-graph-input" : "blocked"); + assert.equal(body.graphCompiler.defaultReadiness.sourceFreeCompile, compilerInputReady); + assert.equal(body.graphCompiler.defaultReadiness.sourceProjectionRequired, false); + assert.equal(body.graphCompiler.defaultReadiness.sourceProjectionState, sourceProjectionState); + assert.equal(body.graphCompiler.defaultReadiness.graphMir.used, graphHirToMirUsed); + assert.equal(Object.hasOwn(body.graphCompiler.defaultReadiness, "fallback"), false); + assert.equal(body.graphCompiler.defaultReadiness.performance.validationInLoad, true); + assert.equal(body.graphCompiler.defaultReadiness.cacheInvalidation.parserArtifactsInKey, false); + assert(body.graphCompiler.defaultReadiness.cacheInvalidation.keyedBy.includes("nodeHashes")); + assert(body.graphCompiler.defaultReadiness.cacheInvalidation.keyedBy.includes("symbolFacts")); + assert(body.graphCompiler.defaultReadiness.cacheInvalidation.keyedBy.includes("modulePaths")); + assert(body.graphCompiler.defaultReadiness.cacheInvalidation.keyedBy.includes("importPaths")); + assert.equal(body.graphCompiler.defaultReadiness.targetReadinessOk, targetReady); + assert.equal(body.compileTime.deterministic, true); + assert.equal(body.targetReadiness.languageOk, true); + assert.equal(body.safetyFacts.schemaVersion, 1); +} + +function assertSourceGraph(body, artifact, moduleIdentity, lowering = "typed-program-graph-mir", canonicalSource = false, sourceProjectionState = undefined) { + assert.equal(body.graph.artifact, artifact); + assert.equal(body.graph.canonicalSource, canonicalSource); + assert.equal(body.graph.moduleIdentity, moduleIdentity); + assert.match(body.graph.graphHash, /^graph:[0-9a-f]{16}$/); + assert.equal(body.graph.lowering, lowering); + if (sourceProjectionState !== undefined) assert.equal(body.graph.sourceProjectionState, sourceProjectionState); +} + +const programGraphParseTreeKeys = new Map(); + +function assertProgramGraphCompilerInput(body, artifact) { + assert(body.compilerCaches.every((cache) => cache.sourceKind === "program-graph" && cache.graphHash === body.graph.graphHash)); + assert(body.compilerCaches.every((cache) => cache.parserArtifactsInKey === false)); + const caches = new Map(body.compilerCaches.map((cache) => [cache.name, cache])); + const assertCacheInputs = (name, includes, excludes = []) => { + const cache = caches.get(name); + assert(cache, `missing compiler cache ${name}`); + for (const key of includes) assert(cache.graphKeyInputs.includes(key), `${name} cache key inputs should include ${key}`); + for (const key of excludes) assert(!cache.graphKeyInputs.includes(key), `${name} cache key inputs should not include ${key}`); + }; + assertCacheInputs("parseTree", ["graphHash", "nodeHashes", "importPaths", "compilerVersion", "packageDependencies"], ["sourceFiles", "targetFacts", "profile"]); + assertCacheInputs("interface", ["graphHash", "modulePaths", "symbolFacts", "importGraph"], ["targetFacts", "profile", "compilerVersion", "packageDependencies"]); + assertCacheInputs("checkedBody", ["graphHash", "importPaths", "targetFacts", "compilerVersion", "packageDependencies"], ["sourceFiles", "profile"]); + assertCacheInputs("specialization", ["graphHash", "importPaths", "targetFacts", "profile", "compilerVersion", "packageDependencies"], ["sourceFiles"]); + if (body.graph.lowering === "mapped-final-mir") { + assertCacheInputs("mappedFinalMir", ["graphHash", "importPaths", "targetFacts", "compilerVersion", "packageDependencies", "emitKind", "backend"], ["sourceFiles", "profile"]); + const mappedMirCache = caches.get("mappedFinalMir"); + assert.match(mappedMirCache.path, /\.zero\/cache\/native\/mir-[0-9a-f]+\.zmir$/); + assert.equal(mappedMirCache.memoryMapped, true); + assert.equal(mappedMirCache.borrowedStorage, true); + assert.equal(mappedMirCache.byteLength > 0, true); + assert.equal(body.incrementalInvalidation.graphInput.mappedFinalMir.path, mappedMirCache.path); + assert.equal(body.incrementalInvalidation.graphInput.mappedFinalMir.memoryMapped, true); + assert.equal(body.incrementalInvalidation.graphInput.mappedFinalMir.borrowedStorage, true); + } + assertCacheInputs("emittedObject", ["graphHash", "importPaths", "targetFacts", "profile", "compilerVersion", "packageDependencies"], ["sourceFiles"]); + const parseTreeCache = caches.get("parseTree"); + assert.equal(parseTreeCache.invalidatesOn, "ProgramGraph input"); + if (programGraphParseTreeKeys.has(artifact)) assert.equal(parseTreeCache.key, programGraphParseTreeKeys.get(artifact)); + else programGraphParseTreeKeys.set(artifact, parseTreeCache.key); + assert.equal(body.incrementalInvalidation.sourceKind, "program-graph"); + assert.equal(body.incrementalInvalidation.graphInput.artifact, artifact); + assert.equal(body.incrementalInvalidation.graphInput.graphHash, body.graph.graphHash); + assert.equal(body.incrementalInvalidation.graphInput.parserArtifactsInKey, false); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("graphHash")); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("nodeHashes")); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("typeFacts")); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("symbolFacts")); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("modulePaths")); + assert(body.incrementalInvalidation.graphInput.keyedBy.includes("importPaths")); + assert.equal(body.incrementalInvalidation.changedInputs.graphArtifact, artifact); + assert.equal(body.incrementalInvalidation.interfaceFingerprints.sourceKind, "program-graph"); + assert.equal(body.incrementalInvalidation.interfaceFingerprints.graphHash, body.graph.graphHash); +} + +function assertLlvmPhiPredecessors(ir) { + const predecessors = new Map(); + const phiEdges = []; + let functionIndex = -1; + let current = "-1:entry"; + const scoped = (label) => `${functionIndex}:${label}`; + for (const line of ir.split("\n")) { + if (line.startsWith("define ")) { + functionIndex++; + current = scoped("entry"); + continue; + } + const label = line.match(/^(L[0-9]+):$/); + if (label) { + current = scoped(label[1]); + continue; + } + const branch = line.match(/^ br (?:label %(L[0-9]+)|i1 [^,]+, label %(L[0-9]+), label %(L[0-9]+))$/); + if (branch) { + for (const target of branch.slice(1).filter(Boolean)) { + const key = scoped(target); + if (!predecessors.has(key)) predecessors.set(key, new Set()); + predecessors.get(key).add(current); + } + continue; + } + if (!line.includes(" = phi ")) continue; + for (const match of line.matchAll(/\[[^\]]+, %(L[0-9]+)\]/g)) { + phiEdges.push({ block: current, predecessor: scoped(match[1]) }); + } + } + for (const edge of phiEdges) { + assert( + predecessors.get(edge.block)?.has(edge.predecessor), + `LLVM phi predecessor ${edge.predecessor} must branch to ${edge.block}`, + ); + } +} + +async function buildLlvmIrFixture(fixture, name) { + const outPath = `${outDir}/${name}.ll`; + const build = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "llvm-ir", + "--backend", + "llvm", + fixture, + "--out", + outPath, + ]); + const body = JSON.parse(build.stdout); + assert.equal(body.emit, "llvm-ir"); + assert.equal(body.compiler, "zero-c-llvm-ir"); + assert.equal(body.generatedCBytes, 0); + return readFile(outPath, "utf8"); +} + +async function assertLlvmHostExitCode(fixture, name, expectedCode) { + const readiness = await execFileAsync(zero, ["check", "--json", "--backend", "llvm", fixture]).catch((error) => error); + if (readiness.code) return; + const readinessBody = JSON.parse(readiness.stdout); + if (!readinessBody.targetReadiness?.ok) return; + + const out = `${outDir}/${name}`; + const build = await execFileAsync(zero, ["build", "--json", "--backend", "llvm", fixture, "--out", out]); + const body = JSON.parse(build.stdout); + assert.equal(body.emit, "exe"); + assert.equal(body.objectBackend.backendFamily, "llvm"); + const run = await execFileAsync(out, []).catch((error) => error); + assert.equal(run.code ?? 0, expectedCode); + assert.equal(run.signal ?? null, null); +} + +async function assertBoundsTrap(fixture, name) { + const out = `${outDir}/${name}`; + const build = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", "linux-musl-x64", fixture, "--out", out]).catch((error) => error); + if (build.code) { + const body = JSON.parse(build.stdout); + assert.equal(body.diagnostics?.[0]?.code, "BLD004"); + return; + } + const body = JSON.parse(build.stdout); + assert.equal(body.generatedCBytes, 0); + if (!canRunLinuxMuslX64) return; + const failedRun = await execFileAsync(out, []).catch((error) => error); + assert.notEqual(failedRun.code ?? (failedRun.signal ? 1 : 0), 0); + if (failedRun.stderr) assert.match(failedRun.stderr, /zero bounds check failed|trap: index out of bounds/); +} + +async function assertDirectRuntimeOrUnsupported(fixture, name, expected) { + const out = `${outDir}/${name}`; + const build = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", "linux-musl-x64", fixture, "--out", out]).catch((error) => error); + if (build.code) { + const body = JSON.parse(build.stdout); + assert.equal(body.diagnostics?.[0]?.code, "BLD004"); + return; + } + + const body = JSON.parse(build.stdout); + assert.equal(body.generatedCBytes, 0); + assert.equal(body.legacy, false); + if (!canRunLinuxMuslX64) return; + const run = await execFileAsync(out, expected.args ?? [], expected.env ? { env: { ...process.env, ...expected.env } } : {}).catch((error) => error); + if (run.code || run.signal) return; + if (expected.stdout instanceof RegExp) assert.match(run.stdout, expected.stdout); + else assert.equal(run.stdout, expected.stdout); + if (expected.stderr !== undefined) assert.equal(run.stderr, expected.stderr); + if (expected.file) assert.equal(await readFile(`${outDir}/${expected.file.name}`, "utf8"), expected.file.text); +} + +async function assertDirectRuntimeRequired(fixture, name, expected) { + const target = runnableDirectTarget ?? "linux-musl-x64"; + const out = `${outDir}/${name}`; + const build = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", target, fixture, "--out", out]); + const body = JSON.parse(build.stdout); + assert.equal(body.generatedCBytes, 0); + assert.equal(body.legacy, false); + if (!runnableDirectTarget) return; + const run = await execFileAsync(out, expected.args ?? [], expected.env ? { env: { ...process.env, ...expected.env } } : {}); + if (expected.stdout instanceof RegExp) assert.match(run.stdout, expected.stdout); + else assert.equal(run.stdout, expected.stdout); + if (expected.stderr !== undefined) assert.equal(run.stderr, expected.stderr); +} + +async function assertCommonRuntimeOrUnsupported(fixture, name, expected) { + const target = runnableDirectTarget ?? "linux-musl-x64"; + const out = `${outDir}/${name}`; + const build = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", target, fixture, "--out", out]).catch((error) => error); + if (build.code) { + const body = JSON.parse(build.stdout); + assert.equal(body.diagnostics?.[0]?.code, "BLD004"); + return; + } + + const body = JSON.parse(build.stdout); + assert.equal(body.generatedCBytes, 0); + assert.equal(body.legacy, false); + if (!runnableDirectTarget) return; + const run = await execFileAsync(out, expected.args ?? [], expected.env ? { env: { ...process.env, ...expected.env } } : {}).catch((error) => error); + assert.equal(run.code ?? 0, 0); + assert.equal(run.signal ?? null, null); + if (expected.stdout instanceof RegExp) assert.match(run.stdout, expected.stdout); + else assert.equal(run.stdout, expected.stdout); + if (expected.stderr !== undefined) assert.equal(run.stderr, expected.stderr); + if (expected.file) assert.equal(await readFile(`${outDir}/${expected.file.name}`, "utf8"), expected.file.text); +} + +async function assertCheckTimeoutOrDiagnostic(fixture, expectedCodes) { + const result = await execFileAsync(zero, ["check", "--json", fixture], { timeout: checkTimeoutMs }).catch((error) => error); + if (result.killed || result.signal) { + assert.equal(result.killed, true); + return { timedOut: true }; + } + assert.notEqual(result.code, 0); + const body = JSON.parse(result.stdout); + const code = body.diagnostics?.[0]?.code; + assert(expectedCodes.includes(code), `expected one of ${expectedCodes.join(", ")}, got ${code}`); + return { timedOut: false, code }; +} + +async function assertElf64Object(path, exportedName) { + const bytes = await readFile(path); + assert.equal(bytes[0], 0x7f); + assert.equal(bytes[1], 0x45); + assert.equal(bytes[2], 0x4c); + assert.equal(bytes[3], 0x46); + assert.equal(bytes[4], 2); + assert.equal(bytes[5], 1); + assert.equal(bytes.readUInt16LE(16), 1); + assert.equal(bytes.readUInt16LE(18), 62); + const shoff = Number(bytes.readBigUInt64LE(40)); + const shentsize = bytes.readUInt16LE(58); + const shnum = bytes.readUInt16LE(60); + const shstrndx = bytes.readUInt16LE(62); + const cstr = (offset) => { + let end = offset; + while (end < bytes.length && bytes[end] !== 0) end++; + return bytes.toString("utf8", offset, end); + }; + const shstrHeader = shoff + shstrndx * shentsize; + const shstrOffset = Number(bytes.readBigUInt64LE(shstrHeader + 24)); + const sections = new Map(); + for (let i = 0; i < shnum; i++) { + const header = shoff + i * shentsize; + sections.set(cstr(shstrOffset + bytes.readUInt32LE(header)), { + index: i, + offset: Number(bytes.readBigUInt64LE(header + 24)), + size: Number(bytes.readBigUInt64LE(header + 32)), + link: bytes.readUInt32LE(header + 40), + entsize: Number(bytes.readBigUInt64LE(header + 56)), + }); + } + assert(sections.get(".text")?.size > 0); + const symtab = sections.get(".symtab"); + assert(symtab); + const strtab = [...sections.values()].find((section) => section.index === symtab.link); + assert(strtab); + let sawExport = false; + for (let offset = symtab.offset; offset < symtab.offset + symtab.size; offset += symtab.entsize) { + const name = cstr(strtab.offset + bytes.readUInt32LE(offset)); + const info = bytes[offset + 4]; + const shndx = bytes.readUInt16LE(offset + 6); + const size = Number(bytes.readBigUInt64LE(offset + 16)); + if (name === exportedName && info === 0x12 && shndx === sections.get(".text").index && size > 0) sawExport = true; + } + assert(sawExport); + return { bytes, sections }; +} + +async function assertElfAarch64Object(path, exportedName) { + const bytes = await readFile(path); + assert.equal(bytes[0], 0x7f); + assert.equal(bytes[1], 0x45); + assert.equal(bytes[2], 0x4c); + assert.equal(bytes[3], 0x46); + assert.equal(bytes[4], 2); + assert.equal(bytes[5], 1); + assert.equal(bytes.readUInt16LE(16), 1); + assert.equal(bytes.readUInt16LE(18), 183); + assert(bytes.includes(Buffer.concat([Buffer.from(exportedName), Buffer.from([0])]))); + return bytes; +} + +function hasAarch64Instruction(bytes, expected) { + for (let offset = 0; offset + 4 <= bytes.length; offset++) { + if (bytes.readUInt32LE(offset) === expected) return true; + } + return false; +} + +function countAarch64InstructionSequence(bytes, expected) { + let count = 0; + for (let offset = 0; offset + expected.length * 4 <= bytes.length; offset++) { + let matched = true; + for (let index = 0; index < expected.length; index++) { + if (bytes.readUInt32LE(offset + index * 4) !== expected[index]) { + matched = false; + break; + } + } + if (matched) count++; + } + return count; +} + +function aarch64SpLoad(reg, offset, wide) { + const scale = wide ? 8 : 4; + assert.equal(offset % scale, 0); + return ((wide ? 0xf9400000 : 0xb9400000) + ((offset / scale) << 10) + (31 << 5) + reg) >>> 0; +} + +function aarch64ByteViewReloadSequence(count) { + const sequence = []; + for (let index = 0; index < count; index++) { + sequence.push(aarch64SpLoad(index * 2, index * 16, true)); + sequence.push(aarch64SpLoad(index * 2 + 1, index * 16 + 8, false)); + } + return sequence; +} + +function hasAarch64CondBranch(bytes, cond) { + for (let offset = 0; offset + 4 <= bytes.length; offset++) { + const instruction = bytes.readUInt32LE(offset); + if ((instruction & 0xff000010) === 0x54000000 && (instruction & 0xf) === cond) return true; + } + return false; +} + +async function assertElf64Executable(path) { + const bytes = await readFile(path); + assert.equal(bytes[0], 0x7f); + assert.equal(bytes[1], 0x45); + assert.equal(bytes[2], 0x4c); + assert.equal(bytes[3], 0x46); + assert.equal(bytes[4], 2); + assert.equal(bytes[5], 1); + assert.equal(bytes.readUInt16LE(16), 2); + assert.equal(bytes.readUInt16LE(18), 62); + assert(Number(bytes.readBigUInt64LE(24)) > 0); + assert.equal(Number(bytes.readBigUInt64LE(32)), 64); + assert.equal(bytes.readUInt16LE(54), 56); + assert.equal(bytes.readUInt16LE(56), 1); + const phoff = Number(bytes.readBigUInt64LE(32)); + assert.equal(bytes.readUInt32LE(phoff), 1); + assert.equal(bytes.readUInt32LE(phoff + 4), 5); + assert(Number(bytes.readBigUInt64LE(phoff + 32)) > 0); + assert.equal(Number(bytes.readBigUInt64LE(phoff + 32)), Number(bytes.readBigUInt64LE(phoff + 40))); +} + +async function assertElfAarch64Executable(path) { + const bytes = await readFile(path); + assert.equal(bytes[0], 0x7f); + assert.equal(bytes[1], 0x45); + assert.equal(bytes[2], 0x4c); + assert.equal(bytes[3], 0x46); + assert.equal(bytes[4], 2); + assert.equal(bytes[5], 1); + assert.equal(bytes.readUInt16LE(16), 2); + assert.equal(bytes.readUInt16LE(18), 183); + assert.equal(bytes.readUInt16LE(54), 56); + assert.equal(bytes.readUInt16LE(56), 1); + assert(bytes.includes(Buffer.from([0x40, 0x05, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6]))); + assert(bytes.includes(Buffer.from([0xa8, 0x0b, 0x80, 0xd2, 0x01, 0x00, 0x00, 0xd4]))); +} + +async function assertMachOArm64Object(path, exportedName) { + const bytes = await readFile(path); + assert.equal(bytes.readUInt32LE(0), 0xfeedfacf); + assert.equal(bytes.readUInt32LE(4), 0x0100000c); + assert.equal(bytes.readUInt32LE(12), 1); + assert(bytes.includes(Buffer.concat([Buffer.from(`_${exportedName}`), Buffer.from([0])]))); + return bytes; +} + +async function assertMachOX64Object(path, exportedName) { + const bytes = await readFile(path); + assert.equal(bytes.readUInt32LE(0), 0xfeedfacf); + assert.equal(bytes.readUInt32LE(4), 0x01000007); + assert.equal(bytes.readUInt32LE(12), 1); + assert(bytes.includes(Buffer.concat([Buffer.from(`_${exportedName}`), Buffer.from([0])]))); + return bytes; +} + +function assertX64SliceBoundsTrapBytes(bytes) { + assert(bytes.includes(Buffer.from([0x0f, 0x86]))); + assert(bytes.includes(Buffer.from([0x0f, 0x0b]))); +} + +function assertX64U16RecordFieldBytes(bytes) { + assert(bytes.includes(Buffer.from([0x0f, 0xb7]))); + assert(bytes.includes(Buffer.from([0x66, 0x89]))); +} + +function isRexW(byte) { + return (byte & 0xf8) === 0x48; +} + +function hasX64MovMemoryToR64(bytes) { + for (let i = 0; i + 2 < bytes.length; i++) { + if (!isRexW(bytes[i]) || bytes[i + 1] !== 0x8b) continue; + const mod = bytes[i + 2] >> 6; + if (mod !== 3) return true; + } + return false; +} + +function hasX64MovR64ToMemory(bytes) { + for (let i = 0; i + 2 < bytes.length; i++) { + if (!isRexW(bytes[i]) || bytes[i + 1] !== 0x89) continue; + const mod = bytes[i + 2] >> 6; + if (mod !== 3) return true; + } + return false; +} + +function hasX64CmpR64(bytes) { + for (let i = 0; i + 2 < bytes.length; i++) { + if (!isRexW(bytes[i]) || bytes[i + 1] !== 0x39) continue; + const mod = bytes[i + 2] >> 6; + if (mod === 3) return true; + } + return false; +} + +async function assertMachOArm64Executable(path) { + const bytes = await readFile(path); + assert.equal(bytes.readUInt32LE(0), 0xfeedfacf); + assert.equal(bytes.readUInt32LE(4), 0x0100000c); + assert.equal(bytes.readUInt32LE(12), 2); + const ncmds = bytes.readUInt32LE(16); + let sawUuid = false; + for (let offset = 32, i = 0; i < ncmds; i++) { + const cmd = bytes.readUInt32LE(offset); + const cmdsize = bytes.readUInt32LE(offset + 4); + assert(cmdsize >= 8); + assert(offset + cmdsize <= bytes.length); + if (cmd === 0x1b) { + assert.equal(cmdsize, 24); + assert(!bytes.subarray(offset + 8, offset + 24).every((byte) => byte === 0)); + sawUuid = true; + } + offset += cmdsize; + } + assert(sawUuid); + assert(bytes.includes(Buffer.from("/usr/lib/dyld"))); + assert(bytes.includes(Buffer.from("/usr/lib/libSystem.B.dylib"))); + assert(bytes.includes(Buffer.from("zero-direct"))); + return bytes; +} + +async function assertCoffX64Object(path, exportedName) { + const bytes = await readFile(path); + assert.equal(bytes.readUInt16LE(0), 0x8664); + assert(bytes.readUInt16LE(2) >= 1); + assert(bytes.readUInt32LE(8) > 0); + assert(bytes.includes(Buffer.concat([Buffer.from(exportedName), Buffer.from([0])]))); + return bytes; +} + +async function assertPeCoffX64Executable(path) { + const bytes = await readFile(path); + assert.equal(bytes[0], 0x4d); + assert.equal(bytes[1], 0x5a); + const peOffset = bytes.readUInt32LE(0x3c); + assert.equal(bytes.toString("ascii", peOffset, peOffset + 4), "PE\u0000\u0000"); + assert.equal(bytes.readUInt16LE(peOffset + 4), 0x8664); + assert.equal(bytes.readUInt16LE(peOffset + 24), 0x20b); + assert.equal(bytes.readUInt16LE(peOffset + 92), 3); + assert(bytes.includes(Buffer.from("KERNEL32.dll"))); + assert(bytes.includes(Buffer.from("ExitProcess"))); + assert(bytes.includes(Buffer.from("WriteFile"))); + return bytes; +} + +const passCheckFixtures = [ + "conformance/run/pass/hello.0", + "conformance/native/pass/params.0", + "conformance/native/pass/shape.0", + "conformance/native/pass/mutref-shape-param.0", + "conformance/native/pass/mutref-shape-param-nested.0", + "conformance/native/pass/primitive-stdlib.0", + "conformance/native/pass/break-continue.0", + "conformance/native/pass/nested-break-continue.0", + "conformance/native/pass/untyped-literal-adoption.0", + "conformance/native/pass/for-range.0", + "conformance/native/pass/choice-payload-reference-return.0", + "conformance/native/pass/choice-match-payload-reference-origin.0", + "conformance/native/pass/choice-match-payload-return-origin.0", + "conformance/native/pass/null-maybe.0", + "conformance/native/pass/maybe-local-null-init-return.0", + "conformance/native/pass/meta-typed-target-type.0", + "conformance/native/pass/std-args.0", + "conformance/native/pass/std-env.0", + "conformance/native/pass/std-hosted-cli.0", + "conformance/native/pass/std-fs.0", + "conformance/native/pass/std-fs-bytes.0", + "conformance/native/pass/std-fs-read-chunks.0", + "conformance/native/pass/frame-large-locals.0", + "conformance/native/pass/frame-limit-boundary.0", + "conformance/native/pass/frame-split-helpers.0", + "conformance/native/pass/fixed-buf-alloc-local.0", + "conformance/native/pass/std-fs-resource.0", + "conformance/native/pass/std-fs-readall.0", + "conformance/native/pass/std-fs-polish.0", + "conformance/native/pass/std-fs-breadth.0", + "conformance/native/pass/std-fs-file-helpers.0", + "conformance/native/pass/std-pty-child.0", + "conformance/native/pass/std-math-breadth.0", + "conformance/native/pass/std-numeric-random-time.0", + "conformance/native/pass/std-regex.0", + "conformance/native/pass/std-unicode.0", + "conformance/native/pass/std-inet.0", + "conformance/native/pass/std-time-rfc3339.0", + "conformance/native/pass/std-io-lines.0", + "conformance/native/pass/std-path-io-breadth.0", + "conformance/native/pass/std-str-breadth.0", + "conformance/native/pass/std-testing-log.0", + "conformance/native/pass/std-term-ansi.0", + "conformance/native/pass/std-testing-helpers-test.0", + "conformance/native/pass/std-path-helper-name-collision.0", + "conformance/native/pass/std-net-http-breadth.0", + "conformance/native/pass/std-http-metadata-neutral.0", + "conformance/native/pass/std-http-fetch.0", + "conformance/native/pass/std-http-errors.0", + "conformance/native/pass/std-http-response-helpers.0", + "conformance/native/pass/std-http-text-html-response-helpers.0", + "conformance/native/pass/std-http-redirect-response-helpers.0", + "conformance/native/pass/std-http-api-helpers.0", + "conformance/native/pass/std-http-cors-helpers.0", + "conformance/native/pass/std-http-auth-helpers.0", + "conformance/native/pass/std-data-formats.0", + "conformance/native/pass/std-csv.0", + "conformance/native/pass/std-diag.0", + "conformance/native/pass/std-codec-json-url.0", + "conformance/native/pass/std-json-cursors.0", + "conformance/native/pass/std-json-bytes.0", + "conformance/native/pass/std-json-inline-bytes.0", + "conformance/native/pass/std-json-duplicate-keys.0", + "conformance/native/pass/std-json-allocator-capacity.0", + "conformance/native/pass/std-platform-basics.0", + "conformance/native/pass/std-mem-arrays.0", + "conformance/native/pass/std-mem-generic-items.0", + "conformance/native/pass/std-mem-field-items.0", + "conformance/native/pass/std-mem-byte-field-copy.0", + "conformance/native/pass/std-mem-bool-copy-items.0", + "conformance/native/pass/std-mem-u64-contains.0", + "conformance/native/pass/std-mem-u64-copy-items.0", + "conformance/native/pass/std-mem-local-u64-copy-items.0", + "conformance/native/pass/array-repeat-literal.0", + "conformance/native/pass/array-repeat-record-field.0", + "conformance/native/pass/integer-widths.0", + "conformance/native/pass/std-codec-widths.0", + "conformance/native/pass/std-crypto-hmac32.0", + "conformance/native/pass/std-crypto-sha256.0", + "conformance/native/pass/parse-integers.0", + "conformance/native/pass/explicit-casts.0", + "conformance/native/pass/float-char-casts.0", + "conformance/native/pass/radix-suffix-literals.0", + "conformance/native/pass/char-literals.0", + "conformance/native/pass/float-primitives.0", + "conformance/native/pass/wrapping-saturating-arithmetic.0", + "conformance/native/pass/maybe-guard-branch-restore.0", + "conformance/native/pass/maybe-guard-negated-conjunction.0", + "conformance/native/pass/maybe-guard-scalar-match.0", + "conformance/native/pass/maybe-guard-short-circuit-match.0", + "conformance/native/pass/maybe-guard-static-index-literals.0", + "conformance/native/pass/maybe-guard-variant-match-after.0", + "conformance/native/pass/maybe-guard-variant-guard-side-effect.0", + "conformance/native/pass/match-scalar-guards.0", + "conformance/native/pass/indexing-primitives.0", + "conformance/native/pass/checked-bounds-get.0", + "conformance/native/pass/check-maybe-fallibility.0", + "conformance/native/pass/fallibility-error-sets.0", + "conformance/native/pass/checked-fallible-wrapper.0", + "conformance/native/pass/checked-fallible-static-method.0", + "conformance/native/pass/checked-fallible-interface-method.0", + "conformance/native/pass/fallibility-check-value.0", + "conformance/native/pass/rescue-check.0", + "conformance/native/pass/std-fs-fallible.0", + "conformance/native/pass/std-fs-fallible-resources.0", + "conformance/native/pass/std-cli-helpers.0", + "conformance/native/pass/std-mem-copy-fill.0", + "conformance/native/pass/const-layout.0", + "conformance/native/pass/c-abi-export.0", + "conformance/native/pass/range-slices.0", + "conformance/native/pass/byte-view-call-single-eval.0", + "conformance/native/pass/generic-spans.0", + "conformance/native/pass/open-ended-slices.0", + "conformance/native/pass/string-slices.0", + "conformance/native/pass/string-param-span-slice.0", + "conformance/native/pass/coff-dynamic-byte-slice.0", + "conformance/native/pass/macho-large-byte-slice-blocked.0", + "conformance/native/pass/macho-nested-call-scratch-blocked.0", + "conformance/native/pass/macho-open-byte-slice.0", + "conformance/native/pass/string-byte-ergonomics.0", + "conformance/native/pass/indexed-mutation.0", + "conformance/native/pass/dynamic-indexed-store-scratch.0", + "conformance/native/pass/nested-lvalues.0", + "conformance/native/pass/mutable-spans.0", + "conformance/native/pass/mutref-indexed-lvalues.0", + "conformance/native/pass/generic-mem.0", + "conformance/native/pass/generic-function-basic.0", + "conformance/native/pass/generic-nested-calls.0", + "conformance/native/pass/generic-specialization-reuse.0", + "conformance/native/pass/generic-multi-specialization.0", + "conformance/native/pass/generic-inferred-specialized-call.0", + "conformance/native/pass/generic-nested-local-specialization.0", + "conformance/native/pass/generic-static-array-specialization.0", + "conformance/native/pass/generic-static-forwarded-array-specialization.0", + "conformance/native/pass/generic-shape-basic.0", + "conformance/native/pass/generic-shape-multi.0", + "conformance/native/pass/generic-shape-methods.0", + "conformance/native/pass/generic-shape-nested-defaults-alias.0", + "conformance/native/pass/generic-constructor-expected.0", + "conformance/native/pass/generic-expected-return.0", + "conformance/native/pass/generic-literals-arrays.0", + "conformance/native/pass/receiver-method-calls.0", + "conformance/native/pass/constructors-defaults.0", + "conformance/native/pass/static-value-params.0", + "conformance/native/pass/static-value-types-const-expr.0", + "conformance/native/pass/compile-time-v1.0", + "conformance/native/pass/static-interface-basic.0", + "conformance/native/pass/static-interface-mutref.0", + "conformance/native/pass/static-interface-static-param.0", + "conformance/native/pass/top-level-const.0", + "conformance/native/pass/const-arithmetic.0", + "conformance/native/pass/type-alias-basic.0", + "conformance/native/pass/static-method-namespace.0", + "conformance/native/pass/c-import-type-shadowing.0", + "conformance/native/pass/c-import-alias-later-local.0", + "conformance/native/pass/memory-types.0", + "conformance/native/pass/owned-transfer.0", + "conformance/native/pass/owned-field-move-return-branch.0", + "conformance/native/pass/owned-field-reassignment-after-move.0", + "conformance/native/pass/owned-array-static-index-reassignment.0", + "conformance/native/pass/owned-mutspan-alias-reassignment-after-move.0", + "conformance/native/pass/maybe-owned-null-repeat.0", + "conformance/native/pass/owned-drop-cleanup.0", + "conformance/native/pass/owned-drop-move-suppressed.0", + "conformance/native/pass/borrow-primitives.0", + "conformance/native/pass/borrow-field-independent-assignment.0", + "conformance/native/pass/borrow-aggregate-reassignment-same-origin.0", + "conformance/native/pass/receiver-return-field-origin-clears-other-field.0", + "conformance/native/pass/borrow-return-param-field-subpath.0", + "conformance/native/pass/borrow-return-explicit-ref-field-origin.0", + "conformance/native/pass/borrow-unreachable-return-origin.0", + "conformance/native/pass/borrow-unreachable-loop-return-origin.0", + "conformance/native/pass/borrow-unreachable-if-return-origin.0", + "conformance/native/pass/borrow-unreachable-match-return-origin.0", + "conformance/native/pass/borrow-unreachable-raise-return-origin.0", + "conformance/native/pass/borrow-return-ref-alias-field.0", + "conformance/native/pass/assignment-rhs-side-effect-clears-old-origin.0", + "conformance/native/pass/shadowed-mutref-side-effect-clears-old-origin.0", + "conformance/native/pass/mutref-alias-assignment-clears-old-origin.0", + "conformance/native/pass/mutref-alias-assignment-same-origin.0", + "conformance/native/pass/function-mutref-reference-store.0", + "conformance/native/pass/generic-mutref-reference-store.0", + "conformance/native/pass/receiver-method-reference-store.0", + "conformance/native/pass/static-interface-mutref-reference-store.0", + "conformance/native/pass/static-interface-return-reference-origin.0", + "conformance/native/pass/borrow-return-param-ref.0", + "conformance/native/pass/borrow-assignment-same-origin.0", + "conformance/native/pass/borrow-shadowed-root-reassignment.0", + "conformance/native/pass/borrow-branch-reassignment.0", + "conformance/native/pass/branch-overwrite-away-reference-origin.0", + "conformance/native/pass/shape-field-reference-reassignment-clears-origin.0", + "conformance/native/pass/index-reference-assignment-clears-origin.0", + "conformance/native/pass/allocator-primitives.0", + "conformance/native/pass/std-mem-arena.0", + "conformance/native/pass/std-mem-collections.0", + "conformance/native/pass/std-collections-algorithms.0", + "conformance/native/pass/std-collections-u8.0", + "conformance/native/pass/std-collections-mutspan-memory.0", + "conformance/native/pass/std-collections-usize-memory.0", + "conformance/native/pass/std-collections-query-memory.0", + "conformance/native/pass/std-search-sort-widths.0", + "conformance/native/pass/owned-byte-buffer.0", + "conformance/check/pass/generic-function-basic.0", + "conformance/check/pass/generic-array-inference.0", + "conformance/check/pass/generic-static-explicit-shadowing.0", + "conformance/check/pass/generic-static-forwarding.0", + "conformance/check/pass/generic-static-method-forwarding.0", + "conformance/check/pass/generic-static-return-substitution.0", + "conformance/check/pass/generic-static-wrapper-type-collision.0", + "conformance/check/pass/static-interface-return-substitution.0", + "conformance/check/pass/generic-untyped-static-const-inference.0", + "conformance/check/pass/generic-const-shadowing.0", + "conformance/check/pass/generic-const-type-name-collision.0", + "conformance/check/pass/generic-mixed-const-type-name-collision.0", + "conformance/check/pass/generic-method-outer-param-inference.0", + "conformance/native/pass/generic-nested-calls.0", + "conformance/native/pass/generic-specialization-reuse.0", + "conformance/native/pass/generic-multi-specialization.0", + "conformance/native/pass/generic-inferred-specialized-call.0", + "conformance/native/pass/generic-nested-local-specialization.0", + "conformance/native/pass/generic-static-array-specialization.0", + "conformance/native/pass/generic-static-forwarded-array-specialization.0", + "conformance/check/pass/generic-shape-basic.0", + "conformance/check/pass/generic-shape-multi.0", + "conformance/check/pass/generic-shape-methods.0", + "conformance/native/pass/generic-constructor-expected.0", + "conformance/native/pass/generic-literals-arrays.0", + "conformance/check/pass/receiver-method-calls.0", + "conformance/check/pass/shape-field-defaults.0", + "conformance/check/pass/static-value-params.0", + "conformance/check/pass/static-interface-basic.0", + "conformance/check/pass/call-resolution-edge-cases.0", + "conformance/native/pass/static-interface-mutref.0", + "conformance/native/pass/static-interface-static-param.0", + "conformance/check/pass/top-level-const.0", + "conformance/check/pass/const-arithmetic.0", + "conformance/check/pass/type-alias-basic.0", + "conformance/check/pass/static-method-namespace.0", + "conformance/check/pass/fmt-core-usability.0", + "conformance/check/pass/c-header-import.0", + "conformance/check/pass/c-import-local-shadowing.0", + "conformance/check/pass/match-fallback.0", + "conformance/check/pass/memory-types.0", + "conformance/check/pass/std-mem-field-items.0", + "conformance/check/pass/std-mem-field-slice.0", + "conformance/check/pass/fixed-array-length-match.0", + "conformance/check/pass/imports", + "examples/memory-package", + "examples/const-arithmetic.0", + "examples/generic-pair.0", + "examples/fixed-vec.0", + "examples/static-value-params.0", + "examples/compile-time-v1.0", + "examples/type-alias.0", + "examples/static-method.0", + "examples/static-interface.0", + "examples/ownership-cleanup.0", +]; +await mapLimit(passCheckFixtures, checkJobs, (fixture, _index, workerIndex) => checkFixtureParallel(fixture, workerIndex)); + +const stdSortMergeOverlap = await writeImportFailureFixture(`${outDir}/std-sort-merge-overlap.0`, `pub fn main() -> Void { + var values: [5]i32 = [1, 3, 5, 2, 4] + let written: usize = std.sort.mergeSortedI32(values, std.mem.prefix(values, 3_usize), std.mem.dropPrefix(values, 3_usize)) +} +`); +assert.equal(stdSortMergeOverlap.diagnostics[0].code, "STD003"); +assert.match(stdSortMergeOverlap.diagnostics[0].message, /std\.sort\.mergeSorted source must not overlap destination storage/); + +const stdMemStartsWithMismatch = await writeImportFailureFixture(`${outDir}/std-mem-startswith-mismatch.0`, `pub fn main() -> Void { + let left_values: [2]i32 = [1, 2] + let right_values: [2]u32 = [1_u32, 2_u32] + let left: Span = left_values + let right: Span = right_values + let ok: Bool = std.mem.startsWith(left, right) +} +`); +assert.equal(stdMemStartsWithMismatch.diagnostics[0].code, "STD003"); +assert.match(stdMemStartsWithMismatch.diagnostics[0].message, /std\.mem\.startsWith span element types must match/); + +const stdMemStartsWithUnsupported = await writeImportFailureFixture(`${outDir}/std-mem-startswith-unsupported.0`, `type Point { + x: i32, +} + +pub fn main() -> Void { + let points: [1]Point = [Point { x: 1 }] + let span: Span = points + let ok: Bool = std.mem.startsWith(span, span) +} +`); +assert.equal(stdMemStartsWithUnsupported.diagnostics[0].code, "STD003"); +assert.match(stdMemStartsWithUnsupported.diagnostics[0].message, /std\.mem\.startsWith item element type is not supported/); + +// Fixtures using the gated typed graph MIR constructs fail check with the +// same BLD004 diagnostics zero build reports for their graph stores. +const gateBlockedCheckFixtures = [ + "conformance/check/pass/package", + "conformance/native/pass/variants-defer-stdlib.0", + "conformance/native/pass/defer-return-raise-nested.0", + "conformance/native/pass/payload-match.0", + "conformance/native/pass/match-payload-binding.0", + "conformance/native/pass/match-choice-fallback.0", + "conformance/native/pass/maybe-error-flow.0", + "conformance/native/pass/match-fallback.0", + "conformance/check/pass/call-resolution-inspection.0", + "conformance/check/pass/checker-type-forms.0", +]; +await mapLimit(gateBlockedCheckFixtures, checkJobs, (fixture, _index, workerIndex) => checkFailureFixtureParallel(fixture, /BLD004/, workerIndex)); + +const checkJsonSuccess = await execFileAsync(zero, ["check", "--json", "conformance/native/pass/explicit-casts.0"]); +const checkJsonSuccessBody = JSON.parse(checkJsonSuccess.stdout); +assert.equal(checkJsonSuccessBody.ok, true); +assert.equal(checkJsonSuccessBody.diagnostics.length, 0); +assert.equal(checkJsonSuccessBody.artifact, "conformance/native/pass/explicit-casts.graph"); +assert.equal(checkJsonSuccessBody.canonicalSource, false); +assert.equal(checkJsonSuccessBody.moduleIdentity, "module:explicit-casts"); +assert.match(checkJsonSuccessBody.graphHash, /^graph:[0-9a-f]{16}$/); +assert.equal(checkJsonSuccessBody.check.phase, "typecheck"); +assert.equal(checkJsonSuccessBody.check.lowering, "graph-native-check"); +assert.equal(checkJsonSuccessBody.graphCompiler.input, "program-graph-artifact"); +assert.equal(checkJsonSuccessBody.graphCompiler.graphNativeCheckerUsed, true); +assert.equal(checkJsonSuccessBody.graphCompiler.graphHirToMirUsed, true); + +const stdDiagGraphCheck = await execFileAsync(zero, ["check", "--json", "conformance/native/pass/std-diag.graph"]); +const stdDiagGraphCheckBody = JSON.parse(stdDiagGraphCheck.stdout); +assert.equal(stdDiagGraphCheckBody.ok, true); +assert.equal(stdDiagGraphCheckBody.targetReadiness.ok, true); +assert.equal(stdDiagGraphCheckBody.targetReadiness.buildable, true); +assert.equal(stdDiagGraphCheckBody.targetReadiness.diagnostics.length, 0); + +const agentSurfaceClassification = JSON.parse(await readFile("conformance/agent-surface/classification.json", "utf8")); +assert.equal(agentSurfaceClassification.schema, 1); +assert.deepEqual(agentSurfaceClassification.fixtures.map((item) => item.id), [ + "interface-method-generic-binding", + "direct-generic-recursion", + "direct-generic-specialization-name-collision", + "stdlib-signature-parity", + "owned-drop-direct-backend-unsupported", +]); + +const agentSurfaceInterfaceMethodGeneric = await execFileAsync(zero, ["check", "--json", "conformance/agent-surface/fixtures/interface-method-generic-binding.0"]); +const agentSurfaceInterfaceMethodGenericBody = JSON.parse(agentSurfaceInterfaceMethodGeneric.stdout); +assert.equal(agentSurfaceInterfaceMethodGenericBody.ok, true); +assert.equal(agentSurfaceInterfaceMethodGenericBody.diagnostics.length, 0); + +const agentSurfaceDirectGenericCheck = await execFileAsync(zero, ["check", "--json", "conformance/agent-surface/fixtures/direct-generic-recursion.0"]); +const agentSurfaceDirectGenericCheckBody = JSON.parse(agentSurfaceDirectGenericCheck.stdout); +assert.equal(agentSurfaceDirectGenericCheckBody.ok, true); +assert.equal(agentSurfaceDirectGenericCheckBody.diagnostics.length, 0); +const agentSurfaceDirectGenericReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "linux-musl-x64", + "conformance/agent-surface/fixtures/direct-generic-recursion.0", +]); +const agentSurfaceDirectGenericReadinessBody = JSON.parse(agentSurfaceDirectGenericReadiness.stdout); +assert.equal(agentSurfaceDirectGenericReadinessBody.ok, true); +assert.equal(agentSurfaceDirectGenericReadinessBody.targetReadiness.ok, true); +assert.equal(agentSurfaceDirectGenericReadinessBody.targetReadiness.buildable, true); +assert.equal(agentSurfaceDirectGenericReadinessBody.targetReadiness.diagnostics.length, 0); + +const agentSurfaceDirectGenericCollisionReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "linux-musl-x64", + "conformance/agent-surface/fixtures/direct-generic-specialization-name-collision.0", +]); +const agentSurfaceDirectGenericCollisionReadinessBody = JSON.parse(agentSurfaceDirectGenericCollisionReadiness.stdout); +assert.equal(agentSurfaceDirectGenericCollisionReadinessBody.ok, true); +assert.equal(agentSurfaceDirectGenericCollisionReadinessBody.targetReadiness.ok, false); +assert.equal(agentSurfaceDirectGenericCollisionReadinessBody.targetReadiness.buildable, false); +assert.equal(agentSurfaceDirectGenericCollisionReadinessBody.targetReadiness.diagnostics[0].code, "BLD004"); +assert.match(agentSurfaceDirectGenericCollisionReadinessBody.targetReadiness.diagnostics[0].message, /specialization name collides/); + +const compilerMetrics = await execFileAsync("node", ["--experimental-strip-types", "--disable-warning=ExperimentalWarning", "scripts/compiler-metrics.mts"]); +const compilerMetricsBody = JSON.parse(compilerMetrics.stdout); +assert.equal(compilerMetricsBody.schema, 1); +assert(compilerMetricsBody.files["native/zero-c/src/checker.c"].lines > 0); +assert.equal(compilerMetricsBody.files["native/zero-c/src/fs.c"].shellCalls, 0); +assert.equal(compilerMetricsBody.files["native/zero-c/src/main.c"].shellCalls, 0); +for (const [path, metrics] of Object.entries(compilerMetricsBody.files)) { + assert.equal(metrics.shellCalls, 0, `${path} should not introduce shell execution calls`); +} + +const relativeToolDir = `${outDir}/relative-tools`; +await mkdir(relativeToolDir, { recursive: true }); +await writeFile(`${relativeToolDir}/cc`, "#!/bin/sh\nprintf 'fake relative cc\\n'\n"); +await chmod(`${relativeToolDir}/cc`, 0o755); +const relativePathDoctor = await execFileAsync(zero, ["doctor", "--json"], { env: { ...process.env, PATH: relativeToolDir } }).catch((error) => error); +assert.notEqual(relativePathDoctor.code, 0); +const relativePathDoctorBody = JSON.parse(relativePathDoctor.stdout); +const relativePathNativeCompiler = relativePathDoctorBody.checks.find((check) => check.name === "native-c-compiler"); +assert.equal(relativePathNativeCompiler.status, "error"); +assert.match(relativePathNativeCompiler.message, /no native C compiler found/); + +assert(Array.isArray(compilerMetricsBody.largeFunctions)); +assert(compilerMetricsBody.stdlib.mainHelperCount > 0); +assert.equal(compilerMetricsBody.stdlib.mainHelperCount, compilerMetricsBody.stdlib.checkerReturnCount); +assert.equal(compilerMetricsBody.stdlib.mainHelperCount, compilerMetricsBody.stdlib.checkerArgCountCount); +assert.deepEqual(compilerMetricsBody.stdlib.duplicateCheckerReturnTypes, []); +assert.deepEqual(compilerMetricsBody.stdlib.duplicateCheckerArgCounts, []); +assert.deepEqual(compilerMetricsBody.stdlib.checkerReturnsMissingFromMainHelpers, []); +assert.deepEqual(compilerMetricsBody.stdlib.mainHelpersMissingFromCheckerReturns, []); +assert.deepEqual(compilerMetricsBody.stdlib.checkerArgCountsMissingFromMainHelpers, []); +assert.deepEqual(compilerMetricsBody.stdlib.mainHelpersMissingFromCheckerArgCounts, []); +assert.deepEqual(compilerMetricsBody.stdlib.argCountMismatches, []); +assert.equal(compilerMetricsBody.stdlib.sharedSignatureLookup.checkerReturnTypes, true); +assert.equal(compilerMetricsBody.stdlib.sharedSignatureLookup.checkerArgCounts, true); +assert.equal(compilerMetricsBody.backendFormats.elf.sharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.elf.x86ObjectUsesSharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.elf.x86ExecutableUsesSharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.elf.aarch64ObjectUsesSharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.elf.aarch64ExecutableUsesSharedWriter, true); +assert.deepEqual(compilerMetricsBody.backendFormats.elf.archFilesWithLocalSectionWriters, []); +assert.equal(compilerMetricsBody.backendFormats.elf.patchStateModule, true); +assert.equal(compilerMetricsBody.backendFormats.elf.x86UsesPatchStateModule, true); +assert.deepEqual(compilerMetricsBody.backendFormats.elf.archFilesWithLocalPatchState, []); +assert.equal(compilerMetricsBody.backendFormats.coff.sharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.coff.objectUsesSharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.coff.executableUsesSharedWriter, true); +assert.deepEqual(compilerMetricsBody.backendFormats.coff.archFilesWithLocalContainerWriters, []); +assert.equal(compilerMetricsBody.backendFormats.coff.patchStateModule, true); +assert.equal(compilerMetricsBody.backendFormats.coff.x64UsesPatchStateModule, true); +assert.deepEqual(compilerMetricsBody.backendFormats.coff.archFilesWithLocalPatchState, []); +assert.equal(compilerMetricsBody.backendFormats.macho.sharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.macho.objectUsesSharedWriter, true); +assert.equal(compilerMetricsBody.backendFormats.macho.executableUsesSharedWriter, true); +assert.deepEqual(compilerMetricsBody.backendFormats.macho.archFilesWithLocalContainerWriters, []); +assert.equal(compilerMetricsBody.backendFormats.macho.patchStateModule, true); +assert.equal(compilerMetricsBody.backendFormats.macho.archFileUsesPatchStateModule, true); +assert.deepEqual(compilerMetricsBody.backendFormats.macho.archFilesWithLocalPatchState, []); +assert.equal(compilerMetricsBody.backendFormats.x64.sharedEncodingPrimitives, true); +assert.equal(compilerMetricsBody.backendFormats.x64.elfUsesSharedEncodingPrimitives, true); +assert.equal(compilerMetricsBody.backendFormats.x64.coffUsesSharedEncodingPrimitives, true); +assert.deepEqual(compilerMetricsBody.backendFormats.x64.formatFilesWithLocalEncodingPrimitives, []); +assert.equal(compilerMetricsBody.backendFormats.aarch64.sharedEncodingPrimitives, true); +assert.equal(compilerMetricsBody.backendFormats.aarch64.elfUsesSharedEncodingPrimitives, true); +assert.equal(compilerMetricsBody.backendFormats.aarch64.machoUsesSharedEncodingPrimitives, true); +assert.deepEqual(compilerMetricsBody.backendFormats.aarch64.formatFilesWithLocalEncodingPrimitives, []); +assert.equal(compilerMetricsBody.budget.ok, true); +assert.deepEqual(compilerMetricsBody.budget.violations, []); +assert.equal(typeof compilerMetricsBody.budget.reportThreshold, "number"); +for (const item of compilerMetricsBody.largeFunctions) { + assert.equal(typeof item.path, "string"); + assert.equal(typeof item.signature, "string"); + assert.equal(typeof item.line, "number"); + assert.equal(typeof item.lines, "number"); + assert(item.lines >= compilerMetricsBody.budget.reportThreshold); +} + +const unsafeCompilerOverrideBuild = await execFileAsync(zero, [ + "build", + "--json", + "--out", + `${outDir}/unsafe-cc-override`, + "examples/json-api-client.graph", +], { env: { ...process.env, ZERO_CC: "cc;touch" } }).catch((error) => error); +assert.notEqual(unsafeCompilerOverrideBuild.code, 0); +assert.match(unsafeCompilerOverrideBuild.stderr, /compiler override contains unsafe shell characters/); +const unsafeCompilerOverrideBody = JSON.parse(unsafeCompilerOverrideBuild.stdout); +assert.equal(unsafeCompilerOverrideBody.ok, false); +assert.equal(unsafeCompilerOverrideBody.diagnostics[0].code, "BLD003"); +assert.equal(unsafeCompilerOverrideBody.diagnostics[0].actual, "compiler override contains unsafe shell characters"); +assert.match(unsafeCompilerOverrideBody.diagnostics[0].help, /without flags, whitespace, or shell syntax/); + +const agentSurfaceBorrowExplain = await execFileAsync(zero, ["explain", "--json", "BOR001"]); +const agentSurfaceBorrowExplainBody = JSON.parse(agentSurfaceBorrowExplain.stdout); +assert.equal(agentSurfaceBorrowExplainBody.code, "BOR001"); +assert.equal(agentSurfaceBorrowExplainBody.repair.id, "end-conflicting-borrow"); +assert.match(agentSurfaceBorrowExplainBody.summary, /lexical scope/); + +const agentSurfaceMalformedLocalUseFixture = `${outDir}/malformed-local-use.0`; +await writeFile(agentSurfaceMalformedLocalUseFixture, 'use local.\n\npub fn main(world: World) -> Void raises {\n check world.out.write("malformed local use parser fixture\\n")\n}\n'); +const agentSurfaceMalformedLocalUse = await execFileAsync(zero, ["parse", "--json", agentSurfaceMalformedLocalUseFixture]).catch((error) => error); +assert.notEqual(agentSurfaceMalformedLocalUse.code, 0); +const agentSurfaceMalformedLocalUseBody = JSON.parse(agentSurfaceMalformedLocalUse.stdout); +assert.equal(agentSurfaceMalformedLocalUseBody.diagnostics[0].code, "PAR100"); +assert.match(agentSurfaceMalformedLocalUseBody.diagnostics[0].message, /expected import module segment/); +assert.equal(agentSurfaceMalformedLocalUseBody.diagnostics[0].line, 1); +assert.equal(agentSurfaceMalformedLocalUseBody.diagnostics[0].column, 11); + +const agentSurfaceSplitUseFixture = `${outDir}/split-use-path.0`; +await writeFile(agentSurfaceSplitUseFixture, 'use std.\ncodec\n\npub fn main(world: World) -> Void raises {\n check world.out.write("split use parser fixture\\n")\n}\n'); +const agentSurfaceSplitUse = await execFileAsync(zero, ["parse", "--json", agentSurfaceSplitUseFixture]).catch((error) => error); +assert.notEqual(agentSurfaceSplitUse.code, 0); +const agentSurfaceSplitUseBody = JSON.parse(agentSurfaceSplitUse.stdout); +assert.equal(agentSurfaceSplitUseBody.diagnostics[0].code, "PAR100"); +assert.match(agentSurfaceSplitUseBody.diagnostics[0].message, /expected import module segment/); +assert.equal(agentSurfaceSplitUseBody.diagnostics[0].line, 1); +assert.equal(agentSurfaceSplitUseBody.diagnostics[0].column, 9); + +const agentSurfaceKeywordUseFixture = `${outDir}/keyword-use.0`; +await writeFile(agentSurfaceKeywordUseFixture, 'use pub\n\npub fn main(world: World) -> Void raises {\n check world.out.write("keyword use parser fixture\\n")\n}\n'); +const agentSurfaceKeywordUse = await execFileAsync(zero, ["parse", "--json", agentSurfaceKeywordUseFixture]).catch((error) => error); +assert.notEqual(agentSurfaceKeywordUse.code, 0); +const agentSurfaceKeywordUseBody = JSON.parse(agentSurfaceKeywordUse.stdout); +assert.equal(agentSurfaceKeywordUseBody.diagnostics[0].code, "PAR100"); +assert.match(agentSurfaceKeywordUseBody.diagnostics[0].message, /reserved word cannot be used as an identifier/); +assert.equal(agentSurfaceKeywordUseBody.diagnostics[0].line, 1); +assert.equal(agentSurfaceKeywordUseBody.diagnostics[0].column, 5); + +const agentSurfaceOwnedDropCheck = await execFileAsync(zero, ["check", "--json", "conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0"]); +const agentSurfaceOwnedDropCheckBody = JSON.parse(agentSurfaceOwnedDropCheck.stdout); +assert.equal(agentSurfaceOwnedDropCheckBody.ok, true); + +const agentSurfaceOwnedDropReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "linux-musl-x64", + "conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0", +]); +const agentSurfaceOwnedDropReadinessBody = JSON.parse(agentSurfaceOwnedDropReadiness.stdout); +assert.equal(agentSurfaceOwnedDropReadinessBody.ok, true); +assert.equal(agentSurfaceOwnedDropReadinessBody.diagnostics.length, 0); +assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.ok, false); +assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.buildable, false); +assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.languageOk, true); +assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[0].code, "BLD004"); +assert.deepEqual(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[0].backendBlocker, { + target: "linux-musl-x64", + objectFormat: "elf", + backend: "zero-elf64", + stage: "lower", + unsupportedFeature: "owned", +}); + +const directCallExeReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "exe", + "--target", + "linux-musl-x64", + "examples/direct-call-add.0", +]); +const directCallExeReadinessBody = JSON.parse(directCallExeReadiness.stdout); +assert.equal(directCallExeReadinessBody.ok, true); +assert.equal(directCallExeReadinessBody.diagnostics.length, 0); +assert.equal(directCallExeReadinessBody.targetReadiness.ok, false); +assert.equal(directCallExeReadinessBody.targetReadiness.buildable, false); +assert.equal(directCallExeReadinessBody.targetReadiness.diagnostics[0].code, "BLD004"); +assert.equal(directCallExeReadinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "buildability"); +assert.match(directCallExeReadinessBody.targetReadiness.diagnostics[0].message, /main must not take parameters/); +const directCallExeBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "exe", + "--target", + "linux-musl-x64", + "examples/direct-call-add.0", + "--out", + `${outDir}/direct-call-add-blocked`, +]).catch((error) => error); +assert.notEqual(directCallExeBuild.code, 0); +const directCallExeBuildBody = JSON.parse(directCallExeBuild.stdout); +const directCallReadinessDiag = directCallExeReadinessBody.targetReadiness.diagnostics[0]; +const directCallBuildDiag = directCallExeBuildBody.diagnostics[0]; +for (const key of ["code", "path", "line", "column", "length", "expected", "actual", "help"]) { + assert.equal(directCallBuildDiag[key], directCallReadinessDiag[key]); +} +assert.equal(directCallBuildDiag.backendBlocker.stage, "buildability"); +const directCallExeGraph = await execFileAsync(zero, [ + "inspect", + "--json", + "--emit", + "exe", + "--target", + "linux-musl-x64", + "examples/direct-call-add.0", +]); +const directCallExeGraphBody = JSON.parse(directCallExeGraph.stdout); +assert.equal(directCallExeGraphBody.targetReadiness.ok, false); +assert.equal(directCallExeGraphBody.targetReadiness.diagnostics[0].code, "BLD004"); +const directCallGraphDiag = directCallExeGraphBody.targetReadiness.diagnostics[0]; +assert.equal(directCallGraphDiag.path, "direct-call-add.0"); +assert.equal(directCallGraphDiag.expected, "direct ELF64 object subset"); +assert.equal(directCallGraphDiag.actual, "unsupported construct"); +assert.equal(directCallGraphDiag.backendBlocker.backend, "zero-elf64-exe"); +assert.equal(directCallGraphDiag.backendBlocker.stage, "lower"); + +const llvmLoopIrPath = `${outDir}/llvm-direct-while-sum.ll`; +await execFileAsync(zero, [ + "build", + "--emit", + "llvm-ir", + "--backend", + "llvm", + "examples/direct-while-sum.0", + "--out", + llvmLoopIrPath, +]); +const llvmLoopIr = await readFile(llvmLoopIrPath, "utf8"); +assert.match(llvmLoopIr, /^; zero llvm-ir v1\n/); +assert.match(llvmLoopIr, /target triple = "/); +assert.match(llvmLoopIr, /define i32 @main\(\) \{/); +assert.match(llvmLoopIr, /br label %L0\nL0:\n/); +assert.match(llvmLoopIr, /icmp slt i32 %v[0-9]+, 5/); +assert.match(llvmLoopIr, /add i32 %v[0-9]+, %v[0-9]+/); +assert.match(llvmLoopIr, /ret i32 %v[0-9]+/); + +const llvmArrayIr = await buildLlvmIrFixture("examples/direct-array-sum.0", "llvm-direct-array-sum"); +assert.match(llvmArrayIr, /alloca \[4 x i32\]/); +assert.match(llvmArrayIr, /getelementptr inbounds \[4 x i32\], ptr %slot[0-9]+, i64 0, i64 0/); +assert.match(llvmArrayIr, /getelementptr inbounds \[4 x i32\], ptr %slot[0-9]+, i64 0, i64 %v[0-9]+/); +assert.match(llvmArrayIr, /store i32 4, ptr %v[0-9]+, align 4/); +assert.match(llvmArrayIr, /load i32, ptr %v[0-9]+, align 4/); +assert.match(llvmArrayIr, /call void @llvm\.trap\(\)/); +await assertLlvmHostExitCode("examples/direct-array-sum.0", "llvm-direct-array-sum", 10); + +const llvmStringLenIr = await buildLlvmIrFixture("examples/direct-string-len.0", "llvm-direct-string-len"); +assert.match(llvmStringLenIr, /@\.zero\.data\.0 = private unnamed_addr constant \[6 x i8\] c"token\\00", align 1/); +assert.match(llvmStringLenIr, /insertvalue \{ ptr, i64 \} poison, ptr %v[0-9]+, 0/); +assert.match(llvmStringLenIr, /insertvalue \{ ptr, i64 \} %v[0-9]+, i64 5, 1/); +assert.match(llvmStringLenIr, /ret i64 5/); +await assertLlvmHostExitCode("examples/direct-string-len.0", "llvm-direct-string-len", 5); + +const llvmByteCopyFillIr = await buildLlvmIrFixture("examples/direct-byte-copy-fill.0", "llvm-direct-byte-copy-fill"); +assert.match(llvmByteCopyFillIr, /declare void @llvm\.memcpy\.p0\.p0\.i64\(ptr, ptr, i64, i1\)/); +assert.match(llvmByteCopyFillIr, /declare void @llvm\.memset\.p0\.i64\(ptr, i8, i64, i1\)/); +assert.match(llvmByteCopyFillIr, /alloca \[5 x i8\]/); +assert.match(llvmByteCopyFillIr, /call void @llvm\.memset\.p0\.i64\(ptr %v[0-9]+, i8 33, i64 (?:5|%v[0-9]+), i1 false\)/); +assert.match(llvmByteCopyFillIr, /select i1 %v[0-9]+, i64 %v[0-9]+, i64 %v[0-9]+/); +assert.match(llvmByteCopyFillIr, /call void @llvm\.memcpy\.p0\.p0\.i64\(ptr %v[0-9]+, ptr %v[0-9]+, i64 %v[0-9]+, i1 false\)/); +await assertLlvmHostExitCode("examples/direct-byte-copy-fill.0", "llvm-direct-byte-copy-fill", 111); + +const llvmStringEqlIr = await buildLlvmIrFixture("examples/direct-string-eql.0", "llvm-direct-string-eql"); +assertLlvmPhiPredecessors(llvmStringEqlIr); +assert.match(llvmStringEqlIr, /phi i64 \[0, %L[0-9]+\], \[%v[0-9]+, %L[0-9]+\]/); +assert.match(llvmStringEqlIr, /icmp eq i8 %v[0-9]+, %v[0-9]+/); +assert.match(llvmStringEqlIr, /phi i1 \[1, %L[0-9]+\], \[0, %L[0-9]+\]/); +await assertLlvmHostExitCode("examples/direct-string-eql.0", "llvm-direct-string-eql", 1); + +const llvmByteViewLocalsIr = await buildLlvmIrFixture("examples/direct-byte-view-locals.0", "llvm-direct-byte-view-locals"); +assertLlvmPhiPredecessors(llvmByteViewLocalsIr); +assert.match(llvmByteViewLocalsIr, /alloca \{ ptr, i64 \}/); +assert.match(llvmByteViewLocalsIr, /store \{ ptr, i64 \} %v[0-9]+, ptr %slot[0-9]+, align 8/); +assert.match(llvmByteViewLocalsIr, /load \{ ptr, i64 \}, ptr %slot[0-9]+, align 8/); +assert.match(llvmByteViewLocalsIr, /extractvalue \{ ptr, i64 \} %v[0-9]+, 0/); +assert.match(llvmByteViewLocalsIr, /extractvalue \{ ptr, i64 \} %v[0-9]+, 1/); +await assertLlvmHostExitCode("examples/direct-byte-view-locals.0", "llvm-direct-byte-view-locals", 107); + +const llvmSpanReadIr = await buildLlvmIrFixture("examples/direct-span-read.0", "llvm-direct-span-read"); +assert.match(llvmSpanReadIr, /icmp ule i64 %v[0-9]+, %v[0-9]+/); +assert.match(llvmSpanReadIr, /icmp ult i64 %v[0-9]+, %v[0-9]+/); +assert.match(llvmSpanReadIr, /getelementptr inbounds i8, ptr %v[0-9]+, i64 %v[0-9]+/); +assert.match(llvmSpanReadIr, /load i8, ptr %v[0-9]+, align 1/); +await assertLlvmHostExitCode("examples/direct-span-read.0", "llvm-direct-span-read", 107); + +const llvmShortCircuitSourcePath = `${outDir}/llvm-short-circuit.0`; +const llvmShortCircuitIrPath = `${outDir}/llvm-short-circuit.ll`; +await writeGraphFixture(llvmShortCircuitSourcePath, `fn rhsAnd() -> Bool { + return true +} + +fn rhsOr() -> Bool { + return false +} + +export c fn main() -> i32 { + if false && rhsAnd() { + return 1 + } + if true || rhsOr() { + return 2 + } + if true && (false && rhsAnd()) { + return 3 + } + if false || (true || rhsOr()) { + return 4 + } + return 0 +} +`); +await execFileAsync(zero, [ + "build", + "--emit", + "llvm-ir", + "--backend", + "llvm", + llvmShortCircuitSourcePath, + "--out", + llvmShortCircuitIrPath, +]); +const llvmShortCircuitIr = await readFile(llvmShortCircuitIrPath, "utf8"); +assertLlvmPhiPredecessors(llvmShortCircuitIr); +assert.match(llvmShortCircuitIr, /br i1 0, label %L[0-9]+, label %L[0-9]+\nL[0-9]+:\n %v[0-9]+ = call i1 @\.zero\.fn\.[0-9]+\.rhsAnd\(\)/); +assert.match(llvmShortCircuitIr, /%v[0-9]+ = phi i1 \[%v[0-9]+, %L[0-9]+\], \[0, %L[0-9]+\]/); +assert.match(llvmShortCircuitIr, /br i1 1, label %L[0-9]+, label %L[0-9]+\nL[0-9]+:\n %v[0-9]+ = call i1 @\.zero\.fn\.[0-9]+\.rhsOr\(\)/); +assert.match(llvmShortCircuitIr, /%v[0-9]+ = phi i1 \[%v[0-9]+, %L[0-9]+\], \[1, %L[0-9]+\]/); + +const llvmAddIrPath = `${outDir}/llvm-add.ll`; +const llvmAddBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "llvm-ir", + "--backend", + "llvm", + "examples/add.0", + "--out", + llvmAddIrPath, +]); +const llvmAddBuildBody = JSON.parse(llvmAddBuild.stdout); +assert.equal(llvmAddBuildBody.objectBackend.linking.targetLibraries, "zero-runtime"); +assert.equal(llvmAddBuildBody.objectBackend.linking.toolchainSource, "textual-llvm-ir-runtime-link-plan"); +assert.deepEqual(llvmAddBuildBody.objectBackend.linkerPlan.staticLibraries, ["zero_runtime.o"]); +const llvmAddIr = await readFile(llvmAddIrPath, "utf8"); +assert.match(llvmAddIr, /@\.zero\.data\.0 = private unnamed_addr constant \[12 x i8\] c"math works\\0A\\00", align 1/); +assert.match(llvmAddIr, /declare i32 @zero_world_write\(i32, ptr, i32\)/); +assert.match(llvmAddIr, /declare void @llvm\.trap\(\)/); +assert.match(llvmAddIr, /define i32 @\.zero\.fn\.[0-9]+\.answer\(\) \{/); +assert.match(llvmAddIr, /call i32 @zero_world_write\(i32 1, ptr %v[0-9]+, i32 (?:11|%v[0-9]+)\)/); +assert.match(llvmAddIr, /%v[0-9]+ = call i32 @zero_world_write\(i32 1, ptr %v[0-9]+, i32 (?:11|%v[0-9]+)\)\n %v[0-9]+ = icmp eq i32 %v[0-9]+, 0\n br i1 %v[0-9]+, label %L[0-9]+, label %L[0-9]+\nL[0-9]+:\n call void @llvm\.trap\(\)\n unreachable\nL[0-9]+:/); + +const llvmSymbolCollisionSourcePath = `${outDir}/llvm-symbol-collision.0`; +const llvmSymbolCollisionIrPath = `${outDir}/llvm-symbol-collision.ll`; +await writeGraphFixture(llvmSymbolCollisionSourcePath, `fn foo() -> i32 { + return 1 +} + +export c fn z_fn_0_foo() -> i32 { + return 2 +} + +export c fn main() -> i32 { + return foo() + z_fn_0_foo() +} +`); +await execFileAsync(zero, [ + "build", + "--emit", + "llvm-ir", + "--backend", + "llvm", + llvmSymbolCollisionSourcePath, + "--out", + llvmSymbolCollisionIrPath, +]); +const llvmSymbolCollisionIr = await readFile(llvmSymbolCollisionIrPath, "utf8"); +assert.match(llvmSymbolCollisionIr, /define i32 @\.zero\.fn\.[0-9]+\.foo\(\) \{/); +assert.match(llvmSymbolCollisionIr, /define i32 @z_fn_0_foo\(\) \{/); +assert.match(llvmSymbolCollisionIr, /call i32 @\.zero\.fn\.[0-9]+\.foo\(\)/); +assert.equal((llvmSymbolCollisionIr.match(/define i32 @z_fn_0_foo\(\) \{/g) || []).length, 1); + +const llvmRuntimeCollisionSourcePath = `${outDir}/llvm-runtime-symbol-collision.0`; +await writeGraphFixture(llvmRuntimeCollisionSourcePath, `export c fn zero_world_write() -> i32 { + return 7 +} + +pub fn main(world: World) -> Void raises { + check world.out.write("hello\\n") +} +`); +const llvmRuntimeCollisionReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "llvm-ir", + "--backend", + "llvm", + llvmRuntimeCollisionSourcePath, +]); +const llvmRuntimeCollisionReadinessBody = JSON.parse(llvmRuntimeCollisionReadiness.stdout); +assert.equal(llvmRuntimeCollisionReadinessBody.ok, true); +assert.equal(llvmRuntimeCollisionReadinessBody.targetReadiness.ok, false); +assert.equal(llvmRuntimeCollisionReadinessBody.targetReadiness.diagnostics[0].code, "BLD004"); +assert.equal(llvmRuntimeCollisionReadinessBody.targetReadiness.diagnostics[0].actual, "zero_world_write"); +assert.equal(llvmRuntimeCollisionReadinessBody.targetReadiness.diagnostics[0].backendBlocker.backend, "llvm"); +assert.equal(llvmRuntimeCollisionReadinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "lower"); +const llvmRuntimeCollisionBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "llvm-ir", + "--backend", + "llvm", + llvmRuntimeCollisionSourcePath, + "--out", + `${outDir}/llvm-runtime-symbol-collision.ll`, +]).catch((error) => error); +assert.notEqual(llvmRuntimeCollisionBuild.code, 0); +const llvmRuntimeCollisionBuildBody = JSON.parse(llvmRuntimeCollisionBuild.stdout); +assert.equal(llvmRuntimeCollisionBuildBody.diagnostics[0].actual, "zero_world_write"); +assert.equal(llvmRuntimeCollisionBuildBody.diagnostics[0].backendBlocker.backend, "llvm"); + +const llvmLongExportName = `llvm_export_${"a".repeat(200)}`; +const llvmLongExportSourcePath = `${outDir}/llvm-long-export.0`; +const llvmLongExportIrPath = `${outDir}/llvm-long-export.ll`; +await writeGraphFixture(llvmLongExportSourcePath, `export c fn ${llvmLongExportName}() -> i32 { + return 7 +} + +export c fn main() -> i32 { + return ${llvmLongExportName}() +} +`); +await execFileAsync(zero, [ + "build", + "--emit", + "llvm-ir", + "--backend", + "llvm", + llvmLongExportSourcePath, + "--out", + llvmLongExportIrPath, +]); +const llvmLongExportIr = await readFile(llvmLongExportIrPath, "utf8"); +assert(llvmLongExportIr.includes(`define i32 @${llvmLongExportName}() {`)); +assert(llvmLongExportIr.includes(`call i32 @${llvmLongExportName}()`)); + +const directStringMachOExe = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "exe", + "--target", + "darwin-arm64", + "examples/direct-string-literal.0", + "--out", + `${outDir}/direct-string-literal-darwin`, +]); +const directStringMachOExeBody = JSON.parse(directStringMachOExe.stdout); +assert.equal(directStringMachOExeBody.compiler, "zero-macho64"); +assert.equal(directStringMachOExeBody.generatedCBytes, 0); +const directStringCoffExe = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "exe", + "--target", + "win32-x64.exe", + "examples/direct-string-literal.0", + "--out", + `${outDir}/direct-string-literal-win`, +]); +const directStringCoffExeBody = JSON.parse(directStringCoffExe.stdout); +assert.equal(directStringCoffExeBody.compiler, "zero-coff-x64"); +assert.equal(directStringCoffExeBody.generatedCBytes, 0); + +const coffDynamicSliceFixture = "conformance/native/pass/coff-dynamic-byte-slice.0"; +const coffDynamicSliceReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffDynamicSliceFixture, +]); +const coffDynamicSliceReadinessBody = JSON.parse(coffDynamicSliceReadiness.stdout); +assert.equal(coffDynamicSliceReadinessBody.ok, true); +assert.equal(coffDynamicSliceReadinessBody.diagnostics.length, 0); +assert.equal(coffDynamicSliceReadinessBody.targetReadiness.ok, true); +assert.equal(coffDynamicSliceReadinessBody.targetReadiness.buildable, true); +assert.equal(coffDynamicSliceReadinessBody.targetReadiness.backend, "zero-coff-x64"); +const coffDynamicSliceBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffDynamicSliceFixture, + "--out", + `${outDir}/coff-dynamic-byte-slice.obj`, +]); +const coffDynamicSliceBuildBody = JSON.parse(coffDynamicSliceBuild.stdout); +assert.equal(coffDynamicSliceBuildBody.compiler, "zero-coff-x64"); +assert.equal(coffDynamicSliceBuildBody.generatedCBytes, 0); +assert.equal(coffDynamicSliceBuildBody.objectBackend.objectEmission.path, "direct-coff-x64-object"); +await assertCoffX64Object(`${outDir}/coff-dynamic-byte-slice.obj`, "main"); + +const coffU64CopyFixture = "conformance/native/pass/std-mem-u64-copy-items.0"; +const coffU64CopyReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffU64CopyFixture, +]); +const coffU64CopyReadinessBody = JSON.parse(coffU64CopyReadiness.stdout); +assert.equal(coffU64CopyReadinessBody.ok, true); +assert.equal(coffU64CopyReadinessBody.targetReadiness.ok, true); +assert.equal(coffU64CopyReadinessBody.targetReadiness.buildable, true); +assert.equal(coffU64CopyReadinessBody.targetReadiness.backend, "zero-coff-x64"); +const coffU64CopyPath = `${outDir}/coff-u64-copy-items.obj`; +const coffU64CopyBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffU64CopyFixture, + "--out", + coffU64CopyPath, +]); +const coffU64CopyBuildBody = JSON.parse(coffU64CopyBuild.stdout); +assert.equal(coffU64CopyBuildBody.compiler, "zero-coff-x64"); +assert.equal(coffU64CopyBuildBody.generatedCBytes, 0); +assert.equal(coffU64CopyBuildBody.objectBackend.objectEmission.path, "direct-coff-x64-object"); +const coffU64CopyBytes = await assertCoffX64Object(coffU64CopyPath, "main"); +assert(hasX64MovMemoryToR64(coffU64CopyBytes)); +assert(hasX64MovR64ToMemory(coffU64CopyBytes)); + +const coffU64ContainsFixture = "conformance/native/pass/std-mem-u64-contains.0"; +const coffU64ContainsReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffU64ContainsFixture, +]); +const coffU64ContainsReadinessBody = JSON.parse(coffU64ContainsReadiness.stdout); +assert.equal(coffU64ContainsReadinessBody.ok, true); +assert.equal(coffU64ContainsReadinessBody.targetReadiness.ok, true); +assert.equal(coffU64ContainsReadinessBody.targetReadiness.buildable, true); +assert.equal(coffU64ContainsReadinessBody.targetReadiness.backend, "zero-coff-x64"); +const coffU64ContainsPath = `${outDir}/coff-u64-contains.obj`; +const coffU64ContainsBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffU64ContainsFixture, + "--out", + coffU64ContainsPath, +]); +const coffU64ContainsBuildBody = JSON.parse(coffU64ContainsBuild.stdout); +assert.equal(coffU64ContainsBuildBody.compiler, "zero-coff-x64"); +assert.equal(coffU64ContainsBuildBody.generatedCBytes, 0); +assert.equal(coffU64ContainsBuildBody.objectBackend.objectEmission.path, "direct-coff-x64-object"); +const coffU64ContainsBytes = await assertCoffX64Object(coffU64ContainsPath, "main"); +assert(coffU64ContainsBytes.includes(Buffer.from([0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]))); +assert(hasX64MovMemoryToR64(coffU64ContainsBytes)); +assert(hasX64CmpR64(coffU64ContainsBytes)); + +const coffBoolCopyFixture = "conformance/native/pass/std-mem-bool-copy-items.0"; +const coffBoolCopyReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffBoolCopyFixture, +]); +const coffBoolCopyReadinessBody = JSON.parse(coffBoolCopyReadiness.stdout); +assert.equal(coffBoolCopyReadinessBody.ok, true); +assert.equal(coffBoolCopyReadinessBody.targetReadiness.ok, true); +assert.equal(coffBoolCopyReadinessBody.targetReadiness.buildable, true); +assert.equal(coffBoolCopyReadinessBody.targetReadiness.backend, "zero-coff-x64"); +const coffBoolCopyPath = `${outDir}/coff-bool-copy-items.obj`; +const coffBoolCopyBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "win32-x64.exe", + coffBoolCopyFixture, + "--out", + coffBoolCopyPath, +]); +const coffBoolCopyBuildBody = JSON.parse(coffBoolCopyBuild.stdout); +assert.equal(coffBoolCopyBuildBody.compiler, "zero-coff-x64"); +assert.equal(coffBoolCopyBuildBody.generatedCBytes, 0); +assert.equal(coffBoolCopyBuildBody.objectBackend.objectEmission.path, "direct-coff-x64-object"); +await assertCoffX64Object(coffBoolCopyPath, "main"); + +async function assertMachOObjectBuildabilityBlocked(fixture, outName, expectedMessage) { + const readiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + fixture, + ]); + const readinessBody = JSON.parse(readiness.stdout); + assert.equal(readinessBody.ok, true); + assert.equal(readinessBody.diagnostics.length, 0); + assert.equal(readinessBody.targetReadiness.ok, false); + assert.equal(readinessBody.targetReadiness.buildable, false); + assert.equal(readinessBody.targetReadiness.languageOk, true); + assert.equal(readinessBody.targetReadiness.emit, "obj"); + assert.equal(readinessBody.targetReadiness.target, "darwin-arm64"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].code, "BLD004"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.backend, "zero-macho64"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "buildability"); + assert.match(readinessBody.targetReadiness.diagnostics[0].message, expectedMessage); + const build = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + fixture, + "--out", + `${outDir}/${outName}`, + ]).catch((error) => error); + assert.notEqual(build.code, 0); + const buildBody = JSON.parse(build.stdout); + const readinessDiag = readinessBody.targetReadiness.diagnostics[0]; + const buildDiag = buildBody.diagnostics[0]; + for (const key of ["code", "path", "line", "column", "length", "expected", "actual", "help"]) { + assert.equal(buildDiag[key], readinessDiag[key]); + } + assert.equal(buildDiag.backendBlocker.backend, "zero-macho64"); + assert.equal(buildDiag.backendBlocker.stage, "buildability"); +} + +await assertMachOObjectBuildabilityBlocked( + "conformance/native/pass/macho-large-byte-slice-blocked.0", + "macho-large-byte-slice.o", + /constant start/, +); +await assertMachOObjectBuildabilityBlocked( + "conformance/native/pass/macho-nested-call-scratch-blocked.0", + "macho-nested-call-scratch.o", + /scratch spill capacity/, +); + +const machoOpenByteSliceFixture = "conformance/native/pass/macho-open-byte-slice.0"; +const machoOpenByteSliceReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + machoOpenByteSliceFixture, +]); +const machoOpenByteSliceReadinessBody = JSON.parse(machoOpenByteSliceReadiness.stdout); +assert.equal(machoOpenByteSliceReadinessBody.ok, true); +assert.equal(machoOpenByteSliceReadinessBody.diagnostics.length, 0); +assert.equal(machoOpenByteSliceReadinessBody.targetReadiness.ok, true); +assert.equal(machoOpenByteSliceReadinessBody.targetReadiness.buildable, true); +assert.equal(machoOpenByteSliceReadinessBody.targetReadiness.backend, "zero-macho64"); +const machoOpenByteSliceBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + machoOpenByteSliceFixture, + "--out", + `${outDir}/macho-open-byte-slice.o`, +]); +const machoOpenByteSliceBuildBody = JSON.parse(machoOpenByteSliceBuild.stdout); +assert.equal(machoOpenByteSliceBuildBody.compiler, "zero-macho64"); +assert.equal(machoOpenByteSliceBuildBody.generatedCBytes, 0); +assert.equal(machoOpenByteSliceBuildBody.objectBackend.objectEmission.path, "direct-macho64-object"); +await assertMachOArm64Object(`${outDir}/macho-open-byte-slice.o`, "main"); + +const aarch64OpenSliceBoundsFixture = `${outDir}/aarch64-open-byte-slice-bounds.0`; +await writeGraphFixture(aarch64OpenSliceBoundsFixture, `export c fn main() -> u32 { + let words: [2]u16 = [1_u16, 2_u16] + let suffix: Span = words[3_usize..] + return (std.mem.len(suffix)) as u32 +} +`); +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + aarch64OpenSliceBoundsFixture, + "--out", + `${outDir}/aarch64-open-byte-slice-bounds.o`, +]); +const aarch64OpenSliceBoundsBytes = await assertElfAarch64Object(`${outDir}/aarch64-open-byte-slice-bounds.o`, "main"); +assert(hasAarch64CondBranch(aarch64OpenSliceBoundsBytes, 9)); +assert(hasAarch64Instruction(aarch64OpenSliceBoundsBytes, 0xd4200000)); +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + aarch64OpenSliceBoundsFixture, + "--out", + `${outDir}/macho-open-byte-slice-bounds.o`, +]); +const machoOpenSliceBoundsBytes = await assertMachOArm64Object(`${outDir}/macho-open-byte-slice-bounds.o`, "main"); +assert(hasAarch64CondBranch(machoOpenSliceBoundsBytes, 9)); +assert(hasAarch64Instruction(machoOpenSliceBoundsBytes, 0xd4200000)); + +const trapMessageFixture = `${outDir}/trap-index-message.0`; +const trapMessageGraph = await writeGraphFixture(trapMessageFixture, `pub fn main(world: World) -> Void raises { + var values: [4]u32 = [1, 2, 3, 4] + var index: usize = 4 + index = index + 1000 + let v: u32 = values[index] + if v == 1 { + check world.out.write("unreachable\\n") + } +} +`); +if (runnableDirectTarget) { + const trapMessageOut = `${outDir}/trap-index-message`; + await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", runnableDirectTarget, trapMessageGraph, "--out", trapMessageOut]); + const trapMessageRun = await execFileAsync(trapMessageOut, []).catch((error) => error); + assert.notEqual(trapMessageRun.code ?? (trapMessageRun.signal ? 1 : 0), 0, "deliberate out-of-bounds index must abort"); + assert.match(trapMessageRun.stderr ?? "", /trap: index out of bounds/); + assert.equal(trapMessageRun.stdout ?? "", ""); +} + +const x64OpenSliceBoundsFixture = `${outDir}/x64-open-u16-slice-bounds.0`; +await writeGraphFixture(x64OpenSliceBoundsFixture, `export c fn main() -> u32 { + let words: [2]u16 = [1_u16, 2_u16] + let suffix: Span = words[3_usize..] + return (std.mem.len(suffix)) as u32 +} +`); +const x64SliceLenBoundsFixture = `${outDir}/x64-len-u16-slice-bounds.0`; +await writeGraphFixture(x64SliceLenBoundsFixture, `export c fn main() -> u32 { + let words: [2]u16 = [1_u16, 2_u16] + return (std.mem.len(words[..3_usize])) as u32 +} +`); + +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + x64SliceLenBoundsFixture, + "--out", + `${outDir}/aarch64-len-u16-slice-bounds.o`, +]); +const aarch64LenSliceBoundsBytes = await assertElfAarch64Object(`${outDir}/aarch64-len-u16-slice-bounds.o`, "main"); +assert(hasAarch64CondBranch(aarch64LenSliceBoundsBytes, 9)); +assert(hasAarch64Instruction(aarch64LenSliceBoundsBytes, 0xd4200000)); +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + x64SliceLenBoundsFixture, + "--out", + `${outDir}/macho-len-u16-slice-bounds.o`, +]); +const machoLenSliceBoundsBytes = await assertMachOArm64Object(`${outDir}/macho-len-u16-slice-bounds.o`, "main"); +assert(hasAarch64CondBranch(machoLenSliceBoundsBytes, 9)); +assert(hasAarch64Instruction(machoLenSliceBoundsBytes, 0xd4200000)); + +async function assertX64SliceBoundsObject(fixture, name) { + const elfPath = `${outDir}/${name}-linux.o`; + await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "linux-musl-x64", fixture, "--out", elfPath]); + assertX64SliceBoundsTrapBytes((await assertElf64Object(elfPath, "main")).bytes); + + const machoPath = `${outDir}/${name}-macho-x64.o`; + await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "darwin-x64", fixture, "--out", machoPath]); + assertX64SliceBoundsTrapBytes(await assertMachOX64Object(machoPath, "main")); + + const coffPath = `${outDir}/${name}-coff.obj`; + await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "win32-x64.exe", fixture, "--out", coffPath]); + assertX64SliceBoundsTrapBytes(await assertCoffX64Object(coffPath, "main")); +} + +await assertX64SliceBoundsObject(x64OpenSliceBoundsFixture, "x64-open-u16-slice-bounds"); +await assertX64SliceBoundsObject(x64SliceLenBoundsFixture, "x64-len-u16-slice-bounds"); + +const x64U16RecordFieldFixture = `${outDir}/x64-u16-record-field.0`; +await writeGraphFixture(x64U16RecordFieldFixture, `type Holder { + values: [3]u16, +} + +export c fn main() -> u32 { + var holder: Holder = Holder { values: [10_u16, 20_u16, 30_u16] } + if holder.values[0] == 10_u16 && holder.values[1] == 20_u16 && holder.values[2] == 30_u16 { + return 1 + } + return 0 +} +`); +const x64U16RecordFieldElfPath = `${outDir}/x64-u16-record-field-linux.o`; +await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "linux-musl-x64", x64U16RecordFieldFixture, "--out", x64U16RecordFieldElfPath]); +assertX64U16RecordFieldBytes((await assertElf64Object(x64U16RecordFieldElfPath, "main")).bytes); +const x64U16RecordFieldMachOPath = `${outDir}/x64-u16-record-field-macho-x64.o`; +await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "darwin-x64", x64U16RecordFieldFixture, "--out", x64U16RecordFieldMachOPath]); +assertX64U16RecordFieldBytes(await assertMachOX64Object(x64U16RecordFieldMachOPath, "main")); + +async function assertAArch64ObjectBuildabilityBlocked(target, backend, outName, expectedMessage) { + const fixture = "conformance/native/pass/macho-nested-call-scratch-blocked.0"; + const readiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + target, + fixture, + ]); + const readinessBody = JSON.parse(readiness.stdout); + assert.equal(readinessBody.ok, true); + assert.equal(readinessBody.diagnostics.length, 0); + assert.equal(readinessBody.targetReadiness.ok, false); + assert.equal(readinessBody.targetReadiness.buildable, false); + assert.equal(readinessBody.targetReadiness.languageOk, true); + assert.equal(readinessBody.targetReadiness.emit, "obj"); + assert.equal(readinessBody.targetReadiness.target, target); + assert.equal(readinessBody.targetReadiness.diagnostics[0].code, "BLD004"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.backend, backend); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "buildability"); + assert.match(readinessBody.targetReadiness.diagnostics[0].message, expectedMessage); + const build = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + target, + fixture, + "--out", + `${outDir}/${outName}`, + ]).catch((error) => error); + assert.notEqual(build.code, 0); + const buildBody = JSON.parse(build.stdout); + const readinessDiag = readinessBody.targetReadiness.diagnostics[0]; + const buildDiag = buildBody.diagnostics[0]; + for (const key of ["code", "path", "line", "column", "length", "expected", "actual", "help"]) { + assert.equal(buildDiag[key], readinessDiag[key]); + } + assert.equal(buildDiag.backendBlocker.backend, backend); + assert.equal(buildDiag.backendBlocker.stage, "buildability"); +} + +await assertAArch64ObjectBuildabilityBlocked("linux-arm64", "zero-elf-aarch64", "elf-aarch64-nested-call-scratch.o", /scratch spill capacity/); +await assertAArch64ObjectBuildabilityBlocked("win32-arm64.exe", "zero-coff-aarch64", "coff-aarch64-nested-call-scratch.obj", /scratch spill capacity/); +const machoBoolArraysBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + "conformance/native/pass/bool-arrays.0", + "--out", + `${outDir}/macho-bool-arrays.o`, +]); +const machoBoolArraysBody = JSON.parse(machoBoolArraysBuild.stdout); +assert.equal(machoBoolArraysBody.compiler, "zero-macho64"); +assert.equal(machoBoolArraysBody.generatedCBytes, 0); +assert.equal(machoBoolArraysBody.objectBackend.objectEmission.path, "direct-macho64-object"); + +const directCallArm64ObjReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + "examples/direct-call-add.0", +]); +const directCallArm64ObjReadinessBody = JSON.parse(directCallArm64ObjReadiness.stdout); +assert.equal(directCallArm64ObjReadinessBody.ok, true); +assert.equal(directCallArm64ObjReadinessBody.diagnostics.length, 0); +assert.equal(directCallArm64ObjReadinessBody.targetReadiness.ok, true); +assert.equal(directCallArm64ObjReadinessBody.targetReadiness.buildable, true); +assert.equal(directCallArm64ObjReadinessBody.targetReadiness.backend, "zero-elf-aarch64"); +const directCallArm64ObjBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + "examples/direct-call-add.0", + "--out", + `${outDir}/direct-call-add-arm64.o`, +]); +const directCallArm64ObjBuildBody = JSON.parse(directCallArm64ObjBuild.stdout); +assert.equal(directCallArm64ObjBuildBody.compiler, "zero-elf-aarch64"); +assert.equal(directCallArm64ObjBuildBody.generatedCBytes, 0); +assert.equal(directCallArm64ObjBuildBody.objectBackend.objectEmission.path, "direct-elf-aarch64-object"); +await assertElfAarch64Object(`${outDir}/direct-call-add-arm64.o`, "main"); + +const arm64ProcDynamicViewsSource = `${outDir}/aarch64-proc-dynamic-byte-view-spawn.0`; +const arm64ProcDynamicViewsObj = `${outDir}/aarch64-proc-dynamic-byte-view-spawn.o`; +await writeGraphFixture(arm64ProcDynamicViewsSource, `fn programName() -> String { + return "sh" +} + +fn childArgs() -> Span { + return std.mem.span("-c\\ntrue") +} + +fn cwdName() -> String { + return "." +} + +fn envBlock() -> Span { + return std.mem.span("ZERO_AARCH64_PROC=1") +} + +fn childCommand() -> String { + return "sh -c true" +} + +export c fn main() -> i32 { + let status: ProcStatus = std.proc.spawnInheritArgs(programName(), childArgs(), cwdName(), envBlock()) + let child: ProcChild = std.proc.spawnChildArgs(programName(), childArgs(), cwdName(), envBlock()) + let child_env: ProcChild = std.proc.spawnChildInEnv(childCommand(), cwdName(), envBlock()) + let pty_child: ProcChild = std.pty.spawnArgs(programName(), childArgs(), cwdName(), envBlock()) + return std.proc.exitCode(status) + std.proc.pid(child) + std.proc.pid(child_env) + std.proc.pid(pty_child) +} +`); +const arm64ProcDynamicViewsBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + arm64ProcDynamicViewsSource, + "--out", + arm64ProcDynamicViewsObj, +]); +const arm64ProcDynamicViewsBuildBody = JSON.parse(arm64ProcDynamicViewsBuild.stdout); +assert.equal(arm64ProcDynamicViewsBuildBody.compiler, "zero-elf-aarch64"); +assert.equal(arm64ProcDynamicViewsBuildBody.objectBackend.objectEmission.path, "direct-elf-aarch64-object"); +const arm64ProcDynamicViewsBytes = await assertElfAarch64Object(arm64ProcDynamicViewsObj, "main"); +assert(countAarch64InstructionSequence(arm64ProcDynamicViewsBytes, aarch64ByteViewReloadSequence(4)) >= 3); + +let arm64NestedIndexExpr = "values[idx]"; +for (let i = 0; i < 32; i++) arm64NestedIndexExpr = `(0_u32 + ${arm64NestedIndexExpr})`; +const arm64NestedIndexFixture = `${outDir}/aarch64-nested-index-scratch-blocked.0`; +await writeGraphFixture(arm64NestedIndexFixture, `export c fn main() -> u32 { + let values: [1]u32 = [7] + let idx: u32 = 0_u32 + return ${arm64NestedIndexExpr} +} +`); +async function assertArm64NestedScratchBlocked(fixture, expectedMessage, outPrefix) { + for (const blocked of [["linux-arm64", "zero-elf-aarch64", "linux.o"], ["darwin-arm64", "zero-macho64", "macho.o"], ["win32-arm64.exe", "zero-coff-aarch64", "coff.obj"]]) { + const readiness = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "--target", blocked[0], fixture]); + const readinessBody = JSON.parse(readiness.stdout); + assert.equal(readinessBody.ok, true); + assert.equal(readinessBody.targetReadiness.ok, false); + assert.equal(readinessBody.targetReadiness.diagnostics[0].code, "BLD004"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.backend, blocked[1]); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "buildability"); + assert.match(readinessBody.targetReadiness.diagnostics[0].message, expectedMessage); + const build = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", blocked[0], fixture, "--out", `${outDir}/${outPrefix}-${blocked[2]}`]).catch((error) => error); + assert.notEqual(build.code, 0); + assert.equal(JSON.parse(build.stdout).diagnostics[0].backendBlocker.stage, "buildability"); + } +} +await assertArm64NestedScratchBlocked(arm64NestedIndexFixture, /indexed load exceeds scratch register spill capacity/, "aarch64-nested-index"); +let arm64NestedLenExpr = "((std.mem.len(text[start..end])) as u32)"; +for (let i = 0; i < 32; i++) arm64NestedLenExpr = `(0_u32 + ${arm64NestedLenExpr})`; +const arm64NestedLenFixture = `${outDir}/aarch64-nested-len-scratch-blocked.0`; +await writeGraphFixture(arm64NestedLenFixture, `export c fn main() -> u32 { + let text: String = "abcdef" + let start: usize = 1 + let end: usize = 4 + return ${arm64NestedLenExpr} +} +`); +await assertArm64NestedScratchBlocked(arm64NestedLenFixture, /byte-view length exceeds scratch register spill capacity/, "aarch64-nested-len"); +let arm64NestedDynamicStartLenExpr = "((std.mem.len(text[(start + 0_usize)..end])) as u32)"; +for (let i = 0; i < 31; i++) arm64NestedDynamicStartLenExpr = `(0_u32 + ${arm64NestedDynamicStartLenExpr})`; +const arm64NestedDynamicStartLenFixture = `${outDir}/aarch64-nested-dynamic-start-len-scratch-blocked.0`; +await writeGraphFixture(arm64NestedDynamicStartLenFixture, `export c fn main() -> u32 { + let text: String = "abcdef" + let start: usize = 1 + let end: usize = 4 + return ${arm64NestedDynamicStartLenExpr} +} +`); +await assertArm64NestedScratchBlocked(arm64NestedDynamicStartLenFixture, /byte-view length exceeds scratch register spill capacity/, "aarch64-nested-dynamic-start-len"); +let arm64NestedEndLenExpr = "((std.mem.len(text[1..(end + 0_usize)])) as u32)"; +for (let i = 0; i < 32; i++) arm64NestedEndLenExpr = `(0_u32 + ${arm64NestedEndLenExpr})`; +const arm64NestedEndLenFixture = `${outDir}/aarch64-nested-end-len-scratch-blocked.0`; +await writeGraphFixture(arm64NestedEndLenFixture, `export c fn main() -> u32 { + let text: String = "abcdef" + let end: usize = 4 + return ${arm64NestedEndLenExpr} +} +`); +await assertArm64NestedScratchBlocked(arm64NestedEndLenFixture, /byte-view length exceeds scratch register spill capacity/, "aarch64-nested-end-len"); +let arm64NestedDynamicEqlExpr = "std.mem.eqlBytes(text[(start + (0_usize + 0_usize))..end], text[(start + (0_usize + 0_usize))..end])"; +for (let i = 0; i < 29; i++) arm64NestedDynamicEqlExpr = `(true == ${arm64NestedDynamicEqlExpr})`; +const arm64NestedDynamicEqlFixture = `${outDir}/aarch64-nested-dynamic-eql-scratch-blocked.0`; +await writeGraphFixture(arm64NestedDynamicEqlFixture, `export c fn main() -> Bool { + let text: String = "abcdef" + let start: usize = 1 + let end: usize = 4 + return ${arm64NestedDynamicEqlExpr} +} +`); +await assertArm64NestedScratchBlocked(arm64NestedDynamicEqlFixture, /byte-view equality exceeds scratch register spill capacity/, "aarch64-nested-dynamic-eql"); + +let arm64WorldWriteSliceStart = "(start + 0_usize)"; +for (let i = 0; i < 31; i++) arm64WorldWriteSliceStart = `(0_usize + ${arm64WorldWriteSliceStart})`; +const arm64WorldWriteFixture = `${outDir}/aarch64-world-write-dynamic-slice-scratch-blocked.0`; +await writeGraphFixture(arm64WorldWriteFixture, `pub fn main(world: World) -> Void raises { + let text: String = "abcdef" + let start: usize = 1 + check world.out.write(text[${arm64WorldWriteSliceStart}..6]) +} +`); +async function assertArm64WorldWriteScratchBlocked(fixture, expectedMessage, outPrefix) { + for (const blocked of [["linux-musl-arm64", "zero-elf-aarch64-exe", "linux"], ["win32-arm64.exe", "zero-coff-aarch64-exe", "coff.exe"]]) { + const readiness = await execFileAsync(zero, ["check", "--json", "--emit", "exe", "--target", blocked[0], fixture]); + const readinessBody = JSON.parse(readiness.stdout); + assert.equal(readinessBody.ok, true); + assert.equal(readinessBody.targetReadiness.ok, false); + assert.equal(readinessBody.targetReadiness.diagnostics[0].code, "BLD004"); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.backend, blocked[1]); + assert.equal(readinessBody.targetReadiness.diagnostics[0].backendBlocker.stage, "buildability"); + assert.match(readinessBody.targetReadiness.diagnostics[0].message, expectedMessage); + const build = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", blocked[0], fixture, "--out", `${outDir}/${outPrefix}-${blocked[2]}`]).catch((error) => error); + assert.notEqual(build.code, 0); + const buildDiag = JSON.parse(build.stdout).diagnostics[0]; + assert.equal(buildDiag.backendBlocker.backend, blocked[1]); + assert.equal(buildDiag.backendBlocker.stage, "buildability"); + assert.match(buildDiag.message, expectedMessage); + } +} +await assertArm64WorldWriteScratchBlocked(arm64WorldWriteFixture, /expression nesting exceeds scratch register spill capacity/, "aarch64-world-write-dynamic-slice"); + +let arm64WorldWriteSliceEnd = "(end + 0_usize)"; +for (let i = 0; i < 32; i++) arm64WorldWriteSliceEnd = `(0_usize + ${arm64WorldWriteSliceEnd})`; +const arm64WorldWriteEndFixture = `${outDir}/aarch64-world-write-dynamic-end-scratch-blocked.0`; +await writeGraphFixture(arm64WorldWriteEndFixture, `pub fn main(world: World) -> Void raises { + let text: String = "abcdef" + let end: usize = 6 + check world.out.write(text[1..${arm64WorldWriteSliceEnd}]) +} +`); +await assertArm64WorldWriteScratchBlocked(arm64WorldWriteEndFixture, /expression nesting exceeds scratch register spill capacity/, "aarch64-world-write-dynamic-end"); + +const arm64PrivateHelperObj = `${outDir}/aarch64-private-helper-ignored.o`; +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + "conformance/native/pass/aarch64-private-helper-ignored.0", + "--out", + arm64PrivateHelperObj, +]); +const arm64PrivateHelperBytes = await assertElfAarch64Object(arm64PrivateHelperObj, "main"); +assert(arm64PrivateHelperBytes.includes(Buffer.from([0x40, 0x05, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6]))); + +const arm64CompareSource = `${outDir}/aarch64-typed-compare.0`; +const arm64CompareObj = `${outDir}/aarch64-typed-compare.o`; +await writeGraphFixture(arm64CompareSource, `export c fn main() -> u32 { + let large: u32 = 4294967295 + if large > 0_u32 { + return 7 + } + return 3 +} + +export c fn wide_guard() -> u32 { + let wide: u64 = 4294967296 + if wide != 0_u64 { + return 9 + } + return 4 +} +`); +await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "linux-arm64", + arm64CompareSource, + "--out", + arm64CompareObj, +]); +const arm64CompareBytes = await readFile(arm64CompareObj); +assert(hasAarch64CondBranch(arm64CompareBytes, 9)); +assert(hasAarch64Instruction(arm64CompareBytes, 0xeb09011f)); + +const memoryPackageMachOReadiness = await execFileAsync(zero, [ + "check", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + "examples/memory-package", +]); +const memoryPackageMachOReadinessBody = JSON.parse(memoryPackageMachOReadiness.stdout); +assert.equal(memoryPackageMachOReadinessBody.ok, true); +assert.equal(memoryPackageMachOReadinessBody.diagnostics.length, 0); +assert.equal(memoryPackageMachOReadinessBody.targetReadiness.ok, true); +assert.equal(memoryPackageMachOReadinessBody.targetReadiness.buildable, true); +assert.equal(memoryPackageMachOReadinessBody.targetReadiness.backend, "zero-macho64"); +assert.equal(memoryPackageMachOReadinessBody.targetReadiness.diagnostics.length, 0); +const memoryPackageMachOObj = `${outDir}/memory-package-macho.o`; +const memoryPackageMachOBuild = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + "examples/memory-package", + "--out", + memoryPackageMachOObj, +]); +const memoryPackageMachOBuildBody = JSON.parse(memoryPackageMachOBuild.stdout); +assert.equal(memoryPackageMachOBuildBody.compiler, "zero-macho64"); +assert.equal(memoryPackageMachOBuildBody.generatedCBytes, 0); +assert.equal(memoryPackageMachOBuildBody.objectBackend.objectEmission.path, "direct-macho64-object"); +await assertMachOArm64Object(memoryPackageMachOObj, "main"); + +async function assertAgentSurfaceOwnedDropUnsupported(target, emit, outName, expectedPattern, expectedObjectFormat, expectedBackend, options = {}) { + const extraArgs = options.extraArgs ?? []; + const build = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + emit, + "--target", + target, + ...extraArgs, + "conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0", + "--out", + `${outDir}/${outName}`, + ]).catch((error) => error); + assert.notEqual(build.code, 0); + const body = JSON.parse(build.stdout); + const diagnostic = body.diagnostics[0]; + assert.equal(diagnostic.code, "BLD004"); + assert.match(diagnostic.expected, expectedPattern); + assert.equal(diagnostic.actual, "owned"); + assert.deepEqual(diagnostic.backendBlocker, { + target, + objectFormat: expectedObjectFormat, + backend: expectedBackend, + stage: "lower", + unsupportedFeature: "owned", + }); +} + +await assertAgentSurfaceOwnedDropUnsupported("linux-musl-x64", "obj", "agent-surface-owned-drop-elf.o", /typed program graph MIR subset/, "elf", "zero-elf64-exe"); +await assertAgentSurfaceOwnedDropUnsupported("darwin-arm64", "obj", "agent-surface-owned-drop-macho.o", /typed program graph MIR subset/, "macho", "zero-macho64-exe"); +await assertAgentSurfaceOwnedDropUnsupported("win32-x64.exe", "obj", "agent-surface-owned-drop-coff.obj", /typed program graph MIR subset/, "coff", "zero-coff-x64-exe"); + +const mismatchedDirectEmitter = await execFileAsync(zero, [ + "build", + "--json", + "--emit", + "obj", + "--target", + "darwin-arm64", + "--backend", + "zero-elf64", + "examples/add.0", + "--out", + `${outDir}/direct-emitter-mismatch.o`, +]).catch((error) => error); +assert.notEqual(mismatchedDirectEmitter.code, 0); +const mismatchedDirectEmitterBody = JSON.parse(mismatchedDirectEmitter.stdout); +assert.equal(mismatchedDirectEmitterBody.diagnostics[0].code, "BLD004"); +assert.equal(mismatchedDirectEmitterBody.diagnostics[0].actual, "--backend zero-elf64"); +assert.deepEqual(mismatchedDirectEmitterBody.diagnostics[0].backendBlocker, { + target: "darwin-arm64", + objectFormat: "macho", + backend: "zero-elf64", + stage: "target-selection", + unsupportedFeature: "requested direct emitter does not match target", +}); + +const commonPassFixtures = [ + ["conformance/common/pass/array-sum-min-max.0", "common-array-sum-min-max", { stdout: "array sum min max ok\n" }], + ["conformance/common/pass/bytes-reverse.0", "common-bytes-reverse", { stdout: "bytes reverse ok\n" }], + ["conformance/common/pass/cli-args.0", "common-cli-args", { stdout: "alpha\n", args: ["alpha", "beta"] }], + ["conformance/common/pass/count-words-lines.0", "common-count-words-lines", { stdout: "count words lines ok\n" }], + ["conformance/common/pass/factorial.0", "common-factorial", { stdout: "factorial ok\n" }], + ["conformance/common/pass/fib-iterative.0", "common-fib-iterative", { stdout: "fib iterative ok\n" }], + ["conformance/common/pass/fib-recursive.0", "common-fib-recursive", { stdout: "fib recursive ok\n" }], + ["conformance/common/pass/file-copy.0", "common-file-copy", { stdout: "file copy ok\n", file: { name: "common-file-copy-output.txt", text: "zero file copy\n" } }], + ["conformance/common/pass/gcd.0", "common-gcd", { stdout: "gcd ok\n" }], + ["conformance/common/pass/json-roundtrip.0", "common-json-roundtrip", { stdout: "json roundtrip ok\n" }], + ["conformance/common/pass/palindrome.0", "common-palindrome", { stdout: "palindrome ok\n" }], + ["conformance/common/pass/prime.0", "common-prime", { stdout: "prime ok\n" }], + ["conformance/common/pass/sieve-small.0", "common-sieve-small", { stdout: "sieve small ok\n" }], + ["conformance/common/pass/sort-small.0", "common-sort-small", { stdout: "sort small ok\n" }], + ["conformance/common/pass/string-search.0", "common-string-search", { stdout: "string search ok\n" }], + ["conformance/common/pass/word-reverse.0", "common-word-reverse", { stdout: "word reverse ok\n" }], +]; + +for (const [fixture, name, expected] of commonPassFixtures) { + const check = await execFileAsync(zero, ["check", "--json", fixture]); + assert.equal(JSON.parse(check.stdout).ok, true); + const graph = await execFileAsync(zero, ["inspect", "--json", fixture]); + const graphBody = JSON.parse(graph.stdout); + assert.equal(graphBody.graph.artifact, fixture.replace(/\.0$/, ".graph")); + assert(graphBody.sourceFiles.includes(fixture) || graphBody.sourceFiles.includes(fixture.split("/").at(-1))); + const size = await execFileAsync(zero, ["size", "--json", fixture]); + assert.equal(JSON.parse(size.stdout).schemaVersion, 1); + await assertCommonRuntimeOrUnsupported(fixture, name, expected); +} + +const commonUnsupportedTarget = await execFileAsync(zero, ["check", "--json", "--target", "linux-musl-x64", "conformance/common/fail/unsupported-target-feature.0"]).catch((error) => error); +assert.notEqual(commonUnsupportedTarget.code, 0); +assert.equal(JSON.parse(commonUnsupportedTarget.stdout).diagnostics[0].code, "TAR002"); + +const compileTimeJson = await execFileAsync(zero, ["check", "--json", "conformance/native/pass/compile-time-v1.0"]); +const compileTimeBody = JSON.parse(compileTimeJson.stdout); +assert.equal(compileTimeBody.ok, true); +assert.equal(compileTimeBody.compileTime.deterministic, true); +assert.equal(compileTimeBody.compileTime.sandbox.filesystem, "denied"); +assert.equal(compileTimeBody.compileTime.sandbox.network, "denied"); +assert.equal(compileTimeBody.compileTime.limits.maxDepth, 64); +assert.equal(compileTimeBody.compileTime.limits.maxSteps, 1024); +assert.equal(compileTimeBody.compileTime.cacheKeyInputs.algorithm, "fnv1a64-zero-meta-v1"); +assert.ok(compileTimeBody.compileTime.cacheKeyInputs.sourceHash); +assert.ok(compileTimeBody.compileTime.meta.supportedFacts.includes("target.abi")); +assert.ok(compileTimeBody.compileTime.meta.supportedFacts.includes("fieldType")); +assert.ok(compileTimeBody.compileTime.meta.supportedFacts.includes("hasEnumCase")); +assert.ok(compileTimeBody.compileTime.staticValues.supported.includes("Bool")); +assert.ok(compileTimeBody.compileTime.staticValues.supported.includes("enum")); +assert.equal(compileTimeBody.compileTime.staticValues.runtimeRegistries, false); +assert.equal(compileTimeBody.compileTime.staticValues.reflectionTables, false); +assert.equal(compileTimeBody.compileTime.reflection.compileTimeOnly, true); +assert.equal(compileTimeBody.compileTime.typedBuilders.status, "limited-v1"); +assert.equal(compileTimeBody.compileTime.typedBuilders.rawTokenStrings, false); +assert.equal(compileTimeBody.safetyFacts.schemaVersion, 1); +assert.equal(compileTimeBody.safetyFacts.bounds.runtimeTraps, true); +assert.equal(compileTimeBody.safetyFacts.bounds.optimizerElision, false); +assert.equal(compileTimeBody.safetyFacts.overflow.runtimeArithmetic, "unchecked-machine-wrap"); +assert.equal(compileTimeBody.safetyFacts.overflow.unchecked, true); +assert.equal(compileTimeBody.safetyFacts.initialization.locals, "initializer-required"); +assert.equal(compileTimeBody.safetyFacts.initialization.maybePayloadReads, "guard-checked"); +assert.equal(compileTimeBody.safetyFacts.aliasing.mutableAliases, "diagnostic"); +assert.equal(compileTimeBody.safetyFacts.mir.invalidMemoryContractsBlockEmission, true); + +const compileTimeGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/native/pass/compile-time-v1.0"]); +const compileTimeGraphBody = JSON.parse(compileTimeGraph.stdout); +assert.equal(compileTimeGraphBody.compileTime.deterministic, true); +assert.equal(compileTimeGraphBody.safetyFacts.profileKey, "small"); +assert.equal(compileTimeGraphBody.safetyFacts.lifetimes.escapedLocalBorrow, "diagnostic"); +const readGateGraph = compileTimeGraphBody.functions.find((item) => item.name === "readGate"); +assert.ok(readGateGraph.staticParams.some((item) => item.name === "enabledFlag" && item.kind === "bool")); +assert.ok(readGateGraph.staticParams.some((item) => item.name === "selectedMode" && item.kind === "enum")); +const gateGraph = compileTimeGraphBody.shapes.find((item) => item.name === "Gate"); +assert.ok(gateGraph.staticParams.some((item) => item.name === "enabledFlag" && item.kind === "bool")); +assert.ok(gateGraph.staticParams.some((item) => item.name === "selectedMode" && item.kind === "enum")); + +const fastCheck = await execFileAsync(zero, ["check", "--json", "--profile", "fast", "examples/hello.0"]); +const fastCheckBody = JSON.parse(fastCheck.stdout); +assert.equal(fastCheckBody.artifact, "examples/hello.graph"); +assert.equal(fastCheckBody.canonicalSource, false); +assert.equal(fastCheckBody.check.lowering, "graph-native-check"); +assert.equal(fastCheckBody.safetyFacts.profile, "release-fast"); +assert.equal(fastCheckBody.safetyFacts.profileKey, "fast"); + +const fastGraph = await execFileAsync(zero, ["inspect", "--json", "--profile", "fast", "examples/hello.0"]); +const fastGraphBody = JSON.parse(fastGraph.stdout); +assert.equal(fastGraphBody.packageCache.profile, "fast"); +assert.equal(fastGraphBody.incrementalInvalidation.profileDependency, "fast"); +assert.equal(fastGraphBody.safetyFacts.profile, "release-fast"); +assert.equal(fastGraphBody.safetyFacts.profileKey, "fast"); + +const buildJsonM6 = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", "linux-musl-x64", "--release", "tiny", "examples/hello.0", "--out", `${outDir}/m6-hello`]); +const buildJsonM6Body = JSON.parse(buildJsonM6.stdout); +assert.equal(buildJsonM6Body.legacy, false); +assert.equal(buildJsonM6Body.legacyBackend, null); +assert.equal(buildJsonM6Body.generatedCBytes, 0); +assert.equal(buildJsonM6Body.profileSemantics.canonical, "tiny"); +assert.equal(buildJsonM6Body.profileSemantics.panicPolicy, "abort"); +assert.equal(buildJsonM6Body.profileSemantics.runtimeMetadataPolicy, "minimum"); +assert.equal(buildJsonM6Body.profileSemantics.profileKey, "tiny"); +assert.equal(buildJsonM6Body.profileSemantics.unwindPolicy, "no-unwind-abort"); +assert.equal(buildJsonM6Body.profileSemantics.boundsPolicy, "checked"); +assert.equal(buildJsonM6Body.profileSemantics.overflowPolicy, "literal-range-checked-runtime-unchecked"); +assert.equal(buildJsonM6Body.profileSemantics.profileBudget.generatedCBytes, 0); +assert.equal(buildJsonM6Body.safetyFacts.profile, "tiny"); +assert.equal(buildJsonM6Body.safetyFacts.bounds.policy, "checked"); +assert.equal(buildJsonM6Body.safetyFacts.bounds.optimizerElision, false); +assert.equal(buildJsonM6Body.safetyFacts.overflow.policy, "literal-range-checked-runtime-unchecked"); +assert.equal(buildJsonM6Body.safetyFacts.overflow.integerLiterals, "range-checked"); +assert.equal(buildJsonM6Body.safetyFacts.ownership.useAfterMove, "diagnostic"); +assert.equal(buildJsonM6Body.profileBudget.helperBudgetPolicy, "pay-as-used-minimum-runtime"); +assert(buildJsonM6Body.profileCatalog.some((item) => item.canonical === "debug" && item.debugInfo === true)); +assert(buildJsonM6Body.profileCatalog.some((item) => item.canonical === "release-fast" && item.boundsPolicy === "checked")); +assert(buildJsonM6Body.profileCatalog.some((item) => item.canonical === "audit" && item.runtimeMetadataPolicy === "maximum")); +assert.equal(buildJsonM6Body.objectBackend.internalIr.callRepresentation, "same-object direct calls for supported direct subsets"); +assert.equal(buildJsonM6Body.objectBackend.objectEmission.path, "direct-elf64-exe"); +assert.equal(buildJsonM6Body.objectBackend.objectEmission.symbols, true); +assert.ok(buildJsonM6Body.objectBackend.linking.linkerFlavor); +assert.equal(buildJsonM6Body.objectBackend.linking.stripArtifacts, false); + +for (const [requestedProfile, canonicalProfile, profileKey] of [ + ["debug", "debug", "debug"], + ["fast", "release-fast", "fast"], + ["small", "release-small", "small"], +]) { + const profileBuild = await execFileAsync(zero, ["build", "--json", "--profile", requestedProfile, "--target", "linux-musl-x64", "examples/hello.0", "--out", `${outDir}/m25-${requestedProfile}-hello`]); + const profileBody = JSON.parse(profileBuild.stdout); + assert.equal(profileBody.generatedCBytes, 0); + assert.equal(profileBody.profileSemantics.canonical, canonicalProfile); + assert.equal(profileBody.profileSemantics.profileKey, profileKey); + assert.equal(profileBody.profileSemantics.boundsPolicy, "checked"); + assert.equal(profileBody.profileSemantics.overflowPolicy, "literal-range-checked-runtime-unchecked"); + assert.equal(profileBody.profileSemantics.profileBudget.generatedCBytes, 0); + assert.equal(profileBody.safetyFacts.profile, canonicalProfile); + assert.equal(profileBody.safetyFacts.profileKey, profileKey); + assert.equal(profileBody.safetyFacts.bounds.optimizerElision, false); + assert.equal(profileBody.profileBudget.cBridgeFallback, false); +} + +const profileSize = await execFileAsync(zero, ["size", "--json", "--profile", "debug", "--target", "linux-musl-x64", "examples/hello.0"]); +const profileSizeBody = JSON.parse(profileSize.stdout); +assert.equal(profileSizeBody.generatedCBytes, 0); +assert.equal(profileSizeBody.graph.artifact, "examples/hello.graph"); +assert.equal(profileSizeBody.graph.lowering, "mapped-final-mir"); +assert.equal(profileSizeBody.profileSemantics.profileKey, "debug"); +assert.equal(profileSizeBody.safetyFacts.profileKey, "debug"); +assert.equal(profileSizeBody.safetyFacts.uncheckedSurfaces[0].surface, "C imports"); +assert.equal(profileSizeBody.sizeBreakdown.profileKey, "debug"); +assert(Array.isArray(profileSizeBody.sizeBreakdown.functions)); +assert(profileSizeBody.sizeBreakdown.sections.some((item) => item.name === "text" && item.retainedBy.includes("retained functions"))); +assert(Array.isArray(profileSizeBody.sizeBreakdown.literals.items)); +assert(Array.isArray(profileSizeBody.sizeBreakdown.stdlibHelpers)); +assert(Array.isArray(profileSizeBody.sizeBreakdown.imports)); +assert(Array.isArray(profileSizeBody.sizeBreakdown.runtimeShims)); +assert(profileSizeBody.sizeBreakdown.debugMetadata.bytes > 0); +assert(profileSizeBody.compilerCaches.some((item) => item.name === "mappedFinalMir" && item.sourceKind === "program-graph" && item.programReconstructed === false)); +assert(profileSizeBody.retentionReasons.some((item) => item.kind === "debugMetadata")); +assert(profileSizeBody.optimizationHints.some((item) => item.id === "profile-debug-metadata")); +assert.equal(profileSizeBody.profileBudget.debugMetadataAllowed, true); + +const directObjOut = `${outDir}/direct-obj-add.o`; +const directObjJson = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "linux-musl-x64", "examples/direct-obj-add.0", "--out", directObjOut]); +const directObjBody = JSON.parse(directObjJson.stdout); +assert.equal(directObjBody.emit, "obj"); +assert.equal(directObjBody.compiler, "zero-elf64"); +assert.equal(directObjBody.generatedCBytes, 0); +assert(directObjBody.loweredIrBytes > 0); +assert.equal(directObjBody.objectBackend.objectEmission.path, "direct-elf64-object"); +await assertElf64Object(directObjOut, "main"); + +const directI64ObjOut = `${outDir}/direct-i64-return.o`; +const directI64ObjJson = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "linux-musl-x64", "examples/direct-i64-return.0", "--out", directI64ObjOut]); +const directI64ObjBody = JSON.parse(directI64ObjJson.stdout); +const directI64ObjBytes = await readFile(directI64ObjOut); +assert.equal(directI64ObjBody.emit, "obj"); +assert.equal(directI64ObjBody.compiler, "zero-elf64"); +assert.equal(directI64ObjBody.generatedCBytes, 0); +assert.equal(directI64ObjBody.objectBackend.objectEmission.path, "direct-elf64-object"); +assert(directI64ObjBytes.includes(Buffer.from([0x48, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]))); +assert(directI64ObjBytes.includes(Buffer.from([0x48, 0x01, 0xc8]))); + +const directWideMainSource = `${outDir}/direct-exe-wide-main.0`; +const directWideMainOut = `${outDir}/direct-exe-wide-main`; +await writeGraphFixture(directWideMainSource, `export c fn main() -> usize { + return 8589934590 +} +`); +const directWideMainJson = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", "linux-musl-x64", directWideMainSource, "--out", directWideMainOut]); +const directWideMainBody = JSON.parse(directWideMainJson.stdout); +const directWideMainBytes = await readFile(directWideMainOut); +assert.equal(directWideMainBody.compiler, "zero-elf64"); +assert.equal(directWideMainBody.objectBackend.objectEmission.path, "direct-elf64-exe"); +assert(!directWideMainBytes.includes(Buffer.from([0x48, 0xc1, 0xe9, 0x20]))); +assert(directWideMainBytes.includes(Buffer.from([0x89, 0xc7, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05]))); + +const directI64MainSource = `${outDir}/direct-exe-i64-main.0`; +const directI64MainOut = `${outDir}/direct-exe-i64-main`; +await writeGraphFixture(directI64MainSource, `export c fn main() -> i64 { + return 8589934590_i64 +} +`); +const directI64MainJson = await execFileAsync(zero, ["build", "--json", "--emit", "exe", "--target", "linux-musl-x64", directI64MainSource, "--out", directI64MainOut]); +const directI64MainBody = JSON.parse(directI64MainJson.stdout); +assert.equal(directI64MainBody.compiler, "zero-elf64"); +assert.equal(directI64MainBody.objectBackend.objectEmission.path, "direct-elf64-exe"); + +const directMachOU64LiteralSource = `${outDir}/direct-macho-u64-literal.0`; +const directMachOU64LiteralOut = `${outDir}/direct-macho-u64-literal.o`; +await writeGraphFixture(directMachOU64LiteralSource, `export c fn main() -> u64 { + let value: u64 = 4294967296 + return value +} +`); +const directMachOU64LiteralReadinessJson = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "--target", "darwin-arm64", directMachOU64LiteralSource]); +const directMachOU64LiteralReadinessBody = JSON.parse(directMachOU64LiteralReadinessJson.stdout); +assert.equal(directMachOU64LiteralReadinessBody.targetReadiness.buildable, true); +const directMachOU64LiteralJson = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "darwin-arm64", directMachOU64LiteralSource, "--out", directMachOU64LiteralOut]); +const directMachOU64LiteralBody = JSON.parse(directMachOU64LiteralJson.stdout); +const directMachOU64LiteralBytes = await readFile(directMachOU64LiteralOut); +assert.equal(directMachOU64LiteralBody.compiler, "zero-macho64"); +assert.equal(directMachOU64LiteralBody.objectBackend.objectEmission.path, "direct-macho64-object"); +assert(directMachOU64LiteralBytes.includes(Buffer.from([0x28, 0x00, 0xc0, 0xf2]))); + +const directMachOU64DivSource = `${outDir}/direct-macho-u64-div.0`; +const directMachOU64DivOut = `${outDir}/direct-macho-u64-div.o`; +await writeGraphFixture(directMachOU64DivSource, `export c fn main(a: u64, b: u64) -> u64 { + return a / b +} +`); +const directMachOU64DivJson = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "darwin-arm64", directMachOU64DivSource, "--out", directMachOU64DivOut]); +const directMachOU64DivBody = JSON.parse(directMachOU64DivJson.stdout); +const directMachOU64DivBytes = await readFile(directMachOU64DivOut); +assert.equal(directMachOU64DivBody.compiler, "zero-macho64"); +assert.equal(directMachOU64DivBody.objectBackend.objectEmission.path, "direct-macho64-object"); +assert(directMachOU64DivBytes.includes(Buffer.from([0x00, 0x09, 0xc9, 0x9a]))); +assert(!directMachOU64DivBytes.includes(Buffer.from([0x00, 0x09, 0xc9, 0x1a]))); + +const directMachOU64ModSource = `${outDir}/direct-macho-u64-mod.0`; +const directMachOU64ModOut = `${outDir}/direct-macho-u64-mod.o`; +await writeGraphFixture(directMachOU64ModSource, `export c fn main(a: u64, b: u64) -> u64 { + return a % b +} +`); +const directMachOU64ModJson = await execFileAsync(zero, ["build", "--json", "--emit", "obj", "--target", "darwin-arm64", directMachOU64ModSource, "--out", directMachOU64ModOut]); +const directMachOU64ModBody = JSON.parse(directMachOU64ModJson.stdout); +const directMachOU64ModBytes = await readFile(directMachOU64ModOut); +assert.equal(directMachOU64ModBody.compiler, "zero-macho64"); +assert.equal(directMachOU64ModBody.objectBackend.objectEmission.path, "direct-macho64-object"); +assert(directMachOU64ModBytes.includes(Buffer.from([0x0a, 0x09, 0xc9, 0x9a]))); +assert(directMachOU64ModBytes.includes(Buffer.from([0x40, 0xa1, 0x09, 0x9b]))); +assert(!directMachOU64ModBytes.includes(Buffer.from([0x0a, 0x09, 0xc9, 0x1a]))); +assert(!directMachOU64ModBytes.includes(Buffer.from([0x40, 0xa1, 0x09, 0x1b]))); + +const metaJsonSuccess = await execFileAsync(zero, ["check", "--json", "conformance/native/pass/meta-typed-target-type.0"]); +const metaJsonSuccessBody = JSON.parse(metaJsonSuccess.stdout); +assert.equal(metaJsonSuccessBody.ok, true); +assert.equal(metaJsonSuccessBody.artifact, "conformance/native/pass/meta-typed-target-type.graph"); +assert.equal(metaJsonSuccessBody.canonicalSource, false); +assert.equal(metaJsonSuccessBody.check.lowering, "graph-native-check"); +assert.equal(metaJsonSuccessBody.compileTime.deterministic, true); + +const stdModuleBasenameCollisionDir = `${outDir}/std-module-basename-collision`; +await mkdir(stdModuleBasenameCollisionDir, { recursive: true }); +const stdModuleBasenameCollisionFixture = `${stdModuleBasenameCollisionDir}/term.0`; +const stdModuleBasenameCollisionGraph = await writeGraphFixture(stdModuleBasenameCollisionFixture, `pub fn main(world: World) -> Void raises { + var buffer: [16]u8 = [0_u8; 16] + let cursor: Maybe> = std.term.cursorUp(buffer, 1_usize) + if cursor.has { + check world.out.write(cursor.value) + check world.out.write("local term collision ok\\n") + } +} +`); +const stdModuleBasenameCollisionCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", stdModuleBasenameCollisionGraph])).stdout); +assert.equal(stdModuleBasenameCollisionCheck.ok, true); +assert(stdModuleBasenameCollisionCheck.graphCompiler.semanticFacts.functions.some((fn) => fn.name === "main")); +assert(stdModuleBasenameCollisionCheck.graphCompiler.semanticFacts.functions.some((fn) => fn.name === "term_cursor_up")); +const stdModuleBasenameCollisionInspect = JSON.parse((await execFileAsync(zero, ["inspect", "--json", stdModuleBasenameCollisionGraph])).stdout); +assert(stdModuleBasenameCollisionInspect.modules.some((module) => module.name === "term")); +assert(stdModuleBasenameCollisionInspect.modules.some((module) => module.name === "std.term")); +if (runnableDirectTarget) { + const stdModuleBasenameCollisionRun = await execFileAsync(zero, ["run", "--out", `${outDir}/std-module-basename-collision-run`, stdModuleBasenameCollisionGraph]); + assert.equal(stdModuleBasenameCollisionRun.stdout, "\x1b[1Alocal term collision ok\n"); +} + +const collectionsUsizeMemory = await execFileAsync(zero, ["mem", "--json", "conformance/native/pass/std-collections-usize-memory.0"]); +const collectionsUsizeMemoryBody = JSON.parse(collectionsUsizeMemory.stdout); +assert.equal(collectionsUsizeMemoryBody.graph.artifact, "conformance/native/pass/std-collections-usize-memory.graph"); +assert.equal(collectionsUsizeMemoryBody.graph.canonicalSource, false); +assert.match(collectionsUsizeMemoryBody.graph.graphHash, /^graph:[0-9a-f]{16}$/); +assert.equal(collectionsUsizeMemoryBody.compilerCaches.every((item) => item.sourceKind === "program-graph" && item.graphHash === collectionsUsizeMemoryBody.graph.graphHash), true); +assert.equal(collectionsUsizeMemoryBody.incrementalInvalidation.sourceKind, "program-graph"); +assert.equal(collectionsUsizeMemoryBody.incrementalInvalidation.changedInputs.graphArtifact, "conformance/native/pass/std-collections-usize-memory.graph"); +assert.equal(collectionsUsizeMemoryBody.memoryBudgets.collectionCapacityBytes, 32); +assert.equal(collectionsUsizeMemoryBody.collectionFacts.FixedStorage.capacityBytes, 32); + +const collectionsMutspanMemory = await execFileAsync(zero, ["mem", "--json", "conformance/native/pass/std-collections-mutspan-memory.0"]); +const collectionsMutspanMemoryBody = JSON.parse(collectionsMutspanMemory.stdout); +assert.equal(collectionsMutspanMemoryBody.memoryBudgets.collectionCapacityBytes, 4); +assert.equal(collectionsMutspanMemoryBody.collectionFacts.FixedStorage.storageSites, 1); +assert.equal(collectionsMutspanMemoryBody.collectionFacts.FixedStorage.capacityBytes, 4); + +const collectionsQueryMemory = await execFileAsync(zero, ["mem", "--json", "conformance/native/pass/std-collections-query-memory.0"]); +const collectionsQueryMemoryBody = JSON.parse(collectionsQueryMemory.stdout); +assert.equal(collectionsQueryMemoryBody.memoryBudgets.collectionCapacityBytes, 2); +assert.equal(collectionsQueryMemoryBody.collectionFacts.FixedStorage.storageSites, 1); +assert.equal(collectionsQueryMemoryBody.collectionFacts.FixedStorage.capacityBytes, 2); +assert.equal(collectionsQueryMemoryBody.collectionFacts.FixedStorage.queryCalls, 1); + +const interfaceStaticUnsupportedTypeFixture = `${outDir}/interface-static-unsupported-type.0`; +const interfaceStaticUnsupportedTypeBody = await writeImportFailureFixture(interfaceStaticUnsupportedTypeFixture, `interface Bad { + fn value() -> u8 +} + +pub fn main() -> Void { +} +`); +assert.equal(interfaceStaticUnsupportedTypeBody.diagnostics[0].code, "STC001"); + +const interfaceMethodStaticUnsupportedTypeFixture = `${outDir}/interface-method-static-unsupported-type.0`; +const interfaceMethodStaticUnsupportedTypeBody = await writeImportFailureFixture(interfaceMethodStaticUnsupportedTypeFixture, `interface Bad { + fn value() -> u8 +} + +pub fn main() -> Void { +} +`); +assert.equal(interfaceMethodStaticUnsupportedTypeBody.diagnostics[0].code, "STC001"); + +const shapeMethodStaticUnsupportedTypeFixture = `${outDir}/shape-method-static-unsupported-type.0`; +const shapeMethodStaticUnsupportedTypeBody = await writeImportFailureFixture(shapeMethodStaticUnsupportedTypeFixture, `type Box { + value: u8, + fn value(self: ref) -> u8 { + return self.value + } +} + +pub fn main() -> Void { +} +`); +assert.equal(shapeMethodStaticUnsupportedTypeBody.diagnostics[0].code, "STC001"); + +const methodUnknownConstraintFixtures = [ + { + name: "interface-method-unknown-constraint", + source: `interface Bad { + fn value() -> u8 +} + +pub fn main() -> Void { +} +`, + }, + { + name: "shape-method-unknown-constraint", + source: `type Box { + value: u8, + + fn value(self: ref) -> u8 { + return self.value + } +} + +pub fn main() -> Void { +} +`, + }, +]; + +for (const fixtureCase of methodUnknownConstraintFixtures) { + const fixture = `${outDir}/${fixtureCase.name}.0`; + const methodUnknownConstraintBody = await writeImportFailureFixture(fixture, fixtureCase.source); + assert.equal(methodUnknownConstraintBody.diagnostics[0].code, "IFC001"); +} + +const duplicateStaticGenericFixtures = [ + { + name: "function-duplicate-static-param", + message: /duplicate generic parameter/, + source: `fn id() -> u8 { + return 1 +} + +pub fn main() -> Void { +} +`, + }, + { + name: "interface-duplicate-static-param", + message: /duplicate generic parameter/, + source: `interface Bad { + fn value() -> u8 +} + +pub fn main() -> Void { +} +`, + }, + { + name: "shape-method-duplicate-static-param", + message: /duplicate generic parameter/, + source: `type Box { + value: u8, + + fn value(self: ref) -> u8 { + return self.value + } +} + +pub fn main() -> Void { +} +`, + }, + { + name: "shape-method-static-shadows-shape-static", + message: /shadows outer generic parameter/, + source: `type Box { + value: [N]u8, + + fn len(self: ref) -> usize { + return N + } +} + +pub fn main() -> Void { +} +`, + }, + { + name: "shape-method-static-self-param", + message: /generic type parameter shadows Self type/, + source: `type Box { + value: u8, + + fn value(self: ref) -> usize { + return Self + } +} + +pub fn main() -> Void { +} +`, + }, +]; + +for (const fixtureCase of duplicateStaticGenericFixtures) { + const fixture = `${outDir}/${fixtureCase.name}.0`; + const duplicateStaticBody = await writeImportFailureFixture(fixture, fixtureCase.source); + assert.equal(duplicateStaticBody.diagnostics[0].code, "NAM004"); + assert.match(duplicateStaticBody.diagnostics[0].message, fixtureCase.message); +} + +for (const value of ["4_", "M"]) { + const fixture = `${outDir}/interface-static-constraint-${value.replace(/[^A-Za-z0-9]/g, "_")}.0`; + const interfaceStaticConstraintBody = await writeImportFailureFixture(fixture, `interface First { + fn first(self: ref) -> u8 +} + +fn readFirst>(value: ref) -> u8 { + return T.first(value) +} + +pub fn main() -> Void { +} +`); + assert.equal(interfaceStaticConstraintBody.diagnostics[0].code, "STC002"); + assert.equal(interfaceStaticConstraintBody.diagnostics[0].actual, value); +} + +const shapeMethodStaticParamFixture = `${outDir}/shape-method-static-param.0`; +await writeGraphFixture(shapeMethodStaticParamFixture, `type Box { + value: u8, + + fn tag(self: ref) -> usize { + return N + } + + fn value() -> usize { + return N + } +} + +pub fn main() -> Void { + let box: Box = Box { value: 1 } + let receiverTag: usize = box.tag<4>() + let namespaceTag: usize = Box.value<8>() +} +`); +const shapeMethodStaticParamJson = await execFileAsync(zero, ["check", "--json", shapeMethodStaticParamFixture]); +const shapeMethodStaticParamBody = JSON.parse(shapeMethodStaticParamJson.stdout); +assert.equal(shapeMethodStaticParamBody.ok, true); +const shapeMethodStaticParamGraph = await execFileAsync(zero, ["inspect", "--json", shapeMethodStaticParamFixture]); +const shapeMethodStaticParamGraphBody = JSON.parse(shapeMethodStaticParamGraph.stdout); +const shapeMethodStaticBox = shapeMethodStaticParamGraphBody.shapes.find((item) => item.name === "Box"); +assert(shapeMethodStaticBox); +const shapeMethodStaticTag = shapeMethodStaticBox.methods.find((item) => item.name === "tag"); +assert(shapeMethodStaticTag); +assert(shapeMethodStaticTag.staticParams.some((item) => item.name === "N" && item.type === "usize" && item.staticDispatch === true)); + +const shapeMethodStaticCanonicalFixture = `${outDir}/shape-method-static-canonical.0`; +await writeGraphFixture(shapeMethodStaticCanonicalFixture, `type Box { + fn take(a: [N]u8, b: [N]u8) -> usize { + return N + } +} + +pub fn main() -> Void { + let a: [4]u8 = [1, 2, 3, 4] + let b: [0x4]u8 = [1, 2, 3, 4] + let inferred: usize = Box.take(a, b) + let explicit: usize = Box.take<0x4>(a, b) +} +`); +const shapeMethodStaticCanonicalJson = await execFileAsync(zero, ["check", "--json", shapeMethodStaticCanonicalFixture]); +const shapeMethodStaticCanonicalBody = JSON.parse(shapeMethodStaticCanonicalJson.stdout); +assert.equal(shapeMethodStaticCanonicalBody.ok, true); + +const interfaceMethodStaticParamFixture = `${outDir}/interface-method-static-param.0`; +await writeGraphFixture(interfaceMethodStaticParamFixture, `interface Width { + fn width(self: ref) -> usize +} + +type Bytes { + value: u8, + + fn width(self: ref) -> usize { + return N + } +} + +fn readWidth>(value: ref) -> usize { + return T.width<4>(value) +} + +pub fn main() -> Void { + let bytes: Bytes = Bytes { value: 1 } + let width: usize = readWidth(&bytes) +} +`); +const interfaceMethodStaticParamJson = await execFileAsync(zero, ["check", "--json", interfaceMethodStaticParamFixture]); +const interfaceMethodStaticParamBody = JSON.parse(interfaceMethodStaticParamJson.stdout); +assert.equal(interfaceMethodStaticParamBody.ok, true); +const interfaceMethodStaticParamGraph = await execFileAsync(zero, ["inspect", "--json", interfaceMethodStaticParamFixture]); +const interfaceMethodStaticParamGraphBody = JSON.parse(interfaceMethodStaticParamGraph.stdout); +const interfaceMethodStaticWidth = interfaceMethodStaticParamGraphBody.interfaces.find((item) => item.name === "Width"); +assert(interfaceMethodStaticWidth); +const interfaceMethodStaticWidthMethod = interfaceMethodStaticWidth.methods.find((item) => item.name === "width"); +assert(interfaceMethodStaticWidthMethod); +assert(interfaceMethodStaticWidthMethod.staticParams.some((item) => item.name === "N" && item.type === "usize" && item.staticDispatch === true)); +const interfaceMethodStaticBytes = interfaceMethodStaticParamGraphBody.shapes.find((item) => item.name === "Bytes"); +assert(interfaceMethodStaticBytes); +const interfaceMethodStaticBytesWidth = interfaceMethodStaticBytes.methods.find((item) => item.name === "width"); +assert(interfaceMethodStaticBytesWidth); +assert(interfaceMethodStaticBytesWidth.staticParams.some((item) => item.name === "N" && item.type === "usize" && item.staticDispatch === true)); + +const interfaceMethodStaticRenamedParamFixture = `${outDir}/interface-method-static-renamed-param.0`; +await writeGraphFixture(interfaceMethodStaticRenamedParamFixture, `interface Width { + fn width(self: ref, bytes: [N]u8) -> [N]u8 +} + +type Bytes { + value: u8, + + fn width(self: ref, bytes: [M]u8) -> [M]u8 { + return bytes + } +} + +fn readWidth>(value: ref, bytes: [4]u8) -> [4]u8 { + return T.width<4>(value, bytes) +} + +pub fn main() -> Void { + let bytes: Bytes = Bytes { value: 1 } + let output: [4]u8 = readWidth(&bytes, [1, 2, 3, 4]) +} +`); +const interfaceMethodStaticRenamedParamJson = await execFileAsync(zero, ["check", "--json", interfaceMethodStaticRenamedParamFixture]); +const interfaceMethodStaticRenamedParamBody = JSON.parse(interfaceMethodStaticRenamedParamJson.stdout); +assert.equal(interfaceMethodStaticRenamedParamBody.ok, true); +const interfaceMethodStaticRenamedParamGraph = await execFileAsync(zero, ["inspect", "--json", interfaceMethodStaticRenamedParamFixture]); +const interfaceMethodStaticRenamedParamGraphBody = JSON.parse(interfaceMethodStaticRenamedParamGraph.stdout); +const interfaceMethodStaticRenamedWidth = interfaceMethodStaticRenamedParamGraphBody.interfaces.find((item) => item.name === "Width"); +assert(interfaceMethodStaticRenamedWidth); +const interfaceMethodStaticRenamedWidthMethod = interfaceMethodStaticRenamedWidth.methods.find((item) => item.name === "width"); +assert(interfaceMethodStaticRenamedWidthMethod); +assert(interfaceMethodStaticRenamedWidthMethod.staticParams.some((item) => item.name === "N" && item.type === "usize" && item.staticDispatch === true)); +const interfaceMethodStaticRenamedBytes = interfaceMethodStaticRenamedParamGraphBody.shapes.find((item) => item.name === "Bytes"); +assert(interfaceMethodStaticRenamedBytes); +const interfaceMethodStaticRenamedBytesWidth = interfaceMethodStaticRenamedBytes.methods.find((item) => item.name === "width"); +assert(interfaceMethodStaticRenamedBytesWidth); +assert(interfaceMethodStaticRenamedBytesWidth.staticParams.some((item) => item.name === "M" && item.type === "usize" && item.staticDispatch === true)); + +const staticInterfaceReturnMismatchFixture = `${outDir}/static-interface-return-mismatch.0`; +const staticInterfaceReturnMismatchBody = await writeImportFailureFixture(staticInterfaceReturnMismatchFixture, `interface Sized { + fn bytes(self: ref) -> [N]u8 +} + +type Bytes { + items: [N]u8, + + fn bytes(self: ref) -> [N]u8 { + return self.items + } +} + +fn read, static N: usize>(value: ref) -> [N]u8 { + return T.bytes(value) +} + +pub fn main() -> Void { + let bytes: Bytes<4> = Bytes { items: [1, 2, 3, 4] } + let out: [3]u8 = read, 3>(&bytes) +} +`); +assert.equal(staticInterfaceReturnMismatchBody.diagnostics[0].code, "IFC004"); +assert.match(staticInterfaceReturnMismatchBody.diagnostics[0].expected, /\[3\]u8/); +assert.match(staticInterfaceReturnMismatchBody.diagnostics[0].actual, /\[4\]u8/); + +const shapeMethodGenericConstraintFixture = `${outDir}/shape-method-generic-constraint.0`; +const shapeMethodGenericConstraintBody = await writeImportFailureFixture(shapeMethodGenericConstraintFixture, `interface NeedsMethod { + fn need(self: ref) -> u8 +} + +type Box { + value: u8, + + fn accept>(self: ref, item: T) -> u8 { + return self.value + } +} + +type Plain { + value: u8, +} + +pub fn main() -> Void { + let box: Box = Box { value: 1 } + let plain: Plain = Plain { value: 2 } + let out: u8 = box.accept(plain) +} +`); +assert.equal(shapeMethodGenericConstraintBody.diagnostics[0].code, "IFC002"); + +const interfaceMethodGenericConstraintFixture = `${outDir}/interface-method-generic-constraint.0`; +const interfaceMethodGenericConstraintBody = await writeImportFailureFixture(interfaceMethodGenericConstraintFixture, `interface NeedsMethod { + fn need(self: ref) -> u8 +} + +interface Caller { + fn accept>(self: ref, item: U) -> u8 +} + +type Box { + value: u8, + + fn accept>(self: ref, item: U) -> u8 { + return self.value + } +} + +type Plain { + value: u8, +} + +fn read>(value: ref, plain: Plain) -> u8 { + return T.accept(value, plain) +} + +pub fn main() -> Void { + let box: Box = Box { value: 1 } + let plain: Plain = Plain { value: 2 } + let out: u8 = read(&box, plain) +} +`); +assert.equal(interfaceMethodGenericConstraintBody.diagnostics[0].code, "IFC002"); + +const interfaceMethodGenericMismatchFixtures = [ + { + name: "interface-method-generic-constraint-mismatch", + code: "IFC005", + message: /constraint does not match/, + source: `interface NeedsA { + fn needA(self: ref) -> u8 +} + +interface NeedsB { + fn needB(self: ref) -> u8 +} + +interface Caller { + fn accept>(self: ref, item: U) -> u8 +} + +type Box { + value: u8, + + fn accept>(self: ref, item: U) -> u8 { + return self.value + } +} + +type A { + value: u8, + + fn needA(self: ref) -> u8 { + return self.value + } +} + +fn read>(value: ref, item: A) -> u8 { + return T.accept(value, item) +} + +pub fn main() -> Void { + let box: Box = Box { value: 1 } + let a: A = A { value: 2 } + let out: u8 = read(&box, a) +} +`, + }, + { + name: "interface-method-missing-static-param", + code: "IFC003", + source: `interface Width { + fn width(self: ref) -> usize +} + +type Bytes { + value: u8, + + fn width(self: ref) -> usize { + return 1 + } +} + +fn readWidth>(value: ref) -> usize { + return T.width<4>(value) +} + +pub fn main() -> Void { + let bytes: Bytes = Bytes { value: 1 } + let width: usize = readWidth(&bytes) +} +`, + }, + { + name: "interface-method-extra-static-param", + code: "IFC003", + source: `interface Width { + fn width(self: ref) -> usize +} + +type Bytes { + value: u8, + + fn width(self: ref) -> usize { + return N + } +} + +fn readWidth>(value: ref) -> usize { + return T.width(value) +} + +pub fn main() -> Void { + let bytes: Bytes = Bytes { value: 1 } + let width: usize = readWidth(&bytes) +} +`, + }, + { + name: "interface-method-static-param-type-mismatch", + code: "IFC005", + source: `interface Width { + fn width(self: ref) -> usize +} + +type Bytes { + value: u8, + + fn width(self: ref) -> usize { + return 1 + } +} + +fn readWidth>(value: ref) -> usize { + return T.width<4>(value) +} + +pub fn main() -> Void { + let bytes: Bytes = Bytes { value: 1 } + let width: usize = readWidth(&bytes) +} +`, + }, +]; + +for (const fixtureCase of interfaceMethodGenericMismatchFixtures) { + const fixture = `${outDir}/${fixtureCase.name}.0`; + const interfaceMethodGenericMismatchBody = await writeImportFailureFixture(fixture, fixtureCase.source); + assert.equal(interfaceMethodGenericMismatchBody.diagnostics[0].code, fixtureCase.code); + if (fixtureCase.message) assert.match(interfaceMethodGenericMismatchBody.diagnostics[0].message, fixtureCase.message); +} + +const badTargetNameJson = await execFileAsync(zero, ["check", "--json", "--target", "not-a-target", "examples/hello.0"]).catch((error) => error); +assert.notEqual(badTargetNameJson.code, 0); +const badTargetNameBody = JSON.parse(badTargetNameJson.stdout); +assert.equal(badTargetNameBody.diagnostics[0].code, "TAR001"); +assert.match(badTargetNameBody.diagnostics[0].message, /unknown target/); +assert.match(badTargetNameBody.diagnostics[0].expected, /zero targets/); +assert.equal(badTargetNameBody.diagnostics[0].actual, "not-a-target"); +assert.match(badTargetNameBody.diagnostics[0].help, /zero targets/); +assert.equal(badTargetNameBody.diagnostics[0].fixSafety, "requires-human-review"); +assert.equal(badTargetNameBody.diagnostics[0].repair.id, "manual-review"); +assert.match(badTargetNameBody.diagnostics[0].related[0].message, /not-a-target/); + +const explainTar002 = await execFileAsync(zero, ["explain", "--json", "TAR002"]); +const explainTar002Body = JSON.parse(explainTar002.stdout); +assert.equal(explainTar002Body.schemaVersion, 1); +assert.equal(explainTar002Body.code, "TAR002"); +assert.equal(explainTar002Body.repair.id, "choose-target-with-required-capability"); + +const explainText = await execFileAsync(zero, ["explain", "TYP009"]); +assert.match(explainText.stdout, /Mutable storage required/); + +const importGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/imports"]); +const importGraphBody = JSON.parse(importGraph.stdout); +assert.deepEqual(importGraphBody.imports, []); +assert.equal(importGraphBody.sourceFiles.length, 3); +assert.equal(importGraphBody.sourceMaps.length, 3); +assert(importGraphBody.sourceMaps.every((item) => item.columnUnit === "utf8-byte")); +assert.deepEqual(importGraphBody.targets.map((item) => item.name), ["cli"]); +assert.deepEqual(importGraphBody.modules.map((item) => item.name), ["math", "types", "main"]); +assert.deepEqual(importGraphBody.importEdges.map((item) => `${item.from}->${item.to}`), ["main->math", "main->types"]); +assert.deepEqual(importGraphBody.importEdges.map((item) => `${item.from}->${item.to}:${item.sourceRange.path}:${item.sourceRange.start.line}:${item.sourceRange.start.column}:${item.sourceRange.end.column}`), [ + "main->math:src/main.0:1:1:9", + "main->types:src/main.0:3:1:10", +]); +assert.deepEqual(importGraphBody.useImports.map((item) => `${item.from}->${item.to}:${item.kind}:${item.line}:${item.column}`), [ + "main->math:package-local:1:1", + "main->types:package-local:3:1", +]); +assert.deepEqual(importGraphBody.useImports.map((item) => item.resolvedPath), [ + "src/math.0", + "src/types.0", +]); +assert(importGraphBody.useImports.every((item) => item.sourceRange.columnUnit === "utf8-byte")); +assert(importGraphBody.functions.some((item) => item.name === "add_one" && item.returnType === "i32")); +assert(importGraphBody.functions.some((item) => item.name === "main" && item.returnType === "Void" && item.effects.includes("world"))); + +const whitespaceUsePackage = `${outDir}/use-whitespace-package`; +await mkdir(`${whitespaceUsePackage}/src`, { recursive: true }); +await writeZeroToml(whitespaceUsePackage, { + package: { name: "use-whitespace-package", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + deps: {}, +}); +await mkdir(`${whitespaceUsePackage}/src/math`, { recursive: true }); +await writeFile(`${whitespaceUsePackage}/src/math.0`, 'fn add_one(value: i32) -> i32 {\n return value + 1\n}\n'); +await writeFile(`${whitespaceUsePackage}/src/math/util.0`, 'fn add_two(value: i32) -> i32 {\n return value + 2\n}\n'); +await writeFile(`${whitespaceUsePackage}/src/types.0`, 'type Point {\n value: i32,\n}\n'); +await writeFile(`${whitespaceUsePackage}/src/main.0`, 'use math\nuse math . util\nuse types as model\n\npub fn main(world: World) -> Void raises {\n let point: Point = Point { value: add_two(40) }\n if point.value == add_one(41) {\n check world.out.write("whitespace imports pass\\n")\n }\n}\n'); +await importPackageGraph(whitespaceUsePackage); +const whitespaceUseCheck = await execFileAsync(zero, ["check", "--json", whitespaceUsePackage]); +assert.equal(JSON.parse(whitespaceUseCheck.stdout).ok, true); +const whitespaceUseGraph = await execFileAsync(zero, ["inspect", "--json", whitespaceUsePackage]); +const whitespaceUseGraphBody = JSON.parse(whitespaceUseGraph.stdout); +assert.deepEqual(whitespaceUseGraphBody.useImports.map((item) => `${item.from}->${item.to}:${item.kind}:${item.alias ?? "null"}:${item.sourceRange.end.column}`), [ + "main->math:package-local:null:9", + "main->math.util:package-local:null:14", + "main->types:package-local:model:10", +]); +assert.deepEqual(whitespaceUseGraphBody.importEdges.map((item) => `${item.from}->${item.to}`), ["main->math", "main->math.util", "main->types"]); + +const packageUseGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/package"]); +const packageUseGraphBody = JSON.parse(packageUseGraph.stdout); +assert.deepEqual(packageUseGraphBody.useImports.map((item) => `${item.from}->${item.to}:${item.kind}:${item.resolvedPath ?? "null"}`), [ + "main->std.codec:stdlib:null", + "main->std.parse:stdlib:null", + "main->std.time:stdlib:null", + "main->types:package-local:src/types.0", +]); + +const resourceGraph = await execFileAsync(zero, ["inspect", "--json", "examples/resource-cli"]); +const resourceGraphBody = JSON.parse(resourceGraph.stdout); +assert(resourceGraphBody.targets.some((item) => item.name === "cli" && item.kind === "exe")); +assert.deepEqual(resourceGraphBody.importEdges.map((item) => `${item.from}->${item.to}`), ["main->config", "main->payload"]); +assert(resourceGraphBody.requiresCapabilities.includes("fs")); +assert(resourceGraphBody.functions.some((item) => item.name === "outputDir" && item.effects.includes("env"))); +const resourceMainFunction = resourceGraphBody.functions.find((item) => item.name === "main"); +assert(resourceMainFunction.effects.includes("fs")); +assert.equal(resourceMainFunction.allocationBehavior, "no heap allocation"); +assert.equal(resourceMainFunction.ownership.params[0].type, "World"); + +const memoryGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "linux-musl-x64", "examples/memory-package"]); +const memoryGraphBody = JSON.parse(memoryGraph.stdout); +assert.deepEqual(memoryGraphBody.importEdges.map((item) => `${item.from}->${item.to}`), ["main->buffer", "main->checksum"]); +assert(memoryGraphBody.requiresCapabilities.includes("memory")); +assert(!memoryGraphBody.requiresCapabilities.includes("fs")); +assert.equal(memoryGraphBody.targetSupport.fsAvailable, true); +assert.equal(memoryGraphBody.targetSupport.requiredCapabilitySupport.status, "supported"); +assert.equal(memoryGraphBody.functions.filter((item) => item.public).length, 5); +assert(memoryGraphBody.stdlibHelpers.some((helper) => helper.name === "std.mem.copy" && helper.targetSupport === "target-neutral")); +assert(memoryGraphBody.stdlibHelpers.some((helper) => helper.name === "std.fs.createOrRaise" && helper.targetSupport === "host")); +const memCopyHelper = memoryGraphBody.stdlibHelpers.find((helper) => helper.name === "std.mem.copy"); +assert.equal(memCopyHelper.module, "std.mem"); +assert(memCopyHelper.effects.includes("memory")); +assert.equal(memCopyHelper.errorBehavior, "infallible"); +assert.match(memCopyHelper.ownershipNotes, /caller-owned storage/); +assert.equal(memCopyHelper.example, "examples/memory-primitives.0"); +assert.equal(memCopyHelper.apiStability, "bootstrap-stable"); + +const lexerTokens = await execFileAsync(zero, ["tokens", "--json", "conformance/lexer/compiler-smoke.0"]); +const lexerTokensBody = JSON.parse(lexerTokens.stdout); +assert.equal(lexerTokensBody.schemaVersion, 1); +assert.equal(lexerTokensBody.syntax, "canonical"); +assert.deepEqual(lexerTokensBody.tokens.slice(0, 4).map((token) => `${token.kind}:${token.text}`), [ + "word:use", + "word:std", + "symbol:.", + "word:mem", +]); +assert.deepEqual(lexerTokensBody.tokens.filter((token) => token.kind === "number").map((token) => token.text), ["123", "0xff", "0b101", "42_u8"]); +assert.deepEqual(lexerTokensBody.tokens.filter((token) => token.kind === "string" || token.kind === "char").map((token) => `${token.kind}:${token.text}`), [ + 'string:"hi"', + "char:'x'", +]); +assert.equal(lexerTokensBody.tokens[0].line, 1); +assert.equal(lexerTokensBody.tokens[0].column, 1); +assert.equal(lexerTokensBody.tokens[0].offset, 0); +assert.equal(lexerTokensBody.tokens[0].length, 3); +assert.equal(lexerTokensBody.tokens[5].offset, 12); +assert.equal(lexerTokensBody.tokens[5].length, 1); +assert.equal(lexerTokensBody.tokens[8].kind, "word"); +assert.equal(lexerTokensBody.tokens[8].text, "main"); +assert.equal(lexerTokensBody.tokens[8].line, 3); +assert.equal(lexerTokensBody.tokens[8].column, 8); +assert.equal(lexerTokensBody.tokens.at(-1).kind, "eof"); +assert.equal(lexerTokensBody.tokens.at(-1).length, 0); + +const parseTree = await execFileAsync(zero, ["parse", "--json", "conformance/format/functions-blocks.0"]); +const parseTreeBody = JSON.parse(parseTree.stdout); +assert.equal(parseTreeBody.schemaVersion, 1); +assert.equal(parseTreeBody.root.kind, "module"); +assert.equal(parseTreeBody.root.shapeCount, 0); +assert.equal(parseTreeBody.root.enumCount, 0); +assert.equal(parseTreeBody.root.choiceCount, 0); +assert.equal(parseTreeBody.root.functionCount, 2); +assert.equal(parseTreeBody.functions[0].name, "helper"); +assert.equal(parseTreeBody.functions[0].paramCount, 1); +assert.deepEqual(parseTreeBody.functions[0].bodyKinds, ["if"]); +assert.equal(parseTreeBody.functions[1].name, "main"); +assert.equal(parseTreeBody.functions[1].paramCount, 0); +assert.deepEqual(parseTreeBody.functions[1].bodyKinds, ["let", "while"]); + +const constGraph = await execFileAsync(zero, ["inspect", "--json", "examples/const-arithmetic.0"]); +const constGraphBody = JSON.parse(constGraph.stdout); +assert(constGraphBody.consts.some((item) => item.name === "answer" && item.type === "i32")); + +const genericPairGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/generic-shape-multi.0"]); +const genericPairGraphBody = JSON.parse(genericPairGraph.stdout); +assert(genericPairGraphBody.functions.some((item) => item.name === "makePair" && item.generic === true && item.returnType === "Pair")); +assert(genericPairGraphBody.shapes.some((item) => item.name === "Pair" && item.generic === true && item.typeParams.join(",") === "T,U")); + +const staticValueGraph = await execFileAsync(zero, ["inspect", "--json", "examples/static-value-params.0"]); +const staticValueGraphBody = JSON.parse(staticValueGraph.stdout); +const fixedVecShape = staticValueGraphBody.shapes.find((item) => item.name === "FixedVec"); +assert(fixedVecShape); +assert(fixedVecShape.staticParams.some((item) => item.name === "N" && item.type === "usize" && item.staticDispatch === true)); +assert(staticValueGraphBody.functions.some((item) => item.name === "first" && item.staticParams.some((param) => param.name === "N"))); + +const fixedVecGraph = await execFileAsync(zero, ["inspect", "--json", "examples/fixed-vec.0"]); +const fixedVecGraphBody = JSON.parse(fixedVecGraph.stdout); +const fixedVecMethodsShape = fixedVecGraphBody.shapes.find((item) => item.name === "FixedVec"); +assert(fixedVecMethodsShape); +const pushMethod = fixedVecMethodsShape.methods.find((item) => item.name === "push"); +assert(pushMethod); +assert.equal(pushMethod.inheritedShapeParams, true); +assert.deepEqual(pushMethod.shapeTypeParams, ["T", "N"]); +assert(pushMethod.shapeStaticParams.some((item) => item.name === "N" && item.staticDispatch === true)); + +const aliasGraph = await execFileAsync(zero, ["inspect", "--json", "examples/type-alias.0"]); +const aliasGraphBody = JSON.parse(aliasGraph.stdout); +assert(aliasGraphBody.aliases.some((item) => item.name === "BytePair" && item.target === "Pair")); + +const staticMethodGraph = await execFileAsync(zero, ["inspect", "--json", "examples/static-method.0"]); +const staticMethodGraphBody = JSON.parse(staticMethodGraph.stdout); +const counterShape = staticMethodGraphBody.shapes.find((item) => item.name === "Counter"); +assert(counterShape); +assert(counterShape.methods.some((item) => item.name === "add" && item.staticDispatch === true && item.returnType === "i32")); + +const staticInterfaceGraph = await execFileAsync(zero, ["inspect", "--json", "examples/static-interface.0"]); +const staticInterfaceGraphBody = JSON.parse(staticInterfaceGraph.stdout); +assert(staticInterfaceGraphBody.interfaces.some((item) => item.name === "Readable" && item.staticOnly === true)); +assert(staticInterfaceGraphBody.functions.some((item) => item.name === "readValue" && item.constraints.some((constraint) => constraint.interface === "Readable" && constraint.staticDispatch === true))); + +const callResolutionGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/call-resolution-inspection.0"]); +const callResolutionGraphBody = JSON.parse(callResolutionGraph.stdout); +const callFacts = callResolutionGraphBody.callResolution; +assert.equal(callFacts.schemaVersion, 1); +for (const kind of ["function", "stdlib", "receiver", "shape_namespace", "constrained_interface", "concrete_constrained_shape", "choice_constructor"]) { + assert(callFacts.supportedKinds.includes(kind)); + assert(callFacts.calls.some((item) => item.kind === kind), `missing call-resolution fact for ${kind}`); +} +assert(callFacts.calls.some((item) => item.kind === "function" && item.calleeName === "add" && item.returnType === "i32" && item.expectedArgCount === 2)); +assert(callFacts.calls.some((item) => item.kind === "function" && item.calleeName === "id" && item.bindings.some((binding) => binding.name === "T" && binding.type === "i32"))); +assert(callFacts.calls.some((item) => item.kind === "function" && item.calleeName === "id" && item.owner === "wrap" && item.instantiationDepth === 1 && item.instantiatedBy === "main" && item.returnType === "i32")); +assert(callFacts.calls.some((item) => item.kind === "stdlib" && item.calleeName === "std.mem.len" && item.returnType === "usize" && item.args.some((arg) => arg.actualType === "String"))); +assert(callFacts.calls.some((item) => item.kind === "choice_constructor" && item.calleeName === "Event.key" && item.choice === "Event" && item.choiceCase === "key")); +assert(callFacts.calls.some((item) => item.kind === "shape_namespace" && item.calleeName === "read" && item.shape === "Counter")); +assert(callFacts.calls.some((item) => item.kind === "receiver" && item.calleeName === "bump" && item.shape === "Counter" && item.paramOffset === 1)); +assert(callFacts.calls.some((item) => item.kind === "constrained_interface" && item.calleeName === "read" && item.interface === "Readable" && item.owner === "readValue")); +assert(callFacts.calls.some((item) => item.kind === "concrete_constrained_shape" && item.calleeName === "read" && item.shape === "Counter" && item.instantiatedBy === "main")); +assert(callFacts.calls.some((item) => item.calleeName === "add" && item.path === "call-resolution-inspection.0")); +assert(callFacts.calls.some((item) => item.kind === "function" && item.calleeName === "defaultCount" && item.owner === "Counter.value" && item.returnType === "i32")); +assert(callFacts.calls.some((item) => item.kind === "function" && item.calleeName === "risky" && item.fallible === true && item.errors.includes("BadInput"))); +assert(callFacts.calls.some((item) => item.kind === "receiver" && item.calleeName === "checkedRead" && item.fallible === true && item.errors.includes("EmptyCounter"))); + +const callResolutionMemGetGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/native/pass/checked-bounds-get.0"]); +const callResolutionMemGetFacts = JSON.parse(callResolutionMemGetGraph.stdout).callResolution; +assert(callResolutionMemGetFacts.calls.some((item) => item.kind === "stdlib" && item.calleeName === "std.mem.get" && item.returnType === "Maybe" && item.args.some((arg) => arg.paramIndex === 0 && arg.actualType === "[3]u8"))); + +const callResolutionGenericShapeGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/generic-shape-methods.0"]); +const callResolutionGenericShapeFacts = JSON.parse(callResolutionGenericShapeGraph.stdout).callResolution; +assert(callResolutionGenericShapeFacts.calls.some((item) => + item.kind === "stdlib" && + item.calleeName === "std.mem.get" && + item.owner === "FixedVec.get" && + item.instantiationDepth === 1 && + item.instantiatedBy === "main" && + item.returnType === "Maybe" && + item.args.some((arg) => arg.paramIndex === 0 && arg.actualType === "[4]u8") +)); + +const callResolutionFsReadGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/native/pass/std-fs-resource.0"]); +const callResolutionFsReadFacts = JSON.parse(callResolutionFsReadGraph.stdout).callResolution; +assert(callResolutionFsReadFacts.calls.some((item) => item.kind === "stdlib" && item.calleeName === "std.fs.read" && item.returnType === "Maybe" && item.args.some((arg) => arg.paramIndex === 0 && arg.expectedType === "mutref" && arg.actualType === "mutref"))); + +const callResolutionPackageGraph = await execFileAsync(zero, ["inspect", "--json", "examples/systems-package"]); +const callResolutionPackageFacts = JSON.parse(callResolutionPackageGraph.stdout).callResolution; +assert(callResolutionPackageFacts.calls.some((item) => item.calleeName === "cleanup" && item.path === "src/main.0" && item.line === 18)); + +const callResolutionEdgeGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/call-resolution-edge-cases.0"]); +const callResolutionEdgeFacts = JSON.parse(callResolutionEdgeGraph.stdout).callResolution; +assert(callResolutionEdgeFacts.calls.some((item) => item.kind === "function" && item.calleeName === "add" && item.owner === "constTotal" && item.returnType === "i32")); +assert(callResolutionEdgeFacts.calls.some((item) => item.kind === "stdlib" && item.calleeName === "std.mem.len" && item.owner === "main" && item.args.some((arg) => arg.paramIndex === 0 && arg.actualType === "String"))); + +const programGraphBody = JSON.parse((await execFileAsync(zero, ["inspect", "--json", "examples/hello.0"])).stdout).programGraph; +const programGraphBodyAgain = JSON.parse((await execFileAsync(zero, ["inspect", "--json", "examples/hello.0"])).stdout).programGraph; +const programGraphDump = (await execFileAsync(zero, ["dump", "examples/hello.0"])).stdout; +const programGraphDumpAgain = (await execFileAsync(zero, ["dump", "examples/hello.0"])).stdout; +const programGraphDumpJson = JSON.parse((await execFileAsync(zero, ["dump", "--json", "examples/hello.0"])).stdout); +const programGraphDumpPath = `${outDir}/hello.program-graph`; +const programGraphDumpJsonPath = `${outDir}/hello.dump-json.program-graph`; +const programGraphCanonicalPath = `${outDir}/hello.canonical.program-graph`; +const programGraphViewPath = `${outDir}/hello.graph-view.0`; +const programGraphArtifactRoundtripPath = `${outDir}/hello.roundtrip.program-graph`; +const programGraphSourceFixturePath = "conformance/program-graph/hello.0"; +const programGraphSourceFixturePackage = "conformance/program-graph"; +const programGraphSourceFixtureStorePath = "conformance/program-graph/zero.graph"; +const programGraphSourceFixtureRunPath = `${outDir}/program-graph-fixture-run`; +const programGraphSourceFreePackage = `${outDir}/program-graph-source-free`; +const programGraphSourceFreeBuildPath = `${outDir}/program-graph-source-free-build`; +const programGraphSourceFreeRunPath = `${outDir}/program-graph-source-free-run`; +const programGraphSourceFreeStdStrPackage = `${outDir}/program-graph-source-free-std-str`; +const programGraphCrmApiBuildPath = `${outDir}/program-graph-crm-api-build`; +const programGraphAuthoringPackage = `${outDir}/program-graph-authoring`; +const programGraphAuthoringRunPath = `${outDir}/program-graph-authoring-run`; +const programGraphAuthoringRunAfterHumanEditPath = `${outDir}/program-graph-authoring-run-human-edit`; +const programGraphBuilderOpsPackage = `${outDir}/program-graph-builder-ops`; +const programGraphBuilderOpsRunPath = `${outDir}/program-graph-builder-ops-run`; +const programGraphLoopTestPackage = `${outDir}/program-graph-loop-test`; +const programGraphArrayLengthPackage = `${outDir}/program-graph-array-length-mismatch`; +const programGraphBlockBodyPackage = `${outDir}/program-graph-block-body`; +const programGraphBlockBodyRunPath = `${outDir}/program-graph-block-body-run`; +const programGraphAuthoringCliPackage = `${outDir}/program-graph-authoring-cli`; +const programGraphAuthoringCliGraphBuildPath = `${outDir}/program-graph-authoring-cli-graph-build`; +const programGraphAuthoringCliBuildPath = `${outDir}/program-graph-authoring-cli-build`; +const programGraphAuthoringCliRunPath = `${outDir}/program-graph-authoring-cli-run`; +const programGraphAuthoringCliRunAfterHumanEditPath = `${outDir}/program-graph-authoring-cli-run-human-edit`; +const programGraphSourceFreeCImportPackage = `${outDir}/program-graph-source-free-c-import`; +const programGraphSourceFreeCImportRunPath = `${outDir}/program-graph-source-free-c-import-run`; +const programGraphSourceFreeCImportCwdPackage = `${outDir}/program-graph-source-free-c-import-cwd`; +const programGraphSourceFreeCImportCwdBuildPath = `${outDir}/program-graph-source-free-c-import-cwd-build`; +const programGraphIdentityMismatchPackage = `${outDir}/program-graph-identity-mismatch`; +const programGraphMissingPackageNamePackage = `${outDir}/program-graph-missing-package-name`; +const programGraphBadProjectionPackage = `${outDir}/program-graph-bad-projection`; +const programGraphSourceFixtureDriftPackage = `${outDir}/program-graph-fixture-drift`; +const programGraphMissingStorePackage = `${outDir}/program-graph-missing-store`; +const programGraphInvalidStorePackage = `${outDir}/program-graph-invalid-store`; +const programGraphTargetWebbitsPackage = `${outDir}/program-graph-target-webbits`; +const programGraphTargetIncompatiblePackage = `${outDir}/program-graph-target-incompatible`; +const programGraphTargetCapabilityPackage = `${outDir}/program-graph-target-capability`; +const programGraphRichPath = `${outDir}/open-ended-slices.program-graph`; +const programGraphRichViewPath = `${outDir}/open-ended-slices.graph-view.0`; +const programGraphCharPath = `${outDir}/float-char-casts.program-graph`; +const programGraphCharViewPath = `${outDir}/float-char-casts.graph-view.0`; +await rm(programGraphDumpPath, { force: true }); +await rm(programGraphDumpJsonPath, { force: true }); +await rm(programGraphCanonicalPath, { force: true }); +await rm(programGraphViewPath, { force: true }); +await rm(programGraphArtifactRoundtripPath, { force: true }); +await rm(programGraphSourceFixtureRunPath, { force: true }); +await rm(programGraphSourceFreePackage, { recursive: true, force: true }); +await rm(programGraphSourceFreeBuildPath, { force: true }); +await rm(programGraphSourceFreeRunPath, { force: true }); +await rm(programGraphSourceFreeStdStrPackage, { recursive: true, force: true }); +await rm(programGraphCrmApiBuildPath, { force: true }); +await rm(programGraphAuthoringPackage, { recursive: true, force: true }); +await rm(programGraphAuthoringRunPath, { force: true }); +await rm(programGraphAuthoringRunAfterHumanEditPath, { force: true }); +await rm(programGraphBuilderOpsPackage, { recursive: true, force: true }); +await rm(programGraphBuilderOpsRunPath, { force: true }); +await rm(programGraphLoopTestPackage, { recursive: true, force: true }); +await rm(programGraphArrayLengthPackage, { recursive: true, force: true }); +await rm(programGraphBlockBodyPackage, { recursive: true, force: true }); +await rm(programGraphBlockBodyRunPath, { force: true }); +await rm(programGraphAuthoringCliPackage, { recursive: true, force: true }); +await rm(programGraphAuthoringCliGraphBuildPath, { force: true }); +await rm(programGraphAuthoringCliBuildPath, { force: true }); +await rm(programGraphAuthoringCliRunPath, { force: true }); +await rm(programGraphAuthoringCliRunAfterHumanEditPath, { force: true }); +await rm(programGraphSourceFreeCImportPackage, { recursive: true, force: true }); +await rm(programGraphSourceFreeCImportRunPath, { force: true }); +await rm(programGraphSourceFreeCImportCwdPackage, { recursive: true, force: true }); +await rm(programGraphSourceFreeCImportCwdBuildPath, { force: true }); +await rm(programGraphIdentityMismatchPackage, { recursive: true, force: true }); +await rm(programGraphMissingPackageNamePackage, { recursive: true, force: true }); +await rm(programGraphBadProjectionPackage, { recursive: true, force: true }); +await rm(programGraphSourceFixtureDriftPackage, { recursive: true, force: true }); +await rm(programGraphMissingStorePackage, { recursive: true, force: true }); +await rm(programGraphInvalidStorePackage, { recursive: true, force: true }); +await rm(programGraphTargetWebbitsPackage, { recursive: true, force: true }); +await rm(programGraphTargetIncompatiblePackage, { recursive: true, force: true }); +await rm(programGraphTargetCapabilityPackage, { recursive: true, force: true }); +await rm(programGraphRichPath, { force: true }); +await rm(programGraphRichViewPath, { force: true }); +await rm(programGraphCharPath, { force: true }); +await rm(programGraphCharViewPath, { force: true }); +const programGraphDumpOut = await execFileAsync(zero, ["dump", "--out", programGraphDumpPath, "examples/hello.0"]); +const programGraphDumpOutJson = JSON.parse((await execFileAsync(zero, ["dump", "--json", "--out", programGraphDumpJsonPath, "examples/hello.0"])).stdout); +const programGraphDumpFile = await readFile(programGraphDumpPath, "utf8"); +const programGraphDumpJsonFile = await readFile(programGraphDumpJsonPath, "utf8"); +const programGraphValidate = await execFileAsync(zero, ["validate", programGraphDumpPath]); +const programGraphDumpJsonValidate = await execFileAsync(zero, ["validate", programGraphDumpJsonPath]); +const programGraphValidateJson = JSON.parse((await execFileAsync(zero, ["validate", "--json", "--out", programGraphCanonicalPath, programGraphDumpPath])).stdout); +const programGraphCanonicalFile = await readFile(programGraphCanonicalPath, "utf8"); +const programGraphView = (await execFileAsync(zero, ["view", programGraphDumpPath])).stdout; +const programGraphViewAgain = (await execFileAsync(zero, ["view", programGraphDumpPath])).stdout; +const programGraphViewJson = JSON.parse((await execFileAsync(zero, ["view", "--json", programGraphDumpPath])).stdout); +const programGraphViewOut = await execFileAsync(zero, ["view", "--out", programGraphViewPath, programGraphDumpPath]); +const programGraphViewFile = await readFile(programGraphViewPath, "utf8"); +const programGraphViewOutJson = JSON.parse((await execFileAsync(zero, ["view", "--json", "--out", programGraphViewPath, programGraphDumpPath])).stdout); +const programGraphRoundtrip = await execFileAsync(zero, ["roundtrip", "examples/hello.0"]); +const programGraphRoundtripJson = JSON.parse((await execFileAsync(zero, ["roundtrip", "--json", "examples/hello.0"])).stdout); +const programGraphArtifactRoundtrip = await execFileAsync(zero, ["roundtrip", programGraphDumpPath]); +const programGraphArtifactRoundtripJson = JSON.parse((await execFileAsync(zero, ["roundtrip", "--json", "--out", programGraphArtifactRoundtripPath, programGraphDumpPath])).stdout); +const programGraphSourceFixtureText = await readFile(programGraphSourceFixturePath, "utf8"); +const programGraphSourceFixtureStoreBytes = await readFile(programGraphSourceFixtureStorePath); +const programGraphSourceFixturePackageCheckJson = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphSourceFixturePackage])).stdout); +const programGraphSourceFixturePackageStatusJson = JSON.parse((await execFileAsync(zero, ["status", "--json", programGraphSourceFixturePackage])).stdout); +const programGraphSourceFixturePackageRun = await execFileAsync(zero, ["run", "--out", programGraphSourceFixtureRunPath, programGraphSourceFixturePackage]); +await mkdir(programGraphSourceFreePackage, { recursive: true }); +await writeFile(`${programGraphSourceFreePackage}/zero.toml`, await readFile(`${programGraphSourceFixturePackage}/zero.toml`, "utf8")); +await writeFile(`${programGraphSourceFreePackage}/zero.graph`, programGraphSourceFixtureStoreBytes); +const programGraphSourceFreeCheckJson = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeSizeJson = JSON.parse((await execFileAsync(zero, ["size", "--json", "--target", "linux-musl-x64", programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeBuildJson = JSON.parse((await execFileAsync(zero, ["build", "--json", "--target", "linux-musl-x64", "--out", programGraphSourceFreeBuildPath, programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeMappedMirCache = programGraphSourceFreeBuildJson.graphBuild?.mappedFinalMir; +const programGraphSourceFreeMappedMirCachePath = programGraphSourceFreeMappedMirCache?.path ?? ""; +const programGraphSourceFreeRun = await execFileAsync(zero, ["run", "--out", programGraphSourceFreeRunPath, programGraphSourceFreePackage]); +const programGraphSourceFreeTestJson = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeMemJson = JSON.parse((await execFileAsync(zero, ["mem", "--json", programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeVerify = await execFileAsync(zero, ["verify-projection", "--json", programGraphSourceFreePackage]).catch((error) => error); +const programGraphSourceFreeExport = JSON.parse((await execFileAsync(zero, ["export", "--json", programGraphSourceFreePackage])).stdout); +const programGraphSourceFreeVerifyAfter = JSON.parse((await execFileAsync(zero, ["verify-projection", "--json", programGraphSourceFreePackage])).stdout); +await mkdir(programGraphSourceFreeStdStrPackage, { recursive: true }); +await writeZeroToml(programGraphSourceFreeStdStrPackage, { + package: { name: "program-graph-source-free-std-str", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "main.0" } }, +}); +await writeFile(`${programGraphSourceFreeStdStrPackage}/main.0`, await readFile("examples/std-str.0", "utf8")); +const programGraphSourceFreeStdStrSync = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphSourceFreeStdStrPackage])).stdout); +await rm(`${programGraphSourceFreeStdStrPackage}/main.0`, { force: true }); +const programGraphSourceFreeStdStrCheckJson = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphSourceFreeStdStrPackage])).stdout); +const programGraphCrmApiCheckJson = JSON.parse((await execFileAsync(zero, ["check", "--json", "examples/crm-api"])).stdout); +const programGraphCrmApiStatusJson = JSON.parse((await execFileAsync(zero, ["status", "--json", "examples/crm-api"])).stdout); +const programGraphCrmApiBuildJson = JSON.parse((await execFileAsync(zero, ["build", "--json", "--out", programGraphCrmApiBuildPath, "examples/crm-api"])).stdout); +const programGraphCrmApiHealth = await execFileAsync(programGraphCrmApiBuildPath, ["GET /health\n\n"]); +const programGraphCrmApiAccounts = await execFileAsync(programGraphCrmApiBuildPath, ["GET /crm/accounts?tenant=demo\n\n"]); +const programGraphCrmApiDealUpdate = await execFileAsync(programGraphCrmApiBuildPath, ["POST /crm/deals/42/update\ncontent-type: application/json\n\n{\"stage\":\"won\"}"]); +const programGraphCrmApiMissing = await execFileAsync(programGraphCrmApiBuildPath, ["GET /missing\n\n"]); +const programGraphAuthoringInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringProjectionExistsAfterInit = await fileExists(`${programGraphAuthoringPackage}/src/main.0`); +const programGraphAuthoringPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addMain", + "--op", + "addFunction name=\"add\" ret=\"i32\"", + "--op", + "addParam fn=\"add\" name=\"left\" type=\"i32\"", + "--op", + "addParam fn=\"add\" name=\"right\" type=\"i32\"", + "--op", + "addReturnBinary fn=\"add\" name=\"+\" left=\"left\" right=\"right\" type=\"i32\"", + "--op", + "addCheckWrite fn=\"main\" text=\"graph authoring ok\\n\"", + "--op", + "addTest name=\"addition works\" call=\"add\" arg0=\"40\" arg1=\"2\" expect=\"42\" type=\"i32\"", +], { cwd: programGraphAuthoringPackage })).stdout); +const programGraphAuthoringProjectionExistsAfterPatch = await fileExists(`${programGraphAuthoringPackage}/src/main.0`); +const programGraphAuthoringStatusMissing = JSON.parse((await execFileAsync(zero, ["status", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringVerifyMissing = await execFileAsync(zero, ["verify-projection", "--json", programGraphAuthoringPackage]).catch((error) => error); +const programGraphAuthoringCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringRun = await execFileAsync(zero, ["run", "--out", programGraphAuthoringRunPath, programGraphAuthoringPackage]); +const programGraphAuthoringTest = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringExport = JSON.parse((await execFileAsync(zero, ["export", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringProjectionText = await readFile(`${programGraphAuthoringPackage}/src/main.0`, "utf8"); +const programGraphAuthoringVerifyAfter = JSON.parse((await execFileAsync(zero, ["verify-projection", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringCheckAfter = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringEditedText = programGraphAuthoringProjectionText.replace("graph authoring ok", "human edit ok"); +await writeFile(`${programGraphAuthoringPackage}/src/main.0`, programGraphAuthoringEditedText); +const programGraphAuthoringStatusAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["status", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringImport = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringCheckAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphAuthoringPackage])).stdout); +const programGraphAuthoringRunAfterHumanEdit = await execFileAsync(zero, ["run", "--out", programGraphAuthoringRunAfterHumanEditPath, programGraphAuthoringPackage]); +const programGraphBuilderOpsInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphBuilderOpsPackage])).stdout); +const programGraphBuilderOpsPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addMain", + "--op", + "addLetLiteral fn=\"main\" name=\"message\" type=\"String\" value=\"graph value write ok\\n\"", + "--op", + "addCheckWriteValue fn=\"main\" value=\"message\" type=\"String\"", + "--op", + "addFunction name=\"add_twice\" ret=\"u32\"", + "--op", + "addParam fn=\"add_twice\" name=\"x\" type=\"u32\"", + "--op", + "addParam fn=\"add_twice\" name=\"y\" type=\"u32\"", + "--op", + "addLetBinary fn=\"add_twice\" name=\"first\" type=\"u32\" operator=\"+\" left=\"x\" right=\"y\"", + "--op", + "addLetBinary fn=\"add_twice\" name=\"total\" type=\"u32\" operator=\"+\" left=\"first\" right=\"y\"", + "--op", + "addReturnValue fn=\"add_twice\" value=\"total\" type=\"u32\"", + "--op", + "addTest name=\"add twice\" call=\"add_twice\" arg0=\"3\" arg1=\"2\" expect=\"7\" type=\"u32\"", +], { cwd: programGraphBuilderOpsPackage })).stdout); +const programGraphBuilderOpsProjectionExistsAfterPatch = await fileExists(`${programGraphBuilderOpsPackage}/src/main.0`); +const programGraphBuilderOpsQuery = JSON.parse((await execFileAsync(zero, ["query", "--json", programGraphBuilderOpsPackage])).stdout); +const programGraphBuilderOpsView = (await execFileAsync(zero, ["view", programGraphBuilderOpsPackage])).stdout; +const programGraphBuilderOpsCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphBuilderOpsPackage])).stdout); +const programGraphBuilderOpsTest = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphBuilderOpsPackage])).stdout); +const programGraphBuilderOpsRun = await execFileAsync(zero, ["run", "--out", programGraphBuilderOpsRunPath, programGraphBuilderOpsPackage]); +const programGraphBuilderOpsSync = JSON.parse((await execFileAsync(zero, ["export", "--json", programGraphBuilderOpsPackage])).stdout); +const programGraphBuilderOpsProjectionText = await readFile(`${programGraphBuilderOpsPackage}/src/main.0`, "utf8"); +const programGraphLoopTestInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphLoopTestPackage])).stdout); +const programGraphLoopTestPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addMain", + "--op", + "addFunction name=\"count_to\" ret=\"u32\"", + "--op", + "addParam fn=\"count_to\" name=\"n\" type=\"u32\"", + "--op", + "addParam fn=\"count_to\" name=\"start\" type=\"u32\"", + "--op", + "addReturnBinary fn=\"count_to\" name=\"+\" left=\"n\" right=\"start\" type=\"u32\"", +], { cwd: programGraphLoopTestPackage })).stdout); +const programGraphLoopTestBodyQuery = JSON.parse((await execFileAsync(zero, ["query", "--json", programGraphLoopTestPackage])).stdout); +const programGraphLoopTestBodyPatchText = [ + "zero-program-graph-patch v1", + `expect graphHash "${programGraphLoopTestBodyQuery.graphHash}"`, + "replaceFunctionBody count_to", + " var i u32 = start", + " while i < n", + " i = i + 1", + " return i", + "end", + "", +].join("\n"); +const programGraphLoopTestBodyPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", programGraphLoopTestPackage, "--patch-text", programGraphLoopTestBodyPatchText])).stdout); +const programGraphLoopTestAddTest = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addTest name=\"graph while assignment\" call=\"count_to\" arg0=\"4\" arg1=\"0\" expect=\"4\" type=\"u32\"", +], { cwd: programGraphLoopTestPackage })).stdout); +const programGraphLoopTestCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphLoopTestPackage])).stdout); +const programGraphLoopTestRun = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphLoopTestPackage])).stdout); +const programGraphLoopTestView = (await execFileAsync(zero, ["view", programGraphLoopTestPackage])).stdout; +const programGraphArrayLengthInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphArrayLengthPackage])).stdout); +const programGraphArrayLengthMainPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", "--op", "addMain", programGraphArrayLengthPackage])).stdout); +const programGraphArrayLengthRepeatBodyPath = `${outDir}/program-graph-array-length-repeat-body.0`; +await writeFile(programGraphArrayLengthRepeatBodyPath, 'var b: [64]u8 = [0_u8; 32]\ncheck world.out.write("unreachable\\n")\n'); +const programGraphArrayLengthRepeatPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", "--replace-fn", "main", "--body-file", programGraphArrayLengthRepeatBodyPath, programGraphArrayLengthPackage]).catch((error) => error)).stdout); +const programGraphArrayLengthListBodyPath = `${outDir}/program-graph-array-length-list-body.0`; +await writeFile(programGraphArrayLengthListBodyPath, 'var b: [4]u8 = [1, 2]\ncheck world.out.write("unreachable\\n")\n'); +const programGraphArrayLengthListPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", "--replace-fn", "main", "--body-file", programGraphArrayLengthListBodyPath, programGraphArrayLengthPackage]).catch((error) => error)).stdout); +const programGraphArrayLengthMatchingBodyPath = `${outDir}/program-graph-array-length-matching-body.0`; +await writeFile(programGraphArrayLengthMatchingBodyPath, 'var b: [4]u8 = [0_u8; 4]\nb[0] = 1\ncheck world.out.write("lengths agree\\n")\n'); +const programGraphArrayLengthMatchingPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", "--replace-fn", "main", "--body-file", programGraphArrayLengthMatchingBodyPath, programGraphArrayLengthPackage])).stdout); +const programGraphArrayLengthCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphArrayLengthPackage])).stdout); +const programGraphBlockBodyInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphBlockBodyPackage])).stdout); +const programGraphBlockBodyMainPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addMain", +], { cwd: programGraphBlockBodyPackage })).stdout); +const programGraphBlockBodyMainQuery = JSON.parse((await execFileAsync(zero, ["query", "--json", programGraphBlockBodyPackage])).stdout); +const programGraphBlockBodyGreetingPatchText = [ + "zero-program-graph-patch v1", + `expect graphHash "${programGraphBlockBodyMainQuery.graphHash}"`, + "replaceFunctionBody main", + " let name Maybe = std.args.get 1", + " if name.has", + " check world.out.write \"hello \"", + " check world.out.write name.value", + " check world.out.write \"\\n\"", + " else", + " check world.out.write \"hello anonymous\\n\"", + "end", + "", +].join("\n"); +const programGraphBlockBodyGreetingPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", programGraphBlockBodyPackage, "--patch-text", programGraphBlockBodyGreetingPatchText])).stdout); +const programGraphBlockBodyBlocks = JSON.parse((await execFileAsync(zero, ["query", "--json", "--find", "Block", programGraphBlockBodyPackage])).stdout); +const programGraphBlockBodyThen = programGraphBlockBodyBlocks.matches.find((node) => node.kind === "Block" && node.name === "then"); +assert(programGraphBlockBodyThen, "expected row-patched greeting body to expose a then block handle"); +const programGraphBlockBodyPatchText = [ + "zero-program-graph-patch v1", + `expect graphHash "${programGraphBlockBodyBlocks.graphHash}"`, + `replaceBlockBody ${programGraphBlockBodyThen.id}`, + " check world.out.write \"name + value: \"", + " check world.out.write name.value", + " check world.out.write \"\\n\"", + "end", + "", +].join("\n"); +const programGraphBlockBodyDryRun = JSON.parse((await execFileAsync(zero, ["patch", "--json", "--check-only", programGraphBlockBodyPackage, "--patch-text", programGraphBlockBodyPatchText])).stdout); +const programGraphBlockBodyPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", programGraphBlockBodyPackage, "--patch-text", programGraphBlockBodyPatchText])).stdout); +const programGraphBlockBodyView = (await execFileAsync(zero, ["view", programGraphBlockBodyPackage])).stdout; +const programGraphBlockBodyCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphBlockBodyPackage])).stdout); +const programGraphBlockBodyRun = await execFileAsync(zero, ["run", "--out", programGraphBlockBodyRunPath, programGraphBlockBodyPackage, "--", "Ada"]); +const programGraphAuthoringCliInit = JSON.parse((await execFileAsync(zero, ["init", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliProjectionExistsAfterInit = await fileExists(`${programGraphAuthoringCliPackage}/src/main.0`); +const programGraphAuthoringCliPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addMain", + "--op", + "addFunction name=\"add_u32\" ret=\"u32\"", + "--op", + "addParam fn=\"add_u32\" name=\"x\" type=\"u32\"", + "--op", + "addParam fn=\"add_u32\" name=\"y\" type=\"u32\"", + "--op", + "addReturnBinary fn=\"add_u32\" name=\"+\" left=\"x\" right=\"y\" type=\"u32\"", +], { cwd: programGraphAuthoringCliPackage })).stdout); +const programGraphAuthoringCliBodyQuery = JSON.parse((await execFileAsync(zero, ["query", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliBodyPatchText = [ + "zero-program-graph-patch v1", + `expect graphHash "${programGraphAuthoringCliBodyQuery.graphHash}"`, + "replaceFunctionBody main", + " let x Maybe = std.args.parseU32 1", + " let y Maybe = std.args.parseU32 2", + " if x.has && y.has", + " let result u32 = add_u32 x.value y.value", + " var out [10]u8 = repeat 0_u8 10", + " let text Maybe> = std.fmt.u32 out result", + " if text.has", + " check world.out.write text.value", + " check world.out.write \"\\n\"", + " else", + " check world.err.write \"usage: zero run . -- \\n\"", + "end", + "", +].join("\n"); +const programGraphAuthoringCliBodyPatch = JSON.parse((await execFileAsync(zero, ["patch", "--json", programGraphAuthoringCliPackage, "--patch-text", programGraphAuthoringCliBodyPatchText])).stdout); +const programGraphAuthoringCliProjectionExistsAfterPatch = await fileExists(`${programGraphAuthoringCliPackage}/src/main.0`); +const programGraphAuthoringCliStaleAddPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "addFunction name=\"add\" ret=\"i32\"", + "--op", + "addParam fn=\"add\" name=\"x\" type=\"i32\"", + "--op", + "addParam fn=\"add\" name=\"y\" type=\"i32\"", + "--op", + "addReturnBinary fn=\"add\" name=\"+\" left=\"x\" right=\"y\" type=\"i32\"", + "--op", + "addTest name=\"add works\" call=\"add\" arg0=\"40\" arg1=\"2\" expect=\"42\" type=\"i32\"", +], { cwd: programGraphAuthoringCliPackage })).stdout); +const programGraphAuthoringCliFindAdd = JSON.parse((await execFileAsync(zero, ["query", "--json", "--find", "add", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliNodeAdd = JSON.parse((await execFileAsync(zero, ["query", "--json", "--node", "#fn_add", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliCleanupPatch = JSON.parse((await execFileAsync(zero, [ + "patch", + "--json", + "--op", + "delete node=\"#fn___zero_test_0\"", + "--op", + "delete node=\"#fn_add\"", + "--op", + "addTest name=\"add_u32 works\" call=\"add_u32\" arg0=\"40\" arg1=\"2\" expect=\"42\" type=\"u32\"", +], { cwd: programGraphAuthoringCliPackage })).stdout); +const programGraphAuthoringCliCallsText = (await execFileAsync(zero, ["query", "--fn", "main", "--calls", "std", programGraphAuthoringCliPackage])).stdout; +const programGraphAuthoringCliRefsText = (await execFileAsync(zero, ["query", "--refs", "add_u32", programGraphAuthoringCliPackage])).stdout; +const programGraphAuthoringCliQuery = JSON.parse((await execFileAsync(zero, ["query", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliGraphBuild = await execFileAsync(zero, ["build", "--out", programGraphAuthoringCliGraphBuildPath, programGraphAuthoringCliPackage]); +const programGraphAuthoringCliBuild = await execFileAsync(zero, ["build", "--out", programGraphAuthoringCliBuildPath, programGraphAuthoringCliPackage]); +const programGraphAuthoringCliTest = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliGraphTest = await execFileAsync(zero, ["test", programGraphAuthoringCliPackage]); +const programGraphAuthoringCliRun = await execFileAsync(zero, ["run", "--out", programGraphAuthoringCliRunPath, programGraphAuthoringCliPackage, "--", "40", "2"]); +const programGraphAuthoringCliGraphRun = await execFileAsync(zero, ["run", programGraphAuthoringCliPackage, "--", "7", "8"]); +const programGraphAuthoringCliSize = JSON.parse((await execFileAsync(zero, ["size", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliSync = JSON.parse((await execFileAsync(zero, ["export", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliProjectionText = await readFile(`${programGraphAuthoringCliPackage}/src/main.0`, "utf8"); +const programGraphAuthoringCliEditedProjectionText = programGraphAuthoringCliProjectionText.replace("usage: zero run . -- \\n", "usage: zero run . -- \\n"); +await writeFile(`${programGraphAuthoringCliPackage}/src/main.0`, programGraphAuthoringCliEditedProjectionText); +const programGraphAuthoringCliStatusAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["status", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliImport = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliQueryAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["query", "--json", "--calls", "std", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliFindUsageText = (await execFileAsync(zero, ["query", "--find", "usage", programGraphAuthoringCliPackage])).stdout; +const programGraphAuthoringCliVerifyAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["verify-projection", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliCheckAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliTestAfterHumanEdit = JSON.parse((await execFileAsync(zero, ["test", "--json", programGraphAuthoringCliPackage])).stdout); +const programGraphAuthoringCliRunAfterHumanEdit = await execFileAsync(zero, ["run", "--out", programGraphAuthoringCliRunAfterHumanEditPath, programGraphAuthoringCliPackage, "--", "5", "6"]); +await mkdir(`${programGraphSourceFreeCImportPackage}/src`, { recursive: true }); +await mkdir(`${programGraphSourceFreeCImportPackage}/vendor/include`, { recursive: true }); +await writeZeroToml(programGraphSourceFreeCImportPackage, { + package: { name: "program-graph-source-free-c-import", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { + libs: { + ext: { headers: ["vendor/include/zero_ext.h"], include: ["vendor/include"], lib: [], link: [], mode: "static" }, + }, + }, +}); +await writeFile(`${programGraphSourceFreeCImportPackage}/vendor/include/zero_ext.h`, "int zero_ext_add(int a, int b);\n"); +await writeFile(`${programGraphSourceFreeCImportPackage}/src/main.0`, `extern c "vendor/include/zero_ext.h" as c + +pub fn main(world: World) -> Void raises { + check world.out.write("source-free c import ok\\n") +} +`); +const programGraphSourceFreeCImportSync = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphSourceFreeCImportPackage])).stdout); +await rm(`${programGraphSourceFreeCImportPackage}/src`, { recursive: true, force: true }); +const programGraphSourceFreeCImportCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphSourceFreeCImportPackage])).stdout); +const programGraphSourceFreeCImportRun = await execFileAsync(zero, ["run", "--out", programGraphSourceFreeCImportRunPath, programGraphSourceFreeCImportPackage]); +await mkdir(programGraphSourceFreeCImportCwdPackage, { recursive: true }); +await writeZeroToml(programGraphSourceFreeCImportCwdPackage, { + package: { name: "program-graph-source-free-c-import-cwd", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, +}); +const programGraphSourceFreeCImportCwdBuild = JSON.parse((await execFileAsync(resolve(zero), [ + "build", + "--json", + "--out", + resolve(programGraphSourceFreeCImportCwdBuildPath), + resolve(programGraphSourceFreeCImportPackage), +], { cwd: programGraphSourceFreeCImportCwdPackage })).stdout); +await mkdir(programGraphIdentityMismatchPackage, { recursive: true }); +await writeZeroToml(programGraphIdentityMismatchPackage, { + package: { name: "program-graph-wrong-package", version: "9.9.9" }, + targets: { cli: { kind: "exe", main: "hello.0" } }, +}); +await writeFile(`${programGraphIdentityMismatchPackage}/zero.graph`, programGraphSourceFixtureStoreBytes); +const programGraphIdentityMismatchCheck = await execFileAsync(zero, ["check", "--json", programGraphIdentityMismatchPackage]).catch((error) => error); +const programGraphIdentityMismatchSize = await execFileAsync(zero, ["size", "--json", programGraphIdentityMismatchPackage]).catch((error) => error); +await mkdir(programGraphMissingPackageNamePackage, { recursive: true }); +await writeZeroToml(programGraphMissingPackageNamePackage, { + targets: { cli: { kind: "exe", main: "hello.0" } }, +}); +await writeFile(`${programGraphMissingPackageNamePackage}/zero.graph`, programGraphSourceFixtureStoreBytes); +const programGraphMissingPackageNameCheck = await execFileAsync(zero, ["check", "--json", programGraphMissingPackageNamePackage]).catch((error) => error); +await mkdir(programGraphBadProjectionPackage, { recursive: true }); +await writeFile(`${programGraphBadProjectionPackage}/zero.toml`, await readFile(`${programGraphSourceFixturePackage}/zero.toml`, "utf8")); +await writeFile(`${programGraphBadProjectionPackage}/hello.0`, programGraphSourceFixtureText); +await execFileAsync(zero, ["import", "--format", "text", programGraphBadProjectionPackage]); +const programGraphBadProjectionStoreText = await readFile(`${programGraphBadProjectionPackage}/zero.graph`, "utf8"); +await writeFile(`${programGraphBadProjectionPackage}/zero.graph`, programGraphBadProjectionStoreText.replace( + /^projection path:"hello\.0" text:.*$/m, + `projection path:"hello.0" text:${JSON.stringify("pub fn broken( {\n")}`, +)); +const programGraphBadProjectionStatus = JSON.parse((await execFileAsync(zero, ["status", "--json", programGraphBadProjectionPackage])).stdout); +const programGraphBadProjectionCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphBadProjectionPackage])).stdout); +const programGraphBadProjectionSync = await execFileAsync(zero, ["export", "--json", programGraphBadProjectionPackage]).catch((error) => error); +await mkdir(programGraphMissingStorePackage, { recursive: true }); +await writeZeroToml(programGraphMissingStorePackage, { + package: { name: "program-graph-missing-store", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "main.0" } }, +}); +await writeFile(`${programGraphMissingStorePackage}/main.0`, "pub fn main() -> i32 { return 0 }\n"); +const programGraphMissingStoreCheck = await execFileAsync(zero, ["check", "--json", programGraphMissingStorePackage]).catch((error) => error); +await mkdir(programGraphInvalidStorePackage, { recursive: true }); +await writeZeroToml(programGraphInvalidStorePackage, { + package: { name: "program-graph-invalid-store", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "main.0" } }, +}); +await writeFile(`${programGraphInvalidStorePackage}/main.0`, "pub fn main() -> i32 { return 0 }\n"); +await writeFile(`${programGraphInvalidStorePackage}/zero.graph`, "not a repository graph\n"); +const programGraphInvalidStoreCheck = await execFileAsync(zero, ["check", "--json", programGraphInvalidStorePackage]).catch((error) => error); +await mkdir(programGraphSourceFixtureDriftPackage, { recursive: true }); +await writeFile(`${programGraphSourceFixtureDriftPackage}/zero.toml`, await readFile(`${programGraphSourceFixturePackage}/zero.toml`, "utf8")); +await writeFile(`${programGraphSourceFixtureDriftPackage}/zero.graph`, programGraphSourceFixtureStoreBytes); +await writeFile(`${programGraphSourceFixtureDriftPackage}/hello.0`, programGraphSourceFixtureText.replace("hello from zero", "hello from drift")); +const programGraphSourceFixtureDriftVerify = await execFileAsync(zero, ["verify-projection", "--json", programGraphSourceFixtureDriftPackage]).catch((error) => error); +const programGraphSourceFixtureDriftCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", programGraphSourceFixtureDriftPackage])).stdout); +const programGraphSourceFixtureDriftVerifyAfterRefresh = JSON.parse((await execFileAsync(zero, ["verify-projection", "--json", programGraphSourceFixtureDriftPackage])).stdout); +await mkdir(programGraphTargetWebbitsPackage, { recursive: true }); +await writeFile(`${programGraphTargetWebbitsPackage}/zero.toml`, await readFile("conformance/packages/target-webbits/zero.toml", "utf8")); +await mkdir(`${programGraphTargetIncompatiblePackage}/src`, { recursive: true }); +await writeZeroToml(programGraphTargetIncompatiblePackage, { + package: { name: "program-graph-target-incompatible", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + dependencies: { "target-webbits": { path: "../program-graph-target-webbits", version: "0.1.0", targets: ["win32-x64.exe"] } }, +}); +await writeFile(`${programGraphTargetIncompatiblePackage}/src/main.0`, `pub fn main(world: World) -> Void raises { + check world.out.write("target incompatible\\n") +} +`); +const programGraphTargetIncompatibleSync = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphTargetIncompatiblePackage])).stdout); +const programGraphTargetIncompatibleCheck = await execFileAsync(zero, ["check", "--json", "--target", "linux-musl-x64", programGraphTargetIncompatiblePackage]).catch((error) => error); +await mkdir(programGraphTargetCapabilityPackage, { recursive: true }); +await writeZeroToml(programGraphTargetCapabilityPackage, { + package: { name: "program-graph-target-capability", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "main.0" } }, +}); +await writeFile(`${programGraphTargetCapabilityPackage}/main.0`, `pub fn main(world: World) -> Void raises { + let fs: Fs = std.fs.host() + if std.fs.writeFile(fs, ".zero/out/program-graph-target-capability.txt", "ok\\n") { + check world.out.write("ok\\n") + } +} +`); +const programGraphTargetCapabilitySync = JSON.parse((await execFileAsync(zero, ["import", "--json", programGraphTargetCapabilityPackage])).stdout); +const programGraphTargetCapabilityCheck = await execFileAsync(zero, ["check", "--json", "--target", "linux-arm64", programGraphTargetCapabilityPackage]).catch((error) => error); +await execFileAsync(zero, ["dump", "--out", programGraphRichPath, "conformance/native/pass/open-ended-slices.0"]); +await execFileAsync(zero, ["view", "--out", programGraphRichViewPath, programGraphRichPath]); +const programGraphRichView = await readFile(programGraphRichViewPath, "utf8"); +await execFileAsync(zero, ["dump", "--out", programGraphCharPath, "conformance/native/pass/float-char-casts.0"]); +await execFileAsync(zero, ["view", "--out", programGraphCharViewPath, programGraphCharPath]); +const programGraphCharView = await readFile(programGraphCharViewPath, "utf8"); +const programGraphViewCoverage = [ + ["compile-time-v1", "examples/compile-time-v1.0", [/const field_type: String = meta fieldType\(Point, "x"\)/, /readGate\(&gate\)/]], + ["array-repeat-literal", "conformance/native/pass/array-repeat-literal.0", [/\[7_u8; 8\]/, /\[0_u8; 16\]/]], + ["explicit-casts", "conformance/native/pass/explicit-casts.0", [/let byte: u8 = big as u8/, /let offset: isize = small as isize/]], + ["generic-multi-specialization", "conformance/native/pass/generic-multi-specialization.0", [/first\(21, 7\)/, /second\(a, 6\)/]], + ["generic-shape-nested-defaults-alias", "conformance/native/pass/generic-shape-nested-defaults-alias.0", [/Box \{ value: 42 \}/, /Slot \{ item: 7_u8 \}/]], + ["float-primitives", "conformance/native/pass/float-primitives.0", [/let precise: f64 = 1\.0e-3/]], + ["meta-typed-target-type", "conformance/native/pass/meta-typed-target-type.0", [/const computed: usize = meta 2 \+ 2/]], + ["nested-lvalues", "conformance/native/pass/nested-lvalues.0", [/Point \{ x: 3, y: 4 \}/, /Point \{ x: 5, y: 6 \}/]], + ["radix-suffix-literals", "conformance/native/pass/radix-suffix-literals.0", [/return 0x20_usize/, /1_024_usize/, /0x2a_u8/]], + ["test-blocks", "conformance/native/pass/test-blocks.0", [/test "addition works"/]], + ["type-alias-basic", "conformance/native/pass/type-alias-basic.0", [/let count: ByteCount = 4_usize/]], + ["c-abi-export", "conformance/native/pass/c-abi-export.0", [/extern type CPoint/, /export c fn zero_add\(a: i32, b: i32\) -> i32/]], + ["const-layout", "conformance/native/pass/const-layout.0", [/extern type CPoint/, /packed type Header/]], + ["c-header-import", "conformance/check/pass/c-header-import.0", [/extern c "conformance\/c\/simple\.h" as c/]], + ["constructors-defaults", "conformance/native/pass/constructors-defaults.0", [/FixedVec\.init/]], + ["direct-call-add", "examples/direct-call-add.0", [/export c fn main\(a: i32, b: i32\) -> i32/]], + ["generic-static-explicit-shadowing", "conformance/check/pass/generic-static-explicit-shadowing.0", [/Helper\.needsSame\(left, right\)/]], + ["systems-package", "examples/systems-package", [/use std\.codec/, /pub fn main\(world: World\) -> Void raises/]], + ["std-math", "examples/std-math.0", [/pub fn main\(world: World\) -> Void raises/, /std\.math\.minU32\(8, 3\)/]], +]; +for (const [name, fixture, patterns] of programGraphViewCoverage) { + const graphPath = `${outDir}/${name}.program-graph`; + const viewPath = `${outDir}/${name}.graph-view.0`; + await rm(graphPath, { force: true }); + await rm(viewPath, { force: true }); + await execFileAsync(zero, ["dump", "--out", graphPath, fixture]); + await execFileAsync(zero, ["view", "--out", viewPath, graphPath]); + const view = await readFile(viewPath, "utf8"); + for (const pattern of patterns) assert.match(view, pattern); + assert.doesNotMatch(view, /fn __zero_test_/); + if (name === "systems-package") { + assert.doesNotMatch(view, /^use (helpers|types)$/m); + await execFileAsync(zero, ["import", "--format", "binary", "--out", graphSidecarPath(viewPath), viewPath]); + assert.equal((await execFileAsync(zero, ["check", viewPath])).stdout, "ok\n"); + } + if (name === "std-math") assert.doesNotMatch(view, /fn __zero_std_/); +} +for (const fixture of [ + "conformance/native/pass/open-ended-slices.0", + "conformance/native/pass/float-char-casts.0", + "conformance/check/pass/call-resolution-edge-cases.0", + "conformance/native/pass/match-payload-binding.0", + "conformance/native/pass/match-scalar-guards.0", + "conformance/native/pass/rescue-check.0", + "examples/result-choice.0", + "examples/compile-time-v1.0", + "examples/config-shape.0", + "examples/direct-enum-match.0", + "conformance/native/pass/test-blocks.0", + "examples/std-math.0", + "examples/systems-package", + "std/math.0", + "std/path.0", + "std/str.0", +]) { + const roundtrip = JSON.parse((await execFileAsync(zero, ["roundtrip", "--json", fixture])).stdout); + assert.equal(roundtrip.ok, true); + assert.equal(roundtrip.semanticStable, true); + assert.equal(roundtrip.roundtripModuleIdentity, roundtrip.moduleIdentity); + assert.equal(roundtrip.comparison.ok, true); + assert.deepEqual(roundtrip.semanticCounts.original, roundtrip.semanticCounts.roundtrip); +} +const stdPathRecursiveView = (await execFileAsync(zero, ["view", "conformance/native/pass/std-path-io-breadth.0"])).stdout; +assert.match(stdPathRecursiveView, /std\.path\.relative\(rel_buf, "src", "src\/main\.0"\)/); +assert.doesNotMatch(stdPathRecursiveView, /__zero_std_/); +assert.equal(programGraphBody.schemaVersion, 1); +assert.equal(programGraphBody.canonicalSource, false); +assert.equal(programGraphBody.moduleIdentity, "module:hello"); +assert.deepEqual(programGraphBodyAgain, programGraphBody); +assert.equal(programGraphBody.resolution.state, "resolved"); +assert.equal(programGraphBody.resolution.ok, true); +assert(programGraphBody.resolution.scopes.some((scope) => scope.kind === "module" && scope.name === "hello" && scope.bindings.some((binding) => binding.kind === "function" && binding.name === "main"))); +assert(programGraphBody.resolution.references.some((reference) => reference.kind === "identifier" && reference.name === "world" && reference.targetKind === "param" && reference.symbolId === "symbol:hello::value.main/param.world")); +assert(programGraphBody.resolution.references.some((reference) => reference.kind === "call" && reference.qualifiedName === "world.out.write" && reference.targetKind === "member")); +assert.equal(programGraphBody.semantics.state, "typed-facts"); +assert.equal(programGraphBody.semantics.ok, true); +assert(programGraphBody.semantics.functions.some((item) => item.name === "main" && item.returnType === "Void" && item.fallible === true && item.sourceRange.path === "hello.0" && item.params.some((param) => param.name === "world" && param.type === "World" && param.sourceRange.path === "hello.0"))); +assert(programGraphBody.semantics.calls.some((item) => item.qualifiedName === "world.out.write" && item.returnType === "Void" && item.fallible === true && item.checked === true && item.sourceRange.path === "hello.0" && item.contract.kind === "worldStreamWrite" && item.contract.capability === "io" && item.contract.targetSupport === "world-io" && item.contract.repair?.id === "check-fallible-call" && item.resolution.targetKind === "member" && item.resolution.symbolId === "symbol:hello::value.main/param.world")); +assert(programGraphBody.semantics.ownership.some((item) => item.name === "world" && item.ownership === "resource-handle" && item.resource === true)); +assert(programGraphBody.semantics.resources.some((item) => item.kind === "capabilityUse" && item.resourceKind === "world-io" && item.qualifiedName === "world.out.write")); +assert(programGraphBody.semantics.targetRequirements.some((item) => item.qualifiedName === "world.out.write" && item.capability === "io" && item.targetSupport === "world-io")); +assert(programGraphBody.semantics.repairs.some((item) => item.qualifiedName === "world.out.write" && item.requiresCheck === false && item.repair.id === "check-fallible-call")); +assert.equal(programGraphDumpAgain, programGraphDump); +assert.equal(programGraphDumpOut.stdout, ""); +assert.equal(programGraphDumpFile, programGraphDump); +assert.deepEqual(programGraphDumpOutJson, programGraphDumpJson); +assert.equal(programGraphDumpJsonFile, programGraphDump); +assert.equal(programGraphValidate.stdout, "program graph ok\n"); +assert.equal(programGraphDumpJsonValidate.stdout, "program graph ok\n"); +assert.equal(programGraphCanonicalFile, programGraphDump); +assert.equal(programGraphValidateJson.ok, true); +assert.equal(programGraphValidateJson.moduleIdentity, "module:hello"); +assert.equal(programGraphValidateJson.graphHash, programGraphBody.graphHash); +assert.equal(programGraphValidateJson.saved.path, programGraphCanonicalPath); +assert.equal(programGraphViewAgain, programGraphView); +assert.equal(programGraphViewOut.stdout, ""); +assert.equal(programGraphViewFile, programGraphView); +assert.equal(programGraphViewJson.ok, true); +assert.equal(programGraphViewJson.canonicalSource, false); +assert.equal(programGraphViewJson.moduleIdentity, "module:hello"); +assert.equal(programGraphViewJson.graphHash, programGraphBody.graphHash); +assert.equal(programGraphViewJson.view, programGraphView); +assert.equal(programGraphViewOutJson.ok, true); +assert.equal(programGraphViewOutJson.saved.path, programGraphViewPath); +assert.equal(programGraphViewOutJson.view, null); +assert.equal(programGraphRoundtrip.stdout, "program graph roundtrip ok\n"); +assert.equal(programGraphRoundtripJson.ok, true); +assert.equal(programGraphRoundtripJson.canonicalSource, false); +assert.equal(programGraphRoundtripJson.semanticStable, true); +assert.equal(programGraphRoundtripJson.lowering, "direct-program-graph"); +assert.equal(programGraphRoundtripJson.moduleIdentity, "module:hello"); +assert.equal(programGraphRoundtripJson.roundtripModuleIdentity, "module:hello"); +assert.equal(programGraphRoundtripJson.originalGraphHash, programGraphBody.graphHash); +assert.equal(programGraphRoundtripJson.roundtripGraphHash, programGraphBody.graphHash); +assert.deepEqual(programGraphRoundtripJson.semanticCounts.original, programGraphRoundtripJson.semanticCounts.roundtrip); +assert.equal(programGraphRoundtripJson.comparison.ok, true); +assert.equal(programGraphRoundtripJson.view, null); +assert.equal(programGraphArtifactRoundtrip.stdout, "program graph roundtrip ok\n"); +assert.equal(programGraphArtifactRoundtripJson.ok, true); +assert.equal(programGraphArtifactRoundtripJson.artifact, programGraphDumpPath); +assert.equal(programGraphArtifactRoundtripJson.semanticStable, true); +assert.equal(programGraphArtifactRoundtripJson.lowering, "direct-program-graph"); +assert.equal(programGraphArtifactRoundtripJson.originalGraphHash, programGraphBody.graphHash); +assert.equal(programGraphArtifactRoundtripJson.roundtripGraphHash, programGraphBody.graphHash); +assert.equal(programGraphArtifactRoundtripJson.saved.path, programGraphArtifactRoundtripPath); +assert.equal(programGraphArtifactRoundtripJson.saved.kind, "program-graph"); +assert.equal(programGraphArtifactRoundtripJson.view, null); +assert.equal(await readFile(programGraphArtifactRoundtripPath, "utf8"), programGraphDump); +assert.equal(programGraphSourceFixtureText, await readFile("examples/hello.0", "utf8")); +assert.equal(programGraphSourceFixturePackageCheckJson.ok, true); +assert.equal(programGraphSourceFixturePackageCheckJson.sourceFile, programGraphSourceFixtureStorePath); +assert.equal(programGraphSourceFixturePackageCheckJson.package.name, "program-graph-fixture"); +assert.equal(programGraphSourceFixturePackageCheckJson.graph.artifact, programGraphSourceFixtureStorePath); +assert.equal(programGraphSourceFixturePackageCheckJson.graph.canonicalSource, false); +assert.equal(programGraphSourceFixturePackageCheckJson.graph.moduleIdentity, "package:program-graph-fixture@0.1.0"); +assert.match(programGraphSourceFixturePackageCheckJson.graph.graphHash, /^graph:[0-9a-f]{16}$/); +assert.equal(programGraphSourceFixturePackageCheckJson.graph.lowering, "graph-native-check"); +assert.equal(programGraphSourceFixturePackageStatusJson.store.encoding, "binary"); +assert.equal(programGraphSourceFixturePackageStatusJson.storage.encoding, "single-file-binary"); +assertRepositoryGraphNativeCheck(programGraphSourceFixturePackageCheckJson); +assert.equal(programGraphSourceFixturePackageRun.stdout, "hello from zero\n"); +assert.equal(programGraphSourceFreeCheckJson.ok, true); +assert.equal(programGraphSourceFreeCheckJson.sourceFile, `${programGraphSourceFreePackage}/zero.graph`); +assert.equal(programGraphSourceFreeCheckJson.graph.artifact, `${programGraphSourceFreePackage}/zero.graph`); +assert.equal(programGraphSourceFreeCheckJson.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphSourceFreeCheckJson, "missing"); +assertProgramGraphCompilerInput(programGraphSourceFreeCheckJson, `${programGraphSourceFreePackage}/zero.graph`); +assertSourceGraph(programGraphSourceFreeSizeJson, `${programGraphSourceFreePackage}/zero.graph`, "package:program-graph-fixture@0.1.0", "mapped-final-mir", false, "missing"); +assertProgramGraphCompilerInput(programGraphSourceFreeSizeJson, `${programGraphSourceFreePackage}/zero.graph`); +assert.equal(programGraphSourceFreeBuildJson.sourceFile, `${programGraphSourceFreePackage}/zero.graph`); +assertSourceGraph(programGraphSourceFreeBuildJson, `${programGraphSourceFreePackage}/zero.graph`, "package:program-graph-fixture@0.1.0", "mapped-final-mir", false, "missing"); +assertProgramGraphCompilerInput(programGraphSourceFreeBuildJson, `${programGraphSourceFreePackage}/zero.graph`); +assert(programGraphSourceFreeMappedMirCachePath.endsWith(".zmir"), "repository graph build should report a mapped MIR cache path"); +assert.equal(existsSync(programGraphSourceFreeMappedMirCachePath), true, "repository graph build should write a mapped MIR cache"); +assert.equal(programGraphSourceFreeRun.stdout, "hello from zero\n"); +assert.equal(programGraphSourceFreeTestJson.ok, true); +assertSourceGraph(programGraphSourceFreeTestJson, `${programGraphSourceFreePackage}/zero.graph`, "package:program-graph-fixture@0.1.0", "direct-program-graph", false, "missing"); +assert.equal(programGraphSourceFreeTestJson.testBackend, "direct-program-graph"); +assert.equal(programGraphSourceFreeTestJson.testDiscovery.mode, "package-graph"); +assertSourceGraph(programGraphSourceFreeMemJson, `${programGraphSourceFreePackage}/zero.graph`, "package:program-graph-fixture@0.1.0", "mapped-final-mir", false, "missing"); +assertProgramGraphCompilerInput(programGraphSourceFreeMemJson, `${programGraphSourceFreePackage}/zero.graph`); +assert.notEqual(programGraphSourceFreeVerify.code, 0); +const programGraphSourceFreeVerifyBody = JSON.parse(programGraphSourceFreeVerify.stdout); +assert.equal(programGraphSourceFreeVerifyBody.diagnostics[0].code, "RGP006"); +assert.equal(programGraphSourceFreeVerifyBody.diagnostics[0].actual, "missing source projection file"); +assert.match(programGraphSourceFreeVerifyBody.repairCommands.join("\n"), /zero export/); +assert.equal(programGraphSourceFreeExport.ok, true); +assert.deepEqual(programGraphSourceFreeExport.changedPaths, [`${programGraphSourceFreePackage}/hello.0`]); +assert.equal(await readFile(`${programGraphSourceFreePackage}/hello.0`, "utf8"), programGraphSourceFixtureText); +assert.equal(programGraphSourceFreeVerifyAfter.ok, true); +assert.equal(programGraphSourceFreeVerifyAfter.repositoryGraph.projectionValidity, "clean"); +assert.equal(programGraphSourceFreeStdStrSync.ok, true); +assert.equal(programGraphSourceFreeStdStrCheckJson.ok, true); +assertSourceGraph(programGraphSourceFreeStdStrCheckJson, `${programGraphSourceFreeStdStrPackage}/zero.graph`, "package:program-graph-source-free-std-str@0.1.0", "graph-native-check", false, "missing"); +assertProgramGraphCompilerInput(programGraphSourceFreeStdStrCheckJson, `${programGraphSourceFreeStdStrPackage}/zero.graph`); +assertRepositoryGraphNativeCheck(programGraphSourceFreeStdStrCheckJson, "missing"); +assert.equal(programGraphSourceFreeStdStrCheckJson.targetReadiness.ok, true); +assert.equal(programGraphSourceFreeStdStrCheckJson.targetReadiness.diagnostics.length, 0); +assert(programGraphSourceFreeStdStrCheckJson.graphCompiler.semanticFacts.calls.some((call) => call.qualifiedName === "std.str.reverse" && call.contract.kind === "stdlib" && call.resolution.targetKind === "stdlib" && call.returnType === "Maybe>")); +assert.equal(programGraphCrmApiCheckJson.ok, true); +assert.equal(programGraphCrmApiStatusJson.store.encoding, "binary"); +assert.equal(programGraphCrmApiStatusJson.storage.encoding, "single-file-binary"); +assert.equal(programGraphCrmApiStatusJson.repositoryGraph.projectionValidity, "clean"); +assert.equal(programGraphCrmApiCheckJson.sourceFile, "examples/crm-api/zero.graph"); +assertSourceGraph(programGraphCrmApiCheckJson, "examples/crm-api/zero.graph", "package:crm-api@0.1.0", "graph-native-check", false, "clean"); +assertRepositoryGraphNativeCheck(programGraphCrmApiCheckJson, "clean"); +assert.equal(programGraphCrmApiBuildJson.sourceFile, "examples/crm-api/zero.graph"); +assertSourceGraph(programGraphCrmApiBuildJson, "examples/crm-api/zero.graph", "package:crm-api@0.1.0", "mapped-final-mir", false, "clean"); +assert.equal(programGraphCrmApiBuildJson.generatedCBytes, 0); +assert.equal(programGraphCrmApiBuildJson.incrementalInvalidation.sourceKind, "program-graph"); +assert.equal(programGraphCrmApiBuildJson.incrementalInvalidation.graphInput.parserArtifactsInKey, false); +assert.equal(programGraphCrmApiHealth.stdout, "HTTP/1.1 200 OK\r\ncontent-type: application/json\r\nconnection: close\r\ncontent-length: 27\r\n\r\n{\"ok\":true,\"service\":\"crm\"}"); +assert.match(programGraphCrmApiAccounts.stdout, /"accounts":\[/); +assert.match(programGraphCrmApiDealUpdate.stdout, /"updated":true/); +assert.match(programGraphCrmApiMissing.stdout, /^HTTP\/1\.1 404 Not Found\r\n/); +assert.equal(programGraphAuthoringInit.ok, true); +assert.equal(programGraphAuthoringInit.compilerInput, "repository-graph"); +assert.equal(programGraphAuthoringInit.sourceProjection.path, "src/main.0"); +assert.equal(programGraphAuthoringInit.sourceProjection.materialized, false); +assert.equal(programGraphAuthoringProjectionExistsAfterInit, false); +assert.equal(programGraphAuthoringPatch.ok, true); +assert.equal(programGraphAuthoringPatch.operationCount, 7); +assert.equal(programGraphAuthoringPatch.artifact, "./zero.graph"); +assert.equal(programGraphAuthoringPatch.saved.path, "./zero.graph"); +assert.equal(programGraphAuthoringProjectionExistsAfterPatch, false); +assert.equal(programGraphAuthoringStatusMissing.repositoryGraph.projectionState, "source-missing"); +assert.equal(programGraphAuthoringStatusMissing.repositoryGraph.projectionValidity, "missing"); +assert.notEqual(programGraphAuthoringVerifyMissing.code, 0); +const programGraphAuthoringVerifyMissingBody = JSON.parse(programGraphAuthoringVerifyMissing.stdout); +assert.equal(programGraphAuthoringVerifyMissingBody.diagnostics[0].code, "RGP006"); +assert.equal(programGraphAuthoringVerifyMissingBody.diagnostics[0].actual, "missing source projection file"); +assert.match(programGraphAuthoringVerifyMissingBody.repairCommands.join("\n"), /zero export/); +assert.equal(programGraphAuthoringCheck.ok, true); +assert.equal(programGraphAuthoringCheck.sourceFile, `${programGraphAuthoringPackage}/zero.graph`); +assert.equal(programGraphAuthoringCheck.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphAuthoringCheck, "missing"); +assertProgramGraphCompilerInput(programGraphAuthoringCheck, `${programGraphAuthoringPackage}/zero.graph`); +assert.equal(programGraphAuthoringRun.stdout, "graph authoring ok\n"); +assert.equal(programGraphAuthoringTest.ok, true); +assert.equal(programGraphAuthoringTest.testDiscovery.mode, "package-graph"); +assert.equal(programGraphAuthoringTest.passedTests, 1); +assert.equal(programGraphAuthoringExport.ok, true); +assert.equal(programGraphAuthoringExport.repositoryGraph.projectionState, "clean"); +assert.deepEqual(programGraphAuthoringExport.changedPaths, [`${programGraphAuthoringPackage}/src/main.0`]); +assert.match(programGraphAuthoringProjectionText, /pub fn main\(world: World\) -> Void raises \{/); +assert.match(programGraphAuthoringProjectionText, /fn add\(left: i32, right: i32\) -> i32/); +assert.match(programGraphAuthoringProjectionText, /test "addition works"/); +assert.equal(programGraphAuthoringVerifyAfter.ok, true); +assert.equal(programGraphAuthoringVerifyAfter.repositoryGraph.projectionState, "clean"); +assert.equal(programGraphAuthoringCheckAfter.ok, true); +assert.equal(programGraphAuthoringCheckAfter.graph.sourceProjectionState, "clean"); +assert.equal(programGraphAuthoringStatusAfterHumanEdit.repositoryGraph.projectionState, "source-stale"); +assert.equal(programGraphAuthoringImport.ok, true); +assert.equal(programGraphAuthoringImport.repositoryGraph.projectionState, "clean"); +assert.deepEqual(programGraphAuthoringImport.changedPaths, [`${programGraphAuthoringPackage}/zero.graph`]); +assert.equal(programGraphAuthoringCheckAfterHumanEdit.ok, true); +assert.equal(programGraphAuthoringCheckAfterHumanEdit.graph.sourceProjectionState, "clean"); +assert.equal(programGraphAuthoringRunAfterHumanEdit.stdout, "human edit ok\n"); +assert.equal(programGraphBuilderOpsInit.ok, true); +assert.equal(programGraphBuilderOpsInit.sourceProjection.materialized, false); +assert.equal(programGraphBuilderOpsPatch.ok, true); +assert.equal(programGraphBuilderOpsPatch.operationCount, 10); +assert.equal(programGraphBuilderOpsProjectionExistsAfterPatch, false); +assert.equal(programGraphBuilderOpsQuery.ok, true); +assert(programGraphBuilderOpsQuery.functions.some((fun) => fun.name === "add_twice" && fun.returnType === "u32")); +assert(programGraphBuilderOpsQuery.patchOperations.includes("addLetLiteral fn=\"main\" name=\"count\" type=\"u32\" value=\"0\"")); +assert(programGraphBuilderOpsQuery.patchOperations.includes("addLetBinary fn=\"add\" name=\"sum\" type=\"i32\" operator=\"+\" left=\"left\" right=\"right\"")); +assert(programGraphBuilderOpsQuery.patchOperations.includes("addReturnValue fn=\"identity\" value=\"input\" type=\"i32\"")); +assert(programGraphBuilderOpsQuery.patchOperations.includes("addCheckWriteValue fn=\"main\" value=\"message\" type=\"String\"")); +assert.match(programGraphBuilderOpsView, /let message: String = "graph value write ok\\n"/); +assert.match(programGraphBuilderOpsView, /check world\.out\.write\(message\)/); +assert.match(programGraphBuilderOpsView, /let first: u32 = x \+ y/); +assert.match(programGraphBuilderOpsView, /let total: u32 = first \+ y/); +assert.match(programGraphBuilderOpsView, /return total/); +assert.equal(programGraphBuilderOpsCheck.ok, true); +assert.equal(programGraphBuilderOpsCheck.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphBuilderOpsCheck, "missing"); +assert.equal(programGraphBuilderOpsTest.ok, true); +assert.equal(programGraphBuilderOpsTest.passedTests, 1); +assert.equal(programGraphBuilderOpsRun.stdout, "graph value write ok\n"); +assert.equal(programGraphBuilderOpsSync.ok, true); +assert.deepEqual(programGraphBuilderOpsSync.changedPaths, [`${programGraphBuilderOpsPackage}/src/main.0`]); +assert.equal(programGraphBuilderOpsProjectionText, programGraphBuilderOpsView); +assert.equal(programGraphLoopTestInit.ok, true); +assert.equal(programGraphLoopTestPatch.ok, true); +assert.equal(programGraphLoopTestBodyPatch.ok, true); +assert.equal(programGraphLoopTestBodyPatch.operations[0].op, "replaceFunctionBody"); +assert.equal(programGraphLoopTestAddTest.ok, true); +assert.equal(programGraphLoopTestCheck.ok, true); +assert.equal(programGraphLoopTestCheck.sourceFile, `${programGraphLoopTestPackage}/zero.graph`); +assertRepositoryGraphNativeCheck(programGraphLoopTestCheck, "missing"); +assert.equal(programGraphLoopTestRun.ok, true); +assert.equal(programGraphLoopTestRun.testBackend, "direct-program-graph"); +assert.equal(programGraphLoopTestRun.passedTests, 1); +assert.match(programGraphLoopTestView, /while i < n \{/); +assert.match(programGraphLoopTestView, /i = i \+ 1/); +assert.equal(programGraphArrayLengthInit.ok, true); +assert.equal(programGraphArrayLengthMainPatch.ok, true); +assert.equal(programGraphArrayLengthRepeatPatch.ok, false); +assert.equal(programGraphArrayLengthRepeatPatch.diagnostics[0].code, "TYP002"); +assert.equal(programGraphArrayLengthRepeatPatch.diagnostics[0].expected, "[64]u8"); +assert.equal(programGraphArrayLengthRepeatPatch.diagnostics[0].actual, "repeat count 32"); +assert.match(programGraphArrayLengthRepeatPatch.diagnostics[0].help, /make the lengths agree/); +assert.equal(programGraphArrayLengthListPatch.ok, false); +assert.equal(programGraphArrayLengthListPatch.diagnostics[0].code, "TYP002"); +assert.equal(programGraphArrayLengthListPatch.diagnostics[0].expected, "[4]u8"); +assert.equal(programGraphArrayLengthListPatch.diagnostics[0].actual, "2 element(s)"); +assert.equal(programGraphArrayLengthMatchingPatch.ok, true); +assert.equal(programGraphArrayLengthCheck.ok, true); +assertRepositoryGraphNativeCheck(programGraphArrayLengthCheck, "missing"); +assert.equal(programGraphBlockBodyInit.ok, true); +assert.equal(programGraphBlockBodyMainPatch.ok, true); +assert.equal(programGraphBlockBodyMainPatch.operationCount, 1); +assert.equal(programGraphBlockBodyGreetingPatch.ok, true); +assert.equal(programGraphBlockBodyGreetingPatch.operationCount, 1); +assert.equal(programGraphBlockBodyGreetingPatch.operations[0].op, "replaceFunctionBody"); +assert.equal(programGraphBlockBodyBlocks.ok, true); +assert(programGraphBlockBodyBlocks.patchOperations.some((op) => op.startsWith("replaceBlockBody #block_id\n"))); +assert.equal(programGraphBlockBodyDryRun.ok, true); +assert.equal(programGraphBlockBodyDryRun.checkOnly, true); +assert.equal(programGraphBlockBodyDryRun.saved, null); +assert.equal(programGraphBlockBodyPatch.ok, true); +assert.equal(programGraphBlockBodyPatch.operationCount, 1); +assert.equal(programGraphBlockBodyPatch.operations[0].op, "replaceBlockBody"); +assert.match(programGraphBlockBodyView, /if name\.has \{/); +assert.match(programGraphBlockBodyView, /check world\.out\.write\("name \+ value: "\)/); +assert.match(programGraphBlockBodyView, /check world\.out\.write\("hello anonymous\\n"\)/); +assert.equal(programGraphBlockBodyCheck.ok, true); +assert.equal(programGraphBlockBodyCheck.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphBlockBodyCheck, "missing"); +assert.equal(programGraphBlockBodyRun.stdout, "name + value: Ada\n"); +assert.equal(programGraphAuthoringCliInit.ok, true); +assert.equal(programGraphAuthoringCliInit.sourceProjection.materialized, false); +assert.equal(programGraphAuthoringCliPatch.ok, true); +assert.equal(programGraphAuthoringCliPatch.operationCount, 5); +assert.equal(programGraphAuthoringCliBodyPatch.ok, true); +assert.equal(programGraphAuthoringCliBodyPatch.operationCount, 1); +assert.equal(programGraphAuthoringCliBodyPatch.operations[0].op, "replaceFunctionBody"); +assert.equal(programGraphAuthoringCliProjectionExistsAfterInit, false); +assert.equal(programGraphAuthoringCliProjectionExistsAfterPatch, false); +assert.equal(programGraphAuthoringCliStaleAddPatch.ok, true); +assert.equal(programGraphAuthoringCliStaleAddPatch.operationCount, 5); +assert.equal(programGraphAuthoringCliFindAdd.ok, true); +assert(programGraphAuthoringCliFindAdd.matches.some((node) => node.id === "#fn_add" && node.kind === "Function")); +assert(programGraphAuthoringCliFindAdd.matches.some((node) => node.id === "#fn___zero_test_0" && node.kind === "Function" && node.value === "add works")); +assert.equal(programGraphAuthoringCliNodeAdd.ok, true); +assert.equal(programGraphAuthoringCliNodeAdd.query.node, "#fn_add"); +assert.equal(programGraphAuthoringCliNodeAdd.node.selected.id, "#fn_add"); +assert(programGraphAuthoringCliNodeAdd.node.parents.some((edge) => edge.kind === "function" && edge.from === "#mod_main")); +assert(programGraphAuthoringCliNodeAdd.node.children.some((edge) => edge.kind === "body" && edge.to === "#block_add_body")); +assert(programGraphAuthoringCliNodeAdd.node.children.some((edge) => edge.kind === "param" && edge.to === "#param_add_x")); +assert.equal(programGraphAuthoringCliCleanupPatch.ok, true); +assert.equal(programGraphAuthoringCliCleanupPatch.operationCount, 3); +assert.match(programGraphAuthoringCliCallsText, /query: fn:main calls:std/); +assert.match(programGraphAuthoringCliCallsText, /qualified:std\.args\.parseU32/); +assert.match(programGraphAuthoringCliCallsText, /qualified:std\.fmt\.u32/); +assert.match(programGraphAuthoringCliCallsText, /resolved:true/); +assert.match(programGraphAuthoringCliRefsText, /query: refs:add_u32/); +assert.match(programGraphAuthoringCliRefsText, /target:function node:#fn_add_u32/); +assert.match(programGraphAuthoringCliRefsText, /fn:main name:add_u32/); +assert.equal(programGraphAuthoringCliQuery.ok, true); +assert.equal(programGraphAuthoringCliQuery.inputKind, "repository-graph"); +assert(programGraphAuthoringCliQuery.functions.some((fun) => fun.name === "main" && fun.public && fun.fallible)); +assert(programGraphAuthoringCliQuery.functions.some((fun) => fun.name === "add_u32" && fun.returnType === "u32" && fun.params.length === 2)); +assert(!programGraphAuthoringCliQuery.functions.some((fun) => fun.name === "add")); +assert(programGraphAuthoringCliQuery.functions.some((fun) => fun.test === "add_u32 works")); +assert(!programGraphAuthoringCliQuery.patchOperations.some((op) => op.includes("setMain"))); +assert(programGraphAuthoringCliQuery.patchOperations.some((op) => op.startsWith("replaceFunctionBody main\n"))); +assert.equal(programGraphAuthoringCliCheck.ok, true); +assert.equal(programGraphAuthoringCliCheck.sourceFile, `${programGraphAuthoringCliPackage}/zero.graph`); +assert.equal(programGraphAuthoringCliCheck.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphAuthoringCliCheck, "missing"); +assert.match(programGraphAuthoringCliGraphBuild.stdout, /program-graph-authoring-cli-graph-build/); +assert.match(programGraphAuthoringCliBuild.stdout, /program-graph-authoring-cli-build/); +assert.equal(programGraphAuthoringCliTest.ok, true); +assert.equal(programGraphAuthoringCliTest.passedTests, 1); +assert.equal(programGraphAuthoringCliGraphTest.stdout, "1 test(s) ok\n"); +assert.equal(programGraphAuthoringCliRun.stdout, "42\n"); +assert.equal(programGraphAuthoringCliGraphRun.stdout, "15\n"); +assertSourceGraph(programGraphAuthoringCliSize, `${programGraphAuthoringCliPackage}/zero.graph`, "package:program-graph-authoring-cli@0.1.0", "mapped-final-mir", false, "missing"); +assert(programGraphAuthoringCliSize.sizeBreakdown.stdlibHelpers.some((helper) => helper.name === "std.args.parseU32")); +assert(programGraphAuthoringCliSize.sizeBreakdown.stdlibHelpers.some((helper) => helper.name === "std.fmt.u32")); +assert.equal(programGraphAuthoringCliSync.ok, true); +assert.deepEqual(programGraphAuthoringCliSync.changedPaths, [`${programGraphAuthoringCliPackage}/src/main.0`]); +assert.match(programGraphAuthoringCliProjectionText, /fn add_u32\(x: u32, y: u32\) -> u32/); +assert.match(programGraphAuthoringCliProjectionText, /test "add_u32 works"/); +assert.doesNotMatch(programGraphAuthoringCliProjectionText, /fn add\(x: i32, y: i32\) -> i32/); +assert.equal(programGraphAuthoringCliStatusAfterHumanEdit.repositoryGraph.projectionState, "source-stale"); +assert.equal(programGraphAuthoringCliImport.ok, true); +assert.deepEqual(programGraphAuthoringCliImport.changedPaths, [`${programGraphAuthoringCliPackage}/zero.graph`]); +assert.equal(programGraphAuthoringCliImport.store.nodes, 96); +assert.equal(programGraphAuthoringCliImport.store.sources, 1); +assert.equal(programGraphAuthoringCliQueryAfterHumanEdit.ok, true); +assert.equal(programGraphAuthoringCliQueryAfterHumanEdit.counts.nodes, 96); +assert.deepEqual(programGraphAuthoringCliQueryAfterHumanEdit.modules.map((module) => module.name), ["main"]); +assert(programGraphAuthoringCliQueryAfterHumanEdit.calls.some((call) => call.qualifiedName === "std.args.parseU32" && call.targetKind === "stdlib")); +assert(programGraphAuthoringCliQueryAfterHumanEdit.calls.some((call) => call.qualifiedName === "std.fmt.u32" && call.targetKind === "stdlib")); +assert(!programGraphAuthoringCliQueryAfterHumanEdit.calls.some((call) => call.targetKind === "graphBackedStdlib")); +assert.match(programGraphAuthoringCliFindUsageText, /value:usage: zero run \. -- \\n path:/); +assert.equal(programGraphAuthoringCliVerifyAfterHumanEdit.ok, true); +assert.equal(programGraphAuthoringCliVerifyAfterHumanEdit.repositoryGraph.projectionState, "clean"); +assert.equal(programGraphAuthoringCliCheckAfterHumanEdit.ok, true); +assert.equal(programGraphAuthoringCliCheckAfterHumanEdit.sourceFile, `${programGraphAuthoringCliPackage}/zero.graph`); +assertRepositoryGraphNativeCheck(programGraphAuthoringCliCheckAfterHumanEdit, "clean"); +assert.equal(programGraphAuthoringCliTestAfterHumanEdit.ok, true); +assert.equal(programGraphAuthoringCliTestAfterHumanEdit.passedTests, 1); +assert.equal(programGraphAuthoringCliRunAfterHumanEdit.stdout, "11\n"); +assert.equal(programGraphSourceFreeCImportSync.ok, true); +assert.equal(programGraphSourceFreeCImportSync.repositoryGraph.projectionValidity, "clean"); +assert.equal(programGraphSourceFreeCImportCheck.ok, true); +assert.equal(programGraphSourceFreeCImportCheck.graph.sourceProjectionState, "missing"); +assertRepositoryGraphNativeCheck(programGraphSourceFreeCImportCheck, "missing"); +assert.equal(programGraphSourceFreeCImportRun.stdout, "source-free c import ok\n"); +assert.equal(programGraphSourceFreeCImportCwdBuild.sourceFile, resolve(`${programGraphSourceFreeCImportPackage}/zero.graph`)); +assert.equal(programGraphSourceFreeCImportCwdBuild.graph.artifact, resolve(`${programGraphSourceFreeCImportPackage}/zero.graph`)); +assert.equal(programGraphSourceFreeCImportCwdBuild.graph.sourceProjectionState, "missing"); +assert.notEqual(programGraphIdentityMismatchCheck.code, 0); +const programGraphIdentityMismatchCheckBody = JSON.parse(programGraphIdentityMismatchCheck.stdout); +assert.equal(programGraphIdentityMismatchCheckBody.diagnostics[0].code, "RGP007"); +assert.equal(programGraphIdentityMismatchCheckBody.diagnostics[0].expected, "package:program-graph-wrong-package@9.9.9"); +assert.equal(programGraphIdentityMismatchCheckBody.diagnostics[0].actual, "package:program-graph-fixture@0.1.0"); +assert.notEqual(programGraphIdentityMismatchSize.code, 0); +assert.equal(JSON.parse(programGraphIdentityMismatchSize.stdout).diagnostics[0].code, "RGP007"); +assert.notEqual(programGraphMissingPackageNameCheck.code, 0); +const programGraphMissingPackageNameBody = JSON.parse(programGraphMissingPackageNameCheck.stdout); +assert.equal(programGraphMissingPackageNameBody.diagnostics[0].code, "RGP007"); +assert.equal(programGraphMissingPackageNameBody.diagnostics[0].message, "repository graph compiler input requires package.name"); +assert.match(programGraphMissingPackageNameBody.diagnostics[0].actual, /package:program-graph-fixture@0\.1\.0/); +assert.equal(programGraphBadProjectionStatus.repositoryGraph.projectionState, "conflict"); +assert.equal(programGraphBadProjectionStatus.repositoryGraph.projectionValidity, "conflict"); +assert.equal(programGraphBadProjectionCheck.ok, true); +assert.equal(programGraphBadProjectionCheck.graph.sourceProjectionState, "conflict"); +assert.notEqual(programGraphBadProjectionSync.code, 0); +assert.equal(JSON.parse(programGraphBadProjectionSync.stdout).diagnostics[0].code, "RGP004"); +assert.notEqual(programGraphMissingStoreCheck.code, 0); +const programGraphMissingStoreBody = JSON.parse(programGraphMissingStoreCheck.stdout); +assert.equal(programGraphMissingStoreBody.ok, false); +assert.equal(programGraphMissingStoreBody.mode, "compiler-input"); +assert.equal(programGraphMissingStoreBody.repositoryGraph.storePresent, false); +assert.equal(programGraphMissingStoreBody.diagnostics[0].code, "RGP001"); +assert.equal(programGraphMissingStoreBody.diagnostics[0].path, programGraphMissingStorePackage); +assert.match(programGraphMissingStoreBody.repairCommands.join("\n"), /zero import/); +assert.notEqual(programGraphInvalidStoreCheck.code, 0); +const programGraphInvalidStoreBody = JSON.parse(programGraphInvalidStoreCheck.stdout); +assert.equal(programGraphInvalidStoreBody.ok, false); +assert.equal(programGraphInvalidStoreBody.mode, "compiler-input"); +assert.equal(programGraphInvalidStoreBody.repositoryGraph.storePresent, true); +assert.equal(programGraphInvalidStoreBody.repositoryGraph.storeValid, false); +assert.equal(programGraphInvalidStoreBody.diagnostics[0].code, "RGP003"); +assert.equal(programGraphInvalidStoreBody.diagnostics[0].path, programGraphInvalidStorePackage); +assert.match(programGraphInvalidStoreBody.repairCommands.join("\n"), /zero import/); +assert.equal(programGraphSourceFixtureDriftCheck.ok, true); +assert.equal(programGraphSourceFixtureDriftCheck.graph.lowering, "graph-native-check"); +assert.equal(programGraphSourceFixtureDriftCheck.graph.sourceProjectionState, "clean"); +assertRepositoryGraphNativeCheck(programGraphSourceFixtureDriftCheck, "clean"); +assert.equal(programGraphSourceFixtureDriftVerifyAfterRefresh.ok, true); +assert.notEqual(programGraphSourceFixtureDriftVerify.code, 0); +const programGraphSourceFixtureDriftBody = JSON.parse(programGraphSourceFixtureDriftVerify.stdout); +assert.equal(programGraphSourceFixtureDriftBody.ok, false); +assert.equal(programGraphSourceFixtureDriftBody.mode, "verify-projection"); +assert.equal(programGraphSourceFixtureDriftBody.repositoryGraph.compilerInput, "repository-graph"); +assert.equal(programGraphSourceFixtureDriftBody.diagnostics[0].code, "RGP006"); +assert.match(programGraphSourceFixtureDriftBody.repairCommands.join("\n"), /zero import/); +assert.match(programGraphSourceFixtureDriftBody.repairCommands.join("\n"), /zero export/); +assert.equal(programGraphTargetIncompatibleSync.ok, true); +assert.notEqual(programGraphTargetIncompatibleCheck.code, 0); +const programGraphTargetIncompatibleBody = JSON.parse(programGraphTargetIncompatibleCheck.stdout); +assert.equal(programGraphTargetIncompatibleBody.ok, false); +assert.equal(programGraphTargetIncompatibleBody.diagnostics[0].code, "PKG004"); +assert.match(programGraphTargetIncompatibleBody.diagnostics[0].actual, /target-webbits targets/); +assert.equal(programGraphTargetCapabilitySync.ok, true); +assert.notEqual(programGraphTargetCapabilityCheck.code, 0); +const programGraphTargetCapabilityBody = JSON.parse(programGraphTargetCapabilityCheck.stdout); +assert.equal(programGraphTargetCapabilityBody.ok, false); +assert.equal(programGraphTargetCapabilityBody.diagnostics[0].code, "TAR002"); +assert.match(programGraphTargetCapabilityBody.diagnostics[0].actual, /lacks Fs/); +const programGraphBackendMismatchCheck = JSON.parse((await execFileAsync(zero, ["check", "--json", "--backend", "zero-coff-x64", "--target", "linux-musl-x64", programGraphSourceFixturePackage])).stdout); +assert.equal(programGraphBackendMismatchCheck.ok, true); +assert.equal(programGraphBackendMismatchCheck.targetReadiness.ok, false); +assert.equal(programGraphBackendMismatchCheck.targetReadiness.diagnostics[0].code, "BLD004"); +assert.equal(programGraphBackendMismatchCheck.targetReadiness.diagnostics[0].backendBlocker.backend, "zero-coff-x64"); +const programGraphRepositoryStatus = JSON.parse((await execFileAsync(zero, ["status", "--json", "--target", "linux-musl-x64", programGraphSourceFixturePackage])).stdout); +assert.equal(programGraphRepositoryStatus.repositoryGraph.storePresent, true); +assert.equal(programGraphRepositoryStatus.repositoryGraph.storeValid, true); +assert.equal(programGraphRepositoryStatus.repositoryGraph.projectionState, "clean"); +assert.equal(programGraphRepositoryStatus.repositoryGraph.compilerInput, "repository-graph"); +const programGraphRepositoryVerify = JSON.parse((await execFileAsync(zero, ["verify-projection", "--json", "--target", "linux-musl-x64", programGraphSourceFixturePackage])).stdout); +assert.equal(programGraphRepositoryVerify.ok, true); +assert.equal(programGraphRepositoryVerify.writes, false); +assert.deepEqual(programGraphDumpJson, programGraphBody); +assert.match(programGraphDump, /^zero-graph v1\n/); +assert.match(programGraphDump, /origin source-text/); +assert.match(programGraphDump, /module "hello"/); +assert.match(programGraphDump, /hash "graph:[0-9a-f]{16}"/); +assert.doesNotMatch(programGraphDump, /validation "shape-valid" ok/); +assert.doesNotMatch(programGraphDump, /idStrategy/); +assert.doesNotMatch(programGraphDump, /canonicalSource/); +assert.doesNotMatch(programGraphDump, /moduleIdentity/); +assert.doesNotMatch(programGraphDump, /graphHash/); +assert.doesNotMatch(programGraphDump, /counts nodes=/); +assert.doesNotMatch(programGraphDump, /node id=/); +assert.match(programGraphDump, /node #mod_[0-9a-f]{8} Module name:"hello"/); +assert.match(programGraphDump, /edge #mod_[0-9a-f]{8} function #decl_[0-9a-f]{8} order:0/); +assert.match(programGraphView, /^pub fn main\(world: World\) -> Void raises \{\n/); +assert.match(programGraphView, /check world\.out\.write\("hello from zero\\n"\)/); +assert.match(programGraphRichView, /bytesTail\(bytesSpan\)\[1\]/); +assert.match(programGraphRichView, /numbers\[2\.\.\]/); +assert.match(programGraphCharView, /again == 'A'/); +assert.match(programGraphBody.graphHash, /^graph:[0-9a-f]{16}$/); +assert.equal(programGraphBody.validation.ok, true); +assert.equal(programGraphBody.validation.state, "shape-valid"); +assert(programGraphBody.counts.nodes > 0); +assert(programGraphBody.counts.edges > 0); +assert(programGraphBody.nodes.every((item) => /^#[a-z][a-z0-9]*_[0-9a-f]{8}(-[0-9a-f]{4}(-[0-9]+)?)?$/.test(item.id) && /^nodehash:[0-9a-f]{16}$/.test(item.nodeHash))); +assert(programGraphBody.edges.every((item) => item.target === "node")); +assert(programGraphBody.nodes.some((item) => item.kind === "Module" && item.name === "hello" && item.symbolId === "symbol:hello::module")); +assert(programGraphBody.nodes.some((item) => item.kind === "Function" && item.name === "main" && item.public === true && item.fallible === true && item.symbolId === "symbol:hello::value.main" && /^type:[0-9a-f]{16}$/.test(item.typeId))); +assert(programGraphBody.nodes.some((item) => item.kind === "Param" && item.name === "world" && item.type === "World" && item.symbolId === "symbol:hello::value.main/param.world")); +assert(programGraphBody.nodes.some((item) => item.kind === "EffectRef" && item.name === "error" && /^effect:[0-9a-f]{16}$/.test(item.effectId))); +assert(programGraphBody.nodes.some((item) => item.kind === "Check")); +assert(programGraphBody.nodes.some((item) => item.kind === "MethodCall")); +assert(programGraphBody.edges.some((item) => item.kind === "body")); +const programGraphWrongSchemaPath = `${outDir}/wrong-schema.program-graph`; +await writeFile(programGraphWrongSchemaPath, "zero-graph v2\n"); +const programGraphWrongSchema = await execFileAsync(zero, ["validate", "--json", programGraphWrongSchemaPath]).catch((error) => error); +assert(programGraphWrongSchema.code); +assert.equal(JSON.parse(programGraphWrongSchema.stdout).diagnostics[0].message, "unknown program graph schema version"); +const programGraphFailedArtifactPath = `${outDir}/failed-validation.program-graph`; +await writeFile(programGraphFailedArtifactPath, [ + "zero-graph v1", + "origin source-text", + "module \"main\"", + "hash \"\"", + "validation \"decoded\" failed", + "diagnostic code:\"GRF001\" message:\"program graph construction failed\"", + "", +].join("\n")); +const programGraphFailedArtifact = await execFileAsync(zero, ["validate", "--json", programGraphFailedArtifactPath]).catch((error) => error); +assert(programGraphFailedArtifact.code); +assert.equal(JSON.parse(programGraphFailedArtifact.stdout).diagnostics[0].message, "program graph input reports failed validation"); +const programGraphTrailingArtifactPath = `${outDir}/trailing-content.program-graph`; +await writeFile(programGraphTrailingArtifactPath, `${programGraphDump}\nextra\n`); +const programGraphTrailingArtifact = await execFileAsync(zero, ["validate", "--json", programGraphTrailingArtifactPath]).catch((error) => error); +assert(programGraphTrailingArtifact.code); +assert.equal(JSON.parse(programGraphTrailingArtifact.stdout).diagnostics[0].message, "unexpected content after graph header"); +assert(programGraphBody.edges.some((item) => item.kind === "statement" && item.order === 0)); + +const programGraphDuplicateIdFixture = `${outDir}/program-graph-duplicate-id-stress.0`; +const programGraphDuplicateIdPath = `${outDir}/program-graph-duplicate-id-stress.program-graph`; +await writeFile(programGraphDuplicateIdFixture, [ + "pub fn main() -> Void {", + ...Array.from({ length: 700 }, (_, index) => ` let x${index}: i32 = 1`), + "}", + "", +].join("\n")); +const programGraphDuplicateIdDump = await execFileAsync(zero, ["import", "--format", "text", "--out", programGraphDuplicateIdPath, programGraphDuplicateIdFixture]); +assert.equal(programGraphDuplicateIdDump.stdout, ""); +assert.equal((await execFileAsync(zero, ["validate", programGraphDuplicateIdPath])).stdout, "program graph ok\n"); +const programGraphDuplicateIdText = await readFile(programGraphDuplicateIdPath, "utf8"); +const programGraphDuplicateIds = [...programGraphDuplicateIdText.matchAll(/^node (#[^ ]+)/gm)].map((match) => match[1]); +assert.equal(new Set(programGraphDuplicateIds).size, programGraphDuplicateIds.length); +assert(programGraphDuplicateIds.some((id) => /^#[a-z][a-z0-9]*_[0-9a-f]{8}-[0-9a-f]{4}(-[0-9]+)?$/.test(id))); + +const programGraphControlFixture = `${outDir}/program-graph-control.0`; +const programGraphControlPath = await writeGraphFixture(programGraphControlFixture, "pub fn main(world: World) -> Void raises {\n check world.out.write(\"\\x01 ok\\n\")\n}\n"); +const programGraphControl = JSON.parse((await execFileAsync(zero, ["inspect", "--json", programGraphControlPath])).stdout).programGraph; +assert(programGraphControl.nodes.some((item) => item.kind === "Literal" && item.value === "\u0001 ok\n")); + +const programGraphMatchRanges = JSON.parse((await execFileAsync(zero, ["inspect", "--json", "conformance/native/pass/match-scalar-guards.0"])).stdout).programGraph; +const programGraphRangeArm = programGraphMatchRanges.nodes.find((item) => item.kind === "MatchArm" && item.name === "1"); +assert(programGraphRangeArm); +const programGraphRangeEdge = programGraphMatchRanges.edges.find((item) => item.from === programGraphRangeArm.id && item.kind === "rangeEnd"); +assert(programGraphRangeEdge); +assert(programGraphMatchRanges.nodes.some((item) => item.id === programGraphRangeEdge.to && item.kind === "Literal" && item.value === "3")); + +const programGraphShapeDefaults = JSON.parse((await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/shape-field-defaults.0"])).stdout).programGraph; +const programGraphDefaultField = programGraphShapeDefaults.nodes.find((item) => item.kind === "Field" && item.name === "left"); +assert(programGraphDefaultField); +const programGraphDefaultEdge = programGraphShapeDefaults.edges.find((item) => item.from === programGraphDefaultField.id && item.kind === "default"); +assert(programGraphDefaultEdge); +assert(programGraphShapeDefaults.nodes.some((item) => item.id === programGraphDefaultEdge.to && item.kind === "Literal" && item.value === "1")); + +const memorySize = await execFileAsync(zero, ["size", "--json", "--target", "linux-musl-x64", "examples/memory-package"]); +const memorySizeBody = JSON.parse(memorySize.stdout); +assert.equal(memorySizeBody.target, "linux-musl-x64"); +assert.equal(memorySizeBody.targetSupport.fsAvailable, true); +assert(memorySizeBody.requiresCapabilities.includes("memory")); +assert(!memorySizeBody.requiresCapabilities.includes("fs")); +assert.equal(memorySizeBody.generatedCBytes, 0); +assert.equal(memorySizeBody.cBridgeFallback, false); +assert(memorySizeBody.loweredIrBytes > 0); +assert(memorySizeBody.sections.some((section) => section.name === "direct-size-metadata" && section.kind === "metadata")); +assert(memorySizeBody.objectBackend.objectEmission.path.includes("direct-")); +assert.equal(memorySizeBody.objectBackend.emitKind, "size"); +assert(memorySizeBody.stdlibHelpers.some((helper) => helper.name === "std.mem.copy")); +assert(memorySizeBody.stdlibHelperAttribution.some((helper) => helper.name === "std.mem.copy" && helper.estimatedDirectBytes > 0)); +assert(memorySizeBody.usedStdlibHelpers.every((helper) => helper.module && helper.effects?.length && helper.errorBehavior && helper.ownershipNotes && helper.example)); +assert(memorySizeBody.runtimeShims.some((shim) => shim.name === "stdio-world" && shim.payAsUsed === true)); + +const genericPairSize = await execFileAsync(zero, ["size", "--json", "examples/generic-pair.0"]); +const genericPairSizeBody = JSON.parse(genericPairSize.stdout); +assert.equal(genericPairSizeBody.graph.artifact, "examples/generic-pair.graph"); +assert.equal(genericPairSizeBody.graph.lowering, "mapped-final-mir"); +assert.equal(genericPairSizeBody.cBridgeFallback, false); +assert(genericPairSizeBody.loweredIrBytes > 0); + +const genericInferredSize = await execFileAsync(zero, ["size", "--json", "conformance/native/pass/generic-inferred-specialized-call.0"]); +const genericInferredSizeBody = JSON.parse(genericInferredSize.stdout); +assert.equal(genericInferredSizeBody.graph.artifact, "conformance/native/pass/generic-inferred-specialized-call.graph"); +assert.equal(genericInferredSizeBody.graph.lowering, "mapped-final-mir"); +assert.equal(genericInferredSizeBody.cBridgeFallback, false); +assert(genericInferredSizeBody.loweredIrBytes > 0); + +const genericStaticForwardedSize = await execFileAsync(zero, ["size", "--json", "conformance/native/pass/generic-static-forwarded-array-specialization.0"]); +const genericStaticForwardedSizeBody = JSON.parse(genericStaticForwardedSize.stdout); +assert.equal(genericStaticForwardedSizeBody.graph.artifact, "conformance/native/pass/generic-static-forwarded-array-specialization.graph"); +assert.equal(genericStaticForwardedSizeBody.graph.lowering, "mapped-final-mir"); +assert.equal(genericStaticForwardedSizeBody.cBridgeFallback, false); +assert(genericStaticForwardedSizeBody.loweredIrBytes > 0); + +const targetsJson = await execFileAsync(zero, ["targets"]); +const targetsBody = JSON.parse(targetsJson.stdout); +const linuxMuslTarget = targetsBody.targets.find((item) => item.name === "linux-musl-x64"); +const windowsMsvcTarget = targetsBody.targets.find((item) => item.aliases.includes("x86_64-windows-msvc")); +const linuxGnuTarget = targetsBody.targets.find((item) => item.name === "linux-x64"); +const darwinArm64Target = targetsBody.targets.find((item) => item.name === "darwin-arm64"); +const darwinX64Target = targetsBody.targets.find((item) => item.name === "darwin-x64"); +const linuxArm64Target = targetsBody.targets.find((item) => item.name === "linux-arm64"); +assert(linuxMuslTarget.capabilityFacts.some((item) => item.name === "fs" && item.available === true)); +assert.equal(linuxMuslTarget.abi, "musl"); +assert.equal(linuxMuslTarget.objectFormat, "elf"); +assert.equal(linuxMuslTarget.toolchain.crossCompiler, "target-capable C compiler"); +assert.equal(linuxMuslTarget.libcFacts.mode, "bundled-libc"); +assert.equal(linuxMuslTarget.directBackend.status, "native-exe"); +assert.equal(linuxMuslTarget.directBackend.objectSupported, true); +assert.equal(linuxMuslTarget.directBackend.exeSupported, true); +assert.equal(linuxMuslTarget.directBackend.objectEmitter, "zero-elf64"); +assert.equal(linuxMuslTarget.directBackend.exeEmitter, "zero-elf64-exe"); +assert.equal(linuxMuslTarget.directBackend.explicitDirectFallback, "never-c-bridge"); +assert.equal(linuxMuslTarget.httpRuntime.status, "unsupported"); +assert.equal(linuxMuslTarget.httpRuntime.provider, null); +assert.match(linuxMuslTarget.httpRuntime.reason, /lacks net/i); +assert.equal(windowsMsvcTarget.objectFormat, "coff"); +assert.equal(windowsMsvcTarget.libcFacts.mode, "sysroot"); +assert.equal(windowsMsvcTarget.libcFacts.sysrootStatus, "missing"); +assert.equal(windowsMsvcTarget.directBackend.objectSupported, true); +assert.equal(windowsMsvcTarget.directBackend.exeSupported, true); +assert.equal(windowsMsvcTarget.directBackend.objectEmitter, "zero-coff-x64"); +assert.equal(windowsMsvcTarget.directBackend.exeEmitter, "zero-coff-x64-exe"); +assert.equal(linuxGnuTarget.directBackend.objectEmitter, "zero-elf64"); +assert.equal(darwinArm64Target.directBackend.objectEmitter, "zero-macho64"); +assert.equal(darwinArm64Target.directBackend.exeSupported, true); +assert.equal(darwinArm64Target.directBackend.exeEmitter, "zero-macho64-exe"); +assert.equal(darwinX64Target.directBackend.objectEmitter, "zero-macho-x64"); +assert.equal(darwinX64Target.directBackend.exeSupported, true); +assert.equal(darwinX64Target.directBackend.exeEmitter, "zero-macho-x64-exe"); +assert.equal(darwinArm64Target.httpRuntime.provider, targetsBody.host === "darwin-arm64" ? "curl" : null); +assert.equal(darwinArm64Target.httpRuntime.tlsVerification, targetsBody.host === "darwin-arm64"); +if (targetsBody.host === "darwin-arm64") { + assert.equal(darwinArm64Target.httpRuntime.customCa.env, "ZERO_HTTP_TEST_CA_BUNDLE"); +} +assert.equal(linuxArm64Target.directBackend.status, "native-exe"); +assert.equal(linuxArm64Target.directBackend.objectEmitter, "zero-elf-aarch64"); +assert.equal(linuxArm64Target.directBackend.exeEmitter, "zero-elf-aarch64-exe"); +assert.match(linuxArm64Target.directBackend.reason, /direct object and executable backend available/); + +const zlsSelfTest = await execFileAsync("node", [ + "--experimental-strip-types", + "--disable-warning=ExperimentalWarning", + "scripts/zls.mts", + "--self-test", +]); +assert.match(zlsSelfTest.stdout, /zls self-test ok/); + +const cHeaderGraph = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/c-header-import.0"]); +const cHeaderGraphBody = JSON.parse(cHeaderGraph.stdout); +assert(cHeaderGraphBody.cImports.some((item) => item.header === "conformance/c/simple.h" && item.imports.functions >= 1 && item.cacheKey)); +const simpleHeaderImport = cHeaderGraphBody.cImports.find((item) => item.header === "conformance/c/simple.h"); +assert(simpleHeaderImport.typedModel.functions.some((item) => item.name === "zero_c_add" && item.params.length === 2)); +assert(simpleHeaderImport.typedModel.constants.some((item) => item.name === "ZERO_C_ANSWER" && item.value === "42")); +assert(simpleHeaderImport.typedModel.structs.some((item) => item.name === "zero_c_point" && item.fields.some((field) => field.name === "y"))); +assert(simpleHeaderImport.typedModel.enums.some((item) => item.name === "zero_c_color" && item.cases.some((entry) => entry.name === "ZERO_C_BLUE"))); +assert(simpleHeaderImport.typedModel.typedefs.some((item) => item.name === "zero_c_int" && item.target === "int")); +assert.equal(typeof simpleHeaderImport.cache.target, "string"); + +const cImportTargetLinux = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "--target", "linux-musl-x64", "conformance/check/pass/c-import-target-linux.0"]); +const cImportTargetLinuxBody = JSON.parse(cImportTargetLinux.stdout); +assert.equal(cImportTargetLinuxBody.ok, true); +assert.equal(cImportTargetLinuxBody.targetReadiness.ok, true); +assert.equal(cImportTargetLinuxBody.targetReadiness.buildable, true); +const cImportTargetWin = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "--target", "win32-x64.exe", "conformance/check/pass/c-import-target-win.0"]); +const cImportTargetWinBody = JSON.parse(cImportTargetWin.stdout); +assert.equal(cImportTargetWinBody.ok, true); +assert.equal(cImportTargetWinBody.targetReadiness.ok, true); +assert.equal(cImportTargetWinBody.targetReadiness.buildable, true); +const cImportTargetLinuxGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "linux-musl-x64", "conformance/check/pass/c-import-target-linux.0"]); +const cImportTargetLinuxModel = JSON.parse(cImportTargetLinuxGraph.stdout).cImports.find((item) => item.header === "conformance/c/target-conditional.h").typedModel; +assert(cImportTargetLinuxModel.functions.some((item) => item.name === "zero_c_linux_add")); +assert(cImportTargetLinuxModel.functions.some((item) => item.name === "zero_c_not_windows")); +assert(!cImportTargetLinuxModel.functions.some((item) => item.name === "zero_c_windows_add")); +const cImportTargetWinGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "win32-x64.exe", "conformance/check/pass/c-import-target-win.0"]); +const cImportTargetWinModel = JSON.parse(cImportTargetWinGraph.stdout).cImports.find((item) => item.header === "conformance/c/target-conditional.h").typedModel; +assert(cImportTargetWinModel.functions.some((item) => item.name === "zero_c_windows_add")); +assert(!cImportTargetWinModel.functions.some((item) => item.name === "zero_c_linux_add")); +assert(!cImportTargetWinModel.functions.some((item) => item.name === "zero_c_not_windows")); + +const cImportTypeShadowReadiness = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "conformance/native/pass/c-import-type-shadowing.0"]); +const cImportTypeShadowReadinessBody = JSON.parse(cImportTypeShadowReadiness.stdout); +assert.equal(cImportTypeShadowReadinessBody.ok, true); +assert.equal(cImportTypeShadowReadinessBody.targetReadiness.buildable, false); +assert.equal(cImportTypeShadowReadinessBody.targetReadiness.diagnostics[0].backendBlocker.unsupportedFeature, "ref"); +const cImportLaterLocalReadiness = await execFileAsync(zero, ["check", "--json", "--emit", "obj", "conformance/native/pass/c-import-alias-later-local.0"]); +const cImportLaterLocalReadinessBody = JSON.parse(cImportLaterLocalReadiness.stdout); +assert.equal(cImportLaterLocalReadinessBody.ok, true); +assert.equal(cImportLaterLocalReadinessBody.targetReadiness.ok, true); +assert.equal(cImportLaterLocalReadinessBody.targetReadiness.buildable, true); +assert.equal(cImportLaterLocalReadinessBody.targetReadiness.diagnostics.length, 0); +const cImportMissingLinkReadiness = await execFileAsync(zero, ["check", "--json", "conformance/native/pass/c-import-alias-later-local.0"]); +const cImportMissingLinkReadinessBody = JSON.parse(cImportMissingLinkReadiness.stdout); +assert.equal(cImportMissingLinkReadinessBody.ok, true); +assert.equal(cImportMissingLinkReadinessBody.targetReadiness.ok, false); +assert.equal(cImportMissingLinkReadinessBody.targetReadiness.buildable, false); +assert.equal(cImportMissingLinkReadinessBody.targetReadiness.diagnostics[0].code, "CIMP005"); +const cImportMissingLinkBuild = await execFileAsync(zero, ["build", "--json", "conformance/native/pass/c-import-alias-later-local.0", "--out", `.zero/out/c-import-missing-link-${process.pid}`]).catch((error) => error); +assert.notEqual(cImportMissingLinkBuild.code, 0); +assert.equal(JSON.parse(cImportMissingLinkBuild.stdout).diagnostics[0].code, "CIMP005"); + +const cImportPartialLinkRoot = `/tmp/zero-c-import-partial-link-${process.pid}`; +await rm(cImportPartialLinkRoot, { recursive: true, force: true }); +await mkdir(`${cImportPartialLinkRoot}/src`, { recursive: true }); +await mkdir(`${cImportPartialLinkRoot}/vendor/include`, { recursive: true }); +await writeFile(`${cImportPartialLinkRoot}/vendor/include/a.h`, "int zero_a_add(int left, int right);\n"); +await writeFile(`${cImportPartialLinkRoot}/vendor/include/b.h`, "int zero_b_add(int left, int right);\n"); +await writeZeroToml(cImportPartialLinkRoot, { + package: { name: "c-import-partial-link", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { libs: { + a: { headers: ["vendor/include/a.h"], include: ["vendor/include"], lib: ["vendor/lib/a.o"], link: [], mode: "static" }, + b: { headers: ["vendor/include/b.h"], include: ["vendor/include"], lib: [], link: [], mode: "static" } + } } +}); +await writeFile(`${cImportPartialLinkRoot}/src/main.0`, `extern c "vendor/include/a.h" as a +extern c "vendor/include/b.h" as b + +export c fn main() -> i32 { + return a.zero_a_add(1, 2) + b.zero_b_add(3, 4) +} +`); +await importPackageGraph(cImportPartialLinkRoot); +const cImportPartialLinkReadiness = await execFileAsync(zero, ["check", "--json", cImportPartialLinkRoot]); +const cImportPartialLinkReadinessBody = JSON.parse(cImportPartialLinkReadiness.stdout); +assert.equal(cImportPartialLinkReadinessBody.ok, true); +assert.equal(cImportPartialLinkReadinessBody.targetReadiness.ok, false); +assert.equal(cImportPartialLinkReadinessBody.targetReadiness.buildable, false); +assert.equal(cImportPartialLinkReadinessBody.targetReadiness.diagnostics[0].code, "CIMP005"); +assert.match(cImportPartialLinkReadinessBody.targetReadiness.diagnostics[0].actual, /b\.h/); +const cImportPartialLinkBuild = await execFileAsync(zero, ["build", "--json", cImportPartialLinkRoot, "--out", `${cImportPartialLinkRoot}/partial-link`]).catch((error) => error); +assert.notEqual(cImportPartialLinkBuild.code, 0); +const cImportPartialLinkBuildBody = JSON.parse(cImportPartialLinkBuild.stdout); +assert.equal(cImportPartialLinkBuildBody.diagnostics[0].code, "CIMP005"); +assert.match(cImportPartialLinkBuildBody.diagnostics[0].actual, /b\.h/); +await rm(cImportPartialLinkRoot, { recursive: true, force: true }); + +const externCallRoot = `/tmp/zero-extern-c-call-${process.pid}`; +const externCallShadowRoot = `/tmp/zero-extern-c-shadow-${process.pid}`; +const externCallScalarRoot = `/tmp/zero-extern-c-scalar-${process.pid}`; +await rm(externCallRoot, { recursive: true, force: true }); +await rm(externCallShadowRoot, { recursive: true, force: true }); +await rm(externCallScalarRoot, { recursive: true, force: true }); +await mkdir(`${externCallRoot}/src`, { recursive: true }); +await mkdir(`${externCallRoot}/vendor/include`, { recursive: true }); +await mkdir(`${externCallRoot}/vendor/lib`, { recursive: true }); +await mkdir(`${externCallShadowRoot}/vendor/include`, { recursive: true }); +await mkdir(`${externCallScalarRoot}/src`, { recursive: true }); +const externCallObjectRel = "vendor/lib/zero ext's.o"; +const externCallDirtyObjectRel = "vendor/lib/zero_ext_dirty.o"; +const externCallSymbolPrefix = process.platform === "darwin" ? "_" : ""; +let externCallDirtyAsm = ""; +if (process.arch === "x64") { + externCallDirtyAsm = `.text +.globl ${externCallSymbolPrefix}zero_ext_dirty_u8 +${externCallSymbolPrefix}zero_ext_dirty_u8: + movq $257, %rax + ret +`; +} else if (process.arch === "arm64") { + externCallDirtyAsm = `.text +.globl ${externCallSymbolPrefix}zero_ext_dirty_u8 +${externCallSymbolPrefix}zero_ext_dirty_u8: + movz x0, #257 + ret +`; +} +await writeFile(`${externCallRoot}/vendor/include/zero_ext.h`, `#ifdef __cplusplus +extern "C" { +#endif + +int +zero_ext_add( + int left, + int right +); +// int zero_ext_commented(int value); +/* +int zero_ext_block_commented(int value); +*/ +#if 0 +int zero_ext_disabled(int value); +#endif +int zero_ext_inline_left(int value); int zero_ext_inline_right(int value); +unsigned char zero_ext_dirty_u8(void); + +#ifdef __cplusplus +} +#endif +`); +await writeFile(`${externCallShadowRoot}/vendor/include/zero_ext.h`, "int zero_ext_wrong(int, int);\n"); +await writeFile(`${externCallRoot}/vendor/lib/zero_ext.c`, '#include "zero_ext.h"\nint zero_ext_add(int a, int b) { return a + b; }\nint zero_ext_inline_left(int value) { return value + 1; }\nint zero_ext_inline_right(int value) { return value + 2; }\n'); +await execFileAsync("cc", ["-I", `${externCallRoot}/vendor/include`, "-c", `${externCallRoot}/vendor/lib/zero_ext.c`, "-o", `${externCallRoot}/${externCallObjectRel}`]); +if (externCallDirtyAsm) { + await writeFile(`${externCallRoot}/vendor/lib/zero_ext_dirty.S`, externCallDirtyAsm); + await execFileAsync("cc", ["-c", `${externCallRoot}/vendor/lib/zero_ext_dirty.S`, "-o", `${externCallRoot}/${externCallDirtyObjectRel}`]); +} +await writeZeroToml(externCallRoot, { + package: { name: "extern-c-call", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { libs: { + ext: { headers: ["vendor/include/zero_ext.h"], include: ["vendor/include"], lib: [externCallObjectRel, ...(externCallDirtyAsm ? [externCallDirtyObjectRel] : [])], link: [], mode: "static" }, + unused: { headers: ["vendor/include/unused.h"], include: ["vendor/include"], lib: ["vendor/lib/missing-unused.o"], link: [], mode: "static" } + } } +}); +await writeZeroToml(externCallScalarRoot, { + package: { name: "extern-c-scalar", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { libs: { ext: { headers: [`${externCallRoot}/vendor/include/zero_ext.h`], include: [`${externCallRoot}/vendor/include`], lib: [`${externCallRoot}/${externCallObjectRel}`], link: [], mode: "static" } } } +}); +await writeFile(`${externCallRoot}/src/main.0`, `extern c "vendor/include/zero_ext.h" as c + +pub fn main(world: World) -> Void raises { + let total: i32 = c.zero_ext_add(20, 20) + c.zero_ext_inline_right(0) + if total == 42${externCallDirtyAsm ? " && c.zero_ext_dirty_u8() == 1_u8" : ""} { + check world.out.write("extern c call ok\\n") + } else { + check world.out.write("extern c call failed\\n") + } +} +`); +await writeFile(`${externCallRoot}/src/disabled.0`, `extern c "vendor/include/zero_ext.h" as c + +pub fn main() -> i32 { + return c.zero_ext_disabled(1) +} +`); +await writeFile(`${externCallRoot}/src/commented.0`, `extern c "vendor/include/zero_ext.h" as c + +pub fn main() -> i32 { + return c.zero_ext_commented(1) +} +`); +await writeFile(`${externCallScalarRoot}/src/main.0`, `extern c "${externCallRoot}/vendor/include/zero_ext.h" as c + +export c fn main() -> i32 { + return c.zero_ext_add(20, 22) +} +`); +await importPackageGraph(externCallRoot); +await importPackageGraph(externCallScalarRoot); +const externCallShadowCheck = await execFileAsync(zero, ["check", "--json", externCallRoot], { cwd: externCallShadowRoot }); +const externCallShadowCheckBody = JSON.parse(externCallShadowCheck.stdout); +assert.equal(externCallShadowCheckBody.ok, true); +const externCallDisabledCheck = await execFileAsync(zero, ["import", "--json", "--format", "binary", "--out", "src/disabled.graph", "src/disabled.0"], { cwd: externCallRoot }).catch((error) => error); +assert.notEqual(externCallDisabledCheck.code, 0); +const externCallDisabledCheckBody = JSON.parse(externCallDisabledCheck.stdout); +assert.equal(externCallDisabledCheckBody.diagnostics[0].code, "CIMP004"); +const externCallCommentedCheck = await execFileAsync(zero, ["import", "--json", "--format", "binary", "--out", "src/commented.graph", "src/commented.0"], { cwd: externCallRoot }).catch((error) => error); +assert.notEqual(externCallCommentedCheck.code, 0); +const externCallCommentedCheckBody = JSON.parse(externCallCommentedCheck.stdout); +assert.equal(externCallCommentedCheckBody.diagnostics[0].code, "CIMP004"); +assert.match(externCallCommentedCheckBody.diagnostics[0].message, /not declared/); +const externCallScalarCrossReadiness = await execFileAsync(zero, ["check", "--json", "--target", "linux-musl-arm64", externCallScalarRoot]); +const externCallScalarCrossReadinessBody = JSON.parse(externCallScalarCrossReadiness.stdout); +assert.equal(externCallScalarCrossReadinessBody.ok, true); +assert.equal(externCallScalarCrossReadinessBody.targetReadiness.ok, true); +assert.equal(externCallScalarCrossReadinessBody.targetReadiness.buildable, true); +assert.equal(externCallScalarCrossReadinessBody.targetReadiness.diagnostics.length, 0); +const externCallGraph = await execFileAsync(zero, ["inspect", "--json", externCallRoot]); +const externCallGraphBody = JSON.parse(externCallGraph.stdout); +const externCallImport = externCallGraphBody.cImports.find((item) => item.header === "vendor/include/zero_ext.h"); +assert(externCallImport); +assert.equal(externCallImport.alias, "c"); +assert.match(externCallImport.cache.headerHash, /^[0-9a-f]{16}$/); +assert(Array.isArray(externCallImport.typedModel.functions)); +assert(!externCallImport.typedModel.functions.some((item) => item.name === "zero_ext_commented")); +assert(!externCallImport.typedModel.functions.some((item) => item.name === "zero_ext_block_commented")); +assert(!externCallImport.typedModel.functions.some((item) => item.name === "zero_ext_disabled")); +const externCallGraphSizeArtifact = `${externCallRoot}/extern-call-size-metadata.json`; +const externCallGraphSize = await execFileAsync(zero, ["size", "--json", "--out", externCallGraphSizeArtifact, externCallRoot]); +assert.equal(JSON.parse(externCallGraphSize.stdout).graph.moduleIdentity, "package:extern-c-call@0.1.0"); +const externCallBuildOut = `${externCallRoot}/extern-call`; +const externCallBuild = await execFileAsync(zero, ["build", "--json", externCallRoot, "--out", externCallBuildOut]); +const externCallBuildBody = JSON.parse(externCallBuild.stdout); +assert.equal(externCallBuildBody.emit, "exe"); +assert.notEqual(externCallBuildBody.objectBackend.objectEmission.path, "none"); +assert.notEqual(externCallBuildBody.objectBackend.linking.externalToolchain, "none"); +assert.match(externCallBuildBody.objectBackend.linking.targetLibraries, /zero-runtime/); +assert.match(externCallBuildBody.objectBackend.linking.targetLibraries, /c-imports/); +assert(externCallBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item.endsWith(externCallObjectRel))); +if (externCallDirtyAsm) assert(externCallBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item.endsWith(externCallDirtyObjectRel))); +assert(!externCallBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item.endsWith("vendor/lib/missing-unused.o"))); +const externCallObjectOverhead = externCallBuildBody.objectBackend.linking.objectFormat === "coff" + ? (externCallBuildBody.objectBackend.objectEmission.dataSections ? 2 : 1) + : (externCallBuildBody.objectBackend.objectEmission.dataSections ? 1 : 0); +const externCallExpectedExternalSymbols = externCallDirtyAsm ? 3 : 2; +assert.equal( + externCallBuildBody.objectBackend.objectEmission.symbolCount, + externCallBuildBody.objectBackend.directFacts.functionCount + + externCallBuildBody.objectBackend.directFacts.runtimeHelperCount + + externCallExpectedExternalSymbols + + externCallObjectOverhead +); +assert.equal(externCallBuildBody.releaseTargetContract.selectedEmitter, externCallBuildBody.releaseTargetContract.directObjectEmitter); +assert.equal(externCallBuildBody.releaseTargetContract.libc.artifactMode, externCallBuildBody.releaseTargetContract.libc.targetMode); +const externCallRun = await execFileAsync(zero, ["run", externCallRoot]); +assert.equal(externCallRun.stdout, "extern c call ok\n"); +const externCallScalarBuildOut = `${externCallScalarRoot}/extern-scalar`; +const externCallScalarBuild = await execFileAsync(zero, ["build", "--json", externCallScalarRoot, "--out", externCallScalarBuildOut]); +const externCallScalarBuildBody = JSON.parse(externCallScalarBuild.stdout); +assert.equal(externCallScalarBuildBody.emit, "exe"); +assert.match(externCallScalarBuildBody.objectBackend.linking.targetLibraries, /c-imports/); +assert.doesNotMatch(externCallScalarBuildBody.objectBackend.linking.targetLibraries, /zero-runtime/); +assert.notEqual(externCallScalarBuildBody.objectBackend.linking.externalToolchain, "none"); +assert(!externCallScalarBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item === "zero_runtime.o")); +assert(externCallScalarBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item.endsWith(externCallObjectRel))); +assert.equal(externCallScalarBuildBody.releaseTargetContract.selectedEmitter, externCallScalarBuildBody.releaseTargetContract.directObjectEmitter); +assert.equal(externCallScalarBuildBody.releaseTargetContract.libc.artifactMode, externCallScalarBuildBody.releaseTargetContract.libc.targetMode); +await rm(externCallRoot, { recursive: true, force: true }); +await rm(externCallShadowRoot, { recursive: true, force: true }); +await rm(externCallScalarRoot, { recursive: true, force: true }); + +const unsafeExternLinkRoot = `/tmp/zero-extern-c-unsafe-link-${process.pid}`; +await rm(unsafeExternLinkRoot, { recursive: true, force: true }); +await mkdir(`${unsafeExternLinkRoot}/src`, { recursive: true }); +await mkdir(`${unsafeExternLinkRoot}/vendor/include`, { recursive: true }); +await writeFile(`${unsafeExternLinkRoot}/vendor/include/zero_ext.h`, "int zero_ext_add(int left, int right);\n"); +await writeZeroToml(unsafeExternLinkRoot, { + package: { name: "extern-c-unsafe-link", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { libs: { ext: { headers: ["vendor/include/zero_ext.h"], include: ["vendor/include"], lib: [], link: ["zero_ext;touch"], mode: "static" } } } +}); +await writeFile(`${unsafeExternLinkRoot}/src/main.0`, `extern c "vendor/include/zero_ext.h" as c + +pub fn main() -> i32 { + return c.zero_ext_add(20, 22) +} +`); +await importPackageGraph(unsafeExternLinkRoot); +const unsafeExternLinkReadiness = await execFileAsync(zero, ["check", "--json", unsafeExternLinkRoot]); +const unsafeExternLinkReadinessBody = JSON.parse(unsafeExternLinkReadiness.stdout); +assert.equal(unsafeExternLinkReadinessBody.ok, true); +assert.equal(unsafeExternLinkReadinessBody.targetReadiness.buildable, false); +assert.equal(unsafeExternLinkReadinessBody.targetReadiness.diagnostics[0].code, "CIMP005"); +assert.match(unsafeExternLinkReadinessBody.targetReadiness.diagnostics[0].actual, /unsafe library name/); +const unsafeExternLinkBuild = await execFileAsync(zero, ["build", "--json", unsafeExternLinkRoot, "--out", `${unsafeExternLinkRoot}/extern-link`]).catch((error) => error); +assert.notEqual(unsafeExternLinkBuild.code, 0); +assert.equal(JSON.parse(unsafeExternLinkBuild.stdout).diagnostics[0].code, "CIMP005"); +await rm(unsafeExternLinkRoot, { recursive: true, force: true }); + +const runtimeManifestRoot = `/tmp/zero-runtime-manifest-link-${process.pid}`; +await rm(runtimeManifestRoot, { recursive: true, force: true }); +await mkdir(`${runtimeManifestRoot}/src`, { recursive: true }); +await writeZeroToml(runtimeManifestRoot, { + package: { name: "runtime-manifest-link", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + c: { libs: { unused: { headers: ["vendor/include/unused.h"], include: ["vendor/include"], lib: ["vendor/lib/missing.o"], link: ["zero_missing_system"], mode: "static" } } } +}); +await writeFile(`${runtimeManifestRoot}/src/main.0`, `pub fn main(world: World) -> Void raises { + check world.out.write("runtime manifest ignored\\n") +} +`); +await importPackageGraph(runtimeManifestRoot); +const runtimeManifestBuildOut = `${runtimeManifestRoot}/runtime-manifest`; +const runtimeManifestBuild = await execFileAsync(zero, ["build", "--json", runtimeManifestRoot, "--out", runtimeManifestBuildOut]); +const runtimeManifestBuildBody = JSON.parse(runtimeManifestBuild.stdout); +assert(!runtimeManifestBuildBody.objectBackend.linkerPlan.staticLibraries.some((item) => item.endsWith("vendor/lib/missing.o"))); +assert(!runtimeManifestBuildBody.objectBackend.linkerPlan.systemLibraries.includes("zero_missing_system")); +const runtimeManifestRun = await execFileAsync(zero, ["run", runtimeManifestRoot]); +assert.equal(runtimeManifestRun.stdout, "runtime manifest ignored\n"); +await rm(runtimeManifestRoot, { recursive: true, force: true }); + +const cInteropGraph = await execFileAsync(zero, ["inspect", "--json", "examples/c-interop"]); +const cInteropGraphBody = JSON.parse(cInteropGraph.stdout); +const mathLib = cInteropGraphBody.cLibraries.find((item) => item.name === "math"); +assert(mathLib); +assert.equal(mathLib.linkMode, "static"); +assert.equal(mathLib.targetValidation.vendoredLibraries, true); +assert.equal(mathLib.targetValidation.vendoredHeaders, true); +assert.equal(mathLib.targetValidation.pkgConfigTargetSafe, true); +const cInteropCrossGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "linux-musl-x64", "examples/c-interop"]); +const cInteropCrossGraphBody = JSON.parse(cInteropCrossGraph.stdout); +assert.equal(cInteropCrossGraphBody.cLibraries[0].targetValidation.pkgConfigTargetSafe, true); +assert.equal(cInteropCrossGraphBody.cLibraries[0].targetValidation.implicitHostDiscovery, false); +assert.equal(cInteropCrossGraphBody.cLibraries[0].linkPlan.hostDiscovery, "none"); +const hostLeakGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "linux-musl-x64", "conformance/c/host-leak-package"]); +const hostLeakGraphBody = JSON.parse(hostLeakGraph.stdout); +assert.equal(hostLeakGraphBody.cLibraries[0].targetValidation.hostHeaderLeakage, true); +assert.equal(hostLeakGraphBody.cLibraries[0].targetValidation.implicitHostDiscovery, true); +assert.equal(hostLeakGraphBody.cLibraries[0].targetValidation.status, "blocked"); +const hostLeakReadiness = await execFileAsync(zero, ["check", "--json", "--target", "linux-musl-x64", "conformance/c/host-leak-package"]); +const hostLeakReadinessBody = JSON.parse(hostLeakReadiness.stdout); +assert.equal(hostLeakReadinessBody.ok, true); +assert.equal(hostLeakReadinessBody.diagnostics.length, 0); +assert.equal(hostLeakReadinessBody.targetReadiness.ok, false); +assert.equal(hostLeakReadinessBody.targetReadiness.buildable, false); +assert.equal(hostLeakReadinessBody.targetReadiness.diagnostics[0].code, "CIMP003"); +assert.match(hostLeakReadinessBody.targetReadiness.diagnostics[0].help, /target sysroot|vendored/); +const hostLeakBuild = await execFileAsync(zero, ["build", "--json", "--target", "linux-musl-x64", "conformance/c/host-leak-package", "--out", ".zero/out/host-leak-package"], { encoding: "utf8" }).catch((error) => error); +assert.notEqual(hostLeakBuild.code, 0); +const hostLeakBuildBody = JSON.parse(hostLeakBuild.stdout); +assert.equal(hostLeakBuildBody.diagnostics[0].code, "CIMP003"); +assert.match(hostLeakBuildBody.diagnostics[0].help, /target sysroot|vendored/); + +const depGraph = await execFileAsync(zero, ["inspect", "--json", "--target", "linux-musl-x64", "conformance/packages/dep-app"]); +const depGraphBody = JSON.parse(depGraph.stdout); +assert.equal(depGraphBody.package.name, "dep-app"); +assert.equal(depGraphBody.package.version, "0.1.0"); +assert.equal(depGraphBody.package.resolver.deterministic, true); +assert.match(depGraphBody.package.lockfile.path, /\.zero\/package-locks\/[0-9a-f]+\.lock\.json/); +assert(depGraphBody.package.dependencies.some((item) => item.name === "dep-lib" && item.status === "path-resolved" && item.targetCompatible === true)); +assert(depGraphBody.package.dependencies.some((item) => item.name === "remote-tools" && item.status === "registry-reference" && item.version === "1.2.3")); +assert.equal(depGraphBody.packageCache.cacheKeyInputs.compilerVersion, "0.3.4"); +assert.equal(depGraphBody.packageCache.cacheKeyInputs.packageVersion, "0.1.0"); +assert.match(depGraphBody.packageCache.cacheKeyInputs.dependencyGraphHash, /^[0-9a-f]{16}$/); +const depDoc = await execFileAsync(zero, ["doc", "--json", "conformance/packages/dep-app"]); +const depDocBody = JSON.parse(depDoc.stdout); +assert.equal(depDocBody.package.name, "dep-app"); +assert.equal(depDocBody.publicationGate.requiresExamplesForPublicApi, true); +assert.equal(depDocBody.packageCache.cacheKeyInputs.packageVersion, "0.1.0"); +const depBuild = await execFileAsync(zero, ["build", "--json", "--target", "linux-musl-x64", "conformance/packages/dep-app", "--out", ".zero/out/dep-app"]); +const depBuildBody = JSON.parse(depBuild.stdout); +assert.equal(depBuildBody.package.dependencies.length, 2); +assert.equal(depBuildBody.packageCache.invalidationReasons.includes("dependency graph changed"), true); +assert.match(depBuildBody.compilerCaches[0].dependencyGraphHash, /^[0-9a-f]{16}$/); + +const malformedTomlArrayRoot = `${outDir}/malformed-toml-array`; +await rm(malformedTomlArrayRoot, { recursive: true, force: true }); +await mkdir(malformedTomlArrayRoot, { recursive: true }); +await writeFile(`${malformedTomlArrayRoot}/zero.toml`, `[package] +name = "malformed-toml-array" +version = "0.1.0" + +[targets.cli] +kind = "exe" +main = "src/main.0" + +[c.libs.demo] +headers = ["unterminated] +`); +const malformedTomlArrayStatus = await execFileAsync(zero, ["status", "--json", malformedTomlArrayRoot]); +const malformedTomlArrayBody = JSON.parse(malformedTomlArrayStatus.stdout); +assert.equal(malformedTomlArrayBody.ok, true); +assert.equal(malformedTomlArrayBody.repositoryGraph.storePresent, false); +assert.deepEqual(malformedTomlArrayBody.diagnostics, []); + +const zeroTestRun = await execFileAsync(zero, ["test", "conformance/native/pass/test-blocks.0"]); +assert.equal(zeroTestRun.stdout, "1 test(s) ok\n"); + +const zeroTestingHelpersRun = await execFileAsync(zero, ["test", "conformance/native/pass/std-testing-helpers-test.0"]); +assert.equal(zeroTestingHelpersRun.stdout, "1 test(s) ok\n"); + +const zeroTestJsonRun = await execFileAsync(zero, ["test", "--json", "--filter", "addition", "conformance/native/pass/test-blocks.0"]); +const zeroTestJsonBody = JSON.parse(zeroTestJsonRun.stdout); +assert.equal(zeroTestJsonBody.ok, true); +assert.equal(zeroTestJsonBody.discoveredTests, 1); +assert.equal(zeroTestJsonBody.selectedTests, 1); +assert.equal(zeroTestJsonBody.passedTests, 1); +assert.equal(zeroTestJsonBody.graph.artifact, "conformance/native/pass/test-blocks.graph"); +assert.equal(zeroTestJsonBody.graph.canonicalSource, false); +assert.equal(zeroTestJsonBody.graph.moduleIdentity, "module:test-blocks"); +assert.equal(zeroTestJsonBody.graph.lowering, "direct-program-graph"); +assert.equal(zeroTestJsonBody.testDiscovery.mode, "program-graph"); +assert.equal(zeroTestJsonBody.testDiscovery.filter, "addition"); +assert.equal(zeroTestJsonBody.fixtures.snapshotKey, "zero-test-graph-native-v1"); +assert.equal(zeroTestJsonBody.results[0].status, "passed"); + +const zeroPtyTargetCapTest = await execFileAsync(zero, ["test", "--json", "--filter", "pty target", "--target", "linux-arm64", "conformance/check/pass/test-target-proc-caps.graph"]).catch((error) => error); +assert.notEqual(zeroPtyTargetCapTest.code, 0); +const zeroPtyTargetCapBody = JSON.parse(zeroPtyTargetCapTest.stdout); +assert.equal(zeroPtyTargetCapBody.ok, false); +assert.equal(zeroPtyTargetCapBody.diagnostics[0].code, "PAR100"); +assert.match(zeroPtyTargetCapBody.diagnostics[0].actual, /lacks proc/); + +const zeroTermTargetCapTest = await execFileAsync(zero, ["test", "--json", "--filter", "term target", "--target", "linux-arm64", "conformance/check/pass/test-target-proc-caps.graph"]).catch((error) => error); +assert.notEqual(zeroTermTargetCapTest.code, 0); +const zeroTermTargetCapBody = JSON.parse(zeroTermTargetCapTest.stdout); +assert.equal(zeroTermTargetCapBody.ok, false); +assert.equal(zeroTermTargetCapBody.diagnostics[0].code, "PAR100"); +assert.match(zeroTermTargetCapBody.diagnostics[0].actual, /lacks proc/); + +const zeroPackageTestJsonRun = await execFileAsync(zero, ["test", "--json", "conformance/packages/test-app"]); +const zeroPackageTestBody = JSON.parse(zeroPackageTestJsonRun.stdout); +assert.equal(zeroPackageTestBody.ok, true); +assert.equal(zeroPackageTestBody.graph.artifact, "conformance/packages/test-app/zero.graph"); +assert.equal(zeroPackageTestBody.graph.canonicalSource, false); +assert.equal(zeroPackageTestBody.graph.moduleIdentity, "package:test-app@0.1.0"); +assert.equal(zeroPackageTestBody.graph.lowering, "direct-program-graph"); +assert.equal(zeroPackageTestBody.testDiscovery.mode, "package-graph"); +assert.equal(zeroPackageTestBody.discoveredTests, 3); +assert.equal(zeroPackageTestBody.selectedTests, 3); +assert.equal(zeroPackageTestBody.expectedFailures, 1); +assert(zeroPackageTestBody.fixtures.sourceFiles.some((path) => path.endsWith("helper.0"))); + +const zeroExpectedFailJsonRun = await execFileAsync(zero, ["test", "--json", "conformance/native/pass/test-expected-fail.0"]); +const zeroExpectedFailBody = JSON.parse(zeroExpectedFailJsonRun.stdout); +assert.equal(zeroExpectedFailBody.ok, true); +assert.equal(zeroExpectedFailBody.expectedFailures, 1); +assert.equal(zeroExpectedFailBody.failedTests, 0); +assert.equal(zeroExpectedFailBody.results[0].status, "expected-fail"); + +const fmtRun = await execFileAsync(zero, ["fmt", "conformance/native/pass/test-blocks.0"]); +assert.equal(fmtRun.stdout, await readFile("conformance/native/pass/test-blocks.0", "utf8")); + +const fmtFallibilityRun = await execFileAsync(zero, ["fmt", "conformance/native/pass/fallibility-check-value.0"]); +assert.equal(fmtFallibilityRun.stdout, await readFile("conformance/native/pass/fallibility-check-value.0", "utf8")); + +const fmtImportsRun = await execFileAsync(zero, ["fmt", "conformance/check/pass/imports/src/main.0"]); +assert.match(fmtImportsRun.stdout, /use math/); +assert.match(fmtImportsRun.stdout, /use types/); + +const fmtCoreRun = await execFileAsync(zero, ["fmt", "conformance/check/pass/fmt-core-usability.0"]); +assert.equal(fmtCoreRun.stdout, await readFile("conformance/check/pass/fmt-core-usability.0", "utf8")); + +const fmtMessyRun = await execFileAsync(zero, ["fmt", "conformance/format/messy.0"]); +assert.equal(fmtMessyRun.stdout, await readFile("conformance/format/messy.0", "utf8")); + +const fmtIdempotentPath = `${outDir}/fmt-idempotent.0`; +await writeFile(fmtIdempotentPath, fmtMessyRun.stdout); +const fmtIdempotentRun = await execFileAsync(zero, ["fmt", fmtIdempotentPath]); +assert.equal(fmtIdempotentRun.stdout, fmtMessyRun.stdout); + +const fmtFunctionsBlocksRun = await execFileAsync(zero, ["fmt", "conformance/format/functions-blocks.0"]); +assert.equal(fmtFunctionsBlocksRun.stdout, await readFile("conformance/format/functions-blocks.0", "utf8")); + +const fmtGenericsStaticRun = await execFileAsync(zero, ["fmt", "conformance/format/generics-static.0"]); +assert.equal(fmtGenericsStaticRun.stdout, await readFile("conformance/format/generics-static.0", "utf8")); + +const fmtMatchRun = await execFileAsync(zero, ["fmt", "conformance/format/match-expressions.0"]); +assert.equal(fmtMatchRun.stdout, await readFile("conformance/format/match-expressions.0", "utf8")); + +const helloRunArgs = runnableExeArgs("conformance/run/pass/hello.0", `${outDir}/hello`); +if (helloRunArgs) { + await rm(`${outDir}/hello`, { force: true }); + await execFileAsync(zero, helloRunArgs); + const run = await execFileAsync(`${outDir}/hello`, []); + assert.match(run.stdout, /hello conformance/); +} + +const packageGraphJson = await execFileAsync(zero, ["inspect", "--json", "conformance/check/pass/package"]); +const packageGraph = JSON.parse(packageGraphJson.stdout); +assert.deepEqual(packageGraph.sourceFiles.sort(), [ + "src/main.0", + "src/types.0", + "std/codec.0", +]); +assert(packageGraph.requiresCapabilities.includes("codec")); +assert(packageGraph.requiresCapabilities.includes("parse")); +assert(packageGraph.requiresCapabilities.includes("time")); +assert.equal(packageGraph.selfHostRouting.cBridge.policy, "removed"); + +for (const runtimeFixture of [ + ["conformance/native/pass/break-continue.0", "break-continue", { stdout: "loop tick\nloop tick\n" }], + ["conformance/native/pass/nested-break-continue.0", "nested-break-continue", { stdout: "inner tick\ninner tick\nouter tick\ninner tick\ninner tick\nnested break continue ok\n" }], + ["conformance/native/pass/mutref-shape-param.0", "mutref-shape-param", { stdout: "mutref x ok\nmutref y ok\nref sum ok\n" }], + ["conformance/native/pass/mutref-shape-param-nested.0", "mutref-shape-param-nested", { stdout: "nested count ok\nnested total ok\nnested flag ok\nnested copy ok\nnested bytes ok\ngeneric mutref ok\n" }], + ["conformance/native/pass/untyped-literal-adoption.0", "untyped-literal-adoption", { stdout: "usize literal adoption ok\nflipped literal adoption ok\nliteral arithmetic adoption ok\nu8 literal adoption ok\n" }], + ["conformance/native/pass/for-range.0", "for-range", { stdout: "range tick\nrange tick\nrange tick\n" }], + ["conformance/native/pass/match-payload-binding.0", "match-payload-binding", { stdout: /payload binding ok/ }], + ["conformance/native/pass/match-fallback.0", "match-fallback", { stdout: "match fallback ok\n" }], + ["conformance/native/pass/match-choice-fallback.0", "match-choice-fallback", { stdout: "choice fallback ok\n" }], + ["conformance/native/pass/null-maybe.0", "null-maybe", { stdout: /null maybe ok/ }], + ["conformance/native/pass/maybe-local-null-init-return.0", "maybe-local-null-init-return", { stdout: "maybe local found 42\nmaybe local none\nmaybe local sum 5\n" }], + ["conformance/native/pass/top-level-const.0", "top-level-const", { stdout: "const ok\n" }], + ["conformance/native/pass/const-arithmetic.0", "const-arithmetic", { stdout: "const arithmetic ok\n" }], + ["conformance/native/pass/std-args.0", "std-args", { stdout: "alpha\n", args: ["alpha", "beta"] }], + ["conformance/native/pass/std-env.0", "std-env", { stdout: "env ok\n", env: { ZERO_CONFORMANCE_ENV: "agent-env", ZERO_CONFORMANCE_ENV_DELTA: "-3", ZERO_CONFORMANCE_ENV_WORKERS: "5" } }], + ["conformance/native/pass/std-hosted-cli.0", "std-hosted-cli", { stdout: "std hosted cli ok\n", args: ["run", "7", "--json", "--name", "agent", "--count", "3", "--enabled", "false", "--delta", "-5", "--size", "9"], env: { ZERO_CONFORMANCE_MODE: "test", ZERO_CONFORMANCE_VERBOSE: "true", ZERO_CONFORMANCE_LIMIT: "9" } }], + ["conformance/native/pass/std-proc-helpers.0", "std-proc-helpers", { stdout: "std proc helpers ok\n" }], + ["conformance/native/pass/std-proc-capture.0", "std-proc-capture", { stdout: "std proc capture ok\n" }], + ["conformance/native/pass/std-proc-capture-files.0", "std-proc-capture-files", { stdout: "std proc capture files ok\n" }], + ["conformance/native/pass/std-proc-child.0", "std-proc-child", { stdout: "std proc child ok\n" }], + ["conformance/native/pass/std-pty-child.0", "std-pty-child", { stdout: "std pty child ok\n" }], + ["conformance/native/pass/std-proc-child-cwd.0", "std-proc-child-cwd", { stdout: "std proc child cwd ok\n" }], + ["conformance/native/pass/std-proc-spawn-inherit.0", "std-proc-spawn-inherit", { stdout: "inherit-out status-ok\n" }], + ["conformance/native/pass/std-fs.0", "std-fs", { stdout: "fs ok\n", file: { name: "std-fs-write.txt", text: "zero write\n" } }], + ["conformance/native/pass/std-fs-bytes.0", "std-fs-bytes", { stdout: "fs bytes ok\n", stderr: "fs bytes err ok\n" }], + ["conformance/native/pass/std-fs-write-file-bool.0", "std-fs-write-file-bool", { stdout: "fs write file bool ok\n" }], + ["conformance/native/pass/std-fs-append-bytes.0", "std-fs-append-bytes", { stdout: "fs append ok\n" }], + ["conformance/native/pass/std-fs-direct-return.0", "std-fs-direct-return", { stdout: "fs direct return ok\n" }], + ["conformance/native/pass/std-fs-read-chunks.0", "std-fs-read-chunks", { stdout: "fs read chunks ok\n" }], + ["conformance/native/pass/frame-large-locals.0", "frame-large-locals", { stdout: "frame large locals ok alpha\n", args: ["alpha"] }], + ["conformance/native/pass/frame-limit-boundary.0", "frame-limit-boundary", { stdout: "frame limit boundary ok\n" }], + ["conformance/native/pass/frame-split-helpers.0", "frame-split-helpers", { stdout: "frame split helpers ok\n" }], + ["conformance/native/pass/fixed-buf-alloc-local.0", "fixed-buf-alloc-local", { stdout: "fixed buf alloc local ok\n" }], + ["conformance/native/pass/std-fs-resource.0", "std-fs-resource", { stdout: "fs resource ok\n", file: { name: "std-fs-resource.txt", text: "zero file\n" } }], + ["conformance/native/pass/std-fs-file-helpers.0", "std-fs-file-helpers", { stdout: "std fs file helpers ok\n" }], + ["conformance/native/pass/std-io-lines.0", "std-io-lines", { stdout: "std io lines ok\n" }], + ["conformance/native/pass/integer-widths.0", "integer-widths", { stdout: "integer widths ok\n" }], + ["conformance/native/pass/std-codec-widths.0", "std-codec-widths", { stdout: "codec widths ok\n" }], + ["conformance/native/pass/std-csv.0", "std-csv", { stdout: "std csv ok\n" }], + ["conformance/native/pass/std-diag.0", "std-diag", { stdout: "std diag ok\n" }], + ["conformance/native/pass/std-codec-json-url.0", "std-codec-json-url", { stdout: "std codec json url ok\n" }], + ["conformance/native/pass/std-json-cursors.0", "std-json-cursors", { stdout: "std json cursors ok\n" }], + ["conformance/native/pass/std-crypto-hmac32.0", "std-crypto-hmac32", { stdout: "crypto hmac32 ok\n" }], + ["conformance/native/pass/std-crypto-sha256.0", "std-crypto-sha256", { stdout: "crypto sha256 ok\n" }], + ["conformance/native/pass/parse-integers.0", "parse-integers", { stdout: "parse integers ok\n" }], + ["conformance/native/pass/std-parse-text.0", "std-parse-text", { stdout: "std parse text ok\n" }], + ["conformance/native/pass/explicit-casts.0", "explicit-casts", { stdout: "explicit casts ok\n" }], + ["conformance/native/pass/float-char-casts.0", "float-char-casts", { stdout: "float char casts ok\n" }], + ["conformance/native/pass/radix-suffix-literals.0", "radix-suffix-literals", { stdout: "radix suffix literals ok\n" }], + ["conformance/native/pass/char-literals.0", "char-literals", { stdout: "char literals ok\n" }], + ["conformance/native/pass/float-primitives.0", "float-primitives", { stdout: "float primitives ok\n" }], + ["conformance/native/pass/recursive-fibonacci.0", "recursive-fibonacci", { stdout: "recursive fibonacci ok\n" }], + ["conformance/native/pass/mutual-recursion.0", "mutual-recursion", { stdout: "mutual recursion ok\n" }], + ["conformance/native/pass/scratch-nested-index.0", "scratch-nested-index", { stdout: "scratch nested index ok\n" }], + ["conformance/native/pass/checked-bounds-get.0", "checked-bounds-get", { stdout: "checked bounds get ok\n" }], + ["conformance/native/pass/check-maybe-fallibility.0", "check-maybe-fallibility", { stdout: "check maybe fallibility ok\n" }], + ["conformance/native/pass/fallibility-error-sets.0", "fallibility-error-sets", { stdout: "fallibility error sets ok\n" }], + ["conformance/native/pass/rescue-check.0", "rescue-check", { stdout: "rescue ok\n" }], + ["conformance/native/pass/std-fs-fallible.0", "std-fs-fallible", { stdout: "fs named errors ok\n" }], + ["conformance/native/pass/std-fs-fallible-resources.0", "std-fs-fallible-resources", { stdout: "fs fallible resources ok\n" }], + ["conformance/native/pass/std-cli-helpers.0", "std-cli-helpers", { stdout: "cli helpers ok\n" }], + ["conformance/native/pass/std-mem-copy-fill.0", "std-mem-copy-fill", { stdout: "mem copy fill ok\n" }], + ["conformance/native/pass/const-layout.0", "const-layout", { stdout: "const layout ok\n" }], + ["conformance/native/pass/c-abi-export.0", "c-abi-export", { stdout: "c abi export ok\n" }], +]) { + await assertDirectRuntimeOrUnsupported(...runtimeFixture); +} + +await assertDirectRuntimeRequired("conformance/native/pass/untyped-literal-adoption.0", "untyped-literal-adoption-required", { stdout: "usize literal adoption ok\nflipped literal adoption ok\nliteral arithmetic adoption ok\nu8 literal adoption ok\n" }); + +await assertDirectRuntimeRequired("conformance/native/pass/recursive-multi-call-let.0", "recursive-multi-call-let-required", { stdout: "recursive multi call ok\n" }); + +const literalAdoptionInitOverflowFixture = `${outDir}/untyped-literal-adoption-init-overflow.0`; +const literalAdoptionInitOverflowBody = await writeImportFailureFixture(literalAdoptionInitOverflowFixture, `pub fn main(world: World) -> Void raises { + var small: u8 = 300 + check world.out.write("unreachable\\n") +} +`); +assert.equal(literalAdoptionInitOverflowBody.diagnostics[0].code, "TYP016"); +assert.equal(literalAdoptionInitOverflowBody.diagnostics[0].expected, "u8"); +assert.equal(literalAdoptionInitOverflowBody.diagnostics[0].actual, "300 overflows u8"); + +const literalAdoptionCompareOverflowFixture = `${outDir}/untyped-literal-adoption-compare-overflow.0`; +const literalAdoptionCompareOverflowBody = await writeImportFailureFixture(literalAdoptionCompareOverflowFixture, `pub fn main(world: World) -> Void raises { + var small: u8 = 7 + if 300 > small { + check world.out.write("unreachable\\n") + } +} +`); +assert.equal(literalAdoptionCompareOverflowBody.diagnostics[0].code, "TYP016"); +assert.equal(literalAdoptionCompareOverflowBody.diagnostics[0].expected, "u8"); +assert.equal(literalAdoptionCompareOverflowBody.diagnostics[0].actual, "300 overflows u8"); +assert.match(literalAdoptionCompareOverflowBody.diagnostics[0].help, /smaller literal or a wider integer type/); + +const fixedArrayRepeatLengthFixture = `${outDir}/fixed-array-repeat-length-mismatch.0`; +const fixedArrayRepeatLengthBody = await writeImportFailureFixture(fixedArrayRepeatLengthFixture, `pub fn main(world: World) -> Void raises { + var b: [64]u8 = [0_u8; 32] + b[0] = 1 + check world.out.write("unreachable\\n") +} +`); +assert.equal(fixedArrayRepeatLengthBody.diagnostics[0].code, "TYP002"); +assert.equal(fixedArrayRepeatLengthBody.diagnostics[0].expected, "[64]u8"); +assert.equal(fixedArrayRepeatLengthBody.diagnostics[0].actual, "repeat count 32"); +assert.match(fixedArrayRepeatLengthBody.diagnostics[0].help, /make the lengths agree/); + +const fixedArrayListLengthFixture = `${outDir}/fixed-array-list-length-mismatch.0`; +const fixedArrayListLengthBody = await writeImportFailureFixture(fixedArrayListLengthFixture, `pub fn main(world: World) -> Void raises { + var b: [4]u8 = [1, 2] + b[0] = 1 + check world.out.write("unreachable\\n") +} +`); +assert.equal(fixedArrayListLengthBody.diagnostics[0].code, "TYP002"); +assert.equal(fixedArrayListLengthBody.diagnostics[0].expected, "[4]u8"); +assert.equal(fixedArrayListLengthBody.diagnostics[0].actual, "2 element(s)"); +assert.match(fixedArrayListLengthBody.diagnostics[0].help, /annotate the intended array length/); + +await assertDirectRuntimeRequired("conformance/native/pass/break-continue.0", "break-continue-required", { stdout: "loop tick\nloop tick\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/maybe-local-null-init-return.0", "maybe-local-null-init-return-required", { stdout: "maybe local found 42\nmaybe local none\nmaybe local sum 5\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/top-level-const.0", "top-level-const-required", { stdout: "const ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/const-arithmetic.0", "const-arithmetic-required", { stdout: "const arithmetic ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/nested-break-continue.0", "nested-break-continue-required", { stdout: "inner tick\ninner tick\nouter tick\ninner tick\ninner tick\nnested break continue ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/mutref-shape-param.0", "mutref-shape-param-required", { stdout: "mutref x ok\nmutref y ok\nref sum ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/mutref-shape-param-nested.0", "mutref-shape-param-nested-required", { stdout: "nested count ok\nnested total ok\nnested flag ok\nnested copy ok\nnested bytes ok\ngeneric mutref ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-function-basic.0", "generic-function-basic-required", { stdout: "generic function ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-nested-calls.0", "generic-nested-calls-required", { stdout: "generic nested calls ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-inferred-specialized-call.0", "generic-inferred-specialized-call-required", { stdout: "generic inferred specialized call ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-nested-local-specialization.0", "generic-nested-local-specialization-required", { stdout: "generic nested local specialization ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-static-array-specialization.0", "generic-static-array-specialization-required", { stdout: "generic static array specialization ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/generic-static-forwarded-array-specialization.0", "generic-static-forwarded-array-specialization-required", { stdout: "generic static forwarded array specialization ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/explicit-cast-narrow-direct.0", "explicit-cast-narrow-direct-required", { stdout: "explicit cast narrow direct ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/frame-large-locals.0", "frame-large-locals-required", { stdout: "frame large locals ok alpha\n", args: ["alpha"] }); +await assertDirectRuntimeRequired("conformance/native/pass/frame-limit-boundary.0", "frame-limit-boundary-required", { stdout: "frame limit boundary ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/frame-split-helpers.0", "frame-split-helpers-required", { stdout: "frame split helpers ok\n" }); +await assertDirectRuntimeRequired("conformance/native/pass/fixed-buf-alloc-local.0", "fixed-buf-alloc-local-required", { stdout: "fixed buf alloc local ok\n" }); + +const frameLimitOverFixture = `${outDir}/frame-limit-over.0`; +const frameLimitOverBody = await writeImportFailureFixture(frameLimitOverFixture, `pub fn main(world: World) -> Void raises { + var buffer: [262144]u8 = [0; 262144] + buffer[0] = 1 + check world.out.write("unreachable\\n") +} +`); +assert.equal(frameLimitOverBody.diagnostics[0].code, "MEM003"); +assert.match(frameLimitOverBody.diagnostics[0].expected, /131072 bytes of locals/); +assert.match(frameLimitOverBody.diagnostics[0].actual, /262144 bytes of locals/); +assert.match(frameLimitOverBody.diagnostics[0].help, /smaller buffers in helper functions/); + +// Typed graph MIR constructs outside the buildable subset fail at import +// time with the same BLD004 diagnostics zero build would report later. +const pageAllocLocalBody = await writeImportFailureFixture(`${outDir}/page-alloc-local.0`, `pub fn main(world: World) -> Void raises { + let page: PageAlloc = std.mem.pageAlloc() + let bytes: Maybe> = std.mem.allocBytes(page, 4096) + if bytes.has { + check world.out.write("unreachable\\n") + } +} +`); +assert.equal(pageAllocLocalBody.diagnostics[0].code, "BLD004"); +assert.match(pageAllocLocalBody.diagnostics[0].message, /allocator local requires FixedBufAlloc/); +assert.equal(pageAllocLocalBody.diagnostics[0].actual, "PageAlloc"); +assert.equal(pageAllocLocalBody.diagnostics[0].line, 2); +assert.match(pageAllocLocalBody.diagnostics[0].help, /std\.mem\.fixedBufAlloc/); +assert.match(pageAllocLocalBody.diagnostics[0].help, /do not lower to direct backends yet/); + +const generalAllocLocalBody = await writeImportFailureFixture(`${outDir}/general-alloc-local.0`, `pub fn main(world: World) -> Void raises { + let general: GeneralAlloc = std.mem.generalAlloc() + let bytes: Maybe> = std.mem.allocBytes(general, 64) + if bytes.has { + check world.out.write("unreachable\\n") + } +} +`); +assert.equal(generalAllocLocalBody.diagnostics[0].code, "BLD004"); +assert.equal(generalAllocLocalBody.diagnostics[0].actual, "GeneralAlloc"); +assert.equal(generalAllocLocalBody.diagnostics[0].line, 2); + +const deferStatementBody = await writeImportFailureFixture(`${outDir}/defer-statement-gate.0`, `pub fn main(world: World) -> Void raises { + defer check world.out.write("bye\\n") + check world.out.write("hi\\n") +} +`); +assert.equal(deferStatementBody.diagnostics[0].code, "BLD004"); +assert.match(deferStatementBody.diagnostics[0].message, /statement kind is unsupported/); +assert.equal(deferStatementBody.diagnostics[0].actual, "Defer"); +assert.equal(deferStatementBody.diagnostics[0].line, 2); +assert.equal(deferStatementBody.diagnostics[0].column, 5); + +const rescueNonPrimitiveBody = await writeImportFailureFixture(`${outDir}/rescue-non-primitive-gate.0`, `fn label(ok: Bool) -> String raises [Invalid] { + if ok { + return "primary" + } + raise Invalid +} + +pub fn main(world: World) -> Void raises { + let value: String = rescue label(false) err "fallback" + check world.out.write(value) +} +`); +assert.equal(rescueNonPrimitiveBody.diagnostics[0].code, "BLD004"); +assert.equal(rescueNonPrimitiveBody.diagnostics[0].actual, "String"); +assert.equal(rescueNonPrimitiveBody.diagnostics[0].line, 1); + +const refByteBufParamBody = await writeImportFailureFixture(`${outDir}/ref-bytebuf-param-gate.0`, `fn buffered(buf: ref) -> usize { + return std.mem.bufLen(buf) +} + +pub fn main(world: World) -> Void raises { + var storage: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + let maybe: Maybe> = std.mem.byteBuf(alloc, 4) + if !maybe.has { + return + } + let buf: owned = maybe.value + if buffered(&buf) == 4 { + check world.out.write("buffered\\n") + } +} +`); +assert.equal(refByteBufParamBody.diagnostics[0].code, "BLD004"); +assert.equal(refByteBufParamBody.diagnostics[0].actual, "ref"); +assert.equal(refByteBufParamBody.diagnostics[0].line, 1); + +await assertDirectRuntimeRequired("conformance/run/pass/world-output-helper.0", "world-output-helper-required", { stdout: "world helper ok\n" }); + +const codecReadU32Body = await writeImportFailureFixture(`${outDir}/codec-readu32-gate.0`, `use std.codec + +pub fn main(world: World) -> Void raises { + let value: u32 = std.codec.readU32("abcd") + if value > 0 { + check world.out.write("read\\n") + } +} +`); +assert.equal(codecReadU32Body.diagnostics[0].code, "STD002"); +assert.match(codecReadU32Body.diagnostics[0].message, /unknown std helper/); +assert.equal(codecReadU32Body.diagnostics[0].actual, "unknown std helper"); +assert.equal(codecReadU32Body.diagnostics[0].line, 4); + +const enumLocalPackage = `${outDir}/enum-local-package`; +await mkdir(`${enumLocalPackage}/src`, { recursive: true }); +await writeZeroToml(enumLocalPackage, { + package: { name: "enum-local-package", version: "0.1.0" }, + targets: { cli: { kind: "exe", main: "src/main.0" } }, + deps: {}, +}); +await writeFile(`${enumLocalPackage}/src/main.0`, `enum Status { + ready, + failed, +} + +choice Outcome { + ok, + failed: i32, +} + +pub fn main(world: World) -> Void raises { + let status: Status = Status.ready + let outcome: Outcome = Outcome.ok + if status == Status.ready { + check world.out.write("ready\\n") + } +} +`); +const enumLocalImport = await execFileAsync(zero, ["import", "--json", enumLocalPackage]).catch((error) => error); +assert.notEqual(enumLocalImport.code, 0); +const enumLocalImportBody = JSON.parse(enumLocalImport.stdout); +assert.equal(enumLocalImportBody.diagnostics[0].code, "BLD004"); +assert.match(enumLocalImportBody.diagnostics[0].message, /local type is unsupported/); +assert.equal(enumLocalImportBody.diagnostics[0].actual, "Status"); +assert.match(enumLocalImportBody.diagnostics[0].path, /src\/main\.0$/); +assert.equal(enumLocalImportBody.diagnostics[0].line, 12); + +const abiDump = await execFileAsync(zero, ["abi", "dump", "--json", "conformance/native/pass/const-layout.0"]); +const abiDumpBody = JSON.parse(abiDump.stdout); +assert.equal(abiDumpBody.schemaVersion, 1); +assert.equal(abiDumpBody.pointerSize, 8); +assert(abiDumpBody.externShapes.some((item) => item.name === "CPoint" && item.size === 8 && item.fields[1].offset === 4)); +assert(abiDumpBody.primitiveLayouts.some((item) => item.name === "usize" && item.size === 8)); +const cAbiDump = await execFileAsync(zero, ["abi", "dump", "--json", "conformance/native/pass/c-abi-export.0"]); +const cAbiDumpBody = JSON.parse(cAbiDump.stdout); +assert(cAbiDumpBody.cExports.some((item) => item.name === "zero_add" && item.cReturnType === "int32_t")); +assert.equal(cAbiDumpBody.generatedHeader.available, true); +assert.match(cAbiDumpBody.generatedHeader.text, /int32_t zero_add\(int32_t a, int32_t b\);/); +const abiCheck = await execFileAsync(zero, ["abi", "check", "--json", "conformance/native/pass/const-layout.0"]); +assert.equal(JSON.parse(abiCheck.stdout).ok, true); + +for (const runtimeFixture of [ + ["conformance/native/pass/indexing-primitives.0", "indexing-primitives", { stdout: "indexing primitives ok\n" }], + ["conformance/native/pass/range-slices.0", "range-slices", { stdout: "range slices ok\n" }], + ["conformance/native/pass/byte-view-call-single-eval.0", "byte-view-call-single-eval", { stdout: "byte view call single eval ok\n" }], + ["conformance/native/pass/std-math-breadth.0", "std-math-breadth", { stdout: "std math breadth ok\n" }], + ["conformance/native/pass/std-numeric-random-time.0", "std-numeric-random-time", { stdout: "std numeric random time ok\n" }], + ["conformance/native/pass/std-regex.0", "std-regex", { stdout: "std regex ok\n" }], + ["conformance/native/pass/std-unicode.0", "std-unicode", { stdout: "std unicode ok\n" }], + ["conformance/native/pass/std-inet.0", "std-inet", { stdout: "std inet ok\n" }], + ["conformance/native/pass/std-time-rfc3339.0", "std-time-rfc3339", { stdout: "std time rfc3339 ok\n" }], + ["conformance/native/pass/std-str-breadth.0", "std-str-breadth", { stdout: "std str breadth ok\n" }], + ["conformance/native/pass/std-mem-generic-items.0", "std-mem-generic-items", { stdout: "std mem generic items ok\n" }], + ["conformance/native/pass/std-mem-field-items.0", "std-mem-field-items", { stdout: "std mem field items ok\n" }], + ["conformance/native/pass/std-mem-byte-field-copy.0", "std-mem-byte-field-copy", { stdout: "std mem byte field copy ok\n" }], + ["conformance/native/pass/std-mem-bool-copy-items.0", "std-mem-bool-copy-items", { stdout: "std mem bool copy items ok\n" }], + ["conformance/native/pass/std-mem-u64-contains.0", "std-mem-u64-contains", { stdout: "std mem u64 contains ok\n" }], + ["conformance/native/pass/std-mem-u64-copy-items.0", "std-mem-u64-copy-items", { stdout: "std mem u64 copy items ok\n" }], + ["conformance/native/pass/std-mem-local-u64-copy-items.0", "std-mem-local-u64-copy-items", { stdout: "std mem local u64 copy items ok\n" }], + ["conformance/native/pass/std-path-helper-name-collision.0", "std-path-helper-name-collision", { stdout: "std path helper collision ok\n" }], + ["conformance/native/pass/byte-view-params.0", "byte-view-params", { stdout: "byte view params ok\n" }], + ["conformance/native/pass/bool-arrays.0", "bool-arrays", { stdout: "bool arrays ok\n" }], + ["conformance/native/pass/generic-spans.0", "generic-spans", { stdout: "generic spans ok\n" }], + ["conformance/native/pass/open-ended-slices.0", "open-ended-slices", { stdout: "open ended slices ok\n" }], + ["conformance/native/pass/string-slices.0", "string-slices", { stdout: "string slices ok\n" }], + ["conformance/native/pass/indexed-mutation.0", "indexed-mutation", { stdout: "indexed mutation ok\n" }], + ["conformance/native/pass/dynamic-indexed-store-scratch.0", "dynamic-indexed-store-scratch", { stdout: "dynamic indexed store ok\n" }], + ["conformance/native/pass/nested-lvalues.0", "nested-lvalues", { stdout: "nested lvalues ok\n" }], + ["conformance/native/pass/mutable-spans.0", "mutable-spans", { stdout: "mutable spans ok\n" }], + ["conformance/native/pass/mutref-indexed-lvalues.0", "mutref-indexed-lvalues", { stdout: "mutref indexed lvalues ok\n" }], + ["conformance/native/pass/generic-mem.0", "generic-mem", { stdout: "generic mem ok\n" }], + ["conformance/native/pass/generic-nested-calls.0", "generic-nested-calls", { stdout: "generic nested calls ok\n" }], + ["conformance/native/pass/generic-multi-specialization.0", "generic-multi-specialization", { stdout: "generic multi specialization ok\n" }], + ["conformance/native/pass/generic-inferred-specialized-call.0", "generic-inferred-specialized-call", { stdout: "generic inferred specialized call ok\n" }], + ["conformance/native/pass/generic-nested-local-specialization.0", "generic-nested-local-specialization", { stdout: "generic nested local specialization ok\n" }], + ["conformance/native/pass/generic-static-array-specialization.0", "generic-static-array-specialization", { stdout: "generic static array specialization ok\n" }], + ["conformance/native/pass/generic-static-forwarded-array-specialization.0", "generic-static-forwarded-array-specialization", { stdout: "generic static forwarded array specialization ok\n" }], + ["conformance/native/pass/static-interface-mutref.0", "static-interface-mutref", { stdout: "static interface mutref ok\n" }], + ["conformance/native/pass/owned-transfer.0", "owned-transfer", { stdout: "owned transfer ok\n" }], + ["conformance/native/pass/owned-drop-cleanup.0", "owned-drop-cleanup", { stdout: "owned drop cleanup ok\n" }], + ["conformance/native/pass/owned-drop-move-suppressed.0", "owned-drop-move-suppressed", { stdout: "owned drop move suppressed ok\n" }], + ["conformance/native/pass/borrow-primitives.0", "borrow-primitives", { stdout: "borrow primitives ok\n" }], + ["conformance/native/pass/allocator-primitives.0", "allocator-primitives", { stdout: "allocator primitives ok\n" }], + ["conformance/native/pass/owned-byte-buffer.0", "owned-byte-buffer", { stdout: "owned byte buffer ok\n" }], +]) { + await assertDirectRuntimeOrUnsupported(...runtimeFixture); +} + +finishAggregateAssert(assert, { suite: "conformance", reportPath: `${outDir}/failures.json` }); +console.log("conformance ok"); diff --git a/conformance/run/pass/hello.0 b/conformance/run/pass/hello.0 new file mode 100644 index 0000000..b1e9045 --- /dev/null +++ b/conformance/run/pass/hello.0 @@ -0,0 +1,3 @@ +pub fn main(world: World) -> Void raises { + check world.out.write("hello conformance\n") +} diff --git a/conformance/run/pass/hello.graph b/conformance/run/pass/hello.graph new file mode 100644 index 0000000..d7e1059 Binary files /dev/null and b/conformance/run/pass/hello.graph differ diff --git a/conformance/run/pass/world-output-helper.0 b/conformance/run/pass/world-output-helper.0 new file mode 100644 index 0000000..3739190 --- /dev/null +++ b/conformance/run/pass/world-output-helper.0 @@ -0,0 +1,8 @@ +fn writeLine(world: World, text: Span) -> Void raises { + check world.out.write(text) + check world.out.write("\n") +} + +pub fn main(world: World) -> Void raises { + check writeLine(world, "world helper ok") +} diff --git a/conformance/run/pass/world-output-helper.graph b/conformance/run/pass/world-output-helper.graph new file mode 100644 index 0000000..c99b403 Binary files /dev/null and b/conformance/run/pass/world-output-helper.graph differ diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..3632e49 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +node_modules +.next +out +.env*.local +*.tsbuildinfo +next-env.d.ts diff --git a/docs/app/(docs)/[...slug]/page.tsx b/docs/app/(docs)/[...slug]/page.tsx new file mode 100644 index 0000000..86bd492 --- /dev/null +++ b/docs/app/(docs)/[...slug]/page.tsx @@ -0,0 +1,155 @@ +import Link from "next/link"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import type { HTMLAttributes, ReactNode } from "react"; +import { MDXRemote } from "next-mdx-remote/rsc"; +import rehypePrettyCode from "rehype-pretty-code"; +import rehypeSlug from "rehype-slug"; +import remarkGfm from "remark-gfm"; +import { docs, getAdjacentDocs } from "@/lib/docs"; +import { extractHeadings, readArticleByPath } from "@/lib/articles"; +import { pageMetadata } from "@/lib/page-metadata"; +import { DocsToc } from "@/components/docs-toc"; +import { CopyCodeButton } from "@/components/copy-button"; +import { HeadingAnchor } from "@/components/heading-anchor"; +import { rehypeZeroHighlight } from "@/lib/rehype-zero-highlight"; +import { rehypeJsonRender } from "@/lib/rehype-json-render"; +import { AgentChat } from "@/components/agent-chat"; +import { FlowChart } from "@/components/flow-chart"; + +type DocsPageParams = { slug?: string[] }; +type DocsPageProps = { params: Promise }; + +export function generateStaticParams(): DocsPageParams[] { + return docs.map((doc) => ({ + slug: doc.path.replace(/^\//, "").split("/"), + })); +} + +export async function generateMetadata({ params }: DocsPageProps): Promise { + const { slug } = await params; + return pageMetadata((slug ?? []).join("/")); +} + +const rehypePrettyCodeOptions = { + theme: { light: "github-light", dark: "github-dark" }, + keepBackground: false, +}; + +type HeadingProps = HTMLAttributes & { + id?: string; + children?: ReactNode; +}; + +function makeHeading(Tag: "h2" | "h3" | "h4") { + return function Heading({ id, children, ...rest }: HeadingProps) { + return ( + + {children} + + + ); + }; +} + +const mdxComponents = { + pre: ({ children, ...props }: HTMLAttributes) => ( +
+ +
{children}
+
+ ), + h2: makeHeading("h2"), + h3: makeHeading("h3"), + h4: makeHeading("h4"), + agentchat: ({ value }: { value?: string }) => { + if (!value) return null; + try { + const spec = JSON.parse(Buffer.from(value, "base64").toString("utf8")); + return ; + } catch { + return null; + } + }, + flowchart: ({ value }: { value?: string }) => { + if (!value) return null; + try { + const spec = JSON.parse(Buffer.from(value, "base64").toString("utf8")); + return ; + } catch { + return null; + } + }, +}; + +export default async function DocsPage({ params }: DocsPageProps) { + const { slug } = await params; + const routePath = "/" + (slug ?? []).join("/"); + const result = await readArticleByPath(routePath); + if (!result) notFound(); + + const { doc, source } = result; + const headings = extractHeadings(source); + const { prev, next } = getAdjacentDocs(doc.slug); + + return ( + <> +
+
+

+ {doc.section ?? "Documentation"} +

+

+ {doc.title} +

+

{doc.description}

+
+ +
+ +
+ + +
+ + + + ); +} diff --git a/docs/app/(docs)/layout.tsx b/docs/app/(docs)/layout.tsx new file mode 100644 index 0000000..071bcc6 --- /dev/null +++ b/docs/app/(docs)/layout.tsx @@ -0,0 +1,14 @@ +import type { ReactNode } from "react"; +import { DocsSidebarShell } from "@/components/docs-sidebar"; +import { docs, groupBySection } from "@/lib/docs"; + +export default function DocsLayout({ children }: { children: ReactNode }) { + const groups = groupBySection(docs); + + return ( +
+ + {children} +
+ ); +} diff --git a/docs/app/api/docs-chat/route.ts b/docs/app/api/docs-chat/route.ts new file mode 100644 index 0000000..8b3c6f7 --- /dev/null +++ b/docs/app/api/docs-chat/route.ts @@ -0,0 +1,163 @@ +import { NextResponse } from "next/server"; +import { convertToModelMessages, stepCountIs, streamText } from "ai"; +import type { ModelMessage, UIMessage } from "ai"; +import { createBashTool } from "bash-tool"; +import { docs } from "@/lib/docs"; +import { readArticleBySlug } from "@/lib/articles"; +import { + docsChatDailyRateLimit, + docsChatMinuteRateLimit, + isDocsChatRateLimitConfigured, +} from "@/lib/rate-limit"; + +export const maxDuration = 60; + +const DEFAULT_MODEL = "anthropic/claude-haiku-4.5"; + +const SYSTEM_PROMPT = `You are a helpful documentation assistant for Zero, a pre-1 experimental programming language project exploring agent-first design. Zero intentionally makes breaking changes while searching for the language, library, and tooling patterns that work best for agents. Security vulnerabilities should be expected; Zero should be run or developed only in safe, non-production environments. + +GitHub repository: https://github.com/vercel-labs/zerolang +Documentation: https://zerolang.ai + +You have access to the full Zero documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/ directory. The file layout matches each page's URL slug — e.g. /workspace/index.md is the home, /workspace/getting-started.md is /getting-started, /workspace/modules/parse.md is /modules/parse. + +When answering questions: +- Use the bash tool to list files (ls /workspace/) or search for content (grep -r "keyword" /workspace/) +- Use the readFile tool to read specific documentation pages (e.g. readFile with path "/workspace/getting-started.md") +- Do NOT use bash to write, create, modify, or delete files (no tee, cat >, sed -i, echo >, cp, mv, rm, mkdir, touch, etc.) — you are read-only +- Always base your answers on the actual documentation content +- Be concise and accurate +- If the docs don't cover a topic, say so honestly +- Do NOT include source references or file paths in your response +- Do NOT use emojis in your responses +- When showing Zero projection syntax, use \`\`\`zero fenced blocks and say it is a human-readable projection of the graph. When showing shell commands, use \`\`\`sh.`; + +type DocsFiles = Record; + +let cachedDocsFiles: DocsFiles | null = null; + +async function loadDocsFiles(): Promise { + if (cachedDocsFiles) return cachedDocsFiles; + + const files: DocsFiles = {}; + const results = await Promise.allSettled( + docs.map(async (doc) => { + const md = await readArticleBySlug(doc.slug); + if (!md) return null; + const fileName = doc.path === "/" ? "/index.md" : `${doc.path}.md`; + return { fileName: fileName.replace(/^\//, ""), md }; + }), + ); + for (const result of results) { + if (result.status === "fulfilled" && result.value) { + files[result.value.fileName] = result.value.md; + } + } + cachedDocsFiles = files; + return files; +} + +function addCacheControl(messages: ModelMessage[]): ModelMessage[] { + if (messages.length === 0) return messages; + return messages.map((message, index) => { + if (index === messages.length - 1) { + return { + ...message, + providerOptions: { + ...message.providerOptions, + anthropic: { cacheControl: { type: "ephemeral" } }, + }, + }; + } + return message; + }); +} + +function getRateLimitIdentifier(req: Request): string { + const forwardedFor = req.headers.get("x-forwarded-for"); + const realIp = req.headers.get("x-real-ip"); + return forwardedFor?.split(",")[0]?.trim() || realIp?.trim() || "anonymous"; +} + +async function rateLimitRequest(req: Request): Promise { + if (!isDocsChatRateLimitConfigured()) { + return NextResponse.json( + { + error: "Chat rate limiting is not configured", + message: "Set KV_REST_API_URL and KV_REST_API_TOKEN to enable docs chat.", + }, + { status: 503 }, + ); + } + + const identifier = getRateLimitIdentifier(req); + let minuteResult; + let dailyResult; + try { + [minuteResult, dailyResult] = await Promise.all([ + docsChatMinuteRateLimit.limit(identifier), + docsChatDailyRateLimit.limit(identifier), + ]); + } catch (error) { + console.error("Docs chat rate limiting failed", error); + return NextResponse.json( + { + error: "Chat rate limiting unavailable", + message: "Docs chat is temporarily unavailable because rate limiting could not be applied.", + }, + { status: 503 }, + ); + } + + if (minuteResult.success && dailyResult.success) return null; + + const isMinuteLimit = !minuteResult.success; + return NextResponse.json( + { + error: "Rate limit exceeded", + message: isMinuteLimit + ? "Too many requests. Please wait a moment before trying again." + : "Daily limit reached. Please try again tomorrow.", + }, + { status: 429 }, + ); +} + +export async function POST(req: Request) { + if (!process.env.AI_GATEWAY_API_KEY && !process.env.VERCEL_OIDC_TOKEN) { + return NextResponse.json( + { error: "Chat is not configured. Set AI_GATEWAY_API_KEY or configure deployment OIDC." }, + { status: 503 }, + ); + } + + const rateLimitResponse = await rateLimitRequest(req); + if (rateLimitResponse) return rateLimitResponse; + + let body: { messages?: UIMessage[] } | null; + try { + body = (await req.json()) as { messages?: UIMessage[] }; + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } + + const messages = Array.isArray(body?.messages) ? body.messages : []; + + const docsFiles = await loadDocsFiles(); + const { + tools: { bash, readFile }, + } = await createBashTool({ files: docsFiles }); + + const result = streamText({ + model: DEFAULT_MODEL, + system: SYSTEM_PROMPT, + messages: await convertToModelMessages(messages), + stopWhen: stepCountIs(5), + tools: { bash, readFile }, + prepareStep: ({ messages: stepMessages }) => ({ + messages: addCacheControl(stepMessages), + }), + }); + + return result.toUIMessageStreamResponse(); +} diff --git a/docs/app/api/search/route.ts b/docs/app/api/search/route.ts new file mode 100644 index 0000000..5c4037d --- /dev/null +++ b/docs/app/api/search/route.ts @@ -0,0 +1,60 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getSearchIndex } from "@/lib/search-index"; +import type { SearchResult } from "@/lib/types"; + +type ScoredSearchResult = SearchResult & { score: number }; + +function isScoredResult(result: ScoredSearchResult | null): result is ScoredSearchResult { + return result !== null; +} + +export async function GET(req: NextRequest) { + const q = req.nextUrl.searchParams.get("q")?.trim().toLowerCase(); + if (!q) return NextResponse.json({ results: [] }); + + const index = await getSearchIndex(); + const terms = q.split(/\s+/).filter(Boolean); + + const results = index + .map((entry) => { + const titleLower = entry.title.toLowerCase(); + const contentLower = entry.content.toLowerCase(); + + const titleMatch = terms.every((t) => titleLower.includes(t)); + const contentMatch = terms.every((t) => contentLower.includes(t)); + + if (!titleMatch && !contentMatch) return null; + + let snippet = ""; + if (contentMatch) { + const firstTermIdx = Math.min( + ...terms.map((t) => { + const idx = contentLower.indexOf(t); + return idx === -1 ? Infinity : idx; + }), + ); + if (firstTermIdx !== Infinity) { + const start = Math.max(0, firstTermIdx - 40); + const end = Math.min(entry.content.length, firstTermIdx + 120); + snippet = + (start > 0 ? "..." : "") + + entry.content.slice(start, end).replace(/\n/g, " ") + + (end < entry.content.length ? "..." : ""); + } + } + + return { + title: entry.title, + href: entry.href, + section: entry.section, + snippet, + score: titleMatch ? 2 : 1, + }; + }) + .filter(isScoredResult) + .sort((a, b) => b.score - a.score) + .slice(0, 20) + .map(({ score: _, ...rest }) => rest); + + return NextResponse.json({ results }, { headers: { "Cache-Control": "public, max-age=60" } }); +} diff --git a/docs/app/favicon.ico b/docs/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/docs/app/favicon.ico differ diff --git a/docs/app/globals.css b/docs/app/globals.css new file mode 100644 index 0000000..b987ac3 --- /dev/null +++ b/docs/app/globals.css @@ -0,0 +1,312 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *, .dark)); + +@theme { + --color-bg: #ffffff; + --color-fg: #171717; + --color-muted: #666666; + --color-border: #eaeaea; + --color-surface: #ffffff; + --color-surface-muted: #fafafa; + --color-accent: #000000; + --color-accent-fg: #ffffff; + --color-blue: #0070f3; + --color-blue-hover: #0060df; + --color-success: #0070f3; + --color-danger: #ee0000; + --color-danger-bg: #fff0f0; + --color-code-bg: #f5f5f5; + --color-code-fg: #171717; + + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + + --container-content: 72rem; + + --shadow-card: 0 4px 14px 0 rgb(0 0 0 / 0.1); + --shadow-soft: 0 2px 8px rgb(0 0 0 / 0.08); +} + +.dark { + --color-bg: #000000; + --color-fg: #ededed; + --color-muted: #888888; + --color-border: #333333; + --color-surface: #111111; + --color-surface-muted: #0a0a0a; + --color-accent: #ffffff; + --color-accent-fg: #000000; + --color-blue: #3291ff; + --color-blue-hover: #52a9ff; + --color-danger: #ff4444; + --color-danger-bg: #2a0000; + --color-code-bg: #111111; + --color-code-fg: #ededed; + --shadow-card: 0 4px 14px 0 rgb(0 0 0 / 0.25); + --shadow-soft: 0 2px 8px rgb(0 0 0 / 0.2); +} + +:root { + color-scheme: light; +} + +.dark { + color-scheme: dark; +} + +html { + background: var(--color-bg); + color: var(--color-fg); + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +body { + margin: 0; + min-height: 100vh; +} + +::selection { + background: color-mix(in srgb, var(--color-blue) 25%, transparent); +} + +/* ─── Shiki (rehype-pretty-code) ───────────────────── */ + +[data-rehype-pretty-code-figure] { + margin: 1.5rem 0; +} + +[data-rehype-pretty-code-figure] pre { + overflow-x: auto; + padding: 1rem; + border: 1px solid var(--color-border); + border-radius: 0.5rem; + background: var(--color-code-bg); + font-size: 0.8125rem; + line-height: 1.6; +} + +[data-rehype-pretty-code-figure] pre > code { + display: grid; + background: transparent; + padding: 0; + border: 0; + font-size: inherit; + color: inherit; +} + +[data-rehype-pretty-code-figure] pre > code [data-line] { + padding: 0 0.25rem; +} + +[data-rehype-pretty-code-title] { + padding: 0.5rem 1rem; + font-size: 0.75rem; + font-family: var(--font-mono); + color: var(--color-muted); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-bottom: 0; + border-radius: 0.5rem 0.5rem 0 0; +} + +[data-rehype-pretty-code-title] + pre { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.prose :where(h1, h2, h3, h4, h5, h6) a, +.prose :where(h1, h2, h3, h4, h5, h6) a:hover { + color: inherit; + font-weight: inherit; + text-decoration: none; +} + +[data-rehype-pretty-code-figure] [data-theme] code { + color: inherit; + background-color: transparent; +} + +[data-rehype-pretty-code-figure] span { + color: var(--shiki-light); +} + +.dark [data-rehype-pretty-code-figure] span { + color: var(--shiki-dark); +} + +/* ─── Inline syntax highlight (home page) ──────────── */ + +.hl-comment { color: #6b7280; font-style: italic; } +.hl-string { color: #067a6e; } +.hl-keyword { color: #d6409f; } +.hl-type { color: #0070c0; } +.hl-function { color: #6f42c1; } +.hl-number { color: #0070c0; } +.hl-variable { color: var(--color-fg); } +.hl-id { color: #6f42c1; } +.hl-key { color: #d6409f; } +.hl-boolean { color: #0070c0; } +.hl-punctuation { color: var(--color-fg); } +.hl-operator { color: #d6409f; } + +.dark .hl-comment { color: #a1a1a1; } +.dark .hl-string { color: #00ca50; } +.dark .hl-keyword { color: #ff4d8d; } +.dark .hl-type { color: #c472fb; } +.dark .hl-function { color: #c472fb; } +.dark .hl-number { color: #47a8ff; } +.dark .hl-variable { color: #47a8ff; } +.dark .hl-id { color: #c472fb; } +.dark .hl-key { color: #ff4d8d; } +.dark .hl-boolean { color: #47a8ff; } +.dark .hl-punctuation { color: #ededed; } +.dark .hl-operator { color: #ff4d8d; } + +.hl-bash-function { + color: var(--color-fg); + font-weight: 600; +} +.hl-bash-key { color: #0070f3; } +.hl-bash-string { color: #067a6e; } +.hl-bash-keyword { color: #d6409f; } +.hl-bash-variable { color: #6f42c1; } +.hl-bash-operator, +.hl-bash-punctuation { + color: var(--color-muted); +} +.hl-bash-number { color: var(--color-fg); } + +.dark .hl-bash-function { color: #ffffff; } +.dark .hl-bash-key { color: #52a9ff; } +.dark .hl-bash-string { color: #00ca50; } +.dark .hl-bash-keyword { color: #ff4d8d; } +.dark .hl-bash-variable { color: #c472fb; } +.dark .hl-bash-operator, +.dark .hl-bash-punctuation { + color: #888888; +} +.dark .hl-bash-number { color: #ffffff; } + +/* ─── Home chat motion ────────────────────────────── */ + +@keyframes home-chat-reveal { + from { + opacity: 0; + transform: translateY(0.875rem) scale(0.985); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +@keyframes home-command-sheen { + 0% { + opacity: 0; + transform: translateX(-120%); + } + 28% { + opacity: 0.18; + } + 100% { + opacity: 0; + transform: translateX(120%); + } +} + +.home-chat-shell { + transform-origin: center top; +} + +.home-chat-code { + background: color-mix(in srgb, var(--color-fg) 9%, var(--color-bg)); + border: 1px solid color-mix(in srgb, var(--color-fg) 14%, transparent); + border-radius: 0.375rem; + color: var(--color-fg); + font-family: var(--font-mono); + font-size: 0.78125rem; + padding: 0.125rem 0.45rem; +} + +.home-chat-reveal, +.home-tool-row { + opacity: 0; + transform: translateY(0.875rem) scale(0.985); + will-change: opacity, transform; +} + +.home-chat-viewport[data-chat-visible="true"] .home-chat-reveal, +.home-chat-viewport[data-chat-visible="true"] .home-tool-row { + animation: home-chat-reveal 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards; + animation-delay: var(--chat-delay, 0ms); +} + +.home-tool-row { + position: relative; +} + +.home-tool-row::after { + opacity: 0; +} + +.home-chat-viewport[data-chat-visible="true"] .home-tool-row::after { + animation: home-command-sheen 1150ms ease-out forwards; + animation-delay: calc(var(--chat-delay, 0ms) + 140ms); + background: linear-gradient( + 90deg, + transparent, + color-mix(in srgb, var(--color-fg) 18%, transparent), + transparent + ); + content: ""; + inset: 0; + pointer-events: none; + position: absolute; + transform: translateX(-120%); +} + +@media (prefers-reduced-motion: reduce) { + .home-chat-reveal, + .home-tool-row { + animation: none; + opacity: 1; + transform: none; + } + + .home-tool-row::after { + animation: none; + content: none; + } +} + +/* ─── Prose adjustments ────────────────────────────── */ + +.prose pre { + background: var(--color-code-bg) !important; + color: var(--color-code-fg) !important; +} + +.prose :where(code):not(:where([class~="not-prose"] *)) { + background: var(--color-surface-muted); + border: 1px solid var(--color-border); + border-radius: 0.25rem; + padding: 0.15rem 0.4rem; + font-weight: 400; +} + +.prose :where(code):not(:where([class~="not-prose"] *))::before, +.prose :where(code):not(:where([class~="not-prose"] *))::after { + content: none; +} + +.prose :where(pre code):not(:where([class~="not-prose"] *)) { + background: transparent; + border: 0; + padding: 0; +} diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx new file mode 100644 index 0000000..e8bb9b1 --- /dev/null +++ b/docs/app/layout.tsx @@ -0,0 +1,61 @@ +import "./globals.css"; +import "@xyflow/react/dist/style.css"; +import { Analytics } from "@vercel/analytics/next"; +import { SpeedInsights } from "@vercel/speed-insights/next"; +import type { Metadata, Viewport } from "next"; +import type { ReactNode } from "react"; +import { ThemeProvider } from "@/components/theme-provider"; +import { SiteHeader } from "@/components/site-header"; +import { DocsChat } from "@/components/docs-chat"; +import { getStarCount } from "@/lib/github"; + +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://zerolang.ai"; +const SITE_DESCRIPTION = + "Zero is a pre-1 agent-first language experiment. Expect breaking changes and run it only in safe, non-production environments."; + +export const metadata: Metadata = { + metadataBase: new URL(SITE_URL), + title: { + default: "Zero - Agent-first language experiment.", + template: "%s | Zero", + }, + description: SITE_DESCRIPTION, + openGraph: { + type: "website", + locale: "en_US", + url: SITE_URL, + siteName: "Zero", + title: "Zero - Agent-first language experiment.", + description: SITE_DESCRIPTION, + images: [{ url: "/og", width: 1200, height: 630, alt: "Zero" }], + }, + twitter: { + card: "summary_large_image", + title: "Zero - Agent-first language experiment.", + description: SITE_DESCRIPTION, + images: ["/og"], + }, +}; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, +}; + +export default async function RootLayout({ children }: Readonly<{ children: ReactNode }>) { + const stars = await getStarCount(); + + return ( + + + + + {children} + + + + + + + ); +} diff --git a/docs/app/not-found.tsx b/docs/app/not-found.tsx new file mode 100644 index 0000000..0c5778d --- /dev/null +++ b/docs/app/not-found.tsx @@ -0,0 +1,12 @@ +import { ButtonLink } from "@/components/button"; + +export default function NotFound() { + return ( +
+

404

+

Not found

+

The page you requested does not exist.

+ Go home +
+ ); +} diff --git a/docs/app/og/[...slug]/route.tsx b/docs/app/og/[...slug]/route.tsx new file mode 100644 index 0000000..5d4c879 --- /dev/null +++ b/docs/app/og/[...slug]/route.tsx @@ -0,0 +1,15 @@ +import { NextResponse } from "next/server"; +import { getPageTitle, renderOgImage } from "../og-image"; + +type OgRouteContext = { params: Promise<{ slug: string[] }> }; + +export async function GET(_request: Request, { params }: OgRouteContext) { + const { slug } = await params; + const title = getPageTitle(slug.join("/")); + + if (!title) { + return NextResponse.json({ error: "Not found" }, { status: 404 }); + } + + return renderOgImage(title); +} diff --git a/docs/app/og/og-image.tsx b/docs/app/og/og-image.tsx new file mode 100644 index 0000000..b5d140d --- /dev/null +++ b/docs/app/og/og-image.tsx @@ -0,0 +1,112 @@ +import { ImageResponse } from "next/og"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +export { getPageTitle } from "@/lib/page-titles"; + +type FontCache = { + geistRegular: Buffer; + geistPixelSquare: Buffer; +}; + +let fontCache: FontCache | null = null; + +async function loadFonts(): Promise { + if (fontCache) return fontCache; + const [geistRegular, geistPixelSquare] = await Promise.all([ + readFile(join(process.cwd(), "public/Geist-Regular.ttf")), + readFile(join(process.cwd(), "public/GeistPixel-Square.ttf")), + ]); + fontCache = { geistRegular, geistPixelSquare }; + return fontCache; +} + +export async function renderOgImage(title: string): Promise { + const { geistRegular, geistPixelSquare } = await loadFonts(); + + return new ImageResponse( + ( +
+
+ + + + + / + + + zero + +
+ +
+ {title.split("\n").map((line, i) => ( + + {line} + + ))} +
+
+ ), + { + width: 1200, + height: 630, + fonts: [ + { + name: "Geist", + data: geistRegular, + style: "normal", + weight: 400, + }, + { + name: "GeistPixelSquare", + data: geistPixelSquare, + style: "normal", + weight: 400, + }, + ], + }, + ); +} diff --git a/docs/app/og/route.tsx b/docs/app/og/route.tsx new file mode 100644 index 0000000..5283f30 --- /dev/null +++ b/docs/app/og/route.tsx @@ -0,0 +1,6 @@ +import { getPageTitle, renderOgImage } from "./og-image"; + +export async function GET() { + const title = getPageTitle("") ?? "Zero"; + return renderOgImage(title); +} diff --git a/docs/app/page.tsx b/docs/app/page.tsx new file mode 100644 index 0000000..0ecfe09 --- /dev/null +++ b/docs/app/page.tsx @@ -0,0 +1,400 @@ +import { ArrowRightIcon, LogoIcon } from "@/components/icons"; +import type { CSSProperties, ReactNode } from "react"; +import { ButtonLink } from "@/components/button"; +import { InstallCopy } from "@/components/install-copy"; +import { HomeChatViewport } from "@/components/home-chat-viewport"; +import { ChatToolRuns } from "@/components/home-chat-tools"; +import { LoopDiagram } from "@/components/loop-diagram"; +import { highlight } from "@/lib/highlight"; +import { pageMetadata } from "@/lib/page-metadata"; + +export const metadata = pageMetadata(""); + +const CODE_EXAMPLE = `pub fn main(world: World) -> Void raises { + check world.out.write("hello from zero\\n") +}`; + +const GRAPH_EXAMPLE = `zero-graph v1 +origin source-text +module "hello" +hash "graph:a7f7e6899a73f3b4" + +node #decl_ad8d9028 Function name:"main" type:"Void" public:true fallible:true +node #param_4610ae76 Param name:"world" type:"World" +node #expr_c403020c MethodCall name:"write" type:"Void" +node #expr_653eeb6e Literal type:"String" value:"hello from zero\\n" +edge #expr_c403020c arg #expr_653eeb6e order:0`; + +const PATCH_EXAMPLE = `zero patch \\ + --expect-graph-hash graph:a7f7e6899a73f3b4 \\ + --op 'set node="#expr_653eeb6e" \\ + field="value" \\ + expect="hello from zero\\n" \\ + value="hello graph\\n"'`; + +const CONSTRAINTS = [ + "Token efficiency", + "Low memory", + "Fast startup", + "Fast builds", + "Low latency", + "Zero dependencies", +]; + +const ARCHITECTURE_STEPS = [ + { + label: "Human", + title: "Asks for an outcome", + body: "Build a CRM API, add auth, or fix a failing route", + }, + { + label: "Agent", + title: "Uses Graph", + body: "Symbols, calls, types, effects, node IDs, and graph hashes", + }, + { + label: "Compiler", + title: "Checks patches", + body: "Shape, type, stale-state, and repository metadata checks", + }, + { + label: "Human", + title: "Reviews projection", + body: "Readable .0 projections stay available for review and rare manual edits", + }, +]; + +/* ─── Primitives ──────────────────────────────────── */ + +function GridGlow() { + return ( +
+
+
+ ); +} + +function SectionHeader({ + title, + description, +}: { + title: string; + description: string; +}) { + return ( +
+

+ {title} +

+

+ {description} +

+
+ ); +} + +function VisualGlow({ className = "", children }: { className?: string; children: ReactNode }) { + return
{children}
; +} + +function Panel({ className = "", children }: { className?: string; children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +function WindowBar({ title }: { title: string }) { + return ( +
+
+ + + +
+ + {title} + +
+ ); +} + +function CodeWindow({ + title, + html, + className = "", +}: { + title: string; + html: string; + className?: string; +}) { + return ( + + +
+        
+      
+
+ ); +} + +function ArchitectureDiagram() { + return ( +
+
+ {ARCHITECTURE_STEPS.map((item, index) => ( +
+
+ + {index + 1} + + + {item.label} + +
+

+ {item.title} +

+

+ {item.body} +

+ + {index < ARCHITECTURE_STEPS.length - 1 ? ( + + + + ) : null} + +
+ ))} +
+
+ ); +} + +/* ─── Chat mockup ─────────────────────────────────── */ + +function chatRevealStyle(delay: string): CSSProperties { + return { "--chat-delay": delay } as CSSProperties; +} + +function ChatMockup() { + return ( + + + + +
+
+
+build hello world in zerolang +
+
+ +
+ I'll set up the package, patch the graph, and run it. +
+ + + +
+ Done. zero.graph validated at{" "} + graph:a7f7e689 with symbol{" "} + main. It prints: +
+ +
+ hello from zero +
+
+
+
+ ); +} + +/* ─── Section wrapper ─────────────────────────────── */ + +function Section({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +export default function HomePage() { + return ( +
+
+ {/* Hero */} +
+ +
+
+ Experimental +
+

+ The Programming +
+ Language for Agents +

+

+ A programming language where the graph is the program. Humans ask + for outcomes. Agents query the program graph, submit checked + edits, and prove the result. +

+ + +

+ Expect breaking changes. Run it in a safe environment, not against + production systems. +

+
+
+ + {/* 01 — Chat */} +
+ + + + +
+ + {/* 02 — Loop */} +
+ + +
+ + {/* 03 — Patch */} +
+ + + + +
+ {[ + ["graph hash", "graph:b3c1d04f"], + ["node", "#expr_653eeb6e"], + ["field", "value"], + ["symbols", "main"], + ["projection", "src/main.0"], + ].map(([k, v]) => ( +
+
{k}
+
{v}
+
+ ))} +
+
+
+
+ + {/* 04 — Graph */} +
+ + + + + +
+ + {/* 04 — Constraints */} +
+ + +
+ {CONSTRAINTS.map((c) => ( +
+ + {c} +
+ ))} +
+
+
+ + {/* CTA */} +
+
+

+ Explore with us. +

+

+ Start with the getting started guide, then read the graph architecture + and compile-path pages to see why the program database matters. +

+
+ + Get started + + + Graph architecture + +
+
+
+
+ +
+
+
+ + zerolang +
+

+ Experimental graph-first language design. +

+
+
+
+ ); +} diff --git a/docs/articles/benchmarks.md b/docs/articles/benchmarks.md new file mode 100644 index 0000000..91999a4 --- /dev/null +++ b/docs/articles/benchmarks.md @@ -0,0 +1,55 @@ +## Measure The Current Compiler + +Zerolang benchmarks are regression signals for the current compiler. They are not broad +marketing claims. Use them to compare graph inputs, artifact sizes, build time, +startup/runtime behavior, and memory use across changes. + +Run: + +```sh +pnpm run bench +``` + +Outputs: + +```sh +.zero/bench/latest.json +.zero/bench/trends/latest.json +.zero/bench/trends/summary.md +``` + +## Cases + +Current benchmark cases include: + +- `hello`, `add`, `structs`, `params` +- `buffers`, `parser`, `codec`, `parse` +- `slices`, `arena`, `fallibility`, `branches` +- `module-package`, `rescue` +- `fs-resource`, `mem-copy-fill`, `zero-hash` + +The Zero inputs live under `benchmarks/zero`. Host targets that cannot run a +case report `skipped` with a reason instead of failing the entire benchmark. + +## Metrics + +Important fields: + +- `buildMs` +- `runMs` +- `runMinMs` +- `runRuns` +- `artifactBytes` +- `compressedArtifactBytes` +- `peakRssBytes` +- `expectedStdout` +- `outputMatches` + +## Options + +```sh +ZERO_BENCH_RUNS= pnpm run bench +ZERO_BENCH_MODE=sandbox pnpm run bench +``` + +Use one run for smoke checks and more runs when comparing trends. diff --git a/docs/articles/building-from-source.md b/docs/articles/building-from-source.md new file mode 100644 index 0000000..fcb5dc5 --- /dev/null +++ b/docs/articles/building-from-source.md @@ -0,0 +1,64 @@ +## Use A Checkout Compiler + +Use this page when you are working on Zerolang itself or testing a local compiler +change. Public docs use `zero`; contributor notes cover checkout wrapper +details. + +## Build + +```sh +pnpm install +make -C native/zero-c +zero --version +``` + +## Focused Loop + +```sh +zero check examples/hello.graph +zero run examples/add.graph +zero build --emit exe --target linux-musl-x64 examples/add.graph --out .zero/out/add +``` + +Inspect graph and size facts: + +```sh +zero inspect --json examples/systems-package +zero size --json examples/point.graph +zero targets +``` + +Explain diagnostics: + +```sh +zero explain TAR002 +zero explain --json TYP009 +``` + +## Validation + +```sh +pnpm run conformance +pnpm run command-contracts:local +pnpm run native:test +pnpm run docs:build +``` + +Before ending an agent turn that changed the repository, run +`pnpm run conformance`. + +## Native Targets + +The documented native target names are: + +- `darwin-arm64` +- `darwin-x64` +- `linux-arm64` +- `linux-musl-arm64` +- `linux-musl-x64` +- `linux-x64` +- `win32-arm64.exe` +- `win32-x64.exe` + +Unsupported target or feature requests report diagnostics instead of silently +choosing another backend. diff --git a/docs/articles/c-interop.md b/docs/articles/c-interop.md new file mode 100644 index 0000000..2d0f9b6 --- /dev/null +++ b/docs/articles/c-interop.md @@ -0,0 +1,76 @@ +## The C Boundary + +Zerolang supports a small explicit C ABI surface from graph inputs. C-facing code +should stay narrow, inspectable, and target-aware. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "can this call my tiny c helper on linux musl?" + }, + { + "role": "assistant", + "text": "I’ll check the target link plan and tell you what blocks the call." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero inspect --json --target linux-musl-x64", + "output": "{\"cLibraries\":[{\"linkPlan\":{\"sysrootStatus\":\"configured\"}}]}" + } + ] + } + ] +} +``` + +## What This Means + +Projection example: + +```zero +export c fn add(a: i32, b: i32) -> i32 { + return a + b +} +``` + +Graph checks: + +```sh +zero check conformance/native/pass/c-abi-export.graph +zero abi dump --json conformance/native/pass/c-abi-export.graph +``` + +`zero abi dump --json` reports exported symbols and generated header facts such +as `generatedHeader.available`. + +## Import Metadata + +Header imports expose typed metadata: + +```sh +zero inspect --json --target linux-musl-x64 conformance/check/pass/c-header-import.graph +``` + +The JSON includes `cImports[].typedModel` with imported functions, constants, +structs, enums, and typedefs. + +Callable imports are limited to direct scalar ABI types today: `Void`, `Bool`, +`u8`, `u16`, `usize`, `i32`, `u32`, `i64`, and `u64`. Pointer, array, struct, +and unsupported-width parameters should be wrapped by a small C shim. + +## Link Plans + +Executable builds with direct extern C calls require package link metadata in +`zero.toml`. Imported headers must appear in `c.libs.*.headers`, and the +library must provide `lib` or `link` inputs. + +`zero inspect --json` reports each `cLibraries[].linkPlan` with include paths, +library paths, sysroot status, target ABI, host discovery status, and header +hash data. + +Unsafe foreign-target discovery fails with `CIMP003`. Missing or unsafe link +inputs fail with `CIMP005`. diff --git a/docs/articles/cli-reference.md b/docs/articles/cli-reference.md new file mode 100644 index 0000000..e1de2d5 --- /dev/null +++ b/docs/articles/cli-reference.md @@ -0,0 +1,290 @@ +## Use Commands By Workflow + +The Zerolang CLI is organized around graph-first agent work. Humans ask for a task; +agents inspect and patch the graph; projections are exported only for review or +manual edits. + +Most commands default to the current directory: + +```sh +zero status +zero query +zero patch --op help +zero check +zero run -- +``` + +Pass an explicit graph input or package when you are outside the project: + +```sh +zero check examples/hello.graph +zero query examples/crm-api +zero run examples/json-api-router.graph -- $'GET /health\n\n' +``` + +## Create A Project + +Use `zero init` for all project creation. + +```sh +zero init +zero init --template cli crm-tool +zero init --manifest toml --format binary --template package api-server +``` + +If no path is given, `zero init` creates the package in the current directory. +For `.` or an omitted path, the package name comes from the directory name. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "start a cli here" + }, + { + "role": "assistant", + "text": "I’ll initialize this directory and add the starting CLI shape." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero init --template cli", + "output": "graph project init ok\nwrote: ./zero.toml\nwrote: ./zero.graph" + }, + { + "command": "zero patch --op 'addMain'", + "output": "program graph patch ok" + } + ] + } + ] +} +``` + +## Inspect Before Editing + +Agents should query for the exact thing they need instead of dumping the whole +program. + +```sh +zero status +zero query --fn main +zero query --find customer +zero query --refs handle +zero query --calls write +zero inspect --json +zero size --json +zero mem --json +``` + +Use plain text first. Use `--json` when a tool needs exact fields such as node +ids, graph hashes, `interfaceFingerprints`, `targetToolchains`, +`usedStdlibHelpers`, `memoryBudgets`, or `releaseTargetContract`. + +## Patch The Graph + +Patch commands are checked graph edits: + +```sh +zero patch --op 'addFunction name="add" ret="i32"' +zero patch --op 'addParam fn="add" name="x" type="i32"' +zero patch --op 'addParam fn="add" name="y" type="i32"' +zero patch --op 'addReturnBinary fn="add" name="+" left="x" right="y" type="i32"' +``` + +For larger edits, use a patch file under `/tmp`: + +```text +zero-program-graph-patch v1 +expect graphHash "graph:a7f7e6899a73f3b4" +replaceFunctionBody main + check world.out.write "hello\n" +end +``` + +Dry-run a repository graph patch without writing: + +```sh +zero patch --check-only /tmp/main.patch +zero patch --dry-run --json /tmp/main.patch +``` + +Apply it: + +```sh +zero patch /tmp/main.patch +``` + +To replace one function body without patch syntax, pass only the new body rows +(exactly what `zero view --fn ` prints between the signature braces). +`--body-file -` reads them from stdin, so a heredoc does the whole edit in one +call: + +```sh +zero patch --replace-fn main --body-file - <<'EOF' + check world.out.write("hello agent\n") +EOF +``` + +A file path works as the alternative: + +```sh +zero patch --replace-fn main --body-file /tmp/main.body +``` + +To change a few characters inside a function without retyping the body, +`--replace-in-fn` replaces one unique literal occurrence of `--old` in the +function's canonical body text (what `zero view --fn ` prints) with +`--new`, then revalidates exactly like `--replace-fn`: + +```sh +zero patch --replace-in-fn main --old 'limit + 1' --new 'limit + 2' +``` + +A missing or non-unique `--old` fails with the occurrence count. Inline +`--old`/`--new` accept `\n` escapes for multi-line text; `--old-file` and +`--new-file ` read the text from a file or stdin. + +The patch step validates graph shape and repository metadata. A stale graph +hash, missing required edge, sparse ordered child group, or invalid row body +fails before the package store is updated. + +## Validate Only What You Need + +Do not run every command after every patch. `zero patch` already reports whether +the edit applied. Run the next command that proves the user-visible behavior. + +```sh +zero check +zero test +zero test --json --filter add +zero run -- add 40 2 +``` + +Use `zero check --json` when an editor, CI job, or agent needs stable +diagnostic fields. Test JSON includes `expectedFailures`, `fixtures`, +`snapshotKey`, and per-test results. + +## Run And Build + +Use `zero run` for local behavior: + +```sh +zero run -- help +zero run examples/hello.graph +``` + +Use `zero build` for artifacts: + +```sh +zero build --emit exe --target linux-musl-x64 --out .zero/out/app +zero build --emit obj --target darwin-arm64 examples/direct-call-add.graph --out .zero/out/add.o +zero build --emit llvm-ir examples/hello.graph --out .zero/out/hello.ll +``` + +Build JSON reports profile and target readiness: + +```sh +zero build --json --profile tiny --target linux-musl-x64 examples/hello.graph --out .zero/out/hello +``` + +Important fields include `profileSemantics`, `profileBudget`, +`releaseTargetContract`, `targetToolchains`, `compileTime`, and repeat-build +hash policy data for artifact determinism. + +## Review Projections + +Projection commands are for humans: + +```sh +zero export +zero verify-projection +zero import +zero diff +zero view +``` + +Use `zero export` when a human wants the current `.0` projection. Use +`zero import` after a human intentionally edits projection text. Use +`zero verify-projection` to catch drift without writing. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "show me the projection before we keep going" + }, + { + "role": "assistant", + "text": "I’ll export the current projection and verify it matches the graph." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero export", + "output": "repository graph export ok\nwrote: ./src/main.0" + }, + { + "command": "zero verify-projection", + "output": "repository graph verify-projection ok" + } + ] + } + ] +} +``` + +## Diagnose And Repair + +```sh +zero explain NAM003 +zero fix --plan --json +zero doctor +zero dev --json +zero dev --json --trace +``` + +`zero dev --json` is the editor-facing snapshot. It includes diagnostics, +document symbols, hover data, completions, definition targets, and +`interfaceFingerprints`. + +## Command Groups + +| Workflow | Commands | +| --- | --- | +| create | `init` | +| inspect | `status`, `query`, `inspect`, `size`, `mem`, `doc`, `source-map` | +| edit graph | `patch`, `reconcile`, `merge` | +| validate | `check`, `test`, `verify-projection`, `validate`, `roundtrip` | +| run/build | `run`, `build`, `targets`, `abi` | +| projection review | `export`, `import`, `view`, `diff`, `fmt`, `tokens`, `parse` | +| support | `skills`, `explain`, `fix`, `doctor`, `clean`, `dev`, `time` | + +## Input Forms + +| Input | Meaning | +| --- | --- | +| `project/` | A package directory. Normal package commands compile from `zero.graph`. | +| `zero.toml` | Preferred package manifest. Takes precedence over `zero.json` for directory inputs. | +| `zero.json` | Compatibility manifest. Prefer `zero.toml` for new packages. | +| `file.graph` | Binary or text graph store/artifact. | +| `file.0` | Human-readable projection for formatting, import/export, and review workflows. It is not the normal compiler input. | + +## JSON Rule + +Humans and interactive agents should start with concise text output. Use JSON +when a program needs exact structured data: + +```sh +zero check --json +zero test --json +zero inspect --json +zero size --json +zero doctor --json +``` + +JSON is a contract for tools, not the default reading experience for humans. diff --git a/docs/articles/concepts/compile-path.md b/docs/articles/concepts/compile-path.md new file mode 100644 index 0000000..ff69c64 --- /dev/null +++ b/docs/articles/concepts/compile-path.md @@ -0,0 +1,117 @@ +## From Graph To Code + +Zerolang is easiest to understand against parse-first compilers. Most languages +have a parse-first compile path: the normal compiler input is text, so every +compile starts by parsing text into compiler data structures. Zerolang's +package path is graph-first. It is the same kind of pipeline with fewer stages, +and the input is the graph store instead of text: + +```json-render +{ + "type": "flow", + "title": "Parse-first compile path vs Zero graph-first path", + "nodes": [ + { "id": "p1", "label": "source files", "x": 0, "y": 0, "tone": "text" }, + { "id": "p2", "label": "lexer / parser", "x": 0, "y": 96, "tone": "text" }, + { "id": "p3", "label": "AST", "x": 0, "y": 192, "tone": "text" }, + { "id": "p4", "label": "name resolution", "x": 0, "y": 288, "tone": "text" }, + { "id": "p5", "label": "type checking", "x": 0, "y": 384, "tone": "text" }, + { "id": "p6", "label": "IR lowering", "x": 0, "y": 480, "tone": "text" }, + { "id": "p7", "label": "optimization", "x": 0, "y": 576, "tone": "text" }, + { "id": "p8", "label": "codegen", "x": 0, "y": 672, "tone": "text" }, + { "id": "p9", "label": "artifact", "x": 0, "y": 768, "tone": "text" }, + + { "id": "g1", "label": "zero.graph", "x": 380, "y": 0, "tone": "graph" }, + { "id": "g2", "label": "repository graph tables", "x": 380, "y": 96, "tone": "graph" }, + { "id": "g3", "label": "semantic validation", "x": 380, "y": 192, "tone": "compiler" }, + { "id": "g4", "label": "type checking", "x": 380, "y": 288, "tone": "compiler" }, + { "id": "g5", "label": "MIR and backend facts", "x": 380, "y": 384, "tone": "graph" }, + { "id": "g6", "label": "direct codegen", "x": 380, "y": 480, "tone": "compiler" }, + { "id": "g7", "label": "artifact", "x": 380, "y": 576, "tone": "graph" } + ], + "edges": [ + { "source": "p1", "target": "p2" }, + { "source": "p2", "target": "p3" }, + { "source": "p3", "target": "p4" }, + { "source": "p4", "target": "p5" }, + { "source": "p5", "target": "p6" }, + { "source": "p6", "target": "p7" }, + { "source": "p7", "target": "p8" }, + { "source": "p8", "target": "p9" }, + { "source": "g1", "target": "g2" }, + { "source": "g2", "target": "g3" }, + { "source": "g3", "target": "g4" }, + { "source": "g4", "target": "g5" }, + { "source": "g5", "target": "g6" }, + { "source": "g6", "target": "g7" } + ] +} +``` + +Rust, Go, Zig, C, and many other languages differ in details, but the normal +compiler input is text. The `.0` projection can be exported and imported, but it +is not the normal package compile input. The compiler loads the graph store +directly. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "why is this less work for the agent?" + }, + { + "role": "assistant", + "text": "The agent can ask the compiler for graph facts, submit one checked semantic patch, and avoid a separate write-format-parse cycle. The compiler still checks and builds, but the edit itself is closer to the final compiler model." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query --calls write", + "output": "call write\n receiver: world.out\n arg0: #expr_653eeb6e String" + }, + { + "command": "zero patch --op 'set node=\"#expr_653eeb6e\" field=\"value\" expect=\"old\\n\" value=\"new\\n\"'", + "output": "program graph patch ok" + } + ] + } + ] +} +``` + +## Why The Path Matters + +For agents, the compile path is also the authoring path. If text is primary, +the agent writes text and waits for the compiler to tell it whether the text +meant what it intended. + +If the graph is primary, the agent can ask the compiler for node handles, +symbol facts, calls, references, diagnostics, and patch operations before it +edits. The edit is already expressed in compiler terms. + +That is why Zero keeps investing in binary graph storage and direct graph +loading. The long-term goal is to memory-map final compiler IR and codegen from +semantic data with as little redundant parsing and reconstruction as possible. + +## Performance And Size Angle + +The graph model is not only about agent ergonomics. It also supports Zero's +systems goals: + +- fewer reparsed text inputs on normal package commands +- deterministic graph identity and stable diff/review output +- pay-as-used standard library helpers +- explicit capability and allocation facts +- small direct artifacts when the selected profile and target support them + +Use `zero size --json`, `zero mem --json`, and the benchmark docs to inspect +those facts for a graph input instead of treating performance claims as prose. + +## Current Boundary + +Zero is still experimental. Some commands and targets expose readiness facts or +structured diagnostics when a backend cannot build a graph shape yet. That is +intentional: the docs should show what works today and what the compiler can +explain, not imply production completeness. diff --git a/docs/articles/concepts/graph-architecture.md b/docs/articles/concepts/graph-architecture.md new file mode 100644 index 0000000..9294701 --- /dev/null +++ b/docs/articles/concepts/graph-architecture.md @@ -0,0 +1,123 @@ +## The Program Database + +Zerolang exists because humans increasingly ask agents to write programs. + +Most programming languages still make text the primary program database. That +works for humans, but it is a poor interface for agents. An agent has to infer +semantic structure from text, make a text edit, run tools to learn whether the +edit was valid, format the result, and then inspect failures after the fact. + +In Zero, the graph is the program database. The graph stores declarations, +types, calls, blocks, imports, capabilities, and source-map facts directly. +Agents edit those facts with checked graph patches. Humans read `.0` +projections when they want a source-like review view. + +## The Editing Loop + +A traditional agent loop writes text, then runs check, format, and build to find +out what the text meant. Zero's loop queries the graph, submits one checked +patch, and only runs the validation a task actually needs: + +```json-render +{ + "type": "flow", + "title": "Traditional source loop vs Zero graph loop", + "height": 520, + "nodes": [ + { "id": "t1", "label": "agent writes text", "x": 0, "y": 0, "tone": "text" }, + { "id": "t2", "label": "check", "x": 0, "y": 90, "tone": "compiler" }, + { "id": "t3", "label": "format", "x": 0, "y": 180, "tone": "text" }, + { "id": "t4", "label": "build", "x": 0, "y": 270, "tone": "compiler" }, + { "id": "t5", "label": "inspect failures", "x": 0, "y": 360, "tone": "human" }, + { "id": "z1", "label": "agent queries graph", "x": 360, "y": 0, "tone": "graph" }, + { "id": "z2", "label": "agent submits checked patch", "x": 360, "y": 105, "tone": "graph" }, + { "id": "z3", "label": "compiler rejects invalid graph edits immediately", "x": 360, "y": 210, "tone": "compiler" }, + { "id": "z4", "label": "agent runs only task validation", "x": 360, "y": 315, "tone": "graph" }, + { "id": "z5", "label": "human reviews projection when useful", "x": 360, "y": 420, "tone": "human" } + ], + "edges": [ + { "source": "t1", "target": "t2" }, + { "source": "t2", "target": "t3" }, + { "source": "t3", "target": "t4" }, + { "source": "t4", "target": "t5" }, + { "source": "t5", "target": "t1", "label": "repeat" }, + { "source": "z1", "target": "z2" }, + { "source": "z2", "target": "z3" }, + { "source": "z3", "target": "z4" }, + { "source": "z4", "target": "z5" } + ] +} +``` + +The difference is not just syntax. A graph patch can target “the literal +argument to this write call” or “the body of this block” instead of asking an +agent to locate and rewrite a span of text. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "change the greeting to hello graph" + }, + { + "role": "assistant", + "text": "I’ll patch the greeting and run the program so you can see the output." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query --fn main", + "output": "main\n check world.out.write \"hello from zero\\n\"\n graphHash graph:a7f7e6899a73f3b4" + }, + { + "command": "zero patch --expect-graph-hash graph:a7f7e6899a73f3b4 --op 'set node=\"#expr_653eeb6e\" field=\"value\" expect=\"hello from zero\\n\" value=\"hello graph\\n\"'", + "output": "program graph patch ok" + }, + { + "command": "zero run", + "output": "hello graph" + } + ] + } + ] +} +``` + +## Invalid Edits Fail Earlier + +The graph store has shape rules. Required edges, ordered child groups, node +kinds, type facts, and repository metadata are validated when patches are +applied. If an edit would leave a sparse argument list, a missing expression, a +stale graph hash, or an invalid repository store, the patch fails before the +package becomes the new compiler input. + +That is the agent-facing contract: write checked semantic edits, not hopeful +text diffs. + +## Human Review Stays Textual + +Humans should not have to inspect graph dumps to trust a change. `.0` +projections exist so people can read, review, and occasionally manually edit a +program. + +The important distinction is ownership: + +- agents normally author through `zero query` and `zero patch` +- humans review through projections +- humans may edit projections as an escape hatch +- `zero import` reconstructs the graph from reviewed projection text +- `zero verify-projection` catches drift instead of hiding it + +Zero is a graph-native language with human-editable text projections. + +## The Payoff + +The graph-first model is meant to reduce guessing and reduce tool calls. A +checked patch combines edit intent, stale-state protection, shape validation, +and formatting-normalized projection output into one compiler-mediated step. + +That gives agents a smaller, more precise work surface. It gives humans a +reviewable source-like view. It gives the compiler a direct path to semantic +program facts without reparsing text on the normal package compile path. diff --git a/docs/articles/concepts/projections.md b/docs/articles/concepts/projections.md new file mode 100644 index 0000000..99a707c --- /dev/null +++ b/docs/articles/concepts/projections.md @@ -0,0 +1,100 @@ +## Human Review Surface + +In Zerolang, `.0` files are human-readable projections of the graph. + +They exist for trust. A human can read the program, review a change, and make a +manual edit when needed. They are also useful for diagnostics because source +maps can point to familiar line and column spans. + +They are not the normal agent write surface. + +## Explicit Sync Model + +Zero uses explicit commands so graph and projection state cannot silently drift: + +```sh +zero export +zero import +zero verify-projection +zero status +``` + +Use `zero export` when a human needs the latest `.0` review text. Use +`zero import` after a human intentionally edits projection text and wants to +reconstruct the graph. Use `zero verify-projection` in review or CI when you +want a no-write drift check. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "show me the projection so i can review it" + }, + { + "role": "assistant", + "text": "I’ll export the current projection and verify it is in sync." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero export", + "output": "repository graph export ok\nwrote: ./src/main.0" + }, + { + "command": "zero verify-projection", + "output": "repository graph verify-projection ok" + } + ] + } + ] +} +``` + +## No Silent Divergence + +`zero status` reports whether the projection is clean, missing, stale, conflicting, or unavailable. +When the checked-in `.0` sources were edited after `zero.graph` was written, commands that consume the store, including `zero check`, `zero build`, `zero run`, `zero test`, `zero query`, `zero view`, and `zero diff`, refresh the store from the edited source first and report the refresh on stderr. +When the graph is the newer side, for example right after `zero patch`, those commands keep using `zero.graph` until `zero export` syncs the projection, and they say so on stderr. +When both sides were edited independently, they fail with an `RGP006` diagnostic that offers `zero import` and `zero export` as repairs instead of picking a side. +Which side moved is decided by content: every store write records a hash of the source projection inside `zero.graph`, so a freshly staged, cloned, or extracted workspace classifies the same way everywhere regardless of file timestamps. +Set `ZERO_STALE=fail` to fail with an `RGP008` diagnostic instead of refreshing automatically. + +That rule prevents the worst ambiguity: an agent editing text, seeing `zero check` pass, and then running a binary built from different code. + +## Human Escape Hatch + +The escape hatch is deliberate. A project should remain reconstructable from +text projections. A human can edit `src/main.0`, reconcile it back into the +graph, and confirm the projection still matches: + +```json-render +{ + "type": "flow", + "title": "Human escape hatch: edit text, reconcile to the graph", + "nodes": [ + { "id": "n1", "label": "human reviews src/main.0", "x": 0, "y": 0, "tone": "human" }, + { "id": "n2", "label": "human edits src/main.0", "x": 0, "y": 96, "tone": "human" }, + { "id": "n3", "label": "zero import", "x": 0, "y": 192, "tone": "compiler" }, + { "id": "n4", "label": "zero check", "x": 0, "y": 288, "tone": "compiler" }, + { "id": "n5", "label": "zero export", "x": 0, "y": 384, "tone": "graph" }, + { "id": "n6", "label": "zero verify-projection", "x": 0, "y": 480, "tone": "compiler" } + ], + "edges": [ + { "source": "n1", "target": "n2" }, + { "source": "n2", "target": "n3" }, + { "source": "n3", "target": "n4" }, + { "source": "n4", "target": "n5" }, + { "source": "n5", "target": "n6" } + ] +} +``` + +This keeps the system auditable. The graph is first-class, but humans are not +locked out of a readable format. + +## Review Rule + +Use graph commands for authoring. Use projections for review. Import +projections only when a human intentionally edits them. diff --git a/docs/articles/concepts/semantic-vs-text.md b/docs/articles/concepts/semantic-vs-text.md new file mode 100644 index 0000000..da18158 --- /dev/null +++ b/docs/articles/concepts/semantic-vs-text.md @@ -0,0 +1,94 @@ +## Semantic Edits + +In Zerolang, text is an excellent human review format. It is not an ideal agent edit format. + +When an agent edits text, it must keep many details in its context at once: +syntax, indentation, imports, symbol names, surrounding control flow, stale +file contents, and the compiler's current capabilities. A small mistake can +produce invalid code that is only discovered after another tool call. + +Graph edits carry more intent: + +```text +replaceFunctionBody main + check world.out.write "hello\n" +end +``` + +That patch says which function body changes and what rows should become the new +body. The compiler parses those rows, builds graph nodes, checks graph shape, +updates repository metadata, and can export a stable projection for humans. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "add a help command and then show me the source" + }, + { + "role": "assistant", + "text": "I’ll add the help branch, run it, and then show the projection." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero patch --check-only /tmp/help.patch", + "output": "program graph patch ok (check-only)" + }, + { + "command": "zero patch /tmp/help.patch", + "output": "program graph patch ok" + }, + { + "command": "zero run -- help", + "output": "usage: zero run -- " + }, + { + "command": "zero export", + "output": "repository graph export ok\nwrote: ./src/main.0" + } + ] + } + ] +} +``` + +## Text Edit Failure Modes + +Common text-edit problems for agents: + +- editing the wrong overload or similarly named function +- losing an import or closing brace +- creating syntax that looks plausible but is not accepted by this compiler +- formatting code that later changes the span the agent intended to patch +- using stale file contents after another edit +- changing source while the graph store remains the actual compiler input + +Zero does not remove all errors. It moves the primary edit operation closer to +the compiler's semantic model so more errors are caught at patch time. + +## Graph Patch Guardrails + +Graph patches can include: + +- graph hash expectations +- node hash expectations +- field expectations +- typed operation names +- function body or block body replacement +- dry-run and check-only modes + +Those guardrails make stale edits explicit. They also make the failure useful: +the agent can query the graph again and patch the current node rather than +guessing from text. + +## Where Projections Fit + +Projection text is still part of the system. It gives humans a compact review +format and a manual edit escape hatch. It also gives diagnostics stable spans +and makes examples readable. + +The rule is simple: agents write the graph by default; humans review +projections by choice. diff --git a/docs/articles/cross-compilation.md b/docs/articles/cross-compilation.md new file mode 100644 index 0000000..ec5c056 --- /dev/null +++ b/docs/articles/cross-compilation.md @@ -0,0 +1,75 @@ +## Targets Are Explicit + +In Zerolang, start with the result the human wants. The agent decides whether it needs target +facts, a no-write readiness check, or an artifact build. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "build this for linux musl and tell me if anything blocks it" + }, + { + "role": "assistant", + "text": "I’ll check the target facts first, then build if it is ready." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero check --json --target linux-musl-x64", + "output": "{\"ok\":true,\"targetReadiness\":{\"ok\":true}}" + }, + { + "command": "zero build --target linux-musl-x64 --out .zero/out/app", + "output": "" + } + ] + } + ] +} +``` + +## What This Means + +Zero cross-compilation starts from graph facts, target facts, and direct +emitters. Unsupported target or feature combinations should fail with +diagnostics rather than fallback silently. + +## Check Target Readiness + +Use `check --json` before writing artifacts when an agent needs readiness data: + +```sh +zero targets +zero check --json --emit obj --target linux-musl-x64 examples/direct-call-add.graph +zero check --target linux-musl-x64 examples/memory-package +``` + +Target readiness can include `target`, `objectFormat`, `backend`, `stage`, and +unsupported feature facts. + +## Build Direct Artifacts + +```sh +zero build --target linux-musl-x64 examples/memory-package --out .zero/out/memory-package +zero build --emit exe --target linux-musl-x64 examples/direct-exe-return.graph --out .zero/out/direct-exe-return +zero build --emit obj --target darwin-arm64 examples/direct-call-add.graph --out .zero/out/direct-call-add.o +``` + +Use `zero size --json --target ` when the question is about retained +helpers, section sizes, or artifact facts rather than producing a new file. + +## Sysroots And C Boundaries + +Cross-target C interop must use explicit package metadata, vendored inputs, or +target sysroots. The compiler should not silently reuse host headers and +libraries for a foreign target. + +Use: + +```sh +zero inspect --json --target linux-musl-x64 +zero abi dump --json --target linux-musl-x64 +``` diff --git a/docs/articles/diagnostics.md b/docs/articles/diagnostics.md new file mode 100644 index 0000000..c28dadb --- /dev/null +++ b/docs/articles/diagnostics.md @@ -0,0 +1,107 @@ +## Read The Error As A Repair Contract + +Zerolang diagnostics are written for humans and agents together. A human should see what +went wrong. An agent should see the safest next edit shape. + +Example: + +```text +error[NAM003]: Unknown identifier + unknown identifier 'message' + examples/hello.0:2:27 + + 2 | check world.out.write(message) + | ^^^^^^^ + rule: Names must be declared before use in the current lexical scope. + expected: local binding, parameter, function, builtin value + actual: no visible symbol named 'message' + fix: Introduce a local binding before this use (local-edit) + explain: zero explain NAM003 +``` + +The path may name a `.0` projection because source maps point at readable text. +That does not mean the agent should edit that file. In a graph-first package, +the repair should normally be a graph patch. + +## Agent Repair Flow + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "this fails with NAM003. can you fix it?" + }, + { + "role": "assistant", + "text": "I’ll inspect the diagnostic, patch the missing binding, and rerun the focused check." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero explain NAM003", + "output": "NAM003: unresolved name\nfix: introduce a binding or use an in-scope symbol" + }, + { + "command": "zero query --fn main", + "output": "main\n check world.out.write message" + }, + { + "command": "zero patch /tmp/introduce-message.patch", + "output": "program graph patch ok" + } + ] + } + ] +} +``` + +## Text And JSON Modes + +Use text first: + +```sh +zero check +zero explain NAM003 +``` + +Use JSON when a tool needs exact spans, codes, fix ids, or nested readiness +facts: + +```sh +zero check --json +zero fix --plan --json +zero doctor --json +``` + +Agents should not default to JSON for every command. JSON is for automation, +edit planning, CI, editors, and exact diagnostic fields. + +## Common Graph-First Diagnostics + +`BLD002` means a command expected graph-backed input but received a projection +or another unsupported input. Import the projection or pass the package/graph +store. + +`RGP003` means projection state is invalid, stale, conflicting, or impossible +to verify. Use `zero status`, then choose `zero export`, `zero import`, or +manual review. + +`GRF013` and related graph diagnostics mean the proposed graph edit would leave +an invalid shape, such as a sparse ordered group. + +`CIMP003` and `CIMP005` report unsafe or incomplete C import/link metadata. +Use package-relative headers/libraries or configure target C dependency facts +instead of relying on host paths. + +## Fix Plans + +`zero fix --plan --json` returns a repair plan, not an automatic edit: + +```sh +zero fix --plan --json +``` + +Use it when an agent needs a safer next step. The agent should still explain +the patch it will apply and validate the user-visible behavior afterward. diff --git a/docs/articles/examples.md b/docs/articles/examples.md new file mode 100644 index 0000000..be74e68 --- /dev/null +++ b/docs/articles/examples.md @@ -0,0 +1,112 @@ +## Start With Graph Examples + +The Zerolang examples directory is the fastest way to understand what works today. Most +examples are checked-in `.graph` inputs or graph-first packages with +`zero.graph` stores. Sibling `.0` files, when present, are human-readable +projections for review. + +## Expected Usage + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "make a tiny contacts api like the crm example" + }, + { + "role": "assistant", + "text": "I’ll use the CRM example as the pattern and run the route checks." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query examples/crm-api --find route", + "output": "routes: health, contacts list, contact by id, create, update, delete" + }, + { + "command": "zero patch /tmp/contacts-api.patch", + "output": "program graph patch ok" + }, + { + "command": "zero run -- $'GET /health\\n\\n'", + "output": "HTTP/1.1 200 OK" + } + ] + } + ] +} +``` + +## Learning Order + +Small programs: + +- `examples/hello.graph` +- `examples/add.graph` +- `examples/point.graph` +- `examples/result-choice.graph` +- `examples/fallibility.graph` + +Memory and ownership: + +- `examples/memory-primitives.graph` +- `examples/allocator-collections.graph` +- `examples/ownership-cleanup.graph` +- `examples/fixed-vec.graph` + +CLI and files: + +- `examples/cli-file.graph` +- `examples/cli-config.graph` +- `examples/file-copy.graph` +- `examples/readall-cli/` +- `examples/resource-cli/` + +Data and web: + +- `examples/std-data-formats.graph` +- `examples/json-api-client.graph` +- `examples/json-api-router.graph` +- `examples/crm-api/` +- `examples/std-http-json.graph` +- `examples/std-http-request.graph` + +Compiler and agent workflows: + +- `examples/compile-time-v1.graph` +- `examples/agent-repair-demo/` +- `examples/error-tour/` +- `examples/memory-package/` +- `examples/zero-hash/` + +## Inspect And Run Examples + +```sh +zero query examples/hello.graph +zero check examples/hello.graph +zero run examples/add.graph +zero build --emit exe --target linux-musl-x64 examples/add.graph --out .zero/out/add +``` + +## Build And Size Examples + +```sh +zero build --release tiny --target linux-musl-x64 examples/hello.graph --out .zero/out/hello-linux-musl +zero build --release tiny --target win32-x64.exe examples/hello.graph --out .zero/out/hello-win32 +zero size --json --release tiny --target linux-musl-x64 examples/hello.graph +``` + +Size output reports retained helpers, section sizes, profile facts, target +report data, and artifact size. Use it when an agent needs to explain why bytes +were retained. + +## Native Workflow Coverage + +The examples intentionally cover arguments and environment, filesystem +resources, deterministic exit status, unhandled error exit paths, fixed-capacity +storage, HTTP routing, JSON helpers, target status, and repair workflows. + +Use them as prompts for agents and as smoke checks for humans reviewing a +language change. diff --git a/docs/articles/getting-started.md b/docs/articles/getting-started.md new file mode 100644 index 0000000..1dc00c7 --- /dev/null +++ b/docs/articles/getting-started.md @@ -0,0 +1,120 @@ +## Start With An Agent + +Zerolang is designed for a human working with an agent. + +The agent should author the program through the graph. The human should review +the graph summary, command output, and the `.0` projection when useful. A +projection is readable and bidirectional, but it is not the normal place for an +agent to write code. + +## Install + +```sh +curl -fsSL https://zerolang.ai/install.sh | bash +export PATH="$HOME/.zero/bin:$PATH" +zero --version +``` + +Then install the agent bootstrap skill: + +```sh +npx skills add vercel-labs/zerolang +``` + +Use the installed `zero` command in public examples. If you are developing Zero +itself, follow the repository contributor notes for checkout-local compiler +work. + +## Hello World + +Start by asking: + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "build hello world for zerolang" + }, + { + "role": "assistant", + "text": "I’ll initialize this directory, add main, and run it." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero init", + "output": "graph project init ok\nwrote: ./zero.toml\nwrote: ./zero.graph" + }, + { + "command": "zero patch --op 'addMain' --op 'addCheckWrite fn=\"main\" text=\"hello from zero\\n\"'", + "output": "program graph patch ok" + }, + { + "command": "zero run", + "output": "hello from zero" + } + ] + } + ] +} +``` + +The expected projection is: + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write("hello from zero\n") +} +``` + +That file is a projection of `zero.graph`. Humans can read it, review it, and +occasionally edit it. Agents should normally keep using `zero query` and +`zero patch`. + +## The Daily Loop + +Use this loop for most tasks: + +```sh +zero query +zero patch --op help +zero patch --op 'addMain' +zero check +zero test +zero run -- +``` + +The default input is the current directory, so a package command does not need +`.` unless you want to be explicit. + +## Reviewing A Projection + +When a human wants to see readable text: + +```sh +zero export +zero verify-projection +``` + +When a human intentionally edits `src/main.0`, import the projection back into +the graph before checking or running: + +```sh +zero import +zero check +``` + +Do not use projection export as an automatic agent step. Export when a human +asks to review source-like text or when CI wants a projection drift gate. + +## Build An Artifact + +Use `zero build` for executable, object, or LLVM IR artifacts: + +```sh +zero build --emit exe --target linux-musl-x64 --out .zero/out/app +``` + +For early exploration, `zero run` is usually enough. diff --git a/docs/articles/install.md b/docs/articles/install.md new file mode 100644 index 0000000..8d6c3cc --- /dev/null +++ b/docs/articles/install.md @@ -0,0 +1,57 @@ +## Install The Compiler + +Install Zerolang when you want an agent to build graph-first programs on your +machine. The compiler is experimental. Use it in isolated workspaces and avoid +production data. + +## Install The Latest Release + +```sh +curl -fsSL https://zerolang.ai/install.sh | bash +export PATH="$HOME/.zero/bin:$PATH" +zero --version +``` + +The installer downloads the latest release asset for your platform and checks +the release checksum file before installing the binary. + +## Verify The Environment + +```sh +zero doctor +zero targets +zero skills +``` + +`zero doctor --json` includes host and toolchain readiness. `zero targets --json` +includes `targetToolchains`, target aliases, hosted capability facts, and +cross-target support notes. + +## Load Version-Matched Agent Knowledge + +Agents should not rely on a stale external Zero guide. Ask the installed +compiler for the skills bundled with that exact binary: + +```sh +zero skills +zero skills get agent +zero skills get graph +zero skills get language +zero skills get stdlib +``` + +The thin external Zero skill is only a bootstrap stub. The compiler-bundled +skills are the current command and language reference for that release. + +## Repository Checkout + +When working inside the Zero compiler repository, build the local compiler and +then use the checkout's `zero` binary for experiments: + +```sh +pnpm install +make -C native/zero-c +zero --version +``` + +The repository contributor notes cover checkout-specific wrapper commands. diff --git a/docs/articles/language-reference.md b/docs/articles/language-reference.md new file mode 100644 index 0000000..ccafea6 --- /dev/null +++ b/docs/articles/language-reference.md @@ -0,0 +1,194 @@ +## How Programs Are Shaped + +Zerolang programs are semantic graph declarations with a human-readable `.0` +projection. This page names the language pieces that appear in both views. + +Read **Primitives And Types** first when you want scalar types, `Maybe`, +spans, arrays, ownership, and layout. Use this page for declarations, function +bodies, capabilities, packages, and projection rules. + +## Declarations + +The graph stores declarations for functions, types, enums, constants, imports, +tests, and package modules. Projection syntax makes those declarations readable: + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write("hello\n") +} +``` + +Public declarations should have explicit type information. That makes graph +facts, diagnostics, docs, and repair plans stable. + +## Functions + +```zero +fn add(x: i32, y: i32) -> i32 { + return x + y +} +``` + +Function graph facts include the name, parameters, return type, fallibility, +body block, references, and call edges. Agents should use `zero query --fn add` +before editing a function body or signature. + +Fallible functions use `raises`: + +```zero +fn requirePositive(value: i32) -> i32 raises [Invalid] { + if value > 0 { + return value + } + raise Invalid +} +``` + +`check` propagates failure through explicit control flow. There are no hidden +exceptions. + +## Blocks And Control Flow + +Blocks are graph nodes. Agents can patch a whole function body or a specific +block body: + +```text +replaceFunctionBody main + check world.out.write "hello\n" +end +``` + +```text +replaceBlockBody #block_then_1234 + check world.out.write "ready\n" +end +``` + +Projection syntax: + +```zero +if ready { + check world.out.write("ready\n") +} else { + check world.out.write("not ready\n") +} +``` + +Conditions must be `Bool`. `while` loops and `match` expressions also lower to +explicit graph control-flow nodes. + +## Capabilities + +Zero avoids ambient global runtime access. Programs receive capabilities +explicitly: + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write("ok\n") +} +``` + +Standard library helpers document their effects and target support. Use +`zero inspect --json` and `zero size --json` to see which helpers and +capabilities a graph input actually retains. + +## Packages + +Graph-first packages normally have: + +```text +zero.toml +zero.graph +src/main.0 +``` + +The target `main` path points at the readable projection for source maps and +review. It does not make `src/main.0` the normal package compile input. +Package commands compile from `zero.graph`. + +## Imports And Modules + +Package-local modules resolve from `src/` projection paths so humans have +stable review files: + +- `src/foo.0` defines module `foo` +- `src/foo/mod.0` defines directory module `foo` + +The graph store records the module declarations and relationships. Import +cycles and duplicate public exports are diagnosed before build output. + +## Compile-Time Facts + +Zero exposes a small compile-time metadata surface for target and type facts. +Current compile-time values include integer, `Bool`, and enum static values. +Representative metadata includes `compileTime`, `target.pointerWidth`, +`fieldType`, and `hasEnumCase`. + +Invalid compile-time queries report diagnostics such as `MET001`. The design +goal is explicit graph metadata, not runtime registries or raw token-string +builders. + +## Projection Rules + +Projection syntax is the human-readable view of the graph: + +- export projections for review with `zero export` +- import projections after human edits with `zero import` +- verify drift with `zero verify-projection` +- use graph commands for normal agent authoring + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "change just the ready branch" + }, + { + "role": "assistant", + "text": "I’ll change that branch only and run the behavior it affects." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query --fn main", + "output": "if block\n then #block_then_1234\n else #block_else_5678" + }, + { + "command": "zero patch /tmp/replace-then.patch", + "output": "program graph patch ok" + }, + { + "command": "zero export", + "output": "repository graph export ok\nwrote: ./src/main.0" + } + ] + } + ] +} +``` + +## What Is Not Hidden + +Zero intentionally avoids hidden method registries, vtables, reflection, +ambient heap allocation, and process-global cleanup lists in the current +language model. When a program uses owned resources, allocator state, hosted +I/O, network capability, or C interop, those facts should be visible through +graph inspection and diagnostics. + +## Targets + +The public native target names are: + +- `darwin-arm64` +- `darwin-x64` +- `linux-arm64` +- `linux-musl-arm64` +- `linux-musl-x64` +- `linux-x64` +- `win32-arm64.exe` +- `win32-x64.exe` + +Use `zero targets --json` and `zero check --json --target ` before +asking an agent to rely on target-specific capabilities. diff --git a/docs/articles/learn-zero.md b/docs/articles/learn-zero.md new file mode 100644 index 0000000..d677c2e --- /dev/null +++ b/docs/articles/learn-zero.md @@ -0,0 +1,192 @@ +## The Human Model + +Zerolang has two views of the same program: + +- The graph is the program database. Agents inspect and patch it. +- The `.0` projection is readable text. Humans use it for review and rare + manual edits. + +When this page shows Zero syntax, it is showing projection syntax. The graph +contains the same declarations, types, calls, and edges as structured facts. + +## Expected Usage + +Ask for the behavior in normal language. The Zero skills tell the agent to use +the graph: + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "make a cli that adds two numbers" + }, + { + "role": "assistant", + "text": "I’ll add the function, wire the CLI, and run a sample input." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query --fn main", + "output": "main\n check world.out.write \"hello\\n\"" + }, + { + "command": "zero patch /tmp/add-cli.patch", + "output": "program graph patch ok" + }, + { + "command": "zero run -- 40 2", + "output": "42" + } + ] + } + ] +} +``` + +Under the hood, the agent gathers current compiler knowledge with `zero skills`, +inspects the package with `zero status` or `zero query`, patches the graph, then +runs `zero check`, `zero test`, or `zero run` only when useful for the task. + +## A Minimal Program + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write("hello\n") +} +``` + +Pieces visible in both graph and projection: + +- `pub fn main` declares the entry point. +- `world: World` is an explicit capability parameter. +- `Void` means no useful return value. +- `raises` marks a fallible function. +- `check` propagates a fallible operation. +- `world.out.write(...)` writes through an explicit output capability. + +Run the graph input: + +```sh +zero run examples/hello.graph +``` + +## Values And Bindings + +```zero +let name: String = "Ada" +var count: u32 = 0 +count = count + 1 +``` + +`let` is immutable. `var` is mutable. Public constants and declarations should +carry explicit types because the graph, diagnostics, and docs all benefit from +stable type facts. + +## Functions + +```zero +fn add(x: i32, y: i32) -> i32 { + return x + y +} + +test "add works" { + expect (add(40, 2) == 42) +} +``` + +Agents should usually add this through patch operations such as `addFunction`, +`addParam`, `addReturnBinary`, and `addTest`, or through the row-based body DSL +when replacing a function or block body. + +## Types + +```zero +type Point { + x: i32, + y: i32, +} + +enum Status { + Pending, + Ready, +} +``` + +Types are graph declarations. Projection snippets make them readable for +humans, but tools should inspect declaration nodes and symbol references. + +## Control Flow + +```zero +if ready { + return 1 +} else { + return 0 +} + +while index < count { + index = index + 1 +} +``` + +Conditions must be `Bool`. Branch and loop bodies are blocks in the graph, so +agents can patch a specific block without replacing the entire function. + +## Absence And Errors + +```zero +let value: Maybe = std.args.parseU32(1) +if value.has { + return value.value +} +return 0 +``` + +`Maybe` represents absence. Fallible functions use `raises`; `check` +propagates failure through explicit control flow rather than exceptions. + +## Memory Views + +```zero +let bytes: Span = std.mem.span("hello") +var scratch: [16]u8 = [0_u8; 16] +let copied: usize = std.mem.copy(scratch, bytes) +``` + +`Span` borrows contiguous storage. `[N]T` is fixed storage. Standard library +helpers prefer caller-owned buffers so the graph can expose allocation and +ownership facts. + +## Packages And Projections + +A graph-first package has: + +```text +zero.toml +zero.graph +src/main.0 +``` + +`zero.graph` is the normal compile input. `src/main.0` is the readable +projection named by the package target for source maps and review. + +Use: + +```sh +zero export +zero import +zero verify-projection +``` + +Only use `export` or `import` when a human review or manual edit calls for it. + +## What To Read Next + +- Read **CLI Reference** for command groups and graph patch forms. +- Read **Primitives And Types** for the language pieces behind graph facts. +- Read **Standard Library** before asking an agent for CLI, HTTP, JSON, or + filesystem programs. +- Read **Diagnostics And Repair** when an agent hits a compiler error. diff --git a/docs/articles/modules/args.md b/docs/articles/modules/args.md new file mode 100644 index 0000000..73c85ef --- /dev/null +++ b/docs/articles/modules/args.md @@ -0,0 +1,46 @@ +## When To Use std.args + +In Zerolang, use `std.args` for hosted command-line programs that need positional +arguments, option lookup, or simple numeric argument parsing. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.args.len()` | `usize` | Returns the process argument count. | +| `std.args.get(index)` | `Maybe` | Returns the argument at `index` when present. | +| `std.args.has(index)` | `Bool` | Reports whether `index` has an argument. | +| `std.args.getOr(index, fallback)` | `String` | Returns the argument or a caller-provided fallback. | +| `std.args.find(name)` | `Maybe` | Finds the first exact argument match after the executable path. | +| `std.args.valueAfter(name)` | `Maybe` | Returns the argument immediately after a matched option name. | +| `std.args.parseU32(index)` | `Maybe` | Parses an indexed argument as `u32`. | + +Current limits: + +- Iterator-style argument APIs. +- Target diagnostics for platforms without process arguments. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let count: usize = std.args.len() + let first: String = std.args.getOr(1, "default") + let maybe_count: Maybe = std.args.parseU32(2) + if count > 2 && maybe_count.has { + check world.out.write(first) + check world.out.write("\n") + } +} +``` + +## Design Notes + +The module is hosted-only. Freestanding, edge, and embedded targets should +reject it unless they explicitly provide an argument capability. + +On native Windows-style targets, `std.args` is byte-oriented process input. It +is not a Unicode argv normalization layer. + +Programs that need portable argument semantics should keep target-specific +decoding outside the target-neutral core. diff --git a/docs/articles/modules/ascii.md b/docs/articles/modules/ascii.md new file mode 100644 index 0000000..f45c12b --- /dev/null +++ b/docs/articles/modules/ascii.md @@ -0,0 +1,38 @@ +## When To Use std.ascii + +In Zerolang, use `std.ascii` when a program needs byte-level ASCII predicates, case +conversion, or digit values without Unicode normalization. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.ascii.isDigit(byte)` | `Bool` | Checks `0` through `9`. | +| `std.ascii.isAlpha(byte)` | `Bool` | Checks `A` through `Z` or `a` through `z`. | +| `std.ascii.isAlnum(byte)` | `Bool` | Checks ASCII alphabetic or digit bytes. | +| `std.ascii.isWhitespace(byte)` | `Bool` | Checks space, tab, line feed, and carriage return. | +| `std.ascii.isLower(byte)` / `std.ascii.isUpper(byte)` | `Bool` | Checks ASCII case ranges. | +| `std.ascii.isHexDigit(byte)` | `Bool` | Checks decimal digits and `a-f` / `A-F`. | +| `std.ascii.toLower(byte)` / `std.ascii.toUpper(byte)` | `u8` | Converts ASCII letters and leaves other bytes unchanged. | +| `std.ascii.digitValue(byte)` | `Maybe` | Converts an ASCII decimal digit to `0..9`. | +| `std.ascii.hexValue(byte)` | `Maybe` | Converts an ASCII hexadecimal digit to `0..15`. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let digit: Maybe = std.ascii.digitValue(55_u8) + let hex: Maybe = std.ascii.hexValue(70_u8) + if std.ascii.isAlpha(65_u8) && std.ascii.toLower(90_u8) == 122_u8 && digit.has && digit.value == 7_u8 && hex.has && hex.value == 15_u8 { + check world.out.write("ascii ok\n") + } +} +``` + +Effects: none. + +Allocation behavior: no allocation. + +Error behavior: value helpers return `null` when the byte is outside the accepted range. + +Target support: current compiler targets. diff --git a/docs/articles/modules/cli.md b/docs/articles/modules/cli.md new file mode 100644 index 0000000..4adc6a6 --- /dev/null +++ b/docs/articles/modules/cli.md @@ -0,0 +1,87 @@ +## When To Use std.cli + +In Zerolang, use `std.cli` for hosted command-line flag and option helpers that sit one +level above raw `std.args` access. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.cli.argEquals(index, expected)` | `Bool` | Checks one argument against an exact string. | +| `std.cli.command()` | `Maybe` | Returns argument 1 as the command name. | +| `std.cli.commandOr(fallback)` | `String` | Returns the command name or a fallback. | +| `std.cli.commandEquals(expected)` | `Bool` | Checks argument 1 against an exact command string. | +| `std.cli.argOr(index, fallback)` | `String` | Returns an argument or a fallback. | +| `std.cli.argU32Or(index, fallback)` | `u32` | Parses an argument as `u32` or returns a fallback. | +| `std.cli.hasFlag(name)` | `Bool` | Reports whether an exact flag is present. | +| `std.cli.optionValue(name)` | `Maybe` | Returns the value immediately after an option name. | +| `std.cli.optionValueOr(name, fallback)` | `String` | Returns the option value or a fallback. | +| `std.cli.optionU32(name)` | `Maybe` | Parses an option value as `u32`. | +| `std.cli.successExitCode()` | `i32` | Returns the conventional success exit code. | +| `std.cli.usageExitCode()` | `i32` | Returns the conventional command-line usage error code. | +| `std.cli.isHelp(command)` | `Bool` | Recognizes `help`, `--help`, and `-h`. | +| `std.cli.needsHelp()` | `Bool` | Reports whether the current invocation has no command or asks for help. | +| `std.cli.commandIn2(command, first, second)` | `Bool` | Checks a command against two accepted command names. | +| `std.cli.commandIn3(command, first, second, third)` | `Bool` | Checks a command against three accepted command names. | +| `std.cli.formatUsage(buffer, program, syntax)` | `Maybe>` | Writes `usage: ` into caller storage. | +| `std.cli.formatCommand(buffer, name, syntax, summary)` | `Maybe>` | Writes one indented command help row. | +| `std.cli.formatOption(buffer, name, valueName, summary)` | `Maybe>` | Writes one indented option help row. | +| `std.cli.formatSection(buffer, title)` | `Maybe>` | Writes a section heading such as `Options:\n`. | +| `std.cli.formatHelpRow(buffer, label, summary)` | `Maybe>` | Writes one padded, newline-terminated help row using the default label width. | +| `std.cli.formatHelpRowCustom(buffer, label, summary, indent, width)` | `Maybe>` | Writes one padded help row using caller-supplied indentation and label width. | +| `std.cli.formatHelpRowWithWidth(buffer, label, summary, width)` | `Maybe>` | Writes one padded help row using a caller-supplied label width. | +| `std.cli.formatHelp(buffer, usage, description)` | `Maybe>` | Writes a help header with `Usage:` and an optional description. | +| `std.cli.formatError(buffer, message)` | `Maybe>` | Writes an `error: ...` line. | +| `std.cli.formatUnknownCommand(buffer, command)` | `Maybe>` | Writes a conventional unknown-command error. | +| `std.cli.formatMissingOperand(buffer, operand)` | `Maybe>` | Writes a conventional missing-operand error. | +| `std.cli.formatInvalidOption(buffer, option)` | `Maybe>` | Writes a conventional invalid-option error. | + +Current limits: + +- Table-driven command schemas. +- Bool, signed integer, and `usize` option shortcuts; compose `optionValue` with `std.parse`. +- Process exit from inside `std.cli`; use the returned exit-code constants with host process handling. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + if std.cli.needsHelp() { + var command_storage: [96]u8 = [0_u8; 96] + let commands: Maybe> = std.cli.formatHelpRow(command_storage, "hello [name]", "print a greeting") + if commands.has { + var help_storage: [256]u8 = [0_u8; 256] + let help: Maybe> = std.cli.formatHelp(help_storage, "zero-test [command]", "Small hosted CLI.") + if help.has { + check world.out.write(help.value) + check world.out.write("\nCommands:\n") + check world.out.write(commands.value) + } + } + return + } + let command: String = std.cli.commandOr("") + if std.mem.eql(command, "hello") { + let name: String = std.cli.argOr(2, "world") + check world.out.write("hello ") + check world.out.write(name) + check world.out.write("\n") + return + } + var error_storage: [64]u8 = [0_u8; 64] + let error: Maybe> = std.cli.formatUnknownCommand(error_storage, command) + if error.has { + check world.err.write(error.value) + check world.err.write("\n") + } +} +``` + +## Design Notes + +`std.cli` is a thin, hosted layer over `std.args`. It keeps subcommand, fallback, +typed argument, help row, and usage error patterns regular without hiding +process arguments behind a global parser or allocating command tables. For +custom help layouts, compose `formatHelp`, `formatSection`, `formatHelpRow`, +`formatHelpRowWithWidth`, and `formatHelpRowCustom` rather than relying on a +global formatter state. diff --git a/docs/articles/modules/codec.md b/docs/articles/modules/codec.md new file mode 100644 index 0000000..a2fb452 --- /dev/null +++ b/docs/articles/modules/codec.md @@ -0,0 +1,82 @@ +## When To Use std.codec + +In Zerolang, use `std.codec` for byte encodings: endian integer reads/writes, +varints, base32, base64, hex, and checksums over caller-owned storage. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.codec.crc32(bytes)` | `u32` | Computes CRC-32 for a string-backed byte input. | +| `std.codec.crc32Bytes(bytes)` | `u32` | Computes CRC-32 for a span or mutable span without allocation. | +| `std.codec.encodedVarintLen(value)` | `usize` | Returns the byte length of an unsigned varint encoding. | +| `std.codec.encodedVarintLen64(value)` | `usize` | Returns the byte length of an unsigned 64-bit varint encoding. | +| `std.codec.encodedSignedVarintLen(value)` / `std.codec.encodedSignedVarintLen64(value)` | `usize` | Returns the byte length of a ZigZag signed varint encoding. | +| `std.codec.varintEncode(buffer, value)` | `Maybe>` | Writes an unsigned varint into caller storage. | +| `std.codec.varintDecode(bytes)` | `Maybe` | Decodes a bounded unsigned varint. | +| `std.codec.varintEncode64(buffer, value)` / `std.codec.varintDecode64(bytes)` | `Maybe>` / `Maybe` | Writes and reads bounded unsigned 64-bit varints. | +| `std.codec.signedVarintEncode(buffer, value)` / `std.codec.signedVarintDecode(bytes)` | `Maybe>` / `Maybe` | Writes and reads ZigZag signed 32-bit varints. | +| `std.codec.signedVarintEncode64(buffer, value)` / `std.codec.signedVarintDecode64(bytes)` | `Maybe>` / `Maybe` | Writes and reads ZigZag signed 64-bit varints. | +| `std.codec.readU16Le(bytes)` | `Maybe` | Bounds-checked little-endian read from a byte span. | +| `std.codec.readU16Be(bytes)` | `Maybe` | Bounds-checked big-endian read from a byte span. | +| `std.codec.readU32Le(bytes)` | `Maybe` | Bounds-checked little-endian read from a byte span. | +| `std.codec.readU32Be(bytes)` | `Maybe` | Bounds-checked big-endian read from a byte span. | +| `std.codec.readU64Le(bytes)` | `Maybe` | Bounds-checked little-endian read from a byte span. | +| `std.codec.readU64Be(bytes)` | `Maybe` | Bounds-checked big-endian read from a byte span. | +| `std.codec.writeU16Le(buffer, value)` | `Maybe>` | Writes little-endian bytes into caller storage. | +| `std.codec.writeU16Be(buffer, value)` | `Maybe>` | Writes big-endian bytes into caller storage. | +| `std.codec.writeU32Le(buffer, value)` | `Maybe>` | Writes little-endian bytes into caller storage. | +| `std.codec.writeU32Be(buffer, value)` | `Maybe>` | Writes big-endian bytes into caller storage. | +| `std.codec.writeU64Le(buffer, value)` | `Maybe>` | Writes little-endian `u64` bytes into caller storage. | +| `std.codec.writeU64Be(buffer, value)` | `Maybe>` | Writes big-endian `u64` bytes into caller storage. | +| `std.codec.base32EncodedLen(len)` / `std.codec.base32RawEncodedLen(len)` | `usize` | Returns padded or unpadded base32 encoded length. | +| `std.codec.base32Encode(buffer, bytes)` / `std.codec.base32RawEncode(buffer, bytes)` | `Maybe>` | Writes RFC 4648 base32 text into caller storage. | +| `std.codec.base32DecodedLen(bytes)` / `std.codec.base32RawDecodedLen(bytes)` | `Maybe` | Validates padded or unpadded base32 text and returns decoded length. | +| `std.codec.base32Decode(buffer, bytes)` / `std.codec.base32RawDecode(buffer, bytes)` | `Maybe>` | Writes decoded base32 bytes into caller storage. | +| `std.codec.base64EncodedLen(len)` | `usize` | Returns the encoded length for a base64 payload. | +| `std.codec.base64Encode(buffer, bytes)` | `Maybe` | Writes base64 text into caller storage. | +| `std.codec.base64DecodedLen(bytes)` | `Maybe` | Validates padded base64 text and returns decoded length. | +| `std.codec.base64Decode(buffer, bytes)` | `Maybe>` | Writes decoded base64 bytes into caller storage. | +| `std.codec.base64RawEncodedLen(len)` / `std.codec.base64RawEncode(buffer, bytes)` | `usize` / `Maybe>` | Uses the standard base64 alphabet without padding. | +| `std.codec.base64RawDecodedLen(bytes)` / `std.codec.base64RawDecode(buffer, bytes)` | `Maybe` / `Maybe>` | Validates and decodes unpadded standard base64. | +| `std.codec.base64UrlEncodedLen(len)` / `std.codec.base64UrlEncode(buffer, bytes)` | `usize` / `Maybe>` | Uses the URL-safe base64 alphabet without padding. | +| `std.codec.base64UrlDecodedLen(bytes)` / `std.codec.base64UrlDecode(buffer, bytes)` | `Maybe` / `Maybe>` | Validates and decodes unpadded URL-safe base64. | +| `std.codec.hexEncode(buffer, bytes)` | `Maybe` | Writes lowercase hexadecimal text into caller storage. | +| `std.codec.hexDecodedLen(bytes)` | `Maybe` | Validates hex text and returns decoded length. | +| `std.codec.hexDecode(buffer, bytes)` | `Maybe>` | Writes decoded hex bytes into caller storage. | +| `std.codec.utf8Valid(bytes)` | `Bool` | Validates a byte span as UTF-8. | +| `std.codec.urlEncode(buffer, text)` | `Maybe` | Percent-encodes a string into caller storage. | + +Current limits: + +- Buffer-backed write APIs. +- Streaming encoders and decoders. + +## Example + +```zero +use std.codec + +use std.mem + +pub fn main(world: World) -> Void raises { + let len: usize = std.codec.encodedVarintLen(300) + let checksum: u32 = std.codec.crc32("zero") + let bytes: Span = std.mem.span("zero") + let byte_checksum: u32 = std.codec.crc32Bytes(bytes) + var encoded: [5]u8 = [0_u8; 5] + let varint: Maybe> = std.codec.varintEncode(encoded, 300_u32) + var decoded: [4]u8 = [0_u8; 4] + let text: Maybe> = std.codec.base64Decode(decoded, "emVybw==") + var base32_storage: [8]u8 = [0_u8; 8] + let base32: Maybe> = std.codec.base32Encode(base32_storage, "zero") + if len == 2 && checksum == byte_checksum && varint.has && text.has && std.mem.eql(text.value, "zero") && base32.has && std.mem.eql(base32.value, "PJSXE3Y=") { + check world.out.write("codec primitives ok\n") + } +} +``` + +## Design Notes + +Codec helpers are byte-oriented and allocation-free. Decoders return +`Maybe` on malformed input or insufficient caller storage. diff --git a/docs/articles/modules/collections.md b/docs/articles/modules/collections.md new file mode 100644 index 0000000..9d1e160 --- /dev/null +++ b/docs/articles/modules/collections.md @@ -0,0 +1,256 @@ +## When To Use std.collections + +In Zerolang, use `std.collections` for fixed-capacity collection operations where the caller +owns the storage and growth must be explicit. + +Runnable today: + +The collection helpers operate over caller-owned fixed arrays or `MutSpan` +storage plus an explicit live length. They do not allocate, grow, or retain +hidden state. Generic helpers currently support the same non-owned scalar item +types as the generic `std.mem` item helpers: `Bool`, `u8`, `u16`, `usize`, +`i32`, `u32`, `i64`, and `u64`. + +Use `FixedSet`, `FixedDeque`, `FixedRingBuffer`, or +`FixedMap` when it is clearer to carry storage and live length as one +value. These resource values still borrow caller-owned mutable storage; +inserting, removing, clearing, or truncating values never allocates. + +For byte collections, storage can come from a fixed array or from an explicit +allocator. Use `std.mem.allocBytes(alloc, capacity)` to request a +`MutSpan`, then pass that mutable span to `FixedSet`, +`FixedDeque`, or `FixedMap`. The wrapper does not own the storage; +the allocator-backed span must remain live for as long as the collection value +is used. + +| API | Return | Notes | +| --- | --- | --- | +| `std.collections.push(items, len, value)` | `usize` | Writes `value` at `len` when capacity remains and returns the next length. Returns the unchanged length on overflow. | +| `std.collections.append(items, len, values)` | `usize` | Copies all non-overlapping `values` into `items` at `len` when the full append fits. Returns the unchanged length on overflow or invalid length. | +| `std.collections.clear(items, len)` | `usize` | Returns `0` as the next live length. Storage contents are left unchanged. | +| `std.collections.truncate(items, len, newLen)` | `usize` | Returns the smaller of the current live length and `newLen`, after clamping invalid `len` to storage capacity. Storage contents are left unchanged. | +| `std.collections.first(items, len)` | `Maybe` | Returns the first live item, or `null` when the live prefix is empty. | +| `std.collections.last(items, len)` | `Maybe` | Returns the last live item, or `null` when the live prefix is empty. | +| `std.collections.pop(items, len)` | `usize` | Returns the next live length after removing the last item. Returns `0` when empty. Storage contents are left unchanged. | +| `std.collections.dequePushBack(items, len, value)` | `usize` | Writes `value` at the back when capacity remains and returns the next length. | +| `std.collections.dequePushFront(items, len, value)` | `usize` | Inserts `value` at the front by shifting the live prefix right. Returns the unchanged length when full or invalid. | +| `std.collections.dequeFront(items, len)` | `Maybe` | Returns the first live deque item, or `null` when empty. | +| `std.collections.dequeBack(items, len)` | `Maybe` | Returns the last live deque item, or `null` when empty. | +| `std.collections.dequePopBack(items, len)` | `usize` | Returns the next live length after removing the back item. Storage contents are left unchanged. | +| `std.collections.dequePopFront(items, len)` | `usize` | Removes the front item by shifting the live suffix left and returns the next length. Returns the unchanged length when empty or invalid. | +| `std.collections.fixedDeque(items, len)` | `FixedDeque` | Builds a fixed-capacity deque value over caller-owned mutable storage and clamps the initial live length to capacity. | +| `std.collections.fixedDequeBack(deque)` | `Maybe` | Returns the last live deque item, or `null` when empty. | +| `std.collections.fixedDequeClear(deque)` | `usize` | Clears a `FixedDeque` and returns `0` as the next live length. | +| `std.collections.fixedDequeFront(deque)` | `Maybe` | Returns the first live deque item, or `null` when empty. | +| `std.collections.fixedDequeIsFull(deque)` | `Bool` | Reports whether a `FixedDeque` has no remaining capacity. | +| `std.collections.fixedDequeLen(deque)` | `usize` | Returns the current live length of a `FixedDeque`. | +| `std.collections.fixedDequePopBack(deque)` | `Maybe` | Removes and returns the back item when present. | +| `std.collections.fixedDequePopFront(deque)` | `Maybe` | Removes and returns the front item when present, shifting the live suffix left. | +| `std.collections.fixedDequePushBack(deque, value)` | `Bool` | Appends `value` at the back when capacity remains. Returns whether the live deque changed. | +| `std.collections.fixedDequePushFront(deque, value)` | `Bool` | Inserts `value` at the front when capacity remains. Returns whether the live deque changed. | +| `std.collections.fixedDequeRemaining(deque)` | `usize` | Returns remaining storage capacity for a `FixedDeque`. | +| `std.collections.fixedDequeTruncate(deque, newLen)` | `usize` | Updates a `FixedDeque` live prefix to the smaller of the current live length and `newLen`, clamped to storage. | +| `std.collections.fixedDequeView(deque)` | `Span` | Returns a read-only prefix view over live `FixedDeque` items. | +| `std.collections.fixedRingBuffer(items, head, len)` | `FixedRingBuffer` | Builds a fixed-capacity ring buffer over caller-owned mutable storage, normalizing `head` and clamping `len` to capacity. | +| `std.collections.fixedRingBufferBack(ring)` | `Maybe` | Returns the last live logical item, or `null` when empty. | +| `std.collections.fixedRingBufferCapacity(ring)` | `usize` | Returns the storage capacity. | +| `std.collections.fixedRingBufferClear(ring)` | `usize` | Clears a `FixedRingBuffer`, resets its head to `0`, and returns `0` as the next live length. | +| `std.collections.fixedRingBufferFront(ring)` | `Maybe` | Returns the first live logical item, or `null` when empty. | +| `std.collections.fixedRingBufferGet(ring, index)` | `Maybe` | Reads a logical index through wrap-around storage, or returns `null` outside the live length. | +| `std.collections.fixedRingBufferIsFull(ring)` | `Bool` | Reports whether a `FixedRingBuffer` has no remaining capacity. | +| `std.collections.fixedRingBufferLen(ring)` | `usize` | Returns the current live length. | +| `std.collections.fixedRingBufferPopBack(ring)` | `Maybe` | Removes and returns the back logical item when present. | +| `std.collections.fixedRingBufferPopFront(ring)` | `Maybe` | Removes and returns the front logical item when present, advancing the stored head. | +| `std.collections.fixedRingBufferPushBack(ring, value)` | `Bool` | Appends `value` at the logical back when capacity remains. | +| `std.collections.fixedRingBufferPushFront(ring, value)` | `Bool` | Inserts `value` at the logical front when capacity remains. | +| `std.collections.fixedRingBufferRemaining(ring)` | `usize` | Returns remaining storage capacity. | +| `std.collections.fixedRingBufferTruncate(ring, newLen)` | `usize` | Updates the live logical prefix to the smaller of the current live length and `newLen`, clamped to storage. | +| `std.collections.fill(items, len, value)` | `Bool` | Writes `value` across the live prefix. Returns `false` for invalid length. | +| `std.collections.insertAt(items, len, index, value)` | `usize` | Inserts `value` at `index` by shifting the live suffix right. Returns the unchanged length when full or invalid. | +| `std.collections.replaceAt(items, len, index, value)` | `Bool` | Replaces the live item at `index`. Returns `false` for invalid length or index. | +| `std.collections.swapAt(items, len, left, right)` | `Bool` | Swaps two live items. Returns `false` for invalid length or index. | +| `std.collections.insertUnique(items, len, value)` | `usize` | Treats the live prefix as a fixed-capacity set. Appends `value` only when absent and capacity remains. | +| `std.collections.setClear(items, len)` | `usize` | Returns `0` as the next live set length. Storage contents are left unchanged. | +| `std.collections.setContains(items, len, value)` | `Bool` | Reports whether the live prefix contains `value` as a fixed-capacity set. | +| `std.collections.setInsert(items, len, value)` | `usize` | Appends `value` only when absent and capacity remains. | +| `std.collections.setRemaining(items, len)` | `usize` | Returns remaining fixed-capacity set storage. Returns `0` when `len` is at or past capacity. | +| `std.collections.setIsFull(items, len)` | `Bool` | Reports whether the fixed-capacity set has no remaining storage. | +| `std.collections.setRemove(items, len, value)` | `usize` | Removes the first matching set value with swap-remove and returns the next length. | +| `std.collections.setTruncate(items, len, newLen)` | `usize` | Returns the smaller of the current live set length and `newLen`, after clamping invalid `len` to storage capacity. | +| `std.collections.setView(items, len)` | `Span` | Returns a clamped read-only prefix view over the live fixed-capacity set items. | +| `std.collections.fixedSet(items, len)` | `FixedSet` | Builds a fixed-capacity set value over caller-owned mutable storage and clamps the initial live length to capacity. | +| `std.collections.fixedSetClear(set)` | `usize` | Clears a `FixedSet` and returns `0` as the next live length. | +| `std.collections.fixedSetContains(set, value)` | `Bool` | Reports whether a `FixedSet` contains `value` in its live prefix. | +| `std.collections.fixedSetInsert(set, value)` | `Bool` | Inserts `value` when absent and capacity remains. Returns whether the live set changed. | +| `std.collections.fixedSetIsFull(set)` | `Bool` | Reports whether a `FixedSet` has no remaining capacity. | +| `std.collections.fixedSetLen(set)` | `usize` | Returns the current live length of a `FixedSet`. | +| `std.collections.fixedSetRemaining(set)` | `usize` | Returns remaining storage capacity for a `FixedSet`. | +| `std.collections.fixedSetRemove(set, value)` | `Bool` | Removes `value` with swap-remove when present. Returns whether the live set changed. | +| `std.collections.fixedSetTruncate(set, newLen)` | `usize` | Updates a `FixedSet` live prefix to the smaller of the current live length and `newLen`, clamped to storage. | +| `std.collections.fixedSetView(set)` | `Span` | Returns a read-only prefix view over live `FixedSet` items. | +| `std.collections.fixedMap(keys, values, len)` | `FixedMap` | Builds a fixed-capacity map value over caller-owned mutable key/value storage and clamps the initial live length to the shorter storage. | +| `std.collections.fixedMapClear(map)` | `usize` | Clears a `FixedMap` and returns `0` as the next live length. | +| `std.collections.fixedMapContains(map, key)` | `Bool` | Reports whether a `FixedMap` contains `key` in its live key prefix. | +| `std.collections.fixedMapGet(map, key)` | `Maybe` | Returns the value for `key` when present. | +| `std.collections.fixedMapIndex(map, key)` | `usize` | Searches live keys and returns the matching index, or the live length when absent. | +| `std.collections.fixedMapIsFull(map)` | `Bool` | Reports whether a `FixedMap` has no remaining capacity in the shorter storage. | +| `std.collections.fixedMapKeys(map)` | `Span` | Returns a read-only prefix view over live `FixedMap` keys. | +| `std.collections.fixedMapLen(map)` | `usize` | Returns the current live length of a `FixedMap`. | +| `std.collections.fixedMapPut(map, key, value)` | `Bool` | Updates an existing key's value or appends a key/value pair when capacity remains. Returns whether the key exists afterward. | +| `std.collections.fixedMapRemaining(map)` | `usize` | Returns remaining storage capacity in the shorter key/value storage. | +| `std.collections.fixedMapRemove(map, key)` | `Bool` | Removes a key/value pair with swap-remove when present. Returns whether the live map changed. | +| `std.collections.fixedMapTruncate(map, newLen)` | `usize` | Updates the live prefix to the smaller of the current live length and `newLen`, clamped to storage. | +| `std.collections.fixedMapValues(map)` | `Span` | Returns a read-only prefix view over live `FixedMap` values. | +| `std.collections.mapClear(keys, values, len)` | `usize` | Returns `0` as the next live map length. Key/value storage contents are left unchanged. | +| `std.collections.mapContains(keys, len, key)` | `Bool` | Reports whether the live key prefix contains `key`. | +| `std.collections.mapIndex(keys, len, key)` | `usize` | Searches the live key prefix and returns the matching index. Returns the live length when absent. | +| `std.collections.mapIsFull(keys, values, len)` | `Bool` | Reports whether the shorter of key/value storage has no remaining capacity. | +| `std.collections.mapKeys(keys, len)` | `Span` | Returns a clamped read-only prefix view over the live map keys. | +| `std.collections.mapGet(keys, values, len, key)` | `Maybe` | Treats parallel key/value storage as a fixed-capacity map and returns the value for `key` when present. | +| `std.collections.mapPut(keys, values, len, key, value)` | `usize` | Updates an existing key's value or appends a key/value pair when capacity remains. Returns the unchanged length on overflow or invalid length. | +| `std.collections.mapRemaining(keys, values, len)` | `usize` | Returns remaining capacity in the shorter of key/value storage. | +| `std.collections.mapRemove(keys, values, len, key)` | `usize` | Removes the first matching key/value pair with swap-remove and returns the next length. Returns the unchanged length when absent or invalid. | +| `std.collections.mapTruncate(keys, values, len, newLen)` | `usize` | Returns the smaller of the live map length and `newLen`, clamped to key and value storage. | +| `std.collections.mapValues(keys, values, len)` | `Span` | Returns a clamped read-only prefix view over live map values, bounded by both key and value storage. | +| `std.collections.view(items, len)` | `Span` | Returns a clamped read-only prefix view over the live collection items. | +| `std.collections.remaining(items, len)` | `usize` | Returns remaining capacity after the live prefix. Returns `0` when `len` is at or past capacity. | +| `std.collections.isFull(items, len)` | `Bool` | Reports whether no capacity remains. Invalid lengths at or past capacity count as full. | +| `std.collections.contains(items, len, needle)` | `Bool` | Searches only the live prefix for `needle`. | +| `std.collections.count(items, len, needle)` | `usize` | Counts matching values in the live prefix. | +| `std.collections.removeAt(items, len, index)` | `usize` | Removes `index` by shifting the live suffix left and returns the next length. Returns the unchanged length for invalid length or index. | +| `std.collections.removeValue(items, len, value)` | `usize` | Removes the first matching live value with swap-remove and returns the next length. Returns the unchanged length when absent. | +| `std.collections.removeSwap(items, len, index)` | `usize` | Replaces `index` with the last live item and returns the next length. Returns the unchanged length for invalid length or index. | +| `std.collections.reverse(items, len)` | `Bool` | Reverses the live prefix in place. Returns `false` for invalid length. | +| `std.collections.rotateLeft(items, len, count)` | `Bool` | Rotates the live prefix left by `count`. Returns `false` for invalid length. | +| `std.collections.rotateRight(items, len, count)` | `Bool` | Rotates the live prefix right by `count`. Returns `false` for invalid length. | +| `std.collections.moveToFront(items, len, index)` | `usize` | Moves the item at `index` to the front by shifting the live prefix. Returns the unchanged length for invalid length or index. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var values: [5]i32 = [0, 0, 0, 0, 0] + let extra: [2]i32 = [4, 1] + var len: usize = 0 + len = std.collections.push(values, len, 3) + len = std.collections.push(values, len, 1) + len = std.collections.setInsert(values, len, 3) + len = std.collections.setInsert(values, len, 2) + let has_three: Bool = std.collections.setContains(values, len, 3) + let set_live: Span = std.collections.setView(values, len) + let set_remaining: usize = std.collections.setRemaining(values, len) + len = std.collections.setRemove(values, len, 2) + let set_truncated: usize = std.collections.setTruncate(values, len, 1) + var fixed_storage: [4]i32 = [1, 2, 0, 0] + var fixed_set: FixedSet = std.collections.fixedSet(fixed_storage, 2_usize) + let fixed_inserted: Bool = std.collections.fixedSetInsert(&mut fixed_set, 3) + let fixed_removed: Bool = std.collections.fixedSetRemove(&mut fixed_set, 1) + let fixed_live: Span = std.collections.fixedSetView(&fixed_set) + let fixed_remaining: usize = std.collections.fixedSetRemaining(&fixed_set) + let fixed_len: usize = std.collections.fixedSetLen(&fixed_set) + let fixed_truncated: usize = std.collections.fixedSetTruncate(&mut fixed_set, 1_usize) + var fixed_keys: [3]u8 = [1_u8, 2_u8, 0_u8] + var fixed_scores: [3]u16 = [10_u16, 20_u16, 0_u16] + var fixed_map: FixedMap = std.collections.fixedMap(fixed_keys, fixed_scores, 2_usize) + let fixed_map_added: Bool = std.collections.fixedMapPut(&mut fixed_map, 3_u8, 30_u16) + let fixed_map_updated: Bool = std.collections.fixedMapPut(&mut fixed_map, 2_u8, 25_u16) + let fixed_map_score: Maybe = std.collections.fixedMapGet(&fixed_map, 2_u8) + let fixed_map_keys: Span = std.collections.fixedMapKeys(&fixed_map) + let fixed_map_values: Span = std.collections.fixedMapValues(&fixed_map) + let fixed_map_index: usize = std.collections.fixedMapIndex(&fixed_map, 2_u8) + let fixed_map_full: Bool = std.collections.fixedMapIsFull(&fixed_map) + let fixed_map_removed: Bool = std.collections.fixedMapRemove(&mut fixed_map, 1_u8) + let inserted_len: usize = std.collections.insertAt(values, len, 1_usize, 2) + let replaced: Bool = std.collections.replaceAt(values, inserted_len, 1_usize, 6) + let swapped: Bool = std.collections.swapAt(values, inserted_len, 0_usize, 1_usize) + len = std.collections.removeAt(values, inserted_len, 1_usize) + let first: Maybe = std.collections.first(values, len) + let last: Maybe = std.collections.last(values, len) + let popped_len: usize = std.collections.pop(values, len) + len = std.collections.truncate(values, len, 2) + len = std.collections.append(values, len, extra) + let live: Span = std.collections.view(values, len) + var deque_values: [4]i32 = [0, 0, 0, 0] + var deque_len: usize = 0 + deque_len = std.collections.dequePushBack(deque_values, deque_len, 2) + deque_len = std.collections.dequePushFront(deque_values, deque_len, 1) + deque_len = std.collections.dequePushBack(deque_values, deque_len, 3) + let deque_front: Maybe = std.collections.dequeFront(deque_values, deque_len) + let deque_back: Maybe = std.collections.dequeBack(deque_values, deque_len) + let deque_after_pop_back: usize = std.collections.dequePopBack(deque_values, deque_len) + deque_len = std.collections.dequePopFront(deque_values, deque_after_pop_back) + var fixed_deque_values: [4]i32 = [0, 0, 0, 0] + var fixed_deque: FixedDeque = std.collections.fixedDeque(fixed_deque_values, 0_usize) + let fixed_deque_pushed: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 2) + let fixed_deque_front_pushed: Bool = std.collections.fixedDequePushFront(&mut fixed_deque, 1) + let fixed_deque_back: Maybe = std.collections.fixedDequeBack(&fixed_deque) + let fixed_deque_front: Maybe = std.collections.fixedDequeFront(&fixed_deque) + let fixed_deque_live: Span = std.collections.fixedDequeView(&fixed_deque) + let fixed_deque_removed: Maybe = std.collections.fixedDequePopFront(&mut fixed_deque) + var fixed_ring_values: [4]i32 = [0, 0, 0, 0] + var fixed_ring: FixedRingBuffer = std.collections.fixedRingBuffer(fixed_ring_values, 0_usize, 0_usize) + let fixed_ring_back_pushed: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 2) + let fixed_ring_front_pushed: Bool = std.collections.fixedRingBufferPushFront(&mut fixed_ring, 1) + let fixed_ring_middle: Maybe = std.collections.fixedRingBufferGet(&fixed_ring, 1_usize) + let fixed_ring_front: Maybe = std.collections.fixedRingBufferPopFront(&mut fixed_ring) + let fixed_ring_wrapped: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 3) + var transform: [4]i32 = [1, 2, 3, 4] + let reversed: Bool = std.collections.reverse(transform, 4_usize) + let filled: Bool = std.collections.fill(transform, 2_usize, 9) + let rotated_left: Bool = std.collections.rotateLeft(transform, 4_usize, 1_usize) + let rotated_right: Bool = std.collections.rotateRight(transform, 4_usize, 2_usize) + var keys: [3]u8 = [1_u8, 2_u8, 3_u8] + var scores: [3]u16 = [10_u16, 20_u16, 30_u16] + var map_len: usize = 2 + map_len = std.collections.mapPut(keys, scores, map_len, 3_u8, 30_u16) + map_len = std.collections.mapPut(keys, scores, map_len, 2_u8, 25_u16) + let has_score: Bool = std.collections.mapContains(keys, map_len, 2_u8) + let score: Maybe = std.collections.mapGet(keys, scores, map_len, 2_u8) + let live_keys: Span = std.collections.mapKeys(keys, map_len) + let live_scores: Span = std.collections.mapValues(keys, scores, map_len) + let map_remaining: usize = std.collections.mapRemaining(keys, scores, map_len) + let map_full: Bool = std.collections.mapIsFull(keys, scores, map_len) + map_len = std.collections.mapRemove(keys, scores, map_len, 2_u8) + let removed_index: usize = std.collections.mapIndex(keys, map_len, 2_u8) + if len == 4 && inserted_len == 3 && replaced && swapped && std.collections.clear(values, len) == 0 && std.collections.setClear(values, len) == 0 && first.has && first.value == 6 && last.has && last.value == 1 && popped_len == 1 && std.collections.remaining(values, len) == 1 && !std.collections.isFull(values, len) && has_three && std.mem.len(set_live) == 3 && set_remaining == 2 && set_truncated == 1 && fixed_inserted && fixed_removed && std.mem.len(fixed_live) == 2 && fixed_remaining == 2 && fixed_len == 2 && fixed_truncated == 1 && std.collections.fixedSetClear(&mut fixed_set) == 0 && fixed_map_added && fixed_map_updated && fixed_map_score.has && fixed_map_score.value == 25_u16 && std.mem.len(fixed_map_keys) == 3 && std.mem.len(fixed_map_values) == 3 && fixed_map_index == 1 && fixed_map_full && fixed_map_removed && std.collections.fixedMapClear(&mut fixed_map) == 0 && std.collections.contains(values, len, 4) && std.collections.count(values, len, 1) == 2 && std.mem.len(live) == 4 && deque_front.has && deque_front.value == 1 && deque_back.has && deque_back.value == 3 && deque_after_pop_back == 2 && deque_len == 1 && deque_values[0] == 2 && fixed_deque_pushed && fixed_deque_front_pushed && fixed_deque_back.has && fixed_deque_back.value == 2 && fixed_deque_front.has && fixed_deque_front.value == 1 && std.mem.len(fixed_deque_live) == 2 && fixed_deque_removed.has && fixed_deque_removed.value == 1 && fixed_ring_back_pushed && fixed_ring_front_pushed && fixed_ring_middle.has && fixed_ring_middle.value == 2 && fixed_ring_front.has && fixed_ring_front.value == 1 && fixed_ring_wrapped && reversed && filled && rotated_left && rotated_right && transform[0] == 1 && transform[1] == 9 && transform[2] == 9 && transform[3] == 2 && has_score && score.has && score.value == 25_u16 && std.mem.len(live_keys) == 3 && std.mem.len(live_scores) == 3 && map_remaining == 0 && map_full && std.collections.mapClear(keys, scores, map_len) == 0 && std.collections.mapTruncate(keys, scores, map_len, 2_usize) == 2 && map_len == 2 && removed_index == 2 { + check world.out.write("collections ok\n") + } +} +``` + +## Allocator-Backed Storage + +```zero +pub fn main(world: World) -> Void raises { + var key_storage: [4]u8 = [0_u8; 4] + var value_storage: [4]u8 = [0_u8; 4] + var key_alloc: FixedBufAlloc = std.mem.fixedBufAlloc(key_storage) + var value_alloc: FixedBufAlloc = std.mem.fixedBufAlloc(value_storage) + let keys_maybe: Maybe> = std.mem.allocBytes(key_alloc, 4_usize) + let values_maybe: Maybe> = std.mem.allocBytes(value_alloc, 4_usize) + if keys_maybe.has && values_maybe.has { + var map: FixedMap = std.collections.fixedMap(keys_maybe.value, values_maybe.value, 0_usize) + let stored: Bool = std.collections.fixedMapPut(&mut map, 7_u8, 42_u8) + let value: Maybe = std.collections.fixedMapGet(&map, 7_u8) + if stored && value.has && value.value == 42_u8 { + check world.out.write("allocator-backed map ok\n") + } + } +} +``` + +Effects: writes to caller-provided mutable storage. + +Allocation behavior: no allocation. + +Error behavior: capacity and index failures are value-level. Helpers return the +unchanged length instead of growing or raising. + +`append` rejects source spans that the checker can prove overlap the destination +storage. Use separate storage when copying a live prefix back into the same +collection. + +Ownership: helpers reject owned item elements; move or transfer owned values +explicitly. + +Target support: current compiler targets. diff --git a/docs/articles/modules/crypto.md b/docs/articles/modules/crypto.md new file mode 100644 index 0000000..bdc9c6b --- /dev/null +++ b/docs/articles/modules/crypto.md @@ -0,0 +1,58 @@ +## When To Use std.crypto + +In Zerolang, use `std.crypto` for small hashes, SHA-256 digests, keyed hashes, +constant-time equality, and target entropy helpers with explicit capability +boundaries. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.crypto.hash32(bytes)` | `u32` | Computes the current 32-bit hash helper over bytes. | +| `std.crypto.hmac32(key, bytes)` | `u32` | Computes the current keyed 32-bit helper over bytes. | +| `std.crypto.constantTimeEql(a, b)` | `Bool` | Compares byte spans without data-dependent early exit. | +| `std.crypto.secureRandomU32()` | `u32` | Reads target entropy where the target provides it. | +| `std.crypto.fixedHex32(buffer, value)` | `Maybe>` | Writes an 8-byte lowercase hex value into caller storage. | +| `std.crypto.hashHex32(buffer, bytes)` | `Maybe>` | Writes the 32-bit hash as fixed-width lowercase hex. | +| `std.crypto.hmacHex32(buffer, key, bytes)` | `Maybe>` | Writes the keyed 32-bit helper as fixed-width lowercase hex. | +| `std.crypto.stableId32(buffer, bytes)` | `Maybe>` | Writes a deterministic 8-byte ID from input bytes. | +| `std.crypto.randomId32(buffer)` | `Maybe>` | Writes an 8-byte random ID from target entropy. | +| `std.crypto.sha256(buffer, bytes)` | `Maybe>` | Writes the 32-byte SHA-256 digest into caller storage. | +| `std.crypto.sha256Hex(buffer, bytes)` | `Maybe>` | Writes the SHA-256 digest as 64 lowercase hex bytes. | +| `std.crypto.hmacSha256(buffer, key, bytes)` | `Maybe>` | Writes the 32-byte HMAC-SHA256 digest into caller storage. | +| `std.crypto.hmacSha256Hex(buffer, key, bytes)` | `Maybe>` | Writes the HMAC-SHA256 digest as 64 lowercase hex bytes. | + +Metadata labels: + +- effects: codec, memory, or rand +- allocation behavior: no allocation; text helpers write caller-provided buffers +- target support: hash helpers are target-neutral; secure random requires a rand-capable target +- error behavior: caller-buffer helpers return `null` when storage is too small +- ownership notes: borrows caller-provided byte spans +- example: `examples/std-platform.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let hash: u32 = std.crypto.hash32(std.mem.span("message")) + let hmac: u32 = std.crypto.hmac32(std.mem.span("key"), std.mem.span("message")) + var id_buf: [8]u8 = [0_u8; 8] + var sha_buf: [64]u8 = [0_u8; 64] + var hmac_buf: [64]u8 = [0_u8; 64] + let id: Maybe> = std.crypto.stableId32(id_buf, std.mem.span("message")) + let sha: Maybe> = std.crypto.sha256Hex(sha_buf, std.mem.span("abc")) + let hmac_sha: Maybe> = std.crypto.hmacSha256Hex(hmac_buf, std.mem.span("key"), std.mem.span("message")) + if hash > 0 && hmac > 0 && id.has && sha.has && hmac_sha.has && std.crypto.constantTimeEql(std.mem.span("same"), std.mem.span("same")) { + check world.out.write("crypto ok\n") + } +} +``` + +## Design Notes + +`std.crypto` is a small helper surface. The SHA-256 and HMAC-SHA256 helpers +cover common digest, keyed digest, and fixture needs without allocation. The +fixed-width ID helpers are useful for deterministic labels, cache keys, +fixtures, and examples. The module is not a TLS stack, certificate store, +password hashing API, or secret-management API. diff --git a/docs/articles/modules/csv.md b/docs/articles/modules/csv.md new file mode 100644 index 0000000..a790245 --- /dev/null +++ b/docs/articles/modules/csv.md @@ -0,0 +1,53 @@ +## When To Use std.csv + +In Zerolang, use `std.csv` for allocation-free CSV validation, record scanning, +field decoding, and small fixed-arity CSV writers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.csv.valid(bytes)` | `Bool` | Validates bounded CSV input with quoted fields and CRLF or LF records. | +| `std.csv.recordCount(bytes)` | `Maybe` | Counts valid records, returning null on malformed input. | +| `std.csv.record(bytes, index)` | `Maybe>` | Borrows one record slice by ordinal, excluding the line terminator. | +| `std.csv.fieldCount(record)` | `Maybe` | Counts fields in one valid record. | +| `std.csv.field(buffer, record, index)` | `Maybe>` | Decodes one field into caller storage. | +| `std.csv.encodedFieldLen(field)` | `usize` | Computes the bytes needed to write one CSV field. | +| `std.csv.writeField(buffer, field)` | `Maybe>` | Writes one CSV field with quotes when required. | +| `std.csv.writeRecord2(buffer, left, right)` | `Maybe>` | Writes a two-field record ending in `\n`. | +| `std.csv.writeRecord3(buffer, first, second, third)` | `Maybe>` | Writes a three-field record ending in `\n`. | + +Metadata labels: + +- effects: parse +- allocation behavior: no allocation; decoded fields and writer output use caller storage +- target support: target-neutral +- error behavior: `Maybe` helpers return null on malformed input or insufficient storage +- ownership notes: records borrow from the input; fields and writer output borrow from caller buffers +- examples: `conformance/native/pass/std-csv.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let input: Span = "name,quote\nAda,\"a,b\"\n" + let record: Maybe> = std.csv.record(input, 1_usize) + var field_buf: [16]u8 = [0_u8; 16] + var quote: Maybe> = null + if record.has { + quote = std.csv.field(field_buf, record.value, 1_usize) + } + if std.csv.valid(input) && quote.has && std.mem.eql(quote.value, "a,b") { + check world.out.write("csv ok\n") + } +} +``` + +## Design Notes + +CSV helpers follow the common RFC 4180 field rules: comma-separated fields, +quoted fields, doubled quotes inside quoted fields, and CRLF or LF record +separators. Quoted fields may contain newlines. + +The writer surface is fixed-arity. Use `writeField` when a custom writer loop +is needed. diff --git a/docs/articles/modules/diag.md b/docs/articles/modules/diag.md new file mode 100644 index 0000000..f6ca0ed --- /dev/null +++ b/docs/articles/modules/diag.md @@ -0,0 +1,47 @@ +## When To Use std.diag + +In Zerolang, use `std.diag` to turn byte offsets into source locations and +small diagnostic snippets without allocating. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.diag.line(bytes, offset)` | `usize` | Returns the 1-based line for a byte offset, clamping offsets past the end. | +| `std.diag.column(bytes, offset)` | `usize` | Returns the 1-based byte column for a byte offset. | +| `std.diag.lineStart(bytes, offset)` | `usize` | Returns the byte index where the containing line starts. | +| `std.diag.lineEnd(bytes, offset)` | `usize` | Returns the byte index where the containing line ends, trimming a trailing CR before LF. | +| `std.diag.lineText(bytes, offset)` | `Span` | Borrows the containing line without its newline. | +| `std.diag.rangeLen(bytes, start, end)` | `usize` | Returns the clamped byte length for a half-open range. | +| `std.diag.rangeText(bytes, start, end)` | `Span` | Borrows the clamped half-open byte range. | +| `std.diag.formatLocation(buffer, path, line, column)` | `Maybe>` | Writes `path:line:column` into caller storage. | +| `std.diag.formatOffsetLocation(buffer, path, bytes, offset)` | `Maybe>` | Computes line and column from an offset, then writes `path:line:column`. | + +Metadata labels: + +- effects: parse for source offset scanning; memory for caller-buffer formatting +- allocation behavior: no allocation, except formatting writes into caller storage +- target support: target-neutral +- error behavior: formatting returns `null` when the buffer is too small +- ownership notes: text helpers return borrowed views into the input bytes +- example: `conformance/native/pass/std-diag.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let source: Span = "one\ntwo\nthree" + var storage: [32]u8 = [0_u8; 32] + let location: Maybe> = std.diag.formatOffsetLocation(storage, "input.0", source, 5) + if location.has && std.mem.eql(location.value, "input.0:2:2") && std.mem.eql(std.diag.lineText(source, 5), "two") { + check world.out.write("diag ok\n") + } +} +``` + +## Design Notes + +Offsets are byte offsets, not Unicode scalar indexes or terminal display +columns. That keeps parser diagnostics deterministic and cheap across targets. +Line and column numbers are 1-based for user-facing output. Range helpers use +half-open byte ranges and clamp both ends to the input length. diff --git a/docs/articles/modules/env.md b/docs/articles/modules/env.md new file mode 100644 index 0000000..9e41969 --- /dev/null +++ b/docs/articles/modules/env.md @@ -0,0 +1,52 @@ +## When To Use std.env + +In Zerolang, use `std.env` for hosted environment variable lookup and simple typed +configuration values. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.env.get(name)` | `Maybe` | Returns a hosted process environment value when present. | +| `std.env.has(name)` | `Bool` | Reports whether a hosted environment variable exists. | +| `std.env.getOr(name, fallback)` | `String` | Returns the environment value or a caller-provided fallback. | +| `std.env.equals(name, expected)` | `Bool` | Compares an environment value with expected text without exposing a missing value as success. | +| `std.env.parseBool(name)` | `Maybe` | Parses an environment value as `Bool`. | +| `std.env.parseBoolOr(name, fallback)` | `Bool` | Parses a boolean environment value or returns a caller-provided fallback. | +| `std.env.parseI32(name)` | `Maybe` | Parses an environment value as `i32`. | +| `std.env.parseI32Or(name, fallback)` | `i32` | Parses an `i32` environment value or returns a caller-provided fallback. | +| `std.env.parseU32(name)` | `Maybe` | Parses an environment value as `u32`. | +| `std.env.parseU32Or(name, fallback)` | `u32` | Parses a `u32` environment value or returns a caller-provided fallback. | +| `std.env.parseUsize(name)` | `Maybe` | Parses an environment value as `usize`. | +| `std.env.parseUsizeOr(name, fallback)` | `usize` | Parses a `usize` environment value or returns a caller-provided fallback. | + +Current limits: + +- Dotenv/source composition. +- Secret redaction metadata. +- Rich diagnostics for missing keys, invalid values, and source precedence. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let mode: String = std.env.getOr("ZERO_MODE", "default") + let verbose: Bool = std.env.parseBoolOr("ZERO_VERBOSE", false) + let delta: i32 = std.env.parseI32Or("ZERO_DELTA", 0) + let limit: u32 = std.env.parseU32Or("ZERO_LIMIT", 10) + let workers: usize = std.env.parseUsizeOr("ZERO_WORKERS", 1) + if std.env.equals("ZERO_MODE", "debug") && verbose && delta >= 0 && limit > 0_u32 && workers > 0 { + check world.out.write(mode) + check world.out.write("\n") + } else { + check world.out.write("default\n") + } +} +``` + +## Design Notes + +Environment access is a hosted capability. Non-host targets reject `std.env` +unless they explicitly provide an environment capability. + +Diagnostics name the selected target context. diff --git a/docs/articles/modules/fmt.md b/docs/articles/modules/fmt.md new file mode 100644 index 0000000..2a4c694 --- /dev/null +++ b/docs/articles/modules/fmt.md @@ -0,0 +1,62 @@ +## When To Use std.fmt + +In Zerolang, use `std.fmt` when a program needs to format booleans or integers into a +caller-owned buffer instead of allocating text. + +Runnable today: + +Formatting helpers write into caller-provided `MutSpan` storage and return a +borrowed prefix on success. + +| API | Return | Notes | +| --- | --- | --- | +| `std.fmt.bool(buffer, value)` | `Maybe>` | Writes `true` or `false`. | +| `std.fmt.u32(buffer, value)` | `Maybe>` | Writes decimal unsigned 32-bit text. | +| `std.fmt.u32Base(buffer, value, base)` | `Maybe>` | Writes unsigned 32-bit text in base 2 through 36. | +| `std.fmt.u64(buffer, value)` | `Maybe>` | Writes decimal unsigned 64-bit text. | +| `std.fmt.u64Base(buffer, value, base)` | `Maybe>` | Writes unsigned 64-bit text in base 2 through 36. | +| `std.fmt.usize(buffer, value)` | `Maybe>` | Writes decimal `usize` text. | +| `std.fmt.usizeBase(buffer, value, base)` | `Maybe>` | Writes `usize` text in base 2 through 36. | +| `std.fmt.i32(buffer, value)` | `Maybe>` | Writes decimal signed 32-bit text, including the minimum value. | +| `std.fmt.i32Base(buffer, value, base)` | `Maybe>` | Writes signed 32-bit text in base 2 through 36. | +| `std.fmt.i32Sign(buffer, value)` | `Maybe>` | Writes decimal signed 32-bit text with an explicit `+` for non-negative values. | +| `std.fmt.i64(buffer, value)` | `Maybe>` | Writes decimal signed 64-bit text, including the minimum value. | +| `std.fmt.i64Base(buffer, value, base)` | `Maybe>` | Writes signed 64-bit text in base 2 through 36. | +| `std.fmt.i64Sign(buffer, value)` | `Maybe>` | Writes decimal signed 64-bit text with an explicit `+` for non-negative values. | +| `std.fmt.hexLowerU32(buffer, value)` | `Maybe>` | Writes lowercase hexadecimal without a prefix. | +| `std.fmt.padLeft(buffer, text, width, pad)` | `Maybe>` | Left-pads `text` with `pad` until `width`, or copies `text` when already wide enough. | +| `std.fmt.padRight(buffer, text, width, pad)` | `Maybe>` | Right-pads `text` with `pad` until `width`, or copies `text` when already wide enough. | +| `std.fmt.writeSpan(writer, text)` | `Bool` | Writes bytes into a `FixedWriter`. | +| `std.fmt.writeBool(writer, value)` | `Bool` | Formats a boolean into a `FixedWriter`. | +| `std.fmt.writeU32(writer, value)` / `std.fmt.writeU64(writer, value)` / `std.fmt.writeUsize(writer, value)` | `Bool` | Formats unsigned decimal text into a `FixedWriter`. | +| `std.fmt.writeI32(writer, value)` / `std.fmt.writeI64(writer, value)` | `Bool` | Formats signed decimal text into a `FixedWriter`. | +| `std.fmt.writeI32Sign(writer, value)` / `std.fmt.writeI64Sign(writer, value)` | `Bool` | Formats signed decimal text with an explicit `+` for non-negative values into a `FixedWriter`. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var number_buf: [12]u8 = [0_u8; 12] + var big_buf: [20]u8 = [0_u8; 20] + var hex_buf: [8]u8 = [0_u8; 8] + var padded_buf: [6]u8 = [0_u8; 6] + let number: Maybe> = std.fmt.i32(number_buf, -42) + let big: Maybe> = std.fmt.u64(big_buf, 18446744073709551615_u64) + let hex: Maybe> = std.fmt.hexLowerU32(hex_buf, 48879_u32) + let padded: Maybe> = std.fmt.padLeft(padded_buf, "42", 5, 48_u8) + var writer_storage: [24]u8 = [0_u8; 24] + var writer: FixedWriter = std.io.fixedWriter(writer_storage, 0) + let wrote: Bool = std.fmt.writeSpan(&mut writer, "n=") && std.fmt.writeI32(&mut writer, -42) + if number.has && big.has && hex.has && padded.has && wrote && std.mem.eql(number.value, "-42") && std.mem.eql(big.value, "18446744073709551615") && std.mem.eql(hex.value, "beef") && std.mem.eql(padded.value, "00042") && std.mem.eql(std.io.fixedWriterView(&writer), "n=-42") { + check world.out.write("fmt ok\n") + } +} +``` + +Effects: writes to caller-provided mutable storage or an explicit fixed writer. + +Allocation behavior: no allocation. + +Error behavior: returns `null` when the buffer is too small. + +Target support: current compiler targets. diff --git a/docs/articles/modules/fs.md b/docs/articles/modules/fs.md new file mode 100644 index 0000000..71b6881 --- /dev/null +++ b/docs/articles/modules/fs.md @@ -0,0 +1,82 @@ +## When To Use std.fs + +In Zerolang, use `std.fs` for hosted file reads, writes, existence checks, copies, renames, +and explicit file-resource cleanup. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.fs.read(path, buf)` | `usize` | Reads bytes from a hosted path into a caller-provided `MutSpan` buffer. | +| `std.fs.write(path, bytes)` | `usize` | Writes bytes to a hosted path and returns the byte count. | +| `std.fs.host()` | `Fs` | Creates the hosted filesystem capability. | +| `std.fs.open(fs, path)` | `Maybe>` | Opens a file and returns `null` when unavailable. | +| `std.fs.openOrRaise(fs, path)` | `owned` | Opens a file or raises `raises [NotFound, TooLarge, Io]`. | +| `std.fs.create(fs, path)` | `Maybe>` | Creates a file and returns `null` when unavailable. | +| `std.fs.createOrRaise(fs, path)` | `owned` | Creates a file or raises `raises [NotFound, TooLarge, Io]`. | +| `std.fs.readOrRaise(&mut file, buf)` | `usize` | Reads into caller storage or raises. | +| `std.fs.writeAll(&mut file, bytes)` | `Bool` | Writes bytes to an owned file handle. | +| `std.fs.writeAllOrRaise(&mut file, bytes)` | `Void` | Writes all bytes or raises. | +| `std.fs.fileLen(&mut file)` | `Maybe` | Reports file length when available. | +| `std.fs.fileLenOrRaise(&mut file)` | `usize` | Reports the file length or raises. | +| `std.fs.fileSize(fs, path)` | `Maybe` | Opens a hosted path through `fs` and reports file length when available. | +| `std.fs.readAll(alloc, fs, path, limit)` | `Maybe>` | Reads through an explicit allocator and size limit. | +| `std.fs.readAllOrRaise(alloc, fs, path, limit)` | `owned` | Reads through an explicit allocator and size limit. | +| `std.fs.readBytes(path, buf)` | `Maybe` | Fills caller storage and returns the total file size; a value above `len(buf)` means the buffer holds only the first `len(buf)` bytes. | +| `std.fs.readBytesAt(path, offset, buf)` | `Maybe` | Fills caller storage starting at a byte offset and returns the total file size, so bounded buffers can process larger files in chunks. | +| `std.fs.writeBytes(path, bytes)` | `Maybe` | Writes byte spans to a hosted path. | +| `std.fs.appendBytes(path, bytes)` | `Maybe` | Appends byte spans to a hosted path, creating the file when missing. | +| `std.fs.exists(path)` | `Bool` | Checks whether a hosted path exists. | +| `std.fs.isFile(path)` | `Bool` | Checks whether a hosted path opens and reports a file length. | +| `std.fs.isDir(path)` | `Bool` | Checks whether a hosted path is a directory. | +| `std.fs.makeDir(path)` | `Bool` | Creates a hosted directory. | +| `std.fs.ensureDir(path)` | `Bool` | Succeeds when a hosted directory already exists or can be created. | +| `std.fs.removeDir(path)` | `Bool` | Removes a hosted directory. | +| `std.fs.remove(path)` | `Bool` | Removes a hosted file path. | +| `std.fs.rename(old, new)` | `Bool` | Renames a hosted file path. | +| `std.fs.dirEntryCount(path)` | `Maybe` | Counts entries in a hosted directory. | +| `std.fs.dirEntryName(buffer, path, index)` | `Maybe>` | Writes one hosted directory entry name into caller storage. | +| `std.fs.tempName(buffer, prefix)` | `Maybe` | Writes a temporary path into caller storage. | +| `std.fs.atomicWrite(path, temp, bytes)` | `Bool` | Writes through a caller-provided temporary path and renames. | +| `std.fs.close(&mut file)` | `Void` | Closes an owned file handle explicitly; remaining owned files are cleaned up deterministically. | +| `std.fs.readFile(fs, path, buffer)` | `Maybe` | Opens, fills caller storage, and closes through explicit `Fs`; returns the total file size, so a value above `len(buffer)` signals truncation. | +| `std.fs.writeFile(fs, path, bytes)` | `Bool` | Creates, writes all bytes, and closes through explicit `Fs`. | +| `std.fs.appendFile(fs, path, bytes)` | `Bool` | Opens, appends all bytes, and closes through explicit `Fs`. | +| `std.fs.readFileBytes(fs, path, buffer)` | `Maybe>` | Opens, reads a full file, closes it, and returns the live prefix of caller storage; `null` when the file exceeds the buffer. | +| `std.fs.readFileEquals(fs, path, buffer, expected)` | `Bool` | Reads a full file through caller storage and compares the bytes with an expected span. | +| `std.fs.copyFile(from, to, buffer)` | `Bool` | Copies a hosted file through caller-provided scratch storage. | + +Current limits: + +- Richer permissions and platform-specific file modes. +- Recursive directory walking helpers. +- Async or nonblocking I/O. + +## Example + +```zero +pub fn main(world: World) -> Void raises [NotFound, TooLarge, Io] { + let fs: Fs = std.fs.host() + var buf: [32]u8 = [0_u8; 32] + if std.fs.ensureDir(".zero/out") && std.fs.writeFile(fs, ".zero/out/example.txt", "hello\n") { + let bytes: Maybe> = std.fs.readFileBytes(fs, ".zero/out/example.txt", buf) + let size: Maybe = std.fs.fileSize(fs, ".zero/out/example.txt") + if bytes.has && size.has && size.value == 6 && std.fs.isFile(".zero/out/example.txt") && std.fs.readFileEquals(fs, ".zero/out/example.txt", buf, "hello\n") && std.fs.rename(".zero/out/example.txt", ".zero/out/example-renamed.txt") { + if std.fs.remove(".zero/out/example-renamed.txt") { + check world.out.write("fs ok\n") + } + } + } +} +``` + +## Design Notes + +The path helpers are a small current API, not a hidden global filesystem. + +Stable file APIs make effects, ownership, and cleanup visible through +capabilities. + +Hosted filesystem APIs are denied on non-host targets with `TAR002`. +Target-neutral packages should keep filesystem code outside their cross-target +entry point. diff --git a/docs/articles/modules/http.md b/docs/articles/modules/http.md new file mode 100644 index 0000000..27e5b43 --- /dev/null +++ b/docs/articles/modules/http.md @@ -0,0 +1,406 @@ +## When To Use std.http + +In Zerolang, use `std.http` for HTTP request parsing, response envelope writing, hosted +fetch, local listen support, and web API helpers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.http.parseMethod(text)` | `HttpMethod` | Parses a small HTTP method token. | +| `std.http.client(net)` | `HttpClient` | Creates hosted client metadata from a network capability. | +| `std.http.server(net, address)` | `HttpServer` | Creates hosted server metadata from a network capability and address. | +| `std.http.listen(world)` | `Void raises [Io]` | Starts a loopback HTTP listener from `zero run`, auto-selecting a development port from 3000 upward. | +| `std.http.listen(world, port)` | `Void raises [Io]` | Starts a loopback HTTP listener on exactly `port`, failing if the port is occupied. | +| `std.http.fetch(client, request, response, timeout)` | `HttpResult` | Performs a hosted HTTP(S) request with a `Duration` timeout and writes response metadata, headers, and body into caller-owned storage. | +| `std.http.resultOk(result)` | `Bool` | True when transport succeeded and the status is 2xx. | +| `std.http.resultStatus(result)` | `u16` | Reads the HTTP status, or `0` when no status was available. | +| `std.http.resultBodyLen(result)` | `usize` | Reads the number of response body bytes written into the response buffer. | +| `std.http.resultError(result)` | `HttpError` | Reads the transport/provider error. | +| `std.http.errorNone()` | `HttpError` | Transport succeeded. | +| `std.http.errorInvalidUrl()` | `HttpError` | The request URL was invalid. | +| `std.http.errorUnsupportedProtocol()` | `HttpError` | The URL protocol is not supported. | +| `std.http.errorDns()` | `HttpError` | DNS lookup failed. | +| `std.http.errorConnect()` | `HttpError` | Connecting to the remote host failed. | +| `std.http.errorTls()` | `HttpError` | TLS verification or connection setup failed. | +| `std.http.errorTimeout()` | `HttpError` | The request timed out. | +| `std.http.errorTooLarge()` | `HttpError` | The response did not fit in the caller-owned buffer. | +| `std.http.errorProviderUnavailable()` | `HttpError` | The hosted HTTP provider is unavailable. | +| `std.http.errorIo()` | `HttpError` | The provider reported an I/O failure. | +| `std.http.errorInvalidRequest()` | `HttpError` | The request envelope was invalid. | +| `std.http.errorName(error)` | `String` | Returns a stable label for a transport error. | +| `std.http.responseLen(response)` | `usize` | Reads the response byte count written after the internal metadata prefix. | +| `std.http.responseHeadersLen(response)` | `usize` | Reads the raw response header byte count. | +| `std.http.responseBodyOffset(response)` | `usize` | Reads the body start offset within the response buffer. | +| `std.http.headerValue(response, name)` | `HttpHeaderValue` | Locates a response header value by case-insensitive header name. | +| `std.http.headerFound(value)` | `Bool` | True when `headerValue` found a matching header. | +| `std.http.headerOffset(value)` | `usize` | Reads the header value byte offset within the response buffer. | +| `std.http.headerLen(value)` | `usize` | Reads the header value byte length. | +| `std.http.tlsBoundary()` | `String` | Names the platform or C-library TLS boundary. | +| `std.http.statusReason(status)` | `String` | Returns a common reason phrase for status-line writing. | +| `std.http.statusIsInformational(status)` | `Bool` | True for 1xx statuses. | +| `std.http.statusIsSuccess(status)` | `Bool` | True for 2xx statuses. | +| `std.http.statusIsRedirect(status)` | `Bool` | True for 3xx statuses. | +| `std.http.statusIsClientError(status)` | `Bool` | True for 4xx statuses. | +| `std.http.statusIsServerError(status)` | `Bool` | True for 5xx statuses. | +| `std.http.writeRequest(buffer, startLine, body)` | `Maybe>` | Writes `METHOD URL`, optional content length, blank line, and body into caller storage. | +| `std.http.writeRequestWithHeader(buffer, startLine, headerLine, body)` | `Maybe>` | Writes a request envelope with one validated `name: value` header line. | +| `std.http.writeRequestWithHeaders(buffer, startLine, headers, body)` | `Maybe>` | Writes a request envelope with a validated newline-separated header block. | +| `std.http.writeMethodRequest(buffer, method, target, body)` | `Maybe>` | Writes a request envelope from separate method and target spans. | +| `std.http.writeGetRequest(buffer, target)` | `Maybe>` | Writes an empty GET request envelope. | +| `std.http.writeHeadRequest(buffer, target)` | `Maybe>` | Writes an empty HEAD request envelope. | +| `std.http.writeDeleteRequest(buffer, target)` | `Maybe>` | Writes an empty DELETE request envelope. | +| `std.http.writeJsonRequest(buffer, startLine, body)` | `Maybe>` | Writes a JSON request envelope with `content-type` and `content-length`. | +| `std.http.writeJsonMethodRequest(buffer, method, target, body)` | `Maybe>` | Writes a JSON request envelope from separate method and target spans. | +| `std.http.writeJsonRequestWithHeader(buffer, startLine, headerLine, body)` | `Maybe>` | Writes a JSON request envelope with one validated extra header line. | +| `std.http.writeJsonRequestWithHeaders(buffer, startLine, headers, body)` | `Maybe>` | Writes a JSON request envelope with a validated extra header block. | +| `std.http.writePostJsonRequest(buffer, target, body)` | `Maybe>` | Writes a POST JSON request envelope. | +| `std.http.writePutJsonRequest(buffer, target, body)` | `Maybe>` | Writes a PUT JSON request envelope. | +| `std.http.writePatchJsonRequest(buffer, target, body)` | `Maybe>` | Writes a PATCH JSON request envelope. | +| `std.http.writeResponse(buffer, status, body)` | `Maybe>` | Writes an HTTP/1.1 response envelope into caller storage. | +| `std.http.writeResponseWithHeader(buffer, status, headerLine, body)` | `Maybe>` | Writes a response envelope with one validated `name: value` header line. | +| `std.http.writeResponseWithHeaders(buffer, status, headers, body)` | `Maybe>` | Writes a response envelope with a validated newline-separated header block. | +| `std.http.writeJsonResponse(buffer, status, body)` | `Maybe>` | Writes a JSON HTTP/1.1 response envelope into caller storage. | +| `std.http.writeJsonResponseWithHeader(buffer, status, headerLine, body)` | `Maybe>` | Writes a JSON response envelope with one validated `name: value` header line. | +| `std.http.writeJsonResponseWithHeaders(buffer, status, headers, body)` | `Maybe>` | Writes a JSON response envelope with a validated extra header block. | +| `std.http.writeJsonResponseWithCookie(buffer, status, cookie, body)` | `Maybe>` | Writes a JSON response envelope with one `Set-Cookie` header value. | +| `std.http.writeJsonError(buffer, status, code)` | `Maybe>` | Writes `{"error":"code"}` after validating the code is JSON-safe lower-case ASCII, digits, `_`, or `-`. | +| `std.http.writeCorsPreflight(buffer, allowOrigin, allowMethods, allowHeaders)` | `Maybe>` | Writes a 204 CORS preflight response with caller-provided allow headers. | +| `std.http.writeCorsJsonResponse(buffer, statusLine, body, allowOrigin)` | `Maybe>` | Writes a JSON response with `access-control-allow-origin`; `statusLine` is a fragment such as `"200 OK"`. | +| `std.http.writeTextResponse(buffer, status, body)` | `Maybe>` | Writes a `text/plain; charset=utf-8` response envelope into caller storage. | +| `std.http.writeTextOk(buffer, body)` | `Maybe>` | Writes a 200 plain-text response envelope into caller storage. | +| `std.http.writeHtmlResponse(buffer, status, body)` | `Maybe>` | Writes a `text/html; charset=utf-8` response envelope into caller storage. | +| `std.http.writeHtmlOk(buffer, body)` | `Maybe>` | Writes a 200 HTML response envelope into caller storage. | +| `std.http.writeRedirect(buffer, status, location)` | `Maybe>` | Writes a redirect response with a safe `Location` header; rejects non-3xx statuses and empty or control-character locations. | +| `std.http.writeFound(buffer, location)` | `Maybe>` | Writes a 302 redirect response. | +| `std.http.writeSeeOther(buffer, location)` | `Maybe>` | Writes a 303 redirect response. | +| `std.http.writeMovedPermanently(buffer, location)` | `Maybe>` | Writes a 301 redirect response. | +| `std.http.writePermanentRedirect(buffer, location)` | `Maybe>` | Writes a 308 redirect response. | +| `std.http.contentTypeForPath(path)` | `String` | Returns a small static-file media type from a path suffix. | +| `std.http.writeStaticResponse(buffer, status, path, body)` | `Maybe>` | Writes a response with `content-type` inferred from the path suffix. | +| `std.http.writeJsonOk(buffer, body)` | `Maybe>` | Writes a 200 JSON response envelope into caller storage. | +| `std.http.writeJsonCreated(buffer, body)` | `Maybe>` | Writes a 201 JSON response envelope into caller storage. | +| `std.http.writeJsonBadRequest(buffer, body)` | `Maybe>` | Writes a 400 JSON response envelope into caller storage. | +| `std.http.writeJsonUnauthorized(buffer, body)` | `Maybe>` | Writes a 401 JSON response envelope into caller storage. | +| `std.http.writeJsonForbidden(buffer, body)` | `Maybe>` | Writes a 403 JSON response envelope into caller storage. | +| `std.http.writeJsonNotFound(buffer, body)` | `Maybe>` | Writes a 404 JSON response envelope into caller storage. | +| `std.http.writeJsonMethodNotAllowed(buffer, body)` | `Maybe>` | Writes a 405 JSON response envelope into caller storage. | +| `std.http.writeJsonConflict(buffer, body)` | `Maybe>` | Writes a 409 JSON response envelope into caller storage. | +| `std.http.writeJsonUnprocessable(buffer, body)` | `Maybe>` | Writes a 422 JSON response envelope into caller storage. | +| `std.http.writeJsonTooManyRequests(buffer, body)` | `Maybe>` | Writes a 429 JSON response envelope into caller storage. | +| `std.http.writeJsonInternalServerError(buffer, body)` | `Maybe>` | Writes a 500 JSON response envelope into caller storage. | +| `std.http.writeNoContent(buffer)` | `Maybe>` | Writes a 204 response envelope with an empty body. | +| `std.http.requestMethodName(request)` | `Maybe>` | Borrows the method token from a request envelope. | +| `std.http.requestTarget(request)` | `Maybe>` | Borrows the raw target from a request envelope. | +| `std.http.requestPath(request)` | `Maybe>` | Borrows the path from an absolute or origin-form request target. | +| `std.http.pathSegmentCount(path)` | `usize` | Counts non-empty path segments in a path span. | +| `std.http.pathSegment(path, index)` | `Maybe>` | Borrows a zero-based non-empty path segment from a path span. | +| `std.http.pathMatchesPattern(path, pattern)` | `Bool` | Matches path segments against literals, `:params`, and a trailing `*` wildcard. | +| `std.http.pathParam(path, pattern, name)` | `Maybe>` | Borrows a named path parameter from a matched path pattern. | +| `std.http.requestPathSegmentCount(request)` | `usize` | Counts non-empty path segments from a request envelope path. | +| `std.http.requestPathSegment(request, index)` | `Maybe>` | Borrows a zero-based non-empty request path segment. | +| `std.http.requestQuery(request)` | `Maybe>` | Borrows the query string from a request target. | +| `std.http.requestQueryValue(request, name)` | `Maybe>` | Borrows a query value by name from a request envelope. | +| `std.http.requestHeader(request, name)` | `Maybe>` | Borrows a case-insensitive request header value. | +| `std.http.requestBearerToken(request)` | `Maybe>` | Borrows the bearer token from the `Authorization` request header. | +| `std.http.requestCookie(request, name)` | `Maybe>` | Borrows a named cookie value from the `Cookie` request header. | +| `std.http.requestContentLength(request)` | `Maybe` | Parses the `Content-Length` request header. | +| `std.http.requestContentType(request)` | `Maybe>` | Borrows the media type from `Content-Type`, excluding parameters. | +| `std.http.requestAccepts(request, media)` | `Bool` | Checks whether `Accept` allows an exact media type, type wildcard, or `*/*`; absent `Accept` allows any media and `q=0` rejects a range. | +| `std.http.requestAcceptsJson(request)` | `Bool` | Checks whether `Accept` allows `application/json`. | +| `std.http.requestBody(request)` | `Maybe>` | Borrows the request body after the blank line. | +| `std.http.requestBodyWithin(request, max)` | `Maybe>` | Borrows the request body only when it is at most `max` bytes. | +| `std.http.requestHasJsonContentType(request)` | `Bool` | True when the request content type is `application/json`, ignoring ASCII case and allowing parameters. | +| `std.http.requestJsonBodyWithin(request, max)` | `Maybe>` | Borrows the body only when content type is JSON, body length is within `max`, and bytes validate as JSON. | +| `std.http.requestJsonField(request, name, max)` | `Maybe>` | Borrows a top-level JSON field from a bounded JSON request body. | +| `std.http.requestMatches(request, method, path)` | `Bool` | True when a request envelope has the exact method and normalized path. | +| `std.http.methodAllowed(method, allowed)` | `Bool` | Checks a method against a comma-separated allow list. | +| `std.http.requestMethodAllowed(request, allowed)` | `Bool` | Checks a request method against a comma-separated allow list. | +| `std.http.requestMethodIs(request, method)` | `Bool` | True when a request envelope has the exact method. | +| `std.http.requestIsGet(request, path)` | `Bool` | True when a request envelope is `GET` for the normalized path. | +| `std.http.requestIsHead(request, path)` | `Bool` | True when a request envelope is `HEAD` for the normalized path. | +| `std.http.requestIsOptions(request, path)` | `Bool` | True when a request envelope is `OPTIONS` for the normalized path. | +| `std.http.requestIsPost(request, path)` | `Bool` | True when a request envelope is `POST` for the normalized path. | +| `std.http.requestIsPut(request, path)` | `Bool` | True when a request envelope is `PUT` for the normalized path. | +| `std.http.requestIsPatch(request, path)` | `Bool` | True when a request envelope is `PATCH` for the normalized path. | +| `std.http.requestIsDelete(request, path)` | `Bool` | True when a request envelope is `DELETE` for the normalized path. | +| `std.http.requestPathStartsWith(request, prefix)` | `Bool` | True when the normalized request path starts with `prefix`. | +| `std.http.requestPathTailAfter(request, prefix)` | `Maybe>` | Borrows the normalized request path after `prefix`, or `null` when it does not match. | +| `std.http.requestRouteMatches(request, method, pattern)` | `Bool` | True when the method matches and the path matches a segment pattern. | +| `std.http.requestRouteMethodAllowed(request, pattern, allowed)` | `Bool` | True when the path pattern matches and the method is in a comma-separated allow list. | +| `std.http.requestPathParam(request, pattern, name)` | `Maybe>` | Borrows a named request path parameter from a matched pattern. | +| `std.http.headerBlockSafe(headers)` | `Bool` | Validates a newline-separated header block before writing it. | +| `std.http.headerBytes(response, value)` | `Maybe>` | Borrows a response header value after validating packed metadata. | +| `std.http.responseBody(response, result)` | `Maybe>` | Borrows the response body when the transport result succeeded. | +| `std.http.responseBodyBytes(response)` | `Maybe>` | Borrows the body bytes from a local response envelope written by response helpers. | +| `std.http.responseStatus(response)` | `Maybe` | Parses the status code from a local response envelope. | +| `std.http.responseStatusIs(response, status)` | `Bool` | Checks the status code in a local response envelope. | +| `std.http.responseHeader(response, name)` | `Maybe>` | Borrows a case-insensitive header value from a local response envelope. | +| `std.http.responseContentType(response)` | `Maybe>` | Borrows the media type from a local response envelope. | +| `std.http.responseRedirectLocation(response)` | `Maybe>` | Borrows a redirect `Location` header from a local response envelope. | +| `std.http.responseBodyEquals(response, expected)` | `Bool` | Compares a local response body with expected bytes. | +| `std.http.responseMatches(response, status, contentType, body)` | `Bool` | Checks local response status, optional content type, and body bytes. | +| `std.http.testRequest(buffer, method, target, body)` | `Maybe>` | Writes a synthetic request envelope for handler tests. | +| `std.http.testJsonRequest(buffer, method, target, body)` | `Maybe>` | Writes a synthetic JSON request envelope for handler tests. | + +The `WithHeader` and `WithHeaders` writers reject header names they manage +themselves. Request writers reject `content-length` and `transfer-encoding`; +JSON request writers also reject `content-type`; response writers reject +`content-length`, `transfer-encoding`, and `connection`; JSON response writers +also reject `content-type`. + +Metadata labels: + +- effects: net, memory, parse, or none for named error constants +- allocation behavior: metadata helpers do not allocate; `fetch` writes into a + caller-owned response buffer and uses the provider runtime internally +- target support: request/response parsing and writing helpers are + target-neutral; client/server require a net-capable target; `fetch` and + `listen` run on supported Darwin arm64 and Linux x64 host executable targets +- error behavior: metadata helpers are infallible; `fetch` returns status, + body length, and error metadata so non-2xx responses are distinguishable from + transport failures +- ownership notes: HTTP helpers borrow network capability metadata and write + only to caller-owned buffers +- examples: `conformance/native/pass/std-http-metadata-neutral.graph`, + `conformance/native/pass/std-http-fetch.graph`, + `conformance/native/pass/std-http-errors.graph`, + `conformance/native/pass/std-http-response-helpers.graph`, + `conformance/native/pass/std-http-api-helpers.graph`, + `conformance/native/pass/std-http-cors-helpers.graph`, + `conformance/native/pass/std-http-auth-helpers.graph`, + `conformance/native/pass/std-http-path-segments.graph`, + `examples/json-api-client.graph`, + `examples/json-api-router.graph`, + `examples/std-http-json.graph`, + `examples/std-http-request.graph`, + `examples/std-http-headers.graph` + +## Example + +Metadata helpers: + +```zero +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let addr: Address = std.net.address("localhost", 8080_u16) + let _client: HttpClient = std.http.client(net) + let _server: HttpServer = std.http.server(net, addr) + let method: HttpMethod = std.http.parseMethod("GET") + if method == std.http.parseMethod("GET") && std.mem.len(std.mem.span("body")) == 4 { + check world.out.write("http ok\n") + } +} +``` + +GET request: + +```zero +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let client: HttpClient = std.http.client(net) + var response: [512]u8 = [0_u8; 512] + let request: Span = std.mem.span("GET https://example.com\n\n") + let result: HttpResult = std.http.fetch(client, request, response, std.time.ms(1000)) + if std.http.resultOk(result) { + check world.out.write("http get ok\n") + return + } + check world.err.write("http get failed\n") +} +``` + +Request with headers and body: + +```zero +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let client: HttpClient = std.http.client(net) + var request_buf: [256]u8 = [0_u8; 256] + let request: Maybe> = std.http.writeJsonRequestWithHeader(request_buf, "POST https://example.com/api", "accept: application/json", "{\"ping\":1}") + var response: [512]u8 = [0_u8; 512] + if request.has { + let result: HttpResult = std.http.fetch(client, request.value, response, std.time.ms(1000)) + if std.http.resultOk(result) { + check world.out.write("http post ok\n") + return + } + } + check world.err.write("http post failed\n") +} +``` + +Request routing: + +```zero +pub fn main(world: World) -> Void raises { + let request: Span = std.mem.span("POST /users/7?tenant=demo\naccept: application/json\ncontent-type: application/json; charset=utf-8\n\n{\"id\":7}") + var response: [256]u8 = [0_u8; 256] + let id: Maybe> = std.http.requestPathParam(request, "/users/:id", "id") + let body_id: Maybe> = std.http.requestJsonField(request, "id", 64) + let tenant: Maybe> = std.http.requestQueryValue(request, "tenant") + if std.http.requestRouteMatches(request, "POST", "/users/:id") && id.has && tenant.has && body_id.has && std.http.requestAcceptsJson(request) { + let written: Maybe> = std.http.writeJsonResponseWithHeader(response, 201_u16, "location: /users/7", "{\"created\":true}") + if written.has { + check world.out.write("http route ok\n") + return + } + } + let failed: Maybe> = std.http.writeJsonError(response, 400, "bad_request") + if failed.has { + check world.err.write("http route failed\n") + } +} +``` + +CORS preflight and JSON response: + +```zero +pub fn main(world: World) -> Void raises { + let request: Span = std.mem.span("OPTIONS /users\naccess-control-request-method: POST\n\n") + var response: [256]u8 = [0_u8; 256] + if std.http.requestIsOptions(request, "/users") { + let written: Maybe> = std.http.writeCorsPreflight(response, "*", "GET, POST, OPTIONS", "content-type, authorization") + if written.has { + check world.out.write("http cors preflight ok\n") + return + } + } + let failed: Maybe> = std.http.writeCorsJsonResponse(response, "400 Bad Request", "{\"error\":\"bad_request\"}", "*") + if failed.has { + check world.err.write("http cors failed\n") + } +} +``` + +Authorization and session cookie helpers: + +```zero +pub fn main(world: World) -> Void raises { + let request: Span = std.mem.span("GET /me\nauthorization: Bearer token-123\ncookie: sid=abc; theme=dark\n\n") + let token: Maybe> = std.http.requestBearerToken(request) + let session: Maybe> = std.http.requestCookie(request, "sid") + if token.has && session.has { + check world.out.write("http auth ok\n") + } +} +``` + +Response body: + +```zero +pub fn main(world: World) -> Void raises { + let maybe_request: Maybe = std.args.get(1) + if !maybe_request.has { + check world.err.write("usage: pass HTTP request envelope\n") + return + } + let net: Net = std.net.host() + let client: HttpClient = std.http.client(net) + var response: [512]u8 = [0_u8; 512] + let result: HttpResult = std.http.fetch(client, std.mem.span(maybe_request.value), response, std.time.ms(5000)) + let bytes: Maybe> = std.http.responseBody(response, result) + var arena_buf: [16]u8 = [0_u8; 16] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + var parsed: Maybe = null + if bytes.has { + parsed = std.json.parseBytes(arena, bytes.value) + } + if std.http.resultOk(result) && bytes.has && parsed.has { + check world.out.write("http response json ok\n") + return + } + check world.err.write("http response json failed\n") +} +``` + +Loopback API server: + +```zero +pub fn main(world: World) -> Void raises { + check std.http.listen(world) +} + +fn handle(request: Span, response: MutSpan) -> Maybe> { + if std.http.requestIsGet(request, "/ping") { + return std.http.writeJsonOk(response, "{\"message\":\"pong\"}") + } + if std.http.requestIsGet(request, "/robots.txt") { + return std.http.writeTextOk(response, "user-agent: *\nallow: /\n") + } + if std.http.requestIsGet(request, "/old") { + return std.http.writeMovedPermanently(response, "/ping") + } + return std.http.writeJsonError(response, 404, "not_found") +} +``` + +Run it and use the port printed by the listener: + +```sh +zero run . +# listening on http://127.0.0.1:3001 +curl -sS -i http://127.0.0.1:3001/ping +``` + +When no port is passed, `listen(world)` starts at `3000` and increments by one +until it finds a free loopback port. This lets small local services coexist on a +development machine. When the program passes an explicit port, such as +`std.http.listen(world, 3000_u16)`, Zero tries exactly that port and reports the +bind failure instead of auto-incrementing. + +Synthetic handler check: + +```zero +fn handle(request: Span, response: MutSpan) -> Maybe> { + if std.http.requestRouteMethodAllowed(request, "/users/:id", "GET, HEAD") { + return std.http.writeJsonResponseWithHeaders(response, 200_u16, "cache-control: no-store", "{\"ok\":true}") + } + return std.http.writeJsonError(response, 404_u16, "not_found") +} + +pub fn main(world: World) -> Void raises { + var request_storage: [128]u8 = [0_u8; 128] + var response_storage: [256]u8 = [0_u8; 256] + let request: Maybe> = std.http.testRequest(request_storage, "GET", "/users/7", "") + if request.has { + let response: Maybe> = handle(request.value, response_storage) + if response.has && std.http.responseMatches(response.value, 200_u16, "application/json", "{\"ok\":true}") { + check world.out.write("handler ok\n") + return + } + } + check world.err.write("handler failed\n") +} +``` + +## Design Notes + +`std.http.fetch` is the outbound HTTP client primitive. The request argument is +one byte envelope: `METHOD URL`, followed by zero or more `Header-Name: value` +lines, a blank line, and optional request body bytes. Use `writeRequest` and +`writeJsonRequest` when you want the standard library to write that envelope +into caller storage. The timeout is a `Duration`, typically built with +`std.time.ms(...)`. + +`fetch` supports `http://` and `https://` URLs on supported host executable +targets, uses the C-library curl provider, does not follow redirects, and +verifies TLS through the provider. TLS verification is always enabled. +`ZERO_HTTP_TEST_CA_BUNDLE` can point the provider at an explicit CA bundle +while keeping certificate verification on. + +The response buffer starts with internal metadata, followed by raw response +headers and then the response body. Use `responseHeadersLen` and +`responseBodyOffset` rather than hard-coding offsets. `headerValue` scans the +response buffer and returns packed offset/length metadata for the matching +value; `headerFound`, `headerOffset`, and `headerLen` inspect that metadata +without allocating. Prefer `headerBytes` and `responseBody` when you need a +borrowed byte span and want metadata bounds checked in one call. + +Compare `resultError` with the named `HttpError` helpers rather than raw +numbers. Use `errorName(resultError(result))` for diagnostics and logs. +`errorNone` means the transport succeeded; HTTP non-2xx statuses still carry +`errorNone` and can be inspected with `resultStatus`. + +The module does not expose raw socket read/write APIs, streaming bodies, a +global router, or a heap-allocated response object. diff --git a/docs/articles/modules/inet.md b/docs/articles/modules/inet.md new file mode 100644 index 0000000..7c41400 --- /dev/null +++ b/docs/articles/modules/inet.md @@ -0,0 +1,56 @@ +## When To Use std.inet + +In Zerolang, use `std.inet` to validate and parse internet address literals: +IPv4, IPv6, and RFC 1123 hostnames. These helpers are target-neutral and need +no network capability, so they work in validators and parsers on any compiler +target. Use `std.net` when a program actually opens connections or listeners. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.inet.isIpv4(text)` | `Bool` | Validates a dotted-quad IPv4 literal: four decimal octets 0-255 with no leading zeros. | +| `std.inet.parseIpv4(text)` | `Maybe` | Parses an IPv4 literal into a big-endian packed `u32`. | +| `std.inet.writeIpv4(buffer, value)` | `Maybe>` | Writes a packed IPv4 value as dotted-quad text into caller storage. | +| `std.inet.isIpv4Unspecified(value)` | `Bool` | Checks `0.0.0.0/32`. | +| `std.inet.isIpv4Loopback(value)` | `Bool` | Checks `127.0.0.0/8`. | +| `std.inet.isIpv4Private(value)` | `Bool` | Checks RFC 1918 private ranges. | +| `std.inet.isIpv4LinkLocal(value)` | `Bool` | Checks `169.254.0.0/16`. | +| `std.inet.isIpv4Multicast(value)` | `Bool` | Checks `224.0.0.0/4`. | +| `std.inet.isIpv6(text)` | `Bool` | Validates an RFC 4291 IPv6 literal, including `::` compression and embedded IPv4. | +| `std.inet.parseIpv6(buffer, text)` | `Maybe>` | Parses an IPv6 literal into 16 network-order bytes in a caller buffer. | +| `std.inet.isIp(text)` | `Bool` | Validates either a strict IPv4 literal or an RFC 4291 IPv6 literal. | +| `std.inet.parseIp(buffer, text)` | `Maybe>` | Parses IPv4 into 4 bytes or IPv6 into 16 bytes in caller storage. | +| `std.inet.isIpv6Unspecified(bytes)` | `Bool` | Checks `::/128` over a 16-byte IPv6 span. | +| `std.inet.isIpv6Loopback(bytes)` | `Bool` | Checks `::1/128` over a 16-byte IPv6 span. | +| `std.inet.isIpv6Multicast(bytes)` | `Bool` | Checks `ff00::/8`. | +| `std.inet.isIpv6LinkLocal(bytes)` | `Bool` | Checks `fe80::/10`. | +| `std.inet.isIpv6Private(bytes)` | `Bool` | Checks the `fc00::/7` unique-local range. | +| `std.inet.isIpv6UniqueLocal(bytes)` | `Bool` | Alias for the `fc00::/7` unique-local range. | +| `std.inet.isIpv6MappedIpv4(bytes)` | `Bool` | Checks `::ffff:0:0/96` IPv4-mapped addresses. | +| `std.inet.ipv6MappedIpv4(bytes)` | `Maybe` | Extracts the packed IPv4 value from an IPv4-mapped IPv6 span. | +| `std.inet.isHostname(text)` | `Bool` | Validates an RFC 1123 hostname: dot-separated 1-63 byte alphanumeric/hyphen labels, 253 bytes total. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [16]u8 = [0; 16] + let buffer: MutSpan = storage + let quad: Maybe = std.inet.parseIpv4("192.168.0.1") + let mapped: Maybe> = std.inet.parseIpv6(buffer, "::ffff:192.168.1.1") + if std.inet.isHostname("example.com") && (quad.has && mapped.has) && (std.inet.isIpv4Private(quad.value) && std.inet.isIpv6MappedIpv4(mapped.value)) { + check world.out.write("inet ok\n") + } +} +``` + +Effects: none. + +Allocation behavior: `parseIp` and `parseIpv6` write into caller buffers; +`writeIpv4` writes into caller storage. The other helpers allocate nothing. + +Error behavior: validators return `Bool`; parsers return `null` for invalid +literals or undersized buffers. + +Target support: current compiler targets; no network capability required. diff --git a/docs/articles/modules/io.md b/docs/articles/modules/io.md new file mode 100644 index 0000000..7c515f7 --- /dev/null +++ b/docs/articles/modules/io.md @@ -0,0 +1,128 @@ +## When To Use std.io + +In Zerolang, use `std.io` for byte reads and writes over caller-owned storage. +The module provides explicit cursor helpers for spans plus fixed reader and +writer values for sequential stream-style code. + +Span helpers: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.copy(dst, src)` | `usize` | Copies as many bytes as fit and returns the count. | +| `std.io.copyN(dst, src, count)` | `Maybe` | Copies exactly `count` bytes when both spans are large enough. | +| `std.io.read(bytes, offset, dst)` | `Maybe` | Copies available bytes and returns the next cursor. | +| `std.io.readExact(bytes, offset, dst)` | `Maybe` | Fills the destination only when enough input bytes remain. | +| `std.io.readAt(bytes, offset, dst)` | `Maybe` | Reads from an explicit offset without owning state. | +| `std.io.readAll(bytes, dst)` | `Maybe>` | Copies all input bytes and returns the written prefix. | +| `std.io.readByte(bytes, offset)` | `Maybe` | Reads one byte at an explicit cursor. | +| `std.io.writeByte(buffer, offset, byte)` | `Maybe` | Writes one byte and returns the next cursor. | +| `std.io.writeSpan(buffer, offset, bytes)` | `Maybe` | Writes bytes and returns the next cursor. | +| `std.io.writeAll(buffer, offset, bytes)` | `Maybe` | Writes all bytes only when the full input fits. | +| `std.io.writeAt(buffer, offset, bytes)` | `Maybe` | Writes at an explicit offset. | +| `std.io.written(buffer, len)` | `Span` | Borrows the written prefix, clamped to buffer length. | +| `std.io.remaining(buffer, offset)` | `usize` | Reports remaining byte capacity from an explicit cursor. | + +Line and delimiter helpers: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.nextLine(bytes, start)` | `Maybe>` | Borrows the next line without `\n` or trailing `\r`. | +| `std.io.nextLineStart(bytes, start)` | `usize` | Advances to the next line start or end of input. | +| `std.io.readLine(bytes, start)` | `Maybe>` | Alias for line-oriented reads using the same line rules. | +| `std.io.readLineStart(bytes, start)` | `usize` | Advances to the next line start using the same line rules. | +| `std.io.readUntilDelimiter(bytes, start, delimiter)` | `Maybe>` | Borrows bytes up to the delimiter or end of input. | +| `std.io.readUntilDelimiterStart(bytes, start, delimiter)` | `usize` | Advances past the delimiter when found, otherwise to end of input. | +| `std.io.countLines(bytes)` | `usize` | Counts lines using the same next-line rules. | + +Fixed stream helpers: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.fixedReader(bytes, cursor)` | `FixedReader` | Builds a sequential reader over borrowed bytes. | +| `std.io.fixedReaderRead(&mut reader, dst)` | `usize` | Reads up to `dst` length and advances the reader cursor. | +| `std.io.fixedReaderReadExact(&mut reader, dst)` | `Bool` | Reads exactly `dst` length or leaves the cursor unchanged. | +| `std.io.fixedReaderReadLine(&mut reader)` | `Maybe>` | Borrows the next line and advances the cursor. | +| `std.io.fixedReaderReadUntilDelimiter(&mut reader, delimiter)` | `Maybe>` | Borrows bytes up to a delimiter and advances the cursor. | +| `std.io.fixedReaderReadAll(&mut reader, dst)` | `Maybe>` | Copies remaining bytes into caller storage. | +| `std.io.fixedReaderReadAt(&reader, offset, dst)` | `Maybe` | Reads from an offset without moving the cursor. | +| `std.io.fixedReaderReadByte(&mut reader)` | `Maybe` | Reads one byte and advances the cursor. | +| `std.io.fixedReaderLimit(&reader, count)` | `FixedReader` | Creates a bounded reader view from the current cursor. | +| `std.io.fixedReaderSeek(&mut reader, cursor)` | `Bool` | Moves the cursor when the target is in range. | +| `std.io.fixedReaderCursor(&reader)` | `usize` | Reports the cursor. | +| `std.io.fixedReaderLen(&reader)` | `usize` | Reports input length. | +| `std.io.fixedReaderRemaining(&reader)` | `usize` | Reports unread bytes. | +| `std.io.fixedReaderDone(&reader)` | `Bool` | Reports whether the cursor reached the end. | +| `std.io.fixedWriter(buffer, cursor)` | `FixedWriter` | Builds a sequential writer over caller storage. | +| `std.io.fixedWriterWrite(&mut writer, bytes)` | `Bool` | Writes bytes and advances the writer cursor. | +| `std.io.fixedWriterWriteAll(&mut writer, bytes)` | `Bool` | Writes all bytes only when they fit. | +| `std.io.fixedWriterWriteByte(&mut writer, byte)` | `Bool` | Writes one byte and advances the cursor. | +| `std.io.fixedWriterWriteAt(&mut writer, offset, bytes)` | `Bool` | Writes at an offset without moving the cursor. | +| `std.io.fixedWriterView(&writer)` | `Span` | Borrows the live writer prefix. | +| `std.io.fixedWriterSeek(&mut writer, cursor)` | `Bool` | Moves the cursor when the target is in range. | +| `std.io.fixedWriterTruncate(&mut writer, len)` | `usize` | Clamps the live prefix length. | +| `std.io.fixedWriterClear(&mut writer)` | `usize` | Resets the cursor to zero. | +| `std.io.fixedWriterCursor(&writer)` | `usize` | Reports the cursor. | +| `std.io.fixedWriterCapacity(&writer)` | `usize` | Reports storage length. | +| `std.io.fixedWriterRemaining(&writer)` | `usize` | Reports unwritten capacity. | + +Stream composition helpers: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.copyBuffer(&mut reader, &mut writer, scratch)` | `usize` | Copies until EOF or writer capacity using caller scratch storage. | +| `std.io.copyReaderN(&mut reader, &mut writer, count, scratch)` | `Maybe` | Copies exactly `count` bytes through scratch storage. | +| `std.io.discard(&mut reader, scratch)` | `usize` | Reads and drops bytes through scratch storage. | +| `std.io.teeRead(&mut reader, &mut writer, dst)` | `Maybe` | Reads into `dst` and writes the same bytes to the writer. | +| `std.io.multiRead(&mut first, &mut second, dst)` | `usize` | Fills `dst` from the first reader, then the second. | + +Error labels: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.errorNone()` | `u32` | No IO error. | +| `std.io.errorEof()` | `u32` | End of input. | +| `std.io.errorShortRead()` | `u32` | Input ended before an exact read completed. | +| `std.io.errorShortWrite()` | `u32` | Output capacity ended before an exact write completed. | +| `std.io.errorCapacity()` | `u32` | Caller storage was too small. | +| `std.io.errorPermission()` | `u32` | Permission was denied. | +| `std.io.errorTimeout()` | `u32` | Operation timed out. | +| `std.io.errorIo()` | `u32` | General IO failure. | +| `std.io.errorName(code)` | `String` | Returns a stable label for a known code. | + +Buffered capacity helpers: + +| API | Return | Notes | +| --- | --- | --- | +| `std.io.bufferedReader(buffer)` | `BufferedReader` | Builds a reader descriptor over caller storage. | +| `std.io.bufferedWriter(buffer)` | `BufferedWriter` | Builds a writer descriptor over caller storage. | +| `std.io.readerCapacity(&reader)` | `usize` | Reports reader storage capacity. | +| `std.io.writerCapacity(&writer)` | `usize` | Reports writer storage capacity. | + +Metadata labels: + +- effects: memory +- allocation behavior: uses caller buffer; no hidden heap +- target support: target-neutral +- error behavior: exact cursor reads and writes return `Maybe.none` or `false` on overflow or insufficient input +- ownership notes: borrows or writes caller-owned storage + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var scratch: [4]u8 = [0_u8; 4] + var output: [16]u8 = [0_u8; 16] + var reader: FixedReader = std.io.fixedReader("one\ntwo", 0) + var writer: FixedWriter = std.io.fixedWriter(output, 0) + let line: Maybe> = std.io.fixedReaderReadLine(&mut reader) + let copied: usize = std.io.copyBuffer(&mut reader, &mut writer, scratch) + if line.has && copied == 3 && std.mem.eql(std.io.fixedWriterView(&writer), "two") { + check world.out.write("io ok\n") + } +} +``` + +## Design Notes + +`std.io` is a caller-owned buffer surface, not an ambient process I/O layer. +Process stdin/stdout stay behind explicit capabilities such as `World` streams. diff --git a/docs/articles/modules/json.md b/docs/articles/modules/json.md new file mode 100644 index 0000000..eba9295 --- /dev/null +++ b/docs/articles/modules/json.md @@ -0,0 +1,179 @@ +## When To Use std.json + +In Zerolang, use `std.json` for validation, shallow field lookup, object and +array cursor access, explicit-allocator parsing, and caller-buffer JSON writing. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.json.validate(text)` | `Bool` | Checks the current JSON subset without allocation. | +| `std.json.validateBytes(bytes)` | `Bool` | Checks a `Span` JSON payload without allocation. | +| `std.json.parse(alloc, text)` | `Maybe` | Parses with an explicit allocator and returns `null` on failure. | +| `std.json.parseBytes(alloc, bytes)` | `Maybe` | Parses a `Span` payload with an explicit allocator and returns `null` on failure. | +| `std.json.streamTokens(text)` | `usize` | Counts stream tokens without building an owned tree. | +| `std.json.streamTokensBytes(bytes)` | `usize` | Counts stream tokens from a `Span` payload. | +| `std.json.writeString(buffer, text)` | `Maybe` | Writes an escaped JSON string into caller storage. | +| `std.json.decodeBoundary()` | `String` | Documents the typed decode boundary exposed by current metadata. | +| `std.json.errorNone()` | `u32` | Validation status for a clean payload. | +| `std.json.errorInvalid()` | `u32` | Validation status for malformed JSON. | +| `std.json.errorTrailing()` | `u32` | Validation status for trailing non-whitespace bytes. | +| `std.json.errorName(code)` | `String` | Returns a stable label for a validation status. | +| `std.json.errorExpected(code)` | `String` | Returns stable expected-token text for a validation status. | +| `std.json.validateError(bytes)` | `u32` | Validates a byte span and returns a structured status code. | +| `std.json.errorOffset(bytes)` | `usize` | Returns the byte offset where validation fails, or the input length for valid JSON. | +| `std.json.errorLine(bytes)` | `usize` | Returns the one-based line for the validation error offset. | +| `std.json.errorColumn(bytes)` | `usize` | Returns the one-based column for the validation error offset. | +| `std.json.field(bytes, key)` | `Maybe>` | Returns the raw top-level object field value. | +| `std.json.objectFieldCount(bytes)` | `Maybe` | Counts fields in a JSON object slice. | +| `std.json.objectKey(buffer, bytes, ordinal)` | `Maybe>` | Decodes an ordinal object key into caller storage. | +| `std.json.objectValue(bytes, ordinal)` | `Maybe>` | Returns an ordinal object value as a raw JSON slice. | +| `std.json.arrayCount(bytes)` | `Maybe` | Counts items in a JSON array slice. | +| `std.json.arrayValue(bytes, ordinal)` | `Maybe>` | Returns an ordinal array value as a raw JSON slice. | +| `std.json.path(bytes, path)` | `Maybe>` | Returns a raw value for a dot-separated object path. | +| `std.json.pathString(buffer, bytes, path)` | `Maybe>` | Looks up and decodes a string at a dot-separated object path. | +| `std.json.pathU32(bytes, path)` | `Maybe` | Looks up and decodes a `u32` at a dot-separated object path. | +| `std.json.pathBool(bytes, path)` | `Maybe` | Looks up and decodes a bool at a dot-separated object path. | +| `std.json.stringDecode(buffer, value)` | `Maybe>` | Decodes a JSON string value, including Unicode escapes as UTF-8, into caller storage. | +| `std.json.string(buffer, bytes, key)` | `Maybe>` | Looks up and decodes a top-level string field. | +| `std.json.u32(bytes, key)` | `Maybe` | Looks up and decodes a top-level unsigned integer field. | +| `std.json.bool(bytes, key)` | `Maybe` | Looks up and decodes a top-level boolean field. | +| `std.json.writeStringBytes(buffer, text)` | `Maybe>` | Writes an escaped JSON string from byte input. | +| `std.json.writeObject1String(buffer, key, value)` | `Maybe>` | Writes a one-field object with a string value. | +| `std.json.writeObject1U32(buffer, key, value)` | `Maybe>` | Writes a one-field object with a `u32` value. | +| `std.json.writeObject1Bool(buffer, key, value)` | `Maybe>` | Writes a one-field object with a bool value. | +| `std.json.writeFieldRaw(buffer, key, value)` | `Maybe>` | Writes one object field from a key and validated raw JSON value. | +| `std.json.writeFieldString(buffer, key, value)` | `Maybe>` | Writes one object field with an escaped string value. | +| `std.json.writeFieldU32(buffer, key, value)` | `Maybe>` | Writes one object field with a `u32` value. | +| `std.json.writeFieldBool(buffer, key, value)` | `Maybe>` | Writes one object field with a bool value. | +| `std.json.writeObject2Fields(buffer, field0, field1)` | `Maybe>` | Writes a two-field object from field fragments and validates the final object. | +| `std.json.writeObject2StringField(buffer, key, value, field1)` | `Maybe>` | Writes a two-field object from a string field and a prebuilt field fragment. | +| `std.json.writeObject2U32Field(buffer, key, value, field1)` | `Maybe>` | Writes a two-field object from a `u32` field and a prebuilt field fragment. | +| `std.json.writeObject2BoolField(buffer, key, value, field1)` | `Maybe>` | Writes a two-field object from a bool field and a prebuilt field fragment. | +| `std.json.writeArray2Strings(buffer, value0, value1)` | `Maybe>` | Writes a two-item array with escaped string values. | +| `std.json.writeArray2U32(buffer, value0, value1)` | `Maybe>` | Writes a two-item array with `u32` values. | +| `std.json.writeArray2Bools(buffer, value0, value1)` | `Maybe>` | Writes a two-item array with bool values. | + +Metadata labels: + +- effects: memory, parse, or alloc +- allocation behavior: validation and streaming are allocation-free; parse uses explicit allocator only; direct writers write caller buffers +- target support: target-neutral +- error behavior: `Maybe` helpers return null on failure +- diagnostics: `errorOffset`, `errorLine`, and `errorColumn` locate validation failures; valid JSON reports the end of input +- ownership notes: parsed documents are owned by explicit allocator storage in this compiler slice +- examples: `examples/std-data-formats.graph`, `examples/std-json-bytes.graph`, `conformance/native/pass/std-codec-json-url.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var arena_buf: [16]u8 = [0_u8; 16] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parse(arena, "{\"ok\":true}") + var out: [16]u8 = [0_u8; 16] + let text: Maybe = std.json.writeString(out, "zero") + if parsed.has && text.has && std.json.streamTokens("{\"ok\":true}") == 3 { + check world.out.write("json ok\n") + } +} +``` + +Byte-span parse form: + +```zero +pub fn main(world: World) -> Void raises { + let bytes: Span = std.mem.span("{\"ok\":1}") + var arena_buf: [16]u8 = [0_u8; 16] + var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf) + let parsed: Maybe = std.json.parseBytes(arena, bytes) + if parsed.has && std.json.validateBytes(bytes) && std.json.streamTokensBytes(bytes) == 3 { + check world.out.write("json bytes ok\n") + return + } + check world.err.write("json bytes failed\n") +} +``` + +Top-level object lookup and caller-buffer writing: + +```zero +pub fn main(world: World) -> Void raises { + let input: Span = "{\"name\":\"zero\",\"count\":42,\"ok\":true}" + var name_buf: [8]u8 = [0_u8; 8] + let name: Maybe> = std.json.string(name_buf, input, "name") + let count: Maybe = std.json.u32(input, "count") + var count_field_buf: [24]u8 = [0_u8; 24] + let count_field: Maybe> = std.json.writeFieldU32(count_field_buf, "count", 42_u32) + var out: [48]u8 = [0_u8; 48] + var written: Maybe> = null + if count_field.has { + written = std.json.writeObject2StringField(out, "name", "zero", count_field.value) + } + if name.has && count.has && written.has && std.json.validateError(written.value) == std.json.errorNone() { + check world.out.write("json lookup ok\n") + } +} +``` + +Object and array cursors return borrowed raw JSON slices. Object keys are +decoded into caller-owned storage: + +```zero +pub fn main(world: World) -> Void raises { + let input: Span = "{\"user\":{\"name\":\"zero\",\"count\":42},\"items\":[1,2]}" + let field_count: Maybe = std.json.objectFieldCount(input) + var key_buf: [8]u8 = [0_u8; 8] + let first_key: Maybe> = std.json.objectKey(key_buf, input, 0) + let items: Maybe> = std.json.field(input, "items") + var name_buf: [8]u8 = [0_u8; 8] + let name: Maybe> = std.json.pathString(name_buf, input, "user.name") + if field_count.has && first_key.has && items.has && std.json.arrayCount(items.value).has && name.has { + check world.out.write("json cursors ok\n") + } +} +``` + +JSON validation diagnostics are allocation-free. Use `std.diag` when a +human-readable file location is needed: + +```zero +pub fn main(world: World) -> Void raises { + let input: Span = "{\n \"ok\": tru\n}" + var location_buf: [24]u8 = [0_u8; 24] + let location: Maybe> = std.diag.formatOffsetLocation(location_buf, "config.json", input, std.json.errorOffset(input)) + if std.json.validateError(input) == std.json.errorInvalid() && std.json.errorLine(input) == 2 && location.has { + check world.out.write(location.value) + check world.out.write("\n") + } +} +``` + +Small array responses use the same caller-buffer pattern: + +```zero +pub fn main(world: World) -> Void raises { + var out: [32]u8 = [0_u8; 32] + let tags: Maybe> = std.json.writeArray2Strings(out, "api", "agent") + if tags.has { + check world.out.write(tags.value) + } +} +``` + +## Design Notes + +JSON should not fake allocation-free semantics. Validation, field lookup, +string decode, and writing stay allocation-free. String decode writes UTF-8 for +Unicode escapes and rejects malformed surrogate pairs. + +Parsing into an owned document requires an explicit allocator. The current +`JsonDoc` value is opaque; examples inspect `Maybe.has` and use token streaming +for allocation-free summaries. Field lookup is intentionally shallow: it reads +top-level object fields and returns raw slices or typed scalar decodes. Object +path lookup follows dot-separated object keys; array indexing remains explicit +through `arrayValue`. When an object contains duplicate keys, name-based lookup +returns the first matching value, while ordinal object cursors preserve the +source order and expose every field. Validation diagnostics report byte offsets +in the source payload; line and column helpers treat lines and columns as +one-based byte positions. diff --git a/docs/articles/modules/log.md b/docs/articles/modules/log.md new file mode 100644 index 0000000..662ee1a --- /dev/null +++ b/docs/articles/modules/log.md @@ -0,0 +1,65 @@ +## When To Use std.log + +In Zerolang, use `std.log` for explicit-buffer structured log record formatting. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.log.levelDebug()` | `String` | Static `debug` level text. | +| `std.log.levelInfo()` | `String` | Static `info` level text. | +| `std.log.levelWarn()` | `String` | Static `warn` level text. | +| `std.log.levelError()` | `String` | Static `error` level text. | +| `std.log.message(buffer, level, message)` | `Maybe>` | Writes one newline-terminated JSON Lines record with `level` and `message`. | +| `std.log.keyValue(buffer, level, key, value)` | `Maybe>` | Writes one newline-terminated JSON Lines record with `level`, `key`, and `value`. | +| `std.log.stringField(buffer, key, value)` | `Maybe>` | Writes one JSON string field fragment. | +| `std.log.messageField(buffer, level, message, field)` | `Maybe>` | Writes one JSON Lines message record with one field fragment. | +| `std.log.redacted(buffer, level, key)` | `Maybe>` | Writes one JSON Lines record marking a field name as redacted. | + +Metadata labels: + +- effects: memory; `messageField` also validates JSON field fragments +- allocation behavior: writes caller buffer; no hidden heap +- target support: target-neutral +- error behavior: returns `null` when the buffer is too small or a value cannot be JSON-escaped +- ownership notes: borrows returned bytes from caller-owned storage +- example: `examples/std-testing-log.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [128]u8 = [0_u8; 128] + var field_storage: [64]u8 = [0_u8; 64] + let field: Maybe> = std.log.stringField(field_storage, "event", "startup") + if field.has { + let entry: Maybe> = std.log.messageField(storage, std.log.levelInfo(), "started", field.value) + if entry.has { + check world.out.write(entry.value) + } + } +} +``` + +Expected output: + +```json +{"level":"info","message":"started","event":"startup"} +``` + +## Design Notes + +`std.log` is a formatting surface, not a global logger. The caller owns the +storage and chooses where to write the resulting span, such as `World.out`, a +file handle, or a test assertion. + +Records use JSON Lines so downstream tools can parse them without guessing at +ad hoc separators. The helpers write exactly one record and include a trailing +newline. + +`messageField` validates the final JSON object before returning it. Build field +fragments with `stringField` unless the field fragment is already known to be +valid JSON. + +Use `redacted` for logs that need to state which field was intentionally +withheld without writing the sensitive value. diff --git a/docs/articles/modules/math.md b/docs/articles/modules/math.md new file mode 100644 index 0000000..f71fd2c --- /dev/null +++ b/docs/articles/modules/math.md @@ -0,0 +1,86 @@ +## When To Use std.math + +In Zerolang, use `std.math` for pure fixed-width integer helpers, checked/saturating +arithmetic, and small number-theory routines. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.math.minU32(left, right)` | `u32` | Returns the smaller unsigned value. | +| `std.math.minI32(left, right)` | `i32` | Returns the smaller signed 32-bit value. | +| `std.math.minUsize(left, right)` | `usize` | Returns the smaller pointer-width unsigned value. | +| `std.math.minI64(left, right)` | `i64` | Returns the smaller signed 64-bit value. | +| `std.math.minU64(left, right)` | `u64` | Returns the smaller unsigned 64-bit value. | +| `std.math.maxU32(left, right)` | `u32` | Returns the larger unsigned value. | +| `std.math.maxI32(left, right)` | `i32` | Returns the larger signed 32-bit value. | +| `std.math.maxUsize(left, right)` | `usize` | Returns the larger pointer-width unsigned value. | +| `std.math.maxI64(left, right)` | `i64` | Returns the larger signed 64-bit value. | +| `std.math.maxU64(left, right)` | `u64` | Returns the larger unsigned 64-bit value. | +| `std.math.clampU32(value, low, high)` | `u32` | Clamps between the two bounds; swapped bounds are normalized. | +| `std.math.clampI32(value, low, high)` | `i32` | Clamps a signed 32-bit value; swapped bounds are normalized. | +| `std.math.clampUsize(value, low, high)` | `usize` | Clamps a pointer-width unsigned value; swapped bounds are normalized. | +| `std.math.clampI64(value, low, high)` | `i64` | Clamps a signed 64-bit value; swapped bounds are normalized. | +| `std.math.clampU64(value, low, high)` | `u64` | Clamps an unsigned 64-bit value; swapped bounds are normalized. | +| `std.math.absI32(value)` | `u32` | Returns the unsigned magnitude of a signed 32-bit value. | +| `std.math.absI64(value)` | `u64` | Returns the unsigned magnitude of a signed 64-bit value. | +| `std.math.checkedAddU32(left, right)` | `Maybe` | Adds only when the result fits in `u32`. | +| `std.math.checkedSubU32(left, right)` | `Maybe` | Subtracts only when the result fits in `u32`. | +| `std.math.checkedMulU32(left, right)` | `Maybe` | Multiplies only when the result fits in `u32`. | +| `std.math.checkedAddUsize(left, right)` | `Maybe` | Adds only when the result fits in `usize`. | +| `std.math.checkedSubUsize(left, right)` | `Maybe` | Subtracts only when the result fits in `usize`. | +| `std.math.checkedMulUsize(left, right)` | `Maybe` | Multiplies only when the result fits in `usize`. | +| `std.math.checkedAddI32(left, right)` | `Maybe` | Adds only when the result fits in `i32`. | +| `std.math.checkedSubI32(left, right)` | `Maybe` | Subtracts only when the result fits in `i32`. | +| `std.math.checkedMulI32(left, right)` | `Maybe` | Multiplies only when the result fits in `i32`. | +| `std.math.saturatingAddU32(left, right)` | `u32` | Adds and clamps overflow to `u32` max. | +| `std.math.saturatingSubU32(left, right)` | `u32` | Subtracts and clamps underflow to `0`. | +| `std.math.saturatingMulU32(left, right)` | `u32` | Multiplies and clamps overflow to `u32` max. | +| `std.math.saturatingAddUsize(left, right)` | `usize` | Adds and clamps overflow to `usize` max. | +| `std.math.saturatingSubUsize(left, right)` | `usize` | Subtracts and clamps underflow to `0`. | +| `std.math.saturatingMulUsize(left, right)` | `usize` | Multiplies and clamps overflow to `usize` max. | +| `std.math.saturatingAddI32(left, right)` | `i32` | Adds and clamps overflow to the nearest `i32` bound. | +| `std.math.saturatingSubI32(left, right)` | `i32` | Subtracts and clamps overflow to the nearest `i32` bound. | +| `std.math.saturatingMulI32(left, right)` | `i32` | Multiplies and clamps overflow to the nearest `i32` bound. | +| `std.math.gcdU32(left, right)` | `u32` | Euclidean greatest common divisor. | +| `std.math.lcmU32(left, right)` | `u32` | Least common multiple; returns `0` when either input is `0`. | +| `std.math.checkedLcmU32(left, right)` | `Maybe` | Least common multiple only when the result fits in `u32`. | +| `std.math.powU32(base, exponent)` | `u32` | Fixed-width exponentiation by squaring. | +| `std.math.checkedPowU32(base, exponent)` | `Maybe` | Exponentiation only when the result fits in `u32`. | +| `std.math.modPowU32(base, exponent, modulus)` | `u32` | Modular exponentiation; returns `0` for modulus `0`. | +| `std.math.isPrimeU32(value)` | `Bool` | Trial division primality for unsigned integers. | +| `std.math.isEvenU32(value)` | `Bool` | Reports whether a `u32` value is even. | +| `std.math.isOddU32(value)` | `Bool` | Reports whether a `u32` value is odd. | +| `std.math.sqrtFloorU32(value)` | `u32` | Integer square root rounded down. | +| `std.math.factorialU32(value)` | `Maybe` | Factorial only when the result fits in `u32`. | +| `std.math.binomialU32(n, k)` | `Maybe` | Binomial coefficient only when the result fits in `u32`. | +| `std.math.divisorCountU32(value)` | `u32` | Counts positive divisors; returns `0` for `0`. | +| `std.math.properDivisorSumU32(value)` | `u32` | Sums positive divisors smaller than `value`. | + +Current scope: + +- Helpers are pure, target-neutral fixed-width integer operations. +- Checked helpers return `Maybe` instead of wrapping or trapping. +- Saturating helpers clamp to documented integer bounds. +- The module does not provide floating-point math, big integers, or arbitrary-precision number theory. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + if std.math.gcdU32(84, 30) == 6 && std.math.isPrimeU32(31) { + check world.out.write("math helper ok\n") + } +} +``` + +## Design Notes + +`std.math` does not allocate and does not require a hosted runtime capability. +Names include the integer width because Zero does not overload standard-library +helpers by argument type. + +Number-theory helpers are intentionally simple and deterministic. They are +suitable for small fixed-width tasks, examples, and compiler-portable checks. +Large-number algorithms should be added as explicit APIs with documented bounds +instead of hidden heap allocation or implicit widening. diff --git a/docs/articles/modules/mem.md b/docs/articles/modules/mem.md new file mode 100644 index 0000000..caf9e7f --- /dev/null +++ b/docs/articles/modules/mem.md @@ -0,0 +1,172 @@ +## When To Use std.mem + +In Zerolang, use `std.mem` for spans, clamped span views, copy/fill, +fixed-buffer allocators, explicit byte buffers, and memory-budget-visible +collection foundations. + +Runnable today: + +The item-generic helpers currently support these direct-backed scalar element +types: `Bool`, `u8`, `u16`, `usize`, `i32`, `u32`, `i64`, and `u64`. + +| API | Return | Notes | +| --- | --- | --- | +| `std.mem.copy(dst, src)` | `usize` | Copies from `Span` into caller-owned `MutSpan` storage and returns the copied byte count. | +| `std.mem.copyItems(dst, src)` | `usize` | Copies matching scalar `Span` items into caller-owned mutable item storage and returns the copied item count. | +| `std.mem.fill(dst, value)` | `usize` | Fills caller-owned `MutSpan` storage with a `u8` byte and returns the filled byte count. | +| `std.mem.fillItems(dst, value)` | `usize` | Fills caller-owned mutable scalar item storage with a matching `T` value and returns the filled item count. | +| `std.mem.eql(a, b)` | `Bool` | Compares string-backed byte inputs. | +| `std.mem.span(value)` | `Span` | Builds a native `Span` view over a string literal. | +| `std.mem.contains(items, needle)` | `Bool` | Searches readable contiguous non-owned scalar `T` storage for a matching value. | +| `std.mem.compareI32(left, right)` | `i32` | Lexicographically compares two `Span` values and returns `-1`, `0`, or `1`. | +| `std.mem.compareU8(left, right)` | `i32` | Lexicographically compares two `Span` values and returns `-1`, `0`, or `1`. | +| `std.mem.compareBytes(left, right)` | `i32` | Alias-style byte lexicographic comparison for `Span` values. | +| `std.mem.compareU32(left, right)` | `i32` | Lexicographically compares two `Span` values and returns `-1`, `0`, or `1`. | +| `std.mem.compareUsize(left, right)` | `i32` | Lexicographically compares two `Span` values and returns `-1`, `0`, or `1`. | +| `std.mem.startsWith(items, prefix)` | `Bool` | Checks whether a scalar span begins with a matching prefix span. | +| `std.mem.endsWith(items, suffix)` | `Bool` | Checks whether a scalar span ends with a matching suffix span. | +| `std.mem.splitBefore(items, delimiter)` | `Span` | Returns the read-only scalar item prefix before the first delimiter, or the full span when absent. | +| `std.mem.splitAfter(items, delimiter)` | `Span` | Returns the read-only scalar item suffix after the first delimiter, or an empty span when absent. | +| `std.mem.isEmpty(items)` | `Bool` | Reports whether readable contiguous scalar item storage has zero items. | +| `std.mem.chunkCount(items, chunkSize)` | `usize` | Returns the number of non-overlapping chunks needed to cover the span, or `0` when `chunkSize` is zero. | +| `std.mem.chunk(items, chunkIndex, chunkSize)` | `Span` | Returns a clamped read-only scalar item chunk by zero-based chunk index. | +| `std.mem.windowCount(items, windowSize)` | `usize` | Returns the number of overlapping fixed-size windows available in a span, or `0` when the size is zero or larger than the span. | +| `std.mem.window(items, windowIndex, windowSize)` | `Span` | Returns an overlapping read-only scalar item window by zero-based window index. | +| `std.mem.advance(items, cursor, count)` | `usize` | Returns a clamped cursor advanced by at most `count` items. | +| `std.mem.cursorDone(items, cursor)` | `Bool` | Reports whether a cursor is at or past the end of a span. | +| `std.mem.remaining(items, cursor)` | `Span` | Returns the clamped read-only scalar item view from `cursor` to the end. | +| `std.mem.cursorChunk(items, cursor, count)` | `Span` | Returns a clamped read-only scalar item window beginning at `cursor`. | +| `std.mem.prefix(items, count)` | `Span` | Returns a clamped read-only scalar item prefix view. | +| `std.mem.dropPrefix(items, count)` | `Span` | Returns a clamped read-only scalar item view after the first `count` items. | +| `std.mem.suffix(items, count)` | `Span` | Returns a clamped read-only scalar item suffix view. | +| `std.mem.dropSuffix(items, count)` | `Span` | Returns a clamped read-only scalar item view before the last `count` items. | +| `std.mem.slice(items, start, count)` | `Span` | Returns a clamped read-only scalar item window beginning at `start`. | +| `std.mem.len(bytes)` | `usize` | Returns the length of a fixed array, `Span`, or `MutSpan`. | +| `std.mem.get(bytes, index)` | `Maybe` | Reads one indexed element from an array/span-like value when the index is in bounds. | +| `std.mem.eqlBytes(a, b)` | `Bool` | Compares two `Span`/`MutSpan` values with the same element type. | +| `std.mem.nullAlloc()` | `NullAlloc` | Creates an allocator that always returns `null`, useful for proving code does not allocate. | +| `std.mem.fixedBufAlloc(buffer)` | `FixedBufAlloc` | Creates a mutable fixed-buffer allocator from caller-owned `MutSpan` bytes. | +| `std.mem.arena(buffer)` | `FixedBufAlloc` | Arena-style alias over the fixed-buffer allocator model; `reset` rewinds the caller-owned storage. | +| `std.mem.pageAlloc()` | `PageAlloc` | Explicit host allocator handle metadata; it never creates an ambient global allocator. | +| `std.mem.generalAlloc()` | `GeneralAlloc` | Explicit general allocator handle metadata; callers still pass allocator state deliberately. | +| `std.mem.allocBytes(alloc, len)` | `Maybe>` | Allocates bytes from `NullAlloc` or a mutable `FixedBufAlloc` binding. | +| `std.mem.byteBuf(alloc, len)` | `Maybe>` | Creates an owned byte buffer backed by explicit caller-provided allocator storage. | +| `std.mem.bufBytes(&buf)` | `MutSpan` | Borrows writable bytes from an owned `ByteBuf`. | +| `std.mem.bufLen(&buf)` | `usize` | Returns the live length of a `ByteBuf`. | +| `std.mem.reset(&mut arena)` | `Void` | Resets caller-owned arena/fixed-buffer allocation state. | +| `std.mem.capacity(arena)` | `usize` | Reports fixed-buffer capacity. | +| `std.mem.vec(storage)` | `Vec` | Monomorphic byte vector over caller-owned mutable storage. | +| `std.mem.vecPush(&mut vec, value)` | `Bool` | Appends one byte when capacity remains; returns `false` instead of growing implicitly. | +| `std.mem.vecBytes(&vec)` | `Span` | Borrows the live bytes in the vector without copying. | +| `std.mem.vecGet(&vec, index)` | `Maybe` | Reads one live vector byte when `index` is in bounds. | +| `std.mem.vecSet(&mut vec, index, value)` | `Bool` | Replaces one live vector byte when `index` is in bounds; returns `false` out of bounds. | +| `std.mem.vecClear(&mut vec)` | `usize` | Resets live length to zero and keeps caller-owned storage available for reuse. | +| `std.mem.vecPop(&mut vec)` | `Bool` | Removes one live byte when the vector is non-empty. | +| `std.mem.vecTruncate(&mut vec, len)` | `usize` | Shrinks the live length to at most `len` and returns the resulting length. | +| `std.mem.vecRemoveSwap(&mut vec, index)` | `Bool` | Removes one live byte by replacing it with the last live byte. Returns `false` out of bounds. | +| `std.mem.vecIndex(&vec, value)` | `usize` | Returns the first live index for `value`, or the current live length when absent. | +| `std.mem.vecContains(&vec, value)` | `Bool` | Reports whether a byte value is present in the live vector prefix. | +| `std.mem.vecInsertUnique(&mut vec, value)` | `Bool` | Appends `value` only when it is absent and capacity remains. | +| `std.mem.vecRemoveValue(&mut vec, value)` | `Bool` | Swap-removes the first matching live byte by value. | +| `std.mem.vecLen(&vec)` | `usize` | Reports current vector length. | +| `std.mem.vecCapacity(&vec)` | `usize` | Reports caller-provided vector capacity. | +| `std.mem.vecRemaining(&vec)` | `usize` | Reports remaining byte-vector capacity. Returns `0` when the vector is full. | +| `std.mem.vecIsEmpty(&vec)` | `Bool` | Reports whether the vector has no live bytes. | +| `std.mem.vecIsFull(&vec)` | `Bool` | Reports whether the vector has no remaining capacity. | + +## Example + +```zero +type SliceView { + bytes: Span, + values: Span, +} + +pub fn main(world: World) -> Void raises { + let bytes: Span = std.mem.span("zero-memory") + let same: Span = std.mem.span("zero-memory") + var scratch: [11]u8 = [0_u8; 11] + let copied: usize = std.mem.copy(scratch, bytes) + var ints: [3]i32 = [1, 2, 3] + let intSpan: MutSpan = ints + let filled: usize = std.mem.fillItems(intSpan, 7) + let prefix: Span = std.mem.prefix(intSpan, 2) + let suffix: Span = std.mem.suffix(intSpan, 2) + let middle: Span = std.mem.slice(intSpan, 1, 1) + let before: Span = std.mem.splitBefore(intSpan, 7) + let after: Span = std.mem.splitAfter(intSpan, 7) + let chunk: Span = std.mem.chunk(intSpan, 1_usize, 2_usize) + let sliding: Span = std.mem.window(intSpan, 1_usize, 2_usize) + let cursor: usize = std.mem.advance(intSpan, 0_usize, 2_usize) + let rest: Span = std.mem.remaining(intSpan, cursor) + let view: SliceView = SliceView { bytes: bytes, values: intSpan } + let ordered: Bool = std.mem.compareI32(prefix, suffix) == 0 + let starts: Bool = std.mem.startsWith(view.bytes, std.mem.span("zero")) + let ends: Bool = std.mem.endsWith(view.bytes, std.mem.span("memory")) + if copied == 11 && filled == 3 && ordered && starts && ends && std.mem.len(view.bytes) == 11 && std.mem.eqlBytes(view.bytes, same) && std.mem.len(view.values) == 3 && std.mem.contains(view.values, 7) && std.mem.isEmpty(before) && std.mem.len(after) == 2 && std.mem.len(prefix) == 2 && std.mem.len(suffix) == 2 && std.mem.len(middle) == 1 && std.mem.len(chunk) == 1 && std.mem.len(sliding) == 2 && std.mem.len(rest) == 1 && !std.mem.cursorDone(intSpan, cursor) { + check world.out.write("memory type forms runnable\n") + } +} +``` + +## Allocator Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0] + var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage) + let bytes: Maybe> = std.mem.allocBytes(alloc, 4) + if bytes.has { + bytes.value[0] = 90 + check world.out.write("fixed buffer allocated\n") + } +} +``` + +Effects: none beyond writes performed by caller code. + +Allocation behavior: + +- `NullAlloc` always returns `null`. +- `FixedBufAlloc` and `Arena` return `MutSpan` views into caller-owned + storage. +- `ByteBuf` owns a slice of explicit allocator storage and never reaches for a + global heap. + +Use `std.mem.allocBytes(alloc, capacity)` when a byte-oriented API needs +allocator-backed mutable storage directly. `std.collections.fixedSet`, +`std.collections.fixedDeque`, and `std.collections.fixedMap` can use those +returned spans as caller-owned backing storage. + +Ownership: returned spans borrow from the original fixed buffer; no heap +ownership is created. + +Target support: current compiler targets. Direct native builds lower +`FixedBufAlloc` locals only; `PageAlloc`, `GeneralAlloc`, and `NullAlloc` +locals type-check but fail `zero build` with a `BLD004` diagnostic that points +back to `std.mem.fixedBufAlloc`. + +## Reporting Contract + +`zero mem --json [graph-input]` reports the allocator contract in machine-readable form: + +- `memoryBudgets`: stack, static, heap, arena, fixed-buffer, collection-capacity, allocator-capacity, requested-allocation, and linear-memory floor budgets. +- `allocatorFacts`: `NullAlloc`, `FixedBufAlloc`, `Arena`, `PageAlloc`, and + `GeneralAlloc` usage, capacity, failure behavior, and + hidden-global-allocator status. +- `allocationInstrumentation`: pay-as-used hooks for attempts, successes, failures, requested bytes, granted bytes, and peak live bytes. +- `collectionFacts`: fixed-capacity `Vec`, fixed-storage `std.collections` + helpers, and owned `ByteBuf`, including growth/failure/cleanup behavior. +- `safetyFacts`: the selected profile plus the current bounds, + initialization, aliasing, lifetime, ownership, span, MIR, literal integer + range-check, runtime arithmetic, and unchecked-surface facts. + +All heap budgets are explicit. A program that only uses `std.mem` remains at +`heapBytes: 0`, `globalHeapBytes: 0`, and `hiddenHeapAllocation: false` unless +an allocator API documents otherwise. + +## Design Notes + +No standard collection may silently allocate from a global heap. Heap-owning APIs +will require an allocator capability and document ownership, capacity, and +cleanup. diff --git a/docs/articles/modules/net.md b/docs/articles/modules/net.md new file mode 100644 index 0000000..004d359 --- /dev/null +++ b/docs/articles/modules/net.md @@ -0,0 +1,47 @@ +## When To Use std.net + +In Zerolang, use `std.net` for network capability metadata, local address construction, +timeouts, and bootstrap client/listener handles. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.net.host()` | `Net` | Creates the hosted network capability. | +| `std.net.address(host, port)` | `Address` | Builds an address value without allocation. | +| `std.net.dnsName(address)` | `String` | Reads the address host name. | +| `std.net.withTimeout(address, duration)` | `Address` | Returns address metadata with a timeout. | +| `std.net.localhost(port)` | `Address` | Builds a `localhost` address with the requested port. | +| `std.net.loopback(port)` | `Address` | Builds a `127.0.0.1` loopback address with the requested port. | +| `std.net.connect(net, address)` | `Maybe` | Returns a bootstrap connection handle when available. | +| `std.net.listen(net, address)` | `Maybe` | Returns a bootstrap listener handle when available. | + +Metadata labels: + +- effects: net +- allocation behavior: no allocation +- target support: address helpers are target-neutral; host/connect/listen require a net-capable target +- error behavior: connection helpers return `Maybe` +- ownership notes: no stream ownership transfer in the current handle model +- example: `conformance/native/pass/std-net-http-breadth.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let net: Net = std.net.host() + let addr: Address = std.net.withTimeout(std.net.localhost(8080_u16), std.time.ms(250)) + let loopback: Address = std.net.loopback(8080_u16) + let conn: Maybe = std.net.connect(net, addr) + if conn.has && std.mem.eql(std.net.dnsName(addr), "localhost") && std.mem.eql(std.net.dnsName(loopback), "127.0.0.1") { + check world.out.write("net ok\n") + } +} +``` + +## Design Notes + +`std.net` exposes network capability metadata and bootstrap handles. Current +fixtures expect connection and listener handles to be absent. It does not +provide socket read/write APIs in the current public surface. Outbound HTTP is +exposed through `std.http.fetch(...)` rather than through raw sockets. diff --git a/docs/articles/modules/parse.md b/docs/articles/modules/parse.md new file mode 100644 index 0000000..290b65e --- /dev/null +++ b/docs/articles/modules/parse.md @@ -0,0 +1,78 @@ +## When To Use std.parse + +In Zerolang, use `std.parse` for allocation-free byte scanners and scalar parsers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.parse.isAsciiDigit(value)` | `Bool` | Checks whether the first byte is `0` through `9`. | +| `std.parse.isAsciiAlpha(value)` | `Bool` | Checks whether the first byte is ASCII alphabetic. | +| `std.parse.isIdentifierStart(value)` | `Bool` | Checks whether the first byte can start a Zero identifier. | +| `std.parse.isWhitespace(value)` | `Bool` | Checks ASCII whitespace. | +| `std.parse.scanDigits(value)` | `usize` | Counts a leading run of ASCII digits. | +| `std.parse.scanIdentifier(value)` | `usize` | Counts a leading identifier token. | +| `std.parse.scanWhitespace(value)` | `usize` | Counts leading spaces, tabs, line feeds, and carriage returns. | +| `std.parse.scanUntilByte(value, byte)` | `usize` | Returns the first matching byte index or the input length. | +| `std.parse.tokenAscii(value)` | `Span` | Borrows the first non-whitespace ASCII token. | +| `std.parse.parseBool(value)` | `Maybe` | Parses `true` or `false`. | +| `std.parse.parseDuration(value)` | `Maybe` | Parses signed `ns`, `us`, `ms`, `s`, `m`, and `h` duration components such as `1h30m`. | +| `std.parse.parseByteSize(value)` | `Maybe` | Parses bytes plus `KB`, `MB`, `GB`, `KiB`, `MiB`, and `GiB` suffixes. | +| `std.parse.parseI32(value)` | `Maybe` | Parses a full signed 32-bit decimal value. | +| `std.parse.parseI32Base(value, base)` | `Maybe` | Parses a full signed 32-bit value in base 2 through 36. | +| `std.parse.parseI32Prefix(value)` | `Maybe` | Parses signed decimal, `0x`, `0o`, or `0b` input. | +| `std.parse.parseI64(value)` | `Maybe` | Parses a full signed 64-bit decimal value. | +| `std.parse.parseI64Base(value, base)` | `Maybe` | Parses a full signed 64-bit value in base 2 through 36. | +| `std.parse.parseI64Prefix(value)` | `Maybe` | Parses signed decimal, `0x`, `0o`, or `0b` input. | +| `std.parse.parseU8(value)` | `Maybe` | Parses a full decimal byte value. | +| `std.parse.parseU16(value)` | `Maybe` | Parses a full decimal unsigned 16-bit value. | +| `std.parse.parseU32(value)` | `Maybe` | Parses a full decimal unsigned 32-bit value. | +| `std.parse.parseU32Base(value, base)` | `Maybe` | Parses a full unsigned 32-bit value in base 2 through 36. | +| `std.parse.parseU32Prefix(value)` | `Maybe` | Parses unsigned decimal, `0x`, `0o`, or `0b` input. | +| `std.parse.parseU64(value)` | `Maybe` | Parses a full decimal unsigned 64-bit value. | +| `std.parse.parseU64Base(value, base)` | `Maybe` | Parses a full unsigned 64-bit value in base 2 through 36. | +| `std.parse.parseU64Prefix(value)` | `Maybe` | Parses unsigned decimal, `0x`, `0o`, or `0b` input. | +| `std.parse.parseUsize(value)` | `Maybe` | Parses a full decimal `usize` value. | +| `std.parse.parseUsizeBase(value, base)` | `Maybe` | Parses a full `usize` value in base 2 through 36. | +| `std.parse.parseUsizePrefix(value)` | `Maybe` | Parses `usize` decimal, `0x`, `0o`, or `0b` input. | + +Current limits: + +- Source position and span types. +- Rich cursor objects beyond the current allocation-free scanner primitives. +- Token and diagnostic data shared by language and data parsers. + +## Example + +```zero +use std.parse + +pub fn main(world: World) -> Void raises { + let digit: Bool = std.parse.isAsciiDigit("7") + let ident: Bool = std.parse.isIdentifierStart("_") + let scanned: usize = std.parse.scanDigits("123abc") + let parsed: Maybe = std.parse.parseU16("8080") + let signed: Maybe = std.parse.parseI32Prefix("-0x2a") + let signed64: Maybe = std.parse.parseI64("-9223372036854775808") + let hex: Maybe = std.parse.parseU32Base("ff", 16_u32) + let hex64: Maybe = std.parse.parseU64Prefix("0xffffffffffffffff") + let duration: Maybe = std.parse.parseDuration("1h30m") + let size: Maybe = std.parse.parseByteSize("2MiB") + let token: Span = std.parse.tokenAscii(" zero text") + if digit && ident && scanned == 3 && parsed.has && parsed.value == 8080 && signed.has && signed.value == -42 && signed64.has && signed64.value == 0 - 9223372036854775807 - 1 && hex.has && hex.value == 255_u32 && hex64.has && hex64.value == 18446744073709551615_u64 && duration.has && std.time.asSecondsFloor(duration.value) == 5400_i64 && size.has && size.value == 2097152 && std.mem.eql(token, "zero") { + check world.out.write("parse primitives ok\n") + } +} +``` + +## Design Notes + +The module stays byte-oriented so compiler, config, and codec code can parse +without Unicode scalar handling or heap allocation. Public helpers accept byte +spans, so callers can parse string literals, fixed arrays, and runtime buffers. + +Integer parsers return `Maybe` instead of allocating diagnostics. Base parsers +accept bases 2 through 36, consume the full input, and reject overflow. Prefix +parsers recognize decimal by default plus `0x`, `0o`, and `0b` for 32-bit, +64-bit, and `usize` widths. Duration and byte-size parsers also consume the +full input and reject overflow. diff --git a/docs/articles/modules/path.md b/docs/articles/modules/path.md new file mode 100644 index 0000000..488565a --- /dev/null +++ b/docs/articles/modules/path.md @@ -0,0 +1,51 @@ +## When To Use std.path + +In Zerolang, use `std.path` for lexical path operations that borrow from input +paths or write into caller-owned buffers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.path.basename(path)` | `String` | Borrows the final lexical component of `path`. | +| `std.path.dirname(path)` | `String` | Borrows or returns the lexical parent portion of `path`. | +| `std.path.extension(path)` | `String` | Borrows the suffix after the last `.` in the final component. | +| `std.path.stem(path)` | `String` | Borrows the final component without its extension. | +| `std.path.splitDir(path)` | `String` | Borrows the directory side of the final split. | +| `std.path.splitBase(path)` | `String` | Borrows the basename side of the final split. | +| `std.path.isAbs(path)` | `Bool` | Returns true for paths that begin with a path separator. | +| `std.path.componentCount(path)` | `usize` | Counts non-empty lexical path components. | +| `std.path.component(path, index)` | `Maybe` | Borrows one non-empty lexical component by index. | +| `std.path.abs(buffer, base, target)` | `Maybe` | Copies `target` when already absolute, or joins `base` and `target` into caller storage. | +| `std.path.join(buffer, left, right)` | `Maybe` | Joins two path fragments into caller-provided fixed buffer storage. | +| `std.path.normalize(buffer, path)` | `Maybe` | Collapses repeated `/`, `.`, and lexical `..` segments into caller-provided storage. | +| `std.path.relative(buffer, base, target)` | `Maybe` | Produces a target-relative lexical path when possible, or copies `target`. | + +Current scope: + +- Helpers are target-neutral lexical operations over `/` and `\` separators. +- Buffer-writing helpers return `null` when caller storage is too small. +- The module does not implement platform-specific path rules, drive prefixes, or filesystem access. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + let path: Maybe = std.path.join(storage, ".zero", "example.txt") + if path.has { + check world.out.write(path.value) + check world.out.write("\n") + } +} +``` + +## Design Notes + +`std.path.basename`, `dirname`, `extension`, `stem`, `splitDir`, `splitBase`, +and `component` return borrowed views into the input path. `std.path.abs`, +`join`, `normalize`, and `relative` write into caller storage and return `null` +when the buffer is too small. They do not allocate. + +The current behavior uses `/` as the portable package/example separator. These +helpers are lexical string helpers, not target-specific filesystem resolvers. diff --git a/docs/articles/modules/proc.md b/docs/articles/modules/proc.md new file mode 100644 index 0000000..b2ff948 --- /dev/null +++ b/docs/articles/modules/proc.md @@ -0,0 +1,122 @@ +## When To Use std.proc + +In Zerolang, use `std.proc` for hosted process helpers behind explicit process +capability boundaries. The surface is host-only and supports both status-style +helpers and owned child handles for incremental I/O. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.proc.spawn(command)` | `ProcStatus` | Runs an argv-style command with inherited stdio through the explicit proc capability surface and returns its status. | +| `std.proc.spawnInherit(command)` | `ProcStatus` | Runs an argv-style command while inheriting stdin, stdout, and stderr from the parent process. | +| `std.proc.spawnInheritArgs(program, args, cwd, env)` | `ProcStatus` | Runs a program path plus newline-separated argv entries while inheriting stdio, using a working directory and newline-separated `KEY=value` environment bindings. | +| `std.proc.exitCode(status)` | `i32` | Reads the process status code. | +| `std.proc.succeeded(status)` | `Bool` | Reports whether the status exit code is `0`. | +| `std.proc.failed(status)` | `Bool` | Reports whether the status exit code is nonzero. | +| `std.proc.runOk(command)` | `Bool` | Spawns a hosted command and reports whether the resulting status succeeded. | +| `std.proc.runCode(command)` | `i32` | Spawns a hosted command and returns its exit code. | +| `std.proc.capture(command, buffer)` | `Maybe` | Runs an argv-style command and captures stdout into caller storage. Returns `null` on parse failure, spawn failure, nonzero exit, unsupported target, or output truncation. | +| `std.proc.captureArgs(program, args, buffer)` | `Maybe` | Runs a program path plus newline-separated argv entries and captures stdout into caller storage. | +| `std.proc.captureFiles(command, stdoutPath, stderrPath)` | `ProcStatus` | Runs an argv-style command and writes stdout and stderr to hosted paths. Returns `127` when the command cannot be parsed, spawned, waited on, or the output files cannot be opened. | +| `std.proc.captureFilesArgs(program, args, stdoutPath, stderrPath)` | `ProcStatus` | Runs a program path plus newline-separated argv entries and redirects stdout and stderr to hosted paths. | +| `std.proc.spawnChild(command)` | `ProcChild` | Starts a hosted child process with piped stdin, stdout, and stderr. Returns an invalid handle when the process cannot be created. | +| `std.proc.spawnChildIn(command, cwd)` | `ProcChild` | Starts a hosted child process in a working directory with piped stdin, stdout, and stderr. Returns an invalid handle when the cwd is invalid or the process cannot be created. | +| `std.proc.spawnChildInEnv(command, cwd, env)` | `ProcChild` | Starts a hosted child process in a working directory with piped stdin/stdout/stderr and explicit newline-separated `KEY=value` environment bindings. | +| `std.proc.spawnChildArgs(program, args, cwd, env)` | `ProcChild` | Starts a hosted child process from a program path plus newline-separated argv entries, working directory, and newline-separated `KEY=value` environment bindings. | +| `std.proc.childValid(child)` | `Bool` | Reports whether the handle currently names an open child slot. | +| `std.proc.running(child)` | `Bool` | Polls the child process without blocking. | +| `std.proc.wait(child)` | `ProcStatus` | Waits for process exit and returns its status. | +| `std.proc.kill(child)` | `Bool` | Sends the child a termination signal on supported hosts. | +| `std.proc.interrupt(child)` | `Bool` | Sends the child an interrupt signal on supported hosts. | +| `std.proc.close(child)` | `Bool` | Closes the handle and any remaining pipes. | +| `std.proc.closeStdin(child)` | `Bool` | Closes the child stdin pipe while keeping stdout, stderr, and status available. | +| `std.proc.pid(child)` | `i32` | Returns the hosted process id for a child handle, or `0` when unavailable. | +| `std.proc.pidRunning(pid)` | `Bool` | Reports whether a hosted process id appears to be running. | +| `std.proc.killPid(pid)` | `Bool` | Sends a termination signal to a hosted process id on supported hosts. | +| `std.proc.interruptPid(pid)` | `Bool` | Sends an interrupt signal to a hosted process id on supported hosts. | +| `std.proc.killGroupPid(pid)` | `Bool` | Sends a termination signal to the hosted process group whose id is `pid` on supported hosts. | +| `std.proc.interruptGroupPid(pid)` | `Bool` | Sends an interrupt signal to the hosted process group whose id is `pid` on supported hosts. | +| `std.proc.readStdout(child, buffer)` | `Maybe` | Nonblocking read from the child's stdout pipe into caller storage. Returns `null` when no bytes are currently available or the stream is closed. | +| `std.proc.readStderr(child, buffer)` | `Maybe` | Nonblocking read from the child's stderr pipe into caller storage. Returns `null` when no bytes are currently available or the stream is closed. | +| `std.proc.writeStdin(child, bytes)` | `Maybe` | Nonblocking write to the child's stdin pipe. Returns `null` when the stream is not writable. | + +Metadata labels: + +- effects: proc +- allocation behavior: child handles may keep runtime-owned pipe buffers while + `wait` drains stdout, stderr, or PTY output +- target support: hosted targets with the `proc` capability +- error behavior: `spawn`, `spawnInherit`, `captureFiles`, and `wait` return `ProcStatus`; `exitCode` is infallible; `capture` and child I/O helpers return `null` when they cannot produce a complete result +- ownership notes: `ProcChild` values name runtime-owned process slots; call `wait` when process status matters and `close` when the handle is no longer needed +- example: `examples/std-platform.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let status: ProcStatus = std.proc.spawn("zero-noop") + var storage: [64]u8 = [0_u8; 64] + let captured: Maybe = std.proc.capture("printf proc-capture", storage) + let files: ProcStatus = std.proc.captureFiles("sh -c 'printf proc-out; printf proc-err >&2'", ".zero/proc.out", ".zero/proc.err") + if std.proc.succeeded(status) && std.proc.succeeded(files) && std.proc.runOk("sh -c true") && std.proc.runCode("sh -c true") == 0 && captured.has { + check world.out.write("proc ok\n") + } +} +``` + +Incremental child I/O uses caller-owned buffers: + +```zero +pub fn main(world: World) -> Void raises { + let child: ProcChild = std.proc.spawnChild("zero-noop") + var stdoutStorage: [64]u8 = [0_u8; 64] + let out: Maybe = std.proc.readStdout(child, stdoutStorage) + if out.has { + check world.out.write("child stdout available\n") + } + let pid: i32 = std.proc.pid(child) + let status: ProcStatus = std.proc.wait(child) + let closed: Bool = std.proc.close(child) + if pid > 0 && std.proc.succeeded(status) && closed { + check world.out.write("child ok\n") + } +} +``` + +## Design Notes + +`std.proc` requires a hosted target that advertises the `proc` capability. +Targets without process support, including Windows hosts until their process +runtime is implemented, must reject process helpers before code generation. + +They should not compile a placeholder process implementation. + +`capture` and `captureFiles` do not invoke a shell. They split simple +argv-style command text and support quoted arguments. `capture` captures stdout +only into caller storage. `captureFiles` redirects stdout and stderr to +separate hosted paths. + +`spawnInherit` uses the same argv-style parser and leaves stdin, stdout, and +stderr connected to the parent process. `spawnInheritArgs` uses an explicit +program plus newline-separated argv entries, working directory, and environment +block. Use inherited stdio for editor, pager, and terminal program launches +where captured pipes would be the wrong interface. + +Child handles use the same command parser when created from command text. The +`*Args` helpers avoid command-text parsing: `program` is argv[0], and each +non-empty line in `args` becomes one following argv entry with spaces preserved. +`captureArgs` captures stdout into caller storage, `captureFilesArgs` redirects +stdout and stderr to hosted paths, and `spawnChildArgs` returns nonblocking +pipes so event loops can poll process state and terminal input without owning +threads. `wait` drains child output into the handle before reaping so post-wait +`readStdout` and `readStderr` calls can still consume buffered data. + +Hosted child helpers start children in their own process group where the target +platform supports it. Use `killGroupPid` or `interruptGroupPid` when a stored +pid should stop a command tree instead of only the direct parent process. + +`spawnChildInEnv`, `spawnInheritArgs`, and `spawnChildArgs` accept a +newline-separated env block such as `"TOKEN=...\nMODE=batch"`. Empty lines are +ignored. Invalid entries or oversized env blocks make the helper fail: status +helpers return an error status and child helpers return an invalid handle. diff --git a/docs/articles/modules/pty.md b/docs/articles/modules/pty.md new file mode 100644 index 0000000..7ba3c20 --- /dev/null +++ b/docs/articles/modules/pty.md @@ -0,0 +1,78 @@ +## When To Use std.pty + +In Zerolang, use `std.pty` for hosted child processes that need terminal +semantics instead of plain pipes. PTY children are useful for interactive CLIs, +REPLs, shells, editors, pagers, and tools that change behavior when stdout is a +terminal. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.pty.spawn(command)` | `ProcChild` | Starts an argv-style command attached to a pseudoterminal. | +| `std.pty.spawnIn(command, cwd)` | `ProcChild` | Starts a PTY child in a hosted working directory. | +| `std.pty.spawnInEnv(command, cwd, env)` | `ProcChild` | Starts a PTY child with a working directory and newline-separated `KEY=value` environment bindings. | +| `std.pty.spawnArgs(program, args, cwd, env)` | `ProcChild` | Starts a program path plus newline-separated argv entries attached to a pseudoterminal. | +| `std.pty.valid(child)` | `Bool` | Reports whether the handle currently names an open child slot. | +| `std.pty.childValid(child)` | `Bool` | Alias for `std.pty.valid`. | +| `std.pty.running(child)` | `Bool` | Polls the PTY child without blocking. | +| `std.pty.wait(child)` | `ProcStatus` | Waits for process exit and returns its status. | +| `std.pty.kill(child)` | `Bool` | Sends the child a termination signal on supported hosts. | +| `std.pty.interrupt(child)` | `Bool` | Sends the child an interrupt signal on supported hosts. | +| `std.pty.close(child)` | `Bool` | Closes the handle and remaining terminal resources. | +| `std.pty.pid(child)` | `i32` | Returns the hosted process id for the child, or `0` when unavailable. | +| `std.pty.read(child, buffer)` | `Maybe` | Nonblocking read from the PTY master into caller storage. | +| `std.pty.write(child, bytes)` | `Maybe` | Nonblocking write to the PTY master. | +| `std.pty.resize(child, columns, rows)` | `Bool` | Sets the child terminal size on supported hosts. | + +Metadata labels: + +- effects: proc +- allocation behavior: child handles may keep runtime-owned PTY output buffers while `wait` drains terminal output +- target support: hosted targets with the `proc` capability +- error behavior: spawn helpers return an invalid `ProcChild` handle on failure; `read` and `write` return `null` when no bytes can be transferred +- ownership notes: `ProcChild` values name runtime-owned process slots; call `wait` when process status matters and `close` when the handle is no longer needed + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let child: ProcChild = std.pty.spawnArgs("printf", "hello pty\n", ".", "") + let resized: Bool = std.pty.resize(child, 80_usize, 24_usize) + + var storage: [64]u8 = [0_u8; 64] + var saw_output: Bool = false + var attempts: usize = 0 + while attempts < 20_usize && !saw_output { + let read: Maybe = std.pty.read(child, storage) + if read.has { + let bytes: Span = std.io.written(storage, read.value) + saw_output = std.str.contains(bytes, "hello pty") + } + if !saw_output { + let slept: Bool = std.time.sleep(std.time.ms(10)) + } + attempts = attempts + 1_usize + } + + let status: ProcStatus = std.pty.wait(child) + if resized && saw_output && std.proc.succeeded(status) { + check world.out.write("pty ok\n") + } + let closed: Bool = std.pty.close(child) +} +``` + +## Design Notes + +`std.pty` returns the same `ProcChild` handle shape used by `std.proc`, but the +underlying child is connected to a pseudoterminal master. PTY output is a single +terminal byte stream: stderr is merged by the terminal, and `std.pty.read` +reads from that stream. +Targets without process support, including Windows hosts until their process +runtime is implemented, reject PTY helpers before code generation. +For short-lived programs, drain the PTY with `std.pty.read` before `wait`; some +hosts report the terminal as closed once the child exits. + +Use `std.proc.spawnChild*` for programs where separate stdout and stderr pipes +matter. Use `std.pty.spawn*` for programs where terminal behavior matters. diff --git a/docs/articles/modules/rand.md b/docs/articles/modules/rand.md new file mode 100644 index 0000000..9c4a9b9 --- /dev/null +++ b/docs/articles/modules/rand.md @@ -0,0 +1,50 @@ +## When To Use std.rand + +In Zerolang, use `std.rand` for deterministic random sources and target-gated entropy. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.rand.seed(value)` | `RandSource` | Creates a deterministic test source. | +| `std.rand.nextU32(&mut source)` | `u32` | Advances an explicit random source. | +| `std.rand.nextBool(&mut source)` | `Bool` | Advances an explicit random source and returns one random bit. | +| `std.rand.nextBelow(&mut source, bound)` | `Maybe` | Returns a value in `[0, bound)` using rejection sampling, or null when bound is zero. | +| `std.rand.rangeU32(&mut source, low, high)` | `Maybe` | Returns a value in `[low, high)` using rejection sampling, or null for an empty range. | +| `std.rand.entropyU32()` | `u32` | Reads target entropy where the target provides it. | +| `std.rand.entropySeed()` | `RandSource` | Creates a `RandSource` from target entropy where available. | +| `std.rand.entropyHex32(buffer)` | `Maybe>` | Writes an 8-byte lowercase entropy ID into caller storage. | + +Metadata labels: + +- effects: rand +- allocation behavior: no allocation; `entropyHex32` writes caller-provided storage +- target support: deterministic source is target-neutral; entropy requires a rand-capable target +- error behavior: bounded helpers return null for invalid bounds; `entropyHex32` returns null when storage is too small +- ownership notes: deterministic helpers mutate the caller-owned source +- example: `examples/std-platform.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var rng: RandSource = std.rand.seed(7_u32) + let first: u32 = std.rand.nextU32(&mut rng) + let second: Bool = std.rand.nextBool(&mut rng) + let bounded: Maybe = std.rand.nextBelow(&mut rng, 10_u32) + let ranged: Maybe = std.rand.rangeU32(&mut rng, 40_u32, 50_u32) + var id_buf: [8]u8 = [0_u8; 8] + let entropy_id: Maybe> = std.rand.entropyHex32(id_buf) + if first == 1025555898_u32 && second && bounded.has && ranged.has && entropy_id.has { + check world.out.write("rand ok\n") + } +} +``` + +## Design Notes + +Zero keeps random sources explicit. Deterministic tests use `std.rand.seed`; +bounded helpers use rejection sampling so the range is not modulo-biased. +Caller-facing IDs use `entropyHex32` when target entropy is available. + +Production entropy stays target-capability-gated. diff --git a/docs/articles/modules/regex.md b/docs/articles/modules/regex.md new file mode 100644 index 0000000..0a44cca --- /dev/null +++ b/docs/articles/modules/regex.md @@ -0,0 +1,100 @@ +## When To Use std.regex + +In Zerolang, use `std.regex` to match text against a documented ECMA-262-leaning +regular expression subset, such as JSON Schema `pattern` checks. + +Supported syntax: literals, `.`, character classes with negation, ranges, and +`\d \D \w \W \s \S`, anchors `^` `$` and word boundaries `\b` `\B`, greedy +quantifiers `*` `+` `?` `{m}` `{m,}` `{m,n}`, alternation `|`, and capturing or +`(?:...)` non-capturing groups (matching only; no capture extraction). Matching +is by Unicode codepoint over UTF-8 text and searches anywhere in the text unless +the pattern is anchored, like ECMAScript `RegExp.prototype.test`. When multiple +matches start at the same byte, span-returning helpers use the longest end +position, so `a|ab` finds `ab` in `ab`. + +Unsupported constructs never misparse silently. Compilation fails with a +structured status code: `1` backreference, `2` lookahead, `3` lookbehind, +`4` named group, `5` lazy quantifier, `6` group modifier or inline flags, +`7` unicode property escape, `8` invalid syntax, `9` invalid quantifier range, +`10` program over the buffer or 2048-byte limit, `11` pattern is not valid +UTF-8, `12` group nesting over depth 32. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.regex.compile(buffer, pattern)` | `Maybe>` | Compiles a pattern into a caller-owned buffer; returns the compiled program span or `null` on any compile failure. | +| `std.regex.compileStatus(buffer, pattern)` | `u32` | Compiles and returns `0` or the structured status code for diagnostics. | +| `std.regex.compileErrorOffset(buffer, pattern)` | `Maybe` | Returns the pattern byte offset for a compile failure, or `null` when the pattern compiles. | +| `std.regex.statusName(status)` | `String` | Names a status code, such as `unsupported backreference`. | +| `std.regex.isMatch(program, text)` | `Bool` | Tests text against a compiled program. Compile once, then match many times. | +| `std.regex.matches(pattern, text)` | `Maybe` | One-shot compile and match with an internal 1024-byte program buffer; returns `null` when the pattern does not compile. | +| `std.regex.contains(pattern, text)` | `Maybe` | Alias-shaped one-shot search helper; returns `null` when the pattern does not compile. | +| `std.regex.findIndex(pattern, text)` | `Maybe` | Returns the first matching byte index, the input length when absent, or `null` when the pattern does not compile. | +| `std.regex.find(pattern, text)` | `Maybe>` | Borrows the first matching span, or returns `null` when absent or invalid. | +| `std.regex.findCount(pattern, text)` | `Maybe` | Counts non-overlapping matches, or returns `null` when the pattern does not compile. | +| `std.regex.findNth(pattern, text, index)` | `Maybe>` | Borrows the zero-based non-overlapping match at `index`, or returns `null` when absent or invalid. | +| `std.regex.findNthIndex(pattern, text, index)` | `Maybe` | Returns the byte index of the zero-based non-overlapping match, the input length when absent, or `null` when invalid. | +| `std.regex.replace(buffer, pattern, text, replacement)` | `Maybe>` | Replaces non-overlapping matches with literal replacement bytes into caller storage. | +| `std.regex.splitCount(pattern, text)` | `Maybe` | Counts fields separated by non-empty regex matches, or returns `null` when the pattern does not compile. | +| `std.regex.split(pattern, text, index)` | `Maybe>` | Borrows the zero-based field separated by non-empty regex matches, or returns `null` when absent or invalid. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [512]u8 = [0; 512] + let buffer: MutSpan = storage + let compiled: Maybe> = std.regex.compile(buffer, "^[a-z]+-\\d{2,4}$") + if !compiled.has { + return + } + let program: Span = compiled.value + let quick: Maybe = std.regex.matches("^(cat|dog)s?$", "dogs") + let first: Maybe> = std.regex.find("\\d+", "build-2048") + let second: Maybe> = std.regex.findNth("\\d+", "a1 b22 c333", 1) + var replaced_storage: [16]u8 = [0; 16] + let replaced: Maybe> = std.regex.replace(replaced_storage, "\\d+", "a1 b22", "#") + let fields: Maybe = std.regex.splitCount("[,;]", "red,green;blue") + let middle: Maybe> = std.regex.split("[,;]", "red,green;blue", 1) + if std.regex.isMatch(program, "build-2048") && !std.regex.isMatch(program, "build-1") && (quick.has && quick.value) && first.has && std.mem.eql(first.value, "2048") && second.has && std.mem.eql(second.value, "22") && replaced.has && std.mem.eql(replaced.value, "a# b#") && fields.has && fields.value == 3 && middle.has && std.mem.eql(middle.value, "green") { + check world.out.write("regex ok\n") + } +} +``` + +Diagnosing a rejected pattern: + +```zero +pub fn main(world: World) -> Void raises { + var storage: [128]u8 = [0; 128] + let buffer: MutSpan = storage + let status: u32 = std.regex.compileStatus(buffer, "(?=lookahead)") + let offset: Maybe = std.regex.compileErrorOffset(buffer, "(?=lookahead)") + if status != 0 { + check world.out.write(std.regex.statusName(status)) + check world.out.write("\n") + } +} +``` + +Effects: writes to caller-provided mutable storage for `compile`, +`compileStatus`, `compileErrorOffset`, and `replace`; other helpers only borrow +input spans or return scalar results. + +Allocation behavior: `compile`, `compileStatus`, and `compileErrorOffset` write +the caller program buffer. `replace` writes the caller output buffer. One-shot +search, split, and match helpers use fixed internal program storage and allocate +nothing on the heap. + +Error behavior: `compile` returns `null`, `compileStatus` returns a status code +naming the construct, and `compileErrorOffset` returns the byte offset for a +failed compile. One-shot helpers return `null` for invalid patterns; `isMatch` +returns `false` for malformed program spans or invalid UTF-8 text. + +`find`, `findNth`, `replace`, `split`, and their index/count variants use the +leftmost start and longest end for each match. `split` and `splitCount` use +non-empty regex matches as separators. Zero-length matches are ignored as +separators so callers get deterministic field traversal without a cursor object. + +Target support: current compiler targets. diff --git a/docs/articles/modules/search.md b/docs/articles/modules/search.md new file mode 100644 index 0000000..da9ea53 --- /dev/null +++ b/docs/articles/modules/search.md @@ -0,0 +1,104 @@ +## When To Use std.search + +In Zerolang, use `std.search` for scalar span lookup and binary-search helpers +over sorted caller-owned data. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.search.indexOf(items, needle)` | `usize` | Returns the first matching index or `std.mem.len(items)` when absent. | +| `std.search.lastIndexOf(items, needle)` | `usize` | Returns the last matching index or `std.mem.len(items)` when absent. | +| `std.search.lowerBoundI32(items, needle)` | `usize` | Returns the insertion point in sorted `Span` input. | +| `std.search.upperBoundI32(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span` input. | +| `std.search.binaryI32(items, needle)` | `usize` | Returns the matching sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedI32(items, needle)` | `Bool` | Checks whether sorted `Span` input contains the needle. | +| `std.search.countSortedI32(items, needle)` | `usize` | Counts equal values in sorted `Span` input. | +| `std.search.equalRangeI32(items, needle)` | `Span` | Borrows the equal-value range in sorted `Span` input. | +| `std.search.partitionPointI32(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. | +| `std.search.lowerBoundDescI32(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span` input. | +| `std.search.upperBoundDescI32(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span` input. | +| `std.search.binaryDescI32(items, needle)` | `usize` | Returns the matching descending sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedDescI32(items, needle)` | `Bool` | Checks whether descending sorted `Span` input contains the needle. | +| `std.search.countSortedDescI32(items, needle)` | `usize` | Counts equal values in descending sorted `Span` input. | +| `std.search.equalRangeDescI32(items, needle)` | `Span` | Borrows the equal-value range in descending sorted `Span` input. | +| `std.search.partitionPointDescI32(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. | +| `std.search.minI32(items)` | `Maybe` | Returns the minimum value in `Span` input or `null` for an empty span. | +| `std.search.maxI32(items)` | `Maybe` | Returns the maximum value in `Span` input or `null` for an empty span. | +| `std.search.minIndexI32(items)` | `usize` | Returns the first minimum-value index in `Span` input or `std.mem.len(items)` for an empty span. | +| `std.search.maxIndexI32(items)` | `usize` | Returns the first maximum-value index in `Span` input or `std.mem.len(items)` for an empty span. | +| `std.search.lowerBoundU32(items, needle)` | `usize` | Returns the insertion point in sorted `Span` input. | +| `std.search.upperBoundU32(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span` input. | +| `std.search.binaryU32(items, needle)` | `usize` | Returns the matching sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedU32(items, needle)` | `Bool` | Checks whether sorted `Span` input contains the needle. | +| `std.search.countSortedU32(items, needle)` | `usize` | Counts equal values in sorted `Span` input. | +| `std.search.equalRangeU32(items, needle)` | `Span` | Borrows the equal-value range in sorted `Span` input. | +| `std.search.partitionPointU32(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. | +| `std.search.lowerBoundDescU32(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span` input. | +| `std.search.upperBoundDescU32(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span` input. | +| `std.search.binaryDescU32(items, needle)` | `usize` | Returns the matching descending sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedDescU32(items, needle)` | `Bool` | Checks whether descending sorted `Span` input contains the needle. | +| `std.search.countSortedDescU32(items, needle)` | `usize` | Counts equal values in descending sorted `Span` input. | +| `std.search.equalRangeDescU32(items, needle)` | `Span` | Borrows the equal-value range in descending sorted `Span` input. | +| `std.search.partitionPointDescU32(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. | +| `std.search.minU32(items)` | `Maybe` | Returns the minimum value in `Span` input or `null` for an empty span. | +| `std.search.maxU32(items)` | `Maybe` | Returns the maximum value in `Span` input or `null` for an empty span. | +| `std.search.minIndexU32(items)` | `usize` | Returns the first minimum-value index in `Span` input or `std.mem.len(items)` for an empty span. | +| `std.search.maxIndexU32(items)` | `usize` | Returns the first maximum-value index in `Span` input or `std.mem.len(items)` for an empty span. | +| `std.search.lowerBoundUsize(items, needle)` | `usize` | Returns the insertion point in sorted `Span` input. | +| `std.search.upperBoundUsize(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span` input. | +| `std.search.binaryUsize(items, needle)` | `usize` | Returns the matching sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedUsize(items, needle)` | `Bool` | Checks whether sorted `Span` input contains the needle. | +| `std.search.countSortedUsize(items, needle)` | `usize` | Counts equal values in sorted `Span` input. | +| `std.search.equalRangeUsize(items, needle)` | `Span` | Borrows the equal-value range in sorted `Span` input. | +| `std.search.partitionPointUsize(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. | +| `std.search.lowerBoundDescUsize(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span` input. | +| `std.search.upperBoundDescUsize(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span` input. | +| `std.search.binaryDescUsize(items, needle)` | `usize` | Returns the matching descending sorted `Span` index or `std.mem.len(items)` when absent. | +| `std.search.containsSortedDescUsize(items, needle)` | `Bool` | Checks whether descending sorted `Span` input contains the needle. | +| `std.search.countSortedDescUsize(items, needle)` | `usize` | Counts equal values in descending sorted `Span` input. | +| `std.search.equalRangeDescUsize(items, needle)` | `Span` | Borrows the equal-value range in descending sorted `Span` input. | +| `std.search.partitionPointDescUsize(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. | +| `std.search.minUsize(items)` | `Maybe` | Returns the minimum value in `Span` input or `null` for an empty span. | +| `std.search.maxUsize(items)` | `Maybe` | Returns the maximum value in `Span` input or `null` for an empty span. | +| `std.search.minIndexUsize(items)` | `usize` | Returns the first minimum-value index in `Span` input or `std.mem.len(items)` for an empty span. | +| `std.search.maxIndexUsize(items)` | `usize` | Returns the first maximum-value index in `Span` input or `std.mem.len(items)` for an empty span. | + +Generic equality search supports the same non-owned scalar item types as +`std.mem.contains`. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let values: [5]i32 = [1, 2, 3, 5, 8] + let found: usize = std.search.binaryI32(values, 5) + let present: Bool = std.search.containsSortedI32(values, 5) + let after_five: usize = std.search.upperBoundI32(values, 5) + let fives: usize = std.search.countSortedI32(values, 5) + let five_range: Span = std.search.equalRangeI32(values, 5) + let partition_point: usize = std.search.partitionPointI32(values, 5) + let missing: usize = std.search.indexOf(values, 13) + let minimum: Maybe = std.search.minI32(values) + let maximum: Maybe = std.search.maxI32(values) + let max_index: usize = std.search.maxIndexI32(values) + let descending: [5]i32 = [9, 5, 5, 3, 1] + let descending_found: usize = std.search.binaryDescI32(descending, 5) + let descending_count: usize = std.search.countSortedDescI32(descending, 5) + if found == 3 && present && after_five == 4 && fives == 1 && std.mem.len(five_range) == 1 && partition_point == 3 && missing == std.mem.len(values) && minimum.has && minimum.value == 1 && maximum.has && maximum.value == 8 && max_index == 4 && descending_found == 1 && descending_count == 2 { + check world.out.write("search ok\n") + } +} +``` + +Effects: none. + +Allocation behavior: no allocation. + +Error behavior: absent values return the input length; sorted-count helpers +return `0`; min/max helpers return `null` for empty input; min/max index +helpers return the input length for empty input. + +Ownership: generic equality search rejects owned item elements. + +Target support: current compiler targets. diff --git a/docs/articles/modules/sort.md b/docs/articles/modules/sort.md new file mode 100644 index 0000000..dbebc71 --- /dev/null +++ b/docs/articles/modules/sort.md @@ -0,0 +1,114 @@ +## When To Use std.sort + +In Zerolang, use `std.sort` for in-place sorting and scalar ordering helpers +over caller-owned storage. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.sort.insertionI32(items)` | `Void` | Sorts caller-owned mutable `i32` storage in ascending order. | +| `std.sort.insertionDescI32(items)` | `Void` | Sorts caller-owned mutable `i32` storage in descending order. | +| `std.sort.stableI32(items)` | `Void` | Stable ascending sort over caller-owned mutable `i32` storage. | +| `std.sort.unstableI32(items)` | `Void` | Unstable ascending sort over caller-owned mutable `i32` storage. | +| `std.sort.stableDescI32(items)` | `Void` | Stable descending sort over caller-owned mutable `i32` storage. | +| `std.sort.unstableDescI32(items)` | `Void` | Unstable descending sort over caller-owned mutable `i32` storage. | +| `std.sort.reverseI32(items)` | `Void` | Reverses caller-owned mutable `i32` storage in place. | +| `std.sort.swapI32(items, left, right)` | `Bool` | Swaps two in-bounds `i32` elements and returns `false` for invalid indexes. | +| `std.sort.rotateLeftI32(items, amount)` | `Void` | Rotates caller-owned mutable `i32` storage left by `amount` positions. | +| `std.sort.rotateRightI32(items, amount)` | `Void` | Rotates caller-owned mutable `i32` storage right by `amount` positions. | +| `std.sort.isSortedI32(items)` | `Bool` | Checks whether `Span` input is sorted ascending. | +| `std.sort.isSortedDescI32(items)` | `Bool` | Checks whether `Span` input is sorted descending. | +| `std.sort.partitionI32(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. | +| `std.sort.partitionDescI32(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. | +| `std.sort.isPartitionedI32(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `i32` values. | +| `std.sort.isPartitionedDescI32(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `i32` values. | +| `std.sort.dedupeSortedI32(items)` | `usize` | Compacts sorted mutable `i32` storage in place and returns the unique prefix length. | +| `std.sort.selectNthI32(items, index)` | `Bool` | Partially reorders mutable `i32` storage so `items[index]` contains the nth ascending value. | +| `std.sort.selectNthDescI32(items, index)` | `Bool` | Partially reorders mutable `i32` storage so `items[index]` contains the nth descending value. | +| `std.sort.mergeSortedI32(dst, left, right)` | `usize` | Merges ascending sorted `i32` inputs into non-overlapping caller storage and returns the written count. | +| `std.sort.mergeSortedDescI32(dst, left, right)` | `usize` | Merges descending sorted `i32` inputs into non-overlapping caller storage and returns the written count. | +| `std.sort.insertionU32(items)` | `Void` | Sorts caller-owned mutable `u32` storage in ascending order. | +| `std.sort.insertionDescU32(items)` | `Void` | Sorts caller-owned mutable `u32` storage in descending order. | +| `std.sort.stableU32(items)` | `Void` | Stable ascending sort over caller-owned mutable `u32` storage. | +| `std.sort.unstableU32(items)` | `Void` | Unstable ascending sort over caller-owned mutable `u32` storage. | +| `std.sort.stableDescU32(items)` | `Void` | Stable descending sort over caller-owned mutable `u32` storage. | +| `std.sort.unstableDescU32(items)` | `Void` | Unstable descending sort over caller-owned mutable `u32` storage. | +| `std.sort.reverseU32(items)` | `Void` | Reverses caller-owned mutable `u32` storage in place. | +| `std.sort.swapU32(items, left, right)` | `Bool` | Swaps two in-bounds `u32` elements and returns `false` for invalid indexes. | +| `std.sort.rotateLeftU32(items, amount)` | `Void` | Rotates caller-owned mutable `u32` storage left by `amount` positions. | +| `std.sort.rotateRightU32(items, amount)` | `Void` | Rotates caller-owned mutable `u32` storage right by `amount` positions. | +| `std.sort.isSortedU32(items)` | `Bool` | Checks whether `Span` input is sorted ascending. | +| `std.sort.isSortedDescU32(items)` | `Bool` | Checks whether `Span` input is sorted descending. | +| `std.sort.partitionU32(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. | +| `std.sort.partitionDescU32(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. | +| `std.sort.isPartitionedU32(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `u32` values. | +| `std.sort.isPartitionedDescU32(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `u32` values. | +| `std.sort.dedupeSortedU32(items)` | `usize` | Compacts sorted mutable `u32` storage in place and returns the unique prefix length. | +| `std.sort.selectNthU32(items, index)` | `Bool` | Partially reorders mutable `u32` storage so `items[index]` contains the nth ascending value. | +| `std.sort.selectNthDescU32(items, index)` | `Bool` | Partially reorders mutable `u32` storage so `items[index]` contains the nth descending value. | +| `std.sort.mergeSortedU32(dst, left, right)` | `usize` | Merges ascending sorted `u32` inputs into non-overlapping caller storage and returns the written count. | +| `std.sort.mergeSortedDescU32(dst, left, right)` | `usize` | Merges descending sorted `u32` inputs into non-overlapping caller storage and returns the written count. | +| `std.sort.insertionUsize(items)` | `Void` | Sorts caller-owned mutable `usize` storage in ascending order. | +| `std.sort.insertionDescUsize(items)` | `Void` | Sorts caller-owned mutable `usize` storage in descending order. | +| `std.sort.stableUsize(items)` | `Void` | Stable ascending sort over caller-owned mutable `usize` storage. | +| `std.sort.unstableUsize(items)` | `Void` | Unstable ascending sort over caller-owned mutable `usize` storage. | +| `std.sort.stableDescUsize(items)` | `Void` | Stable descending sort over caller-owned mutable `usize` storage. | +| `std.sort.unstableDescUsize(items)` | `Void` | Unstable descending sort over caller-owned mutable `usize` storage. | +| `std.sort.reverseUsize(items)` | `Void` | Reverses caller-owned mutable `usize` storage in place. | +| `std.sort.swapUsize(items, left, right)` | `Bool` | Swaps two in-bounds `usize` elements and returns `false` for invalid indexes. | +| `std.sort.rotateLeftUsize(items, amount)` | `Void` | Rotates caller-owned mutable `usize` storage left by `amount` positions. | +| `std.sort.rotateRightUsize(items, amount)` | `Void` | Rotates caller-owned mutable `usize` storage right by `amount` positions. | +| `std.sort.isSortedUsize(items)` | `Bool` | Checks whether `Span` input is sorted ascending. | +| `std.sort.isSortedDescUsize(items)` | `Bool` | Checks whether `Span` input is sorted descending. | +| `std.sort.partitionUsize(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. | +| `std.sort.partitionDescUsize(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. | +| `std.sort.isPartitionedUsize(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `usize` values. | +| `std.sort.isPartitionedDescUsize(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `usize` values. | +| `std.sort.dedupeSortedUsize(items)` | `usize` | Compacts sorted mutable `usize` storage in place and returns the unique prefix length. | +| `std.sort.selectNthUsize(items, index)` | `Bool` | Partially reorders mutable `usize` storage so `items[index]` contains the nth ascending value. | +| `std.sort.selectNthDescUsize(items, index)` | `Bool` | Partially reorders mutable `usize` storage so `items[index]` contains the nth descending value. | +| `std.sort.mergeSortedUsize(dst, left, right)` | `usize` | Merges ascending sorted `usize` inputs into non-overlapping caller storage and returns the written count. | +| `std.sort.mergeSortedDescUsize(dst, left, right)` | `usize` | Merges descending sorted `usize` inputs into non-overlapping caller storage and returns the written count. | + +The first sort surface is deliberately small and typed. Generic comparator +sorting should wait for stronger comparator contracts instead of smuggling an +untyped callback convention into the standard library. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var values: [5]i32 = [5, 1, 4, 2, 3] + std.sort.stableI32(values) + let unique_len: usize = std.sort.dedupeSortedI32(values) + std.sort.unstableI32(values) + std.sort.reverseI32(values) + let swapped: Bool = std.sort.swapI32(values, 0_usize, 4_usize) + std.sort.rotateLeftI32(values, 2_usize) + std.sort.rotateRightI32(values, 2_usize) + let high_len: usize = std.sort.partitionDescI32(values, 2) + let high_partitioned: Bool = std.sort.isPartitionedDescI32(values, 2) + std.sort.stableDescI32(values) + std.sort.unstableDescI32(values) + var selected: [5]i32 = [9, 1, 4, 7, 2] + let selected_ok: Bool = std.sort.selectNthI32(selected, 2_usize) + let left_sorted: [2]i32 = [1, 3] + let right_sorted: [3]i32 = [2, 4, 5] + var merged: [5]i32 = [0, 0, 0, 0, 0] + let merged_len: usize = std.sort.mergeSortedI32(merged, left_sorted, right_sorted) + if std.sort.isSortedDescI32(values) && swapped && unique_len == 5 && high_len == 3 && high_partitioned && selected_ok && selected[2] == 4 && merged_len == 5 && values[0] == 5 && values[4] == 1 { + check world.out.write("sort ok\n") + } +} +``` + +Effects: writes to caller-provided mutable storage. + +Allocation behavior: no allocation. + +Error behavior: none. + +Ownership: sort helpers are typed scalar helpers and do not move owned values. + +Target support: current compiler targets. diff --git a/docs/articles/modules/str.md b/docs/articles/modules/str.md new file mode 100644 index 0000000..8c25a97 --- /dev/null +++ b/docs/articles/modules/str.md @@ -0,0 +1,73 @@ +## When To Use std.str + +In Zerolang, use `std.str` for allocation-free byte-string helpers over spans and +caller-owned storage. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.str.copy(buffer, text)` | `Maybe>` | Copies `text` into caller storage. | +| `std.str.concat(buffer, left, right)` | `Maybe>` | Writes `left` followed by `right`. | +| `std.str.repeat(buffer, text, count)` | `Maybe>` | Repeats `text` into caller storage. | +| `std.str.replace(buffer, text, old, replacement)` | `Maybe>` | Replaces non-overlapping `old` byte substrings into caller storage; empty `old` returns `null`. | +| `std.str.reverse(buffer, text)` | `Maybe>` | Writes reversed bytes into non-overlapping caller-provided storage. | +| `std.str.countByte(text, byte)` | `usize` | Counts exact byte matches. | +| `std.str.count(text, needle)` | `usize` | Counts non-overlapping byte substring matches; the empty needle returns `len + 1`. | +| `std.str.splitCount(text, separator)` | `usize` | Counts byte-separator split parts; an empty separator returns `0`. | +| `std.str.split(text, separator, index)` | `Maybe>` | Borrows a zero-based split part. | +| `std.str.fieldCountAscii(text)` | `usize` | Counts non-empty ASCII-whitespace separated fields. | +| `std.str.fieldAscii(text, index)` | `Maybe>` | Borrows a zero-based ASCII-whitespace field. | +| `std.str.lineCount(text)` | `usize` | Counts LF-delimited lines; a trailing LF does not add a final empty line. | +| `std.str.line(text, index)` | `Maybe>` | Borrows a zero-based line and strips `\r` before `\n`. | +| `std.str.indexOf(text, needle)` / `std.str.lastIndexOf(text, needle)` | `usize` | Returns a matching byte index or the input length when absent. | +| `std.str.startsWith(text, prefix)` | `Bool` | Checks a byte prefix. | +| `std.str.endsWith(text, suffix)` | `Bool` | Checks a byte suffix. | +| `std.str.contains(text, needle)` | `Bool` | Checks for a byte substring; the empty needle is present. | +| `std.str.trimAscii(text)` | `Span` | Borrows `text` without leading or trailing ASCII space bytes. | +| `std.str.trimStartAscii(text)` / `std.str.trimEndAscii(text)` | `Span` | Borrows one-sided trimmed views. | +| `std.str.toLowerAscii(buffer, text)` / `std.str.toUpperAscii(buffer, text)` | `Maybe>` | Writes ASCII case-converted bytes into caller storage. | +| `std.str.eqlIgnoreAsciiCase(left, right)` | `Bool` | Compares ASCII case-insensitively. | +| `std.str.wordCountAscii(text)` | `usize` | Counts non-empty runs separated by ASCII space bytes. | + +Current scope: + +- Helpers operate on byte spans and ASCII delimiter rules for space, tab, line feed, and carriage return. +- `reverse`, `repeat`, `replace`, `copy`, and `concat` write into caller storage and return `null` when the buffer is too small. The destination buffer must not overlap the input. +- The module does not implement Unicode case mapping, grapheme segmentation, or locale-aware text rules. + +## Example + +```zero +pub fn main(world: World) -> Void raises { + var storage: [6]u8 = [0_u8; 6] + let reversed: Maybe> = std.str.reverse(storage, "drawer") + var repeated_storage: [8]u8 = [0_u8; 8] + let repeated: Maybe> = std.str.repeat(repeated_storage, "ha", 3) + var lower_storage: [4]u8 = [0_u8; 4] + let lower: Maybe> = std.str.toLowerAscii(lower_storage, "ZERO") + let field: Maybe> = std.str.fieldAscii("zero text", 1) + if reversed.has && repeated.has && (lower.has && field.has) { + if std.mem.eql(reversed.value, "reward") && std.mem.eql(repeated.value, "hahaha") && (std.mem.eql(lower.value, "zero") && std.mem.eql(field.value, "text")) { + check world.out.write("string helper ok\n") + } + } +} +``` + +## Design Notes + +`std.str` is allocation-free. Functions that create new byte sequences use +caller-provided storage, and functions that return spans borrow from an input or +that caller-provided storage. + +`reverse` is a copy helper, not an in-place reversal primitive. Pass separate +destination storage when the source text comes from mutable bytes. + +String literals can be passed directly to these helpers; fixed arrays and +mutable buffers can be passed as spans when the caller needs non-literal input. + +The current helpers are byte-string primitives. They are suitable for protocol +tokens, Rosetta-style ASCII examples, and fixed-buffer tools. Unicode text +algorithms should be added as explicit APIs with documented behavior instead of +being implied by these byte-span helpers. diff --git a/docs/articles/modules/term.md b/docs/articles/modules/term.md new file mode 100644 index 0000000..0c65706 --- /dev/null +++ b/docs/articles/modules/term.md @@ -0,0 +1,162 @@ +## When To Use std.term + +In Zerolang, use `std.term` when terminal code needs ANSI output sequences, +hosted terminal metadata, nonblocking input reads, or key decoding for bytes +already read from input. + +Runnable today: + +| Helper | Returns | Notes | +| --- | --- | --- | +| `std.term.reset()` | `String` | ANSI SGR reset. | +| `std.term.bold()` | `String` | ANSI SGR bold style. | +| `std.term.dim()` | `String` | ANSI SGR dim style. | +| `std.term.underline()` | `String` | ANSI SGR underline style. | +| `std.term.inverse()` | `String` | ANSI SGR inverse style. | +| `std.term.fgDefault()` | `String` | Reset foreground color. | +| `std.term.fgBlack()` | `String` | Black foreground color. | +| `std.term.fgRed()` | `String` | Red foreground color. | +| `std.term.fgGreen()` | `String` | Green foreground color. | +| `std.term.fgYellow()` | `String` | Yellow foreground color. | +| `std.term.fgBlue()` | `String` | Blue foreground color. | +| `std.term.fgMagenta()` | `String` | Magenta foreground color. | +| `std.term.fgCyan()` | `String` | Cyan foreground color. | +| `std.term.fgWhite()` | `String` | White foreground color. | +| `std.term.bgDefault()` | `String` | Reset background color. | +| `std.term.bgBlack()` | `String` | Black background color. | +| `std.term.bgRed()` | `String` | Red background color. | +| `std.term.bgGreen()` | `String` | Green background color. | +| `std.term.bgYellow()` | `String` | Yellow background color. | +| `std.term.bgBlue()` | `String` | Blue background color. | +| `std.term.bgMagenta()` | `String` | Magenta background color. | +| `std.term.bgCyan()` | `String` | Cyan background color. | +| `std.term.bgWhite()` | `String` | White background color. | +| `std.term.clearScreen()` | `String` | Clear the full terminal screen. | +| `std.term.clearScreenDown()` | `String` | Clear from the cursor through the end of the screen. | +| `std.term.clearScreenUp()` | `String` | Clear from the cursor through the start of the screen. | +| `std.term.clearLine()` | `String` | Clear the current terminal line. | +| `std.term.clearLineRight()` | `String` | Clear from the cursor through the end of the line. | +| `std.term.clearLineLeft()` | `String` | Clear from the cursor through the start of the line. | +| `std.term.cursorHome()` | `String` | Move the cursor to row 1, column 1. | +| `std.term.cursorTo(buffer, row, column)` | `Maybe>` | Writes a 1-based ANSI cursor-position sequence into caller storage. | +| `std.term.cursorUp(buffer, count)` | `Maybe>` | Writes an ANSI cursor-up sequence into caller storage; count `0` writes an empty span. | +| `std.term.cursorDown(buffer, count)` | `Maybe>` | Writes an ANSI cursor-down sequence into caller storage; count `0` writes an empty span. | +| `std.term.cursorRight(buffer, count)` | `Maybe>` | Writes an ANSI cursor-right sequence into caller storage; count `0` writes an empty span. | +| `std.term.cursorLeft(buffer, count)` | `Maybe>` | Writes an ANSI cursor-left sequence into caller storage; count `0` writes an empty span. | +| `std.term.saveCursor()` | `String` | Save the current cursor position. | +| `std.term.restoreCursor()` | `String` | Restore the saved cursor position. | +| `std.term.hideCursor()` | `String` | Hide the terminal cursor. | +| `std.term.showCursor()` | `String` | Show the terminal cursor. | +| `std.term.enterAltScreen()` | `String` | Enter the alternate screen buffer. | +| `std.term.leaveAltScreen()` | `String` | Leave the alternate screen buffer. | +| `std.term.enterBracketedPaste()` | `String` | Enable bracketed paste markers in supporting terminals. | +| `std.term.leaveBracketedPaste()` | `String` | Disable bracketed paste markers in supporting terminals. | +| `std.term.enterMouseCapture()` | `String` | Enable SGR mouse tracking and drag/wheel capture in supporting terminals. | +| `std.term.leaveMouseCapture()` | `String` | Disable the SGR mouse tracking modes enabled by `enterMouseCapture`. | +| `std.term.keyNone()` | `u32` | Sentinel returned for incomplete or unsupported key bytes. | +| `std.term.keyEscape()` | `u32` | Escape key code. | +| `std.term.keyEnter()` | `u32` | Enter key code for `\r` or `\n`. | +| `std.term.keyTab()` | `u32` | Tab key code. | +| `std.term.keyBackspace()` | `u32` | Backspace key code for `0x7f` or `0x08`. | +| `std.term.keyCtrlA()` | `u32` | Ctrl-A key code. | +| `std.term.keyCtrlC()` | `u32` | Ctrl-C key code. | +| `std.term.keyCtrlD()` | `u32` | Ctrl-D key code. | +| `std.term.keyCtrlE()` | `u32` | Ctrl-E key code. | +| `std.term.keyCtrlK()` | `u32` | Ctrl-K key code. | +| `std.term.keyCtrlL()` | `u32` | Ctrl-L key code. | +| `std.term.keyCtrlN()` | `u32` | Ctrl-N key code. | +| `std.term.keyCtrlP()` | `u32` | Ctrl-P key code. | +| `std.term.keyCtrlR()` | `u32` | Ctrl-R key code. | +| `std.term.keyCtrlU()` | `u32` | Ctrl-U key code. | +| `std.term.keyCtrlW()` | `u32` | Ctrl-W key code. | +| `std.term.keyArrowUp()` | `u32` | Up-arrow key code above the Unicode scalar range. | +| `std.term.keyArrowDown()` | `u32` | Down-arrow key code above the Unicode scalar range. | +| `std.term.keyArrowRight()` | `u32` | Right-arrow key code above the Unicode scalar range. | +| `std.term.keyArrowLeft()` | `u32` | Left-arrow key code above the Unicode scalar range. | +| `std.term.keyDelete()` | `u32` | Delete key code above the Unicode scalar range. | +| `std.term.keyHome()` | `u32` | Home key code above the Unicode scalar range. | +| `std.term.keyEnd()` | `u32` | End key code above the Unicode scalar range. | +| `std.term.keyPageUp()` | `u32` | Page Up key code above the Unicode scalar range. | +| `std.term.keyPageDown()` | `u32` | Page Down key code above the Unicode scalar range. | +| `std.term.keyInsert()` | `u32` | Insert key code above the Unicode scalar range. | +| `std.term.keyShiftTab()` | `u32` | Shift-Tab key code above the Unicode scalar range. | +| `std.term.keyF1()` | `u32` | F1 key code above the Unicode scalar range. | +| `std.term.keyF2()` | `u32` | F2 key code above the Unicode scalar range. | +| `std.term.keyF3()` | `u32` | F3 key code above the Unicode scalar range. | +| `std.term.keyF4()` | `u32` | F4 key code above the Unicode scalar range. | +| `std.term.keyF5()` | `u32` | F5 key code above the Unicode scalar range. | +| `std.term.keyF6()` | `u32` | F6 key code above the Unicode scalar range. | +| `std.term.keyF7()` | `u32` | F7 key code above the Unicode scalar range. | +| `std.term.keyF8()` | `u32` | F8 key code above the Unicode scalar range. | +| `std.term.keyF9()` | `u32` | F9 key code above the Unicode scalar range. | +| `std.term.keyF10()` | `u32` | F10 key code above the Unicode scalar range. | +| `std.term.keyF11()` | `u32` | F11 key code above the Unicode scalar range. | +| `std.term.keyF12()` | `u32` | F12 key code above the Unicode scalar range. | +| `std.term.keyPasteStart()` | `u32` | Bracketed paste start marker code above the Unicode scalar range. | +| `std.term.keyPasteEnd()` | `u32` | Bracketed paste end marker code above the Unicode scalar range. | +| `std.term.keyCode(bytes)` | `u32` | Decodes one key from caller-provided bytes, returning Unicode scalar values for printable UTF-8 and named constants for control keys. | +| `std.term.keyByteLen(bytes)` | `usize` | Returns the decoded key width in bytes, or `0` for incomplete or unsupported input. | +| `std.term.stdinIsTty()` | `Bool` | Reports whether standard input is attached to a terminal. | +| `std.term.stdoutIsTty()` | `Bool` | Reports whether standard output is attached to a terminal. | +| `std.term.widthOr(fallback)` | `usize` | Returns terminal columns, or `fallback` when unavailable. | +| `std.term.heightOr(fallback)` | `usize` | Returns terminal rows, or `fallback` when unavailable. | +| `std.term.enterRawMode()` | `Bool` | Puts standard input into raw, nonblocking terminal mode when supported. | +| `std.term.leaveRawMode()` | `Bool` | Restores the terminal mode saved by `enterRawMode()`. | +| `std.term.readInput(buffer)` | `Maybe` | Fills the caller buffer with currently available stdin bytes without blocking; returns `null` when no bytes are available or the input source is unsupported. | + +Metadata labels: + +- effects: ANSI/key helpers are pure; TTY/size helpers read hosted terminal metadata; raw-mode helpers update the hosted terminal; `readInput` reads from hosted stdin +- allocation behavior: no allocation +- target support: ANSI/key helpers are target-neutral; TTY/size/raw-mode/input helpers require hosted runtime support +- error behavior: ANSI/key helpers are infallible; hosted helpers return fallbacks, `false`, or `null` when unavailable +- ownership notes: ANSI sequences are borrowed static byte views +- example: `conformance/native/pass/std-term-ansi.graph` + +Example: + +```zero +pub fn main(world: World) -> Void raises { + check world.out.write(std.term.enterAltScreen()) + check world.out.write(std.term.enterMouseCapture()) + check world.out.write(std.term.clearScreen()) + check world.out.write(std.term.cursorHome()) + var cursor: [24]u8 = [0_u8; 24] + let top: Maybe> = std.term.cursorTo(cursor, 1_usize, 1_usize) + if top.has { + check world.out.write(top.value) + } + check world.out.write(std.term.bold()) + check world.out.write(std.term.fgCyan()) + let width: usize = std.term.widthOr(80_usize) + let height: usize = std.term.heightOr(24_usize) + let raw: Bool = std.term.enterRawMode() + var input: [16]u8 = [0_u8; 16] + let pending: Maybe = std.term.readInput(input) + if pending.has { + let bytes: Span = std.mem.prefix(input, pending.value) + let key: u32 = std.term.keyCode(bytes) + if key == std.term.keyCtrlC() { + check world.out.write("cancel") + } + } + check world.out.write("ready") + if raw { + let restored: Bool = std.term.leaveRawMode() + if !restored { + return + } + } + check world.out.write(std.term.reset()) + check world.out.write(std.term.leaveMouseCapture()) + check world.out.write(std.term.leaveAltScreen()) +} +``` + +Key decoding is target-neutral: it parses bytes the caller already has. TTY and +size helpers are hosted metadata calls and return caller fallbacks when a +terminal size is unavailable. Raw mode is a hosted terminal capability: call +`leaveRawMode()` before returning to normal line-oriented terminal input. +`readInput()` is nonblocking; in raw mode it can be polled by interactive +programs, and on noninteractive stdin it returns available piped bytes when the +host exposes them. diff --git a/docs/articles/modules/testing.md b/docs/articles/modules/testing.md new file mode 100644 index 0000000..40ad25f --- /dev/null +++ b/docs/articles/modules/testing.md @@ -0,0 +1,59 @@ +## When To Use std.testing + +In Zerolang, use `std.testing` inside test blocks for output checks and small boolean +assertion helpers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.testing.isTrue(value)` | `Bool` | Passes through a `Bool` for readable `expect` statements. | +| `std.testing.isFalse(value)` | `Bool` | Returns true when the value is false. | +| `std.testing.equalBool(actual, expected)` | `Bool` | Compares booleans explicitly. | +| `std.testing.equalUsize(actual, expected)` | `Bool` | Compares `usize` values explicitly. | +| `std.testing.equalU32(actual, expected)` | `Bool` | Compares `u32` values explicitly. | +| `std.testing.equalI32(actual, expected)` | `Bool` | Compares `i32` values explicitly. | +| `std.testing.equalBytes(actual, expected)` | `Bool` | Compares byte spans by value. | +| `std.testing.containsBytes(actual, needle)` | `Bool` | Checks whether a byte span contains a byte substring. | +| `std.testing.startsWith(actual, prefix)` | `Bool` | Checks a byte prefix. | +| `std.testing.endsWith(actual, suffix)` | `Bool` | Checks a byte suffix. | +| `std.testing.notEqualBytes(actual, expected)` | `Bool` | Checks byte-span inequality. | +| `std.testing.diffIndexBytes(actual, expected)` | `Maybe` | Returns the first differing byte index, or `null` when spans are equal. | +| `std.testing.jsonFieldEquals(actual, key, expected)` | `Bool` | Compares a raw top-level JSON field value. | +| `std.testing.jsonPathEquals(actual, path, expected)` | `Bool` | Compares a raw dotted JSON path value. | +| `std.testing.caseName(buffer, suite, index)` | `Maybe>` | Writes a stable table-case name like `suite[3]` into caller storage. | + +Metadata labels: + +- effects: none for scalar comparisons; memory for byte-span/case-name checks; parse for JSON checks +- allocation behavior: no allocation +- target support: target-neutral +- error behavior: infallible +- ownership notes: no ownership transfer +- example: `examples/std-testing-log.graph` + +## Example + +```zero +test "testing helpers support direct test blocks" { + let diff: Maybe = std.testing.diffIndexBytes("zero", "zeta") + expect std.testing.equalU32(42_u32, 42_u32) + expect std.testing.equalBytes("zero", "zero") + expect std.testing.containsBytes("zerolang", "lang") + expect diff.has && diff.value == 2 + expect std.testing.jsonPathEquals("{\"user\":{\"name\":\"zero\"}}", "user.name", "\"zero\"") +} +``` + +## Design Notes + +`std.testing` helpers return `Bool`; they do not register tests, hide failures, +allocate output, or produce process I/O. Use them inside ordinary `expect` +statements so the compiler and `zero test` keep one visible test model. + +The byte helpers are byte-span predicates. They are suitable for output checks, +protocol fixtures, and small examples where a full parser would be more complex +than the assertion. + +JSON helpers compare raw JSON values, so string expectations include their JSON +quotes, such as `"\"zero\""`. diff --git a/docs/articles/modules/text.md b/docs/articles/modules/text.md new file mode 100644 index 0000000..162e85a --- /dev/null +++ b/docs/articles/modules/text.md @@ -0,0 +1,36 @@ +## When To Use std.text + +In Zerolang, use `std.text` for ASCII and UTF-8 byte-backed validation. + +Runnable today: + +`std.text` is for byte-backed text validation and counting. It does not imply +locale-aware case mapping, grapheme segmentation, normalization, or display-width +rules. + +| API | Return | Notes | +| --- | --- | --- | +| `std.text.isAscii(text)` | `Bool` | Checks that every byte is below `0x80`. | +| `std.text.utf8Valid(text)` | `Bool` | Validates UTF-8 byte structure, rejecting overlong encodings, surrogate code points, and values above `U+10FFFF`. | +| `std.text.utf8Len(text)` | `Maybe` | Counts Unicode scalar values when UTF-8 is valid; returns `null` on invalid input. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let valid: [2]u8 = [195_u8, 169_u8] + let invalid: [1]u8 = [128_u8] + let len: Maybe = std.text.utf8Len(valid) + if !std.text.isAscii(valid) && std.text.utf8Valid(valid) && !std.text.utf8Valid(invalid) && len.has && len.value == 1 { + check world.out.write("text ok\n") + } +} +``` + +Effects: none. + +Allocation behavior: no allocation. + +Error behavior: `utf8Len` returns `null` for invalid UTF-8. + +Target support: current compiler targets. diff --git a/docs/articles/modules/time.md b/docs/articles/modules/time.md new file mode 100644 index 0000000..b039633 --- /dev/null +++ b/docs/articles/modules/time.md @@ -0,0 +1,102 @@ +## When To Use std.time + +In Zerolang, use `std.time` for duration math, RFC 3339 date and time validation +and parsing, and target-gated monotonic or wall-clock helpers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.time.ns(value)` | `Duration` | Builds a nanosecond duration. | +| `std.time.us(value)` | `Duration` | Builds a microsecond duration. | +| `std.time.ms(value)` | `Duration` | Builds a millisecond duration. | +| `std.time.seconds(value)` | `Duration` | Builds a second duration. | +| `std.time.minutes(value)` | `Duration` | Builds a minute duration. | +| `std.time.hours(value)` | `Duration` | Builds an hour duration. | +| `std.time.zero()` | `Duration` | Returns a zero duration. | +| `std.time.add(a, b)` | `Duration` | Adds two durations. | +| `std.time.sub(a, b)` | `Duration` | Subtracts one duration from another. | +| `std.time.min(a, b)` | `Duration` | Returns the smaller duration. | +| `std.time.max(a, b)` | `Duration` | Returns the larger duration. | +| `std.time.clamp(value, low, high)` | `Duration` | Clamps a duration between normalized bounds. | +| `std.time.abs(value)` | `Duration` | Returns a non-negative duration magnitude. | +| `std.time.between(start, end)` | `Duration` | Returns the non-negative duration between two values. | +| `std.time.hasElapsed(start, now, timeout)` | `Bool` | Reports whether a timeout window has elapsed. | +| `std.time.deadlineAfter(start, timeout)` | `Duration` | Builds a deadline by adding a timeout to a start instant. | +| `std.time.remainingUntil(deadline, now)` | `Duration` | Returns remaining time or zero once the deadline has passed. | +| `std.time.deadlineExpired(deadline, now)` | `Bool` | Reports whether `now` is at or past `deadline`. | +| `std.time.sleep(duration)` | `Bool` | Sleeps for a hosted non-negative duration; returns `false` on host failure. | +| `std.time.asNs(value)` | `i64` | Converts to nanoseconds. | +| `std.time.asUsFloor(value)` | `i64` | Converts to whole microseconds. | +| `std.time.asMsFloor(value)` | `i32` | Converts to whole milliseconds. | +| `std.time.asSecondsFloor(value)` | `i64` | Converts to whole seconds. | +| `std.time.lessThan(a, b)` | `Bool` | Compares two durations. | +| `std.time.isZero(value)` | `Bool` | Reports whether a duration is zero. | +| `std.time.monotonic()` | `Duration` | Reads a monotonic target clock where available. | +| `std.time.wallSeconds()` | `i64` | Reads target wall-clock seconds where available. | +| `std.time.isRfc3339Date(text)` | `Bool` | Validates an RFC 3339 full-date with leap years and days-in-month. | +| `std.time.isRfc3339Time(text)` | `Bool` | Validates an RFC 3339 full-time with fractional seconds, numeric offsets, and the leap-second rule. | +| `std.time.isRfc3339DateTime(text)` | `Bool` | Validates an RFC 3339 date-time joined by `T` or `t`. | +| `std.time.parseRfc3339DateTimeOr(text, fallback)` | `i64` | Parses a date-time into UTC epoch seconds; returns the fallback when invalid. Fractional seconds truncate; a valid leap second maps to the same epoch second as `:59`. | +| `std.time.isLeapYear(year)` | `Bool` | Gregorian leap-year predicate. | +| `std.time.daysInMonth(year, month)` | `u32` | Days in a month; returns `0` for invalid months. | +| `std.time.writeDurationNs(buffer, value)` | `Maybe>` | Writes nanoseconds with an `ns` suffix into caller storage. | +| `std.time.writeDurationMs(buffer, value)` | `Maybe>` | Writes whole milliseconds with an `ms` suffix into caller storage. | +| `std.time.writeDurationSeconds(buffer, value)` | `Maybe>` | Writes whole seconds with an `s` suffix into caller storage. | + +Current limits: + +- Target-specific clock availability diagnostics. +- Timer handles and fake-clock handles are not public APIs. + +Metadata labels: + +- effects: time +- allocation behavior: no allocation +- target support: duration math is target-neutral; clock reads and sleep require a time-capable target +- error behavior: infallible helpers; RFC 3339 validators return `Bool` and the epoch parser returns its fallback for invalid text +- ownership notes: no ownership transfer +- example: `examples/std-platform.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let a: Duration = std.time.ms(250) + let b: Duration = std.time.seconds(1) + let total: Duration = std.time.add(a, b) + let span: Duration = std.time.between(std.time.seconds(2), std.time.ms(250)) + let deadline: Duration = std.time.deadlineAfter(std.time.seconds(10), std.time.ms(500)) + let remaining: Duration = std.time.remainingUntil(deadline, std.time.seconds(10)) + let slept: Bool = std.time.sleep(std.time.zero()) + var text_storage: [32]u8 = [0_u8; 32] + let text: Maybe> = std.time.writeDurationMs(text_storage, total) + if slept && std.time.asMsFloor(total) == 1250 && std.time.asMsFloor(span) == 1750 && (std.time.asMsFloor(remaining) == 500 && text.has) { + check world.out.write("duration ok\n") + } +} +``` + +RFC 3339 validation includes the exact leap-second rule: `seconds == 60` is +valid only when the time normalized by its numeric offset equals `23:59:60` +UTC, wrapping modulo 24 hours. `00:29:60+00:30` is valid because it normalizes +to `23:59:60` UTC on the previous day, while `23:59:60-01:00` is invalid +because it normalizes to `00:59:60` UTC. + +```zero +pub fn main(world: World) -> Void raises { + let wrapped: Bool = std.time.isRfc3339Time("00:29:60+00:30") + let not_leap: Bool = std.time.isRfc3339Time("23:59:60-01:00") + let epoch: i64 = std.time.parseRfc3339DateTimeOr("2000-01-01T00:00:00Z", -1) + if wrapped && !not_leap && epoch == 946684800 && std.time.daysInMonth(2024, 2) == 29 { + check world.out.write("rfc3339 ok\n") + } +} +``` + +## Design Notes + +Time is an effect when it observes or waits on the outside world. + +Pure duration math can stay allocation-free and target-independent. +Timer and fake-clock APIs are not exposed in the current public surface. diff --git a/docs/articles/modules/toml.md b/docs/articles/modules/toml.md new file mode 100644 index 0000000..57e62bf --- /dev/null +++ b/docs/articles/modules/toml.md @@ -0,0 +1,67 @@ +## When To Use std.toml + +In Zerolang, use `std.toml` for TOML validation, shallow field lookup, and typed scalar +decode helpers. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.toml.validate(text)` | `Bool` | Checks the current TOML subset without allocation. | +| `std.toml.validateBytes(bytes)` | `Bool` | Checks a `Span` TOML payload without allocation. | +| `std.toml.field(bytes, key)` | `Maybe>` | Returns the raw value for a direct, dotted, or shallow table field. | +| `std.toml.stringDecode(buffer, value)` | `Maybe>` | Decodes a TOML string value into caller storage. | +| `std.toml.string(buffer, bytes, key)` | `Maybe>` | Looks up and decodes a TOML string field. | +| `std.toml.u32(bytes, key)` | `Maybe` | Looks up and decodes an unsigned integer field. | +| `std.toml.i32(bytes, key)` | `Maybe` | Looks up and decodes a signed integer field. | +| `std.toml.bool(bytes, key)` | `Maybe` | Looks up and decodes a boolean field. | +| `std.toml.arrayCount(value)` | `Maybe` | Counts items in a raw array value. | +| `std.toml.arrayValue(value, index)` | `Maybe>` | Borrows a raw array item by ordinal. | +| `std.toml.arrayString(buffer, value, index)` | `Maybe>` | Decodes a string array item into caller storage. | +| `std.toml.arrayU32(value, index)` | `Maybe` | Decodes an unsigned integer array item. | +| `std.toml.arrayI32(value, index)` | `Maybe` | Decodes a signed integer array item. | +| `std.toml.arrayBool(value, index)` | `Maybe` | Decodes a boolean array item. | +| `std.toml.writeKeyValueString(buffer, key, value)` | `Maybe>` | Writes one string key/value line. | +| `std.toml.writeKeyValueU32(buffer, key, value)` | `Maybe>` | Writes one unsigned integer key/value line. | +| `std.toml.writeKeyValueBool(buffer, key, value)` | `Maybe>` | Writes one boolean key/value line. | +| `std.toml.writeTableHeader(buffer, table)` | `Maybe>` | Writes one table header line. | + +Metadata labels: + +- effects: parse +- allocation behavior: allocation-free; decoded strings and writer output use caller storage +- target support: target-neutral +- error behavior: `Maybe` helpers return null on malformed or missing fields +- ownership notes: returned raw fields borrow from the input span; decoded strings borrow from the caller buffer +- examples: `conformance/native/pass/std-toml-basic.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let input: Span = "[package]\nname = \"demo\"\n\n[features]\ngraph = true\nlevels = [1, 2, 3]\n" + var name_buffer: [16]u8 = [0_u8; 16] + let name: Maybe> = std.toml.string(name_buffer, input, "package.name") + let graph: Maybe = std.toml.bool(input, "features.graph") + let levels: Maybe> = std.toml.field(input, "features.levels") + var count: Maybe = null + if levels.has { + count = std.toml.arrayCount(levels.value) + } + if std.toml.validateBytes(input) && name.has && graph.has && graph.value && count.has && count.value == 3 { + check world.out.write("toml ok\n") + } +} +``` + +## Design Notes + +The current TOML helper surface is deliberately narrow. It supports the package +manifest subset used by Zero packages: tables, dotted keys, strings, booleans, +integers, scalar arrays, and small writer helpers. Field lookup is shallow and table-aware, so +`std.toml.string(buffer, input, "package.name")` can read `name` inside a +`[package]` table. + +The helpers avoid hidden allocation. Use `field` when a raw value slice is +enough, and use `string` or `stringDecode` when escape decoding into explicit +caller storage is required. diff --git a/docs/articles/modules/unicode.md b/docs/articles/modules/unicode.md new file mode 100644 index 0000000..7e9b59e --- /dev/null +++ b/docs/articles/modules/unicode.md @@ -0,0 +1,60 @@ +## When To Use std.unicode + +In Zerolang, use `std.unicode` for UTF-8 codepoint decode/encode iteration and +codepoint-class checks. For whole-span validation and codepoint counting, use +the existing `std.text.utf8Valid` and `std.text.utf8Len` helpers; `std.unicode` +extends them with per-codepoint access. + +Decoding is strict UTF-8: overlong encodings, surrogate codepoints, values +above `U+10FFFF`, and truncated sequences all return `null`. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.unicode.decodeAt(text, index)` | `Maybe` | Decodes the codepoint starting at a byte index; `null` for invalid or out-of-range positions. | +| `std.unicode.widthAt(text, index)` | `Maybe` | Byte width of the sequence at a byte index; advance `index` by this to iterate codepoints. | +| `std.unicode.nextIndex(text, index)` | `Maybe` | Next byte index after the codepoint at `index`; `null` for invalid input. | +| `std.unicode.invalidIndex(text)` | `usize` | First invalid UTF-8 byte index, or the input length when valid. | +| `std.unicode.decodeStatusAt(text, index)` | `u32` | Strict UTF-8 status code at a byte index. | +| `std.unicode.statusName(status)` | `String` | Names a status code such as `truncated sequence`. | +| `std.unicode.encode(buffer, cp)` | `Maybe>` | Encodes a codepoint as UTF-8 into a caller buffer; `null` for surrogates, values above `U+10FFFF`, or a too-small buffer. | +| `std.unicode.encodedWidth(cp)` | `Maybe` | UTF-8 byte width a codepoint needs (1-4); `null` for invalid codepoints. | +| `std.unicode.isDigit(cp)` | `Bool` | ASCII digit class, matching regex `\d` semantics by codepoint. | +| `std.unicode.isWord(cp)` | `Bool` | ASCII word class `[A-Za-z0-9_]`, matching regex `\w` semantics. | +| `std.unicode.isSpace(cp)` | `Bool` | ECMA-262 whitespace plus line terminators, matching regex `\s` semantics. | + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let text: Span = std.mem.span("aé💯") + var index: usize = 0 + var count: usize = 0 + while index < std.mem.len(text) { + let next: Maybe = std.unicode.nextIndex(text, index) + if !next.has { + return + } + index = next.value + count = count + 1 + } + var storage: [4]u8 = [0; 4] + let buffer: MutSpan = storage + let encoded: Maybe> = std.unicode.encode(buffer, 233) + if count == 3 && std.unicode.invalidIndex(text) == std.mem.len(text) && (encoded.has && std.mem.len(encoded.value) == 2) { + check world.out.write("unicode ok\n") + } +} +``` + +Effects: none. + +Allocation behavior: `encode` writes the caller buffer; all other helpers +allocate nothing. + +Error behavior: decode/encode and cursor helpers return `null` for invalid +input. `decodeStatusAt` and `statusName` provide allocation-free status details; +class helpers are infallible. + +Target support: current compiler targets. diff --git a/docs/articles/modules/url.md b/docs/articles/modules/url.md new file mode 100644 index 0000000..e69a287 --- /dev/null +++ b/docs/articles/modules/url.md @@ -0,0 +1,71 @@ +## When To Use std.url + +In Zerolang, use `std.url` for lexical URL splitting, percent encoding, decoded +query lookup, form-urlencoded bodies, and query appending. + +Runnable today: + +| API | Return | Notes | +| --- | --- | --- | +| `std.url.percentEncode(buffer, bytes)` | `Maybe>` | Percent-encodes bytes into caller storage. | +| `std.url.percentDecode(buffer, bytes)` | `Maybe>` | Percent-decodes bytes into caller storage. | +| `std.url.queryEscape(buffer, bytes)` | `Maybe>` | Query-escapes bytes, using `+` for spaces. | +| `std.url.queryUnescape(buffer, bytes)` | `Maybe>` | Query-unescapes bytes, converting `+` back to space. | +| `std.url.scheme(url)` | `Maybe>` | Borrows the URL scheme if present. | +| `std.url.authority(url)` | `Maybe>` | Borrows the URL authority if present. | +| `std.url.host(url)` | `Maybe>` | Borrows the host from the URL authority. | +| `std.url.path(url)` | `Span` | Borrows the path or an empty suffix. | +| `std.url.query(url)` | `Maybe>` | Borrows the raw query string if present. | +| `std.url.fragment(url)` | `Maybe>` | Borrows the raw fragment if present. | +| `std.url.queryValue(query, key)` | `Maybe>` | Borrows a raw query parameter value by key. | +| `std.url.queryValueDecoded(buffer, query, key)` | `Maybe>` | Looks up a raw or escaped query key and writes the decoded value. | +| `std.url.writeQueryParam(buffer, key, value)` | `Maybe>` | Writes an escaped `key=value` query parameter. | +| `std.url.writeFormField(buffer, key, value)` | `Maybe>` | Writes one application/x-www-form-urlencoded field. | +| `std.url.appendFormField(buffer, form, field)` | `Maybe>` | Appends one encoded field to an existing form body. | +| `std.url.formValue(buffer, form, key)` | `Maybe>` | Looks up a form field by raw or escaped key and writes the decoded value. | +| `std.url.appendQuery(buffer, base, query)` | `Maybe>` | Writes a URL with an appended raw query segment. | +| `std.url.writeUrl(buffer, scheme, host, path)` | `Maybe>` | Writes a `scheme://host/path` URL. | +| `std.url.appendFragment(buffer, base, fragment)` | `Maybe>` | Writes a URL with an appended raw fragment. | + +Metadata labels: + +- effects: parse +- allocation behavior: no allocation; writers use caller storage +- target support: target-neutral +- error behavior: `Maybe` helpers return null on malformed input or insufficient storage +- ownership notes: borrowed slices point into the input; encoded output points into caller storage +- examples: `conformance/native/pass/std-codec-json-url.graph` + +## Example + +```zero +pub fn main(world: World) -> Void raises { + let url: Span = "https://example.com/path?q=zero%20lang#part" + let host: Maybe> = std.url.host(url) + let query: Maybe> = std.url.query(url) + let fragment: Maybe> = std.url.fragment(url) + var out: [48]u8 = [0_u8; 48] + var param_buf: [16]u8 = [0_u8; 16] + let param: Maybe> = std.url.writeQueryParam(param_buf, "q", "zero lang") + var decoded_buf: [16]u8 = [0_u8; 16] + var decoded: Maybe> = null + var next: Maybe> = null + if param.has { + next = std.url.appendQuery(out, "https://example.com/path", param.value) + } + if query.has { + decoded = std.url.queryValueDecoded(decoded_buf, query.value, "q") + } + if host.has && query.has && fragment.has && decoded.has && next.has && std.mem.eql(host.value, "example.com") && std.mem.eql(decoded.value, "zero lang") { + check world.out.write("url ok\n") + } +} +``` + +## Design Notes + +URL helpers are lexical and byte-oriented. They do not resolve DNS, normalize +paths, or allocate. Decoding rejects malformed percent escapes. Form helpers use +the same encoding as query strings: spaces become `+`, and other non-unreserved +bytes are percent-escaped. URL builders expect path, query, and fragment bytes +that are already escaped for their position. diff --git a/docs/articles/optimization.md b/docs/articles/optimization.md new file mode 100644 index 0000000..5aa4f7d --- /dev/null +++ b/docs/articles/optimization.md @@ -0,0 +1,77 @@ +## Make Retention Visible + +Zerolang optimization docs should answer a concrete question: why did this graph +input retain these bytes, helpers, allocations, or runtime shims? + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "why is this binary bigger now?" + }, + { + "role": "assistant", + "text": "I’ll inspect the size report and trace what is being retained." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero size --json", + "output": "{\"sizeBreakdown\":{},\"retentionReasons\":[\"std.http.fetch retained by handle\"]}" + } + ] + } + ] +} +``` + +## What This Means + +Use graph inputs or graph-first packages: + +```sh +zero size --json examples/fixed-vec.graph +zero mem --json examples/allocator-collections.graph +zero build --json --profile tiny --target linux-musl-x64 examples/hello.graph --out .zero/out/hello +``` + +## Profiles + +| Profile | Use when | Contract | +| --- | --- | --- | +| `debug` | You need diagnostics and local symbols. | Keeps diagnostic/debug metadata. | +| `fast` | Throughput matters more than minimum size. | Optimizes for speed within current direct codegen limits. | +| `small` | You want the normal release shape. | Pay-as-used helpers and deterministic artifacts. | +| `tiny` | Artifact size is the main constraint. | Minimum runtime metadata and strict helper budget. | + +Build JSON includes `profileSemantics`, `profileCatalog`, `profileBudget`, and +`safetyFacts`. + +## Size Reports + +`zero size --json` adds: + +- `sizeBreakdown` +- `retentionReasons` +- `optimizationHints` +- retained function and helper facts +- literal and section cost +- target and profile facts + +Use `retentionReasons` when an agent needs to explain why a helper, literal, or +debug metadata block stayed in the artifact. + +## Memory Reports + +`zero mem --json` includes: + +- `memoryBudgets` +- `allocatorFacts` +- `allocationInstrumentation` +- `collectionFacts` +- `safetyFacts` + +These fields are the public way to check whether an example remains +fixed-capacity, caller-buffered, or heap-free. diff --git a/docs/articles/package-manifest.md b/docs/articles/package-manifest.md new file mode 100644 index 0000000..52dca60 --- /dev/null +++ b/docs/articles/package-manifest.md @@ -0,0 +1,125 @@ +## Packages Are Graph-Backed + +In Zerolang, a human usually starts with the package they want, not the manifest fields: + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "start a small crm api package here" + }, + { + "role": "assistant", + "text": "I’ll start the package with zero.toml and add the first API route." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero init --template package --manifest toml --format binary", + "output": "graph project init ok\nwrote: ./zero.toml\nwrote: ./zero.graph" + }, + { + "command": "zero patch /tmp/crm-routes.patch", + "output": "program graph patch ok" + }, + { + "command": "zero run -- $'GET /health\\n\\n'", + "output": "HTTP/1.1 200 OK\ncontent-type: application/json\nconnection: close" + } + ] + } + ] +} +``` + +## What This Means + +A Zero package has a manifest, a graph store, and review projections: + +```text +zero.toml +zero.graph +src/main.0 +``` + +The manifest tells Zero where the human-readable target projection lives. The +graph store is the normal compiler input. The projection is for spans, review, +and rare manual edits. + +## Preferred Manifest + +Use `zero.toml` for new projects: + +```toml +[package] +name = "crm-api" +version = "0.1.0" +license = "MIT" + +[targets.cli] +kind = "exe" +main = "src/main.0" + +[dependencies.local-tools] +path = "../local-tools" +version = "0.1.0" +``` + +`zero.json` is still accepted as a compatibility manifest format. Use one +manifest in normal projects. If both exist for a directory input, `zero.toml` +takes precedence. + +## What `main` Means + +The `main` path names the human-readable projection associated with a target. +It does not make the projection the normal package compile input. + +Normal package commands compile from the checked-in `zero.graph` store: + +```sh +zero check +zero test +zero run -- help +zero build --out .zero/out/app +zero size --json +``` + +These commands report projection state, but they do not rewrite `.0` files. + +## Projection Import And Export + +Use projection commands only when humans need them: + +```sh +zero export +zero verify-projection +zero import +``` + +`zero export` refreshes `.0` files from the graph. `zero import` rebuilds the +graph from projection text after a human edit. `zero verify-projection` is the +no-write drift gate. + +## Dependencies + +Package-local imports resolve from `src/` projection paths for stable human +review: + +- `src/foo.0` defines module `foo` +- `src/foo/mod.0` defines directory module `foo` + +Local path dependencies are accepted. Exact versioned registry references are +recorded as metadata without remote fetches in the current compiler slice. + +Package inspection and docs JSON may include `dependencies`, `package.lockfile`, +`packageCache.cacheKeyInputs`, and `publicationGate` facts. Diagnostics such as +`PKG001` and `PKG004` explain invalid manifests, dependency shape issues, and +package resolution failures. + +## Profiles + +Profiles are declared in the manifest and reported by build/size JSON. Use +`zero build --json` and `zero size --json` to inspect `profileSemantics`, +`profileBudget`, retained helpers, and target readiness. diff --git a/docs/articles/primitives.md b/docs/articles/primitives.md new file mode 100644 index 0000000..6a4cadb --- /dev/null +++ b/docs/articles/primitives.md @@ -0,0 +1,177 @@ +## The Pieces The Graph Stores + +Zerolang exposes language pieces as graph facts and as `.0` +projection syntax. The graph stores the type and layout facts. The projection +lets humans read them. + +## Scalar Values + +| Type | Purpose | +| --- | --- | +| `Bool` | Conditions and logical results. | +| `i8` `i16` `i32` `i64` | Signed fixed-width integers. | +| `u8` `u16` `u32` `u64` | Unsigned fixed-width integers. | +| `usize` `isize` | Pointer-sized integers. | +| `f32` `f64` | Floating-point values. | +| `char` | Byte-sized character value for ASCII/parser/codec work. | +| `String` | Text value used by string literals and current I/O examples. | +| `Void` | Return type for functions that produce no useful value. | + +Integer literals support decimal, hexadecimal, binary, octal, `_` separators, +and optional suffixes such as `_u8` or `_usize`. An unsuffixed integer literal +adopts the type of a typed integer operand in arithmetic and comparisons when +the value fits, so `index + 1` and `index < 10` work when `index` is `usize`. +Out-of-range literals are rejected, so `byte > 300` fails for a `u8` operand. + +```zero +let count: u32 = 0x12c_u32 +let byte: u8 = 255 +let page: usize = 4_096 +``` + +Primitive numeric types do not implicitly narrow, widen, or change signedness. +Use an explicit cast when the conversion is intentional. + +```zero +let count: u32 = 300 +let byte: u8 = count as u8 +``` + +## Absence + +`Maybe` represents an optional value: + +```zero +let parsed: Maybe = std.args.parseU32(1) +if parsed.has { + return parsed.value +} +return 0 +``` + +`.value` reads require a visible `.has` guard or fallible handling. That rule is +part of the graph semantics, not a formatter convention. + +## Fixed Storage And Views + +| Type form | Meaning | +| --- | --- | +| `[N]T` | Fixed-size array with `N` elements of `T`. | +| `Span` | Read-only borrowed pointer plus length. | +| `MutSpan` | Mutable borrowed pointer plus length. | +| `ref` | Immutable reference. | +| `mutref` | Mutable reference. | + +```zero +var scratch: [16]u8 = [0_u8; 16] +let bytes: Span = std.mem.span("hello") +let copied: usize = std.mem.copy(scratch, bytes) +``` + +These types are central to Zero's size and memory model. Helpers generally +write into caller-owned storage so allocation behavior remains visible. + +Fixed-size locals live in one stack frame per function, and a single function +may declare at most 131072 bytes of locals. `zero check` reports `MEM003` when +a frame exceeds that limit; split the buffer into smaller buffers in helper +functions so each frame stays within the limit, or process the data in +fixed-size chunks. `PageAlloc` and `GeneralAlloc` handles type-check but do +not lower to the direct backends yet, so they cannot replace frame-sized +buffers today. + +## Ownership + +Owned values use explicit ownership forms: + +```zero +fn drop(self: mutref) -> Void { + return +} +``` + +The canonical non-raising `fn drop(self: mutref) -> Void` shape lets the +graph model cleanup without a hidden runtime cleanup registry. Owned resources, +allocators, and cleanup behavior should be visible through graph inspection. + +## User Types + +```zero +type Point { + x: i32, + y: i32, +} +``` + +Fields, defaults, and constructor-like projections are graph declarations and +edges. Public type surfaces should stay explicit because agents rely on stable +field and type facts. + +## Enums And Choices + +```zero +enum Status { + Pending, + Ready, +} +``` + +Enums are named value sets. Choices and payload-bearing cases are represented +as graph facts so `match` can be checked semantically. + +## Fallibility + +Fallible functions use `raises`: + +```zero +fn validate(ok: Bool) -> i32 raises [InvalidInput] { + if !ok { + raise InvalidInput + } + return 42 +} +``` + +`check` propagates failure explicitly. There is no hidden exception system. + +## Compile-Time Values + +Compile-time facts currently cover bounded integer, `Bool`, and enum static +values. The metadata surface includes facts such as `compileTime`, +`target.pointerWidth`, `fieldType`, and `hasEnumCase`. + +Use `zero inspect --json` or `zero check --json` when an agent needs those facts +for a patch. + +## Projection Examples + +Projection syntax is for humans. The graph stores the same facts directly. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "what types does this helper use?" + }, + { + "role": "assistant", + "text": "I’ll inspect the function facts and summarize the types." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero query --fn add", + "output": "fn add(x: i32, y: i32) -> i32\n return x + y" + } + ] + } + ] +} +``` + +For manual review, export the projection: + +```sh +zero export +``` diff --git a/docs/articles/standard-library.md b/docs/articles/standard-library.md new file mode 100644 index 0000000..78484ce --- /dev/null +++ b/docs/articles/standard-library.md @@ -0,0 +1,102 @@ +## Graph-Backed Library Surface + +The Zerolang standard library is graph-backed. The compiler uses binary `std/*.graph` +stores. Sibling `std/*.0` files are human-readable projections for review, not +the normal compile path. + +Agents should learn the callable surface from the installed compiler: + +```sh +zero skills get stdlib +``` + +Humans should use this page to decide which module to ask for. + +## Expected Usage + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "make a small json http api" + }, + { + "role": "assistant", + "text": "I’ll use the HTTP and JSON helpers and run a couple route checks." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero skills get stdlib", + "output": "stdlib helpers: std.http.writeJsonOk, std.http.requestIsGet, std.json.field, ..." + }, + { + "command": "zero query --find handle", + "output": "fn handle(request: Span, response: MutSpan) -> Maybe>" + } + ] + } + ] +} +``` + +## Module Groups + +Core data and memory: + +- `std.mem`: spans, byte equality, copy/fill, allocators, byte buffers, and fixed-capacity vectors. +- `std.collections`: fixed-capacity collection operations over caller-owned storage. +- `std.search`: scalar span search and binary search. +- `std.sort`: in-place sorting over caller-owned scalar storage. +- `std.ascii`, `std.text`, `std.str`: byte-backed text helpers. +- `std.unicode`: strict UTF-8 codepoint decode/encode iteration and codepoint classes. +- `std.parse`, `std.fmt`, `std.codec`, `std.math`: parsers, formatters, codecs, and numeric helpers. +- `std.regex`: compile-once regular expression matching for a documented subset. +- `std.inet`: IPv4, IPv6, and hostname literal validation and parsing. + +Program surfaces: + +- `std.args`, `std.cli`, `std.env`: command-line and environment helpers. +- `std.io`, `std.fs`, `std.path`: caller-buffer I/O, hosted filesystem helpers, and lexical paths. +- `std.json`, `std.toml`, `std.url`, `std.csv`, `std.log`: data formats and structured output. +- `std.testing`: test-block predicates. + +Runtime and web: + +- `std.time`, `std.rand`, `std.proc`, `std.term`, `std.crypto`: hosted/runtime helper surfaces, terminal sequences, key decoding, terminal metadata, and raw terminal mode. +- `std.net`, `std.http`: network metadata, HTTP client/server metadata, request parsing, response writing, hosted fetch, and local listen support. + +## Inspect What A Program Uses + +```sh +zero inspect --json examples/crm-api +zero size --json examples/crm-api +zero mem --json examples/allocator-collections.graph +``` + +Useful JSON fields include: + +- `usedStdlibHelpers` +- `stdlibHelpers` +- `effects` +- `allocationBehavior` +- `targetSupport` +- `errorBehavior` +- `ownershipNotes` +- `apiStability` + +## Allocation And Capability Rule + +Standard library helpers should make ownership, effects, and target support +visible. Hosted APIs such as filesystem, process, time, random, network, and +HTTP require target capabilities. Buffer-oriented helpers should write into +caller-owned storage rather than silently allocating. + +## Projections In Module Pages + +Module pages include `.0` snippets because humans need readable examples. Treat +those snippets as projection examples. Agents should patch user programs +through graph commands and use `zero export` only when a human asks to review +the resulting projection. diff --git a/docs/articles/target-capabilities.md b/docs/articles/target-capabilities.md new file mode 100644 index 0000000..4544079 --- /dev/null +++ b/docs/articles/target-capabilities.md @@ -0,0 +1,81 @@ +## Capabilities Are Target Facts + +In Zerolang, start with the user request. The agent should inspect target capability facts +before patching APIs that depend on hosted runtime support. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "make this cli work on linux musl too" + }, + { + "role": "assistant", + "text": "I’ll check the target facts and call out anything that blocks the port." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero check --json --target linux-musl-x64", + "output": "{\"ok\":false,\"diagnostics\":[{\"code\":\"TAR002\",\"message\":\"target does not provide required capability\"}]}" + } + ] + } + ] +} +``` + +## What This Means + +Zero does not assume every target can do filesystem, network, process, time, or +random operations. Those are explicit target facts. Target JSON includes host +identity, aliases, object formats, C target mapping, capabilities, HTTP runtime +metadata, and `targetToolchains`. + +## Inspect Targets + +```sh +zero targets +zero targets --json +zero check --json --target linux-musl-x64 examples/memory-package +``` + +## Hosted Capabilities + +The current hosted capability set includes: + +- `args` +- `env` +- `fs` +- `memory` +- `net` +- `proc` +- `rand` +- `stdio` +- `time` + +Non-host targets expose only the capabilities listed for that target. Network +support is intentionally target-gated. HTTP helpers that only parse or write +request/response envelopes are target-neutral; hosted fetch and listen require +network-capable host support. + +## Capability Failure Is A Feature + +If a graph input uses `std.fs` on a target that cannot provide filesystem +support, `zero check --target ...` should report a diagnostic instead of +silently changing behavior. + +```sh +zero check --json --target linux-musl-x64 conformance/common/fail/unsupported-target-feature.graph +``` + +The diagnostic is `TAR002` and the repair id points at choosing a target with +the required capability. + +## What To Remember + +Capabilities are part of the graph contract. Standard library pages document +effects and target support so an agent can choose the right helper before it +patches the program. diff --git a/docs/articles/testing.md b/docs/articles/testing.md new file mode 100644 index 0000000..14b5c0b --- /dev/null +++ b/docs/articles/testing.md @@ -0,0 +1,80 @@ +## Run Tests From The Graph + +In Zerolang, `zero test` runs test blocks from graph inputs and graph-first packages. When +this page shows `test` projection syntax, read it as the human review view of a +graph-backed test declaration. + +```json-render +{ + "messages": [ + { + "role": "user", + "text": "add subtract and prove it works" + }, + { + "role": "assistant", + "text": "I’ll add the function and a focused test for it." + }, + { + "role": "tools", + "calls": [ + { + "command": "zero patch /tmp/subtract.patch", + "output": "program graph patch ok" + }, + { + "command": "zero test --json --filter subtract", + "output": "{\"ok\":true,\"passedTests\":1,\"expectedFailures\":0}" + } + ] + } + ] +} +``` + +## What This Means + +```zero +test "add works" { + expect (add(40, 2) == 42) +} +``` + +Agents should add or update tests through graph patches. + +## Daily Test Commands + +```sh +zero test +zero test --json +zero test --json --filter add +``` + +Package tests discover test blocks across the package entry file and local +modules. Filters use substring matching on the test name. + +## Expected Failures + +Expected-fail tests are named with `xfail:`, `expected fail:`, or `[xfail]`. + +| Result | JSON effect | +| --- | --- | +| The test fails as expected. | `expectedFailures` increments. | +| The test passes unexpectedly. | `unexpectedPasses` increments and the command fails. | + +`zero test --json` includes `fixtures`, `snapshotKey`, selected/discovered +counts, stdout/stderr, and per-test results. + +## Repository Reliability + +Repository-level reliability checks still live outside the docs site: + +```sh +pnpm run reliability:smoke +pnpm run native:sanitize +pnpm run conformance +``` + +The reliability smoke covers golden output rows, structured snapshot rows, +fuzz cases, and crasher regressions. Use those suites for compiler reliability, +not per-page documentation assertions. diff --git a/docs/components/agent-chat.tsx b/docs/components/agent-chat.tsx new file mode 100644 index 0000000..aae94ab --- /dev/null +++ b/docs/components/agent-chat.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { useState } from "react"; + +type ToolCall = { command: string; output?: string }; + +type Message = + | { role: "user"; text: string } + | { role: "assistant"; text: string } + | { role: "skill"; name: string } + | { role: "tools"; calls: ToolCall[] } + | { role: "output"; text: string }; + +export type ChatSpec = { title?: string; messages: Message[] }; + +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + return ( + + ); +} + +function ChevronIcon({ open }: { open: boolean }) { + return ( + + + + ); +} + +function ToolCalls({ calls }: { calls: ToolCall[] }) { + const [open, setOpen] = useState([]); + const toggle = (i: number) => + setOpen((prev) => (prev.includes(i) ? prev.filter((n) => n !== i) : [...prev, i])); + + return ( +
+ {calls.map((call, i) => { + const isOpen = open.includes(i); + const hasOutput = typeof call.output === "string" && call.output.length > 0; + return ( +
+ + {isOpen && hasOutput && ( +
+                {call.output}
+              
+ )} +
+ ); + })} +
+ ); +} + +function ChatMessage({ message }: { message: Message }) { + if (message.role === "user") { + return ( +
+ +
+ {message.text} +
+
+ ); + } + if (message.role === "assistant") { + return ( +
{message.text}
+ ); + } + if (message.role === "skill") { + return ( +
+ + + + + Loaded {message.name} skill + +
+ ); + } + if (message.role === "tools") { + return ; + } + return ( +
+      {message.text}
+    
+ ); +} + +export function AgentChat({ spec }: { spec: ChatSpec }) { + return ( +
+
+ {spec.messages.map((message, i) => ( + + ))} +
+
+ ); +} diff --git a/docs/components/button.tsx b/docs/components/button.tsx new file mode 100644 index 0000000..bcaf2d6 --- /dev/null +++ b/docs/components/button.tsx @@ -0,0 +1,46 @@ +import Link from "next/link"; +import type { ButtonHTMLAttributes, ComponentProps } from "react"; + +const SIZES = { + sm: "h-8 px-4 text-[0.8125rem]", + md: "h-10 px-6 text-sm", + lg: "h-11 gap-2 px-8 text-[0.9375rem]", +}; + +const VARIANTS = { + default: "border-border bg-bg text-fg hover:border-fg", + primary: + "border-accent bg-accent text-accent-fg hover:bg-transparent hover:text-accent", +}; + +type ButtonVariant = keyof typeof VARIANTS; +type ButtonSize = keyof typeof SIZES; + +type ButtonStyleProps = { + variant?: ButtonVariant; + size?: ButtonSize; + className?: string; +}; + +function classes({ variant = "default", size = "md", className = "" }: ButtonStyleProps): string { + return [ + "inline-flex items-center justify-center rounded-md border font-medium no-underline transition", + VARIANTS[variant], + SIZES[size], + className, + ] + .filter(Boolean) + .join(" "); +} + +type ButtonProps = ButtonHTMLAttributes & ButtonStyleProps; + +export function Button({ variant, size, className, ...rest }: ButtonProps) { + return