fix(speckl): dispatch 'state as <Name>' to parseStateBlockMultiline (cycle 63)
End-to-end compile of FederatedMeetup.speckdl with the new
'state as StateSnapshot' header (cycle 63) surfaced that the
parser's main loop and parseMemberBlock dispatcher were both
missing 'state as ' as a recognized block-starter prefix. The
state-block parser (parseStateBlockMultiline) already extracts
the 'as' clause correctly (cycle 59), but the call site never
reached it: the block fell through the 'state:' / 'state {'
checks and was parsed as a regular member line, producing no
StateNode at all.
Fix: add 'state as ' to the blockStarters list in the main
parse loop, and add a parallel dispatch in parseMemberBlock
that routes 'state as ' headers to parseStateBlockMultiline.
The dispatcher change is symmetric with the existing 'state:'
and 'state {' forms.
Tests: 1 new parser test confirms the dispatch works for
'state as StateSnapshot { ... }'. All 55 tests pass (54
baseline + 1 new). tsc build clean.
End-to-end verified: running generateProtobuf against the
federated-meetup speck with 'state as StateSnapshot' now
emits 'message StateSnapshot { ... }' (correct), where
pre-fix the StateNode was lost entirely.
This commit is contained in:
parent
0e03aa4e04
commit
4d95a2b975
2 changed files with 28 additions and 1 deletions
|
|
@ -833,4 +833,24 @@ speck Second {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Cycle 63: 'state as <Name> { ... }' must dispatch through the
|
||||
// same parseStateBlockMultiline path as 'state: { ... }'. Cycle 59
|
||||
// added the parser-side extraction of the `as` clause; this test
|
||||
// confirms the block-dispatcher also routes the new header form.
|
||||
describe('state as <Name> dispatch (cycle 63)', () => {
|
||||
it('should dispatch state as StateSnapshot to parseStateBlockMultiline', () => {
|
||||
const content = `speck FederatedMeetup {
|
||||
state as StateSnapshot {
|
||||
branch_id: Nat
|
||||
}
|
||||
}`;
|
||||
const ast = parseSpeckContent(content);
|
||||
const state = ast.specks[0].members.find(m => m.type === 'state');
|
||||
if (state?.type === 'state') {
|
||||
expect(state.messageName).toBe('StateSnapshot');
|
||||
expect(state.variables.map(v => v.name)).toEqual(['branch_id']);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -373,7 +373,8 @@ function parseSpeck(lines: string[], startIndex: number): SpeckNode | null {
|
|||
|
||||
// Check for block-opening members (those that open braces on the same line)
|
||||
const blockStarters = ['state:', 'init:', 'action ', 'event ', 'provenance ', 'bom ', 'interface ',
|
||||
'state {', 'init {', 'verify ', 'constraint ', 'input:', 'output:', 'service ', 'oneof ', 'transition '];
|
||||
'state {', 'init {', 'verify ', 'constraint ', 'input:', 'output:', 'service ', 'oneof ', 'transition ',
|
||||
'state as '];
|
||||
const isBlockStarter = blockStarters.some(s => line.startsWith(s));
|
||||
|
||||
if (isBlockStarter && openCount > 0) {
|
||||
|
|
@ -482,6 +483,12 @@ function parseMemberBlock(lines: string[], startIndex: number): MemberNode | nul
|
|||
if (firstLine.startsWith('state {') || firstLine === 'state') {
|
||||
return parseStateBlockMultiline(lines, startIndex, startBraceCount);
|
||||
}
|
||||
if (firstLine.startsWith('state as ')) {
|
||||
// Cycle 63: `state as <MessageName> { ... }` form must dispatch
|
||||
// through the same state-block parser so the `as` override on the
|
||||
// first line is captured.
|
||||
return parseStateBlockMultiline(lines, startIndex, startBraceCount);
|
||||
}
|
||||
if (firstLine.startsWith('init:')) {
|
||||
return parseInitBlockMultiline(lines, startIndex, startBraceCount);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue