1use std::collections::HashMap;
2
3use crate::{
4 compile::{CompileResult, CompileState},
5 error::{CompileError, CompileErrorKind, Feature, ParseError, ParseErrorKind},
6 features::RulexFeatures,
7 options::{CompileOptions, ParseOptions, RegexFlavor},
8 regex::Regex,
9 rule::Rule,
10 span::Span,
11};
12
13#[derive(Clone)]
14pub(crate) struct Lookaround<'i> {
15 kind: LookaroundKind,
16 rule: Rule<'i>,
17 pub(crate) span: Span,
18}
19
20#[cfg(feature = "dbg")]
21impl core::fmt::Debug for Lookaround<'_> {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.write_str("Lookaround ")?;
24 f.write_str(match self.kind {
25 LookaroundKind::Ahead => ">> ",
26 LookaroundKind::Behind => "<< ",
27 LookaroundKind::AheadNegative => "!>> ",
28 LookaroundKind::BehindNegative => "!<< ",
29 })?;
30 self.rule.fmt(f)
31 }
32}
33
34#[derive(Clone, Copy, PartialEq, Eq)]
35#[cfg_attr(feature = "dbg", derive(Debug))]
36pub(crate) enum LookaroundKind {
37 Ahead,
38 Behind,
39 AheadNegative,
40 BehindNegative,
41}
42
43impl<'i> Lookaround<'i> {
44 pub(crate) fn get_capturing_groups(
45 &self,
46 count: &mut u32,
47 map: &'i mut HashMap<String, u32>,
48 within_variable: bool,
49 ) -> Result<(), CompileError> {
50 self.rule.get_capturing_groups(count, map, within_variable)
51 }
52
53 pub(crate) fn new(rule: Rule<'i>, kind: LookaroundKind, span: Span) -> Self {
54 Lookaround { rule, kind, span }
55 }
56
57 pub(crate) fn negate(&mut self) -> Result<(), ParseErrorKind> {
58 match self.kind {
59 LookaroundKind::AheadNegative | LookaroundKind::BehindNegative => {
60 Err(ParseErrorKind::UnallowedDoubleNot)
61 }
62 LookaroundKind::Ahead => {
63 self.kind = LookaroundKind::AheadNegative;
64 Ok(())
65 }
66 LookaroundKind::Behind => {
67 self.kind = LookaroundKind::BehindNegative;
68 Ok(())
69 }
70 }
71 }
72
73 pub(crate) fn compile<'c>(
74 &'c self,
75 options: CompileOptions,
76 state: &mut CompileState<'c, 'i>,
77 ) -> CompileResult<'i> {
78 if options.flavor == RegexFlavor::Rust {
79 return Err(
80 CompileErrorKind::Unsupported(Feature::Lookaround, options.flavor).at(self.span)
81 );
82 }
83
84 Ok(Regex::Lookaround(Box::new(RegexLookaround {
85 content: self.rule.comp(options, state)?,
86 kind: self.kind,
87 })))
88 }
89
90 pub(crate) fn validate(&self, options: &ParseOptions) -> Result<(), ParseError> {
91 let feature = match self.kind {
92 LookaroundKind::Ahead => RulexFeatures::LOOKAHEAD,
93 LookaroundKind::Behind => RulexFeatures::LOOKBEHIND,
94 LookaroundKind::AheadNegative => RulexFeatures::LOOKAHEAD,
95 LookaroundKind::BehindNegative => RulexFeatures::LOOKBEHIND,
96 };
97 options.allowed_features.require(feature, self.span)
98 }
99}
100
101#[cfg_attr(feature = "dbg", derive(Debug))]
102pub(crate) struct RegexLookaround<'i> {
103 content: Regex<'i>,
104 kind: LookaroundKind,
105}
106
107impl<'i> RegexLookaround<'i> {
108 pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor) {
109 buf.push_str(match self.kind {
110 LookaroundKind::Ahead => "(?=",
111 LookaroundKind::Behind => "(?<=",
112 LookaroundKind::AheadNegative => "(?!",
113 LookaroundKind::BehindNegative => "(?<!",
114 });
115 self.content.codegen(buf, flavor);
116 buf.push(')');
117 }
118}