1use std::borrow::{Borrow, Cow};
2use std::cell::OnceCell;
3use std::collections::{BTreeMap, HashMap};
4use std::fmt::{Debug, Formatter};
5use std::ops::Deref;
6use std::sync::Arc;
7
8use cpclib_common::camino::Utf8PathBuf;
9use cpclib_common::itertools::Itertools;
10#[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
11use cpclib_common::rayon::prelude::*;
12use cpclib_disc::amsdos::AmsdosFileType;
13use cpclib_tokens::symbols::{SymbolFor, SymbolsTableTrait};
14use cpclib_tokens::{
15 AssemblerControlCommand, AssemblerFlavor, BinaryTransformation, ExprElement, ListingElement,
16 MacroParamElement, TestKindElement, ToSimpleToken, Token
17};
18use ouroboros::*;
19
20use super::AssemblerWarning;
21use super::control::ControlOutputStore;
22use super::file::{get_filename_to_read, load_file, read_source};
23use super::function::{Function, FunctionBuilder};
24use super::r#macro::Expandable;
25use crate::implementation::expression::ExprEvaluationExt;
26use crate::implementation::instructions::Cruncher;
27use crate::preamble::{LocatedListing, MayHaveSpan, Z80Span};
28use crate::progress::{self, Progress};
29use crate::{AssemblerError, Env, LocatedToken, Visited, r#macro, parse_z80_with_context_builder};
30
31#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct ProcessedToken<'token, T: Visited + Debug + ListingElement + Sync> {
35 token: &'token T,
37 state: Option<ProcessedTokenState<'token, T>>
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42enum ProcessedTokenState<'token, T: Visited + ListingElement + Debug + Sync> {
43 RestrictedAssemblingEnvironment {
44 listing: SimpleListingState<'token, T>,
45 commands: Option<ControlOutputStore>
46 },
47 Confined(SimpleListingState<'token, T>),
48 CrunchedSection {
49 listing: SimpleListingState<'token, T>,
51 previous_bytes: Option<Vec<u8>>,
53 previous_compressed_bytes: Option<Vec<u8>>
55 },
56 For(SimpleListingState<'token, T>),
57 FunctionDefinition(FunctionDefinitionState),
58 If(IfState<'token, T>),
60 Include(IncludeState),
62 Incbin(IncbinState),
65
66 Iterate(SimpleListingState<'token, T>),
67 MacroCallOrBuildStruct(ExpandState),
68 Module(SimpleListingState<'token, T>),
69 RepeatToken(
70 SingleTokenState<'token, T>,
71 &'token <T as ListingElement>::Expr
72 ),
73 Repeat(SimpleListingState<'token, T>),
74 RepeatUntil(SimpleListingState<'token, T>),
75 While(SimpleListingState<'token, T>),
76 Rorg(SimpleListingState<'token, T>),
77 Switch(SwitchState<'token, T>),
78 Warning(Box<ProcessedToken<'token, T>>)
79}
80
81#[derive(PartialEq, Eq, Clone, Debug, Default)]
82struct IncbinState {
83 contents: BTreeMap<Utf8PathBuf, Vec<u8>>
84}
85
86#[derive(PartialEq, Eq, Clone, Debug)]
87struct SingleTokenState<'token, T: Visited + ListingElement + Debug + Sync> {
88 token: Box<ProcessedToken<'token, T>>
89}
90
91#[derive(PartialEq, Eq, Clone)]
92struct SimpleListingState<'token, T: Visited + ListingElement + Debug + Sync> {
93 processed_tokens: Vec<ProcessedToken<'token, T>>,
94 span: Option<Z80Span>
95}
96
97impl<T: Visited + ListingElement + Debug + Sync> Debug for SimpleListingState<'_, T> {
98 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
99 write!(fmt, "SimpleListingState")
100 }
101}
102
103impl<'token, T: Visited + ListingElement + Debug + Sync + MayHaveSpan> SimpleListingState<'token, T>
104where <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt
105{
106 fn build(
107 tokens: &'token [T],
108 span: Option<Z80Span>,
109 env: &mut Env
110 ) -> Result<Self, AssemblerError> {
111 Ok(Self {
112 processed_tokens: build_processed_tokens_list(tokens, env)?,
113 span
114 })
115 }
116
117 fn tokens_mut(&mut self) -> &mut [ProcessedToken<'token, T>] {
118 &mut self.processed_tokens
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
123struct FunctionDefinitionState(Option<Arc<Function>>);
124
125#[derive(PartialEq, Eq, Clone, Debug)]
126struct SwitchState<'token, T: Visited + ListingElement + Debug + Sync> {
127 cases: Vec<SimpleListingState<'token, T>>,
128 default: Option<SimpleListingState<'token, T>>
129}
130
131#[derive(PartialEq, Eq, Clone, Debug, Default)]
132struct IncludeState(BTreeMap<Utf8PathBuf, IncludeStateInner>);
133
134impl IncludeState {
135 fn retreive_listing(
137 &mut self,
138 env: &mut Env,
139 fname: &Utf8PathBuf
140 ) -> Result<&mut IncludeStateInner, AssemblerError> {
141 if cfg!(target_arch = "wasm32") {
142 return Err(AssemblerError::AssemblingError {
143 msg: "INCLUDE-like directives are not allowed in a web-based assembling."
144 .to_owned()
145 });
146 }
147
148 let state: &mut IncludeStateInner = if !self.0.contains_key(fname) {
150 let content = read_source(fname.clone(), env.options().parse_options())?;
151
152 if env.options().show_progress() {
153 Progress::progress().add_parse(progress::normalize(fname));
154 }
155
156 let builder = env
157 .options()
158 .clone()
159 .context_builder()
160 .set_current_filename(fname.clone());
161
162 let listing = parse_z80_with_context_builder(content, builder)?;
163
164 if env.options().show_progress() {
166 Progress::progress().remove_parse(progress::normalize(fname));
167 }
168
169 let include_state = IncludeStateInnerTryBuilder {
170 listing,
171 processed_tokens_builder: |listing: &LocatedListing| {
172 build_processed_tokens_list(listing.as_slice(), env)
173 }
174 }
175 .try_build()?;
176
177 self.0.try_insert(fname.clone(), include_state).unwrap()
178 }
179 else {
180 self.0.get_mut(fname).unwrap()
181 };
182
183 env.included_marks_add(fname.clone());
185
186 Ok(state)
187 }
188
189 fn handle(
190 &mut self,
191 env: &mut Env,
192 fname: &str,
193 namespace: Option<&str>,
194 once: bool
195 ) -> Result<(), AssemblerError> {
196 let fname = get_filename_to_read(fname, env.options().parse_options(), Some(env))?;
197
198 let need_to_include = !once || !env.included_marks_includes(&fname);
199
200 if need_to_include {
202 let state = self.retreive_listing(env, &fname)?;
204
205 if let Some(namespace) = namespace {
207 env.enter_namespace(namespace)?;
208 }
211
212 env.enter_current_working_file(fname);
214 let res = state.with_processed_tokens_mut(|tokens| {
215 let tokens: &mut [ProcessedToken<'_, LocatedToken>] = &mut tokens[..];
216 visit_processed_tokens::<'_, LocatedToken>(tokens, env)
217 });
218 env.leave_current_working_file();
219 res?;
220
221 if namespace.is_some() {
223 env.leave_namespace()?;
224 }
226
227 Ok(())
228 }
229 else {
230 Ok(())
231 }
232 }
233}
234
235#[self_referencing]
236struct IncludeStateInner {
237 listing: LocatedListing,
238 #[borrows(listing)]
239 #[covariant]
240 processed_tokens: Vec<ProcessedToken<'this, LocatedToken>>
241}
242
243impl Clone for IncludeStateInner {
244 fn clone(&self) -> Self {
245 todo!()
246 }
247}
248
249impl PartialEq for IncludeStateInner {
250 fn eq(&self, other: &Self) -> bool {
251 self.with_listing(|l1| other.with_listing(|l2| l1.eq(l2)))
252 }
253}
254
255impl Eq for IncludeStateInner {}
256
257impl Debug for IncludeStateInner {
258 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
259 write!(fmt, "IncludeState")
260 }
261}
262
263#[self_referencing]
264struct ExpandState {
265 listing: LocatedListing,
266 #[borrows(listing)]
267 #[covariant]
268 processed_tokens: Vec<ProcessedToken<'this, LocatedToken>>
269}
270
271impl PartialEq for ExpandState {
272 fn eq(&self, other: &Self) -> bool {
273 self.with_listing(|l1| other.with_listing(|l2| l1.eq(l2)))
274 }
275}
276
277impl Eq for ExpandState {}
278
279impl Clone for ExpandState {
280 fn clone(&self) -> Self {
281 todo!()
282 }
283}
284
285impl Debug for ExpandState {
286 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
287 write!(fmt, "ExpandState")
288 }
289}
290
291#[derive(Debug, Clone, PartialEq, Eq)]
293struct IfState<'token, T: Visited + Debug + ListingElement + Sync> {
294 token: &'token T,
296 if_token_adr_to_used_decision: std::collections::HashMap<usize, bool>,
297 if_token_adr_to_unused_decision: std::collections::HashMap<usize, bool>,
298 tests_listing: HashMap<usize, Vec<ProcessedToken<'token, T>>>,
300 else_listing: Option<Vec<ProcessedToken<'token, T>>>
302}
303
304impl<'token, T: Visited + Debug + ListingElement + Sync + MayHaveSpan> IfState<'token, T>
305where <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt
306{
307 fn new(token: &'token T) -> Self {
308 Self {
309 token,
310 if_token_adr_to_used_decision: Default::default(),
311 if_token_adr_to_unused_decision: Default::default(),
312 tests_listing: Default::default(),
313 else_listing: None
314 }
315 }
316
317 fn choose_listing_to_assemble(
318 &mut self,
319 env: &mut Env
320 ) -> Result<Option<&mut [ProcessedToken<'token, T>]>, AssemblerError>
321 where
322 <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr:
323 ExprEvaluationExt,
324 <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt
325 {
326 let mut selected_idx = None;
327 let mut request_additional_pass = false;
328 use cpclib_tokens::ExprResult;
329 let FLAG_FAILURE: OnceCell<ExprResult> = OnceCell::new();
330 let FLAG_FAILURE =
331 FLAG_FAILURE.get_or_init(|| "__BASM_INNER_TEST_FAILURE__".to_owned().into());
332
333 for idx in 0..self.token.if_nb_tests() {
334 let (test, _) = self.token.if_test(idx);
335 let token_adr = test as *const _ as usize;
336
337 if test.is_true_test() {
340 let exp = test.expr_unchecked();
341 let value = env
343 .resolve_expr_may_fail_in_first_pass_with_default(exp, FLAG_FAILURE.clone())?;
344 if &value == FLAG_FAILURE {
345 return Ok(None);
347 }
348 if value.bool()? {
349 selected_idx = Some(idx);
350 break;
351 }
352 }
353 else if test.is_false_test() {
356 let exp = test.expr_unchecked();
357 let value = env
358 .resolve_expr_may_fail_in_first_pass_with_default(exp, FLAG_FAILURE.clone())?;
359 if &value == FLAG_FAILURE {
360 return Ok(None);
362 }
363 if !value.bool()? {
364 selected_idx = Some(idx);
365 break;
366 }
367 }
368 else if test.is_label_used_test() {
370 let label = test.label_unchecked();
371 let decision = env.symbols().is_used(label);
372
373 if let Some(res) = self.if_token_adr_to_used_decision.get(&token_adr) {
375 if *res != decision {
376 request_additional_pass = true;
377 }
378 }
379
380 self.if_token_adr_to_used_decision
382 .insert(token_adr, decision);
383
384 if decision {
385 selected_idx = Some(idx);
386 break;
387 }
388 }
389 else if test.is_label_nused_test() {
391 let label = test.label_unchecked();
392 let decision = !env.symbols().is_used(label);
393
394 if let Some(res) = self.if_token_adr_to_unused_decision.get(&token_adr) {
396 if *res != decision {
397 request_additional_pass = true;
398 }
399 }
400
401 self.if_token_adr_to_unused_decision
403 .insert(token_adr, decision);
404
405 if decision {
406 selected_idx = Some(idx);
407 break;
408 }
409 }
410 else if test.is_label_exists_test() {
413 let label = test.label_unchecked();
414 if env.symbols().symbol_exist_in_current_pass(label)? {
415 selected_idx = Some(idx);
416 break;
417 }
418 }
419 else {
422 let label = test.label_unchecked();
423 if !env.symbols().symbol_exist_in_current_pass(label)? {
424 selected_idx = Some(idx);
425 break;
426 }
427 }
428 }
429
430 let selected_listing = match selected_idx {
431 Some(selected_idx) => {
432 if self.tests_listing.get(&selected_idx).is_none() {
434 let listing = self.token.if_test(selected_idx).1;
435 let listing = build_processed_tokens_list(listing, env)?;
436 self.tests_listing.insert(selected_idx, listing);
437 }
438 self.tests_listing.get_mut(&selected_idx)
439 },
440 None => {
441 if self.else_listing.is_none() && self.token.if_else().is_some() {
443 let listing = self.token.if_else();
444 self.else_listing = listing
445 .map(|listing| build_processed_tokens_list(listing, env))
446 .transpose()?;
447 }
448 self.else_listing.as_mut()
449 }
450 };
451
452 let request_additional_pass =
454 *env.request_additional_pass.read().unwrap().deref() | request_additional_pass;
455 *env.request_additional_pass.write().unwrap() = request_additional_pass;
456
457 Ok(selected_listing.map(|l| l.as_mut_slice()))
458 }
459}
460
461impl<T: Visited + Debug + ListingElement + Sync + ToSimpleToken> ToSimpleToken
462 for ProcessedToken<'_, T>
463where <T as ListingElement>::Expr: ExprEvaluationExt
464{
465 fn as_simple_token(&self) -> Cow<Token> {
466 self.token.as_simple_token()
467 }
468}
469
470pub type AssemblerInfo = AssemblerError;
471
472pub fn build_processed_token<'token, T: Visited + Debug + Sync + ListingElement + MayHaveSpan>(
474 token: &'token T,
475 env: &mut Env
476) -> Result<ProcessedToken<'token, T>, AssemblerError>
477where
478 <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt
479{
480 let state = if token.is_confined() {
481 Some(ProcessedTokenState::Confined(SimpleListingState {
482 processed_tokens: build_processed_tokens_list(token.confined_listing(), env)?,
483 span: token.possible_span().cloned()
484 }))
485 }
486 else if token.is_if() {
487 let state = IfState::new(token);
488 Some(ProcessedTokenState::If(state))
489 }
490 else if token.is_include() {
491 let fname = token.include_fname();
493 let fname = env.build_fname(fname)?;
494 let options = env.options().parse_options();
495 match get_filename_to_read(fname, options, Some(env)) {
496 Ok(fname) => {
497 match read_source(fname.clone(), options) {
498 Ok(content) => {
499 let ctx_builder = options
500 .clone()
501 .context_builder()
502 .set_current_filename(fname.clone());
503
504 match parse_z80_with_context_builder(content, ctx_builder) {
505 Ok(listing) => {
506 if token.include_is_standard_include()
508 && env.options().show_progress()
509 {
510 Progress::progress().remove_parse(progress::normalize(&fname));
511 }
512
513 let include_state = IncludeStateInnerTryBuilder {
514 listing,
515 processed_tokens_builder: |listing: &LocatedListing| {
516 build_processed_tokens_list(listing, env)
517 }
518 }
519 .try_build()?;
520
521 let mut map = BTreeMap::new();
522 map.insert(fname, include_state);
523
524 Some(ProcessedTokenState::Include(IncludeState(map)))
525 },
526 Err(_) => Some(ProcessedTokenState::Include(Default::default()))
527 }
528 },
529 Err(_) => Some(ProcessedTokenState::Include(Default::default()))
530 }
531 },
532 Err(_) => Some(ProcessedTokenState::Include(Default::default())) }
534 }
535 else if token.is_incbin() {
536 Some(ProcessedTokenState::Incbin(Default::default()))
537 }
538 else if token.is_crunched_section() {
539 Some(ProcessedTokenState::CrunchedSection {
540 listing: SimpleListingState {
541 processed_tokens: build_processed_tokens_list(
542 token.crunched_section_listing(),
543 env
544 )?,
545 span: token.possible_span().cloned()
546 },
547 previous_bytes: None,
548 previous_compressed_bytes: None
549 })
550 }
551 else if token.is_for() {
552 Some(ProcessedTokenState::For(SimpleListingState {
553 processed_tokens: build_processed_tokens_list(token.for_listing(), env)?,
554 span: token.possible_span().cloned()
555 }))
556 }
557 else if token.is_function_definition() {
558 Some(ProcessedTokenState::FunctionDefinition(
559 FunctionDefinitionState(None)
560 ))
561 }
562 else if token.is_iterate() {
563 Some(ProcessedTokenState::Iterate(SimpleListingState {
564 processed_tokens: build_processed_tokens_list(token.iterate_listing(), env)?,
565 span: token.possible_span().cloned()
566 }))
567 }
568 else if token.is_module() {
569 Some(ProcessedTokenState::Module(SimpleListingState {
570 processed_tokens: build_processed_tokens_list(token.module_listing(), env)?,
571 span: token.possible_span().cloned()
572 }))
573 }
574 else if token.is_repeat() {
575 Some(ProcessedTokenState::Repeat(SimpleListingState {
576 processed_tokens: build_processed_tokens_list(token.repeat_listing(), env)?,
577 span: token.possible_span().cloned()
578 }))
579 }
580 else if token.is_repeat_token() {
581 Some(ProcessedTokenState::RepeatToken(
582 SingleTokenState {
583 token: Box::new(build_processed_token(token.repeat_token(), env)?)
584 },
585 token.repeat_count()
586 ))
587 }
588 else if token.is_assembler_control()
589 && token
590 .assembler_control_command()
591 .is_restricted_assembling_environment()
592 {
593 assert!(
594 token.assembler_control_get_max_passes().is_some(),
595 "We currently only support a maximum number of passes, so it as to be provided ..."
596 );
597
598 let passes = match token.assembler_control_get_max_passes() {
599 Some(passes) => Some(env.resolve_expr_must_never_fail(passes)?.int()? as u8),
600 None => None
601 };
602
603 let tokens = token.assembler_control_get_listing();
604 Some(ProcessedTokenState::RestrictedAssemblingEnvironment {
605 listing: SimpleListingState {
606 processed_tokens: build_processed_tokens_list(tokens, env)?,
607 span: token.possible_span().cloned()
608 },
609 commands: Some(ControlOutputStore::with_passes(passes.unwrap()))
610 })
611 }
612 else if token.is_repeat_until() {
613 Some(ProcessedTokenState::RepeatUntil(SimpleListingState {
614 processed_tokens: build_processed_tokens_list(token.repeat_until_listing(), env)?,
615 span: token.possible_span().cloned()
616 }))
617 }
618 else if token.is_rorg() {
619 Some(ProcessedTokenState::Rorg(SimpleListingState {
620 processed_tokens: build_processed_tokens_list(token.rorg_listing(), env)?,
621 span: token.possible_span().cloned()
622 }))
623 }
624 else if token.is_switch() {
625 Some(ProcessedTokenState::Switch(SwitchState {
627 cases: token
628 .switch_cases()
629 .map(|(_v, l, _b)| {
630 SimpleListingState::build(l, token.possible_span().cloned(), env)
631 })
632 .collect::<Result<Vec<_>, _>>()?,
633
634 default: token
635 .switch_default()
636 .map(|l| SimpleListingState::build(l, token.possible_span().cloned(), env))
637 .transpose()?
638 }))
639 }
640 else if token.is_warning() {
641 Some(ProcessedTokenState::Warning(Box::new(
642 build_processed_token(token.warning_token(), env)?
643 )))
644 }
645 else if token.is_while() {
646 Some(ProcessedTokenState::While(SimpleListingState {
647 processed_tokens: build_processed_tokens_list(token.while_listing(), env)?,
648 span: token.possible_span().cloned()
649 }))
650 }
651 else if token.is_call_macro_or_build_struct() {
652 None
654 }
655 else {
656 None
657 };
658
659 Ok(ProcessedToken { token, state })
660}
661
662pub fn build_processed_tokens_list<
663 'token,
664 T: Visited + Debug + Sync + ListingElement + MayHaveSpan
665>(
666 tokens: &'token [T],
667 env: &mut Env
668) -> Result<Vec<ProcessedToken<'token, T>>, AssemblerError>
669where
670 <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt
671{
672 let show_progress = env.options().parse_options().show_progress;
673 if show_progress {
674 let iter = tokens.iter();
679
680 let include_fnames = iter
682 .filter(|t| t.include_is_standard_include())
683 .flat_map(|t| {
684 let fname = t.include_fname();
685 let fname = env.build_fname(fname)?;
686 get_filename_to_read(fname, env.options().parse_options(), Some(env))
687 })
688 .collect::<Vec<_>>();
689 let include_fnames = include_fnames.iter().map(|t| progress::normalize(t));
690
691 Progress::progress().add_parses(include_fnames);
694 }
695
696 #[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
699 let iter = tokens.par_iter();
700 #[cfg(any(target_arch = "wasm32", not(feature = "rayon")))]
701 let iter = tokens.iter();
702 iter.map(|t| build_processed_token(t, env))
703 .collect::<Result<Vec<_>, _>>()
704}
705
706pub fn visit_processed_tokens<'token, T: Visited + Debug + ListingElement + Sync + MayHaveSpan>(
708 tokens: &mut [ProcessedToken<'token, T>],
709 env: &mut Env
710) -> Result<(), AssemblerError>
711where
712 <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
713 <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
714 ProcessedToken<'token, T>: FunctionBuilder
715{
716 if env.return_value.is_some() {
718 return Ok(());
719 }
720
721 let options = env.options();
722
723 if options.show_progress() {
724 Progress::progress().add_expected_to_pass(tokens.len() as _);
726 for chunk in &tokens.iter_mut().chunks(64) {
727 let mut visited = 0;
728 for token in chunk {
729 token.visited(env)?;
730 visited += 1;
731 }
732
733 Progress::progress().add_visited_to_pass(visited);
734 }
735 }
736 else {
737 for token in tokens.iter_mut() {
739 token.visited(env)?;
740 }
741 }
742
743 env.cleanup_warnings();
744
745 Ok(())
746}
747
748impl<T: Visited + Debug + ListingElement + Sync + MayHaveSpan> MayHaveSpan for ProcessedToken<'_, T>
749where <T as ListingElement>::Expr: ExprEvaluationExt
750{
751 fn possible_span(&self) -> Option<&Z80Span> {
752 self.token.possible_span()
753 }
754
755 fn span(&self) -> &Z80Span {
756 self.token.span()
757 }
758
759 fn has_span(&self) -> bool {
760 self.token.has_span()
761 }
762}
763
764impl<T: Visited + Debug + ListingElement + Sync + MayHaveSpan> ProcessedToken<'_, T>
765where <T as ListingElement>::Expr: ExprEvaluationExt
766{
767 #[inline]
769 pub fn update_macro_or_struct_state(&mut self, env: &mut Env) -> Result<(), AssemblerError>
770 where <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt {
771 let caller = self.token;
772 let name = caller.macro_call_name();
773 let parameters = caller.macro_call_arguments();
774
775 let listing = {
776 let r#macro = env
778 .symbols()
779 .macro_value(name)?
780 .map(|m| r#macro::MacroWithArgs::build(m, parameters))
781 .transpose()?;
782 let r#struct = if r#macro.is_none() {
783 env.symbols()
784 .struct_value(name)?
785 .map(|s| r#macro::StructWithArgs::build(s, parameters))
786 .transpose()?
787 }
788 else {
789 None
790 };
791
792 if r#macro.is_none() && r#struct.is_none() {
794 let e = AssemblerError::UnknownMacro {
795 symbol: name.into(),
796 closest: env.symbols().closest_symbol(name, SymbolFor::Macro)?
797 };
798 return match self.possible_span() {
799 Some(span) => {
800 Err(AssemblerError::RelocatedError {
801 error: e.into(),
802 span: span.clone()
803 })
804 },
805 None => Err(e)
806 };
807 }
808
809 let (source, code, flavor) = if let Some(r#macro) = &r#macro {
812 let source = r#macro.source();
813 let flavor = r#macro.flavor();
814 let code = r#macro.expand(env)?;
815 (source, code, flavor)
816 }
817 else {
818 let r#struct = r#struct.as_ref().unwrap();
819 let mut parameters = parameters.to_vec();
820 parameters.resize(r#struct.r#struct().nb_args(), T::MacroParam::empty());
821 (
822 r#struct.source(),
823 r#struct.expand(env)?,
824 AssemblerFlavor::Basm
825 )
826 };
827
828 let listing = match self.token.possible_span() {
830 Some(span) => {
831 use crate::ParserContextBuilder;
832 let ctx_builder = ParserContextBuilder::default() .set_state(span.state.state)
835 .set_options(span.state.options.clone())
836 .set_context_name(format!(
837 "{}:{}:{} > {} {}:",
838 source.map(|s| s.fname()).unwrap_or_else(|| "???"),
839 source.map(|s| s.line()).unwrap_or(0),
840 source.map(|s| s.column()).unwrap_or(0),
841 if r#macro.is_some() { "MACRO" } else { "STRUCT" },
842 name,
843 ));
844 parse_z80_with_context_builder(code, ctx_builder)?
845 },
846 _ => {
847 use crate::parse_z80_str;
848 parse_z80_str(&code)?
849 }
850 };
851 listing
852 };
853
854 let expand_state = ExpandStateTryBuilder {
855 listing,
856 processed_tokens_builder: |listing: &LocatedListing| {
857 build_processed_tokens_list(listing, env)
858 }
859 }
860 .try_build()?;
861
862 self.state = Some(ProcessedTokenState::MacroCallOrBuildStruct(expand_state));
863
864 Ok(())
865 }
866}
867
868impl<'token, T: Visited + Debug + ListingElement + Sync + MayHaveSpan> ProcessedToken<'token, T>
869where
870 <T as cpclib_tokens::ListingElement>::Expr: ExprEvaluationExt,
871 <<T as cpclib_tokens::ListingElement>::TestKind as TestKindElement>::Expr: ExprEvaluationExt,
872 ProcessedToken<'token, T>: FunctionBuilder
873{
874 pub fn visited(&mut self, env: &mut Env) -> Result<(), AssemblerError> {
876 let possible_span = self.possible_span().cloned();
877
878 let mut really_does_the_job = move |possible_span: Option<&Z80Span>| {
879 let deferred = self.token.defer_listing_output();
880 if !deferred {
881 let outer_token = unsafe {
883 (self.token as *const T as *const LocatedToken)
884 .as_ref()
885 .unwrap()
886 };
887
888 env.handle_output_trigger(outer_token);
889 }
890
891 if self.token.is_call_macro_or_build_struct() {
893 self.update_macro_or_struct_state(env)?;
894 }
895
896 if self.token.is_macro_definition() {
898 let name = self.token.macro_definition_name();
900 let arguments = self.token.macro_definition_arguments();
901 let code = self.token.macro_definition_code();
902 env.visit_macro_definition(
903 name,
904 &arguments,
905 code,
906 self.possible_span(),
907 self.token.macro_flavor()
908 )
909 }
910 else {
912 match &mut self.state {
914 Some(ProcessedTokenState::RestrictedAssemblingEnvironment {
915 listing,
916 commands
917 }) => {
918 let mut new_commands = commands.take().unwrap();
919
920 if !new_commands.has_remaining_passes() {
921 new_commands.execute(env)?;
922 }
923 else {
924 new_commands.new_pass();
926 env.assembling_control_current_output_commands
927 .push(new_commands);
928 visit_processed_tokens(&mut listing.processed_tokens, env)?;
929 new_commands = env
930 .assembling_control_current_output_commands
931 .pop()
932 .unwrap();
933 }
934 commands.replace(new_commands);
935 Ok(())
936 },
937
938 Some(ProcessedTokenState::Confined(SimpleListingState {
939 processed_tokens,
940 span
941 })) => env.visit_confined(processed_tokens, span.as_ref()),
942 Some(ProcessedTokenState::CrunchedSection {
943 listing:
944 SimpleListingState {
945 processed_tokens,
946 span
947 },
948 previous_bytes,
949 previous_compressed_bytes
950 }) => {
951 env.visit_crunched_section(
952 self.token.crunched_section_kind(),
953 processed_tokens,
954 previous_bytes,
955 previous_compressed_bytes,
956 span.as_ref()
957 )
958 },
959
960 Some(ProcessedTokenState::For(SimpleListingState {
961 processed_tokens,
962 span
963 })) => {
964 env.visit_for(
965 self.token.for_label(),
966 self.token.for_start(),
967 self.token.for_stop(),
968 self.token.for_step(),
969 processed_tokens,
970 span.as_ref()
971 )
972 },
973
974 Some(ProcessedTokenState::RepeatToken(state, count)) => {
975 env.visit_repeat_token(&mut state.token, count)
976 },
977
978 Some(ProcessedTokenState::FunctionDefinition(FunctionDefinitionState(
979 Some(_fun)
980 ))) => {
981 Ok(())
983 },
984 Some(ProcessedTokenState::FunctionDefinition(FunctionDefinitionState(
985 option
986 ))) => {
987 let name = self.token.function_definition_name();
988 if !env.functions.contains_key(name) {
989 let inner = self.token.function_definition_inner();
990 let params = self.token.function_definition_params();
991
992 let inner = build_processed_tokens_list(inner, env)?;
993 let f =
994 Arc::new(unsafe { FunctionBuilder::new(&name, ¶ms, inner) }?);
995 option.replace(f.clone());
996
997 env.functions.insert(name.to_owned(), f);
998 }
999 else {
1000 }
1002 Ok(())
1003 },
1004
1005 Some(ProcessedTokenState::Incbin(IncbinState { contents })) => {
1006 if cfg!(target_arch = "wasm32") {
1007 return Err(AssemblerError::AssemblingError { msg:
1008 "INCBIN-like directives are not allowed in a web-based assembling.".to_owned()
1009 });
1010 }
1011
1012 let fname = self.token.incbin_fname();
1014 let fname = env.build_fname(fname)?;
1015 let to_print_fname = &fname;
1016 let fname =
1017 get_filename_to_read(&fname, env.options().parse_options(), Some(env))?;
1018
1019 if let Some(extension) = fname.extension() {
1021 match extension.to_ascii_uppercase().as_str() {
1022 "ASM" | "Z80" => {
1023 let warning = format!(
1024 "{} seems to be a source code and not a binary file.",
1025 &fname
1026 );
1027 env.add_warning(dbg!(AssemblerWarning::AssemblingError {
1028 msg: warning
1029 }));
1030 },
1031 _ => {}
1032 }
1033 }
1034
1035 let data = if !contents.contains_key(&fname) {
1037 let (data, header) =
1040 load_file(fname.as_path(), env.options().parse_options())?;
1041
1042 if let Some(header) = header {
1043 let ams_fname = header
1044 .amsdos_filename()
1045 .map(|ams_fname| ams_fname.filename_with_user())
1046 .unwrap_or_else(|_| "<WRONG FILENAME>".to_owned());
1047 let txt = match header.file_type() {
1048 Ok(AmsdosFileType::Binary) => {
1049 format! {"{to_print_fname}|{ams_fname} BINARY L:0x{:x} X:0x{:x}", header.loading_address(), header.execution_address()}
1050 },
1051 Ok(AmsdosFileType::Protected) => {
1052 format! {"{to_print_fname}|{ams_fname} PROTECTED L:0x{:x} X:0x{:x}", header.loading_address(), header.execution_address()}
1053 },
1054 Ok(AmsdosFileType::Basic) => format!("{ams_fname} BASIC"),
1055 Err(_) => format!("{ams_fname} <WRONG FILETYPE>")
1056 };
1057
1058 let warning = AssemblerWarning::AssemblingError {
1059 msg: format!("Header has been removed for {txt}")
1060 };
1061 let warning = if let Some(span) = possible_span {
1062 warning.locate_warning(span.clone())
1063 }
1064 else {
1065 warning
1066 };
1067
1068 env.add_warning(warning)
1069 }
1070
1071 contents.try_insert(fname.clone(), data.into()).unwrap()
1072 }
1073 else {
1074 contents.get(&fname).unwrap()
1075 };
1076
1077 let mut data = data.as_slice();
1078
1079 let offset = self.token.incbin_offset();
1081 let length = self.token.incbin_length();
1082 let transformation = self.token.incbin_transformation();
1083
1084 if let Some(offset) = offset {
1085 let offset = env.resolve_expr_must_never_fail(offset)?.int()? as usize;
1086 if offset >= data.len() {
1087 return Err(AssemblerError::AssemblingError {
1088 msg: format!(
1089 "Unable to read {:?}. Only {} are available",
1090 self.token.incbin_fname(),
1091 data.len()
1092 )
1093 });
1094 }
1095 data = &data[offset..];
1096 }
1097
1098 if let Some(length) = length {
1099 let length = env.resolve_expr_must_never_fail(length)?.int()? as usize;
1100 if data.len() < length {
1101 return Err(AssemblerError::AssemblingError {
1102 msg: format!(
1103 "Unable to read {:?}. Only {} bytes are available ({} expected)",
1104 self.token.incbin_fname(),
1105 data.len(),
1106 length
1107 )
1108 });
1109 }
1110 data = &data[..length];
1111 }
1112
1113 let data = match transformation {
1114 BinaryTransformation::None => Cow::Borrowed(data),
1115
1116 other => {
1117 if data.is_empty() {
1118 return Err(AssemblerError::EmptyBinaryFile(
1119 self.token.incbin_fname().to_string()
1120 ));
1121 }
1122
1123 let crunch_type = other.crunch_type().unwrap();
1124 Cow::Owned(crunch_type.crunch(data)?)
1125 }
1126 };
1127
1128 env.visit_incbin(data.borrow())
1129 },
1130
1131 Some(ProcessedTokenState::Include(state)) => {
1132 let fname = env.build_fname(self.token.include_fname())?;
1133
1134 state.handle(
1135 env,
1136 &fname,
1137 self.token.include_namespace(),
1138 self.token.include_once()
1139 )
1140 },
1141
1142 Some(ProcessedTokenState::If(if_state)) => {
1143 let listing = if_state.choose_listing_to_assemble(env)?;
1144
1145 if let Some(listing) = listing {
1146 visit_processed_tokens(listing, env)?;
1147 }
1148
1149 Ok(())
1150 },
1151
1152 Some(ProcessedTokenState::Iterate(SimpleListingState {
1153 processed_tokens,
1154 span
1155 })) => {
1156 env.visit_iterate(
1157 self.token.iterate_counter_name(),
1158 self.token.iterate_values(),
1159 processed_tokens,
1160 span.as_ref()
1161 )
1162 },
1163
1164 Some(ProcessedTokenState::MacroCallOrBuildStruct(state)) => {
1165 let name = self.token.macro_call_name();
1166
1167 env.inc_macro_seed();
1168 let seed = env.macro_seed();
1169 env.symbols_mut().push_seed(seed);
1170
1171 let nb_prints = env
1174 .sna
1175 .pages_info
1176 .iter()
1177 .map(|ti| ti.print_commands().len())
1178 .collect_vec();
1179
1180 state
1181 .with_processed_tokens_mut(|listing| {
1182 let tokens: &mut [ProcessedToken<'_, LocatedToken>] =
1183 &mut listing[..];
1184 visit_processed_tokens::<'_, LocatedToken>(tokens, env)
1185 })
1186 .map_err(|e| {
1187 let location = env
1188 .symbols()
1189 .any_value(name)
1190 .unwrap()
1191 .unwrap()
1192 .location()
1193 .cloned();
1194
1195 let e = AssemblerError::MacroError {
1196 name: name.into(),
1197 root: Box::new(e),
1198 location
1199 };
1200 let caller_span = self.possible_span();
1201 match caller_span {
1202 Some(span) => {
1203 AssemblerError::RelocatedError {
1204 error: e.into(),
1205 span: span.clone()
1206 }
1207 },
1208 None => e
1209 }
1210 })?;
1211
1212 let caller_span = self.possible_span();
1213 if let Some(span) = caller_span {
1214 env.sna
1215 .pages_info
1216 .iter_mut()
1217 .zip(nb_prints.into_iter())
1218 .for_each(|(ti, count)| {
1219 ti.print_commands_mut()[count..]
1220 .iter_mut()
1221 .for_each(|cmd| cmd.relocate(span.clone()))
1222 });
1223 }
1224
1225 env.symbols_mut().pop_seed();
1226
1227 Ok(())
1228 },
1229
1230 Some(ProcessedTokenState::Module(SimpleListingState {
1231 processed_tokens,
1232 ..
1233 })) => {
1234 env.enter_namespace(self.token.module_name())?;
1235 visit_processed_tokens(processed_tokens, env)?;
1236 env.leave_namespace()?;
1237
1238 Ok(())
1239 },
1240
1241 Some(ProcessedTokenState::Repeat(SimpleListingState {
1242 processed_tokens,
1243 ..
1244 })) => {
1245 env.visit_repeat(
1246 self.token.repeat_count(),
1247 processed_tokens,
1248 self.token.repeat_counter_name(),
1249 self.token.repeat_counter_start(),
1250 self.token.repeat_counter_step(),
1251 self.token.possible_span()
1252 )
1253 },
1254
1255 Some(ProcessedTokenState::RepeatUntil(SimpleListingState {
1256 processed_tokens,
1257 ..
1258 })) => {
1259 env.visit_repeat_until(
1260 self.token.repeat_until_condition(),
1261 processed_tokens,
1262 self.token.possible_span()
1263 )
1264 },
1265
1266 Some(ProcessedTokenState::Rorg(SimpleListingState {
1267 processed_tokens,
1268 span
1269 })) => env.visit_rorg(self.token.rorg_expr(), processed_tokens, span.as_ref()),
1270
1271 Some(ProcessedTokenState::Switch(state)) => {
1272 let value = env.resolve_expr_must_never_fail(self.token.switch_expr())?;
1273 let mut met = false;
1274 let mut broken = false;
1275 for (case, listing, r#break) in state
1276 .cases
1277 .iter_mut()
1278 .zip(self.token.switch_cases())
1279 .map(|(pt, t)| (t.0, pt.tokens_mut(), t.2))
1280 {
1281 let case = env.resolve_expr_must_never_fail(case)?;
1283 met |= case == value;
1284
1285 if met {
1287 visit_processed_tokens(listing, env)?;
1288 if r#break {
1289 broken = true;
1290 break;
1291 }
1292 }
1293 }
1294
1295 if !met || !broken {
1297 if let Some(default) = &mut state.default {
1298 visit_processed_tokens(&mut default.processed_tokens, env)?;
1299 }
1300 }
1301
1302 Ok(())
1303 },
1304
1305 Some(ProcessedTokenState::Warning(box token)) => {
1306 let warning = AssemblerError::RelocatedWarning {
1307 warning: Box::new(AssemblerError::AssemblingError {
1308 msg: self.token.warning_message().to_owned()
1309 }),
1310 span: self.token.possible_span().unwrap().clone()
1311 };
1312 let warning = AssemblerError::AlreadyRenderedError(warning.to_string());
1313
1314 env.add_warning(warning);
1315 token.visited(env)
1316 },
1317 Some(ProcessedTokenState::While(SimpleListingState {
1318 processed_tokens,
1319 ..
1320 })) => {
1321 env.visit_while(
1322 self.token.while_expr(),
1323 processed_tokens,
1324 self.token.possible_span()
1325 )
1326 },
1327
1328 None => self.token.visited(env)
1330 }
1331 }?;
1332
1333 if !self.token.is_buildcpr() {
1334 env.update_dollar();
1336 }
1337
1338 if deferred {
1339 let outer_token = unsafe {
1340 (self.token as *const T as *const LocatedToken)
1341 .as_ref()
1342 .unwrap()
1343 };
1344
1345 env.handle_output_trigger(outer_token);
1346 }
1347 Ok(())
1348 };
1349
1350 really_does_the_job(possible_span.as_ref()).map_err(|e| {
1351 let e = match possible_span {
1352 Some(span) => e.locate(span.clone()),
1353 None => e
1354 };
1355 AssemblerError::AlreadyRenderedError(e.to_string())
1356 })
1357 }
1358}
1359
1360#[cfg(test)]
1398mod test_super {
1399 use super::*;
1400
1401 #[test]
1402 fn test_located_include() {
1403 use crate::parse_z80;
1404
1405 let src = "include \"toto\"";
1406
1407 let tokens = parse_z80(src).unwrap();
1408 let token = &tokens[0];
1409 let mut env = Env::default();
1410
1411 let processed = build_processed_token(token, &mut env);
1412 assert!(matches!(
1413 processed.unwrap().state,
1414 Some(ProcessedTokenState::Include(..))
1415 ));
1416 }
1417}