diff options
Diffstat (limited to 'web/c_cpp/cweb/ctangle.w')
-rw-r--r-- | web/c_cpp/cweb/ctangle.w | 460 |
1 files changed, 240 insertions, 220 deletions
diff --git a/web/c_cpp/cweb/ctangle.w b/web/c_cpp/cweb/ctangle.w index e7e2d311fe..1a9e8065af 100644 --- a/web/c_cpp/cweb/ctangle.w +++ b/web/c_cpp/cweb/ctangle.w @@ -2,7 +2,7 @@ % 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 +% Version 4.6 --- December 2021 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth @@ -27,11 +27,11 @@ \mathchardef\RA="3221 % right arrow \mathchardef\BA="3224 % double arrow -\def\title{CTANGLE (Version 4.2)} +\def\title{CTANGLE (Version 4.6)} \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont CTANGLE} processor} \vskip 15pt - \centerline{(Version 4.2)} + \centerline{(Version 4.6)} \vfill} \def\botofcontents{\vfill \noindent @@ -61,7 +61,7 @@ Joachim Schrod, Lee Wittenberg, and others who have contributed improvements. The ``banner line'' defined here should be changed whenever \.{CTANGLE} is modified. -@d banner "This is CTANGLE (Version 4.2)" +@d banner "This is CTANGLE (Version 4.6)" @c @<Include files@>@/ @@ -75,7 +75,7 @@ is modified. two phases: First it reads the source file, saving the \CEE/ code in compressed form; then it shuffles and outputs the code. -Please read the documentation for \.{common}, the set of routines common +Please read the documentation for \.{COMMON}, the set of routines common to \.{CTANGLE} and \.{CWEAVE}, before proceeding further. @c @@ -126,7 +126,9 @@ typedef struct { } text; typedef text *text_pointer; -@ @<Private...@>= +@ @d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d max_toks 270000 /* number of bytes in compressed \CEE/ code */ +@<Private...@>= static text text_info[max_texts]; static text_pointer text_info_end=text_info+max_texts-1; static text_pointer text_ptr; /* first unused position in |text_info| */ @@ -151,14 +153,13 @@ init_node(name_dir); /* the undefined section has no replacement text */ starting at position |first| equals the identifier pointed to by |p|: @c -boolean names_match(@t\1\1@> +boolean names_match( name_pointer p, /* points to the proposed match */ const char *first, /* position of first character of string */ size_t l, /* length of identifier */ -eight_bits t@t\2\2@>) /* not used by \.{TANGLE} */ -{@+(void)t; - if (length(p)!=l) return false; - return !strncmp(first,p->byte_start,l); +eight_bits t) /* not used by \.{TANGLE} */ +{@+(void)t;@/ + return length(p)==l && strncmp(first,p->byte_start,l)==0; } @ The common lookup routine refers to separate routines |init_node| and @@ -193,12 +194,13 @@ name, and they also tie together all the \CEE/ texts of unnamed sections. The replacement text pointer for the first unnamed section appears in |text_info->text_link|, and the most recent such pointer is |last_unnamed|. +@d macro 0 @d section_flag max_texts /* final |text_link| in section replacement texts */ @<Private...@>= static text_pointer last_unnamed; /* most recent replacement text of unnamed section */ -@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=0; +@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=macro; @ If the first byte of a token is less than |0200|, the token occupies a single byte. Otherwise we make a sixteen-bit token by combining two consecutive @@ -214,16 +216,19 @@ Codes less than |0200| are 7-bit |char| codes that represent themselves. Some of the 7-bit codes will not be present, however, so we can use them for special purposes. The following symbolic names are used: -\yskip \hang |join| denotes the concatenation of adjacent items with no -space or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}). +\yskip \hang |string| denotes the beginning or end of a string +or a verbatim construction. + +\hang |constant| denotes a numerical constant. -\hang |string| denotes the beginning or end of a string, verbatim -construction or numerical constant. +\hang |join| denotes the concatenation of adjacent items with no space +or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}). @^ASCII code dependencies@> -@d string 02 /* takes the place of extended ASCII \.{\char2} */ -@d join 0177 /* takes the place of ASCII delete */ -@d output_defs_flag (2*024000-1) +@d string 02 /* takes the place of ASCII \.{STX} */ +@d constant 03 /* takes the place of ASCII \.{ETX} */ +@d join 0177 /* takes the place of ASCII \.{DEL} */ +@d output_defs_flag (2*024000-1) /* |024000==(0250-0200)*0400| */ @ The following procedure is used to enter a two-byte value into |tok_mem| when a replacement text is being generated. @@ -276,7 +281,8 @@ typedef struct { } output_state; typedef output_state *stack_pointer; -@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */ +@ @d stack_size 50 /* number of simultaneous levels of macro expansion */ +@d cur_end cur_state.end_field /* current ending location in |tok_mem| */ @d cur_byte cur_state.byte_field /* location of next output byte in |tok_mem|*/ @d cur_name cur_state.name_field /* pointer to current name being expanded */ @d cur_repl cur_state.repl_field /* pointer to current replacement text */ @@ -308,8 +314,8 @@ We assume that the \CEE/ compiler can copy structures. @c static void -push_level(@t\1\1@> /* suspends the current level */ -name_pointer p@t\2\2@>) +push_level( /* suspends the current level */ +name_pointer p) { if (stack_ptr==stack_end) overflow("stack"); *stack_ptr=cur_state; @@ -331,8 +337,8 @@ text or returns the state to the most recently stacked level. @c static void -pop_level(@t\1\1@> /* do this when |cur_byte| reaches |cur_end| */ -boolean flag@t\2\2@>) /* |flag==false| means we are in |output_defs| */ +pop_level( /* do this when |cur_byte| reaches |cur_end| */ +boolean flag) /* |flag==false| means we are in |output_defs| */ { if (flag && cur_repl->text_link<section_flag) { /* link to a continuation */ cur_repl=cur_repl->text_link+text_info; /* stay on the same level */ @@ -350,7 +356,7 @@ necessary stacking and unstacking. It sends the value |section_number| if the next output begins or ends the replacement text of some section, in which case |cur_val| is that section's number (if beginning) or the negative of that value (if ending). (A section number of 0 indicates -not the beginning or ending of a section, but a \&{\#line} command.) +not the beginning or ending of a section, but a \#\&{line} command.) And it sends the value |identifier| if the next output is an identifier, in which case |cur_val| points to that identifier name. @@ -384,11 +390,12 @@ get_output(void) /* sends next token to |out_char| */ else { a=(a-0200)*0400+*cur_byte++; switch (a/024000) { /* |024000==(0250-0200)*0400| */ - case 0: cur_val=a; out_char(identifier); break; + case 0: cur_val=(int)a; out_char(identifier); break; case 1: if (a==output_defs_flag) output_defs(); else @<Expand section |a-024000|, |goto restart|@>@; break; - default: cur_val=a-050000; if (cur_val>0) cur_section=cur_val; + default: cur_val=(int)a-050000; + if (cur_val>0) cur_section=(sixteen_bits)cur_val; out_char(section_number); } } @@ -463,7 +470,7 @@ flush_buffer(void) /* writes one line to output file */ { C_putc('\n'); if (cur_line % 100 == 0 && show_progress) { - printf("."); + putchar('.'); if (cur_line % 500 == 0) printf("%d",cur_line); update_terminal; /* progress report */ } @@ -502,9 +509,7 @@ complain we're out of room@>= if (an_output_file==end_output_files) { if (cur_out_file>output_files) *--cur_out_file=cur_section_name; - else { - overflow("output files"); - } + else overflow("output files"); } } @@ -514,18 +519,21 @@ output. @c static void phase_two (void) { + phase=2; web_file_open=false; cur_line=1; @<Initialize the output stacks@>@; @<Output macro definitions if appropriate@>@; - if (text_info->text_link==0 && cur_out_file==end_output_files) { + if (text_info->text_link==macro && cur_out_file==end_output_files) { fputs("\n! No program text was specified.",stdout); mark_harmless; @.No program text...@> } else { - if(cur_out_file==end_output_files) { - if(show_progress) + if (cur_out_file==end_output_files) { + if (show_progress) { printf("\nWriting the output file (%s):",C_file_name); + update_terminal; + } } else { if (show_progress) { @@ -534,7 +542,7 @@ phase_two (void) { printf(" (%s)",C_file_name); update_terminal; } - if (text_info->text_link==0) goto writeloop; + if (text_info->text_link==macro) goto writeloop; } while (stack_ptr>stack) get_output(); flush_buffer(); @@ -557,14 +565,14 @@ for (an_output_file=end_output_files; an_output_file>cur_out_file;) { an_output_file--; sprint_section_name(output_file_name,*an_output_file); fclose(C_file); - C_file=fopen(output_file_name,"wb"); - if (C_file ==0) fatal("! Cannot open output file ",output_file_name); + if ((C_file=fopen(output_file_name,"wb"))==NULL) + fatal("! Cannot open output file ",output_file_name); @.Cannot open output file@> if (show_progress) { printf("\n(%s)",output_file_name); update_terminal; } cur_line=1; stack_ptr=stack+1; - cur_name= (*an_output_file); - cur_repl= (text_pointer)cur_name->equiv; + cur_name=*an_output_file; + cur_repl=(text_pointer)cur_name->equiv; cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; while (stack_ptr > stack) get_output(); @@ -586,14 +594,17 @@ static boolean output_defs_seen=false; static void output_defs(void);@/ static void out_char(eight_bits); -@ @c +@ @d C_printf(c,a) fprintf(C_file,c,a) +@d C_putc(c) putc((int)(c),C_file) /* isn't \CEE/ wonderfully consistent? */ + +@c static void output_defs(void) { sixteen_bits a; push_level(NULL); for (cur_text=text_info+1; cur_text<text_ptr; cur_text++) - if (cur_text->text_link==0) { /* |cur_text| is the text for a macro */ + if (cur_text->text_link==macro) { /* |cur_text| is the text for a |macro| */ cur_byte=cur_text->tok_start; cur_end=(cur_text+1)->tok_start; C_printf("%s","#define "); @@ -609,11 +620,12 @@ output_defs(void) else { a=(a-0200)*0400+*cur_byte++; if (a<024000) { /* |024000==(0250-0200)*0400| */ - cur_val=a; out_char(identifier); + cur_val=(int)a; out_char(identifier); } - else if (a<050000) { confusion("macro defs have strange char");} + else if (a<050000) confusion("macro defs have strange char"); else { - cur_val=a-050000; cur_section=cur_val; out_char(section_number); + cur_val=(int)a-050000; cur_section=(sixteen_bits)cur_val; + out_char(section_number); } /* no other cases */ } @@ -643,7 +655,7 @@ restart: @/@t\4@>@<Case of an identifier@>@; @/@t\4@>@<Case of a section number@>@; @/@t\4@>@<Cases like \.{!=}@>@; - case '=': case '>': C_putc(cur_char); C_putc(' '); + case '=': case '>': C_putc(cur_char);@+C_putc(' '); out_state=normal; break; case join: out_state=unbreakable; break; case constant: if (out_state==verbatim) { @@ -659,7 +671,7 @@ restart: } } -@ @<Cases like \.{!=}@>= +@ @<Cases like \.{!=}@>=@t\1\quad@> case plus_plus: C_putc('+');@+C_putc('+'); out_state=normal; break; case minus_minus: C_putc('-');@+C_putc('-'); out_state=normal; break; case minus_gt: C_putc('-');@+C_putc('>'); out_state=normal; break; @@ -697,41 +709,38 @@ static char translit[128][translit_length]; for (i=0;i<128;i++) sprintf(translit[i],"X%02X",(unsigned int)(128+i)); } -@ @<Case of an identifier@>= +@ @<Case of an identifier@>=@t\1\quad@> case identifier: if (out_state==num_or_id) C_putc(' '); - j=(cur_val+name_dir)->byte_start; - k=(cur_val+name_dir+1)->byte_start; - while (j<k) { + for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start; + j<k; j++) if ((eight_bits)(*j)<0200) C_putc(*j); @^high-bit character handling@> else C_printf("%s",translit[(eight_bits)(*j)-0200]); - j++; - } out_state=num_or_id; break; -@ @<Case of a sec...@>= +@ @<Case of a sec...@>=@t\1\quad@> case section_number: if (cur_val>0) C_printf("/*%d:*/",cur_val); else if(cur_val<0) C_printf("/*:%d*/",-cur_val); else if (protect) { cur_byte +=4; /* skip line number and file name */ - cur_char = '\n'; + cur_char = (eight_bits)'\n'; goto restart; } else { sixteen_bits a; - a=0400* *cur_byte++; + a=*cur_byte++ *0400; a+=*cur_byte++; /* gets the line number */ - C_printf("\n#line %d \"",a); + C_printf("\n#line %d \"",(int)a); @:line}{\.{\#line}@> - cur_val=*cur_byte++; - cur_val=0400*(cur_val-0200)+ *cur_byte++; /* points to the file name */ + cur_val=(int)(*cur_byte++-0200)*0400; + cur_val+=*cur_byte++; /* points to the file name */ for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start; j<k; j++) { if (*j=='\\' || *j=='"') C_putc('\\'); C_putc(*j); } - C_printf("%s","\"\n"); + C_putc('"');@+C_putc('\n'); } break; @@ -756,7 +765,7 @@ of these internal code numbers has been chosen to simplify the program logic; larger numbers are given to the control codes that denote more significant milestones. -@d ignore 0 /* control code of no interest to \.{CTANGLE} */ +@d ignore 00 /* control code of no interest to \.{CTANGLE} */ @d ord 0302 /* control code for `\.{@@'}' */ @d control_text 0303 /* control code for `\.{@@t}', `\.{@@\^}', etc. */ @d translit_code 0304 /* control code for `\.{@@l}' */ @@ -773,20 +782,20 @@ static eight_bits ccode[256]; /* meaning of a char following \.{@@} */ @ @<Set ini...@>= { int c; /* must be |int| so the |for| loop will end */ for (c=0; c<256; c++) ccode[c]=ignore; - ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] - =ccode['*']=new_section; - ccode['@@']='@@'; ccode['=']=string; - ccode['d']=ccode['D']=definition; - ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; - ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; - ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']= - ccode['q']=ccode['Q']=control_text; - ccode['h']=ccode['H']=output_defs_code; - ccode['l']=ccode['L']=translit_code; - ccode['&']=join; - ccode['<']=ccode['(']=section_name; - ccode['\'']=ord; } +ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] + =ccode['*']=new_section; +ccode['@@']=(eight_bits)'@@'; ccode['=']=string; +ccode['d']=ccode['D']=definition; +ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; +ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; +ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']= +ccode['q']=ccode['Q']=control_text; +ccode['h']=ccode['H']=output_defs_code; +ccode['l']=ccode['L']=translit_code; +ccode['&']=join; +ccode['<']=ccode['(']=section_name; +ccode['\'']=ord; @ The |skip_ahead| procedure reads through the input at fairly high speed until finding the next non-ignorable control code, which it returns. @@ -817,7 +826,7 @@ to the output. If the comment is introduced by \.{/*}, |skip_comment| proceeds until finding the end-comment token \.{*/} or a newline; in the latter case |skip_comment| will be called again by |get_next|, since the comment is not finished. This is done so that each newline in the -\CEE/ part of a section is copied to the output; otherwise the \&{\#line} +\CEE/ part of a section is copied to the output; otherwise the \#\&{line} commands inserted into the \CEE/ file by the output routines become useless. On the other hand, if the comment is introduced by \.{//} (i.e., if it is a \CPLUSPLUS/ ``short comment''), it always is simply delimited by the next @@ -831,15 +840,15 @@ No comment, long or short, is allowed to contain `\.{@@\ }' or `\.{@@*}'. static boolean comment_continues=false; /* are we scanning a comment? */ @ @c -static boolean skip_comment(@t\1\1@> /* skips over comments */ -boolean is_long_comment@t\2\2@>) +static boolean skip_comment( /* skips over comments */ +boolean is_long_comment) { char c; /* current character */ while (true) { if (loc>limit) { if (is_long_comment) { - if(get_line()) return comment_continues=true; - else{ + if (get_line()) return comment_continues=true; + else { err_print("! Input ended in mid-comment"); @.Input ended in mid-comment@> return comment_continues=false; @@ -864,8 +873,6 @@ boolean is_long_comment@t\2\2@>) @* Inputting the next token. -@d constant 03 - @<Private...@>= static name_pointer cur_section_name; /* name of section just scanned */ static boolean no_where; /* suppress |print_where|? */ @@ -873,32 +880,26 @@ static boolean no_where; /* suppress |print_where|? */ @ As one might expect, |get_next| consists mostly of a big switch that branches to the various special cases that can arise. -@d isxalpha(c) ((c)=='_' || (c)=='$') - /* non-alpha characters allowed in identifier */ -@d ishigh(c) ((eight_bits)(c)>0177) -@^high-bit character handling@> - @c static eight_bits get_next(void) /* produces the next input token */ { - static int preprocessing=0; + static boolean preprocessing=false; eight_bits c; /* the current character */ while (true) { if (loc>limit) { - if (preprocessing && *(limit-1)!='\\') preprocessing=0; + if (preprocessing && *(limit-1)!='\\') preprocessing=false; if (get_line()==false) return new_section; else if (print_where && !no_where) { print_where=false; @<Insert the line number into |tok_mem|@>@; } - else return '\n'; + else return (eight_bits)'\n'; } - c=*loc; + c=(eight_bits)*loc; if (comment_continues || (c=='/' && (*(loc+1)=='*' || *(loc+1)=='/'))) { - skip_comment(comment_continues||*(loc+1)=='*'); + if (skip_comment(comment_continues||*(loc+1)=='*')) return '\n'; /* scan to end of comment or newline */ - if (comment_continues) return '\n'; else continue; } loc++; @@ -907,15 +908,16 @@ get_next(void) /* produces the next input token */ || ((c=='L' || c=='u' || c=='U')&&(*loc=='\'' || *loc=='"'))@| || ((c=='u' && *loc=='8')&&(*(loc+1)=='\'' || *(loc+1)=='"'))) @<Get a string@>@; - else if (isalpha(c) || isxalpha(c) || ishigh(c)) + else if (isalpha((int)c) || isxalpha(c) || ishigh(c)) @<Get an identifier@>@; else if (c=='@@') @<Get control code and possible section name@>@; else if (xisspace(c)) { if (!preprocessing || loc>limit) continue; /* we don't want a blank after a final backslash */ - else return ' '; /* ignore spaces and tabs, unless preprocessing */ + else return (eight_bits)' '; + /* ignore spaces and tabs, unless |preprocessing| */ } - else if (c=='#' && loc==buffer+1) preprocessing=1; + else if (c=='#' && loc==buffer+1) preprocessing=true; mistake: @<Compress two-symbol operator@>@; return c; } @@ -924,30 +926,29 @@ get_next(void) /* produces the next input token */ @ @<Predecl...@>=@+static eight_bits get_next(void); @ The following code assigns values to the combinations \.{++}, -\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and -\.{\&\&}, and to the \CPLUSPLUS/ +\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, %\.{||} +\.{\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(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);} + 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; + 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);} + case '>': if (*loc=='=') {@+compress(gt_eq);@+} else if (*loc=='>') compress(gt_gt); break; - case '<': if (*loc=='=') {compress(lt_eq);} + 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; @@ -956,30 +957,41 @@ switch(c) { @ @<Get an identifier@>= { id_first=--loc; - while (isalpha((eight_bits)*++loc) || isdigit((eight_bits)*loc) @| - || isxalpha((eight_bits)*loc) || ishigh((eight_bits)*loc)); + do + ++loc; + while (isalpha((int)*loc) || isdigit((int)*loc) @| + || isxalpha(*loc) || ishigh(*loc)); id_loc=loc; return identifier; } @ @<Get a constant@>= { + boolean hex_flag = false; /* are we reading a hexadecimal literal? */ id_first=loc-1; if (*id_first=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */ if (*id_first=='0') { if (*loc=='x' || *loc=='X') { /* hex constant */ - loc++; while (xisxdigit(*loc)) loc++; goto found; + hex_flag = true; + loc++; while (xisxdigit(*loc) || *loc=='\'') loc++; + } + else if (*loc=='b' || *loc=='B') { /* binary constant */ + loc++; while (*loc=='0' || *loc=='1' || *loc=='\'') loc++; goto found; } } - while (xisdigit(*loc)) loc++; + while (xisdigit(*loc) || *loc=='\'') loc++; if (*loc=='.') { - loc++; - while (xisdigit(*loc)) loc++; + loc++; + while ((hex_flag && xisxdigit(*loc)) || xisdigit(*loc) || *loc=='\'') loc++; } if (*loc=='e' || *loc=='E') { /* float constant */ if (*++loc=='+' || *loc=='-') loc++; - while (xisdigit(*loc)) loc++; + while (xisdigit(*loc) || *loc=='\'') loc++; } - found: while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' - || *loc=='f' || *loc=='F') loc++; + else if (hex_flag && (*loc=='p' || *loc=='P')) { /* hex float constant */ + if (*++loc=='+' || *loc=='-') loc++; + while (xisxdigit(*loc) || *loc=='\'') loc++; + } +found: while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' + || *loc=='f' || *loc=='F') loc++; id_loc=loc; return constant; } @@ -990,11 +1002,11 @@ 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 */ + char delim = (char)c; /* what started the string */ id_first = section_text+1; id_loc = section_text; *++id_loc=delim; if (delim=='L' || delim=='u' || delim=='U') { /* wide character constant */ - if (delim=='u' && *loc=='8') { *++id_loc=*loc++; } + if (delim=='u' && *loc=='8') *++id_loc=*loc++; delim=*loc++; *++id_loc=delim; } while (true) { @@ -1010,16 +1022,16 @@ convention, but do not allow the string to be longer than |longest_name|. else if (++id_loc<=section_text_end) *id_loc='\n'; /* will print as \.{"\\\\\\n"} */ } - if ((c=*loc++)==delim) { - if (++id_loc<=section_text_end) *id_loc=c; + if ((c=(eight_bits)*loc++)==delim) { + if (++id_loc<=section_text_end) *id_loc=(char)c; break; } if (c=='\\') { if (loc>=limit) continue; if (++id_loc<=section_text_end) *id_loc = '\\'; - c=*loc++; + c=(eight_bits)*loc++; } - if (++id_loc<=section_text_end) *id_loc=c; + if (++id_loc<=section_text_end) *id_loc=(char)c; } if (id_loc>=section_text_end) { fputs("\n! String too long: ",stdout); @@ -1034,25 +1046,23 @@ convention, but do not allow the string to be longer than |longest_name|. @ After an \.{@@} sign has been scanned, the next character tells us whether there is more work to do. -@<Get control code and possible section name@>= { - c=ccode[(eight_bits)*loc++]; - switch(c) { - case ignore: continue; - case translit_code: err_print("! Use @@l in limbo only"); continue; +@<Get control code and possible section name@>= +switch(c=ccode[(eight_bits)*loc++]) { + case ignore: continue; + case translit_code: err_print("! Use @@l in limbo only"); continue; @.Use @@l in limbo...@> - case control_text: while ((c=skip_ahead())=='@@'); - /* only \.{@@@@} and \.{@@>} are expected */ - if (*(loc-1)!='>') - err_print("! Double @@ should be used in control text"); + case control_text: while ((c=skip_ahead())=='@@'); + /* only \.{@@@@} and \.{@@>} are expected */ + if (*(loc-1)!='>') + err_print("! Double @@ should be used in control text"); @.Double @@ should be used...@> - continue; - case section_name: - cur_section_name_char=*(loc-1); - @<Scan the section name and make |cur_section_name| point to it@>@; - case string: @<Scan a verbatim string@>@; - case ord: @<Scan an ASCII constant@>@; - default: return c; - } + continue; + case section_name: + cur_section_name_char=*(loc-1); + @<Scan the section name and make |cur_section_name| point to it@>@; + case string: @<Scan a verbatim string@>@; + case ord: @<Scan an ASCII constant@>@; + default: return c; } @ After scanning a valid ASCII constant that follows @@ -1063,9 +1073,8 @@ thus, \.{@@'\\nopq'} gives the same result as \.{@@'\\n'}. @<Scan an ASCII constant@>= id_first=loc; - if (*loc=='\\') { + if (*loc=='\\') if (*++loc=='\'') loc++; - } while (*loc!='\'') { if (*loc=='@@') { if (*(loc+1)!='@@') @@ -1083,11 +1092,13 @@ thus, \.{@@'\\nopq'} gives the same result as \.{@@'\\n'}. return ord; @ @<Scan the section name...@>= { - char *k; /* pointer into |section_text| */ + char *k=section_text; /* pointer into |section_text| */ @<Put section name into |section_text|@>@; if (k-section_text>3 && strncmp(k-2,"...",3)==0) - cur_section_name=section_lookup(section_text+1,k-3,1); /* 1 means is a prefix */ - else cur_section_name=section_lookup(section_text+1,k,0); + cur_section_name=section_lookup(section_text+1,k-3,true); + /* |true| means it's a prefix */ + else cur_section_name=section_lookup(section_text+1,k,false); + /* |false| means it's not */ if (cur_section_name_char=='(') @<If it's not there, add |cur_section_name| to the output file stack, or complain we're out of room@>@; @@ -1103,20 +1114,19 @@ 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; + c=(eight_bits)*loc; @<If end of name or erroneous nesting, |break|@>@; loc++; if (k<section_text_end) k++; if (xisspace(c)) { - c=' '; if (*(k-1)==' ') k--; + c=(eight_bits)' '; if (*(k-1)==' ') k--; } -*k=c; +*k=(char)c; } if (k>=section_text_end) { fputs("\n! Section name too long: ",stdout); @@ -1128,7 +1138,7 @@ if (*k==' ' && k>section_text) k--; @ @<If end of name or erroneous nesting,...@>= if (c=='@@') { - c=*(loc+1); + c=(eight_bits)*(loc+1); if (c=='>') { loc+=2; break; } @@ -1148,14 +1158,13 @@ have |*(loc-1)==string|; we set |id_first| to the beginning of the string itself, and |id_loc| to its ending-plus-one location in the buffer. We also set |loc| to the position just after the ending delimiter. -@<Scan a verbatim string@>= { - id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; - while (*loc!='@@' || *(loc+1)!='>') loc++; - if (loc>=limit) err_print("! Verbatim string didn't end"); +@<Scan a verbatim string@>= +id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; +while (*loc!='@@' || *(loc+1)!='>') loc++; +if (loc>=limit) err_print("! Verbatim string didn't end"); @.Verbatim string didn't end@> - id_loc=loc; loc+=2; - return string; -} +id_loc=loc; loc+=2; +return string; @* Scanning a macro definition. The rules for generating the replacement texts corresponding to macros and @@ -1176,8 +1185,10 @@ ANSI \CEE/ preprocessor sometimes requires it. acted, |cur_text| will point to the replacement text just generated, and |next_control| will contain the control code that terminated the activity. -@d macro 0 -@d app_repl(c) {if (tok_ptr==tok_mem_end) overflow("token"); *tok_ptr++=c;} +@d app_repl(c) { + if (tok_ptr==tok_mem_end) overflow("token"); + else *(tok_ptr++)=(eight_bits)c; +} @<Private...@>= static text_pointer cur_text; /* replacement text formed by |scan_repl| */ @@ -1185,13 +1196,13 @@ static eight_bits next_control; @ @c static void -scan_repl(@t\1\1@> /* creates a replacement text */ -eight_bits t@t\2\2@>) +scan_repl( /* creates a replacement text */ +eight_bits t) { sixteen_bits a; /* the current token */ - if (t==section_name) {@<Insert the line number into |tok_mem|@>@;} + if (t==section_name) @<Insert the line number into |tok_mem|@>@; while (true) switch (a=get_next()) { - @<In cases that |a| is a non-|char| token (|identifier|, + @/@t\4@>@<In cases that |a| is a non-|char| token (|identifier|, |section_name|, etc.), either process it and change |a| to a byte that should be stored, or |continue| if |a| should be ignored, or |goto done| if |a| signals the end of this replacement text@>@; @@ -1211,31 +1222,36 @@ eight_bits t@t\2\2@>) to |0150000|; then the numeric line number; then a pointer to the file name. +@d store_id(a) a=id_lookup(id_first,id_loc,'\0')-name_dir;@/ + app_repl((a / 0400)+0200); + app_repl(a % 0400) + @<Insert the line...@>= -store_two_bytes(0150000); -if (changing && include_depth==change_depth) { /* correction made Feb 2017 */ - id_first=change_file_name; - store_two_bytes((sixteen_bits)change_line); -}@+else { - id_first=cur_file_name; - store_two_bytes((sixteen_bits)cur_line); +{ + eight_bits a; /* shadow variable |a| */ + store_two_bytes(0150000); + if (changing && include_depth==change_depth) { /* correction made Feb 2017 */ + id_first=change_file_name; + store_two_bytes((sixteen_bits)change_line); + }@+else { + id_first=cur_file_name; + store_two_bytes((sixteen_bits)cur_line); + } + id_loc=id_first+strlen(id_first); + store_id(a); } -id_loc=id_first+strlen(id_first); -{int a_l=id_lookup(id_first,id_loc,0)-name_dir; app_repl((a_l / 0400)+0200); - app_repl(a_l % 0400);} -@ @<In cases that |a| is...@>= -case identifier: a=id_lookup(id_first,id_loc,0)-name_dir; - app_repl((a / 0400)+0200); - app_repl(a % 0400); break; +@ @<In cases that |a| is...@>=@t\1\quad@> +case identifier: store_id(a); break; case section_name: if (t!=section_name) goto done; else { @<Was an `\.{@@}' missed here?@>@; a=cur_section_name-name_dir; app_repl((a / 0400)+0250); app_repl(a % 0400); - @<Insert the line number into |tok_mem|@>@; break; + @<Insert the line number into |tok_mem|@>@; } + break; case output_defs_code: if (t!=section_name) err_print("! Misplaced @@h"); @.Misplaced @@h@> else { @@ -1245,11 +1261,13 @@ case output_defs_code: if (t!=section_name) err_print("! Misplaced @@h"); app_repl(a % 0400); @<Insert the line number into |tok_mem|@>@; } - break; + break; case constant: case string: @<Copy a string or verbatim construction or numerical constant@>@; + break; case ord: @<Copy an ASCII constant@>@; + break; case definition: case format_code: case begin_C: if (t!=section_name) goto done; else { err_print("! @@d, @@f and @@c are ignored in C text"); continue; @@ -1268,7 +1286,13 @@ case new_section: goto done; as explained in the manual */ } -@ @<Copy a string...@>= +@ By default, \.{CTANGLE} purges single-quote characters from \CPLUSPLUS/-style +literals, e.g., \.{1'000'000}, so that you can use this notation also in \CEE/ +code. The \.{+k} switch will `keep' the single quotes in the output. + +@d keep_digit_separators flags['k'] + +@<Copy a string...@>= app_repl(a); /* |string| or |constant| */ while (id_first < id_loc) { /* simplify \.{@@@@} pairs */ if (*id_first=='@@') { @@ -1276,18 +1300,20 @@ case new_section: goto done; else err_print("! Double @@ should be used in string"); @.Double @@ should be used...@> } + else if (a==constant && *id_first=='\'' && !keep_digit_separators) + id_first++; app_repl(*id_first++); } - app_repl(a); break; + app_repl(a); @ This section should be rewritten on machines that don't use ASCII code internally. @^ASCII code dependencies@> @<Copy an ASCII constant@>= { - int c=(eight_bits) *id_first; + int c=(int)((eight_bits) *id_first); if (c=='\\') { - c=*++id_first; + c=(int)((eight_bits) *++id_first); if (c>='0' && c<='7') { c-='0'; if (*(id_first+1)>='0' && *(id_first+1)<='7') { @@ -1306,15 +1332,15 @@ code internally. case 'a':c='\7';@+break; case '?':c='?';@+break; case 'x': - if (xisdigit(*(id_first+1))) c=*(++id_first)-'0'; + if (xisdigit(*(id_first+1))) c=(int)(*(++id_first)-'0'); else if (xisxdigit(*(id_first+1))) { ++id_first; - c=toupper((eight_bits)*id_first)-'A'+10; + c=toupper((int)*id_first)-'A'+10; } - if (xisdigit(*(id_first+1))) c=16*c+*(++id_first)-'0'; + if (xisdigit(*(id_first+1))) c=16*c+(int)(*(++id_first)-'0'); else if (xisxdigit(*(id_first+1))) { ++id_first; - c=16*c+toupper((eight_bits)*id_first)-'A'+10; + c=16*c+toupper((int)*id_first)-(int)'A'+10; } break; case '\\':c='\\';@+break; @@ -1326,12 +1352,11 @@ code internally. }@/ /* at this point |c| should have been converted to its ASCII code number */ app_repl(constant); - if (c>=100) app_repl('0'+c/100); - if (c>=10) app_repl('0'+(c/10)%10); - app_repl('0'+c%10); + if (c>=100) app_repl((int)'0'+c/100); + if (c>=10) app_repl((int)'0'+(c/10)%10); + app_repl((int)'0'+c%10); app_repl(constant); } -break; @* Scanning a section. The |scan_section| procedure starts when `\.{@@\ }' or `\.{@@*}' has been @@ -1352,17 +1377,17 @@ scan_section(void) sixteen_bits a; /* token for left-hand side of definition */ section_count++; @+ no_where=true; if (*(loc-1)=='*' && show_progress) { /* starred section */ - printf("*%d",section_count); update_terminal; + printf("*%d",(int)section_count); update_terminal; } - next_control=0; + next_control=ignore; while (true) { @<Skip ahead until |next_control| corresponds to \.{@@d}, \.{@@<}, \.{@@\ } or the like@>@; - if (next_control == definition) { /* \.{@@d} */ + if (next_control == definition) { /* \.{@@d} */ @<Scan a definition@>@; continue; } - if (next_control == begin_C) { /* \.{@@c} or \.{@@p} */ + if (next_control == begin_C) { /* \.{@@c} or \.{@@p} */ p=name_dir; break; } if (next_control == section_name) { /* \.{@@<} or \.{@@(} */ @@ -1391,22 +1416,19 @@ while (next_control<definition) loc-=2; next_control=get_next(); } -@ @<Scan a definition@>= { - while ((next_control=get_next())=='\n'); /* allow newline before definition */ - if (next_control!=identifier) { - err_print("! Definition flushed, must start with identifier"); +@ @<Scan a definition@>= +while ((next_control=get_next())=='\n'); /* allow newline before definition */ +if (next_control!=identifier) { + err_print("! Definition flushed, must start with identifier"); @.Definition flushed...@> - continue; - } - app_repl(((a=id_lookup(id_first,id_loc,0)-name_dir) / 0400)+0200); - /* append the lhs */ - app_repl(a % 0400); - if (*loc!='(') { /* identifier must be separated from replacement text */ - app_repl(string); app_repl(' '); app_repl(string); - } - scan_repl(macro); - cur_text->text_link=macro; + continue; } +store_id(a); /* append the lhs */ +if (*loc!='(') { /* identifier must be separated from replacement text */ + app_repl(string); app_repl(' '); app_repl(string); +} +scan_repl(macro); +cur_text->text_link=macro; @ If the section name is not followed by \.{=} or \.{+=}, no \CEE/ code is forthcoming: the section is being cited, not being @@ -1431,8 +1453,8 @@ store_two_bytes((sixteen_bits)(0150000+section_count)); /* |0150000==0320*0400| */ @ @<Update the data...@>= -if (p==name_dir||p==0) { /* unnamed section, or bad section name */ - (last_unnamed)->text_link=cur_text-text_info; last_unnamed=cur_text; +if (p==name_dir||p==NULL) { /* unnamed section, or bad section name */ + last_unnamed->text_link=cur_text-text_info; last_unnamed=cur_text; } else if (p->equiv==(void *)text_info) p->equiv=(void *)cur_text; /* first section of this name */ @@ -1454,7 +1476,6 @@ phase_one(void) { skip_limbo(); while (!input_has_ended) scan_section(); check_complete(); - phase=2; } @ @<Predec...@>=@+static void phase_one(void); @@ -1466,19 +1487,18 @@ processing is straightforward. static void skip_limbo(void) { - char c; while (true) { if (loc>limit && get_line()==false) return; *(limit+1)='@@'; while (*loc!='@@') loc++; if (loc++<=limit) { - c=*loc++; - if (ccode[(eight_bits)c]==new_section) break; + char c=*loc++; switch (ccode[(eight_bits)c]) { + case new_section: return; case translit_code: @<Read in transliteration of a character@>@; break; case format_code: case '@@': break; case control_text: if (c=='q' || c=='Q') { - while ((c=skip_ahead())=='@@'); + while ((c=(char)skip_ahead())=='@@'); if (*(loc-1)!='>') err_print("! Double @@ should be used in control text"); @.Double @@ should be used...@> @@ -1517,19 +1537,19 @@ skip_limbo(void) } @ Because on some systems the difference between two pointers is a |ptrdiff_t| -but not an |int|, we use \.{\%ld} to print these quantities. +but not an |int|, we use \.{\%td} to print these quantities. @c void print_stats(void) { puts("\nMemory usage statistics:"); - printf("%ld names (out of %ld)\n", + printf("%td names (out of %ld)\n", (ptrdiff_t)(name_ptr-name_dir),(long)max_names); - printf("%ld replacement texts (out of %ld)\n", + printf("%td replacement texts (out of %ld)\n", (ptrdiff_t)(text_ptr-text_info),(long)max_texts); - printf("%ld bytes (out of %ld)\n", + printf("%td bytes (out of %ld)\n", (ptrdiff_t)(byte_ptr-byte_mem),(long)max_bytes); - printf("%ld tokens (out of %ld)\n", + printf("%td tokens (out of %ld)\n", (ptrdiff_t)(tok_ptr-tok_mem),(long)max_toks); } |