summaryrefslogtreecommitdiff
path: root/web/noweb/src/c/notangle.nw
blob: c40c3f3684c6936a7d280ebe670f200200d4c25d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
% 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.
<<header>>=
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "strsave.h"
#include "getline.h"
#include "modules.h"
#include "modtrees.h"
#include "errors.h"
#include "match.h"
#include "notangle.h"

<<Function declarations>>

void emit_module_named (FILE *out, char *rootname, char *locformat) {
    Module root = NULL; /* ptr to root module */

    root = lookup(rootname);
    <<quit if we couldn't find the root>>
    (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.
<<header>>=
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);
        <<track line numbers, then [[continue]] unless [[line]] is [[@begin code]]>>
        <<repeat [[line = getline_nw(in);]] until EOF, [[@defn]], or [[@text]]>>
        insist(line,"defn","code chunk had no definition line");
        <<copy module name into [[modname]]>>
        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"))
                <<complain of botched code chunk>>
            line = getline_nw(in);
        }
        <<if [[line==NULL]] die of premature end of file>>
    }
}
@ 
<<repeat [[line = getline_nw(in);]] until EOF, [[@defn]], or [[@text]]>>=
do { line = getline_nw(in);
} while (line != NULL && !is_keyword(line,"defn") && !is_keyword(line,"text"));
@
<<track line numbers, then [[continue]] unless [[line]] is [[@begin code]]>>=
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")) {
    <<save name from [[line]] ([[@file ...]]) into [[loc.filename]]>>
    loc.lineno = 1;
} else if (is_keyword(line, "line")) {
    <<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>>
    loc.lineno--;
@
When copying the module name or a file name,
we have to strip the trailing newline.
<<copy module name into [[modname]]>>=
strcpy(modname,line+strlen("@defn "));
modname[strlen(modname)-1]='\0';
<<save name from [[line]] ([[@file ...]]) into [[loc.filename]]>>=
{ 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);
}
<<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>>=
{ 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';
  <<fail if string [[temp]] contains a non-digit>>
  loc.lineno = atoi(temp);
}
<<fail if string [[temp]] contains a non-digit>>=
{ 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);
}
<<Function declarations>>=
static void warn_dots(char *modname);          /* warn about names ending in ... */

@ Error checking is perenially dull.
<<quit if we couldn't find the root>>=
if (root==NULL) {
    errormsg(Fatal, "The root module @<<%s@>> was not defined.", rootname);
    return;
}
<<*>>=
void insist(char *line, char *keyword, char *msg) {
  <<if [[line==NULL]] die of premature end of file>>
  if (!is_keyword(line,keyword))
    impossible(msg);
}
<<if [[line==NULL]] die of premature end of file>>=
if (line==NULL) {
    impossible("End of file occurred in mid-module");
}
<<Function declarations>>=
void insist(char *line, char *keyword, char *msg);
<<complain of botched code chunk>>=
errorat(loc.filename, loc.lineno, Error, "botched code chunk `%s'", line);