PreviousUpNext

15.4.844  src/lib/prettyprint/big/src/standard-prettyprinter-g.pkg

## standard-prettyprinter-g.pkg
#

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


stipulate
    package fil =  file__premicrothread;                                        # file__premicrothread                                                  is from   src/lib/std/src/posix/file--premicrothread.pkg
herein

    generic package   standard_prettyprinter_g   (                              #
        #             ========================
        #                                                                       # "tt" == "traitful text"
        package tt:     Traitful_Text;                                          # Traitful_Text                                                         is from   src/lib/prettyprint/big/src/traitful-text.api
        package out:    Prettyprint_Output_Stream;                              # Prettyprint_Output_Stream                                             is from   src/lib/prettyprint/big/src/out/prettyprint-output-stream.api
                                                                                # out will be something like html_prettyprint_output_stream                from   src/lib/prettyprint/big/src/out/html-prettyprint-output-stream.pkg
        sharing tt::Texttraits == out::Texttraits;
    )
#   : (weak)  Standard_Prettyprinter                                            # Standard_Prettyprinter                                                is from   src/lib/prettyprint/big/src/standard-prettyprinter.api
    {
        package pp
            =
            base_prettyprinter_g (                                              # base_prettyprinter_g                                                  is from   src/lib/prettyprint/big/src/base-prettyprinter-g.pkg
                #
                package tt  =  tt;                                              # traitless_text                                                        is from   src/lib/prettyprint/big/src/traitless-text.pkg
                package out =  out;
            );
        package typ = pp::typ;

        Prettyprint_Output_Stream       =  pp::Prettyprint_Output_Stream;
        Traitful_Text                   =  pp::Traitful_Text;
        Texttraits                      =  pp::Texttraits;
        Left_Margin_Is                  == pp::typ::Left_Margin_Is;

        package box {                                                           # Optional args for 'box' fn.
            #
            Arg = LEFT_MARGIN_IS        typ::Left_Margin_Is
                | WIDTH                 Int
                | FORMAT                typ::Wrap_Policy
                ;
        };

        horizontal              =  pp::horizontal;                              # The four precoded box-formatting styles.
        vertical                =  pp::vertical;
        normal                  =  pp::normal;
        ragged_right            =  pp::ragged_right;

        Prettyprinter_Configuration_Args ==  pp::typ::Prettyprinter_Configuration_Args;

        Private_State = pp::Prettyprinter;

        Standard_Prettyprinter
          =
          { pp:                 Private_State,
            #
            tabstops_are_every:         Int,    
            default_target_box_width:   Int,
            default_left_margin_is:     typ::Left_Margin_Is,
            default_wrap_policy:        String,                         # It would be nice to have default_wrap_policy: Wrap_Policy here but I think that will produce nasty circularity issues.
            #
            box':               Int -> Int ->    (Void -> Void) -> Void,
            wrap':              Int -> Int ->    (Void -> Void) -> Void,
            cbox':              Int -> Int ->    (Void -> Void) -> Void,
            cwrap':             Int -> Int ->    (Void -> Void) -> Void,

            box:                                (Void -> Void) -> Void,
            wrap:                               (Void -> Void) -> Void,
            cbox:                               (Void -> Void) -> Void,
            cwrap:                              (Void -> Void) -> Void,

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

            break':   { ifwrap:     { blanks: Int, tab_to: Int },
                        ifnotwrap:  { blanks: Int, tab_to: Int }
                      }
                      ->
                      Void,
            tab:                Void             -> Void,                               # 
            cut:                Void             -> Void,                               # 

            tab':               Int -> Int    -> Void,                                  # Emit 'blanks' blanks, then additional blanks until (column % tabstops_are_every) == tab_to.
            cut':               Int -> Int    -> Void,                                  # If wrapped, emit newline, space to left margin of current box, then do save as above.

            txt':               Int -> Int -> String -> Void,
            txt:                              String -> Void,

            ind:                Int -> Void,                                            # "ind" == "indent"; changes left margin by given amount, except if arg==0 resets left margin to original value for current box.

            lit:                String -> Void,
            endlit:             String -> Void,

            newline:            Void -> Void,

            rulename:           String -> Void
          };  
        Prettyprinter =          Standard_Prettyprinter;
        Pp               =          Standard_Prettyprinter;
        Npp              = Null_Or( Standard_Prettyprinter );                   # We pass this around pervasively as a flag/conduit for verbose compiler debug output.

        fun    open_box (pp:Pp, left_margin_is, box_format, target_width)
        =  pp::open_box (pp.pp, left_margin_is, box_format, target_width);

        fun    break'   (pp:Pp, arg)
        =  pp::break'   (pp.pp, arg);


        fun start_box
                (pp:            Standard_Prettyprinter)                 #
                (args:          List( box::Arg ))
            =
            ();


        fun make_standard_prettyprinter  prettyprint_output_stream   options
            =
            {
                (pp::process_mill_options options)
                    ->
                    { default_target_box_width,
                      default_wrap_policy,
                      default_left_margin_is,
                      tabstops_are_every
                    };


                pp =   pp::make_prettyprinter  prettyprint_output_stream  options;
                #

                fun box'   blanks tab_to thunk  =   {   pp::open_box  (pp,  pp::typ::BOX_RELATIVE    { blanks, tab_to, tabstops_are_every },  default_wrap_policy,  default_target_box_width  );
                                                        thunk();
                                                        pp::shut_box pp;
                                                    };
                fun wrap'  blanks tab_to thunk  =   {   pp::open_box  (pp,  pp::typ::BOX_RELATIVE    { blanks, tab_to, tabstops_are_every },  ragged_right,        default_target_box_width  );
                                                        thunk();
                                                        pp::shut_box pp;
                                                    };

                fun cbox'  blanks tab_to thunk  =   {   pp::open_box  (pp,  pp::typ::CURSOR_RELATIVE { blanks, tab_to, tabstops_are_every },  default_wrap_policy,  default_target_box_width  );
                                                        thunk();
                                                        pp::shut_box pp;
                                                    };
                fun cwrap' blanks tab_to thunk  =   {   pp::open_box  (pp,  pp::typ::CURSOR_RELATIVE { blanks, tab_to, tabstops_are_every },  ragged_right,        default_target_box_width  );
                                                        thunk();
                                                        pp::shut_box pp;
                                                    };

                box   =  box'   1 0;
                wrap  =  wrap'  1 0;
                cbox  =  cbox'  1 0;
                cwrap =  cwrap' 1 0;

                fun flush ()            =   {   pp::flush_prettyprinter  pp;   out::flush  prettyprint_output_stream; };
                fun close ()            =   {   pp::close_prettyprinter  pp;   out::close  prettyprint_output_stream; };

                fun break'
                      { ifwrap:     { blanks: Int, tab_to: Int },
                        ifnotwrap:  { blanks: Int, tab_to: Int }
                      }
                    =
                    pp::break'  ( pp,
                                  { ifnotwrap       => { blanks => ifnotwrap.blanks,  tab_to => ifnotwrap.tab_to,  tabstops_are_every },
                                    ifwrap          => { blanks => ifwrap.blanks,     tab_to => ifwrap.tab_to,     tabstops_are_every }
                                  }
                                );

                fun cut' blanks tab_to   =   {   pp::break' ( pp,
                                                              { ifnotwrap       => { blanks => 0,  tab_to => -1,  tabstops_are_every },
                                                                ifwrap          => { blanks,       tab_to,        tabstops_are_every }
                                             }              );};



                fun tab' blanks tab_to   =   {   pp::tab    pp { blanks, tab_to, tabstops_are_every };             };

                fun ind i =   pp::indent (pp, i);

                fun cut () = cut' 0 -1;
                fun tab () = tab' 1  0;



                fun newline ()    =  pp::newline pp;

                fun rulename name =  pp::set_rulename_for_current_box (pp, name);


                ##############################################################################
                #                       pp.txt'
                #
                # The idea with pp.txt' is to replace
                # explicit calls to pp::break, pp::tab and pp::newline
                # with embedded ' '  '\t'  '\n'  chars:
                #
                #   pp.txt' blanks tab_go <string>:
                #       '\t'     in <string>:  treated as pp.tab 1 0
                #       '\n'     in <string>:  treated as pp::newline
                #       n blanks in <string>:  treated as pp::break { ifnotwrap => { blanks => n, tab_to => -1, tabstops_are_every },
                #                                                     ifwrap    => { blanks,      tab_to,       tabstops_are_every }
                #                                                   }
                fun txt'' (lit: Null_Or(pp::Pp -> String -> Void)) blanks  tab_to  string                       # 'lit' will be   NULL for 'txt', else   THE pp::lit  or  THE pp::endlit  for 'lit'/'endlit'.
                    = 
                    next 0
                    where
                        # If we think of prettyprinter as a simple
                        # compiler, this is the compiler's lexer,
                        # breaking up the input string into tokens
                        # drawn from typ::Phase1_Token.

                        len =   size string;

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

                                case c
                                    '\t' =>  do_tab     i;
                                    '\n' =>  do_newline i;
                                    ' '  =>  do_blanks (i, i+1);
                                     _   =>  do_other  (i, i+1);
                                esac;
                            fi

                        also
                        fun do_tab  i                                                                                   # Treat each \t in 'string' as a call to pp:tab 4.
                            =
                            {    pp::tab pp { blanks => 1,  tab_to => 0,  tabstops_are_every };
                                 next (i+1);
                            }

                        also
                        fun do_newline  i                                                                               # Treat each \n in 'string' as a call to pp:newline.
                            =
                            {    pp::newline pp;
                                 next (i+1);
                            }

                        also
                        fun do_blanks  (i, j)                                                                           # Treat a run of 'n' blanks in 'string' as a call to pp::nonbreakable_blanks (if in lit/endlit) or pp:break { blanks => n, indent_on_wrap => 4 } (if in txt).
                            =
                            {   fun do_blanks' (i, j)
                                    =
                                    case lit
                                        THE lit => pp::nonbreakable_blanks pp (j-i);                                    # We're doing a lit or endlit so the blanks turn into a simple typ::BLANKS token.
                                        NULL =>    pp::break' ( pp,                                                     # We're doing a txt so blanks turn into a typ::BREAK. 
                                                                 { ifnotwrap        => { blanks => j-i,  tab_to => -1,  tabstops_are_every },
                                                                   ifwrap           => { blanks,         tab_to,        tabstops_are_every }
                                                                 }
                                                               );
                                    esac;

                                if (j >= len)
                                    #
                                    do_blanks' (i, j);
                                else
                                    c =  string::get_byte_as_char (string, j);

                                    if (c == ' ')
                                        #
                                        do_blanks (i, j+1);                                                             # Scan to end of string of blanks.
                                    else
                                        do_blanks' (i, j);
                                        next j;
                                    fi;
                                fi;
                            }

                        also                    
                        fun do_other (i, j)                                                                             # Treat literally a run of non-\n, non-\t, non-blank chars in 'string'.
                            =
                            {
                                fun put_lit (i, j)
                                    =
                                    case lit
                                        NULL    =>      pp::lit pp (string::substring (string, i, j-i));                # We're doing a 'txt', so send plain chars as a typ::LIT.
                                        THE lit =>      lit     pp (string::substring (string, i, j-i));                # We're doing a 'lit' or 'endlit'; send plain chars as a typ::LIT or typ:ENDLIT respectively.
                                    esac;

                                if (j >= len)
                                    #
                                    put_lit (i, j);
                                else
                                    c =  string::get_byte_as_char (string, j);

                                    if  (c != ' '
                                    and  c != '\t'
                                    and  c != '\n'
                                    )
                                        do_other (i, j+1);                                                              # Scan to end of string of plain characters.
                                    else
                                        put_lit (i, j);
                                        next j;
                                    fi;
                                fi;
                            };
                    end;                                                                                                # fun output

                txt'   = txt'' NULL;
                txt    = txt'' NULL             0 -1;
                lit    = txt'' (THE pp::lit)    0 -1;
                endlit = txt'' (THE pp::endlit) 0 -1;

                { pp,
                  tabstops_are_every,
                  default_target_box_width,
                  default_left_margin_is,
                  default_wrap_policy => default_wrap_policy.name,                                                      # Circularity issues make it hard to include the complete value here.
                  box', wrap', cbox', cwrap',
                  box,  wrap,  cbox,  cwrap,
                  flush, close,
                  break',
                  cut', tab', cut, tab,
                  newline,
                  lit, endlit,
                  ind,
                  txt, txt',
                  rulename
                };  
            };

        fun make_prettyprinter  prettyprint_output_stream  options
            =
            make_standard_prettyprinter  prettyprint_output_stream  options;

        process_mill_options = pp::process_mill_options;

                                                                                                                        # Next four fns are conveniences for printing standard Mythryl constructs: lists, tuples, records and blocks.


        fun listx  (pp:Pp)  (do_element: X -> Void)  title  (elements: List(X))                                         # Print a list as either   [ val1, val2, ... ]  or
            =                                                                                                           #   [ val1,
            case (title, elements)                                                                                      #     val2,
                #                                                                                                       #     ...
                ("", []) => pp.lit "[]";                                                                                #   ]
                #
                _        => {   pp.box' 0 0 {.
                                    pp.lit title;
                                    pp.txt "[ ";
                                    pp.ind 2;

                                    do_elements elements;

                                    pp.ind 0;
                                    pp.txt " ";
                                    pp.lit "]";
                                };
                            }
                            where
                                fun do_element' element
                                    =
                                    {   do_element element;
                                        #
                                        pp.endlit ",";
                                        pp.txt " ";
                                    };

                                fun do_elements []               => ();
                                    do_elements [ element ]      => do_element element;
                                    do_elements (element ! rest) => {   do_element' element;
                                                                        do_elements rest;
                                                                    };
                                end; 
                            end;
            esac;

        fun tuple  (pp:Pp)  title  (elements:   List( Void -> Void ) )                                                  # Print a tuple as either   (val1, val2,  ...)  or
            =                                                                                                           #   ( val1,
            {   pp.box' 0 0 {.                                                                                          #     val2,
                    pp.lit title;                                                                                       #     ...
                    pp.txt "(";                                                                                         #   )
                    pp.ind 2;

                    do_elements elements;

                    pp.ind 0;
                    pp.cut ();
                    pp.lit ")";
                };
            }
            where
                fun do_element'  do_element
                    =
                    {   do_element();
                        #
                        pp.endlit ",";
                        pp.txt " ";
                    };

                fun do_elements []                  =>   ();
                    do_elements [ do_element ]      =>   do_element ();
                    do_elements (do_element ! rest) =>   {   do_element' do_element;
                                                             do_elements rest;
                                                         };
                end; 
            end;

        fun tuplex (pp:Pp)  (do_element: X -> Void)  title  (elements:   List(X) )                                      # Print a tuple as either   (val1, val2,  ...)  or
            =                                                                                                           #   ( val1,
            {   pp.box' 0 0 {.                                                                                          #     val2,
                    pp.lit title;                                                                                       #     ...
                    pp.txt "(";
                    pp.ind 2;

                    do_elements elements;

                    pp.ind 0;   
                    pp.cut ();
                    pp.lit ")";
                };
            }
            where
                fun do_element'  element
                    =
                    {   do_element element;
                        #
                        pp.endlit ",";
                        pp.txt " ";
                    };

                fun do_elements []               => ();
                    do_elements [ element ]      => do_element element;
                    do_elements (element ! rest) => {   do_element' element;
                                                        do_elements rest;
                                                    };
                end; 
            end;

        fun record  (pp:Pp)  title  (pairs: List( (String, Void -> Void) ) )                                            # Print a record as either   { key1 => val1,  key2 => val2,  ... }  or
            =                                                                                                           #   { key1 => val1,
            {   pp.box' 0 0 {.                                                                                          #     key2 => val2,
                    pp.lit title;                                                                                       #     ...
                    pp.txt "{ ";                                                                                        #   }
                    pp.ind 2;

                    do_pairs pairs;

                    pp.ind 0;
                    pp.txt " ";
                    pp.lit "}";
                };
            }
            where
                fun do_pair (key, value)
                    =
                    pp.box' 0 0 {.
                        pp.txt key;
                        if (key != "...")                                                                               # Special hack to support printing incomplete records: If key is "..." we ignore the value.
                            pp.ind 4;
                            pp.break' { ifnotwrap => { blanks => 0, tab_to => -1 },
                                           ifwrap => { blanks => 1, tab_to =>  0 }
                                      };
                            pp.txt " => ";
                            value ();
                        fi;
                    };

                fun do_pair' pair
                    =
                    {   do_pair pair;
                        #
                        pp.endlit ",";
                        pp.txt "  ";
                    };

                fun do_pairs []            =>   ();
                    do_pairs [pair]        =>   do_pair pair;
                    do_pairs (pair ! rest) =>   {   do_pair' pair;
                                                    do_pairs rest;
                                                };
                end; 
            end;

        fun block  (pp:Pp)  (expressions:   List( Void -> Void ) )                                                      # Print a block as either   { exp1;  exp2;  ... ]  or
            =                                                                                                           # {   exp1;
            {   pp.box' 0 0 {.                                                                                          #     exp2;
                    pp.txt "{";                                                                                         #     ...
                    pp.ind 4;

                    do_expressions expressions;

                    pp.ind 0;
                    pp.txt " ";
                    pp.lit "}";
                };
            }
            where
                fun do_expression'  do_expression
                    =
                    {   do_expression();
                        #
                        pp.endlit ";";
                        pp.txt "  ";
                    };

                fun do_expressions []                       =>  ();
                    do_expressions [ do_expression ]        =>  do_expression ();
                    do_expressions (do_expression ! rest)   =>  {   do_expression' do_expression;
                                                                    do_expressions rest;
                                                                };
                end; 
            end;



        ##################################################################################################
        # Backward compatibility stuff to make standard_prettyprinter a 100% drop-in replacement for base_prettyprinter:

        Prettyprint_Output_Stream       =  pp::Prettyprint_Output_Stream;
        Traitful_Text                   =  pp::Traitful_Text;
        Texttraits                      =  pp::Texttraits;
        Left_Margin_Is                  == pp::typ::Left_Margin_Is;

        fun flush_prettyprinter         (pp:Pp)         =  pp::flush_prettyprinter              pp.pp;
        fun close_prettyprinter         (pp:Pp)         =  pp::close_prettyprinter              pp.pp;

        fun shut_box                            (pp:Pp)         =  pp::shut_box                         pp.pp;

        fun traitful_text                       (pp:Pp) s       =  pp::traitful_text                    pp.pp s; 
        fun lit                                 (pp:Pp) s       =  pp::lit                              pp.pp s; 
        fun endlit                              (pp:Pp) s       =  pp::endlit                           pp.pp s; 

        fun push_texttraits                     (pp:Pp,ts)      =  pp::push_texttraits                 (pp.pp,ts);              
        fun pop_texttraits                      (pp:Pp)         =  pp::pop_texttraits                   pp.pp;

        fun indent                              (pp:Pp, i)      =  pp::indent                          (pp.pp, i);
        fun break                               (pp:Pp) a       =  pp::break                            pp.pp a;
        fun blank                               (pp:Pp) i       =  pp::blank                            pp.pp i;
        fun cut                                 (pp:Pp)         =  pp::cut                              pp.pp;
        fun newline                             (pp:Pp)         =  pp::newline                          pp.pp;
        fun nonbreakable_blanks                 (pp:Pp) i       =  pp::nonbreakable_blanks              pp.pp i;
        fun tab                                 (pp:Pp) i       =  pp::tab                              pp.pp i;

        fun control                             (pp:Pp) f       =  pp::control                          pp.pp f;

        fun nblanks                                     i       =  pp::nblanks                                i;
        fun set_rulename_for_current_box        (pp:Pp, name)   =  pp::set_rulename_for_current_box (pp.pp, name);

        fun get_prettyprint_output_stream       (pp:Pp)         =  pp::get_prettyprint_output_stream    pp.pp;

        fun with_standard_prettyprinter  output_stream  pp_args  (f: Prettyprinter -> Void)             # Compared to the make_standard_prettyprinter() approach, this
            =                                                                                                   # approach makes it harder to forget to flush+close the prettyprinter.
            {   pp =   make_standard_prettyprinter  output_stream  pp_args;
                #
                f pp;

                close_prettyprinter  pp;
            };


        fun seqx
                (separator:     Void -> Void)
                (print_element: X -> Void)
                (elements:      List(X))
            =
            print_elements  elements
            where
                fun print_elements [element]
                        =>
                        print_element element;

                    print_elements (element ! rest)
                        =>
                        {   print_element  element;
                            separator ();
                            print_elements rest;
                        };

                    print_elements [] =>   ();
                end;
            end;





        # End of backward compatibility stuff.
        ##################################################################################################

    };                                                                                  # package standard_prettyprinter
end;



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext