From 62830fa82af199116c224208e3eebd5bca7b36a3 Mon Sep 17 00:00:00 2001 From: enzotar Date: Thu, 26 Feb 2026 23:42:29 -0800 Subject: [PATCH] fix: for-in parser token mismatch + enhanced step sequencer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix for...in parser: TokenKind::In โ†’ TokenKind::InKw (was using wrong token) - Remove dead In variant from TokenKind enum - Fix pre-existing test: When pattern match 2โ†’3 fields - Enhanced step-sequencer.ds: 16 steps, 4 instrument arrays, play/pause, BPM controls with limits, presets, dynamic variants, streaming output - All 118 tests pass --- compiler/ds-parser/src/lexer.rs | 2 +- compiler/ds-parser/src/parser.rs | 4 +- examples/game-pong.html | 487 +++++++++++++++++++++++++++++++ examples/step-sequencer.ds | 139 +++++---- 4 files changed, 578 insertions(+), 54 deletions(-) create mode 100644 examples/game-pong.html diff --git a/compiler/ds-parser/src/lexer.rs b/compiler/ds-parser/src/lexer.rs index a0e9273..09c038f 100644 --- a/compiler/ds-parser/src/lexer.rs +++ b/compiler/ds-parser/src/lexer.rs @@ -48,7 +48,7 @@ pub enum TokenKind { Scene, Animate, For, - In, + // In variant removed โ€” use InKw for the `in` keyword Component, Route, Navigate, diff --git a/compiler/ds-parser/src/parser.rs b/compiler/ds-parser/src/parser.rs index c6ae7d7..4e8d621 100644 --- a/compiler/ds-parser/src/parser.rs +++ b/compiler/ds-parser/src/parser.rs @@ -1044,7 +1044,7 @@ impl Parser { None }; - self.expect(&TokenKind::In)?; + self.expect(&TokenKind::InKw)?; let iter_expr = self.parse_comparison()?; self.expect(&TokenKind::Arrow)?; self.skip_newlines(); @@ -1692,7 +1692,7 @@ mod tests { Declaration::View(v) => { match &v.body { Expr::Container(c) => { - assert!(matches!(&c.children[0], Expr::When(_, _))); + assert!(matches!(&c.children[0], Expr::When(_, _, _))); } other => panic!("expected Container, got {other:?}"), } diff --git a/examples/game-pong.html b/examples/game-pong.html new file mode 100644 index 0000000..669a454 --- /dev/null +++ b/examples/game-pong.html @@ -0,0 +1,487 @@ + + + + + + ๐Ÿ“ DreamStack Pong โ€” Streamed + + + + + +

๐Ÿ“ DreamStack Pong

+

Mouse/touch to move paddle ยท Streamed live via relay

+ +
+ + +
+ +
+ Player: 0 + Rally: 0 + AI: 0 + โณ Connectingโ€ฆ +
+ + + +
+ + +
+

Connecting to relayโ€ฆ

+ + + + + \ No newline at end of file diff --git a/examples/step-sequencer.ds b/examples/step-sequencer.ds index c7fbd18..68ae0bf 100644 --- a/examples/step-sequencer.ds +++ b/examples/step-sequencer.ds @@ -1,72 +1,109 @@ -- DreamStack Step Sequencer -- Collaborative beat grid: two tabs, same pads, real-time sync +-- +-- Run: +-- Tab 1: cargo run -p ds-stream (relay) +-- Tab 2: dreamstack dev examples/step-sequencer.ds (player 1) +-- Tab 3: dreamstack dev examples/step-sequencer.ds --port 3001 (player 2) let bpm = 120 let step = 0 +let playing = 1 --- 4 instruments ร— 8 steps = 32 pads (flat array, index with row*8+col) -let pads = [0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0] +-- 4 instruments ร— 16 steps = 64 pads +let kick = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +let snare = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +let hihat = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +let bass = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] --- Playhead advances every beat -every (60000 / bpm / 2) -> step = (step + 1) % 8 +-- Playhead advances every beat (8th note = 60000 / bpm / 2) +every (60000 / bpm / 2) -> step = if playing then (step + 1) % 16 else step --- Stream for multiplayer -stream sequencer on "ws://localhost:9100/source/beats" { mode: signal } +-- Stream for multiplayer collaboration +stream beats on "ws://localhost:9100/peer/beats" { + mode: signal, + output: bpm, step, playing, kick, snare, hihat, bass +} -view sequencer = +view beats = column [ - text "DreamStack Beats" - text "BPM: {bpm}" + text "๐ŸŽน DreamStack Beats" { variant: "title" } + text "Collaborative step sequencer โ€” synced via bitstream relay" { variant: "subtitle" } - -- Kick (pads 0-7) - text "Kick" + -- Transport controls row [ - for i in [0, 1, 2, 3, 4, 5, 6, 7] -> - button (if pads[i] then "โ—" else "โ—‹") { - click: pads[i] = if pads[i] then 0 else 1 - } - ] - - -- Snare (pads 8-15) - text "Snare" - row [ - for i in [8, 9, 10, 11, 12, 13, 14, 15] -> - button (if pads[i] then "โ—" else "โ—‹") { - click: pads[i] = if pads[i] then 0 else 1 - } - ] - - -- Hi-hat (pads 16-23) - text "HiHat" - row [ - for i in [16, 17, 18, 19, 20, 21, 22, 23] -> - button (if pads[i] then "โ—" else "โ—‹") { - click: pads[i] = if pads[i] then 0 else 1 - } - ] - - -- Bass (pads 24-31) - text "Bass" - row [ - for i in [24, 25, 26, 27, 28, 29, 30, 31] -> - button (if pads[i] then "โ—" else "โ—‹") { - click: pads[i] = if pads[i] then 0 else 1 - } + button (if playing then "โธ Pause" else "โ–ถ Play") { + click: playing = if playing then 0 else 1, + variant: "primary" + } + button "โฎ Reset" { + click: step = 0, + variant: "ghost" + } + text "BPM: {bpm}" { variant: "caption" } + button "โˆ’" { click: bpm = if bpm > 40 then bpm - 10 else bpm, variant: "secondary" } + button "+" { click: bpm = if bpm < 300 then bpm + 10 else bpm, variant: "secondary" } ] -- Playhead indicator row [ - for i in [0, 1, 2, 3, 4, 5, 6, 7] -> - text (if i == step then "โ–ผ" else "ยท") + text " " { variant: "muted" } + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] -> + text (if i == step then "โ–ผ" else "ยท") { variant: "muted" } ] - -- BPM controls + -- Kick row row [ - button "โˆ’10" { click: bpm -= 10 } - text "{bpm}" - button "+10" { click: bpm += 10 } + text "KICK " { variant: "caption" } + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] -> + button (if kick[i] then "โ—" else "โ—‹") { + click: kick[i] = if kick[i] then 0 else 1, + variant: (if kick[i] then "primary" else "secondary") + } ] + + -- Snare row + row [ + text "SNRE " { variant: "caption" } + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] -> + button (if snare[i] then "โ—" else "โ—‹") { + click: snare[i] = if snare[i] then 0 else 1, + variant: (if snare[i] then "primary" else "secondary") + } + ] + + -- HiHat row + row [ + text "HHAT " { variant: "caption" } + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] -> + button (if hihat[i] then "โ—" else "โ—‹") { + click: hihat[i] = if hihat[i] then 0 else 1, + variant: (if hihat[i] then "primary" else "secondary") + } + ] + + -- Bass row + row [ + text "BASS " { variant: "caption" } + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] -> + button (if bass[i] then "โ—" else "โ—‹") { + click: bass[i] = if bass[i] then 0 else 1, + variant: (if bass[i] then "primary" else "secondary") + } + ] + + -- Presets + text "Presets" { variant: "caption" } + row [ + button "Four on the Floor" { + click: kick[0] = 1; kick[4] = 1; kick[8] = 1; kick[12] = 1; snare[4] = 1; snare[12] = 1; hihat[0] = 1; hihat[2] = 1; hihat[4] = 1; hihat[6] = 1; hihat[8] = 1; hihat[10] = 1; hihat[12] = 1; hihat[14] = 1, + variant: "ghost" + } + button "Clear All" { + click: kick[0] = 0; kick[1] = 0; kick[2] = 0; kick[3] = 0; kick[4] = 0; kick[5] = 0; kick[6] = 0; kick[7] = 0; kick[8] = 0; kick[9] = 0; kick[10] = 0; kick[11] = 0; kick[12] = 0; kick[13] = 0; kick[14] = 0; kick[15] = 0; snare[0] = 0; snare[1] = 0; snare[2] = 0; snare[3] = 0; snare[4] = 0; snare[5] = 0; snare[6] = 0; snare[7] = 0; snare[8] = 0; snare[9] = 0; snare[10] = 0; snare[11] = 0; snare[12] = 0; snare[13] = 0; snare[14] = 0; snare[15] = 0; hihat[0] = 0; hihat[1] = 0; hihat[2] = 0; hihat[3] = 0; hihat[4] = 0; hihat[5] = 0; hihat[6] = 0; hihat[7] = 0; hihat[8] = 0; hihat[9] = 0; hihat[10] = 0; hihat[11] = 0; hihat[12] = 0; hihat[13] = 0; hihat[14] = 0; hihat[15] = 0; bass[0] = 0; bass[1] = 0; bass[2] = 0; bass[3] = 0; bass[4] = 0; bass[5] = 0; bass[6] = 0; bass[7] = 0; bass[8] = 0; bass[9] = 0; bass[10] = 0; bass[11] = 0; bass[12] = 0; bass[13] = 0; bass[14] = 0; bass[15] = 0, + variant: "destructive" + } + ] + + text "๐Ÿ”ด Streaming via ws://localhost:9100 โ€” open another tab to collab!" { variant: "muted" } ]