1use std::{
2 ffi::{OsStr, OsString},
3 fmt,
4 str::FromStr,
5 sync::Arc,
6};
7
8use clap::{Parser, ValueEnum};
9use midenc_codegen_masm::NativePtr;
10use midenc_hir::{ArrayType, PointerType, Type};
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
42#[derive(Default, Debug, Parser)]
43#[command(name = "read")]
44pub struct Read {
45 #[arg(required(true), value_name = "ADDR", value_parser(parse_address))]
47 pub addr: u32,
48 #[arg(
50 short = 't',
51 long = "type",
52 value_name = "TYPE",
53 value_parser(TypeParser)
54 )]
55 pub ty: Option<Type>,
56 #[arg(short = 'c', long = "count", value_name = "N", default_value_t = 1)]
58 pub count: u8,
59 #[arg(
61 short = 'm',
62 long = "mode",
63 value_name = "MODE",
64 default_value_t = MemoryMode::Word,
65 value_parser(MemoryModeParser)
66 )]
67 pub mode: MemoryMode,
68 #[arg(
70 short = 'f',
71 long = "format",
72 value_name = "FORMAT",
73 default_value_t = FormatType::Decimal,
74 value_parser(FormatTypeParser)
75 )]
76 pub format: FormatType,
77}
78impl Read {
79 pub fn parse<I, S>(argv: I) -> Result<Self, String>
80 where
81 I: IntoIterator<Item = S>,
82 S: Into<OsString> + Clone,
83 {
84 let command = <Self as clap::CommandFactory>::command()
85 .disable_help_flag(true)
86 .disable_version_flag(true)
87 .disable_colored_help(true)
88 .no_binary_name(true);
89
90 let mut matches = command.try_get_matches_from(argv).map_err(|err| err.to_string())?;
91 <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
92 .map_err(|err| err.to_string())
93 }
94}
95
96#[doc(hidden)]
97#[derive(Clone)]
98struct TypeParser;
99impl clap::builder::TypedValueParser for TypeParser {
100 type Value = Type;
101
102 fn parse_ref(
103 &self,
104 _cmd: &clap::Command,
105 _arg: Option<&clap::Arg>,
106 value: &OsStr,
107 ) -> Result<Self::Value, clap::error::Error> {
108 use clap::error::{Error, ErrorKind};
109
110 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
111
112 Ok(match value {
113 "i1" => Type::I1,
114 "i8" => Type::I8,
115 "i16" => Type::I16,
116 "i32" => Type::I32,
117 "i64" => Type::I64,
118 "i128" => Type::I128,
119 "u8" => Type::U8,
120 "u16" => Type::U16,
121 "u32" => Type::U32,
122 "u64" => Type::U64,
123 "u128" => Type::U128,
124 "felt" => Type::Felt,
125 "word" => Type::from(ArrayType::new(Type::Felt, 4)),
126 "ptr" | "pointer" => Type::from(PointerType::new(Type::U32)),
127 _ => {
128 return Err(Error::raw(
129 ErrorKind::InvalidValue,
130 format!("invalid/unsupported type '{value}'"),
131 ))
132 }
133 })
134 }
135}
136
137fn parse_address(s: &str) -> Result<u32, String> {
138 if let Some(s) = s.strip_prefix("0x") {
139 u32::from_str_radix(s, 16).map_err(|err| format!("invalid memory address: {err}"))
140 } else if s.is_empty() {
141 Err(format!("expected memory address at '{s}'"))
142 } else {
143 s.parse::<u32>().map_err(|err| format!("invalid memory address: {err}"))
144 }
145}
146
147#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
148pub enum MemoryMode {
149 #[default]
150 Word,
151 Byte,
152}
153impl fmt::Display for MemoryMode {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 Self::Word => f.write_str("word"),
157 Self::Byte => f.write_str("byte"),
158 }
159 }
160}
161impl FromStr for MemoryMode {
162 type Err = String;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 match s {
166 "w" | "word" | "words" | "miden" => Ok(Self::Word),
167 "b" | "byte" | "bytes" | "rust" => Ok(Self::Byte),
168 _ => Err(format!("invalid memory mode '{s}'")),
169 }
170 }
171}
172
173#[doc(hidden)]
174#[derive(Clone)]
175struct MemoryModeParser;
176impl clap::builder::TypedValueParser for MemoryModeParser {
177 type Value = MemoryMode;
178
179 fn possible_values(
180 &self,
181 ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
182 use clap::builder::PossibleValue;
183 Some(Box::new(
184 [
185 PossibleValue::new("words").aliases(["w", "word", "miden"]),
186 PossibleValue::new("bytes").aliases(["b", "byte", "rust"]),
187 ]
188 .into_iter(),
189 ))
190 }
191
192 fn parse_ref(
193 &self,
194 _cmd: &clap::Command,
195 _arg: Option<&clap::Arg>,
196 value: &OsStr,
197 ) -> Result<Self::Value, clap::error::Error> {
198 use clap::error::{Error, ErrorKind};
199
200 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
201 value.parse().map_err(|err| Error::raw(ErrorKind::InvalidValue, err))
202 }
203}
204
205#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
206pub enum FormatType {
207 #[default]
208 Decimal,
209 Hex,
210 Binary,
211}
212impl fmt::Display for FormatType {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 match self {
215 Self::Decimal => f.write_str("decimal"),
216 Self::Hex => f.write_str("hex"),
217 Self::Binary => f.write_str("binary"),
218 }
219 }
220}
221impl FromStr for FormatType {
222 type Err = String;
223
224 fn from_str(s: &str) -> Result<Self, Self::Err> {
225 match s {
226 "d" | "decimal" => Ok(Self::Decimal),
227 "x" | "hex" | "hexadecimal" => Ok(Self::Hex),
228 "b" | "bin" | "binary" | "bits" => Ok(Self::Binary),
229 _ => Err(format!("invalid format type '{s}'")),
230 }
231 }
232}
233
234#[doc(hidden)]
235#[derive(Clone)]
236struct FormatTypeParser;
237impl clap::builder::TypedValueParser for FormatTypeParser {
238 type Value = FormatType;
239
240 fn possible_values(
241 &self,
242 ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
243 use clap::builder::PossibleValue;
244 Some(Box::new(
245 [
246 PossibleValue::new("decimal").alias("d"),
247 PossibleValue::new("hex").aliases(["x", "hexadecimal"]),
248 PossibleValue::new("binary").aliases(["b", "bin", "bits"]),
249 ]
250 .into_iter(),
251 ))
252 }
253
254 fn parse_ref(
255 &self,
256 _cmd: &clap::Command,
257 _arg: Option<&clap::Arg>,
258 value: &OsStr,
259 ) -> Result<Self::Value, clap::error::Error> {
260 use clap::error::{Error, ErrorKind};
261
262 let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
263 value.parse().map_err(|err| Error::raw(ErrorKind::InvalidValue, err))
264 }
265}