1use std::fmt::Display;
2use std::ops::Deref;
3
4use beef::lean::Cow;
5use cpclib_common::camino::Utf8Path;
6use cpclib_common::itertools::Itertools;
7use cpclib_tokens::{
8 BinaryOperation, BinaryTransformation, DataAccess, DataAccessElem, Expr, ExprElement,
9 ListingElement, MacroParam, MacroParamElement, Mnemonic, TestKind, TestKindElement, Token,
10 UnaryOperation
11};
12
13use crate::{
14 LocatedDataAccess, LocatedExpr, LocatedMacroParam, LocatedTestKind, MayHaveSpan, ParserContext,
15 ParserContextBuilder, SourceString, TokenExt, Z80Span, parse_z80
16};
17
18fn ctx_and_span(code: &'static str) -> (Box<ParserContext>, Z80Span) {
19 let ctx = Box::new(
20 ParserContextBuilder::default()
21 .set_context_name("TEST")
22 .build(code)
23 );
24 let span = Z80Span::new_extra(code, ctx.deref());
25 (ctx, span)
26}
27
28#[derive(Debug)]
29pub struct ToOrgamsError(String);
30
31impl Display for ToOrgamsError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.write_str(&self.0)
34 }
35}
36impl From<String> for ToOrgamsError {
37 fn from(val: String) -> Self {
38 ToOrgamsError(val)
39 }
40}
41impl From<&std::io::Error> for ToOrgamsError {
42 fn from(val: &std::io::Error) -> Self {
43 let content = val.to_string();
44 content.into()
45 }
46}
47
48pub trait ToOrgams {
50 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError>;
51}
52
53macro_rules! macro_params_to_orgams {
54 () => {
55 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
56 let repr: String = if self.is_single() {
57 let arg = self.single_argument();
58 let (_ctx, mut code) = ctx_and_span(unsafe { std::mem::transmute(arg.deref()) });
59 let value = crate::located_expr(&mut code);
60 match value {
61 Ok(expr) => expr.to_orgams_string()?.into_owned(),
62 Err(_) => arg.into_owned()
63 }
64 }
65 else {
66 unimplemented!("We consider it does not happens with ORGAMS")
67 };
68
69 Ok(repr.into())
70 }
71 };
72}
73
74impl ToOrgams for MacroParam {
75 macro_params_to_orgams!();
76}
77
78impl ToOrgams for LocatedMacroParam {
79 macro_params_to_orgams!();
80}
81
82impl ToOrgams for Mnemonic {
83 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
84 Ok(self.to_string().to_lowercase().into())
85 }
86}
87
88impl ToOrgams for BinaryOperation {
89 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
90 Ok(self.to_string().to_uppercase().into())
91 }
92}
93
94impl ToOrgams for UnaryOperation {
95 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
96 Ok(self.to_string().to_ascii_uppercase().into())
97 }
98}
99
100macro_rules! expr_to_orgams {
101 () => {
102 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
103 let repr = match self {
104 Self::Value(v, ..) => {
105 if self.has_span() {
106 let span = self.span().as_str().replace("_", "");
108 if span.starts_with("0x") || span.starts_with("0X") {
109 format!("&{}", &span[2..])
110 }
111 else if span.starts_with("#") {
112 format!("&{}", &span[1..])
113 }
114 else {
115 format!("{}", v)
116 }
117 }
118 else {
119 format!("{}", v)
120 }
121 },
122
123 Self::Label(l) => {
124 format!("{}", l)
125 },
126
127 Self::String(s) => {
128 format!("\"{}\"", s)
129 },
130
131 Self::BinaryOperation(op, left, right, ..) => {
132 let rleft = left.to_orgams_string()?;
133 let rright = right.to_orgams_string()?;
134 let rleft = rleft.as_ref();
135 let op = op.to_orgams_string()?;
136
137 let protect = |expr: &Self, repr: &str| -> String {
138 if expr.is_label() || expr.is_value() {
139 repr.into()
140 }
141 else {
142 format!("[{}]", repr).into()
143 }
144 };
145
146 let rleft = protect(left, rleft.as_ref());
147 let rright = protect(right, rright.as_ref());
148
149 format!("{}{}{}", rleft, op, rright)
150 },
151
152 Self::UnaryOperation(op, exp, ..) => {
153 let exp = exp.to_orgams_string()?;
154 let op = op.to_orgams_string()?;
155
156 format!("{} {}", exp, op)
157 },
158
159 Self::Bool(f, ..) => {
160 format!("{}", if *f { 0 } else { 1 })
161 },
162
163 _ => unimplemented!("{:?}", self)
164 };
165
166 Ok(repr.into())
167 }
168 };
169}
170
171impl ToOrgams for LocatedExpr {
172 expr_to_orgams!();
173}
174
175impl ToOrgams for Expr {
176 expr_to_orgams!();
177}
178
179macro_rules! test_kind_to_orgams {
180 () => {
181 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
182 if self.is_true_test() {
183 let expr = self.expr_unchecked();
184 Ok(format!("IF {}", expr.to_orgams_string()?).into())
185 }
186 else if self.is_label_exists_test() {
187 let label = self.label_unchecked();
188 Ok(format!("IFDEF {}", label).into())
189 }
190 else if self.is_label_nexists_test() {
191 let label = self.label_unchecked();
192 Ok(format!("IFNDEF {}", label).into())
193 }
194 else {
195 Err(format!("{:?} unhandled", self).into())
196 }
197 }
198 };
199}
200
201impl ToOrgams for LocatedTestKind {
202 test_kind_to_orgams!();
203}
204
205impl ToOrgams for TestKind {
206 test_kind_to_orgams!();
207}
208
209macro_rules! data_access_to_orgams {
210 () => {
211 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
212 let repr = if self.is_expression() {
213 let exp = self.get_expression().unwrap();
214 return exp.to_orgams_string();
215 }
216 else if self.is_memory() || self.is_port_n() {
217 let exp = self.get_expression().unwrap();
218 let exp = exp.to_orgams_string()?;
219 format!("({})", exp)
220 }
221 else if self.is_register16()
222 || self.is_register8()
223 || self.is_indexregister16()
224 || self.is_indexregister8()
225 || self.is_port_c()
226 || self.is_address_in_register16()
227 || self.is_address_in_indexregister16()
228 || self.is_flag_test()
229 {
230 self.to_string().to_lowercase()
231 }
232 else {
233 unimplemented!("{:?}", self)
234 };
235
236 Ok(repr.into())
237 }
238 };
239}
240
241impl ToOrgams for DataAccess {
242 data_access_to_orgams!();
243}
244
245impl ToOrgams for LocatedDataAccess {
246 data_access_to_orgams!();
247}
248
249impl<T> ToOrgams for T
250where
251 T: TokenExt + MayHaveSpan + ListingElement + ToString + ?Sized,
252 T::DataAccess: ToOrgams,
253 T::Expr: ToOrgams,
254 T::TestKind: ToOrgams,
255 T::MacroParam: ToOrgams
256{
257 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
258 let handle_macro_definition = |token: &T| -> Cow<str> {
260 let macro_name = token.macro_definition_name();
261 let arguments_name = token.macro_definition_arguments();
262 let mut macro_content = token.macro_definition_code().to_owned();
263
264 for arg in arguments_name.iter() {
265 macro_content = macro_content.replace(&format!("{{{arg}}}"), arg);
266 }
267 macro_content = macro_content.replace('\n', "\n\t");
268
269 let macro_content = if let Ok(macro_content_listing) = parse_z80(¯o_content) {
272 let macro_content_listing = macro_content_listing.as_slice();
273 macro_content_listing
274 .to_orgams_string()
275 .map(|s| s.to_string())
276 .unwrap_or(macro_content)
277 }
278 else {
279 macro_content
280 };
281
282 let macro_args = arguments_name.into_iter().join(", ");
283
284 let output = format!("\tMACRO {macro_name} {macro_args}\n{macro_content}\tENDM");
285 Cow::owned(output)
286 };
287
288 let handle_macro_call = |token: &T| -> Cow<str> {
289 let name = token.macro_call_name();
290 let arguments = token
291 .macro_call_arguments()
292 .iter()
293 .map(|s| s.to_orgams_string().unwrap())
294 .join(",");
295
296 let repr = format!("{name}({arguments})");
297 repr.into()
298 };
299
300 let handle_standard_directive = |token: &T| -> Cow<str> {
301 token.to_token().to_string().into()
307 };
308
309 let comment_token = |token: &T| -> Result<Cow<str>, ToOrgamsError> {
310 let repr = token.to_string();
311 let repr: String = repr.lines().map(|l| format!(" ; {l}")).join("\n");
312 let token = Token::Comment(format!("; {repr}",));
313 let res = token.to_orgams_string()?;
314 Ok(res.into_owned().into())
315 };
316
317 let handle_org = |token: &T| -> Result<Cow<str>, ToOrgamsError> {
318 let org1 = token.org_first();
319 let org2 = token.org_second();
320
321 let org1 = org1.to_orgams_string()?;
322 let repr = if let Some(org2) = org2 {
323 format!("ORG {}, {}", org1, org2.to_orgams_string()?)
324 }
325 else {
326 format!("ORG {}", org1)
327 };
328
329 Ok(repr.into())
330 };
331
332 let handle_data = |token: &T| -> Result<Cow<str>, ToOrgamsError> {
333 let exprs = token
334 .data_exprs()
335 .iter()
336 .map(|e| e.to_orgams_string())
337 .collect::<Result<Vec<_>, ToOrgamsError>>()?;
338 let exprs = exprs.into_iter().join(",");
339 let mne = if token.is_db() {
340 "BYTE"
341 }
342 else if token.is_dw() {
343 "WORD"
344 }
345 else {
346 unreachable!()
347 };
348
349 Ok(format!("{} {}", mne, exprs).into())
350 };
351
352 let handle_run = |token: &T| -> Result<Cow<str>, ToOrgamsError> {
353 let repr = format!("ENT {}", token.run_expr().to_orgams_string()?);
354 Ok(repr.into())
355 };
356
357 let handle_opcode = |token: &T| -> String {
359 let mut op = token
361 .mnemonic()
362 .unwrap()
363 .to_orgams_string()
364 .unwrap()
365 .to_string();
366
367 if let Some(arg) = token.mnemonic_arg1() {
368 op.push(' ');
369 op.push_str(&arg.to_orgams_string().unwrap())
370 }
371
372 if let Some(arg) = token.mnemonic_arg2() {
373 if token.mnemonic_arg1().is_some() {
374 op.push(',');
375 }
376 else {
377 op.push(' ');
378 }
379 op.push_str(&arg.to_orgams_string().unwrap())
380 }
381
382 op
383 };
384
385 let handle_assign = |token: &T| -> String {
386 let label = token.assign_symbol();
387 let value = token.assign_value();
388
389 format!("{}={}", label, value.to_orgams_string().unwrap())
390 };
391
392 let handle_equ = |token: &T| -> String {
393 let label = token.equ_symbol();
394 let value = token.equ_value();
395
396 format!("{} EQU {}", label, value.to_orgams_string().unwrap())
397 };
398
399 let handle_if = |token: &T| -> String {
400 assert!(self.if_nb_tests() == 1);
401
402 let (test, code) = token.if_test(0);
403 let mut content = format!(
404 "{}\n{}",
405 test.to_orgams_string().unwrap(),
406 code.to_orgams_string().unwrap()
407 );
408
409 if let Some(code) = token.if_else() {
410 content.push_str("\n\tELSE\n");
411 content.push_str(&code.to_orgams_string().unwrap());
412 }
413
414 content.push_str("\n\tEND\n");
415 content
416 };
417
418 let handle_include = |token: &T| -> Result<String, ToOrgamsError> {
421 let fname = token.include_fname().string();
422 let mut include = format!(" ; START Included from {fname}\n");
423 let content = convert_from(fname)?;
424 include.push_str(&format!("{content}\n ; STOP Included from {fname}\n"));
425 Ok(include)
426 };
427
428 let handle_incbin = |token: &T| -> Result<String, ToOrgamsError> {
429 let fname = token.incbin_fname().string();
430 let repr = format!("LOAD \"{}\"", fname);
431
432 assert!(token.incbin_length().is_none());
433 assert!(token.incbin_offset().is_none());
434 assert_eq!(token.incbin_transformation(), &BinaryTransformation::None);
435
436 Ok(repr)
437 };
438
439 let repr = if self.is_opcode() {
441 Cow::owned(handle_opcode(self))
442 }
443 else if self.is_org() {
444 handle_org(self)?
445 }
446 else if self.is_macro_definition() {
447 handle_macro_definition(self)
448 }
449 else if self.is_call_macro_or_build_struct() {
450 handle_macro_call(self)
451 }
452 else if self.is_assign() {
453 handle_assign(self).into()
454 }
455 else if self.is_equ() {
456 handle_equ(self).into()
457 }
458 else if self.is_if() {
459 handle_if(self).into()
460 }
461 else if self.is_include() {
462 handle_include(self)?.into()
463 }
464 else if self.is_incbin() {
465 handle_incbin(self)?.into()
466 }
467 else if self.is_assert() || self.is_breakpoint() || self.is_print() || self.is_save() {
468 comment_token(self)?
469 }
470 else if self.is_db() || self.is_dw() {
471 handle_data(self)?
472 }
473 else if self.is_run() {
474 handle_run(self)?
475 }
476 else {
477 handle_standard_directive(self)
478 };
479
480 if repr.is_empty() {
481 return Ok(repr);
482 }
483
484 let repr = if !self.is_comment() && !self.is_label() && !self.is_equ() && !self.is_assign()
486 {
487 let first = repr.chars().next().unwrap();
488 if first != ' ' && first != '\t' {
489 Cow::owned(format!("\t{}", repr))
490 }
491 else {
492 repr
493 }
494 }
495 else {
496 repr
497 };
498 Ok(repr)
499 }
500}
501
502impl<T: ToOrgams> ToOrgams for &[T] {
521 fn to_orgams_string(&self) -> Result<Cow<str>, ToOrgamsError> {
522 let mut content = String::with_capacity(self.len() * 10);
523
524 for token in self.iter() {
525 content.push_str(token.to_orgams_string()?.deref());
526 content.push('\n');
527 }
528
529 Ok(content.into())
533 }
534}
535
536pub fn convert_source(code: &str) -> Result<String, ToOrgamsError> {
537 let lst = parse_z80(code).map_err(|e| ToOrgamsError(format!("Error while parsing. {}", e)))?;
538 let lst = lst.as_slice();
539 lst.to_orgams_string().map(|s| s.into_owned())
540}
541
542pub fn convert_from<P: AsRef<Utf8Path>>(p: P) -> Result<String, ToOrgamsError> {
543 let p = p.as_ref();
544 let code = std::fs::read_to_string(p)
545 .map_err(|e| ToOrgamsError(format!("Error while reading {}. {}", p, e)))?;
546 convert_source(&code).map_err(|e| format!("Error while handling {}. {}", p, e).into())
547}
548
549pub fn convert_from_to<P1: AsRef<Utf8Path>, P2: AsRef<Utf8Path>>(
555 src: P1,
556 tgt: P2
557) -> Result<(), ToOrgamsError> {
558 let src = src.as_ref();
559 let tgt = tgt.as_ref();
560 let orgams = convert_from(src)?;
561 std::fs::write(tgt, orgams.as_bytes())
562 .map_err(|e| format!("Error while saving {}. {}", tgt, e).into())
563}
564
565#[cfg(test)]
566mod test {
567 use std::ops::Deref;
568
569 use cpclib_common::winnow::ModalParser;
570 use cpclib_common::winnow::error::ParseError;
571 use cpclib_tokens::{DataAccess, Expr};
572
573 use super::{ToOrgams, ctx_and_span};
574 use crate::{
575 AssemblerError, InnerZ80Span, ParserContext, Z80ParserError, Z80Span, located_expr
576 };
577
578 #[derive(Debug)]
579 struct TestResult<O: std::fmt::Debug> {
580 ctx: Box<ParserContext>,
581 span: Z80Span,
582 res: Result<O, ParseError<InnerZ80Span, Z80ParserError>>
583 }
584
585 impl<O: std::fmt::Debug> Deref for TestResult<O> {
586 type Target = Result<O, ParseError<InnerZ80Span, Z80ParserError>>;
587
588 fn deref(&self) -> &Self::Target {
589 &self.res
590 }
591 }
592
593 fn parse_test<O, P: ModalParser<InnerZ80Span, O, Z80ParserError>>(
594 mut parser: P,
595 code: &'static str
596 ) -> TestResult<O>
597 where
598 O: std::fmt::Debug
599 {
600 let (ctx, span) = ctx_and_span(code);
601 let res = parser.parse(span.0);
602 if let Err(e) = &res {
603 let e = e.inner();
604 let e = AssemblerError::SyntaxError { error: e.clone() };
605 eprintln!("Parse error: {}", e);
606 }
607
608 TestResult { ctx, span, res }
609 }
610
611 #[test]
612 fn test_expression() {
613 assert_eq!(
614 parse_test(located_expr, "25")
615 .as_ref()
616 .unwrap()
617 .to_orgams_string()
618 .unwrap(),
619 "25"
620 );
621 assert_eq!(
622 parse_test(located_expr, "0x25")
623 .as_ref()
624 .unwrap()
625 .to_orgams_string()
626 .unwrap(),
627 "&25"
628 );
629 }
630
631 #[test]
632 fn test_data_access() {
633 assert_eq!(
634 DataAccess::Expression(Expr::Value(25))
635 .to_orgams_string()
636 .unwrap(),
637 "25"
638 );
639 assert_eq!(
640 DataAccess::Memory(Expr::Value(25))
641 .to_orgams_string()
642 .unwrap(),
643 "(25)"
644 );
645 }
646}