diff options
Diffstat (limited to 'Build/source/texk/web2c/cwebdir/ctangle.w')
-rw-r--r-- | Build/source/texk/web2c/cwebdir/ctangle.w | 1543 |
1 files changed, 0 insertions, 1543 deletions
diff --git a/Build/source/texk/web2c/cwebdir/ctangle.w b/Build/source/texk/web2c/cwebdir/ctangle.w deleted file mode 100644 index e7e2d311fe5..00000000000 --- a/Build/source/texk/web2c/cwebdir/ctangle.w +++ /dev/null @@ -1,1543 +0,0 @@ -% This file is part of CWEB. -% This program by Silvio Levy and Donald E. Knuth -% is based on a program by Knuth. -% It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.2 --- February 2021 - -% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth - -% Permission is granted to make and distribute verbatim copies of this -% document provided that the copyright notice and this permission notice -% are preserved on all copies. - -% Permission is granted to copy and distribute modified versions of this -% document under the conditions for verbatim copying, provided that the -% entire resulting derived work is given a different name and distributed -% under the terms of a permission notice identical to this one. - -% Amendments to 'ctangle.w' resulting in this updated version were created -% by numerous collaborators over the course of many years. - -% Please send comments, suggestions, etc. to tex-k@@tug.org. - -% Here is TeX material that gets inserted after \input cwebmac -\def\hang{\hangindent 3em\indent\ignorespaces} -\def\pb{$\.|\ldots\.|$} % C brackets (|...|) -\def\v{\char'174} % vertical (|) in typewriter font -\mathchardef\RA="3221 % right arrow -\mathchardef\BA="3224 % double arrow - -\def\title{CTANGLE (Version 4.2)} -\def\topofcontents{\null\vfill - \centerline{\titlefont The {\ttitlefont CTANGLE} processor} - \vskip 15pt - \centerline{(Version 4.2)} - \vfill} -\def\botofcontents{\vfill -\noindent -Copyright \copyright\ 1987, 1990, 1993, 2000 Silvio Levy and Donald E. Knuth -\bigskip\noindent -Permission is granted to make and distribute verbatim copies of this -document provided that the copyright notice and this permission notice -are preserved on all copies. - -\smallskip\noindent -Permission is granted to copy and distribute modified versions of this -document under the conditions for verbatim copying, provided that the -entire resulting derived work is given a different name and distributed -under the terms of a permission notice identical to this one. -} -\pageno=\contentspagenumber \advance\pageno by 1 -\let\maybe=\iftrue - -@** Introduction. -This is the \.{CTANGLE} program by Silvio Levy and Donald E. Knuth, -based on \.{TANGLE} by Knuth. -We are thankful to -Nelson Beebe, Hans-Hermann Bode (to whom the \CPLUSPLUS/ adaptation is due), -Klaus Guntermann, Norman Ramsey, Tomas Rokicki, Joachim Schnitter, -Joachim Schrod, Lee Wittenberg, and others who have contributed improvements. - -The ``banner line'' defined here should be changed whenever \.{CTANGLE} -is modified. - -@d banner "This is CTANGLE (Version 4.2)" - -@c -@<Include files@>@/ -@h -@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/ -@<Typedef declarations@>@/ -@<Private variables@>@/ -@<Predeclaration of procedures@> - -@ \.{CTANGLE} has a fairly straightforward outline. It operates in -two phases: First it reads the source file, saving the \CEE/ code in -compressed form; then it shuffles and outputs the code. - -Please read the documentation for \.{common}, the set of routines common -to \.{CTANGLE} and \.{CWEAVE}, before proceeding further. - -@c -int main ( -int ac, -char **av) -{ - argc=ac; argv=av; - program=ctangle; - @<Set initial values@>@; - common_init(); - if (show_banner) puts(banner); /* print a ``banner line'' */ - phase_one(); /* read all the user's text and compress it into |tok_mem| */ - phase_two(); /* output the contents of the compressed tables */ - return wrap_up(); /* and exit gracefully */ -} - -@ The next few sections contain stuff from the file |"common.w"| that must -be included in both |"ctangle.w"| and |"cweave.w"|. It appears in -file |"common.h"|, which is also included in |"common.w"| to propagate -possible changes from this \.{COMMON} interface consistently. - -@i common.h - -@* Data structures exclusive to {\tt CTANGLE}. -We've already seen that the |byte_mem| array holds the names of identifiers, -strings, and sections; -the |tok_mem| array holds the replacement texts -for sections. Allocation is sequential, since things are deleted only -during Phase II, and only in a last-in-first-out manner. - -A \&{text} variable is a structure containing a pointer into -|tok_mem|, which tells where the corresponding text starts, and an -integer |text_link|, which, as we shall see later, is used to connect -pieces of text that have the same name. All the \&{text}s are stored in -the array |text_info|, and we use a \&{text\_pointer} variable to refer -to them. - -The first position of |tok_mem| that is unoccupied by -replacement text is called |tok_ptr|, and the first unused location of -|text_info| is called |text_ptr|. Thus we usually have the identity -|text_ptr->tok_start==tok_ptr|. - -@<Typed...@>= -typedef struct { - eight_bits *tok_start; /* pointer into |tok_mem| */ - sixteen_bits text_link; /* relates replacement texts */ -} text; -typedef text *text_pointer; - -@ @<Private...@>= -static text text_info[max_texts]; -static text_pointer text_info_end=text_info+max_texts-1; -static text_pointer text_ptr; /* first unused position in |text_info| */ -static eight_bits tok_mem[max_toks]; -static eight_bits *tok_mem_end=tok_mem+max_toks-1; -static eight_bits *tok_ptr; /* first unused position in |tok_mem| */ - -@ @<Set init...@>= -text_info->tok_start=tok_ptr=tok_mem; -text_ptr=text_info+1; text_ptr->tok_start=tok_mem; - /* this makes replacement text 0 of length zero */ - -@ If |p| is a pointer to a section name, |p->equiv| is a pointer to its -replacement text, an element of the array |text_info|. - -@d equiv equiv_or_xref /* info corresponding to names */ - -@ @<Set init...@>= -init_node(name_dir); /* the undefined section has no replacement text */ - -@ Here's the procedure that decides whether a name of length |l| -starting at position |first| equals the identifier pointed to by |p|: - -@c -boolean names_match(@t\1\1@> -name_pointer p, /* points to the proposed match */ -const char *first, /* position of first character of string */ -size_t l, /* length of identifier */ -eight_bits t@t\2\2@>) /* not used by \.{TANGLE} */ -{@+(void)t; - if (length(p)!=l) return false; - return !strncmp(first,p->byte_start,l); -} - -@ The common lookup routine refers to separate routines |init_node| and -|init_p| when the data structure grows. Actually |init_p| is called only by -\.{CWEAVE}, but we need to declare a dummy version so that -the loader won't complain of its absence. - -@c -void -init_node( -name_pointer node) -{ - node->equiv=(void *)text_info; -} -void -init_p(name_pointer p,eight_bits t) {@+(void)p;@+(void)t;@+} - -@* Tokens. -Replacement texts, which represent \CEE/ code in a compressed format, -appear in |tok_mem| as mentioned above. The codes in -these texts are called `tokens'; some tokens occupy two consecutive -eight-bit byte positions, and the others take just one byte. - -If $p$ points to a replacement text, |p->tok_start| is the |tok_mem| position -of the first eight-bit code of that text. If |p->text_link==0|, -this is the replacement text for a macro, otherwise it is the replacement -text for a section. In the latter case |p->text_link| is either equal to -|section_flag|, which means that there is no further text for this section, or -|p->text_link| points to a continuation of this replacement text; such -links are created when several sections have \CEE/ texts with the same -name, and they also tie together all the \CEE/ texts of unnamed sections. -The replacement text pointer for the first unnamed section appears in -|text_info->text_link|, and the most recent such pointer is |last_unnamed|. - -@d section_flag max_texts /* final |text_link| in section replacement texts */ - -@<Private...@>= -static text_pointer last_unnamed; /* most recent replacement text of unnamed section */ - -@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=0; - -@ If the first byte of a token is less than |0200|, the token occupies a -single byte. Otherwise we make a sixteen-bit token by combining two consecutive -bytes |a| and |b|. If |0200<=a<0250|, then |(a-0200)@t${}\times2^8$@>+b| -points to an identifier; if |0250<=a<0320|, then -|(a-0250)@t${}\times2^8$@>+b| points to a section name -(or, if it has the special value |output_defs_flag|, -to the area where the preprocessor definitions are stored); and if -|0320<=a<0400|, then |(a-0320)@t${}\times2^8$@>+b| is the number of the section -in which the current replacement text appears. - -Codes less than |0200| are 7-bit |char| codes that represent themselves. -Some of the 7-bit codes will not be present, however, so we can -use them for special purposes. The following symbolic names are used: - -\yskip \hang |join| denotes the concatenation of adjacent items with no -space or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}). - -\hang |string| denotes the beginning or end of a string, verbatim -construction or numerical constant. -@^ASCII code dependencies@> - -@d string 02 /* takes the place of extended ASCII \.{\char2} */ -@d join 0177 /* takes the place of ASCII delete */ -@d output_defs_flag (2*024000-1) - -@ The following procedure is used to enter a two-byte value into -|tok_mem| when a replacement text is being generated. - -@c -static void -store_two_bytes( -sixteen_bits x) -{ - if (tok_ptr+2>tok_mem_end) overflow("token"); - *tok_ptr++=x>>8; /* store high byte */ - *tok_ptr++=x&0377; /* store low byte */ -} - -@ @<Predecl...@>=@+static void store_two_bytes(sixteen_bits); - -@** Stacks for output. The output process uses a stack to keep track -of what is going on at different ``levels'' as the sections are being -written out. Entries on this stack have five parts: - -\yskip\hang |end_field| is the |tok_mem| location where the replacement -text of a particular level will end; - -\hang |byte_field| is the |tok_mem| location from which the next token -on a particular level will be read; - -\hang |name_field| points to the name corresponding to a particular level; - -\hang |repl_field| points to the replacement text currently being read -at a particular level; - -\hang |section_field| is the section number, or zero if this is a macro. - -\yskip\noindent The current values of these five quantities are referred to -quite frequently, so they are stored in a separate place instead of in -the |stack| array. We call the current values |cur_end|, |cur_byte|, -|cur_name|, |cur_repl|, and |cur_section|. - -The global variable |stack_ptr| tells how many levels of output are -currently in progress. The end of all output occurs when the stack is -empty, i.e., when |stack_ptr==stack|. - -@<Typed...@>= -typedef struct { - eight_bits *end_field; /* ending location of replacement text */ - eight_bits *byte_field; /* present location within replacement text */ - name_pointer name_field; /* |byte_start| index for text being output */ - text_pointer repl_field; /* |tok_start| index for text being output */ - sixteen_bits section_field; /* section number or zero if not a section */ -} output_state; -typedef output_state *stack_pointer; - -@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */ -@d cur_byte cur_state.byte_field /* location of next output byte in |tok_mem|*/ -@d cur_name cur_state.name_field /* pointer to current name being expanded */ -@d cur_repl cur_state.repl_field /* pointer to current replacement text */ -@d cur_section cur_state.section_field /* current section number being expanded */ - -@<Private...@>= -static output_state cur_state; /* |cur_end|, |cur_byte|, |cur_name|, |cur_repl|, - and |cur_section| */ -static output_state stack[stack_size+1]; /* info for non-current levels */ -static stack_pointer stack_end=stack+stack_size; /* end of |stack| */ -static stack_pointer stack_ptr; /* first unused location in the output state stack */ - -@ To get the output process started, we will perform the following -initialization steps. We may assume that |text_info->text_link| is nonzero, -since it points to the \CEE/ text in the first unnamed section that generates -code; if there are no such sections, there is nothing to output, and an -error message will have been generated before we do any of the initialization. - -@<Initialize the output stacks@>= -stack_ptr=stack+1; cur_name=name_dir; cur_repl=text_info->text_link+text_info; -cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; cur_section=0; - -@ When the replacement text for name |p| is to be inserted into the output, -the following subroutine is called to save the old level of output and get -the new one going. - -We assume that the \CEE/ compiler can copy structures. -@^system dependencies@> - -@c -static void -push_level(@t\1\1@> /* suspends the current level */ -name_pointer p@t\2\2@>) -{ - if (stack_ptr==stack_end) overflow("stack"); - *stack_ptr=cur_state; - stack_ptr++; - if (p!=NULL) { /* |p==NULL| means we are in |output_defs| */ - cur_name=p; cur_repl=(text_pointer)p->equiv; - cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; - cur_section=0; - } -} - -@ @<Predecl...@>= -static void push_level(name_pointer);@/ -static void pop_level(boolean); - -@ When we come to the end of a replacement text, the |pop_level| subroutine -does the right thing: It either moves to the continuation of this replacement -text or returns the state to the most recently stacked level. - -@c -static void -pop_level(@t\1\1@> /* do this when |cur_byte| reaches |cur_end| */ -boolean flag@t\2\2@>) /* |flag==false| means we are in |output_defs| */ -{ - if (flag && cur_repl->text_link<section_flag) { /* link to a continuation */ - cur_repl=cur_repl->text_link+text_info; /* stay on the same level */ - cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; - return; - } - stack_ptr--; /* go down to the previous level */ - if (stack_ptr>stack) cur_state=*stack_ptr; -} - -@ The heart of the output procedure is the function |get_output|, -which produces the next token of output and sends it on to the lower-level -function |out_char|. The main purpose of |get_output| is to handle the -necessary stacking and unstacking. It sends the value |section_number| -if the next output begins or ends the replacement text of some section, -in which case |cur_val| is that section's number (if beginning) or the -negative of that value (if ending). (A section number of 0 indicates -not the beginning or ending of a section, but a \&{\#line} command.) -And it sends the value |identifier| -if the next output is an identifier, in which case -|cur_val| points to that identifier name. - -@d section_number 0201 /* code returned by |get_output| for section numbers */ -@d identifier 0202 /* code returned by |get_output| for identifiers */ - -@<Private...@>= -static int cur_val; /* additional information corresponding to output token */ - -@ If |get_output| finds that no more output remains, it returns with -|stack_ptr==stack|. -@^high-bit character handling@> - -@c -static void -get_output(void) /* sends next token to |out_char| */ -{ - sixteen_bits a; /* value of current byte */ - restart: if (stack_ptr==stack) return; - if (cur_byte==cur_end) { - cur_val=-((int)cur_section); /* cast needed because of sign extension */ - pop_level(true); - if (cur_val==0) goto restart; - out_char(section_number); return; - } - a=*cur_byte++; - if (out_state==verbatim && a!=string && a!=constant && a!='\n') - C_putc(a); /* a high-bit character can occur in a string */ - else if (a<0200) out_char(a); /* one-byte token */ - else { - a=(a-0200)*0400+*cur_byte++; - switch (a/024000) { /* |024000==(0250-0200)*0400| */ - case 0: cur_val=a; out_char(identifier); break; - case 1: if (a==output_defs_flag) output_defs(); - else @<Expand section |a-024000|, |goto restart|@>@; - break; - default: cur_val=a-050000; if (cur_val>0) cur_section=cur_val; - out_char(section_number); - } - } -} - -@ @<Predecl...@>=@+static void get_output(void); - -@ The user may have forgotten to give any \CEE/ text for a section name, -or the \CEE/ text may have been associated with a different name by mistake. - -@<Expand section |a-...@>= -{ - a-=024000; - if ((a+name_dir)->equiv!=(void *)text_info) push_level(a+name_dir); - else if (a!=0) { - fputs("\n! Not present: <",stdout); - print_section_name(a+name_dir); err_print(">"); -@.Not present: <section name>@> - } - goto restart; -} - -@* Producing the output. -The |get_output| routine above handles most of the complexity of output -generation, but there are two further considerations that have a nontrivial -effect on \.{CTANGLE}'s algorithms. - -@ First, -we want to make sure that the output has spaces and line breaks in -the right places (e.g., not in the middle of a string or a constant or an -identifier, not at a `\.{@@\&}' position -where quantities are being joined together, and certainly after an \.= -because the \CEE/ compiler thinks \.{=-} is ambiguous). - -The output process can be in one of following states: - -\yskip\hang |num_or_id| means that the last item in the buffer is a number or -identifier, hence a blank space or line break must be inserted if the next -item is also a number or identifier. - -\yskip\hang |unbreakable| means that the last item in the buffer was followed -by the \.{@@\&} operation that inhibits spaces between it and the next item. - -\yskip\hang |verbatim| means we're copying only character tokens, and -that they are to be output exactly as stored. This is the case during -strings, verbatim constructions and numerical constants. - -\yskip\hang |post_slash| means we've just output a slash. - -\yskip\hang |normal| means none of the above. - -\yskip\noindent Furthermore, if the variable |protect| is |true|, newlines -are preceded by a `\.\\'. - -@d normal 0 /* non-unusual state */ -@d num_or_id 1 /* state associated with numbers and identifiers */ -@d post_slash 2 /* state following a \./ */ -@d unbreakable 3 /* state associated with \.{@@\&} */ -@d verbatim 4 /* state in the middle of a string */ - -@<Private...@>= -static eight_bits out_state; /* current status of partial output */ -static boolean protect; /* should newline characters be quoted? */ - -@ Here is a routine that is invoked when we want to output the current line. -During the output process, |cur_line| equals the number of the next line -to be output. - -@c -static void -flush_buffer(void) /* writes one line to output file */ -{ - C_putc('\n'); - if (cur_line % 100 == 0 && show_progress) { - printf("."); - if (cur_line % 500 == 0) printf("%d",cur_line); - update_terminal; /* progress report */ - } - cur_line++; -} - -@ @<Predecl...@>=@+static void flush_buffer(void); - -@ Second, we have modified the original \.{TANGLE} so that it will write output -on multiple files. -If a section name is introduced in at least one place by \.{@@(} -instead of \.{@@<}, we treat it as the name of a file. -All these special sections are saved on a stack, |output_files|. -We write them out after we've done the unnamed section. - -@d max_files 256 -@<Private...@>= -static name_pointer output_files[max_files]; -static name_pointer *cur_out_file, *end_output_files, *an_output_file; -static char cur_section_name_char; /* is it |'<'| or |'('| */ -static char output_file_name[longest_name+1]; /* name of the file */ - -@ We make |end_output_files| point just beyond the end of -|output_files|. The stack pointer -|cur_out_file| starts out there. Every time we see a new file, we -decrement |cur_out_file| and then write it in. -@<Set initial...@>= -cur_out_file=end_output_files=output_files+max_files; - -@ @<If it's not there, add |cur_section_name| to the output file stack, or -complain we're out of room@>= -{ - for (an_output_file=cur_out_file; - an_output_file<end_output_files; an_output_file++) - if (*an_output_file==cur_section_name) break; - if (an_output_file==end_output_files) { - if (cur_out_file>output_files) - *--cur_out_file=cur_section_name; - else { - overflow("output files"); - } - } -} - -@* The big output switch. Here then is the routine that does the -output. - -@c -static void -phase_two (void) { - web_file_open=false; - cur_line=1; - @<Initialize the output stacks@>@; - @<Output macro definitions if appropriate@>@; - if (text_info->text_link==0 && cur_out_file==end_output_files) { - fputs("\n! No program text was specified.",stdout); mark_harmless; -@.No program text...@> - } - else { - if(cur_out_file==end_output_files) { - if(show_progress) - printf("\nWriting the output file (%s):",C_file_name); - } - else { - if (show_progress) { - fputs("\nWriting the output files:",stdout); -@.Writing the output...@> - printf(" (%s)",C_file_name); - update_terminal; - } - if (text_info->text_link==0) goto writeloop; - } - while (stack_ptr>stack) get_output(); - flush_buffer(); -writeloop: @<Write all the named output files@>@; - if (show_happiness) { - if (show_progress) new_line; - fputs("Done.",stdout); - } - } -} - -@ @<Predecl...@>=@+static void phase_two(void); - -@ To write the named output files, we proceed as for the unnamed -section. -The only subtlety is that we have to open each one. - -@<Write all the named output files@>= -for (an_output_file=end_output_files; an_output_file>cur_out_file;) { - an_output_file--; - sprint_section_name(output_file_name,*an_output_file); - fclose(C_file); - C_file=fopen(output_file_name,"wb"); - if (C_file ==0) fatal("! Cannot open output file ",output_file_name); -@.Cannot open output file@> - if (show_progress) { printf("\n(%s)",output_file_name); update_terminal; } - cur_line=1; - stack_ptr=stack+1; - cur_name= (*an_output_file); - cur_repl= (text_pointer)cur_name->equiv; - cur_byte=cur_repl->tok_start; - cur_end=(cur_repl+1)->tok_start; - while (stack_ptr > stack) get_output(); - flush_buffer(); -} - -@ If a \.{@@h} was not encountered in the input, -we go through the list of replacement texts and copy the ones -that refer to macros, preceded by the \.{\#define} preprocessor command. - -@<Output macro definitions if appropriate@>= - if (!output_defs_seen) - output_defs(); - -@ @<Private...@>= -static boolean output_defs_seen=false; - -@ @<Predecl...@>= -static void output_defs(void);@/ -static void out_char(eight_bits); - -@ @c -static void -output_defs(void) -{ - sixteen_bits a; - push_level(NULL); - for (cur_text=text_info+1; cur_text<text_ptr; cur_text++) - if (cur_text->text_link==0) { /* |cur_text| is the text for a macro */ - cur_byte=cur_text->tok_start; - cur_end=(cur_text+1)->tok_start; - C_printf("%s","#define "); - out_state=normal; - protect=true; /* newlines should be preceded by |'\\'| */ - while (cur_byte<cur_end) { - a=*cur_byte++; - if (cur_byte==cur_end && a=='\n') break; /* disregard a final newline */ - if (out_state==verbatim && a!=string && a!=constant && a!='\n') - C_putc(a); /* a high-bit character can occur in a string */ -@^high-bit character handling@> - else if (a<0200) out_char(a); /* one-byte token */ - else { - a=(a-0200)*0400+*cur_byte++; - if (a<024000) { /* |024000==(0250-0200)*0400| */ - cur_val=a; out_char(identifier); - } - else if (a<050000) { confusion("macro defs have strange char");} - else { - cur_val=a-050000; cur_section=cur_val; out_char(section_number); - } - /* no other cases */ - } - } - protect=false; - flush_buffer(); - } - pop_level(false); -} - -@ A many-way switch is used to send the output. Note that this function -is not called if |out_state==verbatim|, except perhaps with arguments -|'\n'| (protect the newline), |string| (end the string), or |constant| -(end the constant). - -@c -static void -out_char( -eight_bits cur_char) -{ - char *j, *k; /* pointer into |byte_mem| */ -restart: - switch (cur_char) { - case '\n': if (protect && out_state!=verbatim) C_putc(' '); - if (protect || out_state==verbatim) C_putc('\\'); - flush_buffer(); if (out_state!=verbatim) out_state=normal; break; - @/@t\4@>@<Case of an identifier@>@; - @/@t\4@>@<Case of a section number@>@; - @/@t\4@>@<Cases like \.{!=}@>@; - case '=': case '>': C_putc(cur_char); C_putc(' '); - out_state=normal; break; - case join: out_state=unbreakable; break; - case constant: if (out_state==verbatim) { - out_state=num_or_id; break; - } - if(out_state==num_or_id) C_putc(' '); out_state=verbatim; break; - case string: if (out_state==verbatim) out_state=normal; - else out_state=verbatim; break; - case '/': C_putc('/'); out_state=post_slash; break; - case '*': if (out_state==post_slash) C_putc(' '); - /* fall through */ - default: C_putc(cur_char); out_state=normal; break; - } -} - -@ @<Cases like \.{!=}@>= -case plus_plus: C_putc('+');@+C_putc('+'); out_state=normal; break; -case minus_minus: C_putc('-');@+C_putc('-'); out_state=normal; break; -case minus_gt: C_putc('-');@+C_putc('>'); out_state=normal; break; -case gt_gt: C_putc('>');@+C_putc('>'); out_state=normal; break; -case eq_eq: C_putc('=');@+C_putc('='); out_state=normal; break; -case lt_lt: C_putc('<');@+C_putc('<'); out_state=normal; break; -case gt_eq: C_putc('>');@+C_putc('='); out_state=normal; break; -case lt_eq: C_putc('<');@+C_putc('='); out_state=normal; break; -case non_eq: C_putc('!');@+C_putc('='); out_state=normal; break; -case and_and: C_putc('&');@+C_putc('&'); out_state=normal; break; -case or_or: C_putc('|');@+C_putc('|'); out_state=normal; break; -case dot_dot_dot: C_putc('.');@+C_putc('.');@+C_putc('.'); out_state=normal; - break; -case colon_colon: C_putc(':');@+C_putc(':'); out_state=normal; break; -case period_ast: C_putc('.');@+C_putc('*'); out_state=normal; break; -case minus_gt_ast: C_putc('-');@+C_putc('>');@+C_putc('*'); out_state=normal; - break; - -@ When an identifier is output to the \CEE/ file, characters in the -range 128--255 must be changed into something else, so the \CEE/ -compiler won't complain. By default, \.{CTANGLE} converts the -character with code $16 x+y$ to the three characters `\.X$xy$', but -a different transliteration table can be specified. Thus a German -might want {\it gr\"un\/} to appear as a still readable \.{gruen}. -This makes debugging a lot less confusing. - -@d translit_length 10 - -@<Private...@>= -static char translit[128][translit_length]; - -@ @<Set init...@>= -{ - int i; - for (i=0;i<128;i++) sprintf(translit[i],"X%02X",(unsigned int)(128+i)); -} - -@ @<Case of an identifier@>= -case identifier: - if (out_state==num_or_id) C_putc(' '); - j=(cur_val+name_dir)->byte_start; - k=(cur_val+name_dir+1)->byte_start; - while (j<k) { - if ((eight_bits)(*j)<0200) C_putc(*j); -@^high-bit character handling@> - else C_printf("%s",translit[(eight_bits)(*j)-0200]); - j++; - } - out_state=num_or_id; break; - -@ @<Case of a sec...@>= -case section_number: - if (cur_val>0) C_printf("/*%d:*/",cur_val); - else if(cur_val<0) C_printf("/*:%d*/",-cur_val); - else if (protect) { - cur_byte +=4; /* skip line number and file name */ - cur_char = '\n'; - goto restart; - } else { - sixteen_bits a; - a=0400* *cur_byte++; - a+=*cur_byte++; /* gets the line number */ - C_printf("\n#line %d \"",a); -@:line}{\.{\#line}@> - cur_val=*cur_byte++; - cur_val=0400*(cur_val-0200)+ *cur_byte++; /* points to the file name */ - for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start; - j<k; j++) { - if (*j=='\\' || *j=='"') C_putc('\\'); - C_putc(*j); - } - C_printf("%s","\"\n"); - } - break; - -@** Introduction to the input phase. -We have now seen that \.{CTANGLE} will be able to output the full -\CEE/ program, if we can only get that program into the byte memory in -the proper format. The input process is something like the output process -in reverse, since we compress the text as we read it in and we expand it -as we write it out. - -There are three main input routines. The most interesting is the one that gets -the next token of a \CEE/ text; the other two are used to scan rapidly past -\TEX/ text in the \.{CWEB} source code. One of the latter routines will jump to -the next token that starts with `\.{@@}', and the other skips to the end -of a \CEE/ comment. - -@ Control codes in \.{CWEB} begin with `\.{@@}', and the next character -identifies the code. Some of these are of interest only to \.{CWEAVE}, -so \.{CTANGLE} ignores them; the others are converted by \.{CTANGLE} into -internal code numbers by the |ccode| table below. The ordering -of these internal code numbers has been chosen to simplify the program logic; -larger numbers are given to the control codes that denote more significant -milestones. - -@d ignore 0 /* control code of no interest to \.{CTANGLE} */ -@d ord 0302 /* control code for `\.{@@'}' */ -@d control_text 0303 /* control code for `\.{@@t}', `\.{@@\^}', etc. */ -@d translit_code 0304 /* control code for `\.{@@l}' */ -@d output_defs_code 0305 /* control code for `\.{@@h}' */ -@d format_code 0306 /* control code for `\.{@@f}' */ -@d definition 0307 /* control code for `\.{@@d}' */ -@d begin_C 0310 /* control code for `\.{@@c}' */ -@d section_name 0311 /* control code for `\.{@@<}' */ -@d new_section 0312 /* control code for `\.{@@\ }' and `\.{@@*}' */ - -@<Private...@>= -static eight_bits ccode[256]; /* meaning of a char following \.{@@} */ - -@ @<Set ini...@>= { - int c; /* must be |int| so the |for| loop will end */ - for (c=0; c<256; c++) ccode[c]=ignore; - ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] - =ccode['*']=new_section; - ccode['@@']='@@'; ccode['=']=string; - ccode['d']=ccode['D']=definition; - ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; - ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; - ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']= - ccode['q']=ccode['Q']=control_text; - ccode['h']=ccode['H']=output_defs_code; - ccode['l']=ccode['L']=translit_code; - ccode['&']=join; - ccode['<']=ccode['(']=section_name; - ccode['\'']=ord; -} - -@ The |skip_ahead| procedure reads through the input at fairly high speed -until finding the next non-ignorable control code, which it returns. - -@c -static eight_bits -skip_ahead(void) /* skip to next control code */ -{ - eight_bits c; /* control code found */ - while (true) { - if (loc>limit && (get_line()==false)) return new_section; - *(limit+1)='@@'; - while (*loc!='@@') loc++; - if (loc<=limit) { - loc++; c=ccode[(eight_bits)*loc]; loc++; - if (c!=ignore || *(loc-1)=='>') return c; - } - } -} - -@ @<Predecl...@>= -static eight_bits skip_ahead(void);@/ -static boolean skip_comment(boolean); - -@ The |skip_comment| procedure reads through the input at somewhat high -speed in order to pass over comments, which \.{CTANGLE} does not transmit -to the output. If the comment is introduced by \.{/*}, |skip_comment| -proceeds until finding the end-comment token \.{*/} or a newline; in the -latter case |skip_comment| will be called again by |get_next|, since the -comment is not finished. This is done so that each newline in the -\CEE/ part of a section is copied to the output; otherwise the \&{\#line} -commands inserted into the \CEE/ file by the output routines become useless. -On the other hand, if the comment is introduced by \.{//} (i.e., if it -is a \CPLUSPLUS/ ``short comment''), it always is simply delimited by the next -newline. The boolean argument |is_long_comment| distinguishes between -the two types of comments. - -If |skip_comment| comes to the end of the section, it prints an error message. -No comment, long or short, is allowed to contain `\.{@@\ }' or `\.{@@*}'. - -@<Private...@>= -static boolean comment_continues=false; /* are we scanning a comment? */ - -@ @c -static boolean skip_comment(@t\1\1@> /* skips over comments */ -boolean is_long_comment@t\2\2@>) -{ - char c; /* current character */ - while (true) { - if (loc>limit) { - if (is_long_comment) { - if(get_line()) return comment_continues=true; - else{ - err_print("! Input ended in mid-comment"); -@.Input ended in mid-comment@> - return comment_continues=false; - } - } - else return comment_continues=false; - } - c=*(loc++); - if (is_long_comment && c=='*' && *loc=='/') { - loc++; return comment_continues=false; - } - if (c=='@@') { - if (ccode[(eight_bits)*loc]==new_section) { - err_print("! Section name ended in mid-comment"); loc--; -@.Section name ended in mid-comment@> - return comment_continues=false; - } - else loc++; - } - } -} - -@* Inputting the next token. - -@d constant 03 - -@<Private...@>= -static name_pointer cur_section_name; /* name of section just scanned */ -static boolean no_where; /* suppress |print_where|? */ - -@ As one might expect, |get_next| consists mostly of a big switch -that branches to the various special cases that can arise. - -@d isxalpha(c) ((c)=='_' || (c)=='$') - /* non-alpha characters allowed in identifier */ -@d ishigh(c) ((eight_bits)(c)>0177) -@^high-bit character handling@> - -@c -static eight_bits -get_next(void) /* produces the next input token */ -{ - static int preprocessing=0; - eight_bits c; /* the current character */ - while (true) { - if (loc>limit) { - if (preprocessing && *(limit-1)!='\\') preprocessing=0; - if (get_line()==false) return new_section; - else if (print_where && !no_where) { - print_where=false; - @<Insert the line number into |tok_mem|@>@; - } - else return '\n'; - } - c=*loc; - if (comment_continues || (c=='/' && (*(loc+1)=='*' || *(loc+1)=='/'))) { - skip_comment(comment_continues||*(loc+1)=='*'); - /* scan to end of comment or newline */ - if (comment_continues) return '\n'; - else continue; - } - loc++; - if (xisdigit(c) || c=='.') @<Get a constant@>@; - else if (c=='\'' || c=='"'@| - || ((c=='L' || c=='u' || c=='U')&&(*loc=='\'' || *loc=='"'))@| - || ((c=='u' && *loc=='8')&&(*(loc+1)=='\'' || *(loc+1)=='"'))) - @<Get a string@>@; - else if (isalpha(c) || isxalpha(c) || ishigh(c)) - @<Get an identifier@>@; - else if (c=='@@') @<Get control code and possible section name@>@; - else if (xisspace(c)) { - if (!preprocessing || loc>limit) continue; - /* we don't want a blank after a final backslash */ - else return ' '; /* ignore spaces and tabs, unless preprocessing */ - } - else if (c=='#' && loc==buffer+1) preprocessing=1; - mistake: @<Compress two-symbol operator@>@; - return c; - } -} - -@ @<Predecl...@>=@+static eight_bits get_next(void); - -@ The following code assigns values to the combinations \.{++}, -\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and -\.{\&\&}, and to the \CPLUSPLUS/ -combinations \.{...}, \.{::}, \.{.*} and \.{->*}. -The compound assignment operators (e.g., \.{+=}) are -treated as separate tokens. - -@d compress(c) if (loc++<=limit) return c - -@<Compress tw...@>= -switch(c) { - case '+': if (*loc=='+') compress(plus_plus); break; - case '-': if (*loc=='-') {compress(minus_minus);} - else { if (*loc=='>') { if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);} - else compress(minus_gt); } } break; - case '.': if (*loc=='*') {compress(period_ast);} - else if (*loc=='.' && *(loc+1)=='.') { - loc++; compress(dot_dot_dot); - } - break; - case ':': if (*loc==':') compress(colon_colon); break; - case '=': if (*loc=='=') compress(eq_eq); break; - case '>': if (*loc=='=') {compress(gt_eq);} - else if (*loc=='>') compress(gt_gt); break; - case '<': if (*loc=='=') {compress(lt_eq);} - else if (*loc=='<') compress(lt_lt); break; - case '&': if (*loc=='&') compress(and_and); break; - case '|': if (*loc=='|') compress(or_or); break; - case '!': if (*loc=='=') compress(non_eq); break; -} - -@ @<Get an identifier@>= { - id_first=--loc; - while (isalpha((eight_bits)*++loc) || isdigit((eight_bits)*loc) @| - || isxalpha((eight_bits)*loc) || ishigh((eight_bits)*loc)); - id_loc=loc; return identifier; -} - -@ @<Get a constant@>= { - id_first=loc-1; - if (*id_first=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */ - if (*id_first=='0') { - if (*loc=='x' || *loc=='X') { /* hex constant */ - loc++; while (xisxdigit(*loc)) loc++; goto found; - } - } - while (xisdigit(*loc)) loc++; - if (*loc=='.') { - loc++; - while (xisdigit(*loc)) loc++; - } - if (*loc=='e' || *loc=='E') { /* float constant */ - if (*++loc=='+' || *loc=='-') loc++; - while (xisdigit(*loc)) loc++; - } - found: while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' - || *loc=='f' || *loc=='F') loc++; - id_loc=loc; - return constant; -} - -@ \CEE/ strings and character constants, delimited by double and single -quotes, respectively, can contain newlines or instances of their own -delimiters if they are protected by a backslash. We follow this -convention, but do not allow the string to be longer than |longest_name|. - -@<Get a string@>= { - char delim = c; /* what started the string */ - id_first = section_text+1; - id_loc = section_text; *++id_loc=delim; - if (delim=='L' || delim=='u' || delim=='U') { /* wide character constant */ - if (delim=='u' && *loc=='8') { *++id_loc=*loc++; } - delim=*loc++; *++id_loc=delim; - } - while (true) { - if (loc>=limit) { - if(*(limit-1)!='\\') { - err_print("! String didn't end"); loc=limit; break; -@.String didn't end@> - } - if(get_line()==false) { - err_print("! Input ended in middle of string"); loc=buffer; break; -@.Input ended in middle of string@> - } - else if (++id_loc<=section_text_end) *id_loc='\n'; /* will print as - \.{"\\\\\\n"} */ - } - if ((c=*loc++)==delim) { - if (++id_loc<=section_text_end) *id_loc=c; - break; - } - if (c=='\\') { - if (loc>=limit) continue; - if (++id_loc<=section_text_end) *id_loc = '\\'; - c=*loc++; - } - if (++id_loc<=section_text_end) *id_loc=c; - } - if (id_loc>=section_text_end) { - fputs("\n! String too long: ",stdout); -@.String too long@> - term_write(section_text+1,25); - err_print("..."); - } - id_loc++; - return string; -} - -@ After an \.{@@} sign has been scanned, the next character tells us -whether there is more work to do. - -@<Get control code and possible section name@>= { - c=ccode[(eight_bits)*loc++]; - switch(c) { - case ignore: continue; - case translit_code: err_print("! Use @@l in limbo only"); continue; -@.Use @@l in limbo...@> - case control_text: while ((c=skip_ahead())=='@@'); - /* only \.{@@@@} and \.{@@>} are expected */ - if (*(loc-1)!='>') - err_print("! Double @@ should be used in control text"); -@.Double @@ should be used...@> - continue; - case section_name: - cur_section_name_char=*(loc-1); - @<Scan the section name and make |cur_section_name| point to it@>@; - case string: @<Scan a verbatim string@>@; - case ord: @<Scan an ASCII constant@>@; - default: return c; - } -} - -@ After scanning a valid ASCII constant that follows -\.{@@'}, this code plows ahead until it finds the next single quote. -(Special care is taken if the quote is part of the constant.) -Anything after a valid ASCII constant is ignored; -thus, \.{@@'\\nopq'} gives the same result as \.{@@'\\n'}. - -@<Scan an ASCII constant@>= - id_first=loc; - if (*loc=='\\') { - if (*++loc=='\'') loc++; - } - while (*loc!='\'') { - if (*loc=='@@') { - if (*(loc+1)!='@@') - err_print("! Double @@ should be used in ASCII constant"); -@.Double @@ should be used...@> - else loc++; - } - loc++; - if (loc>limit) { - err_print("! String didn't end"); loc=limit-1; break; -@.String didn't end@> - } - } - loc++; - return ord; - -@ @<Scan the section name...@>= { - char *k; /* pointer into |section_text| */ - @<Put section name into |section_text|@>@; - if (k-section_text>3 && strncmp(k-2,"...",3)==0) - cur_section_name=section_lookup(section_text+1,k-3,1); /* 1 means is a prefix */ - else cur_section_name=section_lookup(section_text+1,k,0); - if (cur_section_name_char=='(') - @<If it's not there, add |cur_section_name| to the output file stack, or - complain we're out of room@>@; - return section_name; -} - -@ Section names are placed into the |section_text| array with consecutive spaces, -tabs, and carriage-returns replaced by single spaces. There will be no -spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate -this, since the |section_lookup| routine uses |section_text[1]| as the first -character of the name.) - -@<Set init...@>=section_text[0]=' '; - -@ @<Put section name...@>= -k=section_text; -while (true) { - if (loc>limit && get_line()==false) { - err_print("! Input ended in section name"); -@.Input ended in section name@> - loc=buffer+1; break; - } - c=*loc; - @<If end of name or erroneous nesting, |break|@>@; - loc++; if (k<section_text_end) k++; - if (xisspace(c)) { - c=' '; if (*(k-1)==' ') k--; - } -*k=c; -} -if (k>=section_text_end) { - fputs("\n! Section name too long: ",stdout); -@.Section name too long@> - term_write(section_text+1,25); - printf("..."); mark_harmless; -} -if (*k==' ' && k>section_text) k--; - -@ @<If end of name or erroneous nesting,...@>= -if (c=='@@') { - c=*(loc+1); - if (c=='>') { - loc+=2; break; - } - if (ccode[(eight_bits)c]==new_section) { - err_print("! Section name didn't end"); break; -@.Section name didn't end@> - } - if (ccode[(eight_bits)c]==section_name) { - err_print("! Nesting of section names not allowed"); break; -@.Nesting of section names...@> - } - *(++k)='@@'; loc++; /* now |c==*loc| again */ -} - -@ At the present point in the program we -have |*(loc-1)==string|; we set |id_first| to the beginning -of the string itself, and |id_loc| to its ending-plus-one location in the -buffer. We also set |loc| to the position just after the ending delimiter. - -@<Scan a verbatim string@>= { - id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; - while (*loc!='@@' || *(loc+1)!='>') loc++; - if (loc>=limit) err_print("! Verbatim string didn't end"); -@.Verbatim string didn't end@> - id_loc=loc; loc+=2; - return string; -} - -@* Scanning a macro definition. -The rules for generating the replacement texts corresponding to macros and -\CEE/ texts of a section are almost identical; the only differences are that - -\yskip \item{a)}Section names are not allowed in macros; -in fact, the appearance of a section name terminates such macros and denotes -the name of the current section. - -\item{b)}The symbols \.{@@d} and \.{@@f} and \.{@@c} are not allowed after -section names, while they terminate macro definitions. - -\item{c)}Spaces are inserted after right parentheses in macros, because the -ANSI \CEE/ preprocessor sometimes requires it. - -\yskip Therefore there is a single procedure |scan_repl| whose parameter -|t| specifies either |macro| or |section_name|. After |scan_repl| has -acted, |cur_text| will point to the replacement text just generated, and -|next_control| will contain the control code that terminated the activity. - -@d macro 0 -@d app_repl(c) {if (tok_ptr==tok_mem_end) overflow("token"); *tok_ptr++=c;} - -@<Private...@>= -static text_pointer cur_text; /* replacement text formed by |scan_repl| */ -static eight_bits next_control; - -@ @c -static void -scan_repl(@t\1\1@> /* creates a replacement text */ -eight_bits t@t\2\2@>) -{ - sixteen_bits a; /* the current token */ - if (t==section_name) {@<Insert the line number into |tok_mem|@>@;} - while (true) switch (a=get_next()) { - @<In cases that |a| is a non-|char| token (|identifier|, - |section_name|, etc.), either process it and change |a| to a byte - that should be stored, or |continue| if |a| should be ignored, - or |goto done| if |a| signals the end of this replacement text@>@; - case ')': app_repl(a); - if (t==macro) app_repl(' '); - break; - default: app_repl(a); /* store |a| in |tok_mem| */ - } - done: next_control=(eight_bits) a; - if (text_ptr>text_info_end) overflow("text"); - cur_text=text_ptr; (++text_ptr)->tok_start=tok_ptr; -} - -@ @<Predecl...@>=@+static void scan_repl(eight_bits); - -@ Here is the code for the line number: first a |sixteen_bits| equal -to |0150000|; then the numeric line number; then a pointer to the -file name. - -@<Insert the line...@>= -store_two_bytes(0150000); -if (changing && include_depth==change_depth) { /* correction made Feb 2017 */ - id_first=change_file_name; - store_two_bytes((sixteen_bits)change_line); -}@+else { - id_first=cur_file_name; - store_two_bytes((sixteen_bits)cur_line); -} -id_loc=id_first+strlen(id_first); -{int a_l=id_lookup(id_first,id_loc,0)-name_dir; app_repl((a_l / 0400)+0200); - app_repl(a_l % 0400);} - -@ @<In cases that |a| is...@>= -case identifier: a=id_lookup(id_first,id_loc,0)-name_dir; - app_repl((a / 0400)+0200); - app_repl(a % 0400); break; -case section_name: if (t!=section_name) goto done; - else { - @<Was an `\.{@@}' missed here?@>@; - a=cur_section_name-name_dir; - app_repl((a / 0400)+0250); - app_repl(a % 0400); - @<Insert the line number into |tok_mem|@>@; break; - } -case output_defs_code: if (t!=section_name) err_print("! Misplaced @@h"); -@.Misplaced @@h@> - else { - output_defs_seen=true; - a=output_defs_flag; - app_repl((a / 0400)+0200); - app_repl(a % 0400); - @<Insert the line number into |tok_mem|@>@; - } - break; -case constant: case string: - @<Copy a string or verbatim construction or numerical constant@>@; -case ord: - @<Copy an ASCII constant@>@; -case definition: case format_code: case begin_C: if (t!=section_name) goto done; - else { - err_print("! @@d, @@f and @@c are ignored in C text"); continue; -@.@@d, @@f and @@c are ignored in C text@> - } -case new_section: goto done; - -@ @<Was an `\.{@@}'...@>= { - char *try_loc=loc; - while (*try_loc==' ' && try_loc<limit) try_loc++; - if (*try_loc=='+' && try_loc<limit) try_loc++; - while (*try_loc==' ' && try_loc<limit) try_loc++; - if (*try_loc=='=') err_print ("! Missing `@@ ' before a named section"); -@.Missing `@@ '...@> - /* user who isn't defining a section should put newline after the name, - as explained in the manual */ -} - -@ @<Copy a string...@>= - app_repl(a); /* |string| or |constant| */ - while (id_first < id_loc) { /* simplify \.{@@@@} pairs */ - if (*id_first=='@@') { - if (*(id_first+1)=='@@') id_first++; - else err_print("! Double @@ should be used in string"); -@.Double @@ should be used...@> - } - app_repl(*id_first++); - } - app_repl(a); break; - -@ This section should be rewritten on machines that don't use ASCII -code internally. -@^ASCII code dependencies@> - -@<Copy an ASCII constant@>= { - int c=(eight_bits) *id_first; - if (c=='\\') { - c=*++id_first; - if (c>='0' && c<='7') { - c-='0'; - if (*(id_first+1)>='0' && *(id_first+1)<='7') { - c=8*c+*(++id_first) - '0'; - if (*(id_first+1)>='0' && *(id_first+1)<='7' && c<32) - c=8*c+*(++id_first)- '0'; - } - } - else switch (c) { - case 't':c='\t';@+break; - case 'n':c='\n';@+break; - case 'b':c='\b';@+break; - case 'f':c='\f';@+break; - case 'v':c='\v';@+break; - case 'r':c='\r';@+break; - case 'a':c='\7';@+break; - case '?':c='?';@+break; - case 'x': - if (xisdigit(*(id_first+1))) c=*(++id_first)-'0'; - else if (xisxdigit(*(id_first+1))) { - ++id_first; - c=toupper((eight_bits)*id_first)-'A'+10; - } - if (xisdigit(*(id_first+1))) c=16*c+*(++id_first)-'0'; - else if (xisxdigit(*(id_first+1))) { - ++id_first; - c=16*c+toupper((eight_bits)*id_first)-'A'+10; - } - break; - case '\\':c='\\';@+break; - case '\'':c='\'';@+break; - case '\"':c='\"';@+break; - default: err_print("! Unrecognized escape sequence"); -@.Unrecognized escape sequence@> - } - }@/ - /* at this point |c| should have been converted to its ASCII code number */ - app_repl(constant); - if (c>=100) app_repl('0'+c/100); - if (c>=10) app_repl('0'+(c/10)%10); - app_repl('0'+c%10); - app_repl(constant); -} -break; - -@* Scanning a section. -The |scan_section| procedure starts when `\.{@@\ }' or `\.{@@*}' has been -sensed in the input, and it proceeds until the end of that section. It -uses |section_count| to keep track of the current section number; with luck, -\.{CWEAVE} and \.{CTANGLE} will both assign the same numbers to sections. - -The body of |scan_section| is a loop where we look for control codes -that are significant to \.{CTANGLE}: those -that delimit a definition, the \CEE/ part of a module, or a new module. - -@c -static void -scan_section(void) -{ - name_pointer p; /* section name for the current section */ - text_pointer q; /* text for the current section */ - sixteen_bits a; /* token for left-hand side of definition */ - section_count++; @+ no_where=true; - if (*(loc-1)=='*' && show_progress) { /* starred section */ - printf("*%d",section_count); update_terminal; - } - next_control=0; - while (true) { - @<Skip ahead until |next_control| corresponds to \.{@@d}, \.{@@<}, - \.{@@\ } or the like@>@; - if (next_control == definition) { /* \.{@@d} */ - @<Scan a definition@>@; - continue; - } - if (next_control == begin_C) { /* \.{@@c} or \.{@@p} */ - p=name_dir; break; - } - if (next_control == section_name) { /* \.{@@<} or \.{@@(} */ - p=cur_section_name; - @<If section is not being defined, |continue| @>@; - break; - } - return; /* \.{@@\ } or \.{@@*} */ - } - no_where=print_where=false; - @<Scan the \CEE/ part of the current section@>@; -} - -@ @<Predecl...@>=@+static void scan_section(void); - -@ At the top of this loop, if |next_control==section_name|, the -section name has already been scanned (see |@<Get control code -and...@>|). Thus, if we encounter |next_control==section_name| in the -skip-ahead process, we should likewise scan the section name, so later -processing will be the same in both cases. - -@<Skip ahead until |next_control| ...@>= -while (next_control<definition) - /* |definition| is the lowest of the ``significant'' codes */ - if((next_control=skip_ahead())==section_name){ - loc-=2; next_control=get_next(); - } - -@ @<Scan a definition@>= { - while ((next_control=get_next())=='\n'); /* allow newline before definition */ - if (next_control!=identifier) { - err_print("! Definition flushed, must start with identifier"); -@.Definition flushed...@> - continue; - } - app_repl(((a=id_lookup(id_first,id_loc,0)-name_dir) / 0400)+0200); - /* append the lhs */ - app_repl(a % 0400); - if (*loc!='(') { /* identifier must be separated from replacement text */ - app_repl(string); app_repl(' '); app_repl(string); - } - scan_repl(macro); - cur_text->text_link=macro; -} - -@ If the section name is not followed by \.{=} or \.{+=}, no \CEE/ -code is forthcoming: the section is being cited, not being -defined. This use is illegal after the definition part of the -current section has started, except inside a comment, but -\.{CTANGLE} does not enforce this rule; it simply ignores the offending -section name and everything following it, up to the next significant -control code. - -@<If section is not being defined, |continue| @>= -while ((next_control=get_next())=='+'); /* allow optional \.{+=} */ -if (next_control!='=' && next_control!=eq_eq) - continue; - -@ @<Scan the \CEE/...@>= -@<Insert the section number into |tok_mem|@>@; -scan_repl(section_name); /* now |cur_text| points to the replacement text */ -@<Update the data structure so that the replacement text is accessible@>@; - -@ @<Insert the section number...@>= -store_two_bytes((sixteen_bits)(0150000+section_count)); - /* |0150000==0320*0400| */ - -@ @<Update the data...@>= -if (p==name_dir||p==0) { /* unnamed section, or bad section name */ - (last_unnamed)->text_link=cur_text-text_info; last_unnamed=cur_text; -} -else if (p->equiv==(void *)text_info) p->equiv=(void *)cur_text; - /* first section of this name */ -else { - q=(text_pointer)p->equiv; - while (q->text_link<section_flag) - q=q->text_link+text_info; /* find end of list */ - q->text_link=cur_text-text_info; -} -cur_text->text_link=section_flag; - /* mark this replacement text as a nonmacro */ - -@ @c -static void -phase_one(void) { - phase=1; - section_count=0; - reset_input(); - skip_limbo(); - while (!input_has_ended) scan_section(); - check_complete(); - phase=2; -} - -@ @<Predec...@>=@+static void phase_one(void); - -@ Only a small subset of the control codes is legal in limbo, so limbo -processing is straightforward. - -@c -static void -skip_limbo(void) -{ - char c; - while (true) { - if (loc>limit && get_line()==false) return; - *(limit+1)='@@'; - while (*loc!='@@') loc++; - if (loc++<=limit) { - c=*loc++; - if (ccode[(eight_bits)c]==new_section) break; - switch (ccode[(eight_bits)c]) { - case translit_code: @<Read in transliteration of a character@>@; break; - case format_code: case '@@': break; - case control_text: if (c=='q' || c=='Q') { - while ((c=skip_ahead())=='@@'); - if (*(loc-1)!='>') - err_print("! Double @@ should be used in control text"); -@.Double @@ should be used...@> - break; - } /* otherwise fall through */ - default: err_print("! Double @@ should be used in limbo"); -@.Double @@ should be used...@> - } - } - } -} - -@ @<Predecl...@>=@+static void skip_limbo(void); - -@ @<Read in transliteration of a character@>= - while(xisspace(*loc)&&loc<limit) loc++; - loc+=3; - if (loc>limit || !xisxdigit(*(loc-3)) || !xisxdigit(*(loc-2)) @| - || (*(loc-3)>='0' && *(loc-3)<='7') || !xisspace(*(loc-1))) - err_print("! Improper hex number following @@l"); -@.Improper hex number...@> - else { - unsigned int i; - char *beg; - sscanf(loc-3,"%x",&i); - while(xisspace(*loc)&&loc<limit) loc++; - beg=loc; - while(loc<limit&&(xisalpha(*loc)||xisdigit(*loc)||*loc=='_')) loc++; - if (loc-beg>=translit_length) - err_print("! Replacement string in @@l too long"); -@.Replacement string in @@l...@> - else{ - strncpy(translit[i-0200],beg,(size_t)(loc-beg)); - translit[i-0200][loc-beg]='\0'; - } - } - -@ Because on some systems the difference between two pointers is a |ptrdiff_t| -but not an |int|, we use \.{\%ld} to print these quantities. - -@c -void -print_stats(void) { - puts("\nMemory usage statistics:"); - printf("%ld names (out of %ld)\n", - (ptrdiff_t)(name_ptr-name_dir),(long)max_names); - printf("%ld replacement texts (out of %ld)\n", - (ptrdiff_t)(text_ptr-text_info),(long)max_texts); - printf("%ld bytes (out of %ld)\n", - (ptrdiff_t)(byte_ptr-byte_mem),(long)max_bytes); - printf("%ld tokens (out of %ld)\n", - (ptrdiff_t)(tok_ptr-tok_mem),(long)max_toks); -} - -@** Index. -Here is a cross-reference table for \.{CTANGLE}. -All sections in which an identifier is -used are listed with that identifier, except that reserved words are -indexed only when they appear in format definitions, and the appearances -of identifiers in section names are not indexed. Underlined entries -correspond to where the identifier was declared. Error messages and -a few other things like ``ASCII code dependencies'' are indexed here too. |