From 7d2950d0bd0955e6c63f537fde282caf755c3e59 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Sat, 1 Aug 2026 17:25:21 +0800 Subject: [PATCH 1/2] fix(ast): correct tree indentation, render use statements, print source-syntax operators Fix three inverted tree-indent flags in the AST tree printer (Program, FnDecl, FnDef pushed !is_last into the child indentation context, contradicting the DisplayAsTree contract and the 19 other sites that push is_last). This broke vertical connector continuity in --emit ast dumps for nested subtrees; all three now push is_last. Render use statements in the AST dump. Program::fmt_tree previously iterated only elements and silently dropped use_stmts. A new leaf-style DisplayAsTree impl for UseStmt prints 'UseStmt ', and Program::fmt_tree now emits use_stmts before elements, marking only the final child overall as is_last. Remove the IR-vocabulary leak from AST Display: ArithBiOp and ComOp printed LLVM mnemonics (sdiv, sgt, ...) instead of TeaLang source syntax. All consumers of these impls are cosmetic -- the AST tree dump (IfStmt/WhileStmt/ReturnStmt condition headers) and ir::Error messages -- while IR emission uses the IR layer's own ArithBinOp/CmpPredicate Display impls in ir::stmt. They now print + - * / and == != > >= < <=. --- src/ast/display.rs | 36 ++++++++++++++++++++++-------------- src/ast/tree.rs | 36 +++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/ast/display.rs b/src/ast/display.rs index f3850fc..73d6b47 100644 --- a/src/ast/display.rs +++ b/src/ast/display.rs @@ -42,15 +42,19 @@ impl Display for TypeSpecifier { } } -/// Formats an arithmetic binary operator as its LLVM IR mnemonic -/// (e.g., `add`, `sub`, `mul`, `sdiv`). +/// Formats an arithmetic binary operator as its TeaLang source-level symbol +/// (`+`, `-`, `*`, `/`). +/// +/// The LLVM IR mnemonics (`add`, `sdiv`, …) are printed by the IR layer's +/// own `ArithBinOp` Display impl in `ir::stmt`; all consumers of this impl +/// (the AST tree dump and `ir::Error` messages) are cosmetic. impl Display for ArithBiOp { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self { - ArithBiOp::Add => write!(f, "add"), - ArithBiOp::Sub => write!(f, "sub"), - ArithBiOp::Mul => write!(f, "mul"), - ArithBiOp::Div => write!(f, "sdiv"), + ArithBiOp::Add => write!(f, "+"), + ArithBiOp::Sub => write!(f, "-"), + ArithBiOp::Mul => write!(f, "*"), + ArithBiOp::Div => write!(f, "/"), } } } @@ -76,17 +80,21 @@ impl Display for BoolBiOp { } } -/// Formats a comparison operator as its LLVM IR predicate mnemonic -/// (e.g., `eq`, `ne`, `sgt`, …). +/// Formats a comparison operator as its TeaLang source-level symbol +/// (`==`, `!=`, `>`, `>=`, `<`, `<=`). +/// +/// The LLVM IR predicate mnemonics (`eq`, `sgt`, …) are printed by the IR +/// layer's own `CmpPredicate` Display impl in `ir::stmt`; all consumers of +/// this impl (the AST tree dump and `ir::Error` messages) are cosmetic. impl Display for ComOp { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self { - ComOp::Eq => write!(f, "eq"), - ComOp::Ne => write!(f, "ne"), - ComOp::Gt => write!(f, "sgt"), - ComOp::Ge => write!(f, "sge"), - ComOp::Lt => write!(f, "slt"), - ComOp::Le => write!(f, "sle"), + ComOp::Eq => write!(f, "=="), + ComOp::Ne => write!(f, "!="), + ComOp::Gt => write!(f, ">"), + ComOp::Ge => write!(f, ">="), + ComOp::Lt => write!(f, "<"), + ComOp::Le => write!(f, "<="), } } } diff --git a/src/ast/tree.rs b/src/ast/tree.rs index a0171cd..ba40112 100644 --- a/src/ast/tree.rs +++ b/src/ast/tree.rs @@ -67,8 +67,8 @@ fn tree_indent(indent_levels: &[bool], is_last: bool) -> String { s } -/// Formats the root `Program` node, listing every top-level element as a -/// child in the tree. +/// Formats the root `Program` node, listing every `use` statement followed +/// by every top-level element as children in the tree. impl DisplayAsTree for Program { fn fmt_tree( &self, @@ -79,15 +79,37 @@ impl DisplayAsTree for Program { writeln!(f, "{}Program", tree_indent(indent_levels, is_last))?; // Build the indentation context for children. let mut new_indent = indent_levels.to_vec(); - new_indent.push(!is_last); - let last_index = self.elements.len().saturating_sub(1); + new_indent.push(is_last); + // `use` statements are printed before all other top-level elements; + // only the very last child overall is marked as `is_last`. + let last_index = (self.use_stmts.len() + self.elements.len()).saturating_sub(1); + for (i, use_stmt) in self.use_stmts.iter().enumerate() { + use_stmt.fmt_tree(f, &new_indent, i == last_index)?; + } for (i, elem) in self.elements.iter().enumerate() { - elem.fmt_tree(f, &new_indent, i == last_index)?; + elem.fmt_tree(f, &new_indent, self.use_stmts.len() + i == last_index)?; } Ok(()) } } +/// Prints a leaf `UseStmt ` node. +impl DisplayAsTree for UseStmt { + fn fmt_tree( + &self, + f: &mut Formatter<'_>, + indent_levels: &[bool], + is_last: bool, + ) -> Result<(), Error> { + writeln!( + f, + "{}UseStmt {}", + tree_indent(indent_levels, is_last), + self.module_name + ) + } +} + /// Delegates formatting to the concrete element variant. impl DisplayAsTree for ProgramElement { fn fmt_tree( @@ -207,7 +229,7 @@ impl DisplayAsTree for FnDecl { if let Some(params) = &self.param_decl { // Extend the indentation context for the parameter subtree. let mut new_indent = indent_levels.to_vec(); - new_indent.push(!is_last); + new_indent.push(is_last); writeln!(f, "{}Params:", tree_indent(&new_indent, false))?; params.decls.fmt_tree(f, &new_indent, true)?; } @@ -242,7 +264,7 @@ impl DisplayAsTree for FnDef { self.fn_decl.identifier )?; let mut new_indent = indent_levels.to_vec(); - new_indent.push(!is_last); + new_indent.push(is_last); self.stmts.fmt_tree(f, &new_indent, true) } } From 116dddb3f8dd29b4629b4c62f497703e7dc96986 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Sat, 1 Aug 2026 18:36:25 +0800 Subject: [PATCH 2/2] docs(ast): discipline comments per comment spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - removed 10 narrations (§5.1): per-branch connector explainers in tree_indent, restatements of adjacent code or of the enclosing doc comment in Program::fmt_tree, VarDecl, FnDecl, VarDef, FnCall Display - rewrote 1 restatement into the last_index derivation in Program::fmt_tree (§2.2) - trimmed 1 filler clause from the tree.rs module doc (§8.2) - no divider banners, non-spec annotation tags, or commented-out code present in these files --- src/ast/display.rs | 1 - src/ast/tree.rs | 19 +++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/ast/display.rs b/src/ast/display.rs index 73d6b47..1c8f5f8 100644 --- a/src/ast/display.rs +++ b/src/ast/display.rs @@ -245,7 +245,6 @@ impl Display for MemberExpr { /// for qualified calls. impl Display for FnCall { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { - // Format all argument values as a comma-separated string. let args: Vec = self.vals.iter().map(|v| format!("{}", v)).collect(); if let Some(module) = &self.module_prefix { write!(f, "{}::{}({})", module, self.name, args.join(", ")) diff --git a/src/ast/tree.rs b/src/ast/tree.rs index ba40112..0c1f925 100644 --- a/src/ast/tree.rs +++ b/src/ast/tree.rs @@ -1,9 +1,9 @@ //! Tree pretty-printer for the AST. //! //! This module defines the [`DisplayAsTree`] trait and provides implementations -//! for every AST node type. When a node is printed with this trait it -//! produces an indented, Unicode-box-drawing tree that mirrors the logical -//! structure of the AST, making it easy to read program structure at a glance. +//! for every AST node type. A node printed with this trait produces an +//! indented, Unicode-box-drawing tree that mirrors the logical structure of +//! the AST. //! //! The indentation state is passed down through the `indent_levels` slice. //! Each element records whether the corresponding ancestor was the *last* @@ -50,18 +50,14 @@ fn tree_indent(indent_levels: &[bool], is_last: bool) -> String { let mut s = String::new(); for &last in indent_levels.iter() { if last { - // Ancestor was the last child — no vertical connector needed. s.push_str(" "); } else { - // More siblings exist at this ancestor level — draw vertical bar. s.push_str("│ "); } } if is_last { - // This node is the last child — use a corner connector. s.push_str("└─"); } else { - // More siblings follow — use a tee connector. s.push_str("├─"); } s @@ -77,11 +73,10 @@ impl DisplayAsTree for Program { is_last: bool, ) -> Result<(), Error> { writeln!(f, "{}Program", tree_indent(indent_levels, is_last))?; - // Build the indentation context for children. let mut new_indent = indent_levels.to_vec(); new_indent.push(is_last); - // `use` statements are printed before all other top-level elements; - // only the very last child overall is marked as `is_last`. + // `last_index` indexes into the concatenation of `use_stmts` and + // `elements`, so only the final child overall is marked `is_last`. let last_index = (self.use_stmts.len() + self.elements.len()).saturating_sub(1); for (i, use_stmt) in self.use_stmts.iter().enumerate() { use_stmt.fmt_tree(f, &new_indent, i == last_index)?; @@ -166,7 +161,6 @@ impl DisplayAsTree for VarDecl { indent_levels: &[bool], is_last: bool, ) -> Result<(), Error> { - // Render the type specifier, falling back to "unknown" if absent. let type_str = self .type_specifier .as_ref() @@ -227,7 +221,6 @@ impl DisplayAsTree for FnDecl { self.identifier )?; if let Some(params) = &self.param_decl { - // Extend the indentation context for the parameter subtree. let mut new_indent = indent_levels.to_vec(); new_indent.push(is_last); writeln!(f, "{}Params:", tree_indent(&new_indent, false))?; @@ -283,11 +276,9 @@ impl DisplayAsTree for VarDef { VarDefInner::Scalar(s) => writeln!(f, "{}{} = {}", prefix, self.identifier, s.val), VarDefInner::Array(a) => match &a.initializer { ArrayInitializer::ExplicitList(vals) => { - // Print the debug representation of all explicit values. writeln!(f, "{}{} = {:?}", prefix, self.identifier, vals) } ArrayInitializer::Fill { val, count } => { - // Print the fill syntax: `name = [val; count]`. writeln!(f, "{}{} = [{}; {}]", prefix, self.identifier, val, count) } },