1use std::collections::BTreeMap;
2
3use codespan_reporting::diagnostic::Severity;
4use cpclib_common::event::EventObserver;
5use cpclib_common::itertools::Itertools;
6use cpclib_sna::{
7 AceBreakPoint, AceBrkRuntimeMode, AdvancedRemuBreakPoint, RemuBreakPoint, WabpAnyBreakpoint,
8 WinapeBreakPoint
9};
10#[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
11use {cpclib_common::rayon::prelude::*, rayon_cond::CondIterator};
12
13use super::report::SavedFile;
14use super::save_command::SaveCommand;
15use super::string::PreprocessedFormattedString;
16use super::{Env, EnvEventObserver};
17use crate::error::{AssemblerError, build_simple_error_message};
18use crate::preamble::Z80Span;
19
20trait DelayedCommand {}
21
22#[derive(Debug, Clone)]
23pub struct PrintCommand {
24 pub(crate) prefix: Option<String>,
25 pub(crate) span: Option<Z80Span>,
26 pub(crate) print_or_error: either::Either<PreprocessedFormattedString, AssemblerError>
27}
28
29impl PrintCommand {
30 pub fn relocate(&mut self, span: Z80Span) {
31 self.span.replace(span);
32 }
33}
34#[derive(Debug, Clone)]
35pub struct FailedAssertCommand {
36 failure: AssemblerError
37}
38
39impl From<AssemblerError> for FailedAssertCommand {
41 fn from(failure: AssemblerError) -> Self {
42 Self { failure }
43 }
44}
45
46impl DelayedCommand for PrintCommand {}
47
48impl DelayedCommand for FailedAssertCommand {}
49
50impl PrintCommand {
51 #[inline]
52 pub fn string_or_error(&self) -> Result<String, AssemblerError> {
53 match &self.print_or_error {
54 either::Either::Left(msg) => {
55 let file_location = if let Some(span) = &self.span {
57 let fname = span.filename();
58 let (line, col) = span.relative_line_and_column();
59
60 Some((fname, line, col))
61 }
62 else {
63 None
64 };
65
66 let repr = match (&self.prefix, file_location) {
68 (Some(prefix), Some(loc)) => {
69 format!("{}{}:{}:{} PRINT: {}", prefix, loc.0, loc.1, loc.2, msg)
70 },
71
72 (Some(prefix), None) => {
73 format!("{} PRINT: {}", prefix, msg)
74 },
75
76 (None, Some(loc)) => {
77 format!("{}:{}:{} PRINT: {}", loc.0, loc.1, loc.2, msg)
78 },
79
80 (None, None) => {
81 format!("PRINT: {}", msg)
82 }
83 };
84
85 Ok(repr)
86 },
87 either::Either::Right(e) => Err(e.clone())
88 }
89 }
90
91 #[inline]
93 pub fn execute(&self, writer: &dyn EnvEventObserver) -> Result<(), AssemblerError> {
94 match &self.print_or_error {
95 either::Either::Left(msg) => {
96 let file_location = if let Some(span) = &self.span {
98 let fname = span.filename();
99 let (line, col) = span.relative_line_and_column();
100
101 Some((fname, line, col))
102 }
103 else {
104 None
105 };
106
107 match (&self.prefix, file_location) {
109 (Some(prefix), Some(loc)) => {
110 writer.emit_stdout(&format!(
111 "{}{}:{}:{} PRINT: {}\n",
112 prefix, loc.0, loc.1, loc.2, msg
113 ))
114 },
115
116 (Some(prefix), None) => {
117 writer.emit_stdout(&format!("{} PRINT: {}\n", prefix, msg))
118 },
119
120 (None, Some(loc)) => {
121 writer.emit_stdout(&format!("{}:{}:{} PRINT: {}", loc.0, loc.1, loc.2, msg))
122 },
123
124 (None, None) => writer.emit_stdout(&format!("PRINT: {}", msg))
125 };
126
127 Ok(())
128 },
129 either::Either::Right(e) => Err(e.clone())
130 }
131 }
132
133 #[inline]
134 pub fn is_print(&self) -> bool {
135 self.print_or_error.is_left()
136 }
137}
138#[derive(Debug, Clone)]
139
140pub struct PauseCommand(Option<Z80Span>);
141
142impl From<Option<Z80Span>> for PauseCommand {
143 fn from(s: Option<Z80Span>) -> Self {
144 Self(s)
145 }
146}
147
148impl PauseCommand {
149 #[inline]
150 pub fn execute(&self, writer: &dyn EnvEventObserver) -> Result<(), AssemblerError> {
151 let msg = "PAUSE - press enter to continue.";
152 writer.emit_stdout(
153 &(if let Some(span) = &self.0 {
154 build_simple_error_message(msg, span, Severity::Note)
155 }
156 else {
157 msg.to_owned()
158 })
159 .to_string()
160 );
161
162 let mut buf = String::new();
163 std::io::stdin().read_line(&mut buf).unwrap();
164 Ok(())
165 }
166
167 pub fn relocate(&mut self, span: Z80Span) {
168 self.0.replace(span);
169 }
170}
171
172#[derive(Debug, Clone)]
173pub enum PrintOrPauseCommand {
174 Print(PrintCommand),
175 Pause(PauseCommand)
176}
177
178impl From<PrintCommand> for PrintOrPauseCommand {
179 fn from(p: PrintCommand) -> Self {
180 PrintOrPauseCommand::Print(p)
181 }
182}
183
184impl From<PauseCommand> for PrintOrPauseCommand {
185 fn from(p: PauseCommand) -> Self {
186 PrintOrPauseCommand::Pause(p)
187 }
188}
189
190impl PrintOrPauseCommand {
191 pub fn execute(&self, writer: &dyn EnvEventObserver) -> Result<(), AssemblerError> {
192 match self {
193 PrintOrPauseCommand::Print(p) => p.execute(writer),
194 PrintOrPauseCommand::Pause(p) => p.execute(writer)
195 }
196 }
197
198 pub fn relocate(&mut self, span: Z80Span) {
199 match self {
200 PrintOrPauseCommand::Print(p) => p.relocate(span),
201 PrintOrPauseCommand::Pause(p) => p.relocate(span)
202 }
203 }
204}
205
206#[derive(Debug, Clone)]
209pub struct BreakpointCommand {
210 pub(crate) brk: InnerBreakpointCommand,
211 pub(crate) span: Option<Z80Span>
212}
213
214impl BreakpointCommand {
215 pub fn info_repr(&self) -> String {
216 match &self.brk {
217 InnerBreakpointCommand::Simple(brk) => {
218 format! {"PC=&{:X}@{}", brk.address, brk.page}
219 },
220 InnerBreakpointCommand::Advanced(brk) => {
221 format! {"{}", brk}
222 }
223 }
224 }
225}
226
227#[derive(Debug, Clone)]
228pub enum InnerBreakpointCommand {
229 Simple(BreakPointCommandSimple),
230 Advanced(AdvancedRemuBreakPoint)
231}
232
233impl From<AdvancedRemuBreakPoint> for InnerBreakpointCommand {
234 fn from(value: AdvancedRemuBreakPoint) -> Self {
235 Self::Advanced(value)
236 }
237}
238
239impl From<BreakPointCommandSimple> for InnerBreakpointCommand {
240 fn from(value: BreakPointCommandSimple) -> Self {
241 Self::Simple(value)
242 }
243}
244
245#[derive(Debug, Clone)]
246pub struct BreakPointCommandSimple {
247 pub(crate) address: u16,
248 pub(crate) page: u8
249}
250
251impl<T: Into<InnerBreakpointCommand>> From<(T, Option<Z80Span>)> for BreakpointCommand {
252 fn from(value: (T, Option<Z80Span>)) -> Self {
253 Self {
254 brk: value.0.into(),
255 span: value.1
256 }
257 }
258}
259
260impl BreakpointCommand {
261 pub fn new_simple(address: u16, page: u8, span: Option<Z80Span>) -> Self {
262 (BreakPointCommandSimple { address, page }, span).into()
263 }
264
265 pub fn winape(&self) -> Option<WinapeBreakPoint> {
267 match &self.brk {
268 InnerBreakpointCommand::Simple(brk) => {
269 Some(WinapeBreakPoint::new(brk.address, brk.page))
270 },
271 _ => None
272 }
273 }
274
275 pub fn ace(&self) -> Option<AceBreakPoint> {
277 match &self.brk {
278 InnerBreakpointCommand::Simple(brk) => {
279 Some(AceBreakPoint::new_execution(
280 brk.address,
281 AceBrkRuntimeMode::Break,
282 cpclib_sna::AceMemMapType::Undefined
283 ))
284 },
285 _ => None
286 }
287 }
288
289 pub fn remu(&self) -> RemuBreakPoint {
290 match &self.brk {
291 InnerBreakpointCommand::Simple(brk) => RemuBreakPoint::Memory(brk.address, brk.page),
292 InnerBreakpointCommand::Advanced(brk) => RemuBreakPoint::Advanced(brk.clone())
293 }
294 }
295
296 pub fn wabp(&self) -> WabpAnyBreakpoint {
297 match &self.brk {
298 InnerBreakpointCommand::Simple(brk) => WabpAnyBreakpoint::new(brk.address),
299 InnerBreakpointCommand::Advanced(advanced_remu_break_point) => {
300 unimplemented!("{advanced_remu_break_point} not converted in wabp")
301 }
302 }
303 }
304}
305
306#[derive(Debug, Clone, Default)]
307pub struct DelayedCommands {
308 failed_assert_commands: Vec<FailedAssertCommand>,
309 save_commands: BTreeMap<u8, Vec<SaveCommand>>, print_commands: Vec<PrintOrPauseCommand>,
311 breakpoint_commands: Vec<BreakpointCommand>
312}
313
314impl DelayedCommands {
315 pub fn clear(&mut self) {
316 self.failed_assert_commands.clear();
317 self.save_commands.clear();
318 self.print_commands.clear();
319 self.breakpoint_commands.clear();
320 }
321}
322
323impl DelayedCommands {
325 pub fn add_breakpoint_command(&mut self, command: BreakpointCommand) {
326 self.breakpoint_commands.push(command);
327 }
328
329 pub fn add_save_command(&mut self, command: SaveCommand) {
330 self.save_commands
331 .entry(command.ga_mmr())
332 .or_default()
333 .push(command);
334 }
335
336 pub fn get_save_mmrs(&self) -> Vec<u8> {
337 self.save_commands.keys().cloned().collect_vec()
338 }
339
340 pub fn can_save_in_parallel(&self) -> bool {
342 self.save_commands
343 .values()
344 .all(|s| s.iter().all(|s| s.can_be_saved_in_parallel()))
345 }
346
347 pub fn add_failed_assert_command(&mut self, command: FailedAssertCommand) {
348 self.failed_assert_commands.push(command);
349 }
350
351 pub fn add_print_command(&mut self, command: PrintCommand) {
352 self.add_print_or_pause_command(command.into());
353 }
354
355 pub fn add_pause_command(&mut self, command: PauseCommand) {
356 self.add_print_or_pause_command(command.into());
357 }
358
359 pub fn add_print_or_pause_command(&mut self, command: PrintOrPauseCommand) {
360 self.print_commands.push(command)
361 }
362}
363
364impl DelayedCommands {
366 pub fn execute_save(&self, env: &Env, ga_mmr: u8) -> Result<Vec<SavedFile>, AssemblerError> {
368 #[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
369 let iter = CondIterator::new(&self.save_commands, self.can_save_in_parallel());
370 #[cfg(any(target_arch = "wasm32", not(feature = "rayon")))]
371 let iter = self.save_commands.iter();
372
373 let res = iter
374 .filter_map(|(save_mmr, save_cmd)| {
375 if *save_mmr == ga_mmr {
376 Some(save_cmd)
377 }
378 else {
379 None
380 }
381 })
382 .flatten()
383 .map(|cmd| cmd.execute_on(env))
384 .collect::<Result<Vec<_>, AssemblerError>>()?;
385
386 Ok(res)
387 }
388
389 pub fn nb_files_to_save(&self) -> usize {
390 self.save_commands.len()
391 }
392
393 pub fn collect_assert_failure(&self) -> Result<(), AssemblerError> {
395 if self.failed_assert_commands.is_empty() {
396 Ok(())
397 }
398 else {
399 Err(AssemblerError::MultipleErrors {
400 errors: self
401 .failed_assert_commands
402 .iter()
403 .map(|a| a.failure.clone())
404 .collect_vec()
405 })
406 }
407 }
408
409 pub fn execute_print_or_pause(
411 &self,
412 writer: &dyn EnvEventObserver
413 ) -> Result<(), AssemblerError> {
414 let iter = self.print_commands.iter();
415
416 let errors = iter
417 .filter_map(|c| {
418 match c {
419 PrintOrPauseCommand::Print(p) => {
420 if p.is_print() {
421 p.execute(writer);
422 None
423 }
424 else {
425 Some(p.print_or_error.as_ref().right().unwrap().clone())
426 }
427 },
428 PrintOrPauseCommand::Pause(p) => {
429 p.execute(writer);
430 None
431 }
432 }
433 })
434 .collect::<Vec<_>>();
435
436 if errors.is_empty() {
437 Ok(())
438 }
439 else {
440 Err(AssemblerError::MultipleErrors { errors })
441 }
442 }
443}
444
445impl DelayedCommands {
446 pub fn print_commands(&self) -> &[PrintOrPauseCommand] {
447 &self.print_commands
448 }
449
450 pub fn print_commands_mut(&mut self) -> &mut [PrintOrPauseCommand] {
451 &mut self.print_commands
452 }
453
454 pub fn failed_assert_commands(&self) -> &[FailedAssertCommand] {
455 &self.failed_assert_commands
456 }
457
458 pub fn failed_assert_commands_mut(&mut self) -> &mut [FailedAssertCommand] {
459 &mut self.failed_assert_commands
460 }
461}
462
463impl DelayedCommands {
464 pub fn collect_breakpoints(&self) -> &[BreakpointCommand] {
465 &self.breakpoint_commands
466 }
467}