Skip to main content

a9_prettyplease/
mac.rs

1use crate::algorithm::Printer;
2use crate::path::PathKind;
3use crate::token::Token;
4use crate::INDENT;
5use proc_macro2::{Delimiter, Spacing, TokenStream};
6use syn::{Ident, Macro, MacroDelimiter};
7
8impl Printer {
9    pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) {
10        if mac.path.is_ident("macro_rules") {
11            if let Some(ident) = ident {
12                self.macro_rules(ident, &mac.tokens);
13                return;
14            }
15        }
16        #[cfg(feature = "verbatim")]
17        if ident.is_none() && self.standard_library_macro(mac, semicolon) {
18            return;
19        }
20        self.path(&mac.path, PathKind::Simple);
21        self.word("!");
22        if let Some(ident) = ident {
23            self.nbsp();
24            self.ident(ident);
25        }
26        let (open, close, delimiter_break) = match mac.delimiter {
27            MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)),
28            MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)),
29            MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)),
30        };
31        self.word(open);
32        if !mac.tokens.is_empty() {
33            self.cbox(INDENT);
34            delimiter_break(self);
35            self.ibox(0);
36            self.macro_rules_tokens(mac.tokens.clone(), false);
37            self.end();
38            delimiter_break(self);
39            self.offset(-INDENT);
40            self.end();
41        }
42        self.word(close);
43        if semicolon {
44            self.word(";");
45        }
46    }
47
48    fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) {
49        enum State {
50            Start,
51            Matcher,
52            Equal,
53            Greater,
54            Expander,
55        }
56
57        use State::*;
58
59        self.word("macro_rules! ");
60        self.ident(name);
61        self.word(" {");
62        self.cbox(INDENT);
63        self.hardbreak_if_nonempty();
64        let mut state = State::Start;
65        for tt in rules.clone() {
66            let token = Token::from(tt);
67            match (state, token) {
68                (Start, Token::Group(delimiter, stream)) => {
69                    self.delimiter_open(delimiter);
70                    if !stream.is_empty() {
71                        self.cbox(INDENT);
72                        self.zerobreak();
73                        self.ibox(0);
74                        self.macro_rules_tokens(stream, true);
75                        self.end();
76                        self.zerobreak();
77                        self.offset(-INDENT);
78                        self.end();
79                    }
80                    self.delimiter_close(delimiter);
81                    state = Matcher;
82                }
83                (Matcher, Token::Punct('=', Spacing::Joint)) => {
84                    self.word(" =");
85                    state = Equal;
86                }
87                (Equal, Token::Punct('>', Spacing::Alone)) => {
88                    self.word(">");
89                    state = Greater;
90                }
91                (Greater, Token::Group(_delimiter, stream)) => {
92                    self.word(" {");
93                    self.neverbreak();
94                    if !stream.is_empty() {
95                        self.cbox(INDENT);
96                        self.hardbreak();
97                        self.ibox(0);
98                        self.macro_rules_tokens(stream, false);
99                        self.end();
100                        self.hardbreak();
101                        self.offset(-INDENT);
102                        self.end();
103                    }
104                    self.word("}");
105                    state = Expander;
106                }
107                (Expander, Token::Punct(';', Spacing::Alone)) => {
108                    self.word(";");
109                    self.hardbreak();
110                    state = Start;
111                }
112                _ => {
    ::core::panicking::panic_fmt(format_args!("not implemented: {0}",
            format_args!("bad macro_rules syntax")));
}unimplemented!("bad macro_rules syntax"),
113            }
114        }
115        match state {
116            Start => {}
117            Expander => {
118                self.word(";");
119                self.hardbreak();
120            }
121            _ => self.hardbreak(),
122        }
123        self.offset(-INDENT);
124        self.end();
125        self.word("}");
126    }
127
128    pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) {
129        #[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for State {
    #[inline]
    fn eq(&self, other: &State) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
130        enum State {
131            Start,
132            Dollar,
133            DollarCrate,
134            DollarIdent,
135            DollarIdentColon,
136            DollarParen,
137            DollarParenSep,
138            Pound,
139            PoundBang,
140            Dot,
141            Colon,
142            Colon2,
143            Ident,
144            IdentBang,
145            Delim,
146            Other,
147        }
148
149        use State::*;
150
151        let mut state = Start;
152        let mut previous_is_joint = true;
153        for tt in stream {
154            let token = Token::from(tt);
155            let (needs_space, next_state) = match (&state, &token) {
156                (Dollar, Token::Ident(_)) if matcher => (false, DollarIdent),
157                (Dollar, Token::Ident(ident)) if ident == "crate" => (false, DollarCrate),
158                (Dollar, Token::Ident(_)) => (false, Other),
159                (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon),
160                (DollarIdentColon, Token::Ident(_)) => (false, Other),
161                (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other),
162                (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep),
163                (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen),
164                (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep),
165                (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other),
166                (Pound, Token::Punct('!', _)) => (false, PoundBang),
167                (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen),
168                (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other),
169                (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
170                    (false, Delim)
171                }
172                (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang),
173                (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
174                    (false, Other)
175                }
176                (Colon, Token::Punct(':', _)) => (false, Colon2),
177                (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim),
178                (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other),
179                (_, Token::Ident(ident)) if !is_keyword(ident) => {
180                    (state != Dot && state != Colon2, Ident)
181                }
182                (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other),
183                (_, Token::Literal(_)) => (state != Dot, Ident),
184                (_, Token::Punct(',' | ';', _)) => (false, Other),
185                (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
186                (_, Token::Punct(':', Spacing::Joint)) => {
187                    (state != Ident && state != DollarCrate, Colon)
188                }
189                (_, Token::Punct('$', _)) => (true, Dollar),
190                (_, Token::Punct('#', _)) => (true, Pound),
191                (_, _) => (true, Other),
192            };
193            if !previous_is_joint {
194                if needs_space {
195                    self.space();
196                } else if let Token::Punct('.', _) = token {
197                    self.zerobreak();
198                }
199            }
200            previous_is_joint = match token {
201                Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true,
202                _ => false,
203            };
204            self.single_token(
205                token,
206                if matcher {
207                    |printer, stream| printer.macro_rules_tokens(stream, true)
208                } else {
209                    |printer, stream| printer.macro_rules_tokens(stream, false)
210                },
211            );
212            state = next_state;
213        }
214    }
215}
216
217pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool {
218    match delimiter {
219        MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true,
220        MacroDelimiter::Brace(_) => false,
221    }
222}
223
224fn is_keyword(ident: &Ident) -> bool {
225    match ident.to_string().as_str() {
226        "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
227        | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop"
228        | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static"
229        | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true,
230        _ => false,
231    }
232}
233
234#[cfg(feature = "verbatim")]
235mod standard_library {
236    use crate::algorithm::Printer;
237    use crate::expr;
238    use crate::fixup::FixupContext;
239    use crate::iter::IterDelimited;
240    use crate::path::PathKind;
241    use crate::INDENT;
242    use syn::ext::IdentExt;
243    use syn::parse::{Parse, ParseStream, Parser, Result};
244    use syn::punctuated::Punctuated;
245    use syn::{
246        parenthesized, token, Attribute, Expr, ExprAssign, ExprPath, Ident, Lit, Macro, Pat, Path,
247        Token, Type, Visibility,
248    };
249
250    enum KnownMacro {
251        Expr(Expr),
252        Exprs(Vec<Expr>),
253        Cfg(Cfg),
254        Matches(Matches),
255        ThreadLocal(Vec<ThreadLocal>),
256        VecArray(Punctuated<Expr, Token![,]>),
257        VecRepeat { elem: Expr, n: Expr },
258    }
259
260    enum Cfg {
261        Eq(Ident, Option<Lit>),
262        Call(Ident, Vec<Cfg>),
263    }
264
265    struct Matches {
266        expression: Expr,
267        pattern: Pat,
268        guard: Option<Expr>,
269    }
270
271    struct ThreadLocal {
272        attrs: Vec<Attribute>,
273        vis: Visibility,
274        name: Ident,
275        ty: Type,
276        init: Expr,
277    }
278
279    struct FormatArgs {
280        format_string: Expr,
281        args: Vec<Expr>,
282    }
283
284    impl Parse for FormatArgs {
285        fn parse(input: ParseStream) -> Result<Self> {
286            let format_string: Expr = input.parse()?;
287
288            let mut args = Vec::new();
289            while !input.is_empty() {
290                input.parse::<Token![,]>()?;
291                if input.is_empty() {
292                    break;
293                }
294                let arg = if input.peek(Ident::peek_any)
295                    && input.peek2(Token![=])
296                    && !input.peek2(Token![==])
297                {
298                    let key = input.call(Ident::parse_any)?;
299                    let eq_token: Token![=] = input.parse()?;
300                    let value: Expr = input.parse()?;
301                    Expr::Assign(ExprAssign {
302                        attrs: Vec::new(),
303                        left: Box::new(Expr::Path(ExprPath {
304                            attrs: Vec::new(),
305                            qself: None,
306                            path: Path::from(key),
307                        })),
308                        eq_token,
309                        right: Box::new(value),
310                    })
311                } else {
312                    input.parse()?
313                };
314                args.push(arg);
315            }
316
317            Ok(FormatArgs {
318                format_string,
319                args,
320            })
321        }
322    }
323
324    impl KnownMacro {
325        fn parse_expr(input: ParseStream) -> Result<Self> {
326            let expr: Expr = input.parse()?;
327            Ok(KnownMacro::Expr(expr))
328        }
329
330        fn parse_expr_comma(input: ParseStream) -> Result<Self> {
331            let expr: Expr = input.parse()?;
332            input.parse::<Option<Token![,]>>()?;
333            Ok(KnownMacro::Exprs(vec![expr]))
334        }
335
336        fn parse_exprs(input: ParseStream) -> Result<Self> {
337            let exprs = input.parse_terminated(Expr::parse, Token![,])?;
338            Ok(KnownMacro::Exprs(Vec::from_iter(exprs)))
339        }
340
341        fn parse_assert(input: ParseStream) -> Result<Self> {
342            let mut exprs = Vec::new();
343            let cond: Expr = input.parse()?;
344            exprs.push(cond);
345            if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
346                let format_args: FormatArgs = input.parse()?;
347                exprs.push(format_args.format_string);
348                exprs.extend(format_args.args);
349            }
350            Ok(KnownMacro::Exprs(exprs))
351        }
352
353        fn parse_assert_cmp(input: ParseStream) -> Result<Self> {
354            let mut exprs = Vec::new();
355            let left: Expr = input.parse()?;
356            exprs.push(left);
357            input.parse::<Token![,]>()?;
358            let right: Expr = input.parse()?;
359            exprs.push(right);
360            if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
361                let format_args: FormatArgs = input.parse()?;
362                exprs.push(format_args.format_string);
363                exprs.extend(format_args.args);
364            }
365            Ok(KnownMacro::Exprs(exprs))
366        }
367
368        fn parse_cfg(input: ParseStream) -> Result<Self> {
369            fn parse_single(input: ParseStream) -> Result<Cfg> {
370                let ident: Ident = input.parse()?;
371                if input.peek(token::Paren) && (ident == "all" || ident == "any") {
372                    let content;
373                    parenthesized!(content in input);
374                    let list = content.call(parse_multiple)?;
375                    Ok(Cfg::Call(ident, list))
376                } else if input.peek(token::Paren) && ident == "not" {
377                    let content;
378                    parenthesized!(content in input);
379                    let cfg = content.call(parse_single)?;
380                    content.parse::<Option<Token![,]>>()?;
381                    Ok(Cfg::Call(ident, vec![cfg]))
382                } else if input.peek(Token![=]) {
383                    input.parse::<Token![=]>()?;
384                    let string: Lit = input.parse()?;
385                    Ok(Cfg::Eq(ident, Some(string)))
386                } else {
387                    Ok(Cfg::Eq(ident, None))
388                }
389            }
390
391            fn parse_multiple(input: ParseStream) -> Result<Vec<Cfg>> {
392                let mut vec = Vec::new();
393                while !input.is_empty() {
394                    let cfg = input.call(parse_single)?;
395                    vec.push(cfg);
396                    if input.is_empty() {
397                        break;
398                    }
399                    input.parse::<Token![,]>()?;
400                }
401                Ok(vec)
402            }
403
404            let cfg = input.call(parse_single)?;
405            input.parse::<Option<Token![,]>>()?;
406            Ok(KnownMacro::Cfg(cfg))
407        }
408
409        fn parse_env(input: ParseStream) -> Result<Self> {
410            let mut exprs = Vec::new();
411            let name: Expr = input.parse()?;
412            exprs.push(name);
413            if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
414                let error_msg: Expr = input.parse()?;
415                exprs.push(error_msg);
416                input.parse::<Option<Token![,]>>()?;
417            }
418            Ok(KnownMacro::Exprs(exprs))
419        }
420
421        fn parse_format_args(input: ParseStream) -> Result<Self> {
422            let format_args: FormatArgs = input.parse()?;
423            let mut exprs = format_args.args;
424            exprs.insert(0, format_args.format_string);
425            Ok(KnownMacro::Exprs(exprs))
426        }
427
428        fn parse_matches(input: ParseStream) -> Result<Self> {
429            let expression: Expr = input.parse()?;
430            input.parse::<Token![,]>()?;
431            let pattern = input.call(Pat::parse_multi_with_leading_vert)?;
432            let guard = if input.parse::<Option<Token![if]>>()?.is_some() {
433                Some(input.parse()?)
434            } else {
435                None
436            };
437            input.parse::<Option<Token![,]>>()?;
438            Ok(KnownMacro::Matches(Matches {
439                expression,
440                pattern,
441                guard,
442            }))
443        }
444
445        fn parse_thread_local(input: ParseStream) -> Result<Self> {
446            let mut items = Vec::new();
447            while !input.is_empty() {
448                let attrs = input.call(Attribute::parse_outer)?;
449                let vis: Visibility = input.parse()?;
450                input.parse::<Token![static]>()?;
451                let name: Ident = input.parse()?;
452                input.parse::<Token![:]>()?;
453                let ty: Type = input.parse()?;
454                input.parse::<Token![=]>()?;
455                let init: Expr = input.parse()?;
456                if input.is_empty() {
457                    break;
458                }
459                input.parse::<Token![;]>()?;
460                items.push(ThreadLocal {
461                    attrs,
462                    vis,
463                    name,
464                    ty,
465                    init,
466                });
467            }
468            Ok(KnownMacro::ThreadLocal(items))
469        }
470
471        fn parse_vec(input: ParseStream) -> Result<Self> {
472            if input.is_empty() {
473                return Ok(KnownMacro::VecArray(Punctuated::new()));
474            }
475            let first: Expr = input.parse()?;
476            if input.parse::<Option<Token![;]>>()?.is_some() {
477                let len: Expr = input.parse()?;
478                Ok(KnownMacro::VecRepeat {
479                    elem: first,
480                    n: len,
481                })
482            } else {
483                let mut vec = Punctuated::new();
484                vec.push_value(first);
485                while !input.is_empty() {
486                    let comma: Token![,] = input.parse()?;
487                    vec.push_punct(comma);
488                    if input.is_empty() {
489                        break;
490                    }
491                    let next: Expr = input.parse()?;
492                    vec.push_value(next);
493                }
494                Ok(KnownMacro::VecArray(vec))
495            }
496        }
497
498        fn parse_write(input: ParseStream) -> Result<Self> {
499            let mut exprs = Vec::new();
500            let dst: Expr = input.parse()?;
501            exprs.push(dst);
502            input.parse::<Token![,]>()?;
503            let format_args: FormatArgs = input.parse()?;
504            exprs.push(format_args.format_string);
505            exprs.extend(format_args.args);
506            Ok(KnownMacro::Exprs(exprs))
507        }
508
509        fn parse_writeln(input: ParseStream) -> Result<Self> {
510            let mut exprs = Vec::new();
511            let dst: Expr = input.parse()?;
512            exprs.push(dst);
513            if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
514                let format_args: FormatArgs = input.parse()?;
515                exprs.push(format_args.format_string);
516                exprs.extend(format_args.args);
517            }
518            Ok(KnownMacro::Exprs(exprs))
519        }
520    }
521
522    impl Printer {
523        pub fn standard_library_macro(&mut self, mac: &Macro, mut semicolon: bool) -> bool {
524            let name = mac.path.segments.last().unwrap().ident.to_string();
525            let parser = match name.as_str() {
526                "addr_of" | "addr_of_mut" => KnownMacro::parse_expr,
527                "assert" | "debug_assert" => KnownMacro::parse_assert,
528                "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => {
529                    KnownMacro::parse_assert_cmp
530                }
531                "cfg" => KnownMacro::parse_cfg,
532                "compile_error" | "include" | "include_bytes" | "include_str" | "option_env" => {
533                    KnownMacro::parse_expr_comma
534                }
535                "concat" | "concat_bytes" | "dbg" => KnownMacro::parse_exprs,
536                "const_format_args" | "eprint" | "eprintln" | "format" | "format_args"
537                | "format_args_nl" | "panic" | "print" | "println" | "todo" | "unimplemented"
538                | "unreachable" => KnownMacro::parse_format_args,
539                "env" => KnownMacro::parse_env,
540                "matches" => KnownMacro::parse_matches,
541                "thread_local" => KnownMacro::parse_thread_local,
542                "vec" => KnownMacro::parse_vec,
543                "write" => KnownMacro::parse_write,
544                "writeln" => KnownMacro::parse_writeln,
545                _ => return false,
546            };
547
548            let Ok(known_macro) = parser.parse2(mac.tokens.clone()) else {
549                return false;
550            };
551
552            self.path(&mac.path, PathKind::Simple);
553            self.word("!");
554
555            match &known_macro {
556                KnownMacro::Expr(expr) => {
557                    self.word("(");
558                    self.cbox(INDENT);
559                    self.zerobreak();
560                    self.expr(expr, FixupContext::NONE);
561                    self.zerobreak();
562                    self.offset(-INDENT);
563                    self.end();
564                    self.word(")");
565                }
566                KnownMacro::Exprs(exprs) => {
567                    self.word("(");
568                    self.cbox(INDENT);
569                    self.zerobreak();
570                    for elem in exprs.iter().delimited() {
571                        self.expr(&elem, FixupContext::NONE);
572                        self.trailing_comma(elem.is_last);
573                    }
574                    self.offset(-INDENT);
575                    self.end();
576                    self.word(")");
577                }
578                KnownMacro::Cfg(cfg) => {
579                    self.word("(");
580                    self.cfg(cfg);
581                    self.word(")");
582                }
583                KnownMacro::Matches(matches) => {
584                    self.word("(");
585                    self.cbox(INDENT);
586                    self.zerobreak();
587                    self.expr(&matches.expression, FixupContext::NONE);
588                    self.word(",");
589                    self.space();
590                    self.pat(&matches.pattern);
591                    if let Some(guard) = &matches.guard {
592                        self.space();
593                        self.word("if ");
594                        self.expr(guard, FixupContext::NONE);
595                    }
596                    self.zerobreak();
597                    self.offset(-INDENT);
598                    self.end();
599                    self.word(")");
600                }
601                KnownMacro::ThreadLocal(items) => {
602                    self.word(" {");
603                    self.cbox(INDENT);
604                    self.hardbreak_if_nonempty();
605                    for item in items {
606                        self.outer_attrs(&item.attrs);
607                        self.cbox(0);
608                        self.visibility(&item.vis);
609                        self.word("static ");
610                        self.ident(&item.name);
611                        self.word(": ");
612                        self.ty(&item.ty);
613                        self.word(" = ");
614                        self.neverbreak();
615                        self.expr(&item.init, FixupContext::NONE);
616                        self.word(";");
617                        self.end();
618                        self.hardbreak();
619                    }
620                    self.offset(-INDENT);
621                    self.end();
622                    self.word("}");
623                    semicolon = false;
624                }
625                KnownMacro::VecArray(vec) => {
626                    if vec.is_empty() {
627                        self.word("[]");
628                    } else if expr::simple_array(vec) {
629                        self.cbox(INDENT);
630                        self.word("[");
631                        self.zerobreak();
632                        self.ibox(0);
633                        for elem in vec.iter().delimited() {
634                            self.expr(&elem, FixupContext::NONE);
635                            if !elem.is_last {
636                                self.word(",");
637                                self.space();
638                            }
639                        }
640                        self.end();
641                        self.trailing_comma(true);
642                        self.offset(-INDENT);
643                        self.word("]");
644                        self.end();
645                    } else {
646                        self.word("[");
647                        self.cbox(INDENT);
648                        self.zerobreak();
649                        for elem in vec.iter().delimited() {
650                            self.expr(&elem, FixupContext::NONE);
651                            self.trailing_comma(elem.is_last);
652                        }
653                        self.offset(-INDENT);
654                        self.end();
655                        self.word("]");
656                    }
657                }
658                KnownMacro::VecRepeat { elem, n } => {
659                    self.word("[");
660                    self.cbox(INDENT);
661                    self.zerobreak();
662                    self.expr(elem, FixupContext::NONE);
663                    self.word(";");
664                    self.space();
665                    self.expr(n, FixupContext::NONE);
666                    self.zerobreak();
667                    self.offset(-INDENT);
668                    self.end();
669                    self.word("]");
670                }
671            }
672
673            if semicolon {
674                self.word(";");
675            }
676
677            true
678        }
679
680        fn cfg(&mut self, cfg: &Cfg) {
681            match cfg {
682                Cfg::Eq(ident, value) => {
683                    self.ident(ident);
684                    if let Some(value) = value {
685                        self.word(" = ");
686                        self.lit(value);
687                    }
688                }
689                Cfg::Call(ident, args) => {
690                    self.ident(ident);
691                    self.word("(");
692                    self.cbox(INDENT);
693                    self.zerobreak();
694                    for arg in args.iter().delimited() {
695                        self.cfg(&arg);
696                        self.trailing_comma(arg.is_last);
697                    }
698                    self.offset(-INDENT);
699                    self.end();
700                    self.word(")");
701                }
702            }
703        }
704    }
705}