grammar(P0): add peek expression, continue statement, and field type annotations acceptance; add sample apps and interpreter path\n\n- tokenizer: add keywords (peek, continue), tokens (=> as FatArrow, :: as DoubleColon), keep >> as Arrow\n- parser: implement peek as expression (literal patterns only, else required), add continue; accept field 'name: Type' (discard type P0)\n- interpreter: evaluate PeekExpr; add Continue control flow handling\n- apps: add peek-demo, loop-continue-demo, adjust field-decl demo; use ConsoleBox instead of env.console for interpreter backend\n- docs: CURRENT_TASK updated earlier for Phase 12.7 P0\n\nNOTE: peek arms currently single-expression (no block expr yet); VM/MIR path does not lower PeekExpr yet; use --backend interpreter for demos

This commit is contained in:
Moe Charm
2025-09-03 15:26:15 +09:00
parent ceb22b6c18
commit 6d79d7d3ac
20 changed files with 1581 additions and 4 deletions

View File

@ -192,6 +192,10 @@ impl NyashParser {
/// 単項演算子をパース
fn parse_unary(&mut self) -> Result<ASTNode, ParseError> {
// peek式の先読み
if self.match_token(&TokenType::PEEK) {
return self.parse_peek_expr();
}
if self.match_token(&TokenType::MINUS) {
self.advance(); // consume '-'
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
@ -223,6 +227,73 @@ impl NyashParser {
self.parse_call()
}
/// peek式: peek <expr> { lit => expr ... else => expr }
fn parse_peek_expr(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'peek'
let scrutinee = self.parse_expression()?;
self.consume(TokenType::LBRACE)?;
let mut arms: Vec<(crate::ast::LiteralValue, ASTNode)> = Vec::new();
let mut else_expr: Option<ASTNode> = 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; }
// else or literal
let is_else = matches!(self.current_token().token_type, TokenType::ELSE);
if is_else {
self.advance(); // consume 'else'
self.consume(TokenType::FAT_ARROW)?;
let expr = self.parse_expression()?;
else_expr = Some(expr);
} else {
// リテラルのみ許可P0
let lit = self.parse_literal_only()?;
self.consume(TokenType::FAT_ARROW)?;
let expr = self.parse_expression()?;
arms.push((lit, expr));
}
// 区切り(カンマや改行を許可)
if self.match_token(&TokenType::COMMA) { self.advance(); }
if self.match_token(&TokenType::NEWLINE) { self.advance(); }
}
self.consume(TokenType::RBRACE)?;
let else_expr = else_expr.ok_or(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "else => <expr> in peek".to_string(),
line: self.current_token().line,
})?;
Ok(ASTNode::PeekExpr {
scrutinee: Box::new(scrutinee),
arms,
else_expr: Box::new(else_expr),
span: Span::unknown(),
})
}
fn parse_literal_only(&mut self) -> Result<crate::ast::LiteralValue, ParseError> {
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 })
}
}
}
/// 関数・メソッド呼び出しをパース
fn parse_call(&mut self) -> Result<ASTNode, ParseError> {