fix: match parser allows container bodies in arms
- Added can_be_pattern() with look-ahead disambiguation - Ident only treated as pattern if followed by -> or ( - Keywords (row/column/when/each) correctly terminate match - Match arms now support: "active" -> row [ Badge + text ] - Siblings after match (text, button, row) no longer consumed - Project Manager updated with rich row/Badge match arms - All 6 existing examples pass regression
This commit is contained in:
parent
70c9589573
commit
6c9d109ebd
2 changed files with 76 additions and 14 deletions
|
|
@ -77,6 +77,60 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the current token could start a match pattern.
|
||||
/// Patterns are: string literals, integer literals, identifiers (as wildcard/binding).
|
||||
/// For identifiers, we look ahead to disambiguate: a pattern ident is followed by `->`.
|
||||
/// A view element like `text "..."` or `button "..." {}` is NOT a pattern.
|
||||
fn can_be_pattern(&self) -> bool {
|
||||
match self.peek() {
|
||||
// String and integer literals are always valid patterns
|
||||
TokenKind::StringFragment(_) | TokenKind::Int(_) => {
|
||||
// But verify: is this FOLLOWED by an arrow (after the string)?
|
||||
// StringFragment patterns look like: "value" ->
|
||||
// View elements look like: text "value" (no arrow after the string)
|
||||
true
|
||||
}
|
||||
// Identifiers: could be a pattern binding OR a view element (text, button, input...)
|
||||
// Look ahead: patterns are followed eventually by Arrow
|
||||
// e.g., `_ -> body` or `myVar -> body` or `Ok(x) -> body`
|
||||
TokenKind::Ident(name) => {
|
||||
// Special case: _ is always a wildcard pattern
|
||||
if name == "_" {
|
||||
return true;
|
||||
}
|
||||
// Look ahead past the ident to see what follows
|
||||
let next_pos = self.pos + 1;
|
||||
if next_pos < self.tokens.len() {
|
||||
match &self.tokens[next_pos].kind {
|
||||
// Ident followed by Arrow: definitely a pattern (`myVar ->`)
|
||||
TokenKind::Arrow => true,
|
||||
// Ident followed by `(`: constructor pattern (`Ok(x) ->`)
|
||||
TokenKind::LParen => true,
|
||||
// Ident followed by Newline: could be pattern on next line
|
||||
// Check the token after the newline(s)
|
||||
TokenKind::Newline => {
|
||||
let mut peek_pos = next_pos + 1;
|
||||
while peek_pos < self.tokens.len() && self.tokens[peek_pos].kind == TokenKind::Newline {
|
||||
peek_pos += 1;
|
||||
}
|
||||
if peek_pos < self.tokens.len() {
|
||||
matches!(self.tokens[peek_pos].kind, TokenKind::Arrow)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
// Ident followed by anything else (string, LBrace, LBracket, etc.):
|
||||
// it's a view element like `text "hello"` or `button "click" { }`
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&self, msg: String) -> ParseError {
|
||||
let tok = self.current_token();
|
||||
ParseError {
|
||||
|
|
@ -926,13 +980,12 @@ impl Parser {
|
|||
let scrutinee = self.parse_primary()?;
|
||||
self.skip_newlines();
|
||||
let mut arms = Vec::new();
|
||||
while !self.is_at_end()
|
||||
&& !matches!(self.peek(), TokenKind::Let | TokenKind::View | TokenKind::On | TokenKind::Effect
|
||||
| TokenKind::RBracket | TokenKind::RBrace | TokenKind::Else | TokenKind::Eof)
|
||||
{
|
||||
// Match arms: continue while the next token could be a pattern
|
||||
// (StringFragment, Int, Ident that starts with lowercase, or _ wildcard).
|
||||
// Stop on anything that can't be a pattern: keywords, brackets, EOF.
|
||||
while !self.is_at_end() && self.can_be_pattern() {
|
||||
self.skip_newlines();
|
||||
if self.is_at_end() || matches!(self.peek(), TokenKind::Let | TokenKind::View | TokenKind::On | TokenKind::Effect
|
||||
| TokenKind::RBracket | TokenKind::RBrace | TokenKind::Else | TokenKind::Eof) {
|
||||
if self.is_at_end() || !self.can_be_pattern() {
|
||||
break;
|
||||
}
|
||||
let pattern = self.parse_pattern()?;
|
||||
|
|
|
|||
|
|
@ -50,15 +50,24 @@ route "/" -> column [
|
|||
-- Project status
|
||||
Card { title: "Status", subtitle: "current phase" } [
|
||||
match projectStatus
|
||||
"active" -> Badge { label: "🟢 ACTIVE — Project is on track", variant: "success" }
|
||||
"review" -> Badge { label: "🟡 IN REVIEW — Awaiting approval", variant: "warning" }
|
||||
"paused" -> Badge { label: "🔴 PAUSED — Project on hold", variant: "error" }
|
||||
"active" -> row [
|
||||
Badge { label: "🟢 ACTIVE", variant: "success" }
|
||||
text "Project is on track"
|
||||
]
|
||||
"review" -> row [
|
||||
Badge { label: "🟡 IN REVIEW", variant: "warning" }
|
||||
text "Awaiting approval"
|
||||
]
|
||||
"paused" -> row [
|
||||
Badge { label: "🔴 PAUSED", variant: "error" }
|
||||
text "Project on hold"
|
||||
]
|
||||
_ -> Badge { label: "UNKNOWN", variant: "info" }
|
||||
]
|
||||
row [
|
||||
Button { label: "Active", onClick: projectStatus = "active", variant: "primary" }
|
||||
Button { label: "Review", onClick: projectStatus = "review", variant: "secondary" }
|
||||
Button { label: "Pause", onClick: projectStatus = "paused", variant: "ghost" }
|
||||
row [
|
||||
Button { label: "Active", onClick: projectStatus = "active", variant: "primary" }
|
||||
Button { label: "Review", onClick: projectStatus = "review", variant: "secondary" }
|
||||
Button { label: "Pause", onClick: projectStatus = "paused", variant: "ghost" }
|
||||
]
|
||||
]
|
||||
|
||||
-- Progress
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue