Skip to content

Add math built-ins: log, trig, constants, utilities (closes #467, v0.0.116)#498

Merged
aallan merged 1 commit into
mainfrom
math-builtins
Apr 20, 2026
Merged

Add math built-ins: log, trig, constants, utilities (closes #467, v0.0.116)#498
aallan merged 1 commit into
mainfrom
math-builtins

Conversation

@aallan

@aallan aallan commented Apr 20, 2026

Copy link
Copy Markdown
Owner

Summary

Fifteen new pure math functions. Closes #467.

Logarithmic: log, log2, log10
Trigonometric: sin, cos, tan, asin, acos, atan, atan2 (POSIX (y, x) argument order)
Constants: pi(), e()
Numeric utilities: sign, clamp, clamp_float

Implementation split

  • Host-imported (10) — the log/trig family has no WASM equivalent. Each one routes through vera.{op} wrapping Python's math module (Python runtime) or Math.* (browser). Per-op gated via _math_ops_used so unused imports don't bloat modules.
  • Inlined as WAT (5)pi() / e() become f64.const 3.141592… (no host call for a constant); sign uses branchless (x > 0) - (x < 0); clamp uses two WASM select instructions; clamp_float uses native f64.max / f64.min.

Tests

  • tests/conformance/ch09_math_builtins.vera — verify level (covers signatures + effect inference for all 15; no run-level assertions because floating-point precision would flake).
  • tests/test_codegen.py::TestMathBuiltins — 8 tests: identities (log(e()) == 1, sin(0) + cos(0) == 1, atan(1) == atan2(1,1)), exact pi/e constants, sign across negative/zero/positive, 7-case clamp, 4-case clamp_float, and a gating regression that confirms trivial programs don't emit unused math imports.
  • tests/test_browser.py::TestBrowserMathBuiltins — 5 parity tests including an atan2(1, -1) case that disambiguates (y,x) vs (x,y) argument order.

Documentation

  • SKILL.md: new "Logarithmic, trigonometric, and numeric utility functions" subsection.
  • spec/09-standard-library.md: new §9.6.10 with the full table; Float64 Predicates renumbered §9.6.12.
  • spec/12-runtime.md: 10 new import-table rows.
  • Built-in function count: 122 → 137 across README / ROADMAP / FAQ / HISTORY.
  • Conformance count: 76 → 77; test count: 3,369 → 3,387.

Also bundled

Per workflow (closing PR owns the roadmap→history shuffle):

  • ROADMAP: Add math built-ins (log, trig, constants) #467 removed from "What's next" and Phase 4a.
  • HISTORY: v0.0.116 row added to Stage 11 table.
  • Version bump v0.0.115 → v0.0.116 in pyproject.toml, vera/init.py, docs/index.html, README.md, and uv.lock.
  • SKILL.md allowlist drift fix (the new bare-calls fragment needed a FRAGMENT entry; 32 existing entries shifted +22 lines).

Test plan

  • mypy vera/ clean
  • ruff check --select S vera/ clean (the CI lint)
  • pytest tests/ -q — 3,373 passed, 14 skipped (13 new tests)
  • check_spec_examples.py, check_skill_examples.py, check_conformance.py, check_doc_counts.py, check_version_sync.py all green
  • vera run on a program exercising all 15 functions

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added 15 Math built-ins: log, log2, log10; sin, cos, tan, asin, acos, atan, atan2; pi(), e(); sign, clamp, float_clamp.
  • Documentation

    • Updated spec, guides, README, ROADMAP, HISTORY and changelogs to document new built-ins and adjust counts/metrics.
  • Tests

    • Conformance suite increased to 77 programs; new unit and browser tests exercise math built-ins.
  • Chores

    • Project version bumped to v0.0.116.

@coderabbitai

coderabbitai Bot commented Apr 20, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 15 math built‑ins (log/log2/log10, sin/cos/tan, asin/acos/atan/atan2, pi, e, sign, clamp, float_clamp); threads per‑op host‑import tracking through the compiler and WASM emission; conditionally inlines or emits imports; updates browser/runtime bindings, tests, specs, docs, and bumps version + conformance count (76→77).

Changes

Cohort / File(s) Summary
Docs & Version
AGENTS.md, CHANGELOG.md, CLAUDE.md, DESIGN.md, FAQ.md, HISTORY.md, README.md, ROADMAP.md, pyproject.toml, vera/__init__.py
Bump release to v0.0.116; update built‑ins count (122→137) and conformance programs (76→77); add changelog/HISTORY entries describing math built‑ins.
Specification & SKILL
spec/09-standard-library.md, spec/12-runtime.md, SKILL.md
Insert new §9.6.10 documenting 15 math utilities, IEEE‑754/domain notes, atan2(y,x) order; add conditional host‑import entries in runtime spec and renumber following sections.
Tests & CI scripts
TESTING.md, tests/conformance/manifest.json, tests/test_codegen.py, tests/test_browser.py, scripts/check_skill_examples.py, scripts/check_spec_examples.py
Add ch09_math_builtins conformance manifest (level: verify); add codegen/browser tests for semantics, import gating and inlining; update allowlists, parametrizations and documented test counts.
Type env / Builtin registration
vera/environment.py
Register 15 new pure built‑ins with exact signatures (logs, trig, atan2, pi/e, sign, clamp, float_clamp).
CompileResult & propagation
vera/codegen/core.py, vera/codegen/api.py, vera/codegen/compilability.py, vera/codegen/functions.py, vera/codegen/modules.py
Introduce math_ops_used tracking in CodeGenerator/CompileResult; detect math calls during scanning; propagate state through compilation, compilability checks and cross‑module allowlist; register host imports at execution only when used.
WASM assembly & imports
vera/codegen/assembly.py
Emit conditional (import "vera" "<op>") entries for needed unary f64→f64 ops and binary atan2 when _math_ops_used indicates usage.
WASM call translation & inference
vera/wasm/context.py, vera/wasm/calls.py, vera/wasm/calls_math.py, vera/wasm/inference.py
Add WasmContext._math_ops_used; dispatch math built‑ins to dedicated translators; emit host‑call for unary math and atan2 (recording ops); inline pi/e as f64.const; inline sign/clamp/float_clamp as WAT sequences; add return‑type inference for new built‑ins.
Runtime (browser / Python execute)
vera/browser/runtime.mjs, vera/codegen/api.py
Browser runtime binds imports.vera.* math names to Math.* when declared; Python execute registers per‑op math host callbacks (wrapping math.* and mapping ValueError→NaN) only if math_ops_used non‑empty.
Allowlist & examples
scripts/check_skill_examples.py, scripts/check_spec_examples.py
Shifted many FRAGMENT allowlist line numbers and added allowlist entry for Math built‑in examples; adjusted parse‑stage allowlist keys for spec examples.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI / Frontend
    participant Core as CodeGen Core
    participant Asm as WASM Assembly
    participant Runtime as Host (JS / Python)
    CLI->>Core: compile(program with math calls)
    Core->>Core: scan AST → record math_ops_used
    Core->>Asm: translate calls (math translators or inline)
    alt op requires host import
        Asm->>Asm: emit call $vera.<op> and mark import
        Asm->>Core: include op in CompileResult.math_ops_used
    else op inlined
        Asm->>Asm: emit f64.const or select-based sequence
    end
    Core->>Runtime: produce module + CompileResult.math_ops_used
    Runtime->>Runtime: bind vera.<op> → Math.* / python math.* when declared
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

compiler, tests, spec, ci, docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding fifteen new pure math built-in functions (logarithmic, trigonometric, constants, and numeric utilities), with issue reference and version bump.
Linked Issues check ✅ Passed All required APIs from #467 are fully implemented: logarithmic (log, log2, log10), trigonometric (sin, cos, tan, asin, acos, atan, atan2), constants (pi, e), utilities (sign, clamp, float_clamp). Registration as pure, host imports with gating, WASM inlining, and browser/Python delegation are all present.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #467: adding math built-ins, their codegen/type-inference support, documentation, conformance test, and version bump. No unrelated features or architectural changes are present.
Docstring Coverage ✅ Passed Docstring coverage is 88.10% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch math-builtins

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Apr 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.61702% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.27%. Comparing base (3cf122a) to head (8540935).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
vera/wasm/calls_math.py 88.63% 5 Missing ⚠️
vera/codegen/api.py 91.30% 2 Missing ⚠️
vera/wasm/inference.py 75.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #498      +/-   ##
==========================================
+ Coverage   90.25%   90.27%   +0.02%     
==========================================
  Files          58       58              
  Lines       20137    20276     +139     
  Branches      233      236       +3     
==========================================
+ Hits        18174    18304     +130     
- Misses       1959     1968       +9     
  Partials        4        4              
Flag Coverage Δ
javascript 52.58% <100.00%> (+0.58%) ⬆️
python 95.05% <92.03%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ROADMAP.md`:
- Line 3: The roadmap's version anchors are stale: update occurrences of
"v0.0.108" (currently line 3) and "as of v0.0.115" (currently line 269) to
reflect the release this PR targets ("v0.0.116"), and scan the rest of
ROADMAP.md for any other mentions of v0.0.108 or v0.0.115 to replace them for
consistency; ensure the opening summary and the historical note both read
"v0.0.116" so the document remains accurate and synchronized with this release.

In `@spec/09-standard-library.md`:
- Around line 866-867: The spec currently lists the built-ins pi and e with the
wrong signature (Unit -> Float64); update their documented signatures to the
zero-argument form () -> Float64 to match the implemented/runtime usage (so
change the entries for `pi` and `e` from `Unit -> Float64` to `() -> Float64` in
the standard library docs).

In `@spec/12-runtime.md`:
- Around line 48-57: The import subsection text currently states it imports host
functions only for effect operations but the runtime now also imports
host-backed built-ins such as vera.log, vera.log2, vera.log10, vera.sin,
vera.cos, vera.tan, vera.asin, vera.acos, vera.atan and vera.atan2; update the
subsection wording to say it imports host functions both for side-effecting
operations and for host-backed built-ins (built-in math functions), and adjust
any examples or sentences that assert “only for effect operations” so they
acknowledge these vera.* math functions as imported host-backed built-ins.

In `@TESTING.md`:
- Line 76: Update the stale test count in TESTING.md's runner snippet: locate
the reference to the conformance suite for test_conformance.py (the phrase that
currently reads “parametrized — 375 tests”) and change the number from 375 to
385 so it matches the table entry that lists 385 conformance tests.

In `@tests/test_browser.py`:
- Around line 513-599: Add domain-edge parity tests that invoke the browser
runtime with out-of-domain inputs and assert the process completes and the
result is NaN: create tests (e.g. test_log_negative, test_asin_out_of_domain,
test_acos_out_of_domain) that compile sources using _compile_vera and run them
via _run_node with expressions IO.print(float_to_string(log(-1.0))) ,
IO.print(float_to_string(asin(2.0))) and IO.print(float_to_string(acos(2.0))).
Parse node["stdout"] to a float and use math.isnan(...) to assert the printed
value is NaN (and not an error/trap). Ensure each test imports math and uses the
same helpers (_compile_vera, _run_node) as the other tests.

In `@tests/test_codegen.py`:
- Around line 10487-10496: Add tests to tests/test_codegen.py that exercise the
lo > hi semantic branch for the built-ins clamp and clamp_float: extend the
existing cases list (used for clamp/clamp_float tests) to include inputs where
lo > hi (e.g., (v, lo, hi, expected) cases such as lo greater than hi where
expected follows the specified behaviour), and mirror these additions in the
other cases block referenced around the second location (the one at 10516-10521)
so both clamp and clamp_float cover the negative/invalid-range branch and
prevent regressions.
- Around line 10388-10390: The assertion checking for "$vera.log" can false-pass
because it matches substrings in "$vera.log2" and "$vera.log10"; update the test
in tests/test_codegen.py that uses result.wat to assert the import token with
token-boundary semantics instead of a plain substring check (for example,
replace assert "$vera.log" in result.wat with a regex/assert that matches a word
boundary like re.search(r'\$vera\.log\b', result.wat) or assert for the exact
token surrounded by non-word delimiters), keeping the existing checks for
"$vera.log2" and "$vera.log10".
- Around line 10415-10434: The test test_inverse_trig_at_known_points currently
doesn't validate atan2 argument order nor exercise asin/acos; update the test's
source so it computes and returns multiple values (or multiple assertions) that
exercise asin(0.0), acos(1.0), atan(1.0) and atan2 in both orders
(atan2(1.0,1.0) and atan2(1.0,1.0) with swapped args as appropriate) and then
assert expected numeric relationships (asin(0)==0, acos(1)==0, atan(1)≈π/4,
atan2(1,1)≈π/4 and atan2(y,x) respects argument order differences), and also
keep the existing checks that "$vera.atan" and "$vera.atan2" appear in
result.wat; modify the test_inverse_trig_at_known_points source string and the
execution/assertion logic accordingly to ensure all four functions are actually
exercised and the atan2 argument-order behavior is validated.

In `@vera/codegen/api.py`:
- Around line 2256-2313: The math host wrappers currently call Python math
functions directly (see _math_unary_host used with _math_unary_specs and
host_atan2) which lets Python ValueError domain errors become traps; update the
wrappers to catch ValueError around the py_fn(x) call in _math_unary_host and
around _math_mod.atan2(y, x) in host_atan2, and return float("nan") on
ValueError so IEEE‑754 NaN semantics (and existing linker.define_func usage) are
preserved across the WASM boundary.

In `@vera/wasm/inference.py`:
- Around line 328-338: _infer_fncall_vera_type is missing branches for the new
math builtins so calls like "log", "sin", "pi", "clamp_float", "sign", and
"clamp" can return None; update _infer_fncall_vera_type to mirror the logic in
_infer_fncall_wasm_type by adding the same name sets: treat "log", "log2",
"log10", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pi", "e",
"clamp_float" as the Vera float type and treat "sign" and "clamp" as the Vera
integer type (use the same Vera type identifiers used elsewhere in the file),
matching on expr.name and returning the appropriate Vera-type value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe8736b4-054c-45d1-a094-ce8fe4ff905d

📥 Commits

Reviewing files that changed from the base of the PR and between 3cf122a and a0f8a25.

⛔ Files ignored due to path filters (4)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • tests/conformance/ch09_math_builtins.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (30)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • DESIGN.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • spec/09-standard-library.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/environment.py
  • vera/wasm/calls.py
  • vera/wasm/calls_math.py
  • vera/wasm/context.py
  • vera/wasm/inference.py

Comment thread ROADMAP.md Outdated
Comment thread spec/09-standard-library.md Outdated
Comment thread spec/12-runtime.md
Comment thread TESTING.md
Comment thread tests/test_browser.py
Comment thread tests/test_codegen.py Outdated
Comment thread tests/test_codegen.py Outdated
Comment thread tests/test_codegen.py
Comment thread vera/codegen/api.py
Comment thread vera/wasm/inference.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (11)
vera/wasm/inference.py (1)

328-338: ⚠️ Potential issue | 🟠 Major

Mirror these math built-ins in _infer_fncall_vera_type.

Line 328 adds WASM inference, but Vera-type inference still has no corresponding branch for these functions, so type inference can fall through to None in downstream rewriting paths.

Proposed fix
@@
         if call.name in ("sqrt", "pow"):
             return "Float64"
+        if call.name in (
+            "log", "log2", "log10",
+            "sin", "cos", "tan", "asin", "acos", "atan", "atan2",
+            "pi", "e", "float_clamp",
+        ):
+            return "Float64"
+        if call.name in ("sign", "clamp"):
+            return "Int"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vera/wasm/inference.py` around lines 328 - 338, The Vera-type inference
function _infer_fncall_vera_type is missing the branch for the math built-ins
added to WASM inference; add a corresponding conditional that checks expr.name
for ("log", "log2", "log10", "sin", "cos", "tan", "asin", "acos", "atan",
"atan2", "pi", "e", "float_clamp") and returns "f64", and another for ("sign",
"clamp") returning "i64", so _infer_fncall_vera_type mirrors the WASM logic and
avoids falling through to None.
ROADMAP.md (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Version anchors are stale against this release update.

Line 3 still anchors current capabilities to v0.0.108, and Line 269 still says “as of v0.0.115” despite this PR targeting v0.0.116. Please synchronise both anchors so the roadmap remains historically accurate.

Suggested fix
-Vera v0.0.108 delivers a complete compiler pipeline ...
+Vera v0.0.116 delivers a complete compiler pipeline ...

-**810+ commits, 115 tagged releases (as of v0.0.115), 3,387 tests, 96% coverage, 77 conformance programs, 30 examples, 13 spec chapters.**
+**810+ commits, 116 tagged releases (as of v0.0.116), 3,387 tests, 96% coverage, 77 conformance programs, 30 examples, 13 spec chapters.**

As per coding guidelines **/*.md: review Markdown for factual accuracy and outdated information.

Also applies to: 269-269

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ROADMAP.md` at line 3, Update the stale version anchors in the roadmap:
replace the first occurrence referencing "v0.0.108" and the later note that
reads "as of v0.0.115" so they both reflect the actual target release
"v0.0.116"; edit the lines containing those exact version strings to "v0.0.116"
(search for "v0.0.108" and "v0.0.115" in ROADMAP.md and update them) to keep the
historical record accurate.
spec/12-runtime.md (1)

48-57: ⚠️ Potential issue | 🟡 Minor

Import-condition text is now outdated after adding pure math imports.

Line 67 still says imports are emitted only for effect operations, but Line 48–57 adds host imports for pure math builtins (log*, trig, atan2). Please widen this sentence to cover host-backed builtins as well.

Suggested wording
-Imports are only emitted when the program actually uses the corresponding effect operations. A pure program produces a module with no imports.
+Imports are only emitted when the program actually uses the corresponding host-backed operations (effect operations and selected pure builtins). A pure program that uses only inlined builtins produces a module with no imports.

As per coding guidelines spec/**/*.md: Language specification files must be factually accurate against the codebase.

Also applies to: 67-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/12-runtime.md` around lines 48 - 57, Update the import-condition wording
around the sentence at line 67 to reflect that imports are emitted not only for
effectful operations but also for host-backed pure builtins (e.g., vera.log,
vera.log2, vera.log10, vera.sin, vera.cos, vera.tan, vera.asin, vera.acos,
vera.atan, vera.atan2); change the sentence to state that imports are emitted
for operations that require host backing (effectful ops and host-backed
builtins) so the spec matches the added pure math imports listed in the table.
spec/09-standard-library.md (1)

866-867: ⚠️ Potential issue | 🟠 Major

Correct pi/e to zero-argument signatures.

These entries still document Unit -> Float64, but this PR’s API uses zero-arg calls (pi(), e()). Please keep the spec aligned with the implemented call form.

Suggested fix
-| `pi` | `Unit -> Float64` | `3.141592653589793` |
-| `e` | `Unit -> Float64` | `2.718281828459045` |
+| `pi` | `() -> Float64` | `3.141592653589793` |
+| `e` | `() -> Float64` | `2.718281828459045` |

As per coding guidelines: “spec/**/*.md: Language specification. Review for factual accuracy and broken code examples.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/09-standard-library.md` around lines 866 - 867, Update the documented
signatures for the constants pi and e from the current "Unit -> Float64" to the
zero-argument call form used by the implementation (e.g., "() -> Float64") so
the spec shows the actual call form pi() and e(); modify the table entries for
the symbols "pi" and "e" to use the zero-argument signature and corresponding
example call form.
TESTING.md (1)

76-76: ⚠️ Potential issue | 🟡 Minor

Update the stale conformance count in the runner snippet.

This file now states 385 conformance tests here, but the “Via pytest” snippet later still says 375. Please make those counts consistent.

As per coding guidelines: “**/*.md: Review Markdown files for factual accuracy against the codebase, broken links, and outdated information.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TESTING.md` at line 76, Update the mismatched conformance test count in the
"Via pytest" runner snippet so it matches the table entry for
`test_conformance.py` (which lists 385 tests); locate the "Via pytest"
code/snippet that currently says 375 and change that numeric literal to 385 so
both references to `test_conformance.py` are consistent.
tests/test_codegen.py (4)

10487-10496: ⚠️ Potential issue | 🟠 Major

Add lo > hi coverage for clamp to pin edge semantics.

The current cases do not exercise the inverted-bound branch, so regressions there would go undetected.

Proposed fix
         cases = [
             # (v, lo, hi, expected)
             (5, 0, 10, 5),      # within range → v
             (-3, 0, 10, 0),     # below lo → lo
             (15, 0, 10, 10),    # above hi → hi
+            (5, 10, 0, 0),      # lo > hi semantics -> hi
             (-10, -5, 5, -5),   # negative range, below
             (100, -5, 5, 5),    # negative range, above
             (7, 7, 7, 7),       # singleton (lo == hi == v)
             (0, 0, 0, 0),       # zero singleton
         ]

As per coding guidelines "Add codegen/runtime tests in tests/test_codegen.py for built-in functions — cover normal cases, edge cases (empty inputs, zero values), and composition with other built-ins".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 10487 - 10496, Add test cases to the
existing "cases" list for the clamp tests to cover the inverted-bound branch (lo
> hi) so pin-edge semantics are exercised; update the test vector list near the
clamp checks (the "cases" variable used by the clamp tests) with entries where
lo > hi (e.g., v within, below, and above the inverted range) to assert the
expected pinned value, ensuring the clamp function's inverted-bound behavior is
validated.

10388-10390: ⚠️ Potential issue | 🟡 Minor

Harden the log import assertion to avoid substring false-positives.

assert "$vera.log" in result.wat can pass when only $vera.log2/$vera.log10 is present. Match the import token exactly.

Proposed fix
-        assert "$vera.log" in result.wat
+        assert any(
+            '(import "vera" "log"' in line
+            for line in result.wat.splitlines()
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 10388 - 10390, The assertion for the
"$vera.log" import is too permissive and can match "$vera.log2" or
"$vera.log10"; update the test in tests/test_codegen.py to assert the exact
import token instead of a substring match—e.g., check result.wat for a
whole-token match of "$vera.log" (use a word-boundary/regex like \$vera\.log\b
or otherwise ensure surrounding non-word characters) while keeping the existing
checks for "$vera.log2" and "$vera.log10".

10516-10521: ⚠️ Potential issue | 🟠 Major

Add lo > hi coverage for float_clamp as well.

float_clamp has the same uncovered inverted-bound edge case.

Proposed fix
         cases = [
             (0.5, 0.0, 1.0, 0.5),
             (3.5, 0.0, 1.0, 1.0),
             (-3.5, 0.0, 1.0, 0.0),
             (-1.5, -2.0, -1.0, -1.5),  # negative in-range
+            (0.5, 2.0, 1.0, 1.0),       # lo > hi semantics -> hi
         ]

As per coding guidelines "Add codegen/runtime tests in tests/test_codegen.py for built-in functions — cover normal cases, edge cases (empty inputs, zero values), and composition with other built-ins".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 10516 - 10521, The float_clamp tests are
missing coverage for the inverted-bound case (lo > hi); update the cases list
used by the float_clamp test to include a tuple that represents this edge
(value, lo, hi, expected) mirroring the integer clamp inverted-bound entry
(i.e., add an entry where lo > hi and set the expected result to whatever the
float_clamp implementation returns in that scenario), referencing the
float_clamp test's cases variable so the test suite exercises the lo>hi
behavior.

10415-10434: ⚠️ Potential issue | 🟠 Major

atan2 ordering is not truly validated, and asin/acos are not exercised.

Using atan2(1.0, 1.0) is symmetric, so swapped argument order would still pass. The test docstring also claims asin/acos coverage but the body never calls them.

Proposed fix
     def test_inverse_trig_at_known_points(self) -> None:
-        """asin(0)==0, acos(1)==0, atan(1)≈π/4, atan2(1,1)≈π/4.
+        """asin/acos known points and non-symmetric atan2(y, x) order."""
+        import math
         source = """\
 public fn main(-> `@Float64`)
   requires(true) ensures(true) effects(pure)
 {
-  atan(1.0) - atan2(1.0, 1.0)
+  asin(0.0) + acos(1.0) + atan2(1.0, -1.0)
 }
 """
         result = _compile_ok(source)
+        assert "$vera.asin" in result.wat
+        assert "$vera.acos" in result.wat
         assert "$vera.atan" in result.wat
         assert "$vera.atan2" in result.wat
         v = execute(result, fn_name="main").value
-        # Both values equal π/4; difference should be exactly 0.
-        assert v == 0.0, f"atan(1) vs atan2(1,1) disagreement: {v}"
+        assert abs(v - math.atan2(1.0, -1.0)) < 1e-12

As per coding guidelines "Add codegen/runtime tests in tests/test_codegen.py for built-in functions — cover normal cases, edge cases (empty inputs, zero values), and composition with other built-ins".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 10415 - 10434, The test
test_inverse_trig_at_known_points currently only compares atan(1.0) vs
atan2(1.0, 1.0) (a symmetric case) and never exercises asin/acos; update the
test body (function main) to call and assert asin(0.0)==0.0 and acos(1.0)==0.0
and to include a non-symmetric atan2 call to validate argument ordering (e.g.,
compute atan2(1.0, -1.0) and compare to the expected sign/value relative to
atan2(-1.0, 1.0) or known constants), and add asserts that "$vera.asin" and
"$vera.acos" appear in result.wat along with existing "$vera.atan" and
"$vera.atan2"; modify the execute/result assertions in
test_inverse_trig_at_known_points to check these new numeric expectations and
wat symbols so the test truly covers asin/acos and verifies atan2 argument
ordering.
tests/test_browser.py (1)

504-599: ⚠️ Potential issue | 🟠 Major

Add domain-edge parity tests for the new math built-ins.

The new suite is strong on happy paths, but it still misses boundary/domain cases (log(-1.0), log(0.0), asin(2.0), acos(2.0)). Without these, regressions in non-trapping NaN/±inf behaviour can slip through.

Suggested test additions
+    def test_log_negative_is_nan(self, tmp_path: Path) -> None:
+        source = '''\
+public fn main(-> `@Unit`)
+  requires(true) ensures(true) effects(<IO>)
+{
+  IO.print(float_to_string(log(-1.0)))
+}
+'''
+        wasm_path, _ = _compile_vera(source, tmp_path)
+        node = _run_node(wasm_path)
+        import math
+        assert math.isnan(float(node["stdout"]))
+
+    def test_log_zero_is_neg_inf(self, tmp_path: Path) -> None:
+        source = '''\
+public fn main(-> `@Unit`)
+  requires(true) ensures(true) effects(<IO>)
+{
+  IO.print(float_to_string(log(0.0)))
+}
+'''
+        wasm_path, _ = _compile_vera(source, tmp_path)
+        node = _run_node(wasm_path)
+        import math
+        assert math.isinf(float(node["stdout"])) and float(node["stdout"]) < 0.0

As per coding guidelines "DO flag: Missing edge cases for new compiler features".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_browser.py` around lines 504 - 599, Add domain-edge parity tests
to TestBrowserMathBuiltins that exercise log(-1.0), log(0.0), asin(2.0) and
acos(2.0): for each case compile/run the same way as existing tests using
_compile_vera and _run_node with small Vera programs that print float_to_string
of the expression, parse node["stdout"] and assert the runtime produces the
expected IEEE results (use math.isnan for NaN cases: log(-1.0), asin(2.0),
acos(2.0); and math.isinf plus sign check for -inf from log(0.0)). Name the new
test functions clearly (e.g. test_log_domain_edges, test_asin_acos_domain_edges)
and follow the same pattern as test_log_identity and others so they run under
the same test harness.
vera/codegen/api.py (1)

2255-2313: ⚠️ Potential issue | 🔴 Critical

Handle math domain exceptions to prevent runtime traps.

Line 2259 promises IEEE-754-style behaviour, but Line 2280 directly calls py_fn(x). Python raises ValueError for several domain-edge inputs (e.g. log(-1), asin(2)), which currently escape as traps instead of returning Float64 sentinels.

Proposed fix
-        def _math_unary_host(
-            py_fn: Callable[[float], float],
+        def _math_unary_host(
+            op_name: str,
+            py_fn: Callable[[float], float],
         ) -> Callable[[wasmtime.Caller, float], float]:
@@
             def host(_caller: wasmtime.Caller, x: float) -> float:
-                return py_fn(x)
+                try:
+                    return py_fn(x)
+                except ValueError:
+                    # Match JS/IEEE-style non-trapping behaviour.
+                    if op_name in {"log", "log2", "log10"} and x == 0.0:
+                        return float("-inf")
+                    return float("nan")
             return host
@@
-                    _math_unary_host(py_fn), access_caller=True,
+                    _math_unary_host(op_name, py_fn), access_caller=True,
                 )
#!/bin/bash
# Verify Python math domain behaviour that currently propagates as traps.
python - <<'PY'
import math
cases = [
    ("log", -1.0),
    ("log", 0.0),
    ("log2", 0.0),
    ("asin", 2.0),
    ("acos", 2.0),
]
for name, x in cases:
    fn = getattr(math, name)
    try:
        print(f"{name}({x}) -> {fn(x)!r}")
    except Exception as exc:
        print(f"{name}({x}) raised {type(exc).__name__}: {exc}")
PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vera/codegen/api.py` around lines 2255 - 2313, Wrap the direct calls to math
functions in _math_unary_host.host and in host_atan2 with try/except so Python
domain/overflow exceptions are converted to IEEE-754 sentinels: catch ValueError
and return float("nan"); catch OverflowError and return math.copysign(math.inf,
x) (for unary host) or math.copysign(math.inf, y) (for host_atan2); keep using
the same identifiers (_math_unary_host, host, host_atan2, linker.define_func) so
the wrappers still get registered via linker.define_func.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/check_skill_examples.py`:
- Around line 46-47: Two allowlist entries have their reasons swapped: find the
ALLOWLIST tuples for the keys "FRAGMENT" and "QUALIFIED_CALLS" where one
currently uses the reason "Effect handler syntax template" and the other uses
"Qualified calls fragment" and swap those reason strings so each key maps to the
correct descriptive reason; repeat the same swap for the other duplicate pair of
"FRAGMENT"/"QUALIFIED_CALLS" tuples elsewhere in the file so both occurrences
are corrected.

In `@spec/09-standard-library.md`:
- Line 985: Section numbering got duplicated after inserting §9.6.10: the
heading "Float64 Predicates" now marked 9.6.12 conflicts with "String Search"
also starting at 9.6.12; renumber all subsequent §9.6.x headings in
spec/09-standard-library.md (starting from the "Float64 Predicates" and "String
Search" headings) so they form a contiguous sequence, and update any inline
cross-references or links that mention those section numbers (search for the
literal "9.6.12", "Float64 Predicates", and "String Search" to locate affected
headings and references), also update the table of contents entries and any code
examples/comments that reference the old section numbers.

In `@tests/test_codegen.py`:
- Around line 10364-10369: The tests refer to the utility function as
float_clamp while the spec/issue uses clamp_float; choose the canonical name and
make them consistent by renaming all occurrences: if you adopt clamp_float,
update the test cases, any inlined WAT utility declarations, and references in
tests and runtime generation (search for float_clamp) to clamp_float, or vice
versa; ensure function identifiers in the groups listing (e.g., the Utilities
group and any asserts that call float_clamp) match the chosen name and run tests
to verify no remaining references to the old identifier.

In `@vera/browser/runtime.mjs`:
- Around line 1780-1802: The JS math functions in _mathUnary (and
imports.vera.atan2) return NaN for out-of-domain inputs but the Python runtime
raises ValueError, so wrap each function assigned to imports.vera (e.g., via the
_mathUnary loop where you currently do imports.vera[name] = fn) with a wrapper
that calls the JS Math function, checks Number.isNaN(result) and if so throws
the Python ValueError (e.g., new imports.vera.ValueError(`${name} domain
error`)); leave Math.atan2 behavior unchanged but keep the argument-order note
for atan2. Ensure you reference the same symbols: _mathUnary,
imports.vera[name], and atan2 when making the change.

---

Duplicate comments:
In `@ROADMAP.md`:
- Line 3: Update the stale version anchors in the roadmap: replace the first
occurrence referencing "v0.0.108" and the later note that reads "as of v0.0.115"
so they both reflect the actual target release "v0.0.116"; edit the lines
containing those exact version strings to "v0.0.116" (search for "v0.0.108" and
"v0.0.115" in ROADMAP.md and update them) to keep the historical record
accurate.

In `@spec/09-standard-library.md`:
- Around line 866-867: Update the documented signatures for the constants pi and
e from the current "Unit -> Float64" to the zero-argument call form used by the
implementation (e.g., "() -> Float64") so the spec shows the actual call form
pi() and e(); modify the table entries for the symbols "pi" and "e" to use the
zero-argument signature and corresponding example call form.

In `@spec/12-runtime.md`:
- Around line 48-57: Update the import-condition wording around the sentence at
line 67 to reflect that imports are emitted not only for effectful operations
but also for host-backed pure builtins (e.g., vera.log, vera.log2, vera.log10,
vera.sin, vera.cos, vera.tan, vera.asin, vera.acos, vera.atan, vera.atan2);
change the sentence to state that imports are emitted for operations that
require host backing (effectful ops and host-backed builtins) so the spec
matches the added pure math imports listed in the table.

In `@TESTING.md`:
- Line 76: Update the mismatched conformance test count in the "Via pytest"
runner snippet so it matches the table entry for `test_conformance.py` (which
lists 385 tests); locate the "Via pytest" code/snippet that currently says 375
and change that numeric literal to 385 so both references to
`test_conformance.py` are consistent.

In `@tests/test_browser.py`:
- Around line 504-599: Add domain-edge parity tests to TestBrowserMathBuiltins
that exercise log(-1.0), log(0.0), asin(2.0) and acos(2.0): for each case
compile/run the same way as existing tests using _compile_vera and _run_node
with small Vera programs that print float_to_string of the expression, parse
node["stdout"] and assert the runtime produces the expected IEEE results (use
math.isnan for NaN cases: log(-1.0), asin(2.0), acos(2.0); and math.isinf plus
sign check for -inf from log(0.0)). Name the new test functions clearly (e.g.
test_log_domain_edges, test_asin_acos_domain_edges) and follow the same pattern
as test_log_identity and others so they run under the same test harness.

In `@tests/test_codegen.py`:
- Around line 10487-10496: Add test cases to the existing "cases" list for the
clamp tests to cover the inverted-bound branch (lo > hi) so pin-edge semantics
are exercised; update the test vector list near the clamp checks (the "cases"
variable used by the clamp tests) with entries where lo > hi (e.g., v within,
below, and above the inverted range) to assert the expected pinned value,
ensuring the clamp function's inverted-bound behavior is validated.
- Around line 10388-10390: The assertion for the "$vera.log" import is too
permissive and can match "$vera.log2" or "$vera.log10"; update the test in
tests/test_codegen.py to assert the exact import token instead of a substring
match—e.g., check result.wat for a whole-token match of "$vera.log" (use a
word-boundary/regex like \$vera\.log\b or otherwise ensure surrounding non-word
characters) while keeping the existing checks for "$vera.log2" and
"$vera.log10".
- Around line 10516-10521: The float_clamp tests are missing coverage for the
inverted-bound case (lo > hi); update the cases list used by the float_clamp
test to include a tuple that represents this edge (value, lo, hi, expected)
mirroring the integer clamp inverted-bound entry (i.e., add an entry where lo >
hi and set the expected result to whatever the float_clamp implementation
returns in that scenario), referencing the float_clamp test's cases variable so
the test suite exercises the lo>hi behavior.
- Around line 10415-10434: The test test_inverse_trig_at_known_points currently
only compares atan(1.0) vs atan2(1.0, 1.0) (a symmetric case) and never
exercises asin/acos; update the test body (function main) to call and assert
asin(0.0)==0.0 and acos(1.0)==0.0 and to include a non-symmetric atan2 call to
validate argument ordering (e.g., compute atan2(1.0, -1.0) and compare to the
expected sign/value relative to atan2(-1.0, 1.0) or known constants), and add
asserts that "$vera.asin" and "$vera.acos" appear in result.wat along with
existing "$vera.atan" and "$vera.atan2"; modify the execute/result assertions in
test_inverse_trig_at_known_points to check these new numeric expectations and
wat symbols so the test truly covers asin/acos and verifies atan2 argument
ordering.

In `@vera/codegen/api.py`:
- Around line 2255-2313: Wrap the direct calls to math functions in
_math_unary_host.host and in host_atan2 with try/except so Python
domain/overflow exceptions are converted to IEEE-754 sentinels: catch ValueError
and return float("nan"); catch OverflowError and return math.copysign(math.inf,
x) (for unary host) or math.copysign(math.inf, y) (for host_atan2); keep using
the same identifiers (_math_unary_host, host, host_atan2, linker.define_func) so
the wrappers still get registered via linker.define_func.

In `@vera/wasm/inference.py`:
- Around line 328-338: The Vera-type inference function _infer_fncall_vera_type
is missing the branch for the math built-ins added to WASM inference; add a
corresponding conditional that checks expr.name for ("log", "log2", "log10",
"sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pi", "e", "float_clamp")
and returns "f64", and another for ("sign", "clamp") returning "i64", so
_infer_fncall_vera_type mirrors the WASM logic and avoids falling through to
None.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 813e5c8c-5b68-497d-aa8f-9f5985892132

📥 Commits

Reviewing files that changed from the base of the PR and between a0f8a25 and 2115d87.

⛔ Files ignored due to path filters (4)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • tests/conformance/ch09_math_builtins.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (31)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • DESIGN.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/environment.py
  • vera/wasm/calls.py
  • vera/wasm/calls_math.py
  • vera/wasm/context.py
  • vera/wasm/inference.py

Comment thread scripts/check_skill_examples.py Outdated
Comment thread spec/09-standard-library.md
Comment thread tests/test_codegen.py
Comment thread vera/browser/runtime.mjs
Comment thread vera/wasm/calls_math.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (2)
tests/test_codegen.py (1)

10420-10441: ⚠️ Potential issue | 🟠 Major

atan2 argument-order is still not actually validated

Line 10431 uses atan2(1.0, 1.0), which is symmetric, so a swapped (x, y) implementation would still pass. This leaves the POSIX (y, x) contract unguarded.

Suggested test fix
     def test_inverse_trig_at_known_points(self) -> None:
-        """asin(0)==0, acos(1)==0, atan(1)≈π/4, atan2(1,1)≈π/4.
+        """asin/acos known points and non-symmetric atan2(y, x) ordering.
 
         Each expression exercises a distinct host import; the final
-        subtraction (atan vs atan2) is zero because both equal π/4,
-        confirming the two share consistent host implementations.
+        value must respect atan2(y, x) argument order.
         """
+        import math
         source = """\
 public fn main(-> `@Float64`)
   requires(true) ensures(true) effects(pure)
 {
-  asin(0.0) + acos(1.0) + (atan(1.0) - atan2(1.0, 1.0))
+  asin(0.0) + acos(1.0) + atan(1.0) + atan2(1.0, -1.0)
 }
 """
         result = _compile_ok(source)
         assert "$vera.asin" in result.wat
         assert "$vera.acos" in result.wat
         assert "$vera.atan" in result.wat
         assert "$vera.atan2" in result.wat
         v = execute(result, fn_name="main").value
-        # asin(0) = 0, acos(1) = 0, atan(1) - atan2(1, 1) = 0 → sum 0.
-        assert v == 0.0, f"inverse-trig identity broken: got {v}"
+        expected = math.asin(0.0) + math.acos(1.0) + math.atan(1.0) + math.atan2(1.0, -1.0)
+        assert abs(v - expected) < 1e-12, f"inverse-trig check failed: got {v}, expected {expected}"

As per coding guidelines "Add codegen/runtime tests in tests/test_codegen.py for built-in functions — cover normal cases, edge cases (empty inputs, zero values), and composition with other built-ins".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 10420 - 10441, The test
test_inverse_trig_at_known_points currently uses symmetric arguments atan2(1.0,
1.0) so a swapped-arg implementation would still pass; change the test to use
asymmetric values (e.g., atan2(0.5, 1.0)) and assert the POSIX contract by
comparing atan2(y,x) to atan(y/x) (for x>0) — update the source string in
test_inverse_trig_at_known_points (and any related asserts that inspect the
compiled wat or execute(result, fn_name="main").value) to use the asymmetric
inputs and the corresponding expected numeric check so that a swapped (x,y)
implementation fails.
spec/12-runtime.md (1)

31-31: ⚠️ Potential issue | 🟡 Minor

Update the subsection prose to match the new import table.

Line 67 still says imports are emitted only for effect operations, but Lines 48-57 now include pure host-backed math imports (vera.log, vera.sin, etc.). Please align the sentence to avoid an internal contradiction.

Suggested fix
-Imports are only emitted when the program actually uses the corresponding effect operations. A pure program produces a module with no imports.
+Imports are only emitted when the program actually uses the corresponding host-backed operations (effect operations and selected pure built-ins). A program that uses only inlined built-ins produces a module with no imports.

As per coding guidelines **/*.md: Review Markdown files for factual accuracy against the codebase, broken links, and outdated information.

Also applies to: 48-57, 67-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/12-runtime.md` at line 31, Update the subsection prose so it no longer
claims imports are emitted only for effect operations; instead state that the
module imports host functions for both effectful operations and pure host-backed
built-ins (e.g., vera.log, vera.sin and other math imports shown in the table).
Edit the sentence that currently asserts "imports are emitted only for effect
operations" to explicitly mention pure host-backed math imports and align the
surrounding text with the import table entries (vera.log, vera.sin, etc.) so the
paragraph is factually consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@HISTORY.md`:
- Line 252: Update the HISTORY.md sentence to clearly distinguish
implementations: state that only float_clamp uses f64.max/f64.min in its inline
WAT sequence, while sign and clamp are implemented as short WAT sequences using
select (or whatever their actual implementation is) without referencing
f64.max/f64.min; reference the symbols sign, clamp, and float_clamp so the
wording unambiguously ties float_clamp to f64.max/f64.min and keeps sign/clamp
described separately.

In `@spec/09-standard-library.md`:
- Around line 874-879: The fenced code block that starts with "let `@Float64` =
log(e())" is missing a language tag which triggers MD040 in CI; fix it by adding
the Vera language identifier immediately after the opening backticks (e.g.,
```vera) for that block so the snippet containing expressions like log(e()),
atan2(1.0, 1.0), sign(-42), and clamp(15, 0, 10) is properly tagged.

In `@tests/conformance/manifest.json`:
- Line 809: The manifest entry for the new math built-ins incorrectly references
"Section 9.6.3"; update the spec reference for the conformance entry by changing
the value of the spec_ref field in tests/conformance/manifest.json from "Section
9.6.3" to "Section 9.6.10" so it points to the log/trig/constants/utilities
section that documents the new functions (ensure the same manifest entry that
mentions the new math built-ins and float_clamp uses the corrected spec_ref).

In `@tests/test_browser.py`:
- Around line 513-624: Add parity assertions for the newly-wired unary host ops
by extending tests in tests/test_browser.py: create a small parametrised test
(similar to test_log_identity/test_pi_constant) that compiles and runs snippets
calling log2, log10, tan, and atan via IO.print(float_to_string(...)) using
_compile_vera and _run_node, then compare the stdout-parsed float to the
corresponding math.* value (math.log2, math.log10, math.tan, math.atan) within a
suitable tolerance; name the test something like test_unary_host_parity and
reuse helpers float_to_string, _compile_vera, and _run_node to keep parity
checks consistent.

In `@vera/environment.py`:
- Around line 1471-1474: The comment describing the clamp invariant is incorrect
for inverted bounds; update the documentation near the numeric utilities
(sign/clamp/float_clamp) in vera/environment.py to state that clamp semantics
are defined as min(max(v, lo), hi) and therefore the result equals min(max(v,
lo), hi) (which, when lo > hi, will yield hi), rather than unconditionally
claiming lo <= result <= hi; mention that tests (tests/test_codegen.py) expect
this inverted-bounds behavior for clamp(Int) and float_clamp(Float64).

---

Duplicate comments:
In `@spec/12-runtime.md`:
- Line 31: Update the subsection prose so it no longer claims imports are
emitted only for effect operations; instead state that the module imports host
functions for both effectful operations and pure host-backed built-ins (e.g.,
vera.log, vera.sin and other math imports shown in the table). Edit the sentence
that currently asserts "imports are emitted only for effect operations" to
explicitly mention pure host-backed math imports and align the surrounding text
with the import table entries (vera.log, vera.sin, etc.) so the paragraph is
factually consistent.

In `@tests/test_codegen.py`:
- Around line 10420-10441: The test test_inverse_trig_at_known_points currently
uses symmetric arguments atan2(1.0, 1.0) so a swapped-arg implementation would
still pass; change the test to use asymmetric values (e.g., atan2(0.5, 1.0)) and
assert the POSIX contract by comparing atan2(y,x) to atan(y/x) (for x>0) —
update the source string in test_inverse_trig_at_known_points (and any related
asserts that inspect the compiled wat or execute(result, fn_name="main").value)
to use the asymmetric inputs and the corresponding expected numeric check so
that a swapped (x,y) implementation fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a7d4c330-a39f-4aad-ab40-eb467b1f5522

📥 Commits

Reviewing files that changed from the base of the PR and between 2115d87 and 4a94dfa.

⛔ Files ignored due to path filters (8)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • docs/sitemap.xml is excluded by !docs/**
  • tests/conformance/ch09_math_builtins.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (31)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • DESIGN.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/environment.py
  • vera/wasm/calls.py
  • vera/wasm/calls_math.py
  • vera/wasm/context.py
  • vera/wasm/inference.py

Comment thread HISTORY.md Outdated
Comment thread spec/09-standard-library.md Outdated
Comment thread tests/conformance/manifest.json Outdated
Comment thread tests/test_browser.py
Comment thread vera/environment.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
spec/12-runtime.md (1)

31-67: ⚠️ Potential issue | 🟡 Minor

Resolve the contradictory import-emission statement.

Line 67 still says imports are emitted only for effect operations, but Lines 31 and 48-57 now document pure host-backed math imports. Please align that sentence so the subsection is internally consistent.

Suggested doc fix
-Imports are only emitted when the program actually uses the corresponding effect operations. A pure program produces a module with no imports.
+Imports are only emitted when the program actually uses the corresponding host-backed operations (effect operations and selected pure built-ins such as log/trig). A pure program that uses only inlined built-ins can produce a module with no imports.

As per coding guidelines **/*.md: Review Markdown files for factual accuracy against the codebase, broken links, and outdated information.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/12-runtime.md` around lines 31 - 67, The paragraph stating "Imports are
only emitted when the program actually uses the corresponding effect operations"
contradicts the earlier table that shows pure host-backed built-ins (e.g.
vera.log, vera.sin, vera.atan2) are also imported; update that sentence to say
that imports are emitted for any host-backed features the module uses —
including both effect operations (IO.*, State.*, Random.*, etc.) and pure
host-backed built-ins (e.g. vera.log, vera.sin, vera.cos, vera.atan2) — so the
subsection consistently reflects that a module only imports the host functions
it actually uses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DESIGN.md`:
- Line 32: The table row referencing "Standard library | 137 built-in functions
| Strings, arrays, maps, sets, decimals, JSON, HTML, Markdown, regex, base64,
URL — no external deps" is missing "math" in the domain breakdown; update that
row to include "math" (and adjust the surrounding rationale text if it mentions
domains) and verify/update the "137 built-in functions" number if the math
additions change the total so the count and domain list remain consistent.

In `@README.md`:
- Line 188: The features list in the README is missing the built-in "math"
domain while still claiming 137 built-ins; update the feature string that
currently includes "typed De Bruijn indices (`@T.n`), mandatory contracts,
algebraic effects (...), 137 built-in functions (...)" to include "math" in the
parenthetical domains (and, if available, add a link to the math docs like
(math) similar to other domains); ensure the wording still matches the 137
built-ins count and existing comma/parenthesis formatting around the built-in
domains.

In `@tests/test_codegen.py`:
- Around line 10363-10590: Add tests in TestMathBuiltins that assert domain-edge
inputs produce NaN by compiling small functions with log(-1.0), asin(2.0), and
acos(2.0) and checking the runtime result is NaN (use math.isnan or compare to
float("nan")). Locate the test class TestMathBuiltins and add a new test method
(e.g. test_math_domain_nan) that uses _compile_ok to compile each
single-expression source and execute(result, fn_name="main").value to assert NaN
for the three builtins (log, asin, acos); reuse the existing pattern from other
tests for compilation and execution.

---

Duplicate comments:
In `@spec/12-runtime.md`:
- Around line 31-67: The paragraph stating "Imports are only emitted when the
program actually uses the corresponding effect operations" contradicts the
earlier table that shows pure host-backed built-ins (e.g. vera.log, vera.sin,
vera.atan2) are also imported; update that sentence to say that imports are
emitted for any host-backed features the module uses — including both effect
operations (IO.*, State.*, Random.*, etc.) and pure host-backed built-ins (e.g.
vera.log, vera.sin, vera.cos, vera.atan2) — so the subsection consistently
reflects that a module only imports the host functions it actually uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4c871554-734b-4812-b483-d801a3d5ef2c

📥 Commits

Reviewing files that changed from the base of the PR and between 4a94dfa and 56484e9.

⛔ Files ignored due to path filters (8)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • docs/sitemap.xml is excluded by !docs/**
  • tests/conformance/ch09_math_builtins.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (31)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • DESIGN.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/environment.py
  • vera/wasm/calls.py
  • vera/wasm/calls_math.py
  • vera/wasm/context.py
  • vera/wasm/inference.py

Comment thread DESIGN.md Outdated
Comment thread README.md Outdated
Comment thread tests/test_codegen.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_browser.py`:
- Around line 505-660: Add two new browser-parity tests to
TestBrowserMathBuiltins: implement test_float_clamp and test_sign following the
same pattern as existing tests (use _compile_vera to build a small main that
prints float_to_string(float_clamp(...)) and float_to_string(sign(...)), run
with _run_node, and assert the returned stdout matches expected values). For
test_float_clamp include typical and edge cases (values inside range, below min,
above max, and exact bounds) and compare numeric results with tolerances similar
to other float checks; for test_sign include positive, negative, zero, and NaN
semantics (use float_is_nan to assert NaN behavior) and ensure comparisons use
float_to_string/parsing or boolean checks consistent with other tests. Name the
methods test_float_clamp and test_sign so they integrate with pytest and
reference the helper functions _compile_vera, _run_node, float_to_string, and
float_is_nan in the new tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ea5412da-232f-4340-9a27-cf07eace4e90

📥 Commits

Reviewing files that changed from the base of the PR and between 56484e9 and 45763db.

⛔ Files ignored due to path filters (8)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • docs/sitemap.xml is excluded by !docs/**
  • tests/conformance/ch09_math_builtins.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (31)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • DESIGN.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/environment.py
  • vera/wasm/calls.py
  • vera/wasm/calls_math.py
  • vera/wasm/context.py
  • vera/wasm/inference.py

Comment thread tests/test_browser.py
Fifteen new pure functions across four groups — the standard math
library every other language has.  Unlocks scientific computing,
graphics, audio, physics, and statistics workloads.

Operations:
- Logarithmic: log, log2, log10
- Trigonometric: sin, cos, tan, asin, acos, atan, atan2
- Constants: pi(), e()
- Numeric utilities: sign, clamp, clamp_float

Implementation split by whether WASM has native instructions:

HOST-IMPORTED (10):
The log/trig functions have no WASM equivalent, so they route
through host imports wrapping Python's `math` module and the
browser's `Math` global.  Per-op gated via `_math_ops_used` — a
program that only uses `sin` imports only `vera.sin`, keeping
modules small.  IEEE 754 semantics: NaN for out-of-domain inputs,
±Infinity for overflow.  atan2 takes `(y, x)` matching POSIX.

INLINED AS WAT (5):
- pi() / e() become `f64.const 3.141592653589793` /
  `f64.const 2.718281828459045` — no host call for a constant.
- sign(x) inlines as `(x > 0) - (x < 0)` using two comparison
  widenings and a subtraction, branchless.
- clamp(v, lo, hi) uses two WASM `select` instructions for
  min(max(v, lo), hi).  When lo > hi the outer min dominates and
  the result is hi (matches Rust's pre-1.50 behaviour).
- clamp_float uses native `f64.max` and `f64.min`.  NaN
  propagates through both, intentional.

Files touched (the pattern is now well-trodden after #463, #465):

- environment.py: 15 FunctionInfo entries, all pure-effect
- codegen/core.py: _math_ops_used set + propagation to 4
  CompileResult sites (caught a bug where the primary return
  path was missing the field — would have silently emitted
  no host functions for modules that need them)
- codegen/api.py: CompileResult.math_ops_used field, 10 host
  functions via a factory to avoid the classic late-binding
  closure trap
- codegen/compilability.py: _MATH_BUILTINS frozenset + scan
- codegen/functions.py: propagate WasmContext._math_ops_used
  back to codegen
- codegen/assembly.py: gated import declarations for the 10
  host-imported ops
- codegen/modules.py: known-names allowlist for all 15
- wasm/context.py: _math_ops_used mixin state
- wasm/calls_math.py: _translate_math_unary_host,
  _translate_atan2, _translate_pi, _translate_e, _translate_sign,
  _translate_clamp, _translate_clamp_float
- wasm/calls.py: dispatch branches for all 15
- wasm/inference.py: Float64/Int return types for all 15 — the
  inferencer needs to know that `log(x)` returns f64, otherwise
  `log(x) + log(y)` emits `i64.add` and wasmtime rejects the
  module at load time
- browser/runtime.mjs: `Math.log`/`Math.sin`/etc. bindings +
  `atan2` argument-order comment

Tests:
- tests/conformance/ch09_math_builtins.vera — verify level;
  covers signature plumbing for all 15 without the wall-clock /
  FP-precision flake of run-level assertions on trig
- TestMathBuiltins with 8 tests: identities (log/e, sin/cos at
  zero, atan vs atan2), pi/e exact constant values, sign across
  -/0/+, clamp on 7 cases including negative ranges and
  singletons, clamp_float on 4 cases, and a gating regression
  that confirms trivial programs don't emit unused math imports
- TestBrowserMathBuiltins with 5 parity tests

Documentation:
- SKILL.md: new "Logarithmic, trigonometric, and numeric utility
  functions" subsection
- spec/09-standard-library.md: new §9.6.10 with the full table;
  Float64 Predicates renumbered §9.6.12
- spec/12-runtime.md: 10 new import table rows
- Doc counts: 76 → 77 conformance, 3,369 → 3,387 tests

Also bundled (per workflow: closing PR owns the shuffle):
- ROADMAP: #467 removed from "What's next" and from Phase 4a
- HISTORY: v0.0.116 row added to Stage 11 table
- Version bump 0.0.115 → 0.0.116 in pyproject.toml,
  vera/__init__.py, docs/index.html, README.md, and uv.lock
- Built-in function count 122 → 137 in README / HISTORY /
  ROADMAP / FAQ

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan aallan merged commit eeb7688 into main Apr 20, 2026
19 checks passed
@aallan aallan deleted the math-builtins branch April 20, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add math built-ins (log, trig, constants)

1 participant