asmdoc/
assembly_project.rs1use 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 global_sources: HashMap<String, PathBuf>,
21 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(¤t_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 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}