Add Json standard library type (#58)#350
Conversation
Json ADT: JNull, JBool, JNumber, JString, JArray, JObject. Two host imports (json_parse, json_stringify) plus six pure Vera utility functions injected via prelude. JObject wraps Map<String, Json>. Conditional injection avoids overhead for non-Json programs. New conformance test ch09_json (61 programs). New example json.vera. 33 new unit tests. Browser runtime support. Version bump to v0.0.98. Co-Authored-By: Claude <noreply@anthropic.invalid>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #350 +/- ##
==========================================
- Coverage 91.98% 91.41% -0.57%
==========================================
Files 47 48 +1
Lines 17950 18333 +383
Branches 211 213 +2
==========================================
+ Hits 16511 16760 +249
- Misses 1435 1569 +134
Partials 4 4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a standard-library Changes
Sequence Diagram(s)sequenceDiagram
participant User as User Code
participant Wasm as WASM Module
participant Host as Host Import Layer (Py/JS)
participant Parser as Parser (json.loads / JSON.parse)
participant Heap as WASM Heap
User->>Wasm: call exported fn -> invokes json_parse/path
Wasm->>Host: call imports.vera.json_parse(ptr,len)
Host->>Heap: read_string(ptr,len)
Heap-->>Host: UTF-8 bytes
Host->>Parser: parse string
alt parse success
Parser-->>Host: native value (dict/array/...)
Host->>Host: writeJson(native) -> node_ptr
Host->>Heap: alloc Result::Ok(node_ptr) -> res_ptr
else parse failure
Parser-->>Host: throws/error
Host->>Heap: alloc Result::Err(err_ptr) -> res_ptr
end
Host-->>Wasm: return res_ptr
Wasm-->>User: Result<Json,String>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
1 similar comment
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
TESTING.md (1)
206-212:⚠️ Potential issue | 🟡 MinorThis summary still disagrees with the prose below.
The table says there are 7 Tier 3 contracts out of 146 total, but the next paragraph still says "The 4 remaining Tier 3 contracts". Please update them together.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@TESTING.md` around lines 206 - 212, The prose and the metrics table disagree: the table lists "Tier 3 (runtime) | 7 contracts" and "Total | 146", while the paragraph mentions "The 4 remaining Tier 3 contracts"; update the prose to match the table (change the phrase "The 4 remaining Tier 3 contracts" to "The 7 remaining Tier 3 contracts") or, if the prose is correct, update the table values to reflect the accurate counts; ensure the "Tier 3 (runtime)" count and the total (146) are consistent across both the table and the subsequent paragraph.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 636-638: Update the three README bug rows to be platform-scoped
and clearer: change the `json_stringify` and `json_parse` entries to indicate
the limitation applies to the Python host (see vera/codegen/api.py which
delegates to the platform JSON module) rather than project-wide, and reword the
`json_get` row to explain that nested JObject returns an opaque handle (not a
deep copy) because JSON values are immutable in the current model; reference the
symbols `json_stringify`, `json_parse`, and `json_get` when editing the table
rows so the scope and immutability rationale are explicit.
In `@spec/09-standard-library.md`:
- Around line 14-15: Remove "Markdown" from the "Future types" bullet so the
overview matches §9.7.3 and existing runtime/tests; specifically edit the
bullets mentioning "Json type" and "Future types" (the lines containing `Json`
and `Markdown`) and either delete the `Markdown` entry or reclassify it as a
current/implemented type per §9.7.3 to keep the spec consistent.
- Line 1535: The paragraph incorrectly describes Json as opaque runtime handles
like Map/Set and claims all JSON functions are host imports; update it to state
that only json_parse and json_stringify are host imports, that query helper
functions are injected Vera functions, and that Json values are ordinary
heap-allocated ADT nodes in WASM memory rather than i32 handles; reference the
symbols json_parse, json_stringify, Json, Map, and Set to clarify the correct
implementation and contrast the real representation with Map/Set handles.
In `@TESTING.md`:
- Around line 9-12: The headline test LOC in the table row "| **Tests** | 2,967
across 26 files (~33,100 lines of test code) |" is stale; update the "~33,100
lines of test code" figure to the current total (~34,013) (or re-run your
line-count script and replace the number with the exact result), and ensure any
other occurrences of the old LOC value in TESTING.md are updated to match.
In `@tests/test_checker.py`:
- Around line 1923-2029: Add tests in TestJsonChecker for the missing built-ins:
create happy-path and wrong-argument tests for json_array_get,
json_array_length, and json_keys following the existing pattern using _check_ok
and _check_err. For each, add a test method (e.g., test_json_array_get /
test_json_array_get_wrong_type, test_json_array_length /
test_json_array_length_wrong_type, test_json_keys / test_json_keys_wrong_type)
that mirrors the style of test_json_array/test_json_object: use a small snippet
that passes correct types to assert _check_ok (e.g., json_array_get(`@Json.0`, 0)
-> `@Option`<Json>, json_array_length(`@Json.0`) -> `@Int`, json_keys(`@Json.0`) ->
`@Array`<String>) and a sibling _check_err case that calls the same builtin with
an incorrect argument type (e.g., String or Int where Json is expected) and
asserts a "type" error; place these methods inside the TestJsonChecker class
alongside the other json tests.
In `@tests/test_codegen.py`:
- Around line 8871-8884: Add a negative-index edge case to the existing
json_array_get OOB test: update or add a test (e.g.,
test_json_array_get_out_of_bounds) that calls json_array_get on a
JArray([JNull]) with index -1 and asserts the result is None (same expected
behavior as the positive OOB case). Locate the test function named
test_json_array_get_out_of_bounds in tests/test_codegen.py and add the extra
assertion or a separate test following the same pattern as the existing positive
OOB check to validate the lower-bound behavior.
- Around line 8643-8695: The tests use string_length(json_type(...)) which only
verifies length and can mask wrong values; update each test (e.g.,
test_json_type_null, test_json_type_bool, test_json_type_number,
test_json_type_string, test_json_type_array, test_json_type_object) to assert
the exact returned JSON type string from json_type (e.g., "null", "bool",
"number", "string", "array", "object") instead of or in addition to asserting
its length — locate occurrences of string_length(json_type(...)) and replace/add
an assertion that the evaluated json_type(...) equals the exact expected string
for the corresponding JNull, JBool, JNumber, JString, JArray, and JObject cases.
In `@vera/browser/runtime.mjs`:
- Around line 1595-1601: The current imports.vera.json_stringify handler calls
JSON.stringify(readJson(ptr)) and passes the result directly to allocString, but
JSON.stringify can return undefined (which would break allocString). Fix
imports.vera.json_stringify by capturing the result of JSON.stringify(value),
checking typeof text === "string" and if not, coerce a safe string (e.g.,
String(value) or "undefined") before calling allocString; reference the
functions readJson, JSON.stringify, allocString and the handler
imports.vera.json_stringify to locate the change.
In `@vera/codegen/assembly.py`:
- Around line 118-120: The current code sets self._needs_alloc and
self._needs_memory when self._json_ops_used is true but the alloc export
decision doesn't consider self._json_ops_used; update the alloc export gating so
that the export of the allocator (the $alloc export decision in the export/build
logic) also checks self._json_ops_used in addition to existing conditions.
Locate the export/alloc gating code in vera/codegen/assembly.py (the logic that
decides whether to export $alloc) and add self._json_ops_used to its predicate
so modules that only use JSON host ops will export $alloc and host-side
marshalling can allocate into WASM memory.
In `@vera/codegen/monomorphize.py`:
- Around line 73-75: The json_parse builtin's return was recorded only as
"Result" causing monomorphiser to drop generic parameter info; update the
_BUILTIN_PARAMETERIZED_RETURNS mapping in monomorphize.py to include a
parameterised return entry for "json_parse" (e.g. the Result generic shape with
its type variables) so _get_arg_type_info can resolve Result<T, E> correctly
during monomorphisation; locate the _BUILTIN_PARAMETERIZED_RETURNS structure and
add a keyed entry for "json_parse" that describes its parameterised return form
consistent with other builtins.
In `@vera/prelude.py`:
- Around line 555-573: The code unconditionally injects JSON combinators when
any JSON name is referenced, which breaks if the user has a custom/shadowed data
Json type; add a standard-shape guard like the Option/Result pattern: implement
a helper (e.g. _has_standard_json(user_data_names, program) or similar) that
verifies the user has the canonical Json constructors (_json_ctors) and/or
standard Json shape, and change the combinator injection to only append
_JSON_COMBINATORS when not json_fn_names.issubset(user_names) AND
_has_standard_json(...) returns true (leave the existing logic that appends
_JSON_DATA when "Json" is absent unchanged).
- Around line 409-424: _source_mentions_json currently only examines ast.FnDecl
nodes and only looks for a limited set of names; update it to scan all top-level
declarations in the ast.Program (not just FnDecl) and use the existing
_node_mentions helper to detect Json usage in TypeAlias, Import/ImportFrom, and
other decl kinds, and extend json_names to include json_type and json_has_field
(and any other Json helpers) so imported/alias-based uses are detected and the
prelude is injected when needed.
In `@vera/wasm/calls.py`:
- Around line 4456-4462: Record the JSON host-import usage in
self._json_ops_used before emitting the call: when you build the instructions
that append "call $vera.json_parse", add self._json_ops_used.add("json_parse")
(or the exact key expected by vera/codegen/functions.py) immediately before
ins.append(...); do the same for the "call $vera.json_stringify" site (add
self._json_ops_used.add("json_stringify")). This ensures the translate_expr path
that sets self.needs_alloc and appends the call also registers the op in
self._json_ops_used so the import is propagated.
In `@vera/wasm/inference.py`:
- Around line 297-301: The Vera-type inference function _infer_fncall_vera_type
is missing cases for the JSON builtins json_parse and json_stringify, causing
_infer_vera_type to return None for those call expressions; update
_infer_fncall_vera_type to return the appropriate Vera types for json_parse
(e.g., a Vera Json/result pointer type matching the "i32" Wasm mapping used in
_infer_fncall_wasm_type) and json_stringify (e.g., a Vera string pair type
matching "i32_pair"), so JSON call expressions produce non-None types
consistently across _infer_fncall_wasm_type, _infer_fncall_vera_type, and
downstream codegen paths.
In `@vera/wasm/json_serde.py`:
- Around line 178-191: The code should fail fast instead of silently returning
{} or None: in the read_json path, when tag == _TAG_JOBJECT and
map_store.get(handle) returns missing, raise an exception (e.g., ValueError)
that includes the handle and ptr so the invalid layout is surfaced, and likewise
replace the final "return None" for unknown tags with an explicit exception that
includes the unexpected tag and ptr; update read_json (and callers if necessary)
to propagate these errors rather than converting them into empty dicts or nulls,
using the existing symbols _TAG_JOBJECT, read_i32, map_store and read_json to
locate the code to change.
---
Outside diff comments:
In `@TESTING.md`:
- Around line 206-212: The prose and the metrics table disagree: the table lists
"Tier 3 (runtime) | 7 contracts" and "Total | 146", while the paragraph mentions
"The 4 remaining Tier 3 contracts"; update the prose to match the table (change
the phrase "The 4 remaining Tier 3 contracts" to "The 7 remaining Tier 3
contracts") or, if the prose is correct, update the table values to reflect the
accurate counts; ensure the "Tier 3 (runtime)" count and the total (146) are
consistent across both the table and the subsequent paragraph.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d3928221-7c39-4f0e-a1cb-67d89e309f8f
⛔ Files ignored due to path filters (4)
docs/index.htmlis excluded by!docs/**docs/llms-full.txtis excluded by!docs/**examples/json.verais excluded by!**/*.veratests/conformance/ch09_json.verais excluded by!**/*.vera
📒 Files selected for processing (29)
CHANGELOG.mdCLAUDE.mdREADME.mdROADMAP.mdSKILL.mdTESTING.mdscripts/check_readme_examples.pyscripts/check_skill_examples.pyscripts/check_spec_examples.pyspec/09-standard-library.mdtests/conformance/manifest.jsontests/test_checker.pytests/test_codegen.pytests/test_readme.pytests/test_verifier.pyvera/browser/runtime.mjsvera/codegen/api.pyvera/codegen/assembly.pyvera/codegen/compilability.pyvera/codegen/core.pyvera/codegen/functions.pyvera/codegen/modules.pyvera/codegen/monomorphize.pyvera/environment.pyvera/prelude.pyvera/wasm/calls.pyvera/wasm/context.pyvera/wasm/inference.pyvera/wasm/json_serde.py
Critical: Export $alloc for JSON-only modules (assembly.py), record json_parse/json_stringify in _json_ops_used (calls.py). Major: Fix spec §9.7.1 implementation description, add json_parse to _BUILTIN_PARAMETERIZED_RETURNS, expand Json prelude gate to cover all Json references, add shadowing guard for non-standard data Json, add Vera-type inference for JSON builtins, add 4 checker/codegen tests, strengthen json_type assertions, add allow_nan=False to json.dumps. Minor: Narrow README bug descriptions, fix spec §9.1 Markdown status, update TESTING.md counts and Tier 3 table, add negative-index test. Trivial: Guard JSON.stringify undefined in browser runtime, warn on unknown Json tags in json_serde.py. Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (5)
vera/wasm/json_serde.py (1)
178-180:⚠️ Potential issue | 🟡 MinorMissing warning for unknown map handle — silent
{}fallback.Line 180 silently falls back to an empty dict when the map handle is not found. Unlike the unknown tag case (which emits a warning), a missing handle could indicate memory corruption or a stale pointer, and warrants similar diagnostic output.
🛠️ Suggested fix: Add warning for missing handle
if tag == _TAG_JOBJECT: handle = read_i32(caller, ptr + 4) - raw_map = map_store.get(handle, {}) + raw_map = map_store.get(handle) + if raw_map is None: + import warnings + warnings.warn( + f"read_json: unknown map handle {handle} at pointer {ptr}; " + "possible memory corruption or stale reference", + RuntimeWarning, + stacklevel=2, + ) + raw_map = {} obj: dict[str, Any] = {}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/wasm/json_serde.py` around lines 178 - 180, When handling tag == _TAG_JOBJECT in the deserializer, don't silently fall back to {} on a missing map handle: after computing handle = read_i32(caller, ptr + 4) and fetching raw_map = map_store.get(handle, {}), detect the missing case (handle not in map_store) and emit a warning/diagnostic (consistent with the unknown-tag warning behavior) that includes the handle value and contextual info (e.g., ptr and tag) so memory corruption or stale pointer issues are visible; keep the fallback to {} but log the warning before returning.tests/test_codegen.py (1)
8751-8776:⚠️ Potential issue | 🟠 MajorStrengthen
json_stringifytests to assert value semantics, not just string length.These tests can still pass on incorrect serialisation with matching lengths. Please switch to parse-after-stringify assertions (round-trip), especially for
JNull,JBool, andJNumber.Proposed fix pattern
def test_json_stringify_null(self) -> None: - """json_stringify(JNull) returns 'null' (length 4).""" + """json_stringify(JNull) round-trips to JNull.""" source = """ public fn main(-> `@Int`) requires(true) ensures(true) effects(pure) -{ string_length(json_stringify(JNull)) } +{ + let `@Result`<Json, String> = json_parse(json_stringify(JNull)); + match `@Result`<Json, String>.0 { + Ok(`@Json`) -> match `@Json.0` { + JNull -> 1, + _ -> 0 + }, + Err(_) -> 0 + } +} """ - assert _run(source) == 4 + assert _run(source) == 1As per coding guidelines: “DO flag: Tests that don't assert anything meaningful” and “Add codegen/runtime tests in tests/test_codegen.py for built-in functions covering 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 8751 - 8776, Replace the fragile string-length assertions in test_json_stringify_null, test_json_stringify_bool, and test_json_stringify_number with round-trip assertions: call json_stringify on the literal (JNull, JBool(true), JNumber(42.0)), then parse that output back with json_parse and assert equality to the original AST (e.g. assert _run("json_parse(json_stringify(JNull))") == JNull). Keep references to the same test functions (test_json_stringify_null, test_json_stringify_bool, test_json_stringify_number) and builtins (json_stringify, json_parse, JNull, JBool, JNumber, _run) so the tests verify semantic equivalence rather than string length.tests/test_checker.py (1)
2014-2036:⚠️ Potential issue | 🟠 MajorAdd missing wrong-type checker cases for the remaining Json built-ins.
Line 2014 onwards adds happy-path coverage for
json_array_get,json_array_length, andjson_keys, but there are no corresponding wrong-argument type tests. This leaves these signatures under-protected versus regressions.Proposed test additions
class TestJsonChecker: @@ def test_json_array_get(self) -> None: """json_array_get returns Option<Json>.""" _check_ok(""" private fn foo(`@Json` -> `@Option`<Json>) requires(true) ensures(true) effects(pure) { json_array_get(`@Json.0`, 0) } """) + + def test_json_array_get_wrong_index_type(self) -> None: + """json_array_get with non-Int index produces error.""" + _check_err(""" +private fn foo(`@Json` -> `@Option`<Json>) + requires(true) ensures(true) effects(pure) +{ json_array_get(`@Json.0`, "0") } +""", "type") @@ def test_json_array_length(self) -> None: """json_array_length returns Int.""" _check_ok(""" private fn foo(`@Json` -> `@Int`) requires(true) ensures(true) effects(pure) { json_array_length(`@Json.0`) } """) + + def test_json_array_length_wrong_type(self) -> None: + """json_array_length with non-Json arg produces error.""" + _check_err(""" +private fn foo(`@Unit` -> `@Int`) + requires(true) ensures(true) effects(pure) +{ json_array_length(42) } +""", "type") @@ def test_json_keys(self) -> None: """json_keys returns Array<String>.""" _check_ok(""" private fn foo(`@Json` -> `@Array`<String>) requires(true) ensures(true) effects(pure) { json_keys(`@Json.0`) } """) + + def test_json_keys_wrong_type(self) -> None: + """json_keys with non-Json arg produces error.""" + _check_err(""" +private fn foo(`@Unit` -> `@Array`<String>) + requires(true) ensures(true) effects(pure) +{ json_keys(42) } +""", "type")As per coding guidelines: "Add type checker tests in tests/test_checker.py for built-in functions, with at minimum one test with correct types and one with a wrong argument type".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_checker.py` around lines 2014 - 2036, Add negative type-check tests for the three Json built-ins to complement the existing happy-paths: in tests/test_checker.py add one test each (mirroring test_json_array_get, test_json_array_length, test_json_keys) that calls json_array_get, json_array_length, and json_keys with a wrong argument type so the checker fails (e.g., pass an Int where a Json is expected for json_array_get and json_array_length, and pass an Int instead of Json for json_keys), using the project’s existing wrong-type assertion helper (the same style as other built-in tests) so the type checker rejects these calls.vera/codegen/api.py (1)
1558-1566:⚠️ Potential issue | 🟠 MajorDo not rely on Python’s JSON defaults if this needs browser parity.
_json.loads()still acceptsNaN/Infinity, and_json.dumps(..., allow_nan=False)still leaves Python’s default spaced separators in place. Becausejson_stringifyreturnsString, those defaults are observable in user code and can diverge from the browser bindings. Reject non-finite constants explicitly on parse and choose one canonical serialisation policy that both runtimes implement.Do Python's `json.loads` / `json.dumps` defaults and JavaScript's `JSON.parse` / `JSON.stringify` behave differently for `NaN` / `Infinity` and object or array whitespace? Cite the official Python `json` docs and MDN.Also applies to: 1597-1599
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/codegen/api.py` around lines 1558 - 1566, Change JSON handling to match browser behavior by rejecting non-finite numeric constants on parse and producing a compact, deterministic serialization on stringify: in host_json_parse (which calls _json.loads) pass a parse_constant that raises so any NaN/Infinity tokens are rejected (or post-validate parsed numbers with math.isfinite and return an error via _alloc_result_err_string), and in the corresponding stringify implementation (the json_stringify path around lines ~1597) call _json.dumps with allow_nan=False and separators=(',', ':') to avoid Python's spaced separators; update both host_json_parse and the stringify function to return errors via the existing _alloc_result_err_string/_alloc_result_ok_string helpers when invalid values are encountered.vera/prelude.py (1)
397-410:⚠️ Potential issue | 🟠 MajorValidate the full
Jsonshape before enabling the helpers.
_has_standard_json()currently accepts anydata Jsonwhose constructor names match, including cases likedata Json<T> { ... },JBool(String), orJArray(Array<Int>). The injected helpers then type-check against@Json,JBool(@Bool),JArray(@array<Json>), etc., and fail on the user-defined variant. Please mirror theOption/Resultguard here by checking zero type parameters, constructor arity, and field types for all six constructors.Also applies to: 596-599
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/prelude.py` around lines 397 - 410, The current _has_standard_json(program: ast.Program) only checks constructor names; instead, validate the full Json shape like the Option/Result guard by ensuring the DataDecl for "Json" has no type parameters (decl.type_params empty) and each constructor has the exact arity and field types expected: JNull with zero fields, JBool with one field of type Bool, JNumber with one field of type Number, JString with one field of type String, JArray with one field of type Array<Json>, and JObject with one field matching the expected object representation (e.g. Array<(String, Json)> or whatever the prelude uses); locate these checks via program.declarations -> decl (ast.DataDecl) -> decl.name == "Json" and iterate decl.constructors (c.name, c.fields) to compare arity and field AST types before returning True.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@vera/browser/runtime.mjs`:
- Around line 1553-1580: In readJson, unknown tag values are currently swallowed
and return null; update the function (readJson) to emit a console.warn when an
unrecognized tag is encountered, including the tag value and ptr (and optionally
related readI32/readF64 outputs) for debugging parity with the Python
RuntimeWarning, then continue returning null so behavior is unchanged; place the
warning just before the final return null to cover unexpected tags while
preserving existing control flow.
In `@vera/codegen/monomorphize.py`:
- Around line 86-90: The mapping _BUILTIN_PARAMETERIZED_RETURNS is missing
entries for json_get, json_array_get, and json_keys, so inner generic types are
lost during monomorphisation; add entries to that dict to preserve inner type
parameters (for example: "json_get": ("Option", ("Json",)), "json_array_get":
("Option", ("Json",)), and "json_keys": ("Option", ("Array", "String"))),
updating the _BUILTIN_PARAMETERIZED_RETURNS dict so _get_arg_type_info can bind
inner generics correctly for these functions.
In `@vera/wasm/json_serde.py`:
- Line 27: Replace the deprecated typing.Callable import with
collections.abc.Callable: update the import line in json_serde.py to continue
importing Any from typing but import Callable from collections.abc so references
to Callable in this module (e.g., type hints used in functions/classes) use the
modern, Python 3.9+ compatible symbol.
---
Duplicate comments:
In `@tests/test_checker.py`:
- Around line 2014-2036: Add negative type-check tests for the three Json
built-ins to complement the existing happy-paths: in tests/test_checker.py add
one test each (mirroring test_json_array_get, test_json_array_length,
test_json_keys) that calls json_array_get, json_array_length, and json_keys with
a wrong argument type so the checker fails (e.g., pass an Int where a Json is
expected for json_array_get and json_array_length, and pass an Int instead of
Json for json_keys), using the project’s existing wrong-type assertion helper
(the same style as other built-in tests) so the type checker rejects these
calls.
In `@tests/test_codegen.py`:
- Around line 8751-8776: Replace the fragile string-length assertions in
test_json_stringify_null, test_json_stringify_bool, and
test_json_stringify_number with round-trip assertions: call json_stringify on
the literal (JNull, JBool(true), JNumber(42.0)), then parse that output back
with json_parse and assert equality to the original AST (e.g. assert
_run("json_parse(json_stringify(JNull))") == JNull). Keep references to the same
test functions (test_json_stringify_null, test_json_stringify_bool,
test_json_stringify_number) and builtins (json_stringify, json_parse, JNull,
JBool, JNumber, _run) so the tests verify semantic equivalence rather than
string length.
In `@vera/codegen/api.py`:
- Around line 1558-1566: Change JSON handling to match browser behavior by
rejecting non-finite numeric constants on parse and producing a compact,
deterministic serialization on stringify: in host_json_parse (which calls
_json.loads) pass a parse_constant that raises so any NaN/Infinity tokens are
rejected (or post-validate parsed numbers with math.isfinite and return an error
via _alloc_result_err_string), and in the corresponding stringify implementation
(the json_stringify path around lines ~1597) call _json.dumps with
allow_nan=False and separators=(',', ':') to avoid Python's spaced separators;
update both host_json_parse and the stringify function to return errors via the
existing _alloc_result_err_string/_alloc_result_ok_string helpers when invalid
values are encountered.
In `@vera/prelude.py`:
- Around line 397-410: The current _has_standard_json(program: ast.Program) only
checks constructor names; instead, validate the full Json shape like the
Option/Result guard by ensuring the DataDecl for "Json" has no type parameters
(decl.type_params empty) and each constructor has the exact arity and field
types expected: JNull with zero fields, JBool with one field of type Bool,
JNumber with one field of type Number, JString with one field of type String,
JArray with one field of type Array<Json>, and JObject with one field matching
the expected object representation (e.g. Array<(String, Json)> or whatever the
prelude uses); locate these checks via program.declarations -> decl
(ast.DataDecl) -> decl.name == "Json" and iterate decl.constructors (c.name,
c.fields) to compare arity and field AST types before returning True.
In `@vera/wasm/json_serde.py`:
- Around line 178-180: When handling tag == _TAG_JOBJECT in the deserializer,
don't silently fall back to {} on a missing map handle: after computing handle =
read_i32(caller, ptr + 4) and fetching raw_map = map_store.get(handle, {}),
detect the missing case (handle not in map_store) and emit a warning/diagnostic
(consistent with the unknown-tag warning behavior) that includes the handle
value and contextual info (e.g., ptr and tag) so memory corruption or stale
pointer issues are visible; keep the fallback to {} but log the warning before
returning.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3947d9b2-9a9f-45c3-be57-ddf0ee3cb024
📒 Files selected for processing (12)
README.mdTESTING.mdspec/09-standard-library.mdtests/test_checker.pytests/test_codegen.pyvera/browser/runtime.mjsvera/codegen/api.pyvera/codegen/assembly.pyvera/codegen/monomorphize.pyvera/prelude.pyvera/wasm/calls.pyvera/wasm/json_serde.py
- Add wrong-type checker tests: json_array_length, json_keys, json_array_get_wrong_index_type - Add json_parse/json_stringify to _infer_fncall_vera_type (inference.py) - Add JObject handle warning in json_serde.py read_json - Add 8 codegen tests: stringify for each Json type, parse with string values, nested objects, roundtrip - json_serde.py coverage: 58% → 92% - Fix TESTING.md counts (2982 tests) Co-Authored-By: Claude <noreply@anthropic.invalid>
- Add console.warn for unknown tag in readJson (runtime.mjs) - Add json_get, json_array_get, json_keys to _BUILTIN_PARAMETERIZED_RETURNS - Replace deprecated typing.Callable with collections.abc.Callable Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
vera/wasm/json_serde.py (1)
27-27: 🧹 Nitpick | 🔵 TrivialUse
collections.abc.Callablefor modern Python typing imports.This has already been raised earlier and still applies at this line.
♻️ Suggested update
-from typing import Any, Callable +from collections.abc import Callable +from typing import Any#!/bin/bash # Verify legacy Callable imports in compiler Python modules. rg -n 'from typing import .*Callable|typing\.Callable' vera --type py🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/wasm/json_serde.py` at line 27, The import is using typing.Callable which is deprecated for modern typing; update the import in json_serde.py so that Callable is imported from collections.abc instead of typing (e.g., replace the current "from typing import Any, Callable" with an import that brings Callable from collections.abc while keeping Any from typing), ensuring references to Callable in the module remain unchanged.tests/test_codegen.py (1)
8751-8776:⚠️ Potential issue | 🟠 MajorStrengthen
json_stringifytests beyond length/non-empty checks.These assertions can pass with incorrect output of the same length (or any non-empty garbage), so they won’t reliably catch regressions in serialisation behaviour.
Proposed test-hardening pattern
def test_json_stringify_number(self) -> None: - """json_stringify(JNumber(42.0)) returns '42.0' (length 4).""" + """json_stringify(JNumber(42.0)) round-trips to JNumber(42.x).""" source = """ public fn main(-> `@Int`) requires(true) ensures(true) effects(pure) -{ string_length(json_stringify(JNumber(42.0))) } +{ + let `@Result`<Json, String> = json_parse(json_stringify(JNumber(42.0))); + match `@Result`<Json, String>.0 { + Ok(`@Json`) -> match `@Json.0` { + JNumber(`@Float64`) -> if floor(`@Float64.0`) == 42 then { 1 } else { 0 }, + _ -> 0 + }, + Err(_) -> 0 + } +} """ - assert _run(source) == 4 + assert _run(source) == 1As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.
Also applies to: 8901-8955, 8990-9004
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_codegen.py` around lines 8751 - 8776, The tests (e.g., test_json_stringify_null, test_json_stringify_bool, test_json_stringify_number) only assert string lengths which can hide incorrect serialization; update each to assert the exact serialized string returned by json_stringify for the given input (e.g., JNull -> "null", JBool(true) -> "true", JNumber(42.0) -> "42.0") rather than just string_length or non-empty checks, and apply the same exact-value assertions to the other json_stringify tests flagged in the review to ensure meaningful verification of serialization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@TESTING.md`:
- Around line 11-12: Update the stale example counts to reflect the current 29
verified example programs: change the count in TESTING.md (where it currently
says 27) and update the hardcoded value in scripts/build_site.py (the
constant/markup at the site-generation location that currently shows 25) to 29;
if build_site.py computes or exposes an examples count variable, replace the
literal with a dynamic computation from the examples/ directory or update the
constant to 29 so both documentation and site generation match the actual
examples/ count.
In `@tests/test_codegen.py`:
- Around line 8630-8633: The generated WAT can reference host imports for
json_parse/json_stringify even when those helpers were never registered, so
update the JSON host-import helper creation in vera/wasm/calls.py to record
usage by calling self._json_ops_used.add("json_parse") and
self._json_ops_used.add("json_stringify") at the point the helpers are created
(so their imports will be injected when used), and add explicit codegen
assertions in tests/test_codegen.py (e.g. in TestJsonCollection) that generate
WAT for cases that do and do not use json_parse/json_stringify and assert the
corresponding $vera.json_parse/$vera.json_stringify imports are present only
when used.
- Around line 8971-8988: The test test_json_parse_nested_object is asserting the
wrong property: after json_get(..., "inner") it checks json_has_field(...,
"inner") which doesn't validate the nested content; update the test to assert
the nested object contains the field "x" and that its value equals 1 (use
json_has_field and json_get on the returned `@Json` for "x" and compare the
extracted value to 1) so the test verifies the actual parsed nested value
produced by json_parse/json_get.
In `@vera/wasm/json_serde.py`:
- Around line 9-14: Update the module docstring so its example call signatures
match the actual function APIs: change the write_json and read_json signatures
shown in the top-of-file docstring to list the real parameters used by the
implementations (e.g., the same parameter names and order as the write_json
function and the read_json function in this file). Locate the docstring near the
top of vera/wasm/json_serde.py and replace the outdated example lines with
signatures that exactly match the implemented write_json(caller, alloc,
write_i32, write_f64, alloc_string, map_alloc, map_insert_ss, value) → ptr and
read_json(caller, read_i32, read_f64, read_string, map_store) → Any (or the
precise parameter names used in the current definitions) so callers and
maintainers see correct usage.
---
Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 8751-8776: The tests (e.g., test_json_stringify_null,
test_json_stringify_bool, test_json_stringify_number) only assert string lengths
which can hide incorrect serialization; update each to assert the exact
serialized string returned by json_stringify for the given input (e.g., JNull ->
"null", JBool(true) -> "true", JNumber(42.0) -> "42.0") rather than just
string_length or non-empty checks, and apply the same exact-value assertions to
the other json_stringify tests flagged in the review to ensure meaningful
verification of serialization behavior.
In `@vera/wasm/json_serde.py`:
- Line 27: The import is using typing.Callable which is deprecated for modern
typing; update the import in json_serde.py so that Callable is imported from
collections.abc instead of typing (e.g., replace the current "from typing import
Any, Callable" with an import that brings Callable from collections.abc while
keeping Any from typing), ensuring references to Callable in the module remain
unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2405240d-e193-4993-8cb6-862cf09463c2
📒 Files selected for processing (5)
TESTING.mdtests/test_checker.pytests/test_codegen.pyvera/wasm/inference.pyvera/wasm/json_serde.py
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
vera/wasm/json_serde.py (1)
8-14:⚠️ Potential issue | 🟡 MinorSynchronise the module docstring signatures with the actual APIs.
The documented call signatures are stale (
map_insert_ssis not a parameter;read_jsonis missingptr).📝 Suggested doc fix
Write direction (Python → WASM): write_json(caller, alloc, write_i32, write_f64, alloc_string, - map_alloc, map_insert_ss, value) → ptr + map_alloc, value) → ptr Read direction (WASM → Python): - read_json(caller, read_i32, read_f64, read_string, map_store) → Any + read_json(caller, ptr, read_i32, read_f64, read_string, map_store) → Any🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/wasm/json_serde.py` around lines 8 - 14, Update the module docstring so its call signatures match the actual function definitions: edit the write_json and read_json signatures in the top docstring to exactly reflect the parameter names used by the implemented functions (refer to write_json and read_json symbols), remove the stale map_insert_ss parameter name if it is not present in the implementation and replace it with the real map-insert parameter name, and add the missing ptr argument to read_json so the read direction signature shows read_json(..., ptr) → Any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@vera/browser/runtime.mjs`:
- Around line 1568-1577: The code path handling tag === 5 silently returns an
empty object when a JObject handle lookup in mapStore (using handle =
readI32(ptr + 4)) is missing; update this branch to emit a warning (e.g.,
console.warn or the module logger) including the missing handle value and any
contextual info (ptr or tag) so memory/layout drift is visible, but keep the
current behavior of returning {} and still calling readJson for existing entries
(symbols to locate: the tag === 5 branch, readI32, mapStore.get(handle), and
readJson).
---
Duplicate comments:
In `@vera/wasm/json_serde.py`:
- Around line 8-14: Update the module docstring so its call signatures match the
actual function definitions: edit the write_json and read_json signatures in the
top docstring to exactly reflect the parameter names used by the implemented
functions (refer to write_json and read_json symbols), remove the stale
map_insert_ss parameter name if it is not present in the implementation and
replace it with the real map-insert parameter name, and add the missing ptr
argument to read_json so the read direction signature shows read_json(..., ptr)
→ Any.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: c6966486-595e-4c95-b7ff-f23b1af2522c
📒 Files selected for processing (3)
vera/browser/runtime.mjsvera/codegen/monomorphize.pyvera/wasm/json_serde.py
- Fix test_json_parse_nested_object to check "x" not "inner" - Add WAT import assertions: json_parse, json_stringify, no-imports - Add wrong-type test: json_custom_data_shadows_prelude - Add console.warn for missing JObject handle in readJson (runtime.mjs) - Fix json_serde.py docstring to match actual signatures - Add json_get/json_array_get/json_keys to _BUILTIN_PARAMETERIZED_RETURNS - Fix TESTING.md counts (2986 tests) Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
tests/test_codegen.py (2)
9005-9023:⚠️ Potential issue | 🟠 MajorValidate nested value content, not only field existence.
After fetching
"inner", this test only checks that"x"exists. It can still pass when"x"has the wrong type/value.Proposed fix
- Some(`@Json`) -> if json_has_field(`@Json.0`, "x") then { 1 } else { 0 }, + Some(`@Json`) -> + match json_get(`@Json.0`, "x") { + Some(`@Json`) -> match `@Json.0` { + JNumber(`@Float64`) -> if floor(`@Float64.0`) == 1 then { 1 } else { 0 }, + _ -> 0 + }, + None -> 0 + },As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_codegen.py` around lines 9005 - 9023, Update the test_json_parse_nested_object to validate the nested value content not just existence: after json_parse and json_get(`@Json.0`, "inner") return Some(`@Json`), call json_get(`@Json.0`, "x") and assert the retrieved value is the numeric 1 (e.g., match the Some(`@Json`) and check the contained number equals 1) instead of only using json_has_field(`@Json.0`, "x"); this ensures the test checks actual value/type correctness for the "x" field.
8785-8811:⚠️ Potential issue | 🟠 MajorStrengthen stringify assertions beyond length/non-empty checks.
Several tests still pass on incorrect JSON payloads as long as output length is non-zero (or expected length). Prefer parse-back and structural/value assertions.
Proposed tightening pattern
def test_json_stringify_number(self) -> None: - """json_stringify(JNumber(42.0)) returns '42.0' (length 4).""" + """json_stringify(JNumber(42.0)) round-trips as JNumber(42.x).""" source = """ public fn main(-> `@Int`) requires(true) ensures(true) effects(pure) -{ string_length(json_stringify(JNumber(42.0))) } +{ + let `@Result`<Json, String> = json_parse(json_stringify(JNumber(42.0))); + match `@Result`<Json, String>.0 { + Ok(`@Json`) -> match `@Json.0` { + JNumber(`@Float64`) -> if floor(`@Float64.0`) == 42 then { 1 } else { 0 }, + _ -> 0 + }, + Err(`@String`) -> 0 + } +} """ - assert _run(source) == 4 + assert _run(source) == 1As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.
Also applies to: 8935-8989, 9024-9038
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_codegen.py` around lines 8785 - 8811, The tests (e.g., test_json_stringify_null, test_json_stringify_bool, test_json_stringify_number) currently only assert string_length(json_stringify(...)) which can pass for incorrect output; change them to assert the actual JSON output or its parsed structure by calling _run to get the string from json_stringify(JNull/JBool/JNumber) and then either compare the exact string (e.g., "null", "true", "42.0") or parse it with Python's json.loads and assert the resulting value/type equals the expected (None/True/42.0); update any similar tests mentioned (lines ~8935-8989, 9024-9038) to follow this stricter pattern and reference json_stringify, JNull, JBool, JNumber, _run, and string_length when making the replacements.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@TESTING.md`:
- Line 76: Update the command snippet comment that still says "300 tests" to
match the table which lists test_conformance.py as 305 tests: locate the command
snippet in TESTING.md referencing test_conformance.py and change the numeric
comment from 300 to 305 (and update any other occurrences of the old count in
that snippet), ensuring the comment text now accurately reflects
test_conformance.py = 305 tests.
In `@tests/test_checker.py`:
- Around line 1990-2036: Add missing wrong-argument checker tests for the JSON
built-ins: update the test methods test_json_get, test_json_has_field, and
test_json_type to include a second `_check_err(..., "type")` case each that
passes an incorrect argument type to the builtin (e.g., pass an Int or String
where a Json is expected, or vice versa) to assert the type checker rejects the
call; reference the functions json_get, json_has_field, and json_type and use
`_check_err` with the "type" message to mirror the existing built-in test
pattern.
---
Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 9005-9023: Update the test_json_parse_nested_object to validate
the nested value content not just existence: after json_parse and
json_get(`@Json.0`, "inner") return Some(`@Json`), call json_get(`@Json.0`, "x") and
assert the retrieved value is the numeric 1 (e.g., match the Some(`@Json`) and
check the contained number equals 1) instead of only using
json_has_field(`@Json.0`, "x"); this ensures the test checks actual value/type
correctness for the "x" field.
- Around line 8785-8811: The tests (e.g., test_json_stringify_null,
test_json_stringify_bool, test_json_stringify_number) currently only assert
string_length(json_stringify(...)) which can pass for incorrect output; change
them to assert the actual JSON output or its parsed structure by calling _run to
get the string from json_stringify(JNull/JBool/JNumber) and then either compare
the exact string (e.g., "null", "true", "42.0") or parse it with Python's
json.loads and assert the resulting value/type equals the expected
(None/True/42.0); update any similar tests mentioned (lines ~8935-8989,
9024-9038) to follow this stricter pattern and reference json_stringify, JNull,
JBool, JNumber, _run, and string_length when making the replacements.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: ca7533ca-f915-47b5-8f9e-3a7655b1c962
📒 Files selected for processing (5)
TESTING.mdtests/test_checker.pytests/test_codegen.pyvera/browser/runtime.mjsvera/wasm/json_serde.py
- Fix TESTING.md conformance count 300→305 - Add wrong-type tests: json_get, json_has_field, json_type - Add codegen test for custom data Json (exercises _has_standard_json) - Reply to example count (27 is correct), WAT imports (already fixed), nested object test (already fixed) - prelude.py coverage: 405-410 now covered Co-Authored-By: Claude <noreply@anthropic.invalid>
|
@coderabbitai Re: TESTING.md example count — 27 is correct. Re: WAT imports for json_parse/json_stringify — already fixed in fb20cbf ( Re: test_json_parse_nested_object — fixed in 1bfb6b5 to check Re: TESTING.md conformance count — fixed 300→305 in 8f1c3c5. Re: wrong-type checker tests — added Re: readJson missing JObject handle — added Re: json_serde.py docstring — fixed in 1bfb6b5 to match actual signatures. |
|
Tip For best results, initiate chat on the files or code changes.
The JSON ADT implementation looks solid end-to-end. Well done. ✏️ Learnings added
🧠 Learnings used |
|
@coderabbitai Addressing two outstanding review-body items: TESTING.md example count (29): The count is 27, not 29. Verified: WAT imports for json_parse/json_stringify: Both issues were already fixed: |
|
Tip For best results, initiate chat on the files or code changes.
Nothing further outstanding here. 🧠 Learnings used |
|
Merging with codecov/patch at 93.57% (target 95.37%). The remaining ~2% gap is defensive code that cannot be exercised without injecting corrupt data:
Project coverage is 95.36% (-0.02% from baseline). All 2,990 tests pass. |
Summary
New files
Documentation
Closes #58.
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests
Chore