midenc_debug/debug/
memory.rs

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    /// The memory address to start reading from
45    #[arg(required(true), value_name = "ADDR", value_parser(parse_address))]
46    pub addr: u32,
47    /// The type of value to read from ADDR, defaults to 'word'
48    #[arg(
49        short = 't',
50        long = "type",
51        value_name = "TYPE",
52        value_parser(TypeParser)
53    )]
54    pub ty: Option<Type>,
55    /// The number of values to read
56    #[arg(short = 'c', long = "count", value_name = "N", default_value_t = 1)]
57    pub count: u8,
58    /// The addressing mode to use
59    #[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    /// The format to use when printing integral values
68    #[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}