cpclib_asm/implementation/
listing.rs1use std::borrow::Borrow;
2use std::collections::HashMap;
3use std::fmt;
4
5use cpclib_common::camino::Utf8Path;
6use cpclib_tokens::tokens::*;
7
8use crate::EnvOptions;
9use crate::error::*;
10use crate::implementation::expression::*;
11use crate::implementation::tokens::*;
12use crate::preamble::parse_z80_str;
13
14pub trait ListingExt {
16 fn add_code<S: AsRef<str> + core::fmt::Display>(
17 &mut self,
18 code: S
19 ) -> Result<(), AssemblerError>;
20
21 fn to_bytes(&self) -> Result<Vec<u8>, AssemblerError> {
23 let options = EnvOptions::default();
24 self.to_bytes_with_options(options)
25 }
26
27 fn to_bytes_with_options(&self, option: EnvOptions) -> Result<Vec<u8>, AssemblerError>;
28
29 fn number_of_bytes(&self) -> Result<usize, AssemblerError> {
32 Ok(self.to_bytes()?.len())
33 }
34 fn fallback_number_of_bytes(&self) -> Result<usize, String>;
35
36 fn estimated_duration(&self) -> Result<usize, AssemblerError>;
39 fn save<P: AsRef<Utf8Path>>(&self, path: P) -> ::std::io::Result<()> {
41 use std::fs::File;
42 use std::io::prelude::*;
43
44 let mut file = File::create(path.as_ref())?;
46 file.write_all(self.to_string().as_bytes())?;
47
48 Ok(())
49 }
50
51 fn to_string(&self) -> String;
52
53 fn to_enhanced_string(&self) -> String;
56
57 fn inject_labels<S: Borrow<str>>(&mut self, labels: HashMap<u16, S>);
59}
60
61impl ListingExt for Listing {
62 fn add_code<S: AsRef<str> + core::fmt::Display>(
64 &mut self,
65 code: S
66 ) -> Result<(), AssemblerError> {
67 parse_z80_str(code.as_ref().trim_end()).map(|local_tokens| {
68 let mut local_tokens = local_tokens.as_listing().to_vec();
69 self.listing_mut().append(&mut local_tokens);
70 })
71 }
72
73 fn to_bytes_with_options(&self, options: EnvOptions) -> Result<Vec<u8>, AssemblerError> {
74 let (_, env) =
75 crate::assembler::visit_tokens_all_passes_with_options(self.listing(), options)
76 .map_err(|(_, _, e)| AssemblerError::AlreadyRenderedError(e.to_string()))?;
77 Ok(env.produced_bytes())
78 }
79
80 fn estimated_duration(&self) -> Result<usize, AssemblerError> {
83 if let Some(duration) = self.duration() {
84 Ok(duration)
85 }
86 else {
87 let mut duration = 0;
88 for token in self.listing().iter() {
89 duration += token.estimated_duration()?;
90 }
91 Ok(duration)
92 }
93 }
94
95 fn to_string(&self) -> String {
96 PrintableListing::from(self).to_string()
97 }
98
99 fn to_enhanced_string(&self) -> String {
100 todo!()
101 }
102
103 fn inject_labels<S: Borrow<str>>(&mut self, mut labels: HashMap<u16, S>) {
105 use cpclib_tokens::builder::{equ, label};
106
107 let mut current_address: Option<u16> = None;
108 let mut current_idx = 0;
109 let mut nb_labels_added = 0;
110
111 while current_idx < self.len() && !labels.is_empty() {
113 if let Some(current_address) = ¤t_address {
114 if let Some(new_label) = labels.remove(current_address) {
115 self.listing_mut()
116 .insert(current_idx, label(new_label.borrow()));
117 nb_labels_added += 1;
118 }
119 }
120
121 let current_instruction = &self.listing()[current_idx];
122
123 let next_address = if let Token::Org { val1: address, .. } = current_instruction {
124 current_address = Some(address.eval().unwrap().int().unwrap() as u16);
125 current_address
126 }
127 else {
128 let nb_bytes = current_instruction.number_of_bytes().unwrap();
129 match current_address {
130 Some(address) => Some(address + nb_bytes as u16),
131 None => {
132 if nb_bytes != 0 {
133 panic!("Unable to run if assembling address is unknown")
134 }
135 else {
136 None
137 }
138 },
139 }
140 };
141
142 current_idx += 1;
143 current_address = next_address;
144 }
145
146 for (next_address, next_label) in labels.into_iter() {
148 self.listing_mut()
149 .insert(0, equ(next_label.borrow(), next_address));
150 }
151 }
152
153 fn fallback_number_of_bytes(&self) -> Result<usize, String> {
154 self.iter().map(|t| t.fallback_number_of_bytes()).sum()
155 }
156}
157
158pub struct PrintableListing<'a>(&'a Listing);
160impl<'a> From<&'a Listing> for PrintableListing<'a> {
161 fn from(src: &'a Listing) -> PrintableListing<'a> {
162 PrintableListing(src)
163 }
164}
165impl fmt::Display for PrintableListing<'_> {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 for token in self.0.listing().iter() {
168 match token {
169 Token::Label(_) | Token::Equ { .. } | Token::Comment(_) => (),
170 _ => {
171 write!(f, "\t")?;
172 }
173 }
174 writeln!(f, "{}", token)?;
176 }
177
178 Ok(())
179 }
180}
181
182pub trait ListingFromStr {
184 fn from_str(s: &str) -> Result<Listing, AssemblerError>;
185}
186
187impl ListingFromStr for Listing {
188 fn from_str(s: &str) -> Result<Listing, AssemblerError> {
189 crate::parser::parse_z80_str(s).map(|ll| ll.as_listing())
190 }
191}
192
193#[derive(Default)]
194pub enum ListingSelectorStrategy {
195 #[default]
196 Speed,
197 Size
198}
199#[derive(Default)]
200pub struct ListingSelector {
201 choices: Vec<Listing>,
202 strategy: ListingSelectorStrategy
203}
204
205impl ListingSelector {
206 pub fn add(mut self, lst: Listing) -> Self {
207 self.choices.push(lst);
208 self
209 }
210
211 pub fn select(mut self) -> Listing {
212 let key_fn: Box<dyn Fn(&Listing) -> (usize, usize)> = match self.strategy {
213 ListingSelectorStrategy::Speed => {
214 Box::new(|l: &Listing| {
215 (
216 l.estimated_duration().unwrap(),
217 l.number_of_bytes().unwrap()
218 )
219 })
220 },
221 ListingSelectorStrategy::Size => {
222 Box::new(|l: &Listing| {
223 (
224 l.number_of_bytes().unwrap(),
225 l.estimated_duration().unwrap()
226 )
227 })
228 },
229 };
230
231 self.choices.sort_by_cached_key(key_fn);
232 self.choices.into_iter().next().unwrap()
233 }
234}