Coverage Report

Created: 2025-09-27 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.6/src/ast/print.rs
Line
Count
Source
1
/*!
2
This module provides a regular expression printer for `Ast`.
3
*/
4
5
use core::fmt;
6
7
use crate::ast::{
8
    self,
9
    visitor::{self, Visitor},
10
    Ast,
11
};
12
13
/// A builder for constructing a printer.
14
///
15
/// Note that since a printer doesn't have any configuration knobs, this type
16
/// remains unexported.
17
#[derive(Clone, Debug)]
18
struct PrinterBuilder {
19
    _priv: (),
20
}
21
22
impl Default for PrinterBuilder {
23
0
    fn default() -> PrinterBuilder {
24
0
        PrinterBuilder::new()
25
0
    }
26
}
27
28
impl PrinterBuilder {
29
0
    fn new() -> PrinterBuilder {
30
0
        PrinterBuilder { _priv: () }
31
0
    }
32
33
0
    fn build(&self) -> Printer {
34
0
        Printer { _priv: () }
35
0
    }
36
}
37
38
/// A printer for a regular expression abstract syntax tree.
39
///
40
/// A printer converts an abstract syntax tree (AST) to a regular expression
41
/// pattern string. This particular printer uses constant stack space and heap
42
/// space proportional to the size of the AST.
43
///
44
/// This printer will not necessarily preserve the original formatting of the
45
/// regular expression pattern string. For example, all whitespace and comments
46
/// are ignored.
47
#[derive(Debug)]
48
pub struct Printer {
49
    _priv: (),
50
}
51
52
impl Printer {
53
    /// Create a new printer.
54
0
    pub fn new() -> Printer {
55
0
        PrinterBuilder::new().build()
56
0
    }
57
58
    /// Print the given `Ast` to the given writer. The writer must implement
59
    /// `fmt::Write`. Typical implementations of `fmt::Write` that can be used
60
    /// here are a `fmt::Formatter` (which is available in `fmt::Display`
61
    /// implementations) or a `&mut String`.
62
0
    pub fn print<W: fmt::Write>(&mut self, ast: &Ast, wtr: W) -> fmt::Result {
63
0
        visitor::visit(ast, Writer { wtr })
64
0
    }
65
}
66
67
#[derive(Debug)]
68
struct Writer<W> {
69
    wtr: W,
70
}
71
72
impl<W: fmt::Write> Visitor for Writer<W> {
73
    type Output = ();
74
    type Err = fmt::Error;
75
76
0
    fn finish(self) -> fmt::Result {
77
0
        Ok(())
78
0
    }
79
80
0
    fn visit_pre(&mut self, ast: &Ast) -> fmt::Result {
81
0
        match *ast {
82
0
            Ast::Group(ref x) => self.fmt_group_pre(x),
83
0
            Ast::ClassBracketed(ref x) => self.fmt_class_bracketed_pre(x),
84
0
            _ => Ok(()),
85
        }
86
0
    }
87
88
0
    fn visit_post(&mut self, ast: &Ast) -> fmt::Result {
89
0
        match *ast {
90
0
            Ast::Empty(_) => Ok(()),
91
0
            Ast::Flags(ref x) => self.fmt_set_flags(x),
92
0
            Ast::Literal(ref x) => self.fmt_literal(x),
93
0
            Ast::Dot(_) => self.wtr.write_str("."),
94
0
            Ast::Assertion(ref x) => self.fmt_assertion(x),
95
0
            Ast::ClassPerl(ref x) => self.fmt_class_perl(x),
96
0
            Ast::ClassUnicode(ref x) => self.fmt_class_unicode(x),
97
0
            Ast::ClassBracketed(ref x) => self.fmt_class_bracketed_post(x),
98
0
            Ast::Repetition(ref x) => self.fmt_repetition(x),
99
0
            Ast::Group(ref x) => self.fmt_group_post(x),
100
0
            Ast::Alternation(_) => Ok(()),
101
0
            Ast::Concat(_) => Ok(()),
102
        }
103
0
    }
104
105
0
    fn visit_alternation_in(&mut self) -> fmt::Result {
106
0
        self.wtr.write_str("|")
107
0
    }
108
109
0
    fn visit_class_set_item_pre(
110
0
        &mut self,
111
0
        ast: &ast::ClassSetItem,
112
0
    ) -> Result<(), Self::Err> {
113
0
        match *ast {
114
0
            ast::ClassSetItem::Bracketed(ref x) => {
115
0
                self.fmt_class_bracketed_pre(x)
116
            }
117
0
            _ => Ok(()),
118
        }
119
0
    }
120
121
0
    fn visit_class_set_item_post(
122
0
        &mut self,
123
0
        ast: &ast::ClassSetItem,
124
0
    ) -> Result<(), Self::Err> {
125
        use crate::ast::ClassSetItem::*;
126
127
0
        match *ast {
128
0
            Empty(_) => Ok(()),
129
0
            Literal(ref x) => self.fmt_literal(x),
130
0
            Range(ref x) => {
131
0
                self.fmt_literal(&x.start)?;
132
0
                self.wtr.write_str("-")?;
133
0
                self.fmt_literal(&x.end)?;
134
0
                Ok(())
135
            }
136
0
            Ascii(ref x) => self.fmt_class_ascii(x),
137
0
            Unicode(ref x) => self.fmt_class_unicode(x),
138
0
            Perl(ref x) => self.fmt_class_perl(x),
139
0
            Bracketed(ref x) => self.fmt_class_bracketed_post(x),
140
0
            Union(_) => Ok(()),
141
        }
142
0
    }
143
144
0
    fn visit_class_set_binary_op_in(
145
0
        &mut self,
146
0
        ast: &ast::ClassSetBinaryOp,
147
0
    ) -> Result<(), Self::Err> {
148
0
        self.fmt_class_set_binary_op_kind(&ast.kind)
149
0
    }
150
}
151
152
impl<W: fmt::Write> Writer<W> {
153
0
    fn fmt_group_pre(&mut self, ast: &ast::Group) -> fmt::Result {
154
        use crate::ast::GroupKind::*;
155
0
        match ast.kind {
156
0
            CaptureIndex(_) => self.wtr.write_str("("),
157
0
            CaptureName { ref name, starts_with_p } => {
158
0
                let start = if starts_with_p { "(?P<" } else { "(?<" };
159
0
                self.wtr.write_str(start)?;
160
0
                self.wtr.write_str(&name.name)?;
161
0
                self.wtr.write_str(">")?;
162
0
                Ok(())
163
            }
164
0
            NonCapturing(ref flags) => {
165
0
                self.wtr.write_str("(?")?;
166
0
                self.fmt_flags(flags)?;
167
0
                self.wtr.write_str(":")?;
168
0
                Ok(())
169
            }
170
        }
171
0
    }
172
173
0
    fn fmt_group_post(&mut self, _ast: &ast::Group) -> fmt::Result {
174
0
        self.wtr.write_str(")")
175
0
    }
176
177
0
    fn fmt_repetition(&mut self, ast: &ast::Repetition) -> fmt::Result {
178
        use crate::ast::RepetitionKind::*;
179
0
        match ast.op.kind {
180
0
            ZeroOrOne if ast.greedy => self.wtr.write_str("?"),
181
0
            ZeroOrOne => self.wtr.write_str("??"),
182
0
            ZeroOrMore if ast.greedy => self.wtr.write_str("*"),
183
0
            ZeroOrMore => self.wtr.write_str("*?"),
184
0
            OneOrMore if ast.greedy => self.wtr.write_str("+"),
185
0
            OneOrMore => self.wtr.write_str("+?"),
186
0
            Range(ref x) => {
187
0
                self.fmt_repetition_range(x)?;
188
0
                if !ast.greedy {
189
0
                    self.wtr.write_str("?")?;
190
0
                }
191
0
                Ok(())
192
            }
193
        }
194
0
    }
195
196
0
    fn fmt_repetition_range(
197
0
        &mut self,
198
0
        ast: &ast::RepetitionRange,
199
0
    ) -> fmt::Result {
200
        use crate::ast::RepetitionRange::*;
201
0
        match *ast {
202
0
            Exactly(x) => write!(self.wtr, "{{{}}}", x),
203
0
            AtLeast(x) => write!(self.wtr, "{{{},}}", x),
204
0
            Bounded(x, y) => write!(self.wtr, "{{{},{}}}", x, y),
205
        }
206
0
    }
207
208
0
    fn fmt_literal(&mut self, ast: &ast::Literal) -> fmt::Result {
209
        use crate::ast::LiteralKind::*;
210
211
0
        match ast.kind {
212
0
            Verbatim => self.wtr.write_char(ast.c),
213
0
            Meta | Superfluous => write!(self.wtr, r"\{}", ast.c),
214
0
            Octal => write!(self.wtr, r"\{:o}", u32::from(ast.c)),
215
            HexFixed(ast::HexLiteralKind::X) => {
216
0
                write!(self.wtr, r"\x{:02X}", u32::from(ast.c))
217
            }
218
            HexFixed(ast::HexLiteralKind::UnicodeShort) => {
219
0
                write!(self.wtr, r"\u{:04X}", u32::from(ast.c))
220
            }
221
            HexFixed(ast::HexLiteralKind::UnicodeLong) => {
222
0
                write!(self.wtr, r"\U{:08X}", u32::from(ast.c))
223
            }
224
            HexBrace(ast::HexLiteralKind::X) => {
225
0
                write!(self.wtr, r"\x{{{:X}}}", u32::from(ast.c))
226
            }
227
            HexBrace(ast::HexLiteralKind::UnicodeShort) => {
228
0
                write!(self.wtr, r"\u{{{:X}}}", u32::from(ast.c))
229
            }
230
            HexBrace(ast::HexLiteralKind::UnicodeLong) => {
231
0
                write!(self.wtr, r"\U{{{:X}}}", u32::from(ast.c))
232
            }
233
            Special(ast::SpecialLiteralKind::Bell) => {
234
0
                self.wtr.write_str(r"\a")
235
            }
236
            Special(ast::SpecialLiteralKind::FormFeed) => {
237
0
                self.wtr.write_str(r"\f")
238
            }
239
0
            Special(ast::SpecialLiteralKind::Tab) => self.wtr.write_str(r"\t"),
240
            Special(ast::SpecialLiteralKind::LineFeed) => {
241
0
                self.wtr.write_str(r"\n")
242
            }
243
            Special(ast::SpecialLiteralKind::CarriageReturn) => {
244
0
                self.wtr.write_str(r"\r")
245
            }
246
            Special(ast::SpecialLiteralKind::VerticalTab) => {
247
0
                self.wtr.write_str(r"\v")
248
            }
249
            Special(ast::SpecialLiteralKind::Space) => {
250
0
                self.wtr.write_str(r"\ ")
251
            }
252
        }
253
0
    }
254
255
0
    fn fmt_assertion(&mut self, ast: &ast::Assertion) -> fmt::Result {
256
        use crate::ast::AssertionKind::*;
257
0
        match ast.kind {
258
0
            StartLine => self.wtr.write_str("^"),
259
0
            EndLine => self.wtr.write_str("$"),
260
0
            StartText => self.wtr.write_str(r"\A"),
261
0
            EndText => self.wtr.write_str(r"\z"),
262
0
            WordBoundary => self.wtr.write_str(r"\b"),
263
0
            NotWordBoundary => self.wtr.write_str(r"\B"),
264
0
            WordBoundaryStart => self.wtr.write_str(r"\b{start}"),
265
0
            WordBoundaryEnd => self.wtr.write_str(r"\b{end}"),
266
0
            WordBoundaryStartAngle => self.wtr.write_str(r"\<"),
267
0
            WordBoundaryEndAngle => self.wtr.write_str(r"\>"),
268
0
            WordBoundaryStartHalf => self.wtr.write_str(r"\b{start-half}"),
269
0
            WordBoundaryEndHalf => self.wtr.write_str(r"\b{end-half}"),
270
        }
271
0
    }
272
273
0
    fn fmt_set_flags(&mut self, ast: &ast::SetFlags) -> fmt::Result {
274
0
        self.wtr.write_str("(?")?;
275
0
        self.fmt_flags(&ast.flags)?;
276
0
        self.wtr.write_str(")")?;
277
0
        Ok(())
278
0
    }
279
280
0
    fn fmt_flags(&mut self, ast: &ast::Flags) -> fmt::Result {
281
        use crate::ast::{Flag, FlagsItemKind};
282
283
0
        for item in &ast.items {
284
0
            match item.kind {
285
0
                FlagsItemKind::Negation => self.wtr.write_str("-"),
286
0
                FlagsItemKind::Flag(ref flag) => match *flag {
287
0
                    Flag::CaseInsensitive => self.wtr.write_str("i"),
288
0
                    Flag::MultiLine => self.wtr.write_str("m"),
289
0
                    Flag::DotMatchesNewLine => self.wtr.write_str("s"),
290
0
                    Flag::SwapGreed => self.wtr.write_str("U"),
291
0
                    Flag::Unicode => self.wtr.write_str("u"),
292
0
                    Flag::CRLF => self.wtr.write_str("R"),
293
0
                    Flag::IgnoreWhitespace => self.wtr.write_str("x"),
294
                },
295
0
            }?;
296
        }
297
0
        Ok(())
298
0
    }
299
300
0
    fn fmt_class_bracketed_pre(
301
0
        &mut self,
302
0
        ast: &ast::ClassBracketed,
303
0
    ) -> fmt::Result {
304
0
        if ast.negated {
305
0
            self.wtr.write_str("[^")
306
        } else {
307
0
            self.wtr.write_str("[")
308
        }
309
0
    }
310
311
0
    fn fmt_class_bracketed_post(
312
0
        &mut self,
313
0
        _ast: &ast::ClassBracketed,
314
0
    ) -> fmt::Result {
315
0
        self.wtr.write_str("]")
316
0
    }
317
318
0
    fn fmt_class_set_binary_op_kind(
319
0
        &mut self,
320
0
        ast: &ast::ClassSetBinaryOpKind,
321
0
    ) -> fmt::Result {
322
        use crate::ast::ClassSetBinaryOpKind::*;
323
0
        match *ast {
324
0
            Intersection => self.wtr.write_str("&&"),
325
0
            Difference => self.wtr.write_str("--"),
326
0
            SymmetricDifference => self.wtr.write_str("~~"),
327
        }
328
0
    }
329
330
0
    fn fmt_class_perl(&mut self, ast: &ast::ClassPerl) -> fmt::Result {
331
        use crate::ast::ClassPerlKind::*;
332
0
        match ast.kind {
333
0
            Digit if ast.negated => self.wtr.write_str(r"\D"),
334
0
            Digit => self.wtr.write_str(r"\d"),
335
0
            Space if ast.negated => self.wtr.write_str(r"\S"),
336
0
            Space => self.wtr.write_str(r"\s"),
337
0
            Word if ast.negated => self.wtr.write_str(r"\W"),
338
0
            Word => self.wtr.write_str(r"\w"),
339
        }
340
0
    }
341
342
0
    fn fmt_class_ascii(&mut self, ast: &ast::ClassAscii) -> fmt::Result {
343
        use crate::ast::ClassAsciiKind::*;
344
0
        match ast.kind {
345
0
            Alnum if ast.negated => self.wtr.write_str("[:^alnum:]"),
346
0
            Alnum => self.wtr.write_str("[:alnum:]"),
347
0
            Alpha if ast.negated => self.wtr.write_str("[:^alpha:]"),
348
0
            Alpha => self.wtr.write_str("[:alpha:]"),
349
0
            Ascii if ast.negated => self.wtr.write_str("[:^ascii:]"),
350
0
            Ascii => self.wtr.write_str("[:ascii:]"),
351
0
            Blank if ast.negated => self.wtr.write_str("[:^blank:]"),
352
0
            Blank => self.wtr.write_str("[:blank:]"),
353
0
            Cntrl if ast.negated => self.wtr.write_str("[:^cntrl:]"),
354
0
            Cntrl => self.wtr.write_str("[:cntrl:]"),
355
0
            Digit if ast.negated => self.wtr.write_str("[:^digit:]"),
356
0
            Digit => self.wtr.write_str("[:digit:]"),
357
0
            Graph if ast.negated => self.wtr.write_str("[:^graph:]"),
358
0
            Graph => self.wtr.write_str("[:graph:]"),
359
0
            Lower if ast.negated => self.wtr.write_str("[:^lower:]"),
360
0
            Lower => self.wtr.write_str("[:lower:]"),
361
0
            Print if ast.negated => self.wtr.write_str("[:^print:]"),
362
0
            Print => self.wtr.write_str("[:print:]"),
363
0
            Punct if ast.negated => self.wtr.write_str("[:^punct:]"),
364
0
            Punct => self.wtr.write_str("[:punct:]"),
365
0
            Space if ast.negated => self.wtr.write_str("[:^space:]"),
366
0
            Space => self.wtr.write_str("[:space:]"),
367
0
            Upper if ast.negated => self.wtr.write_str("[:^upper:]"),
368
0
            Upper => self.wtr.write_str("[:upper:]"),
369
0
            Word if ast.negated => self.wtr.write_str("[:^word:]"),
370
0
            Word => self.wtr.write_str("[:word:]"),
371
0
            Xdigit if ast.negated => self.wtr.write_str("[:^xdigit:]"),
372
0
            Xdigit => self.wtr.write_str("[:xdigit:]"),
373
        }
374
0
    }
375
376
0
    fn fmt_class_unicode(&mut self, ast: &ast::ClassUnicode) -> fmt::Result {
377
        use crate::ast::ClassUnicodeKind::*;
378
        use crate::ast::ClassUnicodeOpKind::*;
379
380
0
        if ast.negated {
381
0
            self.wtr.write_str(r"\P")?;
382
        } else {
383
0
            self.wtr.write_str(r"\p")?;
384
        }
385
0
        match ast.kind {
386
0
            OneLetter(c) => self.wtr.write_char(c),
387
0
            Named(ref x) => write!(self.wtr, "{{{}}}", x),
388
0
            NamedValue { op: Equal, ref name, ref value } => {
389
0
                write!(self.wtr, "{{{}={}}}", name, value)
390
            }
391
0
            NamedValue { op: Colon, ref name, ref value } => {
392
0
                write!(self.wtr, "{{{}:{}}}", name, value)
393
            }
394
0
            NamedValue { op: NotEqual, ref name, ref value } => {
395
0
                write!(self.wtr, "{{{}!={}}}", name, value)
396
            }
397
        }
398
0
    }
399
}
400
401
#[cfg(test)]
402
mod tests {
403
    use alloc::string::String;
404
405
    use crate::ast::parse::ParserBuilder;
406
407
    use super::*;
408
409
    fn roundtrip(given: &str) {
410
        roundtrip_with(|b| b, given);
411
    }
412
413
    fn roundtrip_with<F>(mut f: F, given: &str)
414
    where
415
        F: FnMut(&mut ParserBuilder) -> &mut ParserBuilder,
416
    {
417
        let mut builder = ParserBuilder::new();
418
        f(&mut builder);
419
        let ast = builder.build().parse(given).unwrap();
420
421
        let mut printer = Printer::new();
422
        let mut dst = String::new();
423
        printer.print(&ast, &mut dst).unwrap();
424
        assert_eq!(given, dst);
425
    }
426
427
    #[test]
428
    fn print_literal() {
429
        roundtrip("a");
430
        roundtrip(r"\[");
431
        roundtrip_with(|b| b.octal(true), r"\141");
432
        roundtrip(r"\x61");
433
        roundtrip(r"\x7F");
434
        roundtrip(r"\u0061");
435
        roundtrip(r"\U00000061");
436
        roundtrip(r"\x{61}");
437
        roundtrip(r"\x{7F}");
438
        roundtrip(r"\u{61}");
439
        roundtrip(r"\U{61}");
440
441
        roundtrip(r"\a");
442
        roundtrip(r"\f");
443
        roundtrip(r"\t");
444
        roundtrip(r"\n");
445
        roundtrip(r"\r");
446
        roundtrip(r"\v");
447
        roundtrip(r"(?x)\ ");
448
    }
449
450
    #[test]
451
    fn print_dot() {
452
        roundtrip(".");
453
    }
454
455
    #[test]
456
    fn print_concat() {
457
        roundtrip("ab");
458
        roundtrip("abcde");
459
        roundtrip("a(bcd)ef");
460
    }
461
462
    #[test]
463
    fn print_alternation() {
464
        roundtrip("a|b");
465
        roundtrip("a|b|c|d|e");
466
        roundtrip("|a|b|c|d|e");
467
        roundtrip("|a|b|c|d|e|");
468
        roundtrip("a(b|c|d)|e|f");
469
    }
470
471
    #[test]
472
    fn print_assertion() {
473
        roundtrip(r"^");
474
        roundtrip(r"$");
475
        roundtrip(r"\A");
476
        roundtrip(r"\z");
477
        roundtrip(r"\b");
478
        roundtrip(r"\B");
479
    }
480
481
    #[test]
482
    fn print_repetition() {
483
        roundtrip("a?");
484
        roundtrip("a??");
485
        roundtrip("a*");
486
        roundtrip("a*?");
487
        roundtrip("a+");
488
        roundtrip("a+?");
489
        roundtrip("a{5}");
490
        roundtrip("a{5}?");
491
        roundtrip("a{5,}");
492
        roundtrip("a{5,}?");
493
        roundtrip("a{5,10}");
494
        roundtrip("a{5,10}?");
495
    }
496
497
    #[test]
498
    fn print_flags() {
499
        roundtrip("(?i)");
500
        roundtrip("(?-i)");
501
        roundtrip("(?s-i)");
502
        roundtrip("(?-si)");
503
        roundtrip("(?siUmux)");
504
    }
505
506
    #[test]
507
    fn print_group() {
508
        roundtrip("(?i:a)");
509
        roundtrip("(?P<foo>a)");
510
        roundtrip("(?<foo>a)");
511
        roundtrip("(a)");
512
    }
513
514
    #[test]
515
    fn print_class() {
516
        roundtrip(r"[abc]");
517
        roundtrip(r"[a-z]");
518
        roundtrip(r"[^a-z]");
519
        roundtrip(r"[a-z0-9]");
520
        roundtrip(r"[-a-z0-9]");
521
        roundtrip(r"[-a-z0-9]");
522
        roundtrip(r"[a-z0-9---]");
523
        roundtrip(r"[a-z&&m-n]");
524
        roundtrip(r"[[a-z&&m-n]]");
525
        roundtrip(r"[a-z--m-n]");
526
        roundtrip(r"[a-z~~m-n]");
527
        roundtrip(r"[a-z[0-9]]");
528
        roundtrip(r"[a-z[^0-9]]");
529
530
        roundtrip(r"\d");
531
        roundtrip(r"\D");
532
        roundtrip(r"\s");
533
        roundtrip(r"\S");
534
        roundtrip(r"\w");
535
        roundtrip(r"\W");
536
537
        roundtrip(r"[[:alnum:]]");
538
        roundtrip(r"[[:^alnum:]]");
539
        roundtrip(r"[[:alpha:]]");
540
        roundtrip(r"[[:^alpha:]]");
541
        roundtrip(r"[[:ascii:]]");
542
        roundtrip(r"[[:^ascii:]]");
543
        roundtrip(r"[[:blank:]]");
544
        roundtrip(r"[[:^blank:]]");
545
        roundtrip(r"[[:cntrl:]]");
546
        roundtrip(r"[[:^cntrl:]]");
547
        roundtrip(r"[[:digit:]]");
548
        roundtrip(r"[[:^digit:]]");
549
        roundtrip(r"[[:graph:]]");
550
        roundtrip(r"[[:^graph:]]");
551
        roundtrip(r"[[:lower:]]");
552
        roundtrip(r"[[:^lower:]]");
553
        roundtrip(r"[[:print:]]");
554
        roundtrip(r"[[:^print:]]");
555
        roundtrip(r"[[:punct:]]");
556
        roundtrip(r"[[:^punct:]]");
557
        roundtrip(r"[[:space:]]");
558
        roundtrip(r"[[:^space:]]");
559
        roundtrip(r"[[:upper:]]");
560
        roundtrip(r"[[:^upper:]]");
561
        roundtrip(r"[[:word:]]");
562
        roundtrip(r"[[:^word:]]");
563
        roundtrip(r"[[:xdigit:]]");
564
        roundtrip(r"[[:^xdigit:]]");
565
566
        roundtrip(r"\pL");
567
        roundtrip(r"\PL");
568
        roundtrip(r"\p{L}");
569
        roundtrip(r"\P{L}");
570
        roundtrip(r"\p{X=Y}");
571
        roundtrip(r"\P{X=Y}");
572
        roundtrip(r"\p{X:Y}");
573
        roundtrip(r"\P{X:Y}");
574
        roundtrip(r"\p{X!=Y}");
575
        roundtrip(r"\P{X!=Y}");
576
    }
577
}