diff --git a/compiler/__tests__/parser.test.ts b/compiler/__tests__/parser.test.ts index e56a0eb..d4d5e33 100644 --- a/compiler/__tests__/parser.test.ts +++ b/compiler/__tests__/parser.test.ts @@ -833,4 +833,24 @@ speck Second { } }); }); + + // Cycle 63: 'state as { ... }' 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 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']); + } + }); + }); }); diff --git a/compiler/src/parser.ts b/compiler/src/parser.ts index 45f1dd8..1e3d8a8 100644 --- a/compiler/src/parser.ts +++ b/compiler/src/parser.ts @@ -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 { ... }` 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); }