1use std::io::Write;
2use std::str::FromStr;
3
4use cpclib_common::itertools::Itertools;
5use cpclib_sna::{AceSymbol, AceSymbolChunk, AceSymbolType, RemuChunk, RemuEntry};
6use cpclib_tokens::ExprResult;
7use cpclib_tokens::symbols::{Symbol, SymbolsTableTrait, Value};
8
9pub const NEVER_EXPORTED_SYMBOLS: &[&str] =
10 &["$", "$$", "BASM_VERSION", "BASM", "BASM_FEATURE_HFE"];
11
12pub enum SymbolOutputFormat {
13 Basm,
14 Winape
15}
16
17impl SymbolOutputFormat {
18 pub fn format(&self, k: &Symbol, v: &Value) -> String {
19 match self {
20 SymbolOutputFormat::Basm => {
21 match v {
22 Value::Address(a) => {
23 format!("{} equ #{:04X}", k.value(), a.address())
24 },
25 Value::Expr(ExprResult::Value(i)) => {
26 format!("{} equ #{:04X}", k.value(), i)
27 },
28 Value::Expr(ExprResult::Bool(b)) => {
29 format!("{} equ {}", k.value(), *b)
30 },
31 Value::Expr(e @ ExprResult::Float(_f)) => {
32 format!("{} equ #{:04X}", k.value(), e.int().unwrap())
33 },
34 Value::Expr(ExprResult::String(s)) => {
35 format!("{} equ {}", k.value(), s)
36 },
37 Value::Expr(l @ ExprResult::List(_)) => {
38 format!("{} equ {}", k.value(), l)
39 },
40 Value::Expr(m @ ExprResult::Matrix { .. }) => {
41 format!("{} equ {}", k.value(), m)
42 },
43
44 Value::Expr(ExprResult::Char(c)) => {
45 let c = *c as char;
46 format!(
47 "{} equ '{}{}'",
48 k.value(),
49 if c == '\'' { "\\" } else { "" },
50 c
51 )
52 },
53
54 _ => unimplemented!("{:?}", v)
55 }
56 },
57 SymbolOutputFormat::Winape => {
58 match v {
59 Value::Address(a) => {
60 format!("{} #{:X}", k.value(), a.address())
61 },
62 Value::Expr(ExprResult::Value(i)) => {
63 format!("{} #{:X}", k.value(), i)
64 },
65 Value::Expr(ExprResult::Bool(b)) => {
66 format!("{} {}", k.value(), *b)
67 },
68 Value::Expr(e @ ExprResult::Float(_f)) => {
69 format!("{} #{:X}", k.value(), e.int().unwrap())
70 },
71 Value::Expr(ExprResult::String(_s)) => {
72 "".to_owned() },
74 Value::Expr(_l @ ExprResult::List(_)) => {
75 "".to_owned() },
77 Value::Expr(_m @ ExprResult::Matrix { .. }) => {
78 "".to_owned() },
80
81 _ => unimplemented!("{:?}", v)
82 }
83 }
84 }
85 }
86}
87
88impl FromStr for SymbolOutputFormat {
89 type Err = String;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 match s.to_ascii_lowercase().as_str() {
93 "basm" => Ok(Self::Basm),
94 "winape" => Ok(Self::Winape),
95 _ => Err(format!("Wrong symbol format {s}"))
96 }
97 }
98}
99
100#[derive(Clone)]
103pub struct SymbolOutputGenerator {
104 forbidden: Vec<Symbol>,
105 allowed: Vec<Symbol>,
106
107 all_forbidden: bool,
108 all_allowed: bool
109}
110
111impl Default for SymbolOutputGenerator {
112 fn default() -> Self {
113 Self {
114 forbidden: Vec::new(),
115 allowed: Vec::new(),
116
117 all_forbidden: false,
118 all_allowed: true
119 }
120 }
121}
122
123impl SymbolOutputGenerator {
124 fn symbol_to_u16(v: &Value) -> Option<u16> {
125 match v {
126 Value::Address(a) => Some(a.address()),
127 Value::Expr(ExprResult::Value(i)) => Some(*i as u16),
128 Value::Expr(ExprResult::Bool(b)) => Some(*b as u16),
129 Value::Expr(_e @ ExprResult::Float(_f)) => None,
130 Value::Expr(ExprResult::String(_s)) => None,
131 Value::Expr(_l @ ExprResult::List(_)) => None,
132 Value::Expr(_m @ ExprResult::Matrix { .. }) => None,
133
134 _ => None
135 }
136 }
137
138 pub fn build_ace_snapshot_chunk(&self, symbs: &impl SymbolsTableTrait) -> AceSymbolChunk {
139 let mut symbols = Vec::new();
140
141 for (k, v) in symbs
142 .expression_symbol()
143 .iter()
144 .filter(|(s, _v)| self.keep_symbol(s))
145 {
147 let k = k.value();
149
150 let v = Self::symbol_to_u16(v);
152
153 if let Some(v) = v {
156 let symb = AceSymbol::new(
157 k,
158 v,
159 cpclib_sna::AceMemMapType::Undefined,
160 AceSymbolType::Absolute
161 );
162 symbols.push(symb);
163 }
164 }
165
166 let mut chunk = AceSymbolChunk::empty();
167 chunk.add_symbols(symbols.into_iter());
168 chunk
169 }
170
171 pub fn fill_remu_snapshot_chunk(&self, symbs: &impl SymbolsTableTrait, remu: &mut RemuChunk) {
172 for (k, raw) in symbs
173 .expression_symbol()
174 .iter()
175 .filter(|(s, _)| self.keep_symbol(s))
176 {
177 let k = k.value();
179
180 let v = Self::symbol_to_u16(raw);
182
183 if let Some(v) = v {
185 let k = k.to_string();
186
187 let entry = if raw.is_expr() {
188 RemuEntry::new_alias(k, v)
189 }
190 else {
191 let remu_bank = raw.address().unwrap().remu_bank();
192 RemuEntry::new_label(k, v, remu_bank as _)
193 };
194
195 remu.add_entry(&entry);
196 }
197 }
198 }
199
200 pub fn generate<W: Write>(
202 &self,
203 w: &mut W,
204 symbs: &impl SymbolsTableTrait,
205 format: SymbolOutputFormat
206 ) -> std::io::Result<()> {
207 for (k, v) in symbs
208 .expression_symbol()
209 .iter()
210 .filter(|(s, _v)| self.keep_symbol(s))
211 .sorted_by_key(|(s, _v)| s.to_string().to_ascii_lowercase())
212 {
213 writeln!(w, "{}", format.format(k, v))?;
214 }
215
216 Ok(())
217 }
218
219 pub fn keep_symbol(&self, sym: &Symbol) -> bool {
221 assert!(self.all_allowed ^ self.all_forbidden);
222
223 let value = sym.value();
224
225 if NEVER_EXPORTED_SYMBOLS.contains(&value) || value.starts_with(".__hidden__") {
226 false
227 }
228 else if self.all_allowed {
229 !Self::is_included(&self.forbidden, sym)
230 }
231 else
232 {
234 Self::is_included(&self.allowed, sym)
235 }
236 }
237
238 fn is_included(list: &[Symbol], sym: &Symbol) -> bool {
239 list.iter()
240 .find(|s2| {
241 if **s2 == *sym {
242 return true;
243 }
244 sym.value().starts_with(&format!("{}.", s2.value()))
246 })
247 .is_some()
248 }
249
250 pub fn forbid_all_symbols(&mut self) {
251 self.forbidden.clear();
252 self.all_forbidden = true;
253 self.all_allowed = false;
254 }
255
256 pub fn allow_all_symbols(&mut self) {
257 self.allowed.clear();
258 self.all_allowed = true;
259 self.all_forbidden = false;
260 }
261
262 pub fn forbid_symbol<S: Into<Symbol>>(&mut self, s: S) {
263 self.forbidden.push(s.into());
264 }
265
266 pub fn allow_symbol<S: Into<Symbol>>(&mut self, s: S) {
267 self.allowed.push(s.into());
268 }
269}