midenc_debug/debug/
memory.rs

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