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