feat: multi-statement event handlers with semicolons
- Added Semicolon token to lexer - Enables: click: items.push(x); input = "" - Push-and-clear, increment-and-reset, clear-all patterns - Browser-verified: both actions fire in one click - All existing examples pass regression
This commit is contained in:
parent
6c9d109ebd
commit
10b2717281
3 changed files with 48 additions and 1 deletions
|
|
@ -82,6 +82,7 @@ pub enum TokenKind {
|
|||
PlusEq, // +=
|
||||
MinusEq, // -=
|
||||
Arrow, // ->
|
||||
Semicolon, // ;
|
||||
Pipe, // |
|
||||
Dot, // .
|
||||
|
||||
|
|
@ -223,6 +224,7 @@ impl Lexer {
|
|||
'>' => { self.advance(); Token { kind: TokenKind::Gt, lexeme: ">".into(), line, col } }
|
||||
'!' => { self.advance(); Token { kind: TokenKind::Not, lexeme: "!".into(), line, col } }
|
||||
'|' => { self.advance(); Token { kind: TokenKind::Pipe, lexeme: "|".into(), line, col } }
|
||||
';' => { self.advance(); Token { kind: TokenKind::Semicolon, lexeme: ";".into(), line, col } }
|
||||
'.' => { self.advance(); Token { kind: TokenKind::Dot, lexeme: ".".into(), line, col } }
|
||||
'(' => { self.advance(); Token { kind: TokenKind::LParen, lexeme: "(".into(), line, col } }
|
||||
')' => { self.advance(); Token { kind: TokenKind::RParen, lexeme: ")".into(), line, col } }
|
||||
|
|
|
|||
|
|
@ -1355,7 +1355,24 @@ impl Parser {
|
|||
let key = self.expect_ident()?;
|
||||
self.expect(&TokenKind::Colon)?;
|
||||
self.skip_newlines();
|
||||
let val = self.parse_expr()?;
|
||||
let first = self.parse_expr()?;
|
||||
// Multi-action: `click: action1; action2; action3`
|
||||
// Collects semicolon-separated expressions into a Block
|
||||
let val = if self.check(&TokenKind::Semicolon) {
|
||||
let mut exprs = vec![first];
|
||||
while self.check(&TokenKind::Semicolon) {
|
||||
self.advance(); // consume ';'
|
||||
self.skip_newlines();
|
||||
// Stop if we hit } (end of props) or , (next prop)
|
||||
if self.check(&TokenKind::RBrace) || self.check(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
exprs.push(self.parse_expr()?);
|
||||
}
|
||||
Expr::Block(exprs)
|
||||
} else {
|
||||
first
|
||||
};
|
||||
props.push((key, val));
|
||||
self.skip_newlines();
|
||||
if self.check(&TokenKind::Comma) {
|
||||
|
|
|
|||
28
examples/multi-action.ds
Normal file
28
examples/multi-action.ds
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-- Multi-Action Click Handler Test
|
||||
-- Tests semicolon-separated actions in click handlers
|
||||
|
||||
let items = ["Buy milk", "Walk dog"]
|
||||
let input = ""
|
||||
let count = 0
|
||||
|
||||
view main = column [
|
||||
text "Multi-Action Demo" { variant: "title" }
|
||||
text "Add items AND clear input in one click" { variant: "subtitle" }
|
||||
|
||||
row [
|
||||
input { bind: input, placeholder: "New item..." }
|
||||
button "Add & Clear" { click: items.push(input); input = "", variant: "primary" }
|
||||
]
|
||||
|
||||
each item in items ->
|
||||
row [
|
||||
text "• {item}"
|
||||
button "×" { click: items.remove(_idx), variant: "ghost" }
|
||||
]
|
||||
|
||||
text "Count: {count}"
|
||||
row [
|
||||
button "+5 & Reset Name" { click: count += 5; input = "reset!", variant: "secondary" }
|
||||
button "Reset All" { click: count = 0; items = [], variant: "ghost" }
|
||||
]
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue