% noweb source
%
% $Id: prettycee2html.nw,v 1.1 1996/12/16 18:53:15 gaertner Exp $
%
% History at end
%
\documentclass{article}
\usepackage{noweb}
\usepackage{pretzel-latex}
\begin{document}

\title{A Prettyprinter for {\tt noweb} that beautifies C to {\sc
    ASCII}\thanks{This file is nealy an exact copy of {\tt
      prettycee.nw}.}}
\maketitle

\tableofcontents

\section{Introduction}

This document describes the implementation of a prettyprinter for {\tt
noweb} that outputs to an {\sc ASCII} terminal. There are two more
files that contain a complete description of how the actual
prettyprinter acts and what it outputs (the files {\tt cee2html.ft}
and {\tt cee2html.fg}). By tuning these two file, you can make the
output go to a line printer or look good when using {\tt HTML}.

It is written in C++ and conforms to an
API given to me by Norman Ramsey.

This file is nealy an exact copy of {\tt prettycee.nw}.

\section{Interface}

[This section is the API by Norman Ramsey. Additional text by the
current author is put in angled brackets.]

Here is a suitable API for a prettyprinter for noweb.
Input to be prettyprinted is a sequence of strings, newlines, and
chunks used.  A [[STRING]] is always free of newlines.

<<API for prettyprinter>>=
typedef enum parttype {
  START_OF_CHUNK=1, END_OF_CHUNK, STRING, USE_CHUNK, NEWLINE,
  LITERAL, DEFINITION, WHATSIT
} Parttype;
@

The prettyprinter calls back into the application as follows:

<<API for prettyprinter>>=
typedef void (*PrettyPrinterCallback) (void *closure, Parttype type, char *contents);
@

The [[contents]] must be
\begin{quote}
\begin{tabular}{ll}
[[START_OF_CHUNK]]&The name of the chunk being prettyprinted\\
[[END_OF_CHUNK]]&[[NULL]]\\
[[STRING]]&A string with no newlines\\
[[LITERAL]]${}^*$&A string with no newlines, to get no further formatting\\
[[USE_CHUNK]]&The name of the chunk referred to\\
[[NEWLINE]]&[[NULL]]\\
[[DEFINITION]]${}^*$&An identifier whose definition is in this chunk.\\
[[WHATSIT]]&An uninterpreted string passed to the callback function
\end{tabular}
\end{quote}
Items marked with a star ($*$) are legal only when the prettyprinter
is calling back into the application.
The identifier given in [[DEFINITION]] isn't printed, so if the
prettyprinter wants to print it, it has to send it twice.

@

The prettyprinter exports two calls to the application.

<<API for prettyprinter>>=
typedef struct prettyprinter *PrettyPrinter;

extern PrettyPrinter new_prettyprinter(PrettyPrinterCallback callback, void *closure);
extern void prettyprint(PrettyPrinter pp, Parttype type, char *contents);

@

The prettyprinter accepts calls to the [[prettyprint]] procedure.
Any call to [[prettyprint]] may result in one or more callbacks to the
procedure registered when [[new_prettyprinter]] is called.
For any given [[PrettyPrinter]], the application must call
[[prettyprint]] with [[type==END_OF_CHUNK]] exactly once, and that
call must be the last call.

<<header>>=
<<API for prettyprinter>>
@








\section{Implementation}

\subsection{Overview}

The implementation will use a prettyprinter generated by
the Pretzel system. A Pretzel prettyprinter comes in the form of a
class:
 %
\begin{verbatim}
     // abstract base class of prettyprinter:

  #include<iostream>
  #include"attr.h"
  #include"output.h"

  class Pparse {
  public:
    Pparse() {};
    ~Pparse() {};
    virtual int prettyprint(istream*, ostream*) = 0;
    virtual int prettyprint(istream*, Output*) = 0;
  };

     // actual prettyprinter:

  #ifndef PPARSE_NAME
  #define PPARSE_NAME Ppparse
  #endif

  class PPARSE_NAME : public Pparse {
  public:
    PPARSE_NAME();
    ~PPARSE_NAME();
    int prettyprint(istream*, ostream*);
    int prettyprint(istream*, Output*);

    void debug_on();
    void debug_off();
  };
\end{verbatim}
 %
The idea of the implementation is to translate calls to the API
functions into suitable sequences for the Pretzel prettyprinter. This
is not the nicest way of doing it, but it seems the fastest solution
at the moment. (A more elegant way would be to change Pretzel
internals, but that is a different story.)

