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