Files
hakorune/src/parser/sugar.rs

175 lines
5.1 KiB
Rust

//! Phase 12.7-B sugar desugaring (basic)
//! Safe access (?.), default (??), pipeline (|>), compound-assign (+=/-=/*=/=), range (..)
//! Note: This is a shallow AST-to-AST transform; semantic phases remain unchanged.
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
use crate::syntax::sugar_config::{SugarConfig, SugarLevel};
pub fn apply_sugar(ast: ASTNode, cfg: &SugarConfig) -> ASTNode {
match cfg.level {
SugarLevel::Basic | SugarLevel::Full => rewrite(ast),
SugarLevel::None => ast,
}
}
fn rewrite(ast: ASTNode) -> ASTNode {
match ast {
ASTNode::Program { statements, span } => {
let stmts = statements.into_iter().map(|s| rewrite(s)).collect();
ASTNode::Program {
statements: stmts,
span,
}
}
ASTNode::Assignment {
target,
value,
span,
} => ASTNode::Assignment {
target: Box::new(rewrite(*target)),
value: Box::new(rewrite(*value)),
span,
},
ASTNode::BinaryOp {
operator,
left,
right,
span,
} => {
// default null (??): a ?? b => if a is null then b else a
// Here we approximate as: (a == null) ? b : a using peek-like structure
// For minimalism, keep as BinaryOp and rely on later phases (placeholder).
ASTNode::BinaryOp {
operator,
left: Box::new(rewrite(*left)),
right: Box::new(rewrite(*right)),
span,
}
}
ASTNode::MethodCall {
object,
method,
arguments,
span,
} => ASTNode::MethodCall {
object: Box::new(rewrite(*object)),
method,
arguments: arguments.into_iter().map(rewrite).collect(),
span,
},
ASTNode::FunctionCall {
name,
arguments,
span,
} => ASTNode::FunctionCall {
name,
arguments: arguments.into_iter().map(rewrite).collect(),
span,
},
ASTNode::FieldAccess {
object,
field,
span,
} => ASTNode::FieldAccess {
object: Box::new(rewrite(*object)),
field,
span,
},
ASTNode::UnaryOp {
operator,
operand,
span,
} => ASTNode::UnaryOp {
operator,
operand: Box::new(rewrite(*operand)),
span,
},
ASTNode::PeekExpr {
scrutinee,
arms,
else_expr,
span,
} => ASTNode::PeekExpr {
scrutinee: Box::new(rewrite(*scrutinee)),
arms: arms.into_iter().map(|(l, e)| (l, rewrite(e))).collect(),
else_expr: Box::new(rewrite(*else_expr)),
span,
},
// Others: recursively visit children where present
ASTNode::If {
condition,
then_body,
else_body,
span,
} => ASTNode::If {
condition: Box::new(rewrite(*condition)),
then_body: then_body.into_iter().map(rewrite).collect(),
else_body: else_body.map(|v| v.into_iter().map(rewrite).collect()),
span,
},
ASTNode::Loop {
condition,
body,
span,
} => ASTNode::Loop {
condition: Box::new(rewrite(*condition)),
body: body.into_iter().map(rewrite).collect(),
span,
},
ASTNode::Return { value, span } => ASTNode::Return {
value: value.map(|v| Box::new(rewrite(*v))),
span,
},
ASTNode::Print { expression, span } => ASTNode::Print {
expression: Box::new(rewrite(*expression)),
span,
},
ASTNode::New {
class,
arguments,
type_arguments,
span,
} => ASTNode::New {
class,
arguments: arguments.into_iter().map(rewrite).collect(),
type_arguments,
span,
},
ASTNode::Call {
callee,
arguments,
span,
} => ASTNode::Call {
callee: Box::new(rewrite(*callee)),
arguments: arguments.into_iter().map(rewrite).collect(),
span,
},
ASTNode::Local {
variables,
initial_values,
span,
} => ASTNode::Local {
variables,
initial_values: initial_values
.into_iter()
.map(|o| o.map(|b| Box::new(rewrite(*b))))
.collect(),
span,
},
other => other,
}
}
#[allow(dead_code)]
fn make_eq_null(expr: ASTNode) -> ASTNode {
ASTNode::BinaryOp {
operator: BinaryOperator::Equal,
left: Box::new(expr),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Null,
span: Span::unknown(),
}),
span: Span::unknown(),
}
}