summaryrefslogtreecommitdiff
path: root/web/noweb/src/c/modules.nw
diff options
context:
space:
mode:
Diffstat (limited to 'web/noweb/src/c/modules.nw')
-rw-r--r--web/noweb/src/c/modules.nw326
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;
+ }
+}