1#![allow(unused)]
2
3use std::{env::current_dir, error::Error, io::BufWriter, path::PathBuf};
4
5use steel::compiler::modules::MANGLER_PREFIX;
6use steel::steel_vm::engine::Engine;
7
8use std::collections::HashSet;
9use std::io::Write;
10
11struct DocumentGenerator {
12 output_dir: PathBuf,
14 writers: HashSet<PathBuf>,
16}
17
18impl DocumentGenerator {
19 pub fn new(output_dir: Option<PathBuf>) -> Self {
20 let output_dir = output_dir.unwrap_or({
21 let mut current_dir = current_dir().unwrap();
23
24 current_dir.push("/docs");
25
26 if !current_dir.exists() {
27 std::fs::create_dir(¤t_dir).unwrap();
28 }
29
30 current_dir
31 });
32
33 Self {
34 output_dir,
35 writers: HashSet::new(),
36 }
37 }
38
39 pub fn record(&mut self, path: PathBuf) {
40 self.writers.insert(path);
41 }
42}
43
44fn walk_for_defines<W: Write>(
45 writer: &mut W,
46 ast: &[steel::parser::ast::ExprKind],
47) -> Result<(), Box<dyn Error>> {
48 let mut nodes = ast.iter();
49
50 while let Some(node) = nodes.next() {
51 match &node {
52 steel::parser::ast::ExprKind::Define(d) => {
53 let name = d.name.atom_identifier().unwrap().resolve();
54
55 if !name.starts_with(MANGLER_PREFIX) && name.ends_with("__doc__") {
57 writeln!(writer, "### **{}**", name.trim_end_matches("__doc__"))?;
58
59 let ast_node = nodes.next().unwrap();
60
61 if let steel::parser::ast::ExprKind::Define(def) = &ast_node {
62 if let steel::parser::ast::ExprKind::Quote(q) = &def.body {
63 if let steel::parser::ast::ExprKind::Define(d) = &q.expr {
64 if let steel::parser::ast::ExprKind::LambdaFunction(l) = &d.body {
65 writeln!(writer, "```scheme")?;
66 write!(writer, "({}", name.trim_end_matches("__doc__"))?;
67
68 for arg in &l.args {
69 if let Some(ident) = arg.atom_identifier() {
70 write!(
74 writer,
75 " {}",
76 ident.resolve().trim_start_matches('#')
77 )?;
78 } else {
79 write!(writer, " {arg}")?;
80 }
81 }
82
83 if l.rest && l.args.len() == 1 {
84 write!(writer, " ...")?;
85 }
86
87 writeln!(writer, ")")?;
88 writeln!(writer, "```")?;
89 }
90 }
91 }
92 }
93
94 writeln!(writer, "{}", d.body.string_literal().unwrap())?;
95 }
96 }
97
98 steel::parser::ast::ExprKind::Begin(b) => {
99 walk_for_defines(writer, &b.exprs)?;
100 }
101
102 _ => {}
103 }
104 }
105
106 Ok(())
107}
108
109fn top_level_walk_directory(path: PathBuf, vm: &mut Engine) {
110 todo!()
111}
112
113pub fn walk_dir<W: Write>(
114 writer: &mut W,
115 path: PathBuf,
116 vm: &mut Engine,
117) -> Result<(), Box<dyn Error>> {
118 if path.is_dir() {
119 let directory_contents = path.read_dir()?.collect::<Result<Vec<_>, _>>()?;
123
124 for file in directory_contents {
125 let path = file.path();
126 walk_dir(writer, path, vm)?;
127 }
128 } else if path.extension().and_then(|x| x.to_str()) == Some("scm")
129 && path.file_name().and_then(|x| x.to_str()) != Some("cog.scm")
130 {
131 writeln!(writer, "# {}", path.to_str().unwrap())?;
132
133 let contents = std::fs::read_to_string(&path)?;
134
135 let ast = vm.emit_fully_expanded_ast(&contents, Some(path.clone()))?;
147
148 walk_for_defines(writer, &ast)?;
155
156 }
164
165 writer.flush()?;
166
167 Ok(())
168}
169
170pub fn parse_cog_file(path: PathBuf) -> steel::rvals::Result<String> {
173 let contents = std::fs::read_to_string(path)?;
174 let exprs = steel::parser::parser::Parser::parse(&contents)?;
175 todo!()
176}