groff before CVS: release 1.06
[s-roff.git] / pic / lex.cc
blob86a710ab8d1b91f3f8498cda9b1f75bccf510c44
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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 2, or (at your option) any later
10 version.
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.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "pic.h"
22 #include "ptable.h"
23 #include "object.h"
24 #include "pic.tab.h"
26 declare_ptable(char)
27 implement_ptable(char)
29 PTABLE(char) macro_table;
31 class macro_input : public input {
32 char *s;
33 char *p;
34 public:
35 macro_input(const char *);
36 ~macro_input();
37 int get();
38 int peek();
41 class argument_macro_input : public input {
42 char *s;
43 char *p;
44 char *ap;
45 int argc;
46 char *argv[9];
47 public:
48 argument_macro_input(const char *, int, char **);
49 ~argument_macro_input();
50 int get();
51 int peek();
54 input::input() : next(0)
58 input::~input()
62 int input::get_location(const char **, int *)
64 return 0;
67 file_input::file_input(FILE *f, const char *fn)
68 : lineno(0), ptr(""), filename(fn)
70 fp = f;
73 file_input::~file_input()
75 fclose(fp);
78 int file_input::read_line()
80 for (;;) {
81 line.clear();
82 lineno++;
83 for (;;) {
84 int c = getc(fp);
85 if (c == EOF)
86 break;
87 else if (illegal_input_char(c))
88 lex_error("illegal input character code %1", c);
89 else {
90 line += char(c);
91 if (c == '\n')
92 break;
95 if (line.length() == 0)
96 return 0;
97 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
98 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
99 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
100 || compatible_flag))) {
101 line += '\0';
102 ptr = line.contents();
103 return 1;
108 int file_input::get()
110 if (*ptr != '\0' || read_line())
111 return (unsigned char)*ptr++;
112 else
113 return EOF;
116 int file_input::peek()
118 if (*ptr != '\0' || read_line())
119 return (unsigned char)*ptr;
120 else
121 return EOF;
124 int file_input::get_location(const char **fnp, int *lnp)
126 *fnp = filename;
127 *lnp = lineno;
128 return 1;
131 macro_input::macro_input(const char *str)
133 p = s = strsave(str);
136 macro_input::~macro_input()
138 a_delete s;
141 int macro_input::get()
143 if (p == 0 || *p == '\0')
144 return EOF;
145 else
146 return (unsigned char)*p++;
149 int macro_input::peek()
151 if (p == 0 || *p == '\0')
152 return EOF;
153 else
154 return (unsigned char)*p;
157 // Character respresenting $1. Must be illegal input character.
158 #define ARG1 14
160 char *process_body(const char *body)
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';
169 else
170 s[j++] = s[i];
171 s[j] = '\0';
172 return s;
176 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
177 : argc(ac), ap(0)
179 for (int i = 0; i < argc; i++)
180 argv[i] = av[i];
181 p = s = process_body(body);
185 argument_macro_input::~argument_macro_input()
187 for (int i = 0; i < argc; i++)
188 a_delete argv[i];
189 a_delete s;
192 int argument_macro_input::get()
194 if (ap) {
195 if (*ap != '\0')
196 return (unsigned char)*ap++;
197 ap = 0;
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++;
208 if (*p == '\0')
209 return EOF;
210 return (unsigned char)*p++;
213 int argument_macro_input::peek()
215 if (ap) {
216 if (*ap != '\0')
217 return (unsigned char)*ap;
218 ap = 0;
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;
229 if (*p == '\0')
230 return EOF;
231 return (unsigned char)*p;
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 int bol();
247 input *input_stack::current_input = 0;
248 int input_stack::bol_flag = 0;
250 inline int input_stack::bol()
252 return bol_flag;
255 void input_stack::clear()
257 while (current_input != 0) {
258 input *tem = current_input;
259 current_input = current_input->next;
260 delete tem;
262 bol_flag = 1;
265 void input_stack::push(input *in)
267 in->next = current_input;
268 current_input = in;
271 void lex_init(input *top)
273 input_stack::clear();
274 input_stack::push(top);
277 void lex_cleanup()
279 while (input_stack::get_char() != EOF)
283 int input_stack::get_char()
285 while (current_input != 0) {
286 int c = current_input->get();
287 if (c != EOF) {
288 bol_flag = c == '\n';
289 return c;
291 // don't pop the top-level input off the stack
292 if (current_input->next == 0)
293 return EOF;
294 input *tem = current_input;
295 current_input = current_input->next;
296 delete tem;
298 return EOF;
301 int input_stack::peek_char()
303 while (current_input != 0) {
304 int c = current_input->peek();
305 if (c != EOF)
306 return c;
307 if (current_input->next == 0)
308 return EOF;
309 input *tem = current_input;
310 current_input = current_input->next;
311 delete tem;
313 return EOF;
316 class char_input : public input {
317 int c;
318 public:
319 char_input(int);
320 int get();
321 int peek();
324 char_input::char_input(int n) : c((unsigned char)n)
328 int char_input::get()
330 int n = c;
331 c = EOF;
332 return n;
335 int char_input::peek()
337 return c;
340 void input_stack::push_back(unsigned char c, int was_bol)
342 push(new char_input(c));
343 bol_flag = was_bol;
346 int input_stack::get_location(const char **fnp, int *lnp)
348 for (input *p = current_input; p; p = p->next)
349 if (p->get_location(fnp, lnp))
350 return 1;
351 return 0;
354 string context_buffer;
356 string token_buffer;
357 double token_double;
358 int token_int;
360 void interpolate_macro_with_args(const char *body)
362 char *argv[9];
363 int argc = 0;
364 for (int i = 0; i < 9; i++)
365 argv[i] = 0;
366 int level = 0;
367 int c;
368 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
369 do {
370 token_buffer.clear();
371 for (;;) {
372 c = input_stack::get_char();
373 if (c == EOF) {
374 lex_error("end of input while scanning macro arguments");
375 break;
377 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
378 if (token_buffer.length() > 0) {
379 token_buffer += '\0';
380 argv[argc] = strsave(token_buffer.contents());
382 // for `foo()', argc = 0
383 if (argc > 0 || c != ')' || i > 0)
384 argc++;
385 break;
387 token_buffer += char(c);
388 switch (state) {
389 case NORMAL:
390 if (c == '"')
391 state = IN_STRING;
392 else if (c == '(')
393 level++;
394 else if (c == ')')
395 level--;
396 break;
397 case IN_STRING:
398 if (c == '"')
399 state = NORMAL;
400 else if (c == '\\')
401 state = IN_STRING_QUOTED;
402 break;
403 case IN_STRING_QUOTED:
404 state = IN_STRING;
405 break;
408 } while (c != ')' && c != EOF);
409 input_stack::push(new argument_macro_input(body, argc, argv));
412 static int docmp(const char *s1, int n1, const char *s2, int n2)
414 if (n1 < n2) {
415 int r = memcmp(s1, s2, n1);
416 return r ? r : -1;
418 else if (n1 > n2) {
419 int r = memcmp(s1, s2, n2);
420 return r ? r : 1;
422 else
423 return memcmp(s1, s2, n1);
426 int lookup_keyword(const char *str, int len)
428 static struct keyword {
429 const char *name;
430 int token;
431 } table[] = {
432 "Here", HERE,
433 "above", ABOVE,
434 "aligned", ALIGNED,
435 "and", AND,
436 "arc", ARC,
437 "arrow", ARROW,
438 "at", AT,
439 "atan2", ATAN2,
440 "below", BELOW,
441 "between", BETWEEN,
442 "bottom", BOTTOM,
443 "box", BOX,
444 "by", BY,
445 "ccw", CCW,
446 "center", CENTER,
447 "chop", CHOP,
448 "circle", CIRCLE,
449 "command", COMMAND,
450 "copy", COPY,
451 "cos", COS,
452 "cw", CW,
453 "dashed", DASHED,
454 "define", DEFINE,
455 "diam", DIAMETER,
456 "diameter", DIAMETER,
457 "do", DO,
458 "dotted", DOTTED,
459 "down", DOWN,
460 "ellipse", ELLIPSE,
461 "else", ELSE,
462 "end", END,
463 "exp", EXP,
464 "fill", FILL,
465 "filled", FILL,
466 "for", FOR,
467 "from", FROM,
468 "height", HEIGHT,
469 "ht", HEIGHT,
470 "if", IF,
471 "int", INT,
472 "invis", INVISIBLE,
473 "invisible", INVISIBLE,
474 "last", LAST,
475 "left", LEFT,
476 "line", LINE,
477 "ljust", LJUST,
478 "log", LOG,
479 "lower", LOWER,
480 "max", MAX,
481 "min", MIN,
482 "move", MOVE,
483 "of", OF,
484 "plot", PLOT,
485 "print", PRINT,
486 "rad", RADIUS,
487 "radius", RADIUS,
488 "rand", RAND,
489 "reset", RESET,
490 "right", RIGHT,
491 "rjust", RJUST,
492 "same", SAME,
493 "sh", SH,
494 "sin", SIN,
495 "spline", SPLINE,
496 "sprintf", SPRINTF,
497 "sqrt", SQRT,
498 "start", START,
499 "the", THE,
500 "then", THEN,
501 "thick", THICKNESS,
502 "thickness", THICKNESS,
503 "thru", THRU,
504 "to", TO,
505 "top", TOP,
506 "undef", UNDEF,
507 "until", UNTIL,
508 "up", UP,
509 "upper", UPPER,
510 "way", WAY,
511 "wid", WIDTH,
512 "width", WIDTH,
513 "with", WITH,
516 const keyword *start = table;
517 const keyword *end = table + sizeof(table)/sizeof(table[0]);
518 while (start < end) {
519 // start <= target < end
520 const keyword *mid = start + (end - start)/2;
522 int cmp = docmp(str, len, mid->name, strlen(mid->name));
523 if (cmp == 0)
524 return mid->token;
525 if (cmp < 0)
526 end = mid;
527 else
528 start = mid + 1;
530 return 0;
533 int get_token_after_dot(int c)
535 // get_token deals with the case where c is a digit
536 switch (c) {
537 case 'h':
538 input_stack::get_char();
539 c = input_stack::peek_char();
540 if (c == 't') {
541 input_stack::get_char();
542 context_buffer = ".ht";
543 return DOT_HT;
545 else if (c == 'e') {
546 input_stack::get_char();
547 c = input_stack::peek_char();
548 if (c == 'i') {
549 input_stack::get_char();
550 c = input_stack::peek_char();
551 if (c == 'g') {
552 input_stack::get_char();
553 c = input_stack::peek_char();
554 if (c == 'h') {
555 input_stack::get_char();
556 c = input_stack::peek_char();
557 if (c == 't') {
558 input_stack::get_char();
559 context_buffer = ".height";
560 return DOT_HT;
562 input_stack::push_back('h');
564 input_stack::push_back('g');
566 input_stack::push_back('i');
568 input_stack::push_back('e');
570 input_stack::push_back('h');
571 return '.';
572 case 'x':
573 input_stack::get_char();
574 context_buffer = ".x";
575 return DOT_X;
576 case 'y':
577 input_stack::get_char();
578 context_buffer = ".y";
579 return DOT_Y;
580 case 'c':
581 input_stack::get_char();
582 c = input_stack::peek_char();
583 if (c == 'e') {
584 input_stack::get_char();
585 c = input_stack::peek_char();
586 if (c == 'n') {
587 input_stack::get_char();
588 c = input_stack::peek_char();
589 if (c == 't') {
590 input_stack::get_char();
591 c = input_stack::peek_char();
592 if (c == 'e') {
593 input_stack::get_char();
594 c = input_stack::peek_char();
595 if (c == 'r') {
596 input_stack::get_char();
597 context_buffer = ".center";
598 return DOT_C;
600 input_stack::push_back('e');
602 input_stack::push_back('t');
604 input_stack::push_back('n');
606 input_stack::push_back('e');
608 context_buffer = ".c";
609 return DOT_C;
610 case 'n':
611 input_stack::get_char();
612 c = input_stack::peek_char();
613 if (c == 'e') {
614 input_stack::get_char();
615 context_buffer = ".ne";
616 return DOT_NE;
618 else if (c == 'w') {
619 input_stack::get_char();
620 context_buffer = ".nw";
621 return DOT_NW;
623 else {
624 context_buffer = ".n";
625 return DOT_N;
627 break;
628 case 'e':
629 input_stack::get_char();
630 c = input_stack::peek_char();
631 if (c == 'n') {
632 input_stack::get_char();
633 c = input_stack::peek_char();
634 if (c == 'd') {
635 input_stack::get_char();
636 context_buffer = ".end";
637 return DOT_END;
639 input_stack::push_back('n');
640 context_buffer = ".e";
641 return DOT_E;
643 context_buffer = ".e";
644 return DOT_E;
645 case 'w':
646 input_stack::get_char();
647 c = input_stack::peek_char();
648 if (c == 'i') {
649 input_stack::get_char();
650 c = input_stack::peek_char();
651 if (c == 'd') {
652 input_stack::get_char();
653 c = input_stack::peek_char();
654 if (c == 't') {
655 input_stack::get_char();
656 c = input_stack::peek_char();
657 if (c == 'h') {
658 input_stack::get_char();
659 context_buffer = ".width";
660 return DOT_WID;
662 input_stack::push_back('t');
664 context_buffer = ".wid";
665 return DOT_WID;
667 input_stack::push_back('i');
669 context_buffer = ".w";
670 return DOT_W;
671 case 's':
672 input_stack::get_char();
673 c = input_stack::peek_char();
674 if (c == 'e') {
675 input_stack::get_char();
676 context_buffer = ".se";
677 return DOT_SE;
679 else if (c == 'w') {
680 input_stack::get_char();
681 context_buffer = ".sw";
682 return DOT_SW;
684 else {
685 if (c == 't') {
686 input_stack::get_char();
687 c = input_stack::peek_char();
688 if (c == 'a') {
689 input_stack::get_char();
690 c = input_stack::peek_char();
691 if (c == 'r') {
692 input_stack::get_char();
693 c = input_stack::peek_char();
694 if (c == 't') {
695 input_stack::get_char();
696 context_buffer = ".start";
697 return DOT_START;
699 input_stack::push_back('r');
701 input_stack::push_back('a');
703 input_stack::push_back('t');
705 context_buffer = ".s";
706 return DOT_S;
708 break;
709 case 't':
710 input_stack::get_char();
711 c = input_stack::peek_char();
712 if (c == 'o') {
713 input_stack::get_char();
714 c = input_stack::peek_char();
715 if (c == 'p') {
716 input_stack::get_char();
717 context_buffer = ".top";
718 return DOT_N;
720 input_stack::push_back('o');
722 context_buffer = ".t";
723 return DOT_N;
724 case 'l':
725 input_stack::get_char();
726 c = input_stack::peek_char();
727 if (c == 'e') {
728 input_stack::get_char();
729 c = input_stack::peek_char();
730 if (c == 'f') {
731 input_stack::get_char();
732 c = input_stack::peek_char();
733 if (c == 't') {
734 input_stack::get_char();
735 context_buffer = ".left";
737 input_stack::push_back('f');
739 input_stack::push_back('e');
741 context_buffer = ".l";
742 return DOT_W;
743 case 'r':
744 input_stack::get_char();
745 c = input_stack::peek_char();
746 if (c == 'a') {
747 input_stack::get_char();
748 c = input_stack::peek_char();
749 if (c == 'd') {
750 input_stack::get_char();
751 context_buffer = ".rad";
752 return DOT_RAD;
754 input_stack::push_back('a');
756 else if (c == 'i') {
757 input_stack::get_char();
758 c = input_stack::peek_char();
759 if (c == 'g') {
760 input_stack::get_char();
761 c = input_stack::peek_char();
762 if (c == 'h') {
763 input_stack::get_char();
764 c = input_stack::peek_char();
765 if (c == 't') {
766 input_stack::get_char();
767 context_buffer = ".right";
769 input_stack::push_back('h');
771 input_stack::push_back('g');
773 input_stack::push_back('i');
775 context_buffer = ".r";
776 return DOT_E;
777 case 'b':
778 input_stack::get_char();
779 c = input_stack::peek_char();
780 if (c == 'o') {
781 input_stack::get_char();
782 c = input_stack::peek_char();
783 if (c == 't') {
784 input_stack::get_char();
785 c = input_stack::peek_char();
786 if (c == 't') {
787 input_stack::get_char();
788 c = input_stack::peek_char();
789 if (c == 'o') {
790 input_stack::get_char();
791 c = input_stack::peek_char();
792 if (c == 'm') {
793 input_stack::get_char();
794 context_buffer = ".bottom";
795 return DOT_S;
797 input_stack::push_back('o');
799 input_stack::push_back('t');
801 context_buffer = ".bot";
802 return DOT_S;
804 input_stack::push_back('o');
806 context_buffer = ".b";
807 return DOT_S;
808 default:
809 context_buffer = '.';
810 return '.';
814 int get_token(int lookup_flag)
816 context_buffer.clear();
817 for (;;) {
818 int n = 0;
819 int bol = input_stack::bol();
820 int c = input_stack::get_char();
821 if (bol && c == command_char) {
822 token_buffer.clear();
823 token_buffer += c;
824 // the newline is not part of the token
825 for (;;) {
826 c = input_stack::peek_char();
827 if (c == EOF || c == '\n')
828 break;
829 input_stack::get_char();
830 token_buffer += char(c);
832 context_buffer = token_buffer;
833 return COMMAND_LINE;
835 switch (c) {
836 case EOF:
837 return EOF;
838 case ' ':
839 case '\t':
840 break;
841 case '\\':
843 int d = input_stack::peek_char();
844 if (d != '\n') {
845 context_buffer = '\\';
846 return '\\';
848 input_stack::get_char();
849 break;
851 case '#':
852 do {
853 c = input_stack::get_char();
854 } while (c != '\n' && c != EOF);
855 if (c == '\n')
856 context_buffer = '\n';
857 return c;
858 case '"':
859 context_buffer = '"';
860 token_buffer.clear();
861 for (;;) {
862 c = input_stack::get_char();
863 if (c == '\\') {
864 context_buffer += '\\';
865 c = input_stack::peek_char();
866 if (c == '"') {
867 input_stack::get_char();
868 token_buffer += '"';
869 context_buffer += '"';
871 else
872 token_buffer += '\\';
874 else if (c == '\n') {
875 error("newline in string");
876 break;
878 else if (c == EOF) {
879 error("missing `\"'");
880 break;
882 else if (c == '"') {
883 context_buffer += '"';
884 break;
886 else {
887 context_buffer += char(c);
888 token_buffer += char(c);
891 return TEXT;
892 case '0':
893 case '1':
894 case '2':
895 case '3':
896 case '4':
897 case '5':
898 case '6':
899 case '7':
900 case '8':
901 case '9':
903 int overflow = 0;
904 n = 0;
905 for (;;) {
906 if (n > (INT_MAX - 9)/10) {
907 overflow = 1;
908 break;
910 n *= 10;
911 n += c - '0';
912 context_buffer += char(c);
913 c = input_stack::peek_char();
914 if (c == EOF || !csdigit(c))
915 break;
916 c = input_stack::get_char();
918 token_double = n;
919 if (overflow) {
920 for (;;) {
921 token_double *= 10.0;
922 token_double += c - '0';
923 context_buffer += char(c);
924 c = input_stack::peek_char();
925 if (c == EOF || !csdigit(c))
926 break;
927 c = input_stack::get_char();
929 // if somebody asks for 1000000000000th, we will silently
930 // give them INT_MAXth
931 double temp = token_double; // work around gas 1.34/sparc bug
932 if (token_double > INT_MAX)
933 n = INT_MAX;
934 else
935 n = int(temp);
938 switch (c) {
939 case 'i':
940 case 'I':
941 context_buffer += char(c);
942 input_stack::get_char();
943 return NUMBER;
944 case '.':
946 context_buffer += '.';
947 input_stack::get_char();
948 got_dot:
949 double factor = 1.0;
950 for (;;) {
951 c = input_stack::peek_char();
952 if (!c == EOF || !csdigit(c))
953 break;
954 input_stack::get_char();
955 context_buffer += char(c);
956 factor /= 10.0;
957 if (c != '0')
958 token_double += factor*(c - '0');
960 if (c != 'e' && c != 'E') {
961 if (c == 'i' || c == 'I') {
962 context_buffer += char(c);
963 input_stack::get_char();
965 return NUMBER;
968 // fall through
969 case 'e':
970 case 'E':
972 int echar = c;
973 input_stack::get_char();
974 c = input_stack::peek_char();
975 int sign = '+';
976 if (c == '+' || c == '-') {
977 sign = c;
978 input_stack::get_char();
979 c = input_stack::peek_char();
980 if (c == EOF || !csdigit(c)) {
981 input_stack::push_back(sign);
982 input_stack::push_back(echar);
983 return NUMBER;
985 context_buffer += char(echar);
986 context_buffer += char(sign);
988 else {
989 if (c == EOF || !csdigit(c)) {
990 input_stack::push_back(echar);
991 return NUMBER;
993 context_buffer += char(echar);
995 input_stack::get_char();
996 context_buffer += char(c);
997 n = c - '0';
998 for (;;) {
999 c = input_stack::peek_char();
1000 if (c == EOF || !csdigit(c))
1001 break;
1002 input_stack::get_char();
1003 context_buffer += char(c);
1004 n = n*10 + (c - '0');
1006 if (sign == '-')
1007 n = -n;
1008 if (c == 'i' || c == 'I') {
1009 context_buffer += char(c);
1010 input_stack::get_char();
1012 token_double *= pow(10.0, n);
1013 return NUMBER;
1015 case 'n':
1016 input_stack::get_char();
1017 c = input_stack::peek_char();
1018 if (c == 'd') {
1019 input_stack::get_char();
1020 token_int = n;
1021 context_buffer += "nd";
1022 return ORDINAL;
1024 input_stack::push_back('n');
1025 return NUMBER;
1026 case 'r':
1027 input_stack::get_char();
1028 c = input_stack::peek_char();
1029 if (c == 'd') {
1030 input_stack::get_char();
1031 token_int = n;
1032 context_buffer += "rd";
1033 return ORDINAL;
1035 input_stack::push_back('r');
1036 return NUMBER;
1037 case 't':
1038 input_stack::get_char();
1039 c = input_stack::peek_char();
1040 if (c == 'h') {
1041 input_stack::get_char();
1042 token_int = n;
1043 context_buffer += "th";
1044 return ORDINAL;
1046 input_stack::push_back('t');
1047 return NUMBER;
1048 case 's':
1049 input_stack::get_char();
1050 c = input_stack::peek_char();
1051 if (c == 't') {
1052 input_stack::get_char();
1053 token_int = n;
1054 context_buffer += "st";
1055 return ORDINAL;
1057 input_stack::push_back('s');
1058 return NUMBER;
1059 default:
1060 return NUMBER;
1062 break;
1063 case '\'':
1065 c = input_stack::peek_char();
1066 if (c == 't') {
1067 input_stack::get_char();
1068 c = input_stack::peek_char();
1069 if (c == 'h') {
1070 input_stack::get_char();
1071 context_buffer = "'th";
1072 return TH;
1074 else
1075 input_stack::push_back('t');
1077 context_buffer = "'";
1078 return '\'';
1080 case '.':
1082 c = input_stack::peek_char();
1083 if (c != EOF && csdigit(c)) {
1084 n = 0;
1085 token_double = 0.0;
1086 context_buffer = '.';
1087 goto got_dot;
1089 return get_token_after_dot(c);
1091 case '<':
1092 c = input_stack::peek_char();
1093 if (c == '-') {
1094 input_stack::get_char();
1095 c = input_stack::peek_char();
1096 if (c == '>') {
1097 input_stack::get_char();
1098 context_buffer = "<->";
1099 return DOUBLE_ARROW_HEAD;
1101 context_buffer = "<-";
1102 return LEFT_ARROW_HEAD;
1104 else if (c == '=') {
1105 input_stack::get_char();
1106 context_buffer = "<=";
1107 return LESSEQUAL;
1109 context_buffer = "<";
1110 return '<';
1111 case '-':
1112 c = input_stack::peek_char();
1113 if (c == '>') {
1114 input_stack::get_char();
1115 context_buffer = "->";
1116 return RIGHT_ARROW_HEAD;
1118 context_buffer = "-";
1119 return '-';
1120 case '!':
1121 c = input_stack::peek_char();
1122 if (c == '=') {
1123 input_stack::get_char();
1124 context_buffer = "!=";
1125 return NOTEQUAL;
1127 context_buffer = "!";
1128 return '!';
1129 case '>':
1130 c = input_stack::peek_char();
1131 if (c == '=') {
1132 input_stack::get_char();
1133 context_buffer = ">=";
1134 return GREATEREQUAL;
1136 context_buffer = ">";
1137 return '>';
1138 case '=':
1139 c = input_stack::peek_char();
1140 if (c == '=') {
1141 input_stack::get_char();
1142 context_buffer = "==";
1143 return EQUALEQUAL;
1145 context_buffer = "=";
1146 return '=';
1147 case '&':
1148 c = input_stack::peek_char();
1149 if (c == '&') {
1150 input_stack::get_char();
1151 context_buffer = "&&";
1152 return ANDAND;
1154 context_buffer = "&";
1155 return '&';
1156 case '|':
1157 c = input_stack::peek_char();
1158 if (c == '|') {
1159 input_stack::get_char();
1160 context_buffer = "||";
1161 return OROR;
1163 context_buffer = "|";
1164 return '|';
1165 default:
1166 if (c != EOF && csalpha(c)) {
1167 token_buffer.clear();
1168 token_buffer = c;
1169 for (;;) {
1170 c = input_stack::peek_char();
1171 if (c == EOF || (!csalnum(c) && c != '_'))
1172 break;
1173 input_stack::get_char();
1174 token_buffer += char(c);
1176 int tok = lookup_keyword(token_buffer.contents(),
1177 token_buffer.length());
1178 if (tok != 0) {
1179 context_buffer = token_buffer;
1180 return tok;
1182 char *def = 0;
1183 if (lookup_flag) {
1184 token_buffer += '\0';
1185 def = macro_table.lookup(token_buffer.contents());
1186 token_buffer.set_length(token_buffer.length() - 1);
1187 if (def) {
1188 if (c == '(') {
1189 input_stack::get_char();
1190 interpolate_macro_with_args(def);
1192 else
1193 input_stack::push(new macro_input(def));
1196 if (!def) {
1197 context_buffer = token_buffer;
1198 if (csupper(token_buffer[0]))
1199 return LABEL;
1200 else
1201 return VARIABLE;
1204 else {
1205 context_buffer = char(c);
1206 return (unsigned char)c;
1208 break;
1213 int get_delimited()
1215 token_buffer.clear();
1216 int c = input_stack::get_char();
1217 while (c == ' ' || c == '\t' || c == '\n')
1218 c = input_stack::get_char();
1219 if (c == EOF) {
1220 lex_error("missing delimiter");
1221 return 0;
1223 context_buffer = char(c);
1224 int had_newline = 0;
1225 int start = c;
1226 int level = 0;
1227 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1228 for (;;) {
1229 c = input_stack::get_char();
1230 if (c == EOF) {
1231 lex_error("missing closing delimiter");
1232 return 0;
1234 if (c == '\n')
1235 had_newline = 1;
1236 else if (!had_newline)
1237 context_buffer += char(c);
1238 switch (state) {
1239 case NORMAL:
1240 if (start == '{') {
1241 if (c == '{') {
1242 level++;
1243 break;
1245 if (c == '}') {
1246 if (--level < 0)
1247 state = DELIM_END;
1248 break;
1251 else {
1252 if (c == start) {
1253 state = DELIM_END;
1254 break;
1257 if (c == '"')
1258 state = IN_STRING;
1259 break;
1260 case IN_STRING_QUOTED:
1261 if (c == '\n')
1262 state = NORMAL;
1263 else
1264 state = IN_STRING;
1265 break;
1266 case IN_STRING:
1267 if (c == '"' || c == '\n')
1268 state = NORMAL;
1269 else if (c == '\\')
1270 state = IN_STRING_QUOTED;
1271 break;
1272 case DELIM_END:
1273 // This case it just to shut cfront 2.0 up.
1274 default:
1275 assert(0);
1277 if (state == DELIM_END)
1278 break;
1279 token_buffer += c;
1281 return 1;
1284 void do_define()
1286 int t = get_token(0); // do not expand what we are defining
1287 if (t != VARIABLE && t != LABEL) {
1288 lex_error("can only define variable or placename");
1289 return;
1291 token_buffer += '\0';
1292 string nm = token_buffer;
1293 const char *name = nm.contents();
1294 if (!get_delimited())
1295 return;
1296 token_buffer += '\0';
1297 macro_table.define(name, strsave(token_buffer.contents()));
1300 void do_undef()
1302 int t = get_token(0); // do not expand what we are undefining
1303 if (t != VARIABLE && t != LABEL) {
1304 lex_error("can only define variable or placename");
1305 return;
1307 token_buffer += '\0';
1308 macro_table.define(token_buffer.contents(), 0);
1312 class for_input : public input {
1313 char *var;
1314 char *body;
1315 double to;
1316 int by_is_multiplicative;
1317 double by;
1318 const char *p;
1319 int done_newline;
1320 public:
1321 for_input(char *, double, int, double, char *);
1322 ~for_input();
1323 int get();
1324 int peek();
1327 for_input::for_input(char *vr, double t, int bim, double b, char *bd)
1328 : var(vr), to(t), by_is_multiplicative(bim), by(b), body(bd), p(body),
1329 done_newline(0)
1333 for_input::~for_input()
1335 a_delete var;
1336 a_delete body;
1339 int for_input::get()
1341 if (p == 0)
1342 return EOF;
1343 for (;;) {
1344 if (*p != '\0')
1345 return (unsigned char)*p++;
1346 if (!done_newline) {
1347 done_newline = 1;
1348 return '\n';
1350 double val;
1351 if (!lookup_variable(var, &val)) {
1352 lex_error("body of `for' terminated enclosing block");
1353 return EOF;
1355 if (by_is_multiplicative)
1356 val *= by;
1357 else
1358 val += by;
1359 define_variable(var, val);
1360 if (val > to) {
1361 p = 0;
1362 return EOF;
1364 p = body;
1365 done_newline = 0;
1369 int for_input::peek()
1371 if (p == 0)
1372 return EOF;
1373 if (*p != '\0')
1374 return (unsigned char)*p;
1375 if (!done_newline)
1376 return '\n';
1377 double val;
1378 if (!lookup_variable(var, &val))
1379 return EOF;
1380 if (by_is_multiplicative) {
1381 if (val * by > to)
1382 return EOF;
1384 else {
1385 if (val + by > to)
1386 return EOF;
1388 if (*body == '\0')
1389 return EOF;
1390 return (unsigned char)*body;
1393 void do_for(char *var, double from, double to, int by_is_multiplicative,
1394 double by, char *body)
1396 define_variable(var, from);
1397 if (from <= to)
1398 input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
1402 void do_copy(const char *filename)
1404 errno = 0;
1405 FILE *fp = fopen(filename, "r");
1406 if (fp == 0) {
1407 lex_error("can't open `%1': %2", filename, strerror(errno));
1408 return;
1410 input_stack::push(new file_input(fp, filename));
1413 class copy_thru_input : public input {
1414 int done;
1415 char *body;
1416 char *until;
1417 const char *p;
1418 const char *ap;
1419 int argv[9];
1420 int argc;
1421 string line;
1422 int get_line();
1423 virtual int inget() = 0;
1424 public:
1425 copy_thru_input(const char *b, const char *u);
1426 ~copy_thru_input();
1427 int get();
1428 int peek();
1431 class copy_file_thru_input : public copy_thru_input {
1432 input *in;
1433 public:
1434 copy_file_thru_input(input *, const char *b, const char *u);
1435 ~copy_file_thru_input();
1436 int inget();
1439 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1440 const char *u)
1441 : in(i), copy_thru_input(b, u)
1445 copy_file_thru_input::~copy_file_thru_input()
1447 delete in;
1450 int copy_file_thru_input::inget()
1452 if (!in)
1453 return EOF;
1454 else
1455 return in->get();
1458 class copy_rest_thru_input : public copy_thru_input {
1459 public:
1460 copy_rest_thru_input(const char *, const char *u);
1461 int inget();
1464 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1465 : copy_thru_input(b, u)
1469 int copy_rest_thru_input::inget()
1471 while (next != 0) {
1472 int c = next->get();
1473 if (c != EOF)
1474 return c;
1475 if (next->next == 0)
1476 return EOF;
1477 input *tem = next;
1478 next = next->next;
1479 delete tem;
1481 return EOF;
1485 copy_thru_input::copy_thru_input(const char *b, const char *u)
1486 : done(0)
1488 ap = 0;
1489 body = process_body(b);
1490 p = 0;
1491 until = strsave(u);
1495 copy_thru_input::~copy_thru_input()
1497 a_delete body;
1498 a_delete until;
1501 int copy_thru_input::get()
1503 if (ap) {
1504 if (*ap != '\0')
1505 return (unsigned char)*ap++;
1506 ap = 0;
1508 for (;;) {
1509 if (p == 0) {
1510 if (!get_line())
1511 break;
1512 p = body;
1514 if (*p == '\0') {
1515 p = 0;
1516 return '\n';
1518 while (*p >= ARG1 && *p <= ARG1 + 8) {
1519 int i = *p++ - ARG1;
1520 if (i < argc && line[argv[i]] != '\0') {
1521 ap = line.contents() + argv[i];
1522 return (unsigned char)*ap++;
1525 if (*p != '\0')
1526 return (unsigned char)*p++;
1528 return EOF;
1531 int copy_thru_input::peek()
1533 if (ap) {
1534 if (*ap != '\0')
1535 return (unsigned char)*ap;
1536 ap = 0;
1538 for (;;) {
1539 if (p == 0) {
1540 if (!get_line())
1541 break;
1542 p = body;
1544 if (*p == '\0')
1545 return '\n';
1546 while (*p >= ARG1 && *p <= ARG1 + 8) {
1547 int i = *p++ - ARG1;
1548 if (i < argc && line[argv[i]] != '\0') {
1549 ap = line.contents() + argv[i];
1550 return (unsigned char)*ap;
1553 if (*p != '\0')
1554 return (unsigned char)*p;
1556 return EOF;
1559 int copy_thru_input::get_line()
1561 if (done)
1562 return 0;
1563 line.clear();
1564 argc = 0;
1565 int c = inget();
1566 for (;;) {
1567 while (c == ' ')
1568 c = inget();
1569 if (c == EOF || c == '\n')
1570 break;
1571 if (argc == 9) {
1572 do {
1573 c = inget();
1574 } while (c != '\n' && c != EOF);
1575 break;
1577 argv[argc++] = line.length();
1578 do {
1579 line += char(c);
1580 c = inget();
1581 } while (c != ' ' && c != '\n');
1582 line += '\0';
1584 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1585 done = 1;
1586 return 0;
1588 return argc > 0 || c == '\n';
1591 class simple_file_input : public input {
1592 const char *filename;
1593 int lineno;
1594 FILE *fp;
1595 public:
1596 simple_file_input(FILE *, const char *);
1597 ~simple_file_input();
1598 int get();
1599 int peek();
1600 int get_location(const char **, int *);
1603 simple_file_input::simple_file_input(FILE *p, const char *s)
1604 : filename(s), fp(p), lineno(1)
1608 simple_file_input::~simple_file_input()
1610 // don't delete the filename
1611 fclose(fp);
1614 int simple_file_input::get()
1616 int c = getc(fp);
1617 while (illegal_input_char(c)) {
1618 error("illegal input character code %1", c);
1619 c = getc(fp);
1621 if (c == '\n')
1622 lineno++;
1623 return c;
1626 int simple_file_input::peek()
1628 int c = getc(fp);
1629 while (illegal_input_char(c)) {
1630 error("illegal input character code %1", c);
1631 c = getc(fp);
1633 if (c != EOF)
1634 ungetc(c, fp);
1635 return c;
1638 int simple_file_input::get_location(const char **fnp, int *lnp)
1640 *fnp = filename;
1641 *lnp = lineno;
1642 return 1;
1646 void copy_file_thru(const char *filename, const char *body, const char *until)
1648 errno = 0;
1649 FILE *fp = fopen(filename, "r");
1650 if (fp == 0) {
1651 lex_error("can't open `%1': %2", filename, strerror(errno));
1652 return;
1654 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1655 body, until);
1656 input_stack::push(in);
1659 void copy_rest_thru(const char *body, const char *until)
1661 input_stack::push(new copy_rest_thru_input(body, until));
1664 void push_body(const char *s)
1666 input_stack::push(new char_input('\n'));
1667 input_stack::push(new macro_input(s));
1670 int delim_flag = 0;
1672 char *get_thru_arg()
1674 int c = input_stack::peek_char();
1675 while (c == ' ') {
1676 input_stack::get_char();
1677 c = input_stack::peek_char();
1679 if (c != EOF && csalpha(c)) {
1680 // looks like a macro
1681 input_stack::get_char();
1682 token_buffer = c;
1683 for (;;) {
1684 c = input_stack::peek_char();
1685 if (c == EOF || (!csalnum(c) && c != '_'))
1686 break;
1687 input_stack::get_char();
1688 token_buffer += char(c);
1690 context_buffer = token_buffer;
1691 token_buffer += '\0';
1692 char *def = macro_table.lookup(token_buffer.contents());
1693 if (def)
1694 return strsave(def);
1695 // I guess it wasn't a macro after all; so push the macro name back.
1696 // -2 because we added a '\0'
1697 for (int i = token_buffer.length() - 2; i >= 0; i--)
1698 input_stack::push_back(token_buffer[i]);
1700 if (get_delimited()) {
1701 token_buffer += '\0';
1702 return strsave(token_buffer.contents());
1704 else
1705 return 0;
1708 int lookahead_token = -1;
1709 string old_context_buffer;
1711 void do_lookahead()
1713 if (lookahead_token == -1) {
1714 old_context_buffer = context_buffer;
1715 lookahead_token = get_token(1);
1719 int yylex()
1721 if (delim_flag) {
1722 assert(lookahead_token == -1);
1723 if (delim_flag == 2) {
1724 if ((yylval.str = get_thru_arg()) != 0)
1725 return DELIMITED;
1726 else
1727 return 0;
1729 else {
1730 if (get_delimited()) {
1731 token_buffer += '\0';
1732 yylval.str = strsave(token_buffer.contents());
1733 return DELIMITED;
1735 else
1736 return 0;
1739 for (;;) {
1740 int t;
1741 if (lookahead_token >= 0) {
1742 t = lookahead_token;
1743 lookahead_token = -1;
1745 else
1746 t = get_token(1);
1747 switch (t) {
1748 case '\n':
1749 return ';';
1750 case EOF:
1751 return 0;
1752 case DEFINE:
1753 do_define();
1754 break;
1755 case UNDEF:
1756 do_undef();
1757 break;
1758 case ORDINAL:
1759 yylval.n = token_int;
1760 return t;
1761 case NUMBER:
1762 yylval.x = token_double;
1763 return t;
1764 case COMMAND_LINE:
1765 case TEXT:
1766 token_buffer += '\0';
1767 if (!input_stack::get_location(&yylval.lstr.filename,
1768 &yylval.lstr.lineno)) {
1769 yylval.lstr.filename = 0;
1770 yylval.lstr.lineno = -1;
1772 yylval.lstr.str = strsave(token_buffer.contents());
1773 return t;
1774 case LABEL:
1775 case VARIABLE:
1776 token_buffer += '\0';
1777 yylval.str = strsave(token_buffer.contents());
1778 return t;
1779 case LEFT:
1780 // change LEFT to LEFT_CORNER when followed by OF
1781 old_context_buffer = context_buffer;
1782 lookahead_token = get_token(1);
1783 if (lookahead_token == OF)
1784 return LEFT_CORNER;
1785 else
1786 return t;
1787 case RIGHT:
1788 // change RIGHT to RIGHT_CORNER when followed by OF
1789 old_context_buffer = context_buffer;
1790 lookahead_token = get_token(1);
1791 if (lookahead_token == OF)
1792 return RIGHT_CORNER;
1793 else
1794 return t;
1795 case UPPER:
1796 // recognise UPPER only before LEFT or RIGHT
1797 old_context_buffer = context_buffer;
1798 lookahead_token = get_token(1);
1799 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1800 yylval.str = strsave("upper");
1801 return VARIABLE;
1803 else
1804 return t;
1805 case LOWER:
1806 // recognise LOWER only before LEFT or RIGHT
1807 old_context_buffer = context_buffer;
1808 lookahead_token = get_token(1);
1809 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1810 yylval.str = strsave("lower");
1811 return VARIABLE;
1813 else
1814 return t;
1815 case TOP:
1816 // recognise TOP only before OF
1817 old_context_buffer = context_buffer;
1818 lookahead_token = get_token(1);
1819 if (lookahead_token != OF) {
1820 yylval.str = strsave("top");
1821 return VARIABLE;
1823 else
1824 return t;
1825 case BOTTOM:
1826 // recognise BOTTOM only before OF
1827 old_context_buffer = context_buffer;
1828 lookahead_token = get_token(1);
1829 if (lookahead_token != OF) {
1830 yylval.str = strsave("bottom");
1831 return VARIABLE;
1833 else
1834 return t;
1835 case CENTER:
1836 // recognise CENTER only before OF
1837 old_context_buffer = context_buffer;
1838 lookahead_token = get_token(1);
1839 if (lookahead_token != OF) {
1840 yylval.str = strsave("center");
1841 return VARIABLE;
1843 else
1844 return t;
1845 case START:
1846 // recognise START only before OF
1847 old_context_buffer = context_buffer;
1848 lookahead_token = get_token(1);
1849 if (lookahead_token != OF) {
1850 yylval.str = strsave("start");
1851 return VARIABLE;
1853 else
1854 return t;
1855 case END:
1856 // recognise END only before OF
1857 old_context_buffer = context_buffer;
1858 lookahead_token = get_token(1);
1859 if (lookahead_token != OF) {
1860 yylval.str = strsave("end");
1861 return VARIABLE;
1863 else
1864 return t;
1865 default:
1866 return t;
1871 void lex_error(const char *message,
1872 const errarg &arg1,
1873 const errarg &arg2,
1874 const errarg &arg3)
1876 const char *filename;
1877 int lineno;
1878 if (!input_stack::get_location(&filename, &lineno))
1879 error(message, arg1, arg2, arg3);
1880 else
1881 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1884 void lex_warning(const char *message,
1885 const errarg &arg1,
1886 const errarg &arg2,
1887 const errarg &arg3)
1889 const char *filename;
1890 int lineno;
1891 if (!input_stack::get_location(&filename, &lineno))
1892 warning(message, arg1, arg2, arg3);
1893 else
1894 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1897 void yyerror(const char *s)
1899 const char *filename;
1900 int lineno;
1901 const char *context = 0;
1902 if (lookahead_token == -1) {
1903 if (context_buffer.length() > 0) {
1904 context_buffer += '\0';
1905 context = context_buffer.contents();
1908 else {
1909 if (old_context_buffer.length() > 0) {
1910 old_context_buffer += '\0';
1911 context = old_context_buffer.contents();
1914 if (!input_stack::get_location(&filename, &lineno)) {
1915 if (context) {
1916 if (context[0] == '\n' && context[1] == '\0')
1917 error("%1 before newline", s);
1918 else
1919 error("%1 before `%2'", s, context);
1921 else
1922 error("%1 at end of picture", s);
1924 else {
1925 if (context) {
1926 if (context[0] == '\n' && context[1] == '\0')
1927 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1928 else
1929 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1930 s, context);
1932 else
1933 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);