1use std::{
2 ffi::{OsStr, OsString},
3 fmt,
4 str::FromStr,
5};
6
7use clap::{Parser, ValueEnum};
8use miden_assembly_syntax::ast::types::{ArrayType, PointerType, Type};
9
10use super::NativePtr;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct ReadMemoryExpr {
14 pub addr: NativePtr,
15 pub ty: Type,
16 pub count: u8,
17 pub mode: MemoryMode,
18 pub format: FormatType,
19}
20impl FromStr for ReadMemoryExpr {
21 type Err = String;
22
23 fn from_str(s: &str) -> Result<Self, Self::Err> {
24 let argv = s.split_whitespace();
25 let args = Read::parse(argv)?;
26
27 let ty = args.ty.unwrap_or_else(|| Type::from(ArrayType::new(Type::Felt, 4)));
28 let addr = match args.mode {
29 MemoryMode::Word => NativePtr::new(args.addr, 0),
30 MemoryMode::Byte => NativePtr::from_ptr(args.addr),
31 };
32 Ok(Self {
33 addr,
34 ty,
35 count: args.count,
36 mode: args.mode,
37 format: args.format,
38 })
39 }
40}
41
42impl fmt::Display for ReadMemoryExpr {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "{}", self.raw_addr())?;
45 write!(f, " -t {}", self.ty_name())?;
46 if self.count != 1 {
47 write!(f, " -c {}", self.count)?;
48 }
49 if self.mode != MemoryMode::Word {
50 write!(f, " -m {}", self.mode)?;
51 }
52 if self.format != FormatType::Decimal {
53 write!(f, " -f {}", self.format)?;
54 }
55 Ok(())
56 }
57}
58
59impl ReadMemoryExpr {
60 fn raw_addr(&self) -> u32 {
61 match self.mode {
62 MemoryMode::Word => self.addr.addr,
63 MemoryMode::Byte => self.addr.addr.saturating_mul(4) + u32::from(self.addr.offset),
64 }
65 }
66
67 fn ty_name(&self) -> &'static str {
68 match &self.ty {
69 Type::I1 => "i1",
70 Type::I8 => "i8",
71 Type::I16 => "i16",
72 Type::I32 => "i32",
73 Type::I64 => "i64",
74 Type::I128 => "i128",
75 Type::U8 => "u8",
76 Type::U16 => "u16",
77 Type::U32 => "u32",
78 Type::U64 => "u64",
79 Type::U128 => "u128",
80 Type::Felt => "felt",
81 Type::Array(array_ty)
82 if array_ty.element_type() == &Type::Felt && array_ty.len() == 4 =>
83 {
84 "word"
85 }
86 Type::Ptr(_) => "ptr",
87 ty => panic!("unsupported memory read type serialization: {ty}"),
88 }
89 }
90}
91
92#[derive(Default, Debug, Parser)]
93#[command(name = "read")]
94pub struct Read {
95 #[arg(required(true), value_name = "ADDR", value_parser(parse_address))]
97 pub addr: u32,
98 #[arg(
100 short = 't',
101 long = "type",
102 value_name = "TYPE",
103 value_parser(TypeParser)
104 )]
105 pub ty: Option<Type>,
106 #[arg(short = 'c', long = "count", value_name = "N", default_value_t = 1)]
108 pub count: u8,
109 #[arg(
111 short = 'm',
112 long = "mode",
113 value_name = "MODE",
114 default_value_t = MemoryMode::Word,
115 value_parser(MemoryModeParser)
116 )]
117 pub mode: MemoryMode,
118 #[arg(
120 short = 'f',
121 long = "format",
122 value_name = "FORMAT",
123 default_value_t = FormatType::Decimal,
124 value_parser(FormatTypeParser)
125 )]
126 pub format: FormatType,
127}
128impl Read {
129 pub fn parse<I, S>(argv: I) -> Result<Self, String>
130 where
131 I: IntoIterator<Item = S>,
132 S: Into<OsString> + Clone,
133 {
134 let command = <Self as clap::CommandFactory>::command()
135 .disable_help_flag(true)
136 .disable_version_flag(true)
137 .disable_colored_help(true)
138 .no_binary_name(true);
139
140 let mut matches = command.try_get_matches_from(argv).map_err(|err| err.to_string())?;
141 <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
142 .map_err(|err| err.to_string())
143 }
144}
145
146#[doc(hidden)]
147#[derive(Clone)]
148struct TypeParser;
149impl clap::builder::TypedValueParser for TypeParser {
150 type Value = Type;
151
152 fn parse_ref(
153 &self,
154 _cmd: &clap::Command,
155 _arg: Option<&clap::Arg>,
156 value: &OsStr,
157 ) -> Result<Self::Value, clap::error::Error> {
158 use clap::error::{Error, ErrorKind};
159
160 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
161
162 Ok(match value {
163 "i1" => Type::I1,
164 "i8" => Type::I8,
165 "i16" => Type::I16,
166 "i32" => Type::I32,
167 "i64" => Type::I64,
168 "i128" => Type::I128,
169 "u8" => Type::U8,
170 "u16" => Type::U16,
171 "u32" => Type::U32,
172 "u64" => Type::U64,
173 "u128" => Type::U128,
174 "felt" => Type::Felt,
175 "word" => Type::from(ArrayType::new(Type::Felt, 4)),
176 "ptr" | "pointer" => Type::from(PointerType::new(Type::U32)),
177 _ => {
178 return Err(Error::raw(
179 ErrorKind::InvalidValue,
180 format!("invalid/unsupported type '{value}'"),
181 ));
182 }
183 })
184 }
185}
186
187fn parse_address(s: &str) -> Result<u32, String> {
188 if let Some(s) = s.strip_prefix("0x") {
189 u32::from_str_radix(s, 16).map_err(|err| format!("invalid memory address: {err}"))
190 } else if s.is_empty() {
191 Err(format!("expected memory address at '{s}'"))
192 } else {
193 s.parse::<u32>().map_err(|err| format!("invalid memory address: {err}"))
194 }
195}
196
197#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
198pub enum MemoryMode {
199 #[default]
200 Word,
201 Byte,
202}
203impl fmt::Display for MemoryMode {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 match self {
206 Self::Word => f.write_str("word"),
207 Self::Byte => f.write_str("byte"),
208 }
209 }
210}
211impl FromStr for MemoryMode {
212 type Err = String;
213
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 match s {
216 "w" | "word" | "words" | "miden" => Ok(Self::Word),
217 "b" | "byte" | "bytes" | "rust" => Ok(Self::Byte),
218 _ => Err(format!("invalid memory mode '{s}'")),
219 }
220 }
221}
222
223#[doc(hidden)]
224#[derive(Clone)]
225struct MemoryModeParser;
226impl clap::builder::TypedValueParser for MemoryModeParser {
227 type Value = MemoryMode;
228
229 fn possible_values(
230 &self,
231 ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
232 use clap::builder::PossibleValue;
233 Some(Box::new(
234 [
235 PossibleValue::new("words").aliases(["w", "word", "miden"]),
236 PossibleValue::new("bytes").aliases(["b", "byte", "rust"]),
237 ]
238 .into_iter(),
239 ))
240 }
241
242 fn parse_ref(
243 &self,
244 _cmd: &clap::Command,
245 _arg: Option<&clap::Arg>,
246 value: &OsStr,
247 ) -> Result<Self::Value, clap::error::Error> {
248 use clap::error::{Error, ErrorKind};
249
250 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
251 value.parse().map_err(|err| Error::raw(ErrorKind::InvalidValue, err))
252 }
253}
254
255#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
256pub enum FormatType {
257 #[default]
258 Decimal,
259 Hex,
260 Binary,
261}
262impl fmt::Display for FormatType {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 match self {
265 Self::Decimal => f.write_str("decimal"),
266 Self::Hex => f.write_str("hex"),
267 Self::Binary => f.write_str("binary"),
268 }
269 }
270}
271impl FromStr for FormatType {
272 type Err = String;
273
274 fn from_str(s: &str) -> Result<Self, Self::Err> {
275 match s {
276 "d" | "decimal" => Ok(Self::Decimal),
277 "x" | "hex" | "hexadecimal" => Ok(Self::Hex),
278 "b" | "bin" | "binary" | "bits" => Ok(Self::Binary),
279 _ => Err(format!("invalid format type '{s}'")),
280 }
281 }
282}
283
284#[doc(hidden)]
285#[derive(Clone)]
286struct FormatTypeParser;
287impl clap::builder::TypedValueParser for FormatTypeParser {
288 type Value = FormatType;
289
290 fn possible_values(
291 &self,
292 ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
293 use clap::builder::PossibleValue;
294 Some(Box::new(
295 [
296 PossibleValue::new("decimal").alias("d"),
297 PossibleValue::new("hex").aliases(["x", "hexadecimal"]),
298 PossibleValue::new("binary").aliases(["b", "bin", "bits"]),
299 ]
300 .into_iter(),
301 ))
302 }
303
304 fn parse_ref(
305 &self,
306 _cmd: &clap::Command,
307 _arg: Option<&clap::Arg>,
308 value: &OsStr,
309 ) -> Result<Self::Value, clap::error::Error> {
310 use clap::error::{Error, ErrorKind};
311
312 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
313 value.parse().map_err(|err| Error::raw(ErrorKind::InvalidValue, err))
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::FormatType;
320 use crate::test_utils::write_scalar_bytes;
321
322 #[test]
323 fn write_scalar_bytes_reads_little_endian_u64() {
324 let mut output = String::new();
325
326 write_scalar_bytes(&mut output, "u64", FormatType::Decimal, &[1, 2, 3, 4, 5, 6, 7, 8])
327 .unwrap();
328
329 assert_eq!(output, u64::from_le_bytes([1, 2, 3, 4, 5, 6, 7, 8]).to_string());
330 }
331
332 #[test]
333 fn write_scalar_bytes_reads_little_endian_u16_hex() {
334 let mut output = String::new();
335
336 write_scalar_bytes(&mut output, "u16", FormatType::Hex, &[0x34, 0x12]).unwrap();
337
338 assert_eq!(output, "1234");
339 }
340}