cpclib_asm/assembler/
macro.rs

1use std::ops::Deref;
2
3use aho_corasick::{AhoCorasick, MatchKind};
4use compact_str::CompactString;
5use cpclib_common::itertools::{EitherOrBoth, Itertools};
6use cpclib_common::winnow::Parser;
7use cpclib_tokens::symbols::{Macro, Source, Struct};
8use cpclib_tokens::{AssemblerFlavor, MacroParamElement, Token};
9
10use crate::Env;
11use crate::error::AssemblerError;
12use crate::preamble::{Z80ParserError, Z80Span};
13
14/// To be implemented for each element that can be expended based on some patterns (i.e. macros, structs)
15pub trait Expandable {
16    /// Returns a string version of the element after expansion
17    fn expand(&self, env: &mut Env) -> Result<String, AssemblerError>;
18}
19
20#[inline]
21fn expand_param<'p, P: MacroParamElement>(
22    m: &'p P,
23    env: &mut Env
24) -> Result<beef::lean::Cow<'p, str>, AssemblerError> {
25    let extended = if m.is_single() {
26        let s = m.single_argument();
27        let trimmed = s.trim();
28        if m.must_be_evaluated() {
29            let src = &s[..];
30            let ctx_builder = env
31                .options()
32                .parse_options()
33                .clone()
34                .context_builder()
35                .remove_filename()
36                .set_context_name("MACRO parameter expansion");
37            let ctx = ctx_builder.build(src);
38            let src = Z80Span::new_extra(src, &ctx);
39            let expr_token = crate::parser::located_expr.parse(src.0).map_err(|e| {
40                let e: &Z80ParserError = e.inner();
41                AssemblerError::SyntaxError { error: e.clone() }
42            })?;
43            let value = env
44                .resolve_expr_must_never_fail(&expr_token)
45                .map_err(|e| AssemblerError::AssemblingError { msg: e.to_string() })?;
46            beef::lean::Cow::owned(value.to_string())
47        }
48        else {
49            s
50        }
51    }
52    else {
53        let l = m.list_argument();
54        beef::lean::Cow::owned(
55            l.iter()
56                .map(|p| expand_param(p.deref(), env))
57                .collect::<Result<Vec<_>, AssemblerError>>()?
58                .join(",")
59                .to_string()
60        )
61    };
62
63    Ok(extended)
64}
65
66/// Encodes both the arguments and the macro
67#[derive(Debug)]
68pub struct MacroWithArgs<P: MacroParamElement> {
69    r#macro: Macro,
70    args: Vec<P>
71}
72
73impl<P: MacroParamElement> MacroWithArgs<P> {
74    /// The construction fails if the number pf arguments is incorrect
75    #[inline]
76    pub fn build(r#macro: &Macro, args: &[P]) -> Result<Self, AssemblerError> {
77        if r#macro.nb_args() != args.len() {
78            Err(AssemblerError::MacroError {
79                name: r#macro.name().into(),
80                root: Box::new(AssemblerError::AssemblingError {
81                    msg: format!(
82                        "{} arguments provided, but {} expected.",
83                        args.len(),
84                        r#macro.nb_args()
85                    )
86                }),
87                location: r#macro.source().cloned() // TODO set up the location
88            })
89        }
90        else {
91            Ok(Self {
92                r#macro: r#macro.clone(),
93                args: args.to_vec()
94            })
95        }
96    }
97
98    #[inline]
99    pub fn source(&self) -> Option<&Source> {
100        self.r#macro.source()
101    }
102
103    #[inline]
104    pub fn flavor(&self) -> AssemblerFlavor {
105        self.r#macro.flavor()
106    }
107
108    #[inline]
109    fn expand_for_basm(&self, env: &mut Env) -> Result<String, AssemblerError> {
110        //        assert_eq!(args.len(), self.nb_args());
111        let listing = self.r#macro.code();
112        let all_expanded = self.args.iter().map(|argvalue| expand_param(argvalue, env)); //.collect::<Result<Vec<_>, _ >>()?; // we ensure there is no more resizing
113        // build the needed datastructures for replacement
114        // let (patterns, replacements) =
115        {
116            // let capacity = all_expanded.len();
117            // let mut patterns = Vec::with_capacity(capacity);
118            // let mut replacement = Vec::with_capacity(capacity);
119
120            let mut listing = beef::lean::Cow::borrowed(listing);
121
122            for (argname, expanded) in self.r#macro.params().iter().zip(/* & */ all_expanded) {
123                let expanded = expanded?;
124                let (pattern, replacement) = if argname.starts_with("r#")
125                    & expanded.starts_with("\"")
126                    & expanded.ends_with("\"")
127                {
128                    let mut search = CompactString::with_capacity(argname.len() - 2 + 2);
129                    search += "{";
130                    search += &argname[2..];
131                    search += "}";
132                    // remove " " before doing the expansion
133                    (search, &expanded[1..(expanded.len() - 1)])
134                }
135                else {
136                    let mut search = CompactString::with_capacity(argname.len() + 2);
137                    search += "{";
138                    search += argname;
139                    search += "}";
140                    (search, &expanded[..])
141                };
142
143                listing = listing.replace(pattern.as_str(), replacement).into();
144                // sadly this dumb way is faster than the ahocarasick one ...
145            }
146            Ok(listing.into_owned())
147
148            //(patterns, replacement)
149        }
150    }
151
152    #[inline]
153    fn expand_for_orgams(&self, env: &mut Env) -> Result<String, AssemblerError> {
154        let listing = self.r#macro.code();
155        let all_expanded = self
156            .args
157            .iter()
158            .map(|argvalue| expand_param(argvalue, env))
159            .collect::<Result<Vec<_>, AssemblerError>>()?;
160
161        let capacity: usize = self.args.len();
162        let mut patterns = Vec::with_capacity(capacity);
163        let mut replacements = Vec::with_capacity(capacity);
164
165        for (argname, expanded) in self.r#macro.params().iter().zip(&all_expanded) {
166            let (pattern, replacement) = if argname.starts_with("r#")
167                & expanded.starts_with("\"")
168                & expanded.ends_with("\"")
169            {
170                // remove " " before doing the expansion
171                (&argname[2..], &expanded[1..(expanded.len() - 1)])
172            }
173            else {
174                (&argname[..], &expanded[..])
175            };
176
177            patterns.push(pattern);
178            replacements.push(replacement);
179        }
180
181        let ac = AhoCorasick::builder()
182            .match_kind(MatchKind::LeftmostLongest)
183            .kind(None)
184            .build(&patterns)
185            .unwrap();
186        let result = ac.replace_all(listing, &replacements);
187
188        Ok(result)
189    }
190}
191
192impl<P: MacroParamElement> Expandable for MacroWithArgs<P> {
193    /// Develop the macro with the given arguments
194    #[inline]
195    fn expand(&self, env: &mut Env) -> Result<String, AssemblerError> {
196        if self.flavor() == AssemblerFlavor::Basm {
197            self.expand_for_basm(env)
198        }
199        else {
200            assert_eq!(self.flavor(), AssemblerFlavor::Orgams);
201            self.expand_for_orgams(env)
202        }
203
204        // make all replacements in one row :( sadly it is too slow :(
205        // let ac = AhoCorasick::builder()
206        // .match_kind(MatchKind::Standard)
207        // .kind(None)
208        // .build(&patterns)
209        // .unwrap();
210        // let result = ac.replace_all(listing, &replacements);
211        //
212        // Ok(result)
213        //
214
215        // replace the arguments for the listing
216        // for (argname, argvalue) in self.r#macro.params().iter().zip(self.args.iter()) {
217        // let expanded = expand_param(argvalue, env)?;
218        // listing =
219        // if argname.starts_with("r#") & expanded.starts_with("\"") & expanded.ends_with("\"")
220        // {
221        // remove " " before doing the expansion
222        // listing.replace(
223        // &format!("{{{}}}", &argname[2..]),
224        // &expanded[1..(expanded.len() - 1)]
225        // ).into()
226        // }
227        // else {
228        // listing.replace(&format!("{{{}}}", argname), &expanded).into()
229        // }
230        // }
231        //
232        // Ok(listing)
233    }
234}
235
236#[derive(Debug)]
237pub struct StructWithArgs<P: MacroParamElement> {
238    r#struct: Struct,
239    args: Vec<P>
240}
241
242impl<P: MacroParamElement> StructWithArgs<P> {
243    pub fn r#struct(&self) -> &Struct {
244        &self.r#struct
245    }
246
247    /// The construction fails if the number pf arguments is incorrect
248    pub fn build(r#struct: &Struct, args: &[P]) -> Result<Self, AssemblerError> {
249        if r#struct.nb_args() < args.len() {
250            Err(AssemblerError::MacroError {
251                name: r#struct.name().into(),
252                root: Box::new(AssemblerError::AssemblingError {
253                    msg: format!(
254                        "{} arguments provided, but at most {} expected.",
255                        args.len(),
256                        r#struct.nb_args()
257                    )
258                }),
259                location: r#struct.source().cloned() // TODO setup the location
260            })
261        }
262        else {
263            Ok(Self {
264                r#struct: r#struct.clone(),
265                args: args.to_vec()
266            })
267        }
268    }
269
270    pub fn source(&self) -> Option<&Source> {
271        self.r#struct.source()
272    }
273}
274
275impl<P: MacroParamElement> Expandable for StructWithArgs<P> {
276    /// Generate the token that correspond to the current structure
277    /// Current bersion does not handle at all directive with several arguments
278    /// BUG does not work when directives have a prefix
279    fn expand(&self, env: &mut Env) -> Result<String, AssemblerError> {
280        //        dbg!("{:?} != {:?}", self.args, self.r#struct().content());
281
282        let prefix = ""; // TODO acquire this prefix
283
284        // self.args has priority over self.content information
285        let mut developped: String = self
286            .r#struct()
287            .content()
288            .iter()
289            .zip_longest(self.args.iter())
290            .map(
291                |current_information| -> Result<String, AssemblerError> {
292                    let ((name, token), provided_param) = {
293                        match current_information {
294                            EitherOrBoth::Both((name, token), provided_param) => {
295                                ((name, token), Some(provided_param))
296                            }
297                            EitherOrBoth::Left((name, token)) => ((name, token), None),
298                            _ => unreachable!()
299                        }
300                    };
301
302                    match token {
303                        Token::Defb(c) | Token::Defw(c) => {
304                            let tok = if matches!(token, Token::Defb(_)) {
305                                "DB"
306                            }
307                            else {
308                                "DW"
309                            };
310
311                            let elem  = match provided_param {
312                                Some(provided_param) => {
313                                    let elem = expand_param(provided_param, env)?;
314                                    if elem.is_empty() {
315                                        beef::lean::Cow::owned(c[0].to_simplified_string())
316                                    }
317                                    else {
318                                        elem
319                                    }
320                                }
321                                None => {
322                                    if c.is_empty() {
323                                        return Err(AssemblerError::AssemblingError {
324                                            msg: format!("A value is expected for {} (no default value is provided)", name)
325                                        })
326                                    } else {
327                                        beef::lean::Cow::owned(c[0].to_string())
328                                    }
329                                }
330                            };
331
332
333                            Ok(format!(" {}{} {}", prefix, tok, elem))
334                        }
335
336                        Token::MacroCall(r#macro, current_default_args) => {
337                            let mut call = format!(" {}{} ", prefix, r#macro);
338
339                            let args: Vec<beef::lean::Cow<str>> = match provided_param {
340                                Some(provided_param2) => {
341                                    if provided_param2.is_single() {
342                                        provided_param
343                                            .into_iter()
344                                            .zip_longest(current_default_args)
345                                            .map(|a| {
346                                                match a {
347                                                    EitherOrBoth::Both(provided, _)
348                                                    | EitherOrBoth::Left(provided) => {
349                                                        (
350                                                            provided.is_single(),
351                                                            expand_param(provided, env)
352                                                        )
353                                                    }
354                                                    EitherOrBoth::Right(default) => {
355                                                        (
356                                                            default.is_single(),
357                                                            expand_param(default, env)
358                                                        )
359                                                    }
360                                                }
361                                            })
362                                            .map(|(is_single, a)| {
363                                                a.map(|repr| {
364                                                    if is_single {
365                                                        repr
366                                                    }
367                                                    else {
368                                                        beef::lean::Cow::owned(format!("[{}]", repr))
369                                                    }
370                                                })
371                                            })
372                                            .collect::<Result<Vec<_>, AssemblerError>>()?
373                                    }
374                                    else {
375                                        provided_param2
376                                            .list_argument()
377                                            .iter()
378                                            .zip_longest(current_default_args)
379                                            .map(|a| {
380                                                match a {
381                                                    EitherOrBoth::Both(provided, _)
382                                                    | EitherOrBoth::Left(provided) => {
383                                                        (
384                                                            provided.is_single(),
385                                                            expand_param(provided.deref(), env)
386                                                        )
387                                                    }
388                                                    EitherOrBoth::Right(default) => {
389                                                        (
390                                                            default.is_single(),
391                                                            expand_param(default, env)
392                                                        )
393                                                    }
394                                                }
395                                            })
396                                            .map(|(is_single, a)| {
397                                                a.map(|repr| {
398                                                    if is_single {
399                                                        repr
400                                                    }
401                                                    else {
402                                                        beef::lean::Cow::owned(format!("[{}]", repr))
403                                                    }
404                                                })
405                                            })
406                                            .collect::<Result<Vec<_>, AssemblerError>>()?
407                                    }
408                                }
409
410                                None => {
411                                    current_default_args
412                                        .iter()
413                                        .map(|a| expand_param(a, env))
414                                        .collect::<Result<Vec<_>, AssemblerError>>()?
415                                }
416                            };
417                            call.push_str(&args.join(",")); // TODO push all strings instead of creating a new one and pushing it
418                            Ok(call)
419                        }
420                        _ => unreachable!("{:?}", token)
421                    }
422                }
423            )
424            .collect::<Result<Vec<String>, AssemblerError>>()?
425            .join("\n");
426
427        let last = developped.pop().unwrap();
428        developped.push(last);
429        if last != 'n' {
430            developped.push('\n');
431        }
432        Ok(developped)
433    }
434}