cpclib_asm/assembler/
function.rs

1use std::borrow::Borrow;
2use std::collections::HashMap;
3use std::fmt::Debug;
4use std::ops::Deref;
5use std::sync::{LazyLock, RwLock};
6
7use cpclib_common::itertools::Itertools;
8use cpclib_tokens::{
9    CrunchType, Expr, ExprResult, ListingElement, TestKindElement, ToSimpleToken, Token
10};
11
12use super::list::{
13    list_argsort, list_get, list_len, list_push, list_sort, list_sublist, string_from_list,
14    string_new, string_push
15};
16use super::matrix::{
17    matrix_col, matrix_get, matrix_height, matrix_row, matrix_set_col, matrix_set_row, matrix_width
18};
19use super::processed_token::ProcessedToken;
20use super::{Env, file};
21use crate::assembler::list::{list_new, list_set};
22use crate::assembler::matrix::{matrix_new, matrix_set};
23use crate::error::{AssemblerError, ExpressionError};
24use crate::implementation::expression::ExprEvaluationExt;
25use crate::list::list_extend;
26use crate::preamble::{LocatedExpr, LocatedToken, LocatedTokenInner, MayHaveSpan, ParsingState};
27use crate::section::*;
28use crate::{Cruncher, Visited};
29
30/// Returns the expression of the RETURN directive
31pub trait ReturnExpr {
32    type Expr: ExprEvaluationExt;
33    fn return_expr(&self) -> Option<&Self::Expr>;
34}
35
36impl ReturnExpr for Token {
37    type Expr = Expr;
38
39    fn return_expr(&self) -> Option<&Self::Expr> {
40        match self {
41            Token::Return(exp) => Some(exp),
42            _ => None
43        }
44    }
45}
46
47impl ReturnExpr for LocatedToken {
48    type Expr = LocatedExpr;
49
50    fn return_expr(&self) -> Option<&Self::Expr> {
51        match self.deref() {
52            LocatedTokenInner::Return(e) => Some(e),
53            _ => None
54        }
55    }
56}
57
58#[derive(Debug)]
59pub struct AnyFunction<'token, T: ListingElement + Visited + ToSimpleToken + Sync + MayHaveSpan>
60where
61    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
62    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
63    ProcessedToken<'token, T>: FunctionBuilder
64{
65    name: String,
66    args: Vec<String>,
67    inner: RwLock<Vec<ProcessedToken<'token, T>>>
68}
69
70impl<'token, T: ListingElement + Visited + ToSimpleToken + Sync + MayHaveSpan> Clone
71    for AnyFunction<'token, T>
72where
73    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
74    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
75    ProcessedToken<'token, T>: FunctionBuilder
76{
77    fn clone(&self) -> Self {
78        Self {
79            name: self.name.clone(),
80            args: self.args.clone(),
81            inner: todo!()
82        }
83    }
84}
85
86impl<'token, T: ListingElement + Visited + ToSimpleToken + Sync + MayHaveSpan> PartialEq
87    for AnyFunction<'token, T>
88where
89    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
90    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
91    ProcessedToken<'token, T>: FunctionBuilder
92{
93    fn eq(&self, other: &Self) -> bool {
94        self.name == other.name && self.args == other.args
95    }
96}
97
98impl<'token, T: ListingElement + Visited + ToSimpleToken + Sync + MayHaveSpan> Eq
99    for AnyFunction<'token, T>
100where
101    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
102    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
103    ProcessedToken<'token, T>: FunctionBuilder
104{
105}
106
107impl<'token, T: ListingElement + Visited + ToSimpleToken + Sync + MayHaveSpan>
108    AnyFunction<'token, T>
109where
110    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
111    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
112    ProcessedToken<'token, T>: FunctionBuilder
113{
114    fn new<S1: AsRef<str>, S2: Borrow<str>>(
115        name: S1,
116        args: &[S2],
117        inner: Vec<ProcessedToken<'token, T>>
118    ) -> Self {
119        AnyFunction {
120            name: name.as_ref().to_owned(),
121            args: args.iter().map(|s| s.borrow().into()).collect_vec(),
122            inner: inner.into()
123        }
124    }
125}
126
127impl<'token, T: ListingElement + Visited + ToSimpleToken + Sync + ReturnExpr + MayHaveSpan>
128    AnyFunction<'token, T>
129where
130    <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
131    <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
132    ProcessedToken<'token, T>: FunctionBuilder + Clone
133{
134    pub fn eval(
135        &self,
136        init_env: &Env,
137        params: &[ExprResult]
138    ) -> Result<ExprResult, AssemblerError> {
139        if self.args.len() != params.len() {
140            return Err(AssemblerError::FunctionWithWrongNumberOfArguments(
141                self.name.clone(),
142                self.args.len(),
143                params.len()
144            ));
145        }
146        // we copy the environement to be sure no bug can modify it
147        // and to keep the symbol table fixed.
148        // a better alternative would be to backup the symbol table
149        let mut env = init_env.clone();
150
151        // set the parameters
152        for param in self.args.iter().zip(params.iter()) {
153            // TODO modify the code according to the value
154            env.add_function_parameter_to_symbols_table(
155                format!("{{{}}}", param.0),
156                param.1.clone()
157            )
158            .unwrap();
159        }
160
161        let inner = self.inner.read().unwrap();
162        let mut inner = inner.iter().cloned().collect_vec(); // BUG: memory issue in case of error generated
163        for token in inner.iter_mut() {
164            token
165                .visited(&mut env)
166                .map_err(|e| AssemblerError::FunctionError(self.name.clone(), Box::new(e)))?;
167
168            if env.return_value.is_some() {
169                let extra_print = &env.active_page_info().print_commands()
170                    [init_env.active_page_info().print_commands().len()..];
171                let extra_assert = &env.active_page_info().failed_assert_commands()
172                    [init_env.active_page_info().failed_assert_commands().len()..];
173
174                init_env
175                    .extra_print_from_function
176                    .write()
177                    .unwrap()
178                    .extend_from_slice(extra_print);
179                init_env
180                    .extra_failed_assert_from_function
181                    .write()
182                    .unwrap()
183                    .extend_from_slice(extra_assert);
184
185                return Ok(env.return_value.take().unwrap());
186            }
187        }
188
189        Err(AssemblerError::FunctionWithoutReturn(self.name.clone()))
190    }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum Function {
195    Located(AnyFunction<'static, LocatedToken>), // Here we cheat, it should not be 'static ...
196    Standard(AnyFunction<'static, Token>),
197    HardCoded(HardCodedFunction)
198}
199
200static HARD_CODED_FUNCTIONS: LazyLock<HashMap<&'static str, Function>> = LazyLock::new(|| {
201    velcro::hash_map! {
202        "mode0_byte_to_pen_at": Function::HardCoded(HardCodedFunction::Mode0ByteToPenAt),
203        "mode1_byte_to_pen_at": Function::HardCoded(HardCodedFunction::Mode1ByteToPenAt),
204        "mode2_byte_to_pen_at": Function::HardCoded(HardCodedFunction::Mode2ByteToPenAt),
205
206        "pen_at_mode0_byte": Function::HardCoded(HardCodedFunction::PenAtToMode0Byte),
207        "pen_at_mode1_byte":Function::HardCoded(HardCodedFunction::PenAtToMode1Byte),
208        "pen_at_mode2_byte": Function::HardCoded(HardCodedFunction::PenAtToMode2Byte),
209        "pens_to_mode0_byte": Function::HardCoded(HardCodedFunction::PensToMode0Byte),
210        "pens_to_mode1_byte": Function::HardCoded(HardCodedFunction::PensToMode1Byte),
211        "pens_to_mode2_byte": Function::HardCoded(HardCodedFunction::PensToMode2Byte),
212
213        "list_new": Function::HardCoded(HardCodedFunction::ListNew),
214        "list_get": Function::HardCoded(HardCodedFunction::ListGet),
215        "list_set": Function::HardCoded(HardCodedFunction::ListSet),
216        "list_len": Function::HardCoded(HardCodedFunction::ListLen),
217        "list_sublist": Function::HardCoded(HardCodedFunction::ListSublist),
218        "list_sort": Function::HardCoded(HardCodedFunction::ListSort),
219        "list_argsort": Function::HardCoded(HardCodedFunction::ListArgsort),
220        "list_push": Function::HardCoded(HardCodedFunction::ListPush),
221        "list_extend": Function::HardCoded(HardCodedFunction::ListExtend),
222
223        "string_new": Function::HardCoded(HardCodedFunction::StringNew),
224        "string_push": Function::HardCoded(HardCodedFunction::StringPush),
225        "string_concat": Function::HardCoded(HardCodedFunction::StringConcat),
226        "string_from_list": Function::HardCoded(HardCodedFunction::StringFromList),
227        "string_len": Function::HardCoded(HardCodedFunction::ListLen),
228
229
230        "assemble": Function::HardCoded(HardCodedFunction::Assemble),
231
232        "matrix_new": Function::HardCoded(HardCodedFunction::MatrixNew),
233        "matrix_set": Function::HardCoded(HardCodedFunction::MatrixSet),
234        "matrix_get": Function::HardCoded(HardCodedFunction::MatrixGet),
235        "matrix_col": Function::HardCoded(HardCodedFunction::MatrixCol),
236        "matrix_row": Function::HardCoded(HardCodedFunction::MatrixRow),
237        "matrix_set_row": Function::HardCoded(HardCodedFunction::MatrixSetRow),
238        "matrix_set_col": Function::HardCoded(HardCodedFunction::MatrixSetCol),
239        "matrix_width": Function::HardCoded(HardCodedFunction::MatrixWidth),
240        "matrix_height": Function::HardCoded(HardCodedFunction::MatrixHeight),
241
242        "load": Function::HardCoded(HardCodedFunction::Load),
243
244        "section_start": Function::HardCoded(HardCodedFunction::SectionStart),
245        "section_stop": Function::HardCoded(HardCodedFunction::SectionStop),
246        "section_length": Function::HardCoded(HardCodedFunction::SectionLength),
247        "section_used": Function::HardCoded(HardCodedFunction::SectionUsed),
248        "section_mmr": Function::HardCoded(HardCodedFunction::SectionMmr),
249
250        "binary_transform": Function::HardCoded(HardCodedFunction::BinaryTransform)
251    }
252});
253
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub enum HardCodedFunction {
256    Mode0ByteToPenAt,
257    Mode1ByteToPenAt,
258    Mode2ByteToPenAt,
259
260    PenAtToMode0Byte,
261    PenAtToMode1Byte,
262    PenAtToMode2Byte,
263
264    PensToMode0Byte,
265    PensToMode1Byte,
266    PensToMode2Byte,
267
268    ListNew,
269    ListSet,
270    ListGet,
271    ListSublist,
272    ListLen,
273    ListPush,
274    ListExtend,
275    ListSort,
276    ListArgsort,
277
278    MatrixNew,
279    MatrixSet,
280    MatrixGet,
281    MatrixCol,
282    MatrixRow,
283    MatrixSetRow,
284    MatrixSetCol,
285    MatrixWidth,
286    MatrixHeight,
287
288    SectionStart,
289    SectionStop,
290    SectionLength,
291    SectionUsed,
292    SectionMmr,
293
294    StringNew,
295    StringPush,
296    StringConcat,
297    StringFromList,
298
299    Load,
300    Assemble,
301
302    BinaryTransform
303}
304
305impl HardCodedFunction {
306    pub fn nb_expected_params(&self) -> Option<usize> {
307        match self {
308            HardCodedFunction::Mode0ByteToPenAt => Some(2),
309            HardCodedFunction::Mode1ByteToPenAt => Some(2),
310            HardCodedFunction::Mode2ByteToPenAt => Some(2),
311
312            HardCodedFunction::PenAtToMode0Byte => Some(2),
313            HardCodedFunction::PenAtToMode1Byte => Some(2),
314            HardCodedFunction::PenAtToMode2Byte => Some(2),
315
316            HardCodedFunction::PensToMode0Byte => Some(2),
317            HardCodedFunction::PensToMode1Byte => Some(4),
318            HardCodedFunction::PensToMode2Byte => Some(8),
319
320            HardCodedFunction::ListNew => Some(2),
321            HardCodedFunction::ListSet => Some(3),
322            HardCodedFunction::ListGet => Some(2),
323            HardCodedFunction::ListSublist => Some(3),
324            HardCodedFunction::ListLen => Some(1),
325            HardCodedFunction::ListSort => Some(1),
326            HardCodedFunction::ListArgsort => Some(1),
327            HardCodedFunction::ListPush => Some(2),
328
329            HardCodedFunction::StringNew => Some(2),
330            HardCodedFunction::StringPush => Some(2),
331            HardCodedFunction::StringFromList => Some(1),
332            HardCodedFunction::StringConcat => None,
333
334            HardCodedFunction::Assemble => Some(1),
335
336            HardCodedFunction::MatrixNew => Some(3),
337            HardCodedFunction::MatrixSet => Some(4),
338            HardCodedFunction::MatrixCol => Some(2),
339            HardCodedFunction::MatrixRow => Some(2),
340            HardCodedFunction::MatrixGet => Some(3),
341            HardCodedFunction::MatrixSetRow => Some(3),
342            HardCodedFunction::MatrixSetCol => Some(3),
343
344            HardCodedFunction::MatrixWidth => Some(1),
345            HardCodedFunction::MatrixHeight => Some(1),
346
347            HardCodedFunction::Load => Some(1),
348
349            HardCodedFunction::SectionStart => Some(1),
350            HardCodedFunction::SectionStop => Some(1),
351            HardCodedFunction::SectionLength => Some(1),
352            HardCodedFunction::SectionUsed => Some(1),
353            HardCodedFunction::SectionMmr => Some(1),
354
355            HardCodedFunction::BinaryTransform => Some(2),
356            HardCodedFunction::ListExtend => Some(2)
357        }
358    }
359
360    pub fn by_name(name: &str) -> Option<&Function> {
361        HARD_CODED_FUNCTIONS.get(name.to_lowercase().as_str())
362    }
363
364    pub fn name(&self) -> &str {
365        HARD_CODED_FUNCTIONS
366            .iter()
367            .find_map(|(k, v)| {
368                match v {
369                    Function::HardCoded(v) => {
370                        if v == self {
371                            Some(k)
372                        }
373                        else {
374                            None
375                        }
376                    },
377                    _ => None
378                }
379            })
380            .unwrap() // Cannot fail by definition
381    }
382
383    pub fn eval(&self, env: &Env, params: &[ExprResult]) -> Result<ExprResult, AssemblerError> {
384        if let Some(nb) = self.nb_expected_params() {
385            if nb != params.len() {
386                return Err(AssemblerError::FunctionWithWrongNumberOfArguments(
387                    self.name().into(),
388                    nb,
389                    params.len()
390                ));
391            }
392        }
393
394        match self {
395            HardCodedFunction::Mode0ByteToPenAt => {
396                Ok(
397                    cpclib_image::pixels::mode0::byte_to_pens(params[0].int()? as _)
398                        [params[1].int()? as usize % 2]
399                        .number()
400                        .into()
401                )
402            },
403            HardCodedFunction::Mode1ByteToPenAt => {
404                Ok(
405                    cpclib_image::pixels::mode1::byte_to_pens(params[0].int()? as _)
406                        [params[1].int()? as usize % 4]
407                        .number()
408                        .into()
409                )
410            },
411            HardCodedFunction::Mode2ByteToPenAt => {
412                Ok(
413                    cpclib_image::pixels::mode2::byte_to_pens(params[0].int()? as _)
414                        [params[1].int()? as usize % 8]
415                        .number()
416                        .into()
417                )
418            },
419
420            HardCodedFunction::PenAtToMode0Byte => {
421                Ok(cpclib_image::pixels::mode0::pen_to_pixel_byte(
422                    (params[0].int()? as u8 % 16).into(),
423                    (params[1].int()? as u8 % 2).into()
424                )
425                .into())
426            },
427            HardCodedFunction::PenAtToMode1Byte => {
428                Ok(cpclib_image::pixels::mode1::pen_to_pixel_byte(
429                    (params[0].int()? as u8 % 4).into(),
430                    params[1].int()? as u8 % 4
431                )
432                .into())
433            },
434
435            HardCodedFunction::PenAtToMode2Byte => {
436                Ok(cpclib_image::pixels::mode2::pen_to_pixel_byte(
437                    (params[0].int()? as u8 % 2).into(),
438                    (params[1].int()? as u8 % 8).into()
439                )
440                .into())
441            },
442
443            HardCodedFunction::PensToMode0Byte => {
444                Ok(cpclib_image::pixels::mode0::pens_to_byte(
445                    params[0].int()?.into(),
446                    params[1].int()?.into()
447                )
448                .into())
449            },
450            HardCodedFunction::PensToMode1Byte => {
451                Ok(cpclib_image::pixels::mode1::pens_to_byte(
452                    params[0].int()?.into(),
453                    params[1].int()?.into(),
454                    params[2].int()?.into(),
455                    params[3].int()?.into()
456                )
457                .into())
458            },
459            HardCodedFunction::PensToMode2Byte => {
460                Ok(cpclib_image::pixels::mode2::pens_to_byte(
461                    params[0].int()?.into(),
462                    params[1].int()?.into(),
463                    params[2].int()?.into(),
464                    params[3].int()?.into(),
465                    params[4].int()?.into(),
466                    params[5].int()?.into(),
467                    params[6].int()?.into(),
468                    params[7].int()?.into()
469                )
470                .into())
471            },
472            HardCodedFunction::ListNew => Ok(list_new(params[0].int()? as _, params[1].clone())),
473            HardCodedFunction::ListSet => {
474                list_set(params[0].clone(), params[1].int()? as _, params[2].clone())
475            },
476            HardCodedFunction::ListGet => list_get(&params[0], params[1].int()? as _),
477            HardCodedFunction::ListPush => list_push(params[0].clone(), params[1].clone()),
478            HardCodedFunction::ListExtend => list_extend(params[0].clone(), params[1].clone()),
479
480            HardCodedFunction::StringNew => string_new(params[0].int()? as _, params[1].clone()),
481            HardCodedFunction::ListLen => list_len(&params[0]),
482            HardCodedFunction::ListSublist => {
483                list_sublist(&params[0], params[1].int()? as _, params[2].int()? as _)
484            },
485
486            HardCodedFunction::StringPush => string_push(params[0].clone(), params[1].clone()),
487
488            HardCodedFunction::StringFromList => string_from_list(params[0].clone()),
489
490            HardCodedFunction::Assemble => assemble(params[0].clone(), env),
491            HardCodedFunction::StringConcat => {
492                let mut base = params[0].clone();
493                for i in 1..params.len() {
494                    base = string_push(base, params[i].clone())?
495                }
496                Ok(base)
497            },
498            HardCodedFunction::ListSort => list_sort(params[0].clone()),
499            HardCodedFunction::ListArgsort => list_argsort(&params[0]),
500
501            HardCodedFunction::MatrixNew => {
502                Ok(matrix_new(
503                    params[0].int()? as _,
504                    params[1].int()? as _,
505                    params[2].clone()
506                ))
507            },
508            HardCodedFunction::MatrixSet => {
509                matrix_set(
510                    params[0].clone(),
511                    params[1].int()? as _,
512                    params[2].int()? as _,
513                    params[3].clone()
514                )
515            },
516            HardCodedFunction::MatrixGet => {
517                matrix_get(&params[0], params[1].int()? as _, params[2].int()? as _)
518            },
519            HardCodedFunction::MatrixCol => matrix_col(&params[0], params[1].int()? as _),
520            HardCodedFunction::MatrixRow => matrix_row(&params[0], params[1].int()? as _),
521            HardCodedFunction::MatrixSetRow => {
522                matrix_set_row(params[0].clone(), params[1].int()? as _, &params[2])
523            },
524
525            HardCodedFunction::MatrixSetCol => {
526                matrix_set_col(params[0].clone(), params[1].int()? as _, &params[2])
527            },
528            HardCodedFunction::MatrixWidth => matrix_width(&params[0]),
529            HardCodedFunction::MatrixHeight => matrix_height(&params[0]),
530
531            HardCodedFunction::Load => {
532                let fname = params[0].string()?;
533                let (data, _) = file::load_file((fname, env), env.options().parse_options())?;
534                let data = Vec::from(data);
535                Ok(ExprResult::from(data.as_slice()))
536            },
537
538            HardCodedFunction::SectionStart => section_start(params[0].string()?, env),
539            HardCodedFunction::SectionStop => section_stop(params[0].string()?, env),
540            HardCodedFunction::SectionLength => section_length(params[0].string()?, env),
541            HardCodedFunction::SectionUsed => section_used(params[0].string()?, env),
542            HardCodedFunction::SectionMmr => section_mmr(params[0].string()?, env),
543
544            HardCodedFunction::BinaryTransform => {
545                let crunch_type = params[1].string()?;
546                let crunch_type = match crunch_type.to_uppercase().as_bytes() {
547                    #[cfg(not(target_arch = "wasm32"))]
548                    b"LZEXO" => CrunchType::LZEXO,
549                    #[cfg(not(target_arch = "wasm32"))]
550                    b"LZ4" => CrunchType::LZ4,
551                    b"LZ48" => CrunchType::LZ48,
552                    b"LZ49" => CrunchType::LZ49,
553                    #[cfg(not(target_arch = "wasm32"))]
554                    b"LZSHRINKLER" => CrunchType::Shrinkler,
555                    b"LZX7" => CrunchType::LZX7,
556                    #[cfg(not(target_arch = "wasm32"))]
557                    b"LZX0" => CrunchType::LZX0,
558                    #[cfg(not(target_arch = "wasm32"))]
559                    b"LZAPU" => CrunchType::LZAPU,
560                    _ => {
561                        return Err(AssemblerError::AssemblingError {
562                            msg: format!("{crunch_type} is not a valid crunch")
563                        });
564                    }
565                };
566
567                let data = params[0]
568                    .list_content()
569                    .iter()
570                    .map(|item| item.int().map(|v| v as u8))
571                    .collect::<Result<Vec<u8>, _>>()?;
572
573                let data = crunch_type.crunch(&data)?;
574                let data = ExprResult::from(data.as_slice());
575                Ok(data)
576            }
577        }
578    }
579}
580
581impl Function {
582    /// Be sure the function lives shorter than inner
583    pub unsafe fn new_located<S1: AsRef<str>, S2: Borrow<str>>(
584        name: &S1,
585        args: &[S2],
586        inner: Vec<ProcessedToken<'_, LocatedToken>>
587    ) -> Result<Self, AssemblerError> {
588        unsafe {
589            if inner.is_empty() {
590                return Err(AssemblerError::FunctionWithEmptyBody(
591                    name.as_ref().to_owned()
592                ));
593            }
594
595            let inner = std::mem::transmute(inner);
596
597            Ok(Function::Located(AnyFunction::new(name, args, inner)))
598        }
599    }
600
601    pub unsafe fn new_standard<S1: AsRef<str>, S2: Borrow<str>>(
602        name: &S1,
603        args: &[S2],
604        inner: Vec<ProcessedToken<'_, Token>>
605    ) -> Result<Self, AssemblerError> {
606        unsafe {
607            if inner.is_empty() {
608                return Err(AssemblerError::FunctionWithEmptyBody(
609                    name.as_ref().to_owned()
610                ));
611            }
612
613            let inner = std::mem::transmute(inner);
614
615            Ok(Function::Standard(AnyFunction::new(name, args, inner)))
616        }
617    }
618
619    /// Be sure the function lives shorter than inner
620
621    pub fn eval(&self, env: &Env, params: &[ExprResult]) -> Result<ExprResult, AssemblerError> {
622        match self {
623            Self::Located(f) => f.eval(env, params),
624            Self::Standard(f) => f.eval(env, params),
625            Self::HardCoded(f) => f.eval(env, params)
626        }
627    }
628}
629
630pub trait FunctionBuilder {
631    unsafe fn new<S1: AsRef<str>, S2: Borrow<str>>(
632        name: &S1,
633        args: &[S2],
634        inner: Vec<Self>
635    ) -> Result<Function, AssemblerError>
636    where
637        Self: Sized;
638}
639
640impl FunctionBuilder for ProcessedToken<'_, LocatedToken> {
641    unsafe fn new<S1: AsRef<str>, S2: Borrow<str>>(
642        name: &S1,
643        args: &[S2],
644        inner: Vec<Self>
645    ) -> Result<Function, AssemblerError>
646    where
647        Self: Sized
648    {
649        unsafe { Function::new_located(name, args, inner) }
650    }
651}
652
653impl FunctionBuilder for ProcessedToken<'_, Token> {
654    unsafe fn new<S1: AsRef<str>, S2: Borrow<str>>(
655        name: &S1,
656        args: &[S2],
657        inner: Vec<Self>
658    ) -> Result<Function, AssemblerError>
659    where
660        Self: Sized
661    {
662        unsafe { Function::new_standard(name, args, inner) }
663    }
664}
665
666/// Assemble a simple listing with no directives.
667/// Warning !!! As the env is read only, we cannot assemble directly inside
668/// To overcome that, we use a bank in a copied Env. It has not been deeply tested
669pub fn assemble(code: ExprResult, base_env: &Env) -> Result<ExprResult, AssemblerError> {
670    let code = match code {
671        ExprResult::String(code) => code,
672        _ => {
673            return Err(AssemblerError::ExpressionError(ExpressionError::OwnError(
674                Box::new(AssemblerError::AssemblingError {
675                    msg: "Wrong type. String expected".to_owned()
676                })
677            )));
678        }
679    };
680
681    let builder = base_env
682        .options()
683        .clone()
684        .context_builder()
685        .set_state(ParsingState::GeneratedLimited)
686        .set_context_name("Generated source");
687
688    let tokens = crate::parse_z80_with_context_builder(code, builder)?;
689
690    let mut env = Env::new(base_env.options().clone());
691    env.symbols = base_env.symbols().clone();
692    env.start_new_pass();
693    env.visit_page_or_bank::<Expr>(None)?; // assemble in a new bank
694    env.visit_listing(&tokens)?;
695    let bank_info = env.free_banks.pages.pop().unwrap();
696    match &bank_info.1.startadr {
697        Some(startadr) => {
698            let bytes = bank_info.0[*startadr as _..=bank_info.1.maxadr as _]
699                .iter()
700                .map(|b| ExprResult::from(*b))
701                .collect_vec();
702            Ok(ExprResult::List(bytes))
703        },
704        None => Ok(ExprResult::List(Default::default()))
705    }
706}