Skip to main content

regast_core/
lower.rs

1use regast_syntax::{AstKind, ClassSet, NodeId, NodeMap, Pattern, RepKind};
2
3use crate::{Disambiguation, IrId, IrPool, Prov};
4
5const MAX_REPEAT: u32 = 1_000;
6
7#[derive(Clone, Debug)]
8pub struct Lowering {
9    pub root: IrId,
10    pub origins: NodeMap<Vec<IrId>>,
11}
12
13impl Lowering {
14    #[must_use]
15    pub fn ir_for(&self, node: NodeId) -> Option<IrId> {
16        self.origins.get(node)?.last().copied()
17    }
18}
19
20pub fn lower(pattern: &Pattern, pool: &mut IrPool, disambiguation: Disambiguation) -> Lowering {
21    let mut origins: NodeMap<Vec<IrId>> = NodeMap::new(pattern);
22    for cursor in pattern.root().walk().collect::<Vec<_>>().into_iter().rev() {
23        let node = cursor.id();
24        let prov = Prov::Ast(node);
25        let root = match cursor.kind() {
26            AstKind::Empty => pool.one(),
27            AstKind::Anchor { kind } => pool.anchor(*kind, prov),
28            AstKind::Literal { c, .. } => {
29                let set = pool.class_set(ClassSet::singleton(*c));
30                pool.class(set, prov)
31            }
32            AstKind::Dot => {
33                let set = pool.class_set(ClassSet::singleton('\n').negate());
34                pool.class(set, prov)
35            }
36            AstKind::Class { set, .. } => {
37                let set = pool.class_set(set.clone());
38                pool.class(set, prov)
39            }
40            AstKind::Group { inner, .. } => origins.get(*inner).expect("inner lowered")[0],
41            AstKind::Alt { arms } => fold_alt(
42                &origins,
43                pool,
44                arms,
45                node,
46                disambiguation == Disambiguation::Greedy,
47            ),
48            AstKind::Concat { parts } => fold_seq(&origins, pool, parts, node),
49            AstKind::Repeat {
50                inner,
51                kind,
52                greedy,
53            } => {
54                let inner = origins.get(*inner).expect("inner lowered")[0];
55                let greedy = disambiguation == Disambiguation::Posix || *greedy;
56                lower_repeat(pool, inner, *kind, greedy, node)
57            }
58        };
59        origins.insert(node, vec![root]);
60    }
61    Lowering {
62        root: origins.get(pattern.root_id()).expect("root lowered")[0],
63        origins,
64    }
65}
66
67fn fold_alt(
68    origins: &NodeMap<Vec<IrId>>,
69    pool: &mut IrPool,
70    arms: &[NodeId],
71    node: NodeId,
72    ordered: bool,
73) -> IrId {
74    let mut iter = arms.iter().rev();
75    let mut result = origins
76        .get(*iter.next().expect("at least two arms"))
77        .unwrap()[0];
78    for arm in iter {
79        let left = origins.get(*arm).unwrap()[0];
80        result = pool.alt(left, result, Prov::Ast(node), ordered);
81    }
82    result
83}
84
85fn fold_seq(
86    origins: &NodeMap<Vec<IrId>>,
87    pool: &mut IrPool,
88    parts: &[NodeId],
89    node: NodeId,
90) -> IrId {
91    let mut iter = parts.iter().rev();
92    let mut result = origins
93        .get(*iter.next().expect("at least two parts"))
94        .unwrap()[0];
95    for part in iter {
96        let left = origins.get(*part).unwrap()[0];
97        result = pool.seq(left, result, Prov::Ast(node));
98    }
99    result
100}
101
102fn lower_repeat(pool: &mut IrPool, inner: IrId, kind: RepKind, greedy: bool, node: NodeId) -> IrId {
103    let prov = Prov::Ast(node);
104    match kind {
105        RepKind::ZeroOrOne => optional(pool, inner, greedy, node),
106        RepKind::ZeroOrMore => pool.star(inner, prov, greedy),
107        RepKind::OneOrMore => {
108            let star = pool.star(inner, Prov::Synth(node), greedy);
109            pool.seq(inner, star, prov)
110        }
111        RepKind::Range { min, max } => {
112            assert!(min <= MAX_REPEAT, "repeat lower bound exceeds limit");
113            assert!(
114                max.is_none_or(|max| max <= MAX_REPEAT),
115                "repeat upper bound exceeds limit"
116            );
117            lower_range(pool, inner, min, max, greedy, node)
118        }
119    }
120}
121
122fn lower_range(
123    pool: &mut IrPool,
124    inner: IrId,
125    min: u32,
126    max: Option<u32>,
127    greedy: bool,
128    node: NodeId,
129) -> IrId {
130    let (mut suffix, has_suffix) = match max {
131        None => (pool.star(inner, Prov::Synth(node), greedy), true),
132        Some(max) => {
133            let mut optional_suffix = pool.one();
134            for _ in 0..max - min {
135                let seq = if optional_suffix == pool.one() {
136                    inner
137                } else {
138                    pool.seq(inner, optional_suffix, Prov::Synth(node))
139                };
140                optional_suffix = optional(pool, seq, greedy, node);
141            }
142            (optional_suffix, max > min)
143        }
144    };
145    if min == 0 {
146        return suffix;
147    }
148    if !has_suffix {
149        suffix = inner;
150        for _ in 1..min {
151            suffix = pool.seq(inner, suffix, Prov::Synth(node));
152        }
153        return suffix;
154    }
155    for _ in 0..min {
156        suffix = pool.seq(inner, suffix, Prov::Synth(node));
157    }
158    suffix
159}
160
161fn optional(pool: &mut IrPool, inner: IrId, greedy: bool, node: NodeId) -> IrId {
162    if greedy {
163        pool.alt(inner, pool.one(), Prov::Ast(node), false)
164    } else {
165        pool.alt(pool.one(), inner, Prov::Ast(node), true)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use regast_syntax::Pattern;
172
173    use super::*;
174
175    #[test]
176    fn lowers_every_repeat_form() {
177        for pattern in ["a?", "a*", "a+", "a{2}", "a{1,3}", "a{2,}", "a??"] {
178            let pattern = Pattern::parse(pattern).unwrap();
179            let mut pool = IrPool::new();
180            let lowered = lower(&pattern, &mut pool, Disambiguation::Posix);
181            assert!(lowered.root.index() < u32::try_from(pool.len()).unwrap());
182        }
183    }
184}