Skip to content

fix(parser): reject C-style radix literals (0x/0b/0o) instead of silently parsing to 0 (#337)#343

Merged
avrabe merged 1 commit into
mainfrom
feat/reject-c-style-hex-literal-337
Jul 22, 2026
Merged

fix(parser): reject C-style radix literals (0x/0b/0o) instead of silently parsing to 0 (#337)#343
avrabe merged 1 commit into
mainfrom
feat/reject-c-style-hex-literal-337

Conversation

@avrabe

@avrabe avrabe commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #337. A 0x…-style C-style hex integer literal in a property association silently parsed to 0 with no diagnostic. The lexer split 0x40003000 into INTEGER_LIT 0 + IDENT x40003000, and property_expression_primary then treated the trailing identifier as a unit, lowering the property to Integer(0, unit="x40003000"). A downstream consumer of items --format json reading typed_value.Integer[0] got a plausible-looking wrong value (0) with no signal.

Decision: reject, don't extend the dialect

The issue offered two resolutions — accept 0x… as hex, or emit a diagnostic — and stated "a hard error is strictly better than silent 0." C-style radix prefixes are not valid AADL v2.3 numeric literals; AADL uses based notation base#digits# (e.g. 16#40003000#). Accepting 0x… would introduce a spar-specific dialect divergence (cf. the interop/anti-dialect-drift concern in #246), and OSATE/Ocarina would reject it. So spar now rejects it, spec-faithfully. (0x… already appears in the tree only as opaque string content — e.g. the TSN Gate_Control_List GCL — which this change leaves untouched.)

Fix

  • crates/spar-parser/src/lexer.rsscan_number recognizes a lone leading 0 glued to a radix marker (x/X, b/B, o/O) followed by a valid digit of that radix, and lexes the whole malformed run as one ERROR token (new is_radix_digit helper).
  • crates/spar-parser/src/grammar/properties.rs — a new ERROR arm in property_expression_primary attaches a targeted diagnostic on that token and recovers to the ; (new is_c_style_radix_literal helper).

Observable behavior: spar parse now reports …:5:18: C-style radix literal is not valid AADL; use based notation, e.g. 16#…# for hexadecimal and exits non-zero; spar items no longer emits a deceptive Integer[0].

Oracles (all executed; production guard confirmed red-flips the parser/lowering/lexer tests)

  • Lexer0x40003000, 0X1F, 0b1010, 0o17, 0xDEAD_BEEF each lex as a single ERROR token (before the fix they split into 0 + IDENT). Negative controls: 0 ns0+ns, 0bar0+bar, 0x (no hex digit) → 0+x, 16#40003000# → one INTEGER_LIT.
  • Lowering (end-to-end)=> 0x40003000; no longer lowers to Integer(0, Some(unit)); positive control => 16#40003000#; lowers to Integer(1073754112, None).
  • Parser — the same input produces a diagnostic and a 0x40003000 ERROR token in the tree; 16#40003000# parses clean.

Verification

  • cargo test -p spar-parser -p spar-syntax -p spar-hir-def — green (parser 77, syntax 291, hir-def 472); the 5 lexer + lowering + parser oracles confirmed red before the fix.
  • cargo fmt --check clean; cargo clippy -p spar-parser -p spar-hir-def -p spar-syntax --all-targets -- -D warnings clean.
  • rivet validate — PASS, 0 broken cross-refs, REQ-PARSER-003 present with resolving traces-to.
  • Independent clean-room verification performed a true red-flip (production guard removed → exact bug Integer(0, Some(Name("x40003000"))) reproduced), re-derived 0x40003000 = 1073754112, swept 23 false-positive candidates (none), and confirmed the TSN hex-in-string GCL test stays green.

Scope / honesty

Fixes the token-split silent-zero path (the reported bug) for hex and the same-class 0b/0o prefixes. It does not make spar items itself exit non-zero on the malformed input — that command remains a best-effort structural dump; the hard error surfaces via spar parse/analyze. The unrelated silent-hex-in-property discussion is fully addressed by rejection here.

Requirements

Adds REQ-PARSER-003 (traces-to REQ-PARSER-001), release v0.32.0.

🤖 Generated with Claude Code


Generated by Claude Code

…ntly parsing to 0 (#337)

A `0x…`-style C-style hex integer literal in a property association silently
parsed to `0`: the lexer split `0x40003000` into INTEGER_LIT `0` + IDENT
`x40003000`, and the parser then treated the trailing identifier as a *unit*,
yielding `Integer(0, unit="x40003000")` with no diagnostic — a silent wrong
value for any consumer of `items --format json`.

C-style radix prefixes are not valid AADL v2.3 numeric literals (AADL uses
based notation `base#digits#`, e.g. `16#40003000#`). Accepting them would
introduce a spar-specific dialect divergence, so — per the issue's stated
preference ("a hard error is strictly better than silent 0") — spar now
rejects them:

- `scan_number` (lexer) recognizes a lone leading `0` glued to a radix marker
  (`x`/`X`, `b`/`B`, `o`/`O`) followed by a valid digit of that radix, and
  lexes the whole malformed run as one ERROR token.
- `property_expression_primary` (grammar) attaches a targeted diagnostic on
  that token: "C-style radix literal is not valid AADL; use based notation,
  e.g. `16#…#` for hexadecimal".

`spar parse` now reports the diagnostic with location and exits non-zero;
`spar items` no longer emits a deceptive `Integer[0]`.

Oracles (all executed; lexer + lowering + parser tests confirmed red before
the fix): the C-style forms each lex as a single ERROR token, `=> 0x40003000;`
no longer lowers to `Integer(0, Some(unit))`, the parser emits a diagnostic,
and negative controls (`0 ns`, `0bar`, `0x`, `16#40003000#`) are unaffected;
positive control `16#40003000#` lowers to `Integer(1073754112, None)`.

Adds REQ-PARSER-003 (traces-to REQ-PARSER-001).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Rivet verification gate

20/20 passed

count
Passed 20
Failed 0
Skipped (no steps) 0

Filter: (and (= type "feature") (or (has-tag "v093") (has-tag "v0100")))

Failed artifacts

(none)

Updated automatically by tools/post_verification_comment.py. Source of truth: artifacts/verification.yaml.

@avrabe
avrabe merged commit 9557dd0 into main Jul 22, 2026
19 checks passed
@avrabe
avrabe deleted the feat/reject-c-style-hex-literal-337 branch July 22, 2026 19:00
avrabe added a commit that referenced this pull request Jul 23, 2026
Release v0.32.0 — ships #337 (#343): reject C-style radix literals (0x/0b/0o) with a parse diagnostic instead of silently parsing to 0. REQ-PARSER-003 verified with release v0.32.0.
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.

Property parser: 0x… hex integer literal silently parses to 0 (only 16#…# works)

2 participants