build(rust): vendor googletest crate
This commit was merged in pull request #76.
This commit is contained in:
1807
third_party/crates/regex-syntax-0.8.11/src/ast/mod.rs
vendored
Normal file
1807
third_party/crates/regex-syntax-0.8.11/src/ast/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6377
third_party/crates/regex-syntax-0.8.11/src/ast/parse.rs
vendored
Normal file
6377
third_party/crates/regex-syntax-0.8.11/src/ast/parse.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
577
third_party/crates/regex-syntax-0.8.11/src/ast/print.rs
vendored
Normal file
577
third_party/crates/regex-syntax-0.8.11/src/ast/print.rs
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
/*!
|
||||
This module provides a regular expression printer for `Ast`.
|
||||
*/
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::ast::{
|
||||
self,
|
||||
visitor::{self, Visitor},
|
||||
Ast,
|
||||
};
|
||||
|
||||
/// A builder for constructing a printer.
|
||||
///
|
||||
/// Note that since a printer doesn't have any configuration knobs, this type
|
||||
/// remains unexported.
|
||||
#[derive(Clone, Debug)]
|
||||
struct PrinterBuilder {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl Default for PrinterBuilder {
|
||||
fn default() -> PrinterBuilder {
|
||||
PrinterBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrinterBuilder {
|
||||
fn new() -> PrinterBuilder {
|
||||
PrinterBuilder { _priv: () }
|
||||
}
|
||||
|
||||
fn build(&self) -> Printer {
|
||||
Printer { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
/// A printer for a regular expression abstract syntax tree.
|
||||
///
|
||||
/// A printer converts an abstract syntax tree (AST) to a regular expression
|
||||
/// pattern string. This particular printer uses constant stack space and heap
|
||||
/// space proportional to the size of the AST.
|
||||
///
|
||||
/// This printer will not necessarily preserve the original formatting of the
|
||||
/// regular expression pattern string. For example, all whitespace and comments
|
||||
/// are ignored.
|
||||
#[derive(Debug)]
|
||||
pub struct Printer {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
/// Create a new printer.
|
||||
pub fn new() -> Printer {
|
||||
PrinterBuilder::new().build()
|
||||
}
|
||||
|
||||
/// Print the given `Ast` to the given writer. The writer must implement
|
||||
/// `fmt::Write`. Typical implementations of `fmt::Write` that can be used
|
||||
/// here are a `fmt::Formatter` (which is available in `fmt::Display`
|
||||
/// implementations) or a `&mut String`.
|
||||
pub fn print<W: fmt::Write>(&mut self, ast: &Ast, wtr: W) -> fmt::Result {
|
||||
visitor::visit(ast, Writer { wtr })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Writer<W> {
|
||||
wtr: W,
|
||||
}
|
||||
|
||||
impl<W: fmt::Write> Visitor for Writer<W> {
|
||||
type Output = ();
|
||||
type Err = fmt::Error;
|
||||
|
||||
fn finish(self) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_pre(&mut self, ast: &Ast) -> fmt::Result {
|
||||
match *ast {
|
||||
Ast::Group(ref x) => self.fmt_group_pre(x),
|
||||
Ast::ClassBracketed(ref x) => self.fmt_class_bracketed_pre(x),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_post(&mut self, ast: &Ast) -> fmt::Result {
|
||||
match *ast {
|
||||
Ast::Empty(_) => Ok(()),
|
||||
Ast::Flags(ref x) => self.fmt_set_flags(x),
|
||||
Ast::Literal(ref x) => self.fmt_literal(x),
|
||||
Ast::Dot(_) => self.wtr.write_str("."),
|
||||
Ast::Assertion(ref x) => self.fmt_assertion(x),
|
||||
Ast::ClassPerl(ref x) => self.fmt_class_perl(x),
|
||||
Ast::ClassUnicode(ref x) => self.fmt_class_unicode(x),
|
||||
Ast::ClassBracketed(ref x) => self.fmt_class_bracketed_post(x),
|
||||
Ast::Repetition(ref x) => self.fmt_repetition(x),
|
||||
Ast::Group(ref x) => self.fmt_group_post(x),
|
||||
Ast::Alternation(_) => Ok(()),
|
||||
Ast::Concat(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_alternation_in(&mut self) -> fmt::Result {
|
||||
self.wtr.write_str("|")
|
||||
}
|
||||
|
||||
fn visit_class_set_item_pre(
|
||||
&mut self,
|
||||
ast: &ast::ClassSetItem,
|
||||
) -> Result<(), Self::Err> {
|
||||
match *ast {
|
||||
ast::ClassSetItem::Bracketed(ref x) => {
|
||||
self.fmt_class_bracketed_pre(x)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_class_set_item_post(
|
||||
&mut self,
|
||||
ast: &ast::ClassSetItem,
|
||||
) -> Result<(), Self::Err> {
|
||||
use crate::ast::ClassSetItem::*;
|
||||
|
||||
match *ast {
|
||||
Empty(_) => Ok(()),
|
||||
Literal(ref x) => self.fmt_literal(x),
|
||||
Range(ref x) => {
|
||||
self.fmt_literal(&x.start)?;
|
||||
self.wtr.write_str("-")?;
|
||||
self.fmt_literal(&x.end)?;
|
||||
Ok(())
|
||||
}
|
||||
Ascii(ref x) => self.fmt_class_ascii(x),
|
||||
Unicode(ref x) => self.fmt_class_unicode(x),
|
||||
Perl(ref x) => self.fmt_class_perl(x),
|
||||
Bracketed(ref x) => self.fmt_class_bracketed_post(x),
|
||||
Union(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_class_set_binary_op_in(
|
||||
&mut self,
|
||||
ast: &ast::ClassSetBinaryOp,
|
||||
) -> Result<(), Self::Err> {
|
||||
self.fmt_class_set_binary_op_kind(&ast.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: fmt::Write> Writer<W> {
|
||||
fn fmt_group_pre(&mut self, ast: &ast::Group) -> fmt::Result {
|
||||
use crate::ast::GroupKind::*;
|
||||
match ast.kind {
|
||||
CaptureIndex(_) => self.wtr.write_str("("),
|
||||
CaptureName { ref name, starts_with_p } => {
|
||||
let start = if starts_with_p { "(?P<" } else { "(?<" };
|
||||
self.wtr.write_str(start)?;
|
||||
self.wtr.write_str(&name.name)?;
|
||||
self.wtr.write_str(">")?;
|
||||
Ok(())
|
||||
}
|
||||
NonCapturing(ref flags) => {
|
||||
self.wtr.write_str("(?")?;
|
||||
self.fmt_flags(flags)?;
|
||||
self.wtr.write_str(":")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_group_post(&mut self, _ast: &ast::Group) -> fmt::Result {
|
||||
self.wtr.write_str(")")
|
||||
}
|
||||
|
||||
fn fmt_repetition(&mut self, ast: &ast::Repetition) -> fmt::Result {
|
||||
use crate::ast::RepetitionKind::*;
|
||||
match ast.op.kind {
|
||||
ZeroOrOne if ast.greedy => self.wtr.write_str("?"),
|
||||
ZeroOrOne => self.wtr.write_str("??"),
|
||||
ZeroOrMore if ast.greedy => self.wtr.write_str("*"),
|
||||
ZeroOrMore => self.wtr.write_str("*?"),
|
||||
OneOrMore if ast.greedy => self.wtr.write_str("+"),
|
||||
OneOrMore => self.wtr.write_str("+?"),
|
||||
Range(ref x) => {
|
||||
self.fmt_repetition_range(x)?;
|
||||
if !ast.greedy {
|
||||
self.wtr.write_str("?")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_repetition_range(
|
||||
&mut self,
|
||||
ast: &ast::RepetitionRange,
|
||||
) -> fmt::Result {
|
||||
use crate::ast::RepetitionRange::*;
|
||||
match *ast {
|
||||
Exactly(x) => write!(self.wtr, "{{{x}}}"),
|
||||
AtLeast(x) => write!(self.wtr, "{{{x},}}"),
|
||||
Bounded(x, y) => write!(self.wtr, "{{{x},{y}}}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_literal(&mut self, ast: &ast::Literal) -> fmt::Result {
|
||||
use crate::ast::LiteralKind::*;
|
||||
|
||||
match ast.kind {
|
||||
Verbatim => self.wtr.write_char(ast.c),
|
||||
Meta | Superfluous => write!(self.wtr, r"\{}", ast.c),
|
||||
Octal => write!(self.wtr, r"\{:o}", u32::from(ast.c)),
|
||||
HexFixed(ast::HexLiteralKind::X) => {
|
||||
write!(self.wtr, r"\x{:02X}", u32::from(ast.c))
|
||||
}
|
||||
HexFixed(ast::HexLiteralKind::UnicodeShort) => {
|
||||
write!(self.wtr, r"\u{:04X}", u32::from(ast.c))
|
||||
}
|
||||
HexFixed(ast::HexLiteralKind::UnicodeLong) => {
|
||||
write!(self.wtr, r"\U{:08X}", u32::from(ast.c))
|
||||
}
|
||||
HexBrace(ast::HexLiteralKind::X) => {
|
||||
write!(self.wtr, r"\x{{{:X}}}", u32::from(ast.c))
|
||||
}
|
||||
HexBrace(ast::HexLiteralKind::UnicodeShort) => {
|
||||
write!(self.wtr, r"\u{{{:X}}}", u32::from(ast.c))
|
||||
}
|
||||
HexBrace(ast::HexLiteralKind::UnicodeLong) => {
|
||||
write!(self.wtr, r"\U{{{:X}}}", u32::from(ast.c))
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::Bell) => {
|
||||
self.wtr.write_str(r"\a")
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::FormFeed) => {
|
||||
self.wtr.write_str(r"\f")
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::Tab) => self.wtr.write_str(r"\t"),
|
||||
Special(ast::SpecialLiteralKind::LineFeed) => {
|
||||
self.wtr.write_str(r"\n")
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::CarriageReturn) => {
|
||||
self.wtr.write_str(r"\r")
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::VerticalTab) => {
|
||||
self.wtr.write_str(r"\v")
|
||||
}
|
||||
Special(ast::SpecialLiteralKind::Space) => {
|
||||
self.wtr.write_str(r"\ ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_assertion(&mut self, ast: &ast::Assertion) -> fmt::Result {
|
||||
use crate::ast::AssertionKind::*;
|
||||
match ast.kind {
|
||||
StartLine => self.wtr.write_str("^"),
|
||||
EndLine => self.wtr.write_str("$"),
|
||||
StartText => self.wtr.write_str(r"\A"),
|
||||
EndText => self.wtr.write_str(r"\z"),
|
||||
WordBoundary => self.wtr.write_str(r"\b"),
|
||||
NotWordBoundary => self.wtr.write_str(r"\B"),
|
||||
WordBoundaryStart => self.wtr.write_str(r"\b{start}"),
|
||||
WordBoundaryEnd => self.wtr.write_str(r"\b{end}"),
|
||||
WordBoundaryStartAngle => self.wtr.write_str(r"\<"),
|
||||
WordBoundaryEndAngle => self.wtr.write_str(r"\>"),
|
||||
WordBoundaryStartHalf => self.wtr.write_str(r"\b{start-half}"),
|
||||
WordBoundaryEndHalf => self.wtr.write_str(r"\b{end-half}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_set_flags(&mut self, ast: &ast::SetFlags) -> fmt::Result {
|
||||
self.wtr.write_str("(?")?;
|
||||
self.fmt_flags(&ast.flags)?;
|
||||
self.wtr.write_str(")")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_flags(&mut self, ast: &ast::Flags) -> fmt::Result {
|
||||
use crate::ast::{Flag, FlagsItemKind};
|
||||
|
||||
for item in &ast.items {
|
||||
match item.kind {
|
||||
FlagsItemKind::Negation => self.wtr.write_str("-"),
|
||||
FlagsItemKind::Flag(ref flag) => match *flag {
|
||||
Flag::CaseInsensitive => self.wtr.write_str("i"),
|
||||
Flag::MultiLine => self.wtr.write_str("m"),
|
||||
Flag::DotMatchesNewLine => self.wtr.write_str("s"),
|
||||
Flag::SwapGreed => self.wtr.write_str("U"),
|
||||
Flag::Unicode => self.wtr.write_str("u"),
|
||||
Flag::CRLF => self.wtr.write_str("R"),
|
||||
Flag::IgnoreWhitespace => self.wtr.write_str("x"),
|
||||
},
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_class_bracketed_pre(
|
||||
&mut self,
|
||||
ast: &ast::ClassBracketed,
|
||||
) -> fmt::Result {
|
||||
if ast.negated {
|
||||
self.wtr.write_str("[^")
|
||||
} else {
|
||||
self.wtr.write_str("[")
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_class_bracketed_post(
|
||||
&mut self,
|
||||
_ast: &ast::ClassBracketed,
|
||||
) -> fmt::Result {
|
||||
self.wtr.write_str("]")
|
||||
}
|
||||
|
||||
fn fmt_class_set_binary_op_kind(
|
||||
&mut self,
|
||||
ast: &ast::ClassSetBinaryOpKind,
|
||||
) -> fmt::Result {
|
||||
use crate::ast::ClassSetBinaryOpKind::*;
|
||||
match *ast {
|
||||
Intersection => self.wtr.write_str("&&"),
|
||||
Difference => self.wtr.write_str("--"),
|
||||
SymmetricDifference => self.wtr.write_str("~~"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_class_perl(&mut self, ast: &ast::ClassPerl) -> fmt::Result {
|
||||
use crate::ast::ClassPerlKind::*;
|
||||
match ast.kind {
|
||||
Digit if ast.negated => self.wtr.write_str(r"\D"),
|
||||
Digit => self.wtr.write_str(r"\d"),
|
||||
Space if ast.negated => self.wtr.write_str(r"\S"),
|
||||
Space => self.wtr.write_str(r"\s"),
|
||||
Word if ast.negated => self.wtr.write_str(r"\W"),
|
||||
Word => self.wtr.write_str(r"\w"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_class_ascii(&mut self, ast: &ast::ClassAscii) -> fmt::Result {
|
||||
use crate::ast::ClassAsciiKind::*;
|
||||
match ast.kind {
|
||||
Alnum if ast.negated => self.wtr.write_str("[:^alnum:]"),
|
||||
Alnum => self.wtr.write_str("[:alnum:]"),
|
||||
Alpha if ast.negated => self.wtr.write_str("[:^alpha:]"),
|
||||
Alpha => self.wtr.write_str("[:alpha:]"),
|
||||
Ascii if ast.negated => self.wtr.write_str("[:^ascii:]"),
|
||||
Ascii => self.wtr.write_str("[:ascii:]"),
|
||||
Blank if ast.negated => self.wtr.write_str("[:^blank:]"),
|
||||
Blank => self.wtr.write_str("[:blank:]"),
|
||||
Cntrl if ast.negated => self.wtr.write_str("[:^cntrl:]"),
|
||||
Cntrl => self.wtr.write_str("[:cntrl:]"),
|
||||
Digit if ast.negated => self.wtr.write_str("[:^digit:]"),
|
||||
Digit => self.wtr.write_str("[:digit:]"),
|
||||
Graph if ast.negated => self.wtr.write_str("[:^graph:]"),
|
||||
Graph => self.wtr.write_str("[:graph:]"),
|
||||
Lower if ast.negated => self.wtr.write_str("[:^lower:]"),
|
||||
Lower => self.wtr.write_str("[:lower:]"),
|
||||
Print if ast.negated => self.wtr.write_str("[:^print:]"),
|
||||
Print => self.wtr.write_str("[:print:]"),
|
||||
Punct if ast.negated => self.wtr.write_str("[:^punct:]"),
|
||||
Punct => self.wtr.write_str("[:punct:]"),
|
||||
Space if ast.negated => self.wtr.write_str("[:^space:]"),
|
||||
Space => self.wtr.write_str("[:space:]"),
|
||||
Upper if ast.negated => self.wtr.write_str("[:^upper:]"),
|
||||
Upper => self.wtr.write_str("[:upper:]"),
|
||||
Word if ast.negated => self.wtr.write_str("[:^word:]"),
|
||||
Word => self.wtr.write_str("[:word:]"),
|
||||
Xdigit if ast.negated => self.wtr.write_str("[:^xdigit:]"),
|
||||
Xdigit => self.wtr.write_str("[:xdigit:]"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_class_unicode(&mut self, ast: &ast::ClassUnicode) -> fmt::Result {
|
||||
use crate::ast::ClassUnicodeKind::*;
|
||||
use crate::ast::ClassUnicodeOpKind::*;
|
||||
|
||||
if ast.negated {
|
||||
self.wtr.write_str(r"\P")?;
|
||||
} else {
|
||||
self.wtr.write_str(r"\p")?;
|
||||
}
|
||||
match ast.kind {
|
||||
OneLetter(c) => self.wtr.write_char(c),
|
||||
Named(ref x) => write!(self.wtr, "{{{}}}", x),
|
||||
NamedValue { op: Equal, ref name, ref value } => {
|
||||
write!(self.wtr, "{{{}={}}}", name, value)
|
||||
}
|
||||
NamedValue { op: Colon, ref name, ref value } => {
|
||||
write!(self.wtr, "{{{}:{}}}", name, value)
|
||||
}
|
||||
NamedValue { op: NotEqual, ref name, ref value } => {
|
||||
write!(self.wtr, "{{{}!={}}}", name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::ast::parse::ParserBuilder;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn roundtrip(given: &str) {
|
||||
roundtrip_with(|b| b, given);
|
||||
}
|
||||
|
||||
fn roundtrip_with<F>(mut f: F, given: &str)
|
||||
where
|
||||
F: FnMut(&mut ParserBuilder) -> &mut ParserBuilder,
|
||||
{
|
||||
let mut builder = ParserBuilder::new();
|
||||
f(&mut builder);
|
||||
let ast = builder.build().parse(given).unwrap();
|
||||
|
||||
let mut printer = Printer::new();
|
||||
let mut dst = String::new();
|
||||
printer.print(&ast, &mut dst).unwrap();
|
||||
assert_eq!(given, dst);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_literal() {
|
||||
roundtrip("a");
|
||||
roundtrip(r"\[");
|
||||
roundtrip_with(|b| b.octal(true), r"\141");
|
||||
roundtrip(r"\x61");
|
||||
roundtrip(r"\x7F");
|
||||
roundtrip(r"\u0061");
|
||||
roundtrip(r"\U00000061");
|
||||
roundtrip(r"\x{61}");
|
||||
roundtrip(r"\x{7F}");
|
||||
roundtrip(r"\u{61}");
|
||||
roundtrip(r"\U{61}");
|
||||
|
||||
roundtrip(r"\a");
|
||||
roundtrip(r"\f");
|
||||
roundtrip(r"\t");
|
||||
roundtrip(r"\n");
|
||||
roundtrip(r"\r");
|
||||
roundtrip(r"\v");
|
||||
roundtrip(r"(?x)\ ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_dot() {
|
||||
roundtrip(".");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_concat() {
|
||||
roundtrip("ab");
|
||||
roundtrip("abcde");
|
||||
roundtrip("a(bcd)ef");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_alternation() {
|
||||
roundtrip("a|b");
|
||||
roundtrip("a|b|c|d|e");
|
||||
roundtrip("|a|b|c|d|e");
|
||||
roundtrip("|a|b|c|d|e|");
|
||||
roundtrip("a(b|c|d)|e|f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_assertion() {
|
||||
roundtrip(r"^");
|
||||
roundtrip(r"$");
|
||||
roundtrip(r"\A");
|
||||
roundtrip(r"\z");
|
||||
roundtrip(r"\b");
|
||||
roundtrip(r"\B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_repetition() {
|
||||
roundtrip("a?");
|
||||
roundtrip("a??");
|
||||
roundtrip("a*");
|
||||
roundtrip("a*?");
|
||||
roundtrip("a+");
|
||||
roundtrip("a+?");
|
||||
roundtrip("a{5}");
|
||||
roundtrip("a{5}?");
|
||||
roundtrip("a{5,}");
|
||||
roundtrip("a{5,}?");
|
||||
roundtrip("a{5,10}");
|
||||
roundtrip("a{5,10}?");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_flags() {
|
||||
roundtrip("(?i)");
|
||||
roundtrip("(?-i)");
|
||||
roundtrip("(?s-i)");
|
||||
roundtrip("(?-si)");
|
||||
roundtrip("(?siUmux)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_group() {
|
||||
roundtrip("(?i:a)");
|
||||
roundtrip("(?P<foo>a)");
|
||||
roundtrip("(?<foo>a)");
|
||||
roundtrip("(a)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_class() {
|
||||
roundtrip(r"[abc]");
|
||||
roundtrip(r"[a-z]");
|
||||
roundtrip(r"[^a-z]");
|
||||
roundtrip(r"[a-z0-9]");
|
||||
roundtrip(r"[-a-z0-9]");
|
||||
roundtrip(r"[-a-z0-9]");
|
||||
roundtrip(r"[a-z0-9---]");
|
||||
roundtrip(r"[a-z&&m-n]");
|
||||
roundtrip(r"[[a-z&&m-n]]");
|
||||
roundtrip(r"[a-z--m-n]");
|
||||
roundtrip(r"[a-z~~m-n]");
|
||||
roundtrip(r"[a-z[0-9]]");
|
||||
roundtrip(r"[a-z[^0-9]]");
|
||||
|
||||
roundtrip(r"\d");
|
||||
roundtrip(r"\D");
|
||||
roundtrip(r"\s");
|
||||
roundtrip(r"\S");
|
||||
roundtrip(r"\w");
|
||||
roundtrip(r"\W");
|
||||
|
||||
roundtrip(r"[[:alnum:]]");
|
||||
roundtrip(r"[[:^alnum:]]");
|
||||
roundtrip(r"[[:alpha:]]");
|
||||
roundtrip(r"[[:^alpha:]]");
|
||||
roundtrip(r"[[:ascii:]]");
|
||||
roundtrip(r"[[:^ascii:]]");
|
||||
roundtrip(r"[[:blank:]]");
|
||||
roundtrip(r"[[:^blank:]]");
|
||||
roundtrip(r"[[:cntrl:]]");
|
||||
roundtrip(r"[[:^cntrl:]]");
|
||||
roundtrip(r"[[:digit:]]");
|
||||
roundtrip(r"[[:^digit:]]");
|
||||
roundtrip(r"[[:graph:]]");
|
||||
roundtrip(r"[[:^graph:]]");
|
||||
roundtrip(r"[[:lower:]]");
|
||||
roundtrip(r"[[:^lower:]]");
|
||||
roundtrip(r"[[:print:]]");
|
||||
roundtrip(r"[[:^print:]]");
|
||||
roundtrip(r"[[:punct:]]");
|
||||
roundtrip(r"[[:^punct:]]");
|
||||
roundtrip(r"[[:space:]]");
|
||||
roundtrip(r"[[:^space:]]");
|
||||
roundtrip(r"[[:upper:]]");
|
||||
roundtrip(r"[[:^upper:]]");
|
||||
roundtrip(r"[[:word:]]");
|
||||
roundtrip(r"[[:^word:]]");
|
||||
roundtrip(r"[[:xdigit:]]");
|
||||
roundtrip(r"[[:^xdigit:]]");
|
||||
|
||||
roundtrip(r"\pL");
|
||||
roundtrip(r"\PL");
|
||||
roundtrip(r"\p{L}");
|
||||
roundtrip(r"\P{L}");
|
||||
roundtrip(r"\p{X=Y}");
|
||||
roundtrip(r"\P{X=Y}");
|
||||
roundtrip(r"\p{X:Y}");
|
||||
roundtrip(r"\P{X:Y}");
|
||||
roundtrip(r"\p{X!=Y}");
|
||||
roundtrip(r"\P{X!=Y}");
|
||||
}
|
||||
}
|
||||
522
third_party/crates/regex-syntax-0.8.11/src/ast/visitor.rs
vendored
Normal file
522
third_party/crates/regex-syntax-0.8.11/src/ast/visitor.rs
vendored
Normal file
@@ -0,0 +1,522 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use crate::ast::{self, Ast};
|
||||
|
||||
/// A trait for visiting an abstract syntax tree (AST) in depth first order.
|
||||
///
|
||||
/// The principle aim of this trait is to enable callers to perform case
|
||||
/// analysis on an abstract syntax tree without necessarily using recursion.
|
||||
/// In particular, this permits callers to do case analysis with constant stack
|
||||
/// usage, which can be important since the size of an abstract syntax tree
|
||||
/// may be proportional to end user input.
|
||||
///
|
||||
/// Typical usage of this trait involves providing an implementation and then
|
||||
/// running it using the [`visit`] function.
|
||||
///
|
||||
/// Note that the abstract syntax tree for a regular expression is quite
|
||||
/// complex. Unless you specifically need it, you might be able to use the much
|
||||
/// simpler [high-level intermediate representation](crate::hir::Hir) and its
|
||||
/// [corresponding `Visitor` trait](crate::hir::Visitor) instead.
|
||||
pub trait Visitor {
|
||||
/// The result of visiting an AST.
|
||||
type Output;
|
||||
/// An error that visiting an AST might return.
|
||||
type Err;
|
||||
|
||||
/// All implementors of `Visitor` must provide a `finish` method, which
|
||||
/// yields the result of visiting the AST or an error.
|
||||
fn finish(self) -> Result<Self::Output, Self::Err>;
|
||||
|
||||
/// This method is called before beginning traversal of the AST.
|
||||
fn start(&mut self) {}
|
||||
|
||||
/// This method is called on an `Ast` before descending into child `Ast`
|
||||
/// nodes.
|
||||
fn visit_pre(&mut self, _ast: &Ast) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called on an `Ast` after descending all of its child
|
||||
/// `Ast` nodes.
|
||||
fn visit_post(&mut self, _ast: &Ast) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called between child nodes of an
|
||||
/// [`Alternation`](ast::Alternation).
|
||||
fn visit_alternation_in(&mut self) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called between child nodes of a concatenation.
|
||||
fn visit_concat_in(&mut self) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called on every [`ClassSetItem`](ast::ClassSetItem)
|
||||
/// before descending into child nodes.
|
||||
fn visit_class_set_item_pre(
|
||||
&mut self,
|
||||
_ast: &ast::ClassSetItem,
|
||||
) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called on every [`ClassSetItem`](ast::ClassSetItem)
|
||||
/// after descending into child nodes.
|
||||
fn visit_class_set_item_post(
|
||||
&mut self,
|
||||
_ast: &ast::ClassSetItem,
|
||||
) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called on every
|
||||
/// [`ClassSetBinaryOp`](ast::ClassSetBinaryOp) before descending into
|
||||
/// child nodes.
|
||||
fn visit_class_set_binary_op_pre(
|
||||
&mut self,
|
||||
_ast: &ast::ClassSetBinaryOp,
|
||||
) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called on every
|
||||
/// [`ClassSetBinaryOp`](ast::ClassSetBinaryOp) after descending into child
|
||||
/// nodes.
|
||||
fn visit_class_set_binary_op_post(
|
||||
&mut self,
|
||||
_ast: &ast::ClassSetBinaryOp,
|
||||
) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is called between the left hand and right hand child nodes
|
||||
/// of a [`ClassSetBinaryOp`](ast::ClassSetBinaryOp).
|
||||
fn visit_class_set_binary_op_in(
|
||||
&mut self,
|
||||
_ast: &ast::ClassSetBinaryOp,
|
||||
) -> Result<(), Self::Err> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes an implementation of `Visitor` in constant stack space.
|
||||
///
|
||||
/// This function will visit every node in the given `Ast` while calling the
|
||||
/// appropriate methods provided by the [`Visitor`] trait.
|
||||
///
|
||||
/// The primary use case for this method is when one wants to perform case
|
||||
/// analysis over an `Ast` without using a stack size proportional to the depth
|
||||
/// of the `Ast`. Namely, this method will instead use constant stack size, but
|
||||
/// will use heap space proportional to the size of the `Ast`. This may be
|
||||
/// desirable in cases where the size of `Ast` is proportional to end user
|
||||
/// input.
|
||||
///
|
||||
/// If the visitor returns an error at any point, then visiting is stopped and
|
||||
/// the error is returned.
|
||||
pub fn visit<V: Visitor>(ast: &Ast, visitor: V) -> Result<V::Output, V::Err> {
|
||||
HeapVisitor::new().visit(ast, visitor)
|
||||
}
|
||||
|
||||
/// HeapVisitor visits every item in an `Ast` recursively using constant stack
|
||||
/// size and a heap size proportional to the size of the `Ast`.
|
||||
struct HeapVisitor<'a> {
|
||||
/// A stack of `Ast` nodes. This is roughly analogous to the call stack
|
||||
/// used in a typical recursive visitor.
|
||||
stack: Vec<(&'a Ast, Frame<'a>)>,
|
||||
/// Similar to the `Ast` stack above, but is used only for character
|
||||
/// classes. In particular, character classes embed their own mini
|
||||
/// recursive syntax.
|
||||
stack_class: Vec<(ClassInduct<'a>, ClassFrame<'a>)>,
|
||||
}
|
||||
|
||||
/// Represents a single stack frame while performing structural induction over
|
||||
/// an `Ast`.
|
||||
enum Frame<'a> {
|
||||
/// A stack frame allocated just before descending into a repetition
|
||||
/// operator's child node.
|
||||
Repetition(&'a ast::Repetition),
|
||||
/// A stack frame allocated just before descending into a group's child
|
||||
/// node.
|
||||
Group(&'a ast::Group),
|
||||
/// The stack frame used while visiting every child node of a concatenation
|
||||
/// of expressions.
|
||||
Concat {
|
||||
/// The child node we are currently visiting.
|
||||
head: &'a Ast,
|
||||
/// The remaining child nodes to visit (which may be empty).
|
||||
tail: &'a [Ast],
|
||||
},
|
||||
/// The stack frame used while visiting every child node of an alternation
|
||||
/// of expressions.
|
||||
Alternation {
|
||||
/// The child node we are currently visiting.
|
||||
head: &'a Ast,
|
||||
/// The remaining child nodes to visit (which may be empty).
|
||||
tail: &'a [Ast],
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents a single stack frame while performing structural induction over
|
||||
/// a character class.
|
||||
enum ClassFrame<'a> {
|
||||
/// The stack frame used while visiting every child node of a union of
|
||||
/// character class items.
|
||||
Union {
|
||||
/// The child node we are currently visiting.
|
||||
head: &'a ast::ClassSetItem,
|
||||
/// The remaining child nodes to visit (which may be empty).
|
||||
tail: &'a [ast::ClassSetItem],
|
||||
},
|
||||
/// The stack frame used while a binary class operation.
|
||||
Binary { op: &'a ast::ClassSetBinaryOp },
|
||||
/// A stack frame allocated just before descending into a binary operator's
|
||||
/// left hand child node.
|
||||
BinaryLHS {
|
||||
op: &'a ast::ClassSetBinaryOp,
|
||||
lhs: &'a ast::ClassSet,
|
||||
rhs: &'a ast::ClassSet,
|
||||
},
|
||||
/// A stack frame allocated just before descending into a binary operator's
|
||||
/// right hand child node.
|
||||
BinaryRHS { op: &'a ast::ClassSetBinaryOp, rhs: &'a ast::ClassSet },
|
||||
}
|
||||
|
||||
/// A representation of the inductive step when performing structural induction
|
||||
/// over a character class.
|
||||
///
|
||||
/// Note that there is no analogous explicit type for the inductive step for
|
||||
/// `Ast` nodes because the inductive step is just an `Ast`. For character
|
||||
/// classes, the inductive step can produce one of two possible child nodes:
|
||||
/// an item or a binary operation. (An item cannot be a binary operation
|
||||
/// because that would imply binary operations can be unioned in the concrete
|
||||
/// syntax, which is not possible.)
|
||||
enum ClassInduct<'a> {
|
||||
Item(&'a ast::ClassSetItem),
|
||||
BinaryOp(&'a ast::ClassSetBinaryOp),
|
||||
}
|
||||
|
||||
impl<'a> HeapVisitor<'a> {
|
||||
fn new() -> HeapVisitor<'a> {
|
||||
HeapVisitor { stack: vec![], stack_class: vec![] }
|
||||
}
|
||||
|
||||
fn visit<V: Visitor>(
|
||||
&mut self,
|
||||
mut ast: &'a Ast,
|
||||
mut visitor: V,
|
||||
) -> Result<V::Output, V::Err> {
|
||||
self.stack.clear();
|
||||
self.stack_class.clear();
|
||||
|
||||
visitor.start();
|
||||
loop {
|
||||
visitor.visit_pre(ast)?;
|
||||
if let Some(x) = self.induct(ast, &mut visitor)? {
|
||||
let child = x.child();
|
||||
self.stack.push((ast, x));
|
||||
ast = child;
|
||||
continue;
|
||||
}
|
||||
// No induction means we have a base case, so we can post visit
|
||||
// it now.
|
||||
visitor.visit_post(ast)?;
|
||||
|
||||
// At this point, we now try to pop our call stack until it is
|
||||
// either empty or we hit another inductive case.
|
||||
loop {
|
||||
let (post_ast, frame) = match self.stack.pop() {
|
||||
None => return visitor.finish(),
|
||||
Some((post_ast, frame)) => (post_ast, frame),
|
||||
};
|
||||
// If this is a concat/alternate, then we might have additional
|
||||
// inductive steps to process.
|
||||
if let Some(x) = self.pop(frame) {
|
||||
match x {
|
||||
Frame::Alternation { .. } => {
|
||||
visitor.visit_alternation_in()?;
|
||||
}
|
||||
Frame::Concat { .. } => {
|
||||
visitor.visit_concat_in()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ast = x.child();
|
||||
self.stack.push((post_ast, x));
|
||||
break;
|
||||
}
|
||||
// Otherwise, we've finished visiting all the child nodes for
|
||||
// this AST, so we can post visit it now.
|
||||
visitor.visit_post(post_ast)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a stack frame for the given AST if one is needed (which occurs if
|
||||
/// and only if there are child nodes in the AST). Otherwise, return None.
|
||||
///
|
||||
/// If this visits a class, then the underlying visitor implementation may
|
||||
/// return an error which will be passed on here.
|
||||
fn induct<V: Visitor>(
|
||||
&mut self,
|
||||
ast: &'a Ast,
|
||||
visitor: &mut V,
|
||||
) -> Result<Option<Frame<'a>>, V::Err> {
|
||||
Ok(match *ast {
|
||||
Ast::ClassBracketed(ref x) => {
|
||||
self.visit_class(x, visitor)?;
|
||||
None
|
||||
}
|
||||
Ast::Repetition(ref x) => Some(Frame::Repetition(x)),
|
||||
Ast::Group(ref x) => Some(Frame::Group(x)),
|
||||
Ast::Concat(ref x) if x.asts.is_empty() => None,
|
||||
Ast::Concat(ref x) => {
|
||||
Some(Frame::Concat { head: &x.asts[0], tail: &x.asts[1..] })
|
||||
}
|
||||
Ast::Alternation(ref x) if x.asts.is_empty() => None,
|
||||
Ast::Alternation(ref x) => Some(Frame::Alternation {
|
||||
head: &x.asts[0],
|
||||
tail: &x.asts[1..],
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Pops the given frame. If the frame has an additional inductive step,
|
||||
/// then return it, otherwise return `None`.
|
||||
fn pop(&self, induct: Frame<'a>) -> Option<Frame<'a>> {
|
||||
match induct {
|
||||
Frame::Repetition(_) => None,
|
||||
Frame::Group(_) => None,
|
||||
Frame::Concat { tail, .. } => {
|
||||
if tail.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Frame::Concat { head: &tail[0], tail: &tail[1..] })
|
||||
}
|
||||
}
|
||||
Frame::Alternation { tail, .. } => {
|
||||
if tail.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Frame::Alternation {
|
||||
head: &tail[0],
|
||||
tail: &tail[1..],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_class<V: Visitor>(
|
||||
&mut self,
|
||||
ast: &'a ast::ClassBracketed,
|
||||
visitor: &mut V,
|
||||
) -> Result<(), V::Err> {
|
||||
let mut ast = ClassInduct::from_bracketed(ast);
|
||||
loop {
|
||||
self.visit_class_pre(&ast, visitor)?;
|
||||
if let Some(x) = self.induct_class(&ast) {
|
||||
let child = x.child();
|
||||
self.stack_class.push((ast, x));
|
||||
ast = child;
|
||||
continue;
|
||||
}
|
||||
self.visit_class_post(&ast, visitor)?;
|
||||
|
||||
// At this point, we now try to pop our call stack until it is
|
||||
// either empty or we hit another inductive case.
|
||||
loop {
|
||||
let (post_ast, frame) = match self.stack_class.pop() {
|
||||
None => return Ok(()),
|
||||
Some((post_ast, frame)) => (post_ast, frame),
|
||||
};
|
||||
// If this is a union or a binary op, then we might have
|
||||
// additional inductive steps to process.
|
||||
if let Some(x) = self.pop_class(frame) {
|
||||
if let ClassFrame::BinaryRHS { ref op, .. } = x {
|
||||
visitor.visit_class_set_binary_op_in(op)?;
|
||||
}
|
||||
ast = x.child();
|
||||
self.stack_class.push((post_ast, x));
|
||||
break;
|
||||
}
|
||||
// Otherwise, we've finished visiting all the child nodes for
|
||||
// this class node, so we can post visit it now.
|
||||
self.visit_class_post(&post_ast, visitor)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the appropriate `Visitor` methods given an inductive step.
|
||||
fn visit_class_pre<V: Visitor>(
|
||||
&self,
|
||||
ast: &ClassInduct<'a>,
|
||||
visitor: &mut V,
|
||||
) -> Result<(), V::Err> {
|
||||
match *ast {
|
||||
ClassInduct::Item(item) => {
|
||||
visitor.visit_class_set_item_pre(item)?;
|
||||
}
|
||||
ClassInduct::BinaryOp(op) => {
|
||||
visitor.visit_class_set_binary_op_pre(op)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Call the appropriate `Visitor` methods given an inductive step.
|
||||
fn visit_class_post<V: Visitor>(
|
||||
&self,
|
||||
ast: &ClassInduct<'a>,
|
||||
visitor: &mut V,
|
||||
) -> Result<(), V::Err> {
|
||||
match *ast {
|
||||
ClassInduct::Item(item) => {
|
||||
visitor.visit_class_set_item_post(item)?;
|
||||
}
|
||||
ClassInduct::BinaryOp(op) => {
|
||||
visitor.visit_class_set_binary_op_post(op)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build a stack frame for the given class node if one is needed (which
|
||||
/// occurs if and only if there are child nodes). Otherwise, return None.
|
||||
fn induct_class(&self, ast: &ClassInduct<'a>) -> Option<ClassFrame<'a>> {
|
||||
match *ast {
|
||||
ClassInduct::Item(&ast::ClassSetItem::Bracketed(ref x)) => {
|
||||
match x.kind {
|
||||
ast::ClassSet::Item(ref item) => {
|
||||
Some(ClassFrame::Union { head: item, tail: &[] })
|
||||
}
|
||||
ast::ClassSet::BinaryOp(ref op) => {
|
||||
Some(ClassFrame::Binary { op })
|
||||
}
|
||||
}
|
||||
}
|
||||
ClassInduct::Item(&ast::ClassSetItem::Union(ref x)) => {
|
||||
if x.items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ClassFrame::Union {
|
||||
head: &x.items[0],
|
||||
tail: &x.items[1..],
|
||||
})
|
||||
}
|
||||
}
|
||||
ClassInduct::BinaryOp(op) => {
|
||||
Some(ClassFrame::BinaryLHS { op, lhs: &op.lhs, rhs: &op.rhs })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the given frame. If the frame has an additional inductive step,
|
||||
/// then return it, otherwise return `None`.
|
||||
fn pop_class(&self, induct: ClassFrame<'a>) -> Option<ClassFrame<'a>> {
|
||||
match induct {
|
||||
ClassFrame::Union { tail, .. } => {
|
||||
if tail.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ClassFrame::Union {
|
||||
head: &tail[0],
|
||||
tail: &tail[1..],
|
||||
})
|
||||
}
|
||||
}
|
||||
ClassFrame::Binary { .. } => None,
|
||||
ClassFrame::BinaryLHS { op, rhs, .. } => {
|
||||
Some(ClassFrame::BinaryRHS { op, rhs })
|
||||
}
|
||||
ClassFrame::BinaryRHS { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Frame<'a> {
|
||||
/// Perform the next inductive step on this frame and return the next
|
||||
/// child AST node to visit.
|
||||
fn child(&self) -> &'a Ast {
|
||||
match *self {
|
||||
Frame::Repetition(rep) => &rep.ast,
|
||||
Frame::Group(group) => &group.ast,
|
||||
Frame::Concat { head, .. } => head,
|
||||
Frame::Alternation { head, .. } => head,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ClassFrame<'a> {
|
||||
/// Perform the next inductive step on this frame and return the next
|
||||
/// child class node to visit.
|
||||
fn child(&self) -> ClassInduct<'a> {
|
||||
match *self {
|
||||
ClassFrame::Union { head, .. } => ClassInduct::Item(head),
|
||||
ClassFrame::Binary { op, .. } => ClassInduct::BinaryOp(op),
|
||||
ClassFrame::BinaryLHS { ref lhs, .. } => {
|
||||
ClassInduct::from_set(lhs)
|
||||
}
|
||||
ClassFrame::BinaryRHS { ref rhs, .. } => {
|
||||
ClassInduct::from_set(rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ClassInduct<'a> {
|
||||
fn from_bracketed(ast: &'a ast::ClassBracketed) -> ClassInduct<'a> {
|
||||
ClassInduct::from_set(&ast.kind)
|
||||
}
|
||||
|
||||
fn from_set(ast: &'a ast::ClassSet) -> ClassInduct<'a> {
|
||||
match *ast {
|
||||
ast::ClassSet::Item(ref item) => ClassInduct::Item(item),
|
||||
ast::ClassSet::BinaryOp(ref op) => ClassInduct::BinaryOp(op),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> core::fmt::Debug for ClassFrame<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let x = match *self {
|
||||
ClassFrame::Union { .. } => "Union",
|
||||
ClassFrame::Binary { .. } => "Binary",
|
||||
ClassFrame::BinaryLHS { .. } => "BinaryLHS",
|
||||
ClassFrame::BinaryRHS { .. } => "BinaryRHS",
|
||||
};
|
||||
write!(f, "{x}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> core::fmt::Debug for ClassInduct<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let x = match *self {
|
||||
ClassInduct::Item(it) => match *it {
|
||||
ast::ClassSetItem::Empty(_) => "Item(Empty)",
|
||||
ast::ClassSetItem::Literal(_) => "Item(Literal)",
|
||||
ast::ClassSetItem::Range(_) => "Item(Range)",
|
||||
ast::ClassSetItem::Ascii(_) => "Item(Ascii)",
|
||||
ast::ClassSetItem::Perl(_) => "Item(Perl)",
|
||||
ast::ClassSetItem::Unicode(_) => "Item(Unicode)",
|
||||
ast::ClassSetItem::Bracketed(_) => "Item(Bracketed)",
|
||||
ast::ClassSetItem::Union(_) => "Item(Union)",
|
||||
},
|
||||
ClassInduct::BinaryOp(it) => match it.kind {
|
||||
ast::ClassSetBinaryOpKind::Intersection => {
|
||||
"BinaryOp(Intersection)"
|
||||
}
|
||||
ast::ClassSetBinaryOpKind::Difference => {
|
||||
"BinaryOp(Difference)"
|
||||
}
|
||||
ast::ClassSetBinaryOpKind::SymmetricDifference => {
|
||||
"BinaryOp(SymmetricDifference)"
|
||||
}
|
||||
},
|
||||
};
|
||||
write!(f, "{x}")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user