diff --git a/src/parser/expr/match_expr.rs b/src/parser/expr/match_expr.rs new file mode 100644 index 00000000..4d4af15f --- /dev/null +++ b/src/parser/expr/match_expr.rs @@ -0,0 +1,149 @@ +use crate::ast::{ASTNode, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; + +impl NyashParser { + /// match式: match { lit[ '|' lit ]* => , ..., _ => } + /// MVP: リテラルパターン+OR+デフォルト(_) のみ。アーム本体は式またはブロック。 + pub(crate) fn expr_parse_match(&mut self) -> Result { + self.advance(); // consume 'match' + // Scrutinee: MVPでは primary/call に限定(表現力は十分) + let scrutinee = self.expr_parse_primary()?; + self.consume(TokenType::LBRACE)?; + + let mut arms: Vec<(crate::ast::LiteralValue, ASTNode)> = Vec::new(); + let mut default_expr: Option = None; + + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) { + self.advance(); + self.skip_newlines(); + } + if self.match_token(&TokenType::RBRACE) { + break; + } + + // 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)?; + let expr = if self.match_token(&TokenType::LBRACE) { + // ブロックを式として扱う(最後の文の値が返る) + self.advance(); // consume '{' + let mut stmts: Vec = Vec::new(); + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + if !self.match_token(&TokenType::RBRACE) { + stmts.push(self.parse_statement()?); + } + } + self.consume(TokenType::RBRACE)?; + ASTNode::Program { + statements: stmts, + span: Span::unknown(), + } + } else { + // MVP: アームは primary/call を優先 + self.expr_parse_primary()? + }; + default_expr = Some(expr); + } else { + // リテラル(OR結合可) + let mut lits: Vec = Vec::new(); + let first = self.lit_only_for_match()?; + lits.push(first); + while self.match_token(&TokenType::BitOr) { + self.advance(); // consume '|' + let nxt = self.lit_only_for_match()?; + lits.push(nxt); + } + self.consume(TokenType::FatArrow)?; + let expr = if self.match_token(&TokenType::LBRACE) { + self.advance(); // consume '{' + let mut stmts: Vec = Vec::new(); + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + if !self.match_token(&TokenType::RBRACE) { + stmts.push(self.parse_statement()?); + } + } + self.consume(TokenType::RBRACE)?; + ASTNode::Program { + statements: stmts, + span: Span::unknown(), + } + } else { + self.expr_parse_primary()? + }; + for lit in lits { + arms.push((lit.clone(), expr.clone())); + } + } + + // 区切り(カンマや改行を許可) + while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) { + self.advance(); + } + self.skip_newlines(); + } + + self.consume(TokenType::RBRACE)?; + let else_expr = default_expr.ok_or(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "_ => in match".to_string(), + line: self.current_token().line, + })?; + + // 既存の Lower を活用するため PeekExpr に落とす + Ok(ASTNode::PeekExpr { + scrutinee: Box::new(scrutinee), + arms, + else_expr: Box::new(else_expr), + span: Span::unknown(), + }) + } + + // match 用の最小リテラルパーサ(式は受け付けない) + fn lit_only_for_match(&mut self) -> Result { + match &self.current_token().token_type { + TokenType::STRING(s) => { + let v = crate::ast::LiteralValue::String(s.clone()); + self.advance(); + Ok(v) + } + TokenType::NUMBER(n) => { + let v = crate::ast::LiteralValue::Integer(*n); + self.advance(); + Ok(v) + } + TokenType::FLOAT(f) => { + let v = crate::ast::LiteralValue::Float(*f); + self.advance(); + Ok(v) + } + TokenType::TRUE => { + self.advance(); + Ok(crate::ast::LiteralValue::Bool(true)) + } + TokenType::FALSE => { + self.advance(); + Ok(crate::ast::LiteralValue::Bool(false)) + } + TokenType::NULL => { + self.advance(); + Ok(crate::ast::LiteralValue::Null) + } + _ => { + let line = self.current_token().line; + Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "literal".to_string(), + line, + }) + } + } + } +} diff --git a/src/parser/expr/mod.rs b/src/parser/expr/mod.rs index dc38bad4..13bd9543 100644 --- a/src/parser/expr/mod.rs +++ b/src/parser/expr/mod.rs @@ -9,3 +9,4 @@ pub(crate) mod range; pub(crate) mod shift; pub(crate) mod term; pub(crate) mod ternary; +pub(crate) mod match_expr; diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index f2dccd0c..e5ae5cac 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -187,7 +187,7 @@ impl NyashParser { pub(crate) fn parse_unary(&mut self) -> Result { // match式(peek置換)の先読み if self.match_token(&TokenType::MATCH) { - return self.parse_match_expr(); + return self.expr_parse_match(); } if self.match_token(&TokenType::MINUS) { self.advance(); // consume '-' @@ -221,99 +221,7 @@ impl NyashParser { self.parse_call() } - /// match式: match { pat => arm ... _ => arm } - /// MVP: pat はリテラルのみ(OR/型/構造は後段)。アームは式またはブロック(最後の式が値)。 - fn parse_match_expr(&mut self) -> Result { - 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 default_expr: Option = None; - - while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { - self.skip_newlines(); - while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) { - self.advance(); - self.skip_newlines(); - } - if self.match_token(&TokenType::RBRACE) { - break; - } - - // 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) { - // ブロックを式として扱う(最後の文の値が返る) - self.advance(); // consume '{' - let mut stmts: Vec = Vec::new(); - while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { - self.skip_newlines(); - if !self.match_token(&TokenType::RBRACE) { - stmts.push(self.parse_statement()?); - } - } - self.consume(TokenType::RBRACE)?; - ASTNode::Program { - statements: stmts, - span: Span::unknown(), - } - } else { - // MVP: accept a primary/call expression for arm body - self.parse_primary()? - }; - default_expr = Some(expr); - } else { - // リテラルのみ許可(P0) - let lit = self.parse_literal_only()?; - self.consume(TokenType::FatArrow)?; - // アーム: ブロック or 式 - let expr = if self.match_token(&TokenType::LBRACE) { - self.advance(); // consume '{' - let mut stmts: Vec = Vec::new(); - while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { - self.skip_newlines(); - if !self.match_token(&TokenType::RBRACE) { - stmts.push(self.parse_statement()?); - } - } - self.consume(TokenType::RBRACE)?; - ASTNode::Program { - statements: stmts, - span: Span::unknown(), - } - } else { - self.parse_expression()? - }; - arms.push((lit, expr)); - } - - // 区切り(カンマや改行を許可) - while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) { - self.advance(); - } - self.skip_newlines(); - } - - self.consume(TokenType::RBRACE)?; - let else_expr = default_expr.ok_or(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "_ => in match".to_string(), - line: self.current_token().line, - })?; - - Ok(ASTNode::PeekExpr { - scrutinee: Box::new(scrutinee), - arms, - else_expr: Box::new(else_expr), - span: Span::unknown(), - }) - } + // parse_match_expr moved to expr/match_expr.rs as expr_parse_match fn parse_literal_only(&mut self) -> Result { match &self.current_token().token_type {