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

pomsky-lang / pomsky / 12099739427

30 Nov 2024 09:51PM UTC coverage: 80.473% (-0.6%) from 81.072%
12099739427

push

github

Aloso
feat: character set intersections

200 of 274 new or added lines in 19 files covered. (72.99%)

3 existing lines in 3 files now uncovered.

4422 of 5495 relevant lines covered (80.47%)

391063.72 hits per line

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

91.51
/pomsky-lib/src/exprs/char_class/char_set_item.rs
1
use std::fmt;
2

3
use crate::options::RegexFlavor;
4

5
use super::{literal, Regex, RegexProperty, RegexShorthand, UnicodeSet};
6

7
#[cfg_attr(feature = "dbg", derive(Debug))]
8
#[derive(Default)]
9
pub(crate) struct RegexCompoundCharSet {
10
    pub(crate) negative: bool,
11
    pub(crate) intersections: Vec<RegexCharSet>,
12
}
13

14
impl RegexCompoundCharSet {
15
    pub(crate) fn new(set: RegexCharSet) -> Self {
7✔
16
        RegexCompoundCharSet { negative: false, intersections: vec![set] }
7✔
17
    }
7✔
18

NEW
19
    pub(crate) fn negate(mut self) -> RegexCompoundCharSet {
×
NEW
20
        self.negative = !self.negative;
×
NEW
21
        self
×
NEW
22
    }
×
23

24
    pub(crate) fn add(mut self, other: RegexCharSet) -> Regex {
7✔
25
        if other.negative && self.intersections.iter().all(|i| i.negative) {
7✔
26
            let mut intersections = self.intersections.into_iter();
1✔
27
            let mut char_set = intersections.next().expect("Intersection is empty");
1✔
28
            for next_set in intersections {
1✔
NEW
29
                char_set.set.extend(next_set.set);
×
NEW
30
            }
×
31
            char_set.set.extend(other.set);
1✔
32
            if self.negative {
1✔
NEW
33
                char_set = char_set.negate();
×
34
            }
1✔
35
            Regex::CharSet(char_set)
1✔
36
        } else {
37
            self.intersections.push(other);
6✔
38
            Regex::CompoundCharSet(self)
6✔
39
        }
40
    }
7✔
41

42
    pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor) {
5✔
43
        if self.negative {
5✔
NEW
44
            buf.push_str("[^");
×
45
        } else {
5✔
46
            buf.push('[');
5✔
47
        }
5✔
48

49
        let mut is_first = true;
5✔
50
        for intersection in &self.intersections {
15✔
51
            if !is_first {
10✔
52
                buf.push_str("&&");
5✔
53
            }
5✔
54
            intersection.codegen(buf, flavor, true);
10✔
55
            is_first = false;
10✔
56
        }
57

58
        buf.push(']');
5✔
59
    }
5✔
60
}
61

62
#[cfg_attr(feature = "dbg", derive(Debug))]
63
#[derive(Default)]
64
pub(crate) struct RegexCharSet {
65
    pub(crate) negative: bool,
66
    pub(crate) set: UnicodeSet,
67
}
68

69
impl RegexCharSet {
70
    pub(crate) fn new(items: UnicodeSet) -> Self {
244✔
71
        Self { negative: false, set: items }
244✔
72
    }
244✔
73

74
    pub(crate) fn negate(mut self) -> Self {
51✔
75
        self.negative = !self.negative;
51✔
76
        self
51✔
77
    }
51✔
78

79
    pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor, inside_compound: bool) {
423✔
80
        if self.set.len() == 1 {
423✔
81
            if let Some(range) = self.set.ranges().next() {
328✔
82
                let (first, last) = range.as_chars();
246✔
83
                if first == last && !self.negative {
246✔
84
                    return literal::codegen_char_esc(first, buf, flavor);
75✔
85
                }
171✔
86
            } else if let Some(prop) = self.set.props().next() {
82✔
87
                match prop {
82✔
88
                    RegexCharSetItem::Shorthand(s) => {
32✔
89
                        let shorthand = if self.negative { s.negate() } else { Some(s) };
32✔
90
                        if let Some(shorthand) = shorthand {
32✔
91
                            return shorthand.codegen(buf);
30✔
92
                        }
2✔
93
                    }
94
                    RegexCharSetItem::Property { negative, value } => {
50✔
95
                        return value.codegen(buf, negative ^ self.negative, flavor);
50✔
96
                    }
97
                }
NEW
98
            }
×
99
        }
95✔
100

101
        if self.negative {
268✔
102
            buf.push_str("[^");
31✔
103
        } else if !inside_compound {
237✔
104
            buf.push('[');
232✔
105
        }
232✔
106

107
        let mut is_first = true;
268✔
108
        for prop in self.set.props() {
268✔
109
            match prop {
79✔
110
                RegexCharSetItem::Shorthand(s) => s.codegen(buf),
58✔
111
                RegexCharSetItem::Property { negative, value } => {
21✔
112
                    value.codegen(buf, negative, flavor);
21✔
113
                }
21✔
114
            }
115
            is_first = false;
79✔
116
        }
117
        for range in self.set.ranges() {
383✔
118
            let (first, last) = range.as_chars();
383✔
119
            if first == last {
383✔
120
                literal::compile_char_esc_in_class(first, buf, is_first, flavor);
120✔
121
            } else {
120✔
122
                literal::compile_char_esc_in_class(first, buf, is_first, flavor);
263✔
123
                if range.first + 1 < range.last {
263✔
124
                    buf.push('-');
211✔
125
                }
211✔
126
                literal::compile_char_esc_in_class(last, buf, false, flavor);
263✔
127
            }
128
            is_first = false;
383✔
129
        }
130

131
        if self.negative || !inside_compound {
268✔
132
            buf.push(']');
263✔
133
        }
263✔
134
    }
423✔
135
}
136

137
#[derive(Clone, Copy, PartialEq, Eq)]
138
pub(crate) enum RegexCharSetItem {
139
    Shorthand(RegexShorthand),
140
    Property { negative: bool, value: RegexProperty },
141
}
142

143
impl RegexCharSetItem {
144
    pub(crate) fn negate(self) -> Option<Self> {
49✔
145
        match self {
49✔
146
            RegexCharSetItem::Shorthand(s) => s.negate().map(RegexCharSetItem::Shorthand),
23✔
147
            RegexCharSetItem::Property { negative, value } => {
26✔
148
                Some(RegexCharSetItem::Property { negative: !negative, value })
26✔
149
            }
150
        }
151
    }
49✔
152
}
153

154
impl fmt::Debug for RegexCharSetItem {
155
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6✔
156
        match self {
6✔
157
            Self::Shorthand(s) => f.write_str(s.as_str()),
4✔
158
            &Self::Property { value, negative } => {
2✔
159
                if negative {
2✔
160
                    f.write_str("!")?;
1✔
161
                }
1✔
162
                f.write_str(value.prefix_as_str())?;
2✔
163
                f.write_str(value.as_str())
2✔
164
            }
165
        }
166
    }
6✔
167
}
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