179 lines
5.1 KiB
Rust
179 lines
5.1 KiB
Rust
/// True if `c` is considered a whitespace according to Rust language definition.
|
|
/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html)
|
|
/// for definitions of these classes.
|
|
pub fn is_whitespace(c: char) -> bool {
|
|
// This is Pattern_White_Space.
|
|
//
|
|
// Note that this set is stable (ie, it doesn't change with different
|
|
// Unicode versions), so it's ok to just hard-code the values.
|
|
|
|
matches!(
|
|
c,
|
|
// Usual ASCII suspects
|
|
'\u{0009}' // \t
|
|
| '\u{000A}' // \n
|
|
| '\u{000B}' // vertical tab
|
|
| '\u{000C}' // form feed
|
|
| '\u{000D}' // \r
|
|
| '\u{0020}' // space
|
|
|
|
// NEXT LINE from latin1
|
|
| '\u{0085}'
|
|
|
|
// Bidi markers
|
|
| '\u{200E}' // LEFT-TO-RIGHT MARK
|
|
| '\u{200F}' // RIGHT-TO-LEFT MARK
|
|
|
|
// Dedicated whitespace characters from Unicode
|
|
| '\u{2028}' // LINE SEPARATOR
|
|
| '\u{2029}' // PARAGRAPH SEPARATOR
|
|
)
|
|
}
|
|
|
|
/// True if `c` is valid as a first character of an identifier.
|
|
/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
|
|
/// a formal definition of valid identifier name.
|
|
pub fn is_id_start(c: char) -> bool {
|
|
// This is XID_Start OR '_' (which formally is not a XID_Start).
|
|
c == '_' || unicode_xid::UnicodeXID::is_xid_start(c)
|
|
}
|
|
|
|
/// True if `c` is valid as a non-first character of an identifier.
|
|
/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
|
|
/// a formal definition of valid identifier name.
|
|
pub fn is_id_continue(c: char) -> bool {
|
|
unicode_xid::UnicodeXID::is_xid_continue(c)
|
|
}
|
|
|
|
/// The passed string is lexically an identifier.
|
|
pub fn is_ident(string: &str) -> bool {
|
|
let mut chars = string.chars();
|
|
if let Some(start) = chars.next() {
|
|
is_id_start(start) && chars.all(is_id_continue)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn is_digit(ch: char) -> bool {
|
|
('0'..='9').contains(&ch)
|
|
}
|
|
|
|
pub fn is_bin_digit(ch: char) -> bool {
|
|
ch == '0' || ch == '1'
|
|
}
|
|
|
|
pub fn is_nonzero_digit(ch: char) -> bool {
|
|
('1'..='9').contains(&ch)
|
|
}
|
|
|
|
pub fn is_oct_digit(ch: char) -> bool {
|
|
('0'..='7').contains(&ch)
|
|
}
|
|
|
|
pub fn is_hex_digit(ch: char) -> bool {
|
|
('0'..='9').contains(&ch)
|
|
|| ('a'..='f').contains(&ch)
|
|
|| ('A'..='F').contains(&ch)
|
|
}
|
|
|
|
/// Trait for only yielding the next item in the Iterator if it tests true for some predicate
|
|
pub trait NextIf<I>: Iterator<Item = I> + Clone {
|
|
/// Yield next item if `pred` returns `true`.
|
|
/// If `pred` returns `false` the Iterator is not advanced.
|
|
#[must_use]
|
|
fn next_if<F>(&mut self, pred: F) -> Option<I>
|
|
where
|
|
F: FnOnce(&Self::Item) -> bool,
|
|
{
|
|
let old = self.clone();
|
|
match self.next() {
|
|
Some(item) => {
|
|
if pred(&item) {
|
|
Some(item)
|
|
} else {
|
|
*self = old;
|
|
None
|
|
}
|
|
}
|
|
None => None,
|
|
}
|
|
}
|
|
/// Yield next item if `pred` returns `Some(T)`.
|
|
/// If `pred` returns `None` the Iterator is not advanced.
|
|
#[must_use]
|
|
fn next_if_map<F, T>(&mut self, pred: F) -> Option<T>
|
|
where
|
|
F: FnOnce(Self::Item) -> Option<T>,
|
|
{
|
|
let old = self.clone();
|
|
match self.next() {
|
|
Some(item) => match pred(item) {
|
|
None => {
|
|
*self = old;
|
|
None
|
|
}
|
|
some => some,
|
|
},
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<I, T> NextIf<I> for T where T: Iterator<Item = I> + Clone {}
|
|
|
|
pub trait FallibleParse<I>: Iterator<Item = I> + Clone {
|
|
/// consumes items from `self` if and only if `map` yields `Some`.
|
|
#[must_use]
|
|
fn try_parse<F, U>(&mut self, map: F) -> Option<U>
|
|
where
|
|
F: FnOnce(&mut Self) -> Option<U>,
|
|
{
|
|
// clone iterator and keep around
|
|
let old = self.clone();
|
|
match map(self) {
|
|
Some(result) => Some(result),
|
|
None => {
|
|
// the map function failed, restore iterator and yield None.
|
|
*self = old;
|
|
None
|
|
}
|
|
}
|
|
}
|
|
#[must_use]
|
|
fn try_parse_result<F, U, E>(&mut self, map: F) -> Result<U, E>
|
|
where
|
|
F: FnOnce(&mut Self) -> Result<U, E>,
|
|
{
|
|
// clone iterator and keep around
|
|
let old = self.clone();
|
|
match map(self) {
|
|
Ok(result) => Ok(result),
|
|
Err(e) => {
|
|
// the map function failed, restore iterator and yield None.
|
|
*self = old;
|
|
Err(e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<I, T> FallibleParse<I> for T where T: Iterator<Item = I> + Clone {}
|
|
|
|
#[macro_export]
|
|
macro_rules! variant {
|
|
($value:expr => $pattern:pat) => {
|
|
let $pattern = $value else { unreachable!() };
|
|
};
|
|
($pattern:pat = $value:expr) => {
|
|
let $pattern = $value else { unreachable!() };
|
|
};
|
|
}
|
|
|
|
pub fn from_lo_hi_dwords(lo: u32, hi: u32) -> u64 {
|
|
lo as u64 | (hi as u64) << 32
|
|
}
|
|
pub fn into_lo_hi_dwords(qword: u64) -> (u32, u32) {
|
|
(qword as u32, (qword >> 32) as u32)
|
|
}
|