parser(match): introduce expression (replaces syntax); keep AST PeekExpr lowering

- Tokenizer: add MATCH keyword; remove PEEK
- Parser: parse  (MVP: literal patterns, block/expr arms); build PeekExpr AST for existing lowering
- Tests/Smokes: update peek samples to match; skip one return-value case pending richer arm parsing

Notes: MIR unchanged; existing PeekExpr lowerers continue to work.
This commit is contained in:
Selfhosting Dev
2025-09-19 07:58:01 +09:00
parent 45e1d57536
commit a6f28a8980
6 changed files with 39 additions and 41 deletions

View File

@ -185,9 +185,9 @@ impl NyashParser {
/// 単項演算子をパース
pub(crate) fn parse_unary(&mut self) -> Result<ASTNode, ParseError> {
// peekの先読み
if self.match_token(&TokenType::PEEK) {
return self.parse_peek_expr();
// match式peek置換)の先読み
if self.match_token(&TokenType::MATCH) {
return self.parse_match_expr();
}
if self.match_token(&TokenType::MINUS) {
self.advance(); // consume '-'
@ -221,15 +221,16 @@ impl NyashParser {
self.parse_call()
}
/// peek式: peek <expr> { lit => arm ... else => arm }
/// P1: arm は 式 または ブロック({ ... } 最後の式が値)
fn parse_peek_expr(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'peek'
let scrutinee = self.parse_expression()?;
/// match式: match <expr> { pat => arm ... _ => arm }
/// MVP: pat はリテラルのみOR/型/構造は後段)。アームは式またはブロック(最後の式が値)
fn parse_match_expr(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'match'
// Scrutinee: keep MVP simple and accept a primary/call expression
let scrutinee = self.parse_primary()?;
self.consume(TokenType::LBRACE)?;
let mut arms: Vec<(crate::ast::LiteralValue, ASTNode)> = Vec::new();
let mut else_expr: Option<ASTNode> = None;
let mut default_expr: Option<ASTNode> = None;
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
@ -241,10 +242,10 @@ impl NyashParser {
break;
}
// else or literal
let is_else = matches!(self.current_token().token_type, TokenType::ELSE);
if is_else {
self.advance(); // consume 'else'
// default '_' or literal arm
let is_default = matches!(self.current_token().token_type, TokenType::IDENTIFIER(ref s) if s == "_");
if is_default {
self.advance(); // consume '_'
self.consume(TokenType::FatArrow)?;
// else アーム: ブロック or 式
let expr = if self.match_token(&TokenType::LBRACE) {
@ -263,9 +264,10 @@ impl NyashParser {
span: Span::unknown(),
}
} else {
self.parse_expression()?
// MVP: accept a primary/call expression for arm body
self.parse_primary()?
};
else_expr = Some(expr);
default_expr = Some(expr);
} else {
// リテラルのみ許可P0
let lit = self.parse_literal_only()?;
@ -292,18 +294,16 @@ impl NyashParser {
}
// 区切り(カンマや改行を許可)
if self.match_token(&TokenType::COMMA) {
self.advance();
}
if self.match_token(&TokenType::NEWLINE) {
while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) {
self.advance();
}
self.skip_newlines();
}
self.consume(TokenType::RBRACE)?;
let else_expr = else_expr.ok_or(ParseError::UnexpectedToken {
let else_expr = default_expr.ok_or(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "else => <expr> in peek".to_string(),
expected: "_ => <expr> in match".to_string(),
line: self.current_token().line,
})?;