Files
hakorune/src/ast/utils.rs
nyash-codex c70e76ff57 feat(parser): Phase 152-A - Grouped assignment expression (箱化モジュール化)
Implement Stage-3 grouped assignment expression `(x = expr)` following
the 箱化モジュール化 (modular box) pattern established in Phase 133/134.

**Implementation**:
- AssignmentExprParser module (Rust: 183 lines)
  - src/parser/stage3/assignment_expr_parser.rs (+183 lines)
  - src/parser/stage3/mod.rs (+9 lines)
- AST node addition: GroupedAssignmentExpr
  - src/ast.rs (+7 lines)
  - src/ast/utils.rs (+9 lines)
- MIR lowering via 1-line delegation
  - src/mir/builder/exprs.rs (+5 lines)
  - src/mir/builder/vars.rs (+4 lines)
- Parser integration via 1-line delegation
  - src/parser/expr/primary.rs (+6 lines)
  - src/parser/mod.rs (+1 line)

**Test Results**: 3/3 PASS
- assignment_expr_simple.hako: RC 1 
- assignment_expr_shortcircuit.hako: RC 1 
- shortcircuit_and_phi_skip.hako: RC 1  (updated to use expression context)

**Stage-3 Gate**: No impact on Stage-2/legacy
- NYASH_FEATURES=stage3 required
- Pattern: '(' IDENT '=' expr ')'
- Value/type same as rhs, side effect assigns to lhs

**箱化モジュール化パターン**:
- Dedicated module for assignment expression parsing
- Clear responsibility separation
- 1-line delegation for integration
- Testability improvement
- Follows Phase 133/134-A/134-B pattern

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:32:58 +09:00

