summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/cwebdir/cweave.w
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/cwebdir/cweave.w
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/cwebdir/cweave.w')
-rw-r--r--Build/source/texk/web2c/cwebdir/cweave.w4696
1 files changed, 4696 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/cwebdir/cweave.w b/Build/source/texk/web2c/cwebdir/cweave.w
new file mode 100644
index 00000000000..a8e31055b23
--- /dev/null
+++ b/Build/source/texk/web2c/cwebdir/cweave.w
@@ -0,0 +1,4696 @@
+% 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 'cweave.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
+\def\dleft{[\![} \def\dright{]\!]} % double brackets
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+\def\({} % ) kludge for alphabetizing certain section names
+\def\TeXxstring{\\{\TEX/\_string}}
+\def\skipxTeX{\\{skip\_\TEX/}}
+\def\copyxTeX{\\{copy\_\TEX/}}
+
+\def\title{CWEAVE (Version 4.2)}
+\def\topofcontents{\null\vfill
+ \centerline{\titlefont The {\ttitlefont CWEAVE} 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 \.{CWEAVE} program by Silvio Levy and Donald E. Knuth,
+based on \.{WEAVE} by Knuth.
+We are thankful to Steve Avery,
+Nelson Beebe, Hans-Hermann Bode (to whom the original \CPLUSPLUS/ adaptation
+is due), Klaus Guntermann, Norman Ramsey, Tomas Rokicki, Joachim Schnitter,
+Joachim Schrod, Lee Wittenberg, Saroj Mahapatra, Cesar Augusto Rorato
+Crusius, and others who have contributed improvements.
+
+The ``banner line'' defined here should be changed whenever \.{CWEAVE}
+is modified.
+
+@d banner "This is CWEAVE (Version 4.2)"
+
+@c @<Include files@>@/
+@h
+@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/
+@<Typedef declarations@>@/
+@<Private variables@>@/
+@<Predeclaration of procedures@>
+
+@ \.{CWEAVE} has a fairly straightforward outline. It operates in
+three phases: First it inputs the source file and stores cross-reference
+data, then it inputs the source once again and produces the \TEX/ output
+file, finally it sorts and outputs the index.
+
+Please read the documentation for \.{common}, the set of routines common
+to \.{CTANGLE} and \.{CWEAVE}, before proceeding further.
+
+@c
+int main (@t\1\1@>
+int ac, /* argument count */
+char **av@t\2\2@>) /* argument values */
+{
+ argc=ac; argv=av;
+ program=cweave;
+ @<Set initial values@>@;
+ common_init();
+ @<Start \TEX/ output@>@;
+ if (show_banner) puts(banner); /* print a ``banner line'' */
+ @<Store all the reserved words@>@;
+ phase_one(); /* read all the user's text and store the cross-references */
+ phase_two(); /* read all the text again and translate it to \TEX/ form */
+ phase_three(); /* output the cross-reference index */
+ if (tracing==2 && !show_progress) new_line;
+ 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
+
+@ The following parameters were sufficient in the original \.{WEAVE} to
+handle \TEX/, so they should be sufficient for most applications of \.{CWEAVE}.
+
+@d line_length 80 /* lines of \TEX/ output have at most this many characters;
+ should be less than 256 */
+@d max_refs 65535 /* number of cross-references; must be less than 65536 */
+@d max_texts 10239 /* number of phrases in \CEE/ texts being parsed;
+ must be less than 10240 */
+@d max_scraps 10000 /* number of tokens in \CEE/ texts being parsed */
+
+@* Data structures exclusive to {\tt CWEAVE}.
+As explained in \.{common.w}, the field of a |name_info| structure
+that contains the |rlink| of a section name is used for a completely
+different purpose in the case of identifiers. It is then called the
+|ilk| of the identifier, and it is used to
+distinguish between various types of identifiers, as follows:
+
+\yskip\hang |normal| and |func_template| identifiers are part of the
+\CEE/ program that will appear in italic type (or in typewriter type
+if all uppercase).
+
+\yskip\hang |custom| identifiers are part of the \CEE/ program that
+will be typeset in special ways.
+
+\yskip\hang |roman| identifiers are index entries that appear after
+\.{@@\^} in the \.{CWEB} file.
+
+\yskip\hang |wildcard| identifiers are index entries that appear after
+\.{@@:} in the \.{CWEB} file.
+
+\yskip\hang |typewriter| identifiers are index entries that appear after
+\.{@@.} in the \.{CWEB} file.
+
+\yskip\hang |alfop|, \dots, |template_like|
+identifiers are \CEE/ or \CPLUSPLUS/ reserved words whose |ilk|
+explains how they are to be treated when \CEE/ code is being
+formatted.
+
+@d ilk dummy.Ilk
+@d normal 0 /* ordinary identifiers have |normal| ilk */
+@d roman 1 /* normal index entries have |roman| ilk */
+@d wildcard 2 /* user-formatted index entries have |wildcard| ilk */
+@d typewriter 3 /* `typewriter type' entries have |typewriter| ilk */
+@d abnormal(a) (a->ilk>typewriter) /* tells if a name is special */
+@d func_template 4 /* identifiers that can be followed by optional template */
+@d custom 5 /* identifiers with user-given control sequence */
+@d alfop 22 /* alphabetic operators like \&{and} or \&{not\_eq} */
+@d else_like 26 /* \&{else} */
+@d public_like 40 /* \&{public}, \&{private}, \&{protected} */
+@d operator_like 41 /* \&{operator} */
+@d new_like 42 /* \&{new} */
+@d catch_like 43 /* \&{catch} */
+@d for_like 45 /* \&{for}, \&{switch}, \&{while} */
+@d do_like 46 /* \&{do} */
+@d if_like 47 /* \&{if}, \&{ifdef}, \&{endif}, \&{pragma}, \dots */
+@d delete_like 48 /* \&{delete} */
+@d raw_ubin 49 /* `\.\&' or `\.*' when looking for \&{const} following */
+@d const_like 50 /* \&{const}, \&{volatile} */
+@d raw_int 51 /* \&{int}, \&{char}, \dots; also structure and class names */
+@d int_like 52 /* same, when not followed by left parenthesis or \DC\ */
+@d case_like 53 /* \&{case}, \&{return}, \&{goto}, \&{break}, \&{continue} */
+@d sizeof_like 54 /* \&{sizeof} */
+@d struct_like 55 /* \&{struct}, \&{union}, \&{enum}, \&{class} */
+@d typedef_like 56 /* \&{typedef} */
+@d define_like 57 /* \&{define} */
+@d template_like 58 /* \&{template} */
+
+@ We keep track of the current section number in |section_count|, which
+is the total number of sections that have started. Sections which have
+been altered by a change file entry have their |changed_section| flag
+turned on during the first phase.
+
+@<Private...@>=
+static boolean change_exists; /* has any section changed? */
+
+@ The other large memory area in \.{CWEAVE} keeps the cross-reference data.
+All uses of the name |p| are recorded in a linked list beginning at
+|p->xref|, which points into the |xmem| array. The elements of |xmem|
+are structures consisting of an integer, |num|, and a pointer |xlink|
+to another element of |xmem|. If |x=p->xref| is a pointer into |xmem|,
+the value of |x->num| is either a section number where |p| is used,
+or |cite_flag| plus a section number where |p| is mentioned,
+or |def_flag| plus a section number where |p| is defined;
+and |x->xlink| points to the next such cross-reference for |p|,
+if any. This list of cross-references is in decreasing order by
+section number. The next unused slot in |xmem| is |xref_ptr|.
+The linked list ends at |&xmem[0]|.
+
+The global variable |xref_switch| is set either to |def_flag| or to zero,
+depending on whether the next cross-reference to an identifier is to be
+underlined or not in the index. This switch is set to |def_flag| when
+\.{@@!} or \.{@@d} is scanned, and it is cleared to zero when
+the next identifier or index entry cross-reference has been made.
+Similarly, the global variable |section_xref_switch| is either
+|def_flag| or |cite_flag| or zero, depending
+on whether a section name is being defined, cited or used in \CEE/ text.
+
+@<Type...@>=
+typedef struct xref_info {
+ sixteen_bits num; /* section number plus zero or |def_flag| */
+ struct xref_info *xlink; /* pointer to the previous cross-reference */
+} xref_info;
+typedef xref_info *xref_pointer;
+
+@ @<Private...@>=
+static xref_info xmem[max_refs]; /* contains cross-reference information */
+static xref_pointer xmem_end = xmem+max_refs-1;
+static xref_pointer xref_ptr; /* the largest occupied position in |xmem| */
+static sixteen_bits xref_switch,section_xref_switch; /* either zero or |def_flag| */
+
+@ A section that is used for multi-file output (with the \.{@@(} feature)
+has a special first cross-reference whose |num| field is |file_flag|.
+
+@d file_flag (3*cite_flag)
+@d def_flag (2*cite_flag)
+@d cite_flag 10240 /* must be strictly larger than |max_sections| */
+@d xref equiv_or_xref
+
+@<Set init...@>=
+xref_ptr=xmem; init_node(name_dir); xref_switch=0; section_xref_switch=0;
+xmem->num=0; /* sentinel value */
+
+@ A new cross-reference for an identifier is formed by calling |new_xref|,
+which discards duplicate entries and ignores non-underlined references
+to one-letter identifiers or \CEE/'s reserved words.
+
+If the user has sent the |no_xref| flag (the \.{-x} option of the command line),
+it is unnecessary to keep track of cross-references for identifiers.
+If one were careful, one could probably make more changes around section
+100 to avoid a lot of identifier looking up.
+
+@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference");
+ else (++xref_ptr)->num=c;
+@d no_xref (!make_xrefs)
+@d is_tiny(p) ((p+1)->byte_start==(p)->byte_start+1)
+@d unindexed(a) (a<res_wd_end && a->ilk>=custom)
+ /* tells if uses of a name are to be indexed */
+
+@<Predecl...@>=
+static void new_xref(name_pointer);@/
+static void new_section_xref(name_pointer);@/
+static void set_file_flag(name_pointer);
+
+@ @c
+static void
+new_xref(
+name_pointer p)
+{
+ xref_pointer q; /* pointer to previous cross-reference */
+ sixteen_bits m, n; /* new and previous cross-reference value */
+ if (no_xref) return;
+ if ((unindexed(p) || is_tiny(p)) && xref_switch==0) return;
+ m=section_count+xref_switch; xref_switch=0; q=(xref_pointer)p->xref;
+ if (q != xmem) {
+ n=q->num;
+ if (n==m || n==m+def_flag) return;
+ else if (m==n+def_flag) {
+ q->num=m; return;
+ }
+ }
+ append_xref(m); xref_ptr->xlink=q; update_node(p);
+}
+
+@ The cross-reference lists for section names are slightly different.
+Suppose that a section name is defined in sections $m_1$, \dots,
+$m_k$, cited in sections $n_1$, \dots, $n_l$, and used in sections
+$p_1$, \dots, $p_j$. Then its list will contain $m_1+|def_flag|$,
+\dots, $m_k+|def_flag|$, $n_1+|cite_flag|$, \dots,
+$n_l+|cite_flag|$, $p_1$, \dots, $p_j$, in this order.
+
+Although this method of storage takes quadratic time with respect to
+the length of the list, under foreseeable uses of \.{CWEAVE} this inefficiency
+is insignificant.
+
+@c
+static void
+new_section_xref(
+name_pointer p)
+{
+ xref_pointer q,r; /* pointers to previous cross-references */
+ q=(xref_pointer)p->xref; r=xmem;
+ if (q>xmem)
+ while (q->num>section_xref_switch) {r=q; q=q->xlink;}
+ if (r->num==section_count+section_xref_switch)
+ return; /* don't duplicate entries */
+ append_xref(section_count+section_xref_switch);
+ xref_ptr->xlink=q; section_xref_switch=0;
+ if (r==xmem) update_node(p);
+ else r->xlink=xref_ptr;
+}
+
+@ The cross-reference list for a section name may also begin with
+|file_flag|. Here's how that flag gets put~in.
+
+@c
+static void
+set_file_flag(
+name_pointer p)
+{
+ xref_pointer q;
+ q=(xref_pointer)p->xref;
+ if (q->num==file_flag) return;
+ append_xref(file_flag);
+ xref_ptr->xlink = q;
+ update_node(p);
+}
+
+@ A third large area of memory is used for sixteen-bit `tokens', which appear
+in short lists similar to the strings of characters in |byte_mem|. Token lists
+are used to contain the result of \CEE/ code translated into \TEX/ form;
+further details about them will be explained later. A \&{text\_pointer}
+variable is an index into |tok_start|.
+
+@<Typed...@>=
+typedef sixteen_bits token;
+typedef token *token_pointer;
+typedef token_pointer *text_pointer;
+
+@ The first position of |tok_mem|
+that is unoccupied by replacement text is called |tok_ptr|, and the first
+unused location of |tok_start| is called |text_ptr|.
+Thus, we usually have |*text_ptr==tok_ptr|.
+
+@<Private...@>=
+static token tok_mem[max_toks]; /* tokens */
+static token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */
+static token_pointer tok_ptr; /* first unused position in |tok_mem| */
+static token_pointer max_tok_ptr; /* largest value of |tok_ptr| */
+static token_pointer tok_start[max_texts]; /* directory into |tok_mem| */
+static text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */
+static text_pointer text_ptr; /* first unused position in |tok_start| */
+static text_pointer max_text_ptr; /* largest value of |text_ptr| */
+
+@ @<Set init...@>=
+tok_ptr=max_tok_ptr=tok_mem+1;@/
+tok_start[0]=tok_start[1]=tok_mem+1;@/
+text_ptr=max_text_ptr=tok_start+1;
+
+@ Here are the three procedures needed to complete |id_lookup|:
+@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@>) /* desired |ilk| */
+{
+ if (length(p)!=l) return false;
+ if (p->ilk!=t && !(t==normal && abnormal(p))) return false;
+ return !strncmp(first,p->byte_start,l);
+}
+
+void
+init_p(
+name_pointer p,
+eight_bits t)
+{
+ p->ilk=t; init_node(p);
+}
+
+void
+init_node(
+name_pointer p)
+{
+ p->xref=(void *)xmem;
+}
+
+static void
+update_node(
+name_pointer p)
+{
+ p->xref=(void *)xref_ptr;
+}
+
+@ @<Predecl...@>=@+static void update_node(name_pointer p);
+
+@ We have to get \CEE/'s
+reserved words into the hash table, and the simplest way to do this is
+to insert them every time \.{CWEAVE} is run. Fortunately there are relatively
+few reserved words. (Some of these are not strictly ``reserved,'' but
+are defined in header files of the ISO Standard \CEE/ Library.)
+@^reserved words@>
+
+@<Store all the reserved words@>=
+id_lookup("and",NULL,alfop);
+id_lookup("and_eq",NULL,alfop);
+id_lookup("asm",NULL,sizeof_like);
+id_lookup("auto",NULL,int_like);
+id_lookup("bitand",NULL,alfop);
+id_lookup("bitor",NULL,alfop);
+id_lookup("bool",NULL,raw_int);
+id_lookup("break",NULL,case_like);
+id_lookup("case",NULL,case_like);
+id_lookup("catch",NULL,catch_like);
+id_lookup("char",NULL,raw_int);
+id_lookup("class",NULL,struct_like);
+id_lookup("clock_t",NULL,raw_int);
+id_lookup("compl",NULL,alfop);
+id_lookup("const",NULL,const_like);
+id_lookup("const_cast",NULL,raw_int);
+id_lookup("continue",NULL,case_like);
+id_lookup("default",NULL,case_like);
+id_lookup("define",NULL,define_like);
+id_lookup("defined",NULL,sizeof_like);
+id_lookup("delete",NULL,delete_like);
+id_lookup("div_t",NULL,raw_int);
+id_lookup("do",NULL,do_like);
+id_lookup("double",NULL,raw_int);
+id_lookup("dynamic_cast",NULL,raw_int);
+id_lookup("elif",NULL,if_like);
+id_lookup("else",NULL,else_like);
+id_lookup("endif",NULL,if_like);
+id_lookup("enum",NULL,struct_like);
+id_lookup("error",NULL,if_like);
+id_lookup("explicit",NULL,int_like);
+id_lookup("export",NULL,int_like);
+id_lookup("extern",NULL,int_like);
+id_lookup("FILE",NULL,raw_int);
+id_lookup("float",NULL,raw_int);
+id_lookup("for",NULL,for_like);
+id_lookup("fpos_t",NULL,raw_int);
+id_lookup("friend",NULL,int_like);
+id_lookup("goto",NULL,case_like);
+id_lookup("if",NULL,if_like);
+id_lookup("ifdef",NULL,if_like);
+id_lookup("ifndef",NULL,if_like);
+id_lookup("include",NULL,if_like);
+id_lookup("inline",NULL,int_like);
+id_lookup("int",NULL,raw_int);
+id_lookup("jmp_buf",NULL,raw_int);
+id_lookup("ldiv_t",NULL,raw_int);
+id_lookup("line",NULL,if_like);
+id_lookup("long",NULL,raw_int);
+id_lookup("mutable",NULL,int_like);
+id_lookup("namespace",NULL,struct_like);
+id_lookup("new",NULL,new_like);
+id_lookup("not",NULL,alfop);
+id_lookup("not_eq",NULL,alfop);
+id_lookup("NULL",NULL,custom);
+id_lookup("offsetof",NULL,raw_int);
+id_lookup("operator",NULL,operator_like);
+id_lookup("or",NULL,alfop);
+id_lookup("or_eq",NULL,alfop);
+id_lookup("pragma",NULL,if_like);
+id_lookup("private",NULL,public_like);
+id_lookup("protected",NULL,public_like);
+id_lookup("ptrdiff_t",NULL,raw_int);
+id_lookup("public",NULL,public_like);
+id_lookup("register",NULL,int_like);
+id_lookup("reinterpret_cast",NULL,raw_int);
+id_lookup("return",NULL,case_like);
+id_lookup("short",NULL,raw_int);
+id_lookup("sig_atomic_t",NULL,raw_int);
+id_lookup("signed",NULL,raw_int);
+id_lookup("size_t",NULL,raw_int);
+id_lookup("sizeof",NULL,sizeof_like);
+id_lookup("static",NULL,int_like);
+id_lookup("static_cast",NULL,raw_int);
+id_lookup("struct",NULL,struct_like);
+id_lookup("switch",NULL,for_like);
+id_lookup("template",NULL,template_like);
+id_lookup("this",NULL,custom);
+id_lookup("throw",NULL,case_like);
+id_lookup("time_t",NULL,raw_int);
+id_lookup("try",NULL,else_like);
+id_lookup("typedef",NULL,typedef_like);
+id_lookup("typeid",NULL,raw_int);
+id_lookup("typename",NULL,struct_like);
+id_lookup("undef",NULL,if_like);
+id_lookup("union",NULL,struct_like);
+id_lookup("unsigned",NULL,raw_int);
+id_lookup("using",NULL,int_like);
+id_lookup("va_dcl",NULL,decl); /* Berkeley's variable-arg-list convention */
+id_lookup("va_list",NULL,raw_int); /* ditto */
+id_lookup("virtual",NULL,int_like);
+id_lookup("void",NULL,raw_int);
+id_lookup("volatile",NULL,const_like);
+id_lookup("wchar_t",NULL,raw_int);
+id_lookup("while",NULL,for_like);
+id_lookup("xor",NULL,alfop);
+id_lookup("xor_eq",NULL,alfop);@+ res_wd_end=name_ptr;
+id_lookup("TeX",NULL,custom);
+id_lookup("make_pair",NULL,func_template);
+
+@* Lexical scanning.
+Let us now consider the subroutines that read the \.{CWEB} source file
+and break it into meaningful units. There are four such procedures:
+One simply skips to the next `\.{@@\ }' or `\.{@@*}' that begins a
+section; another passes over the \TEX/ text at the beginning of a
+section; the third passes over the \TEX/ text in a \CEE/ comment;
+and the last, which is the most interesting, gets the next token of
+a \CEE/ text. They all use the pointers |limit| and |loc| into
+the line of input currently being studied.
+
+@ Control codes in \.{CWEB}, which begin with `\.{@@}', are converted
+into a numeric code designed to simplify \.{CWEAVE}'s logic; for example,
+larger numbers are given to the control codes that denote more significant
+milestones, and the code of |new_section| should be the largest of
+all. Some of these numeric control codes take the place of |char|
+control codes that will not otherwise appear in the output of the
+scanning routines.
+@^ASCII code dependencies@>
+
+@d ignore 00 /* control code of no interest to \.{CWEAVE} */
+@d verbatim 02 /* takes the place of extended ASCII \.{\char2} */
+@d begin_short_comment 03 /* \CPLUSPLUS/ short comment */
+@d begin_comment '\t' /* tab marks will not appear */
+@d underline '\n' /* this code will be intercepted without confusion */
+@d noop 0177 /* takes the place of ASCII delete */
+@d xref_roman 0203 /* control code for `\.{@@\^}' */
+@d xref_wildcard 0204 /* control code for `\.{@@:}' */
+@d xref_typewriter 0205 /* control code for `\.{@@.}' */
+@d TeX_string 0206 /* control code for `\.{@@t}' */
+@f TeX_string TeX
+@d ord 0207 /* control code for `\.{@@'}' */
+@d join 0210 /* control code for `\.{@@\&}' */
+@d thin_space 0211 /* control code for `\.{@@,}' */
+@d math_break 0212 /* control code for `\.{@@\v}' */
+@d line_break 0213 /* control code for `\.{@@/}' */
+@d big_line_break 0214 /* control code for `\.{@@\#}' */
+@d no_line_break 0215 /* control code for `\.{@@+}' */
+@d pseudo_semi 0216 /* control code for `\.{@@;}' */
+@d macro_arg_open 0220 /* control code for `\.{@@[}' */
+@d macro_arg_close 0221 /* control code for `\.{@@]}' */
+@d trace 0222 /* control code for `\.{@@0}', `\.{@@1}' and `\.{@@2}' */
+@d translit_code 0223 /* control code for `\.{@@l}' */
+@d output_defs_code 0224 /* control code for `\.{@@h}' */
+@d format_code 0225 /* control code for `\.{@@f}' and `\.{@@s}' */
+@d definition 0226 /* control code for `\.{@@d}' */
+@d begin_C 0227 /* control code for `\.{@@c}' */
+@d section_name 0230 /* control code for `\.{@@<}' */
+@d new_section 0231 /* control code for `\.{@@\ }' and `\.{@@*}' */
+
+@ Control codes are converted to \.{CWEAVE}'s internal
+representation by means of the table |ccode|.
+
+@<Private...@>=
+static eight_bits ccode[256]; /* meaning of a char following \.{@@} */
+
+@ @<Set ini...@>=
+{int c; for (c=0; c<256; c++) ccode[c]=0;}
+ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f']
+ =ccode['*']=new_section;
+ccode['@@']='@@'; /* `quoted' at sign */
+ccode['=']=verbatim;
+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['t']=ccode['T']=TeX_string;
+ccode['l']=ccode['L']=translit_code;
+ccode['q']=ccode['Q']=noop;
+ccode['h']=ccode['H']=output_defs_code;
+ccode['&']=join; ccode['<']=ccode['(']=section_name;
+ccode['!']=underline; ccode['^']=xref_roman;
+ccode[':']=xref_wildcard; ccode['.']=xref_typewriter; ccode[',']=thin_space;
+ccode['|']=math_break; ccode['/']=line_break; ccode['#']=big_line_break;
+ccode['+']=no_line_break; ccode[';']=pseudo_semi;
+ccode['[']=macro_arg_open; ccode[']']=macro_arg_close;
+ccode['\'']=ord;
+@<Special control codes for debugging@>@;
+
+@ Users can write
+\.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on,
+and off, respectively.
+
+@<Special control codes...@>=
+ccode['0']=ccode['1']=ccode['2']=trace;
+
+@ The |skip_limbo| routine is used on the first pass to skip through
+portions of the input that are not in any sections, i.e., that precede
+the first section. After this procedure has been called, the value of
+|input_has_ended| will tell whether or not a section has actually been found.
+
+There's a complication that we will postpone until later: If the \.{@@s}
+operation appears in limbo, we want to use it to adjust the default
+interpretation of identifiers.
+
+@<Predec...@>=
+static void skip_limbo(void);@/
+static eight_bits skip_TeX(void);
+
+@ @c
+static void
+skip_limbo(void) {
+ while(true) {
+ if (loc>limit && get_line()==false) return;
+ *(limit+1)='@@';
+ while (*loc!='@@') loc++; /* look for `\.{@@}', then skip two chars */
+ if (loc++ <=limit) { int c=ccode[(eight_bits)*loc++];
+ if (c==new_section) return;
+ if (c==noop) skip_restricted();
+ else if (c==format_code) @<Process simple format in limbo@>@;
+ }
+ }
+}
+
+@ The |skip_TeX| routine is used on the first pass to skip through
+the \TEX/ code at the beginning of a section. It returns the next
+control code or `\.{\v}' found in the input. A |new_section| is
+assumed to exist at the very end of the file.
+
+@f skip_TeX TeX
+
+@c
+static eight_bits
+skip_TeX(void) /* skip past pure \TEX/ code */
+{
+ while (true) {
+ if (loc>limit && get_line()==false) return new_section;
+ *(limit+1)='@@';
+ while (*loc!='@@' && *loc!='|') loc++;
+ if (*loc++ =='|') return '|';
+ if (loc<=limit) return ccode[(eight_bits)*(loc++)];
+ }
+}
+
+@*1 Inputting the next token.
+As stated above, \.{CWEAVE}'s most interesting lexical scanning routine is the
+|get_next| function that inputs the next token of \CEE/ input. However,
+|get_next| is not especially complicated.
+
+The result of |get_next| is either a |char| code for some special character,
+or it is a special code representing a pair of characters (e.g., `\.{!=}'),
+or it is the numeric value computed by the |ccode|
+table, or it is one of the following special codes:
+
+\yskip\hang |identifier|: In this case the global variables |id_first| and
+|id_loc| will have been set to the beginning and ending-plus-one locations
+in the buffer, as required by the |id_lookup| routine.
+
+\yskip\hang |string|: The string will have been copied into the array
+|section_text|; |id_first| and |id_loc| are set as above (now they are
+pointers into |section_text|).
+
+\yskip\hang |constant|: The constant is copied into |section_text|, with
+slight modifications; |id_first| and |id_loc| are set.
+
+\yskip\noindent Furthermore, some of the control codes cause
+|get_next| to take additional actions:
+
+\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|,
+|verbatim|: The values of |id_first| and |id_loc| will have been set to
+the beginning and ending-plus-one locations in the buffer.
+
+\yskip\hang |section_name|: In this case the global variable |cur_section| will
+point to the |byte_start| entry for the section name that has just been scanned.
+The value of |cur_section_char| will be |'('| if the section name was
+preceded by \.{@@(} instead of \.{@@<}.
+
+\yskip\noindent If |get_next| sees `\.{@@!}'
+it sets |xref_switch| to |def_flag| and goes on to the next token.
+
+@d constant 0200 /* \CEE/ constant */
+@d string 0201 /* \CEE/ string */
+@d identifier 0202 /* \CEE/ identifier or reserved word */
+
+@<Private...@>=
+static name_pointer cur_section; /* name of section just scanned */
+static char cur_section_char; /* the character just before that name */
+
+@ As one might expect, |get_next| consists mostly of a big switch
+that branches to the various special cases that can arise.
+\CEE/ allows underscores to appear in identifiers, and some \CEE/
+compilers even allow the dollar sign.
+
+@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 */
+{
+ eight_bits c; /* the current character */
+ while (true) {
+ @<Check if we're at the end of a preprocessor command@>@;
+ if (loc>limit && get_line()==false) return new_section;
+ c=*(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)=='"'))@|
+ || (c=='<' && sharp_include_line==true))
+ @<Get a string@>@;
+ else if (xisalpha(c) || isxalpha(c) || ishigh(c))
+ @<Get an identifier@>@;
+ else if (c=='@@') @<Get control code and possible section name@>@;
+ else if (xisspace(c)) continue; /* ignore spaces and tabs */
+ if (c=='#' && loc==buffer+1) @<Raise preprocessor flag@>@;
+ mistake: @<Compress two-symbol operator@>@;
+ return c;
+ }
+}
+
+@ @<Predecl...@>=@+static eight_bits get_next(void);
+
+@ Because preprocessor commands do not fit in with the rest of the syntax
+of \CEE/,
+we have to deal with them separately. One solution is to enclose such
+commands between special markers. Thus, when a \.\# is seen as the
+first character of a line, |get_next| returns a special code
+|left_preproc| and raises a flag |preprocessing|.
+
+We can use the same internal code number for |left_preproc| as we do
+for |ord|, since |get_next| changes |ord| into a string.
+
+@d left_preproc ord /* begins a preprocessor command */
+@d right_preproc 0217 /* ends a preprocessor command */
+
+@<Private...@>=
+static boolean preprocessing=false; /* are we scanning a preprocessor command? */
+
+@ @<Raise prep...@>= {
+ preprocessing=true;
+ @<Check if next token is |include|@>@;
+ return left_preproc;
+}
+
+@ An additional complication is the freakish use of \.< and \.> to delimit
+a file name in lines that start with \.{\#include}. We must treat this file
+name as a string.
+
+@<Private...@>=
+static boolean sharp_include_line=false; /* are we scanning a \&{\#include} line? */
+
+@ @<Check if next token is |include|@>=
+while (loc<=buffer_end-7 && xisspace(*loc)) loc++;
+if (loc<=buffer_end-6 && strncmp(loc,"include",7)==0) sharp_include_line=true;
+
+@ When we get to the end of a preprocessor line,
+we lower the flag and send a code |right_preproc|, unless
+the last character was a \.\\.
+
+@<Check if we're at...@>=
+ while (loc==limit-1 && preprocessing && *loc=='\\')
+ if (get_line()==false) return new_section; /* still in preprocessor mode */
+ if (loc>=limit && preprocessing) {
+ preprocessing=sharp_include_line=false;
+ return right_preproc;
+ }
+
+@ The following code assigns values to the combinations \.{++},
+\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{\v\v}, 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(begin_comment);}
+ else if (*loc=='/') compress(begin_short_comment); break;
+ 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;
+}
+
+@ Different conventions are followed by \TEX/ and \CEE/ to express octal
+and hexadecimal numbers; it is reasonable to stick to each convention
+within its realm. Thus the \CEE/ part of a \.{CWEB} file has octals
+introduced by \.0 and hexadecimals by \.{0x}, but \.{CWEAVE} will print
+with \TEX/ macros that the user can redefine to fit the context.
+In order to simplify such macros, we replace some of the characters.
+
+Notice that in this section and the next, |id_first| and |id_loc|
+are pointers into the array |section_text|, not into |buffer|.
+
+@<Get a constant@>= {
+ id_first=id_loc=section_text+1;
+ if (*(loc-1)=='0') {
+ if (*loc=='x' || *loc=='X') {*id_loc++='^'; loc++;
+ while (xisxdigit(*loc)) *id_loc++=*loc++;} /* hex constant */
+ else if (xisdigit(*loc)) {*id_loc++='~';
+ while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */
+ else goto dec; /* decimal constant */
+ }
+ else { /* decimal constant */
+ if (*(loc-1)=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */
+ dec: *id_loc++=*(loc-1);
+ while (xisdigit(*loc) || *loc=='.') *id_loc++=*loc++;
+ if (*loc=='e' || *loc=='E') { /* float constant */
+ *id_loc++='_'; loc++;
+ if (*loc=='+' || *loc=='-') *id_loc++=*loc++;
+ while (xisdigit(*loc)) *id_loc++=*loc++;
+ }
+ }
+ while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L'
+ || *loc=='f' || *loc=='F') {
+ *id_loc++='$'; *id_loc++=toupper((eight_bits)*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;
+ if (delim=='\'' && *(loc-2)=='@@') {*++id_loc='@@'; *++id_loc='@@';}
+ *++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;
+ }
+ if (delim=='<') delim='>'; /* for file names in \&{\#include} lines */
+ 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@>
+ }
+ }
+ if ((c=*loc++)==delim) {
+ if (++id_loc<=section_text_end) *id_loc=c;
+ break;
+ }
+ if (c=='\\') { if (loc>=limit) continue;
+ else { 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);
+ printf("..."); mark_error;
+ }
+ 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=*loc++;
+ switch(ccode[(eight_bits)c]) {
+ case translit_code: err_print("! Use @@l in limbo only"); continue;
+@.Use @@l in limbo...@>
+ case underline: xref_switch=def_flag; continue;
+ case trace: tracing=c-'0'; continue;
+ case xref_roman: case xref_wildcard: case xref_typewriter: case noop:
+ case TeX_string: c=ccode[(eight_bits)c]; skip_restricted(); return c;
+ case section_name:
+ @<Scan the section name and make |cur_section| point to it@>@;
+ case verbatim: @<Scan a verbatim string@>@;
+ case ord: @<Get a string@>@;
+ default: return ccode[(eight_bits)c];
+ }
+}
+
+@ The occurrence of a section name sets |xref_switch| to zero,
+because the section name might (for example) follow \&{int}.
+
+@<Scan the section name...@>= {
+ char *k; /* pointer into |section_text| */
+ cur_section_char=*(loc-1);
+ @<Put section name into |section_text|@>@;
+ if (k-section_text>3 && strncmp(k-2,"...",3)==0)
+ cur_section=section_lookup(section_text+1,k-3,1); /* 1 indicates a prefix */
+ else cur_section=section_lookup(section_text+1,k,0);
+ xref_switch=0; 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 control code, |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...@>=
+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 (c!='@@') {
+ err_print("! Control codes are forbidden in section name"); break;
+@.Control codes are forbidden...@>
+ }
+ *(++k)='@@'; loc++; /* now |c==*loc| again */
+}
+
+@ This function skips over a restricted context at relatively high speed.
+
+@c
+static void
+skip_restricted(void)
+{
+ id_first=loc; *(limit+1)='@@';
+false_alarm:
+ while (*loc!='@@') loc++;
+ id_loc=loc;
+ if (loc++>limit) {
+ err_print("! Control text didn't end"); loc=limit;
+@.Control text didn't end@>
+ }
+ else {
+ if (*loc=='@@'&&loc<=limit) {loc++; goto false_alarm;}
+ if (*loc++!='>')
+ err_print("! Control codes are forbidden in control text");
+@.Control codes are forbidden...@>
+ }
+}
+
+@ @<Predecl...@>=@+static void skip_restricted(void);
+
+@ At the present point in the program we
+have |*(loc-1)==verbatim|; 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 verbatim;
+}
+
+@** Phase one processing.
+We now have accumulated enough subroutines to make it possible to carry out
+\.{CWEAVE}'s first pass over the source file. If everything works right,
+both phase one and phase two of \.{CWEAVE} will assign the same numbers to
+sections, and these numbers will agree with what \.{CTANGLE} does.
+
+The global variable |next_control| often contains the most recent output of
+|get_next|; in interesting cases, this will be the control code that
+ended a section or part of a section.
+
+@<Private...@>=
+static eight_bits next_control; /* control code waiting to be acting upon */
+
+@ The overall processing strategy in phase one has the following
+straightforward outline.
+
+@c
+static void
+phase_one(void) {
+ phase=1; reset_input(); section_count=0;
+ skip_limbo(); change_exists=false;
+ while (!input_has_ended)
+ @<Store cross-reference data for the current section@>@;
+ changed_section[section_count]=change_exists;
+ /* the index changes if anything does */
+ phase=2; /* prepare for second phase */
+ @<Print error messages about unused or undefined section names@>@;
+}
+
+@ @<Predecl...@>=@+static void phase_one(void);
+
+@ @<Store cross-reference data...@>=
+{
+ if (++section_count==max_sections) overflow("section number");
+ changed_section[section_count]=changing;
+ /* it will become |true| if any line changes */
+ if (*(loc-1)=='*' && show_progress) {
+ printf("*%d",section_count);
+ update_terminal; /* print a progress report */
+ }
+ @<Store cross-references in the \TEX/ part of a section@>@;
+ @<Store cross-references in the definition part of a section@>@;
+ @<Store cross-references in the \CEE/ part of a section@>@;
+ if (changed_section[section_count]) change_exists=true;
+}
+
+@ The |C_xref| subroutine stores references to identifiers in
+\CEE/ text material beginning with the current value of |next_control|
+and continuing until |next_control| is `\.\{' or `\.{\v}', or until the next
+``milestone'' is passed (i.e., |next_control>=format_code|). If
+|next_control>=format_code| when |C_xref| is called, nothing will happen;
+but if |next_control=='|'| upon entry, the procedure assumes that this is
+the `\.{\v}' preceding \CEE/ text that is to be processed.
+
+The parameter |spec_ctrl| is used to change this behavior. In most cases
+|C_xref| is called with |spec_ctrl==ignore|, which triggers the default
+processing described above. If |spec_ctrl==section_name|, section names will
+be gobbled. This is used when \CEE/ text in the \TEX/ part or inside comments
+is parsed: It allows for section names to appear in \pb, but these
+strings will not be entered into the cross reference lists since they are not
+definitions of section names.
+
+The program uses the fact that our internal code numbers satisfy
+the relations |xref_roman==identifier+roman| and |xref_wildcard==identifier
++wildcard| and |xref_typewriter==identifier+typewriter|,
+as well as |normal==0|.
+
+@<Predecl...@>=
+static void C_xref(eight_bits);@/
+static void outer_xref(void);
+
+@ @c
+static void
+C_xref(@t\1\1@> /* makes cross-references for \CEE/ identifiers */
+ eight_bits spec_ctrl@t\2\2@>)
+{
+ name_pointer p; /* a referenced name */
+ while (next_control<format_code || next_control==spec_ctrl) {
+ if (next_control>=identifier && next_control<=xref_typewriter) {
+ if (next_control>identifier) @<Replace |"@@@@"| by |"@@"| @>@;
+ p=id_lookup(id_first, id_loc,next_control-identifier); new_xref(p);
+ }
+ if (next_control==section_name) {
+ section_xref_switch=cite_flag;
+ new_section_xref(cur_section);
+ }
+ next_control=get_next();
+ if (next_control=='|' || next_control==begin_comment ||
+ next_control==begin_short_comment) return;
+ }
+}
+
+@ The |outer_xref| subroutine is like |C_xref| except that it begins
+with |next_control!='|'| and ends with |next_control>=format_code|. Thus, it
+handles \CEE/ text with embedded comments.
+
+@c
+static void
+outer_xref(void) /* extension of |C_xref| */
+{
+ int bal; /* brace level in comment */
+ while (next_control<format_code)
+ if (next_control!=begin_comment && next_control!=begin_short_comment)
+ C_xref(ignore);
+ else {
+ boolean is_long_comment=(next_control==begin_comment);
+ bal=copy_comment(is_long_comment,1); next_control='|';
+ while (bal>0) {
+ C_xref(section_name); /* do not reference section names in comments */
+ if (next_control=='|') bal=copy_comment(is_long_comment,bal);
+ else bal=0; /* an error message will occur in phase two */
+ }
+ }
+}
+
+@ In the \TEX/ part of a section, cross-reference entries are made only for
+the identifiers in \CEE/ texts enclosed in \pb, or for control texts
+enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>}
+or \.{@@:}$\,\ldots\,$\.{@@>}.
+
+@<Store cross-references in the \T...@>=
+while (true) {
+ switch (next_control=skip_TeX()) {
+ case translit_code: err_print("! Use @@l in limbo only"); continue;
+@.Use @@l in limbo...@>
+ case underline: xref_switch=def_flag; continue;
+ case trace: tracing=*(loc-1)-'0'; continue;
+ case '|': C_xref(section_name); break;
+ case xref_roman: case xref_wildcard: case xref_typewriter:
+ case noop: case section_name:
+ loc-=2; next_control=get_next(); /* scan to \.{@@>} */
+ if (next_control>=xref_roman && next_control<=xref_typewriter) {
+ @<Replace |"@@@@"| by |"@@"| @>@;
+ new_xref(id_lookup(id_first, id_loc,next_control-identifier));
+ }
+ break;
+ }
+ if (next_control>=format_code) break;
+}
+
+@ @<Replace |"@@@@"| by |"@@"| @>=
+{
+ char *src=id_first,*dst=id_first;
+ while(src<id_loc){
+ if(*src=='@@') src++;
+ *dst++=*src++;
+ }
+ id_loc=dst;
+ while (dst<src) *dst++=' '; /* clean up in case of error message display */
+}
+
+@ During the definition and \CEE/ parts of a section, cross-references
+are made for all identifiers except reserved words. However, the right
+identifier in a format definition is not referenced, and the left
+identifier is referenced only if it has been explicitly
+underlined (preceded by \.{@@!}).
+The \TEX/ code in comments is, of course, ignored, except for
+\CEE/ portions enclosed in \pb; the text of a section name is skipped
+entirely, even if it contains \pb\ constructions.
+
+The variables |lhs| and |rhs| point to the respective identifiers involved
+in a format definition.
+
+@<Private...@>=
+static name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */
+static name_pointer res_wd_end; /* pointer to the first nonreserved identifier */
+
+@ When we get to the following code we have |next_control>=format_code|.
+
+@<Store cross-references in the d...@>=
+while (next_control<=definition) { /* |format_code| or |definition| */
+ if (next_control==definition) {
+ xref_switch=def_flag; /* implied \.{@@!} */
+ next_control=get_next();
+ } else @<Process a format definition@>@;
+ outer_xref();
+}
+
+@ Error messages for improper format definitions will be issued in phase
+two. Our job in phase one is to define the |ilk| of a properly formatted
+identifier, and to remove cross-references to identifiers that we now
+discover should be unindexed.
+
+@<Process a form...@>= {
+ next_control=get_next();
+ if (next_control==identifier) {
+ lhs=id_lookup(id_first, id_loc,normal); lhs->ilk=normal;
+ if (xref_switch) new_xref(lhs);
+ next_control=get_next();
+ if (next_control==identifier) {
+ rhs=id_lookup(id_first, id_loc,normal);
+ lhs->ilk=rhs->ilk;
+ if (unindexed(lhs)) { /* retain only underlined entries */
+ xref_pointer q,r=NULL;
+ for (q=(xref_pointer)lhs->xref;q>xmem;q=q->xlink)
+ if (q->num<def_flag)
+ if (r) r->xlink=q->xlink;
+ else lhs->xref=(void *)q->xlink;
+ else r=q;
+ }
+ next_control=get_next();
+ }
+ }
+}
+
+@ A much simpler processing of format definitions occurs when the
+definition is found in limbo.
+
+@<Process simple format in limbo@>=
+{
+ if (get_next()!=identifier)
+ err_print("! Missing left identifier of @@s");
+@.Missing left identifier...@>
+ else {
+ lhs=id_lookup(id_first,id_loc,normal);
+ if (get_next()!=identifier)
+ err_print("! Missing right identifier of @@s");
+@.Missing right identifier...@>
+ else {
+ rhs=id_lookup(id_first,id_loc,normal);
+ lhs->ilk=rhs->ilk;
+ }
+ }
+}
+
+@ Finally, when the \TEX/ and definition parts have been treated, we have
+|next_control>=begin_C|.
+
+@<Store cross-references in the \CEE/...@>=
+if (next_control<=section_name) { /* |begin_C| or |section_name| */
+ if (next_control==begin_C) section_xref_switch=0;
+ else {
+ section_xref_switch=def_flag;
+ if(cur_section_char=='(' && cur_section!=name_dir)
+ set_file_flag(cur_section);
+ }
+ do {
+ if (next_control==section_name && cur_section!=name_dir)
+ new_section_xref(cur_section);
+ next_control=get_next(); outer_xref();
+ } while ( next_control<=section_name);
+}
+
+@ After phase one has looked at everything, we want to check that each
+section name was both defined and used. The variable |cur_xref| will point
+to cross-references for the current section name of interest.
+
+@<Private...@>=
+static xref_pointer cur_xref; /* temporary cross-reference pointer */
+static boolean an_output; /* did |file_flag| precede |cur_xref|? */
+
+@ The following recursive procedure
+walks through the tree of section names and prints out anomalies.
+@^recursion@>
+
+@c
+static void
+section_check(
+name_pointer p) /* print anomalies in subtree |p| */
+{
+ if (p) {
+ section_check(p->llink);
+ cur_xref=(xref_pointer)p->xref;
+ if (cur_xref->num==file_flag) {an_output=true; cur_xref=cur_xref->xlink;}
+ else an_output=false;
+ if (cur_xref->num <def_flag) {
+ fputs("\n! Never defined: <",stdout);
+ print_section_name(p); putchar('>'); mark_harmless;
+@.Never defined: <section name>@>
+ }
+ while (cur_xref->num >=cite_flag) cur_xref=cur_xref->xlink;
+ if (cur_xref==xmem && !an_output) {
+ fputs("\n! Never used: <",stdout);
+ print_section_name(p); putchar('>'); mark_harmless;
+@.Never used: <section name>@>
+ }
+ section_check(p->rlink);
+ }
+}
+
+@ @<Predecl...@>=@+static void section_check(name_pointer);
+
+@ @<Print error messages about un...@>=section_check(root);
+
+@* Low-level output routines.
+The \TEX/ output is supposed to appear in lines at most |line_length|
+characters long, so we place it into an output buffer. During the output
+process, |out_line| will hold the current line number of the line about to
+be output.
+
+@<Private...@>=
+static char out_buf[line_length+1]; /* assembled characters */
+static char *out_buf_end = out_buf+line_length; /* end of |out_buf| */
+static char *out_ptr; /* last character in |out_buf| */
+static int out_line; /* number of next line to be output */
+
+@ The |flush_buffer| routine empties the buffer up to a given breakpoint,
+and moves any remaining characters to the beginning of the next line.
+If the |per_cent| parameter is |true|, a |'%'| is appended to the line
+that is being output; in this case the breakpoint |b| should be strictly
+less than |out_buf_end|. If the |per_cent| parameter is |false|,
+trailing blanks are suppressed.
+The characters emptied from the buffer form a new line of output;
+if the |carryover| parameter is true, a |"%"| in that line will be
+carried over to the next line (so that \TEX/ will ignore the completion
+of commented-out text).
+
+@d c_line_write(c) fflush(active_file),fwrite(out_buf+1,sizeof(char),c,active_file)
+@d tex_putc(c) putc(c,active_file)
+@d tex_new_line putc('\n',active_file)
+@d tex_printf(c) fprintf(active_file,"%s",c)
+@d tex_puts(c) fputs(c,active_file)
+
+@<Predecl...@>=
+static void flush_buffer(char *,boolean,boolean);@/
+static void finish_line(void);
+
+@ @c
+static void
+flush_buffer(@t\1\1@>
+char *b, /* outputs from |out_buf+1| to |b|, where |b<=out_ptr| */
+boolean per_cent,boolean carryover@t\2\2@>)
+{
+ char *j; j=b; /* pointer into |out_buf| */
+ if (! per_cent) /* remove trailing blanks */
+ while (j>out_buf && *j==' ') j--;
+ c_line_write(j-out_buf);
+ if (per_cent) tex_putc('%');
+ tex_new_line; out_line++;
+ if (carryover)
+ while (j>out_buf)
+ if (*j--=='%' && (j==out_buf || *j!='\\')) {
+ *b--='%'; break;
+ }
+ if (b<out_ptr) memcpy(out_buf+1,b+1,(size_t)(out_ptr-b));
+ out_ptr-=b-out_buf;
+}
+
+@ When we are copying \TEX/ source material, we retain line breaks
+that occur in the input, except that an empty line is not
+output when the \TEX/ source line was nonempty. For example, a line
+of the \TEX/ file that contains only an index cross-reference entry
+will not be copied. The |finish_line| routine is called just before
+|get_line| inputs a new line, and just after a line break token has
+been emitted during the output of translated \CEE/ text.
+
+@c
+static void
+finish_line(void) /* do this at the end of a line */
+{
+ char *k; /* pointer into |buffer| */
+ if (out_ptr>out_buf) flush_buffer(out_ptr,false,false);
+ else {
+ for (k=buffer; k<=limit; k++)
+ if (!(xisspace(*k))) return;
+ flush_buffer(out_buf,false,false);
+ }
+}
+
+@ In particular, the |finish_line| procedure is called near the very
+beginning of phase two. We initialize the output variables in a slightly
+tricky way so that the first line of the output file will be
+`\.{\\input cwebmac}'.
+
+@<Start \TEX/...@>=
+out_ptr=out_buf+1; out_line=1; active_file=tex_file;
+*out_ptr='c'; tex_printf("\\input cwebma");
+
+@ When we wish to append one character |c| to the output buffer, we write
+`|out(c)|'; this will cause the buffer to be emptied if it was already
+full. If we want to append more than one character at once, we say
+|out_str(s)|, where |s| is a string containing the characters.
+
+A line break will occur at a space or after a single-nonletter
+\TEX/ control sequence.
+
+@d out(c) {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;}
+
+@<Predecl...@>=
+static void out_str(const char *);@/
+static void break_out(void);
+
+@ @c
+static void
+out_str(@t\1\1@> /* output characters from |s| to end of string */
+const char*s@t\2\2@>)
+{
+ while (*s) out(*s++);
+}
+
+@ The |break_out| routine is called just before the output buffer is about
+to overflow. To make this routine a little faster, we initialize position
+0 of the output buffer to `\.\\'; this character isn't really output.
+
+@<Set init...@>=
+out_buf[0]='\\';
+
+@ A long line is broken at a blank space or just before a backslash that isn't
+preceded by another backslash. In the latter case, a |'%'| is output at
+the break.
+
+@c
+static void
+break_out(void) /* finds a way to break the output line */
+{
+ char *k=out_ptr; /* pointer into |out_buf| */
+ while (true) {
+ if (k==out_buf) @<Print warning message, break the line, |return|@>@;
+ if (*k==' ') {
+ flush_buffer(k,false,true); return;
+ }
+ if (*(k--)=='\\' && *k!='\\') { /* we've decreased |k| */
+ flush_buffer(k,true,true); return;
+ }
+ }
+}
+
+@ We get to this section only in the unusual case that the entire output line
+consists of a string of backslashes followed by a string of nonblank
+non-backslashes. In such cases it is almost always safe to break the
+line by putting a |'%'| just before the last character.
+
+@<Print warning message...@>=
+{
+ printf("\n! Line had to be broken (output l. %d):\n",out_line);
+@.Line had to be broken@>
+ term_write(out_buf+1, out_ptr-out_buf-1);
+ new_line; mark_harmless;
+ flush_buffer(out_ptr-1,true,true); return;
+}
+
+@ Here is a macro that outputs a section number in decimal notation.
+The number to be converted by |out_section| is known to be less than
+|def_flag|, so it cannot have more than five decimal digits. If
+the section is changed, we output `\.{\\*}' just after the number.
+
+@<Predecl...@>=
+static void out_section(sixteen_bits);@/
+static void out_name(name_pointer,boolean);
+
+@ @c
+static void
+out_section(
+sixteen_bits n)
+{
+ char s[6];
+ sprintf(s,"%d",n); out_str(s);
+ if(changed_section[n]) out_str ("\\*");
+@.\\*@>
+}
+
+@ The |out_name| procedure is used to output an identifier or index
+entry, enclosing it in braces.
+
+@c
+static void
+out_name(
+name_pointer p,
+boolean quote_xalpha)
+{
+ char *k, *k_end=(p+1)->byte_start; /* pointers into |byte_mem| */
+ out('{');
+ for (k=p->byte_start; k<k_end; k++) {
+ if (isxalpha(*k) && quote_xalpha) out('\\');
+@.\\\$@>
+@.\\\_@>
+ out(*k);
+ }
+ out('}');
+}
+
+@* Routines that copy \TEX/ material.
+During phase two, we use the subroutines |copy_limbo| and |copy_TeX| (and
+|copy_comment|) in place of the analogous |skip_limbo| and |skip_TeX|
+that were used in phase one.
+
+The |copy_limbo| routine, for example, takes \TEX/ material that is not
+part of any section and transcribes it almost verbatim to the output file.
+The use of `\.{@@}' signs is severely restricted in such material:
+`\.{@@@@}' pairs are replaced by singletons; `\.{@@l}' and `\.{@@q}' and
+`\.{@@s}' are interpreted.
+
+@<Predecl...@>=
+static void copy_limbo(void);@/
+static eight_bits copy_TeX(void);@/
+static int copy_comment(boolean,int);
+
+@ @c
+static void
+copy_limbo(void)
+{
+ char c;
+ while (true) {
+ if (loc>limit && (finish_line(), get_line()==false)) return;
+ *(limit+1)='@@';
+ while (*loc!='@@') out(*(loc++));
+ if (loc++<=limit) {
+ c=*loc++;
+ if (ccode[(eight_bits)c]==new_section) break;
+ switch (ccode[(eight_bits)c]) {
+ case translit_code: out_str("\\ATL"); break;
+@.\\ATL@>
+ case '@@': out('@@'); break;
+ case noop: skip_restricted(); break;
+ case format_code: if (get_next()==identifier) get_next();
+ if (loc>=limit) get_line(); /* avoid blank lines in output */
+ break; /* the operands of \.{@@s} are ignored on this pass */
+ default: err_print("! Double @@ should be used in limbo");
+@.Double @@ should be used...@>
+ out('@@');
+ }
+ }
+ }
+}
+
+@ The |copy_TeX| routine processes the \TEX/ code at the beginning of a
+section; for example, the words you are now reading were copied in this
+way. It returns the next control code or `\.{\v}' found in the input.
+We don't copy spaces or tab marks into the beginning of a line. This
+makes the test for empty lines in |finish_line| work.
+
+@f copy_TeX TeX
+@c
+static eight_bits
+copy_TeX(void)
+{
+ char c; /* current character being copied */
+ while (true) {
+ if (loc>limit && (finish_line(), get_line()==false)) return new_section;
+ *(limit+1)='@@';
+ while ((c=*(loc++))!='|' && c!='@@') {
+ out(c);
+ if (out_ptr==out_buf+1 && (xisspace(c))) out_ptr--;
+ }
+ if (c=='|') return '|';
+ if (loc<=limit) return ccode[(eight_bits)*(loc++)];
+ }
+}
+
+@ The |copy_comment| function issues a warning if more braces are opened than
+closed, and in the case of a more serious error it supplies enough
+braces to keep \TEX/ from complaining about unbalanced braces.
+Instead of copying the \TEX/ material
+into the output buffer, this function copies it into the token memory
+(in phase two only).
+The abbreviation |app_tok(t)| is used to append token |t| to the current
+token list, and it also makes sure that it is possible to append at least
+one further token without overflow.
+
+@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;}
+
+@c
+static int copy_comment(@t\1\1@> /* copies \TeX\ code in comments */
+boolean is_long_comment, /* is this a traditional \CEE/ comment? */
+int bal@t\2\2@>) /* brace balance */
+{
+ char c; /* current character being copied */
+ while (true) {
+ if (loc>limit) {
+ if (is_long_comment) {
+ if (get_line()==false) {
+ err_print("! Input ended in mid-comment");
+@.Input ended in mid-comment@>
+ loc=buffer+1; goto done;
+ }
+ }
+ else {
+ if (bal>1) err_print("! Missing } in comment");
+@.Missing \} in comment@>
+ goto done;
+ }
+ }
+ c=*(loc++);
+ if (c=='|') return bal;
+ if (is_long_comment) @<Check for end of comment@>@;
+ if (phase==2) {
+ if (ishigh(c)) app_tok(quoted_char);
+ app_tok(c);
+ }
+ @<Copy special things when |c=='@@', '\\'|@>@;
+ if (c=='{') bal++;
+ else if (c=='}') {
+ if(bal>1) bal--;
+ else {err_print("! Extra } in comment");
+@.Extra \} in comment@>
+ if (phase==2) tok_ptr--;
+ }
+ }
+ }
+done:@<Clear |bal| and |return|@>@;
+}
+
+@ @<Check for end of comment@>=
+if (c=='*' && *loc=='/') {
+ loc++;
+ if (bal>1) err_print("! Missing } in comment");
+@.Missing \} in comment@>
+ goto done;
+}
+
+@ @<Copy special things when |c=='@@'...@>=
+if (c=='@@') {
+ if (*(loc++)!='@@') {
+ err_print("! Illegal use of @@ in comment");
+@.Illegal use of @@...@>
+ loc-=2; if (phase==2) *(tok_ptr-1)=' '; goto done;
+ }
+}
+else { if (c=='\\' && *loc!='@@') {
+ if (phase==2) app_tok(*(loc++))@; else loc++; } }
+
+@ We output
+enough right braces to keep \TEX/ happy.
+
+@<Clear |bal|...@>=
+if (phase==2) while (bal-- >0) app_tok('}');
+return 0;
+
+@** Parsing.
+The most intricate part of \.{CWEAVE} is its mechanism for converting
+\CEE/-like code into \TEX/ code, and we might as well plunge into this
+aspect of the program now. A ``bottom up'' approach is used to parse the
+\CEE/-like material, since \.{CWEAVE} must deal with fragmentary
+constructions whose overall ``part of speech'' is not known.
+
+At the lowest level, the input is represented as a sequence of entities
+that we shall call {\it scraps}, where each scrap of information consists
+of two parts, its {\it category} and its {\it translation}. The category
+is essentially a syntactic class, and the translation is a token list that
+represents \TEX/ code. Rules of syntax and semantics tell us how to
+combine adjacent scraps into larger ones, and if we are lucky an entire
+\CEE/ text that starts out as hundreds of small scraps will join
+together into one gigantic scrap whose translation is the desired \TEX/
+code. If we are unlucky, we will be left with several scraps that don't
+combine; their translations will simply be output, one by one.
+
+The combination rules are given as context-sensitive productions that are
+applied from left to right. Suppose that we are currently working on the
+sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
+production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
+no such productions exist, we try to find the longest production
+applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
+try to match $s_3\,s_4\ldots\,$, etc.
+
+A production applies if the category codes have a given pattern. For
+example, one of the productions (see rule~3) is
+$$\hbox{|exp| }\left\{\matrix{\hbox{|binop|}\cr\hbox{|ubinop|}}\right\}
+\hbox{ |exp| }\RA\hbox{ |exp|}$$
+and it means that three consecutive scraps whose respective categories are
+|exp|, |binop| (or |ubinop|),
+and |exp| are converted to one scrap whose category
+is |exp|. The translations of the original
+scraps are simply concatenated. The case of
+$$\hbox{|exp| |comma| |exp| $\RA$ |exp|} \hskip4emE_1C\,\\{opt}9\,E_2$$
+(rule 4) is only slightly more complicated:
+Here the resulting |exp| translation
+consists not only of the three original translations, but also of the
+tokens |opt| and 9 between the translations of the
+|comma| and the following |exp|.
+In the \TEX/ file, this will specify an optional line break after the
+comma, with penalty 90.
+
+At each opportunity the longest possible production is applied. For
+example, if the current sequence of scraps is |if_clause| |stmt|
+|else_like| |if_like|, rule 63 is applied; but if the sequence is
+|if_clause| |stmt| |else_like| followed by anything other than
+|if_like|, rule 64 takes effect; and if the sequence is |if_clause|
+|stmt| followed by anything other than |else_like|, rule 65 takes
+effect.
+
+Translation rules such as `$E_1C\,\\{opt}9\,E_2$' above use subscripts
+to distinguish between translations of scraps whose categories have the
+same initial letter; these subscripts are assigned from left to right.
+
+@ Here is a list of the category codes that scraps can have.
+(A few others, like |int_like|, have already been defined; the
+|cat_name| array contains a complete list.)
+
+@d exp 1 /* denotes an expression, including perhaps a single identifier */
+@d unop 2 /* denotes a unary operator */
+@d binop 3 /* denotes a binary operator */
+@d ubinop 4
+ /* denotes an operator that can be unary or binary, depending on context */
+@d cast 5 /* denotes a cast */
+@d question 6 /* denotes a question mark and possibly the expressions flanking it */
+@d lbrace 7 /* denotes a left brace */
+@d rbrace 8 /* denotes a right brace */
+@d decl_head 9 /* denotes an incomplete declaration */
+@d comma 10 /* denotes a comma */
+@d lpar 11 /* denotes a left parenthesis or left bracket */
+@d rpar 12 /* denotes a right parenthesis or right bracket */
+@d prelangle 13 /* denotes `$<$' before we know what it is */
+@d prerangle 14 /* denotes `$>$' before we know what it is */
+@d langle 15 /* denotes `$<$' when it's used as angle bracket in a template */
+@d colcol 18 /* denotes `::' */
+@d base 19 /* denotes a colon that introduces a base specifier */
+@d decl 20 /* denotes a complete declaration */
+@d struct_head 21 /* denotes the beginning of a structure specifier */
+@d stmt 23 /* denotes a complete statement */
+@d function 24 /* denotes a complete function */
+@d fn_decl 25 /* denotes a function declarator */
+@d semi 27 /* denotes a semicolon */
+@d colon 28 /* denotes a colon */
+@d tag 29 /* denotes a statement label */
+@d if_head 30 /* denotes the beginning of a compound conditional */
+@d else_head 31 /* denotes a prefix for a compound statement */
+@d if_clause 32 /* pending \.{if} together with a condition */
+@d lproc 35 /* begins a preprocessor command */
+@d rproc 36 /* ends a preprocessor command */
+@d insert 37 /* a scrap that gets combined with its neighbor */
+@d section_scrap 38 /* section name */
+@d dead 39 /* scrap that won't combine */
+@d ftemplate 59 /* \\{make\_pair} */
+@d new_exp 60 /* \&{new} and a following type identifier */
+@d begin_arg 61 /* \.{@@[} */
+@d end_arg 62 /* \.{@@]} */
+
+@<Private...@>=
+static char cat_name[256][12];
+
+@ @<Set in...@>=
+{int c; for (c=0;c<256;c++) strcpy(cat_name[c],"UNKNOWN");}
+@.UNKNOWN@>
+ strcpy(cat_name[exp],"exp");
+ strcpy(cat_name[unop],"unop");
+ strcpy(cat_name[binop],"binop");
+ strcpy(cat_name[ubinop],"ubinop");
+ strcpy(cat_name[cast],"cast");
+ strcpy(cat_name[question],"?");
+ strcpy(cat_name[lbrace],"{"@q}@>);
+ strcpy(cat_name[rbrace],@q{@>"}");
+ strcpy(cat_name[decl_head],"decl_head");
+ strcpy(cat_name[comma],",");
+ strcpy(cat_name[lpar],"(");
+ strcpy(cat_name[rpar],")");
+ strcpy(cat_name[prelangle],"<");
+ strcpy(cat_name[prerangle],">");
+ strcpy(cat_name[langle],"\\<");
+ strcpy(cat_name[colcol],"::");
+ strcpy(cat_name[base],"\\:");
+ strcpy(cat_name[decl],"decl");
+ strcpy(cat_name[struct_head],"struct_head");
+ strcpy(cat_name[alfop],"alfop");
+ strcpy(cat_name[stmt],"stmt");
+ strcpy(cat_name[function],"function");
+ strcpy(cat_name[fn_decl],"fn_decl");
+ strcpy(cat_name[else_like],"else_like");
+ strcpy(cat_name[semi],";");
+ strcpy(cat_name[colon],":");
+ strcpy(cat_name[tag],"tag");
+ strcpy(cat_name[if_head],"if_head");
+ strcpy(cat_name[else_head],"else_head");
+ strcpy(cat_name[if_clause],"if()");
+ strcpy(cat_name[lproc],"#{"@q}@>);
+ strcpy(cat_name[rproc],@q{@>"#}");
+ strcpy(cat_name[insert],"insert");
+ strcpy(cat_name[section_scrap],"section");
+ strcpy(cat_name[dead],"@@d");
+ strcpy(cat_name[public_like],"public");
+ strcpy(cat_name[operator_like],"operator");
+ strcpy(cat_name[new_like],"new");
+ strcpy(cat_name[catch_like],"catch");
+ strcpy(cat_name[for_like],"for");
+ strcpy(cat_name[do_like],"do");
+ strcpy(cat_name[if_like],"if");
+ strcpy(cat_name[delete_like],"delete");
+ strcpy(cat_name[raw_ubin],"ubinop?");
+ strcpy(cat_name[const_like],"const");
+ strcpy(cat_name[raw_int],"raw");
+ strcpy(cat_name[int_like],"int");
+ strcpy(cat_name[case_like],"case");
+ strcpy(cat_name[sizeof_like],"sizeof");
+ strcpy(cat_name[struct_like],"struct");
+ strcpy(cat_name[typedef_like],"typedef");
+ strcpy(cat_name[define_like],"define");
+ strcpy(cat_name[template_like],"template");
+ strcpy(cat_name[ftemplate],"ftemplate");
+ strcpy(cat_name[new_exp],"new_exp");
+ strcpy(cat_name[begin_arg],"@@["@q]@>);
+ strcpy(cat_name[end_arg],@q[@>"@@]");
+ strcpy(cat_name[0],"zero");
+
+@ This code allows \.{CWEAVE} to display its parsing steps.
+
+@c
+static void
+print_cat(@t\1\1@> /* symbolic printout of a category */
+eight_bits c@t\2\2@>)
+{
+ fputs(cat_name[c],stdout);
+}
+
+@ @<Predecl...@>=@+static void print_cat(eight_bits);
+
+@ The token lists for translated \TEX/ output contain some special control
+symbols as well as ordinary characters. These control symbols are
+interpreted by \.{CWEAVE} before they are written to the output file.
+
+\yskip\hang |break_space| denotes an optional line break or an en space;
+
+\yskip\hang |force| denotes a line break;
+
+\yskip\hang |big_force| denotes a line break with additional vertical space;
+
+\yskip\hang |preproc_line| denotes that the line will be printed flush left;
+
+\yskip\hang |opt| denotes an optional line break (with the continuation
+line indented two ems with respect to the normal starting position)---this
+code is followed by an integer |n|, and the break will occur with penalty
+$10n$;
+
+\yskip\hang |backup| denotes a backspace of one em;
+
+\yskip\hang |cancel| obliterates any |break_space|, |opt|, |force|, or
+|big_force| tokens that immediately precede or follow it and also cancels any
+|backup| tokens that follow it;
+
+\yskip\hang |indent| causes future lines to be indented one more em;
+
+\yskip\hang |outdent| causes future lines to be indented one less em.
+
+\yskip\noindent All of these tokens are removed from the \TEX/ output that
+comes from \CEE/ text between \pb\ signs; |break_space| and |force| and
+|big_force| become single spaces in this mode. The translation of other
+\CEE/ texts results in \TEX/ control sequences \.{\\1}, \.{\\2},
+\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7}, \.{\\8}
+corresponding respectively to
+|indent|, |outdent|, |opt|, |backup|, |break_space|, |force|,
+|big_force| and |preproc_line|.
+However, a sequence of consecutive `\.\ ', |break_space|,
+|force|, and/or |big_force| tokens is first replaced by a single token
+(the maximum of the given ones).
+
+The token |math_rel| will be translated into
+\.{\\MRL\{}, and it will get a matching \.\} later.
+Other control sequences in the \TEX/ output will be
+`\.{\\\\\{}$\,\ldots\,$\.\}'
+surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding
+reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings,
+`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and
+`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding section names, where
+|n| is the section number.
+
+@d math_rel 0206
+@d big_cancel 0210 /* like |cancel|, also overrides spaces */
+@d cancel 0211 /* overrides |backup|, |break_space|, |force|, |big_force| */
+@d indent 0212 /* one more tab (\.{\\1}) */
+@d outdent 0213 /* one less tab (\.{\\2}) */
+@d opt 0214 /* optional break in mid-statement (\.{\\3}) */
+@d backup 0215 /* stick out one unit to the left (\.{\\4}) */
+@d break_space 0216 /* optional break between statements (\.{\\5}) */
+@d force 0217 /* forced break between statements (\.{\\6}) */
+@d big_force 0220 /* forced break with additional space (\.{\\7}) */
+@d preproc_line 0221 /* begin line without indentation (\.{\\8}) */
+@^high-bit character handling@>
+@d quoted_char 0222
+ /* introduces a character token in the range |0200|--|0377| */
+@d end_translation 0223 /* special sentinel token at end of list */
+@d inserted 0224 /* sentinel to mark translations of inserts */
+@d qualifier 0225 /* introduces an explicit namespace qualifier */
+
+@ The raw input is converted into scraps according to the following table,
+which gives category codes followed by the translations.
+\def\stars {\.{**}}%
+The symbol `\stars' stands for `\.{\\\&\{{\rm identifier}\}}',
+i.e., the identifier itself treated as a reserved word.
+The right-hand column is the so-called |mathness|, which is explained
+further below.
+
+An identifier |c| of length 1 is translated as \.{\\\v c} instead of
+as \.{\\\\\{c\}}. An identifier \.{CAPS} in all caps is translated as
+\.{\\.\{CAPS\}} instead of as \.{\\\\\{CAPS\}}. An identifier that has
+become a reserved word via |typedef| is translated with \.{\\\&} replacing
+\.{\\\\} and |raw_int| replacing |exp|.
+
+A string of length greater than 20 is broken into pieces of size at most~20
+with discretionary breaks in between.
+
+\yskip\halign{\quad#\hfil&\quad#\hfil&\quad\hfil#\hfil\cr
+\.{!=}&|binop|: \.{\\I}&yes\cr
+\.{<=}&|binop|: \.{\\Z}&yes\cr
+\.{>=}&|binop|: \.{\\G}&yes\cr
+\.{==}&|binop|: \.{\\E}&yes\cr
+\.{\&\&}&|binop|: \.{\\W}&yes\cr
+\.{\v\v}&|binop|: \.{\\V}&yes\cr
+\.{++}&|unop|: \.{\\PP}&yes\cr
+\.{--}&|unop|: \.{\\MM}&yes\cr
+\.{->}&|binop|: \.{\\MG}&yes\cr
+\.{>>}&|binop|: \.{\\GG}&yes\cr
+\.{<<}&|binop|: \.{\\LL}&yes\cr
+\.{::}&|colcol|: \.{\\DC}&maybe\cr
+\.{.*}&|binop|: \.{\\PA}&yes\cr
+\.{->*}&|binop|: \.{\\MGA}&yes\cr
+\.{...}&|raw_int|: \.{\\,\\ldots\\,}&yes\cr
+\."string\."&|exp|: \.{\\.\{}string with special characters quoted\.\}&maybe\cr
+\.{@@=}string\.{@@>}&|exp|: \.{\\vb\{}string with special characters
+ quoted\.\}&maybe\cr
+\.{@@'7'}&|exp|: \.{\\.\{@@'7'\}}&maybe\cr
+\.{077} or \.{\\77}&|exp|: \.{\\T\{\\\~77\}}&maybe\cr
+\.{0x7f}&|exp|: \.{\\T\{\\\^7f\}}&maybe\cr
+\.{77}&|exp|: \.{\\T\{77\}}&maybe\cr
+\.{77L}&|exp|: \.{\\T\{77\\\$L\}}&maybe\cr
+\.{0.1E5}&|exp|: \.{\\T\{0.1\\\_5\}}&maybe\cr
+\.+&|ubinop|: \.+&yes\cr
+\.-&|ubinop|: \.-&yes\cr
+\.*&|raw_ubin|: \.*&yes\cr
+\./&|binop|: \./&yes\cr
+\.<&|prelangle|: \.{\\langle}&yes\cr
+\.=&|binop|: \.{\\K}&yes\cr
+\.>&|prerangle|: \.{\\rangle}&yes\cr
+\..&|binop|: \..&yes\cr
+\.{\v}&|binop|: \.{\\OR}&yes\cr
+\.\^&|binop|: \.{\\XOR}&yes\cr
+\.\%&|binop|: \.{\\MOD}&yes\cr
+\.?&|question|: \.{\\?}&yes\cr
+\.!&|unop|: \.{\\R}&yes\cr
+\.\~&|unop|: \.{\\CM}&yes\cr
+\.\&&|raw_ubin|: \.{\\AND}&yes\cr
+\.(&|lpar|: \.(&maybe\cr
+\.[&|lpar|: \.[&maybe\cr
+\.)&|rpar|: \.)&maybe\cr
+\.]&|rpar|: \.]&maybe\cr
+\.\{&|lbrace|: \.\{&yes\cr
+\.\}&|lbrace|: \.\}&yes\cr
+\.,&|comma|: \.,&yes\cr
+\.;&|semi|: \.;&maybe\cr
+\.:&|colon|: \.:&no\cr
+\.\# (within line)&|ubinop|: \.{\\\#}&yes\cr
+\.\# (at beginning)&|lproc|: |force| |preproc_line| \.{\\\#}&no\cr
+end of \.\# line&|rproc|: |force|&no\cr
+identifier&|exp|: \.{\\\\\{}identifier with underlines and
+ dollar signs quoted\.\}&maybe\cr
+\.{and}&|alfop|: \stars&yes\cr
+\.{and\_eq}&|alfop|: \stars&yes\cr
+\.{asm}&|sizeof_like|: \stars&maybe\cr
+\.{auto}&|int_like|: \stars&maybe\cr
+\.{bitand}&|alfop|: \stars&yes\cr
+\.{bitor}&|alfop|: \stars&yes\cr
+\.{bool}&|raw_int|: \stars&maybe\cr
+\.{break}&|case_like|: \stars&maybe\cr
+\.{case}&|case_like|: \stars&maybe\cr
+\.{catch}&|catch_like|: \stars&maybe\cr
+\.{char}&|raw_int|: \stars&maybe\cr
+\.{class}&|struct_like|: \stars&maybe\cr
+\.{clock\_t}&|raw_int|: \stars&maybe\cr
+\.{compl}&|alfop|: \stars&yes\cr
+\.{const}&|const_like|: \stars&maybe\cr
+\.{const\_cast}&|raw_int|: \stars&maybe\cr
+\.{continue}&|case_like|: \stars&maybe\cr
+\.{default}&|case_like|: \stars&maybe\cr
+\.{define}&|define_like|: \stars&maybe\cr
+\.{defined}&|sizeof_like|: \stars&maybe\cr
+\.{delete}&|delete_like|: \stars&maybe\cr
+\.{div\_t}&|raw_int|: \stars&maybe\cr
+\.{do}&|do_like|: \stars&maybe\cr
+\.{double}&|raw_int|: \stars&maybe\cr
+\.{dynamic\_cast}&|raw_int|: \stars&maybe\cr
+\.{elif}&|if_like|: \stars&maybe\cr
+\.{else}&|else_like|: \stars&maybe\cr
+\.{endif}&|if_like|: \stars&maybe\cr
+\.{enum}&|struct_like|: \stars&maybe\cr
+\.{error}&|if_like|: \stars&maybe\cr
+\.{explicit}&|int_like|: \stars&maybe\cr
+\.{export}&|int_like|: \stars&maybe\cr
+\.{extern}&|int_like|: \stars&maybe\cr
+\.{FILE}&|raw_int|: \stars&maybe\cr
+\.{float}&|raw_int|: \stars&maybe\cr
+\.{for}&|for_like|: \stars&maybe\cr
+\.{fpos\_t}&|raw_int|: \stars&maybe\cr
+\.{friend}&|int_like|: \stars&maybe\cr
+\.{goto}&|case_like|: \stars&maybe\cr
+\.{if}&|if_like|: \stars&maybe\cr
+\.{ifdef}&|if_like|: \stars&maybe\cr
+\.{ifndef}&|if_like|: \stars&maybe\cr
+\.{include}&|if_like|: \stars&maybe\cr
+\.{inline}&|int_like|: \stars&maybe\cr
+\.{int}&|raw_int|: \stars&maybe\cr
+\.{jmp\_buf}&|raw_int|: \stars&maybe\cr
+\.{ldiv\_t}&|raw_int|: \stars&maybe\cr
+\.{line}&|if_like|: \stars&maybe\cr
+\.{long}&|raw_int|: \stars&maybe\cr
+\.{make\_pair}&|ftemplate|: \.{\\\\\{make\\\_pair\}}&maybe\cr
+\.{mutable}&|int_like|: \stars&maybe\cr
+\.{namespace}&|struct_like|: \stars&maybe\cr
+\.{new}&|new_like|: \stars&maybe\cr
+\.{not}&|alfop|: \stars&yes\cr
+\.{not\_eq}&|alfop|: \stars&yes\cr
+\.{NULL}&|exp|: \.{\\NULL}&yes\cr
+\.{offsetof}&|raw_int|: \stars&maybe\cr
+\.{operator}&|operator_like|: \stars&maybe\cr
+\.{or}&|alfop|: \stars&yes\cr
+\.{or\_eq}&|alfop|: \stars&yes\cr
+\.{pragma}&|if_like|: \stars&maybe\cr
+\.{private}&|public_like|: \stars&maybe\cr
+\.{protected}&|public_like|: \stars&maybe\cr
+\.{ptrdiff\_t}&|raw_int|: \stars&maybe\cr
+\.{public}&|public_like|: \stars&maybe\cr
+\.{register}&|int_like|: \stars&maybe\cr
+\.{reinterpret\_cast}&|raw_int|: \stars&maybe\cr
+\.{return}&|case_like|: \stars&maybe\cr
+\.{short}&|raw_int|: \stars&maybe\cr
+\.{sig\_atomic\_t}&|raw_int|: \stars&maybe\cr
+\.{signed}&|raw_int|: \stars&maybe\cr
+\.{size\_t}&|raw_int|: \stars&maybe\cr
+\.{sizeof}&|sizeof_like|: \stars&maybe\cr
+\.{static}&|int_like|: \stars&maybe\cr
+\.{static\_cast}&|raw_int|: \stars&maybe\cr
+\.{struct}&|struct_like|: \stars&maybe\cr
+\.{switch}&|for_like|: \stars&maybe\cr
+\.{template}&|template_like|: \stars&maybe\cr
+\.{TeX}&|exp|: \.{\\TeX}&yes\cr
+\.{this}&|exp|: \.{\\this}&yes\cr
+\.{throw}&|case_like|: \stars&maybe\cr
+\.{time\_t}&|raw_int|: \stars&maybe\cr
+\.{try}&|else_like|: \stars&maybe\cr
+\.{typedef}&|typedef_like|: \stars&maybe\cr
+\.{typeid}&|raw_int|: \stars&maybe\cr
+\.{typename}&|struct_like|: \stars&maybe\cr
+\.{undef}&|if_like|: \stars&maybe\cr
+\.{union}&|struct_like|: \stars&maybe\cr
+\.{unsigned}&|raw_int|: \stars&maybe\cr
+\.{using}&|int_like|: \stars&maybe\cr
+\.{va\_dcl}&|decl|: \stars&maybe\cr
+\.{va\_list}&|raw_int|: \stars&maybe\cr
+\.{virtual}&|int_like|: \stars&maybe\cr
+\.{void}&|raw_int|: \stars&maybe\cr
+\.{volatile}&|const_like|: \stars&maybe\cr
+\.{wchar\_t}&|raw_int|: \stars&maybe\cr
+\.{while}&|for_like|: \stars&maybe\cr
+\.{xor}&|alfop|: \stars&yes\cr
+\.{xor\_eq}&|alfop|: \stars&yes\cr
+\.{@@,}&|insert|: \.{\\,}&maybe\cr
+\.{@@\v}&|insert|: |opt| \.0&maybe\cr
+\.{@@/}&|insert|: |force|&no\cr
+\.{@@\#}&|insert|: |big_force|&no\cr
+\.{@@+}&|insert|: |big_cancel| \.{\{\}} |break_space|
+ \.{\{\}} |big_cancel|&no\cr
+\.{@@;}&|semi|: &maybe\cr
+\.{@@[@q]@>}&|begin_arg|: &maybe\cr
+\.{@q[@>@@]}&|end_arg|: &maybe\cr
+\.{@@\&}&|insert|: \.{\\J}&maybe\cr
+\.{@@h}&|insert|: |force| \.{\\ATH} |force|&no\cr
+\.{@@<}\thinspace section name\thinspace\.{@@>}&|section_scrap|:
+ \.{\\X}$n$\.:translated section name\.{\\X}&maybe\cr
+\.{@@(@q)@>}\thinspace section name\thinspace\.{@@>}&|section_scrap|:
+ \.{\\X}$n$\.{:\\.\{}section name with special characters
+ quoted\.{\ \}\\X}&maybe\cr
+\.{/*}comment\.{*/}&|insert|: |cancel|
+ \.{\\C\{}translated comment\.\} |force|&no\cr
+\.{//}comment&|insert|: |cancel|
+ \.{\\SHC\{}translated comment\.\} |force|&no\cr
+}
+
+\smallskip
+The construction \.{@@t}\thinspace stuff\/\thinspace\.{@@>} contributes
+\.{\\hbox\{}\thinspace stuff\/\thinspace\.\} to the following scrap.
+
+@i prod.w
+
+@* Implementing the productions.
+More specifically, a scrap is a structure consisting of a category
+|cat| and a |text_pointer| |trans|, which points to the translation in
+|tok_start|. When \CEE/ text is to be processed with the grammar above,
+we form an array |scrap_info| containing the initial scraps.
+Our production rules have the nice property that the right-hand side is never
+longer than the left-hand side. Therefore it is convenient to use sequential
+allocation for the current sequence of scraps. Five pointers are used to
+manage the parsing:
+
+\yskip\hang |pp| is a pointer into |scrap_info|. We will try to match
+the category codes |pp->cat,@,@,(pp+1)->cat|$,\,\,\ldots\,$
+to the left-hand sides of productions.
+
+\yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that
+the current sequence of scraps appears in positions |scrap_base| through
+|lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and
+|trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have
+been examined, while those in positions |>=hi_ptr| have not yet been
+looked at by the parsing process.
+
+\yskip\noindent Initially |scrap_ptr| is set to the position of the final
+scrap to be parsed, and it doesn't change its value. The parsing process
+makes sure that |lo_ptr>=pp+3|, since productions have as many as four terms,
+by moving scraps from |hi_ptr| to |lo_ptr|. If there are
+fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with
+blanks that will not match in any productions. Parsing stops when
+|pp==lo_ptr+1| and |hi_ptr==scrap_ptr+1|.
+
+Since the |scrap| structure will later be used for other purposes, we
+declare its second element as a union.
+
+@<Type...@>=
+typedef struct {
+ eight_bits cat;
+ eight_bits mathness;
+ union {
+ text_pointer Trans;
+ @<Rest of |trans_plus| union@>@;
+ } trans_plus;
+} scrap;
+typedef scrap *scrap_pointer;
+
+@ @d trans trans_plus.Trans /* translation texts of scraps */
+
+@<Private...@>=
+static scrap scrap_info[max_scraps]; /* memory array for scraps */
+static scrap_pointer scrap_info_end=scrap_info+max_scraps-1; /* end of |scrap_info| */
+static scrap_pointer scrap_base; /* beginning of the current scrap sequence */
+static scrap_pointer scrap_ptr; /* ending of the current scrap sequence */
+static scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */
+static scrap_pointer pp; /* current position for reducing productions */
+static scrap_pointer lo_ptr; /* last scrap that has been examined */
+static scrap_pointer hi_ptr; /* first scrap that has not been examined */
+
+@ @<Set init...@>=
+scrap_base=scrap_info+1;
+max_scr_ptr=scrap_ptr=scrap_info;
+
+@ Token lists in |@!tok_mem| are composed of the following kinds of
+items for \TEX/ output.
+
+\yskip\item{$\bullet$}Character codes and special codes like |force| and
+|math_rel| represent themselves;
+
+\item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|res_flag+p| represents \.{\\\&\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|section_flag+p| represents section name |p|;
+
+\item{$\bullet$}|tok_flag+p| represents token list number |p|;
+
+\item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be
+translated without line-break controls.
+
+@d id_flag 10240 /* signifies an identifier */
+@d res_flag 2*id_flag /* signifies a reserved word */
+@d section_flag 3*id_flag /* signifies a section name */
+@d tok_flag 4*id_flag /* signifies a token list */
+@d inner_tok_flag 5*id_flag /* signifies a token list in `\pb' */
+
+@c
+static void
+print_text(@t\1\1@> /* prints a token list for debugging; not used in |main| */
+text_pointer p@t\2\2@>)
+{
+ token_pointer j; /* index into |tok_mem| */
+ sixteen_bits r; /* remainder of token after the flag has been stripped off */
+ if (p>=text_ptr) printf("BAD");
+ else for (j=*p; j<*(p+1); j++) {
+ r=*j%id_flag;
+ switch (*j/id_flag) {
+ case 1: printf("\\\\{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}");
+ break; /* |id_flag| */
+ case 2: printf("\\&{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}");
+ break; /* |res_flag| */
+ case 3: printf("<"); print_section_name((name_dir+r)); printf(">");
+ break; /* |section_flag| */
+ case 4: printf("[[%d]]",r); break; /* |tok_flag| */
+ case 5: printf("|[[%d]]|",r); break; /* |inner_tok_flag| */
+ default: @<Print token |r| in symbolic form@>@;
+ }
+ }
+ update_terminal;
+}
+
+@ @<Predecl...@>=@+static void print_text(text_pointer p);
+
+@ @<Print token |r|...@>=
+switch (r) {
+ case math_rel: printf("\\mathrel{"@q}@>); break;
+ case big_cancel: printf("[ccancel]"); break;
+ case cancel: printf("[cancel]"); break;
+ case indent: printf("[indent]"); break;
+ case outdent: printf("[outdent]"); break;
+ case backup: printf("[backup]"); break;
+ case opt: printf("[opt]"); break;
+ case break_space: printf("[break]"); break;
+ case force: printf("[force]"); break;
+ case big_force: printf("[fforce]"); break;
+ case preproc_line: printf("[preproc]"); break;
+ case quoted_char: j++; printf("[%o]",(unsigned int)*j); break;
+ case end_translation: printf("[quit]"); break;
+ case inserted: printf("[inserted]"); break;
+ default: putxchar(r);
+}
+
+@ The production rules listed above are embedded directly into \.{CWEAVE},
+since it is easier to do this than to write an interpretive system
+that would handle production systems in general. Several macros are defined
+here so that the program for each production is fairly short.
+
+All of our productions conform to the general notion that some |k|
+consecutive scraps starting at some position |j| are to be replaced by a
+single scrap of some category |c| whose translation is composed from the
+translations of the disappearing scraps. After this production has been
+applied, the production pointer |pp| should change by an amount |d|. Such
+a production can be represented by the quadruple |(j,k,c,d)|. For example,
+the production `|exp@,comma@,exp| $\RA$ |exp|' would be represented by
+`|(pp,3,exp,-2)|'; in this case the pointer |pp| should decrease by 2
+after the production has been applied, because some productions with
+|exp| in their second or third positions might now match,
+but no productions have
+|exp| in the fourth position of their left-hand sides. Note that
+the value of |d| is determined by the whole collection of productions, not
+by an individual one.
+The determination of |d| has been
+done by hand in each case, based on the full set of productions but not on
+the grammar of \CEE/ or on the rules for constructing the initial
+scraps.
+
+We also attach a serial number to each production, so that additional
+information is available when debugging. For example, the program below
+contains the statement `|reduce(pp,3,exp,-2,4)|' when it implements
+the production just mentioned.
+
+Before calling |reduce|, the program should have appended the tokens of
+the new translation to the |tok_mem| array. We commonly want to append
+copies of several existing translations, and macros are defined to
+simplify these common cases. For example, \\{app2}|(pp)| will append the
+translations of two consecutive scraps, |pp->trans| and |(pp+1)->trans|, to
+the current token list. If the entire new translation is formed in this
+way, we write `|squash(j,k,c,d,n)|' instead of `|reduce(j,k,c,d,n)|'. For
+example, `|squash(pp,3,exp,-2,3)|' is an abbreviation for `\\{app3}|(pp);
+reduce(pp,3,exp,-2,3)|'.
+
+A couple more words of explanation:
+Both |big_app| and |app| append a token (while |big_app1| to |big_app3|
+append the specified number of scrap translations) to the current token list.
+The difference between |big_app| and |app| is simply that |big_app|
+checks whether there can be a conflict between math and non-math
+tokens, and intercalates a `\.{\$}' token if necessary. When in
+doubt what to use, use |big_app|.
+
+The |mathness| is an attribute of scraps that says whether they are
+to be printed in a math mode context or not. It is separate from the
+``part of speech'' (the |cat|) because to make each |cat| have
+a fixed |mathness| (as in the original \.{WEAVE}) would multiply the
+number of necessary production rules.
+
+The low two bits (i.e. |mathness % 4|) control the left boundary.
+(We need two bits because we allow cases |yes_math|, |no_math| and
+|maybe_math|, which can go either way.)
+The next two bits (i.e. |mathness / 4|) control the right boundary.
+If we combine two scraps and the right boundary of the first has
+a different mathness from the left boundary of the second, we
+insert a \.{\$} in between. Similarly, if at printing time some
+irreducible scrap has a |yes_math| boundary the scrap gets preceded
+or followed by a \.{\$}. The left boundary is |maybe_math| if and
+only if the right boundary is.
+
+The code below is an exact translation of the production rules into
+\CEE/, using such macros, and the reader should have no difficulty
+understanding the format by comparing the code with the symbolic
+productions as they were listed earlier.
+
+@d no_math 2 /* should be in horizontal mode */
+@d yes_math 1 /* should be in math mode */
+@d maybe_math 0 /* works in either horizontal or math mode */
+@d big_app2(a) big_app1(a);big_app1(a+1)
+@d big_app3(a) big_app2(a);big_app1(a+2)
+@d app(a) *(tok_ptr++)=(token)(a)
+@d app1(a) *(tok_ptr++)=(token)(tok_flag+(int)((a)->trans-tok_start))
+
+@<Private...@>=
+static int cur_mathness, init_mathness;
+
+@ @<Predecl...@>=
+static void app_str(const char *);@/
+static void big_app(token);@/
+static void big_app1(scrap_pointer);
+
+@ @c
+static void
+app_str(
+const char *s)
+{
+ while (*s) app_tok(*(s++));
+}
+
+static void
+big_app(
+token a)
+{
+ if (a==' ' || (a>=big_cancel && a<=big_force)) /* non-math token */ {
+ if (cur_mathness==maybe_math) init_mathness=no_math;
+ else if (cur_mathness==yes_math) app_str("{}$");
+ cur_mathness=no_math;
+ }
+ else {
+ if (cur_mathness==maybe_math) init_mathness=yes_math;
+ else if (cur_mathness==no_math) app_str("${}");
+ cur_mathness=yes_math;
+ }
+ app(a);
+}
+
+static void
+big_app1(
+scrap_pointer a)
+{
+ switch (a->mathness % 4) { /* left boundary */
+ case (no_math):
+ if (cur_mathness==maybe_math) init_mathness=no_math;
+ else if (cur_mathness==yes_math) app_str("{}$");
+ cur_mathness=a->mathness / 4; /* right boundary */
+ break;
+ case (yes_math):
+ if (cur_mathness==maybe_math) init_mathness=yes_math;
+ else if (cur_mathness==no_math) app_str("${}");
+ cur_mathness=a->mathness / 4; /* right boundary */
+ break;
+ case (maybe_math): /* no changes */ break;
+ }
+ app(tok_flag+(int)((a)->trans-tok_start));
+}
+
+@ Let us consider the big switch for productions now, before looking
+at its context. We want to design the program so that this switch
+works, so we might as well not keep ourselves in suspense about exactly what
+code needs to be provided with a proper environment.
+
+@d cat1 (pp+1)->cat
+@d cat2 (pp+2)->cat
+@d cat3 (pp+3)->cat
+@d lhs_not_simple (pp->cat!=public_like
+ && pp->cat!=semi
+ && pp->cat!=prelangle
+ && pp->cat!=prerangle @|
+ && pp->cat!=template_like
+ && pp->cat!=new_like
+ && pp->cat!=new_exp
+ && pp->cat!=ftemplate @|
+ && pp->cat!=raw_ubin
+ && pp->cat!=const_like
+ && pp->cat!=raw_int
+ && pp->cat!=operator_like)
+ /* not a production with left side length 1 */
+
+@<Match a production at |pp|, or increase |pp| if there is no match@>= {
+ if (cat1==end_arg && lhs_not_simple)
+ if (pp->cat==begin_arg) squash(pp,2,exp,-2,124);
+ else squash(pp,2,end_arg,-1,125);
+ else if (cat1==insert) squash(pp,2,pp->cat,-2,0);
+ else if (cat2==insert) squash(pp+1,2,(pp+1)->cat,-1,0);
+ else if (cat3==insert) squash(pp+2,2,(pp+2)->cat,0,0);
+ else
+ switch (pp->cat) {
+ case exp: @<Cases for |exp|@>@; @+break;
+ case lpar: @<Cases for |lpar|@>@; @+break;
+ case unop: @<Cases for |unop|@>@; @+break;
+ case ubinop: @<Cases for |ubinop|@>@; @+break;
+ case binop: @<Cases for |binop|@>@; @+break;
+ case cast: @<Cases for |cast|@>@; @+break;
+ case sizeof_like: @<Cases for |sizeof_like|@>@; @+break;
+ case int_like: @<Cases for |int_like|@>@; @+break;
+ case public_like: @<Cases for |public_like|@>@; @+break;
+ case colcol: @<Cases for |colcol|@>@; @+break;
+ case decl_head: @<Cases for |decl_head|@>@; @+break;
+ case decl: @<Cases for |decl|@>@; @+break;
+ case base: @<Cases for |base|@>@; @+break;
+ case struct_like: @<Cases for |struct_like|@>@; @+break;
+ case struct_head: @<Cases for |struct_head|@>@; @+break;
+ case fn_decl: @<Cases for |fn_decl|@>@; @+break;
+ case function: @<Cases for |function|@>@; @+break;
+ case lbrace: @<Cases for |lbrace|@>@; @+break;
+ case if_like: @<Cases for |if_like|@>@; @+break;
+ case else_like: @<Cases for |else_like|@>@; @+break;
+ case else_head: @<Cases for |else_head|@>@; @+break;
+ case if_clause: @<Cases for |if_clause|@>@; @+break;
+ case if_head: @<Cases for |if_head|@>@; @+break;
+ case do_like: @<Cases for |do_like|@>@; @+break;
+ case case_like: @<Cases for |case_like|@>@; @+break;
+ case catch_like: @<Cases for |catch_like|@>@; @+break;
+ case tag: @<Cases for |tag|@>@; @+break;
+ case stmt: @<Cases for |stmt|@>@; @+break;
+ case semi: @<Cases for |semi|@>@; @+break;
+ case lproc: @<Cases for |lproc|@>@; @+break;
+ case section_scrap: @<Cases for |section_scrap|@>@; @+break;
+ case insert: @<Cases for |insert|@>@; @+break;
+ case prelangle: @<Cases for |prelangle|@>@; @+break;
+ case prerangle: @<Cases for |prerangle|@>@; @+break;
+ case langle: @<Cases for |langle|@>@; @+break;
+ case template_like: @<Cases for |template_like|@>@; @+break;
+ case new_like: @<Cases for |new_like|@>@; @+break;
+ case new_exp: @<Cases for |new_exp|@>@; @+break;
+ case ftemplate: @<Cases for |ftemplate|@>@; @+break;
+ case for_like: @<Cases for |for_like|@>@; @+break;
+ case raw_ubin: @<Cases for |raw_ubin|@>@; @+break;
+ case const_like: @<Cases for |const_like|@>@; @+break;
+ case raw_int: @<Cases for |raw_int|@>@; @+break;
+ case operator_like: @<Cases for |operator_like|@>@; @+break;
+ case typedef_like: @<Cases for |typedef_like|@>@; @+break;
+ case delete_like: @<Cases for |delete_like|@>@; @+break;
+ case question: @<Cases for |question|@>@; @+break;
+ }
+ pp++; /* if no match was found, we move to the right */
+}
+
+@ In \CEE/, new specifier names can be defined via |typedef|, and we want
+to make the parser recognize future occurrences of the identifier thus
+defined as specifiers. This is done by the procedure |make_reserved|,
+which changes the |ilk| of the relevant identifier.
+
+We first need a procedure to recursively seek the first
+identifier in a token list, because the identifier might
+be enclosed in parentheses, as when one defines a function
+returning a pointer.
+
+If the first identifier found is a keyword like `\&{case}', we
+return the special value |case_found|; this prevents underlining
+of identifiers in case labels.
+
+If the first identifier is the keyword `\&{operator}', we give up;
+users who want to index definitions of overloaded \CPLUSPLUS/ operators
+should say, for example, `\.{@@!@@\^\\\&\{operator\} \$+\{=\}\$@@>}' (or,
+more properly alpha\-betized,
+`\.{@@!@@:operator+=\}\{\\\&\{operator\} \$+\{=\}\$@@>}').
+
+@d no_ident_found (token_pointer)0 /* distinct from any identifier token */
+@d case_found (token_pointer)1 /* likewise */
+@d operator_found (token_pointer)2 /* likewise */
+
+@<Predecl...@>=
+static token_pointer find_first_ident(text_pointer);@/
+static void make_reserved(scrap_pointer);@/
+static void make_underlined(scrap_pointer);@/
+static void underline_xref(name_pointer);
+
+@ @c
+static token_pointer
+find_first_ident(
+text_pointer p)
+{
+ token_pointer q; /* token to be returned */
+ token_pointer j; /* token being looked at */
+ sixteen_bits r; /* remainder of token after the flag has been stripped off */
+ if (p>=text_ptr) confusion("find_first_ident");
+ for (j=*p; j<*(p+1); j++) {
+ r=*j%id_flag;
+ switch (*j/id_flag) {
+ case 2: /* |res_flag| */
+ if (name_dir[r].ilk==case_like) return case_found;
+ if (name_dir[r].ilk==operator_like) return operator_found;
+ if (name_dir[r].ilk!=raw_int) break;
+ case 1: return j;
+ case 4: case 5: /* |tok_flag| or |inner_tok_flag| */
+ if ((q=find_first_ident(tok_start+r))!=no_ident_found)
+ return q;
+ default: ; /* char, |section_flag|, fall thru: move on to next token */
+ if (*j==inserted) return no_ident_found; /* ignore inserts */
+ else if (*j==qualifier) j++; /* bypass namespace qualifier */
+ }
+ }
+ return no_ident_found;
+}
+
+@ The scraps currently being parsed must be inspected for any
+occurrence of the identifier that we're making reserved; hence
+the |for| loop below.
+
+@c
+static void
+make_reserved(@t\1\1@> /* make the first identifier in |p->trans| like |int| */
+scrap_pointer p@t\2\2@>)
+{
+ sixteen_bits tok_value; /* the name of this identifier, plus its flag */
+ token_pointer tok_loc; /* pointer to |tok_value| */
+ if ((tok_loc=find_first_ident(p->trans))<=operator_found)
+ return; /* this should not happen */
+ tok_value=*tok_loc;
+ for (;p<=scrap_ptr; p==lo_ptr? p=hi_ptr: p++) {
+ if (p->cat==exp) {
+ if (**(p->trans)==tok_value) {
+ p->cat=raw_int;
+ **(p->trans)=tok_value%id_flag+res_flag;
+ }
+ }
+ }
+ (name_dir+(sixteen_bits)(tok_value%id_flag))->ilk=raw_int;
+ *tok_loc=tok_value%id_flag+res_flag;
+}
+
+@ In the following situations we want to mark the occurrence of
+an identifier as a definition: when |make_reserved| is just about to be
+used; after a specifier, as in |char **argv|;
+before a colon, as in \\{found}:; and in the declaration of a function,
+as in \\{main}()$\{\ldots;\}$. This is accomplished by the invocation
+of |make_underlined| at appropriate times. Notice that, in the declaration
+of a function, we find out that the identifier is being defined only after
+it has been swallowed up by an |exp|.
+
+@c
+static void
+make_underlined(@t\1\1@>
+/* underline the entry for the first identifier in |p->trans| */
+scrap_pointer p@t\2\2@>)
+{
+ token_pointer tok_loc; /* where the first identifier appears */
+ if ((tok_loc=find_first_ident(p->trans))<=operator_found)
+ return; /* this happens, for example, in |case found:| */
+ xref_switch=def_flag;
+ underline_xref(*tok_loc%id_flag+name_dir);
+}
+
+@ We cannot use |new_xref| to underline a cross-reference at this point
+because this would just make a new cross-reference at the end of the list.
+We actually have to search through the list for the existing
+cross-reference.
+
+@c
+static void
+underline_xref(
+name_pointer p)
+{
+ xref_pointer q=(xref_pointer)p->xref; /* pointer to cross-reference being examined */
+ xref_pointer r; /* temporary pointer for permuting cross-references */
+ sixteen_bits m; /* cross-reference value to be installed */
+ sixteen_bits n; /* cross-reference value being examined */
+ if (no_xref) return;
+ m=section_count+xref_switch;
+ while (q != xmem) {
+ n=q->num;
+ if (n==m) return;
+ else if (m==n+def_flag) {
+ q->num=m; return;
+ }
+ else if (n>=def_flag && n<m) break;
+ q=q->xlink;
+ }
+ @<Insert new cross-reference at |q|, not at beginning of list@>@;
+}
+
+@ We get to this section only when the identifier is one letter long,
+so it didn't get a non-underlined entry during phase one. But it may
+have got some explicitly underlined entries in later sections, so in order
+to preserve the numerical order of the entries in the index, we have
+to insert the new cross-reference not at the beginning of the list
+(namely, at |p->xref|), but rather right before |q|.
+
+@<Insert new cross-reference at |q|...@>=
+ append_xref(0); /* this number doesn't matter */
+ xref_ptr->xlink=(xref_pointer)p->xref; r=xref_ptr;
+ update_node(p);
+ while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;}
+ r->num=m; /* everything from |q| on is left undisturbed */
+
+@ Now comes the code that tries to match each production starting
+with a particular type of scrap. Whenever a match is discovered,
+the |squash| or |reduce| macro will cause the appropriate action
+to be performed, followed by |goto found|.
+
+@<Cases for |exp|@>=
+if (cat1==lbrace || cat1==int_like || cat1==decl) {
+ make_underlined(pp); big_app1(pp); big_app(indent); app(indent);
+ reduce(pp,1,fn_decl,0,1);
+}
+else if (cat1==unop) squash(pp,2,exp,-2,2);
+else if ((cat1==binop || cat1==ubinop) && cat2==exp)
+ squash(pp,3,exp,-2,3);
+else if (cat1==comma && cat2==exp) {
+ big_app2(pp);
+ app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4);
+}
+else if (cat1==lpar && cat2==rpar && cat3==colon) squash(pp+3,1,base,0,5);
+else if (cat1==cast && cat2==colon) squash(pp+2,1,base,0,5);
+else if (cat1==semi) squash(pp,2,stmt,-1,6);
+else if (cat1==colon) {
+ make_underlined (pp); squash(pp,2,tag,-1,7);
+}
+else if (cat1==rbrace) squash(pp,1,stmt,-1,8);
+else if (cat1==lpar && cat2==rpar && (cat3==const_like || cat3==case_like)) {
+ big_app1(pp+2); big_app(' '); big_app1(pp+3); reduce(pp+2,2,rpar,0,9);
+}
+else if (cat1==cast && (cat2==const_like || cat2==case_like)) {
+ big_app1(pp+1); big_app(' '); big_app1(pp+2); reduce(pp+1,2,cast,0,9);
+}
+else if (cat1==exp || cat1==cast) squash(pp,2,exp,-2,10);
+
+@ @<Cases for |lpar|@>=
+if ((cat1==exp||cat1==ubinop) && cat2==rpar) squash(pp,3,exp,-2,11);
+else if (cat1==rpar) {
+ big_app1(pp); app('\\'); app(','); big_app1(pp+1);
+@.\\,@>
+ reduce(pp,2,exp,-2,12);
+}
+else if ((cat1==decl_head || cat1==int_like || cat1==cast) && cat2==rpar)
+ squash(pp,3,cast,-2,13);
+else if ((cat1==decl_head || cat1==int_like || cat1==exp) && cat2==comma) {
+ big_app3(pp); app(opt); app('9'); reduce(pp,3,lpar,-1,14);
+}
+else if (cat1==stmt || cat1==decl) {
+ big_app2(pp); big_app(' '); reduce(pp,2,lpar,-1,15);
+}
+
+@ @<Cases for |unop|@>=
+if (cat1==exp || cat1==int_like) squash(pp,2,exp,-2,16);
+
+@ @<Cases for |ubinop|@>=
+if (cat1==cast && cat2==rpar) {
+ big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1);
+ reduce(pp,2,cast,-2,17);
+}
+else if (cat1==exp || cat1==int_like) {
+ big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1);
+ reduce(pp,2,cat1,-2,18);
+}
+else if (cat1==binop) {
+ big_app(math_rel); big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
+ big_app('}'); reduce(pp,2,binop,-1,19);
+}
+
+@ @<Cases for |binop|@>=
+if (cat1==binop) {
+ big_app(math_rel); big_app('{'); big_app1(pp); big_app('}');
+ big_app('{'); big_app1(pp+1); big_app('}');
+ big_app('}'); reduce(pp,2,binop,-1,20);
+}
+
+@ @<Cases for |cast|@>=
+if (cat1==lpar) squash(pp,2,lpar,-1,21);
+else if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,21);
+}
+else if (cat1==semi) squash(pp,1,exp,-2,22);
+
+@ @<Cases for |sizeof_like|@>=
+if (cat1==cast) squash(pp,2,exp,-2,23);
+else if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,24);
+}
+
+@ @<Cases for |int_like|@>=
+if (cat1==int_like|| cat1==struct_like) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,25);
+}
+else if (cat1==exp && (cat2==raw_int||cat2==struct_like))
+ squash(pp,2,int_like,-2,26);
+else if (cat1==exp || cat1==ubinop || cat1==colon) {
+ big_app1(pp); big_app(' '); reduce(pp,1,decl_head,-1,27);
+}
+else if (cat1==semi || cat1==binop) squash(pp,1,decl_head,0,28);
+
+@ @<Cases for |public_like|@>=
+if (cat1==colon) squash(pp,2,tag,-1,29);
+else squash(pp,1,int_like,-2,30);
+
+@ @<Cases for |colcol|@>=
+if (cat1==exp||cat1==int_like) {
+ app(qualifier); squash(pp,2,cat1,-2,31);
+}@+else if (cat1==colcol) squash(pp,2,colcol,-1,32);
+
+@ @<Cases for |decl_head|@>=
+if (cat1==comma) {
+ big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33);
+}
+else if (cat1==ubinop) {
+ big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
+ reduce(pp,2,decl_head,-1,34);
+}
+else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) {
+ make_underlined(pp+1); squash(pp,2,decl_head,-1,35);
+}
+else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma ||
+ cat3==semi || cat3==rpar))
+ squash(pp,3,decl_head,-1,36);
+else if (cat1==cast) squash(pp,2,decl_head,-1,37);
+else if (cat1==lbrace || cat1==int_like || cat1==decl) {
+ big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,38);
+}
+else if (cat1==semi) squash(pp,2,decl,-1,39);
+
+@ @<Cases for |decl|@>=
+if (cat1==decl) {
+ big_app1(pp); big_app(force); big_app1(pp+1);
+ reduce(pp,2,decl,-1,40);
+}
+else if (cat1==stmt || cat1==function) {
+ big_app1(pp); big_app(big_force);
+ big_app1(pp+1); reduce(pp,2,cat1,-1,41);
+}
+
+@ @<Cases for |base|@>=
+if (cat1==int_like || cat1==exp) {
+ if (cat2==comma) {
+ big_app1(pp); big_app(' '); big_app2(pp+1);
+ app(opt); app('9'); reduce(pp,3,base,0,42);
+ }
+ else if (cat2==lbrace) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' '); big_app1(pp+2);
+ reduce(pp,3,lbrace,-2,43);
+ }
+}
+
+@ @<Cases for |struct_like|@>=
+if (cat1==lbrace) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,struct_head,0,44);
+}
+else if (cat1==exp||cat1==int_like) {
+ if (cat2==lbrace || cat2==semi) {
+ make_underlined(pp+1); make_reserved(pp+1);
+ big_app1(pp); big_app(' '); big_app1(pp+1);
+ if (cat2==semi) reduce(pp,2,decl_head,0,45);
+ else {
+ big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46);
+ }
+ }
+ else if (cat2==colon) squash(pp+2,1,base,2,47);
+ else if (cat2!=base) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48);
+ }
+}
+
+@ @<Cases for |struct_head|@>=
+if ((cat1==decl || cat1==stmt || cat1==function) && cat2==rbrace) {
+ big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1);
+ big_app(outdent); big_app(force); big_app1(pp+2);
+ reduce(pp,3,int_like,-2,49);
+}
+else if (cat1==rbrace) {
+ big_app1(pp); app_str("\\,"); big_app1(pp+1);
+@.\\,@>
+ reduce(pp,2,int_like,-2,50);
+}
+
+@ @<Cases for |fn_decl|@>=
+if (cat1==decl) {
+ big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,fn_decl,0,51);
+}
+else if (cat1==stmt) {
+ big_app1(pp); app(outdent); app(outdent); big_app(force);
+ big_app1(pp+1); reduce(pp,2,function,-1,52);
+}
+
+@ @<Cases for |function|@>=
+if (cat1==function || cat1==decl || cat1==stmt) {
+ big_app1(pp); big_app(big_force); big_app1(pp+1); reduce(pp,2,cat1,-1,53);
+}
+
+@ @<Cases for |lbrace|@>=
+if (cat1==rbrace) {
+ big_app1(pp); app('\\'); app(','); big_app1(pp+1);
+@.\\,@>
+ reduce(pp,2,stmt,-1,54);
+}
+else if ((cat1==stmt||cat1==decl||cat1==function) && cat2==rbrace) {
+ big_app(force); big_app1(pp); big_app(indent); big_app(force);
+ big_app1(pp+1); big_app(force); big_app(backup); big_app1(pp+2);
+ big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55);
+}
+else if (cat1==exp) {
+ if (cat2==rbrace) squash(pp,3,exp,-2,56);
+ else if (cat2==comma && cat3==rbrace) squash(pp,4,exp,-2,56);
+}
+
+@ @<Cases for |if_like|@>=
+if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,if_clause,0,57);
+}
+
+@ @<Cases for |else_like|@>=
+if (cat1==colon) squash(pp+1,1,base,1,58);
+else if (cat1==lbrace) squash(pp,1,else_head,0,59);
+else if (cat1==stmt) {
+ big_app(force); big_app1(pp); big_app(indent); big_app(break_space);
+ big_app1(pp+1); big_app(outdent); big_app(force);
+ reduce(pp,2,stmt,-1,60);
+}
+
+@ @<Cases for |else_head|@>=
+if (cat1==stmt || cat1==exp) {
+ big_app(force); big_app1(pp); big_app(break_space); app(noop);
+ big_app(cancel); big_app1(pp+1); big_app(force);
+ reduce(pp,2,stmt,-1,61);
+}
+
+@ @<Cases for |if_clause|@>=
+if (cat1==lbrace) squash(pp,1,if_head,0,62);
+else if (cat1==stmt) {
+ if (cat2==else_like) {
+ big_app(force); big_app1(pp); big_app(indent); big_app(break_space);
+ big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2);
+ if (cat3==if_like) {
+ big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63);
+ }@+else reduce(pp,3,else_like,0,64);
+ }
+ else squash(pp,1,else_like,0,65);
+}
+
+@ @<Cases for |if_head|@>=
+if (cat1==stmt || cat1==exp) {
+ if (cat2==else_like) {
+ big_app(force); big_app1(pp); big_app(break_space); app(noop);
+ big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2);
+ if (cat3==if_like) {
+ big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66);
+ }@+else reduce(pp,3,else_like,0,67);
+ }
+ else squash(pp,1,else_head,0,68);
+}
+
+@ @<Cases for |do_like|@>=
+if (cat1==stmt && cat2==else_like && cat3==semi) {
+ big_app1(pp); big_app(break_space); app(noop); big_app(cancel);
+ big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space);
+ big_app2(pp+2); reduce(pp,4,stmt,-1,69);
+}
+
+@ @<Cases for |case_like|@>=
+if (cat1==semi) squash(pp,2,stmt,-1,70);
+else if (cat1==colon) squash(pp,2,tag,-1,71);
+else if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,72);
+}
+
+@ @<Cases for |catch_like|@>=
+if (cat1==cast || cat1==exp) {
+ big_app2(pp); big_app(indent); big_app(indent); reduce(pp,2,fn_decl,0,73);
+}
+
+@ @<Cases for |tag|@>=
+if (cat1==tag) {
+ big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74);
+}
+else if (cat1==stmt||cat1==decl||cat1==function) {
+ big_app(force); big_app(backup); big_app1(pp); big_app(break_space);
+ big_app1(pp+1); reduce(pp,2,cat1,-1,75);
+}
+
+@ The user can decide at run-time whether short statements should be
+grouped together on the same line.
+
+@d force_lines flags['f'] /* should each statement be on its own line? */
+
+@<Set init...@>=
+force_lines=true;
+
+@ @<Cases for |stmt|@>=
+if (cat1==stmt||cat1==decl||cat1==function) {
+ big_app1(pp);
+ if (cat1==function) big_app(big_force);
+ else if (cat1==decl) big_app(big_force);
+ else if (force_lines) big_app(force);
+ else big_app(break_space);
+ big_app1(pp+1); reduce(pp,2,cat1,-1,76);
+}
+
+@ @<Cases for |semi|@>=
+big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77);
+
+@ @<Cases for |lproc|@>=
+if (cat1==define_like) make_underlined(pp+2);
+if (cat1==else_like || cat1==if_like ||cat1==define_like)
+ squash(pp,2,lproc,0,78);
+else if (cat1==rproc) {
+ app(inserted); big_app2(pp); reduce(pp,2,insert,-1,79);
+} else if (cat1==exp || cat1==function) {
+ if (cat2==rproc) {
+ app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1);
+ reduce(pp,3,insert,-1,80);
+ }
+ else if (cat2==exp && cat3==rproc && cat1==exp) {
+ app(inserted); big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5");
+@.\\5@>
+ big_app2(pp+2); reduce(pp,4,insert,-1,80);
+ }
+}
+
+@ @<Cases for |section_scrap|@>=
+if (cat1==semi) {
+ big_app2(pp); big_app(force); reduce(pp,2,stmt,-2,81);
+}
+else squash(pp,1,exp,-2,82);
+
+@ @<Cases for |insert|@>=
+if (cat1)
+ squash(pp,2,cat1,0,83);
+
+@ @<Cases for |prelangle|@>=
+init_mathness=cur_mathness=yes_math;
+app('<'); reduce(pp,1,binop,-2,84);
+
+@ @<Cases for |prerangle|@>=
+init_mathness=cur_mathness=yes_math;
+app('>'); reduce(pp,1,binop,-2,85);
+
+@ @<Cases for |langle|@>=
+if (cat1==prerangle) {
+ big_app1(pp); app('\\'); app(','); big_app1(pp+1);
+@.\\,@>
+ reduce(pp,2,cast,-1,86);
+}
+else if (cat1==decl_head || cat1==int_like || cat1==exp) {
+ if (cat2==prerangle) squash(pp,3,cast,-1,87);
+ else if (cat2==comma) {
+ big_app3(pp); app(opt); app('9'); reduce(pp,3,langle,0,88);
+ }
+}
+
+@ @<Cases for |template_like|@>=
+if (cat1==exp && cat2==prelangle) squash(pp+2,1,langle,2,89);
+else if (cat1==exp || cat1==raw_int) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,90);
+}@+ else squash(pp,1,raw_int,0,91);
+
+@ @<Cases for |new_like|@>=
+if (cat1==lpar && cat2==exp && cat3==rpar) squash(pp,4,new_like,0,92);
+else if (cat1==cast) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,93);
+}
+else if (cat1!=lpar) squash(pp,1,new_exp,0,94);
+
+@ @<Cases for |new_exp|@>=
+if (cat1==int_like || cat1==const_like) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,new_exp,0,95);
+}
+else if (cat1==struct_like && (cat2==exp || cat2==int_like)) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' ');
+ big_app1(pp+2); reduce(pp,3,new_exp,0,96);
+}
+else if (cat1==raw_ubin) {
+ big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
+ reduce(pp,2,new_exp,0,97);
+}
+else if (cat1==lpar) squash(pp,1,exp,-2,98);
+else if (cat1==exp) {
+ big_app1(pp); big_app(' '); reduce(pp,1,exp,-2,98);
+}
+else if (cat1!=raw_int && cat1!=struct_like && cat1!=colcol)
+ squash(pp,1,exp,-2,99);
+
+@ @<Cases for |ftemplate|@>=
+if (cat1==prelangle) squash(pp+1,1,langle,1,100);
+else squash(pp,1,exp,-2,101);
+
+@ @<Cases for |for_like|@>=
+if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,102);
+}
+
+@ @<Cases for |raw_ubin|@>=
+if (cat1==const_like) {
+ big_app2(pp); app_str("\\ "); reduce(pp,2,raw_ubin,0,103);
+@.\\\ @>
+} else squash(pp,1,ubinop,-2,104);
+
+@ @<Cases for |const_like|@>=
+squash(pp,1,int_like,-2,105);
+
+@ @<Cases for |raw_int|@>=
+if (cat1==prelangle) squash(pp+1,1,langle,1,106);
+else if (cat1==colcol) squash(pp,2,colcol,-1,107);
+else if (cat1==cast) squash(pp,2,raw_int,0,108);
+else if (cat1==lpar) squash(pp,1,exp,-2,109);
+else if (cat1!=langle) squash(pp,1,int_like,-3,110);
+
+@ @<Cases for |operator_like|@>=
+if (cat1==binop || cat1==unop || cat1==ubinop) {
+ if (cat2==binop) break;
+ big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
+ reduce(pp,2,exp,-2,111);
+}
+else if (cat1==new_like || cat1==delete_like) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,112);
+}
+else if (cat1==comma) squash(pp,2,exp,-2,113);
+else if (cat1!=raw_ubin) squash(pp,1,new_exp,0,114);
+
+@ @<Cases for |typedef_like|@>=
+if ((cat1==int_like || cat1==cast) && (cat2==comma || cat2==semi))
+ squash(pp+1,1,exp,-1,115);
+else if (cat1==int_like) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,116);
+}
+else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) {
+ make_underlined(pp+1); make_reserved(pp+1);
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,117);
+}
+else if (cat1==comma) {
+ big_app2(pp); big_app(' '); reduce(pp,2,typedef_like,0,118);
+}
+else if (cat1==semi) squash(pp,2,decl,-1,119);
+else if (cat1==ubinop && (cat2==ubinop || cat2==cast)) {
+ big_app('{'); big_app1(pp+1); big_app('}'); big_app1(pp+2);
+ reduce(pp+1,2,cat2,0,120);
+}
+
+@ @<Cases for |delete_like|@>=
+if (cat1==lpar && cat2==rpar) {
+ big_app2(pp); app('\\'); app(','); big_app1(pp+2);
+@.\\,@>
+ reduce(pp,3,delete_like,0,121);
+}
+else if (cat1==exp) {
+ big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,122);
+}
+
+@ @<Cases for |question|@>=
+if (cat1==exp && (cat2==colon || cat2==base)) {
+ (pp+2)->mathness=5*yes_math; /* this colon should be in math mode */
+ squash(pp,3,binop,-2,123);
+}
+
+@ Now here's the |reduce| procedure used in our code for productions.
+
+The `|freeze_text|' macro is used to give official status to a token list.
+Before saying |freeze_text|, items are appended to the current token list,
+and we know that the eventual number of this token list will be the current
+value of |text_ptr|. But no list of that number really exists as yet,
+because no ending point for the current list has been
+stored in the |tok_start| array. After saying |freeze_text|, the
+old current token list becomes legitimate, and its number is the current
+value of |text_ptr-1| since |text_ptr| has been increased. The new
+current token list is empty and ready to be appended to.
+Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten
+too large, since it is assumed that this test was done beforehand.
+
+@d freeze_text *(++text_ptr)=tok_ptr
+
+@<Predecl...@>=
+static void reduce(scrap_pointer,short,eight_bits,short,short);@/
+static void squash(scrap_pointer,short,eight_bits,short,short);
+
+@ @c
+static void
+reduce(
+scrap_pointer j, short k,
+eight_bits c,
+short d, short n)
+{
+ scrap_pointer i, i1; /* pointers into scrap memory */
+ j->cat=c; j->trans=text_ptr;
+ j->mathness=4*cur_mathness+init_mathness;
+ freeze_text;
+ if (k>1) {
+ for (i=j+k, i1=j+1; i<=lo_ptr; i++, i1++) {
+ i1->cat=i->cat; i1->trans=i->trans;
+ i1->mathness=i->mathness;
+ }
+ lo_ptr=lo_ptr-k+1;
+ }
+ pp=(pp+d<scrap_base? scrap_base: pp+d);
+ @<Print a snapshot of the scrap list if debugging @>@;
+ pp--; /* we next say |pp++| */
+}
+
+@ Here's the |squash| procedure, which
+takes advantage of the simplification that occurs when |k==1|.
+
+@c
+static void
+squash(
+scrap_pointer j, short k,
+eight_bits c,
+short d, short n)
+{
+ scrap_pointer i; /* pointers into scrap memory */
+ if (k==1) {
+ j->cat=c; pp=(pp+d<scrap_base? scrap_base: pp+d);
+ @<Print a snapshot...@>@;
+ pp--; /* we next say |pp++| */
+ return;
+ }
+ for (i=j; i<j+k; i++) big_app1(i);
+ reduce(j,k,c,d,n);
+}
+
+@ And here now is the code that applies productions as long as possible.
+Before applying the production mechanism, we must make sure
+it has good input (at least four scraps, the length of the lhs of the
+longest rules), and that there is enough room in the memory arrays
+to hold the appended tokens and texts. Here we use a very
+conservative test; it's more important to make sure the program
+will still work if we change the production rules (within reason)
+than to squeeze the last bit of space from the memory arrays.
+
+@d safe_tok_incr 20
+@d safe_text_incr 10
+@d safe_scrap_incr 10
+
+@<Reduce the scraps using the productions until no more rules apply@>=
+while (true) {
+ @<Make sure the entries |pp| through |pp+3| of |cat| are defined@>@;
+ if (tok_ptr+safe_tok_incr>tok_mem_end) {
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ overflow("token");
+ }
+ if (text_ptr+safe_text_incr>tok_start_end) {
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ overflow("text");
+ }
+ if (pp>lo_ptr) break;
+ init_mathness=cur_mathness=maybe_math;
+ @<Match a production...@>@;
+}
+
+@ If we get to the end of the scrap list, category codes equal to zero are
+stored, since zero does not match anything in a production.
+
+@<Make sure the entries...@>=
+if (lo_ptr<pp+3) {
+ while (hi_ptr<=scrap_ptr && lo_ptr!=pp+3) {
+ (++lo_ptr)->cat=hi_ptr->cat; lo_ptr->mathness=(hi_ptr)->mathness;
+ lo_ptr->trans=(hi_ptr++)->trans;
+ }
+ for (i=lo_ptr+1;i<=pp+3;i++) i->cat=0;
+}
+
+@ If \.{CWEAVE} is being run in debugging mode, the production numbers and
+current stack categories will be printed out when |tracing| is set to 2;
+a sequence of two or more irreducible scraps will be printed out when
+|tracing| is set to 1.
+
+@<Private...@>=
+static int tracing; /* can be used to show parsing details */
+
+@ @<Print a snapsh...@>=
+{ scrap_pointer k_l; /* pointer into |scrap_info| */
+ if (tracing==2) {
+ printf("\n%d:",n);
+ for (k_l=scrap_base; k_l<=lo_ptr; k_l++) {
+ if (k_l==pp) putxchar('*'); else putxchar(' ');
+ if (k_l->mathness %4 == yes_math) putchar('+');
+ else if (k_l->mathness %4 == no_math) putchar('-');
+ print_cat(k_l->cat);
+ if (k_l->mathness /4 == yes_math) putchar('+');
+ else if (k_l->mathness /4 == no_math) putchar('-');
+ }
+ if (hi_ptr<=scrap_ptr) fputs("...",stdout); /* indicate that more is coming */
+ }
+}
+
+@ The |translate| function assumes that scraps have been stored in
+positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It
+applies productions as much as
+possible. The result is a token list containing the translation of
+the given sequence of scraps.
+
+After calling |translate|, we will have |text_ptr+3<=max_texts| and
+|tok_ptr+6<=max_toks|, so it will be possible to create up to three token
+lists with up to six tokens without checking for overflow. Before calling
+|translate|, we should have |text_ptr<max_texts| and |scrap_ptr<max_scraps|,
+since |translate| might add a new text and a new scrap before it checks
+for overflow.
+
+@c
+static text_pointer
+translate(void) /* converts a sequence of scraps */
+{
+ scrap_pointer i, /* index into |cat| */
+ j; /* runs through final scraps */
+ pp=scrap_base; lo_ptr=pp-1; hi_ptr=pp;
+ @<If tracing, print an indication of where we are@>@;
+ @<Reduce the scraps...@>@;
+ @<Combine the irreducible scraps that remain@>@;
+}
+
+@ @<Predecl...@>=@+static text_pointer translate(void);
+
+@ If the initial sequence of scraps does not reduce to a single scrap,
+we concatenate the translations of all remaining scraps, separated by
+blank spaces, with dollar signs surrounding the translations of scraps
+where appropriate.
+
+@<Combine the irreducible...@>= {
+ @<If semi-tracing, show the irreducible scraps@>@;
+ for (j=scrap_base; j<=lo_ptr; j++) {
+ if (j!=scrap_base) app(' ');
+ if (j->mathness % 4 == yes_math) app('$');
+ app1(j);
+ if (j->mathness / 4 == yes_math) app('$');
+ if (tok_ptr+6>tok_mem_end) overflow("token");
+ }
+ freeze_text; return text_ptr-1;
+}
+
+@ @<If semi-tracing, show the irreducible scraps@>=
+if (lo_ptr>scrap_base && tracing==1) {
+ printf("\nIrreducible scrap sequence in section %d:",section_count);
+@.Irreducible scrap sequence...@>
+ mark_harmless;
+ for (j=scrap_base; j<=lo_ptr; j++) {
+ printf(" "); print_cat(j->cat);
+ }
+}
+
+@ @<If tracing,...@>=
+if (tracing==2) {
+ printf("\nTracing after l. %d:\n",cur_line); mark_harmless;
+@.Tracing after...@>
+ if (loc>buffer+50) {
+ printf("...");
+ term_write(loc-51,51);
+ }
+ else term_write(buffer,loc-buffer);
+}
+
+@* Initializing the scraps.
+If we are going to use the powerful production mechanism just developed, we
+must get the scraps set up in the first place, given a \CEE/ text. A table
+of the initial scraps corresponding to \CEE/ tokens appeared above in the
+section on parsing; our goal now is to implement that table. We shall do this
+by implementing a subroutine called |C_parse| that is analogous to the
+|C_xref| routine used during phase one.
+
+Like |C_xref|, the |C_parse| procedure starts with the current
+value of |next_control| and it uses the operation |next_control=get_next()|
+repeatedly to read \CEE/ text until encountering the next `\.{\v}' or
+`\.{/*}', or until |next_control>=format_code|. The scraps corresponding to
+what it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr|
+is advanced.
+
+@c
+static void
+C_parse(@t\1\1@> /* creates scraps from \CEE/ tokens */
+ eight_bits spec_ctrl@t\2\2@>)
+{
+ int count; /* characters remaining before string break */
+ while (next_control<format_code || next_control==spec_ctrl) {
+ @<Append the scrap appropriate to |next_control|@>@;
+ next_control=get_next();
+ if (next_control=='|' || next_control==begin_comment ||
+ next_control==begin_short_comment) return;
+ }
+}
+
+@ @<Predecl...@>=@+static void C_parse(eight_bits);
+
+@ The following macro is used to append a scrap whose tokens have just
+been appended:
+
+@d app_scrap(c,b) {
+ (++scrap_ptr)->cat=(c); scrap_ptr->trans=text_ptr;
+ scrap_ptr->mathness=5*(b); /* no no, yes yes, or maybe maybe */
+ freeze_text;
+}
+
+@ @<Append the scr...@>=
+@<Make sure that there is room for the new scraps, tokens, and texts@>@;
+switch (next_control) {
+ case section_name:
+ app(section_flag+(int)(cur_section-name_dir));
+ app_scrap(section_scrap,maybe_math);
+ app_scrap(exp,yes_math);@+break;
+ case string: case constant: case verbatim:
+ @<Append a string or constant@>@;@+break;
+ case identifier: app_cur_id(true);@+break;
+ case TeX_string:
+ @<Append a \TEX/ string, without forming a scrap@>@;@+break;
+ case '/': case '.':
+ app(next_control);@+app_scrap(binop,yes_math);@+break;
+ case '<': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break;
+@.\\langle@>
+ case '>': app_str("\\rangle");@+app_scrap(prerangle,yes_math);@+break;
+@.\\rangle@>
+ case '=': app_str("\\K");@+app_scrap(binop,yes_math);@+break;
+@.\\K@>
+ case '|': app_str("\\OR");@+app_scrap(binop,yes_math);@+break;
+@.\\OR@>
+ case '^': app_str("\\XOR");@+app_scrap(binop,yes_math);@+break;
+@.\\XOR@>
+ case '%': app_str("\\MOD");@+app_scrap(binop,yes_math);@+break;
+@.\\MOD@>
+ case '!': app_str("\\R");@+app_scrap(unop,yes_math);@+break;
+@.\\R@>
+ case '~': app_str("\\CM");@+app_scrap(unop,yes_math);@+break;
+@.\\CM@>
+ case '+': case '-': app(next_control);@+app_scrap(ubinop,yes_math);@+break;
+ case '*': app(next_control);@+app_scrap(raw_ubin,yes_math);@+break;
+ case '&': app_str("\\AND");@+app_scrap(raw_ubin,yes_math);@+break;
+@.\\AND@>
+ case '?': app_str("\\?");@+app_scrap(question,yes_math);@+break;
+@.\\?@>
+ case '#': app_str("\\#");@+app_scrap(ubinop,yes_math);@+break;
+@.\\\#@>
+ case ignore: case xref_roman: case xref_wildcard:
+ case xref_typewriter: case noop:@+break;
+ case '(': case '[': app(next_control);@+app_scrap(lpar,maybe_math);@+break;
+ case ')': case ']': app(next_control);@+app_scrap(rpar,maybe_math);@+break;
+ case '{': app_str("\\{"@q}@>);@+app_scrap(lbrace,yes_math);@+break;
+@.\\\{@>@q}@>
+ case '}': app_str(@q{@>"\\}");@+app_scrap(rbrace,yes_math);@+break;
+@q{@>@.\\\}@>
+ case ',': app(',');@+app_scrap(comma,yes_math);@+break;
+ case ';': app(';');@+app_scrap(semi,maybe_math);@+break;
+ case ':': app(':');@+app_scrap(colon,no_math);@+break;@/
+ @t\4@> @<Cases involving nonstandard characters@>@;
+ case thin_space: app_str("\\,");@+app_scrap(insert,maybe_math);@+break;
+@.\\,@>
+ case math_break: app(opt);@+app_str("0");@+
+ app_scrap(insert,maybe_math);@+break;
+ case line_break: app(force);@+app_scrap(insert,no_math);@+break;
+ case left_preproc: app(force);@+app(preproc_line);@+app_str("\\#");
+ app_scrap(lproc,no_math);@+break;
+@.\\\#@>
+ case right_preproc: app(force);@+app_scrap(rproc,no_math);@+break;
+ case big_line_break: app(big_force);@+app_scrap(insert,no_math);@+break;
+ case no_line_break: app(big_cancel);@+app(noop);@+app(break_space);@+
+ app(noop);@+app(big_cancel);
+ app_scrap(insert,no_math);@+break;
+ case pseudo_semi: app_scrap(semi,maybe_math);@+break;
+ case macro_arg_open: app_scrap(begin_arg,maybe_math);@+break;
+ case macro_arg_close: app_scrap(end_arg,maybe_math);@+break;
+ case join: app_str("\\J");@+app_scrap(insert,no_math);@+break;
+@.\\J@>
+ case output_defs_code: app(force);@+app_str("\\ATH");@+app(force);
+ app_scrap(insert,no_math);@+break;
+@.\\ATH@>
+ default: app(inserted);@+app(next_control);
+ app_scrap(insert,maybe_math);@+break;
+}
+
+@ @<Make sure that there is room for the new...@>=
+if (scrap_ptr+safe_scrap_incr>scrap_info_end ||
+ tok_ptr+safe_tok_incr>tok_mem_end @| ||
+ text_ptr+safe_text_incr>tok_start_end) {
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ overflow("scrap/token/text");
+}
+
+@ Some nonstandard characters may have entered \.{CWEAVE} by means of
+standard ones. They are converted to \TEX/ control sequences so that it is
+possible to keep \.{CWEAVE} from outputting unusual |char| codes.
+
+@<Cases involving nonstandard...@>=
+case non_eq: app_str("\\I");@+app_scrap(binop,yes_math);@+break;
+@.\\I@>
+case lt_eq: app_str("\\Z");@+app_scrap(binop,yes_math);@+break;
+@.\\Z@>
+case gt_eq: app_str("\\G");@+app_scrap(binop,yes_math);@+break;
+@.\\G@>
+case eq_eq: app_str("\\E");@+app_scrap(binop,yes_math);@+break;
+@.\\E@>
+case and_and: app_str("\\W");@+app_scrap(binop,yes_math);@+break;
+@.\\W@>
+case or_or: app_str("\\V");@+app_scrap(binop,yes_math);@+break;
+@.\\V@>
+case plus_plus: app_str("\\PP");@+app_scrap(unop,yes_math);@+break;
+@.\\PP@>
+case minus_minus: app_str("\\MM");@+app_scrap(unop,yes_math);@+break;
+@.\\MM@>
+case minus_gt: app_str("\\MG");@+app_scrap(binop,yes_math);@+break;
+@.\\MG@>
+case gt_gt: app_str("\\GG");@+app_scrap(binop,yes_math);@+break;
+@.\\GG@>
+case lt_lt: app_str("\\LL");@+app_scrap(binop,yes_math);@+break;
+@.\\LL@>
+case dot_dot_dot: app_str("\\,\\ldots\\,");@+app_scrap(raw_int,yes_math);
+ @+break;
+@.\\,@>
+@.\\ldots@>
+case colon_colon: app_str("\\DC");@+app_scrap(colcol,maybe_math);@+break;
+@.\\DC@>
+case period_ast: app_str("\\PA");@+app_scrap(binop,yes_math);@+break;
+@.\\PA@>
+case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break;
+@.\\MGA@>
+
+@ The following code must use |app_tok| instead of |app| in order to
+protect against overflow. Note that |tok_ptr+1<=max_toks| after |app_tok|
+has been used, so another |app| is legitimate before testing again.
+
+Many of the special characters in a string must be prefixed by `\.\\' so that
+\TEX/ will print them properly.
+@^special string characters@>
+
+@<Append a string or...@>=
+count= -1;
+if (next_control==constant) app_str("\\T{"@q}@>);
+@.\\T@>
+else if (next_control==string) {
+ count=20; app_str("\\.{"@q}@>);
+}
+@.\\.@>
+else app_str("\\vb{"@q}@>);
+@.\\vb@>
+while (id_first<id_loc) {
+ if (count==0) { /* insert a discretionary break in a long string */
+ app_str(@q(@>@q{@>"}\\)\\.{"@q}@>); count=20;
+@q(@>@.\\)@>
+ }
+@^high-bit character handling@>
+ if((eight_bits)(*id_first)>0177) {
+ app_tok(quoted_char);
+ app_tok((eight_bits)(*id_first++));
+ }
+ else {
+ switch (*id_first) {
+ case ' ':case '\\':case '#':case '%':case '$':case '^':
+ case '{': case '}': case '~': case '&': case '_': app('\\'); break;
+@.\\\ @>
+@.\\\\@>
+@.\\\#@>
+@.\\\%@>
+@.\\\$@>
+@.\\\^@>
+@.\\\{@>@q}@>
+@q{@>@.\\\}@>
+@.\\\~@>
+@.\\\&@>
+@.\\\_@>
+ case '@@': if (*(id_first+1)=='@@') id_first++;
+ else err_print("! Double @@ should be used in strings");
+@.Double @@ should be used...@>
+ }
+ app_tok(*id_first++);
+ }
+ count--;
+}
+app(@q{@>'}');
+app_scrap(exp,maybe_math);
+
+@ We do not make the \TEX/ string into a scrap, because there is no
+telling what the user will be putting into it; instead we leave it
+open, to be picked up by the next scrap. If it comes at the end of a
+section, it will be made into a scrap when |finish_C| is called.
+
+There's a known bug here, in cases where an adjacent scrap is
+|prelangle| or |prerangle|. Then the \TEX/ string can disappear
+when the \.{\\langle} or \.{\\rangle} becomes \.{<} or \.{>}.
+For example, if the user writes \.{\v x<@@ty@@>\v}, the \TEX/ string
+\.{\\hbox\{y\}} eventually becomes part of an |insert| scrap, which is combined
+with a |prelangle| scrap and eventually lost. The best way to work around
+this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that
+the \TEX/ string is treated as an expression.
+@^bug, known@>
+
+@<Append a \TEX/ string, without forming a scrap@>=
+app_str("\\hbox{"@q}@>);
+@^high-bit character handling@>
+while (id_first<id_loc)
+ if((eight_bits)(*id_first)>0177) {
+ app_tok(quoted_char);
+ app_tok((eight_bits)(*id_first++));
+ }
+ else {
+ if (*id_first=='@@') id_first++;
+ app_tok(*id_first++);
+ }
+app(@q{@>'}');
+
+@ The function |app_cur_id| appends the current identifier to the
+token list; it also builds a new scrap if |scrapping==true|.
+
+@<Predec...@>=
+static void app_cur_id(boolean);@/
+static text_pointer C_translate(void);@/
+static void outer_parse(void);
+
+@ @c
+static void
+app_cur_id(@t\1\1@>
+boolean scrapping@t\2\2@>) /* are we making this into a scrap? */
+{
+ name_pointer p=id_lookup(id_first,id_loc,normal);
+ if (p->ilk<=custom) { /* not a reserved word */
+ app(id_flag+(int)(p-name_dir));
+ if (scrapping) app_scrap(p->ilk==func_template? ftemplate: exp,
+ p->ilk==custom? yes_math: maybe_math);
+@.\\NULL@>
+ } else {
+ app(res_flag+(int)(p-name_dir));
+ if (scrapping) {
+ if (p->ilk==alfop) app_scrap(ubinop,yes_math)@;
+ else app_scrap(p->ilk,maybe_math);
+ }
+ }
+}
+
+@ When the `\.{\v}' that introduces \CEE/ text is sensed, a call on
+|C_translate| will return a pointer to the \TEX/ translation of
+that text. If scraps exist in |scrap_info|, they are
+unaffected by this translation process.
+
+@c
+static text_pointer
+C_translate(void)
+{
+ text_pointer p; /* points to the translation */
+ scrap_pointer save_base; /* holds original value of |scrap_base| */
+ save_base=scrap_base; scrap_base=scrap_ptr+1;
+ C_parse(section_name); /* get the scraps together */
+ if (next_control!='|') err_print("! Missing '|' after C text");
+@.Missing '|'...@>
+ app_tok(cancel); app_scrap(insert,maybe_math);
+ /* place a |cancel| token as a final ``comment'' */
+ p=translate(); /* make the translation */
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+ scrap_ptr=scrap_base-1; scrap_base=save_base; /* scrap the scraps */
+ return p;
+}
+
+@ The |outer_parse| routine is to |C_parse| as |outer_xref|
+is to |C_xref|: It constructs a sequence of scraps for \CEE/ text
+until |next_control>=format_code|. Thus, it takes care of embedded comments.
+
+The token list created from within `\pb' brackets is output as an argument
+to \.{\\PB}, if the user has invoked \.{CWEAVE} with the \.{+e} flag.
+Although \.{cwebmac} ignores \.{\\PB}, other macro packages
+might use it to localize the special meaning of the macros that mark up
+program text.
+
+@d make_pb flags['e']
+
+@<Set init...@>=
+make_pb=true;
+
+@ @c
+static void
+outer_parse(void) /* makes scraps from \CEE/ tokens and comments */
+{
+ int bal; /* brace level in comment */
+ text_pointer p, q; /* partial comments */
+ while (next_control<format_code)
+ if (next_control!=begin_comment && next_control!=begin_short_comment)
+ C_parse(ignore);
+ else {
+ boolean is_long_comment=(next_control==begin_comment);
+ @<Make sure that there is room for the new...@>@;
+ app(cancel); app(inserted);
+ if (is_long_comment) app_str("\\C{"@q}@>);
+@.\\C@>
+ else app_str("\\SHC{"@q}@>);
+@.\\SHC@>
+ bal=copy_comment(is_long_comment,1); next_control=ignore;
+ while (bal>0) {
+ p=text_ptr; freeze_text; q=C_translate();
+ /* at this point we have |tok_ptr+6<=max_toks| */
+ app(tok_flag+(int)(p-tok_start));
+ if (make_pb) app_str("\\PB{");
+@.\\PB@>
+ app(inner_tok_flag+(int)(q-tok_start));
+ if (make_pb) app_tok('}');
+ if (next_control=='|') {
+ bal=copy_comment(is_long_comment,bal);
+ next_control=ignore;
+ }
+ else bal=0; /* an error has been reported */
+ }
+ app(force); app_scrap(insert,no_math);
+ /* the full comment becomes a scrap */
+ }
+}
+
+@* Output of tokens.
+So far our programs have only built up multi-layered token lists in
+\.{CWEAVE}'s internal memory; we have to figure out how to get them into
+the desired final form. The job of converting token lists to characters in
+the \TEX/ output file is not difficult, although it is an implicitly
+recursive process. Four main considerations had to be kept in mind when
+this part of \.{CWEAVE} was designed. (a) There are two modes of output:
+|outer| mode, which translates tokens like |force| into line-breaking
+control sequences, and |inner| mode, which ignores them except that blank
+spaces take the place of line breaks. (b) The |cancel| instruction applies
+to adjacent token or tokens that are output, and this cuts across levels
+of recursion since `|cancel|' occurs at the beginning or end of a token
+list on one level. (c) The \TEX/ output file will be semi-readable if line
+breaks are inserted after the result of tokens like |break_space| and
+|force|. (d) The final line break should be suppressed, and there should
+be no |force| token output immediately after `\.{\\Y\\B}'.
+
+@ The output process uses a stack to keep track of what is going on at
+different ``levels'' as the token lists are being written out. Entries on
+this stack have three parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the token list of a
+particular level will end;
+
+\yskip\hang |tok_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\yskip\hang |mode_field| is the current mode, either |inner| or |outer|.
+
+\yskip\noindent The current values of these 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_tok|, and
+|cur_mode|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of output occurs when an |end_translation|
+token is found, so the stack is never empty except when we first begin the
+output process.
+
+@d inner 0 /* value of |mode| for \CEE/ texts within \TEX/ texts */
+@d outer 1 /* value of |mode| for \CEE/ texts in sections */
+
+@<Typed...@>= typedef int mode;
+typedef struct {
+ token_pointer end_field; /* ending location of token list */
+ token_pointer tok_field; /* present location within token list */
+ boolean mode_field; /* interpretation of control tokens */
+} output_state;
+typedef output_state *stack_pointer;
+
+@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */
+@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */
+@d cur_mode cur_state.mode_field /* current mode of interpretation */
+@d init_stack stack_ptr=stack;cur_mode=outer /* initialize the stack */
+
+@<Private...@>=
+static output_state cur_state; /* |cur_end|, |cur_tok|, |cur_mode| */
+static output_state stack[stack_size]; /* info for non-current levels */
+static stack_pointer stack_end=stack+stack_size-1; /* end of |stack| */
+static stack_pointer stack_ptr; /* first unused location in the output state stack */
+static stack_pointer max_stack_ptr; /* largest value assumed by |stack_ptr| */
+
+@ @<Set init...@>=
+max_stack_ptr=stack;
+
+@ To insert token-list |p| into the output, the |push_level| subroutine
+is called; it saves the old level of output and gets a new one going.
+The value of |cur_mode| is not changed.
+
+@<Predecl...@>=
+static void push_level(text_pointer);@/
+static void pop_level(void);
+
+@ @c
+static void
+push_level(@t\1\1@> /* suspends the current level */
+text_pointer p@t\2\2@>)
+{
+ if (stack_ptr==stack_end) overflow("stack");
+ if (stack_ptr>stack) { /* save current state */
+ stack_ptr->end_field=cur_end;
+ stack_ptr->tok_field=cur_tok;
+ stack_ptr->mode_field=cur_mode;
+ }
+ stack_ptr++;
+ if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr;
+ cur_tok=*p; cur_end=*(p+1);
+}
+
+@ Conversely, the |pop_level| routine restores the conditions that were in
+force when the current level was begun. This subroutine will never be
+called when |stack_ptr==1|.
+
+@c
+static void
+pop_level(void)
+{
+ cur_end=(--stack_ptr)->end_field;
+ cur_tok=stack_ptr->tok_field; cur_mode=stack_ptr->mode_field;
+}
+
+@ The |get_output| function returns the next byte of output that is not a
+reference to a token list. It returns the values |identifier| or |res_word|
+or |section_code| if the next token is to be an identifier (typeset in
+italics), a reserved word (typeset in boldface), or a section name (typeset
+by a complex routine that might generate additional levels of output).
+In these cases |cur_name| points to the identifier or section name in
+question.
+
+@<Private...@>=
+static name_pointer cur_name;
+
+@ @d res_word 0201 /* returned by |get_output| for reserved words */
+@d section_code 0200 /* returned by |get_output| for section names */
+
+@<Predecl...@>=
+static eight_bits get_output(void);@/
+static void output_C(void);@/
+static void make_output(void);
+
+@ @c
+static eight_bits
+get_output(void) /* returns the next token of output */
+{
+ sixteen_bits a; /* current item read from |tok_mem| */
+ restart: while (cur_tok==cur_end) pop_level();
+ a=*(cur_tok++);
+ if (a>=0400) {
+ cur_name=a % id_flag + name_dir;
+ switch (a / id_flag) {
+ case 2: return res_word ; /* |a==res_flag+cur_name| */
+ case 3: return section_code ; /* |a==section_flag+cur_name| */
+ case 4: push_level(a % id_flag + tok_start); goto restart;
+ /* |a==tok_flag+cur_name| */
+ case 5: push_level(a % id_flag + tok_start); cur_mode=inner; goto restart;
+ /* |a==inner_tok_flag+cur_name| */
+ default: return identifier; /* |a==id_flag+cur_name| */
+ }
+ }
+ return (eight_bits)a;
+}
+
+@ The real work associated with token output is done by |make_output|.
+This procedure appends an |end_translation| token to the current token list,
+and then it repeatedly calls |get_output| and feeds characters to the output
+buffer until reaching the |end_translation| sentinel. It is possible for
+|make_output| to be called recursively, since a section name may include
+embedded \CEE/ text; however, the depth of recursion never exceeds one
+level, since section names cannot be inside of section names.
+
+A procedure called |output_C| does the scanning, translation, and
+output of \CEE/ text within `\pb' brackets, and this procedure uses
+|make_output| to output the current token list. Thus, the recursive call
+of |make_output| actually occurs when |make_output| calls |output_C|
+while outputting the name of a section.
+@^recursion@>
+
+@c
+static void
+output_C(void) /* outputs the current token list */
+{
+ token_pointer save_tok_ptr;
+ text_pointer save_text_ptr;
+ sixteen_bits save_next_control; /* values to be restored */
+ text_pointer p; /* translation of the \CEE/ text */
+ save_tok_ptr=tok_ptr; save_text_ptr=text_ptr;
+ save_next_control=next_control; next_control=ignore; p=C_translate();
+ app(inner_tok_flag+(int)(p-tok_start));
+ if (make_pb) {
+ out_str("\\PB{"); make_output(); out('}');
+@.\\PB@>
+ }@+else make_output(); /* output the list */
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ text_ptr=save_text_ptr; tok_ptr=save_tok_ptr; /* forget the tokens */
+ next_control=save_next_control; /* restore |next_control| to original state */
+}
+
+@ Here is \.{CWEAVE}'s major output handler.
+
+@c
+static void
+make_output(void) /* outputs the equivalents of tokens */
+{
+ eight_bits a=0, /* current output byte */
+ b; /* next output byte */
+ int c; /* count of |indent| and |outdent| tokens */
+ char scratch[longest_name+1]; /* scratch area for section names */
+ char *k, *k_limit; /* indices into |scratch| */
+ char *j; /* index into |buffer| */
+ char *p; /* index into |byte_mem| */
+ char delim; /* first and last character of string being copied */
+ char *save_loc, *save_limit; /* |loc| and |limit| to be restored */
+ name_pointer cur_section_name; /* name of section being output */
+ boolean save_mode; /* value of |cur_mode| before a sequence of breaks */
+ app(end_translation); /* append a sentinel */
+ freeze_text; push_level(text_ptr-1);
+ while (true) {
+ a=get_output();
+ reswitch: switch(a) {
+ case end_translation: return;
+ case identifier: case res_word: @<Output an identifier@>@; break;
+ case section_code: @<Output a section name@>@; break;
+ case math_rel: out_str("\\MRL{"@q}@>);
+@.\\MRL@>
+ case noop: case inserted: break;
+ case cancel: case big_cancel: c=0; b=a;
+ while (true) {
+ a=get_output();
+ if (a==inserted) continue;
+ if ((a<indent && !(b==big_cancel&&a==' ')) || a>big_force) break;
+ if (a==indent) c++; else if (a==outdent) c--;
+ else if (a==opt) a=get_output();
+ }
+ @<Output saved |indent| or |outdent| tokens@>@;
+ goto reswitch;
+ case indent: case outdent: case opt: case backup: case break_space:
+ case force: case big_force: case preproc_line: @<Output a control,
+ look ahead in case of line breaks, possibly |goto reswitch|@>@; break;
+ case quoted_char: out(*(cur_tok++));
+ case qualifier: break;
+ default: out(a); /* otherwise |a| is an ordinary character */
+ }
+ }
+}
+
+@ An identifier of length one does not have to be enclosed in braces, and it
+looks slightly better if set in a math-italic font instead of a (slightly
+narrower) text-italic font. Thus we output `\.{\\\v}\.{a}' but
+`\.{\\\\\{aa\}}'.
+
+@<Output an identifier@>=
+out('\\');
+if (a==identifier) {
+ if (cur_name->ilk==custom && !doing_format) {
+ custom_out:
+ for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++)
+ out(*p=='_'? 'x': *p=='$'? 'X': *p);
+ break;
+ } else if (is_tiny(cur_name)) out('|')@;
+@.\\|@>
+ else { delim='.';
+ for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++)
+ if (xislower(*p)) { /* not entirely uppercase */
+ delim='\\'; break;
+ }
+ out(delim);
+ }
+@.\\\\@>
+@.\\.@>
+}@+else if (cur_name->ilk==alfop) {
+ out('X');
+ goto custom_out;
+}@+else out('&'); /* |a==res_word| */
+@.\\\&@>
+if (is_tiny(cur_name)) {
+ if (isxalpha((cur_name->byte_start)[0]))
+ out('\\');
+ out((cur_name->byte_start)[0]);
+}
+else out_name(cur_name,true);
+
+@ The current mode does not affect the behavior of \.{CWEAVE}'s output routine
+except when we are outputting control tokens.
+
+@<Output a control...@>=
+if (a<break_space || a==preproc_line) {
+ if (cur_mode==outer) {
+ out('\\'); out(a-cancel+'0');
+@.\\1@>
+@.\\2@>
+@.\\3@>
+@.\\4@>
+@.\\8@>
+ if (a==opt) {
+ b=get_output(); /* |opt| is followed by a digit */
+ if (b!='0' || force_lines==false) out(b)@;
+ else out_str("{-1}"); /* |force_lines| encourages more \.{@@\v} breaks */
+ }
+ } else if (a==opt) b=get_output(); /* ignore digit following |opt| */
+ }
+else @<Look ahead for strongest line break, |goto reswitch|@>
+
+@ If several of the tokens |break_space|, |force|, |big_force| occur in a
+row, possibly mixed with blank spaces (which are ignored),
+the largest one is used. A line break also occurs in the output file,
+except at the very end of the translation. The very first line break
+is suppressed (i.e., a line break that follows `\.{\\Y\\B}').
+
+@<Look ahead for st...@>= {
+ b=a; save_mode=cur_mode; c=0;
+ while (true) {
+ a=get_output();
+ if (a==inserted) continue;
+ if (a==cancel || a==big_cancel) {
+ @<Output saved |indent| or |outdent| tokens@>@;
+ goto reswitch; /* |cancel| overrides everything */
+ }
+ if ((a!=' ' && a<indent) || a==backup || a>big_force) {
+ if (save_mode==outer) {
+ if (out_ptr>out_buf+3 && strncmp(out_ptr-3,"\\Y\\B",4)==0)
+ goto reswitch;
+ @<Output saved |indent| or |outdent| tokens@>@;
+ out('\\'); out(b-cancel+'0');
+@.\\5@>
+@.\\6@>
+@.\\7@>
+ if (a!=end_translation) finish_line();
+ }
+ else if (a!=end_translation && cur_mode==inner) out(' ');
+ goto reswitch;
+ }
+ if (a==indent) c++;
+ else if (a==outdent) c--;
+ else if (a==opt) a=get_output();
+ else if (a>b) b=a; /* if |a==' '| we have |a<b| */
+ }
+}
+
+@ @<Output saved...@>=
+ for (;c>0;c--) out_str("\\1");
+@.\\1@>
+ for (;c<0;c++) out_str("\\2");
+@.\\2@>
+
+@ The remaining part of |make_output| is somewhat more complicated. When we
+output a section name, we may need to enter the parsing and translation
+routines, since the name may contain \CEE/ code embedded in
+\pb\ constructions. This \CEE/ code is placed at the end of the active
+input buffer and the translation process uses the end of the active
+|tok_mem| area.
+
+@<Output a section name@>= {
+ out_str("\\X");
+@.\\X@>
+ cur_xref=(xref_pointer)cur_name->xref;
+ if (cur_xref->num==file_flag) {an_output=true; cur_xref=cur_xref->xlink;}
+ else an_output=false;
+ if (cur_xref->num>=def_flag) {
+ out_section(cur_xref->num-def_flag);
+ if (phase==3) {
+ cur_xref=cur_xref->xlink;
+ while (cur_xref->num>=def_flag) {
+ out_str(", ");
+ out_section(cur_xref->num-def_flag);
+ cur_xref=cur_xref->xlink;
+ }
+ }
+ }
+ else out('0'); /* output the section number, or zero if it was undefined */
+ out(':');
+ if (an_output) out_str("\\.{"@q}@>);
+@.\\.@>
+ @<Output the text of the section name@>@;
+ if (an_output) out_str(@q{@>" }");
+ out_str("\\X");
+}
+
+@ @<Output the text...@>=
+sprint_section_name(scratch,cur_name);
+k=scratch;
+k_limit=scratch+strlen(scratch);
+cur_section_name=cur_name;
+while (k<k_limit) {
+ b=*(k++);
+ if (b=='@@') @<Skip next character, give error if not `\.{@@}'@>@;
+ if (an_output)
+ switch (b) {
+ case ' ':case '\\':case '#':case '%':case '$':case '^':
+ case '{': case '}': case '~': case '&': case '_':
+ out('\\'); /* falls through */
+@.\\\ @>
+@.\\\\@>
+@.\\\#@>
+@.\\\%@>
+@.\\\$@>
+@.\\\^@>
+@.\\\{@>@q}@>
+@q{@>@.\\\}@>
+@.\\\~@>
+@.\\\&@>
+@.\\\_@>
+ default: out(b);
+ }
+ else { if (b!='|') out(b)@;
+ else {
+ @<Copy the \CEE/ text into the |buffer| array@>@;
+ save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1;
+ *limit='|'; output_C();
+ loc=save_loc; limit=save_limit;
+ } }
+}
+
+@ @<Skip next char...@>=
+if (*k++!='@@') {
+ fputs("\n! Illegal control code in section name: <",stdout);
+@.Illegal control code...@>
+ print_section_name(cur_section_name); printf("> "); mark_error;
+}
+
+@ The \CEE/ text enclosed in \pb\ should not contain `\.{\v}' characters,
+except within strings. We put a `\.{\v}' at the front of the buffer, so that an
+error message that displays the whole buffer will look a little bit sensible.
+The variable |delim| is zero outside of strings, otherwise it
+equals the delimiter that began the string being copied.
+
+@<Copy the \CEE/ text into...@>=
+j=limit+1; *j='|'; delim=0;
+while (true) {
+ if (k>=k_limit) {
+ fputs("\n! C text in section name didn't end: <",stdout);
+@.C text...didn't end@>
+ print_section_name(cur_section_name); printf("> "); mark_error; break;
+ }
+ b=*(k++);
+ if (b=='@@' || (b=='\\' && delim!=0))
+ @<Copy a quoted character into the buffer@>@;
+ else {
+ if (b=='\'' || b=='"') {
+ if (delim==0) delim=b;
+ else if (delim==b) delim=0; }
+ if (b!='|' || delim!=0) {
+ if (j>buffer+long_buf_size-3) overflow("buffer");
+ *(++j)=b;
+ }
+ else break;
+ }
+}
+
+@ @<Copy a quoted char...@>= {
+ if (j>buffer+long_buf_size-4) overflow("buffer");
+ *(++j)=b; *(++j)=*(k++);
+}
+
+@** Phase two processing.
+We have assembled enough pieces of the puzzle in order to be ready to specify
+the processing in \.{CWEAVE}'s main pass over the source file. Phase two
+is analogous to phase one, except that more work is involved because we must
+actually output the \TEX/ material instead of merely looking at the
+\.{CWEB} specifications.
+
+@c
+static void
+phase_two(void) {
+reset_input(); if (show_progress) fputs("\nWriting the output file...",stdout);
+@.Writing the output file...@>
+section_count=0; format_visible=true; copy_limbo();
+finish_line(); flush_buffer(out_buf,false,false); /* insert a blank line, it looks nice */
+while (!input_has_ended) @<Translate the current section@>@;
+}
+
+@ @<Predecl...@>=@+static void phase_two(void);
+
+@ The output file will contain the control sequence \.{\\Y} between non-null
+sections of a section, e.g., between the \TEX/ and definition parts if both
+are nonempty. This puts a little white space between the parts when they are
+printed. However, we don't want \.{\\Y} to occur between two definitions
+within a single section. The variables |out_line| or |out_ptr| will
+change if a section is non-null, so the following macros `|save_position|'
+and `|emit_space_if_needed|' are able to handle the situation:
+
+@d save_position save_line=out_line; save_place=out_ptr
+@d emit_space_if_needed if (save_line!=out_line || save_place!=out_ptr)
+ out_str("\\Y");
+ space_checked=true;
+@.\\Y@>
+
+@<Private...@>=
+static int save_line; /* former value of |out_line| */
+static char *save_place; /* former value of |out_ptr| */
+static int sec_depth; /* the integer, if any, following \.{@@*} */
+static boolean space_checked; /* have we done |emit_space_if_needed|? */
+static boolean format_visible; /* should the next format declaration be output? */
+static boolean doing_format=false; /* are we outputting a format declaration? */
+static boolean group_found=false; /* has a starred section occurred? */
+
+@ @<Translate the current section@>= {
+ section_count++;
+ @<Output the code for the beginning of a new section@>@;
+ save_position;
+ @<Translate the \TEX/ part of the current section@>@;
+ @<Translate the definition part of the current section@>@;
+ @<Translate the \CEE/ part of the current section@>@;
+ @<Show cross-references to this section@>@;
+ @<Output the code for the end of a section@>@;
+}
+
+@ Sections beginning with the \.{CWEB} control sequence `\.{@@\ }' start in the
+output with the \TEX/ control sequence `\.{\\M}', followed by the section
+number. Similarly, `\.{@@*}' sections lead to the control sequence `\.{\\N}'.
+In this case there's an additional parameter, representing one plus the
+specified depth, immediately after the \.{\\N}.
+If the section has changed, we put \.{\\*} just after the section number.
+
+@<Output the code for the beginning...@>=
+if (*(loc-1)!='*') out_str("\\M");
+@.\\M@>
+else {
+ while (*loc == ' ') loc++;
+ if (*loc=='*') { /* ``top'' level */
+ sec_depth = -1;
+ loc++;
+ }
+ else {
+ for (sec_depth=0; xisdigit(*loc);loc++)
+ sec_depth = sec_depth*10 + (*loc) -'0';
+ }
+ while (*loc == ' ') loc++; /* remove spaces before group title */
+ group_found=true;
+ out_str("\\N");
+@.\\N@>
+ {@+ char s[32];@+sprintf(s,"{%d}",sec_depth+1);@+out_str(s);@+}
+ if (show_progress)
+ printf("*%d",section_count); update_terminal; /* print a progress report */
+}
+out_str("{");out_section(section_count); out_str("}");
+
+@ In the \TEX/ part of a section, we simply copy the source text, except that
+index entries are not copied and \CEE/ text within \pb\ is translated.
+
+@<Translate the \T...@>= do {
+ next_control=copy_TeX();
+ switch (next_control) {
+ case '|': init_stack; output_C(); break;
+ case '@@': out('@@'); break;
+ case TeX_string: case noop:
+ case xref_roman: case xref_wildcard: case xref_typewriter:
+ case section_name: loc-=2; next_control=get_next(); /* skip to \.{@@>} */
+ if (next_control==TeX_string)
+ err_print("! TeX string should be in C text only"); break;
+@.TeX string should be...@>
+ case thin_space: case math_break: case ord:
+ case line_break: case big_line_break: case no_line_break: case join:
+ case pseudo_semi: case macro_arg_open: case macro_arg_close:
+ case output_defs_code:
+ err_print("! You can't do that in TeX text"); break;
+@.You can't do that...@>
+ }
+} while (next_control<format_code);
+
+@ When we get to the following code we have |next_control>=format_code|, and
+the token memory is in its initial empty state.
+
+@<Translate the d...@>=
+space_checked=false;
+while (next_control<=definition) { /* |format_code| or |definition| */
+ init_stack;
+ if (next_control==definition) @<Start a macro definition@>@;
+ else @<Start a format definition@>@;
+ outer_parse(); finish_C(format_visible); format_visible=true;
+ doing_format=false;
+}
+
+@ The |finish_C| procedure outputs the translation of the current
+scraps, preceded by the control sequence `\.{\\B}' and followed by the
+control sequence `\.{\\par}'. It also restores the token and scrap
+memories to their initial empty state.
+
+A |force| token is appended to the current scraps before translation
+takes place, so that the translation will normally end with \.{\\6} or
+\.{\\7} (the \TEX/ macros for |force| and |big_force|). This \.{\\6} or
+\.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}.
+
+@c
+static void
+finish_C(@t\1\1@> /* finishes a definition or a \CEE/ part */
+ boolean visible@t\2\2@>) /* nonzero if we should produce \TeX\ output */
+{
+ text_pointer p; /* translation of the scraps */
+ if (visible) {
+ out_str("\\B"); app_tok(force); app_scrap(insert,no_math);
+ p=translate();
+@.\\B@>
+ app(tok_flag+(int)(p-tok_start)); make_output(); /* output the list */
+ if (out_ptr>out_buf+1) {
+ if (*(out_ptr-1)=='\\') {
+@.\\6@>
+@.\\7@>
+@.\\Y@>
+ if (*out_ptr=='6') out_ptr-=2;
+ else if (*out_ptr=='7') *out_ptr='Y';
+ }
+ }
+ out_str("\\par"); finish_line();
+ }
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+ tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info;
+ /* forget the tokens and the scraps */
+}
+
+@ @<Predecl...@>=@+static void finish_C(boolean);
+
+@ Keeping in line with the conventions of the \CEE/ preprocessor (and
+otherwise contrary to the rules of \.{CWEB}) we distinguish here
+between the case that `\.(' immediately follows an identifier and the
+case that the two are separated by a space. In the latter case, and
+if the identifier is not followed by `\.(' at all, the replacement
+text starts immediately after the identifier. In the former case,
+it starts after we scan the matching `\.)'.
+
+@<Start a macro...@>= {
+ if (save_line!=out_line || save_place!=out_ptr || space_checked) app(backup);
+ if(!space_checked){emit_space_if_needed;save_position;}
+ app_str("\\D"); /* this will produce `\&{define }' */
+@.\\D@>
+ if ((next_control=get_next())!=identifier)
+ err_print("! Improper macro definition");
+@.Improper macro definition@>
+ else {
+ app('$'); app_cur_id(false);
+ if (*loc=='(')
+ reswitch: switch (next_control=get_next()) {
+ case '(': case ',': app(next_control); goto reswitch;
+ case identifier: app_cur_id(false); goto reswitch;
+ case ')': app(next_control); next_control=get_next(); break;
+ default: err_print("! Improper macro definition"); break;
+ }
+ else next_control=get_next();
+ app_str("$ "); app(break_space);
+ app_scrap(dead,no_math); /* scrap won't take part in the parsing */
+ }
+}
+
+@ @<Start a format...@>= {
+ doing_format=true;
+ if(*(loc-1)=='s' || *(loc-1)=='S') format_visible=false;
+ if(!space_checked){emit_space_if_needed;save_position;}
+ app_str("\\F"); /* this will produce `\&{format }' */
+@.\\F@>
+ next_control=get_next();
+ if (next_control==identifier) {
+ app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir));
+ app(' ');
+ app(break_space); /* this is syntactically separate from what follows */
+ next_control=get_next();
+ if (next_control==identifier) {
+ app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir));
+ app_scrap(exp,maybe_math); app_scrap(semi,maybe_math);
+ next_control=get_next();
+ }
+ }
+ if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition");
+@.Improper format definition@>
+}
+
+@ Finally, when the \TEX/ and definition parts have been treated, we have
+|next_control>=begin_C|. We will make the global variable |this_section|
+point to the current section name, if it has a name.
+
+@<Private...@>=
+static name_pointer this_section; /* the current section name, or zero */
+
+@ @<Translate the \CEE/...@>=
+this_section=name_dir;
+if (next_control<=section_name) {
+ emit_space_if_needed; init_stack;
+ if (next_control==begin_C) next_control=get_next();
+ else {
+ this_section=cur_section;
+ @<Check that '=' or '==' follows this section name, and
+ emit the scraps to start the section definition@>@;
+ }
+ while (next_control<=section_name) {
+ outer_parse();
+ @<Emit the scrap for a section name if present@>@;
+ }
+ finish_C(true);
+}
+
+@ The title of the section and an $\E$ or $\mathrel+\E$ are made
+into a scrap that should not take part in the parsing.
+
+@<Check that '='...@>=
+do next_control=get_next();
+ while (next_control=='+'); /* allow optional `\.{+=}' */
+if (next_control!='=' && next_control!=eq_eq)
+ err_print("! You need an = sign after the section name");
+@.You need an = sign...@>
+ else next_control=get_next();
+if (out_ptr>out_buf+1 && *out_ptr=='Y' && *(out_ptr-1)=='\\') app(backup);
+ /* the section name will be flush left */
+@.\\Y@>
+app(section_flag+(int)(this_section-name_dir));
+cur_xref=(xref_pointer)this_section->xref;
+if(cur_xref->num==file_flag) cur_xref=cur_xref->xlink;
+app_str("${}");
+if (cur_xref->num!=section_count+def_flag) {
+ app_str("\\mathrel+"); /* section name is multiply defined */
+ this_section=name_dir; /* so we won't give cross-reference info here */
+}
+app_str("\\E"); /* output an equivalence sign */
+@.\\E@>
+app_str("{}$");
+app(force); app_scrap(dead,no_math);
+ /* this forces a line break unless `\.{@@+}' follows */
+
+@ @<Emit the scrap...@>=
+if (next_control<section_name) {
+ err_print("! You can't do that in C text");
+@.You can't do that...@>
+ next_control=get_next();
+}
+else if (next_control==section_name) {
+ app(section_flag+(int)(cur_section-name_dir));
+ app_scrap(section_scrap,maybe_math);
+ next_control=get_next();
+}
+
+@ Cross references relating to a named section are given
+after the section ends.
+
+@<Show cross...@>=
+if (this_section>name_dir) {
+ cur_xref=(xref_pointer)this_section->xref;
+ if (cur_xref->num==file_flag){an_output=true;cur_xref=cur_xref->xlink;}
+ else an_output=false;
+ if (cur_xref->num>def_flag)
+ cur_xref=cur_xref->xlink; /* bypass current section number */
+ footnote(def_flag); footnote(cite_flag); footnote(0);
+}
+
+@ The |footnote| procedure gives cross-reference information about
+multiply defined section names (if the |flag| parameter is
+|def_flag|), or about references to a section name
+(if |flag==cite_flag|), or to its uses (if |flag==0|). It assumes that
+|cur_xref| points to the first cross-reference entry of interest, and it
+leaves |cur_xref| pointing to the first element not printed. Typical outputs:
+`\.{\\A101.}'; `\.{\\Us 370\\ET1009.}';
+`\.{\\As 8, 27\\*\\ETs64.}'.
+
+Note that the output of \.{CWEAVE} is not English-specific; users may
+supply new definitions for the macros \.{\\A}, \.{\\As}, etc.
+
+@c
+static void
+footnote(@t\1\1@> /* outputs section cross-references */
+sixteen_bits flag@t\2\2@>)
+{
+ xref_pointer q; /* cross-reference pointer variable */
+ if (cur_xref->num<=flag) return;
+ finish_line(); out('\\');
+@.\\A@>
+@.\\Q@>
+@.\\U@>
+ out(flag==0? 'U': flag==cite_flag? 'Q': 'A');
+ @<Output all the section numbers on the reference list |cur_xref|@>@;
+ out('.');
+}
+
+@ @<Predecl...@>=@+static void footnote(sixteen_bits);
+
+@ The following code distinguishes three cases, according as the number
+of cross-references is one, two, or more than two. Variable |q| points
+to the first cross-reference, and the last link is a zero.
+
+@<Output all the section numbers...@>=
+q=cur_xref; if (q->xlink->num>flag) out('s'); /* plural */
+while (true) {
+ out_section(cur_xref->num-flag);
+ cur_xref=cur_xref->xlink; /* point to the next cross-reference to output */
+ if (cur_xref->num<=flag) break;
+ if (cur_xref->xlink->num>flag) out_str(", "); /* not the last */
+ else {out_str("\\ET"); /* the last */
+@.\\ET@>
+ if (cur_xref != q->xlink) out('s'); /* the last of more than two */
+ }
+}
+
+@ @<Output the code for the end of a section@>=
+out_str("\\fi"); finish_line();
+@.\\fi@>
+flush_buffer(out_buf,false,false); /* insert a blank line, it looks nice */
+
+@** Phase three processing.
+We are nearly finished! \.{CWEAVE}'s only remaining task is to write out the
+index, after sorting the identifiers and index entries.
+
+If the user has set the |no_xref| flag (the \.{-x} option on the command line),
+just finish off the page, omitting the index, section name list, and table of
+contents.
+
+@c
+static void
+phase_three(void) {
+if (no_xref) {
+ finish_line();
+ out_str("\\end");
+@.\\end@>
+ finish_line();
+}
+else {
+ phase=3; if (show_progress) fputs("\nWriting the index...",stdout);
+@.Writing the index...@>
+ finish_line();
+ if ((idx_file=fopen(idx_file_name,"wb"))==NULL)
+ fatal("! Cannot open index file ",idx_file_name);
+@.Cannot open index file@>
+ if (change_exists) {
+ @<Tell about changed sections@>@; finish_line(); finish_line();
+ }
+ out_str("\\inx"); finish_line();
+@.\\inx@>
+ active_file=idx_file; /* change active file to the index file */
+ @<Do the first pass of sorting@>@;
+ @<Sort and output the index@>@;
+ finish_line(); fclose(active_file); /* finished with |idx_file| */
+ active_file=tex_file; /* switch back to |tex_file| for a tic */
+ out_str("\\fin"); finish_line();
+@.\\fin@>
+ if ((scn_file=fopen(scn_file_name,"wb"))==NULL)
+ fatal("! Cannot open section file ",scn_file_name);
+@.Cannot open section file@>
+ active_file=scn_file; /* change active file to section listing file */
+ @<Output all the section names@>@;
+ finish_line(); fclose(active_file); /* finished with |scn_file| */
+ active_file=tex_file;
+ if (group_found) out_str("\\con");@+else out_str("\\end");
+@.\\con@>
+@.\\end@>
+ finish_line();
+ fclose(active_file);
+}
+if (show_happiness) {
+ if (show_progress) new_line;
+ fputs("Done.",stdout);
+}
+check_complete(); /* was all of the change file used? */
+}
+
+@ @<Predecl...@>=@+static void phase_three(void);
+
+@ Just before the index comes a list of all the changed sections, including
+the index section itself.
+
+@<Private...@>=
+static sixteen_bits k_section; /* runs through the sections */
+
+@ @<Tell about changed sections@>= {
+ /* remember that the index is already marked as changed */
+ k_section=0;
+ while (!changed_section[++k_section]);
+ out_str("\\ch ");
+@.\\ch@>
+ out_section(k_section);
+ while (k_section<section_count) {
+ while (!changed_section[++k_section]);
+ out_str(", "); out_section(k_section);
+ }
+ out('.');
+}
+
+@ A left-to-right radix sorting method is used, since this makes it easy to
+adjust the collating sequence and since the running time will be at worst
+proportional to the total length of all entries in the index. We put the
+identifiers into different lists based on their first characters.
+(Uppercase letters are put into the same list as the corresponding lowercase
+letters, since we want to have `$t<\\{TeX}<\&{to}$'.) The
+list for character |c| begins at location |bucket[c]| and continues through
+the |blink| array.
+
+@<Private...@>=
+static name_pointer bucket[256];
+static name_pointer next_name; /* successor of |cur_name| when sorting */
+static name_pointer blink[max_names]; /* links in the buckets */
+
+@ To begin the sorting, we go through all the hash lists and put each entry
+having a nonempty cross-reference list into the proper bucket.
+
+@<Do the first pass...@>= {
+int c;
+for (c=0; c<256; c++) bucket[c]=NULL;
+for (h=hash; h<=hash_end; h++) {
+ next_name=*h;
+ while (next_name) {
+ cur_name=next_name; next_name=cur_name->link;
+ if (cur_name->xref!=(void *)xmem) {
+ c=(eight_bits)((cur_name->byte_start)[0]);
+ if (xisupper(c)) c=tolower(c);
+ blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
+ }
+ }
+}
+}
+
+@ During the sorting phase we shall use the |cat| and |trans| arrays from
+\.{CWEAVE}'s parsing algorithm and rename them |depth| and |head|. They now
+represent a stack of identifier lists for all the index entries that have
+not yet been output. The variable |sort_ptr| tells how many such lists are
+present; the lists are output in reverse order (first |sort_ptr|, then
+|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first
+|k| characters of all entries on this list are known to be equal we have
+|depth[j]==k|.
+
+@<Rest of |trans_plus| union@>=
+name_pointer Head;
+
+@ @d depth cat /* reclaims memory that is no longer needed for parsing */
+@d head trans_plus.Head /* ditto */
+@f sort_pointer int
+@d sort_pointer scrap_pointer /* ditto */
+@d sort_ptr scrap_ptr /* ditto */
+@d max_sorts max_scraps /* ditto */
+
+@<Private...@>=
+static eight_bits cur_depth; /* depth of current buckets */
+static char *cur_byte; /* index into |byte_mem| */
+static sixteen_bits cur_val; /* current cross-reference number */
+static sort_pointer max_sort_ptr; /* largest value of |sort_ptr| */
+
+@ @<Set init...@>=
+max_sort_ptr=scrap_info;
+
+@ The desired alphabetic order is specified by the |collate| array; namely,
+$|collate|[0]<|collate|[1]<\cdots<|collate|[100]$.
+
+@<Private...@>=
+static eight_bits collate[101+128]; /* collation order */
+@^high-bit character handling@>
+
+@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<{}$\.\_${}<
+\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$ Warning: The collation mapping
+needs to be changed if ASCII code is not being used.
+@^ASCII code dependencies@>
+@^high-bit character handling@>
+
+We initialize |collate| by copying a few characters at a time, because
+some \CEE/ compilers choke on long strings.
+
+@<Set init...@>=
+collate[0]=0;
+memcpy((char *)collate+1,
+ " \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17",16);
+/* 16 characters + 1 = 17 */
+memcpy((char *)collate+17,
+ "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37",16);
+/* 16 characters + 17 = 33 */
+memcpy((char *)collate+33,
+ "!\42#$%&'()*+,-./:;<=>?@@[\\]^`{|}~_",32);
+/* 32 characters + 33 = 65 */
+memcpy((char *)collate+65,
+ "abcdefghijklmnopqrstuvwxyz0123456789",36);
+/* (26 + 10) characters + 65 = 101 */
+memcpy((char *)collate+101,
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217",16);
+/* 16 characters + 101 = 117 */
+memcpy((char *)collate+117,
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237",16);
+/* 16 characters + 117 = 133 */
+memcpy((char *)collate+133,
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257",16);
+/* 16 characters + 133 = 149 */
+memcpy((char *)collate+149,
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277",16);
+/* 16 characters + 149 = 165 */
+memcpy((char *)collate+165,
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317",16);
+/* 16 characters + 165 = 181 */
+memcpy((char *)collate+181,
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337",16);
+/* 16 characters + 181 = 197 */
+memcpy((char *)collate+197,
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357",16);
+/* 16 characters + 197 = 213 */
+memcpy((char *)collate+213,
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",16);
+/* 16 characters + 213 = 229 */
+
+@ Procedure |unbucket| goes through the buckets and adds nonempty lists
+to the stack, using the collating sequence specified in the |collate| array.
+The parameter to |unbucket| tells the current depth in the buckets.
+Any two sequences that agree in their first 255 character positions are
+regarded as identical.
+
+@d infinity 255 /* $\infty$ (approximately) */
+
+@c
+static void
+unbucket(@t\1\1@> /* empties buckets having depth |d| */
+eight_bits d@t\2\2@>)
+{
+ int c; /* index into |bucket|; cannot be a simple |char| because of sign
+ comparison below */
+ for (c=100+128; c>= 0; c--) if (bucket[collate[c]]) {
+@^high-bit character handling@>
+ if (sort_ptr>=scrap_info_end) overflow("sorting");
+ sort_ptr++;
+ if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr;
+ if (c==0) sort_ptr->depth=infinity;
+ else sort_ptr->depth=d;
+ sort_ptr->head=bucket[collate[c]]; bucket[collate[c]]=NULL;
+ }
+}
+
+@ @<Predecl...@>=@+static void unbucket(eight_bits);
+
+@ @<Sort and output...@>=
+sort_ptr=scrap_info; unbucket(1);
+while (sort_ptr>scrap_info) {
+ cur_depth=sort_ptr->depth;
+ if (blink[sort_ptr->head-name_dir]==0 || cur_depth==infinity)
+ @<Output index entries for the list at |sort_ptr|@>@;
+ else @<Split the list at |sort_ptr| into further lists@>@;
+}
+
+@ @<Split the list...@>= {
+ eight_bits c;
+ next_name=sort_ptr->head;
+ do {
+ cur_name=next_name; next_name=blink[cur_name-name_dir];
+ cur_byte=cur_name->byte_start+cur_depth;
+ if (cur_byte==(cur_name+1)->byte_start) c=0; /* hit end of the name */
+ else {
+ c=(eight_bits) *cur_byte;
+ if (xisupper(c)) c=tolower(c);
+ }
+ blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
+ } while (next_name);
+ --sort_ptr; unbucket(cur_depth+1);
+}
+
+@ @<Output index...@>= {
+ cur_name=sort_ptr->head;
+ do {
+ out_str("\\I");
+@.\\I@>
+ @<Output the name at |cur_name|@>@;
+ @<Output the cross-references at |cur_name|@>@;
+ cur_name=blink[cur_name-name_dir];
+ } while (cur_name);
+ --sort_ptr;
+}
+
+@ @<Output the name...@>=
+switch (cur_name->ilk) {
+ case normal: case func_template: if (is_tiny(cur_name)) out_str("\\|");
+ else {char *j;
+ for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++)
+ if (xislower(*j)) goto lowcase;
+ out_str("\\."); break;
+lowcase: out_str("\\\\");
+ }
+ break;
+@.\\|@>
+@.\\.@>
+@.\\\\@>
+ case wildcard: out_str("\\9");@+ goto not_an_identifier;
+@.\\9@>
+ case typewriter: out_str("\\.");
+@.\\.@>
+ case roman: not_an_identifier: out_name(cur_name,false); goto name_done;
+ case custom: {char *j; out_str("$\\");
+ for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++)
+ out(*j=='_'? 'x': *j=='$'? 'X': *j);
+ out('$');
+ goto name_done;
+ }
+ default: out_str("\\&");
+@.\\\&@>
+}
+out_name(cur_name,true);
+name_done:@;
+
+@ Section numbers that are to be underlined are enclosed in
+`\.{\\[}$\,\ldots\,$\.]'.
+
+@<Output the cross-references...@>=
+@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>@;
+do {
+ out_str(", "); cur_val=cur_xref->num;
+ if (cur_val<def_flag) out_section(cur_val);
+ else {out_str("\\["); out_section(cur_val-def_flag); out(']');}
+@.\\[@>
+ cur_xref=cur_xref->xlink;
+} while (cur_xref!=xmem);
+out('.'); finish_line();
+
+@ List inversion is best thought of as popping elements off one stack and
+pushing them onto another. In this case |cur_xref| will be the head of
+the stack that we push things onto.
+@<Private...@>=
+static xref_pointer next_xref, this_xref;
+ /* pointer variables for rearranging a list */
+
+@ @<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>=
+this_xref=(xref_pointer)cur_name->xref; cur_xref=xmem;
+do {
+ next_xref=this_xref->xlink; this_xref->xlink=cur_xref;
+ cur_xref=this_xref; this_xref=next_xref;
+} while (this_xref!=xmem);
+
+@ The following recursive procedure walks through the tree of section names and
+prints them.
+@^recursion@>
+
+@c
+static void
+section_print(@t\1\1@> /* print all section names in subtree |p| */
+name_pointer p@t\2\2@>)
+{
+ if (p) {
+ section_print(p->llink); out_str("\\I");
+@.\\I@>
+ tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; init_stack;
+ app(p-name_dir+section_flag); make_output();
+ footnote(cite_flag);
+ footnote(0); /* |cur_xref| was set by |make_output| */
+ finish_line();@/
+ section_print(p->rlink);
+ }
+}
+
+@ @<Predecl...@>=@+static void section_print(name_pointer);
+
+@ @<Output all the section names@>=section_print(root);
+
+@ Because on some systems the difference between two pointers is a |ptrdiff_t|
+rather than an |int|, we use \.{\%ld} to print these quantities.
+
+@c
+void
+print_stats(void) {
+ puts("\nMemory usage statistics:");
+@.Memory usage statistics:@>
+ printf("%ld names (out of %ld)\n",
+ (ptrdiff_t)(name_ptr-name_dir),(long)max_names);
+ printf("%ld cross-references (out of %ld)\n",
+ (ptrdiff_t)(xref_ptr-xmem),(long)max_refs);
+ printf("%ld bytes (out of %ld)\n",
+ (ptrdiff_t)(byte_ptr-byte_mem),(long)max_bytes);
+ puts("Parsing:");
+ printf("%ld scraps (out of %ld)\n",
+ (ptrdiff_t)(max_scr_ptr-scrap_info),(long)max_scraps);
+ printf("%ld texts (out of %ld)\n",
+ (ptrdiff_t)(max_text_ptr-tok_start),(long)max_texts);
+ printf("%ld tokens (out of %ld)\n",
+ (ptrdiff_t)(max_tok_ptr-tok_mem),(long)max_toks);
+ printf("%ld levels (out of %ld)\n",
+ (ptrdiff_t)(max_stack_ptr-stack),(long)stack_size);
+ puts("Sorting:");
+ printf("%ld levels (out of %ld)\n",
+ (ptrdiff_t)(max_sort_ptr-scrap_info),(long)max_scraps);
+}
+
+@** Index.
+If you have read and understood the code for Phase III above, you know what
+is in this index and how it got here. 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, control
+sequences put into the output, and a few
+other things like ``recursion'' are indexed here too.