582 lines
20 KiB
Rust
582 lines
20 KiB
Rust
//! Pretty printing module for AST nodes
|
|||
|
|
//!
|
||
|
|
//! This module provides elegant and functional printing capabilities for AST objects,
|
||
|
|
//! inspired by Rust's debug formatting but optimized for readability and analysis.
|
||
|
|
|
||
|
|
use crate::frontend::{
|
||
|
|
ast::{AstNode, AstNodeKind, Expr, Stmt},
|
||
|
|
source_registry::SourceSlice,
|
||
|
|
};
|
||
|
|
use std::fmt::{self, Write};
|
||
|
|
|
||
|
|
/// Configuration for pretty printing output
|
||
|
|
#[derive(Clone, Debug)]
|
||
|
|
pub struct PrettyConfig {
|
||
|
|
/// Indentation string (default: " ")
|
||
|
|
pub indent: String,
|
||
|
|
/// Maximum depth before truncation (None = unlimited)
|
||
|
|
pub max_depth: Option<usize>,
|
||
|
|
/// Show source position information
|
||
|
|
pub show_positions: bool,
|
||
|
|
/// Use compact single-line format when possible
|
||
|
|
pub compact: bool,
|
||
|
|
/// Include type information
|
||
|
|
pub show_types: bool,
|
||
|
|
/// Use tree-style connectors
|
||
|
|
pub tree_style: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Default for PrettyConfig {
|
||
|
|
fn default() -> Self {
|
||
|
|
Self {
|
||
|
|
indent: " ".to_string(),
|
||
|
|
max_depth: None,
|
||
|
|
show_positions: true,
|
||
|
|
compact: false,
|
||
|
|
show_types: true,
|
||
|
|
tree_style: false,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyConfig {
|
||
|
|
/// Create a compact configuration
|
||
|
|
pub fn compact() -> Self {
|
||
|
|
Self {
|
||
|
|
compact: true,
|
||
|
|
show_positions: false,
|
||
|
|
..Default::default()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Create a tree-style configuration
|
||
|
|
pub fn tree() -> Self {
|
||
|
|
Self {
|
||
|
|
tree_style: true,
|
||
|
|
indent: "│ ".to_string(),
|
||
|
|
..Default::default()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Create a debug configuration with all details
|
||
|
|
pub fn debug() -> Self {
|
||
|
|
Self {
|
||
|
|
show_positions: true,
|
||
|
|
show_types: true,
|
||
|
|
compact: false,
|
||
|
|
max_depth: None,
|
||
|
|
..Default::default()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Context for pretty printing, tracks depth and styling
|
||
|
|
pub struct PrettyContext {
|
||
|
|
config: PrettyConfig,
|
||
|
|
depth: usize,
|
||
|
|
is_last: Vec<bool>, // For tree-style formatting
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyContext {
|
||
|
|
pub fn new(config: PrettyConfig) -> Self {
|
||
|
|
Self {
|
||
|
|
config,
|
||
|
|
depth: 0,
|
||
|
|
is_last: Vec::new(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn indent(&self) -> String {
|
||
|
|
if self.config.tree_style {
|
||
|
|
self.tree_indent()
|
||
|
|
} else {
|
||
|
|
self.config.indent.repeat(self.depth)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn tree_indent(&self) -> String {
|
||
|
|
let mut result = String::new();
|
||
|
|
for (i, &is_last) in self.is_last.iter().enumerate() {
|
||
|
|
if i == self.is_last.len() - 1 {
|
||
|
|
result.push_str(if is_last { "└─ " } else { "├─ " });
|
||
|
|
} else {
|
||
|
|
result.push_str(if is_last { " " } else { "│ " });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
result
|
||
|
|
}
|
||
|
|
|
||
|
|
fn child_context(&self, is_last: bool) -> Self {
|
||
|
|
let mut new_is_last = self.is_last.clone();
|
||
|
|
new_is_last.push(is_last);
|
||
|
|
|
||
|
|
Self {
|
||
|
|
config: self.config.clone(),
|
||
|
|
depth: self.depth + 1,
|
||
|
|
is_last: new_is_last,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn should_truncate(&self) -> bool {
|
||
|
|
self.config.max_depth.map_or(false, |max| self.depth >= max)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Trait for pretty printing AST nodes
|
||
|
|
pub trait PrettyPrint {
|
||
|
|
fn pretty_print(&self, ctx: &PrettyContext, f: &mut fmt::Formatter) -> fmt::Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Convenience functions to get pretty printed strings
|
||
|
|
pub fn pretty_print_with_config<T: PrettyPrint>(item: &T, config: &PrettyConfig) -> String {
|
||
|
|
let mut output = String::new();
|
||
|
|
let ctx = PrettyContext::new(config.clone());
|
||
|
|
write!(&mut output, "{}", PrettyWrapper { item, ctx: &ctx }).unwrap();
|
||
|
|
output
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn pretty_print<T: PrettyPrint>(item: &T) -> String {
|
||
|
|
pretty_print_with_config(item, &PrettyConfig::default())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Wrapper for pretty printing with Display trait
|
||
|
|
struct PrettyWrapper<'a, T: PrettyPrint> {
|
||
|
|
item: &'a T,
|
||
|
|
ctx: &'a PrettyContext,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a, T: PrettyPrint> fmt::Display for PrettyWrapper<'a, T> {
|
||
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
|
self.item.pretty_print(self.ctx, f)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyPrint for Expr {
|
||
|
|
fn pretty_print(&self, ctx: &PrettyContext, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
|
if ctx.should_truncate() {
|
||
|
|
return write!(f, "{}...", ctx.indent());
|
||
|
|
}
|
||
|
|
|
||
|
|
let indent = ctx.indent();
|
||
|
|
|
||
|
|
match self {
|
||
|
|
Expr::Literal { value } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
write!(f, "{:?}", value)
|
||
|
|
} else {
|
||
|
|
write!(f, "{}Literal {{ value: {:?} }}", indent, value)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Expr::Binary {
|
||
|
|
left,
|
||
|
|
operator,
|
||
|
|
right,
|
||
|
|
} => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
// For compact, we need to format recursively
|
||
|
|
let left_str =
|
||
|
|
pretty_print_with_config(left.as_ref(), &PrettyConfig::compact());
|
||
|
|
let right_str =
|
||
|
|
pretty_print_with_config(right.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "({} {:?} {})", left_str, operator, right_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Binary {{", indent)?;
|
||
|
|
writeln!(
|
||
|
|
f,
|
||
|
|
"{}operator: {:?},",
|
||
|
|
ctx.child_context(false).indent(),
|
||
|
|
operator
|
||
|
|
)?;
|
||
|
|
write!(f, "{}left: ", ctx.child_context(false).indent())?;
|
||
|
|
left.pretty_print(&ctx.child_context(false), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}right: ", ctx.child_context(true).indent())?;
|
||
|
|
right.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Expr::Unary { operator, operand } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let operand_str =
|
||
|
|
pretty_print_with_config(operand.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "({:?} {})", operator, operand_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Unary {{", indent)?;
|
||
|
|
writeln!(
|
||
|
|
f,
|
||
|
|
"{}operator: {:?},",
|
||
|
|
ctx.child_context(false).indent(),
|
||
|
|
operator
|
||
|
|
)?;
|
||
|
|
write!(f, "{}operand: ", ctx.child_context(true).indent())?;
|
||
|
|
operand.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Expr::Grouping { expression } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let expr_str =
|
||
|
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "({})", expr_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Grouping {{", indent)?;
|
||
|
|
write!(f, "{}expression: ", ctx.child_context(true).indent())?;
|
||
|
|
expression.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Expr::Variable { name } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
write!(f, "{}", name)
|
||
|
|
} else {
|
||
|
|
write!(f, "{}Variable {{ name: {:?} }}", indent, name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyPrint for Stmt {
|
||
|
|
fn pretty_print(&self, ctx: &PrettyContext, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
|
if ctx.should_truncate() {
|
||
|
|
return write!(f, "{}...", ctx.indent());
|
||
|
|
}
|
||
|
|
|
||
|
|
let indent = ctx.indent();
|
||
|
|
|
||
|
|
match self {
|
||
|
|
Stmt::Expression { expression } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let expr_str =
|
||
|
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "{};", expr_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Expression {{", indent)?;
|
||
|
|
write!(f, "{}expression: ", ctx.child_context(true).indent())?;
|
||
|
|
expression.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Print { expression } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let expr_str =
|
||
|
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "print {};", expr_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Print {{", indent)?;
|
||
|
|
write!(f, "{}expression: ", ctx.child_context(true).indent())?;
|
||
|
|
expression.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Var { name, initializer } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
match initializer {
|
||
|
|
Some(init) => {
|
||
|
|
let init_str =
|
||
|
|
pretty_print_with_config(init.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "var {} = {};", name, init_str)
|
||
|
|
}
|
||
|
|
None => write!(f, "var {};", name),
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Var {{", indent)?;
|
||
|
|
writeln!(f, "{}name: {:?},", ctx.child_context(false).indent(), name)?;
|
||
|
|
match initializer {
|
||
|
|
Some(init) => {
|
||
|
|
write!(f, "{}initializer: Some(", ctx.child_context(true).indent())?;
|
||
|
|
init.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f, "),")?;
|
||
|
|
}
|
||
|
|
None => {
|
||
|
|
writeln!(f, "{}initializer: None,", ctx.child_context(true).indent())?
|
||
|
|
}
|
||
|
|
}
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Assign { name, value } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let value_str =
|
||
|
|
pretty_print_with_config(value.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "{} = {};", name, value_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Assign {{", indent)?;
|
||
|
|
writeln!(f, "{}name: {:?},", ctx.child_context(false).indent(), name)?;
|
||
|
|
write!(f, "{}value: ", ctx.child_context(true).indent())?;
|
||
|
|
value.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Return { expression } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let expr_str =
|
||
|
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "return {};", expr_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Return {{", indent)?;
|
||
|
|
write!(f, "{}expression: ", ctx.child_context(true).indent())?;
|
||
|
|
expression.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Block { statements } => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
write!(f, "{{ ... ({} statements) }}", statements.len())
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}Block {{", indent)?;
|
||
|
|
writeln!(f, "{}statements: [", ctx.child_context(false).indent())?;
|
||
|
|
for (i, stmt) in statements.iter().enumerate() {
|
||
|
|
let is_last = i == statements.len() - 1;
|
||
|
|
stmt.pretty_print(&ctx.child_context(false).child_context(is_last), f)?;
|
||
|
|
if !is_last {
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
} else {
|
||
|
|
writeln!(f)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
writeln!(f, "{}]", ctx.child_context(false).indent())?;
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::If {
|
||
|
|
condition,
|
||
|
|
then_branch,
|
||
|
|
elif_branch,
|
||
|
|
else_branch,
|
||
|
|
} => {
|
||
|
|
if ctx.config.compact {
|
||
|
|
let cond_str =
|
||
|
|
pretty_print_with_config(condition.as_ref(), &PrettyConfig::compact());
|
||
|
|
write!(f, "if {} {{ ... }}", cond_str)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}If {{", indent)?;
|
||
|
|
write!(f, "{}condition: ", ctx.child_context(false).indent())?;
|
||
|
|
condition.pretty_print(&ctx.child_context(false), f)?;
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
write!(f, "{}then_branch: ", ctx.child_context(false).indent())?;
|
||
|
|
then_branch.pretty_print(&ctx.child_context(false), f)?;
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
|
||
|
|
if !elif_branch.is_empty() {
|
||
|
|
writeln!(f, "{}elif_branches: [", ctx.child_context(false).indent())?;
|
||
|
|
for (i, (cond, branch)) in elif_branch.iter().enumerate() {
|
||
|
|
let is_last = i == elif_branch.len() - 1;
|
||
|
|
let child_ctx = ctx.child_context(false).child_context(is_last);
|
||
|
|
writeln!(f, "{}(", child_ctx.indent())?;
|
||
|
|
write!(f, "{}condition: ", child_ctx.child_context(false).indent())?;
|
||
|
|
cond.pretty_print(&child_ctx.child_context(false), f)?;
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
write!(f, "{}branch: ", child_ctx.child_context(true).indent())?;
|
||
|
|
branch.pretty_print(&child_ctx.child_context(true), f)?;
|
||
|
|
writeln!(f)?;
|
||
|
|
write!(f, "{})", child_ctx.indent())?;
|
||
|
|
if !is_last {
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
} else {
|
||
|
|
writeln!(f)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
writeln!(f, "{}],", ctx.child_context(false).indent())?;
|
||
|
|
}
|
||
|
|
|
||
|
|
match else_branch {
|
||
|
|
Some(else_stmt) => {
|
||
|
|
write!(f, "{}else_branch: Some(", ctx.child_context(true).indent())?;
|
||
|
|
else_stmt.pretty_print(&ctx.child_context(true), f)?;
|
||
|
|
writeln!(f, "),")?;
|
||
|
|
}
|
||
|
|
None => {
|
||
|
|
writeln!(f, "{}else_branch: None,", ctx.child_context(true).indent())?
|
||
|
|
}
|
||
|
|
}
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Stmt::Stmt { expression } => expression.pretty_print(ctx, f),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<T: AstNodeKind + fmt::Debug + fmt::Display + PrettyPrint> PrettyPrint for AstNode<T> {
|
||
|
|
fn pretty_print(&self, ctx: &PrettyContext, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
|
let indent = ctx.indent();
|
||
|
|
|
||
|
|
if ctx.config.compact {
|
||
|
|
self.node.pretty_print(ctx, f)
|
||
|
|
} else {
|
||
|
|
writeln!(f, "{}AstNode {{", indent)?;
|
||
|
|
|
||
|
|
if ctx.config.show_types {
|
||
|
|
writeln!(
|
||
|
|
f,
|
||
|
|
"{}kind: {:?},",
|
||
|
|
ctx.child_context(false).indent(),
|
||
|
|
self.node.kind()
|
||
|
|
)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
write!(
|
||
|
|
f,
|
||
|
|
"{}node: ",
|
||
|
|
ctx.child_context(!ctx.config.show_positions).indent()
|
||
|
|
)?;
|
||
|
|
self.node
|
||
|
|
.pretty_print(&ctx.child_context(!ctx.config.show_positions), f)?;
|
||
|
|
writeln!(f, ",")?;
|
||
|
|
|
||
|
|
if ctx.config.show_positions {
|
||
|
|
writeln!(
|
||
|
|
f,
|
||
|
|
"{}source_slice: {:?},",
|
||
|
|
ctx.child_context(true).indent(),
|
||
|
|
self.source_slice
|
||
|
|
)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
write!(f, "{}}}", indent)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extension trait for convenience methods
|
||
|
|
pub trait PrettyPrintExt {
|
||
|
|
fn pretty(&self) -> String;
|
||
|
|
fn pretty_compact(&self) -> String;
|
||
|
|
fn pretty_tree(&self) -> String;
|
||
|
|
fn pretty_with_config(&self, config: &PrettyConfig) -> String;
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyPrintExt for Expr {
|
||
|
|
fn pretty(&self) -> String {
|
||
|
|
pretty_print(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_compact(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::compact())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_tree(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::tree())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_with_config(&self, config: &PrettyConfig) -> String {
|
||
|
|
pretty_print_with_config(self, config)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl PrettyPrintExt for Stmt {
|
||
|
|
fn pretty(&self) -> String {
|
||
|
|
pretty_print(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_compact(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::compact())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_tree(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::tree())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_with_config(&self, config: &PrettyConfig) -> String {
|
||
|
|
pretty_print_with_config(self, config)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<T: AstNodeKind + fmt::Debug + fmt::Display + PrettyPrint> PrettyPrintExt for AstNode<T> {
|
||
|
|
fn pretty(&self) -> String {
|
||
|
|
pretty_print(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_compact(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::compact())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_tree(&self) -> String {
|
||
|
|
pretty_print_with_config(self, &PrettyConfig::tree())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn pretty_with_config(&self, config: &PrettyConfig) -> String {
|
||
|
|
pretty_print_with_config(self, config)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Convenience functions for quick pretty printing
|
||
|
|
pub fn pretty_expr(expr: &Expr) -> String {
|
||
|
|
pretty_print(expr)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn pretty_stmt(stmt: &Stmt) -> String {
|
||
|
|
pretty_print(stmt)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn pretty_expr_compact(expr: &Expr) -> String {
|
||
|
|
pretty_print_with_config(expr, &PrettyConfig::compact())
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn pretty_stmt_compact(stmt: &Stmt) -> String {
|
||
|
|
pretty_print_with_config(stmt, &PrettyConfig::compact())
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn pretty_tree<T: PrettyPrint>(item: &T) -> String {
|
||
|
|
pretty_print_with_config(item, &PrettyConfig::tree())
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
use crate::frontend::{
|
||
|
|
source_registry::{SourceId, SourcePosition, SourceSlice},
|
||
|
|
tokens::{LiteralValue, TokenType},
|
||
|
|
};
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_pretty_expr_literal() {
|
||
|
|
let expr = Expr::Literal {
|
||
|
|
value: LiteralValue::Number(42.0),
|
||
|
|
};
|
||
|
|
|
||
|
|
let compact = expr.pretty_compact();
|
||
|
|
assert!(compact.contains("42"));
|
||
|
|
|
||
|
|
let full = expr.pretty();
|
||
|
|
assert!(full.contains("Literal"));
|
||
|
|
assert!(full.contains("42"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_pretty_expr_binary() {
|
||
|
|
let expr = Expr::Binary {
|
||
|
|
left: Box::new(Expr::Literal {
|
||
|
|
value: LiteralValue::Number(1.0),
|
||
|
|
}),
|
||
|
|
operator: TokenType::Plus,
|
||
|
|
right: Box::new(Expr::Literal {
|
||
|
|
value: LiteralValue::Number(2.0),
|
||
|
|
}),
|
||
|
|
};
|
||
|
|
|
||
|
|
let compact = expr.pretty_compact();
|
||
|
|
assert!(compact.contains("1"));
|
||
|
|
assert!(compact.contains("2"));
|
||
|
|
}
|
||
|
|
}
|