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
Unit
src/lib/parse/LibParse.solparseRHS branch chain: the literal branch (char & CMASK_LITERAL_HEAD > 0, ~line 398) callspushLiteralwithout checkingFSM_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):
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_HEADchar (0-9,-,",[) without the yang check. Consequently adjacent literals parse with no whitespace:_ _: 1-2;→ literals1and-2(driven by-being inCMASK_NUMERIC_LITERAL_HEAD)1"a"→ literal1then string literal"a"1[x y]→ literal1then sub-parseable literalProposed 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 that1 2parses and1-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-2meaning 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