Skip to content

RHS literal branch has no yang guard: adjacent literals parse without whitespace between them #565

Description

@thedavidmeister

Unit

src/lib/parse/LibParse.sol parseRHS branch chain: the literal branch (char & CMASK_LITERAL_HEAD > 0, ~line 398) calls pushLiteral without checking FSM_YANG_MASK, then sets yang itself.

Intent oracle

The yang FSM exists so that adjacent tokens require a separator. The RHS word branch enforces it explicitly (~line 231):

if (char & CMASK_RHS_WORD_HEAD > 0) {
    // If yang we can't start a new word.
    if (state.fsm & FSM_YANG_MASK > 0) {
        revert UnexpectedRHSChar(state.parseErrorOffset(cursor));
    }

and the LHS item branch does the same (~line 155). Maintainer ruling (2026-08-24): literals should have a gap too.

Violated property

After a literal completes, the state is yang, but the next iteration's literal branch admits any CMASK_LITERAL_HEAD char (0-9, -, ", [) without the yang check. Consequently adjacent literals parse with no whitespace:

  • _ _: 1-2; → literals 1 and -2 (driven by - being in CMASK_NUMERIC_LITERAL_HEAD)
  • 1"a" → literal 1 then string literal "a"
  • 1[x y] → literal 1 then sub-parseable literal

Proposed fix

Mirror the word branch: at the top of the literal branch, if (state.fsm & FSM_YANG_MASK > 0) revert UnexpectedRHSChar(state.parseErrorOffset(cursor)); — plus tests pinning that 1 2 parses and 1-2/1"a"/1[x y] revert.

Triage note

This tightens the accepted grammar: any existing rainlang text relying on literal adjacency (e.g. 1-2 meaning two literals) would start reverting. No known corpus depends on it, but the change is observable to every downstream parser consumer.

Found by the 2026-08-24 rain.string CMask consumer-oracle audit (the mask value is correct; the missing guard is at this call site).

🤖 Generated with Claude Code

Metadata

Metadata

Labels

adversarialFound by adversarial testingauditAudit finding

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions