Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  parsing.rs

  Sprache: Rust
 

//! Parsing of [Rust `fmt` syntax][0].
//!
//! [0]: std::fmt#syntax

use std::{convert::identity, iter};

use unicode_xid::UnicodeXID as XID;

/// Output of the [`format_string`] parser.
#[derive(Debug, Clone, Eq, PartialEq)]
pub(cratestruct FormatString<'a> {
    pub(crate) formats: Vec<Format<'a>>,
}

/// Output of the [`format`] parser.
///
/// [`format`]: fn@format
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(cratestruct Format<'a> {
    pub(crate) arg: Option<Argument<'a>>,
    pub(crate) spec: Option<FormatSpec<'a>>,
}

/// Output of the [`format_spec`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(cratestruct FormatSpec<'a> {
    pub(crate) width: Option<Width<'a>>,
    pub(crate) precision: Option<Precision<'a>>,
    pub(crate) ty: Type,
}

/// Output of the [`argument`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crateenum Argument<'a> {
    Integer(usize),
    Identifier(&'a str),
}

/// Output of the [`precision`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crateenum Precision<'a> {
    Count(Count<'a>),
    Star,
}

/// Output of the [`count`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crateenum Count<'a> {
    Integer(usize),
    Parameter(Parameter<'a>),
}

/// Output of the [`type_`] parser. See [formatting traits][1] for more info.
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#formatting-traits
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crateenum Type {
    Display,
    Debug,
    LowerDebug,
    UpperDebug,
    Octal,
    LowerHex,
    UpperHex,
    Pointer,
    Binary,
    LowerExp,
    UpperExp,
}

impl Type {
    /// Returns trait name of this [`Type`].
    pub(cratefn trait_name(&self) -> &'static str {
        match self {
            Type::Display => "Display",
            Type::Debug | Type::LowerDebug | Type::UpperDebug => "Debug",
            Type::Octal => "Octal",
            Type::LowerHex => "LowerHex",
            Type::UpperHex => "UpperHex",
            Type::Pointer => "Pointer",
            Type::Binary => "Binary",
            Type::LowerExp => "LowerExp",
            Type::UpperExp => "UpperExp",
        }
    }
}

/// Type alias for the [`FormatSpec::width`].
type Width<'a> = Count<'a>;

/// Output of the [`maybe_format`] parser.
type MaybeFormat<'a> = Option<Format<'a>>;

/// Output of the [`identifier`] parser.
type Identifier<'a> = &'a str;

/// Output of the [`parameter`] parser.
type Parameter<'a> = Argument<'a>;

/// [`str`] left to parse.
///
/// [`str`]: prim@str
type LeftToParse<'a> = &'a str;

/// Parses a `format_string` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format_string`]` := `[`text`]` [`[`maybe_format text`]`] *`
///
/// # Example
///
/// ```text
/// Hello
/// Hello, {}!
/// {:?}
/// Hello {people}!
/// {} {}
/// {:04}
/// {par:-^#.0$?}
/// ```
///
/// # Return value
///
/// - [`Some`] in case of successful parse.
/// - [`None`] otherwise (not all characters are consumed by underlying
///   parsers).
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
pub(cratefn format_string(input: &str) -> Option<FormatString<'_>> {
    let (mut input, _) = optional_result(text)(input);

    let formats = iter::repeat(())
        .scan(&mut input, |input, _| {
            let (curr, format) =
                alt(&mut [&mut maybe_format, &='color:red'>mut map(text, |(i, _)| (i, None))])(
                    input,
                )?;
            **input = curr;
            Some(format)
        })
        .flatten()
        .collect();

    // Should consume all tokens for a successful parse.
    input.is_empty().then_some(FormatString { formats })
}

/// Parses a `maybe_format` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`maybe_format`]` := '{' '{' | '}' '}' | `[`format`]
///
/// # Example
///
/// ```text
/// {{
/// }}
/// {:04}
/// {:#?}
/// {par:-^#.0$?}
/// ```
///
/// [`format`]: fn@format
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn maybe_format(input: &str) -> Option<(LeftToParse<'_>, MaybeFormat<'_>)> {
    alt(&mut [
        &mut map(str("{{"), |i| (i, None)),
        &mut map(str("}}"), |i| (i, None)),
        &mut map(format, |(i, format)| (i, Some(format))),
    ])(input)
}

/// Parses a `format` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format`]` := '{' [`[`argument`]`] [':' `[`format_spec`]`] '}'`
///
/// # Example
///
/// ```text
/// {par}
/// {:#?}
/// {par:-^#.0$?}
/// ```
///
/// [`format`]: fn@format
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn format(input: &str) -> Option<(LeftToParse<'_>, Format<'_>)> {
    let input = char('{')(input)?;

    let (input, arg) = optional_result(argument)(input);

    let (input, spec) = map_or_else(
        char(':'),
        |i| Some((i, None)),
        map(format_spec, |(i, s)| (i, Some(s))),
    )(input)?;

    let input = char('}')(input)?;

    Some((input, Format { arg, spec }))
}

/// Parses an `argument` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`argument`]` := `[`integer`]` | `[`identifier`]
///
/// # Example
///
/// ```text
/// 0
/// ident
/// Минск
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn argument(input: &str) -> Option<(LeftToParse<'_>, Argument)> {
    alt(&mut [
        &mut map(identifier, |(i, ident)| (i, Argument::Identifier(ident))),
        &mut map(integer, |(i, int)| (i, Argument::Integer(int))),
    ])(input)
}

/// Parses a `format_spec` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format_spec`]` := [[fill]align][sign]['#']['0'][width]`
///                     `['.' `[`precision`]`]`[`type`]
///
/// # Example
///
/// ```text
/// ^
/// <^
/// ->+#0width$.precision$x?
/// ```
///
/// [`type`]: type_
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn format_spec(input: &str) -> Option<(LeftToParse<'_>, FormatSpec<'_>)> {
    let input = unwrap_or_else(
        alt(&mut [
            &mut try_seq(&mut [&mut any_char, &mut one_of("<^>")]),
            &mut one_of("<^>"),
        ]),
        identity,
    )(input);

    let input = seq(&mut [
        &mut optional(one_of("+-")),
        &mut optional(char('#')),
        &mut optional(try_seq(&mut [
            &mut char('0'),
            &mut lookahead(check_char(|c| !matches!(c, '$'))),
        ])),
    ])(input);

    let (input, width) = optional_result(count)(input);

    let (input, precision) = map_or_else(
        char('.'),
        |i| Some((i, None)),
        map(precision, |(i, p)| (i, Some(p))),
    )(input)?;

    let (input, ty) = type_(input)?;

    Some((
        input,
        FormatSpec {
            width,
            precision,
            ty,
        },
    ))
}

/// Parses a `precision` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`precision`]` := `[`count`]` | '*'`
///
/// # Example
///
/// ```text
/// 0
/// 42$
/// par$
/// *
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn precision(input: &str) -> Option<(LeftToParse<'_>, Precision<'_>)> {
    alt(&mut [
        &mut map(count, |(i, c)| (i, Precision::Count(c))),
        &mut map(char('*'), |i| (i, Precision::Star)),
    ])(input)
}

/// Parses a `type` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`type`]` := '' | '?' | 'x?' | 'X?' | identifier`
///
/// # Example
///
/// All possible [`Type`]s.
///
/// ```text
/// ?
/// x?
/// X?
/// o
/// x
/// X
/// p
/// b
/// e
/// E
/// ```
///
/// [`type`]: type_
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn type_(input: &str) -> Option<(&str, Type)> {
    alt(&mut [
        &mut map(str("x?"), |i| (i, Type::LowerDebug)),
        &mut map(str("X?"), |i| (i, Type::UpperDebug)),
        &mut map(char('?'), |i| (i, Type::Debug)),
        &mut map(char('o'), |i| (i, Type::Octal)),
        &mut map(char('x'), |i| (i, Type::LowerHex)),
        &mut map(char('X'), |i| (i, Type::UpperHex)),
        &mut map(char('p'), |i| (i, Type::Pointer)),
        &mut map(char('b'), |i| (i, Type::Binary)),
        &mut map(char('e'), |i| (i, Type::LowerExp)),
        &mut map(char('E'), |i| (i, Type::UpperExp)),
        &mut map(lookahead(char('}')), |i| (i, Type::Display)),
    ])(input)
}

/// Parses a `count` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`count`]` := `[`parameter`]` | `[`integer`]
///
/// # Example
///
/// ```text
/// 0
/// 42$
/// par$
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn count(input: &str) -> Option<(LeftToParse<'_>, Count<'_>)> {
    alt(&mut [
        &mut map(parameter, |(i, p)| (i, Count::Parameter(p))),
        &mut map(integer, |(i, int)| (i, Count::Integer(int))),
    ])(input)
}

/// Parses a `parameter` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`parameter`]` := `[`argument`]` '$'`
///
/// # Example
///
/// ```text
/// 42$
/// par$
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn parameter(input: &str) -> Option<(LeftToParse<'_>, Parameter<'_>)> {
    and_then(argument, |(i, arg)| map(char('$'), |i| (i, arg))(i))(input)
}

/// Parses an `identifier` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// `IDENTIFIER_OR_KEYWORD : XID_Start XID_Continue* | _ XID_Continue+`
///
/// See [rust reference][2] for more info.
///
/// # Example
///
/// ```text
/// identifier
/// Минск
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
/// [2]: https://doc.rust-lang.org/reference/identifiers.html
fn identifier(input: &str) -> Option<(LeftToParse<'_>, Identifier<'_>)> {
    map(
        alt(&mut [
            &mut map(
                check_char(XID::is_xid_start),
                take_while0(check_char(XID::is_xid_continue)),
            ),
            &mut and_then(char('_'), take_while1(check_char(XID::is_xid_continue))),
        ]),
        |(i, _)| (i, &input[..(input.as_bytes().len() - i.as_bytes().len())]),
    )(input)
}

/// Parses an `integer` as defined in the [grammar spec][1].
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn integer(input: &str) -> Option<(LeftToParse<'_>, usize)> {
    and_then(
        take_while1(check_char(|c| c.is_ascii_digit())),
        |(i, int)| int.parse().ok().map(|int| (i, int)),
    )(input)
}

/// Parses a `text` as defined in the [grammar spec][1].
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn text(input: &str) -> Option<(LeftToParse<'_>, &str)> {
    take_until1(any_char, one_of("{}"))(input)
}

type Parser<'p> = &'mut dyn FnMut(&str) -> &str;

/// Applies non-failing parsers in sequence.
fn seq<'p>(parsers: &'mut [Parser<'p>]) -> impl FnMut(&str) -> LeftToParse<'_> + 'p {
    move |input| parsers.iter_mut().fold(input, |i, p| (**p)(i))
}

type FallibleParser<'p> = &'mut dyn FnMut(&str) -> Option<&str>;

/// Tries to apply parsers in sequence. Returns [`None`] in case one of them
/// returned [`None`].
fn try_seq<'p>(
    parsers: &'p mut [FallibleParser<'p>],
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + 'p {
    move |input| parsers.iter_mut().try_fold(input, |i, p| (**p)(i))
}

/// Returns first successful parser or [`None`] in case all of them returned
/// [`None`].
fn alt<'p, 'i, T: 'i>(
    parsers: &'p mut [&'mut dyn FnMut(&>'i str) -> Option<T>],
) -> impl FnMut(&'i str) -> Option<T> + 'p {
    move |input| parsers.iter_mut().find_map(|p| (**p)(input))
}

/// Maps output of the parser in case it returned [`Some`].
fn map<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut f: impl FnMut(I) -> O,
) -> impl FnMut(&'i str) -> Option<O> {
    move |input| parser(input).map(&mut f)
}

/// Maps output of the parser in case it returned [`Some`] or uses `default`.
fn map_or_else<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut default: impl FnMut(&'i str) -> O,
    mut f: impl FnMut(I) -> O,
) -> impl FnMut(&'i str) -> O {
    move |input| parser(input).map_or_else(|| default(input), &mut f)
}

/// Returns the contained [`Some`] value or computes it from a closure.
fn unwrap_or_else<'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<O>,
    mut f: impl FnMut(&'i str) -> O,
) -> impl FnMut(&'i str) -> O {
    move |input| parser(input).unwrap_or_else(|| f(input))
}

/// Returns [`None`] if the parser returned is [`None`], otherwise calls `f`
/// with the wrapped value and returns the result.
fn and_then<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut f: impl FnMut(I) -> Option<O>,
) -> impl FnMut(&'i str) -> Option<O> {
    move |input| parser(input).and_then(&mut f)
}

/// Checks whether `parser` is successful while not advancing the original
/// `input`.
fn lookahead(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| map(&mut parser, |_| input)(input)
}

/// Makes underlying `parser` optional by returning the original `input` in case
/// it returned [`None`].
fn optional(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> LeftToParse<'_> {
    move |input: &str| parser(input).unwrap_or(input)
}

/// Makes underlying `parser` optional by returning the original `input` and
/// [`None`] in case it returned [`None`].
fn optional_result<'i, T: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<(&'i str, T)>,
) -> impl FnMut(&'i str) -> (LeftToParse<'i>, Option<T>) {
    move |input: &str| {
        map_or_else(&mut parser, |i| (i, None), |(i, c)| (i, Some(c)))(input)
    }
}

/// Parses while `parser` is successful. Never fails.
fn take_while0(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> (LeftToParse<'_>, &str) {
    move |input| {
        let mut cur = input;
        while let Some(step) = parser(cur) {
            cur = step;
        }
        (
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        )
    }
}

/// Parses while `parser` is successful. Returns [`None`] in case `parser` never
/// succeeded.
fn take_while1(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
    move |input| {
        let mut cur = parser(input)?;
        while let Some(step) = parser(cur) {
            cur = step;
        }
        Some((
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        ))
    }
}

/// Parses with `basic` while `until` returns [`None`]. Returns [`None`] in case
/// `until` succeeded initially or `basic` never succeeded. Doesn't consume
/// [`char`]s parsed by `until`.
///
/// [`char`]: fn@char
fn take_until1(
    mut basic: impl FnMut(&str) -> Option<&str>,
    mut until: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
    move |input: &str| {
        if until(input).is_some() {
            return None;
        }
        let mut cur = basic(input)?;
        loop {
            if until(cur).is_some() {
                break;
            }
            let Some(b) = basic(cur) else {
                break;
            };
            cur = b;
        }

        Some((
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        ))
    }
}

/// Checks whether `input` starts with `s`.
fn str(s: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
    move |input| input.starts_with(s).then(|| &input[s.as_bytes().len()..])
}

/// Checks whether `input` starts with `c`.
fn char(c: char) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| input.starts_with(c).then(|| &input[c.len_utf8()..])
}

/// Checks whether first [`char`] suits `check`.
///
/// [`char`]: fn@char
fn check_char(
    mut check: impl FnMut(char) -> bool,
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| {
        input
            .chars()
            .next()
            .and_then(|c| check(c).then(|| &input[c.len_utf8()..]))
    }
}

/// Checks whether first [`char`] of input is present in `chars`.
///
/// [`char`]: fn@char
fn one_of(chars: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
    move |input: &str| chars.chars().find_map(|c| char(c)(input))
}

/// Parses any [`char`].
///
/// [`char`]: fn@char
fn any_char(input: &str) -> Option<LeftToParse<'_>> {
    input.chars().next().map(|c| &input[c.len_utf8()..])
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn text() {
        assert_eq!(format_string(""), Some(FormatString { formats: vec![] }));
        assert_eq!(
            format_string("test"),
            Some(FormatString { formats: vec![] }),
        );
        assert_eq!(
            format_string("Минск"),
            Some(FormatString { formats: vec![] }),
        );
        assert_eq!(format_string("��"), Some(FormatString { formats: vec![] }));
    }

    #[test]
    fn argument() {
        assert_eq!(
            format_string("{}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Integer(0)),
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{par}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Identifier("par")),
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{Минск}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Identifier("Минск")),
                    spec: None,
                }],
            }),
        );
    }

    #[test]
    fn spec() {
        assert_eq!(
            format_string("{:}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-<}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: <}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:+}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<-}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:+#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-<#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<-#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:#0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<+#0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:1}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Integer(1)),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:1$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Integer(1))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("par"))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-^-#0Минск$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("Минск"))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.*}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Star),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Integer(0))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.0$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Integer(0),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: >+#2$.par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Integer(2))),
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:x?}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::LowerDebug,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:E}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::UpperExp,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: >+#par$.par$X?}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("par"))),
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::UpperDebug,
                    }),
                }],
            }),
        );
    }

    #[test]
    fn full() {
        assert_eq!(
            format_string("prefix{{{0:#?}postfix{par:-^par$.a$}}}"),
            Some(FormatString {
                formats: vec![
                    Format {
                        arg: Some(Argument::Integer(0)),
                        spec: Some(FormatSpec {
                            width: None,
                            precision: None,
                            ty: Type::Debug,
                        }),
                    },
                    Format {
                        arg: Some(Argument::Identifier("par")),
                        spec: Some(FormatSpec {
                            width: Some(Count::Parameter(Argument::Identifier("par"))),
                            precision: Some(Precision::Count(Count::Parameter(
                                Argument::Identifier("a"),
                            ))),
                            ty: Type::Display,
                        }),
                    },
                ],
            }),
        );
    }

    #[test]
    fn error() {
        assert_eq!(format_string("{"), None);
        assert_eq!(format_string("}"), None);
        assert_eq!(format_string("{{}"), None);
        assert_eq!(format_string("{:x?"), None);
        assert_eq!(format_string("{:.}"), None);
        assert_eq!(format_string("{:q}"), None);
        assert_eq!(format_string("{:par}"), None);
        assert_eq!(format_string("{⚙️}"), None);
    }
}

Messung V0.5 in Prozent
C=89 H=96 G=92

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-06-22) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik