1use crate::{
2 capturing_groups::CapturingGroupsCollector,
3 compile::{CompileResult, CompileState},
4 diagnose::{CompileErrorKind, Diagnostic},
5 options::CompileOptions,
6 regex::Count,
7 validation::Validator,
8 visitor::RuleVisitor,
9};
10
11pub(crate) mod alternation;
12pub(crate) mod boundary;
13pub(crate) mod char_class;
14pub(crate) mod codepoint;
15pub(crate) mod dot;
16pub(crate) mod grapheme;
17pub(crate) mod group;
18pub(crate) mod intersection;
19pub(crate) mod literal;
20pub(crate) mod lookaround;
21pub(crate) mod range;
22pub(crate) mod recursion;
23pub(crate) mod reference;
24pub(crate) mod regex;
25pub(crate) mod repetition;
26pub(crate) mod rule;
27pub(crate) mod stmt;
28pub(crate) mod var;
29
30use pomsky_syntax::Span;
31use pomsky_syntax::exprs::{test::Test, *};
32
33pub(crate) trait Compile {
34 fn compile<'c>(
35 &'c self,
36 options: CompileOptions,
37 state: &mut CompileState<'c>,
38 ) -> CompileResult;
39}
40
41#[derive(Clone)]
43#[cfg_attr(not(feature = "dbg"), derive(Debug))]
44#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
45pub struct Expr(Rule);
46
47impl Expr {
48 pub fn parse(input: &str) -> (Option<Self>, impl Iterator<Item = Diagnostic> + '_) {
53 let (rule, diagnostics) = pomsky_syntax::parse(input, 256);
54 (rule.map(Expr), diagnostics.into_iter().map(|d| Diagnostic::from_parser(&d, input)))
55 }
56
57 pub fn compile(
59 &self,
60 input: &str,
61 options: CompileOptions,
62 ) -> (Option<String>, Vec<Diagnostic>) {
63 let mut validator = Validator::new(options);
64 if let Err(e) = validator.visit_rule(&self.0) {
65 return (None, vec![e.diagnostic(input)]);
66 }
67
68 let mut capt_groups = CapturingGroupsCollector::new();
69 if let Err(e) = capt_groups.visit_rule(&self.0) {
70 return (None, vec![e.diagnostic(input)]);
71 }
72
73 let no_span = Span::empty();
74
75 let start = Rule::Boundary(Boundary::new(BoundaryKind::Start, true, no_span));
76 let end = Rule::Boundary(Boundary::new(BoundaryKind::End, true, no_span));
77 let grapheme = Rule::Grapheme;
78 let codepoint = Rule::Codepoint;
79
80 let builtins = vec![
81 ("Start", &start),
82 ("End", &end),
83 ("Grapheme", &grapheme),
84 ("G", &grapheme),
85 ("Codepoint", &codepoint),
86 ("C", &codepoint),
87 ];
88
89 let mut state = CompileState::new(capt_groups, builtins);
90 let mut compiled = match self.0.compile(options, &mut state) {
91 Ok(compiled) => compiled,
92 Err(e) => return (None, vec![e.diagnostic(input)]),
93 };
94 if let Some(rec_span) = validator.first_recursion
95 && !compiled.terminates()
96 {
97 let error = CompileErrorKind::InfiniteRecursion.at(rec_span);
98 return (None, vec![error.diagnostic(input)]);
99 }
100 let count = compiled.optimize();
101
102 let mut buf = String::new();
103 if count != Count::Zero {
104 compiled.codegen(&mut buf, options.flavor);
105 }
106 (Some(buf), state.diagnostics)
107 }
108
109 pub fn extract_tests(self) -> Vec<Test> {
111 let mut rule = self.0;
112 let mut tests = Vec::new();
113 while let Rule::StmtExpr(expr) = rule {
114 if let Stmt::Test(test) = expr.stmt {
115 tests.push(test);
116 }
117 rule = expr.rule;
118 }
119 tests
120 }
121
122 pub fn extract_tests_ref(&self) -> Vec<&Test> {
124 let mut rule = &self.0;
125 let mut tests = Vec::new();
126 while let Rule::StmtExpr(expr) = rule {
127 if let Stmt::Test(test) = &expr.stmt {
128 tests.push(test);
129 }
130 rule = &expr.rule;
131 }
132 tests
133 }
134
135 pub fn parse_and_compile(
137 input: &str,
138 options: CompileOptions,
139 ) -> (Option<String>, Vec<Diagnostic>, Vec<Test>) {
140 match Self::parse(input) {
141 (Some(parsed), warnings1) => match parsed.compile(input, options) {
142 (Some(compiled), warnings2) => {
143 let mut diagnostics =
144 Vec::with_capacity(warnings1.size_hint().0 + warnings2.len());
145 diagnostics.extend(warnings1);
146 diagnostics.extend(warnings2);
147 (Some(compiled), diagnostics, parsed.extract_tests())
148 }
149 (None, errors) => {
150 let mut diagnostics =
151 Vec::with_capacity(warnings1.size_hint().0 + errors.len());
152 diagnostics.extend(errors);
153 diagnostics.extend(warnings1);
154 (None, diagnostics, parsed.extract_tests())
155 }
156 },
157 (None, diagnostics) => (None, diagnostics.collect(), vec![]),
158 }
159 }
160}
161
162#[cfg(feature = "dbg")]
163impl core::fmt::Debug for Expr {
164 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165 if f.alternate() {
166 core::fmt::Debug::fmt(&self.0, f)
167 } else {
168 core::fmt::Display::fmt(&self.0, f)
169 }
170 }
171}