• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pomsky-lang / pomsky / 12076846628

29 Nov 2024 12:11AM UTC coverage: 81.241% (-1.6%) from 82.811%
12076846628

push

github

Aloso
chore: try linking PCRE2 statically

4296 of 5288 relevant lines covered (81.24%)

406362.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.2
/pomsky-syntax/src/exprs/char_class/char_group.rs
1
//! Contains the [`CharGroup`] type, which is the contents of a
2
//! [`CharClass`](crate::char_class::CharClass).
3
//!
4
//! However, a `CharGroup` doesn't store the information whether the character
5
//! class is negated.
6
//!
7
//! Refer to the [`char_class` module](crate::char_class) for more information.
8

9
use crate::{error::ParseErrorKind, Span};
10

11
use super::unicode::{Category, CodeBlock, OtherProperties, Script};
12

13
/// The contents of a [`CharClass`](crate::char_class::CharClass).
14
///
15
/// Refer to the [`char_class` module](crate::char_class) for more information.
16
#[derive(Clone, PartialEq, Eq)]
17
pub struct CharGroup {
18
    /// This variant is used for the remaining cases.
19
    pub items: Vec<GroupItem>,
20
}
21

22
impl CharGroup {
23
    /// Tries to create a `CharGroup` from a range of characters (inclusive).
24
    /// Returns `None` if `last` is lower than `first`.
25
    pub(crate) fn try_from_range(first: char, last: char) -> Option<Vec<GroupItem>> {
7✔
26
        if first < last {
7✔
27
            Some(vec![GroupItem::Range { first, last }])
6✔
28
        } else {
29
            None
1✔
30
        }
31
    }
7✔
32

33
    /// Try to create a `CharGroup` from the name of a character class. Fails if
34
    /// the name is lowercase and not known, or if it matches a keyword.
35
    ///
36
    /// POSIX classes (e.g. `alnum` or `blank`) are converted to ranges (e.g.
37
    /// `[0-9a-zA-Z]`). This is relatively simple and maximizes
38
    /// compatibility.
39
    ///
40
    /// If the name is uppercase (and not `R`), we just assume that it is a
41
    /// Unicode category, script or block. This needs to be fixed at one
42
    /// point!
43
    pub(crate) fn try_from_group_name(
179✔
44
        kind: Option<&str>,
179✔
45
        name: &str,
179✔
46
        negative: bool,
179✔
47
        span: Span,
179✔
48
    ) -> Result<Vec<GroupItem>, ParseErrorKind> {
179✔
49
        Ok(match name {
179✔
50
            _ if name == "ascii" || name.starts_with("ascii_") => {
179✔
51
                super::ascii::parse_ascii_group(name, negative)?
15✔
52
            }
53
            _ => {
54
                let name = super::unicode::parse_group_name(kind, name)?;
164✔
55
                vec![GroupItem::Named { name, negative, span }]
161✔
56
            }
57
        })
58
    }
179✔
59
}
60

61
/// One item in a character class.
62
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63
pub enum GroupItem {
64
    /// A Unicode code point. It can be denoted in quotes (e.g. `'a'`) or in
65
    /// hexadecimal notation (`U+201`).
66
    ///
67
    /// Some non-printable ASCII characters are also parsed to a
68
    /// [`GroupItem::Char`]: `[n]`, `[t]`, `[r]`, `[a]`, `[e]` and `[f]`.
69
    Char(char),
70
    /// A range of Unicode code points. It is denoted as `A-B`, where `A` and
71
    /// `B` are Unicode code points, allowing the same notation as for
72
    /// [`GroupItem::Char`]. Both `A` and `B` are included in the range.
73
    Range { first: char, last: char },
74
    /// A named character class, i.e. a shorthand or a Unicode
75
    /// category/script/block. Shorthands are `[w]`, `[s]`, `[d]`, `[v]`,
76
    /// `[h]` and `[R]`.
77
    ///
78
    /// Some of them (`w`, `d`, `s` and Unicode) can be negated.
79
    Named { name: GroupName, negative: bool, span: Span },
80
}
81

82
impl GroupItem {
83
    pub(crate) fn range_unchecked(first: char, last: char) -> Self {
24✔
84
        GroupItem::Range { first, last }
24✔
85
    }
24✔
86

87
    #[cfg(feature = "dbg")]
88
    pub(crate) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) {
10✔
89
        fn print_char(c: char, buf: &mut crate::PrettyPrinter) {
8✔
90
            match c {
8✔
91
                '\n' => buf.push('n'),
1✔
92
                '\r' => buf.push('r'),
1✔
93
                '\t' => buf.push('t'),
1✔
94
                '\u{07}' => buf.push('a'),
1✔
95
                '\u{1b}' => buf.push('e'),
1✔
96
                '\u{0c}' => buf.push('f'),
1✔
97
                _ => buf.pretty_print_char(c),
2✔
98
            }
99
        }
8✔
100

101
        match *self {
10✔
102
            Self::Char(c) => print_char(c, buf),
6✔
103
            Self::Range { first, last } => {
1✔
104
                print_char(first, buf);
1✔
105
                buf.push('-');
1✔
106
                print_char(last, buf);
1✔
107
            }
1✔
108
            Self::Named { name, negative, .. } => {
3✔
109
                if negative {
3✔
110
                    buf.push('!');
×
111
                }
3✔
112
                let name = match name {
3✔
113
                    GroupName::Word => "word",
1✔
114
                    GroupName::Digit => "digit",
1✔
115
                    GroupName::Space => "space",
1✔
116
                    GroupName::HorizSpace => "horiz_space",
×
117
                    GroupName::VertSpace => "vert_space",
×
118
                    GroupName::Category(c) => c.as_str(),
×
119
                    GroupName::Script(s, e) => {
×
120
                        buf.push_str(e.as_str());
×
121
                        buf.push_str(s.as_str());
×
122
                        return;
×
123
                    }
124
                    GroupName::CodeBlock(b) => b.as_str(),
×
125
                    GroupName::OtherProperties(b) => b.as_str(),
×
126
                };
127
                buf.push_str(name);
3✔
128
            }
129
        }
130
    }
10✔
131
}
132

133
#[cfg(feature = "arbitrary")]
134
impl arbitrary::Arbitrary<'_> for GroupItem {
135
    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
136
        Ok(match u.int_in_range(0u8..=2)? {
137
            0 => GroupItem::Char(u.arbitrary()?),
138
            1 => {
139
                let first = u.arbitrary()?;
140
                let last = u.arbitrary()?;
141
                if first >= last {
142
                    return Err(arbitrary::Error::IncorrectFormat);
143
                }
144
                GroupItem::Range { first, last }
145
            }
146
            _ => GroupItem::Named {
147
                name: GroupName::arbitrary(u)?,
148
                negative: bool::arbitrary(u)?,
149
                span: Span::arbitrary(u)?,
150
            },
151
        })
152
    }
153

154
    fn size_hint(depth: usize) -> (usize, Option<usize>) {
155
        arbitrary::size_hint::and(
156
            u8::size_hint(depth),
157
            arbitrary::size_hint::or_all(&[
158
                char::size_hint(depth),
159
                arbitrary::size_hint::and(char::size_hint(depth), char::size_hint(depth)),
160
                arbitrary::size_hint::and(GroupName::size_hint(depth), bool::size_hint(depth)),
161
            ]),
162
        )
163
    }
164
}
165

166
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
167
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
168
pub enum GroupName {
169
    Word,
170
    Digit,
171
    Space,
172
    HorizSpace,
173
    VertSpace,
174
    Category(Category),
175
    Script(Script, ScriptExtension),
176
    CodeBlock(CodeBlock),
177
    OtherProperties(OtherProperties),
178
}
179

180
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
181
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
182
pub enum ScriptExtension {
183
    Yes,
184
    No,
185
    Unspecified,
186
}
187

188
impl GroupName {
189
    pub fn kind(self) -> &'static str {
×
190
        match self {
×
191
            GroupName::Word
192
            | GroupName::Digit
193
            | GroupName::Space
194
            | GroupName::HorizSpace
195
            | GroupName::VertSpace => "shorthand",
×
196
            GroupName::Category(_) => "category",
×
197
            GroupName::Script(..) => "script",
×
198
            GroupName::CodeBlock(_) => "block",
×
199
            GroupName::OtherProperties(_) => "property",
×
200
        }
201
    }
×
202
}
203

204
impl ScriptExtension {
205
    pub fn as_str(self) -> &'static str {
×
206
        match self {
×
207
            ScriptExtension::Yes => "scx:",
×
208
            ScriptExtension::No => "sc:",
×
209
            ScriptExtension::Unspecified => "",
×
210
        }
211
    }
×
212
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc