From 66a54f813b82ec04211259121da7ffd6faf89ac1 Mon Sep 17 00:00:00 2001 From: Krusty/Benediction Date: Tue, 27 Aug 2024 17:07:38 +0200 Subject: [PATCH] [orgams] Start to work on expression conversion and tests for orgams output --- cpclib-asm/src/orgams.rs | 226 ++++++++++++++++++++--- cpclib-asm/src/parser/obtained.rs | 21 ++- cpclib-bndbuild/src/lib.rs | 2 - cpclib-bndbuild/src/task.rs | 4 +- cpclib-emucontrol/src/main.rs | 1 - cpclib-runner/src/runner/emulator.rs | 2 - cpclib-tokens/src/tokens/instructions.rs | 55 +++++- cpclib-tokens/src/tokens/listing.rs | 11 +- cpclib-tokens/src/tokens/tokens.rs | 14 ++ 9 files changed, 296 insertions(+), 40 deletions(-) diff --git a/cpclib-asm/src/orgams.rs b/cpclib-asm/src/orgams.rs index 61e2fa5a..86b1f901 100644 --- a/cpclib-asm/src/orgams.rs +++ b/cpclib-asm/src/orgams.rs @@ -4,9 +4,9 @@ use std::ops::Deref; use beef::lean::Cow; use cpclib_common::camino::Utf8Path; use cpclib_common::itertools::Itertools; -use cpclib_tokens::{ListingElement, MacroParamElement, Token}; +use cpclib_tokens::{BinaryOperation, DataAccess, DataAccessElem, Expr, ExprElement, ListingElement, MacroParamElement, Mnemonic, TestKind, TestKindElement, Token}; -use crate::{parse_z80, r#macro, MayHaveSpan, SourceString, TokenExt}; +use crate::{parse_z80, LocatedDataAccess, LocatedExpr, LocatedTestKind, MayHaveSpan, SourceString, TokenExt}; #[derive(Debug)] pub struct ToOrgamsError(String); @@ -16,15 +16,15 @@ impl Display for ToOrgamsError { f.write_str(&self.0) } } -impl Into for &str { +impl Into for String { fn into(self) -> ToOrgamsError { - ToOrgamsError(self.into()) + ToOrgamsError(self) } } impl Into for &std::io::Error { fn into(self) -> ToOrgamsError { let content = self.to_string(); - content.as_str().into() + content.into() } } @@ -33,13 +33,136 @@ pub trait ToOrgams { fn to_orgams_string(&self) -> Result, ToOrgamsError>; } -// impl ToOrgams for Token { -// fn to_orgams_string(&self) -> Result, ToOrgamsError> { -// todo!() -// } -// } -impl ToOrgams for T { +impl ToOrgams for Mnemonic { + fn to_orgams_string(&self) -> Result, ToOrgamsError> { + Ok(self.to_string().to_lowercase().into()) + } +} + +impl ToOrgams for BinaryOperation { + fn to_orgams_string(&self) -> Result, ToOrgamsError> { + Ok(self.to_string().to_uppercase().into()) + } +} + +macro_rules! expr_to_orgams { + () => { + fn to_orgams_string(&self) -> Result, ToOrgamsError> { + let repr = match self { + + Self::Value(v,..) => { + if self.has_span() { + // basm allow _ between numbers + let span = dbg!(self.span().as_str().replace("_", "")); + if span.starts_with("0x") || span.starts_with("0X") { + format!("&{}", &span[2..]) + } else if span.starts_with("#") { + format!("&{}", &span[1..]) + } else { + format!("{}", v) + } + } else { + format!("&{:x}", v) + } + }, + + Self::Label(l) => { + format!("{}", l) + } + + Self::BinaryOperation(op, left, right, ..) => { + let rleft = left.to_orgams_string()?; + let rright = right.to_orgams_string()?; + let rleft = rleft.as_ref(); + let op = op.to_orgams_string()?; + + let protect = |expr: &Self, repr: &str| -> String { + if expr.is_label() || expr.is_value() { + repr.into() + } else { + format!("[{}]", repr).into() + } + }; + + let rleft = protect(left, rleft.as_ref()); + let rright = protect(right, rright.as_ref()); + + format!("{}{}{}", rleft, op, rright) + } + + _ => unimplemented!("{:?}", self) + }; + + Ok(repr.into()) + } + } +} + +impl ToOrgams for LocatedExpr { + expr_to_orgams!(); +} + +impl ToOrgams for Expr { + expr_to_orgams!(); +} + +macro_rules! test_kind_to_orgams { + () => { + fn to_orgams_string(&self) -> Result, ToOrgamsError> { + if self.is_true_test() { + let expr = self.expr_unchecked(); + Ok(format!("IF {}", expr.to_orgams_string()?).into()) + } else { + Err(format!("{:?}", self).into()) + } + } + }; +} + +impl ToOrgams for LocatedTestKind { + test_kind_to_orgams!(); +} + +impl ToOrgams for TestKind { + test_kind_to_orgams!(); +} + + +macro_rules! data_access_to_orgams { + () => { + fn to_orgams_string(&self) -> Result, ToOrgamsError> { + + let repr = if self.is_expression() { + let exp = self.get_expression().unwrap(); + return exp.to_orgams_string(); + } + else { + self.to_string().to_lowercase() + }; + + Ok(repr.into()) + } + + }; +} + + + +impl ToOrgams for DataAccess { + data_access_to_orgams!(); +} + +impl ToOrgams for LocatedDataAccess { + data_access_to_orgams!(); +} + +impl ToOrgams for T where +T: TokenExt + MayHaveSpan + ListingElement + ToString + ?Sized, +T::DataAccess: ToOrgams, +T::Expr: ToOrgams, +T::TestKind: ToOrgams +{ fn to_orgams_string(&self) -> Result, ToOrgamsError> { // we assume it is already a BASM format and not an ORGAMS format let handle_macro_definition = |token: &T| -> Cow { @@ -47,6 +170,7 @@ impl ToOrgams for T { let arguments_name = token.macro_definition_arguments(); let mut macro_content = token.macro_definition_code().to_owned(); + for arg in arguments_name.iter() { macro_content = macro_content.replace(&format!("{{{arg}}}"), arg); } @@ -55,7 +179,7 @@ impl ToOrgams for T { // also transform the content of the macro // in case of failure, fallback to the original content let macro_content = if let Ok(macro_content_listing) = parse_z80(¯o_content) { - let macro_content_listing = ¯o_content_listing[..]; + let macro_content_listing = macro_content_listing.as_slice(); macro_content_listing .to_orgams_string() .map(|s| s.to_string()) @@ -90,7 +214,7 @@ impl ToOrgams for T { repr.into() }; - let handle_standard_instruction = |token: &T| -> Cow { + let handle_standard_directive = |token: &T| -> Cow { // if self.has_span() { // Cow::borrowed(self.span().as_str()) // } else { @@ -107,8 +231,61 @@ impl ToOrgams for T { s.into() }; + // XXX strong limitation, does not yet handle 3 args + let handle_opcode = |token: &T| -> String { + let mut op = token.mnemonic().unwrap().to_orgams_string().unwrap().to_string(); + + if let Some(arg) = token.mnemonic_arg1() { + op.push(' '); + op.push_str(&arg.to_orgams_string().unwrap()) + } + + if let Some(arg) = token.mnemonic_arg2() { + if token.mnemonic_arg1().is_some() { + op.push(','); + } else { + op.push(' '); + } + op.push_str(&arg.to_orgams_string().unwrap()) + } + + op + + }; + + let handle_assign = |token: &T| -> String { + let label = token.assign_symbol(); + let value = token.assign_value(); + + format!("{}={}", label, value.to_orgams_string().unwrap()) + }; + + let handle_equ = |token: &T| -> String { + let label = token.equ_symbol(); + let value = token.equ_value(); + + format!("{} EQU {}", label, value.to_orgams_string().unwrap()) + }; + + let handle_if = |token: &T| -> String { + assert!(self.if_nb_tests() == 1); + + let (test, code) = token.if_test(0); + let mut content = format!("{}\n{}", test.to_orgams_string().unwrap(), code.to_orgams_string().unwrap()); + + if let Some(code) = token.if_else() { + content.push_str("\n\tELSE\n"); + content.push_str(&code.to_orgams_string().unwrap()); + } + + content.push_str("\n\tENDIF\n"); + content + }; + // This is the default behavior that changes nothing - let repr = if self.is_macro_definition() { + let repr = if self.is_opcode() { + Cow::owned(handle_opcode(self)) + } else if self.is_macro_definition() { handle_macro_definition(self) } else if self.is_print() { @@ -117,8 +294,17 @@ impl ToOrgams for T { else if self.is_call_macro_or_build_struct() { handle_macro_call(self) } + else if self.is_assign() { + handle_assign(self).into() + } + else if self.is_equ(){ + handle_equ(self).into() + } + else if self.is_if() { + handle_if(self).into() + } else { - handle_standard_instruction(self) + handle_standard_directive(self) }; if repr.is_empty() { @@ -171,13 +357,7 @@ impl ToOrgams for &[T] { } // TODO do it properly by coding the complete expression display - let content = content.replace("0x", "&"); - - // TODO handle that in macro call - let content = content.replace("(void)", "()"); - - // TODO handle that directly in instruction print out - let content = content.replace(", ", ","); +// let content = content.replace("0x", "&"); Ok(content.into()) } @@ -200,5 +380,5 @@ pub fn convert_source, P2: AsRef>( let lst = lst.as_slice(); let orgams = lst.to_orgams_string()?; std::fs::write(tgt, orgams.as_bytes()) - .map_err(|e| format!("Error while saving {}", tgt).as_str().into()) + .map_err(|e| format!("Error while saving {}. {}", tgt, e.to_string()).into()) } diff --git a/cpclib-asm/src/parser/obtained.rs b/cpclib-asm/src/parser/obtained.rs index 51630452..583a2206 100644 --- a/cpclib-asm/src/parser/obtained.rs +++ b/cpclib-asm/src/parser/obtained.rs @@ -1164,6 +1164,8 @@ impl ListingElement for LocatedToken { is_stuff_delegate!(is_print is_buildcpr is_label is_equ is_assign is_module is_directive is_rorg is_iterate is_for is_repeat_until is_repeat is_macro_definition is_if is_include is_incbin is_call_macro_or_build_struct is_function_definition is_crunched_section is_confined is_switch is_db is_dw is_str is_set is_comment is_org is_assembler_control is_while); any_delegate!( + fn assign_symbol(&self) -> &str; + fn assign_value(&self) -> &Self::Expr; fn equ_symbol(&self) -> &str; fn equ_value(&self) -> &Self::Expr; fn module_name(&self) -> &str; @@ -1569,6 +1571,21 @@ impl ListingElement for LocatedTokenInner { } } + + fn assign_symbol(&self) -> &str { + match &self { + LocatedTokenInner::Assign { label, .. } => label.as_str(), + _ => unreachable!() + } + } + + fn assign_value(&self) -> &Self::Expr { + match &self { + LocatedTokenInner::Assign { expr, .. } => expr, + _ => unreachable!() + } + } + fn is_warning(&self) -> bool { todo!() } @@ -2074,7 +2091,7 @@ impl ListingElement for LocatedTokenInner { fn assembler_control_command(&self) -> &Self::AssemblerControlCommand { match &self { - (LocatedTokenInner::AssemblerControl(cmd)) => cmd, + LocatedTokenInner::AssemblerControl(cmd) => cmd, _ => unreachable!() } } @@ -2101,7 +2118,7 @@ impl ListingElement for LocatedTokenInner { fn macro_flavor(&self) -> AssemblerFlavor { match self { - (LocatedTokenInner::Macro { flavor, .. }) => *flavor, + LocatedTokenInner::Macro { flavor, .. } => *flavor, _ => unreachable!() } } diff --git a/cpclib-bndbuild/src/lib.rs b/cpclib-bndbuild/src/lib.rs index a46f01f1..b6527f09 100644 --- a/cpclib-bndbuild/src/lib.rs +++ b/cpclib-bndbuild/src/lib.rs @@ -8,8 +8,6 @@ use cpclib_common::clap::*; use cpclib_common::itertools::Itertools; use cpclib_runner::runner::RunnerWithClap; use lazy_regex::regex_captures; -use serde::de::IntoDeserializer; -use serde::Deserialize; use task::Task; use thiserror::Error; diff --git a/cpclib-bndbuild/src/task.rs b/cpclib-bndbuild/src/task.rs index 6b2627f7..33523e68 100644 --- a/cpclib-bndbuild/src/task.rs +++ b/cpclib-bndbuild/src/task.rs @@ -3,9 +3,7 @@ use std::fmt::Display; use cpclib_common::itertools::Itertools; use cpclib_runner::emucontrol::EMUCTRL_CMD; use cpclib_runner::runner::assembler::{RasmVersion, RASM_CMD}; -use cpclib_runner::runner::emulator::{ - AceVersion, CpcecVersion, WinapeVersion, ACE_CMD, CPCEC_CMD, WINAPE_CMD -}; +use cpclib_runner::runner::emulator::{ACE_CMD, CPCEC_CMD, WINAPE_CMD}; use cpclib_runner::runner::impdisc::IMPDISC_CMD; use cpclib_runner::runner::martine::MARTINE_CMD; use serde::de::{Error, Visitor}; diff --git a/cpclib-emucontrol/src/main.rs b/cpclib-emucontrol/src/main.rs index 222fc886..7959cb8c 100644 --- a/cpclib-emucontrol/src/main.rs +++ b/cpclib-emucontrol/src/main.rs @@ -1,5 +1,4 @@ use std::process::exit; -use std::time::Duration; use clap::Parser; use cpclib_runner::emucontrol::{handle_arguments, EmuCli}; diff --git a/cpclib-runner/src/runner/emulator.rs b/cpclib-runner/src/runner/emulator.rs index 813f9c41..4df69e06 100644 --- a/cpclib-runner/src/runner/emulator.rs +++ b/cpclib-runner/src/runner/emulator.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use cpclib_common::camino::Utf8PathBuf; use directories::BaseDirs; diff --git a/cpclib-tokens/src/tokens/instructions.rs b/cpclib-tokens/src/tokens/instructions.rs index f37d26e7..ed43edc3 100644 --- a/cpclib-tokens/src/tokens/instructions.rs +++ b/cpclib-tokens/src/tokens/instructions.rs @@ -972,7 +972,42 @@ impl fmt::Display for Token { Token::Equ{label, expr} => write!(f, "{} EQU {}", label, expr.to_simplified_string()), - + Token::If(tests, default) => { + let get_code_string = |tokens: &[Token]| { + let mut code_part = String::new(); + for token in tokens.iter() { + if !token.starts_with_label() { + code_part.push('\t'); + } + code_part += &token.to_string(); + code_part += "\n"; + } + code_part + }; + + let mut first = true; + for (test, code) in tests { + let test_part = match test { + TestKind::True(e) => format!("IF {}", e), + TestKind::False(e) => format!("IFNOT {}", e), + TestKind::LabelExists(l) => format!("IFDEF {}", l), + TestKind::LabelDoesNotExist(l) => format!("IFNDEF {}", l), + TestKind::LabelUsed(l) => format!("IFUSED {}", l), + TestKind::LabelNused(l) => format!("IFNUSED {}", l), + }; + + let code_part = get_code_string(&code); + write!(f, "\t{}{}\n{}", if first {""} else {"ELSE "},test_part, code_part)?; + first = false; + } + + if let Some(code) = default { + let code_part = get_code_string(&code); + write!(f, "{code_part}")?; + } + + write!(f, "\n\tENDIF\n") + } Token::Incbin{ fname, @@ -1039,10 +1074,16 @@ impl fmt::Display for Token { Token::MacroCall(ref name, ref args) => {use cpclib_common::itertools::Itertools; - write!(f, "{} {}", name, args.clone() - .iter() - .map(|a|{a.to_string()}) - .join(", "))?; + let args = args.clone() + .iter() + .map(|a|{a.to_string()}) + .join(", "); + let args = if args.is_empty() { + "(void)".to_owned() + } else { + args + }; + write!(f, "{} {}", name, args)?; Ok(()) }, @@ -1114,7 +1155,9 @@ impl fmt::Display for Token { } }, - _ => unimplemented!() + + + _ => unimplemented!("{:?}", self) } } diff --git a/cpclib-tokens/src/tokens/listing.rs b/cpclib-tokens/src/tokens/listing.rs index b2ea5ebb..294844c6 100644 --- a/cpclib-tokens/src/tokens/listing.rs +++ b/cpclib-tokens/src/tokens/listing.rs @@ -28,6 +28,10 @@ where Self: Debug + Sized + Sync false // self.is_equ() | self.is_set() } + fn is_opcode(&self) -> bool { + !self.is_directive() + } + fn is_buildcpr(&self) -> bool; fn is_assembler_control(&self) -> bool; fn assembler_control_command(&self) -> &Self::AssemblerControlCommand; @@ -42,7 +46,9 @@ where Self: Debug + Sized + Sync fn is_equ(&self) -> bool; fn is_assign(&self) -> bool; fn equ_symbol(&self) -> &str; - fn equ_value(&self) -> &Self::Expr; // TODO Make a located version of Equ to return a located version of the expression + fn equ_value(&self) -> &Self::Expr; + fn assign_symbol(&self) -> &str; + fn assign_value(&self) -> &Self::Expr; fn is_warning(&self) -> bool; fn warning_token(&self) -> &Self; @@ -151,6 +157,9 @@ where Self: Debug + Sized + Sync fn is_print(&self) -> bool; fn to_token(&self) -> Cow; + fn starts_with_label(&self) -> bool { + self.is_label() || self.is_assign() || self.is_equ() || self.is_set() + } } /// A listing is simply a list of things similar to token #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/cpclib-tokens/src/tokens/tokens.rs b/cpclib-tokens/src/tokens/tokens.rs index 8d6cd693..48438c71 100644 --- a/cpclib-tokens/src/tokens/tokens.rs +++ b/cpclib-tokens/src/tokens/tokens.rs @@ -57,6 +57,20 @@ impl ListingElement for Token { } } + fn assign_symbol(&self) -> &str { + match self { + Self::Assign { label, .. } => label.as_str(), + _ => unreachable!() + } + } + + fn assign_value(&self) -> &Self::Expr { + match self { + Self::Assign { expr, .. } => expr, + _ => unreachable!() + } + } + fn is_warning(&self) -> bool { false }