home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / pic / lex.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  35.7 KB  |  1,775 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.uucp)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 1, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file LICENSE.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "pic.h"
  22. #include "ptable.h"
  23. #include "object.h"
  24. #include "pic.tab.h"
  25. #include "key.h"
  26.  
  27. declare_ptable(char)
  28. implement_ptable(char)
  29.  
  30. PTABLE(char) macro_table;
  31.  
  32. class macro_input : public input {
  33.   char *s;
  34.   char *p;
  35. public:
  36.   macro_input(const char *);
  37.   ~macro_input();
  38.   int get();
  39.   int peek();
  40. };
  41.  
  42. class argument_macro_input : public input {
  43.   char *s;
  44.   char *p;
  45.   char *ap;
  46.   int argc;
  47.   char *argv[9];
  48. public:
  49.   argument_macro_input(const char *, int, char **);
  50.   ~argument_macro_input();
  51.   int get();
  52.   int peek();
  53. };
  54.  
  55. input::input() : next(0)
  56. {
  57. }
  58.  
  59. input::~input()
  60. {
  61. }
  62.  
  63. int input::get_location(const char **, int *)
  64. {
  65.   return 0;
  66. }
  67.  
  68. file_input::file_input(FILE *f, const char *fn)
  69. : lineno(0), ptr(""), filename(fn)
  70. {
  71.   fp = f;
  72. }
  73.  
  74. file_input::~file_input()
  75. {
  76.   fclose(fp);
  77. }
  78.  
  79. int file_input::read_line()
  80. {
  81.   for (;;) {
  82.     line.clear();
  83.     lineno++;
  84.     for (;;) {
  85.       int c = getc(fp);
  86.       if (c == EOF)
  87.     break;
  88.       else if (illegal_input_char(c))
  89.     lex_error("illegal input character code %1", c);
  90.       else {
  91.     line += char(c);
  92.     if (c == '\n') 
  93.       break;
  94.       }
  95.     }
  96.     if (line.length() == 0)
  97.       return 0;
  98.     if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
  99.       && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
  100.       && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
  101.           || compatible_flag))) {
  102.       line += '\0';
  103.       ptr = line.contents();
  104.       return 1;
  105.     }
  106.   }
  107. }
  108.  
  109. int file_input::get()
  110. {
  111.   if (*ptr != '\0' || read_line())
  112.     return (unsigned char)*ptr++;
  113.   else
  114.     return EOF;
  115. }
  116.  
  117. int file_input::peek()
  118. {
  119.   if (*ptr != '\0' || read_line())
  120.     return (unsigned char)*ptr;
  121.   else
  122.     return EOF;
  123. }
  124.  
  125. int file_input::get_location(const char **fnp, int *lnp)
  126. {
  127.   *fnp = filename;
  128.   *lnp = lineno;
  129.   return 1;
  130. }
  131.  
  132. macro_input::macro_input(const char *str)
  133. {
  134.   p = s = strsave(str);
  135. }
  136.  
  137. macro_input::~macro_input()
  138. {
  139.   delete s;
  140. }
  141.  
  142. int macro_input::get()
  143. {
  144.   if (p == 0 || *p == '\0')
  145.     return EOF;
  146.   else
  147.     return (unsigned char)*p++;
  148. }
  149.  
  150. int macro_input::peek()
  151. {
  152.   if (p == 0 || *p == '\0')
  153.     return EOF;
  154.   else
  155.     return (unsigned char)*p;
  156. }
  157.  
  158. #define ARG1 11
  159.  
  160. char *process_body(const char *body)
  161. {
  162.   char *s = strsave(body);
  163.   int j = 0;
  164.   for (int i = 0; s[i] != '\0'; i++)
  165.     if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
  166.       if (s[i+1] != '0')
  167.     s[j++] = ARG1 + s[++i] - '1';
  168.     }
  169.     else
  170.       s[j++] = s[i];
  171.   s[j] = '\0';
  172.   return s;
  173. }
  174.  
  175.  
  176. argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
  177. : argc(ac), ap(0)
  178. {
  179.   for (int i = 0; i < argc; i++)
  180.     argv[i] = av[i];
  181.   p = s = process_body(body);
  182. }
  183.  
  184.  
  185. argument_macro_input::~argument_macro_input()
  186. {
  187.   for (int i = 0; i < argc; i++)
  188.     delete argv[i];
  189.   delete s;
  190. }
  191.  
  192. int argument_macro_input::get()
  193. {
  194.   if (ap) {
  195.     if (*ap != '\0')
  196.       return (unsigned char)*ap++;
  197.     ap = 0;
  198.   }
  199.   if (p == 0)
  200.     return EOF;
  201.   while (*p >= ARG1 && *p <= ARG1 + 8) {
  202.     int i = *p++ - ARG1;
  203.     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
  204.       ap = argv[i];
  205.       return (unsigned char)*ap++;
  206.     }
  207.   }
  208.   if (*p == '\0')
  209.     return EOF;
  210.   return (unsigned char)*p++;
  211. }
  212.  
  213. int argument_macro_input::peek()
  214. {
  215.   if (ap) {
  216.     if (*ap != '\0')
  217.       return (unsigned char)*ap;
  218.     ap = 0;
  219.   }
  220.   if (p == 0)
  221.     return EOF;
  222.   while (*p >= ARG1 && *p <= ARG1 + 8) {
  223.     int i = *p++ - ARG1;
  224.     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
  225.       ap = argv[i];
  226.       return (unsigned char)*ap;
  227.     }
  228.   }
  229.   if (*p == '\0')
  230.     return EOF;
  231.   return (unsigned char)*p;
  232. }
  233.  
  234. class input_stack {
  235.   static input *current_input;
  236.   static int bol_flag;
  237. public:
  238.   static void push(input *);
  239.   static void clear();
  240.   static int get_char();
  241.   static int peek_char();
  242.   static int get_location(const char **fnp, int *lnp);
  243.   static void push_back(unsigned char c, int was_bol = 0);
  244.   static inline int bol() { return bol_flag; }
  245. };
  246.  
  247. input *input_stack::current_input = 0;
  248. int input_stack::bol_flag = 0;
  249.  
  250. void input_stack::clear()
  251. {
  252.   while (current_input != 0) {
  253.     input *tem = current_input;
  254.     current_input = current_input->next;
  255.     delete tem;
  256.   }
  257.   bol_flag = 1;
  258. }
  259.  
  260. void input_stack::push(input *in)
  261. {
  262.   in->next = current_input;
  263.   current_input = in;
  264. }
  265.  
  266. void lex_init(input *top)
  267. {
  268.   input_stack::clear();
  269.   input_stack::push(top);
  270. }
  271.  
  272. void lex_cleanup()
  273. {
  274.   while (input_stack::get_char() != EOF)
  275.     ;
  276. }
  277.  
  278. int input_stack::get_char()
  279. {
  280.   while (current_input != 0) {
  281.     int c = current_input->get();
  282.     if (c != EOF) {
  283.       bol_flag = c == '\n';
  284.       return c;
  285.     }
  286.     // don't pop the top-level input off the stack
  287.     if (current_input->next == 0)
  288.       return EOF;
  289.     input *tem = current_input;
  290.     current_input = current_input->next;
  291.     delete tem;
  292.   }
  293.   return EOF;
  294. }
  295.  
  296. int input_stack::peek_char()
  297. {
  298.   while (current_input != 0) {
  299.     int c = current_input->peek();
  300.     if (c != EOF)
  301.       return c;
  302.     if (current_input->next == 0)
  303.       return EOF;
  304.     input *tem = current_input;
  305.     current_input = current_input->next;
  306.     delete tem;
  307.   }
  308.   return EOF;
  309. }
  310.  
  311. class char_input : public input {
  312.   int c;
  313. public:
  314.   char_input(int);
  315.   int get();
  316.   int peek();
  317. };
  318.  
  319. char_input::char_input(int n) : c((unsigned char)n)
  320. {
  321. }
  322.  
  323. int char_input::get()
  324. {
  325.   int n = c;
  326.   c = EOF;
  327.   return n;
  328. }
  329.  
  330. int char_input::peek()
  331. {
  332.   return c;
  333. }
  334.  
  335. void input_stack::push_back(unsigned char c, int was_bol)
  336. {
  337.   push(new char_input(c));
  338.   bol_flag = was_bol;
  339. }
  340.  
  341. int input_stack::get_location(const char **fnp, int *lnp)
  342. {
  343.   for (input *p = current_input; p; p = p->next)
  344.     if (p->get_location(fnp, lnp))
  345.       return 1;
  346.   return 0;
  347. }
  348.  
  349. string context_buffer;
  350.  
  351. string token_buffer;
  352. double token_double;
  353. int token_int;
  354.  
  355. void interpolate_macro_with_args(const char *body)
  356. {
  357.   char *argv[9];
  358.   int argc = 0;
  359.   for (int i = 0; i < 9; i++)
  360.     argv[i] = 0;
  361.   int level = 0;
  362.   int c;
  363.   do {
  364.     token_buffer.clear();
  365.     for (;;) {
  366.       c = input_stack::get_char();
  367.       if (c == EOF) {
  368.     lex_error("end of input while scanning macro arguments");
  369.     break;
  370.       }
  371.       if (level == 0 && (c == ',' || c == ')')) {
  372.     if (token_buffer.length() > 0) {
  373.       token_buffer +=  '\0';
  374.       argv[argc] = strsave(token_buffer.contents());
  375.     }
  376.     // for `foo()', argc = 0
  377.     if (argc > 0 || c != ')' || i > 0)
  378.       argc++;
  379.     break;
  380.       }
  381.       token_buffer += char(c);
  382.       if (c == '(')
  383.     level++;
  384.       else if (c == ')')
  385.     level--;
  386.     }
  387.   } while (c != ')' && c != EOF);
  388.   input_stack::push(new argument_macro_input(body, argc, argv));
  389. }
  390.  
  391. int get_token_after_dot(int c)
  392. {
  393.   // get_token deals with the case where c is a digit
  394.   switch (c) {
  395.   case 'h':
  396.     input_stack::get_char();
  397.     c = input_stack::peek_char();
  398.     if (c == 't') {
  399.       input_stack::get_char();
  400.       context_buffer = ".ht";
  401.       return DOT_HT;
  402.     }
  403.     else if (c == 'e') {
  404.       input_stack::get_char();
  405.       c = input_stack::peek_char();
  406.       if (c == 'i') {
  407.     input_stack::get_char();
  408.     c = input_stack::peek_char();
  409.     if (c == 'g') {
  410.       input_stack::get_char();
  411.       c = input_stack::peek_char();
  412.       if (c == 'h') {
  413.         input_stack::get_char();
  414.         c = input_stack::peek_char();
  415.         if (c == 't') {
  416.           input_stack::get_char();
  417.           context_buffer = ".height";
  418.           return DOT_HT;
  419.         }
  420.         input_stack::push_back('h');
  421.       }
  422.       input_stack::push_back('g');
  423.     }
  424.     input_stack::push_back('i');
  425.       }
  426.       input_stack::push_back('e');
  427.     }
  428.     input_stack::push_back('h');
  429.     return '.';
  430.   case 'x':
  431.     input_stack::get_char();
  432.     context_buffer = ".x";
  433.     return DOT_X;
  434.   case 'y':
  435.     input_stack::get_char();
  436.     context_buffer = ".y";
  437.     return DOT_Y;
  438.   case 'c':
  439.     input_stack::get_char();
  440.     c = input_stack::peek_char();
  441.     if (c == 'e') {
  442.       input_stack::get_char();
  443.       c = input_stack::peek_char();
  444.       if (c == 'n') {
  445.     input_stack::get_char();
  446.     c = input_stack::peek_char();
  447.     if (c == 't') {
  448.       input_stack::get_char();
  449.       c = input_stack::peek_char();
  450.       if (c == 'e') {
  451.         input_stack::get_char();
  452.         c = input_stack::peek_char();
  453.         if (c == 'r') {
  454.           input_stack::get_char();
  455.           context_buffer = ".center";
  456.           return DOT_C;
  457.         }
  458.         input_stack::push_back('e');
  459.       }
  460.       input_stack::push_back('t');
  461.     }
  462.     input_stack::push_back('n');
  463.       }
  464.       input_stack::push_back('e');
  465.     }
  466.     context_buffer = ".c";
  467.     return DOT_C;
  468.   case 'n':
  469.     input_stack::get_char();
  470.     c = input_stack::peek_char();
  471.     if (c == 'e') {
  472.       input_stack::get_char();
  473.       context_buffer = ".ne";
  474.       return DOT_NE;
  475.     }
  476.     else if (c == 'w') {
  477.       input_stack::get_char();
  478.       context_buffer = ".nw";
  479.       return DOT_NW;
  480.     }
  481.     else {
  482.       context_buffer = ".n";
  483.       return DOT_N;
  484.     }
  485.     break;
  486.   case 'e':
  487.     input_stack::get_char();
  488.     c = input_stack::peek_char();
  489.     if (c == 'n') {
  490.       input_stack::get_char();
  491.       c = input_stack::peek_char();
  492.       if (c == 'd') {
  493.     input_stack::get_char();
  494.     context_buffer = ".end";
  495.     return DOT_END;
  496.       }
  497.       input_stack::push_back('n');
  498.       context_buffer = ".e";
  499.       return DOT_E;
  500.     }
  501.     context_buffer = ".e";
  502.     return DOT_E;
  503.   case 'w':
  504.     input_stack::get_char();
  505.     c = input_stack::peek_char();
  506.     if (c == 'i') {
  507.       input_stack::get_char();
  508.       c = input_stack::peek_char();
  509.       if (c == 'd') {
  510.     input_stack::get_char();
  511.     c = input_stack::peek_char();
  512.     if (c == 't') {
  513.       input_stack::get_char();
  514.       c = input_stack::peek_char();
  515.       if (c == 'h') {
  516.         input_stack::get_char();
  517.         context_buffer = ".width";
  518.         return DOT_WID;
  519.       }
  520.       input_stack::push_back('t');
  521.     }
  522.     context_buffer = ".wid";
  523.     return DOT_WID;
  524.       }
  525.       input_stack::push_back('i');
  526.     }
  527.     context_buffer = ".w";
  528.     return DOT_W;
  529.   case 's':
  530.     input_stack::get_char();
  531.     c = input_stack::peek_char();
  532.     if (c == 'e') {
  533.       input_stack::get_char();
  534.       context_buffer = ".se";
  535.       return DOT_SE;
  536.     }
  537.     else if (c == 'w') {
  538.       input_stack::get_char();
  539.       context_buffer = ".sw";
  540.       return DOT_SW;
  541.     }
  542.     else {
  543.       if (c == 't') {
  544.     input_stack::get_char();
  545.     c = input_stack::peek_char();
  546.     if (c == 'a') {
  547.       input_stack::get_char();
  548.       c = input_stack::peek_char();
  549.       if (c == 'r') {
  550.         input_stack::get_char();
  551.         c = input_stack::peek_char();
  552.         if (c == 't') {
  553.           input_stack::get_char();
  554.           context_buffer = ".start";
  555.           return DOT_START;
  556.         }
  557.         input_stack::push_back('r');
  558.       }
  559.       input_stack::push_back('a');
  560.     }
  561.     input_stack::push_back('t');
  562.       }
  563.       context_buffer = ".s";
  564.       return DOT_S;
  565.     }
  566.     break;
  567.   case 't':
  568.     input_stack::get_char();
  569.     c = input_stack::peek_char();
  570.     if (c == 'o') {
  571.       input_stack::get_char();
  572.       c = input_stack::peek_char();
  573.       if (c == 'p') {
  574.     input_stack::get_char();
  575.     context_buffer = ".top";
  576.     return DOT_N;
  577.       }
  578.       input_stack::push_back('o');
  579.     }
  580.     context_buffer = ".t";
  581.     return DOT_N;
  582.   case 'l':
  583.     input_stack::get_char();
  584.     c = input_stack::peek_char();
  585.     if (c == 'e') {
  586.       input_stack::get_char();
  587.       c = input_stack::peek_char();
  588.       if (c == 'f') {
  589.     input_stack::get_char();
  590.     c = input_stack::peek_char();
  591.     if (c == 't') {
  592.       input_stack::get_char();
  593.       context_buffer = ".left";
  594.     }
  595.     input_stack::push_back('f');
  596.       }
  597.       input_stack::push_back('e');
  598.     }
  599.     context_buffer = ".l";
  600.     return DOT_W;
  601.   case 'r':
  602.     input_stack::get_char();
  603.     c = input_stack::peek_char();
  604.     if (c == 'a') {
  605.       input_stack::get_char();
  606.       c = input_stack::peek_char();
  607.       if (c == 'd') {
  608.     input_stack::get_char();
  609.     context_buffer = ".rad";
  610.     return DOT_RAD;
  611.       }
  612.       input_stack::push_back('a');
  613.     }
  614.     else if (c == 'i') {
  615.       input_stack::get_char();
  616.       c = input_stack::peek_char();
  617.       if (c == 'g') {
  618.     input_stack::get_char();
  619.     c = input_stack::peek_char();
  620.     if (c == 'h') {
  621.       input_stack::get_char();
  622.       c = input_stack::peek_char();
  623.       if (c == 't') {
  624.         input_stack::get_char();
  625.         context_buffer = ".right";
  626.       }
  627.       input_stack::push_back('h');
  628.     }
  629.     input_stack::push_back('g');
  630.       }
  631.       input_stack::push_back('i');
  632.     }
  633.     context_buffer = ".r";
  634.     return DOT_E;
  635.   case 'b':
  636.     input_stack::get_char();
  637.     c = input_stack::peek_char();
  638.     if (c == 'o') {
  639.       input_stack::get_char();
  640.       c = input_stack::peek_char();
  641.       if (c == 't') {
  642.     input_stack::get_char();
  643.     c = input_stack::peek_char();
  644.     if (c == 't') {
  645.       input_stack::get_char();
  646.       c = input_stack::peek_char();
  647.       if (c == 'o') {
  648.         input_stack::get_char();
  649.         c = input_stack::peek_char();
  650.         if (c == 'm') {
  651.           input_stack::get_char();
  652.           context_buffer = ".bottom";
  653.           return DOT_S;
  654.         }
  655.         input_stack::push_back('o');
  656.       }
  657.       input_stack::push_back('t');
  658.     }
  659.     context_buffer = ".bot";
  660.     return DOT_S;
  661.       }
  662.       input_stack::push_back('o');
  663.     }
  664.     context_buffer = ".b";
  665.     return DOT_S;
  666.   default:
  667.     context_buffer = '.';
  668.     return '.';
  669.   }
  670. }
  671.  
  672. int get_token(int lookup_flag)
  673. {
  674.   context_buffer.clear();
  675.   for (;;) {
  676.     int n = 0;
  677.     int bol = input_stack::bol();
  678.     int c = input_stack::get_char();
  679.     if (bol && c == command_char) {
  680.       token_buffer.clear();
  681.       token_buffer += c;
  682.       // the newline is not part of the token
  683.       for (;;) {
  684.     c = input_stack::peek_char();
  685.     if (c == EOF || c == '\n')
  686.       break;
  687.     input_stack::get_char();
  688.     token_buffer += char(c);
  689.       }
  690.       context_buffer = token_buffer;
  691.       return COMMAND;
  692.     }
  693.     switch (c) {
  694.     case EOF:
  695.       return EOF;
  696.     case ' ':
  697.     case '\t':
  698.       break;
  699.     case '\\':
  700.       {
  701.     int d = input_stack::peek_char();
  702.     if (d != '\n') {
  703.       context_buffer = '\\';
  704.       return '\\';
  705.     }
  706.     input_stack::get_char();
  707.     break;
  708.       }
  709.     case '#':
  710.       do {
  711.     c = input_stack::get_char();
  712.       } while (c != '\n' && c != EOF);
  713.       if (c == '\n')
  714.     context_buffer = '\n';
  715.       return c;
  716.     case '"':
  717.       context_buffer = '"';
  718.       token_buffer.clear();
  719.       for (;;) {
  720.     c = input_stack::get_char();
  721.     if (c == '\\') {
  722.       context_buffer += '\\';
  723.       c = input_stack::peek_char();
  724.       if (c == '"') {
  725.         input_stack::get_char();
  726.         token_buffer += '"';
  727.         context_buffer += '"';
  728.       }
  729.       else
  730.         token_buffer += '\\';
  731.     }
  732.     else if (c == '\n') {
  733.       error("newline in string");
  734.       break;
  735.     }
  736.     else if (c == EOF) {
  737.       error("missing `\"'");
  738.       break;
  739.     }
  740.     else if (c == '"') {
  741.       context_buffer += '"';
  742.       break;
  743.     }
  744.     else {
  745.       context_buffer += char(c);
  746.       token_buffer += char(c);
  747.     }
  748.       }
  749.       return TEXT;
  750.     case '0':
  751.     case '1':
  752.     case '2':
  753.     case '3':
  754.     case '4':
  755.     case '5':
  756.     case '6':
  757.     case '7':
  758.     case '8':
  759.     case '9':
  760.       {   
  761.     int overflow = 0;
  762.     n = 0;
  763.     for (;;) {
  764.       if (n > (INT_MAX - 9)/10) {
  765.         overflow = 1;
  766.         break;
  767.       }
  768.       n *= 10;
  769.       n += c - '0';
  770.       context_buffer += char(c);
  771.       c = input_stack::peek_char();
  772.       if (c == EOF || !csdigit(c))
  773.         break;
  774.       c = input_stack::get_char();
  775.     }
  776.     token_double = n;
  777.     if (overflow) {
  778.       for (;;) {
  779.         token_double *= 10.0;
  780.         token_double += c - '0';
  781.         context_buffer += char(c);
  782.         c = input_stack::peek_char();
  783.         if (c == EOF || !csdigit(c))
  784.           break;
  785.         c = input_stack::get_char();
  786.       }
  787.       // if somebody asks for 1000000000000th, we will silently
  788.       // give them INT_MAXth
  789.       double temp = token_double; // work around gas 1.34/sparc bug
  790.       if (token_double > INT_MAX)
  791.         n = INT_MAX;
  792.       else
  793.         n = int(temp);
  794.     }
  795.       }
  796.       switch (c) {
  797.       case 'i':
  798.       case 'I':
  799.     context_buffer += char(c);
  800.     input_stack::get_char();
  801.     return NUMBER;
  802.       case '.':
  803.     {
  804.       context_buffer += '.';
  805.       input_stack::get_char();
  806.     got_dot:
  807.       double factor = 1.0;
  808.       for (;;) {
  809.         c = input_stack::peek_char();
  810.         if (!c == EOF || !csdigit(c))
  811.           break;
  812.         input_stack::get_char();
  813.         context_buffer += char(c);
  814.         factor /= 10.0;
  815.         if (c != '0')
  816.           token_double += factor*(c - '0');
  817.       }
  818.       if (c != 'e' && c != 'E') {
  819.         if (c == 'i' || c == 'I') {
  820.           context_buffer += char(c);
  821.           input_stack::get_char();
  822.         }
  823.         return NUMBER;
  824.       }
  825.     }
  826.     // fall through
  827.       case 'e':
  828.       case 'E':
  829.     {
  830.       int echar = c;
  831.       input_stack::get_char();
  832.       c = input_stack::peek_char();
  833.       int sign = '+';
  834.       if (c == '+' || c == '-') {
  835.         sign = c;
  836.         input_stack::get_char();
  837.         c = input_stack::peek_char();
  838.         if (c == EOF || !csdigit(c)) {
  839.           input_stack::push_back(sign);
  840.           input_stack::push_back(echar);
  841.           return NUMBER;
  842.         }
  843.         context_buffer += char(echar);
  844.         context_buffer += char(sign);
  845.       }
  846.       else {
  847.         if (c == EOF || !csdigit(c)) {
  848.           input_stack::push_back(echar);
  849.           return NUMBER;
  850.         }
  851.         context_buffer += char(echar);
  852.       }
  853.       input_stack::get_char();
  854.       context_buffer += char(c);
  855.       n = c - '0';
  856.       for (;;) {
  857.         c = input_stack::peek_char();
  858.         if (c == EOF || !csdigit(c))
  859.           break;
  860.         input_stack::get_char();
  861.         context_buffer += char(c);
  862.         n = n*10 + (c - '0');
  863.       }
  864.       if (sign == '-')
  865.         n = -n;
  866.       if (c == 'i' || c == 'I') {
  867.         context_buffer += char(c);
  868.         input_stack::get_char();
  869.       }
  870.       token_double *= pow(10.0, n);
  871.       return NUMBER;
  872.     }
  873.       case 'n':
  874.     input_stack::get_char();
  875.     c = input_stack::peek_char();
  876.     if (c == 'd') {
  877.       input_stack::get_char();
  878.       token_int = n;
  879.       context_buffer += "nd";
  880.       return ORDINAL;
  881.     }
  882.     input_stack::push_back('n');
  883.     return NUMBER;
  884.       case 'r':
  885.     input_stack::get_char();
  886.     c = input_stack::peek_char();
  887.     if (c == 'd') {
  888.       input_stack::get_char();
  889.       token_int = n;
  890.       context_buffer += "rd";
  891.       return ORDINAL;
  892.     }
  893.     input_stack::push_back('r');
  894.     return NUMBER;
  895.       case 't':
  896.     input_stack::get_char();
  897.     c = input_stack::peek_char();
  898.     if (c == 'h') {
  899.       input_stack::get_char();
  900.       token_int = n;
  901.       context_buffer += "th";
  902.       return ORDINAL;
  903.     }
  904.     input_stack::push_back('t');
  905.     return NUMBER;
  906.       case 's':
  907.     input_stack::get_char();
  908.     c = input_stack::peek_char();
  909.     if (c == 't') {
  910.       input_stack::get_char();
  911.       token_int = n;
  912.       context_buffer += "st";
  913.       return ORDINAL;
  914.     }
  915.     input_stack::push_back('s');
  916.     return NUMBER;
  917.       default:
  918.     return NUMBER;
  919.       }
  920.       break;
  921.     case '.':
  922.       {
  923.     c = input_stack::peek_char();
  924.     if (c != EOF && csdigit(c)) {
  925.       n = 0;
  926.       token_double = 0.0;
  927.       context_buffer = '.';
  928.       goto got_dot;
  929.     }
  930.     return get_token_after_dot(c);
  931.       }
  932.     case '<':
  933.       c = input_stack::peek_char();
  934.       if (c == '-') {
  935.     input_stack::get_char();
  936.     c = input_stack::peek_char();
  937.     if (c == '>') {
  938.       input_stack::get_char();
  939.       context_buffer = "<->";
  940.       return DOUBLE_ARROW_HEAD;
  941.     }
  942.     context_buffer = "<-";
  943.     return LEFT_ARROW_HEAD;
  944.       }
  945.       else if (c == '=') {
  946.     input_stack::get_char();
  947.     context_buffer = "<=";
  948.     return LESSEQUAL;
  949.       }
  950.       context_buffer = "<";
  951.       return '<';
  952.     case '-':
  953.       c = input_stack::peek_char();
  954.       if (c == '>') {
  955.     input_stack::get_char();
  956.     context_buffer = "->";
  957.     return RIGHT_ARROW_HEAD;
  958.       }
  959.       context_buffer = "-";
  960.       return '-';
  961.     case '!':
  962.       c = input_stack::peek_char();
  963.       if (c == '=') {
  964.     input_stack::get_char();
  965.     context_buffer = "!=";
  966.     return NOTEQUAL;
  967.       }
  968.       context_buffer = "!";
  969.       return '!';
  970.     case '>':
  971.       c = input_stack::peek_char();
  972.       if (c == '=') {
  973.     input_stack::get_char();
  974.     context_buffer = ">=";
  975.     return GREATEREQUAL;
  976.       }
  977.       context_buffer = ">";
  978.       return '>';
  979.     case '=':
  980.       c = input_stack::peek_char();
  981.       if (c == '=') {
  982.     input_stack::get_char();
  983.     context_buffer = "==";
  984.     return EQUALEQUAL;
  985.       }
  986.       context_buffer = "=";
  987.       return '=';
  988.     case '&':
  989.       c = input_stack::peek_char();
  990.       if (c == '&') {
  991.     input_stack::get_char();
  992.     context_buffer = "&&";
  993.     return ANDAND;
  994.       }
  995.       context_buffer = "&";
  996.       return '&';
  997.     case '|':
  998.       c = input_stack::peek_char();
  999.       if (c == '|') {
  1000.     input_stack::get_char();
  1001.     context_buffer = "||";
  1002.     return OROR;
  1003.       }
  1004.       context_buffer = "|";
  1005.       return '|';
  1006.     default:
  1007.       if (c != EOF && csalpha(c)) {
  1008.     token_buffer.clear();
  1009.     token_buffer = c;
  1010.     for (;;) {
  1011.       c = input_stack::peek_char();
  1012.       if (c == EOF || (!csalnum(c) && c != '_'))
  1013.         break;
  1014.       input_stack::get_char();
  1015.       token_buffer += char(c);
  1016.     }
  1017.     const keyword *ky = lookup_keyword(token_buffer.contents(),
  1018.                      token_buffer.length());
  1019.     if (ky != 0) {
  1020.       context_buffer = token_buffer;
  1021.       return ky->token;
  1022.     }
  1023.     char *def = 0;
  1024.     if (lookup_flag) {
  1025.       token_buffer += '\0';
  1026.       def = macro_table.lookup(token_buffer.contents());
  1027.       token_buffer.set_length(token_buffer.length() - 1);
  1028.       if (def) {
  1029.         if (c == '(') {
  1030.           input_stack::get_char();
  1031.           interpolate_macro_with_args(def);
  1032.         }
  1033.         else
  1034.           input_stack::push(new macro_input(def));
  1035.       }
  1036.     }
  1037.     if (!def) {
  1038.       context_buffer = token_buffer;
  1039.       if (csupper(token_buffer[0]))
  1040.         return LABEL;
  1041.       else
  1042.         return VARIABLE;
  1043.     }
  1044.       }
  1045.       else {
  1046.     context_buffer = char(c);
  1047.     return (unsigned char)c;
  1048.       }
  1049.       break;
  1050.     }
  1051.   }
  1052. }
  1053.  
  1054. int get_delimited()
  1055. {
  1056.   token_buffer.clear();
  1057.   int c = input_stack::get_char();
  1058.   while (c == ' ' || c == '\n')
  1059.     c = input_stack::get_char();
  1060.   if (c == EOF) {
  1061.     lex_error("missing delimiter");
  1062.     return 0;
  1063.   }
  1064.   context_buffer = char(c);
  1065.   int had_newline = 0;
  1066.   int start = c;
  1067.   int level = 0;
  1068.   enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
  1069.   for (;;) {
  1070.     c = input_stack::get_char();
  1071.     if (c == EOF) {
  1072.       lex_error("missing closing delimiter");
  1073.       return 0;
  1074.     }
  1075.     if (c == '\n')
  1076.       had_newline = 1;
  1077.     else if (!had_newline)
  1078.       context_buffer += char(c);
  1079.     switch (state) {
  1080.     case NORMAL:
  1081.       if (start == '{') {
  1082.     if (c == '{') {
  1083.       level++;
  1084.       break;
  1085.     }
  1086.     if (c == '}') {
  1087.       if (--level < 0)
  1088.         state = DELIM_END;
  1089.       break;
  1090.     }
  1091.       }
  1092.       else {
  1093.     if (c == start) {
  1094.       state = DELIM_END;
  1095.       break;
  1096.     }
  1097.       }
  1098.       if (c == '"')
  1099.     state = IN_STRING;
  1100.       break;
  1101.     case IN_STRING_QUOTED:
  1102.       if (c == '\n')
  1103.     state = NORMAL;
  1104.       else
  1105.     state = IN_STRING;
  1106.       break;
  1107.     case IN_STRING:
  1108.       if (c == '"' || c == '\n')
  1109.     state = NORMAL;
  1110.       else if (c == '\\')
  1111.     state = IN_STRING_QUOTED;
  1112.       break;
  1113.     case DELIM_END:
  1114.       // This case it just to shut cfront 2.0 up.
  1115.     default:
  1116.       assert(0);
  1117.     }
  1118.     if (state == DELIM_END)
  1119.       break;
  1120.     token_buffer += c;
  1121.   }
  1122.   return 1;
  1123. }
  1124.  
  1125. void do_define()
  1126. {
  1127.   int t = get_token(0);        // do not expand what we are defining
  1128.   if (t != VARIABLE && t != LABEL) {
  1129.     lex_error("can only define variable or placename");
  1130.     return;
  1131.   }
  1132.   token_buffer += '\0';
  1133.   string nm = token_buffer;
  1134.   if (!get_delimited())
  1135.     return;
  1136.   token_buffer += '\0';
  1137.   macro_table.define(nm.contents(), strsave(token_buffer.contents()));
  1138. }
  1139.  
  1140. void do_undef()
  1141. {
  1142.   int t = get_token(0);        // do not expand what we are undefining
  1143.   if (t != VARIABLE && t != LABEL) {
  1144.     lex_error("can only define variable or placename");
  1145.     return;
  1146.   }
  1147.   token_buffer += '\0';
  1148.   macro_table.define(token_buffer.contents(), 0);
  1149. }
  1150.  
  1151.  
  1152. class for_input : public input {
  1153.   char *var;
  1154.   char *body;
  1155.   double to;
  1156.   int by_is_multiplicative;
  1157.   double by;
  1158.   const char *p;
  1159.   int done_newline;
  1160. public:
  1161.   for_input(char *, double, int, double, char *);
  1162.   ~for_input();
  1163.   int get();
  1164.   int peek();
  1165. };
  1166.  
  1167. for_input::for_input(char *vr, double t, int bim, double b, char *bd)
  1168. : var(vr), to(t), by_is_multiplicative(bim), by(b), body(bd), p(body),
  1169.   done_newline(0)
  1170. {
  1171. }
  1172.  
  1173. for_input::~for_input()
  1174. {
  1175.   delete var;
  1176.   delete body;
  1177. }
  1178.  
  1179. int for_input::get()
  1180. {
  1181.   if (p == 0)
  1182.     return EOF;
  1183.   for (;;) {
  1184.     if (*p != '\0')
  1185.       return (unsigned char)*p++;
  1186.     if (!done_newline) {
  1187.       done_newline = 1;
  1188.       return '\n';
  1189.     }
  1190.     double val;
  1191.     if (!lookup_variable(var, &val)) {
  1192.       lex_error("body of `for' terminated enclosing block");
  1193.       return EOF;
  1194.     }
  1195.     if (by_is_multiplicative)
  1196.       val *= by;
  1197.     else
  1198.       val += by;
  1199.     define_variable(var, val);
  1200.     if (val > to) {
  1201.       p = 0;
  1202.       return EOF;
  1203.     }
  1204.     p = body;
  1205.     done_newline = 0;
  1206.   }
  1207. }
  1208.  
  1209. int for_input::peek()
  1210. {
  1211.   if (p == 0)
  1212.     return EOF;
  1213.   if (*p != '\0')
  1214.     return (unsigned char)*p;
  1215.   if (!done_newline)
  1216.     return '\n';
  1217.   double val;
  1218.   if (!lookup_variable(var, &val))
  1219.     return EOF;
  1220.   if (by_is_multiplicative) {
  1221.     if (val * by > to)
  1222.       return EOF;
  1223.   }
  1224.   else {
  1225.     if (val + by > to)
  1226.       return EOF;
  1227.   }
  1228.   if (*body == '\0')
  1229.     return EOF;
  1230.   return (unsigned char)*body;
  1231. }
  1232.  
  1233. void do_for(char *var, double from, double to, int by_is_multiplicative,
  1234.         double by, char *body)
  1235. {
  1236.   define_variable(var, from);
  1237.   if (from <= to)
  1238.     input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
  1239. }
  1240.  
  1241.  
  1242. void do_copy(const char *filename)
  1243. {
  1244.   FILE *fp = fopen(filename, "r");
  1245.   if (fp == 0) {
  1246.     lex_error("can't open `%1': %2", filename, strerror(errno));
  1247.     return;
  1248.   }
  1249.   input_stack::push(new file_input(fp, filename));
  1250. }
  1251.  
  1252. class copy_thru_input : public input {
  1253.   int done;
  1254.   char *body;
  1255.   char *until;
  1256.   const char *p;
  1257.   const char *ap;
  1258.   int argv[9];
  1259.   int argc;
  1260.   string line;
  1261.   int get_line();
  1262.   virtual int inget() = 0;
  1263. public:
  1264.   copy_thru_input(const char *b, const char *u);
  1265.   ~copy_thru_input();
  1266.   int get();
  1267.   int peek();
  1268. };
  1269.  
  1270. class copy_file_thru_input : public copy_thru_input {
  1271.   input *in;
  1272. public:
  1273.   copy_file_thru_input(input *, const char *b, const char *u);
  1274.   ~copy_file_thru_input();
  1275.   int inget();
  1276. };
  1277.  
  1278. copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
  1279.                        const char *u)
  1280. : in(i), copy_thru_input(b, u)
  1281. {
  1282. }
  1283.  
  1284. copy_file_thru_input::~copy_file_thru_input()
  1285. {
  1286.   delete in;
  1287. }
  1288.  
  1289. int copy_file_thru_input::inget()
  1290. {
  1291.   if (!in)
  1292.     return EOF;
  1293.   else
  1294.     return in->get();
  1295. }
  1296.  
  1297. class copy_rest_thru_input : public copy_thru_input {
  1298. public:
  1299.   copy_rest_thru_input(const char *, const char *u);
  1300.   int inget();
  1301. };
  1302.  
  1303. copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
  1304. : copy_thru_input(b, u)
  1305. {
  1306. }
  1307.  
  1308. int copy_rest_thru_input::inget()
  1309. {
  1310.   while (next != 0) {
  1311.     int c = next->get();
  1312.     if (c != EOF)
  1313.       return c;
  1314.     if (next->next == 0)
  1315.       return EOF;
  1316.     input *tem = next;
  1317.     next = next->next;
  1318.     delete tem;
  1319.   }
  1320.   return EOF;
  1321.  
  1322. }
  1323.  
  1324. copy_thru_input::copy_thru_input(const char *b, const char *u)
  1325. : done(0)
  1326. {
  1327.   ap = 0;
  1328.   body = process_body(b);
  1329.   p = 0;
  1330.   until = strsave(u);
  1331. }
  1332.  
  1333.  
  1334. copy_thru_input::~copy_thru_input()
  1335. {
  1336.   delete body;
  1337.   delete until;
  1338. }
  1339.  
  1340. int copy_thru_input::get()
  1341. {
  1342.   if (ap) {
  1343.     if (*ap != '\0')
  1344.       return (unsigned char)*ap++;
  1345.     ap = 0;
  1346.   }
  1347.   for (;;) {
  1348.     if (p == 0) {
  1349.       if (!get_line())
  1350.     break;
  1351.       p = body;
  1352.     }
  1353.     if (*p == '\0') {
  1354.       p = 0;
  1355.       return '\n';
  1356.     }
  1357.     while (*p >= ARG1 && *p <= ARG1 + 8) {
  1358.       int i = *p++ - ARG1;
  1359.       if (i < argc && line[argv[i]] != '\0') {
  1360.     ap = line.contents() + argv[i];
  1361.     return (unsigned char)*ap++;
  1362.       }
  1363.     }
  1364.     if (*p != '\0')
  1365.       return (unsigned char)*p++;
  1366.   }
  1367.   return EOF;
  1368. }
  1369.  
  1370. int copy_thru_input::peek()
  1371. {
  1372.   if (ap) {
  1373.     if (*ap != '\0')
  1374.       return (unsigned char)*ap;
  1375.     ap = 0;
  1376.   }
  1377.   for (;;) {
  1378.     if (p == 0) {
  1379.       if (!get_line())
  1380.     break;
  1381.       p = body;
  1382.     }
  1383.     if (*p == '\0')
  1384.       return '\n';
  1385.     while (*p >= ARG1 && *p <= ARG1 + 8) {
  1386.       int i = *p++ - ARG1;
  1387.       if (i < argc && line[argv[i]] != '\0') {
  1388.     ap = line.contents() + argv[i];
  1389.     return (unsigned char)*ap;
  1390.       }
  1391.     }
  1392.     if (*p != '\0')
  1393.       return (unsigned char)*p;
  1394.   }
  1395.   return EOF;
  1396. }
  1397.  
  1398. int copy_thru_input::get_line()
  1399. {
  1400.   if (done)
  1401.     return 0;
  1402.   line.clear();
  1403.   argc = 0;
  1404.   int c = inget();
  1405.   for (;;) {
  1406.     while (c == ' ')
  1407.       c = inget();
  1408.     if (c == EOF || c == '\n')
  1409.       break;
  1410.     if (argc == 9) {
  1411.       do {
  1412.     c = inget();
  1413.       } while (c != '\n' && c != EOF);
  1414.       break;
  1415.     }
  1416.     argv[argc++] = line.length();
  1417.     do {
  1418.       line += char(c);
  1419.       c = inget();
  1420.     } while (c != ' ' && c != '\n');
  1421.     line += '\0';
  1422.   }
  1423.   if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
  1424.     done = 1;
  1425.     return 0;
  1426.   }
  1427.   return argc > 0 || c == '\n';
  1428. }
  1429.  
  1430. class simple_file_input : public input {
  1431.   const char *filename;
  1432.   int lineno;
  1433.   FILE *fp;
  1434. public:
  1435.   simple_file_input(FILE *, const char *);
  1436.   ~simple_file_input();
  1437.   int get();
  1438.   int peek();
  1439.   int get_location(const char **, int *);
  1440. };
  1441.  
  1442. simple_file_input::simple_file_input(FILE *p, const char *s)
  1443. : filename(s), fp(p), lineno(1)
  1444. {
  1445. }
  1446.  
  1447. simple_file_input::~simple_file_input()
  1448. {
  1449.   // don't delete the filename
  1450.   fclose(fp);
  1451. }
  1452.  
  1453. int simple_file_input::get()
  1454. {
  1455.   int c = getc(fp);
  1456.   while (illegal_input_char(c)) {
  1457.     error("illegal input character code %1", c);
  1458.     c = getc(fp);
  1459.   }
  1460.   if (c == '\n')
  1461.     lineno++;
  1462.   return c;
  1463. }
  1464.  
  1465. int simple_file_input::peek()
  1466. {
  1467.   int c = getc(fp);
  1468.   while (illegal_input_char(c)) {
  1469.     error("illegal input character code %1", c);
  1470.     c = getc(fp);
  1471.   }
  1472.   if (c != EOF)
  1473.     ungetc(c, fp);
  1474.   return c;
  1475. }
  1476.  
  1477. int simple_file_input::get_location(const char **fnp, int *lnp)
  1478. {
  1479.   *fnp = filename;
  1480.   *lnp = lineno;
  1481.   return 1;
  1482. }
  1483.  
  1484.  
  1485. void copy_file_thru(const char *filename, const char *body, const char *until)
  1486. {
  1487.   FILE *fp = fopen(filename, "r");
  1488.   if (fp == 0) {
  1489.     lex_error("can't open `%1': %2", filename, strerror(errno));
  1490.     return;
  1491.   }
  1492.   input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
  1493.                        body, until);
  1494.   input_stack::push(in);
  1495. }
  1496.  
  1497. void copy_rest_thru(const char *body, const char *until)
  1498. {
  1499.   input_stack::push(new copy_rest_thru_input(body, until));
  1500. }
  1501.  
  1502. void push_body(const char *s)
  1503. {
  1504.   input_stack::push(new char_input('\n'));
  1505.   input_stack::push(new macro_input(s));
  1506. }
  1507.  
  1508. int delim_flag = 0;
  1509.  
  1510. char *get_thru_arg()
  1511. {
  1512.   int c = input_stack::peek_char();
  1513.   while (c == ' ') {
  1514.     input_stack::get_char();
  1515.     c = input_stack::peek_char();
  1516.   }
  1517.   if (c != EOF && csalpha(c)) {
  1518.     // looks like a macro
  1519.     input_stack::get_char();
  1520.     token_buffer = c;
  1521.     for (;;) {
  1522.       c = input_stack::peek_char();
  1523.       if (c == EOF || (!csalnum(c) && c != '_'))
  1524.     break;
  1525.       input_stack::get_char();
  1526.       token_buffer += char(c);
  1527.     }
  1528.     context_buffer = token_buffer;
  1529.     token_buffer += '\0';
  1530.     char *def = macro_table.lookup(token_buffer.contents());
  1531.     if (def)
  1532.       return strsave(def);
  1533.     // I guess it wasn't a macro after all; so push the macro name back.
  1534.     // -2 because we added a '\0'
  1535.     for (int i = token_buffer.length() - 2; i >= 0; i--)
  1536.       input_stack::push_back(token_buffer[i]);
  1537.   }
  1538.   if (get_delimited()) {
  1539.     token_buffer += '\0';
  1540.     return strsave(token_buffer.contents());
  1541.   }
  1542.   else
  1543.     return 0;
  1544. }
  1545.  
  1546. int lookahead_token = -1;
  1547. string old_context_buffer;
  1548.  
  1549. void do_lookahead()
  1550. {
  1551.   if (lookahead_token == -1) {
  1552.     old_context_buffer = context_buffer;
  1553.     lookahead_token = get_token(1);
  1554.   }
  1555. }
  1556.  
  1557. int yylex()
  1558. {
  1559.   if (delim_flag) {
  1560.     assert(lookahead_token == -1);
  1561.     if (delim_flag == 2) {
  1562.       if ((yylval.str = get_thru_arg()) != 0)
  1563.     return DELIMITED;
  1564.       else
  1565.     return 0;
  1566.     }
  1567.     else {
  1568.       if (get_delimited()) {
  1569.     token_buffer += '\0';
  1570.     yylval.str = strsave(token_buffer.contents());
  1571.     return DELIMITED;
  1572.       }
  1573.       else
  1574.     return 0;
  1575.     }
  1576.   }
  1577.   for (;;) {
  1578.     int t;
  1579.     if (lookahead_token >= 0) {
  1580.       t = lookahead_token;
  1581.       lookahead_token = -1;
  1582.     }
  1583.     else
  1584.       t = get_token(1);
  1585.     switch (t) {
  1586.     case '\n':
  1587.       return ';';
  1588.     case EOF:
  1589.       return 0;
  1590.     case DEFINE:
  1591.       do_define();
  1592.       break;
  1593.     case UNDEF:
  1594.       do_undef();
  1595.       break;
  1596.     case ORDINAL:
  1597.       yylval.n = token_int;
  1598.       return t;
  1599.     case NUMBER:
  1600.       yylval.x = token_double;
  1601.       return t;
  1602.     case COMMAND:
  1603.     case TEXT:
  1604.       token_buffer += '\0';
  1605.       if (!input_stack::get_location(&yylval.lstr.filename,
  1606.                      &yylval.lstr.lineno)) {
  1607.     yylval.lstr.filename = 0;
  1608.     yylval.lstr.lineno = -1;
  1609.       }
  1610.       yylval.lstr.str = strsave(token_buffer.contents());
  1611.       return t;
  1612.     case LABEL:
  1613.     case VARIABLE:
  1614.       token_buffer += '\0';
  1615.       yylval.str = strsave(token_buffer.contents());
  1616.       return t;
  1617.     case LEFT:
  1618.       // change LEFT to LEFT_CORNER when followed by OF
  1619.       old_context_buffer = context_buffer;
  1620.       lookahead_token = get_token(1);
  1621.       if (lookahead_token == OF)
  1622.     return LEFT_CORNER;
  1623.       else
  1624.     return t;
  1625.     case RIGHT:
  1626.       // change RIGHT to RIGHT_CORNER when followed by OF
  1627.       old_context_buffer = context_buffer;
  1628.       lookahead_token = get_token(1);
  1629.       if (lookahead_token == OF)
  1630.     return RIGHT_CORNER;
  1631.       else
  1632.     return t;
  1633.     case UPPER:
  1634.       // recognise UPPER only before LEFT or RIGHT
  1635.       old_context_buffer = context_buffer;
  1636.       lookahead_token = get_token(1);
  1637.       if (lookahead_token != LEFT && lookahead_token != RIGHT) {
  1638.     yylval.str = strsave("upper");
  1639.     return VARIABLE;
  1640.       }
  1641.       else
  1642.     return t;
  1643.     case LOWER:
  1644.       // recognise LOWER only before LEFT or RIGHT
  1645.       old_context_buffer = context_buffer;
  1646.       lookahead_token = get_token(1);
  1647.       if (lookahead_token != LEFT && lookahead_token != RIGHT) {
  1648.     yylval.str = strsave("lower");
  1649.     return VARIABLE;
  1650.       }
  1651.       else
  1652.     return t;
  1653.     case TOP:
  1654.       // recognise TOP only before OF
  1655.       old_context_buffer = context_buffer;
  1656.       lookahead_token = get_token(1);
  1657.       if (lookahead_token != OF) {
  1658.     yylval.str = strsave("top");
  1659.     return VARIABLE;
  1660.       }
  1661.       else
  1662.     return t;
  1663.     case BOTTOM:
  1664.       // recognise BOTTOM only before OF
  1665.       old_context_buffer = context_buffer;
  1666.       lookahead_token = get_token(1);
  1667.       if (lookahead_token != OF) {
  1668.     yylval.str = strsave("bottom");
  1669.     return VARIABLE;
  1670.       }
  1671.       else
  1672.     return t;
  1673.     case CENTER:
  1674.       // recognise CENTER only before OF
  1675.       old_context_buffer = context_buffer;
  1676.       lookahead_token = get_token(1);
  1677.       if (lookahead_token != OF) {
  1678.     yylval.str = strsave("center");
  1679.     return VARIABLE;
  1680.       }
  1681.       else
  1682.     return t;
  1683.     case START:
  1684.       // recognise START only before OF
  1685.       old_context_buffer = context_buffer;
  1686.       lookahead_token = get_token(1);
  1687.       if (lookahead_token != OF) {
  1688.     yylval.str = strsave("start");
  1689.     return VARIABLE;
  1690.       }
  1691.       else
  1692.     return t;
  1693.     case END:
  1694.       // recognise END only before OF
  1695.       old_context_buffer = context_buffer;
  1696.       lookahead_token = get_token(1);
  1697.       if (lookahead_token != OF) {
  1698.     yylval.str = strsave("end");
  1699.     return VARIABLE;
  1700.       }
  1701.       else
  1702.     return t;
  1703.     default:
  1704.       return t;
  1705.     }
  1706.   }
  1707. }
  1708.  
  1709. void lex_error(const char *message,
  1710.            const errarg &arg1,
  1711.            const errarg &arg2,
  1712.            const errarg &arg3)
  1713. {
  1714.   const char *filename;
  1715.   int lineno;
  1716.   if (!input_stack::get_location(&filename, &lineno))
  1717.     error(message, arg1, arg2, arg3);
  1718.   else
  1719.     error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
  1720. }
  1721.  
  1722. void lex_warning(const char *message,
  1723.          const errarg &arg1,
  1724.          const errarg &arg2,
  1725.          const errarg &arg3)
  1726. {
  1727.   const char *filename;
  1728.   int lineno;
  1729.   if (!input_stack::get_location(&filename, &lineno))
  1730.     warning(message, arg1, arg2, arg3);
  1731.   else
  1732.     warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
  1733. }
  1734.  
  1735. void yyerror(const char *s)
  1736. {
  1737.   const char *filename;
  1738.   int lineno;
  1739.   const char *context = 0;
  1740.   if (lookahead_token == -1) {
  1741.     if (context_buffer.length() > 0) {
  1742.       context_buffer += '\0';
  1743.       context = context_buffer.contents();
  1744.     }
  1745.   }
  1746.   else {
  1747.     if (old_context_buffer.length() > 0) {
  1748.       old_context_buffer += '\0';
  1749.       context = old_context_buffer.contents();
  1750.     }
  1751.   }
  1752.   if (!input_stack::get_location(&filename, &lineno)) {
  1753.     if (context) {
  1754.       if (context[0] == '\n' && context[1] == '\0')
  1755.     error("%1 before newline", s);
  1756.       else
  1757.     error("%1 before `%2'", s, context);
  1758.     }
  1759.     else
  1760.       error("%1 at end of picture", s);
  1761.   }
  1762.   else {
  1763.     if (context) {
  1764.       if (context[0] == '\n' && context[1] == '\0')
  1765.     error_with_file_and_line(filename, lineno, "%1 before newline", s);
  1766.       else
  1767.     error_with_file_and_line(filename, lineno, "%1 before `%2'",
  1768.                  s, context);
  1769.     }
  1770.     else
  1771.       error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
  1772.   }
  1773. }
  1774.  
  1775.