cpclib_asm/assembler/
symbols_output.rs

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() // ignored by winape
73                    },
74                    Value::Expr(_l @ ExprResult::List(_)) => {
75                        "".to_owned() // ignored by winape
76                    },
77                    Value::Expr(_m @ ExprResult::Matrix { .. }) => {
78                        "".to_owned() // ignored by winape
79                    },
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/// Manage the generation of the symbols output.
101/// Could be parametrize by some directives
102#[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        //    .sorted_by_key(|(s, _v)| s.to_string().to_ascii_lowercase())
146        {
147            // Get the symbol
148            let k = k.value();
149
150            // get a possible value when using u16
151            let v = Self::symbol_to_u16(v);
152
153            // TODO properly create the value by specifying a correct mem map type and symb type
154            // store if we have a representation
155            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            // Get the symbol
178            let k = k.value();
179
180            // get a possible value when using u16
181            let v = Self::symbol_to_u16(raw);
182
183            // TODO handle aliases
184            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    /// Generate the symbol table in w
201    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    /// Returns true if the symbol needs to be printed
220    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        // if self.all_forbidden
233        {
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                // if !s2.value().contains(".") {return false;}
245                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}