Skip to main content

oxilean_parse/ast_impl/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5#[allow(unused_imports)]
6use crate::ast::{SimpleNodeKindExt, TreeNodeExt};
7
8use super::types::{AttributeKind, TreePathExt, TreeStats, TreeStatsExt2};
9
10/// Parse an attribute name string into an `AttributeKind`.
11#[allow(dead_code)]
12pub fn parse_attribute_kind(s: &str) -> AttributeKind {
13    match s {
14        "simp" => AttributeKind::Simp,
15        "ext" => AttributeKind::Ext,
16        "instance" => AttributeKind::Instance,
17        "reducible" => AttributeKind::Reducible,
18        "irreducible" => AttributeKind::Irreducible,
19        "inline" => AttributeKind::Inline,
20        "noinline" => AttributeKind::NoInline,
21        "specialize" => AttributeKind::SpecializeAttr,
22        other => AttributeKind::Custom(other.to_string()),
23    }
24}
25/// A reference to a tactic used at the expression level (e.g., `by exact rfl`).
26pub type TacticRef = String;
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use crate::ast_impl::*;
31    use crate::tokens::{Span, StringPart};
32    fn mk_span() -> Span {
33        Span::new(0, 1, 1, 1)
34    }
35    fn mk_located<T>(value: T) -> Located<T> {
36        Located::new(value, mk_span())
37    }
38    fn mk_var(name: &str) -> Located<SurfaceExpr> {
39        mk_located(SurfaceExpr::Var(name.to_string()))
40    }
41    fn mk_nat(n: u64) -> Located<SurfaceExpr> {
42        mk_located(SurfaceExpr::Lit(Literal::Nat(n)))
43    }
44    #[test]
45    fn test_surface_expr_var() {
46        let expr = SurfaceExpr::Var("x".to_string());
47        assert!(matches!(expr, SurfaceExpr::Var(_)));
48    }
49    #[test]
50    fn test_surface_expr_lit() {
51        let expr = SurfaceExpr::Lit(Literal::Nat(42));
52        assert_eq!(expr, SurfaceExpr::Lit(Literal::Nat(42)));
53    }
54    #[test]
55    fn test_located() {
56        let loc = Located::new(SurfaceExpr::Var("x".to_string()), mk_span());
57        assert_eq!(loc.value, SurfaceExpr::Var("x".to_string()));
58    }
59    #[test]
60    fn test_binder() {
61        let binder = Binder {
62            name: "x".to_string(),
63            ty: None,
64            info: BinderKind::Default,
65        };
66        assert_eq!(binder.name, "x");
67        assert!(binder.ty.is_none());
68    }
69    #[test]
70    fn test_sort_kind_display() {
71        assert_eq!(format!("{}", SortKind::Type), "Type");
72        assert_eq!(format!("{}", SortKind::Prop), "Prop");
73        assert_eq!(format!("{}", SortKind::TypeU("u".to_string())), "Type u");
74    }
75    #[test]
76    fn test_literal_display() {
77        assert_eq!(format!("{}", Literal::Nat(42)), "42");
78        assert_eq!(
79            format!("{}", Literal::String("hello".to_string())),
80            "\"hello\""
81        );
82        assert_eq!(format!("{}", Literal::Char('x')), "'x'");
83    }
84    #[test]
85    fn test_field_decl() {
86        let field = FieldDecl {
87            name: "x".to_string(),
88            ty: Located::new(SurfaceExpr::Var("Nat".to_string()), mk_span()),
89            default: None,
90        };
91        assert_eq!(field.name, "x");
92        assert!(field.default.is_none());
93    }
94    #[test]
95    fn test_new_expr_variants() {
96        let _have = SurfaceExpr::Have(
97            "h".to_string(),
98            Box::new(Located::new(SurfaceExpr::Var("T".to_string()), mk_span())),
99            Box::new(Located::new(SurfaceExpr::Hole, mk_span())),
100            Box::new(Located::new(
101                SurfaceExpr::Var("body".to_string()),
102                mk_span(),
103            )),
104        );
105        let _ctor = SurfaceExpr::AnonymousCtor(vec![
106            Located::new(SurfaceExpr::Lit(Literal::Nat(1)), mk_span()),
107            Located::new(SurfaceExpr::Lit(Literal::Nat(2)), mk_span()),
108        ]);
109        let _list = SurfaceExpr::ListLit(vec![Located::new(
110            SurfaceExpr::Lit(Literal::Nat(1)),
111            mk_span(),
112        )]);
113        let _tuple = SurfaceExpr::Tuple(vec![
114            Located::new(SurfaceExpr::Lit(Literal::Nat(1)), mk_span()),
115            Located::new(SurfaceExpr::Lit(Literal::Nat(2)), mk_span()),
116        ]);
117    }
118    #[test]
119    fn test_new_decl_variants() {
120        let span = mk_span();
121        let _structure = Decl::Structure {
122            name: "Point".to_string(),
123            univ_params: vec![],
124            extends: vec![],
125            fields: vec![FieldDecl {
126                name: "x".to_string(),
127                ty: Located::new(SurfaceExpr::Var("Nat".to_string()), span.clone()),
128                default: None,
129            }],
130        };
131        let _class = Decl::ClassDecl {
132            name: "Monad".to_string(),
133            univ_params: vec!["u".to_string()],
134            extends: vec![],
135            fields: vec![],
136        };
137        let _var = Decl::Variable {
138            binders: vec![Binder {
139                name: "n".to_string(),
140                ty: Some(Box::new(Located::new(
141                    SurfaceExpr::Var("Nat".to_string()),
142                    span.clone(),
143                ))),
144                info: BinderKind::Default,
145            }],
146        };
147        let _check = Decl::HashCmd {
148            cmd: "check".to_string(),
149            arg: Located::new(SurfaceExpr::Var("Nat".to_string()), span),
150        };
151    }
152    #[test]
153    fn test_mutual_decl() {
154        let span = mk_span();
155        let def1 = mk_located(Decl::Definition {
156            name: "even".to_string(),
157            univ_params: vec![],
158            ty: Some(mk_var("Nat_to_Bool")),
159            val: mk_var("even_body"),
160            where_clauses: vec![],
161            attrs: vec![],
162        });
163        let def2 = mk_located(Decl::Definition {
164            name: "odd".to_string(),
165            univ_params: vec![],
166            ty: Some(Located::new(
167                SurfaceExpr::Var("Nat_to_Bool".to_string()),
168                span,
169            )),
170            val: mk_var("odd_body"),
171            where_clauses: vec![],
172            attrs: vec![],
173        });
174        let mutual = Decl::Mutual {
175            decls: vec![def1, def2],
176        };
177        assert!(mutual.is_mutual());
178        match &mutual {
179            Decl::Mutual { decls } => assert_eq!(decls.len(), 2),
180            _ => panic!("expected Mutual"),
181        }
182    }
183    #[test]
184    fn test_derive_decl() {
185        let derive = Decl::Derive {
186            instances: vec!["DecidableEq".to_string(), "Repr".to_string()],
187            type_name: "Color".to_string(),
188        };
189        match &derive {
190            Decl::Derive {
191                instances,
192                type_name,
193            } => {
194                assert_eq!(instances.len(), 2);
195                assert_eq!(type_name, "Color");
196            }
197            _ => panic!("expected Derive"),
198        }
199    }
200    #[test]
201    fn test_notation_decl() {
202        let notation = Decl::NotationDecl {
203            kind: AstNotationKind::Infixl,
204            prec: Some(65),
205            name: "+".to_string(),
206            notation: "HAdd.hAdd".to_string(),
207        };
208        match &notation {
209            Decl::NotationDecl {
210                kind,
211                prec,
212                name,
213                notation,
214            } => {
215                assert_eq!(*kind, AstNotationKind::Infixl);
216                assert_eq!(*prec, Some(65));
217                assert_eq!(name, "+");
218                assert_eq!(notation, "HAdd.hAdd");
219            }
220            _ => panic!("expected NotationDecl"),
221        }
222    }
223    #[test]
224    fn test_universe_decl() {
225        let univ = Decl::Universe {
226            names: vec!["u".to_string(), "v".to_string()],
227        };
228        match &univ {
229            Decl::Universe { names } => {
230                assert_eq!(names.len(), 2);
231                assert_eq!(names[0], "u");
232                assert_eq!(names[1], "v");
233            }
234            _ => panic!("expected Universe"),
235        }
236    }
237    #[test]
238    fn test_where_clause() {
239        let wc = WhereClause {
240            name: "helper".to_string(),
241            params: vec![Binder {
242                name: "x".to_string(),
243                ty: Some(Box::new(mk_var("Nat"))),
244                info: BinderKind::Default,
245            }],
246            ty: Some(mk_var("Nat")),
247            val: mk_nat(42),
248        };
249        assert_eq!(wc.name, "helper");
250        assert_eq!(wc.params.len(), 1);
251        assert!(wc.ty.is_some());
252    }
253    #[test]
254    fn test_definition_with_where_clauses() {
255        let def = Decl::Definition {
256            name: "foo".to_string(),
257            univ_params: vec![],
258            ty: Some(mk_var("Nat")),
259            val: mk_var("bar_result"),
260            where_clauses: vec![WhereClause {
261                name: "bar".to_string(),
262                params: vec![],
263                ty: None,
264                val: mk_nat(10),
265            }],
266            attrs: vec![AttributeKind::Simp],
267        };
268        match &def {
269            Decl::Definition {
270                where_clauses,
271                attrs,
272                ..
273            } => {
274                assert_eq!(where_clauses.len(), 1);
275                assert_eq!(where_clauses[0].name, "bar");
276                assert_eq!(attrs.len(), 1);
277                assert_eq!(attrs[0], AttributeKind::Simp);
278            }
279            _ => panic!("expected Definition"),
280        }
281    }
282    #[test]
283    fn test_theorem_with_attrs() {
284        let thm = Decl::Theorem {
285            name: "add_comm".to_string(),
286            univ_params: vec![],
287            ty: mk_var("Prop_statement"),
288            proof: mk_var("proof_term"),
289            where_clauses: vec![],
290            attrs: vec![AttributeKind::Simp, AttributeKind::Ext],
291        };
292        let attrs = thm.typed_attrs();
293        assert_eq!(attrs.len(), 2);
294        assert_eq!(attrs[0], AttributeKind::Simp);
295        assert_eq!(attrs[1], AttributeKind::Ext);
296    }
297    #[test]
298    fn test_axiom_with_attrs() {
299        let ax = Decl::Axiom {
300            name: "propext".to_string(),
301            univ_params: vec![],
302            ty: mk_var("Prop_ext_type"),
303            attrs: vec![AttributeKind::Reducible],
304        };
305        let attrs = ax.typed_attrs();
306        assert_eq!(attrs.len(), 1);
307        assert_eq!(attrs[0], AttributeKind::Reducible);
308    }
309    #[test]
310    fn test_attribute_kind_display() {
311        assert_eq!(format!("{}", AttributeKind::Simp), "simp");
312        assert_eq!(format!("{}", AttributeKind::Ext), "ext");
313        assert_eq!(format!("{}", AttributeKind::Instance), "instance");
314        assert_eq!(format!("{}", AttributeKind::Reducible), "reducible");
315        assert_eq!(format!("{}", AttributeKind::Irreducible), "irreducible");
316        assert_eq!(format!("{}", AttributeKind::Inline), "inline");
317        assert_eq!(format!("{}", AttributeKind::NoInline), "noinline");
318        assert_eq!(format!("{}", AttributeKind::SpecializeAttr), "specialize");
319        assert_eq!(
320            format!("{}", AttributeKind::Custom("my_attr".to_string())),
321            "my_attr"
322        );
323    }
324    #[test]
325    fn test_attribute_kind_name() {
326        assert_eq!(AttributeKind::Simp.name(), "simp");
327        assert_eq!(AttributeKind::Custom("foo".to_string()).name(), "foo");
328    }
329    #[test]
330    fn test_attribute_kind_is_custom() {
331        assert!(!AttributeKind::Simp.is_custom());
332        assert!(AttributeKind::Custom("foo".to_string()).is_custom());
333    }
334    #[test]
335    fn test_parse_attribute_kind() {
336        assert_eq!(parse_attribute_kind("simp"), AttributeKind::Simp);
337        assert_eq!(parse_attribute_kind("ext"), AttributeKind::Ext);
338        assert_eq!(parse_attribute_kind("inline"), AttributeKind::Inline);
339        assert_eq!(
340            parse_attribute_kind("my_custom"),
341            AttributeKind::Custom("my_custom".to_string())
342        );
343    }
344    #[test]
345    fn test_return_expr() {
346        let ret = SurfaceExpr::Return(Box::new(mk_nat(42)));
347        match &ret {
348            SurfaceExpr::Return(inner) => {
349                assert_eq!(inner.value, SurfaceExpr::Lit(Literal::Nat(42)));
350            }
351            _ => panic!("expected Return"),
352        }
353        let display = format!("{}", ret);
354        assert!(display.contains("return"));
355    }
356    #[test]
357    fn test_string_interp_expr() {
358        let interp = SurfaceExpr::StringInterp(vec![
359            StringPart::Literal("hello ".to_string()),
360            StringPart::Interpolation(vec![]),
361        ]);
362        match &interp {
363            SurfaceExpr::StringInterp(parts) => {
364                assert_eq!(parts.len(), 2);
365            }
366            _ => panic!("expected StringInterp"),
367        }
368    }
369    #[test]
370    fn test_range_expr_full() {
371        let range = SurfaceExpr::Range(Some(Box::new(mk_nat(1))), Some(Box::new(mk_nat(10))));
372        let display = format!("{}", range);
373        assert!(display.contains(".."));
374    }
375    #[test]
376    fn test_range_expr_open_start() {
377        let range = SurfaceExpr::Range(None, Some(Box::new(mk_nat(10))));
378        let display = format!("{}", range);
379        assert!(display.starts_with(".."));
380    }
381    #[test]
382    fn test_range_expr_open_end() {
383        let range = SurfaceExpr::Range(Some(Box::new(mk_nat(1))), None);
384        let display = format!("{}", range);
385        assert!(display.contains(".."));
386    }
387    #[test]
388    fn test_by_tactic_expr() {
389        let by_tac = SurfaceExpr::ByTactic(vec![
390            mk_located("exact".to_string()),
391            mk_located("rfl".to_string()),
392        ]);
393        match &by_tac {
394            SurfaceExpr::ByTactic(tactics) => {
395                assert_eq!(tactics.len(), 2);
396                assert_eq!(tactics[0].value, "exact");
397                assert_eq!(tactics[1].value, "rfl");
398            }
399            _ => panic!("expected ByTactic"),
400        }
401        let display = format!("{}", by_tac);
402        assert!(display.contains("by"));
403    }
404    #[test]
405    fn test_calc_expr() {
406        let step = CalcStep {
407            lhs: mk_var("a"),
408            rel: "=".to_string(),
409            rhs: mk_var("b"),
410            proof: mk_var("proof_ab"),
411        };
412        let calc = SurfaceExpr::Calc(vec![step]);
413        match &calc {
414            SurfaceExpr::Calc(steps) => {
415                assert_eq!(steps.len(), 1);
416                assert_eq!(steps[0].rel, "=");
417            }
418            _ => panic!("expected Calc"),
419        }
420        let display = format!("{}", calc);
421        assert!(display.contains("calc"));
422    }
423    #[test]
424    fn test_calc_step_constructor() {
425        let step = CalcStep::new(mk_var("x"), "<".to_string(), mk_var("y"), mk_var("proof"));
426        assert_eq!(step.rel, "<");
427    }
428    #[test]
429    fn test_where_clause_constructor() {
430        let wc = WhereClause::new("aux".to_string(), vec![], None, mk_nat(0));
431        assert_eq!(wc.name, "aux");
432        assert!(wc.params.is_empty());
433        assert!(wc.ty.is_none());
434    }
435    #[test]
436    fn test_surface_expr_helpers() {
437        let v = SurfaceExpr::var("x");
438        assert!(v.is_var());
439        assert_eq!(v.as_var(), Some("x"));
440        let n = SurfaceExpr::nat(42);
441        assert!(!n.is_var());
442        let s = SurfaceExpr::string("hello");
443        assert!(matches!(s, SurfaceExpr::Lit(Literal::String(_))));
444        #[allow(clippy::approx_constant)]
445        let f = SurfaceExpr::float(3.14);
446        assert!(matches!(f, SurfaceExpr::Lit(Literal::Float(_))));
447        let h = SurfaceExpr::Hole;
448        assert!(h.is_hole());
449    }
450    #[test]
451    fn test_located_map() {
452        let loc = Located::new(42, mk_span());
453        let mapped = loc.map(|x| x * 2);
454        assert_eq!(mapped.value, 84);
455    }
456    #[test]
457    fn test_located_display() {
458        let loc = Located::new(SurfaceExpr::Var("x".to_string()), mk_span());
459        let display = format!("{}", loc);
460        assert_eq!(display, "x");
461    }
462    #[test]
463    fn test_decl_name() {
464        let def = Decl::Definition {
465            name: "foo".to_string(),
466            univ_params: vec![],
467            ty: None,
468            val: mk_var("bar"),
469            where_clauses: vec![],
470            attrs: vec![],
471        };
472        assert_eq!(def.name(), Some("foo"));
473        let import = Decl::Import {
474            path: vec!["M".to_string()],
475        };
476        assert_eq!(import.name(), None);
477        let derive = Decl::Derive {
478            instances: vec!["Repr".to_string()],
479            type_name: "MyType".to_string(),
480        };
481        assert_eq!(derive.name(), Some("MyType"));
482    }
483    #[test]
484    fn test_ast_notation_kind_display() {
485        assert_eq!(format!("{}", AstNotationKind::Prefix), "prefix");
486        assert_eq!(format!("{}", AstNotationKind::Postfix), "postfix");
487        assert_eq!(format!("{}", AstNotationKind::Infixl), "infixl");
488        assert_eq!(format!("{}", AstNotationKind::Infixr), "infixr");
489        assert_eq!(format!("{}", AstNotationKind::Notation), "notation");
490    }
491    #[test]
492    fn test_binder_kind_display() {
493        assert_eq!(format!("{}", BinderKind::Default), "explicit");
494        assert_eq!(format!("{}", BinderKind::Implicit), "implicit");
495        assert_eq!(format!("{}", BinderKind::Instance), "instance");
496        assert_eq!(format!("{}", BinderKind::StrictImplicit), "strict_implicit");
497    }
498    #[test]
499    fn test_where_clause_display() {
500        let wc = WhereClause {
501            name: "helper".to_string(),
502            params: vec![Binder {
503                name: "n".to_string(),
504                ty: Some(Box::new(mk_var("Nat"))),
505                info: BinderKind::Default,
506            }],
507            ty: Some(mk_var("Nat")),
508            val: mk_nat(0),
509        };
510        let display = format!("{}", wc);
511        assert!(display.contains("helper"));
512        assert!(display.contains("(n : ...)"));
513    }
514    #[test]
515    fn test_literal_float_display() {
516        #[allow(clippy::approx_constant)]
517        let val = 3.14;
518        assert_eq!(format!("{}", Literal::Float(val)), "3.14");
519    }
520    #[test]
521    fn test_do_action_return() {
522        let action = DoAction::Return(mk_nat(42));
523        match &action {
524            DoAction::Return(expr) => {
525                assert_eq!(expr.value, SurfaceExpr::Lit(Literal::Nat(42)));
526            }
527            _ => panic!("expected DoAction::Return"),
528        }
529    }
530    #[test]
531    fn test_surface_expr_display_comprehensive() {
532        assert_eq!(format!("{}", SurfaceExpr::Sort(SortKind::Type)), "Type");
533        assert_eq!(format!("{}", SurfaceExpr::Hole), "_");
534        let proj = SurfaceExpr::Proj(Box::new(mk_var("p")), "x".to_string());
535        assert_eq!(format!("{}", proj), "p.x");
536        let if_expr = SurfaceExpr::If(
537            Box::new(mk_var("cond")),
538            Box::new(mk_var("t")),
539            Box::new(mk_var("f")),
540        );
541        let display = format!("{}", if_expr);
542        assert!(display.contains("if"));
543        assert!(display.contains("then"));
544        assert!(display.contains("else"));
545    }
546    #[test]
547    fn test_all_notation_kinds_are_distinct() {
548        let kinds = [
549            AstNotationKind::Prefix,
550            AstNotationKind::Postfix,
551            AstNotationKind::Infixl,
552            AstNotationKind::Infixr,
553            AstNotationKind::Notation,
554        ];
555        for i in 0..kinds.len() {
556            for j in (i + 1)..kinds.len() {
557                assert_ne!(kinds[i], kinds[j]);
558            }
559        }
560    }
561    #[test]
562    fn test_multiple_calc_steps() {
563        let steps = vec![
564            CalcStep::new(mk_var("a"), "=".to_string(), mk_var("b"), mk_var("p1")),
565            CalcStep::new(mk_var("_"), "<".to_string(), mk_var("c"), mk_var("p2")),
566            CalcStep::new(mk_var("_"), "<=".to_string(), mk_var("d"), mk_var("p3")),
567        ];
568        let calc = SurfaceExpr::Calc(steps);
569        match &calc {
570            SurfaceExpr::Calc(steps) => assert_eq!(steps.len(), 3),
571            _ => panic!("expected Calc"),
572        }
573    }
574}
575/// A mutable transformation pass over tree nodes.
576#[allow(dead_code)]
577pub trait TreeTransformExt {
578    /// Transform a tree node, returning a new (possibly different) node.
579    fn transform(&mut self, node: TreeNodeExt) -> TreeNodeExt;
580}
581/// Applies a tree transform to a root node.
582#[allow(dead_code)]
583#[allow(missing_docs)]
584pub fn apply_transform_ext<T: TreeTransformExt>(root: TreeNodeExt, t: &mut T) -> TreeNodeExt {
585    t.transform(root)
586}
587/// A tree equality checker (structural equality).
588#[allow(dead_code)]
589#[allow(missing_docs)]
590pub fn trees_equal_ext(a: &TreeNodeExt, b: &TreeNodeExt) -> bool {
591    if a.kind != b.kind {
592        return false;
593    }
594    if a.label != b.label {
595        return false;
596    }
597    if a.children.len() != b.children.len() {
598        return false;
599    }
600    a.children
601        .iter()
602        .zip(b.children.iter())
603        .all(|(ac, bc)| trees_equal_ext(ac, bc))
604}
605/// Follow a path in a tree, returning the node at that path.
606#[allow(dead_code)]
607#[allow(missing_docs)]
608pub fn follow_path_ext<'a>(root: &'a TreeNodeExt, path: &TreePathExt) -> Option<&'a TreeNodeExt> {
609    let mut node = root;
610    for &idx in &path.0 {
611        node = node.children.get(idx)?;
612    }
613    Some(node)
614}
615/// Counts occurrences of a given label in a tree.
616#[allow(dead_code)]
617#[allow(missing_docs)]
618pub fn count_label_ext(node: &TreeNodeExt, label: &str) -> usize {
619    let mut count = if node.label == label { 1 } else { 0 };
620    for child in &node.children {
621        count += count_label_ext(child, label);
622    }
623    count
624}
625#[cfg(test)]
626mod ast_impl_ext_tests {
627    use super::*;
628    use crate::ast_impl::*;
629    use crate::tokens::{Span, StringPart};
630    #[test]
631    fn test_identity_transform() {
632        let tree = TreeNodeExt::lam("x", TreeNodeExt::leaf("x"));
633        let mut t = IdentityTransformExt;
634        let result = t.transform(tree.clone());
635        assert!(trees_equal_ext(&result, &tree));
636    }
637    #[test]
638    fn test_rename_transform() {
639        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
640        let mut t = RenameTransformExt::new("x", "z");
641        let result = t.transform(tree);
642        assert_eq!(result.children[0].label, "z");
643        assert_eq!(result.children[1].label, "y");
644        assert_eq!(t.count, 1);
645    }
646    #[test]
647    fn test_trees_equal() {
648        let t1 = TreeNodeExt::leaf("x");
649        let t2 = TreeNodeExt::leaf("x");
650        let t3 = TreeNodeExt::leaf("y");
651        assert!(trees_equal_ext(&t1, &t2));
652        assert!(!trees_equal_ext(&t1, &t3));
653    }
654    #[test]
655    fn test_tree_path() {
656        let path = TreePathExt::root().child(0).child(1);
657        assert_eq!(path.depth(), 2);
658        assert_eq!(path.0, vec![0, 1]);
659    }
660    #[test]
661    fn test_follow_path() {
662        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
663        let path = TreePathExt::root().child(0);
664        let node = follow_path_ext(&tree, &path).expect("test operation should succeed");
665        assert_eq!(node.label, "f");
666    }
667    #[test]
668    fn test_count_label() {
669        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("x"));
670        assert_eq!(count_label_ext(&tree, "x"), 2);
671        assert_eq!(count_label_ext(&tree, "y"), 0);
672    }
673    #[test]
674    fn test_zipper_down_up() {
675        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
676        let zipper = TreeZipperExt::new(tree);
677        let z2 = zipper.down(0).expect("test operation should succeed");
678        assert_eq!(z2.focus.label, "f");
679        let (z3, child) = z2.up().expect("test operation should succeed");
680        assert_eq!(child.label, "f");
681        assert_eq!(z3.focus.kind, SimpleNodeKindExt::App);
682    }
683}
684pub fn collect_stats(node: &TreeNodeExt, depth: usize, stats: &mut TreeStats) {
685    stats.nodes += 1;
686    stats.total_size += 1;
687    if node.children.is_empty() {
688        stats.leaves += 1;
689    }
690    if depth > stats.max_depth {
691        stats.max_depth = depth;
692    }
693    for child in &node.children {
694        collect_stats(child, depth + 1, stats);
695    }
696}
697/// A tree shape fingerprint for structural equality testing.
698#[allow(dead_code)]
699#[allow(missing_docs)]
700pub fn shape_fingerprint(node: &TreeNodeExt) -> String {
701    if node.children.is_empty() {
702        return "L".to_string();
703    }
704    let child_shapes: Vec<String> = node.children.iter().map(shape_fingerprint).collect();
705    format!("({})", child_shapes.join(","))
706}
707#[cfg(test)]
708mod ast_impl_ext2_tests {
709    use super::*;
710    use crate::ast_impl::*;
711    use crate::tokens::{Span, StringPart};
712    #[test]
713    fn test_transform_memo() {
714        let mut memo = TransformMemo::new();
715        let node = TreeNodeExt::leaf("x");
716        memo.store(42, node.clone());
717        assert_eq!(memo.len(), 1);
718        let cached = memo.get(42).expect("key should exist");
719        assert_eq!(cached.label, "x");
720    }
721    #[test]
722    fn test_tree_stats() {
723        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
724        let stats = TreeStats::from_tree(&tree);
725        assert_eq!(stats.nodes, 3);
726        assert_eq!(stats.leaves, 2);
727        assert_eq!(stats.max_depth, 1);
728    }
729    #[test]
730    fn test_shape_fingerprint() {
731        let leaf = TreeNodeExt::leaf("x");
732        assert_eq!(shape_fingerprint(&leaf), "L");
733        let app = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
734        assert_eq!(shape_fingerprint(&app), "(L,L)");
735    }
736    #[test]
737    fn test_rename_transform_ext() {
738        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
739        let mut t = RenameTransformExt::new("x", "z");
740        let result = apply_transform_ext(tree, &mut t);
741        assert_eq!(result.children[0].label, "z");
742        assert_eq!(t.count, 1);
743    }
744}
745/// An accumulator for collecting all labels in a tree.
746#[allow(dead_code)]
747#[allow(missing_docs)]
748pub fn collect_all_labels(node: &TreeNodeExt) -> Vec<String> {
749    let mut labels = vec![node.label.clone()];
750    for child in &node.children {
751        labels.extend(collect_all_labels(child));
752    }
753    labels
754}
755/// Returns the leaves of a tree in pre-order.
756#[allow(dead_code)]
757#[allow(missing_docs)]
758pub fn collect_leaves(node: &TreeNodeExt) -> Vec<&TreeNodeExt> {
759    if node.children.is_empty() {
760        return vec![node];
761    }
762    let mut leaves = Vec::new();
763    for child in &node.children {
764        leaves.extend(collect_leaves(child));
765    }
766    leaves
767}
768/// A deep clone of a tree (already derived via Clone, so this is just an alias).
769#[allow(dead_code)]
770#[allow(missing_docs)]
771pub fn deep_clone(node: &TreeNodeExt) -> TreeNodeExt {
772    node.clone()
773}
774/// Returns true if the tree contains a node with the given label.
775#[allow(dead_code)]
776#[allow(missing_docs)]
777pub fn contains_label(node: &TreeNodeExt, label: &str) -> bool {
778    if node.label == label {
779        return true;
780    }
781    node.children.iter().any(|c| contains_label(c, label))
782}
783#[cfg(test)]
784mod ast_impl_ext3_tests {
785    use super::*;
786    use crate::ast_impl::*;
787    use crate::tokens::{Span, StringPart};
788    #[test]
789    fn test_tree_cursor() {
790        let root = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
791        let cursor = TreeCursor::new(root);
792        assert_eq!(cursor.child_count(), 2);
793        assert_eq!(cursor.depth(), 0);
794    }
795    #[test]
796    fn test_collect_all_labels() {
797        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
798        let labels = collect_all_labels(&tree);
799        assert!(labels.contains(&"f".to_string()));
800        assert!(labels.contains(&"x".to_string()));
801    }
802    #[test]
803    fn test_collect_leaves() {
804        let tree = TreeNodeExt::lam("x", TreeNodeExt::leaf("x"));
805        let leaves = collect_leaves(&tree);
806        assert_eq!(leaves.len(), 1);
807        assert_eq!(leaves[0].label, "x");
808    }
809    #[test]
810    fn test_contains_label() {
811        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
812        assert!(contains_label(&tree, "f"));
813        assert!(!contains_label(&tree, "g"));
814    }
815}
816/// A memoised depth computation for tree nodes.
817#[allow(dead_code)]
818#[allow(missing_docs)]
819pub fn memoised_depth(
820    node: &TreeNodeExt,
821    memo: &mut std::collections::HashMap<u64, usize>,
822) -> usize {
823    let key = crate::ast::tree_fingerprint_ext(node);
824    if let Some(&d) = memo.get(&key) {
825        return d;
826    }
827    let d = if node.children.is_empty() {
828        0
829    } else {
830        1 + node
831            .children
832            .iter()
833            .map(|c| memoised_depth(c, memo))
834            .max()
835            .unwrap_or(0)
836    };
837    memo.insert(key, d);
838    d
839}
840/// A tree serialiser that produces a compact string representation.
841#[allow(dead_code)]
842#[allow(missing_docs)]
843pub fn serialise_tree(node: &TreeNodeExt) -> String {
844    if node.children.is_empty() {
845        return format!("{{{}}}", node.label);
846    }
847    let children: Vec<String> = node.children.iter().map(serialise_tree).collect();
848    format!("{{{}:{}}}", node.label, children.join(","))
849}
850/// A tree deserialiser (simplified: expects serialise_tree format).
851#[allow(dead_code)]
852#[allow(missing_docs)]
853pub fn deserialise_tree_leaf(s: &str) -> TreeNodeExt {
854    TreeNodeExt::leaf(s.trim_matches(|c| c == '{' || c == '}'))
855}
856/// Computes the height of a tree (same as depth).
857#[allow(dead_code)]
858#[allow(missing_docs)]
859pub fn tree_height(node: &TreeNodeExt) -> usize {
860    if node.children.is_empty() {
861        return 0;
862    }
863    1 + node.children.iter().map(tree_height).max().unwrap_or(0)
864}
865/// Returns the branching factor of a tree (average children per non-leaf node).
866#[allow(dead_code)]
867#[allow(missing_docs)]
868pub fn branching_factor(node: &TreeNodeExt) -> f64 {
869    let mut total_children = 0usize;
870    let mut internal_nodes = 0usize;
871    count_branching(node, &mut total_children, &mut internal_nodes);
872    if internal_nodes == 0 {
873        return 0.0;
874    }
875    total_children as f64 / internal_nodes as f64
876}
877pub(super) fn count_branching(node: &TreeNodeExt, total: &mut usize, internals: &mut usize) {
878    if !node.children.is_empty() {
879        *internals += 1;
880        *total += node.children.len();
881    }
882    for child in &node.children {
883        count_branching(child, total, internals);
884    }
885}
886#[cfg(test)]
887mod ast_impl_final_tests {
888    use super::*;
889    use crate::ast_impl::*;
890    use crate::tokens::{Span, StringPart};
891    #[test]
892    fn test_serialise_tree() {
893        let leaf = TreeNodeExt::leaf("x");
894        assert_eq!(serialise_tree(&leaf), "{x}");
895        let app = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
896        let s = serialise_tree(&app);
897        assert!(s.starts_with("{@:"));
898    }
899    #[test]
900    fn test_tree_height() {
901        let leaf = TreeNodeExt::leaf("x");
902        assert_eq!(tree_height(&leaf), 0);
903        let app = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
904        assert_eq!(tree_height(&app), 1);
905    }
906    #[test]
907    fn test_branching_factor() {
908        let leaf = TreeNodeExt::leaf("x");
909        assert_eq!(branching_factor(&leaf), 0.0);
910        let app = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
911        assert!((branching_factor(&app) - 2.0).abs() < 0.01);
912    }
913    #[test]
914    fn test_memoised_depth() {
915        let tree = TreeNodeExt::app(TreeNodeExt::leaf("f"), TreeNodeExt::leaf("x"));
916        let mut memo = std::collections::HashMap::new();
917        let d = memoised_depth(&tree, &mut memo);
918        assert_eq!(d, 1);
919    }
920}
921/// Map a function over all leaf labels.
922#[allow(dead_code)]
923#[allow(missing_docs)]
924pub fn map_leaves<F: Fn(&str) -> String>(node: &TreeNodeExt, f: &F) -> TreeNodeExt {
925    if node.children.is_empty() {
926        return TreeNodeExt::leaf(&f(&node.label));
927    }
928    let children = node.children.iter().map(|c| map_leaves(c, f)).collect();
929    TreeNodeExt {
930        kind: node.kind.clone(),
931        label: node.label.clone(),
932        children,
933    }
934}
935/// Returns true if any leaf satisfies a predicate.
936#[allow(dead_code)]
937#[allow(missing_docs)]
938pub fn any_leaf<F: Fn(&str) -> bool>(node: &TreeNodeExt, pred: &F) -> bool {
939    if node.children.is_empty() {
940        return pred(&node.label);
941    }
942    node.children.iter().any(|c| any_leaf(c, pred))
943}
944#[cfg(test)]
945mod ast_impl_pad {
946    use super::*;
947    use crate::ast_impl::*;
948    use crate::tokens::{Span, StringPart};
949    #[test]
950    fn test_node_cache() {
951        let mut c = NodeCache::new();
952        c.store("x", 42);
953        assert_eq!(c.get("x"), Some(42));
954        assert_eq!(c.get("y"), None);
955    }
956    #[test]
957    fn test_map_leaves() {
958        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
959        let mapped = map_leaves(&tree, &|s: &str| s.to_uppercase());
960        assert_eq!(mapped.children[0].label, "X");
961        assert_eq!(mapped.children[1].label, "Y");
962    }
963    #[test]
964    fn test_any_leaf() {
965        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
966        assert!(any_leaf(&tree, &|s: &str| s == "x"));
967        assert!(!any_leaf(&tree, &|s: &str| s == "z"));
968    }
969}
970/// Compute tree statistics for a TreeNodeExt.
971#[allow(dead_code)]
972#[allow(missing_docs)]
973pub fn compute_tree_stats(node: &TreeNodeExt) -> TreeStatsExt2 {
974    fn go(node: &TreeNodeExt, depth: usize, stats: &mut TreeStatsExt2) {
975        stats.node_count += 1;
976        if depth > stats.max_depth {
977            stats.max_depth = depth;
978        }
979        if node.children.is_empty() {
980            stats.leaf_count += 1;
981        } else {
982            let b = node.children.len();
983            if b > stats.max_branching {
984                stats.max_branching = b;
985            }
986            for child in &node.children {
987                go(child, depth + 1, stats);
988            }
989        }
990    }
991    let mut stats = TreeStatsExt2::default();
992    go(node, 0, &mut stats);
993    stats
994}
995/// Compute a string fingerprint for the shape of a tree (ignoring labels).
996#[allow(dead_code)]
997#[allow(missing_docs)]
998pub fn shape_fingerprint_ext2(node: &TreeNodeExt) -> String {
999    if node.children.is_empty() {
1000        return "L".to_string();
1001    }
1002    let children_fp: String = node
1003        .children
1004        .iter()
1005        .map(shape_fingerprint_ext2)
1006        .collect::<Vec<_>>()
1007        .join(",");
1008    format!("N({})", children_fp)
1009}
1010/// Deep-clones a tree (same as Clone but explicit).
1011#[allow(dead_code)]
1012#[allow(missing_docs)]
1013pub fn deep_clone_ext2(node: &TreeNodeExt) -> TreeNodeExt {
1014    node.clone()
1015}
1016/// Returns all labels in the tree in pre-order.
1017#[allow(dead_code)]
1018#[allow(missing_docs)]
1019pub fn collect_all_labels_ext2(node: &TreeNodeExt) -> Vec<String> {
1020    let mut labels = vec![node.label.clone()];
1021    for child in &node.children {
1022        labels.extend(collect_all_labels_ext2(child));
1023    }
1024    labels
1025}
1026/// Returns all leaf labels in the tree in left-to-right order.
1027#[allow(dead_code)]
1028#[allow(missing_docs)]
1029pub fn collect_leaves_ext2(node: &TreeNodeExt) -> Vec<String> {
1030    if node.children.is_empty() {
1031        return vec![node.label.clone()];
1032    }
1033    node.children.iter().flat_map(collect_leaves_ext2).collect()
1034}
1035/// Returns the height of a tree (number of edges from root to deepest leaf).
1036#[allow(dead_code)]
1037#[allow(missing_docs)]
1038pub fn tree_height_ext2(node: &TreeNodeExt) -> usize {
1039    if node.children.is_empty() {
1040        return 0;
1041    }
1042    1 + node
1043        .children
1044        .iter()
1045        .map(tree_height_ext2)
1046        .max()
1047        .unwrap_or(0)
1048}
1049/// Returns true if a tree contains a node with the given label.
1050#[allow(dead_code)]
1051#[allow(missing_docs)]
1052pub fn contains_label_ext2(node: &TreeNodeExt, label: &str) -> bool {
1053    if node.label == label {
1054        return true;
1055    }
1056    node.children.iter().any(|c| contains_label_ext2(c, label))
1057}
1058#[cfg(test)]
1059mod ast_impl_pad2 {
1060    use super::*;
1061    use crate::ast_impl::*;
1062    use crate::tokens::{Span, StringPart};
1063    #[test]
1064    fn test_transform_memo() {
1065        let mut m = TransformMemoExt2::new();
1066        m.insert("foo", "FOO");
1067        assert_eq!(m.get("foo"), Some("FOO"));
1068        assert!(m.has("foo"));
1069        assert!(!m.has("bar"));
1070    }
1071    #[test]
1072    fn test_compute_tree_stats() {
1073        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
1074        let stats = compute_tree_stats(&tree);
1075        assert_eq!(stats.node_count, 3);
1076        assert_eq!(stats.leaf_count, 2);
1077        assert_eq!(stats.max_depth, 1);
1078    }
1079    #[test]
1080    fn test_shape_fingerprint_ext2() {
1081        let tree = TreeNodeExt::app(TreeNodeExt::leaf("x"), TreeNodeExt::leaf("y"));
1082        let fp = shape_fingerprint_ext2(&tree);
1083        assert!(fp.starts_with("N("));
1084        assert!(fp.contains("L"));
1085    }
1086    #[test]
1087    fn test_collect_labels_and_leaves() {
1088        let tree = TreeNodeExt::app(TreeNodeExt::leaf("a"), TreeNodeExt::leaf("b"));
1089        let labels = collect_all_labels_ext2(&tree);
1090        assert!(labels.contains(&"a".to_string()));
1091        assert!(labels.contains(&"b".to_string()));
1092        let leaves = collect_leaves_ext2(&tree);
1093        assert_eq!(leaves, vec!["a".to_string(), "b".to_string()]);
1094    }
1095    #[test]
1096    fn test_tree_height_ext2_and_contains() {
1097        let leaf = TreeNodeExt::leaf("x");
1098        assert_eq!(tree_height_ext2(&leaf), 0);
1099        let tree = TreeNodeExt::app(TreeNodeExt::leaf("a"), TreeNodeExt::leaf("b"));
1100        assert_eq!(tree_height_ext2(&tree), 1);
1101        assert!(contains_label_ext2(&tree, "a"));
1102        assert!(!contains_label_ext2(&tree, "z"));
1103    }
1104}
1105#[cfg(test)]
1106mod ast_impl_pad3 {
1107    use super::*;
1108    use crate::ast_impl::*;
1109    use crate::tokens::{Span, StringPart};
1110    #[test]
1111    fn test_tree_zipper() {
1112        let tree = TreeNodeExt::app(TreeNodeExt::leaf("a"), TreeNodeExt::leaf("b"));
1113        let z = TreeZipper::new(tree);
1114        assert_eq!(z.depth(), 0);
1115        let z2 = z.down(0).expect("test operation should succeed");
1116        assert_eq!(z2.depth(), 1);
1117        assert_eq!(z2.label(), "a");
1118    }
1119}