iregex_syntax/
display.rs

1use core::fmt;
2use iregex::automata::AnyRange;
3use std::fmt::Write;
4
5use crate::{Ast, Atom, Charset, Disjunction, Repeat, Sequence};
6
7impl fmt::Display for Ast {
8	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9		if self.start_anchor {
10			f.write_char('^')?;
11		}
12
13		self.disjunction.fmt(f)?;
14
15		if self.end_anchor {
16			f.write_char('$')
17		} else {
18			Ok(())
19		}
20	}
21}
22
23impl fmt::Display for Disjunction {
24	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25		for (i, sequence) in self.iter().enumerate() {
26			if i > 0 {
27				f.write_char('|')?;
28			}
29
30			sequence.fmt(f)?;
31		}
32
33		Ok(())
34	}
35}
36
37impl fmt::Display for Sequence {
38	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39		for atom in self {
40			atom.fmt(f)?;
41		}
42
43		Ok(())
44	}
45}
46
47impl fmt::Display for Atom {
48	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49		match self {
50			Self::Any => f.write_char('.'),
51			Self::Char(c) => fmt_char(*c, f),
52			Self::Set(charset) => charset.fmt(f),
53			Self::Repeat(atom, repeat) => {
54				atom.fmt(f)?;
55				repeat.fmt(f)
56			}
57			Self::Group(g) => {
58				f.write_char('(')?;
59				g.fmt(f)?;
60				f.write_char(')')
61			}
62		}
63	}
64}
65
66impl fmt::Display for Charset {
67	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68		if self.negative {
69			f.write_char('^')?;
70		}
71
72		for &range in &self.set {
73			fmt_range(range, f)?
74		}
75
76		Ok(())
77	}
78}
79
80impl fmt::Display for Repeat {
81	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82		if self.min == 0 && self.max == Some(1) {
83			f.write_char('?')
84		} else if self.min == 0 && self.max.is_none() {
85			f.write_char('*')
86		} else if self.min == 1 && self.max.is_some() {
87			f.write_char('+')
88		} else {
89			match self.max {
90				Some(max) => {
91					if self.min == max {
92						write!(f, "{{{}}}", self.min)
93					} else {
94						write!(f, "{{{},{}}}", self.min, max)
95					}
96				}
97				None => write!(f, "{{{},}}", self.min),
98			}
99		}
100	}
101}
102
103pub fn fmt_range(range: AnyRange<char>, f: &mut fmt::Formatter) -> fmt::Result {
104	if range.len() == 1 {
105		fmt_char(range.first().unwrap(), f)
106	} else {
107		let a = range.first().unwrap();
108		let b = range.last().unwrap();
109
110		fmt_char(a, f)?;
111		if a as u32 + 1 < b as u32 {
112			write!(f, "-")?;
113		}
114		fmt_char(b, f)
115	}
116}
117
118pub fn fmt_char(c: char, f: &mut fmt::Formatter) -> fmt::Result {
119	match c {
120		'(' => write!(f, "\\("),
121		')' => write!(f, "\\)"),
122		'[' => write!(f, "\\["),
123		']' => write!(f, "\\]"),
124		'{' => write!(f, "\\{{"),
125		'}' => write!(f, "\\}}"),
126		'?' => write!(f, "\\?"),
127		'*' => write!(f, "\\*"),
128		'+' => write!(f, "\\+"),
129		'-' => write!(f, "\\-"),
130		'^' => write!(f, "\\^"),
131		'|' => write!(f, "\\|"),
132		'\\' => write!(f, "\\\\"),
133		'\0' => write!(f, "\\0"),
134		'\x07' => write!(f, "\\a"),
135		'\x08' => write!(f, "\\b"),
136		'\t' => write!(f, "\\t"),
137		'\n' => write!(f, "\\n"),
138		'\x0b' => write!(f, "\\v"),
139		'\x0c' => write!(f, "\\f"),
140		'\r' => write!(f, "\\r"),
141		'\x1b' => write!(f, "\\e"),
142		_ => fmt::Display::fmt(&c, f),
143	}
144}
145
146// #[cfg(test)]
147// mod tests {
148// 	// Each pair is of the form `(regexp, formatted)`.
149// 	// We check that the regexp is correctly parsed by formatting it and
150// 	// checking that it matches the expected `formatted` string.
151// 	const TESTS: &[(&str, &str)] = &[
152// 		("a*", "a*"),
153// 		("a\\*", "a\\*"),
154// 		("[cab]", "[a-c]"),
155// 		("[^cab]", "[^a-c]"),
156// 		("(abc)|de", "abc|de"),
157// 		("(a|b)?", "(a|b)?"),
158// 		("[A-Za-z0-89]", "[0-9A-Za-z]"),
159// 		("[a|b]", "[ab\\|]"),
160// 	];
161
162// 	#[test]
163// 	fn test() {
164// 		for &(regexp, formatted) in TESTS {
165// 			assert_eq!(
166// 				super::Ast::parse(regexp.chars()).unwrap().to_string(),
167// 				*formatted
168// 			)
169// 		}
170// 	}
171// }