diff --git a/ast/Cargo.toml b/ast/Cargo.toml index dfd18c46..d8d58e0a 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -15,6 +15,7 @@ fold = [] unparse = ["rustpython-literal"] visitor = [] all-nodes-with-ranges = [] +pyo3 = ["dep:pyo3", "num-complex", "once_cell"] [dependencies] rustpython-parser-core = { workspace = true } @@ -23,6 +24,6 @@ rustpython-literal = { workspace = true, optional = true } is-macro = { workspace = true } num-bigint = { workspace = true } static_assertions = "1.1.0" -num-complex = { workspace = true } -once_cell = { workspace = true } +num-complex = { workspace = true, optional = true } +once_cell = { workspace = true, optional = true } pyo3 = { workspace = true, optional = true, features = ["num-bigint", "num-complex"] } diff --git a/core/src/format.rs b/core/src/format.rs index c3ee5bac..d0820299 100644 --- a/core/src/format.rs +++ b/core/src/format.rs @@ -11,3 +11,15 @@ pub enum ConversionFlag { /// Converts by calling `repr()`. Repr = b'r' as i8, } + +impl ConversionFlag { + pub fn to_byte(&self) -> Option { + match self { + Self::None => None, + flag => Some(*flag as u8), + } + } + pub fn to_char(&self) -> Option { + Some(self.to_byte()? as char) + } +} diff --git a/core/src/mode.rs b/core/src/mode.rs index 7d77759e..03940ac6 100644 --- a/core/src/mode.rs +++ b/core/src/mode.rs @@ -1,7 +1,7 @@ //! Control in the different modes by which a source file can be parsed. /// The mode argument specifies in what way code must be parsed. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash, PartialEq, Eq)] pub enum Mode { /// The code consists of a sequence of statements. Module, diff --git a/literal/Cargo.toml b/literal/Cargo.toml index ccb41475..4133d97a 100644 --- a/literal/Cargo.toml +++ b/literal/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT" [dependencies] hexf-parse = "0.2.1" +is-macro.workspace = true lexical-parse-float = { version = "0.8.0", features = ["format"] } num-traits = { workspace = true } unic-ucd-category = "0.9" diff --git a/literal/src/escape.rs b/literal/src/escape.rs index 80a105c6..082248a5 100644 --- a/literal/src/escape.rs +++ b/literal/src/escape.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, is_macro::Is)] pub enum Quote { Single, Double, diff --git a/literal/src/format.rs b/literal/src/format.rs index 3d5d9d7b..4ce21ad7 100644 --- a/literal/src/format.rs +++ b/literal/src/format.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, is_macro::Is, Hash)] pub enum Case { Lower, Upper, diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 03c933f5..824ce61a 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -40,4 +40,4 @@ rustc-hash = "1.1.0" serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] -insta = { workspace = true } \ No newline at end of file +insta = { workspace = true } diff --git a/parser/src/string.rs b/parser/src/string.rs index e9def12c..f575dc37 100644 --- a/parser/src/string.rs +++ b/parser/src/string.rs @@ -156,13 +156,13 @@ impl<'a> StringParser<'a> { 'v' => '\x0b', o @ '0'..='7' => self.parse_octet(o), 'x' => self.parse_unicode_literal(2)?, - 'u' if !self.kind.is_bytes() => self.parse_unicode_literal(4)?, - 'U' if !self.kind.is_bytes() => self.parse_unicode_literal(8)?, - 'N' if !self.kind.is_bytes() => self.parse_unicode_name()?, + 'u' if !self.kind.is_any_bytes() => self.parse_unicode_literal(4)?, + 'U' if !self.kind.is_any_bytes() => self.parse_unicode_literal(8)?, + 'N' if !self.kind.is_any_bytes() => self.parse_unicode_name()?, // Special cases where the escape sequence is not a single character '\n' => return Ok("".to_string()), c => { - if self.kind.is_bytes() && !c.is_ascii() { + if self.kind.is_any_bytes() && !c.is_ascii() { return Err(LexicalError { error: LexicalErrorType::OtherError( "bytes can only contain ASCII literal characters".to_owned(), @@ -578,9 +578,9 @@ impl<'a> StringParser<'a> { } fn parse(&mut self) -> Result, LexicalError> { - if self.kind.is_fstring() { + if self.kind.is_any_fstring() { self.parse_fstring(0) - } else if self.kind.is_bytes() { + } else if self.kind.is_any_bytes() { self.parse_bytes().map(|expr| vec![expr]) } else { self.parse_string().map(|expr| vec![expr]) @@ -611,10 +611,12 @@ pub(crate) fn parse_strings( let initial_start = values[0].0; let last_end = values.last().unwrap().2; let initial_kind = (values[0].1 .1 == StringKind::Unicode).then(|| "u".to_owned()); - let has_fstring = values.iter().any(|(_, (_, kind, ..), _)| kind.is_fstring()); + let has_fstring = values + .iter() + .any(|(_, (_, kind, ..), _)| kind.is_any_fstring()); let num_bytes = values .iter() - .filter(|(_, (_, kind, ..), _)| kind.is_bytes()) + .filter(|(_, (_, kind, ..), _)| kind.is_any_bytes()) .count(); let has_bytes = num_bytes > 0; diff --git a/parser/src/token.rs b/parser/src/token.rs index e09b5b08..e60d6554 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -327,7 +327,7 @@ impl fmt::Display for Tok { /// section of the Python reference. /// /// [String and Bytes literals]: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)] // TODO: is_macro::Is pub enum StringKind { /// A normal string literal with no prefix. String, @@ -398,14 +398,14 @@ impl StringKind { /// Returns true if the string is an f-string, i,e one of /// [`StringKind::FString`] or [`StringKind::RawFString`]. - pub fn is_fstring(&self) -> bool { + pub fn is_any_fstring(&self) -> bool { use StringKind::{FString, RawFString}; matches!(self, FString | RawFString) } /// Returns true if the string is a byte string, i,e one of /// [`StringKind::Bytes`] or [`StringKind::RawBytes`]. - pub fn is_bytes(&self) -> bool { + pub fn is_any_bytes(&self) -> bool { use StringKind::{Bytes, RawBytes}; matches!(self, Bytes | RawBytes) }