PreviousUpNext

15.3.421  src/lib/src/printf-combinator.api

## printf-combinator.api

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




#   Well-typed "printf" for Mythryl, aka "Unparsing Combinators".
#     This code was written by Matthias Blume (2002).  Inspiration
#     obtained from Olivier Danvy's "Functional Prettyprinting" work.
#
# Description:
#
# The idea is to use combinators to construct something akin to
# the format string of C's printf function.  The difference is, however,
# that our formats aren't strings.  Instead, format (fragments) have
# meaningful types, and passing them to function "format" results
# in a curried function whose arguments have precisely the types that
# correspond to argument-consuming parts of the format.  (Such
# argument-consuming parts are similar to the %-specifications of printf.)
#
# Here is how the typing works: There is an underlying notion of
# "abstract formats" of type Format(X).  However, the user operates
# at the level of "format fragments" which have type
# Fragment(X, Y) and are typically typeagnostic in X (where Y is
# macro expanded to some type containing X).  Fragments are
# functions from formats to formats and can be composed freely using
# the function composition operator 'o'.  This form of format
# composition translates to a corresponding concatenation of the
# resulting output.
#
# Fragments are composed from two kinds of primitve fragments called
# "elements" and "glue", respectively.  An "element" is a fragment that
# consumes some argument (which thanks to the typing magic appears as a
# curried argument when the format gets executed).  As "glue" we refer
# to fragments that do not consume arguments but merely insert fixed
# text (fixed at format construction time) into the output.
#
# There are also adjustment operations that pad, trim, or fit the output
# of entire fragments (primitive or not) to a given size.
#
# A number of elements and some glue have been predefined.
#
# Here are examples on how to use this facility:
#
#  include package   printf_combinator;
#
#  format nothing                      ==> ""
#
#  format int                          ==> fn: Int -> String
#  format int 1234                     ==> "1234"
#
#  format (text "The square of " o int o text " is " o int o text ".")
#                                      ==> fn: int -> int -> String
#  format (text "The square of " o int o t" is " o int o t".") 2 4
#                                      ==> "The square of 2 is 4."
#
#  format (int o bool o char)          ==> fn:  Int -> Bool -> char -> String
#  format (int o bool o char) 1 TRUE 'x'
#                                      ==> "1truex"
#
#  format (glue string "glue vs. " o string o glue int 42 o sp 5 o int)
#         "ordinary text " 17
#                                      ==> "glue vs. ordinary text 42     17" 
#
# Fragments can be padded, trimmed, or fitted to generate text pieces of 
# specified sizes.  Padding/trimming/fitting may be nested.
# The operations are parameterized by a place (left, center, right) and
# a width. Padding never shrinks strings, trimming never extends
# strings, and fitting is done as necessary by either padding or trimming.
# Examples:
#
#  format (pad left 6 int) 1234        ==> "  1234"
#  format (pad center 6 int) 1234      ==> " 1234 "
#  format (pad right 6 int) 1234       ==> "1234  "
#  format (trim left 2 int) 1234       ==> "34"
#  format (trim center 2 int) 1234     ==> "23"
#  format (trim right 2 int) 1234      ==> "12"
#  format (fit left 3 int) 12          ==> " 12"
#  format (fit left 3 int) 123         ==> "123"
#  format (fit left 3 int) 1234        ==> "234"
#
# Nesting:
#
#  format (pad right 20 (int o pad left 10 float) o text "x") 12 22.3
#                                      ==> "12      22.3        x"

api Printf_Combinator {

    # We reveal "fragments" to be functions from abstract formats
    # to abstract formats.  This is to make sure we can use function
    # composition on them.

    Format(X);

    Fragment (X, Y)
        =
        Format(X) -> Format(Y);

    # Two primitive kinds of fragments:  Glue inserts some text
    # into the output without consuming an argument.  Elements
    # insert text corresponding to some (curried) argument into
    # the output:

    Glue(X)     = Fragment (X, X); 
    Element(X, T) = Fragment (X, T -> X); 


    #  Format execution

    #   1. Simple version, produce final result as a string: 
    #
    format:   Fragment (String, X) -> X;

    #  2. Complex version, take a receiver function that will
    #     be invoked with the final result.  The result is
    #     still in non-concatenated form at this time.
    #     (Internally, the combinators avoid string concatenation
    #      as long as there is no padding/trimming/fitting going on.)
    #
    format' : (List( String ) -> Y)
              ->
              Fragment( Y, X )
              ->
              X;


    # Make a type-specific element given a
    # to_string function for this type:
    #
    using:  (T -> String) -> Element (X, T);


    # Macro expand 'using' for a few types... 
    #
    int:      Element( X, Int );    #  using int::to_string 
    float:    Element( X, Float );  #  using eight_byte_float::to_string 
    bool:     Element( X, Bool );   #  using bool::to_string 
    string:   Element( X, String ); #  using (fn x => x) 
    string' : Element( X, String ); #  using string::to_string 
    char:     Element( X, Char );   #  using string::from_char 
    char'   : Element( X, Char );   #  using char::to_string 


                                        # number_string is from   src/lib/std/src/number-string.pkg

    # Parameterized elements:
    #
    int'   : number_string::Radix       ->  Element( X, Int  ); #  using (int::format r) 
    float' : number_string::Float_Format ->  Element (X, Float);        #  using (eight_byte_float::format f) 

    # Generic "gluifier":
    #
    glue:  Element (X, T) -> T -> Glue(X);

    # More glue:
    #
    nothing:            Glue(X);        #  null glue 
    text:     String -> Glue(X);        #  Constant text glue 
    sp:       Int ->    Glue(X);        #  n spaces glue 
    nl:                 Glue(X);        #  newline glue 
    tab:                Glue(X);        #  tabulator glue 

    # "Places" tell which side
    # of a string to pad or trim:
    #
    Place;
    left:    Place;
    center:  Place;
    right:   Place;

    # Pad, trim, or fit to size n
    # the output corresponding to
    # a format fragment:
    #
    pad:   Place -> Int -> Fragment( X, T ) -> Fragment( X, T );
    trim:  Place -> Int -> Fragment( X, T ) -> Fragment( X, T );
    fit:   Place -> Int -> Fragment( X, T ) -> Fragment( X, T );

    # Specialized padding (left and right) 
    padl:  Int -> Fragment( X, T ) -> Fragment( X, T );
    padr:  Int -> Fragment( X, T ) -> Fragment( X, T );

};



## COPYRIGHT (c) 2002 Bell Labs, Lucent Technologies
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext