asmdoc/
assembly_project.rs

1// Copyright (C) 2024 Ethan Uppal. All  rights reserved.
2
3use std::{collections::HashMap, path::PathBuf};
4
5use linked_hash_map::LinkedHashMap;
6
7use crate::{
8    assembly_file::{AssemblyFile, AssemblyItem, AssemblySection},
9    docs::{Docs, Visibility}
10};
11
12#[derive(Default)]
13pub struct AssemblyProject {
14    files: HashMap<PathBuf, AssemblyFile>,
15    symbols: HashMap<
16        PathBuf,
17        LinkedHashMap<String, (Visibility, Option<AssemblySection>)>
18    >,
19    /// Location of project-defined globals.
20    global_sources: HashMap<String, PathBuf>,
21    /// Location of project-internal externs.
22    internal_externs: HashMap<String, PathBuf>,
23    symbol_constituents: HashMap<String, Vec<String>>
24}
25
26impl AssemblyProject {
27    pub fn build_from(files: HashMap<PathBuf, AssemblyFile>) -> Self {
28        Self {
29            files,
30            ..Default::default()
31        }
32        .resolve()
33    }
34
35    fn resolve(mut self) -> Self {
36        for (file, asm) in &self.files {
37            for global in &asm.globals {
38                self.global_sources.insert(global.clone(), file.clone());
39            }
40        }
41        for (file, asm) in &self.files {
42            for extern_ in &asm.externs {
43                if let Some(global_def_file) = self.global_sources.get(extern_)
44                {
45                    self.internal_externs
46                        .insert(extern_.clone(), global_def_file.clone());
47                }
48            }
49
50            let local_symbols = self.symbols.entry(file.clone()).or_default();
51
52            for extern_ in &asm.externs {
53                local_symbols
54                    .insert(extern_.clone(), (Visibility::External, None));
55            }
56
57            let mut current_label = String::new();
58            for (section, items) in &asm.sections {
59                for item in items {
60                    if let AssemblyItem::Label(label) = item {
61                        if label.starts_with(".") {
62                            self.symbol_constituents
63                                .entry(current_label.clone())
64                                .or_default()
65                                .push(label.clone());
66                        } else {
67                            current_label = label.clone();
68                            let visibility =
69                                if asm.globals.contains(&current_label) {
70                                    Visibility::Global
71                                } else {
72                                    Visibility::Private
73                                };
74                            local_symbols.insert(
75                                current_label.clone(),
76                                (visibility, Some(*section))
77                            );
78                        }
79                    }
80                }
81            }
82        }
83        self
84    }
85
86    pub fn generate_docs(&self) -> Vec<(PathBuf, Docs)> {
87        // what a nightmare!
88        let mut docs = Vec::new();
89        for (file, asm) in &self.files {
90            let mut symbol_docs = Vec::new();
91            for (symbol, (visibility, section)) in
92                self.symbols.get(file).unwrap()
93            {
94                let file = if *visibility == Visibility::External {
95                    self.internal_externs.get(symbol).cloned()
96                } else {
97                    None
98                };
99                let constituents = self
100                    .symbol_constituents
101                    .get(symbol)
102                    .map(|constituents| {
103                        constituents
104                            .iter()
105                            .map(|constituent| {
106                                Box::new(Docs::InlineCode(format!(
107                                    "`{}`",
108                                    constituent
109                                )))
110                            })
111                            .collect::<Vec<_>>()
112                    })
113                    .unwrap_or_default();
114                let mut symbol_cell =
115                    vec![Box::new(Docs::InlineCode(symbol.clone()))];
116                for constituent in constituents {
117                    symbol_cell.push(Box::new(Docs::Concat(vec![
118                        Box::new(Docs::Text("- ".into())),
119                        constituent,
120                    ])));
121                }
122                symbol_docs.push(vec![
123                    Box::new(Docs::Text(visibility.to_string())),
124                    Box::new(Docs::CellLines(symbol_cell)),
125                    Box::new(Docs::Text(
126                        section.map(|s| s.to_string()).unwrap_or_default()
127                    )),
128                    Box::new(if let Some(file) = file {
129                        Docs::ResolveFile(file)
130                    } else {
131                        Docs::Text("".into())
132                    }),
133                ]);
134            }
135            let defines_docs = asm
136                .defines
137                .iter()
138                .map(|define| {
139                    Box::new(Docs::Define {
140                        name: define.clone()
141                    })
142                })
143                .collect();
144            let macro_docs = asm
145                .macros
146                .iter()
147                .map(|macro_| {
148                    Box::new(Docs::Macro {
149                        name: macro_.name.clone(),
150                        arg_count: macro_.arg_count
151                    })
152                })
153                .collect();
154            let file_docs = Docs::File {
155                path: file.clone(),
156                symbols: Box::new(Docs::Table {
157                    header: vec![
158                        Box::new(Docs::Text("Visibility".into())),
159                        Box::new(Docs::Text("Label".into())),
160                        Box::new(Docs::Text("Section".into())),
161                        Box::new(Docs::Text("Defined in".into())),
162                    ],
163                    rows: symbol_docs
164                }),
165                defines: Box::new(Docs::List(defines_docs)),
166                macros: Box::new(Docs::List(macro_docs))
167            };
168            docs.push((file.clone(), file_docs));
169        }
170        docs
171    }
172}