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

  1. @z --- macs.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{MACS.WEB} % Macro processing for FTANGLE
  11.  
  12. @c
  13.  
  14. @* MACROS. In the C~version of \WEB, namely \CWEB, the macro processor was
  15. removed since C~has its own preprocessor. However, there are advantages to
  16. having an internal processor when several languages are involved, even when
  17. one wants to run a macro preprocessor on each language separately. For
  18. example, the internal processor can modify the text of the outer macros, so
  19. that setting one switch can affect conditional compilation in several
  20. languages.
  21.  
  22. Here we collect the routines dealing with WEB's macro processor, which is
  23. C-like. 
  24.  
  25. (Parts of this code are inelegant; the first goal was to achieve the
  26. desired functionality. Some of the difficulties stemmed from attempting to
  27. integrate this code into \CWEB. In any event, although one might achieve
  28. somewhat more compact and elegant code by rewriting the macro processor
  29. from scratch, that's not a trivial job.)
  30.  
  31. @m _MACS_
  32. @d _MACS_h
  33.  
  34. @A
  35. @<Include files@>@;
  36. @<Typedef declarations@>@;
  37. @<Prototypes@>@;
  38. @<Global variables@>@;
  39.  
  40. @I typedefs.hweb /* Declarations common to both \FTANGLE\ and \FWEAVE. */
  41.  
  42. @I t_codes.hweb
  43. @I texts.hweb
  44. @I stacks.hweb
  45. @I val.hweb
  46. @I trunc.hweb
  47.  
  48. @i macs.hweb /* Macro definitions. */
  49.  
  50. @
  51. @<Include...@>=
  52. #include "map.h"
  53.  
  54. @ The function prototypes must appear before the global variables.
  55. @<Proto...@>=
  56.  
  57. #include "t_type.h" /* Prototypes for \.{ftangle.web}, etc. */
  58.  
  59. @ A token list of the current macro arguments is allocated dynamically. 
  60.  
  61. @<Glob...@>=
  62.  
  63. IN_COMMON sixteen_bits HUGE *args; /* Token list of current macro arguments.
  64.                 Allocated in |predefine_macros| just below. */
  65. IN_COMMON BUF_SIZE max_margs;   // Allocated length of |args|.
  66.  
  67. @ There may be predefined macros. These must be inserted into the
  68. |macrobuf| during |common_init|. 
  69. @a
  70. SRTN predefine_macros(VOID)
  71. {
  72. @<Allocate new macro buffer@>;
  73.  
  74. @<Define internal macros@>; /* We accrete to this from various places, as
  75.         it becomes convenient to discuss the particular macro. */
  76. t_macros(); // Internal macros from \.{ftangle.web}.
  77. e_macros(); // Internal macros from \.{eval.web}.
  78. }
  79.  
  80. @ We also introduce the concept of {\it internal macros}.  These are
  81. identifiers prefaced by `\.{\#\&}'. The identifier corresponds to a
  82. function that is executed during macro expansion; the function places stuff
  83. into the macro buffer.  Internal macros are intended to be used only by the
  84. designer of \FWEB, not by the user.
  85.  
  86. @<Typedef...@>=
  87.  
  88. typedef struct
  89.     {
  90.     const char *name; // Identifier.
  91.     int len; // Length of identifier. Filled in by |ini_internal_fcns|.
  92.     SRTN (*expnd)PROTO((int,unsigned char **)); 
  93.         /* Function that expands this token.  This prototype really
  94. should read |(int,PARGS)|, but that didn't work on the DECstation.  The
  95. name |expand| also seemed special to the DECstation. */ 
  96.     boolean Language;
  97.     eight_bits nargs;
  98.     boolean var_args;
  99.     boolean recursive;
  100.     sixteen_bits id; // The id code returned from |id_lookup|.
  101.     } INTERNAL_FCN;
  102.  
  103. @ Here are all the internal functions and the associated names that invoke
  104. them. 
  105. @<Glob...@>=
  106.  
  107. INTERNAL_FCN internal_fcns[] = {
  108.     {"$$ASCII",0,i_ascii_,0xF,1,NO,NO},
  109.     {"$ASSERT",0,i_assert_,0xF,1,NO,NO},
  110.     {"$$CONST",0,i_const_,0xF,2,YES,NO},
  111.     {"$DEFINE",0,i_define_,0xF,1,NO,NO},
  112.     {"_DUMPDEF",0,i_dumpdef_,0xF,0,YES,NO},
  113.     {"$DUMPDEF",0,i_dumpdef_,0xF,0,YES,NO},
  114.     {"$$ERROR",0,i_error_,0xF,1,NO,NO},
  115.     {"$$EVAL",0,i_eval_,0xF,1,NO,NO},
  116.     {"$$GETENV",0,i_getenv_,0xF,1,NO,NO},
  117.     {"$IF",0,i_if_,0xF,3,NO,YES},
  118.     {"$IFCASE",0,i_ifcase_,0xF,1,YES,YES},
  119.     {"$IFDEF",0,i_ifdef_,0xF,3,NO,YES},
  120.     {"$IFNDEF",0,i_ifndef_,0xF,3,NO,YES},
  121.     {"$IFELSE",0,i_ifelse_,0xF,4,NO,YES},
  122.     {"_INPUT_LINE",0,i_inp_line_,0xF,0,NO,NO},
  123.     {"$INPUT_LINE",0,i_inp_line_,0xF,0,NO,NO},
  124.     {"_LANGUAGE",0,i_lang_,0xF,0,NO,NO},
  125.     {"$LANGUAGE",0,i_lang_,0xF,0,NO,NO},
  126.     {"$$LC",0,i_lowercase_,0xF,1,NO,NO},
  127.     {"$$LEN",0,i_len_,0xF,1,NO,NO},
  128.     {"$$LOG",0,i_log_,0xF,2,NO,NO},
  129.     {"_LANGUAGE_NUM",0,i_lnum_,0xF,0,NO,NO},
  130.     {"$LANGUAGE_NUM",0,i_lnum_,0xF,0,NO,NO},
  131.     {"$M",0,i_define_,0xF,1,NO,NO},
  132.     {"$$META",0,i_meta_,0xF,1,NO,NO},
  133.     {"$$MIN_MAX",0,i_min_max_,0xF,2,YES,NO},
  134.     {"$$MODULE_NAME",0,i_mod_name_,0xF,0,NO,NO},
  135.     {"$$MODULES",0,i_modules_,0xF,1,NO,NO},
  136.     {"$$NARGS",0,i_nargs_,0xF,1,NO,NO},
  137.     {"_OUTPUT_LINE",0,i_outp_line_,0xF,0,NO,NO},
  138.     {"$OUTPUT_LINE",0,i_outp_line_,0xF,0,NO,NO},
  139.     {"$$ROUTINE",0,i_routine_,RATFOR,0,NO,NO},
  140.     {"_SECTION_NUM",0,i_sect_num_,0xF,0,NO,NO},
  141.     {"$SECTION_NUM",0,i_sect_num_,0xF,0,NO,NO},
  142.     {"$$SWITCH",0,i_switch_,0,0,NO,NO},
  143.     {"$$TM",0,i_tm_,0xF,1,NO,NO},
  144.     {"$$TRANSLIT",0,i_translit_,0xF,3,NO,NO},
  145.     {"$UNDEF",0,i_undef_,0xF,1,NO,NO},
  146.     {"$UNSTRING",0,i_unstring_,0xF,1,NO,NO},
  147.     {"$$UC",0,i_uppercase_,0xF,1,NO,NO},
  148.     {"$$VERBATIM",0,i_verbatim_,0xF,1,NO,NO},
  149.     {"$$VERSION",0,i_version_,0xF,0,NO,NO},
  150.     {"_XX",0,i_xflag_,0xF,1,NO,NO},
  151.     {"$XX",0,i_xflag_,0xF,1,NO,NO},
  152.     {"",0,NULL} // The null string terminates the list.
  153.     };
  154.  
  155. /* Put the internal function names into the table. */
  156. SRTN ini_internal_fcns(VOID)
  157. {
  158. INTERNAL_FCN HUGE *s;
  159. name_pointer np;
  160. text_pointer m;
  161.  
  162. for(s=internal_fcns; (s->len=STRLEN(s->name)) != 0; s++)
  163.     {
  164.     ASCII HUGE *p = x_to_ASCII(OC(s->name));
  165.  
  166.     s->id = ID_NUM_ptr(np,p,p+s->len);
  167.  
  168.     np->equiv = (ASCII HUGE *)(m=text_ptr++);
  169.     np->macro_type = IMMEDIATE_MACRO;
  170.     
  171.     m->tok_start = (eight_bits HUGE *)s->expnd; // NON-ANSI cast???
  172.     m->text_link = 0;
  173.     m->Language = s->Language;
  174.     m->nargs = s->nargs;
  175.     m->recursive = s->recursive;
  176.     m->var_args = s->var_args;
  177.     m->module_text = NO;
  178.     m->built_in = YES;
  179.     }
  180.  
  181. /* Regular macro definitions store the replacement text in the token
  182. memory. */
  183. text_ptr->tok_start = tok_mem;
  184. }
  185.  
  186. @ The |macrobuf| is maintained in |cur_state|. (See \.{stacks.hweb}.) Here
  187. we allocate it.
  188. @<Allocate new macro buffer@>=
  189. {
  190. if(macrobuf==NULL)
  191.     {
  192.     macrobuf = GET_MEM("macrobuf",mbuf_size,eight_bits);
  193.     macrobuf_end = macrobuf + mbuf_size;
  194.     }
  195.  
  196. mp = macrobuf; /* Initialize current pointer to the start. */
  197. }
  198.  
  199. @ An interface routine, called from \FTANGLE.
  200. @a
  201. SRTN new_mbuf(VOID)
  202. {
  203. @<Allocate new macro...@>@;
  204. }
  205.  
  206. @
  207. @<Glob...@>=
  208.  
  209. IN_TANGLE text_pointer cur_text; /* See \.{ftangle.web}. */
  210. IN_TANGLE LINE_NUMBER nearest_line;
  211.  
  212. @* ARGUMENT PROCESSING.
  213. On input, after the raw text of a \WEB\ macro has been tokenized, we must
  214. go through and replace the dummy arguments by special tokens. The special
  215. tokens consists of |MACRO_ARGUMENT| in the first byte, and the argument
  216. number in the second byte.
  217.  
  218. @a
  219. eight_bits HUGE *argize FCN((start,end))
  220.     eight_bits HUGE *start C0("Beginning of the raw tokens.")@;
  221.     eight_bits HUGE *end C1("End.")@;
  222. {
  223. eight_bits k,l;
  224. eight_bits HUGE *p, HUGE *last2, HUGE *start0;
  225. boolean var_args; /* Whether variable arguments or not. */
  226.  
  227. start0 = start; /* Remember the beginning of the raw tokens. */
  228.  
  229. if(TOKEN1(*start))
  230.     {
  231.     ERR_PRINT(M,"! Macro must start with identifier"); 
  232.         // SHOULD FLUSH HERE.
  233.     return end;
  234.     }
  235.  
  236. /* Determine the number~|k| of macro arguments and return starting position
  237. of text after arguments. */
  238. start = get_dargs(start,end,args,&k,&var_args);
  239. cur_text->moffset = (unsigned char)(start - start0); 
  240.     /* Offset to text after $(\dots)$ (or
  241.         to text after macro name if no arguments). */
  242. cur_text->nargs = k; /* Number of macro arguments. */
  243. cur_text->var_args = var_args;
  244.  
  245. /* Start after right paren. */
  246. for(last2=p= start; p<end; p++)
  247.     {
  248.     if(TOKEN1(*p))
  249.         switch(*p)
  250.             {
  251.            case @'#':
  252.             @<Possibly argize a variable argument@>@;
  253.             continue;
  254.  
  255.            case dot_const:
  256.             p++;
  257.  
  258.            default:
  259.             continue; /* Skip ordinary token. */
  260.             }
  261.  
  262. /* Search for match with argument token. */
  263.     for(l=0; l<k; ++l) 
  264. /* The following effects |if(args[l] == *(sixteen_bits *)p)|. See the
  265. analogous bit manipulations in |store_two_bytes|. */
  266.         if(args[l]>>8 == *p && (args[l] & 0x00FF) == *(p+1))
  267.             {
  268.             *p = MACRO_ARGUMENT; /* Mark as macro argument. */
  269.             *(p+1) = l; /* Store argument number in following
  270.                         byte. */ 
  271.             break;
  272.             }
  273.  
  274.     last2 = ++p; /* Advance over second byte of two-byte token.
  275. Remember the position |last2| of a two-byte token so we can strip
  276. off newlines properly below. */
  277.     }
  278.  
  279. @<Remove newlines and spaces from end of macro@>;
  280. return p;
  281. }
  282.  
  283. @ Tokenize the $n$th~variable argument, indicated by~\.{\#$n$}. The
  284. counting starts with~1.
  285. @<Possibly argize a var...@>=
  286. @{
  287. int n; // The argument number; must be |int|.
  288. eight_bits HUGE *q = p; // |q|~remembers the position of the number.
  289. outer_char *tmp; // Temporary buffer for argument number.
  290. size_t i;
  291.  
  292. if(*(p+1) != constant) continue; // This isn't the case \.{\#\It{n}}.
  293.  
  294. p += 2; // Position after |constant|.
  295.  
  296. for(i=0; p[i] != constant; i++)
  297.     ; // Find the length of the constant.
  298.  
  299. tmp = GET_MEM("var arg buf",i+1,outer_char);
  300.  
  301. for(i=0; p[i] != constant; i++)
  302.     tmp[i] = XCHR(p[i]); // Convert to |outer_char|.
  303. tmp[i+1] = '\0';
  304.  
  305. n = ATOI(tmp); // Eval.\ the arg.~\#, starting after |constant|.
  306.  
  307. /* \bfit SHOULD CHECK FOR TOO BIG HERE. */
  308.  
  309. FREE_MEM(tmp,"var arg buf",i+1,outer_char);
  310.  
  311. if(!var_args) MACRO_ERR("! #%d may only be used with variable-argument \
  312. macros",YES,n);
  313.  
  314. while(*p != constant) *p++ = ignore;
  315.  
  316. if(n < 0) MACRO_ERR("! #%d is not allowed",YES,n);
  317. else if(n == 0)
  318.     *(q+1) = @'0'; /* Marker for future expansion---the \# of variable
  319. arguments. */
  320. else
  321.     {/* Overwrite the \.\# and the |constant|. */
  322.     *q = MACRO_ARGUMENT;
  323.     *(q+1) = (eight_bits)(k + (eight_bits)(n - 1)); 
  324.         // We must offset by the fixed number of arguments.
  325.     }
  326.  
  327. last2 = p;
  328. *p = ignore;
  329. }
  330.  
  331. @ We must be careful not to interpret the second byte of a |sixteen_bits|
  332. as a newline or a space.
  333. @<Remove newlines...@>=
  334.  
  335. for(last2++; p > last2; )
  336.     if(*(p-1) == @'\n' || *(p-1) == @' ') 
  337.         p--;
  338.     else 
  339.         break;
  340.  
  341. @ Here we determine the number of arguments, and return an array of the
  342. identifier tokens of the dummy arguments. The function value is the
  343. starting position of the token text after the arguments.
  344.  
  345. The macro |MAKE_16| makes a |sixteen_bits| from the two |eight_bits|
  346. starting at |start|. It effectively does |*(sixteen_bits *)start|.
  347.  
  348. @d MAKE_16(start) (((sixteen_bits)(*start)<<8) + (sixteen_bits)(*(start+1)))
  349.  
  350. @a
  351. eight_bits HUGE *get_dargs FCN((start,end,args,n,pvar_args))
  352.     eight_bits HUGE *start C0("Start of token string.")@;
  353.     eight_bits HUGE *end C0("End of token string.")@;
  354.     sixteen_bits HUGE *args C0("Array of argument tokens, to be returned.")@;
  355.     eight_bits *n C0("Number of arguments found.")@;
  356.     boolean *pvar_args C1("Return whether variable arguments")@;
  357. {
  358. eight_bits k; // Counts the arguments.
  359. sixteen_bits id_token; // Identifier for this macro.
  360.  
  361. *pvar_args = NO; // To begin, assume no variable arguments. 
  362.  
  363. id_token = IDENTIFIER(*start,*(start+1));
  364. start +=2; /* After initial identifier. */
  365.  
  366. if(*start != @'(') 
  367.     { /* No args; nothing to do. */
  368.     *n = 0;
  369.     while(*start == @' ') start++;
  370.     return start;
  371.     }
  372.  
  373. for(k=0,++start; *start != @')'; ++k)
  374.     {
  375.     if(start==end)
  376.         {
  377.     err_print(M,"Missing right paren in definition of macro \"%s\"",
  378.             name_of(id_token));
  379.         *n = k;
  380.         return end;
  381.         }
  382.  
  383.     if(TOKEN1(*start))
  384.         {
  385.         @<Check for |ellipsis| and |break| if found@>@;
  386.  
  387.         err_print(M,"Invalid macro parameter in definition of macro \
  388. \"%s\". Token %s is invalid; \
  389. can only have identifiers and commas between (...)",
  390.             name_of(id_token),type1(*start));
  391.         *n = 0;
  392.         return start;
  393.         }
  394.  
  395.     if(k >= (eight_bits)max_margs)
  396.         mac_args(id_token);
  397.  
  398.     args[k] = MAKE_16(start); // Store the argument token.
  399.  
  400.     start += 2; /* After argument token, positioned now either on comma
  401. or right paren. */ 
  402.     if(*start == @',') ++start; // Skip comma.
  403.     }
  404.  
  405. /* Special case of no argument list. We assume this means one dummy
  406. argument. */ 
  407. if(*start == @')' && k == 0 && !*pvar_args) args[k++] = 0; 
  408.  
  409. *n = k; // Number of arguments found.
  410. return start + 1; // Position after right paren.
  411. }
  412.  
  413. @
  414. @<Check for |ellipsis|...@>=
  415.  
  416. if(*start == ellipsis)
  417.     {
  418.     if(*++start != @')') ERR_PRINT(M,"Expected ')' after ellipsis");
  419.     else *pvar_args = YES;
  420.  
  421.     break;
  422.     }
  423.  
  424. @
  425. @a
  426. SRTN mac_args FCN((id_token))
  427.       sixteen_bits id_token C1("")@;
  428. {
  429. char temp[200];
  430.  
  431. sprintf(temp, "arguments to macro \"%s\"", (char *)name_of(id_token));
  432. OVERFLW(temp, ABBREV(max_margs));
  433. }
  434.  
  435. @ For error processing, we have a function that returns a string describing
  436. the value and kind of single-byte token.
  437.  
  438. @d TYPE_DESCR_LEN 20 /* Should be long enough to hold the reasonable type
  439. descriptions that are constructed below. */
  440.  
  441. @a
  442. outer_char *type1 FCN((c))
  443.     eight_bits c C1("")@;
  444. {
  445. outer_char *p = NULL;
  446. static outer_char type_descr[TYPE_DESCR_LEN];
  447.  
  448. if(isprint(XCHR(c)))
  449.     {SPRINTF(TYPE_DESCR_LEN,type_descr,`"'%c'",XCHR(c)`);} /* Printable
  450.             character. */ 
  451. else
  452.     {
  453.     switch(c)
  454.         {
  455.        case constant:
  456.         p = OC("constant"); @+ break;
  457.  
  458.        case stringg:
  459.         p = OC("string"); @+ break;
  460.  
  461.        case @'\n':
  462.         p = OC("newline"); @+ break;
  463.         }
  464.  
  465.     if(p) {SPRINTF(TYPE_DESCR_LEN,type_descr,`"'%s'",p`);} /* Special
  466. \WEB\ token. */ 
  467.     else {SPRINTF(TYPE_DESCR_LEN,type_descr,`"0x%x",c`);} /* Unknown
  468. byte. */ 
  469.     }
  470.  
  471. return type_descr;
  472. }
  473.  
  474. @ Functions to copy and compare $n$~bytes.
  475. @<Unused@>=
  476.  
  477. ncpy(s0,s1,n)
  478.     char *s0,*s1;
  479.     int n;
  480. {
  481. for(; n>0; n--)
  482.     *s0++ = *s1++;
  483. }
  484.  
  485. ncmp(s0,s1,n)
  486.     char *s0,*s1;
  487.     int n;
  488. {
  489. for(; n>0; n--)
  490.     {
  491.     if(*s0 != *s1) return *s0 - *s1;
  492.     s0++; @+ s1++;
  493.     }
  494.  
  495. return 0;
  496. }
  497.  
  498. @ This function is used during output expansion. It fills an array of
  499. pointers to the token strings for the actual arguments of a macro call
  500. beginning at |start|. It returns the position of the token text after the
  501. actual arguments.
  502. @a
  503. eight_bits HUGE *get_margs0 FCN((start,end,pcur_byte,the_end,var_args,pargs,n))
  504.     eight_bits HUGE *start C0("Beginning of the tokens for this \
  505. macro call.")@; 
  506.     eight_bits HUGE *end C0("Maximum possible end.")@;
  507.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  508.     eight_bits HUGE *the_end C0("End of the current buffer.")@;
  509.     boolean var_args C0("Does macro have variable arguments?")@;
  510.     PARGS pargs C0("Array of pointers to the actual arguments, \
  511. to be returned.")@;
  512.     eight_bits *n C1("Number of arguments found.")@;
  513. {
  514. eight_bits k;
  515. int bal,bbal; // Balance for parens and brackets.
  516. boolean mac_protected;
  517. sixteen_bits id_token; // Identifier for this macro.
  518.  
  519. id_token = IDENTIFIER(*start,*(start+1)); // Remember for error processing.
  520. start +=2; // After initial identifier.
  521.  
  522. /* Read more arguments into buffer if necessary. */
  523. if(start == end && the_end != NULL) 
  524.     end = args_to_macrobuf(end,pcur_byte,the_end,var_args);
  525.  
  526. /* Does a parenthesized list follow identifier? */
  527. if(start==end || *start != @'(') 
  528.     {
  529.     return pargs[*n = 0] = start; /* No args; nothing to do. Position
  530. after macro name identifier.  */
  531.     }
  532.  
  533. pargs[k=0] = start++; /* Beginning of first actual argument
  534.     token string. (Actually, this is the position of the left paren, 
  535.     one less than the position of the first token. This is so the ending
  536.     position, which will point to a comma, can be used as the start of
  537.     the next argument. The value~1 is added in |x0macro|.) */
  538.  
  539. bal = 1; // Keep track of balanced parens. Already past the opening one.
  540. bbal = 0; // Also keep track of balanced brackets.
  541. mac_protected = NO; // Reverse accent protects commas, etc.
  542.  
  543. while(start < end)
  544.     {
  545.     eight_bits c = *start;
  546.  
  547.     if(TOKEN1(c))
  548.         {
  549.         switch(c)
  550.             {
  551.            case @'#':
  552.             if(start+1 < end && *(start+1) == @',')
  553.                 { /* Skip over `\.{\#,}'. */
  554.                 *start = '\0'; // Replace '\.\#' by null.
  555.                 start += 2;
  556.                 continue;
  557.                 }
  558.             break;
  559.  
  560.            case constant:
  561.            case stringg:
  562.             for(start++; *start++ != c; );
  563.             continue;
  564.  
  565.            case dot_const:
  566.             start += 2;
  567.             continue;
  568.  
  569.            case @'`':
  570.             mac_protected = BOOLEAN(!mac_protected);
  571.             *start++ = '\0'; /* Replace the protection
  572. character by a null. */
  573.             continue;
  574.  
  575. /* The following scheme needs to be generalized.  Doesn't check for syntax
  576. such as `\.{[(]}' or `\.{([)}'.  Probably must stack counters. */
  577.            case @'(':
  578.             bal++;
  579.             break;
  580.  
  581.            case @')':
  582.             if(bal == 0)
  583.                MACRO_ERR("Unexpected ')' in macro argument",YES);
  584.             else if(bal > 0) bal--;
  585.             break;
  586.  
  587.            case @'[':
  588.             bbal++;
  589.             break;
  590.  
  591.            case @']':
  592.             if(bbal == 0)
  593.                MACRO_ERR("Unexpected ']' in macro argument",YES);
  594.             else if(bbal > 0) bbal--;
  595.             break;
  596.             }
  597.  
  598.         if(!mac_protected && ( (bal==1 && bbal==0 && (c == @',')) 
  599.                 || bal==0) ) 
  600.             {/* Found end of argument token list. Record the
  601. upper limit. */ 
  602.             if(++k >= max_margs)
  603.                 mac_args(id_token);
  604.  
  605.             pargs[k] = start++; /* Count the argument, skip
  606. over comma or paren. */ 
  607.             if(bal==0) break; // End of arguments.
  608.             }
  609.         else start++; // Skip over one-byte token.
  610.         }
  611.     else 
  612.         start += (c < MOD0 ? 2 : 4+4*1); 
  613.             // Skip over two-byte token. (`1' for |line_info|.)
  614.     }
  615.  
  616. *n = k;
  617. return start; // Positioned after right paren.
  618. }
  619.  
  620. @* MACRO LOOKUP, etc.
  621. Here we determine whether the |cur_val| computed during the output phase
  622. corresponds to a \WEB\ macro. We return the appropriate |text_pointer|, or
  623. |NULL| if it's not a macro. The function |mac_lookup| is an interface to
  624. independently compiled modules.
  625.  
  626. @a
  627. void HUGE *mac_lookup FCN((cur_val))
  628.     sixteen_bits cur_val C1("Current id token.")@;
  629. {
  630. return (void *)MAC_LOOKUP(cur_val);
  631. }
  632.  
  633. @ Corresponding to |MAC_LOOKUP|, there is an internal macro |_DEFINED|
  634. that expands to~1 if its argument is a defined macro, or~0 otherwise. This
  635. macro, however, essentially is obsolete since the advent of the |defined|
  636. unary operator. 
  637. @<Define internal...@>=
  638.  
  639. SAVE_MACRO("_DEFINED(macro)$EVAL(defined #!macro)");
  640. SAVE_MACRO("$DEFINED(macro)$EVAL(defined #!macro)");
  641.  
  642. @ Furthermore, the macro |$IFDEF(a,b,c)| returns the expansion of~\.b if the
  643. macro~\.a is defined; otherwise, it returns the expansion of~\.c.
  644.  
  645. @m DEF_RTN(name, cond)
  646. SRTN i_##name##_ FCN((n,pargs))
  647.     int n C0("")@;
  648.     PARGS pargs C1("")@;
  649. {
  650. text_pointer m;
  651. sixteen_bits id;
  652. eight_bits HUGE *p0 = pargs[0] + 1;
  653. boolean e;
  654.  
  655. CHK_ARGS("$IFDEF", 3);
  656.  
  657. if(TOKEN1(*p0))
  658.     {
  659.     MACRO_ERR("! First argument of $IFDEF or $IFNDEF must be a macro", YES);
  660.     return;
  661.     }
  662.  
  663. id = IDENTIFIER(p0[0], p0[1]);
  664. e = ((m=mac_lookup(id)) != NULL && !(m->built_in));
  665.  
  666. if(cond)
  667.     COPY_ARG(1, name)@;
  668. else 
  669.     COPY_ARG(2, name)@;
  670. }
  671.  
  672. @a
  673. DEF_RTN(ifdef, e)@;
  674. DEF_RTN(ifndef, !e)@;
  675.  
  676. @ A similar macro implements the four-argument version of |ifelse|. Here,
  677. we want to compare two strings that need not evaluate to numbers. Thus, we
  678. can't use |_IF|, but must do it explicitly.
  679.  
  680. The following function compares its first two arguments on a byte-by-byte
  681. basis. If they agree, the third argument is copied into the macro buffer;
  682. otherwise, the fourth argument is copied.
  683. @a
  684. SRTN i_ifelse_ FCN((n,pargs))
  685.     int n C0("")@;
  686.     PARGS pargs C1("")@;
  687. {
  688. eight_bits HUGE *p0;
  689. eight_bits HUGE *pp0, HUGE *pp1, HUGE *mp0, HUGE *mp1;
  690. boolean args_identical = YES;
  691.  
  692. CHK_ARGS("$IFELSE", 4);
  693.  
  694. pp0 = xmac_text(mp0=mp, pargs[0] + 1, pargs[1]); 
  695. mp1 = mp; // |expr0| is now in |(pp0,mp1)|.
  696.  
  697. pp1 = xmac_text(mp, pargs[1] + 1, pargs[2]);
  698.     // |expr1| is now in |(pp1,mp)|.
  699.  
  700. /* Are the arguments identical?  For speed, first check just the length of
  701. the arguments; then compare byte by byte. */
  702. if(mp-pp1 != mp1-pp0)
  703.     args_identical = NO;
  704. else
  705.     while(pp0 < mp1)
  706.         if(*pp0++ != *pp1++) 
  707.             args_identical = NO;
  708.  
  709. mp = mp0;
  710.  
  711. if(args_identical) 
  712.     COPY_ARG(2,_ifelse_)@;
  713. else 
  714.     COPY_ARG(3,_ifelse_)@;
  715. }
  716.  
  717. @ A general mechanism handles almost all such cases. (We use a \WEB\ macro
  718. so we can pretty it up with |$EVAL|.)
  719. Given the expression evaluator, |_IF| can be implemented enormously simply.
  720.  
  721. @m COPY_ARG(n,reason) {MCHECK(pargs[$EVAL(n+1)]-pargs[n]-1,#reason);
  722.     for(p0=pargs[n]+1; p0<pargs[$EVAL(n+1)]; ) *mp++ = *p0++;}
  723.  
  724. @a
  725. SRTN i_if_ FCN((n,pargs))
  726.     int n C0("")@;
  727.     PARGS pargs C1("")@;
  728. {
  729. eight_bits HUGE *pp;
  730. eight_bits HUGE *mp0;
  731. eight_bits HUGE *p0;
  732. boolean e;
  733.  
  734. CHK_ARGS("$IF", 3);
  735.  
  736. pp = xmac_text(mp0=mp, p0=pargs[0]+1, pargs[1]); // Expand the expr.
  737. e = eval(pp, mp);
  738. mp = mp0;
  739.  
  740. if(e)
  741.     COPY_ARG(1,_if_)@;
  742. else 
  743.     COPY_ARG(2,_if_)@;
  744. }
  745.  
  746. @ A related routine |$IFCASE| behaves like \TeX's \.{\\ifcase}.
  747. Expanding the first argument of the |$IFCASE| is a bit tricky, since
  748. we're doing this from within a macro expansion.  We use recursion;
  749. watch out for resetting~|mp|.
  750. @a
  751. SRTN i_ifcase_ FCN((n,pargs))
  752.     int n C0("Total number of arguments")@;
  753.     PARGS pargs C1("")@;
  754. {
  755. eight_bits HUGE *pp;
  756. eight_bits HUGE *mp0;
  757. int ncase;
  758.  
  759. CHK_ARGS("$IFCASE", -1);
  760. pp = xmac_text(mp0=mp, pargs[0]+1, pargs[1]); // Expand the |ncase|.
  761. ncase = neval(pp, mp);
  762. mp = mp0;
  763. copy_nth_arg(ncase, n-3, pargs); // Evaluate the |ncase|.
  764. }
  765.  
  766. @ This function copies the $n0$th~argument (after the very first) to the
  767. macro buffer. The cases are numbered 0--$n$, with case~$n+1$ being the default.
  768. @a
  769. SRTN copy_nth_arg FCN((n0,n,pargs))
  770.     int n0 C0("Should be a non-negative integer")@;
  771.     int n C0("Cases are numbered 0--n, default")@;
  772.     PARGS pargs C1("")@;
  773. {
  774. eight_bits HUGE *p0;
  775.  
  776. if(n0 < 0 || n0 > n) n0 = n+1; /* Do the default case. */
  777.  
  778. n0++; /* Don't count the index argument. */
  779. MCHECK(pargs[n0+1]-pargs[n0]-1,"copy_nth_arg");
  780. for(p0=pargs[n0]+1; p0<pargs[n0+1]; ) *mp++ = *p0++;
  781. }
  782.  
  783. @ We have not yet implemented a |_SWITCH| statement.
  784. @a
  785. SRTN i_switch_ FCN((n,pargs))
  786.     int n C0("")@;
  787.     PARGS pargs C1("")@;
  788. {}
  789.  
  790. @ Here are some things one can do with |_IF|.
  791. @<Define internal...@>=
  792.  
  793. SAVE_MACRO("_ABS(a)$IF((a) > 0,$EVAL(a),$EVAL(-(a)))");
  794. SAVE_MACRO("$ABS(a)$IF((a) > 0,$EVAL(a),$EVAL(-(a)))");
  795.  
  796. @ We need a facility to undefine macros. This must be done implicitly
  797. before a new macro is defined; and may also be done explicitly through the
  798. \.{@@\#undef} command.
  799.  
  800. @a
  801. SRTN undef FCN((cur_val,warning))
  802.     sixteen_bits cur_val C0("Token to be undefined.")@;
  803.     boolean warning C1("Complain is there's an error?")@;
  804. {
  805. name_pointer np = name_dir + cur_val;
  806.  
  807. if(np->macro_type == NOT_DEFINED)
  808.     {
  809.     if(warning) 
  810.         MACRO_ERR("WARNING: \"%s\" is already undefined",YES,
  811.             name_of(cur_val));
  812.         
  813.     return;
  814.     }
  815.  
  816.  
  817. if(np->equiv == NULL)
  818.     {
  819.        if(np->macro_type == IMMEDIATE_MACRO)
  820.               {
  821.               MACRO_ERR("Attempting to @@#undef deferred macro \"%s\" \
  822. during phase 1; consider using $UNDEF(%s)",
  823.                       YES, name_of(cur_val), name_of(cur_val));
  824.               }
  825.       else
  826.               {
  827.               MACRO_ERR("Missing equivalence field while undefining \"%s\"; \
  828. this shouldn't happen!",YES,name_of(cur_val));
  829.  
  830.               np->macro_type = NOT_DEFINED;
  831.               }
  832.  
  833.     return;
  834.     }
  835.  
  836. np->macro_type = NOT_DEFINED;
  837. ((text_pointer)np->equiv)->nargs = UNDEFINED_MACRO;
  838. np->equiv = NULL;
  839. }
  840.  
  841. @* ERROR MESSAGES.
  842. We maintain a stack of macro id tokens that we are in the middle of
  843. expanding. This is to prevent recursion.
  844.  
  845. @<Glob...@>=
  846.  
  847. XIDS HUGE *pids[MAX_XLEVELS];
  848. int xlevel = 0;
  849.  
  850. @ Simple macros push or pop the id stack. We also need a routine to see if
  851. an id is on the stack.
  852.  
  853. @d save_name(a) {if(xids->level >= MAX_XLEVELS) 
  854.         {
  855.         MACRO_ERR("! Macro inner recursion depth exceeded",YES);
  856.         FATAL(M, "!! BYE.","");
  857.         }
  858.     xids->token[slevel=xids->level++] = a;
  859.     }
  860.  
  861. @d unsave_name xids->level = slevel
  862.  
  863. @a
  864. boolean recursive_name FCN((a,xids,last_level))
  865.     sixteen_bits a C0("")@;
  866.     XIDS HUGE *xids C0("")@;
  867.     int last_level C1("")@;
  868. {
  869. int i;
  870.  
  871. /* Hunt through levels lower than the present one. */
  872. for(i=0; i<last_level; i++)
  873.     if(xids->token[i] == a) return YES;
  874.  
  875. return NO;
  876. }
  877.  
  878. @ Macro error messages can print the recursion stack as an indication of
  879. where we are.
  880. @a
  881.  
  882. SRTN macro_err FCN(VA_ALIST((s,trail VA_ARGS)))
  883.     VA_DCL(
  884.     CONST outer_char s[] C0("Error message about macro expansion.")@;
  885.     int trail C2("Do we print out the expansion trail?")@;)@;
  886. {
  887. VA_LIST(arg_ptr)@;
  888. outer_char HUGE *temp, HUGE *temp1, HUGE *t, HUGE *near_line;
  889. int i,ntemp;
  890. #if(NUM_VA_ARGS == 1)
  891.     CONST outer_char s[];
  892.     int trail;
  893. #endif
  894.  
  895. /* We allocate dynamically to keep the size of the stack down. */
  896. temp = GET_MEM("macro_err:temp",N_MSGBUF,outer_char);
  897. temp1 = GET_MEM("macro_err:temp1",N_MSGBUF,outer_char);
  898. near_line = GET_MEM("macro_err:near_line",N_MSGBUF,outer_char);
  899.  
  900. VA_START(arg_ptr,trail);
  901. vsprintf_((char *)temp1,(CONST char *)s,arg_ptr)@;
  902. va_end(arg_ptr);
  903.  
  904. if(phase==2) SPRINTF(N_MSGBUF,near_line,`"; near input l. %u",nearest_line`);
  905.  
  906. /* We surround the message that we construct with double quotes. These are
  907. printed into the file, but not to the terminal.  This is to help out
  908. preprocessors that parse the message prematurely and get confused by
  909. unmatched quotes. */
  910. SPRINTF(N_MSGBUF,temp,`"\"%s.  (%s l. %u in %s%s.) %s",
  911.     temp1,
  912.     phase==1 ? "Input" : "Output",
  913.     phase==1 ? cur_line : OUTPUT_LINE,
  914.     phase==1 ? cur_file_name : params.OUTPUT_FILE_NAME,
  915.     near_line,
  916.     trail && (xlevel > 0) ? "Expanding " : ""`);
  917.  
  918. t = temp + STRLEN(temp);
  919.  
  920. /* `Print out' levels associated with each invocation of |xmac_buf| by
  921. attaching them to end of |temp|. */
  922. if(trail && (xlevel > 0))
  923.     for(i=0; i<1; i++) see_xlevel(&t,pids[i]);
  924.  
  925. ntemp = STRLEN(temp);
  926. temp[ntemp] = '"';
  927. temp[ntemp+1] = '\0';
  928.  
  929. /* Message to file. */
  930. OUT_MSG(to_ASCII(temp),NULL);
  931.  
  932. /* Message to terminal. */
  933. temp[ntemp] = '\0'; // Kill off final quote.
  934. printf("\n%s\n", (char *)to_outer((ASCII HUGE *)temp)+1);
  935.  
  936. mark_harmless;
  937.  
  938. FREE_MEM(temp,"macro_err:temp",N_MSGBUF,outer_char);
  939. FREE_MEM(temp1,"macro_err:temp1",N_MSGBUF,outer_char);
  940. FREE_MEM(near_line,"macro_err:near_line",N_MSGBUF,outer_char);
  941. }
  942.  
  943. @ Print out all names stored at some recursive invocation of |xmac_buf|.
  944. @a
  945. SRTN see_xlevel FCN((pt,p))
  946.     outer_char HUGE **pt C0("")@;
  947.     XIDS HUGE *p C1("")@;
  948. {
  949. int i,level;
  950.  
  951. level = p->level; /* Total number at this level. */
  952.  
  953. for(i=0; i<level; 
  954.        i++,sprintf((char *)(*pt),"%s",i==level ? ". " : ", "),(*pt)+=2)
  955.     prn_mname(pt,p->token[i]); 
  956. }
  957.  
  958. /* Print one name. */
  959. SRTN prn_mname FCN((pt,token))
  960.     outer_char HUGE **pt C0("")@;
  961.     sixteen_bits token C1("")@;
  962. {
  963. name_pointer np;
  964. ASCII HUGE *p;
  965. CONST ASCII HUGE *end;
  966.  
  967. np = name_dir + token;
  968.  
  969. PROPER_END(end);
  970.  
  971. for(p=np->byte_start; p<end; )
  972.     *(*pt)++ = XCHR(*p++);
  973. }
  974.  
  975. @
  976. @a
  977. SRTN i_inp_line_ FCN((n,pargs))
  978.     int n C0("")@;
  979.     PARGS pargs C1("")@;
  980. {
  981. num_to_mbuf(n,pargs,"$INPUT_LINE",0,"nearest line",nearest_line);
  982. }
  983.  
  984. SRTN i_outp_line_ FCN((n,pargs))
  985.     int n C0("")@;
  986.     PARGS pargs C1("")@;
  987. {
  988. num_to_mbuf(n,pargs,"$OUTPUT_LINE",0,"output line",OUTPUT_LINE);
  989. }
  990.  
  991. @
  992. @a
  993. SRTN num_to_mbuf FCN((n,pargs,built_in_name,num_args,num_descr,num))
  994.     int n C0("")@;
  995.     PARGS pargs C0("")@;
  996.     CONST char *built_in_name C0("")@;
  997.     int num_args C0("")@;
  998.     CONST char *num_descr C0("")@;
  999.     int num C1("")@;
  1000. {
  1001. CHK_ARGS(built_in_name,num_args);
  1002.  
  1003. MCHECK0(20,num_descr);
  1004.  
  1005. *mp++ = constant;
  1006.  sprintf((char *)mp,"%d",num);
  1007.  to_ASCII((outer_char HUGE *)mp); // Convert the number in place to |ASCII|.
  1008.  mp += STRLEN(mp);
  1009. *mp++ = constant;
  1010. }
  1011.  
  1012.  
  1013. @* EXPANDING a BUFFER.
  1014. Here we actually expand a buffer possibly containing macros. The first
  1015. call to |x0macro| will be a macro name itself, possibly with arguments.
  1016. After that expansion, |x0macro| is called again repeatedly until nothing
  1017. more can be expanded. The expansion will end when no |paste| tokens
  1018. appeared during the previous cycle.  The expansion is put into the next
  1019. available position of |macrobuf|, which is pointed to by |mp|.
  1020.  
  1021. @a
  1022. boolean x0macro FCN((p,end,xids,pcur_byte,the_end))
  1023.     eight_bits HUGE *p C0("Present position in the input buffer.")@;
  1024.     eight_bits HUGE *end C0("Last filled position of the input \
  1025. buffer plus~1.")@; 
  1026.     XIDS HUGE *xids C0("")@;
  1027.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  1028.     eight_bits HUGE *the_end C1("End of buffer.")@;
  1029. {
  1030. boolean expanded; /* Was a macro expanded in this pass? */
  1031. sixteen_bits a;
  1032. eight_bits a0,a1; /* Left and right parts of |sixteen_bits| token. */
  1033. text_pointer m;    /* Points to info about current macro being expanded. */
  1034. eight_bits HUGE *p0, HUGE *p1;
  1035. eight_bits HUGE * HUGE *pargs = GET_MEM("pargs",max_margs,eight_bits HUGE *);  
  1036. boolean must_paste = NO,pasting = NO;
  1037. int level0 = xids->level;
  1038. boolean mac_protected = NO; /* Protection flag flipped by left quote. */
  1039.  
  1040. expanded = NO;    /* If no macros were expanded in this pass, then we're done. */
  1041.  
  1042. /* |p| is current position in input buffer. */
  1043. while(p < end)
  1044.     {
  1045.     a0 = *p++; // The next token to be examined.
  1046.  
  1047.     if(p==end && a0==@'\n') break;
  1048.  
  1049.     if(TOKEN1(a0)) @<Process |eight_bits| token@>@;
  1050.     else @<Process identifier token@>@;
  1051.     }
  1052.  
  1053. /* Get directly to here from |MACRO_ERR|. */
  1054. done_expanding:
  1055.     FREE_MEM(pargs, "pargs", max_margs, eight_bits HUGE *);
  1056.     return expanded; /* Return flag to say whether any macro was
  1057.             expanded. If nothing was, then we're done. */
  1058. }
  1059.         
  1060. @ As we scan through the |macro_buf|, we either encounter |eight_bits|
  1061. tokens, or identifiers (|sixteen_bits|). Here we process the single-byte
  1062. tokens. If it's a left quote, we flip the protection flag. If it's
  1063. |stringg|, we copy the entire contents to and including the concluding
  1064. |stringg|. Otherwise, we just copy over the token.
  1065.  
  1066. @<Process |eight_bits| token@>=
  1067. {
  1068. switch(a0)
  1069.     {
  1070.    case @'`':
  1071.     mac_protected = BOOLEAN(!mac_protected);
  1072.     continue;
  1073.  
  1074.    case stringg:
  1075.    case constant:
  1076.     MCHECK(1,"`");
  1077.     *mp++ = a0; // |stringg| or |constant| token.
  1078.  
  1079.      copy_string:
  1080.     do
  1081.         {
  1082.         if(!TOKEN1(*mp=*p++))
  1083.             {
  1084.             MCHECK(1,"id prefix");
  1085.             *++mp = *p++;
  1086.             }
  1087.         MCHECK(1,"8-bit token");
  1088.         }
  1089.     while(*mp++ != a0);
  1090.  
  1091.     if(a0 == stringg) @<Check for string concatenation@>@;
  1092.  
  1093.     continue;
  1094.  
  1095.    case dot_const:
  1096.     MCHECK(2,"dot_const");
  1097.     *mp++ = a0;
  1098.     *mp++ = *p++;
  1099.     continue;
  1100.  
  1101.    default:
  1102.     MCHECK(1,"`");
  1103.     *mp++ = a0; /* Copy over ASCII token to the macro buffer. */  
  1104.     continue;
  1105.     }
  1106. }
  1107.  
  1108. @ We implement an ANSI type of string concatenation feature.
  1109. @<Check for string concat...@>=
  1110. {
  1111. eight_bits HUGE *p00;
  1112.  
  1113. /* Scan over possible white space. */
  1114. for(p00=p; p < end; p++)
  1115.     if(*p != @' ' && *p != @'\t') break;
  1116.  
  1117. if(p < end && *p == stringg)
  1118.     {
  1119.     eight_bits mchar = *(mp-2);// Quote character from last string.
  1120.     eight_bits pchar = *(p+1);// Quote character from next string.
  1121.  
  1122.     if((mchar == @'\'' || mchar == @'\"') && 
  1123.        (pchar == @'\'' || pchar == @'\"'))
  1124.         {
  1125.         mp -= 2; // Back over |stringg| and quote char.
  1126.         p += 2; // Move over |stringg| and quote char.
  1127.         goto copy_string;
  1128.         }
  1129.     }
  1130. else p = p00; // Didn't find another string.
  1131. }
  1132.  
  1133. @ Deal with identifier while scanning through |macro_buf|.
  1134. @<Process identifier token@>=
  1135. {
  1136. a = IDENTIFIER(a0,a1= *p++);
  1137.  
  1138. if(a == id_defined) 
  1139.     {
  1140.     @<Copy |defined| and its argument@>@;
  1141.     continue;
  1142.     }
  1143.  
  1144. /* If it's a macro token, we must decide whether to expand it. If this
  1145. token is already on the |xids| stack from an earlier level of recursive
  1146. expansion, then we don't expand. If we haven't encountered this name
  1147. before, then we expand the macro. */
  1148. if((m=MAC_LOOKUP(a)) != NULL) 
  1149.     if(mac_protected)
  1150.         {
  1151.         MCHECK(2,"protected macro token");
  1152.         *mp++ = a0;
  1153.         *mp++ = a1;
  1154.         }
  1155.     else if(recursive_name(a,xids,level0))
  1156.         @<Don't expand macro.@>@; 
  1157.     else
  1158.         {
  1159.         int slevel = ignore;
  1160.  
  1161.         if(!m->recursive) save_name(a); // To prevent recursion.
  1162.         @<Expand a macro@>@;
  1163.         if(!m->recursive) unsave_name;
  1164.         }
  1165. else 
  1166.     {/* Copy a nonmacro 2-byte token to the output buffer (pointed to
  1167. by~|mp|). */ 
  1168.     MCHECK(2,"ordinary id");
  1169.     *mp++ = a0;
  1170.     *mp++ = a1;
  1171.  
  1172. /* If we're actually dealing with a module name, we punt here and don't
  1173. expand it at this time; it will be expanded on output. */
  1174.     if(a0 >= MOD0)
  1175.         {
  1176.         int n = 2 + 4*1; // `1' for |line_info|.
  1177.  
  1178.         MCHECK(n,"module defn");
  1179.         while(n-- > 0)
  1180.             *mp++ = *p++;
  1181.         }
  1182.     }
  1183. }
  1184.  
  1185.  
  1186. @ In macro expansions, the token |defined| gets special treatment. If it's
  1187. followed by an identifier, that identifier should not be expanded.
  1188.  
  1189. @d DEFINED_ERR(s) {MACRO_ERR(s,YES); goto done_expanding;}
  1190.  
  1191. @d ERR_IF_DEFINED_AT_END if(p >= end) 
  1192.     DEFINED_ERR("! `defined' ends prematurely")@;
  1193.  
  1194. @<Copy |defined|...@>=
  1195. {
  1196. MCHECK(6,"defined stuff");
  1197.  
  1198. /* Copy the |defined| token. */
  1199. *mp++ = a0;
  1200. *mp++ = a1;
  1201.  
  1202. ERR_IF_DEFINED_AT_END;
  1203. if(TOKEN1(a0= *p++)) /* Possible parenthesis */
  1204.     {
  1205.     if(a0 != @'(') DEFINED_ERR("! Invalid token after `defined'")@;
  1206.     else *mp++ = a0;
  1207.  
  1208.     ERR_IF_DEFINED_AT_END;
  1209.     if(TOKEN1(a0 = *p++)) DEFINED_ERR("! Invalid argument of `defined'")@;
  1210.     else
  1211.         { /* Copy parenthesized id token. */
  1212.         *mp++ = a0;
  1213.         *mp++ = *p++;
  1214.         }
  1215.  
  1216.     ERR_IF_DEFINED_AT_END;
  1217.     if(TOKEN1(a0 = *p++))
  1218.         if(a0 != @')') DEFINED_ERR("! Missing ')' after `defined'")@;
  1219.         else *mp++ = a0;
  1220.     }
  1221. else
  1222.     { /* Copy non-parenthesized id token. */
  1223.     *mp++ = a0;
  1224.     *mp++ = *p++;
  1225.     }
  1226. }
  1227.  
  1228.     
  1229. @ The flag |keep_intact| is used with stringizing; it is set with the
  1230. \.{\#*}~operation. It means that if the parameter is a string, just pass it
  1231. through unchanged; don't add extra quotes arounds it.
  1232.  
  1233. Other flags are used in conjunction with the~`\.{\#'}' and~`\.{\#"}'
  1234. commands.
  1235.  
  1236. @<Glob...@>=
  1237.  
  1238. static boolean keep_intact;
  1239. static boolean single_quote = NO, double_quote = NO;
  1240.  
  1241. @ We endow the preprocessor with ANSI-C's stringize operation. Parameter
  1242. tokens preceded by `\.{\#}' are converted into strings. We must follow the
  1243. rest of \TANGLE's conventions; the string must be bracketed with |stringg|.
  1244. @<Stringize parameter@>=
  1245. @{
  1246. eight_bits HUGE *begin;
  1247.  
  1248. @b
  1249. do_stringize:
  1250.   for(begin=pargs[*p0]+1; *begin == '\0'; begin++)
  1251.     ; /* Skip over leading nulls (that possibly replace protection
  1252.         characters. */ 
  1253.  
  1254. @<String token to |macrobuf|. @>;
  1255.  
  1256. if(!keep_intact || *begin != stringg) 
  1257.     @<Quote token to |macrobuf|. @>@;
  1258.  
  1259. str_to_mb(begin,pargs[*p0 + 1],YES);
  1260. p0++; /* Don't put this into previous stmt, because order of evaluation is
  1261.         undefined. */
  1262.  
  1263. if(!keep_intact || *begin != stringg) 
  1264.     @<Quote token...@>@;
  1265.  
  1266. @<String token...@>;
  1267.  
  1268. single_quote = double_quote = NO;
  1269. }
  1270.  
  1271. @ We must preface and end all strings with |stringg| tokens.
  1272. @<String token...@>=
  1273. MCHECK(1,"stringg"); @+ *mp++ = stringg@;
  1274.  
  1275. @ The string delimiter depends in general on the language, but it can be
  1276. overridden by the commands~`\.{\#'} or~`\.{\#"}, which set the flags
  1277. |single_quote| or |double_quote|.
  1278. @<Quote token...@>=
  1279. {
  1280. MCHECK(1,"quote"); 
  1281. *mp++ = (eight_bits)(single_quote || (!double_quote && R77_or_F) ? 
  1282.     @'\'' : @'"');
  1283. }
  1284.  
  1285. @ Now we prepare to copy/translate a token string into the |macrobuf|. To
  1286. get the spacings right, we use an |OUTPUT_STATE| flag.
  1287. @<Glob...@>=
  1288. OUTPUT_STATE copy_state;
  1289.  
  1290. @ This function is analogous to |out_op|: It copies a string to the
  1291. |macro_buf|, and set |copy_state|.
  1292. @a
  1293. SRTN cpy_op FCN((s))
  1294.     CONST outer_char HUGE *s C1("String such as \.{++}.")@;
  1295. {
  1296. MCHECK(2,"cpy_op");
  1297.  
  1298. while(*s)
  1299.     *mp++ = XORD(*s++);
  1300.  
  1301. copy_state = MISCELLANEOUS;
  1302. }
  1303.  
  1304. @ When copying strings, certain intermediate characters must be escaped,
  1305. depending on the language:
  1306. @a
  1307. eight_bits HUGE *str_to_mb FCN((begin_arg,end_arg,esc_chars))
  1308.     CONST eight_bits HUGE *begin_arg C0("Beginning of string.")@;
  1309.     CONST eight_bits HUGE *end_arg C0("End of string.")@;
  1310.     boolean esc_chars C1("Insert escape characters?")@;
  1311. {
  1312. eight_bits HUGE *mp0 = mp;
  1313. sixteen_bits c;
  1314.  
  1315. copy_state = MISCELLANEOUS;
  1316.  
  1317. while(begin_arg < end_arg) 
  1318.     {
  1319.     if(TOKEN1(c= *begin_arg++)) 
  1320.         {
  1321.         @<Flip copy state and escape certain characters@>@;
  1322.         }
  1323.     else
  1324.         {
  1325.         name_pointer np;
  1326.  
  1327.         if(copy_state == NUM_OR_ID) 
  1328.             @<Copy a space@>@;
  1329.  
  1330.         if(c == MACRO_ARGUMENT) 
  1331.             @<Fill in argument number@>@;
  1332.         else 
  1333.             @<Handle identifier-like token@>@;
  1334.  
  1335.         copy_state = NUM_OR_ID;
  1336.         }
  1337.     }    
  1338.  
  1339. *mp = '\0';
  1340. return mp0;
  1341. }
  1342.  
  1343. @
  1344. @<Fill in arg...@>=
  1345. {
  1346. outer_char temp[10];
  1347. int n;
  1348.  
  1349. n = NSPRINTF(temp,"$%d",*begin_arg++);
  1350. to_ASCII(temp);
  1351. MCHECK(n,"%arg");
  1352. STRCPY(mp,temp);
  1353. mp += n;
  1354. }
  1355.  
  1356. @
  1357. @d UNNAMED_MODULE 0
  1358. @<Handle identifier-like...@>=
  1359. {
  1360. c = IDENTIFIER(c,*begin_arg++);
  1361.  
  1362. switch(c/MODULE_NAME)
  1363.     {
  1364.    case 0: /* Ordinary identifier. */
  1365.     np = name_dir + c;        
  1366.     @<Copy possibly truncated identifier to macro buffer@>@;
  1367.     break;
  1368.  
  1369.    case 1: /* Module name. */
  1370.     *mp++ = @'#';
  1371.     *mp++ = @'<';
  1372.  
  1373.     c -= MODULE_NAME;
  1374.  
  1375.     np = name_dir + c;
  1376.  
  1377.     if(np->equiv != (EQUIV)text_info)
  1378.         @<Copy possibly truncated id...@>@;
  1379.     else if(c != UNNAMED_MODULE) *mp++ = @'?';
  1380.  
  1381.     *mp++ = @'@@';
  1382.     *mp++ = @'>';
  1383.     break;
  1384.  
  1385.    default:
  1386.     if(c == MODULE_NUM) 
  1387.         begin_arg += 4*1; // `1' for |line_info|.
  1388.         // Skip over line number info.
  1389.     break;
  1390.     }
  1391. }
  1392.  
  1393. @ Stringize an id token by copying the actual name into the |macro_buf|.
  1394.  
  1395. @<Copy actual name to macro buffer@>=
  1396. {
  1397. end = proper_end(np);
  1398.  
  1399. p = np->byte_start;
  1400. MCHECK(end - p,"id name");
  1401. while(p<end) *mp++ = *p++;
  1402. }
  1403.  
  1404. @ Here we just copy a space into the |macro_buf|.
  1405. @<Copy a space@>=
  1406. {
  1407. MCHECK(1,"' '"); @+ *mp++ = @' ';
  1408. }
  1409.  
  1410. @ Here we process a single-byte token during stringizing. We have to do
  1411. many of the same operations that are done during output expansion.
  1412.  
  1413. @<Flip copy state...@>=
  1414.  
  1415. switch(c)
  1416.     {
  1417.     case ignore:
  1418.         break;
  1419.  
  1420.     @<Copy cases like \.{!=}@>@;
  1421.  
  1422.     case join:
  1423.         copy_state = UNBREAKABLE;
  1424.         break;
  1425.  
  1426.     case constant:
  1427.         if(copy_state==NUM_OR_ID) 
  1428.             @<Copy a space@>@;
  1429.         @<Copy stuff between |constant| or |stringg|@>@;
  1430.         copy_state = NUM_OR_ID;
  1431.         break;
  1432.         
  1433.     case stringg:
  1434.         @<Copy stuff between |constant|...@>@;
  1435.         copy_state = MISCELLANEOUS;
  1436.         break;
  1437.  
  1438.     case @';':
  1439.         if(R77_or_F)
  1440.             {
  1441.             @<Make semi into string@>;
  1442.             break;
  1443.             }
  1444.  
  1445.     default:
  1446.         esc_certain_chars(c,esc_chars);
  1447.         if(copy_state != VERBATIM) copy_state = MISCELLANEOUS;
  1448.         break;
  1449.     }
  1450.         
  1451. @
  1452. @<Make semi into string@>=
  1453. {
  1454. MCHECK(3,"\";\"");
  1455. *mp++ = constant;
  1456. *mp++ = @';';
  1457. *mp++ = constant;
  1458. }
  1459.  
  1460. @ Expand various internal codes during stringizing.
  1461.  
  1462. @d CPY_OP(token,trans) case token: cpy_op(OC(trans)); break@;
  1463.  
  1464. @<Copy cases like \.{!=}@>=
  1465.  
  1466. CPY_OP(plus_plus,"++");
  1467. CPY_OP(minus_minus,"--");
  1468. CPY_OP(minus_gt,C_LIKE(language) ? "->" : ".EQV.");
  1469. CPY_OP(gt_gt,">>");
  1470. CPY_OP(eq_eq,"==");
  1471. CPY_OP(lt_lt,"<<");
  1472. CPY_OP(gt_eq,">=");
  1473. CPY_OP(lt_eq,"<=");
  1474. CPY_OP(not_eq,"!=");
  1475. CPY_OP(and_and,"&&");
  1476. CPY_OP(or_or,"||");
  1477. CPY_OP(star_star,"**");
  1478. CPY_OP(slash_slash,"//");
  1479. CPY_OP(ellipsis,C_LIKE(language) ? "..." : ".XOR.");
  1480.  
  1481. case dot_const:
  1482.     cpy_op(OC("."));
  1483.     {
  1484.     ASCII *symbol = dots[*begin_arg++].symbol;
  1485.  
  1486.     cpy_op(to_outer(symbol));
  1487.     to_ASCII((outer_char *)symbol);
  1488.     }
  1489.     cpy_op(OC("."));
  1490.     break;
  1491.  
  1492. @ During stringizing, we must just copy verbatim constants or strings.
  1493. (This assumes that no id tokens are buried inside strings.)
  1494. @<Copy stuff between |constant|...@>=
  1495. {
  1496. if(!keep_intact && c==stringg) esc_certain_chars(*begin_arg++,YES);
  1497.         /* Escape the opening quote. */ 
  1498.  
  1499. while(*begin_arg != (eight_bits)c) 
  1500.     {
  1501.     MCHECK(1,"constant");
  1502.     *mp++ = *begin_arg++;
  1503.     }
  1504.  
  1505. if(!keep_intact && c==stringg)
  1506.     esc_certain_chars((sixteen_bits)*(--mp),YES); /* Escape the closing
  1507. quote. */ 
  1508.  
  1509. begin_arg++; /* Skip the closing |stringg| or |constant|. */
  1510. }
  1511.     
  1512. @ During stringizing and certain other places, if the flag |esc_chars|
  1513. is on, we should convert things like a bare double quote to the appropriate
  1514. escaped form. This is language-dependent.
  1515.  
  1516. @a
  1517. SRTN esc_certain_chars FCN((c,esc_chars))
  1518.     sixteen_bits c C0("Character to be maybe escaped.")@;
  1519.     boolean esc_chars C1("Do we escape them?")@;
  1520. {
  1521. if(esc_chars)
  1522. if(C_LIKE(language))
  1523.     {
  1524.     if(c==@'\\' || c==@'"') 
  1525.         {
  1526.         MCHECK(1,"'\\'");
  1527.         *mp++ = @'\\';
  1528.         }
  1529.     }
  1530. else if(R77_or_F)
  1531.     {
  1532.     if(c==@'\'') 
  1533.         {
  1534.         MCHECK(1,"doubled quote");
  1535.         *mp++ = (eight_bits)c; /* Double the quote in Fortran
  1536. string. */ 
  1537.         }
  1538.     }
  1539. else
  1540.     {
  1541.     if(c==@'"')
  1542.         {
  1543.         MCHECK(1,"'\"'");
  1544.         *mp++ = (eight_bits)c;
  1545.         }
  1546.     }
  1547.  
  1548. /* We've added the escape character. Now copy the character itself. */
  1549. MCHECK(1,"escaped character");
  1550. *mp++ = (eight_bits)c;
  1551. }
  1552.  
  1553. @ Associated with stringizing is a predefined macro that creates a string
  1554. from an expanded argument.
  1555. @<Define internal...@>=
  1556.  
  1557. SAVE_MACRO("_STRING(expr)$STRING0(`expr`)"); 
  1558. SAVE_MACRO("$STRING(expr)$STRING0(`expr`)"); /* Expand the argument.
  1559.     Quotes take care of possible commas in |expr|. */
  1560.  
  1561. SAVE_MACRO("_STRING0(expr)#*expr");
  1562. SAVE_MACRO("$STRING0(expr)#*expr");
  1563.  
  1564. @ Here's a macro that takes the length of a string.
  1565. @<Define internal...@>=
  1566.  
  1567. SAVE_MACRO("_LEN(s)$$LEN(#*s)"); // Don't expand argument.
  1568. SAVE_MACRO("$LEN(s)$$LEN(#*s)"); // Don't expand argument.
  1569.  
  1570. @ The internal function that determines the length of a string.
  1571. @a
  1572. SRTN i_len_ FCN((n,pargs))
  1573.     int n C0("")@;
  1574.     PARGS pargs C1("")@;
  1575. {
  1576. int m, num;
  1577.  
  1578. CHK_ARGS("$LEN",1);
  1579.  
  1580. m = (int)(pargs[1] - pargs[0] - 5); 
  1581.     /* 5: 1 from |pargs[0]|, 2 from |constant|, 2 from quotes. 
  1582.         Should this be |unsigned|? */
  1583.  
  1584. num = NSPRINTF((outer_char HUGE *)mp, "%d", m);
  1585. MCHECK(num, "_len_");
  1586. to_ASCII((outer_char HUGE *)mp);
  1587. mp += num;
  1588. }
  1589.  
  1590. @ The inverse of |_STRING| just removes the quotes from a string, so that
  1591. the string contents go verbatim to the output. We also introduce a special
  1592. notation for the preprocessor symbol~'\.\#', namely~|_P|.
  1593. @<Define internal...@>=
  1594.  
  1595. SAVE_MACRO("_VERBATIM(s)$$VERBATIM(s)"); // Possibly expand the argument.
  1596. SAVE_MACRO("$VERBATIM(s)$$VERBATIM(s)"); // Possibly expand the argument.
  1597.  
  1598. SAVE_MACRO("_UNQUOTE(s)$$VERBATIM(s)"); // Alternative name.
  1599. SAVE_MACRO("$UNQUOTE(s)$$VERBATIM(s)"); // Alternative name.
  1600.  
  1601. SAVE_MACRO("_P $VERBATIM(\"#\")"); // Preprocessor symbol.
  1602. SAVE_MACRO("$P $VERBATIM(\"#\")"); // Preprocessor symbol.
  1603.  
  1604. @
  1605. @a
  1606. SRTN i_verbatim_ FCN((n,pargs))
  1607.     int n C0("")@;
  1608.     PARGS pargs C1("")@;
  1609. {
  1610. eight_bits HUGE *p,delim[2];
  1611. eight_bits quote_char[3];
  1612.  
  1613. CHK_ARGS("$VERBATIM",1);
  1614.  
  1615. if(*(p = pargs[0]+1) != stringg) 
  1616.     {
  1617.     MUST_QUOTE("$VERBATIM",p,pargs[1]);
  1618.     return;
  1619.     }
  1620.  
  1621. STRNCPY(delim,@"\0\0",2);
  1622. STRNCPY(quote_char,@"\"\0\0",3);
  1623.  
  1624. /* At this point, |quote_char[0]| is initialized to a double quote. */
  1625. switch(language)
  1626.     {
  1627.    case FORTRAN:
  1628.     quote_char[0] = @'\'';
  1629.     break;
  1630.  
  1631.    case FORTRAN_90:
  1632.     quote_char[1] = @'\''; // Two possibilities for \Fortran--90.
  1633.     break;
  1634.  
  1635.    case TEX:
  1636.     return;
  1637.  
  1638.    default:
  1639.     break;
  1640.     }
  1641.  
  1642. /* Beginning |stringg| token. */
  1643. MCHECK(1,"string token");
  1644. *mp++ = *p++;
  1645.  
  1646. /* Check to ensure it's really a quoted string. */
  1647. delim[0] = *p; // Make the quote character into a string.
  1648.  
  1649. if(STRSPN(delim,quote_char)) p++; // Advance over the quote.
  1650. else delim[0] = stringg;
  1651.  
  1652. while(*p != stringg)
  1653.     {
  1654.     MCHECK(1,"verbatim token");
  1655.     *mp++ = *p++;
  1656.     }
  1657.  
  1658. /* Kill off the final quote, replacing it by |stringg|. */
  1659. if(STRSPN(delim,quote_char)) *(mp-- -1) = stringg;
  1660. }
  1661.  
  1662. @
  1663. @a
  1664. SRTN i_unstring_ FCN((n,pargs))
  1665.     int n C0("")@;
  1666.     PARGS pargs C1("")@;
  1667. {
  1668. eight_bits HUGE *p,delim[2];
  1669. eight_bits quote_char[3];
  1670.  
  1671. CHK_ARGS("$UNSTRING", 1);
  1672.  
  1673. if(*(p = pargs[0]+1) != stringg) 
  1674.     {
  1675.     MUST_QUOTE("$UNSTRING",p,pargs[1]);
  1676.     return;
  1677.     }
  1678.  
  1679. STRNCPY(delim,@"\0\0",2);
  1680. STRNCPY(quote_char,@"\"\0\0",3);
  1681.  
  1682. /* At this point, |quote_char[0]| is initialized to a double quote. */
  1683. switch(language)
  1684.     {
  1685.    case FORTRAN:
  1686.     quote_char[0] = @'\'';
  1687.     break;
  1688.  
  1689.    case FORTRAN_90:
  1690.     quote_char[1] = @'\''; // Two possibilities for \Fortran--90.
  1691.     break;
  1692.  
  1693.    case TEX:
  1694.     return;
  1695.  
  1696.    default:
  1697.     break;
  1698.     }
  1699.  
  1700. /* Skip beginning |stringg| token. */
  1701. p++;
  1702.  
  1703. /* Check to ensure it's really a quoted string. */
  1704. delim[0] = *p; // Make the quote character into a string.
  1705.  
  1706. if(STRSPN(delim, quote_char)) 
  1707.     p++; // Advance over the quote.
  1708. else 
  1709.     delim[0] = stringg;
  1710.  
  1711. while(*p != stringg)
  1712.     {
  1713.     MCHECK(1,"verbatim token");
  1714.     *mp++ = *p++;
  1715.     }
  1716.  
  1717. /* Kill off the final quote */
  1718. if(STRSPN(delim, quote_char)) 
  1719.     mp--;
  1720. }
  1721.  
  1722. @ An error routine for built-ins that don't get a quoted string as argument.
  1723.  
  1724. @d MUST_QUOTE(name,p,p1) must_quote(OC(name),p,p1)
  1725.  
  1726. @a
  1727. SRTN must_quote FCN((name,p,p1))
  1728.     CONST outer_char *name C0("")@;
  1729.     eight_bits HUGE *p C0("")@;
  1730.     eight_bits HUGE *p1 C1("")@;
  1731. {
  1732. MACRO_ERR("! Argument of %s must be a quoted string",YES,name);
  1733.  
  1734. /* Just copy over the argument. */
  1735. MCHECK(p1 - p,"copy quotes");
  1736. while(p < p1) *mp++ = *p++;
  1737. }
  1738.  
  1739. @ Here is another string-related macro, patterned after the
  1740. \.{m4}~|translit|. The call ``|$TRANSLIT(s,from to)|'', where all three
  1741. arguments are strings, modifies~|s| by replacing any character found in
  1742. |from| with the corresponding character of~|to|. If |to|~is shorter
  1743. than~|from|, characters that don't have an entry are deleted.
  1744.  
  1745. @<Define internal...@>=
  1746.  
  1747. SAVE_MACRO("_TRANSLIT(s,from,to)$$TRANSLIT(#*s,#*from,#*to)");
  1748. SAVE_MACRO("$TRANSLIT(s,from,to)$$TRANSLIT(#*s,#*from,#*to)"); /* Make
  1749.    strings from the arguments (but do nothing if they're already strings).  */
  1750.  
  1751. @
  1752. @a
  1753. SRTN i_translit_ FCN((n,pargs))
  1754.     int n C0("")@;
  1755.     PARGS pargs C1("")@;
  1756. {
  1757. int k;
  1758.  
  1759. CHK_ARGS("$TRANSLIT",3);
  1760.  
  1761. for(k=0; k<2; k++)
  1762.     if(*(pargs[k]+1) != stringg) MACRO_ERR("! Argument %d of $TRANSLIT \
  1763. must be a string",YES,k);
  1764.  
  1765. translit((ASCII HUGE *)(pargs[0]+2),
  1766.     (ASCII HUGE *)(pargs[1]+2),
  1767.     (ASCII HUGE *)(pargs[2]+2));
  1768. }
  1769.  
  1770. @ This function actually does the transliteration. 
  1771.  
  1772. @d CHECK_QUOTE(var,n) if(*var++ != end_char) MACRO_ERR("! Argument %d of \
  1773. $TRANSLIT doesn't begin with '%c'",YES,n,end_char)@;
  1774.  
  1775. @a
  1776. SRTN translit FCN((s,from,to))
  1777.     CONST ASCII HUGE *s C0("String to be transliterated")@;
  1778.     CONST ASCII HUGE *from C0("Characters to replace")@;
  1779.     CONST ASCII HUGE *to C1("Replace by")@;
  1780. {
  1781. short code[128],i,n;
  1782. ASCII end_char = *s++;
  1783. ASCII c,cfrom,cto;
  1784. ASCII esc_achar PROTO((CONST ASCII HUGE * HUGE *));
  1785.  
  1786. CHECK_QUOTE(from,1);
  1787. CHECK_QUOTE(to,2);
  1788.  
  1789. @<String token...@>;
  1790.  
  1791. /* First, construct the identity. */
  1792. for(i=0; i<128; i++)
  1793.     code[i] = i;
  1794.  
  1795. /* Put the new characters into the table. */
  1796. while(*(to+1) != stringg)
  1797.     {
  1798.     if(*(from+1) == stringg) break; // Stop when the |from| characters end.
  1799.  
  1800. /* We must watch out for escaped characters. */
  1801.     if((cfrom= *from++) == @'\\') cfrom = esc_achar(&from);
  1802.     if((cto= *to++) == @'\\') cto = esc_achar(&to);
  1803.     
  1804.     code[cfrom] = cto;
  1805.     }
  1806.  
  1807. /* If there are more |from| characters than replacement ones, give the
  1808. extra ones a special delete code of~|-1|. */
  1809. if(*(from+1) != stringg)
  1810.     while(*(from+1) != stringg)
  1811.         {
  1812.         if((cfrom= *from++) == @'\\') cfrom = esc_achar(&from);
  1813.  
  1814.         code[cfrom] = -1; // Delete code.
  1815.         }
  1816.  
  1817. /* Now translate the string. */
  1818. while(*(s+1) != stringg)
  1819.     {
  1820.     if((c= *s++) == @'\\') c = esc_achar(&s);
  1821.  
  1822.     if( (n=code[c]) == -1) continue; // Skip deleted characters.
  1823.     MCHECK(1,"_translit_");
  1824.     *mp++ = (eight_bits)n; // Put the translation into the |macrobuf|.
  1825.     }
  1826.  
  1827. @<String token...@>;
  1828. }
  1829.  
  1830. @ This built-in returns an environmental variable.
  1831. @<Define internal...@>=
  1832.  
  1833. SAVE_MACRO("_GETENV(var)$STRING($$GETENV(#*var))");
  1834. SAVE_MACRO("$GETENV(var)$STRING($$GETENV(#*var))");
  1835.  
  1836. SAVE_MACRO("_HOME $GETENV(HOME)"); 
  1837. SAVE_MACRO("$HOME $GETENV(HOME)"); /* An important special case: the
  1838.                     user's home directory. */
  1839.  
  1840. @ First we make a string out of the argument. Then we query the
  1841. environment for the requested variable. If we get |NULL|, we return the
  1842. empty string; otherwise, we return the answer as an unquoted string of
  1843. characters. 
  1844.  
  1845. @d N_ENVBUF 200
  1846.  
  1847. @d SAVE_ENV(aval) if(t < temp_end) *t++ = XCHR(aval); 
  1848.     else OVERFLW("Env_buf","")@;
  1849.  
  1850. @a
  1851. SRTN i_getenv_ FCN((n,pargs))
  1852.     int n C0("")@;
  1853.     PARGS pargs C1("")@;
  1854. {
  1855. ASCII HUGE *p;
  1856. outer_char *pvar, HUGE *t;
  1857. outer_char HUGE *temp, HUGE *temp_end; /* Holds the name of the requested
  1858.                     variable. */
  1859.  
  1860. #if !HAVE_GETENV
  1861.     MACRO_ERR("Sorry, this machine doesn't support getenv",YES);
  1862. #else
  1863.  
  1864. CHK_ARGS("$GETENV",1);
  1865.  
  1866.  
  1867. temp = GET_MEM("_getenv_:temp",N_ENVBUF,outer_char);
  1868. temp_end = temp + N_ENVBUF;
  1869.  
  1870. for(p=(ASCII HUGE *)(pargs[0]+3),t=temp; *(p+1) != stringg; )
  1871.     SAVE_ENV(*p++);
  1872.  
  1873. SAVE_ENV('\0');
  1874.  
  1875. if( (pvar=GETENV((CONST char *)temp)) != NULL) mcopy(pvar);
  1876.  
  1877. FREE_MEM(temp,"_getenv_:temp",N_ENVBUF,outer_char);
  1878.  
  1879. #endif // |HAVE_GETENV|
  1880. }
  1881.     
  1882. @ If the macro name is recursive, we don't expand it; we just copy the name
  1883. itself. 
  1884. @<Don't expand...@>=
  1885. @{
  1886. name_pointer np;
  1887. CONST ASCII HUGE *end;
  1888.  
  1889. np = name_dir + a;
  1890.  
  1891. PROPER_END(end);
  1892. copy_id(np->byte_start,end,"recursive macro name");
  1893.  
  1894. /* Can't do this; infinite recursion! */
  1895. @#if 0
  1896. MCHECK(2,"recursive macro name");
  1897. *mp++ = LEFT(a,ID0);
  1898. *mp++ = RIGHT(a);
  1899. @#endif
  1900. }
  1901.  
  1902.  
  1903.  
  1904. @* EXPANDING a MACRO.
  1905. Here is the heart of the macro processor. We must actually replace an
  1906. expandable macro token by the replacement text. While processing the
  1907. replacement text, the tokens~'\.\#'  and |MACRO_ARGUMENT| have special
  1908. significance. 
  1909.  
  1910. @<Expand a macro@>=
  1911. @{
  1912. eight_bits n = 0; // Number of actual arguments found.
  1913. eight_bits HUGE *mp0=NULL, HUGE *mp1, HUGE *m_start, HUGE *m_end;
  1914. boolean xpn_argument = YES;
  1915. boolean last_was_paste;
  1916. long max_n = 0; // Maximum statement label offset encountered.
  1917.  
  1918. @b
  1919. /* Get pointers to $n$~actual argument tokens. */
  1920. if(m->nargs > 0 || m->var_args) 
  1921.     p = get_margs0(p-2, end, pcur_byte, the_end,
  1922.         (boolean)(m->var_args), pargs, &n); 
  1923.  
  1924. if( (!m->var_args && n != m->nargs) || (m->var_args && n < m->nargs) )
  1925.     {
  1926.     MACRO_ERR("! Actual number of WEB macro arguments (%u) does not match \
  1927. number of def'n (%u); %s",YES,n,m->nargs,
  1928.         n < m->nargs ? "missing ones assumed to be NULL" : 
  1929.         "extra ones discarded"); 
  1930.  
  1931. /* If there are too many, we'll just ignore the remainder. However, if
  1932. there are too few, we'll essentially supply null arguments by fleshing out
  1933. the pointer list. */
  1934.     while(n < m->nargs)
  1935.         {
  1936.         pargs[n+1] = pargs[n] + 1;
  1937.         n++;
  1938.         }
  1939.     }
  1940.  
  1941. /* Copy macro text, substituting arguments. */
  1942. m_start = mp; /* Remember the beginning. */
  1943. last_was_paste = NO; /* Remember whether last token was |paste|. */
  1944.  
  1945. if(m->built_in)
  1946.     {
  1947.     (*(SRTN (*)(int,unsigned char **))(m->tok_start))(n,pargs);
  1948.     }
  1949. else @<Expand ordinary macro@>@;
  1950.  
  1951. /* If any |paste| tokens were encountered, implement them. */
  1952. if(must_paste) @<Paste expansion.@>@;
  1953.  
  1954. if(max_n > 0) max_stmt += max_n;
  1955.  
  1956. xpn_before(m_start,xids,pcur_byte,the_end);
  1957. #if 0
  1958. if(must_paste) 
  1959. #endif
  1960.     expanded = YES; /* If we pasted something, a new macro may
  1961.                 have been created. */ 
  1962. }
  1963.  
  1964. @
  1965. @<Expand ordinary macro@>=
  1966. {
  1967. /* Beginning and end of the text for this macro. */
  1968. p0 = m->tok_start + m->moffset;
  1969. p1 = (m+1)->tok_start;
  1970.  
  1971. while(p0 < p1)
  1972.     {
  1973.     if(TOKEN1(a = *p0++)) @<``Expand'' a one-byte token@>@;
  1974.     else if(a == MACRO_ARGUMENT) 
  1975.         {
  1976.         eight_bits k = *p0++;
  1977.  
  1978.         pasting = cp_macro_arg(pargs,k,n,&xpn_argument,
  1979.                 last_was_paste,(boolean)(*p0 == paste));
  1980.         }
  1981.     else 
  1982.         {/* Copy nonargument two-byte macro token. */  
  1983.         last_was_paste = NO;
  1984.         MCHECK(2,"nonargument macro token");
  1985.         *mp++ = (eight_bits)a;
  1986.         *mp++ = *p0++;
  1987.         }
  1988.     }
  1989. }
  1990.  
  1991. @ While processing a one-byte token, we must remember if the |paste| token
  1992. appeared, because that means we have more work to do.
  1993. @<``Expand...@>=
  1994. {
  1995. if(!(a==@'#' && *p0==@'.')) last_was_paste = NO;
  1996.  
  1997. if(p0==p1 && a==@'\n') break;
  1998.  
  1999. switch(a)
  2000.     {
  2001.    case @'#':
  2002.     @<Perform stringize or related cases@>@;
  2003.     break;
  2004.  
  2005.    case stringg:
  2006.     MCHECK(1,"\"");
  2007.     *mp++ = (eight_bits)a; // |stringg| token.
  2008.  
  2009.     do
  2010.         {
  2011.         if(!TOKEN1(*mp=*p0++))
  2012.             {
  2013.             MCHECK(1,"id prefix");
  2014.             *++mp = *p0++;
  2015.             }
  2016.         MCHECK(1,"8-bit token");
  2017.         }
  2018.     while(*mp++ != (eight_bits)a);
  2019.  
  2020.     break;
  2021.  
  2022.    case dot_const:
  2023.     MCHECK(2,"dot_const");
  2024.     *mp++ = (eight_bits)a;
  2025.     *mp++ = *p0++;
  2026.     break;
  2027.  
  2028.    default:
  2029. /* Copy over single-byte token; remember if it was |paste|. */ 
  2030.     MCHECK(1,"single-byte token");
  2031.     if( (*mp++ = (eight_bits)a) == paste) 
  2032.         last_was_paste = must_paste = YES;
  2033.     break;
  2034.     }
  2035. }
  2036.  
  2037. @ Here we deal with a macro argument. (The argument number is in |*p0|,
  2038. immediately after the token |MACRO_ARGUMENT|.)
  2039. @a
  2040. boolean cp_macro_arg FCN((pargs,k,n,pxpn_argument,
  2041.         last_was_paste,next_is_paste))
  2042.     PARGS pargs C0("")@;
  2043.     eight_bits k C0("Current argument to process")@;
  2044.     eight_bits n C0("")@;
  2045.     boolean HUGE *pxpn_argument C0("")@;
  2046.     boolean last_was_paste C0("")@;
  2047.     boolean next_is_paste C1("")@;
  2048. {
  2049. boolean pasting;
  2050. eight_bits HUGE *begin_arg, HUGE *end_arg, HUGE *mp0=NULL;
  2051.  
  2052. /* Check for requested argument number bigger than the maximum actually
  2053. used in the call. */
  2054. if(k >= n)
  2055.     { // Make it of zero length.
  2056.     pargs[k] = pargs[n];
  2057.     pargs[k +1] = pargs[n] + 1;
  2058.     }
  2059.  
  2060. begin_arg = pargs[k] + 1; /* The next byte (|k|) after the marker token
  2061.     has the argument number. Make 
  2062.     |begin_arg| point to the token list of the appropriate actual
  2063.     argument. */ 
  2064. while(*begin_arg==@'\n') begin_arg++;
  2065.  
  2066. end_arg = pargs[k + 1]; /* The end is in the next element of |pargs|. */
  2067.  
  2068. /* Check if the last (already copied to |macrobuf|) or next token to this
  2069. parameter is |paste|. */ 
  2070. if(last_was_paste || next_is_paste) pasting = YES;
  2071. else 
  2072.     {
  2073.     pasting = NO;
  2074.     mp0 = mp; /* Remember where this argument text started. */
  2075.     }
  2076.  
  2077. /* Copy the tokens of the argument. If it's a null argument to be pasted,
  2078. explicitly insert a null character to avoid a warning message and/or to
  2079. prevent the paste from pasting the previous identifier. */
  2080. if(begin_arg == end_arg)
  2081.     {
  2082.     if(pasting)
  2083.         {
  2084.         MCHECK(1,"null character");
  2085.         *mp++ = '\0';
  2086.         }        
  2087.     }
  2088. else
  2089.     {/* Copy the argument. */
  2090.     MCHECK(end_arg - begin_arg,"argument tokens");
  2091.     while(begin_arg < end_arg) *mp++ = *begin_arg++;
  2092.     }
  2093.  
  2094. /* If the parameter is to be pasted, the argument does not get expanded.
  2095. It also doesn't get expanded if it was immediately preceded by `\.{\#!}',
  2096. in which case |xpn_argument| was set to |NO|.
  2097. Otherwise, the argument gets expanded before finally substituting it for
  2098. the parameter. */
  2099. if(!*pxpn_argument) *pxpn_argument = YES;
  2100. else if(!pasting) xpn_before(mp0,NULL,NULL,NULL);
  2101.  
  2102. return pasting;
  2103. }
  2104.  
  2105. @ In the ANSI preprocessor, the token `\.{\#}' must be followed by a macro
  2106. argument, when it then means stringize the argument. Here we extend the
  2107. usage to encompass other cases. If `\.{\#}' is followed by a macro token,
  2108. the complete expansion of that macro will be substituted immediately, on
  2109. input.  If the construction `\.{\#!}' is followed by a macro token, the
  2110. token definition of that macro will be copied, but tokens in that
  2111. definition will not be expanded; otherwise, `\.{\#!}' must be followed by a
  2112. macro parameter, which will be substituted but not expanded.  The
  2113. construction `\.{\#\&}' means execute the internal function whose id
  2114. follows. `\.{\#:}$nnn$' is related to automatic generation of labels, where
  2115. when $nnn = 0$ the statement number is assigned immediately (on input), and
  2116. when $nnn > 0$ means generate the current statement number plus~$nnn$ on
  2117. output, uniquely on each execution of the macro.
  2118.  
  2119. @d DOES_ARG_FOLLOW(c)
  2120.     if(*p0 != MACRO_ARGUMENT)
  2121.         {
  2122.        MACRO_ERR("! Macro token `#%c' must be followed by a parameter",YES,c);
  2123.         break;
  2124.         }
  2125.     p0++@; // Skip over |MACRO_ARGUMENT|.
  2126.  
  2127. @<Perform stringize or...@>=
  2128. {
  2129. keep_intact = NO;
  2130.  
  2131. switch(*p0++)
  2132.     {
  2133.    case @'&':
  2134.     @<Expand internal function@>@; break;
  2135.  
  2136.    case @':':
  2137.     @<Generate statement label@>@; break;
  2138.  
  2139.    case @'!':
  2140.     if(*p0 == MACRO_ARGUMENT) xpn_argument = NO;
  2141.     else MACRO_ERR("! Macro token '#!' must be followed by \
  2142. a parameter",YES);
  2143.     break;
  2144.  
  2145.    case @'\'':
  2146.     single_quote = YES;
  2147.     DOES_ARG_FOLLOW('\'');
  2148.     goto do_stringize;
  2149.  
  2150.    case @'"':
  2151.     double_quote = YES;
  2152.     DOES_ARG_FOLLOW('\"'); // Without the escape, bug on VAX.
  2153.     goto do_stringize;
  2154.  
  2155.    case @'*':
  2156.     DOES_ARG_FOLLOW('*');
  2157.     keep_intact = YES;
  2158.     /* Falls through to next case! */
  2159.  
  2160.    case MACRO_ARGUMENT:
  2161.     @<Stringize parameter@>@; break;
  2162.  
  2163.    case @'0':
  2164.     @<Insert the number of variable arguments@>@;        
  2165.     break;
  2166.  
  2167.    case @'{':
  2168.     @<Insert the $n^{\rm th}$ variable argument@>@;
  2169.     break;
  2170.  
  2171.    case @'[':
  2172.     @<Insert the $n^{\rm th}$ fixed argument@>@;
  2173.     break;
  2174.  
  2175.    case @'.':
  2176.     @<Insert all of the variable arguments@>@;
  2177.     break;
  2178.  
  2179.    default: 
  2180.     p0--;
  2181.     MACRO_ERR(_Xx("! Invalid token 0x%x ('%c') after '#'"),YES,
  2182.             *p0,isprint(*p0) ? *p0 : '.');
  2183.     break;
  2184.     }
  2185. }
  2186.  
  2187. @
  2188. @<Insert the number of var...@>=
  2189. {
  2190. eight_bits HUGE *mp0; // For converting the number to |ASCII|.
  2191.  
  2192. p0 += 2; // Skip over null tokens.
  2193.  
  2194. MCHECK(4,"tokens for number of variable arguments");
  2195. *mp++ = constant;
  2196. mp0 = mp;
  2197. mp += NSPRINTF((outer_char *)mp0,"%d",n - m->nargs);
  2198. to_ASCII((outer_char HUGE *)mp0);
  2199. *mp++ = constant;
  2200. }
  2201.  
  2202. @ Format \.{\#[$n$]}:  Insert the $n$-th fixed argument.
  2203.  
  2204. @d INS_ARG_LIST pargs,m,n,&p0,&pasting,&xpn_argument,last_was_paste
  2205.  
  2206. @<Insert the $n^{\rm th}$ fixed argument@>=
  2207. expanded |= ins_arg(@'[',@']',INS_ARG_LIST);
  2208.  
  2209. @ Format \.{\#[$n$]}:  Insert the $n$-th variable argument.
  2210. @<Insert the $n^{\rm th}$ variable argument@>=
  2211. expanded |= ins_arg(@'{',@'}',INS_ARG_LIST);
  2212.  
  2213. @
  2214. @a
  2215. boolean ins_arg FCN((cleft,cright,
  2216.         pargs,m,n,pp0,ppasting,pxpn_argument,last_was_paste)) 
  2217.     ASCII cleft C0("")@;
  2218.     ASCII cright C0("")@;
  2219.     PARGS pargs C0("")@;
  2220.     text_pointer m C0("")@;
  2221.     eight_bits n C0("")@;
  2222.     eight_bits HUGE * HUGE *pp0 C0("")@;
  2223.     boolean *ppasting C0("")@;
  2224.     boolean *pxpn_argument C0("")@;
  2225.     boolean last_was_paste C1("")@;
  2226. {
  2227. int k;
  2228. boolean next_is_paste = BOOLEAN(*(*pp0) == paste);
  2229. eight_bits HUGE *pp;
  2230. eight_bits HUGE *mp0 = mp;
  2231. eight_bits HUGE *p00 = (*pp0);
  2232. boolean fixed = BOOLEAN(cleft == @'[');
  2233.  
  2234. WHILE()
  2235.     if(*(*pp0) == cright) 
  2236.         {
  2237.         break;
  2238.         }
  2239.     else if(TOKEN1(*(*pp0))) (*pp0)++;
  2240.     else (*pp0) += 2;
  2241.         
  2242. pp = xmac_text(mp0,p00,(*pp0)++);
  2243. k = neval(pp,mp);
  2244.  
  2245. mp = mp0;
  2246.  
  2247. /* For debugging */
  2248. if(k == 0)
  2249.     {
  2250.     *mp++ = @'#';
  2251.     *mp++ = @'{';
  2252.  
  2253.     while(p00 < *pp0)
  2254.         *mp++ = *p00++;
  2255.  
  2256.     return YES;
  2257.     }
  2258.  
  2259. if(k <= 0)
  2260.     { /* Insert the total number of arguments. */
  2261.     outer_char temp[5];
  2262.  
  2263.     NSPRINTF(temp,"#%c0%c",5,XCHR(cleft),XCHR(cright));
  2264.     MCHECK(4,temp);
  2265.     *mp++ = constant;
  2266.     mp0 = mp;
  2267.     mp += NSPRINTF((outer_char *)mp0,"%d",n - (fixed ? 0 : m->nargs));
  2268.     to_ASCII((outer_char HUGE *)mp0);
  2269.     *mp++ = constant;
  2270.     }
  2271. else 
  2272.   *ppasting = cp_macro_arg(pargs, (eight_bits)(k-1 + (fixed ? 0 : m->nargs)),
  2273.     n, pxpn_argument, last_was_paste, next_is_paste);
  2274.  
  2275. return NO;
  2276. }
  2277.  
  2278. @ Here we insert the complete list of variable arguments, separated by
  2279. commas, as in~$a,b,c$.
  2280. @<Insert all of the var...@>=
  2281. {
  2282. eight_bits k;
  2283. boolean next_is_paste = BOOLEAN(*p0 == paste);
  2284.  
  2285. for(k=m->nargs; k<n; k++)
  2286.     {
  2287.     pasting = cp_macro_arg(pargs,k,n,&xpn_argument,
  2288.         (boolean)(last_was_paste && k==m->nargs),
  2289.         (boolean)(next_is_paste && k==(eight_bits)(n-1)) );
  2290.     *mp++ = @',';
  2291.     }
  2292.  
  2293. if(*(mp-1) == @',') mp--; 
  2294.     // If we inserted at least one arg, kill off last comma.
  2295. }
  2296.  
  2297. @
  2298. @<Unused@>=
  2299. {
  2300. eight_bits HUGE *begin_arg, HUGE *end_arg;
  2301.  
  2302. begin_arg = pargs[k] + 1;
  2303. while(*begin_arg==@'\n') begin_arg++;
  2304.  
  2305. end_arg = pargs[k+1];
  2306.  
  2307. MCHECK(end_arg - begin_arg+1,"variable argument tokens");
  2308. while(begin_arg < end_arg) *mp++ = *begin_arg++;    
  2309. }
  2310.  
  2311. @ Here we append the tokens of a macro definition, without expanding them.
  2312. @<Unused@>=
  2313. {
  2314. if(m->nargs > 0) 
  2315.     MACRO_ERR("! Macro after #! may not have arguments",YES);
  2316. else
  2317.     {
  2318.     eight_bits HUGE *q0, HUGE *q1;
  2319.  
  2320.     q0 = m->tok_start + m->moffset;
  2321.     q1 = (m+1)->tok_start;
  2322.  
  2323. /* Just copy the definition without expanding. */
  2324.     MCHECK(q1-q0,"unexpanded definition");
  2325.     while(q0 < q1) *mp++ = *q0++;
  2326.     }
  2327. }
  2328.  
  2329. @ Here we expand an argument exhaustively before final substitution.
  2330. @a
  2331. SRTN xpn_before FCN((mp0,xids,pcur_byte,the_end))
  2332.     eight_bits HUGE *mp0 C0("Remember this end of |macro_buf|.")@;
  2333.     XIDS HUGE *xids C0("")@;
  2334.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  2335.     eight_bits HUGE *the_end C1("End of buffer.")@;
  2336. {
  2337. eight_bits HUGE *mp1;
  2338.  
  2339. mp1 = xmac_buf(mp0,xids,pcur_byte,the_end); /* Expand argument before
  2340. substitution. */ 
  2341.  
  2342. while(mp1 < mp) *mp0++ = *mp1++;
  2343.  /* Copy the expansion back to original place. */
  2344.  
  2345. mp = mp0; /* Current end of |macrobuf|. */
  2346. }
  2347.  
  2348. @ When we encounter the juxtaposition `\.{\#\&}', the next identifier must
  2349. correspond to an internal macro function. We get the address of that
  2350. function, then execute it.
  2351. @<Expand internal...@>=
  2352. @{
  2353. sixteen_bits id;
  2354.  
  2355. @b
  2356. if(p0 == p1) MACRO_ERR("! Missing internal function name after #&",YES);
  2357. else
  2358.     {
  2359.     if(TOKEN1(a = *p0++)) MACRO_ERR("! Identifier must follow #&",YES);
  2360.     else if(!x_int_fcn(id=IDENTIFIER(a,*p0++),n,pargs)) 
  2361.         MACRO_ERR("! Internal function name \"%s\" not defined",
  2362.             YES,name_of(id));
  2363.     }
  2364. }
  2365.  
  2366. @ Here we expand a generic internal function.
  2367.  
  2368. @f INTERNAL_FCN int
  2369.  
  2370. @a
  2371. boolean x_int_fcn FCN((id,n,pargs))
  2372.     sixteen_bits id C0("Token for internal function.")@;
  2373.     int n C0("Number of arguments")@;
  2374.     PARGS pargs C1("Array of pointers to arguments.")@;
  2375. {
  2376. INTERNAL_FCN HUGE *f;
  2377.  
  2378. for(f=internal_fcns; f->len != 0; f++)
  2379.     if(f->id == id)
  2380.         {
  2381.         (*f->expnd)(n,pargs); /* Feed the internal function the list
  2382. of (pointers to) arguments; put the expansion into the |macrobuf|. */
  2383.         return YES;
  2384.         }
  2385.  
  2386. return NO; /* Function not found. */
  2387. }
  2388.         
  2389. @ The combination~`\.{\#:}$nnn$', where $nnn$~is a non-negative integer,
  2390. expands to the next available automatic statement label plus~$nnn - 1$.
  2391. @<Generate statement label@>=
  2392. @{
  2393. int m;
  2394. long n; // Label increment.
  2395. outer_char *tmp; // Temporary buffer for the number.
  2396. size_t i;
  2397.  
  2398. @b
  2399. if(*p0 != constant)
  2400.     {
  2401.     MACRO_ERR("Expected constant after \"#:\"",YES);
  2402.     break;
  2403.     }
  2404.  
  2405. p0++; // Position after |constant|.
  2406.  
  2407. for(i=0; p0[i] != constant; i++)
  2408.     ; // Find size of the constant.
  2409.  
  2410. tmp = GET_MEM("stmt number",i+1,outer_char);
  2411.  
  2412. /* Convert to |outer_char|, and also position to after |constant|. */
  2413. for(i=0; *p0 != constant; i++, p0++)
  2414.     tmp[i] = XCHR(*p0);
  2415. tmp[i+1] = '\0';
  2416. p0++;
  2417.  
  2418. n = ATOL(tmp); // Convert the following number.
  2419.  
  2420. FREE_MEM(tmp,"stmt number",i+1,outer_char);
  2421.  
  2422. if(n <= 0) 
  2423.     {
  2424. MACRO_ERR("! Invalid statement number offset (%ld) after #:; 1 assumed",YES,n);
  2425.     n = 1;
  2426.     }
  2427.  
  2428. if(n > max_n) max_n = n; // Remember the maximum offset.
  2429.  
  2430. MCHECK(2,"|constant|");
  2431. *mp++ = constant;
  2432.  
  2433. m = NSPRINTF((outer_char *)mp,"%lu",max_stmt + n - 1);
  2434. MCHECK(m,"stmt label");
  2435. to_ASCII((outer_char HUGE *)mp);
  2436. mp += m;
  2437.  
  2438. *mp++ = constant;
  2439. }
  2440.  
  2441.  
  2442. @ Pasting an expansion is rather complicated. We hunt through the tokens
  2443. looking for |paste|. When we find it, the last and the next objects must be
  2444. expanded side-by-side into their character representations in a buffer.
  2445. Then this expansion must be re-tokenized and substituted for the original
  2446. objects. 
  2447. @<Paste expansion...@>=
  2448. {
  2449. m_end = mp; /* End of the macro tokens to be scanned for pasting; beginning
  2450.         of the new, pasted expansion. */
  2451.  
  2452. /* Copy from |mp0| to |mp|. If we find |paste|, execute that operation. */
  2453. copy_and_paste(m_start,m_end);
  2454.  
  2455. /* Copy pasted expansion back to start of this macro. */
  2456. for(mp1=mp,mp=m_start,mp0=m_end; mp0<mp1; )
  2457.     *mp++ = *mp0++;
  2458. }
  2459.  
  2460. @ Here we copy tokens into the |macrobuf| beginning at |mp|. If we find
  2461. |paste|, we execute that operation.
  2462. @a
  2463. eight_bits HUGE *copy_and_paste FCN((m_start,m_end))
  2464.     eight_bits HUGE *m_start C0("Start of range.")@;
  2465.     eight_bits HUGE *m_end C1("End of range.")@;
  2466. {
  2467. eight_bits HUGE *mp0;
  2468. eight_bits a0;
  2469. eight_bits HUGE *m_last = m_start; // Remember start of last token.
  2470.  
  2471. for(mp0=m_start; mp0 < m_end; )
  2472.     {
  2473.     if(TOKEN1(a0=*mp0)) 
  2474.         {
  2475.         if(a0 == paste) @<Juxtapose left and right.@>@;
  2476.         else
  2477.             {
  2478.             if(a0 == ignore) 
  2479.                 {
  2480.                 mp0++; // Just skip any nulls that sneak in.
  2481.                 continue;
  2482.                 }
  2483.  
  2484.             m_last = mp;
  2485.  
  2486.             switch(a0)
  2487.                 {
  2488.                 case constant:
  2489.                 case stringg:
  2490.                     MCHECK(1,"|constant| or |stringg|");
  2491.                     *mp++ = *mp0++;
  2492.  
  2493.                     do
  2494.                         {
  2495.                         *mp = *mp0++;
  2496.                         MCHECK(1,"text of \
  2497. |constant| or |stringg|");
  2498.                         }
  2499.                     while (*mp++ != a0);
  2500.  
  2501.                     break;
  2502.  
  2503.                 case dot_const:
  2504.                     MCHECK(2,"dot_const");
  2505.                     *mp++ = *mp0++;
  2506.                     *mp++ = *mp0++;
  2507.                     break;
  2508.  
  2509.                 default: /* Copy ASCII token. */
  2510.                     MCHECK(1,"ASCII token");
  2511.                     *mp++ = *mp0++;
  2512.                     break;
  2513.                 }
  2514.             }
  2515.         }
  2516.     else
  2517.         { /* Copy two-byte token. */
  2518.         m_last = mp;
  2519.         MCHECK(2,"two-byte token");
  2520.         *mp++ = *mp0++; *mp++ = *mp0++;
  2521.         }
  2522.     }
  2523.  
  2524. return m_last;
  2525. }
  2526.  
  2527. @ To do token-pasting, we must first juxtapose the expansions of the tokens
  2528. to the left and right of the |paste| token. Then we must retokenize the
  2529. juxtaposition. 
  2530.  
  2531. @d STOP YES
  2532.  
  2533. @<Juxtapose...@>=
  2534. {
  2535. eight_bits HUGE *p;
  2536.  
  2537. p = mp; /* Beginning of the juxtaposition. */
  2538.  
  2539. paste1(m_last,m_start); /* Paste tokens to left of `\.{\#\#}'. */
  2540. mp0 = paste1(++mp0,m_end); /* Paste tokens to right. */
  2541.  
  2542. /* Tokenize the juxtaposition. */
  2543. divert((ASCII HUGE *)p,(ASCII HUGE *)mp,STOP); /* Make the next |scan_repl|
  2544.     read from |macrobuf| between~|p| and~|mp|. */ 
  2545. scan_repl(macro,STOP);
  2546.  
  2547. /* Copy tokenized stuff back into |macrobuf|, overwriting the juxtaposition. */
  2548. mp = m_last;
  2549. m_last = copy_and_paste(cur_text->tok_start,tok_ptr);
  2550.  
  2551. /* Back up the text buffer. */
  2552. text_ptr = cur_text;
  2553. mx_tok_ptr = tok_ptr;
  2554. tok_ptr = text_ptr->tok_start;
  2555. }
  2556.  
  2557. @ Here we expand the tokens beginning at~|p0| into the |macrobuf|. The
  2558. routine returns the next position in the input buffer. 
  2559. @a
  2560. eight_bits HUGE *paste1 FCN((p0,begin_or_end))
  2561.     eight_bits HUGE *p0 C0("Beginning of tokens to be expanded.")@;
  2562.     eight_bits HUGE *begin_or_end C1("")@;
  2563. {
  2564. eight_bits a0,a1;
  2565. sixteen_bits a;
  2566.  
  2567. if(p0 == begin_or_end)
  2568.     {
  2569.     MACRO_ERR("! Missing argument to token-paste operation. Null assumed",
  2570.             YES);
  2571.     return p0;
  2572.     }
  2573.  
  2574. if(TOKEN1(a0=*p0++))
  2575.     switch(a0)
  2576.         {
  2577.         case ignore: break;
  2578.  
  2579.         case constant:
  2580.         case stringg:
  2581. /* Copy the stuff sandwiched between tokens. */
  2582.             while( (a1=*p0++) != a0)
  2583.                 {
  2584.                 MCHECK(1,"stuff between tokens");
  2585.                 *mp++ = a1;
  2586.                 }
  2587.             break;
  2588.  
  2589.         case dot_const:
  2590.             MCHECK(2,"dot_const");
  2591.             *mp++ = a0;
  2592.             *mp++ = *p0++;
  2593.             break;
  2594.  
  2595.         default:
  2596.             MCHECK(1,"default ASCII token");
  2597.             *mp++ = a0; /* Copy ASCII token. */
  2598.             break;
  2599.         }
  2600. else
  2601.     {
  2602.     a = IDENTIFIER(a0,*p0++);
  2603.  
  2604.     if(a < MODULE_NAME)
  2605.         {
  2606.         name_pointer np;
  2607.  
  2608.         np = name_dir + a;
  2609.         @<Copy possibly truncated identifier to macro buffer@>@;
  2610.         }
  2611.     else {} /* ?? */
  2612.     }
  2613.  
  2614. return p0;
  2615. }
  2616.  
  2617. @
  2618. @<Copy possibly truncated id...@>=
  2619. {
  2620. TRUNC HUGE *s;
  2621. ASCII HUGE *pc = np->byte_start;
  2622.  
  2623. if(*pc != BP_MARKER)
  2624.     { /* Not truncated. */
  2625.     CONST ASCII HUGE *end;
  2626.  
  2627.     PROPER_END(end);
  2628.     copy_id((CONST ASCII HUGE *)pc,end,"copied id");
  2629.     }
  2630. else
  2631.     {
  2632.     s = ((BP HUGE *)pc)->Root;
  2633.     copy_id(s->id,s->id_end,"copied id");
  2634.     }
  2635. }
  2636.  
  2637. @ Copy an identifier into the macro buffer.
  2638. @a
  2639. SRTN copy_id FCN((start,end,descr))
  2640.     CONST ASCII HUGE *start C0("Beginning of identifier name.")@;
  2641.     CONST ASCII HUGE *end C0("End of identifier name.")@;
  2642.     CONST char *descr C1("")@;
  2643. {
  2644. CONST ASCII HUGE *j;
  2645.  
  2646. MCHECK(end - start,descr);
  2647.  
  2648. for (j=start; j<end; )
  2649.     *mp++ = (eight_bits)(*j++);
  2650. }
  2651.  
  2652. @ Report macro buffer overflow, and abort. 
  2653.  
  2654. @a
  2655. SRTN mbuf_full FCN((n,reason))
  2656.     unsigned long n C0("Number of bytes requested.")@;
  2657.     CONST outer_char reason[] C1("Reason for request.")@;
  2658. {
  2659. MACRO_ERR("! Macro buffer full; %lu byte(s) requested for %s",YES,n,reason);
  2660. OVERFLW("macro buffer bytes",ABBREV(mbuf_size));
  2661. }
  2662.  
  2663. /* Interface from independently compiled modules. */
  2664. SRTN mcheck0 FCN((n,reason))
  2665.     unsigned long n C0("Number of bytes requested.")@;
  2666.     CONST outer_char reason[] C1("Reason for request.")@;
  2667. {
  2668. MCHECK(n,reason);
  2669. }
  2670.  
  2671. @ Do the complete, recursive expansion of a macro.
  2672. @a
  2673. eight_bits HUGE *xmacro FCN((macro_text,pcur_byte,the_end,mp0))
  2674.     text_pointer macro_text C0("")@;
  2675.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  2676.     eight_bits HUGE *the_end C0("End of buffer.")@;
  2677.     eight_bits HUGE *mp0 C1("Build the expansion beginning here in \
  2678. |macrobuf|.")@; 
  2679. {
  2680. eight_bits HUGE *macro_start;
  2681. extern long cur_val;
  2682.  
  2683. /* Copy the token of this macro. */
  2684. mp = mp0; /* Current position in |macrobuf|. */
  2685.  
  2686. MCHECK(2,"macro token");
  2687.  
  2688. if(macro_text->built_in)
  2689.     {
  2690.     *mp++ = LEFT(cur_val,ID0);
  2691.     *mp++ = RIGHT(cur_val);
  2692.     }
  2693. else
  2694.     {
  2695.     macro_start = macro_text->tok_start;
  2696.     *mp++ = *macro_start++; *mp++ = *macro_start++;
  2697.     }
  2698.  
  2699. /* If there are arguments, must get more tokens, through end of
  2700. parens. Put all these into beginning of |macrobuf|. */ 
  2701. if(macro_text->nargs > 0 || macro_text->var_args) 
  2702.     mp = args_to_macrobuf(mp, pcur_byte, the_end,
  2703.         (boolean)(macro_text->var_args));
  2704.  
  2705. return xmac_buf(mp0,NULL,pcur_byte,the_end); /* Start at expansion level~0;
  2706.     return pointer to start of final expansion. */
  2707. }
  2708.  
  2709. @ The following routine places all the argument tokens into the
  2710. |macro_buf|, ready for expansion. We must watch out for nested parentheses.
  2711. @a
  2712. eight_bits HUGE *args_to_macrobuf FCN((mp,pcur_byte,the_end,var_args))
  2713.     eight_bits HUGE *mp C0("Next available position in |macro_buf|.")@;
  2714.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  2715.     eight_bits HUGE *the_end C0("End of buffer.")@;
  2716.     boolean var_args C1("Does macro have variable args?")@;
  2717. {
  2718. eight_bits c; /* First token of identifier. */
  2719. sixteen_bits id_token; /* Name of this macro. */
  2720. int bal = 0; /* Keep track of balanced parens. */
  2721.  
  2722. id_token = IDENTIFIER(*(mp-2),*(mp-1)); /* Name of the macro; remember for
  2723.                         error processing. */ 
  2724.  
  2725. do
  2726.     {
  2727.     if(*pcur_byte == the_end) 
  2728.         {
  2729.         MACRO_ERR("! No ')' in call to macro \"%s\"",YES,
  2730.             name_of(id_token));
  2731.         break;
  2732.         }
  2733.  
  2734.     MCHECK(1,"arg to macrobuf");
  2735.     c = *mp++ = *(*pcur_byte)++;
  2736.  
  2737.     if(TOKEN1(c)) 
  2738.         @<Copy single character of argument@>@;
  2739.     else
  2740.         {/* Copy second token of identifier, or stuff relating to
  2741. module name and line number. */
  2742.         int n; /* Number of remaining bytes to copy. */
  2743.  
  2744.         n = (c < MOD0 ? 1 : 3 + 4*1); // `1' for |line_info|.
  2745.         MCHECK(n,"second id token");
  2746.         while(n-- > 0) *mp++ = *(*pcur_byte)++;
  2747.         continue;
  2748.         }
  2749.     }
  2750. while(bal > 0);
  2751.  
  2752. done_copying:
  2753. return mp; /* New end. */
  2754. }
  2755.  
  2756. @
  2757. @<Copy single character of arg...@>=
  2758. {
  2759. switch(c)
  2760.     {
  2761.    case stringg:
  2762.     do
  2763.         {
  2764.         MCHECK(1,"string arg");
  2765.         *mp = *(*pcur_byte)++;
  2766.         }
  2767.     while(*mp++ != stringg);
  2768.     break;
  2769.  
  2770.    case dot_const:
  2771.     MCHECK(1,"dot const");
  2772.     *mp++ = *(*pcur_byte)++;
  2773.     break;
  2774.  
  2775.    case @'(':
  2776.     bal++;
  2777.     break;
  2778.  
  2779.    case @')':
  2780.     if(bal == 0 && !var_args) 
  2781.         {
  2782.         MACRO_ERR("! Missing '(' in call to macro \"%s\"",YES,
  2783.             name_of(id_token));
  2784.         goto done_copying;
  2785.         }
  2786.     else bal--;
  2787.         
  2788.     break;
  2789.        }
  2790. }
  2791.  
  2792. @ Expand the macro buffer. Keep expanding until nothing more. The original
  2793. thing to be expanded, either just a macro token or the token plus its
  2794. argument list, starts off in the beginning of |macrobuf|. Successive
  2795. translations are put after that, until on the final pass no macros were
  2796. expanded. |mp|~points to the next free position in |macrobuf|.
  2797.  
  2798. (Some of the code here may be archaic and/or redundant, because of
  2799. changes made in the order of recursive expansion. In some cases, |x0macro|
  2800. may be called one more time than necessary. Fixing this up might save some
  2801. time in macro-bound codes.)
  2802. @a
  2803. eight_bits HUGE *xmac_buf FCN((mp0,old_xids,pcur_byte,the_end))
  2804.     eight_bits HUGE *mp0 C0("Text to be expanded begins here.")@;
  2805.     XIDS HUGE *old_xids C0("")@;
  2806.     eight_bits HUGE **pcur_byte C0("Pointer to |cur_byte|.")@;
  2807.     eight_bits HUGE *the_end C1("End of buffer.")@;
  2808. {
  2809. eight_bits HUGE *p, HUGE *p1;
  2810. XIDS xids;
  2811. XIDS HUGE *pid;
  2812.  
  2813. xids.level = 0;
  2814.  
  2815. if(xlevel >= MAX_XLEVELS) 
  2816.     {
  2817.     MACRO_ERR("! Macro outer recursion depth exceeded",YES);
  2818.     FATAL(M, "!! BYE.","");
  2819.     }
  2820.  
  2821. pid = pids[xlevel++] = old_xids ? old_xids : &xids; /* Store the address of
  2822.             this bunch of recursive names. */
  2823.  
  2824. for(p=mp0, p1= mp; x0macro(p,p1,pid,pcur_byte,the_end); p=p1, p1=mp);
  2825.  
  2826. xlevel--; // Pop the outer recursion stack.
  2827.  
  2828. return p1; // Return beginning of the expanded text.
  2829. }
  2830.  
  2831. @ Copy unexpanded text to the macro buffer, expand it, and return the
  2832. location of the expanded stuff.
  2833. @a
  2834. eight_bits HUGE *xmac_text FCN((mp0,start,end))
  2835.     eight_bits HUGE *mp0 C0("")@;
  2836.     eight_bits HUGE *start C0("")@;
  2837.     eight_bits HUGE *end C1("")@;
  2838. {
  2839. /* Copy the text to the macrobuf. */
  2840. for(mp=mp0; start < end; )
  2841.     *mp++ = *start++;
  2842.  
  2843. /* Expand the contents and return pointer. */
  2844. return xmac_buf(mp0,NULL,NULL,NULL);
  2845. }
  2846.  
  2847. @* BUILT-IN FUNCTIONS.
  2848. Generate a comment in the output.
  2849. @<Define internal...@>=
  2850.  
  2851. SAVE_MACRO("_COMMENT(cmnt)$$META(#*cmnt)");
  2852. SAVE_MACRO("$COMMENT(cmnt)$$META(#*cmnt)");
  2853.  
  2854. @
  2855.  
  2856. @d arg_must_be_constant(name) 
  2857.     MACRO_ERR("Argument of \"%s\" must be constant or string",YES,name);
  2858.  
  2859. @a
  2860. SRTN i_meta_ FCN((n,pargs))
  2861.     int n C0("")@;
  2862.     PARGS pargs C1("")@;
  2863. {
  2864. eight_bits HUGE *p;
  2865.  
  2866. CHK_ARGS("$COMMENT",1);
  2867.  
  2868. IS_IT_CONSTANT($COMMENT);
  2869.  
  2870. @<Write begin-comment token to |macrobuf|@>;
  2871.  
  2872. *(p+1) = *(pargs[1]-2) = @' '; /* Change quotes to blanks. */
  2873.  
  2874. do
  2875.     {
  2876.     MCHECK0(1,"_meta_");
  2877.     *mp++ = *p++;
  2878.     }
  2879. while(p < pargs[1]);
  2880.  
  2881. @<Write end-comment token to |macrobuf|@>;
  2882. }
  2883.  
  2884. @ In the initialization of |begin_C_meta|, we use the octal definition of
  2885. |constant| (see \.{t\_codes}).  This is necessary since otherwise a space is
  2886. inserted between~'\./' and~'\.*' to handle expressions such as \.{x / *p}.
  2887.  
  2888. @<Write begin-comment...@>=
  2889. @{
  2890. static eight_bits begin_C_meta[] = {constant,@'/',@'*',constant,'\0'};
  2891. eight_bits HUGE *p;
  2892.  
  2893. @b
  2894. if(C_LIKE(language))
  2895.     {
  2896.     MCHECK0(4,"begin_C_meta");
  2897.     for(p=begin_C_meta; *p; ) *mp++ = *p++;
  2898.     }
  2899. else
  2900.     {
  2901.     MCHECK0(2,"begin_meta");
  2902.     *mp++ = begin_meta;
  2903.     *mp++ = begin_meta;
  2904.     }
  2905. }
  2906.  
  2907. @
  2908. @<Write end-comment...@>=
  2909. @{
  2910. static eight_bits end_C_meta[] = @"*/";
  2911. eight_bits HUGE *p;
  2912.  
  2913. @b
  2914. if(C_LIKE(language))
  2915.     {
  2916.     MCHECK0(2,"end_C_meta");
  2917.     for(p=end_C_meta; *p; ) *mp++ = *p++;
  2918.     }
  2919. else
  2920.     {
  2921.     MCHECK0(1,"end_meta");
  2922.     *mp++ = end_meta;
  2923.     }
  2924. }
  2925.  
  2926. @
  2927. @m IS_IT_CONSTANT(name)
  2928. p = pargs[0] + 1;
  2929. if(!(*p == constant || *p == stringg))
  2930.     {
  2931.     arg_must_be_constant(#name);
  2932.     return;
  2933.     }
  2934.  
  2935. @ Assert a preprocessor condition.
  2936. @a
  2937. SRTN i_assert_ FCN((n,pargs))
  2938.     int n C0("")@;
  2939.     PARGS pargs C1("")@;
  2940. {
  2941. eight_bits HUGE *p;
  2942. eight_bits HUGE *pp;
  2943. eight_bits HUGE *mp0;
  2944. boolean e;
  2945.  
  2946. CHK_ARGS("$ASSERT",1);
  2947.  
  2948. pp = xmac_text(mp0=mp, p=pargs[0]+1, pargs[1]); // Expand the expression.
  2949. e = eval(pp, mp);
  2950. mp = mp0;
  2951.  
  2952. if(e)
  2953.     return;
  2954.  
  2955. mp = str_to_mb(p, pargs[1], YES);
  2956.  
  2957. MACRO_ERR("! $ASSERT(%s) failed",NO,to_outer((ASCII HUGE *)mp));
  2958. FATAL(M, "","Processing ABORTED!");
  2959. }
  2960.  
  2961. @ Generate error message.
  2962. @<Define internal...@>=
  2963.  
  2964. SAVE_MACRO("_ERROR(text)$$ERROR(#*text)");
  2965. SAVE_MACRO("$ERROR(text)$$ERROR(#*text)");
  2966.  
  2967. @
  2968. @a
  2969. SRTN i_error_ FCN((n,pargs))
  2970.     int n C0("")@;
  2971.     PARGS pargs C1("")@;
  2972. {
  2973. eight_bits c;
  2974. eight_bits HUGE *t, HUGE *p, HUGE *temp;
  2975.  
  2976. CHK_ARGS("$ERROR",1);
  2977.  
  2978. IS_IT_CONSTANT($ERROR);
  2979.  
  2980. temp = GET_MEM("_error_:temp",N_MSGBUF,eight_bits);
  2981.  
  2982. for(c=*p++,t=temp; *p != c; ) *t++ = *p++;
  2983. *t = '\0';
  2984.  
  2985. MACRO_ERR("%cUSER ERROR:  %s",NO, beep(1),to_outer((ASCII HUGE *)temp));
  2986. FREE_MEM(temp,"_error_:temp",N_MSGBUF,eight_bits);
  2987. }
  2988.  
  2989. @ The internal macro |$ROUTINE| generates a string containing the name of
  2990. the current routine.  This macro is associated with the internal function
  2991. |_routine_|, below.
  2992.  
  2993. @<Define internal macros@>=
  2994.  
  2995. SAVE_MACRO("_ROUTINE $STRING($$ROUTINE)");
  2996. SAVE_MACRO("$ROUTINE $STRING($$ROUTINE)");
  2997.  
  2998. @ The internal function |_routine_| expands |cur_fcn| into the |macro_buf|.
  2999. @a
  3000. SRTN i_routine_ FCN((n,pargs))
  3001.     int n C0("")@;
  3002.     PARGS pargs C1("")@;
  3003. {
  3004. name_pointer np;
  3005. CONST ASCII HUGE *f, HUGE *end;
  3006.  
  3007. CHK_ARGS("$ROUTINE",0);
  3008.  
  3009. if(!(is_RATFOR_(language))) return; // So far, only \Ratfor\ is active.
  3010. if(!RAT_OK("")) CONFUSION("_routine_","Language shouldn't be Ratfor here");
  3011.  
  3012. if(cur_fcn == NO_FCN)
  3013.     {
  3014.     MCHECK0(1,"'?'");
  3015.     *mp++ = @'?';
  3016.     return;
  3017.     }
  3018.  
  3019. np = name_dir + cur_fcn;
  3020. end = proper_end(np);
  3021.  
  3022. MCHECK0(end - np->byte_start,"_routine_");
  3023. for(f = np->byte_start; f < end; )
  3024.     *mp++ = *f++;
  3025. }
  3026.  
  3027.  
  3028. @ Case conversion of macro argument.
  3029. @<Define internal macros@>=
  3030.  
  3031. SAVE_MACRO("_L(name)$$LC(name)"); // Possibly expand the argument.
  3032. SAVE_MACRO("$L(name)$$LC(name)"); // Possibly expand the argument.
  3033.  
  3034. SAVE_MACRO("_U(name)$$UC(name)");
  3035. SAVE_MACRO("$U(name)$$UC(name)");
  3036.  
  3037. @
  3038. @a
  3039. SRTN i_lowercase_ FCN((n,pargs))
  3040.     int n C0("")@;
  3041.     PARGS pargs C1("")@;
  3042. {
  3043. eight_bits HUGE *p = pargs[0] + 1, HUGE *p1 = pargs[1];
  3044.  
  3045. CHK_ARGS("$LC",1);
  3046.  
  3047. if(*p != stringg) 
  3048.     {
  3049.     MUST_QUOTE("_L",p,p1);
  3050.     return;
  3051.     }
  3052.  
  3053. MCHECK(p1 - p,"lowercase");
  3054.  
  3055. for( ; p<p1; p++)
  3056.     *mp++ = A_TO_LOWER(*p); // Watch out for side effects in |A_TO_LOWER|!
  3057. }
  3058.  
  3059. SRTN i_uppercase_ FCN((n,pargs))
  3060.     int n C0("")@;
  3061.     PARGS pargs C1("")@;
  3062. {
  3063. eight_bits HUGE *p = pargs[0] + 1, HUGE *p1 = pargs[1];
  3064.  
  3065. CHK_ARGS("$UC",1);
  3066.  
  3067. if(*p != stringg) 
  3068.     {
  3069.     MUST_QUOTE("_U",p,p1);
  3070.     return;
  3071.     }
  3072.  
  3073. MCHECK(p1 - p,"lowercase");
  3074.  
  3075. for( ; p<p1; p++)
  3076.     *mp++ = A_TO_UPPER(*p); // Watch out for side effects in |A_TO_LOWER|!
  3077. }
  3078.  
  3079. @
  3080. @<Define internal macros@>=
  3081.  
  3082. SAVE_MACRO("_NARGS(mname)$$NARGS(#!mname)");
  3083. SAVE_MACRO("$NARGS(mname)$$NARGS(#!mname)");
  3084.  
  3085. @ Determining the number of fixed arguments.
  3086. @a
  3087. SRTN i_nargs_ FCN((n,pargs))
  3088.     int n C0("")@;
  3089.     PARGS pargs C1("")@;
  3090. {
  3091. text_pointer m;
  3092. eight_bits *pa = pargs[0] + 1;
  3093.  
  3094. if((m=MAC_LOOKUP(IDENTIFIER(pa[0],pa[1]))) == NULL)
  3095.     {
  3096.     MACRO_ERR("! Argument of $NARGS is not a WEB macro",YES);
  3097.     put_long(-1L);
  3098.     }
  3099. else put_long((long)m->nargs);
  3100. }
  3101.  
  3102. @ Put a long integer into the macro buffer as a constant.
  3103. @a
  3104. SRTN put_long FCN((l))
  3105.     long l C1("")@;
  3106. {
  3107. outer_char temp[100];
  3108. int n;
  3109.  
  3110. n = NSPRINTF(temp,"%ld",l);
  3111. to_ASCII(temp);
  3112. MCHECK(n+2,"long");
  3113. *mp++ = constant;
  3114. STRCPY(mp,temp);
  3115. mp += n;
  3116. *mp++ = constant;
  3117. }
  3118.  
  3119. @ The code for checking the correct number of arguments in a built-in macro
  3120. call isn't complete yet, since most of them go through an intermediate
  3121. level of expansion.
  3122. @a
  3123. SRTN chk_args FCN((name,proper_num,actual_num,pargs))
  3124.     outer_char *name C0("")@;
  3125.     int proper_num C0("")@;
  3126.     int actual_num C0("")@;
  3127.     PARGS pargs C1("")@;
  3128. {
  3129. if(proper_num >= 0)
  3130.     {
  3131.     if(actual_num != proper_num)
  3132.         MACRO_ERR("Built-in macro %s should be called with %d \
  3133. argument(s), not %d",NO,name,proper_num,actual_num);
  3134.     }
  3135. }
  3136.  
  3137. @ This code is used for debugging; it displays the token list for a macro
  3138. in a slightly translated form. This function can be called from the debugger.
  3139.  
  3140. @d MTEXT_SIZE 2500
  3141.  
  3142. @d SAVE_MTEXT(val) if(p < mtext_end) *p++ = (eight_bits)(val);
  3143.     else OVERFLW("Mtext","")@;
  3144.  
  3145. @a
  3146. SRTN see_macro FCN((p0,p1))
  3147.     CONST eight_bits HUGE *p0 C0("Beginning of token list.")@;
  3148.     CONST eight_bits HUGE *p1 C1("End of token list.")@;
  3149. {
  3150. int k,l,num_tokens;
  3151. ASCII HUGE *q0;
  3152. sixteen_bits HUGE *tokens;
  3153. ASCII HUGE *mtext;
  3154.  
  3155. num_tokens = PTR_DIFF(int, p1, p0); // Why is this |int|?
  3156.  
  3157. tokens = GET_MEM("see_macro:tokens",num_tokens,sixteen_bits);
  3158. mtext = GET_MEM("see_macro:mtext",MTEXT_SIZE,ASCII);
  3159.  
  3160. k = rcvr_macro(mtext,tokens,p0,p1);
  3161.  
  3162. printf(">> \"");
  3163.     for(l=0; l<k; ++l)
  3164.         printf(_Xx("%x "),tokens[l]);
  3165.  
  3166. printf("\"\n== \"");
  3167.     for(q0=mtext; q0<mtext+k; ++q0)
  3168.         putchar(XCHR(*q0));
  3169. puts("\"");
  3170.  
  3171. FREE_MEM(mtext,"see_macro:mtext",MTEXT_SIZE,ASCII); 
  3172. if(num_tokens) FREE_MEM(tokens,"see_macro:tokens",num_tokens,sixteen_bits);
  3173. }
  3174.  
  3175. @ Translate a macro into readable form.
  3176. @a
  3177. int rcvr_macro FCN((mtext,tokens,p0,p1))
  3178.     ASCII HUGE *mtext C0("Holds readable translation of the    text.")@;
  3179.     sixteen_bits HUGE *tokens C0("Slightly translated tokens.")@;
  3180.     CONST eight_bits HUGE *p0 C0("")@;
  3181.     CONST eight_bits HUGE *p1 C1("")@;
  3182. {
  3183. ASCII HUGE *mtext_end = mtext + MTEXT_SIZE;
  3184. ASCII HUGE *p; // Current position in output text buffer.
  3185. ASCII HUGE *j;
  3186. int k;
  3187. sixteen_bits a; // The current token.
  3188.  
  3189. for(k=0,p=mtext; p0 < p1; k++)
  3190.     {
  3191.     if(TOKEN1(a = *p0++))
  3192.         switch(a)
  3193.             {
  3194.             case paste:
  3195.                 SAVE_MTEXT(@'#'); @+ SAVE_MTEXT(@'#');
  3196.                 break;
  3197.  
  3198.             default: 
  3199.                 SAVE_MTEXT(a);
  3200.                 break;
  3201.             }
  3202.     else if(a == MACRO_ARGUMENT)
  3203.         {
  3204.         SAVE_MTEXT(@'$');
  3205.         a = (sixteen_bits)(-(*p0));
  3206.         SAVE_MTEXT(*p0++ + @'0'); // Only for 9 or less???
  3207.         }
  3208.     else            
  3209.         {
  3210.         a = IDENTIFIER(a,*p0++);
  3211.  
  3212.         if(a < MODULE_NAME)
  3213.             {
  3214.             CONST ASCII HUGE *end;
  3215.             name_pointer np = name_dir + a;
  3216.  
  3217.             PROPER_END(end);
  3218.  
  3219.             for(j=np->byte_start; j<end; ++j)
  3220.                 {SAVE_MTEXT(*j);}
  3221.             }
  3222.         else
  3223.             {
  3224.             SAVE_MTEXT(@'M');
  3225.             }
  3226.         }
  3227.  
  3228.     if(tokens) tokens[k] = a; // Should have special color marker for ids.
  3229.     }
  3230.  
  3231. return k;
  3232. }
  3233.  
  3234. @ For manipulating the behavior of various macros, we set a global variable
  3235. |xflag| with the aid of the |$XX| macro.
  3236. @<Glob...@>=
  3237.  
  3238. int xflag = 1;
  3239.  
  3240. @
  3241. @a
  3242. SRTN i_xflag_ FCN((n,pargs))
  3243.     int n C0("")@;
  3244.     PARGS pargs C1("")@;
  3245. {
  3246. eight_bits HUGE *p = pargs[0] + 1;
  3247. outer_char temp[100],*t=temp;
  3248.  
  3249. CHK_ARGS("$XX",1);
  3250.  
  3251. if(*p++ != constant)
  3252.     {
  3253.     MACRO_ERR("Argument of $XX is not a numerical constant",NO);
  3254.     return;
  3255.     }
  3256.  
  3257. while(*p != constant)
  3258.     *t++ = XCHR(*p++);
  3259.  
  3260. TERMINATE(t,0);
  3261.  
  3262. xflag = ATOI(temp);
  3263. }
  3264.  
  3265. @
  3266. @a
  3267. SRTN i_dumpdef_ FCN((n,pargs))
  3268.     int n C0("")@;
  3269.     PARGS pargs C1("")@;
  3270. {
  3271. int k;
  3272. eight_bits HUGE *p,HUGE *mp0,HUGE *mp1,HUGE *mp2;
  3273. sixteen_bits a;
  3274. extern long cur_val;
  3275. eight_bits HUGE *q0,HUGE *q1;
  3276. ASCII HUGE *mtext = GET_MEM("rcvr_macro:mtext",MTEXT_SIZE,ASCII);
  3277. ASCII HUGE *mx, HUGE *mx0;
  3278. name_pointer np;
  3279.  
  3280. CHK_ARGS("$DUMPDEF",INT_MIN);
  3281.  
  3282. for(k=0; k<n; k++)
  3283.     { /* Print translation of $k^{{\rm th}}$ macro. */
  3284.     text_pointer m;
  3285.  
  3286.     if(xflag) 
  3287.         printf("\n");
  3288.  
  3289.     mp0 = mp;
  3290.  
  3291.     p = pargs[k] + 1; // Start of argument.
  3292.  
  3293.     while(IS_WHITE(*p) || *p==@'\n') p++;
  3294.  
  3295.     a = IDENTIFIER(*p,*(p+1));
  3296.  
  3297.     if( (m=MAC_LOOKUP(a)) == NULL)
  3298.         { /* Not a valid WEB macro. */
  3299.         str_to_mb(p,pargs[k+1],NO);
  3300.         printf("NOT WEB MACRO:  %s\n",(char *)to_outer((ASCII *)mp0));
  3301.         }
  3302.     else
  3303.         @<Dump a valid \WEB\ macro@>@;
  3304.  
  3305.     mp = mp0;
  3306.     }
  3307.  
  3308. FREE_MEM(mtext,"_dumpdef_:mtext",MTEXT_SIZE,ASCII); 
  3309. }
  3310.  
  3311. @
  3312. @<Dump a valid...@>=
  3313. {
  3314. p += 2;
  3315.  
  3316. /* Copy the name. */
  3317. np = name_dir + a;
  3318.  
  3319. for(mx=mtext,mx0=np->byte_start; mx0<(np+1)->byte_start; )
  3320.     *mx++ = *mx0++;
  3321.  
  3322. *mx++ = '\0';
  3323. to_outer(mtext);
  3324.  
  3325. /* Translate the definition. */
  3326. if(m->built_in)
  3327.     {
  3328.     cur_val = a;
  3329.     STRCPY(mp0,"<built-in>");
  3330.     mp = mp0 + STRLEN(mp0) + 1;
  3331.     }
  3332. else
  3333.     {
  3334.     q0 = m->tok_start + m->moffset;
  3335.     q1 = (m+1)->tok_start;
  3336.  
  3337.     str_to_mb(q0,q1,NO);
  3338.     mp++;
  3339.     to_outer((ASCII *)mp0);
  3340.     }
  3341.  
  3342. /* Print the definition. */
  3343. printf("%s", (char *)mtext);
  3344.  
  3345. if(m->nargs || m->var_args)
  3346.     {
  3347.     eight_bits n;
  3348.  
  3349.     printf("(");
  3350.     for(n=0; n<m->nargs; n++)
  3351.         printf("$%d%s",(int)n,
  3352.            CHOICE(n==(eight_bits)(m->nargs-1), "", ","));
  3353.     if(m->var_args) printf("%s...",
  3354.         CHOICE(m->nargs,",",""));
  3355.     printf(")");
  3356.     }
  3357.  
  3358. printf(" = %s\n", (char *)(mp=mp0));
  3359.  
  3360. if(xflag)
  3361.     {
  3362. /* Convert arguments to readable form. */
  3363.     mp0 = mp;
  3364.     str_to_mb(p,pargs[k+1],NO);
  3365.     mp++;
  3366.     to_outer((ASCII *)mp0);
  3367.  
  3368. /* Expand the macro. */
  3369.     mp1 = xmacro(m,&p,pargs[k+1],mp);
  3370.     *mp++ = '\0';
  3371.     mp2 = mp;
  3372.     str_to_mb(mp1,mp,NO);
  3373.     mp++;
  3374.     to_outer((ASCII *)mp2);
  3375.  
  3376.     printf("%s%s = %s\n", (char *)mtext, (char *)mp0, (char *)(mp=mp2));
  3377.  
  3378.     if(p != pargs[k+1])
  3379.         ERR_PRINT(M,"Extra text after macro call");
  3380.     }
  3381. }
  3382.  
  3383. @* INDEX.
  3384.