Wolfgang Thallergcc16 merge934c3885 months agoHistory
988 lines · 29.6 KB · D
RawDownload
// Written in the D programming language. /**This is a submodule of $(MREF std, format). It centers around a struct called $(LREF FormatSpec), which takes a$(MREF_ALTTEXT format string, std,format) and provides tools forparsing this string. Additionally this module contains a function$(LREF singleSpec) which helps treating a single format specifier. Copyright: Copyright The D Language Foundation 2000-2013. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,Andrei Alexandrescu), and Kenji Hara Source: $(PHOBOSSRC std/format/spec.d) */module std.format.spec; import std.traits : Unqual; template FormatSpec(Char)if (!is(Unqual!Char == Char)){    alias FormatSpec = FormatSpec!(Unqual!Char);} /**A general handler for format strings. This handler centers around the function $(LREF writeUpToNextSpec),which parses the $(MREF_ALTTEXT format string, std,format) until thenext format specifier is found. After the call, it providesinformation about this format specifier in its numerous variables. Params:    Char = the character type of the format string */struct FormatSpec(Char)if (is(Unqual!Char == Char)){    import std.algorithm.searching : startsWith;    import std.ascii : isDigit;    import std.conv : parse, text, to;    import std.range.primitives;     /**       Minimum width.        _Default: `0`.     */    int width = 0;     /**       Precision. Its semantic depends on the format character.        See $(MREF_ALTTEXT format string, std,format) for more details.       _Default: `UNSPECIFIED`.     */    int precision = UNSPECIFIED;     /**       Number of elements between separators.        _Default: `UNSPECIFIED`.     */    int separators = UNSPECIFIED;     /**       The separator charactar is supplied at runtime.        _Default: false.     */    bool dynamicSeparatorChar = false;     /**       Set to `DYNAMIC` when the separator character is supplied at runtime.        _Default: `UNSPECIFIED`.        $(RED Warning:           `separatorCharPos` is deprecated. It will be removed in 2.107.0.           Please use `dynamicSeparatorChar` instead.)     */    // @@@DEPRECATED_[2.107.0]@@@    deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.")    int separatorCharPos() { return dynamicSeparatorChar ? DYNAMIC : UNSPECIFIED; }     /// ditto    // @@@DEPRECATED_[2.107.0]@@@    deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.")    void separatorCharPos(int value) { dynamicSeparatorChar = value == DYNAMIC; }     /**       Character to use as separator.        _Default: `','`.     */    dchar separatorChar = ',';     /**       Special value for `width`, `precision` and `separators`.        It flags that these values will be passed at runtime through       variadic arguments.     */    enum int DYNAMIC = int.max;     /**       Special value for `precision` and `separators`.        It flags that these values have not been specified.     */    enum int UNSPECIFIED = DYNAMIC - 1;     /**       The format character.        _Default: `'s'`.     */    char spec = 's';     /**       Index of the argument for positional parameters.        Counting starts with `1`. Set to `0` if not used. Default: `0`.     */    ushort indexStart;     /**       Index of the last argument for positional parameter ranges.        Counting starts with `1`. Set to `0` if not used. Default: `0`.        The maximum value of this field is used as a sentinel to indicate the arguments' length.    */    ushort indexEnd;     version (StdDdoc)    {        /// The format specifier contained a `'-'`.        bool flDash;         /// The format specifier contained a `'0'`.        bool flZero;         /// The format specifier contained a space.        bool flSpace;         /// The format specifier contained a `'+'`.        bool flPlus;         /// The format specifier contained a `'#'`.        bool flHash;         /// The format specifier contained a `'='`.        bool flEqual;         /// The format specifier contained a `','`.        bool flSeparator;         // Fake field to allow compilation        ubyte allFlags;    }    else    {        union        {            import std.bitmanip : bitfields;            mixin(bitfields!(                        bool, "flDash", 1,                        bool, "flZero", 1,                        bool, "flSpace", 1,                        bool, "flPlus", 1,                        bool, "flHash", 1,                        bool, "flEqual", 1,                        bool, "flSeparator", 1,                        ubyte, "", 1));            ubyte allFlags;        }    }     /// The inner format string of a nested format specifier.    const(Char)[] nested;     /**       The separator of a nested format specifier.        `null` means, there is no separator. `empty`, but not `null`,       means zero length separator.     */    const(Char)[] sep;     /// Contains the part of the format string, that has not yet been parsed.    const(Char)[] trailing;     /// Sequence `"["` inserted before each range or range like structure.    enum immutable(Char)[] seqBefore = "[";     /// Sequence `"]"` inserted after each range or range like structure.    enum immutable(Char)[] seqAfter = "]";     /**       Sequence `":"` inserted between element key and element value of       an associative array.     */    enum immutable(Char)[] keySeparator = ":";     /**       Sequence `", "` inserted between elements of a range, a range like       structure or the elements of an associative array.     */    enum immutable(Char)[] seqSeparator = ", ";     /**       Creates a new `FormatSpec`.        The string is lazily evaluated. That means, nothing is done,       until $(LREF writeUpToNextSpec) is called.        Params:           fmt = a $(MREF_ALTTEXT format string, std,format)     */    this(in Char[] fmt) @safe pure    {        trailing = fmt;    }     /**       Writes the format string to an output range until the next format       specifier is found and parse that format specifier.        See the $(MREF_ALTTEXT description of format strings, std,format) for more       details about the format specifier.        Params:           writer = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives),                    where the format string is written to           OutputRange = type of the output range        Returns:           True, if a format specifier is found and false, if the end of the           format string has been reached.        Throws:           A $(REF_ALTTEXT FormatException, FormatException, std,format)           when parsing the format specifier did not succeed.     */    bool writeUpToNextSpec(OutputRange)(ref OutputRange writer) scope    {        import std.format : enforceFmt;         if (trailing.empty)            return false;        for (size_t i = 0; i < trailing.length; ++i)        {            if (trailing[i] != '%') continue;            put(writer, trailing[0 .. i]);            trailing = trailing[i .. $];            enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`);            trailing = trailing[1 .. $];             if (trailing[0] != '%')            {                // Spec found. Fill up the spec, and bailout                fillUp();                return true;            }            // Doubled! Reset and Keep going            i = 0;        }        // no format spec found        put(writer, trailing);        trailing = null;        return false;    }     private void fillUp() scope    {        import std.format : enforceFmt, FormatException;         // Reset content        if (__ctfe)        {            flDash = false;            flZero = false;            flSpace = false;            flPlus = false;            flEqual = false;            flHash = false;            flSeparator = false;        }        else        {            allFlags = 0;        }         width = 0;        indexStart = 0;        indexEnd = 0;        precision = UNSPECIFIED;        nested = null;        // Parse the spec (we assume we're past '%' already)        for (size_t i = 0; i < trailing.length; )        {            switch (trailing[i])            {            case '(':                // Embedded format specifier.                auto j = i + 1;                // Get the matching balanced paren                for (uint innerParens;;)                {                    enforceFmt(j + 1 < trailing.length,                        text("Incorrect format specifier: %", trailing[i .. $]));                    if (trailing[j++] != '%')                    {                        // skip, we're waiting for %( and %)                        continue;                    }                    if (trailing[j] == '-') // for %-(                    {                        ++j;    // skip                        enforceFmt(j < trailing.length,                            text("Incorrect format specifier: %", trailing[i .. $]));                    }                    if (trailing[j] == ')')                    {                        if (innerParens-- == 0) break;                    }                    else if (trailing[j] == '|')                    {                        if (innerParens == 0) break;                    }                    else if (trailing[j] == '(')                    {                        ++innerParens;                    }                }                if (trailing[j] == '|')                {                    auto k = j;                    for (++j;;)                    {                        if (trailing[j++] != '%')                            continue;                        if (trailing[j] == '%')                            ++j;                        else if (trailing[j] == ')')                            break;                        else                            throw new FormatException(                                text("Incorrect format specifier: %",                                        trailing[j .. $]));                    }                    nested = trailing[i + 1 .. k - 1];                    sep = trailing[k + 1 .. j - 1];                }                else                {                    nested = trailing[i + 1 .. j - 1];                    sep = null; // no separator                }                //this = FormatSpec(innerTrailingSpec);                spec = '(';                // We practically found the format specifier                trailing = trailing[j + 1 .. $];                return;            case '-': flDash = true; ++i; break;            case '+': flPlus = true; ++i; break;            case '=': flEqual = true; ++i; break;            case '#': flHash = true; ++i; break;            case '0': flZero = true; ++i; break;            case ' ': flSpace = true; ++i; break;            case '*':                ++i;                if (i < trailing.length && isDigit(trailing[i]))                {                    // a '*' followed by digits and '$' is a                    // positional format                    trailing = trailing[1 .. $];                    width = -parse!(typeof(width))(trailing);                    i = 0;                    enforceFmt(trailing[i++] == '$',                        text("$ expected after '*", -width, "' in format string"));                }                else                {                    // read result                    width = DYNAMIC;                }                break;            case '1': .. case '9':                auto tmp = trailing[i .. $];                const widthOrArgIndex = parse!uint(tmp);                enforceFmt(tmp.length,                    text("Incorrect format specifier %", trailing[i .. $]));                i = trailing.length - tmp.length;                if (tmp.startsWith('$'))                {                    // index of the form %n$                    indexEnd = indexStart = to!ubyte(widthOrArgIndex);                    ++i;                }                else if (tmp.startsWith(':'))                {                    // two indexes of the form %m:n$, or one index of the form %m:$                    indexStart = to!ubyte(widthOrArgIndex);                    tmp = tmp[1 .. $];                    if (tmp.startsWith('$'))                    {                        indexEnd = indexEnd.max;                    }                    else                    {                        indexEnd = parse!(typeof(indexEnd))(tmp);                    }                    i = trailing.length - tmp.length;                    enforceFmt(trailing[i++] == '$',                        "$ expected");                }                else                {                    // width                    width = to!int(widthOrArgIndex);                }                break;            case ',':                // Separator (default 3 digits)                ++i;                flSeparator = true;                 if (i < trailing.length && trailing[i] == '*')                {                    ++i;                    // read result                    separators = DYNAMIC;                }                else if (i < trailing.length && isDigit(trailing[i]))                {                    auto tmp = trailing[i .. $];                    separators = parse!int(tmp);                    i = trailing.length - tmp.length;                }                else                {                    // "," was specified, but nothing after it                    separators = 3;                }                 if (i < trailing.length && trailing[i] == '?')                {                    dynamicSeparatorChar = true;                    ++i;                }                 break;            case '.':                // Precision                ++i;                if (i < trailing.length && trailing[i] == '*')                {                    ++i;                    if (i < trailing.length && isDigit(trailing[i]))                    {                        // a '.*' followed by digits and '$' is a                        // positional precision                        trailing = trailing[i .. $];                        i = 0;                        precision = -parse!int(trailing);                        enforceFmt(trailing[i++] == '$',                            "$ expected");                    }                    else                    {                        // read result                        precision = DYNAMIC;                    }                }                else if (i < trailing.length && trailing[i] == '-')                {                    // negative precision, as good as 0                    precision = 0;                    auto tmp = trailing[i .. $];                    parse!int(tmp); // skip digits                    i = trailing.length - tmp.length;                }                else if (i < trailing.length && isDigit(trailing[i]))                {                    auto tmp = trailing[i .. $];                    precision = parse!int(tmp);                    i = trailing.length - tmp.length;                }                else                {                    // "." was specified, but nothing after it                    precision = 0;                }

Lines 1–500 of 988.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t