]>
git.proxmox.com Git - rustc.git/blob - 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.
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.
11 //! Macro support for format strings
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.
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
))))]
23 pub use self::Piece
::*;
24 pub use self::Position
::*;
25 pub use self::Alignment
::*;
26 pub use self::Flag
::*;
27 pub use self::Count
::*;
33 /// A piece is a portion of the format string which represents the next part
34 /// to emit. These are emitted as a stream by the `Parser` class.
35 #[derive(Copy, Clone, PartialEq)]
37 /// A literal string which should directly be emitted
39 /// This describes that formatting should process the next argument (as
40 /// specified inside) for emission.
41 NextArgument(Argument
<'a
>),
44 /// Representation of an argument specification.
45 #[derive(Copy, Clone, PartialEq)]
46 pub struct Argument
<'a
> {
47 /// Where to find this argument
48 pub position
: Position
<'a
>,
49 /// How to format the argument
50 pub format
: FormatSpec
<'a
>,
53 /// Specification for the formatting of an argument in the format string.
54 #[derive(Copy, Clone, PartialEq)]
55 pub struct FormatSpec
<'a
> {
56 /// Optionally specified character to fill alignment with
57 pub fill
: Option
<char>,
58 /// Optionally specified alignment
60 /// Packed version of various flags provided
62 /// The integer precision to use
63 pub precision
: Count
<'a
>,
64 /// The string width requested for the resulting format
66 /// The descriptor string representing the name of the format desired for
67 /// this argument, this can be empty or any number of characters, although
68 /// it is required to be one word.
72 /// Enum describing where an argument for a format can be located.
73 #[derive(Copy, Clone, PartialEq)]
74 pub enum Position
<'a
> {
75 /// The argument is implied to be located at an index
76 ArgumentImplicitlyIs(usize),
77 /// The argument is located at a specific index given in the format
79 /// The argument has a name.
80 ArgumentNamed(&'a
str),
83 /// Enum of alignments which are supported.
84 #[derive(Copy, Clone, PartialEq)]
86 /// The value will be aligned to the left.
88 /// The value will be aligned to the right.
90 /// The value will be aligned in the center.
92 /// The value will take on a default alignment.
96 /// Various flags which can be applied to format strings. The meaning of these
97 /// flags is defined by the formatters themselves.
98 #[derive(Copy, Clone, PartialEq)]
100 /// A `+` will be used to denote positive numbers.
102 /// A `-` will be used to denote negative numbers. This is the default.
104 /// An alternate form will be used for the value. In the case of numbers,
105 /// this means that the number will be prefixed with the supplied string.
107 /// For numbers, this means that the number will be padded with zeroes,
108 /// and the sign (`+` or `-`) will precede them.
109 FlagSignAwareZeroPad
,
110 /// For Debug / `?`, format integers in lower-case hexadecimal.
112 /// For Debug / `?`, format integers in upper-case hexadecimal.
116 /// A count is used for the precision and width parameters of an integer, and
117 /// can reference either an argument or a literal integer.
118 #[derive(Copy, Clone, PartialEq)]
120 /// The count is specified explicitly.
122 /// The count is specified by the argument with the given name.
123 CountIsName(&'a
str),
124 /// The count is specified by the argument at the given index.
126 /// The count is implied and cannot be explicitly specified.
130 pub struct ParseError
{
131 pub description
: string
::String
,
132 pub note
: Option
<string
::String
>,
133 pub label
: string
::String
,
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
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
> {
146 cur
: iter
::Peekable
<str::CharIndices
<'a
>>,
147 /// Error messages accumulated during parsing
148 pub errors
: Vec
<ParseError
>,
149 /// Current position of implicit positional argument pointer
153 impl<'a
> Iterator
for Parser
<'a
> {
154 type Item
= Piece
<'a
>;
156 fn next(&mut self) -> Option
<Piece
<'a
>> {
157 if let Some(&(pos
, c
)) = self.cur
.peek() {
161 if self.consume('
{'
) {
162 Some(String(self.string(pos
+ 1)))
164 let ret
= Some(NextArgument(self.argument()));
165 self.must_consume('
}'
);
172 if self.consume('
}'
) {
173 Some(String(self.string(pos
)))
176 "unmatched `}` found",
178 "if you intended to print `}`, you can escape it using `}}`",
185 _
=> Some(String(self.string(pos
))),
193 impl<'a
> Parser
<'a
> {
194 /// Creates a new parser for the given format string
195 pub fn new(s
: &'a
str) -> Parser
<'a
> {
198 cur
: s
.char_indices().peekable(),
204 /// Notifies of an error. The message doesn't actually need to be of type
205 /// String, but I think it does when this eventually uses conditions so it
206 /// might as well start using it now.
207 fn err
<S1
: Into
<string
::String
>, S2
: Into
<string
::String
>>(
214 self.errors
.push(ParseError
{
215 description
: description
.into(),
223 /// Notifies of an error. The message doesn't actually need to be of type
224 /// String, but I think it does when this eventually uses conditions so it
225 /// might as well start using it now.
226 fn err_with_note
<S1
: Into
<string
::String
>, S2
: Into
<string
::String
>, S3
: Into
<string
::String
>>(
234 self.errors
.push(ParseError
{
235 description
: description
.into(),
236 note
: Some(note
.into()),
243 /// Optionally consumes the specified character. If the character is not at
244 /// the current position, then the current iterator isn't moved and false is
245 /// returned, otherwise the character is consumed and true is returned.
246 fn consume(&mut self, c
: char) -> bool
{
247 if let Some(&(_
, maybe
)) = self.cur
.peek() {
259 /// Forces consumption of the specified character. If the character is not
260 /// found, an error is emitted.
261 fn must_consume(&mut self, c
: char) {
263 if let Some(&(pos
, maybe
)) = self.cur
.peek() {
267 self.err(format
!("expected `{:?}`, found `{:?}`", c
, maybe
),
268 format
!("expected `{}`", c
),
273 let msg
= format
!("expected `{:?}` but string was terminated", c
);
274 let pos
= self.input
.len() + 1; // point at closing `"`
276 self.err_with_note(msg
,
277 format
!("expected `{:?}`", c
),
278 "if you intended to print `{`, you can escape it using `{{`",
282 self.err(msg
, format
!("expected `{:?}`", c
), pos
, pos
);
287 /// Consumes all whitespace characters until the first non-whitespace
290 while let Some(&(_
, c
)) = self.cur
.peek() {
291 if c
.is_whitespace() {
299 /// Parses all of a string which is to be considered a "raw literal" in a
300 /// format string. This is everything outside of the braces.
301 fn string(&mut self, start
: usize) -> &'a
str {
302 // we may not consume the character, peek the iterator
303 while let Some(&(pos
, c
)) = self.cur
.peek() {
306 return &self.input
[start
..pos
];
313 &self.input
[start
..self.input
.len()]
316 /// Parses an Argument structure, or what's contained within braces inside
317 /// the format string
318 fn argument(&mut self) -> Argument
<'a
> {
319 let pos
= self.position();
320 let format
= self.format();
322 // Resolve position after parsing format spec.
323 let pos
= match pos
{
324 Some(position
) => position
,
328 ArgumentImplicitlyIs(i
)
338 /// Parses a positional argument for a format. This could either be an
339 /// integer index of an argument, a named argument, or a blank string.
340 /// Returns `Some(parsed_position)` if the position is not implicitly
341 /// consuming a macro argument, `None` if it's the case.
342 fn position(&mut self) -> Option
<Position
<'a
>> {
343 if let Some(i
) = self.integer() {
346 match self.cur
.peek() {
347 Some(&(_
, c
)) if c
.is_alphabetic() => Some(ArgumentNamed(self.word())),
348 Some(&(pos
, c
)) if c
== '_'
=> {
349 let invalid_name
= self.string(pos
);
350 self.err_with_note(format
!("invalid argument name `{}`", invalid_name
),
351 "invalid argument name",
352 "argument names cannot start with an underscore",
353 pos
+ 1, // add 1 to account for leading `{`
354 pos
+ 1 + invalid_name
.len());
355 Some(ArgumentNamed(invalid_name
))
358 // This is an `ArgumentNext`.
359 // Record the fact and do the resolution after parsing the
360 // format spec, to make things like `{:.*}` work.
366 /// Parses a format specifier at the current position, returning all of the
367 /// relevant information in the FormatSpec struct.
368 fn format(&mut self) -> FormatSpec
<'a
> {
369 let mut spec
= FormatSpec
{
373 precision
: CountImplied
,
375 ty
: &self.input
[..0],
377 if !self.consume('
:'
) {
382 if let Some(&(_
, c
)) = self.cur
.peek() {
383 match self.cur
.clone().skip(1).next() {
384 Some((_
, '
>'
)) | Some((_
, '
<'
)) | Some((_
, '
^')) => {
392 if self.consume('
<'
) {
393 spec
.align
= AlignLeft
;
394 } else if self.consume('
>'
) {
395 spec
.align
= AlignRight
;
396 } else if self.consume('
^') {
397 spec
.align
= AlignCenter
;
400 if self.consume('
+'
) {
401 spec
.flags
|= 1 << (FlagSignPlus
as u32);
402 } else if self.consume('
-'
) {
403 spec
.flags
|= 1 << (FlagSignMinus
as u32);
406 if self.consume('
#') {
407 spec
.flags
|= 1 << (FlagAlternate
as u32);
409 // Width and precision
410 let mut havewidth
= false;
411 if self.consume('
0'
) {
412 // small ambiguity with '0$' as a format string. In theory this is a
413 // '0' flag and then an ill-formatted format string with just a '$'
414 // and no count, but this is better if we instead interpret this as
415 // no '0' flag and '0$' as the width instead.
416 if self.consume('$'
) {
417 spec
.width
= CountIsParam(0);
420 spec
.flags
|= 1 << (FlagSignAwareZeroPad
as u32);
424 spec
.width
= self.count();
426 if self.consume('
.'
) {
427 if self.consume('
*'
) {
428 // Resolve `CountIsNextParam`.
429 // We can do this immediately as `position` is resolved later.
432 spec
.precision
= CountIsParam(i
);
434 spec
.precision
= self.count();
437 // Optional radix followed by the actual format specifier
438 if self.consume('x'
) {
439 if self.consume('?'
) {
440 spec
.flags
|= 1 << (FlagDebugLowerHex
as u32);
445 } else if self.consume('X'
) {
446 if self.consume('?'
) {
447 spec
.flags
|= 1 << (FlagDebugUpperHex
as u32);
452 } else if self.consume('?'
) {
455 spec
.ty
= self.word();
460 /// Parses a Count parameter at the current position. This does not check
461 /// for 'CountIsNextParam' because that is only used in precision, not
463 fn count(&mut self) -> Count
<'a
> {
464 if let Some(i
) = self.integer() {
465 if self.consume('$'
) {
471 let tmp
= self.cur
.clone();
472 let word
= self.word();
477 if self.consume('$'
) {
487 /// Parses a word starting at the current position. A word is considered to
488 /// be an alphabetic character followed by any number of alphanumeric
490 fn word(&mut self) -> &'a
str {
491 let start
= match self.cur
.peek() {
492 Some(&(pos
, c
)) if c
.is_xid_start() => {
497 return &self.input
[..0];
500 while let Some(&(pos
, c
)) = self.cur
.peek() {
501 if c
.is_xid_continue() {
504 return &self.input
[start
..pos
];
507 &self.input
[start
..self.input
.len()]
510 /// Optionally parses an integer at the current position. This doesn't deal
511 /// with overflow at all, it's just accumulating digits.
512 fn integer(&mut self) -> Option
<usize> {
514 let mut found
= false;
515 while let Some(&(_
, c
)) = self.cur
.peek() {
516 if let Some(i
) = c
.to_digit(10) {
517 cur
= cur
* 10 + i
as usize;
536 fn same(fmt
: &'
static str, p
: &[Piece
<'
static>]) {
537 let parser
= Parser
::new(fmt
);
538 assert
!(parser
.collect
::<Vec
<Piece
<'
static>>>() == p
);
541 fn fmtdflt() -> FormatSpec
<'
static> {
546 precision
: CountImplied
,
552 fn musterr(s
: &str) {
553 let mut p
= Parser
::new(s
);
555 assert
!(!p
.errors
.is_empty());
560 same("asdf", &[String("asdf")]);
561 same("a{{b", &[String("a"), String("{b")]);
562 same("a}}b", &[String("a"), String("}b")]);
563 same("a}}", &[String("a"), String("}")]);
564 same("}}", &[String("}")]);
565 same("\\}}", &[String("\\"), String("}")]);
590 fn format_nothing() {
592 &[NextArgument(Argument
{
593 position
: ArgumentImplicitlyIs(0),
598 fn format_position() {
600 &[NextArgument(Argument
{
601 position
: ArgumentIs(3),
606 fn format_position_nothing_else() {
608 &[NextArgument(Argument
{
609 position
: ArgumentIs(3),
616 &[NextArgument(Argument
{
617 position
: ArgumentIs(3),
622 precision
: CountImplied
,
629 fn format_align_fill() {
631 &[NextArgument(Argument
{
632 position
: ArgumentIs(3),
637 precision
: CountImplied
,
643 &[NextArgument(Argument
{
644 position
: ArgumentIs(3),
649 precision
: CountImplied
,
655 &[NextArgument(Argument
{
656 position
: ArgumentIs(3),
661 precision
: CountImplied
,
670 &[NextArgument(Argument
{
671 position
: ArgumentImplicitlyIs(0),
676 precision
: CountImplied
,
682 &[NextArgument(Argument
{
683 position
: ArgumentImplicitlyIs(0),
688 precision
: CountIs(10),
689 width
: CountIsParam(10),
694 &[NextArgument(Argument
{
695 position
: ArgumentImplicitlyIs(1),
700 precision
: CountIsParam(0),
706 &[NextArgument(Argument
{
707 position
: ArgumentImplicitlyIs(0),
712 precision
: CountIsParam(10),
718 &[NextArgument(Argument
{
719 position
: ArgumentImplicitlyIs(0),
724 precision
: CountIsName("b"),
725 width
: CountIsName("a"),
733 &[NextArgument(Argument
{
734 position
: ArgumentImplicitlyIs(0),
738 flags
: (1 << FlagSignMinus
as u32),
739 precision
: CountImplied
,
745 &[NextArgument(Argument
{
746 position
: ArgumentImplicitlyIs(0),
750 flags
: (1 << FlagSignPlus
as u32) | (1 << FlagAlternate
as u32),
751 precision
: CountImplied
,
758 fn format_mixture() {
759 same("abcd {3:a} efg",
761 NextArgument(Argument
{
762 position
: ArgumentIs(3),
767 precision
: CountImplied
,