PreviousUpNext

15.4.830  src/lib/prettyprint/big/src/prettyprinter.pkg

## prettyprinter.pkg

# Compiled by:
#     src/lib/prettyprint/big/prettyprint.lib


package   prettyprinter
: (weak)  Prettyprinter                                         # Prettyprinter is from   src/lib/prettyprint/big/src/prettyprinter.api
{
    # I find the original prettyprinter interface ugly and clumsy;
    # this is an attempt to provide something simpler and cleaner.
    #
    # The idea is to do
    #
    #     pp.horizontal_else_vertical_box .{
    #         pp.out "Various\rstuff\rof consequence.\n";
    #     };
    #
    # or
    #
    #     pp.horizontal_else_vertical_box .{
    #         pp.put "Various stuff of consequence.\n";
    #     };
    #
    # instead of
    #
    #     pp::begin_horizontal_else_vertical_box stream;
    #     pp::string        stream "Various";
    #     pp::break         stream { spaces=>1; indent_on_wrap=>4 };
    #     pp::string        stream "stuff";
    #     pp::break         stream { spaces=>1; indent_on_wrap=>4 };
    #     pp::string        stream "of consequence.";
    #     pp::newline       stream;
    #     pp::end_box     stream;
    #
    # approximately.  In particular, I see the former as:
    #  o  Inherently keeping block open/close pairs matched.
    #     (The original code had at least one such bug.)
    #  o  Reducing clutter by making the 'stream' arguments implicit.
    #  o  Reducing clutter by allowing newlines to be included in strings
    #     instead of having to be separate calls.
    # Currently unused, probably needs tuning.
    #
    #    
    # TO DO:                                   XXX BUGGO FIXME
    #    Drop wrap'_box and wrap'_box'
    #    Change horizontal_else_vertical_box and horizontal_else_vertical_box' to align  and align'
    #    Change wrap_box and wrap_box' to wrap   and wrap' 
    #    Maybe change '\r' to '\ ' (or to \{  } so we can specify the
    #       number of blanks in a break naturally)? Or maybe out/put distinction handles this ok?

    package pp = prettyprint;   # prettyprint   is from   src/lib/prettyprint/big/src/prettyprint.pkg

    Prettyprinter
        =
        { stream:      prettyprint::Stream,
          text_stream: Null_Or( file::Output_Stream ),

          align:   (Void -> Void) -> Void,
          wrap:    (Void -> Void) -> Void,

          align':  Int -> (Void -> Void) -> Void,
          wrap':   Int -> (Void -> Void) -> Void,

          flush:      Void -> Void,
          close:      Void -> Void,

          lit:        String -> Void,
          out:        String -> Void,
          put:        String -> Void
        };  


    fun make_file_prettyprinter  prettyprint_filename
        =
        {   text_stream
                =
                file::open_for_write  prettyprint_filename; 

            prettyprint_device
                =
                {   consumer  =>  (fn string =  file::write  (text_stream,  string)),
                    linewidth =>  2000,        # Arbitrary large number.
                    flush     =>  .{ file::flush  text_stream; }
                };

            stream =   pp::open_stream  prettyprint_device;

            fun align     thunk =   { pp::begin_horizontal_else_vertical_box    stream;    thunk();   pp::end_box stream;   };
            fun wrap      thunk =   { pp::begin_wrap_box    stream;    thunk();   pp::end_box stream;   };
            
            fun align'    i thunk =   { pp::begin_indented_horizontal_else_vertical_box    stream (pp::BOX_RELATIVE i);   thunk();   pp::end_box stream; };
            fun wrap'     i thunk =   { pp::begin_indented_wrap_box    stream (pp::BOX_RELATIVE i);   thunk();   pp::end_box stream; };
            
            fun flush ()           =   { pp::flush_stream  stream;    file::flush         text_stream; };
            fun close ()           =   { pp::close_stream  stream;    file::close_output  text_stream; };



            ##############################################################################
            #                       lit / out / put
            #
            # The idea with out/put is to replace
            # explicit calls to pp::break and pp::newline
            # with embedded ' '  '\r'  '\n'  chars:
            #
            #   lit:   Entire string output literally with no special character treatments.
            #   out:   '\n'                 treated as pp::newline
            #          '\r'                 treated as pp::break { spaces => 3, indent_on_wrap => 0 }
            #          N blanks             treated as pp::break { spaces => n, indent_on_wrap => 0 }
            #   put:   Same, except indent_on_wrap for blanks is 4.
            # 
            # The expectation is that:
            #   lit:   Will typically be used to format short statements in a line box.
            #   put:   Will typically be used to wrap individual words in a wrap box.
            # In the former case, one usually wants wrapped lines to be aligned,  hence the 'indent_on_wrap => 0'
            # In the latter case, one usually wants wrapped lines to be indented, hence the 'indent_on_wrap => 4'
            # 
            # (I don't expect \r to appear in 'put' strings, in general,
            # but there seems no reason not to support it.)
            ##############################################################################


            fun lit string
                = 
                pp::string stream string;

            fun output indent string
                = 
                next 0
                where

                    len =   size string;

                    fun next i
                        =
                        if   (i < len)
                            
                             c =  string::get (string, i);

                             case c
                               
                                  '\n' =>  do_newline i;
                                  '\r' =>  do_cr      i;
                                  ' '  =>  do_spaces (i, i+1);
                                   _   =>  do_other  (i, i+1);
                             esac;
                        fi
                     
                    also
                    fun do_newline  i                   # Treat each \n in 'string' as a call to pp:newline.
                        =
                        {    pp::newline stream;
                             next (i+1);
                        }

                    also
                    fun do_cr  i                        # Treat each \r in 'string' as a call to pp:break { spaces => 3, indent_on_wrap => 0 }.
                        =
                        {    pp::break  stream  { spaces => 1,  indent_on_wrap => 0 };
                             next (i+1);
                        }

                    also
                    fun do_spaces  (i, j)               # Treat a run of 'n' blanks in 'string' as a call to pp:break { spaces => n, indent_on_wrap => 4 }.
                        =
                        {
                            if   (j >= len)
                                
                                 pp::break  stream  { spaces => j-i,  indent_on_wrap => indent };
                            else
                                 c =  string::get (string, j);

                                 if   (c == ' ')
                                     
                                      do_spaces (i, j+1);       # Scan to end of string of blanks.
                                 else
                                      pp::break  stream  { spaces => j-i,  indent_on_wrap => 4 };
                                      next j;
                                 fi;
                            fi;
                        }

                    also                                # Treat literally a run of non-\n, non-\r, non-blank chars in 'string'.
                    fun do_other (i, j)
                        =
                        if   (j >= len)
                            
                             pp::string stream (string::substring (string, i, j-i));
                        else
                             c =  string::get (string, j);

                             if  (c != ' '
                             and  c != '\r'
                             and  c != '\n'
                             )
                                  do_other (i, j+1);    # Scan to end of string of vanilla characters.
                             else
                                  substring =  string::substring (string, i, j-i);
                                  pp::string stream substring;
                                  next j;
                             fi;
                        fi;
                end;                                    # fun output

            fun out string =   output 0 string;
            fun put string =   output 4 string;


            { stream,
              text_stream => THE text_stream,

              align,
              wrap,

              align',
              wrap',

              flush,
              close,

              lit,
              out,
              put
            };  
        };

}; #  package prettyprinter


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext