PreviousUpNext

15.3.343  src/lib/prettyprint/big/src/core-prettyprinter.api

## core-prettyprinter.api
#

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

# This API is implemented in:
#
#     src/lib/prettyprint/big/src/core-prettyprinter-g.pkg
#
api Core_Prettyprinter {
    #
    package typ: Core_Prettyprinter_Types;

    Prettyprinter;
    Pp  =          Prettyprinter  ;
    Npp = Null_Or( Prettyprinter );

    Prettyprint_Output_Stream;

    Traitful_Text;                                      # Styled_Strings wrap a string plus blink/bold/color/... information

    Texttraits;

    open_box:                   (Prettyprinter, typ::Left_Margin_Is, typ::Wrap_Policy, Int) -> Void;                            # 'Int' == target_width.

    indent:                     (Prettyprinter, Int) -> Void;                                                           # 

    break':                     (Prettyprinter,    { ifnotwrap:  { blanks: Int,   tab_to: Int,   tabstops_are_every: Int },
                                                        ifwrap:     { blanks: Int,   tab_to: Int,   tabstops_are_every: Int }
                                                      }
                                )
                                -> Void;

    horizontal:                                 typ::Wrap_Policy;
    vertical:                                   typ::Wrap_Policy;
    normal:                                     typ::Wrap_Policy;
    ragged_right:                               typ::Wrap_Policy;

    make_prettyprinter:                 Prettyprint_Output_Stream -> List(typ::Prettyprinter_Configuration_Args) -> Pp;

    process_mill_options
        :
        List (typ::Prettyprinter_Configuration_Args)
        ->
        { default_target_box_width:     Int,
          default_wrap_policy:          typ::Wrap_Policy,
          default_left_margin_is:       typ::Left_Margin_Is,
          tabstops_are_every:           Int
        };

    get_prettyprint_output_stream:              Pp -> Prettyprint_Output_Stream;

    flush_prettyprinter:                        Pp -> Void;
    close_prettyprinter:                        Pp -> Void;

    traitful_text:      Pp -> Traitful_Text -> Void;

    set_rulename_for_current_box:       (Pp, String) -> Void;

    nblanks:            Int -> String;                                          # Temporary convenience function. Returns a string containing (only) given number of blanks.
};


####################################################################################################
#
# Note [1]:  Prettyprint mill motivation and overview.
#
# Nomenclature:
#    Prettyprinter:     A package which renders a given datastructure as indented text.
#                       A system will usually have many datastructures and consequently many prettyprinters. 
#    Prettyprint mill:  A package which provides the underlying infrastructure needed by all prettyprinters.

# This dirtree contains my third (2014) rewrite of the Mythryl prettyprint mill.
# It is based on John H Reppy's (2005) SML/NJ prettyprint mill, which in turn
# is based on Pierre Weis' 1995 Ocaml prettyprint mill.

# The prettyprint system has a layered architecture.
# Taking a leaf from the X-server playbook, the intent is:

#     Core layer provides mechanism without policy.
#     Base layer adds client-code conveniences.
#     Standard layer implements more conveniences and any required policy.
#     Data-structure-specific prettyprinters are built on the standard layer.

# The central line of development here is

#       core-prettyprinter-g.pkg                        # The central formatting engine.  Exports an api of minimal complexity.
#       base-prettyprinter-g.pkg                        # Wraps the core prettyprint mill with an API more convenient to code clients.
#   standard-prettyprinter-g.pkg                        # Wraps the base prettyprint mill with additional conveniences.
#   standard-prettyprinter.pkg                  # The standard instantiation of the previous, used pervasively
#                                                       # in prettyprinters throughout the codebase.
#
# Independently of the layering, the codebase is also structured to allow
# prettyprinters to be built which output text in various formats such as
# plain ascii, ascii with ansi-terminal escape sequences for bold etc,
# and html, via the 'out' and 'tt' arguments to the above generics.
# However at present in practice we use plain ascii exclusively.


# Mythryl is assembled from parts built at many institutions, which tend
# to all instantiate their own prettyprint mills for no very good reason.
# I'm working to replace these with standard-prettyprinter.pkg, but
# for the moment I've settled on concentrating them in this directory
# at least so the redundancy is more obvious and easier to work on.
# These redundant prettyprint mills are built on  base-prettyprinter-g.pkg.


# The core concept used to structure text to be prettyprinted is the 'box'.

# A box represents text to be printed at a given level of indentation.  A box
# has contents to be printed and a target width-in-chars.  It tries to format
# the contents to fit within its assigned width.  The box width is interpreted
# as a soft limit to be respected on a best-effort basis, rather than a hard
# limit to be obeyed at all costs.

# In general a given box may be printed either monoline or multiline, depending
# on the size and complexity of the subexpressions within it.  One of the core
# responsibilities of core-prettyprinter-g.pkg is to decide which boxes to
# print monoline and which to print multiline.  This is done basically just by
# first trying to print it monoline, and retreating to multiline if that does
# not work.



# In slightly more detail (but still simplified), the prettyprint mill sees
# the contents of a box as consisting of a list of elements drawn from:

