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

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991 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 "eqn.h"
  22. #include "eqn.tab.h"
  23. #include "stringclass.h"
  24. #include "ptable.h"
  25.  
  26. struct definition {
  27.   char is_macro;
  28.   char is_simple;
  29.   union {
  30.     int tok;
  31.     char *contents;
  32.   };
  33.   definition();
  34.   ~definition();
  35. };
  36.  
  37. definition::definition() : is_macro(1), contents(0), is_simple(0)
  38. {
  39. }
  40.  
  41. definition::~definition()
  42. {
  43.   if (is_macro)
  44.     delete contents;
  45. }
  46.  
  47. declare_ptable(definition)
  48. implement_ptable(definition)
  49.  
  50. PTABLE(definition) macro_table;
  51.  
  52. static struct {
  53.   const char *name;
  54.   int token;
  55. } token_table[] = {
  56.   "over", OVER,
  57.   "smallover", SMALLOVER,
  58.   "sqrt", SQRT,
  59.   "sub", SUB,
  60.   "sup", SUP,
  61.   "lpile", LPILE,
  62.   "rpile", RPILE,
  63.   "cpile", CPILE,
  64.   "pile", PILE,
  65.   "left", LEFT,
  66.   "right", RIGHT,
  67.   "to", TO,
  68.   "from", FROM,
  69.   "size", SIZE,
  70.   "font", FONT,
  71.   "roman", ROMAN,
  72.   "bold", BOLD,
  73.   "italic", ITALIC,
  74.   "fat", FAT,
  75.   "bar", BAR,
  76.   "under", UNDER,
  77.   "accent", ACCENT,
  78.   "uaccent", UACCENT,
  79.   "above", ABOVE,
  80.   "fwd", FWD,
  81.   "back", BACK,
  82.   "down", DOWN,
  83.   "up", UP,
  84.   "matrix", MATRIX,
  85.   "col", COL,
  86.   "lcol", LCOL,
  87.   "rcol", RCOL,
  88.   "ccol", CCOL,
  89.   "mark", MARK,
  90.   "lineup", LINEUP,
  91.   "space", SPACE,
  92.   "gfont", GFONT,
  93.   "gsize", GSIZE,
  94.   "define", DEFINE,
  95.   "sdefine", SDEFINE,
  96.   "ndefine", NDEFINE,
  97.   "tdefine", TDEFINE,
  98.   "undef", UNDEF,
  99.   "ifdef", IFDEF,
  100.   "include", INCLUDE,
  101.   "copy", INCLUDE,
  102.   "delim", DELIM,
  103.   "chartype", CHARTYPE,
  104.   "type", TYPE,
  105.   "vcenter", VCENTER,
  106.   "set", SET,
  107.   "opprime", PRIME,
  108.   "grfont", GRFONT,
  109.   "gbfont", GBFONT,
  110.   "split", SPLIT,
  111.   "nosplit", NOSPLIT,
  112. };
  113.  
  114. static struct {
  115.   const char *name;
  116.   const char *def;
  117. } def_table[] = {
  118.   "ALPHA", "\\(*A",
  119.   "BETA", "\\(*B",
  120.   "CHI", "\\(*X",
  121.   "DELTA", "\\(*D",
  122.   "EPSILON", "\\(*E",
  123.   "ETA", "\\(*Y",
  124.   "GAMMA", "\\(*G",
  125.   "IOTA", "\\(*I",
  126.   "KAPPA", "\\(*K",
  127.   "LAMBDA", "\\(*L",
  128.   "MU", "\\(*M",
  129.   "NU", "\\(*N",
  130.   "OMEGA", "\\(*W",
  131.   "OMICRON", "\\(*O",
  132.   "PHI", "\\(*F",
  133.   "PI", "\\(*P",
  134.   "PSI", "\\(*Q",
  135.   "RHO", "\\(*R",
  136.   "SIGMA", "\\(*S",
  137.   "TAU", "\\(*T",
  138.   "THETA", "\\(*H",
  139.   "UPSILON", "\\(*U",
  140.   "XI", "\\(*C",
  141.   "ZETA", "\\(*Z",
  142.   "Alpha", "\\(*A",
  143.   "Beta", "\\(*B",
  144.   "Chi", "\\(*X",
  145.   "Delta", "\\(*D",
  146.   "Epsilon", "\\(*E",
  147.   "Eta", "\\(*Y",
  148.   "Gamma", "\\(*G",
  149.   "Iota", "\\(*I",
  150.   "Kappa", "\\(*K",
  151.   "Lambda", "\\(*L",
  152.   "Mu", "\\(*M",
  153.   "Nu", "\\(*N",
  154.   "Omega", "\\(*W",
  155.   "Omicron", "\\(*O",
  156.   "Phi", "\\(*F",
  157.   "Pi", "\\(*P",
  158.   "Psi", "\\(*Q",
  159.   "Rho", "\\(*R",
  160.   "Sigma", "\\(*S",
  161.   "Tau", "\\(*T",
  162.   "Theta", "\\(*H",
  163.   "Upsilon", "\\(*U",
  164.   "Xi", "\\(*C",
  165.   "Zeta", "\\(*Z",
  166.   "alpha", "\\(*a",
  167.   "beta", "\\(*b",
  168.   "chi", "\\(*x",
  169.   "delta", "\\(*d",
  170.   "epsilon", "\\(*e",
  171.   "eta", "\\(*y",
  172.   "gamma", "\\(*g",
  173.   "iota", "\\(*i",
  174.   "kappa", "\\(*k",
  175.   "lambda", "\\(*l",
  176.   "mu", "\\(*m",
  177.   "nu", "\\(*n",
  178.   "omega", "\\(*w",
  179.   "omicron", "\\(*o",
  180.   "phi", "\\(*f",
  181.   "pi", "\\(*p",
  182.   "psi", "\\(*q",
  183.   "rho", "\\(*r",
  184.   "sigma", "\\(*s",
  185.   "tau", "\\(*t",
  186.   "theta", "\\(*h",
  187.   "upsilon", "\\(*u",
  188.   "xi", "\\(*c",
  189.   "zeta", "\\(*z",
  190.   "max", "{type \"operator\" roman \"max\"}",
  191.   "min", "{type \"operator\" roman \"min\"}",
  192.   "lim", "{type \"operator\" roman \"lim\"}",
  193.   "sin", "{type \"operator\" roman \"sin\"}",
  194.   "cos", "{type \"operator\" roman \"cos\"}",
  195.   "tan", "{type \"operator\" roman \"tan\"}",
  196.   "sinh", "{type \"operator\" roman \"sinh\"}",
  197.   "cosh", "{type \"operator\" roman \"cosh\"}",
  198.   "tanh", "{type \"operator\" roman \"tanh\"}",
  199.   "arc", "{type \"operator\" roman \"arc\"}",
  200.   "log", "{type \"operator\" roman \"log\"}",
  201.   "ln", "{type \"operator\" roman \"ln\"}",
  202.   "exp", "{type \"operator\" roman \"exp\"}",
  203.   "Re", "{type \"operator\" roman \"Re\"}",
  204.   "Im", "{type \"operator\" roman \"Im\"}",
  205.   "det", "{type \"operator\" roman \"det\"}",
  206.   "and", "{roman \"and\"}",
  207.   "if", "{roman \"if\"}",
  208.   "for", "{roman \"for\"}",
  209.   "sum", "{type \"operator\" vcenter size +5 \\(*S}",
  210.   "prod", "{type \"operator\" vcenter size +5 \\(*P}",
  211.   "int", "{type \"operator\" vcenter size +8 \\(is}",
  212.   "union", "{type \"operator\" vcenter size +5 \\(cu}",
  213.   "inter", "{type \"operator\" vcenter size +5 \\(ca}",
  214.   "times", "type \"binary\" \\(mu",
  215.   "ldots", "type \"inner\" { . . . }",
  216.   "inf", "\\(if",
  217.   "partial", "\\(pd",
  218.   "nothing", "\"\"",
  219.   "half", "{1 smallover 2}",
  220.   "hat_def", "\"^\"",
  221.   "hat", "accent { hat_def }",
  222.   "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"",
  223.   "dot", "accent { dot_def }",
  224.   "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"",
  225.   "dotdot", "accent { dotdot_def }",
  226.   "tilde_def", "\"~\"",
  227.   "tilde", "accent { tilde_def }",
  228.   "utilde_def", "\"\\v'75M'~\\v'-75M'\"",
  229.   "utilde", "uaccent { utilde_def }",
  230.   "vec_def", "up 52 size -5 \\(->",
  231.   "vec", "accent { vec_def }",
  232.   "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}",
  233.   "dyad", "accent { dyad_def }",
  234.   "==", "type \"relation\" \\(==",
  235.   "!=", "type \"relation\" \\(!=",
  236.   "+-", "type \"binary\" \\(+-",
  237.   "->", "type \"relation\" \\(->",
  238.   "<-", "type \"relation\" \\(<-",
  239.   "<<", "{ < back 20 < }",
  240.   ">>", "{ > back 20 > }",
  241.   "...", "type \"inner\" vcenter { . . . }",
  242.   "prime", "'",
  243.   "approx", "type \"relation\" \"\\(~=\"",
  244.   "grad", "\\(gr",
  245.   "del", "\\(gr",
  246.   "cdot", "type \"binary\" vcenter ."
  247. };  
  248.  
  249. void init_table(const char *device)
  250. {
  251.   for (int i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
  252.     definition *def = new definition;
  253.     def->is_macro = 0;
  254.     def->tok = token_table[i].token;
  255.     macro_table.define(token_table[i].name, def);
  256.   }
  257.   for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) {
  258.     definition *def = new definition;
  259.     def->is_macro = 1;
  260.     def->contents = strsave(def_table[i].def);
  261.     def->is_simple = 1;
  262.     macro_table.define(def_table[i].name, def);
  263.   }
  264.   definition *def = new definition;
  265.   def->is_macro = 1;
  266.   def->contents = strsave("1");
  267.   macro_table.define(device, def);
  268. }
  269.  
  270. class input {
  271.   input *next;
  272. public:
  273.   input(input *p);
  274.   virtual ~input();
  275.   virtual int get() = 0;
  276.   virtual int peek() = 0;
  277.   virtual int get_location(char **, int *);
  278.  
  279.   friend int get_char();
  280.   friend int peek_char();
  281.   friend int get_location(char **, int *);
  282.   friend void init_lex(const char *str, const char *filename, int lineno);
  283. };
  284.  
  285. class file_input : public input {
  286.   FILE *fp;
  287.   char *filename;
  288.   int lineno;
  289.   string line;
  290.   const char *ptr;
  291.   int read_line();
  292. public:
  293.   file_input(FILE *, const char *, input *);
  294.   ~file_input();
  295.   int get();
  296.   int peek();
  297.   int get_location(char **, int *);
  298. };
  299.  
  300.  
  301. class macro_input : public input {
  302.   char *s;
  303.   char *p;
  304. public:
  305.   macro_input(const char *, input *);
  306.   ~macro_input();
  307.   int get();
  308.   int peek();
  309. };
  310.  
  311. class top_input : public macro_input {
  312.   char *filename;
  313.   int lineno;
  314.  public:
  315.   top_input(const char *, const char *, int, input *);
  316.   ~top_input();
  317.   int get();
  318.   int get_location(char **, int *);
  319. };
  320.  
  321. class argument_macro_input: public input {
  322.   char *s;
  323.   char *p;
  324.   char *ap;
  325.   int argc;
  326.   char *argv[9];
  327. public:
  328.   argument_macro_input(const char *, int, char **, input *);
  329.   ~argument_macro_input();
  330.   int get();
  331.   int peek();
  332. };
  333.  
  334. input::input(input *x) : next(x)
  335. {
  336. }
  337.  
  338. input::~input()
  339. {
  340. }
  341.  
  342. int input::get_location(char **, int *)
  343. {
  344.   return 0;
  345. }
  346.  
  347. file_input::file_input(FILE *f, const char *fn, input *p)
  348. : input(p), lineno(0), ptr("")
  349. {
  350.   fp = f;
  351.   filename = strsave(fn);
  352. }
  353.  
  354. file_input::~file_input()
  355. {
  356.   delete filename;
  357.   fclose(fp);
  358. }
  359.  
  360. int file_input::read_line()
  361. {
  362.   for (;;) {
  363.     line.clear();
  364.     lineno++;
  365.     for (;;) {
  366.       int c = getc(fp);
  367.       if (c == EOF)
  368.     break;
  369.       else if (illegal_input_char(c))
  370.     lex_error("illegal input character code %1", c);
  371.       else {
  372.     line += char(c);
  373.     if (c == '\n') 
  374.       break;
  375.       }
  376.     }
  377.     if (line.length() == 0)
  378.       return 0;
  379.     if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
  380.       && (line[2] == 'Q' || line[2] == 'N')
  381.       && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
  382.           || compatible_flag))) {
  383.       line += '\0';
  384.       ptr = line.contents();
  385.       return 1;
  386.     }
  387.   }
  388. }
  389.  
  390. int file_input::get()
  391. {
  392.   if (*ptr != '\0' || read_line())
  393.     return *ptr++ & 0377;
  394.   else
  395.     return EOF;
  396. }
  397.  
  398. int file_input::peek()
  399. {
  400.   if (*ptr != '\0' || read_line())
  401.     return *ptr;
  402.   else
  403.     return EOF;
  404. }
  405.  
  406. int file_input::get_location(char **fnp, int *lnp)
  407. {
  408.   *fnp = filename;
  409.   *lnp = lineno;
  410.   return 1;
  411. }
  412.  
  413. macro_input::macro_input(const char *str, input *x) : input(x)
  414. {
  415.   p = s = strsave(str);
  416. }
  417.  
  418. macro_input::~macro_input()
  419. {
  420.   delete s;
  421. }
  422.  
  423. int macro_input::get()
  424. {
  425.   if (p == 0 || *p == '\0')
  426.     return EOF;
  427.   else
  428.     return *p++ & 0377;
  429. }
  430.  
  431. int macro_input::peek()
  432. {
  433.   if (p == 0 || *p == '\0')
  434.     return EOF;
  435.   else
  436.     return *p & 0377;
  437. }
  438.  
  439. top_input::top_input(const char *str, const char *fn, int ln, input *x)
  440. : macro_input(str, x), lineno(ln)
  441. {
  442.   filename = strsave(fn);
  443. }
  444.  
  445. top_input::~top_input()
  446. {
  447.   delete filename;
  448. }
  449.  
  450. int top_input::get()
  451. {
  452.   int c = macro_input::get();
  453.   if (c == '\n')
  454.     lineno++;
  455.   return c;
  456. }
  457.  
  458. int top_input::get_location(char **fnp, int *lnp)
  459. {
  460.   *fnp = filename;
  461.   *lnp = lineno;
  462.   return 1;
  463. }
  464.  
  465. #define ARG1 11
  466.  
  467. argument_macro_input::argument_macro_input(const char *body, int ac, 
  468.                        char **av, input *x)
  469. : input(x), argc(ac), ap(0)
  470. {
  471.   for (int i = 0; i < argc; i++)
  472.     argv[i] = av[i];
  473.   p = s = strsave(body);
  474.   int j = 0;
  475.   for (i = 0; s[i] != '\0'; i++)
  476.     if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
  477.       if (s[i+1] != '0')
  478.     s[j++] = ARG1 + s[++i] - '1';
  479.     }
  480.     else
  481.       s[j++] = s[i];
  482.   s[j] = '\0';
  483. }
  484.  
  485.  
  486. argument_macro_input::~argument_macro_input()
  487. {
  488.   for (int i = 0; i < argc; i++)
  489.     delete argv[i];
  490.   delete s;
  491. }
  492.  
  493. int argument_macro_input::get()
  494. {
  495.   if (ap) {
  496.     if (*ap != '\0')
  497.       return *ap++ & 0377;
  498.     ap = 0;
  499.   }
  500.   if (p == 0)
  501.     return EOF;
  502.   while (*p >= ARG1 && *p <= ARG1 + 8) {
  503.     int i = *p++ - ARG1;
  504.     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
  505.       ap = argv[i];
  506.       return *ap++ & 0377;
  507.     }
  508.   }
  509.   if (*p == '\0')
  510.     return EOF;
  511.   return *p++ & 0377;
  512. }
  513.  
  514. int argument_macro_input::peek()
  515. {
  516.   if (ap) {
  517.     if (*ap != '\0')
  518.       return *ap & 0377;
  519.     ap = 0;
  520.   }
  521.   if (p == 0)
  522.     return EOF;
  523.   while (*p >= ARG1 && *p <= ARG1 + 8) {
  524.     int i = *p++ - ARG1;
  525.     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
  526.       ap = argv[i];
  527.       return *ap & 0377;
  528.     }
  529.   }
  530.   if (*p == '\0')
  531.     return EOF;
  532.   return *p & 0377;
  533. }
  534.  
  535. static input *current_input = 0;
  536.  
  537. /* we insert a newline between input from different levels */
  538.  
  539. int get_char()
  540. {
  541.   if (current_input == 0)
  542.     return EOF;
  543.   else {
  544.     int c = current_input->get();
  545.     if (c != EOF)
  546.       return c;
  547.     else {
  548.       input *tem = current_input;
  549.       current_input = current_input->next;
  550.       delete tem;
  551.       return '\n';
  552.     }
  553.   }
  554. }
  555.  
  556. int peek_char()
  557. {
  558.   if (current_input == 0)
  559.     return EOF;
  560.   else {
  561.     int c = current_input->peek();
  562.     if (c != EOF)
  563.       return c;
  564.     else
  565.       return '\n';
  566.   }
  567. }
  568.  
  569. int get_location(char **fnp, int *lnp)
  570. {
  571.   for (input *p = current_input; p; p = p->next)
  572.     if (p->get_location(fnp, lnp))
  573.       return 1;
  574.   return 0;
  575. }
  576.  
  577. string token_buffer;
  578. const int NCONTEXT = 4;
  579. string context_ring[NCONTEXT];
  580. int context_index = 0;
  581.  
  582. void flush_context()
  583. {
  584.   for (int i = 0; i < NCONTEXT; i++)
  585.     context_ring[i] = "";
  586.   context_index = 0;
  587. }
  588.  
  589. void show_context()
  590. {
  591.   int i = context_index;
  592.   fputs(" context is\n\t", stderr);
  593.   for (;;) {
  594.     int j = (i + 1) % NCONTEXT;
  595.     if (j == context_index) {
  596.       fputs(">>> ", stderr);
  597.       put_string(context_ring[i], stderr);
  598.       fputs(" <<<", stderr);
  599.       break;
  600.     }
  601.     else if (context_ring[i].length() > 0) {
  602.       put_string(context_ring[i], stderr);
  603.       putc(' ', stderr);
  604.     }
  605.     i = j;
  606.   }
  607.   putc('\n', stderr);
  608. }
  609.  
  610. void add_context(const string &s)
  611. {
  612.   context_ring[context_index] = s;
  613.   context_index = (context_index + 1) % NCONTEXT;
  614. }
  615.  
  616. void add_context(char c)
  617. {
  618.   context_ring[context_index] = c;
  619.   context_index = (context_index + 1) % NCONTEXT;
  620. }
  621.  
  622. void add_quoted_context(const string &s)
  623. {
  624.   string &r = context_ring[context_index];
  625.   r = '"';
  626.   for (int i = 0; i < s.length(); i++)
  627.     if (s[i] == '"')
  628.       r += "\\\"";
  629.     else
  630.       r += s[i];
  631.   r += '"';
  632.   context_index = (context_index + 1) % NCONTEXT;
  633. }
  634.  
  635. void init_lex(const char *str, const char *filename, int lineno)
  636. {
  637.  while (current_input != 0) {
  638.     input *tem = current_input;
  639.     current_input = current_input->next;
  640.     delete tem;
  641.   }
  642.   current_input = new top_input(str, filename, lineno, 0);
  643.   flush_context();
  644. }
  645.  
  646.  
  647. void get_delimited_text()
  648. {
  649.   char *filename;
  650.   int lineno;
  651.   int got_location = get_location(&filename, &lineno);
  652.   int start = get_char();
  653.   while (start == ' ' || start == '\n')
  654.     start = get_char();
  655.   token_buffer.clear();
  656.   if (start == EOF) {
  657.     if (got_location)
  658.       error_with_file_and_line(filename, lineno,
  659.                    "end of input while defining macro");
  660.     else
  661.       error("end of input while defining macro");
  662.     return;
  663.   }
  664.   for (;;) {
  665.     int c = get_char();
  666.     if (c == EOF) {
  667.       if (got_location)
  668.     error_with_file_and_line(filename, lineno,
  669.                  "end of input while defining macro");
  670.       else
  671.     error("end of input while defining macro");
  672.       add_context(start + token_buffer);
  673.       return;
  674.     }
  675.     if (c == start)
  676.       break;
  677.     token_buffer += char(c);
  678.   }
  679.   add_context(start + token_buffer + start);
  680. }
  681.  
  682. void interpolate_macro_with_args(const char *body)
  683. {
  684.   char *argv[9];
  685.   int argc = 0;
  686.   for (int i = 0; i < 9; i++)
  687.     argv[i] = 0;
  688.   int level = 0;
  689.   int c;
  690.   do {
  691.     token_buffer.clear();
  692.     for (;;) {
  693.       c = get_char();
  694.       if (c == EOF) {
  695.     lex_error("end of input while scanning macro arguments");
  696.     break;
  697.       }
  698.       if (level == 0 && (c == ',' || c == ')')) {
  699.     if (token_buffer.length() > 0) {
  700.       token_buffer +=  '\0';
  701.       argv[argc] = strsave(token_buffer.contents());
  702.     }
  703.     // for `foo()', argc = 0
  704.     if (argc > 0 || c != ')' || i > 0)
  705.       argc++;
  706.     break;
  707.       }
  708.       token_buffer += char(c);
  709.       if (c == '(')
  710.     level++;
  711.       else if (c == ')')
  712.     level--;
  713.     }
  714.   } while (c != ')' && c != EOF);
  715.   current_input = new argument_macro_input(body, argc, argv, current_input);
  716. }
  717.  
  718. /* If lookup flag is non-zero the token will be looked up to see
  719. if it is macro. If it's 1, it will looked up to see if it's a token.
  720. */
  721.  
  722. int get_token(int lookup_flag = 0)
  723. {
  724.   for (;;) {
  725.     int c = get_char();
  726.     while (c == ' ' || c == '\n')
  727.       c = get_char();
  728.     switch (c) {
  729.     case EOF:
  730.       add_context("end of input");
  731.       return 0;
  732.     case '"':
  733.       {
  734.     int quoted = 0;
  735.     token_buffer.clear();
  736.     for (;;) {
  737.       c = get_char();
  738.       if (c == EOF) {
  739.         lex_error("missing \"");
  740.         break;
  741.       }
  742.       else if (c == '\n') {
  743.         lex_error("newline before end of quoted text");
  744.         break;
  745.       }
  746.       else if (c == '"') {
  747.         if (!quoted)
  748.           break;
  749.         token_buffer[token_buffer.length() - 1] = '"';
  750.         quoted = 0;
  751.       }
  752.       else {
  753.         token_buffer += c;
  754.         quoted = quoted ? 0 : c == '\\';
  755.       }
  756.     }
  757.       }
  758.       add_quoted_context(token_buffer);
  759.       return QUOTED_TEXT;
  760.     case '{':
  761.     case '}':
  762.     case '^':
  763.     case '~':
  764.     case '\t':
  765.       add_context(c);
  766.       return c;
  767.     default:
  768.       {
  769.     int break_flag = 0;
  770.     int quoted = 0;
  771.     token_buffer.clear();
  772.     if (c == '\\')
  773.       quoted = 1;
  774.     else
  775.       token_buffer += c;
  776.     int done = 0;
  777.     while (!done) {
  778.       c = peek_char();
  779.       if (!quoted && lookup_flag != 0 && c == '(') {
  780.         token_buffer += '\0';
  781.         definition *def = macro_table.lookup(token_buffer.contents());
  782.         if (def && def->is_macro && !def->is_simple) {
  783.           (void)get_char();    // skip initial '('
  784.           interpolate_macro_with_args(def->contents);
  785.           break_flag = 1;
  786.           break;
  787.         }
  788.         token_buffer.set_length(token_buffer.length() - 1);
  789.       }
  790.       if (quoted) {
  791.         quoted = 0;
  792.         switch (c) {
  793.         case EOF:
  794.           lex_error("`\\' ignored at end of equation");
  795.           done = 1;
  796.           break;
  797.         case '\n':
  798.           lex_error("`\\' ignored because followed by newline");
  799.           done = 1;
  800.           break;
  801.         case '\t':
  802.           lex_error("`\\' ignored because followed by tab");
  803.           done = 1;
  804.           break;
  805.         case '"':
  806.           (void)get_char();
  807.           token_buffer += '"';
  808.           break;
  809.         default:
  810.           (void)get_char();
  811.           token_buffer += '\\';
  812.           token_buffer += c;
  813.           break;
  814.         }
  815.       }
  816.       else {
  817.         switch (c) {
  818.         case EOF:
  819.         case '{':
  820.         case '}':
  821.         case '^':
  822.         case '~':
  823.         case '"':
  824.         case ' ':
  825.         case '\t':
  826.         case '\n':
  827.           done = 1;
  828.           break;
  829.         case '\\':
  830.           (void)get_char();
  831.           quoted = 1;
  832.           break;
  833.         default:
  834.           (void)get_char();
  835.           token_buffer += char(c);
  836.           break;
  837.         }
  838.       }
  839.     }
  840.     if (break_flag || token_buffer.length() == 0)
  841.       break;
  842.     if (lookup_flag != 0) {
  843.       token_buffer += '\0';
  844.       definition *def = macro_table.lookup(token_buffer.contents());
  845.       token_buffer.set_length(token_buffer.length() - 1);
  846.       if (def) {
  847.         if (def->is_macro) {
  848.           current_input = new macro_input(def->contents, current_input);
  849.           break;
  850.         }
  851.         else if (lookup_flag == 1) {
  852.           add_context(token_buffer);
  853.           return def->tok;
  854.         }
  855.       }
  856.     }
  857.     add_context(token_buffer);
  858.     return TEXT;
  859.       }
  860.     }
  861.   }
  862. }
  863.  
  864. void do_include()
  865. {
  866.   int t = get_token(2);
  867.   if (t != TEXT && t != QUOTED_TEXT) {
  868.     lex_error("bad filename for include");
  869.     return;
  870.   }
  871.   token_buffer += '\0';
  872.   const char *filename = token_buffer.contents();
  873.   FILE *fp = fopen(filename, "r");
  874.   if (fp == 0) {
  875.     lex_error("can't open included file `%1'", filename);
  876.     return;
  877.   }
  878.   current_input = new file_input(fp, filename, current_input);
  879. }
  880.  
  881. void ignore_definition()
  882. {
  883.   int t = get_token();
  884.   if (t != TEXT) {
  885.     lex_error("bad definition");
  886.     return;
  887.   }
  888.   get_delimited_text();
  889. }
  890.  
  891. void do_definition(int is_simple)
  892. {
  893.   int t = get_token();
  894.   if (t != TEXT) {
  895.     lex_error("bad definition");
  896.     return;
  897.   }
  898.   token_buffer += '\0';
  899.   const char *name = token_buffer.contents();
  900.   definition *def = macro_table.lookup(name);
  901.   if (def == 0) {
  902.     def = new definition;
  903.     macro_table.define(name, def);
  904.   }
  905.   else if (def->is_macro) {
  906.     delete def->contents;
  907.   }
  908.   get_delimited_text();
  909.   token_buffer += '\0';
  910.   def->is_macro = 1;
  911.   def->contents = strsave(token_buffer.contents());
  912.   def->is_simple = is_simple;
  913. }
  914.  
  915. void do_undef()
  916. {
  917.   int t = get_token();
  918.   if (t != TEXT) {
  919.     lex_error("bad undef command");
  920.     return;
  921.   }
  922.   token_buffer += '\0';
  923.   macro_table.define(token_buffer.contents(), 0);
  924. }
  925.  
  926. void do_gsize()
  927. {
  928.   int t = get_token(2);
  929.   if (t != TEXT && t != QUOTED_TEXT) {
  930.     lex_error("bad argument to gsize command");
  931.     return;
  932.   }
  933.   token_buffer += '\0';
  934.   char *ptr;
  935.   long n = strtol(token_buffer.contents(), &ptr, 10);
  936.   if (n == 0 && ptr == token_buffer.contents())
  937.     lex_error("bad argument `%1' to gsize command");
  938.   else
  939.     set_gsize(int(n));
  940. }
  941.  
  942. void do_gfont()
  943. {
  944.   int t = get_token(2);
  945.   if (t != TEXT && t != QUOTED_TEXT) {
  946.     lex_error("bad argument to gfont command");
  947.     return;
  948.   }
  949.   token_buffer += '\0';
  950.   set_gfont(token_buffer.contents());
  951. }
  952.  
  953. void do_grfont()
  954. {
  955.   int t = get_token(2);
  956.   if (t != TEXT && t != QUOTED_TEXT) {
  957.     lex_error("bad argument to grfont command");
  958.     return;
  959.   }
  960.   token_buffer += '\0';
  961.   set_grfont(token_buffer.contents());
  962. }
  963.  
  964. void do_gbfont()
  965. {
  966.   int t = get_token(2);
  967.   if (t != TEXT && t != QUOTED_TEXT) {
  968.     lex_error("bad argument to gbfont command");
  969.     return;
  970.   }
  971.   token_buffer += '\0';
  972.   set_gbfont(token_buffer.contents());
  973. }
  974.  
  975. void do_space()
  976. {
  977.   int t = get_token(2);
  978.   if (t != TEXT && t != QUOTED_TEXT) {
  979.     lex_error("bad argument to space command");
  980.     return;
  981.   }
  982.   token_buffer += '\0';
  983.   char *ptr;
  984.   long n = strtol(token_buffer.contents(), &ptr, 10);
  985.   if (n == 0 && ptr == token_buffer.contents())
  986.     lex_error("bad argument `%1' to space command");
  987.   else
  988.     set_space(int(n));
  989. }
  990.  
  991. void do_ifdef()
  992. {
  993.   int t = get_token();
  994.   if (t != TEXT) {
  995.     lex_error("bad ifdef");
  996.     return;
  997.   }
  998.   token_buffer += '\0';
  999.   definition *def = macro_table.lookup(token_buffer.contents());
  1000.   int result = def && def->is_macro && !def->is_simple;
  1001.   get_delimited_text();
  1002.   if (result) {
  1003.     token_buffer += '\0';
  1004.     current_input = new macro_input(token_buffer.contents(), current_input);
  1005.   }
  1006. }
  1007.  
  1008. void do_delim()
  1009. {
  1010.   int c = get_char();
  1011.   while (c == ' ' || c == '\n')
  1012.     c = get_char();
  1013.   int d;
  1014.   if (c == EOF || (d = get_char()) == EOF)
  1015.     lex_error("end of file while reading argument to `delim'");
  1016.   else {
  1017.     if (c == 'o' && d == 'f' && peek_char() == 'f') {
  1018.       (void)get_char();
  1019.       start_delim = end_delim = '\0';
  1020.     }
  1021.     else {
  1022.       start_delim = c;
  1023.       end_delim = d;
  1024.     }
  1025.   }
  1026. }
  1027.  
  1028. void do_chartype()
  1029. {
  1030.   int t = get_token(2);
  1031.   if (t != TEXT && t != QUOTED_TEXT) {
  1032.     lex_error("bad chartype");
  1033.     return;
  1034.   }
  1035.   token_buffer += '\0';
  1036.   string type = token_buffer;
  1037.   t = get_token();
  1038.   if (t != TEXT && t != QUOTED_TEXT) {
  1039.     lex_error("bad chartype");
  1040.     return;
  1041.   }
  1042.   token_buffer += '\0';
  1043.   set_char_type(type.contents(), strsave(token_buffer.contents()));
  1044. }
  1045.  
  1046. void do_set()
  1047. {
  1048.   int t = get_token(2);
  1049.   if (t != TEXT && t != QUOTED_TEXT) {
  1050.     lex_error("bad chartype");
  1051.     return;
  1052.   }
  1053.   token_buffer += '\0';
  1054.   string param = token_buffer;
  1055.   t = get_token();
  1056.   if (t != TEXT && t != QUOTED_TEXT) {
  1057.     lex_error("bad chartype");
  1058.     return;
  1059.   }
  1060.   token_buffer += '\0';
  1061.   int n;
  1062.   if (sscanf(&token_buffer[0], "%d", &n) != 1) {
  1063.     lex_error("bad number `%1'", token_buffer.contents());
  1064.     return;
  1065.   }
  1066.   set_param(param.contents(), n);
  1067. }
  1068.  
  1069. int yylex()
  1070. {
  1071.   for (;;) {
  1072.     int tk = get_token(1);
  1073.     switch(tk) {
  1074.     case UNDEF:
  1075.       do_undef();
  1076.       break;
  1077.     case SDEFINE:
  1078.       do_definition(1);
  1079.       break;
  1080.     case TDEFINE:
  1081.     case DEFINE:
  1082.       do_definition(0);
  1083.       break;
  1084.     case NDEFINE:
  1085.       ignore_definition();
  1086.       break;
  1087.     case GSIZE:
  1088.       do_gsize();
  1089.       break;
  1090.     case GFONT:
  1091.       do_gfont();
  1092.       break;
  1093.     case GRFONT:
  1094.       do_grfont();
  1095.       break;
  1096.     case GBFONT:
  1097.       do_gbfont();
  1098.       break;
  1099.     case SPACE:
  1100.       do_space();
  1101.       break;
  1102.     case INCLUDE:
  1103.       do_include();
  1104.       break;
  1105.     case IFDEF:
  1106.       do_ifdef();
  1107.       break;
  1108.     case DELIM:
  1109.       do_delim();
  1110.       break;
  1111.     case CHARTYPE:
  1112.       do_chartype();
  1113.       break;
  1114.     case SET:
  1115.       do_set();
  1116.       break;
  1117.     case QUOTED_TEXT:
  1118.     case TEXT:
  1119.       token_buffer += '\0';
  1120.       yylval.str = strsave(token_buffer.contents());
  1121.       // fall through
  1122.     default:
  1123.       return tk;
  1124.     }
  1125.   }
  1126. }
  1127.  
  1128. void lex_error(const char *message,
  1129.            const errarg &arg1,
  1130.            const errarg &arg2,
  1131.            const errarg &arg3)
  1132. {
  1133.   char *filename;
  1134.   int lineno;
  1135.   if (!get_location(&filename, &lineno))
  1136.     error(message, arg1, arg2, arg3);
  1137.   else
  1138.     error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
  1139. }
  1140.  
  1141. void yyerror(const char *s)
  1142. {
  1143.   char *filename;
  1144.   int lineno;
  1145.   if (!get_location(&filename, &lineno))
  1146.     error(s);
  1147.   else
  1148.     error_with_file_and_line(filename, lineno, s);
  1149.   show_context();
  1150. }
  1151.  
  1152.