% 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. <
>= 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. <
>= typedef struct module { char *name; int usecount; struct modpart *head, *tail; } *Module; <
>= Module newmodule(char *modname); /* create a new, blank module */ <>= 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 #include #include #include #include "modules.h" #include "modtrees.h" #include "errors.h" #include "columns.h" #include "strsave.h" <> <> <> @ <
>= #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); <>= void add_part (Module mp, char *s, Parttype type, Location *loc) { struct modpart *p = newmodpart(type,s,loc); append (mp,p); } <>= 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); <contents>> } if (loc) p->loc = *loc; p->next = NULL; (void)rcsid; /* avoid a warning */ (void)rcsname; /* avoid a warning */ return p; } <>= 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; } } <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"); } @ <>= 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. <
>= 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 */ <>= static char *lastfilename = 0; static int lastlineno = -1; <
>= void resetloc(void); <>= 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. <>= 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 */ <> <> for (p=mp->head; p!=NULL; p=p->next) { switch (p->ptype) { case STRING: <>; break; case MODULE: <>; break; case 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. <>= 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); } <>= 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. <>= 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 */ @ <>= thismodule.this = mp; thismodule.parent = parent; <>= if (seekcycle(mp, parent)) { errormsg(Error, "@<<%s@>>", mp->name); return Error; } <>= static int seekcycle(Module mp, Parent parent); <>= 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. <
>= int printloc(FILE *fp, char *fmt, Location loc, int partial); <>= 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); <> lastfilename = loc.filename; lastlineno = loc.lineno; return 1; } else return 0; } <>= 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 <> break; default: <> break; } } else putc(*p, fp); } <>= { 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. <
>= void remove_final_newline (Module mp); /* remove trailing newline that must be in module */ <>= 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; } }