1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*[toml]
[dependencies]
syn = { version = "2", features = ["extra-traits", "full", "visit"] }
*/
use quote::ToTokens;
use syn::{parse_str, Expr, Stmt};
/// Guided ChatGPT-generated prototype of using a `syn` abstract syntax tree (AST)
/// to detect whether a snippet returns a value that we should print out, or whether
/// it does its own printing.
///
/// Part 2: ChatGPT responds to feedback with an improved algorithm.
//# Purpose: Demo use of `syn` AST to analyse code and use of AI LLM dialogue to flesh out ideas and provide code.
//# Categories: AST, technique
fn main() {
let code = r#"
for i in 1..=5 {
println!("{}", i);
}
"#;
match parse_str::<Expr>(code) {
Ok(expr) => {
if is_last_stmt_unit(&expr) {
println!("Option B: unit return type \n{}", wrap_in_main(&expr));
} else {
println!(
"Option A: non-unit return type\n{}",
wrap_in_main_with_println(&expr)
);
}
}
Err(e) => eprintln!("Failed to parse expression: {:?}", e),
}
}
fn is_last_stmt_unit(expr: &Expr) -> bool {
println!("expr={expr:#?}");
match expr {
Expr::ForLoop(_) => true,
Expr::While(_) => true,
Expr::Loop(_) => true,
Expr::If(expr_if) => expr_if.else_branch.is_none(),
Expr::Block(expr_block) => {
if let Some(last_stmt) = expr_block.block.stmts.last() {
match last_stmt {
Stmt::Expr(_, None) => {
println!("Stmt::Expr(_, None)");
false
} // Expression without semicolon
Stmt::Expr(_, Some(_)) => {
println!("Stmt::Expr(_, Some(_))");
true
} // Expression with semicolon returns unit
Stmt::Macro(m) => {
let is_some = m.semi_token.is_some();
println!("Stmt::Macro({m:#?}), m.semi_token.is_some()={is_some}");
is_some
} // Macro with a semicolon returns unit
_ => {
println!("Something else, returning false");
false
}
}
} else {
println!("Not if let Some(last_stmt) = expr_block.block.stmts.last()");
false
}
}
_ => {
println!("Not if let Expr::Block(expr_block) = expr");
false
}
}
}
fn wrap_in_main(expr: &Expr) -> String {
format!(
r#"
fn main() {{
{expr}
}}
"#,
expr = expr.to_token_stream()
)
}
fn wrap_in_main_with_println(expr: &Expr) -> String {
format!(
r#"
fn main() {{
println!("{{expr:?}}", {{
{expr}
}});
}}
"#,
expr = expr.to_token_stream()
)
}