home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / fweb153.zip / fweb-1.53 / web / ratfor.web < prev    next >
Text File  |  1995-09-23  |  71KB  |  3,353 lines

  1. @z --- ratfor.web ---
  2.  
  3. FWEB version 1.53 (September 23, 1995)
  4.  
  5. Based on version 0.5 of S. Levy's CWEB [copyright (C) 1987 Princeton University]
  6.  
  7. @x-----------------------------------------------------------------------------
  8.  
  9.  
  10. \Title{RATFOR.WEB} % Ratfor statement translation for FTANGLE.
  11.  
  12. @c
  13. @* RATFOR. Here we endow \FTANGLE\ with a \RATFOR-like syntax that will
  14. be expanded directly into \Fortran\ code. This processing will work
  15. during the output phase involving |get_output()| and |out_char()|.
  16.  
  17. This code is recent; the initial goal was to achieve functionality. Some
  18. improvements are obvious. In particular, the \RATFOR\ tokens and associated
  19. functions should be better integrated into the list of all tokens, instead 
  20. of being held in a separate list. This improvement will be made in future
  21. versions. 
  22.  
  23. @m _RATFOR_
  24. @d _RATFOR_h
  25. @d _ratfor_ /* Used in \.{r\_type.web}. */
  26.  
  27. @A
  28. @<Possibly split into parts@>@;
  29.  
  30. @<Include files@>@;
  31. @<Typedef declarations@>@;
  32. @<Prototypes@>@;
  33. @<Global variables@>@;
  34.  
  35. /* For pc's, the file is split into two compilable parts using the
  36. compiler-line macro |part|, which must equal either~1 or~2. */
  37. #if(part != 2)
  38.     @<Part 1@>@;
  39. #endif /* Part 1 */
  40.  
  41. #if(part != 1)
  42.     @<Part 2@>@;
  43. #endif /* Part 2 */
  44.  
  45. @I typedefs.hweb /* Declarations common to both \FTANGLE\ and \FWEAVE. */
  46.  
  47. @I t_codes.hweb /* Definitions of some constants. */
  48. @I texts.hweb 
  49. @I stacks.hweb
  50. @I val.hweb
  51. @I macs.hweb
  52. @I trunc.hweb
  53.  
  54. @
  55. @<Include...@>=
  56. #include "map.h"
  57.  
  58. @ The function prototypes must appear before the global variables.
  59. @<Proto...@>=
  60.  
  61. #include "t_type.h" /* Function prototypes for everything. */
  62.  
  63. @ We need to declare variables defined in \FTANGLE.
  64.  
  65. @d UNNAMED_MODULE 0
  66. @d N_IDBUF 100
  67.  
  68. @<Glob...@>=
  69.  
  70. EXTERN boolean mac_protected,in_string; 
  71. EXTERN text_pointer macro_text;
  72. EXTERN long cur_val;
  73. EXTERN OUTPUT_STATE out_state;
  74. EXTERN int indent_level,out_pos,rst_pos,indnt_size;
  75. EXTERN eight_bits sent;
  76.  
  77. IN_COMMON STMT_LBL max_stmt;
  78. IN_COMMON sixteen_bits outp_line[];
  79.  
  80. @ We need to know whether this whole package has been linked on.
  81.  
  82. @<Part 1@>=@[
  83.  
  84. SRTN 
  85. is_Rat_present(VOID)
  86. {
  87. Rat_is_loaded = YES;
  88. }
  89.  
  90. @
  91. @<Part 1@>=@[
  92.  
  93. boolean 
  94. Rat_OK FCN((msg))
  95.     outer_char *msg C1("")@;
  96. {
  97. return YES;
  98. }
  99.  
  100. @ Here are the various special tokens:
  101.  
  102. @<Global variables@>=
  103.  
  104. /* Expandable input tokens. */
  105. IN_RATFOR sixteen_bits
  106.  id_block, id_blockdata, id_break, 
  107.  id_case, 
  108. #if(0)
  109.  id_continue,
  110. #endif
  111.  id_default, id_do,  
  112.  id_else, id_elseif, id_end,
  113.  id_endif, 
  114.  id_for,
  115. #if(0)
  116.  id_goto,
  117. #endif
  118.  id_if, 
  119.  id_next, id_procedure, id_repeat,
  120.  id_return, id_switch, id_then, id_until, 
  121.  id_while;
  122.  
  123. IN_RATFOR sixteen_bits id_function,id_program,id_subroutine;
  124.  
  125. IN_RATFOR sixteen_bits
  126.  id_contains, id_elsewhere, id_endinterface, id_endtype, id_endmodule,
  127.  id_endselect, id_endwhere, id_interface, id_module,  id_type, id_where;
  128.  
  129. /* Non-expandable input tokens. */
  130. IN_RATFOR sixteen_bits id_data;
  131.  
  132. /* Output tokens. */
  133. IN_RATFOR sixteen_bits
  134.   id__CASE,id__CONTINUE,id__DEFAULT,
  135.     id__DO,id__ELSE,id__ELSEIF,id__END,
  136.     id__ENDIF,id__EXIT,id__GOTO,id__IF,
  137.     id__RETURN,id__THEN,
  138.     id__WHILE;
  139.  
  140. /* More output tokens for \Fortran--90. */
  141. IN_RATFOR sixteen_bits
  142.   id__CONTAINS,id__CYCLE,id__ENDWHERE,id__INTERFACE,id__MODULE,
  143.   id__SELECT,id__TYPE,id__WHERE;
  144.  
  145. @% static sixteen_bits id__ELSEWHERE; // This must be worked on.
  146.  
  147. /* The following tokens are printed as the result of \Ratfor\ translation.
  148. (The lengths are filled in by |ini_out_tokens|.) */
  149. IN_RATFOR SPEC out_tokens[]
  150. #if(part == 0 || part == 1)
  151.      = {
  152.     {"CASE",0,NULL,&id__CASE},
  153.     {"CONTINUE",0,NULL,&id__CONTINUE},
  154.     {"DEFAULT",0,NULL,&id__DEFAULT},
  155.     {"DO",0,NULL,&id__DO},
  156.     {"ELSE",0,NULL,&id__ELSE},
  157.     {"ELSEIF",0,NULL,&id__ELSEIF},
  158.     {"END",0,NULL,&id__END},
  159.     {"ENDIF",0,NULL,&id__ENDIF},
  160.     {"EXIT",0,NULL,&id__EXIT},
  161.     {"GOTO",0,NULL,&id__GOTO},
  162.     {"IF",0,NULL,&id__IF},
  163.     {"RETURN",0,NULL,&id__RETURN},
  164.     {"THEN",0,NULL,&id__THEN},
  165.     {"WHILE",0,NULL,&id__WHILE},
  166.     {"",0,NULL,NULL}
  167.     }
  168. #endif
  169.     ;
  170.  
  171. IN_RATFOR SPEC out90_tokens[]
  172. #if(part == 0 || part == 1)
  173.      = {
  174.     {"CONTAINS",0,NULL,&id__CONTAINS},
  175.     {"CYCLE",0,NULL,&id__CYCLE},
  176.     {"ENDWHERE",0,NULL,&id__ENDWHERE},
  177.     {"INTERFACE",0,NULL,&id__INTERFACE},
  178.     {"MODULE",0,NULL,&id__MODULE},
  179.     {"SELECT",0,NULL,&id__SELECT},
  180.     {"TYPE",0,NULL,&id__TYPE},
  181.     {"WHERE",0,NULL,&id__WHERE},
  182.     {"",0,NULL,NULL}
  183.     }
  184. #endif
  185.     ;
  186.  
  187. /* The following is used during \FORTRAN-88\ |case| expansion to see
  188. whether the last |case| ended with |break|. */
  189. eight_bits break_tokens[3];
  190.  
  191. /* These are the special \Ratfor\ tokens that are expanded. */
  192. IN_RATFOR SPEC spec_tokens[]
  193. #if(part == 0 || part == 1)
  194.      = {
  195.     {"block",0,x_block,&id_block},
  196.     {"blockdata",0,x_blockdata,&id_blockdata},
  197.     {"break",0,x_break,&id_break},
  198.     {"case",0,(X_FCN (*@e)(VOID))x_case,&id_case},
  199.     {"default",0,(X_FCN (*@e)(VOID))x_default,&id_default},
  200.     {"do",0,x_do,&id_do},
  201.     {"else",0,x_else,&id_else},
  202.     {"elseif",0,x_els_if,&id_elseif},
  203.     {"end",0,x_end,&id_end},
  204.     {"endif",0,x_en_if,&id_endif},
  205.     {"for",0,x_for,&id_for},
  206.     {"function",0,x_function,&id_function},
  207.     {"if",0,x_if,&id_if},
  208.     {"next",0,x_next,&id_next},
  209.     {"procedure",0,x_procedure,&id_procedure},
  210.     {"program",0,x_program,&id_program},
  211.     {"repeat",0,x_repeat,&id_repeat},
  212.     {"return",0,x_return,&id_return},
  213.     {"switch",0,x_switch,&id_switch},
  214.     {"subroutine",0,x_subroutine,&id_subroutine},
  215.     {"then",0,x_then,&id_then},
  216.     {"until",0,x_until,&id_until},
  217.     {"while",0,x_while,&id_while},
  218.     {"",0,NULL,NULL}
  219.     }
  220. #endif
  221.     ;
  222.  
  223. /* \Fortran--90. */
  224. IN_RATFOR SPEC spec90_tokens[]
  225. #if(part == 0 || part == 1)
  226.      = {
  227.     {"contains",0,x_contains,&id_contains},
  228.     {"endinterface",0,x_en_interface,&id_endinterface},
  229.     {"endmodule",0,x_en_module,&id_endmodule},
  230.     {"endselect",0,x_en_select,&id_endselect},
  231.     {"endtype",0,x_en_type,&id_endtype},
  232.     {"endwhere",0,x_en_where,&id_endwhere},
  233.     {"interface",0,x_interface,&id_interface},
  234.     {"module",0,x_module,&id_module},
  235.     {"type",0,x_type,&id_type},
  236.     {"where",0,x_where,&id_where},
  237.     {"",0,NULL,NULL}
  238.     }
  239. #endif
  240.     ;
  241.  
  242. @ Interface to \.{reserved}; initialize the special \Ratfor\ tables. 
  243.  
  244. @<Part 1@>=@[
  245.  
  246. SRTN 
  247. ini_RAT_tokens FCN((language0))
  248.     LANGUAGE language0 C1("")@;
  249. {
  250. switch(language0)
  251.     {
  252.    case RATFOR_90:
  253.     ini_special_tokens(language0,spec90_tokens);
  254.     ini_out_tokens(out90_tokens); 
  255.  
  256. /* The previous case falls through to here! */
  257.    case RATFOR:
  258.     ini_special_tokens(language0,spec_tokens);// Initialize special tokens.
  259.     ini_out_tokens(out_tokens); //  Printed during Ratfor expansion.
  260.     break;
  261.  
  262.    default:
  263.     CONFUSION("ini_RAT_tokens","Language should be RATFOR-like here");
  264.     }
  265.  
  266. ini_univ_tokens(language0);
  267. @<Store miscellaneous tokens@>@;
  268. }
  269.  
  270. @
  271. @<Store miscellaneous tokens@>=
  272. {
  273. ASCII HUGE *pd;
  274.  
  275. /* Store the phrase ``|break;|''. */
  276. break_tokens[0] = LEFT(id_break,ID0);
  277. break_tokens[1] = RIGHT(id_break);
  278. break_tokens[2] = @';';
  279.  
  280. pd = x_to_ASCII(OC("data"));
  281. id_data = ID_NUM(pd,pd+4);
  282. }
  283.  
  284. @ Here is another interface to \FTANGLE.  In \Fortran--90, certain loops
  285. can be preceded by symbolic labels.  \Ratfor--90 checks for that, and if
  286. they're present will label the ends of the loops with those labels.
  287. @<Glob...@>=
  288.  
  289. IN_RATFOR sixteen_bits sym_label RSET(0);
  290.  
  291. @ This function is called from \FTANGLE.  It considers whether the token in
  292. |cur_val| is a label; it returns one of three values:
  293. $$\vbox{\halign{#\hfil&\ ---\ \vtop{\hsize0.75\hsize\noindent\hang\strut
  294.     #\strut}\hfil\cr
  295. |NO|&The identifier isn't followed by a colon, so it's not a label.\cr
  296. |-1|&It's a label, but doesn't label a special \Ratfor\ token.\cr
  297. |YES|&It's a label on a special \Ratfor\ token such as |@r9 where|.\cr
  298. }}$$
  299.  
  300. @<Part 1@>=@[
  301.  
  302. int 
  303. chk_lbl(VOID)
  304. {
  305. sixteen_bits a;
  306.  
  307. if(next_byte() == @':')
  308.     {
  309.     sym_label = (sixteen_bits)cur_val; // Remember symbolic label.
  310.  
  311.     if(TOKEN1(a=next_byte())) BACK_UP@;
  312.     else 
  313.         { // Labelled identifier.
  314.         a = IDENTIFIER(a,next_byte()); // Labelled token.
  315.  
  316.         if(name_dir[a].expandable)
  317.             { // It's a labelled \Ratfor\ token.
  318.             cur_val = a;
  319.             return YES;
  320.             }
  321.         else
  322.             { // Nothing special about this label; spit it out.
  323.             BACK_UP@;
  324.             cur_val = sym_label;
  325.             sym_label = ignore;
  326.  
  327.             checking_label = YES;
  328.                 out_char(identifier);
  329.             checking_label = NO;
  330.  
  331.             return -1;
  332.             }
  333.         }
  334.     }
  335.  
  336. // The identifier isn't followed by a colon, so it isn't a label.
  337. sym_label = ignore;
  338. BACK_UP@;
  339. return NO;
  340. }
  341.  
  342. @* ERROR MESSAGES.
  343. The Ratfor routines issue special error messages. They employ ANSI's
  344. variable argument conventions. This may be a sticky point for users of
  345. pre-ANSI compilers.
  346.  
  347. @d fatal_RAT_ERROR(s1,s2,s3) {RAT_ERROR(ERROR,s1,0); FATAL(R, s2,s3);}
  348.  
  349. @<Part 1@>=@[
  350.  
  351. SRTN 
  352. RAT_error FCN(VA_ALIST((err_type,msg,n VA_ARGS)))
  353.     VA_DCL(
  354.     ERR_TYPE err_type C0("Is it warning or error?")@;
  355.     CONST outer_char msg[] C0("Error message.")@;
  356.     int n C2("Number of arguments to follow.")@;)@;
  357. {
  358. VA_LIST(arg_ptr)@;
  359. outer_char HUGE *temp, HUGE *temp1;
  360. int last_level;
  361. #if(NUM_VA_ARGS == 1)
  362.     ERR_TYPE err_type;
  363.     CONST outer_char *msg;
  364.     int n;
  365. #endif
  366.  
  367. temp = GET_MEM("RAT_error:temp",N_MSGBUF,outer_char);
  368. temp1 = GET_MEM("RAT_error:temp1",N_MSGBUF,outer_char);
  369.  
  370. VA_START(arg_ptr,n);
  371.  
  372. #if(NUM_VA_ARGS == 1)
  373.     err_type = va_arg(arg_ptr,ERR_TYPE);
  374.     msg = va_arg(arg_ptr,char *);
  375.     va_arg(arg_ptr,int);
  376. #endif
  377.  
  378. vsprintf((char *)temp1,(CONST char *)msg,arg_ptr);
  379. va_end(arg_ptr);
  380.  
  381. SPRINTF(N_MSGBUF,temp,`"RATFOR %s (Output l. %u in %s):  %s.",
  382.     err_type == ERROR ? "ERROR" : "WARNING",
  383.     OUTPUT_LINE,params.OUTPUT_FILE_NAME,temp1`);
  384.  
  385. last_level = MAX(rlevel-1,0);
  386.  
  387. SPRINTF(N_MSGBUF,temp1,
  388.     `"%s  Expanding \"%s\" (loop level %d) beginning at output line %u.  \
  389. In \"%s %s\" beginning at line %u.",
  390.     (char *)temp,
  391.     (char *)cmd_name(begun[last_level].cmd),
  392.     begun[last_level].level, begun[last_level].line,
  393.     (char *)cmd_name(begun[0].cmd),
  394.     (char *)name_of(begun[0].name),
  395.     begun[0].line`);
  396.  
  397. printf("\n%s\n", (char *)temp1);     // Error msg to the terminal.
  398. OUT_MSG(to_ASCII(temp1),NULL); // Error msg to the file.
  399.  
  400. mark_error;
  401.  
  402. FREE_MEM(temp,"RAT_error:temp",N_MSGBUF,char);
  403. FREE_MEM(temp1,"RAT_error:temp1",N_MSGBUF,char);
  404. }
  405.  
  406. @ Various checks are made for premature end-of-file. The next routine
  407. prints an appropriate error message, then aborts.
  408.  
  409. @m OUTPUT_ENDED(msg,n,...) 
  410.     output_ended(OC(msg),n,#.)
  411.  
  412. @<Part 1@>=@[
  413.  
  414. SRTN 
  415. output_ended FCN(VA_ALIST((msg,n VA_ARGS)))
  416.     VA_DCL(
  417.     CONST outer_char msg[] C0("Error message.")@;
  418.     int n C2("Number of arguments to follow.")@;)@;
  419. {
  420. VA_LIST(arg_ptr)@;
  421. char HUGE *temp;
  422.  
  423. temp = GET_MEM("output_ended:temp",N_MSGBUF,char);
  424.  
  425. VA_START(arg_ptr,n);
  426. vsprintf_(temp,(CONST char *)msg,arg_ptr)@;
  427. va_end(arg_ptr);
  428.  
  429. RAT_ERROR(ERROR,"Output ended %s",1,temp);
  430. FATAL(R, "ABORTING!","");
  431. }
  432.  
  433. @ For the error messages, we need to translate between the |CMD| type and
  434. the actual name.
  435.  
  436. @m TC(name) case name##_CMD: return OC(#name)
  437.  
  438. @<Part 1@>=@[
  439.  
  440. outer_char HUGE *
  441. cmd_name FCN((cmd))
  442.     CMD cmd C1("Type of command.")@;
  443. {
  444. switch(cmd)
  445.     {
  446.    case _DO_CMD:
  447.     return OC("$DO");
  448.  
  449.     TC(blockdata);
  450.     TC(break);
  451.     TC(case);
  452.     TC(contains);
  453.     TC(default);
  454.     TC(do);
  455.     TC(for);
  456.     TC(function);
  457.     TC(if);    
  458.     TC(interface);
  459.     TC(module);
  460.     TC(next);
  461.     TC(program);
  462.     TC(repeat);
  463.     TC(return);
  464.     TC(subroutine);
  465.     TC(switch);
  466.     TC(type);
  467.     TC(until);
  468.     TC(where);
  469.     TC(while);
  470.     default: return OC("UNKNOWN CMD");
  471.     }
  472. }
  473.  
  474. @ Print an error message if |case| or |default| don't occur inside a |switch|.
  475.  
  476. @<Part 1@>=@[
  477.  
  478. SRTN 
  479. not_switch FCN((s))
  480.     CONST outer_char s[] C1("Error message.")@;
  481. {
  482. RAT_ERROR(ERROR,"Misplaced keyword: \
  483. \"%s\" must be used only inside \"switch\"",1,s);
  484. }
  485.  
  486. @ Miscellaneous error-checking macros.
  487. @<Part 1@>=@[
  488. SRTN didnt_expand FCN((c0,c,op))
  489.     eight_bits c0 C0("")@;
  490.     eight_bits c C0("")@;
  491.     CONST char *op C1("")@;
  492. {
  493. RAT_ERROR(ERROR,"Was expecting '%c', not '%c', after \"%s\"; \
  494. expansion aborted",3,XCHR(c0),XCHR(c),op);
  495. }
  496.  
  497. @ Print an error message if the appropriate character didn't follow a
  498. keyword.  If the correct character did follow, then it is eaten.
  499.  
  500. @<Part 1@>=@[
  501. boolean 
  502. char_after FCN((c))
  503.     outer_char c C1("Character expected next.")@;
  504. {
  505. if((ASCII)(next_byte()) != XORD(c)) 
  506.     {
  507.     RAT_ERROR(WARNING,"Inserted '%c' after \"%s\"",
  508.         1,c,cmd_name(begun[rlevel-1].cmd)); 
  509.     BACK_UP@;
  510.     return NO;
  511.     }
  512.  
  513. return YES;
  514. }
  515.  
  516. @* SCANNING AHEAD.
  517. When we scan ahead to process the various \Ratfor\ commands, we must
  518. expand macros; we can't wait until they're sent to the output file. The
  519. function |next_byte| is akin to |get_output|; it returns the next
  520. (eight-bit) byte after macro expansion, but doesn't send it to the output.
  521. Because sometimes two bytes must be read as a unit, but we return only one
  522. at a time, we must sometimes save one byte until the next call to
  523. |next_byte|.
  524.  
  525. @<Glob...@>=
  526.  
  527. IN_RATFOR boolean saved_token RSET(NO); // Is there another byte waiting?
  528. IN_RATFOR eight_bits last_a; // The byte that was saved.
  529. IN_RATFOR int last_bytes; 
  530.     /* Length (either~1 or~2) of the token just read. Used to
  531.             back up properly. */
  532.  
  533. @ The |next_byte| function automatically advances the |cur_byte| pointer
  534. beyond the thing it returns. Sometimes, we must back up because of that.
  535.  
  536. @<Part 1@>=@[
  537.  
  538. eight_bits 
  539. next_byte(VOID)
  540. {
  541. eight_bits a0; // The next byte.
  542. sixteen_bits a; // Next two-byte token.
  543. static boolean ended_module = NO;
  544. long cur_val0; // Incoming value of |cur_val|.
  545.  
  546. /* Check if there's a byte already waiting. */
  547. if(saved_token)
  548.     {
  549.     saved_token = NO;
  550.     return last_a;
  551.     }
  552.  
  553. cur_val0 = cur_val; // Trouble if we don't restore the state of |cur_val|.
  554.  
  555. WHILE()
  556.     {
  557.     if(DONE_LEVEL)
  558.         {
  559.         if(!ended_module)
  560.             {
  561.             cur_val = -(long)cur_mod;
  562.             if(cur_val != ignore) OUT_CHAR(module_number);
  563.             ended_module = YES;
  564.             }
  565.  
  566.         if(!pop_level()) 
  567.             {
  568.             a0 = ignore;
  569.             break;
  570.             }
  571.  
  572.         ended_module = NO;
  573.         }
  574.  
  575.     if(TOKEN1(a0= *cur_byte++)) 
  576.         {
  577.         if(a0==ignore && !in_string) 
  578.             continue; // Forget about null bytes.
  579.  
  580.         if(rlevel > 0 && a0==begin_language)
  581.             { /* Skip the |begin_language|--|NUWEB_OFF| pair. */
  582.             cur_byte++;
  583.             continue;
  584.             }
  585.  
  586.         last_bytes = 1;
  587.         break;
  588.         }
  589.  
  590.     @<Expand two-byte token@>@;
  591.     }
  592.  
  593. return_next_byte:
  594.     cur_val = cur_val0;
  595.     return a0;
  596. }
  597.  
  598. @ For |next_byte|:
  599. @<Expand two-byte token@>=
  600. {
  601. a = IDENTIFIER(a0,last_a= *cur_byte++);
  602. last_bytes = 2;
  603.  
  604. /* Expand the two-byte token. */
  605. switch(a/MODULE_NAME)
  606.     {
  607.     case 0: /* An identifier. */
  608.  
  609.         if(is_deferred(a)) continue; // Execute deferred macro def'n.
  610.  
  611. /* If it's a macro, expand it. */
  612.         if(!mac_protected && 
  613.                (macro_text=(text_pointer)mac_lookup(a)) != NULL) 
  614.             {
  615.             eight_bits HUGE *p;
  616.             long cur_val0 = cur_val;
  617.  
  618.             cur_val = a; // In case it's a built-in function.
  619.             p = xmacro(macro_text,&cur_byte,cur_end,macrobuf);
  620.             cur_val = cur_val0;
  621.             push_level(NULL,p,mp);
  622.             break;
  623.             }
  624.         else if(!balanced && language==RATFOR &&
  625.              (a==id_function || a==id_program || a==id_subroutine))
  626.             {
  627.     RAT_ERROR(ERROR,"Inserted missing '%c' at beginning of function",
  628.         1,XCHR(cur_delim));
  629.             cur_byte -= 2;
  630.             saved_token = NO;
  631.             a0 = cur_delim;
  632.             goto return_next_byte;
  633.             }
  634.         else
  635.             {
  636.             saved_token = YES;
  637.             goto return_next_byte;
  638.             }
  639.  
  640.     case 1: /* Module name. */
  641.         x_mod_a(a);
  642.         break;
  643.  
  644.     default:
  645.         cur_val = a - MODULE_NUM;
  646.         if(cur_val > UNNAMED_MODULE) cur_mod = (sixteen_bits)cur_val;
  647.         OUT_CHAR(module_number);
  648.     }
  649. }
  650.  
  651. @ In various contexts, we must skip over newlines.  In doing so, verbatim
  652. comments are either copied to the output or saved in a buffer for later
  653. writing. 
  654.  
  655. @d COPY_COMMENTS NO
  656. @d SAVE_COMMENTS YES
  657.  
  658. @<Glob...@>=
  659.  
  660. IN_RATFOR eight_bits HUGE *cmnt_buf RSET(NULL), 
  661.     HUGE *cmnt_buf_end RSET(NULL),
  662.     HUGE *cmnt_pos RSET(NULL);
  663.  
  664. @
  665. @<Part 1@>=@[
  666.  
  667. SRTN 
  668. skip_newlines FCN((save_comments))
  669.     boolean save_comments C1("")@;
  670. {
  671. eight_bits a;
  672.  
  673. if(save_comments)
  674.     { // Allocate a buffer to hold the comments.
  675.     cmnt_pos = cmnt_buf = GET_MEM("cmnt_buf",SAVE8,eight_bits);
  676.     cmnt_buf_end = cmnt_buf + SAVE8;
  677.     }
  678.  
  679. while((a=copy_comment(save_comments)) == @'\n') ;
  680.  
  681. if(a == ignore) OUTPUT_ENDED("while skipping newlines",0);
  682.  
  683. BACK_UP@;
  684. }
  685.  
  686. @ While skipping newlines, we should also copy any verbatim comments
  687. directly to the output, or save them in a buffer. Verbatim comments are
  688. bracketed by |stringg|.  
  689.  
  690. @<Part 1@>=@[
  691. eight_bits 
  692. copy_comment FCN((save_comments))
  693.     boolean save_comments C1("")@;
  694. {
  695. eight_bits a;
  696.  
  697. WHILE()
  698.     if((a=next_byte()) != stringg) return a;
  699. /* Beginning of string. */
  700.     else if(save_comments)
  701.         { /* Save in preallocated buffer, for later use with
  702. |flush_comments|. */
  703.         *cmnt_pos++ = a;
  704.         in_string = YES;
  705.         while((a=next_byte()) != stringg)
  706.             {
  707.             if(cmnt_pos == cmnt_buf_end)
  708.                    resize(&cmnt_buf,SAVE8,&cmnt_pos,&cmnt_buf_end);
  709.  
  710.             *cmnt_pos++ = a;
  711.             }
  712.         *cmnt_pos++ = a;
  713.         in_string = NO;
  714.         }
  715.     else
  716.         { // Copy directly to output.
  717.         OUT_CHAR(stringg);
  718.         while((a=get_output()) != stringg) ;
  719.         }
  720.  
  721. DUMMY_RETURN(ignore);
  722. }
  723.  
  724. @ When comments have been saved in |cmnt_buf|, the following code writes
  725. them out. 
  726.  
  727. @<Part 1@>=@[
  728.  
  729. SRTN 
  730. flush_comments(VOID)
  731. {
  732. eight_bits *p;
  733.  
  734. if(!cmnt_buf) return; // Nothing left in buffer.
  735.  
  736. for(p=cmnt_buf; p < cmnt_pos; p++) out_char(*p); // Print out saved stuff.
  737. if(cmnt_pos > cmnt_buf) NL; // If there was a comment, issue a newline.
  738.  
  739. FREE_MEM(cmnt_buf,"cmnt_buf",SAVE8,eight_bits);
  740. cmnt_buf = cmnt_buf_end = cmnt_pos = NULL;
  741. }
  742.  
  743. @ In the course of the expansions, one must print out special tokens,
  744. but not expand them again.
  745.  
  746. @<Part 1@>=@[
  747.  
  748. SRTN 
  749. id0 FCN((cur_val))
  750.     sixteen_bits cur_val C1("Token to print out.")@;
  751. {
  752. if(cur_val == ignore) return;
  753.  
  754. if (out_state==NUM_OR_ID) C_putc(' '); // Space properly between identifiers.
  755.  
  756. out_ptrunc(cur_val); /* Output a possibly truncated identifier; see
  757.             \.{ftangle.web}. */
  758. out_state = NUM_OR_ID;
  759. }
  760.  
  761. @ We will maintain a stack of labels, referring to the top of, the bottom
  762. of, and the next statement after the block being expanded. It also holds
  763. the labels of the next |case| and |default| statements, and the identifier
  764. token that is being used for comparisons in the current |switch|.
  765.  
  766. @d current_cmd lbl[wlevel].cmd
  767. @d do_or_while (current_cmd==do_CMD || current_cmd==while_CMD)
  768.  
  769. @d s_top lbl[wlevel].Top
  770. @d s_next lbl[wlevel].Next
  771. @d was_next lbl[wlevel].was_Next
  772. @d s_break lbl[wlevel].Break
  773. @d was_break lbl[wlevel].was_Break
  774. @d s_case lbl[wlevel].Case
  775. @d s_default lbl[wlevel].Default
  776. @d icase lbl[wlevel].Icase
  777.  
  778. @f CMD int
  779.  
  780. @<Glob...@>=
  781. typedef struct
  782.     {
  783.     CMD cmd; // The command that initiated this block.
  784.     STMT_LBL Top,Next,Break; // Statement labels for loops.
  785.     STMT_LBL Case,Default; // Labels for next |case| or |default|.
  786.     sixteen_bits Icase; // Identifier token for current comparand.
  787.     unsigned was_Break:1, // Did a |break| occur?
  788.         was_Next:1; // Did a |@r next| occur?
  789.     } LBL;
  790.  
  791. IN_RATFOR LBL HUGE *lbl, HUGE *lbl_end; // Dynamic array.
  792. IN_RATFOR BUF_SIZE max_lbls; // Dynamic allocation length.
  793.  
  794. IN_RATFOR int wlevel RSET(0); 
  795.     /* Current level of expansion that can be broken out of
  796.             with a |break| or |next|.  This is incremented for
  797.             such things as |do|, but not for such things as
  798.             |if|. */
  799.  
  800. @ Allocate an array of loop info.
  801. @<Allocate dynamic memory@>=
  802.  
  803. ALLOC(LBL,lbl,ABBREV(max_lbls),max_lbls,0);
  804. lbl_end = lbl + max_lbls;
  805.  
  806. @ At the beginning of the loop expansion routines such as |@n9 where| or~|do|
  807. (but not~|if|), we must put appropriate statement labels onto the stack.
  808.  
  809. @<Part 1@>=@[
  810.  
  811. int 
  812. save_lbls FCN((cmd,top0,next0,break0,n_used))
  813.     CMD cmd C0("The current command.")@;
  814.     STMT_LBL top0 C0("Label number for top of block.")@;
  815.     STMT_LBL next0 C0("Go here on |next|.")@;
  816.     STMT_LBL break0 C0("Go here on |break|.")@;
  817.     int n_used C1("Number of labels used in this expansion.")@;
  818. {
  819. /* Advance the level counter; check for overflow. */
  820. if(++wlevel >= (int)max_lbls) OVERFLW("stmt labels","");
  821.  
  822. current_cmd = cmd; /* Save type of block. */
  823. s_top = top0; /* Top of block. */
  824. s_next = next0; /* Jump here on |@r next|. */
  825. s_break = break0; /* Jump here on |@r break|. */
  826. was_break = was_next = NO; // Did one occur during loop?
  827.  
  828. max_stmt += n_used; /* Advance the statement counter to ensure unique 
  829.                 labels. */ 
  830.  
  831. s_case = s_default = 0;
  832. icase = ignore;
  833.  
  834. return wlevel;
  835. }
  836.  
  837. @ In various contexts, we must send the character expansion of a statement
  838. number or other integer. If it's a statement label, we should suppress it
  839. if it's~0.
  840.  
  841. @d DONT_PRINT_IF_0 YES
  842. @d PRINT_IF_0 NO
  843.  
  844. @<Part 1@>=@[
  845.  
  846. SRTN 
  847. out_label FCN((suppress_0,stmt_num))
  848.     boolean suppress_0 C0("Suppress if zero?")@;
  849.     STMT_LBL stmt_num C1("Statement number to print.")@;
  850. {
  851. outer_char temp[N_IDBUF];
  852. outer_char *p;
  853.  
  854. if(stmt_num == (STMT_LBL)0 && suppress_0) return;
  855.  
  856. /* In \Fortran, the statement number must be $\le 99999$. */
  857. if(stmt_num > (STMT_LBL)99999)
  858.     {
  859.     stmt_num = (STMT_LBL)99999;
  860.     RAT_ERROR(WARNING,
  861.         "Automatic statement number out of bounds; %ld assumed",
  862.         1,stmt_num);
  863.     }
  864.  
  865. SPRINTF(N_IDBUF,temp,`"%ld",stmt_num`);
  866.  
  867. OUT_CHAR(constant);
  868.     for(p=temp; *p; p++) 
  869.         OUT_CHAR(XORD(*p));
  870. OUT_CHAR(constant);
  871. }
  872.  
  873. @ In expanding |if|s and |while|s, we must copy stuff through a balanced
  874. closing delimiter, ignoring such delimiters within strings. The routine is
  875. also used for scanning compound statements. In this case, it is expected
  876. that the opening brace has already been read.
  877.  
  878. When we're in the middle of a scan, the variable |balanced| will be~|NO|;
  879. this can be used in the various output routines such as |get_output| to
  880. help limit the scope of the scan, if one recognizes a situation that
  881. couldn't possibly arise within the scan.
  882.  
  883. @<Glob...@>=
  884.  
  885. IN_RATFOR boolean balanced RSET(YES);
  886. IN_RATFOR ASCII cur_delim RSET('\0');
  887.  
  888. @ The |copyd| function is basically simple: it copies from left-hand
  889. delimiter~|l| to and including right-hand delimiter~|r|. If the |to|
  890. argument is |NULL|, stuff is copied to the output. Otherwise, it is copied
  891. to memory. The memory copy is necessary only when processing a |switch|.
  892. All the text of the |switch| must be read and stored so the cases can be
  893. analyzed for the appropriate kind of expansion---computed |goto| or |if|
  894. statements. While processing a |switch|, only the keywords |case| and
  895. |default| are expanded immediately. (Expansion means closing off the
  896. previous case and initializing the new one, so the tokens are stored in the
  897. appropriate place.)  However, if a |switch| is nested, then the
  898. |case| and |default| of the inner |switch| should not be processed when
  899. it's stored. The argument |xpn_cases| prevents such premature expansion.
  900.  
  901. @d TO_OUTPUT NO /* First  argument of |copyd|. */
  902. @d TO_MEMORY YES
  903.  
  904. @d SAVE_IN_MEM(a) {if(cur_case->txt.next >= cur_case->txt.end)
  905.             resize(&cur_case->txt.start,BIG_SAVE8,
  906.                 &cur_case->txt.next, 
  907.                 &cur_case->txt.end);
  908.     *(cur_case->txt.next++) = (eight_bits)(a);}
  909.  
  910. @d SAVE_16 {SAVE_IN_MEM(a0)@; SAVE_IN_MEM(a1)@;} /* Store a 16-bit token. */
  911.  
  912. @d XPN_CASES YES
  913. @d DONT_XPN_CASES NO
  914.  
  915. @d BLEVELS 100
  916.  
  917. @<Part 1@>=@[
  918.  
  919. SRTN 
  920. copyd FCN((to_memory,xpn_cases,l,r,semi_allowed))
  921.     boolean to_memory C0("To memory?")@;
  922.     boolean xpn_cases C0("Expand |case| statements?")@;
  923.     ASCII l C0("Left-hand delimiter.")@;
  924.     ASCII r C0("Right-hand delimiter.")@;
  925.     boolean semi_allowed C1("Is a semicolon allowed in the text to be \
  926. copied?")@; 
  927. {
  928. int bal,bal0[BLEVELS];
  929. LINE_NUMBER starting_line;
  930. eight_bits (*output_rtn)(VOID);
  931. sixteen_bits a,last_token;
  932. sixteen_bits l0 = ignore,r0 = ignore;
  933. boolean found_semi;
  934. boolean balanced0 = balanced; // Save since possible recursion.
  935. ASCII cur_delim0 = cur_delim;
  936.  
  937. @<Set up |l0| and |r0|@>@;
  938.  
  939. if(l == @'{' && xpn_cases) /* We should be positioned after the brace. */
  940.     {
  941.     if(DONE_LEVEL && !pop_level()) OUTPUT_ENDED("after '{'",0);
  942.  
  943.     bal0[bal = 1] = 0; /* Don't copy the opening brace. */
  944.     }
  945. else 
  946.     {
  947.     if((ASCII)(next_byte()) != l) 
  948.         {
  949.         RAT_ERROR(ERROR,"Missing opening delimiter '%c'; \
  950. text not copied",
  951.             1,XCHR(l));
  952.         return;
  953.         }
  954.  
  955. /* Include the opening delimiter in the copy. */
  956.     BACK_UP@;
  957.     bal0[bal = 0] = 0; 
  958.     }
  959.  
  960. starting_line = OUTPUT_LINE;
  961.  
  962. /* Normally we copy the stuff directly to the output. However, if we're
  963.     processing a |switch|, we store it. */
  964. output_rtn = to_memory ? next_byte : get_output;
  965.  
  966. /* We use |last_token| to help check for a semicolon just before the closing
  967. delimiter. */
  968. last_token = ignore;
  969. found_semi = NO;
  970.  
  971. /* For use with check in |get_output|. */
  972. balanced = NO;
  973. cur_delim = r;
  974.  
  975. WHILE()
  976.     {
  977.     a = (sixteen_bits)(*output_rtn)(); /* Copy a token to the output,
  978. and remember it. */ 
  979.  
  980.     if(to_memory && a==(sixteen_bits)stringg) 
  981.         in_string = BOOLEAN(!in_string);
  982.  
  983.     if(!in_string) @<Check for balanced delimiter@>@;
  984.  
  985.     if(to_memory) @<Store stuff in memory@>@;
  986.     }
  987.  
  988. balanced = balanced0;
  989. cur_delim = cur_delim0;
  990. }
  991.  
  992. @ The routine |copyd| is used to scan only between matched parentheses or
  993. matched braces.  We check to avoid imbalances such as \.{(\dots \{\dots )}.
  994. The scan set is |{l,r}|; the alternate set is |{l0,r0}|.
  995.  
  996. @<Set up |l0|...@>=
  997.  
  998. switch(l)
  999.     {
  1000.    case @'{':
  1001.     l0 = @'('; @~ r0 = @')';
  1002.     break;
  1003.  
  1004.    case @'(':
  1005.     l0 = @'{'; @~ r0 = @'}';
  1006.     break;
  1007.  
  1008.    default:
  1009.     CONFUSION("copyd","Invalid left delimiter");
  1010.     }
  1011.  
  1012. @ We maintain a brace balance for the scan set, and also for the alternate
  1013. set, so we can catch various kinds of interlacing problems.
  1014.  
  1015. @<Check for balanced delim...@>=
  1016. {
  1017. if(a == ignore)    OUTPUT_ENDED("while scanning for '%c'.  Scan began \
  1018. with delimiter '%c' at line %u",3,XCHR(r),XCHR(l),starting_line);
  1019.  
  1020. if(a == (sixteen_bits)l) bal0[++bal] = 0;
  1021. else if(a == (sixteen_bits)r) @<Check right-hand delimiter~|r|@>@;
  1022. else if(a == l0) bal0[bal]++;
  1023. else if(a == r0) @<Check alternate right-hand delimiter~|r0|@>@;
  1024. else if(a != stringg)
  1025.     {
  1026.     if(a==@';')
  1027.         if(semi_allowed) found_semi = YES;
  1028.         else RAT_ERROR(ERROR,"Spurious semicolon",0);
  1029.  
  1030.     if(chk_stmts)
  1031.         if(!to_memory && a==id_keyword) last_token = ignore;
  1032.         else last_token = a; /* Remember last character so we can check
  1033.                 for semicolon. */
  1034.     }
  1035. }
  1036.  
  1037. @
  1038. @<Check right-hand delim...@>=
  1039. {
  1040. if(bal <= 0)
  1041.     {
  1042.     if(!to_memory) out_pos--; // Kill off what was already output.
  1043.     unmatched(l,r);
  1044.     continue;
  1045.     }
  1046. else 
  1047.     {
  1048.     if(bal0[bal] != 0)
  1049.         {
  1050.         inserted(bal0[bal],l0,r0,l,bal);
  1051.  
  1052.         while(bal0[bal]--)
  1053.             if(to_memory) SAVE_IN_MEM(r0)@;
  1054.             else OUT_CHAR(r0);
  1055.         }
  1056.         
  1057.     if(--bal == 0) 
  1058.         {
  1059.         if(semi_allowed && last_token && last_token != @';')
  1060.             {
  1061.             RAT_ERROR(WARNING,"Supplied missing ';' before \
  1062. delimiter '%c'", 1,r);
  1063.  
  1064.             if(to_memory) SAVE_IN_MEM(@';')@;
  1065.             else OUT_CHAR(@';');
  1066.             }
  1067.  
  1068.         if(to_memory) SAVE_IN_MEM(r)@;
  1069.  
  1070. /* We've successfully found the end of the scan. */
  1071.         balanced = YES;
  1072.         cur_delim = '\0';
  1073.         break;
  1074.         }
  1075.     }
  1076. }
  1077.  
  1078. @
  1079. @<Check alternate right...@>=
  1080. {
  1081. if(bal0[bal] <= 0)
  1082.     {
  1083.     if(!to_memory) out_pos--;
  1084.     unmatched((ASCII)l0,(ASCII)r0);
  1085.     continue;
  1086.     }
  1087. else bal0[bal]--;
  1088. }
  1089.  
  1090. @ The nuance here is to remember that certain single-byte tokens such as
  1091. |dot_const| are really escapes that are followed by data.  That data need
  1092. not conform to the standard interpretation of a token, so must be copied
  1093. explicitly. 
  1094. @<Store stuff in memory@>=
  1095. {
  1096. if(TOKEN1(a)) 
  1097.     {
  1098.     SAVE_IN_MEM(a)@; /* Store it if necessary. */ 
  1099.  
  1100.     switch(a)
  1101.         {
  1102.        case dot_const:
  1103.        case begin_language:
  1104.         SAVE_IN_MEM(*cur_byte++);
  1105.         break;
  1106.  
  1107.        case new_output_file:
  1108.         RAT_ERROR(ERROR,"@@o command not allowed inside switch",0);
  1109.         }
  1110.     }
  1111. else
  1112.     {
  1113.     if(xpn_cases)
  1114.         @<Possibly expand 16-bit token@>@;
  1115.      else 
  1116.         {/* For inner |switches|, just copy tokens. */
  1117.         SAVE_IN_MEM(a)@;
  1118.         SAVE_IN_MEM(next_byte())@;
  1119.         }
  1120.     }
  1121. }
  1122.  
  1123. @ While processing a |switch|, we copy everything to memory except for
  1124. |case| and |default|, which are expanded immediately. Also, an inner
  1125. |switch| should just be copied in its entirety.
  1126.  
  1127. @<Possibly expand 16...@>=
  1128. @{
  1129. eight_bits a0,a1;
  1130.  
  1131. @b
  1132. a = IDENTIFIER(a0=(eight_bits)a,a1=next_byte());
  1133.  
  1134. if(a==id_switch)
  1135.     {
  1136.     SAVE_16; /* |switch|. */
  1137.     copyd(TO_MEMORY,DONT_XPN_CASES,@'(',@')',NO); /* $(\dots)$ */
  1138.     skip_newlines(COPY_COMMENTS);
  1139.     copyd(TO_MEMORY,DONT_XPN_CASES,@'{',@'}',YES); /* |{body;}| */
  1140.     }
  1141. else if(a==id_case) x_case();
  1142. else if(a==id_default) x_default();
  1143. else SAVE_16;
  1144. }
  1145.  
  1146. @ Interface to \FTANGLE.
  1147.  
  1148. @<Part 1@>=@[
  1149.  
  1150. SRTN 
  1151. cp_fcn_body(VOID)
  1152. {
  1153. brace_level++;
  1154. copyd(TO_OUTPUT,XPN_CASES,@'{',@'}',YES);
  1155.  
  1156. if(--brace_level == 0) 
  1157.     {
  1158.     END; /* Automatically insert an |@r end| statement. */
  1159.     cur_fcn = NO_FCN; /* No longer inside a function. */
  1160.     rlevel--;
  1161.     }
  1162. }
  1163.  
  1164. @ Copy to output, stopping just \It{before} a delimiter~|r_before|
  1165. (generally~\.{'\{}') or just \It{after} a delimiter~|r_after|
  1166. (generally~\.{';'}).  As a special case, if |r_before == 0177|, we just
  1167. look for |r_after|.
  1168.  
  1169. @d copy_to(r_after) copy_2to(NOT_BEFORE,r_after)
  1170.  
  1171. @<Unused@>=
  1172.  
  1173. unsigned copy_2to FCN((r_before,r_after))
  1174.     ASCII r_before C0("")@;
  1175.     char r_after C1("Terminating delimiter.")@;
  1176. {
  1177. eight_bits a;
  1178. LINE_NUMBER starting_line;
  1179. unsigned k = 0;
  1180.  
  1181. starting_line = OUTPUT_LINE; // Remember where scan started in case of error.
  1182.  
  1183. WHILE()
  1184.   if(TOKEN1(a= next_byte()))
  1185.     {
  1186.     k++;
  1187.     if(!in_string)
  1188.         {
  1189.         if(a == ignore) OUTPUT_ENDED("while copying \
  1190. from line %u to delimiter (before = '%c', after = '%c')",3,
  1191.             starting_line,
  1192.             r_before==NOT_BEFORE ? '\0' : XCHR(r_before),
  1193.             r_after==NOT_AFTER ? '\0' : XCHR(r_after));
  1194.  
  1195.         if(a == (sixteen_bits)r_after && a != NOT_AFTER) return k;
  1196.         if(a == (sixteen_bits)r_before && a != NOT_BEFORE) 
  1197.             {
  1198.             BACK_UP@;
  1199.             return k-1;
  1200.             }
  1201.         }
  1202.  
  1203.     OUT_CHAR(a);
  1204.     }
  1205.   else
  1206.     {
  1207.     cur_val = IDENTIFIER(a,next_byte());
  1208.     k += 2;
  1209.     OUT_CHAR(identifier);
  1210.     }
  1211. }
  1212.  
  1213. @ A very important function is the one that copies and possibly expands a
  1214. (possibly compound) statement. One annoyance is that in the auto-semi mode
  1215. an extra semicolon may be put after constructions such as |for()|; this
  1216. must be eaten.
  1217.  
  1218. @d BRACE_ONLY 1 /* In some situations such as after |switch|, only a brace
  1219.             is expected. */
  1220.  
  1221. @<Part 1@>=@[
  1222.  
  1223. SRTN 
  1224. stmt FCN((to_memory,brace_only))
  1225.     boolean to_memory C0("")@;
  1226.     boolean brace_only C1("Is only a left brace allowed next?")@;
  1227. {
  1228. sixteen_bits a;
  1229.  
  1230. EAT_AUTO_SEMI;
  1231. skip_newlines(COPY_COMMENTS);
  1232.  
  1233. if((a=next_byte()) != @'{')
  1234.     {
  1235.     if(a == ignore) OUTPUT_ENDED("at beginning of statement",0);
  1236.  
  1237. /* Issue error message if was expecting brace. */
  1238.     if(brace_only)
  1239.         {
  1240.         RAT_ERROR(WARNING,"Inserted '{'",0);
  1241.         BACK_UP@;
  1242.         copyd(to_memory,XPN_CASES,@'{',@'}',YES);
  1243.         return;
  1244.         }
  1245.  
  1246.     if(TOKEN1(a)) 
  1247.         { /* Definitely not a compound statement. */
  1248.         BACK_UP@;
  1249.         x_stmt();
  1250.         }
  1251.     else 
  1252.         { /* Check if it's a Ratfor token that needs to be
  1253. expanded. */
  1254.         SPEC HUGE *s;
  1255.  
  1256.         a = IDENTIFIER(a,next_byte());
  1257.  
  1258.         for(s=spec_tokens; s->len != 0; s++)
  1259.             if(a == *s->pid && s->expand != NULL)
  1260.                 {
  1261.                 (*s->expand)();
  1262.                 return; // Successfully expanded special token.
  1263.                 }
  1264.         BACK_UP@;
  1265.         x_stmt();
  1266.         }
  1267.     }
  1268. else copyd(to_memory,XPN_CASES,@'{',@'}',YES); /* Scan compound
  1269.             statement. */ 
  1270. }
  1271.  
  1272.  
  1273. @ Expand a simple statement, by copying to and eating a semicolon. If
  1274. verbatim comments are present, we copy those as well.
  1275.  
  1276. @<Part 1@>=@[
  1277.  
  1278. SRTN 
  1279. x_stmt(VOID)
  1280. {
  1281. eight_bits a;
  1282.  
  1283. WHILE()
  1284.     {
  1285.     if( (a=get_output()) == ignore) OUTPUT_ENDED("during scan of simple \
  1286. statement ",0);
  1287.  
  1288.     if(a == @';' && !in_string) break;
  1289.     }
  1290.  
  1291. /* Does a verbatim comment follow? If so, it's bracketed by |stringg|. */
  1292. if( (a=next_byte()) != stringg) {BACK_UP@; @~ return;}
  1293.  
  1294. if(*cur_byte != @'\n') {BACK_UP@; @~ return;}
  1295.  
  1296. /* Copy verbatim comment. */
  1297. OUT_CHAR(a);
  1298. while((a=get_output()) != stringg) ;
  1299. }
  1300.  
  1301.  
  1302. @* SAVING and OUTPUTTING RATFOR TEXT.  We need a routine to save the
  1303. token-by-token output in a buffer~|p| of maximum length~|nmax|. We scan
  1304. until we encounter the right delimiter, which may be either/or |r_before|
  1305. or |r_after|.  If it's |r_before|, the scan stops before |r_before|.  If
  1306. it's |r_after| (which may be either~\.{')'}, \.{';'}, or~\.{':'}), the scan
  1307. stops after |r_after|, and |r_after| is eaten.  Note that if |r_after ==
  1308. ')'|, then we're in the midst of a parenthesized expression, and we must be
  1309. careful not to stop prematurely if there are extra balanced parentheses.
  1310.  
  1311. In some cases, we just need to copy stuff directly to the output.
  1312. Nevertheless, it's convenient to save it first, then output it, because the
  1313. save operation handles the single-token escapes conveniently.
  1314.  
  1315. @<Glob...@>=
  1316.  
  1317. IN_RATFOR eight_bits HUGE *save_buffer RSET(NULL), HUGE *psave_buffer;
  1318.  
  1319. @
  1320.  
  1321. @d unmatched(l,r) RAT_ERROR(WARNING,"Ignored '%c' not matched with %s",
  1322.         2,XCHR(r),qdelim(l))
  1323.  
  1324. @d inserted(n,l0,r0,l,level) RAT_ERROR(WARNING,
  1325.     "Inserted %d '%c' to balance '%c' at %s level %d",
  1326.     5,n,XCHR(r0),XCHR(l0),qdelim(l),level)
  1327.  
  1328. /* Copy, then immediately output. */
  1329. @d COPY_TO(r) psave_buffer = SAVE_AFTER(&save_buffer,BIG_SAVE8,r);
  1330.     copy_out(save_buffer,psave_buffer,!macro)@;
  1331.  
  1332. @d COPY_2TO(r_before,r_after) 
  1333.     psave_buffer = save_out(&save_buffer,BIG_SAVE8,r_before,r_after);
  1334.     copy_out(save_buffer,psave_buffer,!macro)@;
  1335.  
  1336. @<Part 1@>=@[
  1337.  
  1338. eight_bits HUGE *
  1339. save_out FCN((pp,nmax,r_before,r_after))
  1340.     eight_bits HUGE **pp C0("Address of pointer to buffer where result is \
  1341. saved.")@; 
  1342.     int nmax C0("Length of above buffer.")@;
  1343.     eight_bits r_before C0("Stop before here.")@;
  1344.     eight_bits r_after C1("Stop after here.")@;
  1345. {
  1346. eight_bits a,l;
  1347. eight_bits HUGE *p, HUGE *p_end;
  1348. LINE_NUMBER starting_line;
  1349. int bal,bal0[BLEVELS];
  1350.  
  1351. /* If a save buffer hasn't already been allocated, do that. */
  1352. if(!(*pp)) 
  1353.     *pp = GET_MEM("*pp",nmax,eight_bits); /* Send back the buffer
  1354. address, so we can free later. */
  1355. p = *pp;
  1356. p_end = p + nmax - 1; /* End of buffer. When we get this far, we must
  1357.                 reallocate. The $-1$~is because we might
  1358.                 increment~|p| by~2. */
  1359.  
  1360. switch(r_after)
  1361.     {
  1362.    case @')':
  1363.     l = (eight_bits)@'(';
  1364.     bal = 1;
  1365.     break;
  1366.  
  1367.    case @'}':
  1368.     l = (eight_bits)@'{';
  1369.     bal = 1;
  1370.     break;
  1371.  
  1372.    default:
  1373.     l = '\0';
  1374.     bal = 0;
  1375.     break;
  1376. }
  1377.  
  1378. bal0[bal] = 0;
  1379.                     
  1380. starting_line = OUTPUT_LINE; /* Remember where the scan started, in case
  1381.                     there is an error. */
  1382.  
  1383. if(in_string) 
  1384.     CONFUSION("save_out","Shouldn't be inside string here");
  1385.  
  1386. WHILE()
  1387.     {
  1388.     if(p >= p_end) resize(pp,nmax,&p,&p_end); /* Reallocate the save
  1389. buffer. */ 
  1390.  
  1391.     if(TOKEN1(a= next_byte()))
  1392.         {
  1393.         if(!in_string) 
  1394.             @<Check for balanced parentheses or braces@>@;
  1395.  
  1396.         @<Save single-byte token@>@;
  1397.         }
  1398.     else
  1399.         {
  1400.         *p++ = a;
  1401.         *p++ = next_byte();
  1402.         }
  1403.     }
  1404.  
  1405. DUMMY_RETURN(NULL);
  1406. }
  1407.  
  1408.  
  1409. @
  1410. @<Check for balanced paren...@>=
  1411. {
  1412. if(a == ignore) OUTPUT_ENDED("while scanning from line %u \
  1413. for delimiter (r_before = '%c', r_after = '%c')",
  1414.     3,starting_line,XCHR(r_before),XCHR(r_after));
  1415.             
  1416. if(a==l) bal0[++bal] = 0;
  1417. else if(a == r_after && r_after != NOT_AFTER) @<Check right-hand balance@>@;
  1418. else if(a == r_before && r_before != NOT_BEFORE)
  1419.     {
  1420.     BACK_UP;
  1421.     *p = '\0';
  1422.     return p;
  1423.     }
  1424. else if(a == @'{') bal0[bal]++;
  1425. else if(a == @'}') @<Check alternate balance@>@;
  1426. }
  1427.  
  1428. @
  1429. @<Check right-hand balance@>=
  1430. {
  1431. if(l && bal <= 0)
  1432.     {
  1433.     p--;
  1434.     unmatched(l,r_after);
  1435.     continue;
  1436.     }
  1437. else 
  1438.     {
  1439.     if(bal0[bal] != 0)
  1440.         {
  1441.         inserted(bal0[bal],@'{',@'}',l,bal);
  1442.  
  1443.         while(bal0[bal]--)
  1444.             {
  1445.             *p++ = @'}';
  1446.             if(p >= p_end) resize(pp,nmax,&p,&p_end);
  1447.             }
  1448.         }
  1449.  
  1450.     if(l) bal--;
  1451.     if(bal == 0)
  1452.         {  /* Found right-hand delimiter. */
  1453.         *p = '\0'; /* Mark end of tokens. */
  1454.         return p;
  1455.         }
  1456.     }
  1457. }
  1458.  
  1459. @
  1460. @<Check alternate balance@>=
  1461. {
  1462. if(bal0[bal] <= 0)
  1463.     {
  1464.     p--;
  1465.     unmatched(@'{',@'}');
  1466.     continue;
  1467.     }
  1468. else bal0[bal]--;    
  1469. }
  1470.  
  1471. @
  1472. @<Save single-byte token@>=
  1473. {
  1474. *p++ = a;
  1475.  
  1476. switch(a)
  1477.     {
  1478.    case stringg:
  1479.     in_string = BOOLEAN(!in_string);
  1480.     break;
  1481.  
  1482.    case dot_const:
  1483.    case begin_language:
  1484.     *p++ = *cur_byte++;
  1485.     break;
  1486.     }
  1487. }
  1488.  
  1489. @
  1490. @<Part 1@>=@[
  1491.  
  1492. outer_char *
  1493. qdelim FCN((delim))
  1494.     ASCII delim C1("")@;
  1495. {
  1496. static outer_char q0[4];
  1497.  
  1498. sprintf((char *)q0,delim ? "'%c'" : "?",XCHR(delim));
  1499. return q0;
  1500. }
  1501.  
  1502. @ If necessary, we reallocate the save buffer to a larger size.
  1503.  
  1504. @<Part 1@>=@[
  1505.  
  1506. SRTN 
  1507. resize FCN((pp,nmax,pq,pp_end))
  1508.     eight_bits HUGE **pp C0("Addr of ptr to beginning of buffer")@;
  1509.     int nmax C0("Resizing increment")@;
  1510.     eight_bits HUGE **pq C0("Address of current pointer")@;
  1511.     eight_bits HUGE **pp_end C1("Addr of ptr to end of buffer")@;
  1512. {
  1513. int old_len = PTR_DIFF(int, *pq, *pp); // Old length.  Should this be |size_t|?
  1514. int new_len = old_len + nmax; // New length.
  1515.  
  1516. *pp = (eight_bits HUGE *)REALLOC(*pp, 
  1517.         new_len*sizeof(eight_bits),
  1518.         old_len*sizeof(eight_bits));
  1519. *pq = *pp + old_len; /* New next position to which to accrete. */
  1520. *pp_end = *pp + new_len - 1; // New end.
  1521. }
  1522.  
  1523. @* KEYWORD TRANSLATION.
  1524. A variety of macro definitions facilitate constructing the expanded output.
  1525.  
  1526. /* The |INDENT| and |OUTDENT| macros are used to beautify the \.{FOR}
  1527. output. */
  1528. @d INDENT indent_level++; blank_out(1)@;
  1529. @d OUTDENT indent_level--; out_pos -= indnt_size@;
  1530.  
  1531. @d LABEL(lbl) out_label(DONT_PRINT_IF_0,(STMT_LBL)(lbl)) /* Statement label. */
  1532. @d NUMBER(lbl) out_label(PRINT_IF_0,(STMT_LBL)(lbl)) /* Ordinary integer,
  1533.                             including~0. */ 
  1534.  
  1535. @d PARENS copyd(TO_OUTPUT,XPN_CASES,@'(',@')',NO) /* Copies text between
  1536.                 (and including) parens. */ 
  1537.  
  1538. @m ID(type) id0(id__##type) /* Send identifier directly to output. */
  1539. @m XPN_BODY(var1,flag,var2) xpn_body(id__##var1,flag,id__##var2) /* For
  1540.                 |if| or |where| stmts. */ 
  1541. @m XPN_ELSE(id1,id2,var1,flag,var2) 
  1542.     xpn_else(id1,id2,id__##var1,flag,id__##var2) /* For
  1543.                 |if| or |where| stmts. */ 
  1544.  
  1545. /* Macro up various single characters to be sent to the output. */
  1546. @d NL out_char(@'\n')
  1547. @d LP out_char(@'(')
  1548. @d RP out_char(@')')
  1549. @d COMMA out_char(@',')
  1550. @d NOT out_char(@'!')
  1551. @d EQUALS out_char(@'=')
  1552. @d MINUS out_char(@'-')
  1553. @d EQ_EQ out_char(eq_eq)
  1554. @d OR out_char(or_or)
  1555. @d LT out_char(@'<')
  1556. @d GT out_char(@'>')
  1557.  
  1558. @d IF(stmt_num) LABEL(stmt_num); @~ ID(IF)@;
  1559. @d THEN ID(THEN); @~ NL@;
  1560. @d ELSE ID(ELSE)
  1561. @d ENDIF ID(ENDIF); @~ if(symbolic_label) id0(symbolic_label); @~ NL@;
  1562. @d ENDWHERE ID(ENDWHERE); @~ NL@;
  1563. @d GOTO(stmt) ID(GOTO); @~ LABEL(stmt); @~ NL@;
  1564. @d CONTINUE(stmt) LABEL(stmt); @~ ID(CONTINUE); @~ NL@;
  1565. @d RETURN ID(RETURN); @~ NL@;
  1566. @d END ID(END); @~ NL@;
  1567.  
  1568. @d END_DO ID(END); @~ ID(DO); @~ NL@;
  1569. @d END_SELECT ID(END); @~ ID(SELECT); @~ NL@;
  1570.  
  1571. @ Ratfor has the ability to generate comments about each keyword it's
  1572. expanding. (These can be suppress by command-line option~`\.{-k}'.)
  1573.  
  1574. We'll need a couple of buffers.
  1575. @<Glob...@>=
  1576.  
  1577. IN_RATFOR outer_char HUGE *cmd_fmt;
  1578. IN_RATFOR ASCII HUGE *cmd_msg, HUGE *cmd_end;
  1579. IN_RATFOR BUF_SIZE cmd_fsize,cmd_size;
  1580.  
  1581. @ We also need an interface to \FTANGLE.
  1582.  
  1583. @<Part 2@>=@[
  1584.  
  1585. SRTN 
  1586. alloc_Rat(VOID)
  1587. {
  1588. @<Allocate dynamic memory@>@;
  1589. }
  1590.  
  1591. @
  1592. @<Allocate dyn...@>=
  1593.  
  1594. ALLOC(outer_char,cmd_fmt,ABBREV(cmd_fsize),cmd_fsize,0);
  1595. ALLOC(ASCII,cmd_msg,ABBREV(cmd_size),cmd_size,0);
  1596. cmd_end = cmd_msg + cmd_size;
  1597.  
  1598. @
  1599. @m OUT_CMD(emit,abbrev,beginning,fmt0,n,...)
  1600.     out_cmd(emit,abbrev,OC(beginning),OC(fmt0),n,#.)
  1601.  
  1602. @<Part 2@>=@[
  1603.  
  1604. SRTN 
  1605. out_cmd FCN(VA_ALIST((emit_continue,abbrev,beginning,fmt0,n VA_ARGS)))
  1606.     VA_DCL(
  1607.     boolean emit_continue C0("Put a |continue| in case of label.")@;
  1608.     outer_char abbrev C0("Abbreviation of command.")@;
  1609.     CONST outer_char beginning[] C0("Beginning part of message.")@;
  1610.     CONST outer_char *fmt0 C0("Format of the message.")@;
  1611.     int n C2("Number of arguments to message.")@;)@;
  1612. {
  1613. VA_LIST(arg_ptr)@;
  1614. #if(NUM_VA_ARGS == 1)
  1615.     boolean emit_continue;
  1616.     char abbrev;
  1617.     CONST outer_char *beginning;
  1618.     CONST outer_char *fmt0;
  1619.     int n;
  1620. #endif
  1621.  
  1622. VA_START(arg_ptr,n);
  1623.  
  1624. #if(NUM_VA_ARGS == 1)
  1625.     emit_continue = va_arg(arg_ptr,boolean);
  1626.     abbrev = va_arg(arg_ptr,char);
  1627.     beginning = va_arg(arg_ptr,char *);
  1628.     fmt0 = va_arg(arg_ptr,char *);
  1629.     va_arg(arg_ptr,int);
  1630. #endif
  1631.  
  1632. @<Check if command is suppressed@>@;
  1633.  
  1634. if(emit_continue)
  1635.     {
  1636.     CONTINUE(ignore); /* In case there's a statement label. */
  1637.     }
  1638.  
  1639. /* Make prettier format. */
  1640. SPRINTF(cmd_fsize,cmd_fmt,
  1641.     `"--- %s \"%s%s\" ---",beginning,cmd_name(begun[rlevel-1].cmd),fmt0`); 
  1642.  
  1643. @<Fill in the variable parts of the msg@>;
  1644.  
  1645. if(Fortran88 && symbolic_label)
  1646.     {
  1647.     id0(symbolic_label); @~ OUT_CHAR(@':');
  1648.     }
  1649. }
  1650.  
  1651. @ Filling in the token strings is a bit annoying. We can't simply treat
  1652. them as character strings, because some of the tokens may be zero. Thus, we
  1653. actually parse the format looking for |"%s"| and replace that by the
  1654. appropriate token string.
  1655.  
  1656. @<Fill in the var...@>=
  1657. @{
  1658. outer_char HUGE *p;
  1659. ASCII HUGE *q;
  1660. eight_bits HUGE *s, HUGE *s1;
  1661.  
  1662. @b
  1663. p = cmd_fmt; 
  1664. q = cmd_msg;
  1665.  
  1666. while(*p)
  1667.     {
  1668.     if(q >= cmd_end) 
  1669.         OVERFLW("cmd_msg",ABBREV(cmd_size)); 
  1670.  
  1671.     if(*p == '%' && *(p+1) == 's')
  1672.         {
  1673.         p += 2;
  1674.  
  1675. /* For compilers that don't implement variable arguments, the following
  1676. calls return a string beginning with \.{"KLUDGE"}. (See
  1677. \.{proto.hweb}.) This doesn't work right on the MAC, since it seems to
  1678. put copies of identical strings into different locations. Thus, the
  1679. \Ratfor\ comments look strange. To kill off those comments, use the \.{-k}
  1680. option. */
  1681.         s = va_arg(arg_ptr,eight_bits *);
  1682.         s1 = va_arg(arg_ptr,eight_bits *);
  1683.  
  1684.         while(s < s1)
  1685.             *q++ = *s++;
  1686.         }
  1687.     else 
  1688.         *q++ = XORD(*p++);
  1689.     }
  1690.  
  1691. va_end(arg_ptr);
  1692.  
  1693. /* Translate it to the output. */
  1694. OUT_MSG(cmd_msg,q);
  1695. }
  1696.  
  1697. @ The command-line option~`\.{-k}' gives a list of abbreviations for which
  1698. a comment should not be output. '\.*' means nothing should be output.
  1699. Option `\.{-K}' means output comments only for those abbreviations; '\.*'
  1700. means output all comments. 
  1701. @<Check if command is ...@>=
  1702. @{
  1703. static outer_char brkset[3] = "*?"; /* Prototype list of possible characters to
  1704.             be searched for in the command-line list. */
  1705. char *strpbrk();
  1706. boolean found_abbrev;
  1707.  
  1708. @b
  1709. brkset[1] = abbrev;
  1710. found_abbrev = BOOLEAN(STRPBRK(abbrev_cmds,brkset) != NULL);
  1711.  
  1712. if(suppress_cmds) {if(found_abbrev) return;}
  1713. else {if(!found_abbrev) return;}
  1714. }
  1715.  
  1716. @ We just use |max_lbls| here, rather than defining a new dynamic type.
  1717. @<Allocate dyn...@>=
  1718.  
  1719. begun = GET_MEM("begun",max_lbls,BEGUN);
  1720.  
  1721. @
  1722. @<Part 2@>=@[
  1723.  
  1724. SRTN 
  1725. expanding FCN((cmd))
  1726.     CMD cmd C1("Type of identifier being expanded.")@;
  1727. {
  1728. if(rlevel >= (int)max_lbls) OVERFLW("Nesting","");
  1729.  
  1730. begun[rlevel].cmd = cmd;
  1731. begun[rlevel].name = rlevel ? cur_fcn : NO_FCN;
  1732. begun[rlevel].symbolic = sym_label; // For |do| or |switch|.
  1733. begun[rlevel].function = BOOLEAN(CHOICE(rlevel, is_function, NO));
  1734. begun[rlevel].line = OUTPUT_LINE;
  1735. begun[rlevel].level = wlevel;
  1736. rlevel++;
  1737. }
  1738.  
  1739. @ Expand a |while| statement.
  1740. @<Rdoc@>=
  1741. @r
  1742. /* Source construction: */
  1743.     while(expr) {stmt;}
  1744.  
  1745. @n
  1746. /* Translation: */
  1747. TOP:    continue
  1748.     if(expr) then
  1749.         stmt
  1750.     endif
  1751.     goto TOP
  1752. BREAK:    continue
  1753.  
  1754. @
  1755. @<Part 2@>=@[
  1756.  
  1757. X_FCN 
  1758. x_while(VOID)
  1759. {
  1760. eight_bits HUGE *a = NULL, HUGE *pa;
  1761.  
  1762. expanding(while_CMD);
  1763. save_lbls(while_CMD,max_stmt,max_stmt,max_stmt+1,2);
  1764.  
  1765. /* Is parenthesized condition present? */
  1766. IS_NEXT_PAREN(while);
  1767. pa = SAVE_AFTER(&a,SAVE8,@')'); /* Save the condition. */
  1768.  
  1769. OUT_CMD(YES,'w',"","(%s)",2,a,pa); /* Comment to output. */
  1770.  
  1771. if(Fortran88)
  1772.     {
  1773.     ID(DO); @~ ID(WHILE); @~ LP; @~ copy_out(a,pa,!macro); @~ RP; @~
  1774. NL; /* |@n DO WHILE|$(\dots)$ */
  1775.     }
  1776. else
  1777.     {
  1778.     IF(s_top); @~ LP; @~ copy_out(a,pa,!macro); @~ RP; @~ THEN;
  1779.     }
  1780. INDENT;
  1781.     stmt(TO_OUTPUT,0); /* Body. */
  1782.     if(!Fortran88) {GOTO(s_top);}
  1783. OUTDENT;
  1784.  
  1785. if(Fortran88) {END_DO;}
  1786. else
  1787.     {
  1788.     ENDIF;
  1789.     if(was_break) {CONTINUE(s_break);}
  1790.     }
  1791.  
  1792. wlevel--;
  1793. rlevel--;
  1794. FREE_MEM(a,"while:a",SAVE8,eight_bits);
  1795. }
  1796.  
  1797. @ Expand a  |break| statement. Outputs a jump statement to the |break|
  1798. label saved earlier. 
  1799.  
  1800. @<Part 2@>=@[
  1801.  
  1802. X_FCN 
  1803. x_break(VOID)
  1804. {
  1805. sixteen_bits a;
  1806.  
  1807. /* Check that we're in a loop or |switch|. */
  1808. if(wlevel==0 && switch_level==0)
  1809.     {
  1810.     NOT_LOOP("break"," or \"switch\"");
  1811.     COPY_TO(@';');
  1812.     return;
  1813.     }
  1814.  
  1815. expanding(break_CMD);
  1816.  
  1817. was_break = YES; /* Remember that at least one
  1818.             |break| statement happened during this loop. */
  1819.  
  1820. OUT_CMD(YES,'b',"","",0); /* Comment to output. */
  1821.  
  1822. if(Fortran88 && do_or_while)
  1823.     {
  1824.     ID(EXIT); 
  1825.  
  1826.     if(TOKEN1(a=next_byte())) BACK_UP@;
  1827.     else id0(IDENTIFIER(a,next_byte()));
  1828.  
  1829.     NL; /* The |do_or_while| is used since |EXIT| can only
  1830. be used inside of |do|'s or |while|'s. */
  1831.     }
  1832. else {GOTO(s_break);}
  1833.  
  1834. char_after(';'); /* |break| must be immediately followed by semicolon. */
  1835. rlevel--;
  1836. }
  1837.  
  1838. @ Issue an error message about misplaced command.
  1839.  
  1840. @d NOT_LOOP(id,msg) not_loop(OC(id),OC(msg))
  1841.  
  1842. @<Part 2@>=@[
  1843.  
  1844. SRTN 
  1845. not_loop FCN((id,msg))
  1846.     CONST outer_char id[] C0("Errant identifier name.")@;
  1847.     CONST outer_char msg[] C1("Error message.")@;
  1848. {
  1849. RAT_ERROR(WARNING,"Misplaced keyword: \
  1850. \"%s\" must appear inside loop%s; command ignored",
  1851.     2,id,msg);
  1852. }
  1853.  
  1854. @ Expand a |@r next| statement. Outputs a jump statement to the |@r next| label
  1855. saved earlier.
  1856.  
  1857. @<Part 2@>=@[
  1858.  
  1859. X_FCN 
  1860. x_next(VOID)
  1861. {
  1862. sixteen_bits a;
  1863.  
  1864. /* Check that |next| occurs inside loop. */
  1865. if(wlevel == 0)
  1866.     {
  1867.     NOT_LOOP("next","");
  1868.     COPY_TO(@';');
  1869.     return;
  1870.     }
  1871.  
  1872. expanding(next_CMD);
  1873.  
  1874. was_next = YES; /* At least one |next| occurred during this loop. */
  1875. OUT_CMD(YES,'n',"","",0);
  1876.  
  1877. if(Fortran88 && do_or_while) 
  1878.     {
  1879.     ID(CYCLE); 
  1880.  
  1881.     if(TOKEN1(a=next_byte())) BACK_UP@;
  1882.     else id0(IDENTIFIER(a,next_byte()));
  1883.  
  1884.     NL;
  1885.     }
  1886. else {GOTO(s_next);}
  1887.  
  1888. char_after(';');
  1889. rlevel--;
  1890. }
  1891.  
  1892. @ Expand a |repeat| statement. Note that in the \Ratfor\ syntax the |@r
  1893. until| is optional.
  1894. @<Rdoc@>=
  1895. @r
  1896. /* Source construction: */
  1897.     repeat {stmt;} until(expr);
  1898.  
  1899. @n
  1900. /* Translation: */
  1901. TOP:    continue
  1902.     stmt
  1903. NEXT:    if(!(expr)) goto TOP
  1904. BREAK:    continue
  1905.  
  1906. @
  1907. @<Part 2@>=@[
  1908.  
  1909. X_FCN 
  1910. x_repeat(VOID)
  1911. {
  1912. sixteen_bits a;
  1913. eight_bits HUGE *u = NULL, HUGE *pu;
  1914.  
  1915. expanding(repeat_CMD);
  1916. save_lbls(repeat_CMD,max_stmt,max_stmt+1,max_stmt+2,3);
  1917.  
  1918. OUT_CMD(YES,'p',"","",0); /* Comment to output. */
  1919.  
  1920. CONTINUE(s_top);
  1921. INDENT;
  1922.     stmt(TO_OUTPUT,0);
  1923. OUTDENT;
  1924. if(was_next) LABEL(s_next);
  1925.  
  1926. skip_newlines(SAVE_COMMENTS);
  1927.  
  1928. /* Check for optional |@r until|. */
  1929. if(TOKEN1(a=next_byte())) BACK_UP@;
  1930. else
  1931.     {
  1932.     a = IDENTIFIER(a,next_byte());
  1933.  
  1934.     if(a == id_until)
  1935.         {
  1936.         flush_comments();
  1937.         rlevel--;
  1938.         expanding(until_CMD);
  1939.  
  1940.         IS_NEXT_PAREN(until);
  1941.         pu = SAVE_AFTER(&u,SAVE8,@')'); /* The |until| condition. */
  1942.         OUT_CMD(NO,'p',"","(%s)",2,u,pu);
  1943.  
  1944.         IF(ignore); @~ LP; @~ NOT; 
  1945.             @~ LP; @~ copy_out(u,pu,!macro); @~ RP;
  1946.              @~ RP;
  1947.         FREE_MEM(u,"repeat:u",SAVE8,eight_bits);
  1948.         }
  1949.     else BACK_UP@;
  1950.     }
  1951.  
  1952. GOTO(s_top);
  1953. flush_comments();
  1954.  
  1955. if(was_break) {CONTINUE(s_break);}
  1956.  
  1957. wlevel--;
  1958. rlevel--;
  1959. }
  1960.  
  1961. @ Expand a |do| statement.
  1962. @<Rdoc@>=
  1963. @r
  1964. /* Source construction: */
  1965.     do expr;
  1966.         {
  1967.         stmt;
  1968.         }
  1969.  
  1970. @n
  1971. /* Translation: */
  1972.     do NEXT@,@,expr
  1973.         stmt
  1974. NEXT:    continue
  1975. BREAK:    continue
  1976.  
  1977. @
  1978. @<Part 2@>=@[
  1979. X_FCN 
  1980. x_do(VOID)
  1981. {
  1982. eight_bits b;
  1983. sixteen_bits a;
  1984.  
  1985. /* Is the next a statement number? */
  1986. b = next_byte(); @~ BACK_UP@;
  1987.  
  1988. /* Don't expand the ordinary Fortran numbered |do|. */
  1989. if(b == constant) 
  1990.     {
  1991.     id0(id_do); /* Numbered |do|. */
  1992.     return;
  1993.     }
  1994.  
  1995. /* Expand the Ratfor |do|. */
  1996. expanding(do_CMD);
  1997. save_lbls(do_CMD,0L,max_stmt,max_stmt+1,2);
  1998.  
  1999. OUT_CMD(YES,'d',"","",0); /* Comment to output. */
  2000.  
  2001. /* The following |if| accounts for the possibility of a semicolon or left
  2002. brace immediately following the |do|. */
  2003. if(!TOKEN1(a = next_byte())) 
  2004.     a = IDENTIFIER(a,next_byte());
  2005.  
  2006. BACK_UP@;
  2007.  
  2008. if(!(a==id_while))
  2009.     {
  2010.     ID(DO); @~ if(!Fortran88) LABEL(s_next); @~ COPY_2TO(@'{',@';'); @~ NL;
  2011.     INDENT;
  2012.         stmt(TO_OUTPUT,0);
  2013.     OUTDENT;
  2014.     if(Fortran88)
  2015.         {
  2016.         ID(END); @~ ID(DO); 
  2017.         if(symbolic_label) id0(symbolic_label);
  2018.         NL;
  2019.         }
  2020.     else
  2021.         {
  2022.         CONTINUE(s_next);
  2023.         if(was_break) {CONTINUE(s_break);}
  2024.         }
  2025.     }
  2026.  
  2027. wlevel--;
  2028. rlevel--;
  2029. }
  2030.  
  2031. @ Expand a |for| statement. 
  2032. @<Rdoc@>=
  2033. @r
  2034. /* Source construction: */
  2035.     for(a;b;c)
  2036.         {stmt;}
  2037.  
  2038. @n
  2039. /* Translation: */
  2040.     a
  2041. TOP:    if(b) then
  2042.         stmt
  2043. NEXT:    continue
  2044.     c
  2045.     goto TOP
  2046.     endif
  2047. BREAK:    continue
  2048.  
  2049. @ Here, we must parse and save the three elements
  2050. of the |for|, then spit them out later.
  2051.  
  2052. @d SAVE8 200 /* Default length of buffer for parenthesized stuff like
  2053.             |if(...)@;|. */
  2054. @d BIG_SAVE8 10000 /* Default length for |case| text. */
  2055.  
  2056. @<Part 2@>=@[
  2057.  
  2058. X_FCN 
  2059. x_for(VOID)
  2060. {
  2061. eight_bits HUGE *a=NULL, HUGE *b=NULL, HUGE *c=NULL, 
  2062.      HUGE *pa, HUGE *pb, HUGE *pc;
  2063.  
  2064. expanding(for_CMD);
  2065. save_lbls(for_CMD,max_stmt,max_stmt+1,max_stmt+2,3);
  2066.  
  2067. /* Check for parenthesized list. */
  2068. IS_NEXT_PAREN(for);
  2069. pa = SAVE_AFTER(&a,SAVE8,@';'); /* Initialization. */
  2070. pb = SAVE_AFTER(&b,SAVE8,@';'); /* Test. */
  2071. pc = SAVE_AFTER(&c,SAVE8,@')'); /* Reinitialization. */
  2072.  
  2073. OUT_CMD(YES,'f',"","(%s;%s;%s)",6,a,pa,b,pb,c,pc); /* Comment to output. */
  2074.  
  2075. /* Initialization. */
  2076.     if(pa > a) {copy_out(a,pa,!macro); @~ NL;}
  2077.  
  2078. /* Conditional. */
  2079.     if(pb > b)
  2080.         {IF(s_top); @~ LP; @~ copy_out(b,pb,!macro); @~ RP; @~ THEN;}
  2081.     else {CONTINUE(s_top);}
  2082.  
  2083. /* Body. */
  2084.     INDENT;
  2085.         stmt(TO_OUTPUT,0);
  2086.  
  2087. /* Reinitialization. */
  2088.         if(was_next) {CONTINUE(s_next);}
  2089.         if(pc > c) 
  2090.             {
  2091.             OUT_CMD(NO,'f',"Reinitialization of",
  2092.                     "(%s;%s;%s)",6,a,pa,b,pb,c,pc); 
  2093.             copy_out(c,pc,!macro); @~ NL;
  2094.             }
  2095.         GOTO(s_top);
  2096.     OUTDENT;
  2097.     if(pb > b) {ENDIF;}
  2098.     if(was_break) {CONTINUE(s_break);}
  2099.     wlevel--;
  2100. rlevel--;
  2101. FREE_MEM(a,"for:a",SAVE8,eight_bits); 
  2102. FREE_MEM(b,"for:b",SAVE8,eight_bits); 
  2103. FREE_MEM(c,"for:c",SAVE8,eight_bits);
  2104. }
  2105.  
  2106. @ Expand an |if| statement.
  2107. @<Rdoc@>=
  2108. @r
  2109. /* Source construction: */
  2110.     if(expr)
  2111.         {stmt;}
  2112.     else if(expr)
  2113.         {stmt;}
  2114.     else
  2115.         {stmt;}
  2116.  
  2117. @n
  2118. /* Translation: */
  2119.     if(expr) then
  2120.         stmt
  2121.     else if(expr) then
  2122.         stmt
  2123.     else
  2124.         stmt
  2125.     endif
  2126.  
  2127. @
  2128. @<Part 2@>=@[
  2129.  
  2130. X_FCN 
  2131. x_if(VOID)
  2132. {
  2133. expanding(if_CMD);
  2134. OUT_CMD(YES,'i',"","",0);
  2135.  
  2136. XPN_BODY(IF,YES,THEN);
  2137.  
  2138. /* Hunt for |else| or |elseif|. */
  2139. WHILE()
  2140.     if(!XPN_ELSE(id_if,id_elseif,IF,YES,THEN)) break;
  2141.  
  2142. ENDIF;
  2143. flush_comments();
  2144. rlevel--;
  2145. }
  2146.  
  2147.  
  2148. @
  2149. @<Part 2@>=@[
  2150.  
  2151. SRTN 
  2152. xpn_body FCN((token1,scan_parens,token2))
  2153.     sixteen_bits token1 C0("")@;
  2154.     boolean scan_parens C0("")@;
  2155.     sixteen_bits token2 C1("")@;
  2156. {
  2157. LABEL(ignore); @~ id0(token1);
  2158.  
  2159. if(scan_parens) PARENS;
  2160. if(token2) id0(token2);
  2161. NL;
  2162.  
  2163. INDENT;
  2164.     stmt(TO_OUTPUT,0);
  2165. OUTDENT;
  2166. }
  2167.  
  2168. @
  2169. @<Part 2@>=@[
  2170.  
  2171. boolean 
  2172. xpn_else FCN((id_x,id_else_x,token1,scan_parens,token2))
  2173.     sixteen_bits id_x C0("")@;
  2174.     sixteen_bits id_else_x C0("")@;
  2175.     sixteen_bits token1 C0("")@;
  2176.     boolean scan_parens C0("")@;
  2177.     sixteen_bits token2 C1("")@;
  2178. {
  2179. sixteen_bits a;
  2180.  
  2181. skip_newlines(SAVE_COMMENTS);
  2182.  
  2183. if(TOKEN1(a= next_byte())) 
  2184.     { /* Not a keyword. */
  2185.     BACK_UP@;
  2186.     return NO;
  2187.     }
  2188. else
  2189.     {
  2190.     a = IDENTIFIER(a,next_byte());
  2191.  
  2192.     if(a == id_else_x)
  2193.         { /* |@r elseif| */
  2194.         flush_comments();
  2195.         ELSE;
  2196.         xpn_body(token1,scan_parens,token2);
  2197.         return YES;
  2198.         }
  2199.  
  2200.     if(a != id_else) 
  2201.         { /* Neither |else if| nor |else|. */
  2202.         BACK_UP@;
  2203.         return NO;
  2204.         }
  2205.     else
  2206.         { /* |@r else| */
  2207.         flush_comments();
  2208.         ELSE;
  2209.  
  2210.         if(TOKEN1(a= next_byte())) BACK_UP@;
  2211.         else
  2212.             { /* Possible |@r if| or |@r where|. */
  2213.             a = IDENTIFIER(a,next_byte());
  2214.  
  2215.             if(a == id_x) /* |else if|  or |else where@;| */
  2216.                 {
  2217.                 xpn_body(token1,scan_parens,token2);
  2218.                 return YES;
  2219.                 }
  2220.             else BACK_UP@;
  2221.             }
  2222.  
  2223.         if(out_pos > rst_pos) NL;  /* Terminate the |else|. */
  2224.  
  2225.         INDENT;
  2226.             stmt(TO_OUTPUT,0); /* Expand body of |else|. */
  2227.         OUTDENT;
  2228.         return NO;
  2229.         }
  2230.     }
  2231. }
  2232.  
  2233. @ The previous scan should have found all the |else|'s. If an |else| is
  2234. encountered anywhere else, it's an error and is just skipped.
  2235. @<Part 2@>=@[
  2236.  
  2237. X_FCN 
  2238. x_else(VOID)
  2239. {
  2240. UNEXPECTED("else");
  2241. }
  2242.  
  2243. X_FCN 
  2244. x_els_if(VOID)
  2245. {
  2246. UNEXPECTED("elseif");
  2247. }
  2248.  
  2249. @ Also, no |end| statements should appear explicitly anywhere; the
  2250. terminating |end| statement is inserted automatically. Therefore, if we
  2251. encounter any of these, it's an error.
  2252.  
  2253. @<Part 2@>=@[
  2254.  
  2255. X_FCN 
  2256. x_end(VOID)
  2257. {
  2258. UNEXPECTED("end");
  2259. }
  2260.  
  2261. X_FCN 
  2262. x_en_if(VOID)
  2263. {
  2264. UNEXPECTED("endif");
  2265. }
  2266.  
  2267. X_FCN 
  2268. x_en_interface(VOID)
  2269. {
  2270. UNEXPECTED("endinterface");
  2271. }
  2272.  
  2273. X_FCN 
  2274. x_en_module(VOID)
  2275. {
  2276. UNEXPECTED("endmodule");
  2277. }
  2278.  
  2279. X_FCN 
  2280. x_en_select(VOID)
  2281. {
  2282. UNEXPECTED("endselect");
  2283. }
  2284.  
  2285. X_FCN 
  2286. x_en_type(VOID)
  2287. {
  2288. UNEXPECTED("endtype");
  2289. }
  2290.  
  2291. X_FCN 
  2292. x_en_where(VOID)
  2293. {
  2294. UNEXPECTED("endwhere");
  2295. }
  2296.  
  2297. X_FCN 
  2298. x_procedure(VOID)
  2299. {
  2300. UNEXPECTED("procedure");
  2301. }
  2302.  
  2303. X_FCN 
  2304. x_then(VOID)
  2305. {
  2306. UNEXPECTED("then");
  2307. }
  2308.  
  2309. X_FCN 
  2310. x_until(VOID)
  2311. {
  2312. UNEXPECTED("until");
  2313. }
  2314.  
  2315. @ Expand a |@n9 where| statement.
  2316. @<Rdoc@>=
  2317. @r9
  2318. /* Source construction: */
  2319.     where(expr)
  2320.         {stmt;}
  2321.     else
  2322.         {stmt;}
  2323.  
  2324. @n[-n9]
  2325. /* Translation: */
  2326.     where(expr) 
  2327.         stmt
  2328.     else where
  2329.         stmt
  2330.     end where
  2331.  
  2332. @
  2333. @d id__ignore ignore
  2334.  
  2335. @<Part 2@>=@[
  2336.  
  2337. X_FCN 
  2338. x_where(VOID)
  2339. {
  2340. expanding(where_CMD);
  2341. OUT_CMD(YES,'h',"","",0);
  2342.  
  2343. XPN_BODY(WHERE,YES,ignore);
  2344. XPN_ELSE(id_where,id_elsewhere,WHERE,NO,ignore);
  2345.  
  2346. ENDWHERE;
  2347. rlevel--;
  2348. }
  2349.  
  2350. @ An error message about an unexpected keyword.
  2351.  
  2352. @d UNEXPECTED(id) unexpected(OC(id))
  2353.  
  2354. @<Part 2@>=@[
  2355.  
  2356. SRTN 
  2357. unexpected FCN((id))
  2358.     CONST outer_char id[] C1("Error message.")@;
  2359. {
  2360. RAT_ERROR(WARNING,"Unexpected keyword \"%s\" ignored",1,id);
  2361. }
  2362.  
  2363. @*1 Expand a |switch| statement. This is the most complicated \Ratfor\
  2364. statement. Several different kinds of expansions may be made, for
  2365. efficiency reasons. If the list of cases is fairly dense, with few gaps,
  2366. then a computed |goto| is used; otherwise, the |switch| is expanded into a
  2367. series of |if| statements. In order to know which expansion to make, the
  2368. entire |switch| must be read into memory first. 
  2369. @<Rdoc@>=
  2370. @r
  2371. /* Source construction: */
  2372.     switch(expr)
  2373.         {
  2374.         case 1:
  2375.             stmts;
  2376.             break;
  2377.  
  2378.         case 2:
  2379.             stmts;
  2380.             break;
  2381.  
  2382.         default:
  2383.             stmts;
  2384.             break;
  2385.         }
  2386.  
  2387. @n
  2388. /* Translation: */
  2389.     i123 = expr
  2390.     if(!(i123 == 1)) goto S2
  2391.         stmts
  2392.         goto 123
  2393.  
  2394. S2:    continue
  2395.     if(!(i123==2)) goto S3
  2396.     stmts
  2397.     goto 123
  2398. DFLT:    continue
  2399.     stmts
  2400.     goto 123
  2401. S3:    continue
  2402.     goto DFLT
  2403. 123:    continue
  2404.  
  2405. @ We need a flag to say that we're inside at least one |switch|, so we can
  2406. check whether the |case| or |default| statements are in valid places. We
  2407. also need various structures to hold the various parts of the |switch| as
  2408. it is parsed.
  2409.  
  2410. @<Typedef...@>=
  2411.  
  2412. IN_RATFOR int switch_level RSET(0);
  2413.  
  2414. /* The starting and ending positions of a token string. */
  2415. typedef struct
  2416.     {
  2417.     eight_bits HUGE *start, HUGE *next, HUGE *end;
  2418.     } TEXT;
  2419.  
  2420. /* The info for one |case| or |default|. */
  2421. typedef struct
  2422.     {
  2423.     STMT_LBL label; // Statement label assigned to this |case|.
  2424.     TEXT case_txt; // The token string for the |case| value.
  2425.     CASE_TYPE value; // The numerical value of the above string.
  2426.     TEXT txt; // The body of the |case| or |default|.
  2427.     boolean is_default; // Distinguishes between |default| and |case|.
  2428.     } CASE;
  2429.  
  2430. IN_RATFOR CASE HUGE *cur_case; // A pointer to the current case being processed.
  2431.  
  2432. /* A whole |switch|. */
  2433. typedef struct
  2434.     {
  2435.     CASE HUGE *cases; // The array of cases.
  2436.     unsigned short ncases; // How many cases?
  2437.     boolean has_default; // At most one |default| is allowed.
  2438.     } SWITCH;
  2439.  
  2440. IN_RATFOR SWITCH HUGE *switches; // Switches may be nested, so we need an array.
  2441.  
  2442. @ Memory is only allocated for |switches| and |cases| when and if it is
  2443. actually needed. However, once allocated, it is never deallocated.
  2444.  
  2445. For convenience, |switches[0]| and |cases[0]| are not used.
  2446.  
  2447. @d NSWITCHES 20 /* Nesting level for |switch| statements. */
  2448. @d NCASES 257 /* Number of |case| labels in a |switch|. */
  2449. @d cur_switch switches[switch_level]
  2450.  
  2451. @<Part 2@>=@[
  2452.  
  2453. X_FCN 
  2454. x_switch(VOID)
  2455. {
  2456. eight_bits HUGE *a=NULL, HUGE *pa;
  2457. outer_char temp[N_IDBUF];
  2458. unsigned short k;
  2459. boolean computed_goto = NO;
  2460. CASE_TYPE cmin=0,cmax; /* Minimum and maximum |case| values. */
  2461. CASE_TYPE mcases=0; // Spread in the case value.
  2462. unsigned short num_cases; // Number of cases.
  2463.  
  2464. expanding(switch_CMD);
  2465.  
  2466. if(switches==NULL) switches = GET_MEM("switches",NSWITCHES,SWITCH);
  2467.  
  2468. ++switch_level;
  2469. if(cur_switch.cases == NULL) 
  2470.     cur_switch.cases = GET_MEM("cur_switch.cases",NCASES,CASE); 
  2471. cur_switch.ncases = 0;
  2472. cur_switch.has_default = NO;
  2473.  
  2474. /* Allocate the zeroth case.  This won't be used, except if there's text
  2475. before the first |case|. */
  2476. cur_case = &cur_switch.cases[0];
  2477. cur_case->txt.next = cur_case->txt.start =
  2478.     GET_MEM("cur_case->txt.start",BIG_SAVE8,eight_bits); 
  2479. cur_case->txt.end = cur_case->txt.start + BIG_SAVE8;
  2480.  
  2481. save_lbls(switch_CMD,0L,s_next,max_stmt,1);
  2482.  
  2483. /* Look for the parenthesized expression. */
  2484. IS_NEXT_PAREN(switch);
  2485. pa = SAVE_AFTER(&a,SAVE8,@')'); /* Save the expression. */
  2486.  
  2487. OUT_CMD(YES,'s',"","(%s)",2,a,pa); /* Comment to output. */
  2488.  
  2489. if(Fortran88)
  2490.     {
  2491.     ID(SELECT); @~ ID(CASE); @~ LP; @~ copy_out(a,pa,!macro); @~ RP; @~ NL;
  2492.     }
  2493. INDENT;
  2494.     stmt(TO_MEMORY,BRACE_ONLY); /* Read the |switch| into memory. */ 
  2495.  
  2496.     if(Fortran88)
  2497.         {
  2498.         computed_goto = NO;
  2499.         }
  2500.     else @<Analyze the cases@>;
  2501.  
  2502.     if(computed_goto) @<Use computed |goto|@>@;
  2503.     else @<Use multiple |if|s@>@;
  2504. OUTDENT;
  2505.  
  2506. if(Fortran88) 
  2507.     {
  2508.     if(was_break) LABEL(s_break); 
  2509.     ID(END); @~ ID(SELECT);
  2510.         if(symbolic_label) id0(symbolic_label);
  2511.     NL;
  2512.     }
  2513. else if(was_break) {CONTINUE(s_break);}
  2514.  
  2515. wlevel--;
  2516. rlevel--;
  2517. switch_level--;
  2518.  
  2519. FREE_MEM(a,"switch:a",SAVE8,eight_bits);
  2520. }
  2521.  
  2522. @ First, evaluate all the cases. If they don't all evaluate to integers,
  2523. use |if| statements. Otherwise, if the ratio of the case value spread to
  2524. the total number of cases is less than |g_ratio|, then use a computed
  2525. |goto|. Also do that if the number of cases if greater than
  2526. |marginal_cases| (if the spread is less than |max_spread|). Otherwise, use
  2527. |if| statements.
  2528.  
  2529. @<Glob...@>=
  2530.  
  2531. IN_COMMON double g_ratio;
  2532. IN_COMMON CASE_TYPE max_spread;
  2533. IN_COMMON unsigned short marginal_cases;
  2534.  
  2535. IN_EVAL VAL HUGE *val_ptr, HUGE *val_heap;
  2536.  
  2537. @
  2538. @<Analyze the cases@>=
  2539. @{
  2540. unsigned short k;
  2541. VAL val;
  2542.  
  2543. @b
  2544. /* We need to find the minimum and maximum |case| value. */
  2545. cmin = LONG_MAX; // See |limits.h|.
  2546. cmax = LONG_MIN + 1; // The |+1| takes care of an \.{scc} bug.
  2547.  
  2548. for(k=1; k<=cur_switch.ncases; k++)
  2549.     {
  2550.     cur_case = &cur_switch.cases[k];
  2551.  
  2552.     if(cur_case->is_default) continue;
  2553.     
  2554. /* Call up the expression evaluator to reduce the |case| text to an
  2555. integer. */
  2556.     {
  2557.     extern boolean eval_msgs;
  2558.  
  2559.     eval_msgs = NO;
  2560.     EVALUATE(val,cur_case->case_txt.start,cur_case->case_txt.next);
  2561.     eval_msgs = YES;
  2562.     }
  2563.  
  2564.     switch(val.type)
  2565.         {
  2566.         case Int:
  2567.             cur_case->value = (CASE_TYPE)(val.value.i);
  2568.             break;
  2569.  
  2570.         case Double:
  2571.             RAT_ERROR(WARNING,
  2572. "Case value %#g of type double truncated to int",1,val.value.d); 
  2573.             cur_case->value = (CASE_TYPE)(val.value.d);
  2574.             break;
  2575.  
  2576.         default:
  2577. /* The case didn't evaluate to an integer. */
  2578.             computed_goto = NO;
  2579.             goto not_integer;
  2580.         }
  2581.  
  2582. /* Running determination of the minimum and maximum |case| value. */
  2583.     if(cur_case->value < cmin) cmin = cur_case->value;
  2584.     if(cur_case->value > cmax) cmax = cur_case->value;
  2585.     }
  2586.  
  2587. if(cur_switch.ncases==1 && s_default!=0) 
  2588.     {
  2589.     mcases = 0;
  2590.     computed_goto = YES;
  2591.     goto not_integer;
  2592.     }
  2593. else mcases = (cmax - cmin + 1); // Spread in the cases.
  2594.  
  2595. if((num_cases = cur_switch.ncases-(unsigned short)(s_default!=0)) == 0)
  2596.     {
  2597.     computed_goto = NO;
  2598.     goto not_integer;
  2599.     }
  2600. computed_goto = BOOLEAN((num_cases > marginal_cases &&
  2601.             mcases < max_spread) ? YES : 
  2602.             ((double)mcases)/num_cases <= g_ratio); 
  2603.  
  2604. not_integer: ;
  2605. }
  2606.  
  2607. @ We use the computed |goto| when the list of cases is fairly dense, with
  2608. few gaps. Out of bounds cases branch to the |default| if present, or around
  2609. the whole |switch| otherwise.
  2610.  
  2611. @<Use computed |goto|@>=
  2612. @{
  2613. CASE_TYPE m; // Indexes case values.
  2614. unsigned short k; // Indexes the cases.
  2615.  
  2616. @b
  2617. /* Generate computed |goto| to handle the cases; fill in any gaps. */
  2618. OUTDENT;
  2619. if(mcases > 0) {ID(GOTO); @~ LP;}
  2620.  
  2621. for(m=0; m<mcases; m++,m<mcases ? COMMA : RP)
  2622.     LABEL(label_case(cmin,m));
  2623.  
  2624. if(mcases > 0) 
  2625.     {
  2626.     COMMA; @~ LP; @~ copy_out(a,pa,!macro); @~ RP;
  2627.         @~ MINUS; @~ LP; @~ NUMBER(cmin-1); @~ RP; @~ NL;
  2628.     }
  2629.  
  2630. /* Handle the out-of-bound statements. (If the previous |goto| was out of
  2631. range, control passes to here.) */
  2632. GOTO(s_default ? s_default : (was_break=YES,s_break)); 
  2633. INDENT;
  2634.  
  2635. /* Output the various cases. */
  2636. for(k=1; k<=cur_switch.ncases; k++)
  2637.     {
  2638.     cur_case = &cur_switch.cases[k];
  2639.  
  2640.     show_cmd(cur_case);
  2641.     OUTDENT;
  2642.     CONTINUE(cur_case->label);
  2643.     INDENT;
  2644.     copy_out(cur_case->txt.start,cur_case->txt.next,!macro);
  2645.     rlevel--;
  2646.     }
  2647. }
  2648.  
  2649. @ This code is used when the computed |@r goto| is not appropriate.  In this
  2650. case, the |switch| is expanded into a series of multiple |if|s.
  2651.  
  2652. @<Use multiple...@>=
  2653. {
  2654. boolean case_ended_with_break = NO;
  2655. boolean made_temp = YES; /* Did we construct a temporary integer for the
  2656.                 |switch|? */
  2657.  
  2658. /* |made_temp == NO| means the expression is a single identifier. */
  2659. if(!Fortran88 && (made_temp = BOOLEAN(!((pa-a)==2 && !TOKEN1(*a)))))
  2660.     {
  2661. /* Make a temporary integer identifier to effect the comparisons. */
  2662.     SPRINTF(N_IDBUF,temp,`"I%d",s_break`);
  2663.     to_ASCII(temp);
  2664.     icase = ID_NUM((ASCII HUGE *)temp,(ASCII HUGE *)(temp+STRLEN(temp)));
  2665.  
  2666.     id0(icase); @~ EQUALS; @~ copy_out(a,pa,!macro); @~ NL;
  2667.     }
  2668.  
  2669. for(k=1; k<=cur_switch.ncases; k++) 
  2670.     @<Expand a |case| or |default|@>@; 
  2671.  
  2672. if(!Fortran88)
  2673.     {
  2674.     CONTINUE(s_case); /* Finish off the last |case|. */
  2675.     if(s_default) 
  2676.         {
  2677.         GOTO(s_default); /* Jump to the |default|, if present. */ 
  2678.         }
  2679.     }
  2680. }
  2681.  
  2682. @ Display a |case| or |default| command as an output comment.
  2683.  
  2684. @<Part 2@>=@[
  2685.  
  2686. SRTN 
  2687. show_cmd FCN((cur_case))
  2688.     CONST CASE HUGE *cur_case C1("")@;
  2689. {
  2690. if(cur_case->is_default) 
  2691.     {
  2692.     expanding(default_CMD);
  2693.     OUT_CMD(NO,'t',"",":",0); 
  2694.     }
  2695. else 
  2696.     {
  2697.     expanding(case_CMD);
  2698.     OUT_CMD(NO,'c',""," %s:",2,
  2699.         cur_case->case_txt.start,cur_case->case_txt.next);
  2700.     }
  2701. }
  2702.  
  2703. @ Return the appropriate label: If it's a |case|, generate a new label; if
  2704. it's a |default|, return |s_default|; otherwise, return |s_default| if a
  2705. |default| was present, or |s_break| otherwise.
  2706.  
  2707. @<Part 2@>=@[
  2708.  
  2709. STMT_LBL 
  2710. label_case FCN((cmin,m))
  2711.     CASE_TYPE cmin C0("")@;
  2712.     CASE_TYPE m C1("")@;
  2713. {
  2714. CASE_TYPE num = cmin + m;
  2715. unsigned short k;
  2716.  
  2717. /* Check for ordinary cases. */
  2718. for(k=1; k<=cur_switch.ncases; k++)
  2719.     {
  2720.     cur_case = &cur_switch.cases[k];
  2721.  
  2722.     if(!cur_case->is_default && cur_case->value == num)
  2723.         return cur_case->label = s_case = max_stmt++;
  2724.     }
  2725.  
  2726. /* Look for |default|. */
  2727. for(k=1; k<=cur_switch.ncases; k++)
  2728.     if(cur_case->is_default) return s_default;
  2729.     
  2730. return s_break; // A gap.
  2731. }
  2732.  
  2733. @
  2734. @<Expand a |case|...@>=
  2735. {
  2736. cur_case = &cur_switch.cases[k];
  2737.  
  2738. if(Fortran88)
  2739.     if(k==1) s_case = max_stmt++;
  2740.     else 
  2741.         {
  2742.         @<Did last |case| end with ``|break;|''?@>@;
  2743.         if(!case_ended_with_break) {GOTO(s_case);}
  2744.         }
  2745.     
  2746. show_cmd(cur_case);
  2747. OUTDENT;
  2748. if(Fortran88)
  2749.     {
  2750.     ID(CASE); 
  2751.  
  2752.     if(cur_case->is_default) ID(DEFAULT);
  2753.     else
  2754.         {
  2755.         if(*cur_case->case_txt.start != @'(') LP;
  2756.         copy_out(cur_case->case_txt.start,cur_case->case_txt.next,
  2757.                 !macro);
  2758.         if(*(cur_case->case_txt.next - 1) != @')') RP;
  2759.         }
  2760.     NL;
  2761.     INDENT;
  2762.     if(k > 1 && !case_ended_with_break) 
  2763.         {
  2764.         CONTINUE(s_case);
  2765.         s_case = max_stmt++;
  2766.         }
  2767.     }
  2768. else
  2769.     {
  2770.     if(cur_case->is_default) {CONTINUE(s_default);}
  2771.     else 
  2772.         {
  2773.         IF(s_case); @~ LP; @~ NOT; @~ LP; 
  2774. /* The |made_temp?@e@:@e| form of the next command crashed the Apollo
  2775. compiler. */ 
  2776.             if(made_temp) id0(icase); else copy_out(a,pa,!macro); 
  2777.             EQ_EQ; 
  2778.             copy_out(cur_case->case_txt.start,
  2779.                 cur_case->case_txt.next,!macro); 
  2780.             RP; @~ RP;
  2781.         GOTO(s_case=max_stmt++);
  2782.         }
  2783.     INDENT;
  2784.     }
  2785.  
  2786. /* Recall the text stored previously. */
  2787. copy_out(cur_case->txt.start,cur_case->txt.next,!macro);
  2788.  
  2789. rlevel--;
  2790. }
  2791.  
  2792. @ To pretty up the \FORTRAN-88 output, we check to see if the previous case
  2793. ended with a |break| statement. If so, we don't output a |@r goto| to the
  2794. next case.
  2795. @<Did last...@>=
  2796. @{
  2797. CASE HUGE *last_case = &cur_switch.cases[k-1];
  2798.  
  2799. @b
  2800. if(PTR_DIFF(long,last_case->txt.next,last_case->txt.start) >= 3)
  2801.     case_ended_with_break = 
  2802.         BOOLEAN(MEMCMP(last_case->txt.next-3,break_tokens,3) == 0);
  2803. else case_ended_with_break = NO;
  2804. }
  2805.  
  2806. @ Expand a |case| statement.
  2807. @<Part 2@>=@[
  2808. X_FCN x_case(VOID)
  2809. {
  2810. if(switch_level ==0)
  2811.     {
  2812.     not_switch(OC("case"));
  2813.     return;
  2814.     }
  2815.  
  2816. expanding(case_CMD);
  2817.  
  2818. @<Initialize a |case| or |default|@>;
  2819. cur_case->case_txt.next = SAVE_AFTER(&cur_case->case_txt.start,SAVE8,@':');
  2820. cur_case->is_default = NO;
  2821.  
  2822. @<Check for duplicate |case|s@>@;
  2823.  
  2824. rlevel--;
  2825. }
  2826.  
  2827. @ This fragment is used in expanding |case| and |default| statements; it
  2828. sets things up so the text is stored in the proper place.
  2829. @<Initialize a |case|...@>=
  2830.  
  2831. *cur_case->txt.next = '\0'; /* Terminate previous text. */
  2832.  
  2833. /* Get address of next available |CASE| structure. */
  2834. cur_case = &cur_switch.cases[++cur_switch.ncases];
  2835.  
  2836. /* If that hasn't been allocated yet, do so. */
  2837. if(cur_case->case_txt.start==NULL) 
  2838.     {
  2839.     cur_case->case_txt.start = 
  2840.         GET_MEM("cur_case->case_txt.start",SAVE8,eight_bits);
  2841.     cur_case->case_txt.end = cur_case->case_txt.start + SAVE8;
  2842.  
  2843.     cur_case->txt.start = 
  2844.         GET_MEM("cur_case->txt.start",BIG_SAVE8,eight_bits);
  2845.     cur_case->txt.end = cur_case->txt.start + BIG_SAVE8;
  2846.     }
  2847.  
  2848. /* Initialize the pointer to beginning of buffer. */
  2849. cur_case->txt.next = cur_case->txt.start;
  2850.  
  2851. @
  2852. @<Check for duplicate...@>=
  2853. {
  2854. unsigned short k;
  2855. CONST CASE HUGE *old_case;
  2856.  
  2857. for(k=1; k<cur_switch.ncases; k++)
  2858.     {
  2859.     old_case = &cur_switch.cases[k];
  2860.  
  2861.     if(web_strcmp((CONST ASCII HUGE *)cur_case->case_txt.start,
  2862.             (CONST ASCII HUGE *)cur_case->case_txt.next,
  2863.                (CONST ASCII HUGE *)old_case->case_txt.start,
  2864.             (CONST ASCII HUGE *)old_case->case_txt.next) == EQUAL)
  2865.         {
  2866.         RAT_ERROR(ERROR,"Duplicate case value in switch",0);
  2867.         break;
  2868.         }
  2869.     }
  2870. }
  2871.  
  2872. @ Expand a |default| statement. This just initializes stuff so the text is
  2873. stored in the proper place.
  2874.  
  2875. @<Part 2@>=@[
  2876.  
  2877. X_FCN 
  2878. x_default(VOID)
  2879. {
  2880. if(switch_level == 0) 
  2881.     {
  2882.     not_switch(OC("default"));
  2883.     return;
  2884.     }
  2885.  
  2886. expanding(default_CMD);
  2887.  
  2888. if(cur_switch.has_default)
  2889.     RAT_ERROR(ERROR,"Only one default allowed per switch",0);
  2890. else cur_switch.has_default = YES;
  2891.  
  2892. @<Initialize a |case| or |default|@>;
  2893. cur_case->case_txt.next = cur_case->case_txt.start;
  2894. cur_case->is_default = YES;
  2895.  
  2896. cur_case->label = s_default = max_stmt++;
  2897.  
  2898. char_after(':'); /* |default| must be followed immediately by colon. */
  2899. rlevel--;
  2900. }
  2901.  
  2902. @*1 Program units. 
  2903. @r9
  2904. @v .IN. "\\in" <
  2905. @n9
  2906. @v .IN. "\\in" <
  2907. @<Rdoc@>=
  2908. @r9
  2909. /* Source construction: */
  2910. program main
  2911. {stmt;}
  2912.  
  2913. /* Translation: */
  2914. @n9
  2915.     program main
  2916.     stmt
  2917.     end program main
  2918.  
  2919. /* Source: */
  2920. @r9
  2921. subroutine s(x,y,z)
  2922.     real x,y,z;
  2923. {stmt;}
  2924.  
  2925. /* Translation: */
  2926. @n9
  2927.     subroutine s(x,y,z)
  2928.     real x,y,z
  2929.  
  2930.     stmt
  2931.     end subroutine s
  2932.  
  2933. /* Source: */
  2934. @r9
  2935. function f(i)
  2936.     integer i;
  2937. {
  2938. return 10;
  2939. }
  2940.  
  2941. /* Translation: */
  2942. @n9
  2943.     function f(i)
  2944.     integer i
  2945.  
  2946.     f = 10
  2947.     return
  2948.     end function f
  2949.  
  2950. /* Source: */
  2951. @r9
  2952. block data work
  2953. {
  2954. common/wrkcom/ a,b,c;
  2955. data a/1.0/, b/2.0/, c/0.0/;
  2956. }
  2957.  
  2958. /* Translation: */
  2959. @n9
  2960.     block data work
  2961.     common/wrkcom/ a,b,c
  2962.     data a/1.0/, b/2.0/, c/0.0/
  2963.     end block data work
  2964.  
  2965. /* Source: */
  2966. @r9
  2967. module integer_sets
  2968. {
  2969. integer i;
  2970.  
  2971. type set
  2972.     {
  2973.     private:
  2974.     integer card;
  2975.     };
  2976.  
  2977. interface operator (.IN.)
  2978.     {
  2979.     module procedure element;
  2980.     }
  2981. }
  2982.  
  2983. /* Translation: */
  2984. @n9
  2985.     module integer_sets
  2986.     integer i
  2987.  
  2988.     type set
  2989.         private
  2990.         integer card
  2991.     end type set
  2992.  
  2993.     interface operator (.IN.)
  2994.         module procedure element
  2995.     end interface
  2996.  
  2997.     end module integer_sets
  2998.  
  2999. @ Expand a |@r program|, |@r9 module|, |@r subroutine|, or |@r function|
  3000. statement. We define a \WEB\ macro to generate separate functions.
  3001.  
  3002. @#if 0
  3003. if(brace_level != 0)
  3004.     {
  3005.     RAT_ERROR(ERROR,
  3006. "Missing '}' (level %d) at beginning of %s; \
  3007. END statement inserted",2,brace_level,#type);
  3008.     END;
  3009.     brace_level = 0;
  3010.     }
  3011. @#endif
  3012.  
  3013. @m X_ROUTINE(type,is_fcn,check_id)@/
  3014. X_FCN x_##type(VOID)
  3015. {
  3016. sixteen_bits a;
  3017. eight_bits b;
  3018.  
  3019. expanding(type##_CMD);
  3020.  
  3021. // Insert |brace_level| check here.
  3022.  
  3023. WHILE()
  3024.     {
  3025.     a = next_byte();
  3026.     
  3027.     if(!(a == @' ' || a == tab_mark))
  3028.         break;
  3029.     }
  3030.  
  3031. if(TOKEN1(a))
  3032.     {
  3033.     $P if(check_id)
  3034.         RAT_ERROR(ERROR,"Expected identifier after \"%s\"",1,#type);
  3035.     $P endif
  3036.     BACK_UP@;
  3037.     cur_fcn = NO_FCN;
  3038.     is_function = NO;
  3039.     }
  3040. else
  3041.     {
  3042.     cur_fcn = IDENTIFIER(a,next_byte());
  3043.     is_function = is_fcn;
  3044.     }
  3045.  
  3046. id0(id_##type); @~ id0(cur_fcn); /* |@r subroutine sub| */
  3047.  
  3048. if(cur_fcn == id_procedure) 
  3049.     { // |@r9 module procedure test;|
  3050.     COPY_TO(@';'); @~ NL;
  3051.     }
  3052. else 
  3053.     {
  3054.     b = next_byte(); @~ BACK_UP@; 
  3055.     if(b == @'(') PARENS; /* Routine with arguments. */
  3056.     NL; // Start the body on the next line.
  3057.     EAT_AUTO_SEMI;
  3058.     skip_newlines(COPY_COMMENTS);
  3059.     INDENT;
  3060.         copy_out(insert.type.start,insert.type.end,!macro);
  3061.         out_char(@';');
  3062.         COPY_2TO(@'{',NOT_AFTER);
  3063.         if(psave_buffer > save_buffer) NL; /* Argument declarations,
  3064.            with blank line between argument declarations and body. */
  3065.         brace_level++;
  3066.             stmt(TO_OUTPUT,BRACE_ONLY);
  3067.         brace_level--;
  3068.     OUTDENT;
  3069.  
  3070.     ID(END); 
  3071.     if(Fortran88) {id0(id_##type); @~ id0(cur_fcn);}
  3072.     NL;
  3073.     }
  3074.  
  3075. cur_fcn = NO_FCN; // No longer inside a function.
  3076.  
  3077. rlevel--;
  3078. }
  3079.  
  3080. @<Part 2@>=@[
  3081.  
  3082. X_ROUTINE(program,NO,YES)@;
  3083. X_ROUTINE(module,NO,YES)@;
  3084. X_ROUTINE(subroutine,NO,YES)@;
  3085. X_ROUTINE(function,YES,YES)@;
  3086. X_ROUTINE(blockdata,NO,NO)@;
  3087. X_ROUTINE(interface,NO,NO)@;
  3088.  
  3089. @ The |@r block data| statement has optional spaces.
  3090. @<Part 2@>=@[
  3091.  
  3092. X_FCN 
  3093. x_block(VOID)
  3094. {
  3095. sixteen_bits a;
  3096.  
  3097. if(TOKEN1(a=next_byte()))
  3098.     {
  3099.     BACK_UP@;
  3100.     id0(id_block);
  3101.     }
  3102. else
  3103.     {
  3104.     a = IDENTIFIER(a,next_byte());
  3105.  
  3106.     if(a == id_data) x_blockdata();
  3107.     else
  3108.         {
  3109.         BACK_UP@;
  3110.         id0(a);
  3111.         }
  3112.     }
  3113. }
  3114.  
  3115. @*1 Expand a |@r9 contains| statement.
  3116. @<Rdoc@>=
  3117. @r9
  3118. /* Source construction: */
  3119. subroutine outer
  3120. {
  3121. call inner(a);
  3122.  
  3123. contains:
  3124. subroutine inner(b)
  3125. {}
  3126. }
  3127.  
  3128. /* Translation:  The |@r9 contains| is appropriately outdented. */
  3129.  
  3130. @ We do nothing here except outdent the |@r9 contains|.
  3131.  
  3132. @<Part 2@>=@[
  3133.  
  3134. X_FCN 
  3135. x_contains(VOID)
  3136. {
  3137. OUTDENT;
  3138. ID(CONTAINS);
  3139. char_after(':');
  3140. NL;
  3141. INDENT;
  3142. }
  3143.  
  3144. @*1 Expand a |@r9 type| statement.
  3145.  
  3146. @<Rdoc@>=
  3147. @r9
  3148. /* Source construction: */
  3149. type person
  3150.     {
  3151.     integer i;
  3152.     real x;
  3153.     };
  3154.  
  3155. @n9
  3156. /* Translation: */
  3157. type person
  3158.     integer i
  3159.     real x
  3160. end type person
  3161.  
  3162. @ We define a macro to generate separate functions.
  3163.  
  3164. @m X_STRUCT(type)@/
  3165.  
  3166. X_FCN x_##type(VOID)
  3167. {
  3168. sixteen_bits a;
  3169. eight_bits b;
  3170.  
  3171. b = next_byte(); @~ BACK_UP@; 
  3172. if(b == @',') {} /* Access spec. */
  3173. else if(b==@'(') 
  3174.     {
  3175.     id0(id_##type);
  3176.     return;
  3177.     }
  3178.  
  3179. expanding(type##_CMD);
  3180.  
  3181. if(TOKEN1(a= next_byte())) 
  3182.     {
  3183.     RAT_ERROR(ERROR,"Expected identifier after \"%s\"",1,#type);
  3184.     BACK_UP@;
  3185.     cur_struct = NO_FCN;
  3186.     }
  3187. else
  3188.     {
  3189.     cur_struct = IDENTIFIER(a,next_byte());
  3190.     }
  3191.  
  3192. id0(id_##type); @~ id0(cur_struct); /* |@r9 type person| */
  3193. NL; // Start the body on the next line.
  3194. INDENT;
  3195.     brace_level++;
  3196.     stmt(TO_OUTPUT,BRACE_ONLY);
  3197.     brace_level--;
  3198. OUTDENT;
  3199.  
  3200. ID(END); @~ id0(id_##type); @~ id0(cur_struct);
  3201. char_after(';'); @~ OUT_CHAR(';');
  3202.  
  3203. wlevel--;
  3204. rlevel--;
  3205. }
  3206.  
  3207. @<Part 2@>=@[
  3208. X_STRUCT(type)@;
  3209. @#if 0
  3210.     X_STRUCT(module)@;
  3211. @#endif
  3212.  
  3213. @ Expand a |return| statement. Turns construction `|@r return expr@;|' into `|@n
  3214. f = expr; return;|'
  3215. @<Part 2@>=@[
  3216.  
  3217. X_FCN 
  3218. x_return(VOID)
  3219. {
  3220. eight_bits HUGE *return_expr=NULL, HUGE *pr;
  3221.  
  3222. expanding(return_CMD);
  3223.  
  3224. /* Save the return expression, if it's there. */
  3225. if((pr=SAVE_AFTER(&return_expr,SAVE8,@';')) > return_expr)
  3226.     {
  3227.     if(!is_function)
  3228.         RAT_ERROR(ERROR,
  3229. "Can't return value from program or subroutine",0);
  3230.     else
  3231.         {
  3232.         OUT_CMD(YES,'r',""," %s",2,return_expr,pr);
  3233.         id0(cur_fcn); @~ EQUALS; @~
  3234.             copy_out(return_expr,pr,!macro); @~ NL; 
  3235.         }
  3236.     }
  3237.  
  3238. RETURN;
  3239. rlevel--;
  3240. FREE_MEM(return_expr,"return_expr",SAVE8,eight_bits);
  3241. }
  3242.  
  3243. @ This function implements the |$DO| and |$UNROLL| built-ins.  |$DO| is
  3244. defined in \FTANGLE.
  3245.  
  3246. @<Part 2@>=@[
  3247.  
  3248. X_FCN 
  3249. x_unroll(VOID)
  3250. {
  3251. eight_bits HUGE *I = NULL, HUGE *pI;
  3252. eight_bits HUGE *Imin = NULL, HUGE *pImin;
  3253. eight_bits HUGE *Imax = NULL, HUGE *pImax;
  3254. eight_bits HUGE *Di = NULL, HUGE *pDi;
  3255. eight_bits HUGE *txt = NULL, HUGE *ptxt;
  3256. int i,imin,imax,di;
  3257. name_pointer n;
  3258. text_pointer t;
  3259. eight_bits temp[20];
  3260. extern int last_bytes;
  3261. extern boolean saved_token;
  3262. eight_bits c;
  3263.  
  3264. expanding(_DO_CMD);
  3265.  
  3266. IS_NEXT_PAREN("$DO");
  3267.  
  3268. pI = SAVE_AFTER(&I,SAVE8,@',');
  3269.  
  3270. if(TOKEN1(*I))
  3271.     {
  3272.     RAT_ERROR(ERROR, "Expected identifier for first argument of $DO; \
  3273. expansion aborted",0);
  3274.     return;
  3275.     }
  3276.  
  3277. pImin = SAVE_AFTER(&Imin,SAVE8,@',');
  3278. imin = neval(Imin,pImin);
  3279.  
  3280. pImax = SAVE_AFTER(&Imax,SAVE8,@',');
  3281. imax = neval(Imax,pImax);
  3282.  
  3283. pDi = SAVE_AFTER(&Di,SAVE8,@')');
  3284. di = neval(Di,pDi);
  3285.  
  3286. EAT_AUTO_SEMI;
  3287. skip_newlines(NO);
  3288.  
  3289. c = next_byte();
  3290.  
  3291. if(!(c==@'{' || c==@'('))
  3292.     {
  3293.     RAT_ERROR(ERROR, "Was expecting '{' or '(', not '%c', after $DO(); \
  3294. expansion aborted", 1, XCHR(c));
  3295.     return;
  3296.     }
  3297.  
  3298. /* Absorb the body of the |$DO|.  Tell |next_byte| to not expand macros, so
  3299. the loop counter can be used as an argument to a macro such as |$IFCASE|. */
  3300. mac_protected = YES;
  3301.  ptxt = SAVE_AFTER(&txt, BIG_SAVE8, c==@'{' ? @'}' : @')');
  3302. mac_protected = NO;
  3303.  
  3304. n = name_dir + IDENTIFIER(*I,*(I+1));
  3305. n->info.Macro_type = IMMEDIATE_MACRO;
  3306. t = GET_MEM("equiv",2,text);
  3307. n->equiv_or_xref = (EQUIV)t;
  3308. t->tok_start = temp;
  3309. t->moffset = 2;
  3310.  
  3311. if(!((di >= 0 && imax < imin) || (di < 0 && imax > imin)))
  3312.    for(i=imin;di >= 0 ? i<=imax : i>=imax; i+=di)
  3313.     {
  3314.     STRNCPY(temp,I,2);
  3315.     sprintf((char *)(temp+2),"%c%d%c",XCHR(constant),i,XCHR(constant));
  3316.     to_ASCII(temp+2);
  3317.     (t+1)->tok_start = temp + STRLEN(temp);
  3318.     copy_out(txt,ptxt,!macro);
  3319.     if(i == imax) break;
  3320.     }
  3321.  
  3322. rlevel--;
  3323.  
  3324. FREE_MEM(t,"t",2,text);
  3325. n->equiv_or_xref = NULL;
  3326. n->info.Macro_type = NOT_DEFINED;
  3327.  
  3328. FREE_MEM(I,"unroll:I",SAVE8,eight_bits);
  3329. FREE_MEM(Imin,"unroll:Imin",SAVE8,eight_bits);
  3330. FREE_MEM(Imax,"unroll:Imax",SAVE8,eight_bits);
  3331. FREE_MEM(txt,"unroll:txt",SAVE8,eight_bits);
  3332. }
  3333.  
  3334. @ Initialize automatic insertion material.
  3335. @m INI_INSERT(type) insert.type.start = insert.type.end =
  3336.     GET_MEM(#type,2,eight_bits) 
  3337.  
  3338. @<Part 2@>=@[
  3339.  
  3340. SRTN 
  3341. ini_Ratfor(VOID)
  3342. {
  3343. INI_INSERT(program);
  3344. INI_INSERT(module);
  3345. INI_INSERT(subroutine);
  3346. INI_INSERT(function);
  3347. INI_INSERT(blockdata);
  3348. INI_INSERT(interface);
  3349. }
  3350.  
  3351. @* INDEX.
  3352.