diff options
Diffstat (limited to 'web/noweb/src/c/modules.nw')
-rw-r--r-- | web/noweb/src/c/modules.nw | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/web/noweb/src/c/modules.nw b/web/noweb/src/c/modules.nw new file mode 100644 index 0000000000..a3c5f0069c --- /dev/null +++ b/web/noweb/src/c/modules.nw @@ -0,0 +1,326 @@ +% 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; + } +} |