I have analyzed the code of {\tt pretty.nw} and am using it as the
filter which generates the calls to this prettyprinter. There will
only be calls using the [[NEWLINE]], [[STRING]], [[USE_CHUNK]] and
[[WHATSIT]] part types. The final call to [[prettyprint]] is with an
[[END_OF_CHUNK]].

Here's an overview over the implementation.

<<*>>=
/* implementation of a noweb prettyprinter using Pretzel */

<<Header files to include>>
<<The prettyprinter itself>>
<<The prettyprinter functions>>

@ Of course, we include our own header file, which is {\tt pretty.h}
from the noweb distribution.

<<Header files to include>>=
#include "pretty.h"

@ Because a Pretzel prettyprinter needs to see the entire text before
it can start to prettyprint, we have to wait until an [[END_OF_CHUNK]]
is sent, collecting all the strings and newlines coming in
along the way.  Having done this, we have a view of the entire code
part, including chunk names, etc.~and can send all this to the Pretzel
prettyprinting class (using C++ [[strstream]]s). Pretzel returns a
long string again which is chopped up into lines and sent back to the
application via the callback function.

There are a few problems here:
 %
\begin{itemize}
\item Chunk uses have to be known to the prettyprinter in order to get
  the formatting (indentation etc.) right. This means that they have
  to be part of the prettyprinting grammar of pretzel.
\item The [[WHATSIT]] parttypes would mess up the prettyprinter if
  they were sent to it because they will inevitably be seen by the
  parser and would mess up parsing! So we sent a [[WHATSIT]] directly
  back to the application if we see it.
\end{itemize}


\subsection{The Actual Prettyprinter}

This the actual prettyprinter. We'll use a [[struct]] to conform with
the API. A [[class]] would be nicer though. Inside the prettyprinter
we have to store the callback function and the [[closure]] parameter,
which is a kind of user data. Also, we need a [[strstream]] object to
collect all the strings coming in.

<<The prettyprinter itself>>=
struct prettyprinter {
    PrettyPrinterCallback ppcb;
    void* user_data;
    strstream* to_be_pped; /* space for collecting code */
};

@
<<Header files to include>>=
#include <strstream.h>

@


\subsection{The Prettyprinter Functions}

To create a new prettyprinter for a chunk, we simply create a new
[[PrettyPrinter]] object and store the interesting stuff in the right
places. (Sorry, this is C++, but we're using [[iostreams]] anyway.)

<<The prettyprinter functions>>=
PrettyPrinter new_prettyprinter( PrettyPrinterCallback callback,
        void* closure)
{
    PrettyPrinter pp = (PrettyPrinter)
            malloc(sizeof(struct prettyprinter));
    pp->ppcb = callback;
    pp->user_data = closure;
    pp->to_be_pped = new strstream;
    return(pp);
}

@
<<Header files to include>>=
#include <stdlib.h>

@ Now for the actual [[prettyprint]] function. Its task is to
prettyprint the incoming strings and send them back to the application
via the callback.

Internally, [[prettyprint]] is a giant switch. It looks at the type of
thing that is coming in and decides what to to.

<<The prettyprinter functions>>=
void prettyprint(PrettyPrinter pp, Parttype type, char *contents)
{
    switch (type) {
        <<Cases of [[prettyprint]] switch>>
    default : cerr << "Oops. There's a parttype I don't understand."
                << endl;
                exit(1);
    }
}

@ Now let's look at the different cases. We know from the start that
the case of [[END_OF_CHUNK]] will be the most complicated, because all
work is done there. So let's leave this until the end. We'll have the
simple cases first, e.g.~the [[STRING]] case.

@

When [[type==STRING]], the incoming string is a bit of code that needs
to be mangled and thus simply appended to the [[strstream]] within the
prettyprinter.

<<Cases of [[prettyprint]] switch>>=
case STRING : *(pp->to_be_pped) << contents; break;

@ A similar thing is done when a [[NEWLINE]] arrives.

<<Cases of [[prettyprint]] switch>>=
case NEWLINE : *(pp->to_be_pped) << "\n"; break;

@ Now for one of the tricky bits, the [[USE_CHUNK]] case.

Normally, we would call the callback with the chunk name at that
moment, when the prettyprinted text before the chunk name has been
output (via the callback) and before the text following it is output.
Unfortunately, in the current version of Pretzel, we do not have
real control where the chunk name appears in the output and thus don't
have an obvious chance of calling the callback directly here.

We use a trick here: We know that we'll chop up the prettyprinted
output into lines and output these lines one at a time later on. So
we'll put a full line of text referring to the chunk into the
prettyprinted output. This line has to be spotted by the prettyprinter
and left unchanged. When the prettyprinter has done his work, we'll
check all the lines for their starting characters and call the
callback with [[type==USE_CHUNK]] if a line starts with [[@use]].

<<Cases of [[prettyprint]] switch>>=
case USE_CHUNK : *(pp->to_be_pped) << "\n@use " << contents << "\n";
    break;

@

The last case for now is the [[WHATSIT]] case. These things can
be ignored by the pretzel prettyprinter but they will nevertheless
have to be mangled by it. We follow the same plan as above and add the
incoming line to the string that is to be prettyprinted. The
prettyprinter has to spot this line and leave it unchanged.

We assume that these lines have an [[@]] sign at their beginning.

<<Cases of [[prettyprint]] switch>>=
case WHATSIT : *(pp->to_be_pped) << "\n" << contents << "\n";
    break;

@

\begin{center}
  \large\bf The following chunks contains the only changes to
  {\tt prettycee.nw}.
\end{center}


If an [[END_OF_CHUNK]] is received, we expect it to be the last call
to the current prettyprinter. All code sent to us before has to be
prettyprinted now and output through the callback to the application.
This is where the Pretzel function comes into action.

Say, we have a Pretzel prettyprinter for your favourite programming
language. We need to include its header to be able to use it. As we
don't want to use the default output type (which is \LaTeX), we have
to include the special {\sc ASCII} output header file.

<<Header files to include>>=
#include "Ppparse.h"
#include "asciioutput.h"

@ Ok, now we can outline the case. In general, we'll top off the string
and feed it to the [[prettyprint]] function. This will return stuff on an
[[ostream]], which is replaced again by a [[strstream]] by us. We can
assume that line breaks and so on have been inserted into the output
stream by the [[ascii_terminal_output]] already so it's okay to output
line by line.

<<Cases of [[prettyprint]] switch>>=
case END_OF_CHUNK :
  {
    strstream has_been_pped;
    Ppparse pretzelpp;
    *(pp->to_be_pped) << ends;   /* end up the string */
/*    pretzelpp.debug_on(); */
    Ascii_terminal_output ascii_outp(has_been_pped);
    pretzelpp.prettyprint( pp->to_be_pped, &ascii_outp );
    <<Chop resulting text into lines and send them to application>>
  }
  break;

@

Chopping the stuff inside [[has_been_pped]] into lines is not very
nice, but feasible. Is there a way to do it more elegantly using the
`[[<<]]' operator? For the time being we'll use the [[strtok]]
function from the {\tt string.h} header file.

<<Header files to include>>=
#include <string.h>
@

The text which comes from the prettyprinter will probably not look
faintly as nice as the original code: Special macros will be added (in
the \LaTeX{} case), indentation will have changed, identifiers may
have swapped places. This is why we'll send the individual lines as
[[LITERAL]] lines to the application. It will ensure that the line
will not get mangled further by {\tt noweb} back ends.

If we encounter a line beginning with [[@]], this is a line to be sent
to the application directly. Assuming that the above lines haven't
been changed by the prettyprinter, this case will cover the
[[USE_CHUNK]] case as well as all other cases of {\tt noweb} keywords.

<<Chop resulting text into lines and send them to application>>=
char* entire_pped_code = has_been_pped.str();
char* line = strtok(entire_pped_code, "\n");
do {
  if (strlen(line) == 0) {
      (pp->ppcb)(pp->user_data, NEWLINE, NULL);
      continue;
  }
  if (*line == '@') {
      (pp->ppcb)(pp->user_data, WHATSIT, line);
  } else {
      (pp->ppcb)(pp->user_data, LITERAL, line);
      (pp->ppcb)(pp->user_data, NEWLINE, NULL);
  }
} while ( (line = strtok( NULL, "\n" )) != NULL );

@ Finally, it's our responsibility to cleanup the output [[strstream]],
because we've called the [[str()]] function on it.

<<Chop resulting text into lines and send them to application>>=
delete entire_pped_code;

@





\end{document}


%
% $Log: prettycee2html.nw,v $
 * Revision 1.1  1996/12/16 18:53:15  gaertner
 * Initial revision
 *
%
%
