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 {
|
fn error(&self, msg: String) -> ParseError {
|
||||||
let tok = self.current_token();
|
let tok = self.current_token();
|
||||||
ParseError {
|
ParseError {
|
||||||
|
|
@ -926,13 +980,12 @@ impl Parser {
|
||||||
let scrutinee = self.parse_primary()?;
|
let scrutinee = self.parse_primary()?;
|
||||||
self.skip_newlines();
|
self.skip_newlines();
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
while !self.is_at_end()
|
// Match arms: continue while the next token could be a pattern
|
||||||
&& !matches!(self.peek(), TokenKind::Let | TokenKind::View | TokenKind::On | TokenKind::Effect
|
// (StringFragment, Int, Ident that starts with lowercase, or _ wildcard).
|
||||||
| TokenKind::RBracket | TokenKind::RBrace | TokenKind::Else | TokenKind::Eof)
|
// Stop on anything that can't be a pattern: keywords, brackets, EOF.
|
||||||
{
|
while !self.is_at_end() && self.can_be_pattern() {
|
||||||
self.skip_newlines();
|
self.skip_newlines();
|
||||||
if self.is_at_end() || matches!(self.peek(), TokenKind::Let | TokenKind::View | TokenKind::On | TokenKind::Effect
|
if self.is_at_end() || !self.can_be_pattern() {
|
||||||
| TokenKind::RBracket | TokenKind::RBrace | TokenKind::Else | TokenKind::Eof) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let pattern = self.parse_pattern()?;
|
let pattern = self.parse_pattern()?;
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,25 @@ route "/" -> column [
|
||||||
-- Project status
|
-- Project status
|
||||||
Card { title: "Status", subtitle: "current phase" } [
|
Card { title: "Status", subtitle: "current phase" } [
|
||||||
match projectStatus
|
match projectStatus
|
||||||
"active" -> Badge { label: "🟢 ACTIVE — Project is on track", variant: "success" }
|
"active" -> row [
|
||||||
"review" -> Badge { label: "🟡 IN REVIEW — Awaiting approval", variant: "warning" }
|
Badge { label: "🟢 ACTIVE", variant: "success" }
|
||||||
"paused" -> Badge { label: "🔴 PAUSED — Project on hold", variant: "error" }
|
text "Project is on track"
|
||||||
_ -> Badge { label: "UNKNOWN", variant: "info" }
|
|
||||||
]
|
]
|
||||||
|
"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 [
|
row [
|
||||||
Button { label: "Active", onClick: projectStatus = "active", variant: "primary" }
|
Button { label: "Active", onClick: projectStatus = "active", variant: "primary" }
|
||||||
Button { label: "Review", onClick: projectStatus = "review", variant: "secondary" }
|
Button { label: "Review", onClick: projectStatus = "review", variant: "secondary" }
|
||||||
Button { label: "Pause", onClick: projectStatus = "paused", variant: "ghost" }
|
Button { label: "Pause", onClick: projectStatus = "paused", variant: "ghost" }
|
||||||
]
|
]
|
||||||
|
]
|
||||||
|
|
||||||
-- Progress
|
-- Progress
|
||||||
Card { title: "Sprint Progress", subtitle: "week 3 of 4" } [
|
Card { title: "Sprint Progress", subtitle: "week 3 of 4" } [
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue