PreviousUpNext

15.4.965  src/lib/src/printf-field.pkg

## printf-field.pkg

# Compiled by:
#     src/lib/std/standard.lib

# This module defines types and
# routines that are common to both
# the 'sfprintf' and 'scan' packages.



###                "The most powerful designs are always
###                 the result of a continuous process
###                 of simplification and refinement."
###
###                                 -- Kevin Mullet


stipulate
    package f8b =  eight_byte_float;                                    # eight_byte_float      is from   src/lib/std/eight-byte-float.pkg
herein

    package printf_field: (weak)
    api {

        # Precompiled format specifiers:

        Sign
            = DEFAULT_SIGN      #  Default: put a sign on negative numbers 
            | ALWAYS_SIGN       #  "+"      always has sign (+ or -) 
            | BLANK_SIGN;       #  " "      put a blank in the sign field for positive numbers 

        Neg_Sign
            = MINUS_SIGN        #  Default: use "-" for negative numbers 
            | TILDE_SIGN;       #  "~"      use "~" for negative numbers 

        Field_Flags
            =
            { sign:          Sign,
              neg_char:      Neg_Sign,

              zero_pad:      Bool,
              base:          Bool,
              left_justify:  Bool,
              large:         Bool
            };

        Field_Width
            =
            NO_PAD | WIDTH  Int;

        Float_Format
            = F_FORMAT          #  "%f" 
            | E_FORMAT  Bool    #  "%e" or "%E" 
            | G_FORMAT  Bool;   #  "%g" or "%G" 

        Printf_Field_Type
            = OCTAL_FIELD
            | INT_FIELD
            | HEX_FIELD
            | CAP_HEX_FIELD
            | BINARY_FIELD
            | CHAR_FIELD
            | BOOL_FIELD
            | STRING_FIELD
            | FLOAT_FIELD  { prec:  Int, format:  Float_Format };

        Printf_Field
            = RAW       Substring
            | CHAR_SET  Char -> Bool
            | FIELD     ((Field_Flags, Field_Width, Printf_Field_Type));

        Printf_Arg
            = QUICKSTRING   quickstring__premicrothread::Quickstring
            | LINT   large_int::Int
            | INT    int::Int
            | LUNT   large_unt::Unt
            | UNT    unt::Unt
            | UNT8   one_byte_unt::Unt
            | BOOL   Bool
            | CHAR   Char
            | STRING String
            | FLOAT  f8b::Float
            | LEFT  ((Int, Printf_Arg)) # Left  justify in field of given width.
            | RIGHT ((Int, Printf_Arg));        # Right justify in field of given width.

        exception BAD_FORMAT String;            #  Bad format string 

        scan_field:       Substring -> ((Printf_Field, Substring));

    }
    {
        package ss =  substring;                # substring             is from   src/lib/std/substring.pkg
        package sc =  number_string;    # number_string         is from   src/lib/std/src/number-string.pkg

        # Precompiled format specifiers:
        Sign
            = DEFAULT_SIGN      #  Default: put a sign on negative numbers 
            | ALWAYS_SIGN       #  "+"      always has sign (+ or -) 
            | BLANK_SIGN;       #  " "      put a blank in the sign field for positive numbers 

        Neg_Sign
            = MINUS_SIGN        #  Default: use "-" for negative numbers 
            | TILDE_SIGN;       #  "~"      use "~" for negative numbers 

        Field_Flags
            =
            { sign:          Sign,
              neg_char:      Neg_Sign,

              zero_pad:      Bool,
              base:          Bool,
              left_justify:  Bool,
              large:         Bool
            };

        Field_Width = NO_PAD | WIDTH  Int;

        Float_Format
            = F_FORMAT          #  "%f" 
            | E_FORMAT  Bool    #  "%e" or "%E" 
            | G_FORMAT  Bool;   #  "%g" or "%G" 

        Printf_Field_Type
            = OCTAL_FIELD
            | INT_FIELD
            | HEX_FIELD
            | CAP_HEX_FIELD
            | CHAR_FIELD
            | BOOL_FIELD
            | BINARY_FIELD
            | STRING_FIELD
            | FLOAT_FIELD  { prec:  Int, format:  Float_Format };

        Printf_Field
            = RAW  Substring
            | CHAR_SET  Char -> Bool
            | FIELD  ((Field_Flags, Field_Width, Printf_Field_Type));

        Printf_Arg
          = QUICKSTRING   quickstring__premicrothread::Quickstring
          | LINT   large_int::Int
          | INT    int::Int
          | LUNT   large_unt::Unt
          | UNT    unt::Unt
          | UNT8   one_byte_unt::Unt
          | BOOL   Bool
          | CHAR   Char
          | STRING String
          | FLOAT  f8b::Float
          | LEFT   (Int, Printf_Arg)    # Left  justify in field of given width.
          | RIGHT  (Int, Printf_Arg)    # Right justify in field of given width.
          ;

        exception BAD_FORMAT String;                    #  Bad format string 



        # String to int conversions:

        my dec_to_int:  sc::Reader (Char, Substring) -> sc::Reader (int::Int, Substring)
            =
            int::scan  sc::DECIMAL;

        # Scan a field specification.
        #
        # Assume that the previous character in the
        # base string was "%" and that the first
        # character in the substring format_string
        # is not "%".
        #
        fun scan_field_spec  format_string
            =
            {
                my (format_string, flags)
                    =
                    do_flags
                      (
                        format_string,
                        { sign         =>  DEFAULT_SIGN,
                          neg_char     =>  MINUS_SIGN,
                          zero_pad     =>  FALSE,
                          base         =>  FALSE,
                          left_justify =>  FALSE,
                          large        =>  FALSE
                        } 
                      )
                    where
                        fun do_flags (ss, flags:  Field_Flags)
                            =
                            case (ss::getc ss, flags)
                                #
                                (THE(' ', ss'),  { sign=>ALWAYS_SIGN, ... } )
                                    =>
                                    raise exception BAD_FORMAT "Forbidden blank in format string";

                                (THE(' ', ss'), _)
                                    =>
                                    do_flags (
                                        ss',
                                        { sign         =>  BLANK_SIGN,
                                          neg_char     =>  flags.neg_char,
                                          zero_pad     =>  flags.zero_pad,
                                          base         =>  flags.base,
                                          left_justify =>  flags.left_justify,
                                          large        =>  flags.large
                                        }
                                    );

                                (THE('+', ss'), { sign=>BLANK_SIGN, ... } )
                                    =>
                                    raise exception BAD_FORMAT "Forbidden '+' in format string";

                                (THE('+', ss'), _)
                                    =>
                                    do_flags (
                                        ss',
                                        { sign         =>  ALWAYS_SIGN,
                                          neg_char     =>  flags.neg_char,
                                          zero_pad     =>  flags.zero_pad,
                                          base         =>  flags.base,
                                          left_justify =>  flags.left_justify,
                                          large        =>  flags.large
                                        }
                                    );

                                (THE('~', ss'), _)
                                    =>
                                    do_flags (
                                        ss',
                                        { sign         =>  flags.sign,
                                          neg_char     =>  TILDE_SIGN,
                                          zero_pad     =>  flags.zero_pad,
                                          base         =>  flags.base,
                                          left_justify =>  flags.left_justify,
                                          large        =>  flags.large
                                        }
                                    );

                                (THE('-', ss'), _)
                                    => 
                                    do_flags (
                                        ss',
                                        { sign         =>  flags.sign,
                                          neg_char     =>  MINUS_SIGN,
                                          zero_pad     =>  flags.zero_pad,
                                          base         =>  flags.base,
                                          left_justify =>  TRUE,
                                          large        =>  flags.large
                                        }
                                    );

                                (THE('#', ss'), _)
                                    =>
                                    do_flags (
                                        ss',
                                        { sign         =>  flags.sign,
                                          neg_char     =>  flags.neg_char,
                                          zero_pad     =>  flags.zero_pad,
                                          base         =>  TRUE,
                                          left_justify =>  flags.left_justify,
                                          large        =>  flags.large
                                        }
                                    );

                                (THE('0', ss'), _)
                                    =>
                                    ( ss',
                                      { sign         =>  flags.sign,
                                        neg_char     =>  flags.neg_char,
                                        zero_pad     =>  TRUE,
                                        base         =>  flags.base,
                                        left_justify =>  flags.left_justify,
                                        large        =>  flags.large
                                      }
                                    );

                                _ => (ss, flags);
                            esac;

                        end;            # where

                my (wid, format_string)
                    =
                    if (char::is_digit (the (ss::first format_string)))
                        #
                        (the (dec_to_int  ss::getc  format_string))
                            ->
                            (n, format_string);

                        (WIDTH n, format_string); 
                    else
                        (NO_PAD, format_string);
                    fi;

                my (type, format_string)
                    =
                    case (ss::getc format_string)
                        #
                        THE ('d', ss) => (INT_FIELD, ss);
                        THE ('X', ss) => (CAP_HEX_FIELD, ss);
                        THE ('x', ss) => (HEX_FIELD, ss);
                        THE ('o', ss) => (OCTAL_FIELD, ss);
                        THE ('c', ss) => (CHAR_FIELD, ss);
                        THE ('s', ss) => (STRING_FIELD, ss);
                        THE ('B', ss) => (BOOL_FIELD, ss);
                        THE ('b', ss) => (BINARY_FIELD, ss);
                        THE ('.', ss)
                            =>
                            {   # NOTE: "." ought to be allowed
                                # for d, X, x, o and s formats
                                # as it is in ANSI C.
                                # XXX BUGGO FIXME 

                                (the (dec_to_int  ss::getc ss))
                                    ->
                                    (n, ss);

                                my (format, ss)
                                    =
                                    case (ss::getc ss)
                                        #
                                        THE ('E', ss) =>  (E_FORMAT TRUE, ss);
                                        THE ('e', ss) =>  (E_FORMAT FALSE, ss);
                                        THE ('f', ss) =>  (F_FORMAT, ss);
                                        THE ('G', ss) =>  (G_FORMAT TRUE, ss);
                                        THE ('g', ss) =>  (G_FORMAT FALSE, ss);
                                        THE ( c , ss) => raise exception BAD_FORMAT ("Unsupported char '" + char::to_string c + "' in format string");
                                        _             => raise exception BAD_FORMAT "Incomplete format string";
                                    esac;

                                (FLOAT_FIELD { prec => n, format }, ss);
                            };

                        THE ('E', ss) =>  (FLOAT_FIELD { prec => 6, format => E_FORMAT TRUE  }, ss);
                        THE ('e', ss) =>  (FLOAT_FIELD { prec => 6, format => E_FORMAT FALSE }, ss);
                        THE ('f', ss) =>  (FLOAT_FIELD { prec => 6, format => F_FORMAT       }, ss);
                        THE ('G', ss) =>  (FLOAT_FIELD { prec => 6, format => G_FORMAT TRUE  }, ss);
                        THE ('g', ss) =>  (FLOAT_FIELD { prec => 6, format => G_FORMAT FALSE }, ss);

                        THE (c, ss)   =>  raise exception BAD_FORMAT ("Unsupported char '" + char::to_string c + "' in format string");
                        _             =>  raise exception BAD_FORMAT "Incomplete format string";
                    esac;


                (FIELD (flags, wid, type), format_string);

            };                                                  #  fun scan_field_spec 

        fun scan_field format_string
            =
            case (ss::getc  format_string )
                #
                THE ('%', format_string')
                    =>
                    (RAW (ss::make_slice (format_string, 0, THE 1)), format_string');

                _   => scan_field_spec  format_string;
            esac;
    };
end;



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext