mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 17:06:04 +00:00
Language: match statement
match state {
Title => { if button.start { state = Playing } }
Playing => { /* ... */ }
GameOver => { if button.a { state = Title } }
_ => {}
}
- Lexer: \`match\` keyword and \`=>\` (FatArrow) token
- Parser: \`parse_match\` after the existing loop constructs. Each
arm is \`pattern => { body }\`, with \`_\` as the catch-all. The
match scrutinee is parsed with struct-literal restriction enabled
so the following \`{\` is unambiguously the match body, not a
struct literal.
- The parser desugars match directly into an if/else-if chain so
the analyzer, IR lowering, and codegen don't need new AST variants
— each arm becomes \`scrutinee == pattern\` as the condition, and
the default arm (if any) becomes the final \`else\` block.
Tests cover parse + full pipeline integration for state-style
dispatch using an enum.
https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
c8ae433a7c
commit
f0264b124a
7 changed files with 180 additions and 0 deletions
|
|
@ -850,6 +850,7 @@ impl Parser {
|
|||
TokenKind::KwIf => self.parse_if(),
|
||||
TokenKind::KwWhile => self.parse_while(),
|
||||
TokenKind::KwFor => self.parse_for(),
|
||||
TokenKind::KwMatch => self.parse_match(),
|
||||
TokenKind::KwLoop => self.parse_loop(),
|
||||
TokenKind::KwBreak => {
|
||||
let span = self.current_span();
|
||||
|
|
@ -998,6 +999,83 @@ impl Parser {
|
|||
Ok(Statement::Loop(body, start))
|
||||
}
|
||||
|
||||
/// Parse `match expr { pat => { body }, pat => { body }, _ => { body } }`.
|
||||
/// Desugars to a chain of `if expr == pat { body } else if ...`
|
||||
/// at parse time — no dedicated AST variant needed.
|
||||
fn parse_match(&mut self) -> Result<Statement, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwMatch)?;
|
||||
let saved = self.restrict_struct_literals;
|
||||
self.restrict_struct_literals = true;
|
||||
let scrutinee = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
self.expect(&TokenKind::LBrace)?;
|
||||
|
||||
let mut arms: Vec<(Expr, Block)> = Vec::new();
|
||||
let mut default: Option<Block> = None;
|
||||
while *self.peek() != TokenKind::RBrace && *self.peek() != TokenKind::Eof {
|
||||
// A default arm is `_ => { ... }`.
|
||||
if let TokenKind::Ident(name) = self.peek().clone() {
|
||||
if name == "_" {
|
||||
self.advance();
|
||||
self.expect(&TokenKind::FatArrow)?;
|
||||
let body = self.parse_block()?;
|
||||
default = Some(body);
|
||||
if *self.peek() == TokenKind::Comma {
|
||||
self.advance();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let pat_span = self.current_span();
|
||||
let pat = self.parse_expr()?;
|
||||
self.expect(&TokenKind::FatArrow)?;
|
||||
let body = self.parse_block()?;
|
||||
// Build `scrutinee == pat` as the branch condition.
|
||||
let cond = Expr::BinaryOp(
|
||||
Box::new(scrutinee.clone()),
|
||||
BinOp::Eq,
|
||||
Box::new(pat),
|
||||
pat_span,
|
||||
);
|
||||
arms.push((cond, body));
|
||||
if *self.peek() == TokenKind::Comma {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
self.expect(&TokenKind::RBrace)?;
|
||||
|
||||
if arms.is_empty() {
|
||||
// `match x { _ => body }` or empty match — emit the
|
||||
// default block directly, or an empty no-op.
|
||||
if let Some(body) = default {
|
||||
return Ok(Statement::If(
|
||||
Expr::BoolLiteral(true, start),
|
||||
body,
|
||||
Vec::new(),
|
||||
None,
|
||||
start,
|
||||
));
|
||||
}
|
||||
return Ok(Statement::If(
|
||||
Expr::BoolLiteral(false, start),
|
||||
Block {
|
||||
statements: Vec::new(),
|
||||
span: start,
|
||||
},
|
||||
Vec::new(),
|
||||
None,
|
||||
start,
|
||||
));
|
||||
}
|
||||
|
||||
// Build an if/else-if chain. The first arm becomes the
|
||||
// `then` block; subsequent arms become `else if` entries;
|
||||
// the default arm (if any) becomes the final `else`.
|
||||
let (first_cond, first_body) = arms.remove(0);
|
||||
Ok(Statement::If(first_cond, first_body, arms, default, start))
|
||||
}
|
||||
|
||||
fn parse_for(&mut self) -> Result<Statement, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwFor)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue