pomsky_syntax/exprs/
group.rs

1use crate::Span;
2
3use super::Rule;
4
5/// A group, i.e. sequence of rules. A group is either capturing or
6/// non-capturing.
7///
8/// If it is capturing, it must be wrapped in parentheses, and can have a name.
9/// If it is non-capturing, the parentheses can be omitted in same cases.
10#[derive(Debug, Clone)]
11#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
12pub struct Group {
13    pub parts: Vec<Rule>,
14    pub kind: GroupKind,
15    pub span: Span,
16}
17
18impl Group {
19    pub fn new(parts: Vec<Rule>, kind: GroupKind, span: Span) -> Self {
20        Group { parts, kind, span }
21    }
22
23    #[cfg(feature = "dbg")]
24    pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter, needs_parens: bool) {
25        let use_parens =
26            matches!(self.kind, GroupKind::Capturing(_) | GroupKind::Atomic) || needs_parens;
27
28        match &self.kind {
29            GroupKind::Capturing(capture) => {
30                buf.push(':');
31                if let Some(name) = &capture.name {
32                    buf.push_str(name);
33                }
34            }
35            GroupKind::Atomic => {
36                buf.push_str("atomic ");
37            }
38            GroupKind::Normal | GroupKind::Implicit => {}
39        }
40
41        if self.parts.is_empty() {
42            buf.push_str("()");
43        } else {
44            if self.kind != GroupKind::Implicit {
45                buf.start_indentation("(");
46            }
47
48            let len = self.parts.len();
49            for (i, part) in self.parts.iter().enumerate() {
50                let child_needs_parens = if len == 1 {
51                    if use_parens { false } else { needs_parens }
52                } else {
53                    use Rule::*;
54                    matches!(part, Lookaround(_) | StmtExpr(_) | Alternation(_) | Group(_))
55                };
56                part.pretty_print(buf, child_needs_parens);
57                if i < len - 1 {
58                    buf.write("\n");
59                }
60            }
61
62            if self.kind != GroupKind::Implicit {
63                buf.end_indentation(")");
64            }
65        }
66    }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
71pub enum GroupKind {
72    /// A (possibly named) capturing group e.g. `:foo`
73    Capturing(Capture),
74    /// An atomic group
75    Atomic,
76    /// A normal group with a set of parentheses
77    Normal,
78    /// An implicit group, with no parentheses
79    Implicit,
80}
81
82impl GroupKind {
83    pub fn is_normal(&self) -> bool {
84        matches!(self, GroupKind::Normal)
85    }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct Capture {
90    pub name: Option<String>,
91}
92
93impl Capture {
94    pub fn new(name: Option<&str>) -> Self {
95        Capture { name: name.map(str::to_string) }
96    }
97}
98
99#[cfg(feature = "arbitrary")]
100impl arbitrary::Arbitrary<'_> for Capture {
101    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
102        if u.arbitrary()? {
103            Ok(Capture { name: Some(super::arbitrary::Ident::create(u)?) })
104        } else {
105            Ok(Capture { name: None })
106        }
107    }
108
109    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
110        (1, None)
111    }
112}