% Copyright 1991 by Norman Ramsey.  All rights reserved.
% See file COPYRIGHT for more information.
@ 
\subsection{The internal structure of modules (chunks)}
This code is so old, it dates back to when I used to call ``chunks'' ``modules.''
@
\subsubsection{Assembling modules from parts}
Modules are linked lists.
Each element of the list is either a string or a reference to another 
module name.
During expansion of a module, strings are printed and module references
are expanded.
The structure of module references must be a dag or else [[expand]] will
complain about a cycle.
<<header>>=
typedef struct location {       /* identify lines of source */
    char *filename;
    int lineno;
} Location;

typedef enum parttype {STRING=1, MODULE, NEWLINE} Parttype;

struct modpart {
    Parttype ptype;             /* type of fragment: STRING, MODULE, NEWLINE */
    char *contents;
    Location loc;               /* for String, where's it from ? */
    struct modpart *next;
};
@ 
In order to facilitate expansion of all roots (and to identify roots), 
I keep a use counter in each module.
<<header>>=
typedef struct module {
    char *name;
    int usecount;
    struct modpart *head, *tail;
} *Module;
<<header>>=
Module newmodule(char *modname);         /* create a new, blank module */
<<C functions>>=
Module newmodule (char *modname) {
    Module p = (Module) malloc (sizeof (struct module));
    checkptr(p);
    p->name = strsave(modname);
    p->usecount = 0;
    p->head = p->tail = NULL;
    return p;
}
<<*>>=
static char rcsid[] = "$Id: modules.nw,v 2.26 2008/10/06 01:03:05 nr Exp nr $";
static char rcsname[] = "$Name: v2_12 $";
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "modules.h"
#include "modtrees.h"
#include "errors.h"
#include "columns.h"
#include "strsave.h"

<<Function declarations>>
<<local data>>
<<C functions>>
@
<<header>>=
#define addstring(MP,S,L) add_part(MP,S,STRING,&L)
        /* add a string to a module definition (stripping final newline) */
#define addmodule(MP,S) add_part(MP,S,MODULE,0)
        /* add a module reference to a module definition (stripping final newline) */
#define addnewline(MP) add_part(MP,0,NEWLINE,0)
void add_part (Module mp, char *s, Parttype type, Location *loc);
<<C functions>>=
void add_part (Module mp, char *s, Parttype type, Location *loc) {
    struct modpart *p = newmodpart(type,s,loc);
    append (mp,p);
}
<<C functions>>=
static struct modpart *
newmodpart(int type, char *s, Location *loc) {
    struct modpart *p = (struct modpart *) malloc (sizeof (struct modpart));
    checkptr(p);
    p->ptype = type;
    if (s) {
        p->contents = strsave(s);
        <<strip final newline from p->contents>>
    }
    if (loc) p->loc = *loc;
    p->next = NULL;
    (void)rcsid; /* avoid a warning */
    (void)rcsname; /* avoid a warning */
    return p;
}
<<C functions>>=
static
void append(Module mp, struct modpart *p) {
    /* append p to mp's list of modparts */
    if (mp->head == NULL) {
        mp->head = mp->tail = p;
    } else {
        mp->tail->next = p;
        mp->tail = p;
    }
}
<<strip final newline from p->contents>>=
{ int k = strlen(p->contents)-1;
  if (p->contents[k] == '\n') p->contents[k] = '\0';
  else impossible("input line doesn't end with newline");
}
@
<<Function declarations>>=
static struct modpart *
newmodpart(int type, char *s, Location *loc);   /* create a new module part */

static
void append(Module mp, struct modpart *p);
@
\subsubsection{Expanding modules}
[[expand]] returns 0 if all is well, nonzero if error.
A [[struct parent]] chain lists modules currently being expanded, 
so I can detect cycles.
[[partial_distance]] is the size of the line already written out when
[[expand]] is called.
<<header>>=
typedef struct parent {
    Module this;
    struct parent *parent;
} *Parent;

int expand (Module mp, int indent, int partial_distance, Parent parent, 
            char *locformat, FILE *out);
        /* expand a module, writing to file out */
<<local data>>=
static char *lastfilename = 0;
static int lastlineno = -1;
<<header>>=
void resetloc(void);
<<C functions>>=
void resetloc(void) {
  lastfilename = 0;
  lastlineno = -1;
}
@
[[indent]] is the amount by which this chunk should be indented.
[[partial_distance]] is the width of what has already been written to the current line.
<<C functions>>=
int expand (Module mp, int indent, int partial_distance, Parent parent,  
            char *locformat, FILE *out) {
    struct modpart *p;
    Module newmod;
    int error=Normal;
    struct parent thismodule; /* the value only matters when we're expanding a module */

    <<set [[thismodule = {mp, parent}]]>>
    <<if [[mp]] is an ancestor, complain and return>>

    for (p=mp->head; p!=NULL; p=p->next) {
        switch (p->ptype) {
            case STRING:  <<print a string>>;  break;
            case MODULE:  <<expand a module>>; break;
            case NEWLINE: <<print a newline>>; break;
            default: impossible("bad part type");
        }
    }
    return error;
}
@
If the previous line was not partial, this string starts a fresh line
and it is necessary to indent.
When emitting [[#line]] directives, don't indent---instead try to 
preserve the original column of the information in the source, as
well as the line number.
This means indenting by [[partial_distance]] if a [[#line]] directive 
is emitted, {\em except} when it's the first line of a module (hack! hack!).
[[printloc]] returns nonzero when [[#line]] is actually emitted.

<<print a string>>=
if (*(p->contents) != '\0') {
    if (*locformat) {
        if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head))
              indent_for(partial_distance, out);
    } else if (partial_distance == 0) {
        indent_for(indent, out);
        partial_distance = indent;
    }
    fprintf(out,"%s",p->contents);
    partial_distance = limitcolumn(p->contents, partial_distance);
}
<<print a newline>>=
partial_distance = 0;
putc('\n', out);
lastlineno++;
@
Previously, when we expanded a module, the partial distance became the
new indentation level.  This was clearly wrong when we were at the
beginning of the line, so I just hacked a special case for no
[[locformat]] and zero [[partial_distance]].  It's essentially what
would be done above for a string of length zero if that weren't hacked
out as a special case.
This change probably blows the case where the module being expanded is
empty.
<<expand a module>>=
newmod = lookup(p->contents);
if (newmod==NULL) {
    errormsg (Error, "undefined chunk name: @<<%s@>>", p->contents);
    error=Error;
} else {
    int retcode;
    if (*locformat == 0 && partial_distance == 0) {
        indent_for(indent, out);
        partial_distance = indent;
    }
    retcode = expand (newmod, partial_distance, partial_distance,
                      &thismodule, locformat, out);
    if (retcode > error) error = retcode;
}
partial_distance = limitcolumn(p->contents, partial_distance + 2) + 2; 
                                /* account for surrounding brackets */
@
<<set [[thismodule = {mp, parent}]]>>=
thismodule.this = mp;
thismodule.parent = parent;
<<if [[mp]] is an ancestor, complain and return>>=
if (seekcycle(mp, parent)) {
    errormsg(Error, "@<<%s@>>", mp->name);
    return Error;
}
<<Function declarations>>=
static int seekcycle(Module mp, Parent parent);
<<C functions>>=
static int seekcycle(Module mp, Parent parent) {
    if (parent == NULL) {
        return 0;
    } else if (parent->this == mp || seekcycle(mp, parent->parent)) {
        if (parent->this == mp)
            fprintf(stderr, "Cyclic code chunks: ");
        fprintf(stderr, "@<<%s@>> -> ", parent->this->name);
        return 1;
    } else {
        return 0;
    }
}
@ 

Printing locations means emitting [[#line]] numbers.
[[printloc]] emits a [[#line]] only if the line emitted is out of sequence.
It returns nonzero when [[#line]] is emitted; zero otherwise.
The flag [[partial]] tells whether the preceding line was partial.
If so, then a newline is necessary before [[#line]] can be emitted.
<<header>>=
int printloc(FILE *fp, char *fmt, Location loc, int partial);
<<C functions>>=
int printloc(FILE *fp, char *fmt, Location loc, int partial) {
    char *p;
    if (*fmt
    && (loc.filename!=lastfilename || lastlineno != loc.lineno)) {
        if (partial) putc('\n',fp);
        <<write out the line number pragma>>
        lastfilename = loc.filename;
        lastlineno = loc.lineno;
        return 1;
    } else return 0;
}
<<write out the line number pragma>>=
for (p = fmt; *p; p++) {
    if (*p == '%') {
        switch (*++p) {
            case '%': putc('%', fp);                             break;
            case 'N': putc('\n', fp);                            break;
            case 'F': fprintf(fp, "%s", loc.filename);           break;
            case 'L': fprintf(fp, "%d", loc.lineno);             break;
            case '-': case '+': 
                        if (isdigit(p[1]) && p[2] == 'L') {
                          fprintf(fp, "%d", *p == '+' ? loc.lineno + (p[1] - '0')
                                                      : loc.lineno - (p[1] - '0'));
                          p += 2;
                        } else
                          <<complain about bad format character>>
                      break;            
            default:  <<complain about bad format character>>   break;
        }
    } else putc(*p, fp);
}
<<complain about bad format character>>=
{ static int complained = 0;
  if (!complained) {
    errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt);
    complained = 1;
  }
}
@

\subsubsection{Other operations on completed modules}
For more elegant-looking output, we provide a primitive that removes
the final newline from a module.

We will use [[apply_each_module]] to remove trailing newlines
from each module.
This serves two purposes: first, our output looks nicer, and, second,
it makes it possible to define a module that doesn't end with newline.

As they are read in, all modules end with newline, by definition.%
\footnote{Except of course if this input came from an uncorrected nuweb file, 
in which case modules needn't end with newlines.  Eventually one hopes
I'll fix numarkup to insert exactly the newlines that would be removed here.}
We remove all of the trailing newlines.
<<header>>=
void remove_final_newline (Module mp);
        /* remove trailing newline that must be in module */
<<C functions>>=
void remove_final_newline (Module mp) {
        /* remove trailing newline that must be in module */
    if (mp->tail==NULL) /* module has no text */
        return;
    if (mp->tail->ptype != NEWLINE)
        /* do nothing --- this is possible with nuweb front end
              formerly: impossible("Module doesn't end with newline"); */
        ;
    else if (mp->tail == mp->head)
            mp->head = mp->tail = NULL;
    else {
        struct modpart *p = mp->head;
        while (p->next != mp->tail) p = p->next;
        p->next = NULL;
    }
}
