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

pomsky-lang / pomsky / 6421156176

05 Oct 2023 03:27PM UTC coverage: 80.191% (-0.1%) from 80.315%
6421156176

push

github

Aloso
chore: update deps, fix new lints

25 of 25 new or added lines in 6 files covered. (100.0%)

3267 of 4074 relevant lines covered (80.19%)

246.52 hits per line

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

95.75
/pomsky-syntax/src/parse/parser_impl.rs
1
use std::collections::HashSet;
2

3
use crate::{
4
    diagnose::{
5
        CharClassError, CharStringError, DeprecationWarning, NumberError, ParseWarningKind,
6
        RepetitionError,
7
    },
8
    error::{ParseError, ParseErrorKind as PEK},
9
    exprs::*,
10
    lexer::Token,
11
    Span,
12
};
13

14
use super::{helper, Parser};
15

16
type PResult<T> = Result<T, ParseError>;
17

18
const MAX_REPETITION: u32 = 65_535;
19

20
impl<'i> Parser<'i> {
21
    pub(super) fn parse_modified(&mut self) -> PResult<Rule<'i>> {
679✔
22
        let mut stmts = Vec::new();
679✔
23

24
        loop {
25
            let Some(stmt) = self.parse_mode_modifier()?.try_or_else(|| self.parse_let())? else {
1,445✔
26
                break;
27
            };
673✔
28
            stmts.push(stmt);
52✔
29
        }
30

31
        self.recursion_start()?;
1,352✔
32
        let mut rule = self.parse_or()?;
671✔
33
        self.recursion_end();
358✔
34

35
        // TODO: This should not be part of the parser
36
        if stmts.len() > 1 {
358✔
37
            let mut set = HashSet::new();
6✔
38
            for (stmt, _) in &stmts {
18✔
39
                if let Stmt::Let(l) = stmt {
13✔
40
                    if set.contains(l.name()) {
12✔
41
                        return Err(PEK::LetBindingExists.at(l.name_span));
1✔
42
                    }
43
                    set.insert(l.name());
11✔
44
                }
45
            }
46
        }
6✔
47

48
        let span_end = rule.span();
357✔
49
        for (stmt, span) in stmts.into_iter().rev() {
405✔
50
            rule = Rule::StmtExpr(Box::new(StmtExpr::new(stmt, rule, span.join(span_end))));
48✔
51
        }
52

53
        Ok(rule)
357✔
54
    }
679✔
55

56
    fn parse_mode_modifier(&mut self) -> PResult<Option<(Stmt<'i>, Span)>> {
731✔
57
        let mode = if self.consume_reserved("enable") {
743✔
58
            true
5✔
59
        } else if self.consume_reserved("disable") {
726✔
60
            false
12✔
61
        } else {
×
62
            return Ok(None);
714✔
63
        };
64

65
        let span_start = self.last_span();
17✔
66
        let setting = if self.consume_reserved("lazy") {
31✔
67
            BooleanSetting::Lazy
3✔
68
        } else if let Some((Token::Identifier, "unicode")) = self.peek() {
14✔
69
            self.advance();
14✔
70
            BooleanSetting::Unicode
14✔
71
        } else {
72
            return Err(PEK::Expected("`lazy` or `unicode`").at(self.span()));
×
73
        };
74
        self.expect(Token::Semicolon)?;
748✔
75
        let stmt = if mode { Stmt::Enable(setting) } else { Stmt::Disable(setting) };
17✔
76

77
        let span_end = self.last_span();
17✔
78
        Ok(Some((stmt, span_start.join(span_end))))
17✔
79
    }
731✔
80

81
    fn parse_let(&mut self) -> PResult<Option<(Stmt<'i>, Span)>> {
714✔
82
        if self.consume_reserved("let") {
749✔
83
            let span_start = self.last_span();
41✔
84
            let name_span = self.span();
41✔
85
            let name = self.expect_as(Token::Identifier).map_err(|e| {
43✔
86
                if self.is(Token::ReservedName) {
2✔
87
                    PEK::KeywordAfterLet(self.source_at(self.span()).to_owned()).at(e.span)
1✔
88
                } else {
89
                    e
1✔
90
                }
91
            })?;
4✔
92

93
            self.expect(Token::Equals)?;
39✔
94

95
            self.recursion_start()?;
38✔
96
            let rule = self.parse_or()?;
38✔
97
            self.recursion_end();
36✔
98

99
            self.expect(Token::Semicolon)
750✔
100
                .map_err(|p| PEK::Expected("expression or `;`").at(p.span))?;
2✔
101
            let span_end = self.last_span();
35✔
102

103
            Ok(Some((Stmt::Let(Let::new(name, rule, name_span)), span_start.join(span_end))))
35✔
104
        } else {
1✔
105
            Ok(None)
673✔
106
        }
107
    }
714✔
108

109
    fn parse_or(&mut self) -> PResult<Rule<'i>> {
709✔
110
        let mut span = self.span();
709✔
111
        let leading_pipe = self.consume(Token::Pipe);
709✔
112

113
        let mut alts = Vec::new();
709✔
114
        if let Some(first_alt) = self.parse_sequence()? {
709✔
115
            alts.push(first_alt);
370✔
116

117
            while self.consume(Token::Pipe) {
404✔
118
                if let Some(next_alt) = self.parse_sequence()? {
34✔
119
                    span = span.join(next_alt.span());
34✔
120
                    alts.push(next_alt);
34✔
121
                } else {
122
                    return Err(PEK::LonePipe.at(self.last_span()));
×
123
                }
124
            }
×
125

126
            if alts.len() == 1 {
370✔
127
                Ok(alts.pop().unwrap())
348✔
128
            } else {
129
                Ok(Alternation::new_expr(alts))
22✔
130
            }
131
        } else if leading_pipe {
30✔
132
            Err(PEK::LonePipe.at(span))
6✔
133
        } else {
134
            Ok(Alternation::new_expr(alts))
24✔
135
        }
136
    }
709✔
137

138
    fn parse_sequence(&mut self) -> PResult<Option<Rule<'i>>> {
743✔
139
        let mut fixes = Vec::new();
743✔
140
        while let Some(fix) = self.parse_fixes()? {
1,599✔
141
            fixes.push(fix);
856✔
142
        }
143

144
        Ok(if fixes.is_empty() {
838✔
145
            None
30✔
146
        } else if fixes.len() == 1 {
404✔
147
            Some(fixes.pop().unwrap())
299✔
148
        } else {
149
            let start = fixes.first().map(Rule::span).unwrap_or_default();
105✔
150
            let end = fixes.last().map(Rule::span).unwrap_or_default();
105✔
151
            let span = start.join(end);
105✔
152

153
            Some(Rule::Group(Group::new(fixes, GroupKind::Implicit, span)))
105✔
154
        })
155
    }
743✔
156

157
    fn parse_fixes(&mut self) -> PResult<Option<Rule<'i>>> {
1,599✔
158
        let mut nots_span = self.span();
1,599✔
159
        let mut nots = 0usize;
1,599✔
160
        while self.consume(Token::Not) {
1,680✔
161
            nots += 1;
81✔
162
            nots_span = nots_span.join(self.last_span());
81✔
163
        }
164

165
        let Some(mut rule) = self.parse_lookaround()?.try_or_else(|| self.parse_repeated())? else {
3,166✔
166
            if nots == 0 {
435✔
167
               return Ok(None);
434✔
168
            } else {
169
               return Err(PEK::Expected("expression").at(self.span()));
1✔
170
            }
171
        };
435✔
172

173
        match nots {
871✔
174
            0 => {}
175
            1 => rule.negate().map_err(|k| k.at(nots_span))?,
1,678✔
176
            _ => return Err(PEK::UnallowedMultiNot(nots).at(nots_span)),
5✔
177
        }
178

179
        Ok(Some(rule))
856✔
180
    }
1,599✔
181

182
    fn parse_lookaround(&mut self) -> PResult<Option<Rule<'i>>> {
1,599✔
183
        let kind = if self.consume(Token::LookAhead) {
1,617✔
184
            LookaroundKind::Ahead
14✔
185
        } else if self.consume(Token::LookBehind) {
1,585✔
186
            LookaroundKind::Behind
18✔
187
        } else {
×
188
            return Ok(None);
1,567✔
189
        };
190
        let start_span = self.last_span();
32✔
191

192
        self.recursion_start()?;
1,631✔
193
        let rule = self.parse_modified()?;
32✔
194
        self.recursion_end();
32✔
195

196
        let span = rule.span();
32✔
197
        Ok(Some(Rule::Lookaround(Box::new(Lookaround::new(rule, kind, start_span.join(span))))))
32✔
198
    }
1,599✔
199

200
    /// Parse an atom expression with possibly multiple repetitions, e.g. `E
201
    /// {3,} lazy ?`.
202
    fn parse_repeated(&mut self) -> PResult<Option<Rule<'i>>> {
1,567✔
203
        if let Some(mut rule) = self.parse_atom()? {
2,406✔
204
            if let Some((kind, quantifier, span)) = self.parse_repetition()? {
850✔
205
                let span = rule.span().join(span);
118✔
206
                rule = Rule::Repetition(Box::new(Repetition::new(rule, kind, quantifier, span)));
118✔
207
            }
118✔
208

209
            Ok(Some(rule))
839✔
210
        } else {
11✔
211
            Ok(None)
435✔
212
        }
213
    }
1,567✔
214

215
    /// Parse a repetition that can follow an atom: `+`, `?`, `*`, `{x}`,
216
    /// `{x,}`, `{,x}` or `{x,y}` optionally followed by the `greedy` or
217
    /// `lazy` keyword. `x` and `y` are number literals.
218
    fn parse_repetition(&mut self) -> PResult<Option<(RepetitionKind, Quantifier, Span)>> {
850✔
219
        let start = self.span();
850✔
220

221
        let kind = if self.consume(Token::Plus) {
850✔
222
            RepetitionKind::one_inf()
44✔
223
        } else if self.consume(Token::Star) {
806✔
224
            RepetitionKind::zero_inf()
19✔
225
        } else if self.consume(Token::QuestionMark) {
815✔
226
            RepetitionKind::zero_one()
33✔
227
        } else if let Some(kind) = self.parse_repetition_braces()? {
754✔
228
            kind
28✔
229
        } else {
230
            return Ok(None);
721✔
231
        };
232

233
        let quantifier = if self.consume_reserved("greedy") {
124✔
234
            Quantifier::Greedy
1✔
235
        } else if self.consume_reserved("lazy") {
123✔
236
            Quantifier::Lazy
8✔
237
        } else {
238
            Quantifier::Default
115✔
239
        };
240

241
        let multi_span = self.span();
124✔
242
        if self.consume(Token::Plus) || self.consume(Token::Star) {
124✔
243
            return Err(PEK::Repetition(RepetitionError::Multi).at(multi_span));
2✔
244
        } else if self.consume(Token::QuestionMark) {
122✔
245
            return Err(PEK::Repetition(RepetitionError::QmSuffix).at(multi_span));
2✔
246
        } else if self.parse_repetition_braces()?.is_some() {
120✔
247
            return Err(
2✔
248
                PEK::Repetition(RepetitionError::Multi).at(multi_span.join(self.last_span()))
2✔
249
            );
250
        }
251

252
        let end = self.last_span();
118✔
253
        Ok(Some((kind, quantifier, start.join(end))))
118✔
254
    }
850✔
255

256
    /// Parse `{2}`, `{2,}`, `{,2}` or `{2,5}`.
257
    fn parse_repetition_braces(&mut self) -> PResult<Option<RepetitionKind>> {
874✔
258
        if self.consume(Token::OpenBrace) {
904✔
259
            let num_start = self.span();
35✔
260

261
            // Both numbers and the comma are parsed optionally, then we check that one
262
            // of the allowed syntaxes is used: There must be at least one number, and if
263
            // there are two numbers, the comma is required. It also checks that the
264
            // numbers are in increasing order.
265
            let lower = self.consume_number(65_535)?;
35✔
266
            let comma = self.consume(Token::Comma);
35✔
267
            let upper = self.consume_number(65_535)?;
35✔
268

269
            let num_end = self.last_span();
34✔
270
            let num_span = num_start.join(num_end);
34✔
271

272
            let kind = match (lower, comma, upper) {
34✔
273
                (lower, true, upper) => (lower.unwrap_or(0), upper)
42✔
274
                    .try_into()
275
                    .map_err(|e| PEK::Repetition(e).at(num_span))?,
22✔
276

277
                (Some(_), false, Some(_)) => return Err(PEK::Expected("`}` or `,`").at(num_end)),
×
278
                (Some(rep), false, None) | (None, false, Some(rep)) => RepetitionKind::fixed(rep),
12✔
279
                (None, false, None) => return Err(PEK::Expected("number").at(self.span())),
1✔
280
            };
281

282
            self.expect(Token::CloseBrace)?;
906✔
283

284
            Ok(Some(kind))
30✔
285
        } else {
286
            Ok(None)
839✔
287
        }
288
    }
874✔
289

290
    fn parse_atom(&mut self) -> PResult<Option<Rule<'i>>> {
1,567✔
291
        Ok(self
1,567✔
292
            .parse_group()?
264✔
293
            .try_or_else(|| self.parse_string())?
2,484✔
294
            .try_or_else(|| self.parse_char_set())?
2,294✔
295
            .or_else(|| self.parse_boundary())
2,084✔
296
            .try_or_else(|| self.parse_reference())?
2,042✔
297
            .try_or_else(|| self.parse_code_point_rule())?
2,018✔
298
            .try_or_else(|| self.parse_range())?
1,982✔
299
            .try_or_else(|| self.parse_regex())?
1,956✔
300
            .try_or_else(|| self.parse_variable())?
1,946✔
301
            .or_else(|| self.parse_dot()))
1,746✔
302
    }
1,567✔
303

304
    /// Parses a (possibly capturing) group, e.g. `(E E | E)` or `:name(E)`.
305
    fn parse_group(&mut self) -> PResult<Option<Rule<'i>>> {
1,567✔
306
        let (kind, start_span) = self.parse_group_kind()?;
1,567✔
307
        if !kind.is_normal() {
1,564✔
308
            self.expect(Token::OpenParen)?;
49✔
309
        } else if !self.consume(Token::OpenParen) {
1,515✔
310
            return Ok(None);
1,181✔
311
        }
312

313
        self.recursion_start()?;
382✔
314
        let rule = self.parse_modified()?;
382✔
315
        self.recursion_end();
124✔
316

317
        self.expect(Token::CloseParen)
1,691✔
318
            .map_err(|p| PEK::Expected("`)` or an expression").at(p.span))?;
4✔
319
        // start_span may be 0..0, so we need to use join_unchecked
320
        let span = start_span.join_unchecked(self.last_span());
122✔
321

322
        let rule = Rule::Group(Group::new(vec![rule], kind, span));
122✔
323
        Ok(Some(rule))
122✔
324
    }
1,567✔
325

326
    /// Parses `:name` or just `:`. Returns the span of the colon with the name.
327
    fn parse_group_kind(&mut self) -> PResult<(GroupKind<'i>, Span)> {
1,567✔
328
        if self.consume_reserved("atomic") {
1,567✔
329
            let span = self.last_span();
3✔
330
            Ok((GroupKind::Atomic, span))
3✔
331
        } else if self.consume(Token::Colon) {
1,610✔
332
            let span = self.last_span();
49✔
333

334
            if let Some(keyword) = self.consume_as(Token::ReservedName) {
49✔
335
                return Err(PEK::KeywordAfterColon(keyword.into()).at(self.last_span()));
1✔
336
            }
337

338
            let name = self.consume_as(Token::Identifier);
48✔
339
            if let Some(name) = name {
48✔
340
                if let Some(invalid_index) = name.find(|c: char| !c.is_ascii_alphanumeric()) {
165✔
341
                    let c = name[invalid_index..].chars().next().unwrap();
1✔
342
                    let start = self.last_span().range_unchecked().start + invalid_index;
1✔
343
                    let len = c.len_utf8();
1✔
344
                    return Err(PEK::NonAsciiIdentAfterColon(c).at(Span::new(start, start + len)));
1✔
345
                }
346

347
                if name.len() > 32 {
31✔
348
                    return Err(PEK::GroupNameTooLong(name.len()).at(self.last_span()));
1✔
349
                }
350
            }
351
            Ok((GroupKind::Capturing(Capture::new(name)), span))
46✔
352
        } else {
353
            Ok((GroupKind::Normal, self.span().start()))
1,515✔
354
        }
355
    }
1,567✔
356

357
    /// Parses a string literal.
358
    fn parse_string(&mut self) -> PResult<Option<Rule<'i>>> {
1,181✔
359
        if let Some(s) = self.consume_as(Token::String) {
1,181✔
360
            let span = self.last_span();
189✔
361
            let content = helper::parse_quoted_text(s).map_err(|k| k.at(span))?;
190✔
362
            Ok(Some(Rule::Literal(Literal::new(content, span))))
188✔
363
        } else {
364
            Ok(None)
992✔
365
        }
366
    }
1,181✔
367

368
    /// Parses a char set, surrounded by `[` `]`. This was previously called a
369
    /// "char class", but that name is ambiguous and is being phased out.
370
    ///
371
    /// This function does _not_ parse exclamation marks in front of a char
372
    /// class, because negation is handled separately.
373
    fn parse_char_set(&mut self) -> PResult<Option<Rule<'i>>> {
992✔
374
        if self.consume(Token::OpenBracket) {
1,176✔
375
            let start_span = self.last_span();
197✔
376

377
            if self.consume(Token::Caret) {
197✔
378
                return Err(PEK::CharClass(CharClassError::CaretInGroup).at(self.last_span()));
2✔
379
            }
380

381
            let inner = self.parse_char_set_inner()?;
195✔
382

383
            self.expect(Token::CloseBracket).map_err(|p| {
1,179✔
384
                PEK::Expected("character class, string, code point, Unicode property or `]`")
2✔
385
                    .at(p.span)
1✔
386
            })?;
2✔
387
            let span = start_span.join(self.last_span());
185✔
388

389
            if inner.is_empty() {
185✔
390
                return Err(PEK::CharClass(CharClassError::Empty).at(span));
1✔
391
            }
392

393
            Ok(Some(Rule::CharClass(CharClass::new(inner, span))))
184✔
394
        } else {
2✔
395
            Ok(None)
795✔
396
        }
397
    }
992✔
398

399
    /// Parses a char group, i.e. the contents of a char set. This is a sequence
400
    /// of characters, character classes, character ranges or Unicode
401
    /// properties. Some of them can be negated.
402
    fn parse_char_set_inner(&mut self) -> PResult<Vec<GroupItem>> {
195✔
403
        let mut items = Vec::new();
195✔
404
        loop {
405
            let mut nots_span = self.span();
439✔
406
            let mut nots = 0usize;
439✔
407
            while self.consume(Token::Not) {
466✔
408
                nots += 1;
27✔
409
                nots_span = nots_span.join(self.last_span());
27✔
410
            }
411

412
            let group = if let Some(group) = self.parse_char_group_chars_or_range()? {
587✔
413
                if nots > 0 {
96✔
414
                    return Err(PEK::UnallowedNot.at(nots_span));
×
415
                }
416
                group
96✔
417
            } else if let Some(group) = self.parse_char_group_ident(nots % 2 != 0)? {
338✔
418
                if nots > 1 {
148✔
419
                    return Err(PEK::UnallowedMultiNot(nots).at(nots_span));
×
420
                }
421
                group
148✔
422
            } else if nots > 0 {
186✔
423
                return Err(PEK::ExpectedToken(Token::Identifier).at(self.span()));
×
424
            } else {
425
                break;
426
            };
439✔
427
            items.extend(group);
244✔
428
        }
429

430
        Ok(items)
186✔
431
    }
195✔
432

433
    /// Parses an identifier or dot in a char set
434
    fn parse_char_group_ident(&mut self, negative: bool) -> PResult<Option<Vec<GroupItem>>> {
338✔
435
        if self.consume(Token::Dot) || self.consume(Token::Identifier) {
486✔
436
            let span = self.last_span();
150✔
437

438
            let item = CharGroup::try_from_group_name(self.source_at(span), negative)
300✔
439
                .map_err(|e| e.at(span))?;
152✔
440

441
            Ok(Some(item))
148✔
442
        } else if let Some(name) = self.consume_as(Token::ReservedName) {
188✔
443
            Err(PEK::UnexpectedKeyword(name.to_owned()).at(self.last_span()))
2✔
444
        } else {
445
            Ok(None)
186✔
446
        }
447
    }
338✔
448

449
    /// Parses a string literal or a character range in a char set, e.g. `"axd"`
450
    /// or `'0'-'7'`.
451
    fn parse_char_group_chars_or_range(&mut self) -> PResult<Option<Vec<GroupItem>>> {
439✔
452
        let span1 = self.span();
439✔
453
        let Some(first) = self.parse_string_or_char()? else {
439✔
454
            return Ok(None);
338✔
455
        };
456

457
        if self.consume(Token::Dash) {
197✔
458
            let span2 = self.span();
9✔
459
            let Some(last) = self.parse_string_or_char()? else {
9✔
460
                return Err(PEK::Expected("code point or character").at(self.span()));
1✔
461
            };
462

463
            let first = first.to_char().map_err(|e| e.at(span1))?;
11✔
464
            let last = last.to_char().map_err(|e| e.at(span2))?;
6✔
465

466
            let group = CharGroup::try_from_range(first, last).ok_or_else(|| {
4✔
467
                PEK::CharClass(CharClassError::DescendingRange(first, last)).at(span1.join(span2))
×
468
            })?;
×
469
            Ok(Some(group))
4✔
470
        } else {
471
            let group = match first {
92✔
472
                StringOrChar::String(s) => {
49✔
473
                    let chars = helper::parse_quoted_text(s).map_err(|k| k.at(span1))?;
49✔
474
                    chars.chars().map(GroupItem::Char).collect()
49✔
475
                }
49✔
476
                StringOrChar::Char(c) => vec![GroupItem::Char(c)],
43✔
477
            };
478
            Ok(Some(group))
92✔
479
        }
480
    }
439✔
481

482
    fn parse_string_or_char(&mut self) -> PResult<Option<StringOrChar<'i>>> {
448✔
483
        let res = if let Some(s) = self.consume_as(Token::String) {
448✔
484
            StringOrChar::String(s)
62✔
485
        } else if let Some((c, _)) = self.parse_code_point()? {
425✔
486
            StringOrChar::Char(c)
8✔
487
        } else if let Some(c) = self.parse_special_char() {
378✔
488
            StringOrChar::Char(c)
39✔
489
        } else {
490
            return Ok(None);
339✔
491
        };
492
        Ok(Some(res))
109✔
493
    }
448✔
494

495
    fn parse_code_point(&mut self) -> PResult<Option<(char, Span)>> {
1,115✔
496
        if let Some(cp) = self.consume_as(Token::CodePoint) {
1,115✔
497
            let span = self.last_span();
44✔
498
            let trimmed_u = cp[1..].trim_start();
44✔
499
            if !trimmed_u.starts_with('+') {
44✔
500
                let warning = DeprecationWarning::Unicode(cp.into());
×
501
                self.add_warning(ParseWarningKind::Deprecation(warning).at(span))
×
502
            }
503

504
            let hex = trimmed_u.trim_start_matches(|c: char| c == '+' || c.is_whitespace());
132✔
505
            if hex.len() > 6 {
44✔
506
                return Err(PEK::InvalidCodePoint.at(span));
×
507
            }
508
            u32::from_str_radix(hex, 16)
132✔
509
                .ok()
510
                .and_then(|n| char::try_from(n).ok())
44✔
511
                .map(|c| Some((c, span)))
88✔
512
                .ok_or_else(|| PEK::InvalidCodePoint.at(span))
44✔
513
        } else {
514
            Ok(None)
1,071✔
515
        }
516
    }
1,115✔
517

518
    fn parse_code_point_rule(&mut self) -> PResult<Option<Rule<'i>>> {
729✔
519
        if let Some((c, span)) = self.parse_code_point()? {
729✔
520
            Ok(Some(Rule::CharClass(CharClass::new(vec![GroupItem::Char(c)], span))))
36✔
521
        } else {
522
            Ok(None)
693✔
523
        }
524
    }
729✔
525

526
    fn parse_special_char(&mut self) -> Option<char> {
378✔
527
        if let Some((Token::Identifier, string)) = self.peek() {
417✔
528
            let c = match string {
529
                "n" => '\n',
188✔
530
                "r" => '\r',
178✔
531
                "t" => '\t',
168✔
532
                "a" => '\u{07}',
165✔
533
                "e" => '\u{1B}',
162✔
534
                "f" => '\u{0C}',
159✔
535
                _ => return None,
149✔
536
            };
537
            self.advance();
39✔
538
            Some(c)
39✔
539
        } else {
540
            None
190✔
541
        }
542
    }
378✔
543

544
    /// Parses a boundary. For start and end, there are two syntaxes: `^`, `$`
545
    /// (new) and `<%`, `%>` (deprecated). Word boundaries are `%`.
546
    ///
547
    /// The deprecated syntax issues a warning.
548
    ///
549
    /// This function does _not_ parse negated negated word boundaries (`!%`),
550
    /// since negation is handled elsewhere. It also does _not_ parse the
551
    /// `Start` and `End` global variables.
552
    fn parse_boundary(&mut self) -> Option<Rule<'i>> {
795✔
553
        let span = self.span();
795✔
554
        let kind = if self.consume(Token::Caret) {
795✔
555
            BoundaryKind::Start
11✔
556
        } else if self.consume(Token::Dollar) {
807✔
557
            BoundaryKind::End
8✔
558
        } else if self.consume(Token::BWord) {
776✔
559
            BoundaryKind::Word
23✔
560
        } else {
561
            return None;
753✔
562
        };
563
        Some(Rule::Boundary(Boundary::new(kind, span)))
42✔
564
    }
795✔
565

566
    /// Parses a reference. Supported syntaxes are `::name`, `::3`, `::+3` and
567
    /// `::-3`.
568
    fn parse_reference(&mut self) -> PResult<Option<Rule<'i>>> {
753✔
569
        if self.consume(Token::DoubleColon) {
777✔
570
            let start_span = self.last_span();
24✔
571

572
            let target = if self.consume(Token::Plus) {
25✔
573
                let num = self.expect_number::<i32>()?;
1✔
574
                ReferenceTarget::Relative(num)
1✔
575
            } else if self.consume(Token::Dash) {
25✔
576
                let num = self.expect_number::<i32>()?;
2✔
577
                // negating from positive to negative can't overflow, luckily
578
                ReferenceTarget::Relative(-num)
2✔
579
            } else if let Some(num) = self.consume_number(MAX_REPETITION)? {
28✔
580
                ReferenceTarget::Number(num)
14✔
581
            } else {
582
                // TODO: Better diagnostic for `::let`
583
                let name = self
7✔
584
                    .expect_as(Token::Identifier)
7✔
585
                    .map_err(|p| PEK::Expected("number or group name").at(p.span))?;
×
586
                ReferenceTarget::Named(name)
7✔
587
            };
588

589
            let span = start_span.join(self.last_span());
24✔
590
            Ok(Some(Rule::Reference(Reference::new(target, span))))
24✔
591
        } else {
592
            Ok(None)
729✔
593
        }
594
    }
753✔
595

596
    fn parse_range(&mut self) -> PResult<Option<Rule<'i>>> {
693✔
597
        if self.consume_reserved("range") {
713✔
598
            let span_start = self.last_span();
23✔
599

600
            let first = self.expect_as(Token::String)?;
23✔
601
            let span_1 = self.last_span();
22✔
602
            self.expect(Token::Dash)?;
715✔
603
            let second = self.expect_as(Token::String)?;
22✔
604
            let span_2 = self.last_span();
22✔
605

606
            let radix = if self.consume_reserved("base") {
24✔
607
                let n = self.expect_number()?;
3✔
608
                let span = self.last_span();
2✔
609
                if n > 36 {
2✔
610
                    return Err(PEK::Number(NumberError::TooLarge).at(span));
×
611
                } else if n < 2 {
2✔
612
                    return Err(PEK::Number(NumberError::TooSmall).at(span));
×
613
                }
614
                n
2✔
615
            } else {
616
                10u8
19✔
617
            };
618

619
            let span = span_start.join(self.last_span());
21✔
620

621
            let start = helper::parse_number(helper::strip_first_last(first), radix)
42✔
622
                .map_err(|k| PEK::from(k).at(span_1))?;
21✔
623
            let end = helper::parse_number(helper::strip_first_last(second), radix)
42✔
624
                .map_err(|k| PEK::from(k).at(span_2))?;
21✔
625

626
            if start.len() > end.len() || (start.len() == end.len() && start > end) {
21✔
627
                return Err(PEK::RangeIsNotIncreasing.at(span_1.join(span_2)));
1✔
628
            }
629

630
            Ok(Some(Rule::Range(Range::new(start, end, radix, span))))
20✔
631
        } else {
1✔
632
            Ok(None)
670✔
633
        }
634
    }
693✔
635

636
    /// Parses an unescaped regex expression (`regex "[test]"`)
637
    fn parse_regex(&mut self) -> PResult<Option<Rule<'i>>> {
670✔
638
        if self.consume_reserved("regex") {
680✔
639
            let span_start = self.last_span();
10✔
640
            let lit = self.expect_as(Token::String)?;
10✔
641
            let span_end = self.last_span();
10✔
642

643
            let content = helper::parse_quoted_text(lit).map_err(|k| k.at(span_end))?;
10✔
644

645
            let span = span_start.join(span_end);
10✔
646
            Ok(Some(Rule::Regex(Regex::new(content, span))))
10✔
647
        } else {
648
            Ok(None)
660✔
649
        }
650
    }
670✔
651

652
    /// Parses a variable (usage site).
653
    fn parse_variable(&mut self) -> PResult<Option<Rule<'i>>> {
660✔
654
        if let Some(ident) = self.consume_as(Token::Identifier) {
836✔
655
            let span1 = self.last_span();
199✔
656
            let rule = Rule::Variable(Variable::new(ident, span1));
199✔
657
            if let Some((Token::Equals, span2)) = self.peek_pair() {
199✔
658
                return Err(PEK::MissingLetKeyword.at(span1.join(span2)));
1✔
659
            }
660
            Ok(Some(rule))
198✔
661
        } else {
1✔
662
            Ok(None)
461✔
663
        }
664
    }
660✔
665

666
    /// Parses the dot
667
    fn parse_dot(&mut self) -> Option<Rule<'i>> {
461✔
668
        if self.consume(Token::Dot) {
461✔
669
            Some(Rule::Dot)
26✔
670
        } else {
671
            None
435✔
672
        }
673
    }
461✔
674
}
675

676
#[derive(Clone, Copy)]
×
677
enum StringOrChar<'i> {
678
    String(&'i str),
679
    Char(char),
680
}
681

682
impl StringOrChar<'_> {
683
    fn to_char(self) -> Result<char, PEK> {
13✔
684
        Err(PEK::CharString(match self {
17✔
685
            StringOrChar::Char(c) => return Ok(c),
4✔
686
            StringOrChar::String(s) => {
9✔
687
                let s = helper::parse_quoted_text(s)?;
9✔
688
                let mut iter = s.chars();
9✔
689
                match iter.next() {
9✔
690
                    Some(c) if iter.next().is_none() => return Ok(c),
9✔
691
                    Some(_) => CharStringError::TooManyCodePoints,
4✔
692
                    _ => CharStringError::Empty,
×
693
                }
694
            }
9✔
695
        }))
696
    }
13✔
697
}
698

699
trait TryOptionExt<T> {
700
    fn try_or_else<E>(self, f: impl FnMut() -> Result<Option<T>, E>) -> Result<Option<T>, E>;
701
}
702

703
impl<T> TryOptionExt<T> for Option<T> {
704
    #[inline(always)]
705
    fn try_or_else<E>(self, mut f: impl FnMut() -> Result<Option<T>, E>) -> Result<Option<T>, E> {
11,374✔
706
        match self {
11,374✔
707
            Some(val) => Ok(Some(val)),
3,415✔
708
            None => f(),
7,959✔
709
        }
710
    }
11,374✔
711
}
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

© 2025 Coveralls, Inc