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