#  o  TEXT, just a passive string of characters.
#  o  BOX, a recursively nested box.
#  o  NEWLINE, which the mill renders by doing a newline and then indenting
#           to the left margin of the current box by printing blanks.
#  o  BREAK, which the mill is free to render as either a blank or a NEWLINE.
#     When rendered as a NEWLINE, we say the break has been "wrapped".

# The Ocaml and SML/NJ prettyprint mills process input line-by-line as a
# stream.  The resulting limited context makes it hard to wrap breaks in
# an esthetically satisfying way, so the Mythryl prettyprint mill
# instead buffers a complete set of nested boxes before beginning
# formatting, allowing use of more sophisticated wrapping algorithms.
# (This takes more memory, but ram is much cheaper today than in 1995
# when the original Ocaml prettyprint mill was written.)

# To a first approximation the algorithm used is:

#  o  Format innermost boxes first.  Formatting of a given box is independent
#     of boxes exterior to it, and depends only on the height and width of
#     boxes immediately within it.  (Formatting innermost boxes first means
#     that when a given box is formatted, the height and width of subboxes
#     within it are known exactly.  This makes formatting easier to implement
#     and makes the results of formatting more intuitively understandable.)

#  o  To format a box, first decide if it can be formatted monoline.
#     A box can be formatted monoline if:
#       o It contains no NEWLINEs.
#       o All subboxes are monoline.
#       o The summed length of all box contents, including subboxes,
#         treating BREAKs as blanks, is less than the assigned box width.

#  o  If the box is monoline, just print it as a horizontal string of chars:
#       o Print BREAKs as blanks.
#       o Print TEXT elements in the obvious way -- output a string of chars.
#       o Print monoline subboxes just like TEXT elements.
#       o There are no NEWLINES to print, by definition of monoline box.
#       o There are no multiline subboxes to print,  by definition of monoline box.

#  o  If the box is multiline:
#       o Print NEWLINEs by doing a newline and spacing over to assigned left margin of box.
#       o Print BREAKs as newlines.
#       o Start the box as though it started with a NEWLINE.
#       o Print TEXT elements in the obvious way -- output a string of chars.
#       o Print monoline subboxes as though they were TEXT elements.
#       o Print multiline subboxes recursively just like current multiline box.

# (You may want to read the above twice;  if you understand it, using and maintaining
# these packages should be fairly straightforward, otherwise it is likely to be an
# exercise in frustration and wasted time.)



# In practice to get acceptable prettyprint results and to be flexible enough to
# accomodate different tastes, we need something  more featureful than the above:

#  o  Different users want different base indentation distances, so we want tabstop
#     spacing to be configurable on a per prettyprinter instance basis.

#  o  Prettyprinters may want different boxes to be indented different numbers of
#     tabstops.

#  o  Sometimes we want indentation by a minimum of (say) one blank, other times we
#     are content so long as the box winds up on a tabstop.

#  o  Sometimes we want to move (say) two tabstops, other times we want to
#     move to (say) an even-numbered tabstop.

#  o  Prettyprinters may want some boxes to be indented relative to the left margin
#     but other boxes to be indented relative to the current cursor position.

#  o  A user may want an non-wrapped BREAK to render not just as a single blank, but
#     as several blanks and/or a move-to-tabstop command.

#  o  Prettyprinters may want different boxes to use different algorithms for deciding
#     which BREAKs to wrap and which to leave as blanks.  The two simplest policies are:

#         HORIZONTAL:     Wrap no BREAKs in the box -- pack box contents horizontally.
#         VERTICAL:       Wrap all BREAKs in the box -- pack box contents vertically.

#     For formatting code and datastructures, the workhorse policy is:

#         ALIGN:          Align box contents horizontally if possible, else vertically.
#                         I.e., wrap no BREAKs if the result fits in the box width (monoline box)
#                         else wrap all BREAKs (multiline box).

#     For running text, the traditional policy is:

#         RAGGED_RIGHT:   Paragraph-style wrapping where each line is wrapped at
#                         the last possible BREAK consistent with fitting the line
#                         in the given box width.
#
#
# FUTURE DIRECTIONS:
# If we wind up wanting more flexibility, one try might be defining in
#     src/lib/prettyprint/big/src/core-prettyprinter.api
# a new Phase1_Token case CUSTOM_BREAK with the semantics that the
# end-user supplies a function which is called after the regular wrapping
# pass and which is given full access to the box.contents (and to
# print_box(box.contents) in order to make a wrap decision; it would
# probably return { wrap: Boolean, blanks: Int, tab_to: Int }. This
# would allow the custom code to look at the mono/multiline status of
# all immediate sub-boxes, peer inside subboxes, do a test rendering
# and look for parallel constructs, whatever.
#
# Or we could just accept a post-optimize function which is allowed
# rewrite the post-wrap/pre-print box-tree arbitrarily... that might
# be more to the point if we want to get really sophisticated along
# the lines of rendering parallel constructs as parallel text.  If
# so, we should make explicit in the post-wrap tree the decisions
# which are currently made in the fly by the print pass.



## COPYRIGHT (c) 2005 John Reppy (http://www.cs.uchicago.edu/~jhr)
## All rights reserved.
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext