]> git.lizzy.rs Git - rust.git/blob - src/libfmt_macros/lib.rs
rustfmt: libflate, libfmt_macros, libgetopts, libgraphviz, liblog, librand
[rust.git] / src / libfmt_macros / lib.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Macro support for format strings
12 //!
13 //! These structures are used when parsing format strings for the compiler.
14 //! Parsing does not happen at runtime: structures of `std::fmt::rt` are
15 //! generated instead.
16
17 // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
18 #![cfg_attr(stage0, feature(custom_attribute))]
19 #![crate_name = "fmt_macros"]
20 #![unstable(feature = "rustc_private", issue = "27812")]
21 #![staged_api]
22 #![crate_type = "rlib"]
23 #![crate_type = "dylib"]
24 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
25        html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
26        html_root_url = "https://doc.rust-lang.org/nightly/",
27        html_playground_url = "https://play.rust-lang.org/",
28        test(attr(deny(warnings))))]
29
30 #![feature(staged_api)]
31 #![feature(unicode)]
32
33 pub use self::Piece::*;
34 pub use self::Position::*;
35 pub use self::Alignment::*;
36 pub use self::Flag::*;
37 pub use self::Count::*;
38
39 use std::str;
40 use std::string;
41 use std::iter;
42
43 /// A piece is a portion of the format string which represents the next part
44 /// to emit. These are emitted as a stream by the `Parser` class.
45 #[derive(Copy, Clone, PartialEq)]
46 pub enum Piece<'a> {
47     /// A literal string which should directly be emitted
48     String(&'a str),
49     /// This describes that formatting should process the next argument (as
50     /// specified inside) for emission.
51     NextArgument(Argument<'a>),
52 }
53
54 /// Representation of an argument specification.
55 #[derive(Copy, Clone, PartialEq)]
56 pub struct Argument<'a> {
57     /// Where to find this argument
58     pub position: Position<'a>,
59     /// How to format the argument
60     pub format: FormatSpec<'a>,
61 }
62
63 /// Specification for the formatting of an argument in the format string.
64 #[derive(Copy, Clone, PartialEq)]
65 pub struct FormatSpec<'a> {
66     /// Optionally specified character to fill alignment with
67     pub fill: Option<char>,
68     /// Optionally specified alignment
69     pub align: Alignment,
70     /// Packed version of various flags provided
71     pub flags: u32,
72     /// The integer precision to use
73     pub precision: Count<'a>,
74     /// The string width requested for the resulting format
75     pub width: Count<'a>,
76     /// The descriptor string representing the name of the format desired for
77     /// this argument, this can be empty or any number of characters, although
78     /// it is required to be one word.
79     pub ty: &'a str,
80 }
81
82 /// Enum describing where an argument for a format can be located.
83 #[derive(Copy, Clone, PartialEq)]
84 pub enum Position<'a> {
85     /// The argument will be in the next position. This is the default.
86     ArgumentNext,
87     /// The argument is located at a specific index.
88     ArgumentIs(usize),
89     /// The argument has a name.
90     ArgumentNamed(&'a str),
91 }
92
93 /// Enum of alignments which are supported.
94 #[derive(Copy, Clone, PartialEq)]
95 pub enum Alignment {
96     /// The value will be aligned to the left.
97     AlignLeft,
98     /// The value will be aligned to the right.
99     AlignRight,
100     /// The value will be aligned in the center.
101     AlignCenter,
102     /// The value will take on a default alignment.
103     AlignUnknown,
104 }
105
106 /// Various flags which can be applied to format strings. The meaning of these
107 /// flags is defined by the formatters themselves.
108 #[derive(Copy, Clone, PartialEq)]
109 pub enum Flag {
110     /// A `+` will be used to denote positive numbers.
111     FlagSignPlus,
112     /// A `-` will be used to denote negative numbers. This is the default.
113     FlagSignMinus,
114     /// An alternate form will be used for the value. In the case of numbers,
115     /// this means that the number will be prefixed with the supplied string.
116     FlagAlternate,
117     /// For numbers, this means that the number will be padded with zeroes,
118     /// and the sign (`+` or `-`) will precede them.
119     FlagSignAwareZeroPad,
120 }
121
122 /// A count is used for the precision and width parameters of an integer, and
123 /// can reference either an argument or a literal integer.
124 #[derive(Copy, Clone, PartialEq)]
125 pub enum Count<'a> {
126     /// The count is specified explicitly.
127     CountIs(usize),
128     /// The count is specified by the argument with the given name.
129     CountIsName(&'a str),
130     /// The count is specified by the argument at the given index.
131     CountIsParam(usize),
132     /// The count is specified by the next parameter.
133     CountIsNextParam,
134     /// The count is implied and cannot be explicitly specified.
135     CountImplied,
136 }
137
138 /// The parser structure for interpreting the input format string. This is
139 /// modeled as an iterator over `Piece` structures to form a stream of tokens
140 /// being output.
141 ///
142 /// This is a recursive-descent parser for the sake of simplicity, and if
143 /// necessary there's probably lots of room for improvement performance-wise.
144 pub struct Parser<'a> {
145     input: &'a str,
146     cur: iter::Peekable<str::CharIndices<'a>>,
147     /// Error messages accumulated during parsing
148     pub errors: Vec<string::String>,
149 }
150
151 impl<'a> Iterator for Parser<'a> {
152     type Item = Piece<'a>;
153
154     fn next(&mut self) -> Option<Piece<'a>> {
155         if let Some(&(pos, c)) = self.cur.peek() {
156             match c {
157                 '{' => {
158                     self.cur.next();
159                     if self.consume('{') {
160                         Some(String(self.string(pos + 1)))
161                     } else {
162                         let ret = Some(NextArgument(self.argument()));
163                         self.must_consume('}');
164                         ret
165                     }
166                 }
167                 '}' => {
168                     self.cur.next();
169                     if self.consume('}') {
170                         Some(String(self.string(pos + 1)))
171                     } else {
172                         self.err("unmatched `}` found");
173                         None
174                     }
175                 }
176                 _ => Some(String(self.string(pos))),
177             }
178         } else {
179             None
180         }
181     }
182 }
183
184 impl<'a> Parser<'a> {
185     /// Creates a new parser for the given format string
186     pub fn new(s: &'a str) -> Parser<'a> {
187         Parser {
188             input: s,
189             cur: s.char_indices().peekable(),
190             errors: vec![],
191         }
192     }
193
194     /// Notifies of an error. The message doesn't actually need to be of type
195     /// String, but I think it does when this eventually uses conditions so it
196     /// might as well start using it now.
197     fn err(&mut self, msg: &str) {
198         self.errors.push(msg.to_owned());
199     }
200
201     /// Optionally consumes the specified character. If the character is not at
202     /// the current position, then the current iterator isn't moved and false is
203     /// returned, otherwise the character is consumed and true is returned.
204     fn consume(&mut self, c: char) -> bool {
205         if let Some(&(_, maybe)) = self.cur.peek() {
206             if c == maybe {
207                 self.cur.next();
208                 true
209             } else {
210                 false
211             }
212         } else {
213             false
214         }
215     }
216
217     /// Forces consumption of the specified character. If the character is not
218     /// found, an error is emitted.
219     fn must_consume(&mut self, c: char) {
220         self.ws();
221         if let Some(&(_, maybe)) = self.cur.peek() {
222             if c == maybe {
223                 self.cur.next();
224             } else {
225                 self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe));
226             }
227         } else {
228             self.err(&format!("expected `{:?}` but string was terminated", c));
229         }
230     }
231
232     /// Consumes all whitespace characters until the first non-whitespace
233     /// character
234     fn ws(&mut self) {
235         while let Some(&(_, c)) = self.cur.peek() {
236             if c.is_whitespace() {
237                 self.cur.next();
238             } else {
239                 break;
240             }
241         }
242     }
243
244     /// Parses all of a string which is to be considered a "raw literal" in a
245     /// format string. This is everything outside of the braces.
246     fn string(&mut self, start: usize) -> &'a str {
247         // we may not consume the character, peek the iterator
248         while let Some(&(pos, c)) = self.cur.peek() {
249             match c {
250                 '{' | '}' => {
251                     return &self.input[start..pos];
252                 }
253                 _ => {
254                     self.cur.next();
255                 }
256             }
257         }
258         &self.input[start..self.input.len()]
259     }
260
261     /// Parses an Argument structure, or what's contained within braces inside
262     /// the format string
263     fn argument(&mut self) -> Argument<'a> {
264         Argument {
265             position: self.position(),
266             format: self.format(),
267         }
268     }
269
270     /// Parses a positional argument for a format. This could either be an
271     /// integer index of an argument, a named argument, or a blank string.
272     fn position(&mut self) -> Position<'a> {
273         if let Some(i) = self.integer() {
274             ArgumentIs(i)
275         } else {
276             match self.cur.peek() {
277                 Some(&(_, c)) if c.is_alphabetic() => ArgumentNamed(self.word()),
278                 _ => ArgumentNext,
279             }
280         }
281     }
282
283     /// Parses a format specifier at the current position, returning all of the
284     /// relevant information in the FormatSpec struct.
285     fn format(&mut self) -> FormatSpec<'a> {
286         let mut spec = FormatSpec {
287             fill: None,
288             align: AlignUnknown,
289             flags: 0,
290             precision: CountImplied,
291             width: CountImplied,
292             ty: &self.input[..0],
293         };
294         if !self.consume(':') {
295             return spec;
296         }
297
298         // fill character
299         if let Some(&(_, c)) = self.cur.peek() {
300             match self.cur.clone().skip(1).next() {
301                 Some((_, '>')) | Some((_, '<')) | Some((_, '^')) => {
302                     spec.fill = Some(c);
303                     self.cur.next();
304                 }
305                 _ => {}
306             }
307         }
308         // Alignment
309         if self.consume('<') {
310             spec.align = AlignLeft;
311         } else if self.consume('>') {
312             spec.align = AlignRight;
313         } else if self.consume('^') {
314             spec.align = AlignCenter;
315         }
316         // Sign flags
317         if self.consume('+') {
318             spec.flags |= 1 << (FlagSignPlus as u32);
319         } else if self.consume('-') {
320             spec.flags |= 1 << (FlagSignMinus as u32);
321         }
322         // Alternate marker
323         if self.consume('#') {
324             spec.flags |= 1 << (FlagAlternate as u32);
325         }
326         // Width and precision
327         let mut havewidth = false;
328         if self.consume('0') {
329             // small ambiguity with '0$' as a format string. In theory this is a
330             // '0' flag and then an ill-formatted format string with just a '$'
331             // and no count, but this is better if we instead interpret this as
332             // no '0' flag and '0$' as the width instead.
333             if self.consume('$') {
334                 spec.width = CountIsParam(0);
335                 havewidth = true;
336             } else {
337                 spec.flags |= 1 << (FlagSignAwareZeroPad as u32);
338             }
339         }
340         if !havewidth {
341             spec.width = self.count();
342         }
343         if self.consume('.') {
344             if self.consume('*') {
345                 spec.precision = CountIsNextParam;
346             } else {
347                 spec.precision = self.count();
348             }
349         }
350         // Finally the actual format specifier
351         if self.consume('?') {
352             spec.ty = "?";
353         } else {
354             spec.ty = self.word();
355         }
356         spec
357     }
358
359     /// Parses a Count parameter at the current position. This does not check
360     /// for 'CountIsNextParam' because that is only used in precision, not
361     /// width.
362     fn count(&mut self) -> Count<'a> {
363         if let Some(i) = self.integer() {
364             if self.consume('$') {
365                 CountIsParam(i)
366             } else {
367                 CountIs(i)
368             }
369         } else {
370             let tmp = self.cur.clone();
371             let word = self.word();
372             if word.is_empty() {
373                 self.cur = tmp;
374                 CountImplied
375             } else {
376                 if self.consume('$') {
377                     CountIsName(word)
378                 } else {
379                     self.cur = tmp;
380                     CountImplied
381                 }
382             }
383         }
384     }
385
386     /// Parses a word starting at the current position. A word is considered to
387     /// be an alphabetic character followed by any number of alphanumeric
388     /// characters.
389     fn word(&mut self) -> &'a str {
390         let start = match self.cur.peek() {
391             Some(&(pos, c)) if c.is_xid_start() => {
392                 self.cur.next();
393                 pos
394             }
395             _ => {
396                 return &self.input[..0];
397             }
398         };
399         while let Some(&(pos, c)) = self.cur.peek() {
400             if c.is_xid_continue() {
401                 self.cur.next();
402             } else {
403                 return &self.input[start..pos];
404             }
405         }
406         &self.input[start..self.input.len()]
407     }
408
409     /// Optionally parses an integer at the current position. This doesn't deal
410     /// with overflow at all, it's just accumulating digits.
411     fn integer(&mut self) -> Option<usize> {
412         let mut cur = 0;
413         let mut found = false;
414         while let Some(&(_, c)) = self.cur.peek() {
415             if let Some(i) = c.to_digit(10) {
416                 cur = cur * 10 + i as usize;
417                 found = true;
418                 self.cur.next();
419             } else {
420                 break;
421             }
422         }
423         if found {
424             Some(cur)
425         } else {
426             None
427         }
428     }
429 }
430
431 #[cfg(test)]
432 mod tests {
433     use super::*;
434
435     fn same(fmt: &'static str, p: &[Piece<'static>]) {
436         let parser = Parser::new(fmt);
437         assert!(parser.collect::<Vec<Piece<'static>>>() == p);
438     }
439
440     fn fmtdflt() -> FormatSpec<'static> {
441         return FormatSpec {
442             fill: None,
443             align: AlignUnknown,
444             flags: 0,
445             precision: CountImplied,
446             width: CountImplied,
447             ty: "",
448         };
449     }
450
451     fn musterr(s: &str) {
452         let mut p = Parser::new(s);
453         p.next();
454         assert!(!p.errors.is_empty());
455     }
456
457     #[test]
458     fn simple() {
459         same("asdf", &[String("asdf")]);
460         same("a{{b", &[String("a"), String("{b")]);
461         same("a}}b", &[String("a"), String("}b")]);
462         same("a}}", &[String("a"), String("}")]);
463         same("}}", &[String("}")]);
464         same("\\}}", &[String("\\"), String("}")]);
465     }
466
467     #[test]
468     fn invalid01() {
469         musterr("{")
470     }
471     #[test]
472     fn invalid02() {
473         musterr("}")
474     }
475     #[test]
476     fn invalid04() {
477         musterr("{3a}")
478     }
479     #[test]
480     fn invalid05() {
481         musterr("{:|}")
482     }
483     #[test]
484     fn invalid06() {
485         musterr("{:>>>}")
486     }
487
488     #[test]
489     fn format_nothing() {
490         same("{}",
491              &[NextArgument(Argument {
492                    position: ArgumentNext,
493                    format: fmtdflt(),
494                })]);
495     }
496     #[test]
497     fn format_position() {
498         same("{3}",
499              &[NextArgument(Argument {
500                    position: ArgumentIs(3),
501                    format: fmtdflt(),
502                })]);
503     }
504     #[test]
505     fn format_position_nothing_else() {
506         same("{3:}",
507              &[NextArgument(Argument {
508                    position: ArgumentIs(3),
509                    format: fmtdflt(),
510                })]);
511     }
512     #[test]
513     fn format_type() {
514         same("{3:a}",
515              &[NextArgument(Argument {
516                    position: ArgumentIs(3),
517                    format: FormatSpec {
518                        fill: None,
519                        align: AlignUnknown,
520                        flags: 0,
521                        precision: CountImplied,
522                        width: CountImplied,
523                        ty: "a",
524                    },
525                })]);
526     }
527     #[test]
528     fn format_align_fill() {
529         same("{3:>}",
530              &[NextArgument(Argument {
531                    position: ArgumentIs(3),
532                    format: FormatSpec {
533                        fill: None,
534                        align: AlignRight,
535                        flags: 0,
536                        precision: CountImplied,
537                        width: CountImplied,
538                        ty: "",
539                    },
540                })]);
541         same("{3:0<}",
542              &[NextArgument(Argument {
543                    position: ArgumentIs(3),
544                    format: FormatSpec {
545                        fill: Some('0'),
546                        align: AlignLeft,
547                        flags: 0,
548                        precision: CountImplied,
549                        width: CountImplied,
550                        ty: "",
551                    },
552                })]);
553         same("{3:*<abcd}",
554              &[NextArgument(Argument {
555                    position: ArgumentIs(3),
556                    format: FormatSpec {
557                        fill: Some('*'),
558                        align: AlignLeft,
559                        flags: 0,
560                        precision: CountImplied,
561                        width: CountImplied,
562                        ty: "abcd",
563                    },
564                })]);
565     }
566     #[test]
567     fn format_counts() {
568         same("{:10s}",
569              &[NextArgument(Argument {
570                    position: ArgumentNext,
571                    format: FormatSpec {
572                        fill: None,
573                        align: AlignUnknown,
574                        flags: 0,
575                        precision: CountImplied,
576                        width: CountIs(10),
577                        ty: "s",
578                    },
579                })]);
580         same("{:10$.10s}",
581              &[NextArgument(Argument {
582                    position: ArgumentNext,
583                    format: FormatSpec {
584                        fill: None,
585                        align: AlignUnknown,
586                        flags: 0,
587                        precision: CountIs(10),
588                        width: CountIsParam(10),
589                        ty: "s",
590                    },
591                })]);
592         same("{:.*s}",
593              &[NextArgument(Argument {
594                    position: ArgumentNext,
595                    format: FormatSpec {
596                        fill: None,
597                        align: AlignUnknown,
598                        flags: 0,
599                        precision: CountIsNextParam,
600                        width: CountImplied,
601                        ty: "s",
602                    },
603                })]);
604         same("{:.10$s}",
605              &[NextArgument(Argument {
606                    position: ArgumentNext,
607                    format: FormatSpec {
608                        fill: None,
609                        align: AlignUnknown,
610                        flags: 0,
611                        precision: CountIsParam(10),
612                        width: CountImplied,
613                        ty: "s",
614                    },
615                })]);
616         same("{:a$.b$s}",
617              &[NextArgument(Argument {
618                    position: ArgumentNext,
619                    format: FormatSpec {
620                        fill: None,
621                        align: AlignUnknown,
622                        flags: 0,
623                        precision: CountIsName("b"),
624                        width: CountIsName("a"),
625                        ty: "s",
626                    },
627                })]);
628     }
629     #[test]
630     fn format_flags() {
631         same("{:-}",
632              &[NextArgument(Argument {
633                    position: ArgumentNext,
634                    format: FormatSpec {
635                        fill: None,
636                        align: AlignUnknown,
637                        flags: (1 << FlagSignMinus as u32),
638                        precision: CountImplied,
639                        width: CountImplied,
640                        ty: "",
641                    },
642                })]);
643         same("{:+#}",
644              &[NextArgument(Argument {
645                    position: ArgumentNext,
646                    format: FormatSpec {
647                        fill: None,
648                        align: AlignUnknown,
649                        flags: (1 << FlagSignPlus as u32) | (1 << FlagAlternate as u32),
650                        precision: CountImplied,
651                        width: CountImplied,
652                        ty: "",
653                    },
654                })]);
655     }
656     #[test]
657     fn format_mixture() {
658         same("abcd {3:a} efg",
659              &[String("abcd "),
660                NextArgument(Argument {
661                    position: ArgumentIs(3),
662                    format: FormatSpec {
663                        fill: None,
664                        align: AlignUnknown,
665                        flags: 0,
666                        precision: CountImplied,
667                        width: CountImplied,
668                        ty: "a",
669                    },
670                }),
671                String(" efg")]);
672     }
673 }