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}