412 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Utility helpers for Nyash AST nodes extracted from `ast.rs`.
use super::{ASTNode, ASTNodeType, Span};
use std::fmt;
impl ASTNode {
/// AST nodeの種類を文字列で取得 (デバッグ用)
pub fn node_type(&self) -> &'static str {
match self {
ASTNode::Program { .. } => "Program",
ASTNode::Assignment { .. } => "Assignment",
ASTNode::Print { .. } => "Print",
ASTNode::If { .. } => "If",
ASTNode::Loop { .. } => "Loop",
ASTNode::While { .. } => "While",
ASTNode::ForRange { .. } => "ForRange",
ASTNode::Return { .. } => "Return",
ASTNode::Break { .. } => "Break",
ASTNode::Continue { .. } => "Continue",
ASTNode::UsingStatement { .. } => "UsingStatement",
ASTNode::ImportStatement { .. } => "ImportStatement",
ASTNode::BoxDeclaration { .. } => "BoxDeclaration",
ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration",
ASTNode::GlobalVar { .. } => "GlobalVar",
ASTNode::Literal { .. } => "Literal",
ASTNode::Variable { .. } => "Variable",
ASTNode::UnaryOp { .. } => "UnaryOp",
ASTNode::BinaryOp { .. } => "BinaryOp",
ASTNode::MethodCall { .. } => "MethodCall",
ASTNode::FieldAccess { .. } => "FieldAccess",
ASTNode::Index { .. } => "Index",
ASTNode::New { .. } => "New",
ASTNode::This { .. } => "This",
ASTNode::Me { .. } => "Me",
ASTNode::FromCall { .. } => "FromCall",
ASTNode::ThisField { .. } => "ThisField",
ASTNode::MeField { .. } => "MeField",
ASTNode::Local { .. } => "Local",
ASTNode::Outbox { .. } => "Outbox",
ASTNode::FunctionCall { .. } => "FunctionCall",
ASTNode::Call { .. } => "Call",
ASTNode::Nowait { .. } => "Nowait",
ASTNode::Arrow { .. } => "Arrow",
ASTNode::TryCatch { .. } => "TryCatch",
ASTNode::Throw { .. } => "Throw",
ASTNode::AwaitExpression { .. } => "AwaitExpression",
ASTNode::QMarkPropagate { .. } => "QMarkPropagate",
ASTNode::MatchExpr { .. } => "MatchExpr",
ASTNode::Lambda { .. } => "Lambda",
ASTNode::ArrayLiteral { .. } => "ArrayLiteral",
ASTNode::MapLiteral { .. } => "MapLiteral",
// Optional diagnostic-only wrapper
ASTNode::ScopeBox { .. } => "ScopeBox",
// Phase 152-A: Grouped assignment expression
ASTNode::GroupedAssignmentExpr { .. } => "GroupedAssignmentExpr",
}
}
/// Structure/Expression/Statement の分類
pub fn classify(&self) -> ASTNodeType {
use ASTNodeType::{Expression as E, Statement as S, Structure as St};
match self {
// Structure nodes - 言語の基本構造
ASTNode::BoxDeclaration { .. } => St,
ASTNode::FunctionDeclaration { .. } => St,
ASTNode::If { .. } => St,
ASTNode::Loop { .. } => St,
ASTNode::While { .. } => St,
ASTNode::ForRange { .. } => St,
ASTNode::TryCatch { .. } => St,
ASTNode::ScopeBox { .. } => St, // diagnostic wrapper
// Expression nodes - 値を生成する表現
ASTNode::Literal { .. } => E,
ASTNode::Variable { .. } => E,
ASTNode::BinaryOp { .. } => E,
ASTNode::UnaryOp { .. } => E,
ASTNode::FunctionCall { .. } => E,
ASTNode::Call { .. } => E,
ASTNode::MethodCall { .. } => E,
ASTNode::FieldAccess { .. } => E,
ASTNode::New { .. } => E,
ASTNode::This { .. } => E,
ASTNode::Me { .. } => E,
ASTNode::FromCall { .. } => E,
ASTNode::ThisField { .. } => E,
ASTNode::MeField { .. } => E,
ASTNode::Index { .. } => E,
ASTNode::MatchExpr { .. } => E,
ASTNode::QMarkPropagate { .. } => E,
ASTNode::Lambda { .. } => E,
ASTNode::ArrayLiteral { .. } => E,
ASTNode::MapLiteral { .. } => E,
ASTNode::AwaitExpression { .. } => E,
// Phase 152-A: Grouped assignment expression (expression with side effect)
ASTNode::GroupedAssignmentExpr { .. } => E,
// Statement nodes - 実行可能なアクション
ASTNode::Program { .. } => S,
ASTNode::Assignment { .. } => S,
ASTNode::Print { .. } => S,
ASTNode::Return { .. } => S,
ASTNode::Break { .. } => S,
ASTNode::Continue { .. } => S,
ASTNode::UsingStatement { .. } => S,
ASTNode::ImportStatement { .. } => S,
ASTNode::GlobalVar { .. } => S,
ASTNode::Local { .. } => S,
ASTNode::Outbox { .. } => S,
ASTNode::Nowait { .. } => S,
ASTNode::Arrow { .. } => S,
ASTNode::Throw { .. } => S,
}
}
pub fn is_structure(&self) -> bool {
matches!(self.classify(), ASTNodeType::Structure)
}
pub fn is_expression(&self) -> bool {
matches!(self.classify(), ASTNodeType::Expression)
}
pub fn is_statement(&self) -> bool {
matches!(self.classify(), ASTNodeType::Statement)
}
/// AST nodeの詳細情報を取得 (デバッグ用)
pub fn info(&self) -> String {
match self {
ASTNode::Program { statements, .. } => {
format!("Program({} statements)", statements.len())
}
ASTNode::Assignment { target, .. } => {
format!("Assignment(target: {})", target.info())
}
ASTNode::Print { .. } => "Print".to_string(),
ASTNode::If { .. } => "If".to_string(),
ASTNode::Loop {
condition: _, body, ..
} => {
format!("Loop({} statements)", body.len())
}
ASTNode::While {
condition: _, body, ..
} => {
format!("While({} statements)", body.len())
}
ASTNode::ForRange {
var_name,
start: _,
end: _,
body,
..
} => {
format!("ForRange(var={}, {} statements)", var_name, body.len())
}
ASTNode::Return { value, .. } => {
if value.is_some() {
"Return(with value)".to_string()
} else {
"Return(void)".to_string()
}
}
ASTNode::Break { .. } => "Break".to_string(),
ASTNode::Continue { .. } => "Continue".to_string(),
ASTNode::UsingStatement { namespace_name, .. } => {
format!("UsingStatement({})", namespace_name)
}
ASTNode::ImportStatement { path, alias, .. } => {
if let Some(a) = alias {
format!("ImportStatement({}, as {})", path, a)
} else {
format!("ImportStatement({})", path)
}
}
ASTNode::BoxDeclaration {
name,
fields,
methods,
constructors,
is_interface,
extends,
implements,
..
} => {
let mut desc = if *is_interface {
format!("InterfaceBox({}, {} methods", name, methods.len())
} else {
format!(
"BoxDeclaration({}, {} fields, {} methods, {} constructors",
name,
fields.len(),
methods.len(),
constructors.len()
)
};
if !extends.is_empty() {
desc.push_str(&format!(", extends [{}]", extends.join(", ")));
}
if !implements.is_empty() {
desc.push_str(&format!(", implements [{}]", implements.join(", ")));
}
desc.push(')');
desc
}
ASTNode::FunctionDeclaration {
name,
params,
body,
is_static,
is_override,
..
} => {
let static_str = if *is_static { "static " } else { "" };
let override_str = if *is_override { "override " } else { "" };
format!(
"FunctionDeclaration({}{}{}({}), {} statements)",
override_str,
static_str,
name,
params.join(", "),
body.len()
)
}
ASTNode::GlobalVar { name, .. } => {
format!("GlobalVar({})", name)
}
ASTNode::Literal { .. } => "Literal".to_string(),
ASTNode::Variable { name, .. } => {
format!("Variable({})", name)
}
ASTNode::UnaryOp { operator, .. } => {
format!("UnaryOp({})", operator)
}
ASTNode::BinaryOp { operator, .. } => {
format!("BinaryOp({})", operator)
}
ASTNode::MethodCall {
method, arguments, ..
} => {
format!("MethodCall({}, {} args)", method, arguments.len())
}
ASTNode::FieldAccess { field, .. } => {
format!("FieldAccess({})", field)
}
ASTNode::New {
class,
arguments,
type_arguments,
..
} => {
if type_arguments.is_empty() {
format!("New({}, {} args)", class, arguments.len())
} else {
format!(
"New({}<{}>, {} args)",
class,
type_arguments.join(", "),
arguments.len()
)
}
}
ASTNode::This { .. } => "This".to_string(),
ASTNode::Me { .. } => "Me".to_string(),
ASTNode::FromCall {
parent,
method,
arguments,
..
} => {
format!("FromCall({}.{}, {} args)", parent, method, arguments.len())
}
ASTNode::ThisField { field, .. } => {
format!("ThisField({})", field)
}
ASTNode::MeField { field, .. } => {
format!("MeField({})", field)
}
ASTNode::Local { variables, .. } => {
format!("Local({})", variables.join(", "))
}
ASTNode::Outbox { variables, .. } => {
format!("Outbox({})", variables.join(", "))
}
ASTNode::FunctionCall {
name, arguments, ..
} => {
format!("FunctionCall({}, {} args)", name, arguments.len())
}
ASTNode::Call { .. } => "Call".to_string(),
ASTNode::Nowait { variable, .. } => {
format!("Nowait({})", variable)
}
ASTNode::Arrow { .. } => "Arrow(>>)".to_string(),
ASTNode::TryCatch {
try_body,
catch_clauses,
finally_body,
..
} => {
let mut desc = format!(
"TryCatch({} try statements, {} catch clauses",
try_body.len(),
catch_clauses.len()
);
if finally_body.is_some() {
desc.push_str(", has finally");
}
desc.push(')');
desc
}
ASTNode::Throw { .. } => "Throw".to_string(),
ASTNode::AwaitExpression { expression, .. } => {
format!("Await({:?})", expression)
}
ASTNode::MatchExpr { .. } => "MatchExpr".to_string(),
ASTNode::QMarkPropagate { .. } => "QMarkPropagate".to_string(),
ASTNode::Lambda { params, body, .. } => {
format!("Lambda({} params, {} statements)", params.len(), body.len())
}
ASTNode::ArrayLiteral { elements, .. } => {
format!("ArrayLiteral({} elements)", elements.len())
}
ASTNode::MapLiteral { entries, .. } => {
format!("MapLiteral({} entries)", entries.len())
}
ASTNode::Index { target, index, .. } => {
format!("Index(target={:?}, index={:?})", target, index)
}
ASTNode::ScopeBox { .. } => "ScopeBox".to_string(),
// Phase 152-A: Grouped assignment expression
ASTNode::GroupedAssignmentExpr { lhs, .. } => {
format!("GroupedAssignmentExpr(lhs={})", lhs)
}
}
}
/// ASTードからSpan情報を取得
pub fn span(&self) -> Span {
match self {
ASTNode::Program { span, .. } => *span,
ASTNode::Assignment { span, .. } => *span,
ASTNode::Print { span, .. } => *span,
ASTNode::If { span, .. } => *span,
ASTNode::Loop { span, .. } => *span,
ASTNode::While { span, .. } => *span,
ASTNode::ForRange { span, .. } => *span,
ASTNode::Return { span, .. } => *span,
ASTNode::Break { span, .. } => *span,
ASTNode::Continue { span, .. } => *span,
ASTNode::UsingStatement { span, .. } => *span,
ASTNode::ImportStatement { span, .. } => *span,
ASTNode::Nowait { span, .. } => *span,
ASTNode::Arrow { span, .. } => *span,
ASTNode::TryCatch { span, .. } => *span,
ASTNode::Throw { span, .. } => *span,
ASTNode::BoxDeclaration { span, .. } => *span,
ASTNode::FunctionDeclaration { span, .. } => *span,
ASTNode::GlobalVar { span, .. } => *span,
ASTNode::Literal { span, .. } => *span,
ASTNode::Variable { span, .. } => *span,
ASTNode::UnaryOp { span, .. } => *span,
ASTNode::BinaryOp { span, .. } => *span,
ASTNode::MethodCall { span, .. } => *span,
ASTNode::FieldAccess { span, .. } => *span,
ASTNode::Index { span, .. } => *span,
ASTNode::New { span, .. } => *span,
ASTNode::This { span, .. } => *span,
ASTNode::Me { span, .. } => *span,
ASTNode::FromCall { span, .. } => *span,
ASTNode::ThisField { span, .. } => *span,
ASTNode::MeField { span, .. } => *span,
ASTNode::Local { span, .. } => *span,
ASTNode::Outbox { span, .. } => *span,
ASTNode::FunctionCall { span, .. } => *span,
ASTNode::Call { span, .. } => *span,
ASTNode::AwaitExpression { span, .. } => *span,
ASTNode::MatchExpr { span, .. } => *span,
ASTNode::QMarkPropagate { span, .. } => *span,
ASTNode::Lambda { span, .. } => *span,
ASTNode::ArrayLiteral { span, .. } => *span,
ASTNode::MapLiteral { span, .. } => *span,
ASTNode::ScopeBox { span, .. } => *span,
// Phase 152-A: Grouped assignment expression
ASTNode::GroupedAssignmentExpr { span, .. } => *span,
}
}
}
impl fmt::Display for ASTNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.info())
}
}
impl ASTNode {
/// FunctionDeclarationのパラメータ数を取得
pub fn get_param_count(&self) -> usize {
match self {
ASTNode::FunctionDeclaration { params, .. } => params.len(),
_ => 0,
}
}
}