% Copyright 1991 by Norman Ramsey. All rights reserved. % See file COPYRIGHT for more information. @ \subsection{Tangling a single file} A cheap imitation of tangle. The idea is a very cheap implementation of tangle. The markup of the file is described in file markup.ow. @ The structure of the program is as follows: first we accumulate all of the module definitions, then we write out the definition of the root module (normally [["*"]]). The module definition information will be stored statically in a table supplied by [[modtrees.h]]; we'll cover the details later. <
>= void emit_module_named (FILE *out, char *rootname, char *locformat); <<*>>= static char rcsid[] = "$Id: notangle.nw,v 2.25 2008/10/06 01:03:05 nr Exp nr $"; static char rcsname[] = "$Name: v2_12 $"; #define MAX_MODNAME 255 @ <<*>>= #include #include #include #include #include "strsave.h" #include "getline.h" #include "modules.h" #include "modtrees.h" #include "errors.h" #include "match.h" #include "notangle.h" <> void emit_module_named (FILE *out, char *rootname, char *locformat) { Module root = NULL; /* ptr to root module */ root = lookup(rootname); <> (void) expand(root,0,0,0,locformat,out); putc('\n',out); /* make output end with newline */ (void)rcsid; /* avoid a warning */ (void)rcsname; /* avoid a warning */ } @ We loop looking for the start of a code chunk. When we find one, we get the name of the module in which the code is to appear. Then we just keep adding lines to that module until we see a terminator. After we see the terminator we start all over again looking for another code chunk. <
>= void read_defs(FILE *in); /* read module definitions */ <<*>>= void read_defs(FILE *in) { char modname[MAX_MODNAME+1] = ""; /* name of module currently being read, [[""]] if no module is being read */ Module modptr = NULL; /* ptr to current module, or NULL */ char *line = NULL; /* buffer for input */ Location loc; while ((line = getline_nw(in)) != NULL) { if (is_keyword(line, "fatal")) exit(1); <> <> insist(line,"defn","code chunk had no definition line"); <> warn_dots(modname); /* names ending in ... aren't like web */ modptr = insert(modname); /* find or add module in table */ line = getline_nw(in); insist(line,"nl","definition line not followed by newline"); loc.lineno++; line = getline_nw(in); while (line != NULL && !is_end(line,"code")) { if (is_keyword(line,"nl")) { addnewline(modptr); loc.lineno++; } else if (is_keyword(line,"text")) { addstring(modptr,line+1+4+1,loc); } else if (is_keyword(line,"use")) { warn_dots(line+1+3+5); addmodule(modptr,line+1+3+1); } else if (is_index(line, "nl")) { loc.lineno++; <<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>> } else if (!is_keyword(line, "index")) <> line = getline_nw(in); } <> } } @ <>= do { line = getline_nw(in); } while (line != NULL && !is_keyword(line,"defn") && !is_keyword(line,"text")); @ <>= if (is_keyword(line, "nl") || is_index(line, "nl")) { loc.lineno++; <<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>> } if (!is_begin(line, "code")) continue; @ The only tricky bit with the line numbers is to note that [[@line]] gives the line number of the {\em following} line, not of the line on which the [[@line]] appears. That means [[loc.lineno]] must be decremented after it is set, so that the next newline will increment it to the correct value. <<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>>= } else if (is_keyword(line,"file")) { <> loc.lineno = 1; } else if (is_keyword(line, "line")) { <> loc.lineno--; @ When copying the module name or a file name, we have to strip the trailing newline. <>= strcpy(modname,line+strlen("@defn ")); modname[strlen(modname)-1]='\0'; <>= { char temp[MAX_MODNAME+1]; if (strlen(line) >= MAX_MODNAME + strlen("@file ")) overflow("file name size"); strcpy(temp,line+strlen("@file ")); temp[strlen(temp)-1]='\0'; loc.filename = strsave(temp); } <>= { char temp[MAX_MODNAME+1]; if (strlen(line) >= MAX_MODNAME + strlen("@line ")) overflow("file name size"); strcpy(temp,line+strlen("@line ")); temp[strlen(temp)-1]='\0'; <> loc.lineno = atoi(temp); } <>= { char *p; for (p = temp; *p; p++) if (!isdigit(*p)) errormsg(Error, "non-numeric line number in `@line %s'", temp); } @ In {\tt WEB}, module names ending in ``...'' may be prefixes for other names. We don't do anything like that here, but we do warn the user about ``...'' in case he's got a file converted from {\tt WEB}. <<*>>= static void warn_dots(char *modname) { if (!strcmp(modname+strlen(modname)-3,"...")) errormsg(Warning, "Module name @<<%s@>> isn't completed as in web", modname); } <>= static void warn_dots(char *modname); /* warn about names ending in ... */ @ Error checking is perenially dull. <>= if (root==NULL) { errormsg(Fatal, "The root module @<<%s@>> was not defined.", rootname); return; } <<*>>= void insist(char *line, char *keyword, char *msg) { <> if (!is_keyword(line,keyword)) impossible(msg); } <>= if (line==NULL) { impossible("End of file occurred in mid-module"); } <>= void insist(char *line, char *keyword, char *msg); <>= errorat(loc.filename, loc.lineno, Error, "botched code chunk `%s'", line);