📄 ctangle.w
字号:
@cvoidget_output() /* sends next token to |out_char| */{ sixteen_bits a; /* value of current byte */ restart: if (stack_ptr==stack) return; if (cur_byte==cur_end) { cur_val=-((int)cur_section); /* cast needed because of sign extension */ pop_level(1); if (cur_val==0) goto restart; out_char(section_number); return; } a=*cur_byte++; if (out_state==verbatim && a!=string && a!=constant && a!='\n') C_putc(a); /* a high-bit character can occur in a string */ else if (a<0200) out_char(a); /* one-byte token */ else { a=(a-0200)*0400+*cur_byte++; switch (a/024000) { /* |024000==(0250-0200)*0400| */ case 0: cur_val=a; out_char(identifier); break; case 1: if (a==output_defs_flag) output_defs(); else @<Expand section |a-024000|, |goto restart|@>; break; default: cur_val=a-050000; if (cur_val>0) cur_section=cur_val; out_char(section_number); } }}@ The user may have forgotten to give any \CEE/ text for a section name,or the \CEE/ text may have been associated with a different name by mistake.@<Expand section |a-...@>={ a-=024000; if ((a+name_dir)->equiv!=(char *)text_info) push_level(a+name_dir); else if (a!=0) { printf("\n! Not present: <"); print_section_name(a+name_dir); err_print(">");@.Not present: <section name>@> } goto restart;}@* Producing the output.The |get_output| routine above handles most of the complexity of outputgeneration, but there are two further considerations that have a nontrivialeffect on \.{CTANGLE}'s algorithms.@ First,we want to make sure that the output has spaces and line breaks inthe right places (e.g., not in the middle of a string or a constant or anidentifier, not at a `\.{@@\&}' positionwhere quantities are being joined together, and certainly after an \.=because the \CEE/ compiler thinks \.{=-} is ambiguous).The output process can be in one of following states:\yskip\hang |num_or_id| means that the last item in the buffer is a number oridentifier, hence a blank space or line break must be inserted if the nextitem is also a number or identifier.\yskip\hang |unbreakable| means that the last item in the buffer was followedby the \.{@@\&} operation that inhibits spaces between it and the next item.\yskip\hang |verbatim| means we're copying only character tokens, andthat they are to be output exactly as stored. This is the case duringstrings, verbatim constructions and numerical constants.\yskip\hang |post_slash| means we've just output a slash.\yskip\hang |normal| means none of the above.\yskip\noindent Furthermore, if the variable |protect| is positive, newlinesare preceded by a `\.\\'.@d normal 0 /* non-unusual state */@d num_or_id 1 /* state associated with numbers and identifiers */@d post_slash 2 /* state following a \./ */@d unbreakable 3 /* state associated with \.{@@\&} */@d verbatim 4 /* state in the middle of a string */@<Global...@>=eight_bits out_state; /* current status of partial output */boolean protect; /* should newline characters be quoted? */@ Here is a routine that is invoked when we want to output the current line.During the output process, |cur_line| equals the number of the next lineto be output.@cvoidflush_buffer() /* writes one line to output file */{ C_putc('\n'); if (cur_line % 100 == 0 && show_progress) { printf("."); if (cur_line % 500 == 0) printf("%d",cur_line); update_terminal; /* progress report */ } cur_line++;}@ Second, we have modified the original \.{TANGLE} so that it will write outputon multiple files.If a section name is introduced in at least one place by \.{@@(}instead of \.{@@<}, we treat it as the name of a file.All these special sections are saved on a stack, |output_files|.We write them out after we've done the unnamed section.@d max_files 256@<Glob...@>=name_pointer output_files[max_files];name_pointer *cur_out_file, *end_output_files, *an_output_file;char cur_section_name_char; /* is it |'<'| or |'('| */char output_file_name[longest_name]; /* name of the file */@ We make |end_output_files| point just beyond the end of|output_files|. The stack pointer|cur_out_file| starts out there. Every time we see a new file, wedecrement |cur_out_file| and then write it in.@<Set initial...@>=cur_out_file=end_output_files=output_files+max_files;@ @<If it's not there, add |cur_section_name| to the output file stack, orcomplain we're out of room@>={ for (an_output_file=cur_out_file; an_output_file<end_output_files; an_output_file++) if (*an_output_file==cur_section_name) break; if (an_output_file==end_output_files) { if (cur_out_file>output_files) *--cur_out_file=cur_section_name; else { overflow("output files"); } }}@* The big output switch. Here then is the routine that does theoutput.@<Predecl...@>=void phase_two();@ @cvoidphase_two () { web_file_open=0; cur_line=1; @<Initialize the output stacks@>; @<Output macro definitions if appropriate@>; if (text_info->text_link==0 && cur_out_file==end_output_files) { printf("\n! No program text was specified."); mark_harmless;@.No program text...@> } else { if(cur_out_file==end_output_files) { if(show_progress) printf("\nWriting the output file (%s):",C_file_name); } else { if (show_progress) { printf("\nWriting the output files:");@.Writing the output...@> printf(" (%s)",C_file_name); update_terminal; } if (text_info->text_link==0) goto writeloop; } while (stack_ptr>stack) get_output(); flush_buffer();writeloop: @<Write all the named output files@>; if(show_happiness) printf("\nDone."); }}@ To write the named output files, we proceed as for the unnamedsection.The only subtlety is that we have to open each one.@<Write all the named output files@>=for (an_output_file=end_output_files; an_output_file>cur_out_file;) { an_output_file--; sprint_section_name(output_file_name,*an_output_file); fclose(C_file); C_file=fopen(output_file_name,"w"); if (C_file ==0) fatal("! Cannot open output file:",output_file_name);@.Cannot open output file@> printf("\n(%s)",output_file_name); update_terminal; cur_line=1; stack_ptr=stack+1; cur_name= (*an_output_file); cur_repl= (text_pointer)cur_name->equiv; cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; while (stack_ptr > stack) get_output(); flush_buffer();}@ If a \.{@@h} was not encountered in the input,we go through the list of replacement texts and copy the onesthat refer to macros, preceded by the \.{\#define} preprocessor command.@<Output macro definitions if appropriate@>= if (!output_defs_seen) output_defs();@ @<Glob...@>=boolean output_defs_seen=0;@ @<Predecl...@>=void output_defs();@ @cvoidoutput_defs(){ sixteen_bits a; push_level(NULL); for (cur_text=text_info+1; cur_text<text_ptr; cur_text++) if (cur_text->text_link==0) { /* |cur_text| is the text for a macro */ cur_byte=cur_text->tok_start; cur_end=(cur_text+1)->tok_start; C_printf("%s","#define "); out_state=normal; protect=1; /* newlines should be preceded by |'\\'| */ while (cur_byte<cur_end) { a=*cur_byte++; if (cur_byte==cur_end && a=='\n') break; /* disregard a final newline */ if (out_state==verbatim && a!=string && a!=constant && a!='\n') C_putc(a); /* a high-bit character can occur in a string */@^high-bit character handling@> else if (a<0200) out_char(a); /* one-byte token */ else { a=(a-0200)*0400+*cur_byte++; if (a<024000) { /* |024000==(0250-0200)*0400| */ cur_val=a; out_char(identifier); } else if (a<050000) { confusion("macro defs have strange char");} else { cur_val=a-050000; cur_section=cur_val; out_char(section_number); } /* no other cases */ } } protect=0; flush_buffer(); } pop_level(0);}@ A many-way switch is used to send the output. Note that this functionis not called if |out_state==verbatim|, except perhaps with arguments|'\n'| (protect the newline), |string| (end the string), or |constant|(end the constant).@<Predecl...@>=static void out_char();@ @cstatic voidout_char(cur_char)eight_bits cur_char;{ char *j, *k; /* pointer into |byte_mem| */restart: switch (cur_char) { case '\n': if (protect && out_state!=verbatim) C_putc(' '); if (protect || out_state==verbatim) C_putc('\\'); flush_buffer(); if (out_state!=verbatim) out_state=normal; break; @/@t\4@>@<Case of an identifier@>; @/@t\4@>@<Case of a section number@>; @/@t\4@>@<Cases like \.{!=}@>; case '=': case '>': C_putc(cur_char); C_putc(' '); out_state=normal; break; case join: out_state=unbreakable; break; case constant: if (out_state==verbatim) { out_state=num_or_id; break; } if(out_state==num_or_id) C_putc(' '); out_state=verbatim; break; case string: if (out_state==verbatim) out_state=normal; else out_state=verbatim; break; case '/': C_putc('/'); out_state=post_slash; break; case '*': if (out_state==post_slash) C_putc(' '); /* fall through */ default: C_putc(cur_char); out_state=normal; break; }}@ @<Cases like \.{!=}@>=case plus_plus: C_putc('+'); C_putc('+'); out_state=normal; break;case minus_minus: C_putc('-'); C_putc('-'); out_state=normal; break;case minus_gt: C_putc('-'); C_putc('>'); out_state=normal; break;case gt_gt: C_putc('>'); C_putc('>'); out_state=normal; break;case eq_eq: C_putc('='); C_putc('='); out_state=normal; break;case lt_lt: C_putc('<'); C_putc('<'); out_state=normal; break;case gt_eq: C_putc('>'); C_putc('='); out_state=normal; break;case lt_eq: C_putc('<'); C_putc('='); out_state=normal; break;case not_eq: C_putc('!'); C_putc('='); out_state=normal; break;case and_and: C_putc('&'); C_putc('&'); out_state=normal; break;case or_or: C_putc('|'); C_putc('|'); out_state=normal; break;case dot_dot_dot: C_putc('.'); C_putc('.'); C_putc('.'); out_state=normal; break;case colon_colon: C_putc(':'); C_putc(':'); out_state=normal; break;case period_ast: C_putc('.'); C_putc('*'); out_state=normal; break;case minus_gt_ast: C_putc('-'); C_putc('>'); C_putc('*'); out_state=normal; break;@ When an identifier is output to the \CEE/ file, characters in therange 128--255 must be changed into something else, so the \CEE/compiler won't complain. By default, \.{CTANGLE} converts thecharacter with code $16 x+y$ to the three characters `\.X$xy$', buta different transliteration table can be specified. Thus a Germanmight want {\it gr\"un\/} to appear as a still readable \.{gruen}.This makes debugging a lot less confusing.@d translit_length 10@<Glo...@>=char translit[128][translit_length];@ @<Set init...@>={ int i; for (i=0;i<128;i++) sprintf(translit[i],"X%02X",(unsigned)(128+i));}@ @<Case of an identifier@>=case identifier: if (out_state==num_or_id) C_putc(' '); j=(cur_val+name_dir)->byte_start; k=(cur_val+name_dir+1)->byte_start; while (j<k) { if ((unsigned char)(*j)<0200) C_putc(*j);@^high-bit character handling@> else C_printf("%s",translit[(unsigned char)(*j)-0200]); j++; } out_state=num_or_id; break;@ @<Case of a sec...@>=case section_number: if (cur_val>0) C_printf("/*%d:*/",cur_val); else if(cur_val<0) C_printf("/*:%d*/",-cur_val); else if (protect) { cur_byte +=4; /* skip line number and file name */ cur_char = '\n'; goto restart; } else { sixteen_bits a; a=0400* *cur_byte++; a+=*cur_byte++; /* gets the line number */ C_printf("\n#line %d \"",a);@:line}{\.{\#line}@> cur_val=*cur_byte++; cur_val=0400*(cur_val-0200)+ *cur_byte++; /* points to the file name */ for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start; j<k; j++) { if (*j=='\\' || *j=='"') C_putc('\\'); C_putc(*j); } C_printf("%s","\"\n"); } break;@** Introduction to the input phase.We have now seen that \.{CTANGLE} will be able to output the full\CEE/ program, if we can only get that program into the byte memory inthe proper format. The input process is something like the output processin reverse, since we compress the text as we read it in and we expand itas we write it out.There are three main input routines. The most interesting is the one that getsthe next token of a \CEE/ text; the other two are used to scan rapidly past\TEX/ text in the \.{CWEB} source code. One of the latter routines will jump tothe next token that starts with `\.{@@}', and the other skips to the endof a \CEE/ comment.@ Control codes in \.{CWEB} begin with `\.{@@}', and the next characteridentifies the code. Some of these are of interest only to \.{CWEAVE},so \.{CTANGLE} ignores them; the others are converted by \.{CTANGLE} intointernal code numbers by the |ccode| table below. The orderingof these internal code numbers has been chosen to simplify the program logic;larger numbers are given to the control codes that denote more significantmilestones.@d ignore 0 /* control code of no interest to \.{CTANGLE} */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -