-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
473 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
use std::fmt; | ||
|
||
use crate::options::RegexFlavor; | ||
|
||
use super::{literal, Regex, RegexProperty, RegexShorthand, UnicodeSet}; | ||
|
||
#[cfg_attr(feature = "dbg", derive(Debug))] | ||
#[derive(Default)] | ||
pub(crate) struct RegexCompoundCharSet { | ||
pub(crate) negative: bool, | ||
pub(crate) intersections: Vec<RegexCharSet>, | ||
} | ||
|
||
impl RegexCompoundCharSet { | ||
pub(crate) fn new(set: RegexCharSet) -> Self { | ||
RegexCompoundCharSet { negative: false, intersections: vec![set] } | ||
} | ||
|
||
pub(crate) fn negate(mut self) -> RegexCompoundCharSet { | ||
self.negative = !self.negative; | ||
self | ||
} | ||
|
||
pub(crate) fn add(mut self, other: RegexCharSet) -> Regex { | ||
if other.negative && self.intersections.iter().all(|i| i.negative) { | ||
let mut intersections = self.intersections.into_iter(); | ||
let mut char_set = intersections.next().expect("Intersection is empty"); | ||
for next_set in intersections { | ||
char_set.set.extend(next_set.set); | ||
} | ||
char_set.set.extend(other.set); | ||
if self.negative { | ||
char_set = char_set.negate(); | ||
} | ||
Regex::CharSet(char_set) | ||
} else { | ||
self.intersections.push(other); | ||
Regex::CompoundCharSet(self) | ||
} | ||
} | ||
|
||
pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor) { | ||
if self.negative { | ||
buf.push_str("[^"); | ||
} else { | ||
buf.push('['); | ||
} | ||
|
||
let mut is_first = true; | ||
for intersection in &self.intersections { | ||
if !is_first { | ||
buf.push_str("&&"); | ||
} | ||
intersection.codegen(buf, flavor, true); | ||
is_first = false; | ||
} | ||
|
||
buf.push(']'); | ||
} | ||
} | ||
|
||
#[cfg_attr(feature = "dbg", derive(Debug))] | ||
#[derive(Default)] | ||
pub(crate) struct RegexCharSet { | ||
pub(crate) negative: bool, | ||
pub(crate) set: UnicodeSet, | ||
} | ||
|
||
impl RegexCharSet { | ||
pub(crate) fn new(items: UnicodeSet) -> Self { | ||
Self { negative: false, set: items } | ||
} | ||
|
||
pub(crate) fn negate(mut self) -> Self { | ||
self.negative = !self.negative; | ||
self | ||
} | ||
|
||
pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor, inside_compound: bool) { | ||
if self.set.len() == 1 { | ||
if let Some(range) = self.set.ranges().next() { | ||
let (first, last) = range.as_chars(); | ||
if first == last && !self.negative { | ||
return literal::codegen_char_esc(first, buf, flavor); | ||
} | ||
} else if let Some(prop) = self.set.props().next() { | ||
match prop { | ||
RegexCharSetItem::Shorthand(s) => { | ||
let shorthand = if self.negative { s.negate() } else { Some(s) }; | ||
if let Some(shorthand) = shorthand { | ||
return shorthand.codegen(buf); | ||
} | ||
} | ||
RegexCharSetItem::Property { negative, value } => { | ||
return value.codegen(buf, negative ^ self.negative, flavor); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if self.negative { | ||
buf.push_str("[^"); | ||
} else if !inside_compound { | ||
buf.push('['); | ||
} | ||
|
||
let mut is_first = true; | ||
for prop in self.set.props() { | ||
match prop { | ||
RegexCharSetItem::Shorthand(s) => s.codegen(buf), | ||
RegexCharSetItem::Property { negative, value } => { | ||
value.codegen(buf, negative, flavor); | ||
} | ||
} | ||
is_first = false; | ||
} | ||
for range in self.set.ranges() { | ||
let (first, last) = range.as_chars(); | ||
if first == last { | ||
literal::compile_char_esc_in_class(first, buf, is_first, flavor); | ||
} else { | ||
literal::compile_char_esc_in_class(first, buf, is_first, flavor); | ||
if range.first + 1 < range.last { | ||
buf.push('-'); | ||
} | ||
literal::compile_char_esc_in_class(last, buf, false, flavor); | ||
} | ||
is_first = false; | ||
} | ||
|
||
if self.negative || !inside_compound { | ||
buf.push(']'); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, PartialEq, Eq)] | ||
pub(crate) enum RegexCharSetItem { | ||
Shorthand(RegexShorthand), | ||
Property { negative: bool, value: RegexProperty }, | ||
} | ||
|
||
impl RegexCharSetItem { | ||
pub(crate) fn negate(self) -> Option<Self> { | ||
match self { | ||
RegexCharSetItem::Shorthand(s) => s.negate().map(RegexCharSetItem::Shorthand), | ||
RegexCharSetItem::Property { negative, value } => { | ||
Some(RegexCharSetItem::Property { negative: !negative, value }) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Debug for RegexCharSetItem { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::Shorthand(s) => f.write_str(s.as_str()), | ||
&Self::Property { value, negative } => { | ||
if negative { | ||
f.write_str("!")?; | ||
} | ||
f.write_str(value.prefix_as_str())?; | ||
f.write_str(value.as_str()) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.