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