cfg_sequence/
lib.rs

1//! Sequences are similar to regex repetitions with numbering.
2
3pub mod builder;
4pub mod destination;
5pub mod rewrite;
6
7use std::ops::{Bound, RangeBounds};
8
9use cfg_grammar::HistoryId;
10use cfg_symbol::Symbol;
11
12use self::Separator::*;
13
14/// Sequence rule representation.
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16pub struct Sequence {
17    /// The rule's left-hand side.
18    pub lhs: Symbol,
19    /// The rule's right-hand side.
20    pub rhs: Symbol,
21    /// The minimum number of repetitions.
22    pub start: u32,
23    /// Either the inclusive maximum number of repetitions, or `None` if the number of repetitions
24    /// is unlimited.
25    pub end: Option<u32>,
26    /// The way elements are separated in a sequence, or `Null`.
27    pub separator: Separator,
28    /// The history carried with the sequence rule.
29    pub history_id: Option<HistoryId>,
30}
31
32/// The separator symbol and mode of separation in a sequence, or `Null` for no separation.
33#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
34pub enum Separator {
35    /// Separation with the trailing separator included. In other words, all elements are followed
36    /// by the separator.
37    Trailing(Symbol),
38    /// The separator occurs between elements.
39    Proper(Symbol),
40    /// The union of `Trailing` and `Proper`. In other words, the trailing separator may or may not
41    /// be present.
42    Liberal(Symbol),
43    /// No separation.
44    Null,
45}
46
47impl Sequence {
48    /// Assigns the inclusive range of the number of repetitions.
49    pub fn inclusive(mut self, start: u32, end: Option<u32>) -> Self {
50        self.start = start;
51        self.end = end;
52        self
53    }
54
55    /// Assigns the separator symbol and mode of separation.
56    pub fn separator(mut self, sep: Separator) -> Self {
57        self.separator = sep;
58        self
59    }
60
61    /// Adds a range to the sequence.
62    pub fn range(self, range: impl RangeBounds<u32>) -> Self {
63        let to_option = |bound: Bound<u32>, diff| match bound {
64            Bound::Included(included) => Some(included),
65            Bound::Excluded(excluded) => Some((excluded as i64 + diff) as u32),
66            Bound::Unbounded => None,
67        };
68        self.inclusive(
69            to_option(range.start_bound().cloned(), 1).unwrap_or(0),
70            to_option(range.end_bound().cloned(), -1),
71        )
72    }
73}
74
75impl Separator {
76    /// Returns the kind of separation for a prefix sequence.
77    pub fn prefix_separator(self) -> Self {
78        match self {
79            Proper(sep) | Liberal(sep) => Trailing(sep),
80            other => other,
81        }
82    }
83}
84
85impl Into<Option<Symbol>> for Separator {
86    fn into(self) -> Option<Symbol> {
87        match self {
88            Trailing(sep) => Some(sep),
89            _ => None,
90        }
91    }
92}