* src/preproc/pic/lex.cc (get_token): Fix typo.
[s-roff.git] / src / preproc / pic / lex.cc
blob39f60228a0487511ee86a6edb4f4b11bc7017e7f
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 : fp(f), filename(fn), lineno(0), ptr("")
72 file_input::~file_input()
74 fclose(fp);
77 int file_input::read_line()
79 for (;;) {
80 line.clear();
81 lineno++;
82 for (;;) {
83 int c = getc(fp);
84 if (c == EOF)
85 break;
86 else if (illegal_input_char(c))
87 lex_error("illegal input character code %1", c);
88 else {
89 line += char(c);
90 if (c == '\n')
91 break;
94 if (line.length() == 0)
95 return 0;
96 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
97 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
98 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
99 || compatible_flag))) {
100 line += '\0';
101 ptr = line.contents();
102 return 1;
107 int file_input::get()
109 if (*ptr != '\0' || read_line())
110 return (unsigned char)*ptr++;
111 else
112 return EOF;
115 int file_input::peek()
117 if (*ptr != '\0' || read_line())
118 return (unsigned char)*ptr;
119 else
120 return EOF;
123 int file_input::get_location(const char **fnp, int *lnp)
125 *fnp = filename;
126 *lnp = lineno;
127 return 1;
130 macro_input::macro_input(const char *str)
132 p = s = strsave(str);
135 macro_input::~macro_input()
137 a_delete s;
140 int macro_input::get()
142 if (p == 0 || *p == '\0')
143 return EOF;
144 else
145 return (unsigned char)*p++;
148 int macro_input::peek()
150 if (p == 0 || *p == '\0')
151 return EOF;
152 else
153 return (unsigned char)*p;
156 // Character representing $1. Must be illegal input character.
157 #define ARG1 14
159 char *process_body(const char *body)
161 char *s = strsave(body);
162 int j = 0;
163 for (int i = 0; s[i] != '\0'; i++)
164 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
165 if (s[i+1] != '0')
166 s[j++] = ARG1 + s[++i] - '1';
168 else
169 s[j++] = s[i];
170 s[j] = '\0';
171 return s;
175 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
176 : ap(0), argc(ac)
178 for (int i = 0; i < argc; i++)
179 argv[i] = av[i];
180 p = s = process_body(body);
184 argument_macro_input::~argument_macro_input()
186 for (int i = 0; i < argc; i++)
187 a_delete argv[i];
188 a_delete s;
191 int argument_macro_input::get()
193 if (ap) {
194 if (*ap != '\0')
195 return (unsigned char)*ap++;
196 ap = 0;
198 if (p == 0)
199 return EOF;
200 while (*p >= ARG1 && *p <= ARG1 + 8) {
201 int i = *p++ - ARG1;
202 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
203 ap = argv[i];
204 return (unsigned char)*ap++;
207 if (*p == '\0')
208 return EOF;
209 return (unsigned char)*p++;
212 int argument_macro_input::peek()
214 if (ap) {
215 if (*ap != '\0')
216 return (unsigned char)*ap;
217 ap = 0;
219 if (p == 0)
220 return EOF;
221 while (*p >= ARG1 && *p <= ARG1 + 8) {
222 int i = *p++ - ARG1;
223 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
224 ap = argv[i];
225 return (unsigned char)*ap;
228 if (*p == '\0')
229 return EOF;
230 return (unsigned char)*p;
233 class input_stack {
234 static input *current_input;
235 static int bol_flag;
236 public:
237 static void push(input *);
238 static void clear();
239 static int get_char();
240 static int peek_char();
241 static int get_location(const char **fnp, int *lnp);
242 static void push_back(unsigned char c, int was_bol = 0);
243 static int bol();
246 input *input_stack::current_input = 0;
247 int input_stack::bol_flag = 0;
249 inline int input_stack::bol()
251 return bol_flag;
254 void input_stack::clear()
256 while (current_input != 0) {
257 input *tem = current_input;
258 current_input = current_input->next;
259 delete tem;
261 bol_flag = 1;
264 void input_stack::push(input *in)
266 in->next = current_input;
267 current_input = in;
270 void lex_init(input *top)
272 input_stack::clear();
273 input_stack::push(top);
276 void lex_cleanup()
278 while (input_stack::get_char() != EOF)
282 int input_stack::get_char()
284 while (current_input != 0) {
285 int c = current_input->get();
286 if (c != EOF) {
287 bol_flag = c == '\n';
288 return c;
290 // don't pop the top-level input off the stack
291 if (current_input->next == 0)
292 return EOF;
293 input *tem = current_input;
294 current_input = current_input->next;
295 delete tem;
297 return EOF;
300 int input_stack::peek_char()
302 while (current_input != 0) {
303 int c = current_input->peek();
304 if (c != EOF)
305 return c;
306 if (current_input->next == 0)
307 return EOF;
308 input *tem = current_input;
309 current_input = current_input->next;
310 delete tem;
312 return EOF;
315 class char_input : public input {
316 int c;
317 public:
318 char_input(int);
319 int get();
320 int peek();
323 char_input::char_input(int n) : c((unsigned char)n)
327 int char_input::get()
329 int n = c;
330 c = EOF;
331 return n;
334 int char_input::peek()
336 return c;
339 void input_stack::push_back(unsigned char c, int was_bol)
341 push(new char_input(c));
342 bol_flag = was_bol;
345 int input_stack::get_location(const char **fnp, int *lnp)
347 for (input *p = current_input; p; p = p->next)
348 if (p->get_location(fnp, lnp))
349 return 1;
350 return 0;
353 string context_buffer;
355 string token_buffer;
356 double token_double;
357 int token_int;
359 void interpolate_macro_with_args(const char *body)
361 char *argv[9];
362 int argc = 0;
363 int i;
364 for (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 { "color", COLORED },
450 { "colored", COLORED },
451 { "colour", COLORED },
452 { "coloured", COLORED },
453 { "command", COMMAND },
454 { "copy", COPY },
455 { "cos", COS },
456 { "cw", CW },
457 { "dashed", DASHED },
458 { "define", DEFINE },
459 { "diam", DIAMETER },
460 { "diameter", DIAMETER },
461 { "do", DO },
462 { "dotted", DOTTED },
463 { "down", DOWN },
464 { "ellipse", ELLIPSE },
465 { "else", ELSE },
466 { "end", END },
467 { "exp", EXP },
468 { "fill", FILL },
469 { "filled", FILL },
470 { "for", FOR },
471 { "from", FROM },
472 { "height", HEIGHT },
473 { "ht", HEIGHT },
474 { "if", IF },
475 { "int", INT },
476 { "invis", INVISIBLE },
477 { "invisible", INVISIBLE },
478 { "last", LAST },
479 { "left", LEFT },
480 { "line", LINE },
481 { "ljust", LJUST },
482 { "log", LOG },
483 { "lower", LOWER },
484 { "max", K_MAX },
485 { "min", K_MIN },
486 { "move", MOVE },
487 { "of", OF },
488 { "outline", OUTLINED },
489 { "outlined", OUTLINED },
490 { "plot", PLOT },
491 { "print", PRINT },
492 { "rad", RADIUS },
493 { "radius", RADIUS },
494 { "rand", RAND },
495 { "reset", RESET },
496 { "right", RIGHT },
497 { "rjust", RJUST },
498 { "same", SAME },
499 { "sh", SH },
500 { "shaded", SHADED },
501 { "sin", SIN },
502 { "solid", SOLID },
503 { "spline", SPLINE },
504 { "sprintf", SPRINTF },
505 { "sqrt", SQRT },
506 { "srand", SRAND },
507 { "start", START },
508 { "the", THE },
509 { "then", THEN },
510 { "thick", THICKNESS },
511 { "thickness", THICKNESS },
512 { "thru", THRU },
513 { "to", TO },
514 { "top", TOP },
515 { "undef", UNDEF },
516 { "until", UNTIL },
517 { "up", UP },
518 { "upper", UPPER },
519 { "way", WAY },
520 { "wid", WIDTH },
521 { "width", WIDTH },
522 { "with", WITH },
525 const keyword *start = table;
526 const keyword *end = table + sizeof(table)/sizeof(table[0]);
527 while (start < end) {
528 // start <= target < end
529 const keyword *mid = start + (end - start)/2;
531 int cmp = docmp(str, len, mid->name, strlen(mid->name));
532 if (cmp == 0)
533 return mid->token;
534 if (cmp < 0)
535 end = mid;
536 else
537 start = mid + 1;
539 return 0;
542 int get_token_after_dot(int c)
544 // get_token deals with the case where c is a digit
545 switch (c) {
546 case 'h':
547 input_stack::get_char();
548 c = input_stack::peek_char();
549 if (c == 't') {
550 input_stack::get_char();
551 context_buffer = ".ht";
552 return DOT_HT;
554 else if (c == 'e') {
555 input_stack::get_char();
556 c = input_stack::peek_char();
557 if (c == 'i') {
558 input_stack::get_char();
559 c = input_stack::peek_char();
560 if (c == 'g') {
561 input_stack::get_char();
562 c = input_stack::peek_char();
563 if (c == 'h') {
564 input_stack::get_char();
565 c = input_stack::peek_char();
566 if (c == 't') {
567 input_stack::get_char();
568 context_buffer = ".height";
569 return DOT_HT;
571 input_stack::push_back('h');
573 input_stack::push_back('g');
575 input_stack::push_back('i');
577 input_stack::push_back('e');
579 input_stack::push_back('h');
580 return '.';
581 case 'x':
582 input_stack::get_char();
583 context_buffer = ".x";
584 return DOT_X;
585 case 'y':
586 input_stack::get_char();
587 context_buffer = ".y";
588 return DOT_Y;
589 case 'c':
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 == 'n') {
596 input_stack::get_char();
597 c = input_stack::peek_char();
598 if (c == 't') {
599 input_stack::get_char();
600 c = input_stack::peek_char();
601 if (c == 'e') {
602 input_stack::get_char();
603 c = input_stack::peek_char();
604 if (c == 'r') {
605 input_stack::get_char();
606 context_buffer = ".center";
607 return DOT_C;
609 input_stack::push_back('e');
611 input_stack::push_back('t');
613 input_stack::push_back('n');
615 input_stack::push_back('e');
617 context_buffer = ".c";
618 return DOT_C;
619 case 'n':
620 input_stack::get_char();
621 c = input_stack::peek_char();
622 if (c == 'e') {
623 input_stack::get_char();
624 context_buffer = ".ne";
625 return DOT_NE;
627 else if (c == 'w') {
628 input_stack::get_char();
629 context_buffer = ".nw";
630 return DOT_NW;
632 else {
633 context_buffer = ".n";
634 return DOT_N;
636 break;
637 case 'e':
638 input_stack::get_char();
639 c = input_stack::peek_char();
640 if (c == 'n') {
641 input_stack::get_char();
642 c = input_stack::peek_char();
643 if (c == 'd') {
644 input_stack::get_char();
645 context_buffer = ".end";
646 return DOT_END;
648 input_stack::push_back('n');
649 context_buffer = ".e";
650 return DOT_E;
652 context_buffer = ".e";
653 return DOT_E;
654 case 'w':
655 input_stack::get_char();
656 c = input_stack::peek_char();
657 if (c == 'i') {
658 input_stack::get_char();
659 c = input_stack::peek_char();
660 if (c == 'd') {
661 input_stack::get_char();
662 c = input_stack::peek_char();
663 if (c == 't') {
664 input_stack::get_char();
665 c = input_stack::peek_char();
666 if (c == 'h') {
667 input_stack::get_char();
668 context_buffer = ".width";
669 return DOT_WID;
671 input_stack::push_back('t');
673 context_buffer = ".wid";
674 return DOT_WID;
676 input_stack::push_back('i');
678 context_buffer = ".w";
679 return DOT_W;
680 case 's':
681 input_stack::get_char();
682 c = input_stack::peek_char();
683 if (c == 'e') {
684 input_stack::get_char();
685 context_buffer = ".se";
686 return DOT_SE;
688 else if (c == 'w') {
689 input_stack::get_char();
690 context_buffer = ".sw";
691 return DOT_SW;
693 else {
694 if (c == 't') {
695 input_stack::get_char();
696 c = input_stack::peek_char();
697 if (c == 'a') {
698 input_stack::get_char();
699 c = input_stack::peek_char();
700 if (c == 'r') {
701 input_stack::get_char();
702 c = input_stack::peek_char();
703 if (c == 't') {
704 input_stack::get_char();
705 context_buffer = ".start";
706 return DOT_START;
708 input_stack::push_back('r');
710 input_stack::push_back('a');
712 input_stack::push_back('t');
714 context_buffer = ".s";
715 return DOT_S;
717 break;
718 case 't':
719 input_stack::get_char();
720 c = input_stack::peek_char();
721 if (c == 'o') {
722 input_stack::get_char();
723 c = input_stack::peek_char();
724 if (c == 'p') {
725 input_stack::get_char();
726 context_buffer = ".top";
727 return DOT_N;
729 input_stack::push_back('o');
731 context_buffer = ".t";
732 return DOT_N;
733 case 'l':
734 input_stack::get_char();
735 c = input_stack::peek_char();
736 if (c == 'e') {
737 input_stack::get_char();
738 c = input_stack::peek_char();
739 if (c == 'f') {
740 input_stack::get_char();
741 c = input_stack::peek_char();
742 if (c == 't') {
743 input_stack::get_char();
744 context_buffer = ".left";
745 return DOT_W;
747 input_stack::push_back('f');
749 input_stack::push_back('e');
751 context_buffer = ".l";
752 return DOT_W;
753 case 'r':
754 input_stack::get_char();
755 c = input_stack::peek_char();
756 if (c == 'a') {
757 input_stack::get_char();
758 c = input_stack::peek_char();
759 if (c == 'd') {
760 input_stack::get_char();
761 context_buffer = ".rad";
762 return DOT_RAD;
764 input_stack::push_back('a');
766 else if (c == 'i') {
767 input_stack::get_char();
768 c = input_stack::peek_char();
769 if (c == 'g') {
770 input_stack::get_char();
771 c = input_stack::peek_char();
772 if (c == 'h') {
773 input_stack::get_char();
774 c = input_stack::peek_char();
775 if (c == 't') {
776 input_stack::get_char();
777 context_buffer = ".right";
778 return DOT_E;
780 input_stack::push_back('h');
782 input_stack::push_back('g');
784 input_stack::push_back('i');
786 context_buffer = ".r";
787 return DOT_E;
788 case 'b':
789 input_stack::get_char();
790 c = input_stack::peek_char();
791 if (c == 'o') {
792 input_stack::get_char();
793 c = input_stack::peek_char();
794 if (c == 't') {
795 input_stack::get_char();
796 c = input_stack::peek_char();
797 if (c == 't') {
798 input_stack::get_char();
799 c = input_stack::peek_char();
800 if (c == 'o') {
801 input_stack::get_char();
802 c = input_stack::peek_char();
803 if (c == 'm') {
804 input_stack::get_char();
805 context_buffer = ".bottom";
806 return DOT_S;
808 input_stack::push_back('o');
810 input_stack::push_back('t');
812 context_buffer = ".bot";
813 return DOT_S;
815 input_stack::push_back('o');
817 context_buffer = ".b";
818 return DOT_S;
819 default:
820 context_buffer = '.';
821 return '.';
825 int get_token(int lookup_flag)
827 context_buffer.clear();
828 for (;;) {
829 int n = 0;
830 int bol = input_stack::bol();
831 int c = input_stack::get_char();
832 if (bol && c == command_char) {
833 token_buffer.clear();
834 token_buffer += c;
835 // the newline is not part of the token
836 for (;;) {
837 c = input_stack::peek_char();
838 if (c == EOF || c == '\n')
839 break;
840 input_stack::get_char();
841 token_buffer += char(c);
843 context_buffer = token_buffer;
844 return COMMAND_LINE;
846 switch (c) {
847 case EOF:
848 return EOF;
849 case ' ':
850 case '\t':
851 break;
852 case '\\':
854 int d = input_stack::peek_char();
855 if (d != '\n') {
856 context_buffer = '\\';
857 return '\\';
859 input_stack::get_char();
860 break;
862 case '#':
863 do {
864 c = input_stack::get_char();
865 } while (c != '\n' && c != EOF);
866 if (c == '\n')
867 context_buffer = '\n';
868 return c;
869 case '"':
870 context_buffer = '"';
871 token_buffer.clear();
872 for (;;) {
873 c = input_stack::get_char();
874 if (c == '\\') {
875 context_buffer += '\\';
876 c = input_stack::peek_char();
877 if (c == '"') {
878 input_stack::get_char();
879 token_buffer += '"';
880 context_buffer += '"';
882 else
883 token_buffer += '\\';
885 else if (c == '\n') {
886 error("newline in string");
887 break;
889 else if (c == EOF) {
890 error("missing `\"'");
891 break;
893 else if (c == '"') {
894 context_buffer += '"';
895 break;
897 else {
898 context_buffer += char(c);
899 token_buffer += char(c);
902 return TEXT;
903 case '0':
904 case '1':
905 case '2':
906 case '3':
907 case '4':
908 case '5':
909 case '6':
910 case '7':
911 case '8':
912 case '9':
914 int overflow = 0;
915 n = 0;
916 for (;;) {
917 if (n > (INT_MAX - 9)/10) {
918 overflow = 1;
919 break;
921 n *= 10;
922 n += 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 token_double = n;
930 if (overflow) {
931 for (;;) {
932 token_double *= 10.0;
933 token_double += c - '0';
934 context_buffer += char(c);
935 c = input_stack::peek_char();
936 if (c == EOF || !csdigit(c))
937 break;
938 c = input_stack::get_char();
940 // if somebody asks for 1000000000000th, we will silently
941 // give them INT_MAXth
942 double temp = token_double; // work around gas 1.34/sparc bug
943 if (token_double > INT_MAX)
944 n = INT_MAX;
945 else
946 n = int(temp);
949 switch (c) {
950 case 'i':
951 case 'I':
952 context_buffer += char(c);
953 input_stack::get_char();
954 return NUMBER;
955 case '.':
957 context_buffer += '.';
958 input_stack::get_char();
959 got_dot:
960 double factor = 1.0;
961 for (;;) {
962 c = input_stack::peek_char();
963 if (c == EOF || !csdigit(c))
964 break;
965 input_stack::get_char();
966 context_buffer += char(c);
967 factor /= 10.0;
968 if (c != '0')
969 token_double += factor*(c - '0');
971 if (c != 'e' && c != 'E') {
972 if (c == 'i' || c == 'I') {
973 context_buffer += char(c);
974 input_stack::get_char();
976 return NUMBER;
979 // fall through
980 case 'e':
981 case 'E':
983 int echar = c;
984 input_stack::get_char();
985 c = input_stack::peek_char();
986 int sign = '+';
987 if (c == '+' || c == '-') {
988 sign = c;
989 input_stack::get_char();
990 c = input_stack::peek_char();
991 if (c == EOF || !csdigit(c)) {
992 input_stack::push_back(sign);
993 input_stack::push_back(echar);
994 return NUMBER;
996 context_buffer += char(echar);
997 context_buffer += char(sign);
999 else {
1000 if (c == EOF || !csdigit(c)) {
1001 input_stack::push_back(echar);
1002 return NUMBER;
1004 context_buffer += char(echar);
1006 input_stack::get_char();
1007 context_buffer += char(c);
1008 n = c - '0';
1009 for (;;) {
1010 c = input_stack::peek_char();
1011 if (c == EOF || !csdigit(c))
1012 break;
1013 input_stack::get_char();
1014 context_buffer += char(c);
1015 n = n*10 + (c - '0');
1017 if (sign == '-')
1018 n = -n;
1019 if (c == 'i' || c == 'I') {
1020 context_buffer += char(c);
1021 input_stack::get_char();
1023 token_double *= pow(10.0, n);
1024 return NUMBER;
1026 case 'n':
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 += "nd";
1033 return ORDINAL;
1035 input_stack::push_back('n');
1036 return NUMBER;
1037 case 'r':
1038 input_stack::get_char();
1039 c = input_stack::peek_char();
1040 if (c == 'd') {
1041 input_stack::get_char();
1042 token_int = n;
1043 context_buffer += "rd";
1044 return ORDINAL;
1046 input_stack::push_back('r');
1047 return NUMBER;
1048 case 't':
1049 input_stack::get_char();
1050 c = input_stack::peek_char();
1051 if (c == 'h') {
1052 input_stack::get_char();
1053 token_int = n;
1054 context_buffer += "th";
1055 return ORDINAL;
1057 input_stack::push_back('t');
1058 return NUMBER;
1059 case 's':
1060 input_stack::get_char();
1061 c = input_stack::peek_char();
1062 if (c == 't') {
1063 input_stack::get_char();
1064 token_int = n;
1065 context_buffer += "st";
1066 return ORDINAL;
1068 input_stack::push_back('s');
1069 return NUMBER;
1070 default:
1071 return NUMBER;
1073 break;
1074 case '\'':
1076 c = input_stack::peek_char();
1077 if (c == 't') {
1078 input_stack::get_char();
1079 c = input_stack::peek_char();
1080 if (c == 'h') {
1081 input_stack::get_char();
1082 context_buffer = "'th";
1083 return TH;
1085 else
1086 input_stack::push_back('t');
1088 context_buffer = "'";
1089 return '\'';
1091 case '.':
1093 c = input_stack::peek_char();
1094 if (c != EOF && csdigit(c)) {
1095 n = 0;
1096 token_double = 0.0;
1097 context_buffer = '.';
1098 goto got_dot;
1100 return get_token_after_dot(c);
1102 case '<':
1103 c = input_stack::peek_char();
1104 if (c == '-') {
1105 input_stack::get_char();
1106 c = input_stack::peek_char();
1107 if (c == '>') {
1108 input_stack::get_char();
1109 context_buffer = "<->";
1110 return DOUBLE_ARROW_HEAD;
1112 context_buffer = "<-";
1113 return LEFT_ARROW_HEAD;
1115 else if (c == '=') {
1116 input_stack::get_char();
1117 context_buffer = "<=";
1118 return LESSEQUAL;
1120 context_buffer = "<";
1121 return '<';
1122 case '-':
1123 c = input_stack::peek_char();
1124 if (c == '>') {
1125 input_stack::get_char();
1126 context_buffer = "->";
1127 return RIGHT_ARROW_HEAD;
1129 context_buffer = "-";
1130 return '-';
1131 case '!':
1132 c = input_stack::peek_char();
1133 if (c == '=') {
1134 input_stack::get_char();
1135 context_buffer = "!=";
1136 return NOTEQUAL;
1138 context_buffer = "!";
1139 return '!';
1140 case '>':
1141 c = input_stack::peek_char();
1142 if (c == '=') {
1143 input_stack::get_char();
1144 context_buffer = ">=";
1145 return GREATEREQUAL;
1147 context_buffer = ">";
1148 return '>';
1149 case '=':
1150 c = input_stack::peek_char();
1151 if (c == '=') {
1152 input_stack::get_char();
1153 context_buffer = "==";
1154 return EQUALEQUAL;
1156 context_buffer = "=";
1157 return '=';
1158 case '&':
1159 c = input_stack::peek_char();
1160 if (c == '&') {
1161 input_stack::get_char();
1162 context_buffer = "&&";
1163 return ANDAND;
1165 context_buffer = "&";
1166 return '&';
1167 case '|':
1168 c = input_stack::peek_char();
1169 if (c == '|') {
1170 input_stack::get_char();
1171 context_buffer = "||";
1172 return OROR;
1174 context_buffer = "|";
1175 return '|';
1176 default:
1177 if (c != EOF && csalpha(c)) {
1178 token_buffer.clear();
1179 token_buffer = c;
1180 for (;;) {
1181 c = input_stack::peek_char();
1182 if (c == EOF || (!csalnum(c) && c != '_'))
1183 break;
1184 input_stack::get_char();
1185 token_buffer += char(c);
1187 int tok = lookup_keyword(token_buffer.contents(),
1188 token_buffer.length());
1189 if (tok != 0) {
1190 context_buffer = token_buffer;
1191 return tok;
1193 char *def = 0;
1194 if (lookup_flag) {
1195 token_buffer += '\0';
1196 def = macro_table.lookup(token_buffer.contents());
1197 token_buffer.set_length(token_buffer.length() - 1);
1198 if (def) {
1199 if (c == '(') {
1200 input_stack::get_char();
1201 interpolate_macro_with_args(def);
1203 else
1204 input_stack::push(new macro_input(def));
1207 if (!def) {
1208 context_buffer = token_buffer;
1209 if (csupper(token_buffer[0]))
1210 return LABEL;
1211 else
1212 return VARIABLE;
1215 else {
1216 context_buffer = char(c);
1217 return (unsigned char)c;
1219 break;
1224 int get_delimited()
1226 token_buffer.clear();
1227 int c = input_stack::get_char();
1228 while (c == ' ' || c == '\t' || c == '\n')
1229 c = input_stack::get_char();
1230 if (c == EOF) {
1231 lex_error("missing delimiter");
1232 return 0;
1234 context_buffer = char(c);
1235 int had_newline = 0;
1236 int start = c;
1237 int level = 0;
1238 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1239 for (;;) {
1240 c = input_stack::get_char();
1241 if (c == EOF) {
1242 lex_error("missing closing delimiter");
1243 return 0;
1245 if (c == '\n')
1246 had_newline = 1;
1247 else if (!had_newline)
1248 context_buffer += char(c);
1249 switch (state) {
1250 case NORMAL:
1251 if (start == '{') {
1252 if (c == '{') {
1253 level++;
1254 break;
1256 if (c == '}') {
1257 if (--level < 0)
1258 state = DELIM_END;
1259 break;
1262 else {
1263 if (c == start) {
1264 state = DELIM_END;
1265 break;
1268 if (c == '"')
1269 state = IN_STRING;
1270 break;
1271 case IN_STRING_QUOTED:
1272 if (c == '\n')
1273 state = NORMAL;
1274 else
1275 state = IN_STRING;
1276 break;
1277 case IN_STRING:
1278 if (c == '"' || c == '\n')
1279 state = NORMAL;
1280 else if (c == '\\')
1281 state = IN_STRING_QUOTED;
1282 break;
1283 case DELIM_END:
1284 // This case it just to shut cfront 2.0 up.
1285 default:
1286 assert(0);
1288 if (state == DELIM_END)
1289 break;
1290 token_buffer += c;
1292 return 1;
1295 void do_define()
1297 int t = get_token(0); // do not expand what we are defining
1298 if (t != VARIABLE && t != LABEL) {
1299 lex_error("can only define variable or placename");
1300 return;
1302 token_buffer += '\0';
1303 string nm = token_buffer;
1304 const char *name = nm.contents();
1305 if (!get_delimited())
1306 return;
1307 token_buffer += '\0';
1308 macro_table.define(name, strsave(token_buffer.contents()));
1311 void do_undef()
1313 int t = get_token(0); // do not expand what we are undefining
1314 if (t != VARIABLE && t != LABEL) {
1315 lex_error("can only define variable or placename");
1316 return;
1318 token_buffer += '\0';
1319 macro_table.define(token_buffer.contents(), 0);
1323 class for_input : public input {
1324 char *var;
1325 char *body;
1326 double to;
1327 int by_is_multiplicative;
1328 double by;
1329 const char *p;
1330 int done_newline;
1331 public:
1332 for_input(char *, double, int, double, char *);
1333 ~for_input();
1334 int get();
1335 int peek();
1338 for_input::for_input(char *vr, double t, int bim, double b, char *bd)
1339 : var(vr), body(bd), to(t), by_is_multiplicative(bim), by(b), p(body),
1340 done_newline(0)
1344 for_input::~for_input()
1346 a_delete var;
1347 a_delete body;
1350 int for_input::get()
1352 if (p == 0)
1353 return EOF;
1354 for (;;) {
1355 if (*p != '\0')
1356 return (unsigned char)*p++;
1357 if (!done_newline) {
1358 done_newline = 1;
1359 return '\n';
1361 double val;
1362 if (!lookup_variable(var, &val)) {
1363 lex_error("body of `for' terminated enclosing block");
1364 return EOF;
1366 if (by_is_multiplicative)
1367 val *= by;
1368 else
1369 val += by;
1370 define_variable(var, val);
1371 if (val > to) {
1372 p = 0;
1373 return EOF;
1375 p = body;
1376 done_newline = 0;
1380 int for_input::peek()
1382 if (p == 0)
1383 return EOF;
1384 if (*p != '\0')
1385 return (unsigned char)*p;
1386 if (!done_newline)
1387 return '\n';
1388 double val;
1389 if (!lookup_variable(var, &val))
1390 return EOF;
1391 if (by_is_multiplicative) {
1392 if (val * by > to)
1393 return EOF;
1395 else {
1396 if (val + by > to)
1397 return EOF;
1399 if (*body == '\0')
1400 return EOF;
1401 return (unsigned char)*body;
1404 void do_for(char *var, double from, double to, int by_is_multiplicative,
1405 double by, char *body)
1407 define_variable(var, from);
1408 if (from <= to)
1409 input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
1413 void do_copy(const char *filename)
1415 errno = 0;
1416 FILE *fp = fopen(filename, "r");
1417 if (fp == 0) {
1418 lex_error("can't open `%1': %2", filename, strerror(errno));
1419 return;
1421 input_stack::push(new file_input(fp, filename));
1424 class copy_thru_input : public input {
1425 int done;
1426 char *body;
1427 char *until;
1428 const char *p;
1429 const char *ap;
1430 int argv[9];
1431 int argc;
1432 string line;
1433 int get_line();
1434 virtual int inget() = 0;
1435 public:
1436 copy_thru_input(const char *b, const char *u);
1437 ~copy_thru_input();
1438 int get();
1439 int peek();
1442 class copy_file_thru_input : public copy_thru_input {
1443 input *in;
1444 public:
1445 copy_file_thru_input(input *, const char *b, const char *u);
1446 ~copy_file_thru_input();
1447 int inget();
1450 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1451 const char *u)
1452 : copy_thru_input(b, u), in(i)
1456 copy_file_thru_input::~copy_file_thru_input()
1458 delete in;
1461 int copy_file_thru_input::inget()
1463 if (!in)
1464 return EOF;
1465 else
1466 return in->get();
1469 class copy_rest_thru_input : public copy_thru_input {
1470 public:
1471 copy_rest_thru_input(const char *, const char *u);
1472 int inget();
1475 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1476 : copy_thru_input(b, u)
1480 int copy_rest_thru_input::inget()
1482 while (next != 0) {
1483 int c = next->get();
1484 if (c != EOF)
1485 return c;
1486 if (next->next == 0)
1487 return EOF;
1488 input *tem = next;
1489 next = next->next;
1490 delete tem;
1492 return EOF;
1496 copy_thru_input::copy_thru_input(const char *b, const char *u)
1497 : done(0)
1499 ap = 0;
1500 body = process_body(b);
1501 p = 0;
1502 until = strsave(u);
1506 copy_thru_input::~copy_thru_input()
1508 a_delete body;
1509 a_delete until;
1512 int copy_thru_input::get()
1514 if (ap) {
1515 if (*ap != '\0')
1516 return (unsigned char)*ap++;
1517 ap = 0;
1519 for (;;) {
1520 if (p == 0) {
1521 if (!get_line())
1522 break;
1523 p = body;
1525 if (*p == '\0') {
1526 p = 0;
1527 return '\n';
1529 while (*p >= ARG1 && *p <= ARG1 + 8) {
1530 int i = *p++ - ARG1;
1531 if (i < argc && line[argv[i]] != '\0') {
1532 ap = line.contents() + argv[i];
1533 return (unsigned char)*ap++;
1536 if (*p != '\0')
1537 return (unsigned char)*p++;
1539 return EOF;
1542 int copy_thru_input::peek()
1544 if (ap) {
1545 if (*ap != '\0')
1546 return (unsigned char)*ap;
1547 ap = 0;
1549 for (;;) {
1550 if (p == 0) {
1551 if (!get_line())
1552 break;
1553 p = body;
1555 if (*p == '\0')
1556 return '\n';
1557 while (*p >= ARG1 && *p <= ARG1 + 8) {
1558 int i = *p++ - ARG1;
1559 if (i < argc && line[argv[i]] != '\0') {
1560 ap = line.contents() + argv[i];
1561 return (unsigned char)*ap;
1564 if (*p != '\0')
1565 return (unsigned char)*p;
1567 return EOF;
1570 int copy_thru_input::get_line()
1572 if (done)
1573 return 0;
1574 line.clear();
1575 argc = 0;
1576 int c = inget();
1577 for (;;) {
1578 while (c == ' ')
1579 c = inget();
1580 if (c == EOF || c == '\n')
1581 break;
1582 if (argc == 9) {
1583 do {
1584 c = inget();
1585 } while (c != '\n' && c != EOF);
1586 break;
1588 argv[argc++] = line.length();
1589 do {
1590 line += char(c);
1591 c = inget();
1592 } while (c != ' ' && c != '\n');
1593 line += '\0';
1595 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1596 done = 1;
1597 return 0;
1599 return argc > 0 || c == '\n';
1602 class simple_file_input : public input {
1603 const char *filename;
1604 int lineno;
1605 FILE *fp;
1606 public:
1607 simple_file_input(FILE *, const char *);
1608 ~simple_file_input();
1609 int get();
1610 int peek();
1611 int get_location(const char **, int *);
1614 simple_file_input::simple_file_input(FILE *p, const char *s)
1615 : filename(s), lineno(1), fp(p)
1619 simple_file_input::~simple_file_input()
1621 // don't delete the filename
1622 fclose(fp);
1625 int simple_file_input::get()
1627 int c = getc(fp);
1628 while (illegal_input_char(c)) {
1629 error("illegal input character code %1", c);
1630 c = getc(fp);
1632 if (c == '\n')
1633 lineno++;
1634 return c;
1637 int simple_file_input::peek()
1639 int c = getc(fp);
1640 while (illegal_input_char(c)) {
1641 error("illegal input character code %1", c);
1642 c = getc(fp);
1644 if (c != EOF)
1645 ungetc(c, fp);
1646 return c;
1649 int simple_file_input::get_location(const char **fnp, int *lnp)
1651 *fnp = filename;
1652 *lnp = lineno;
1653 return 1;
1657 void copy_file_thru(const char *filename, const char *body, const char *until)
1659 errno = 0;
1660 FILE *fp = fopen(filename, "r");
1661 if (fp == 0) {
1662 lex_error("can't open `%1': %2", filename, strerror(errno));
1663 return;
1665 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1666 body, until);
1667 input_stack::push(in);
1670 void copy_rest_thru(const char *body, const char *until)
1672 input_stack::push(new copy_rest_thru_input(body, until));
1675 void push_body(const char *s)
1677 input_stack::push(new char_input('\n'));
1678 input_stack::push(new macro_input(s));
1681 int delim_flag = 0;
1683 char *get_thru_arg()
1685 int c = input_stack::peek_char();
1686 while (c == ' ') {
1687 input_stack::get_char();
1688 c = input_stack::peek_char();
1690 if (c != EOF && csalpha(c)) {
1691 // looks like a macro
1692 input_stack::get_char();
1693 token_buffer = c;
1694 for (;;) {
1695 c = input_stack::peek_char();
1696 if (c == EOF || (!csalnum(c) && c != '_'))
1697 break;
1698 input_stack::get_char();
1699 token_buffer += char(c);
1701 context_buffer = token_buffer;
1702 token_buffer += '\0';
1703 char *def = macro_table.lookup(token_buffer.contents());
1704 if (def)
1705 return strsave(def);
1706 // I guess it wasn't a macro after all; so push the macro name back.
1707 // -2 because we added a '\0'
1708 for (int i = token_buffer.length() - 2; i >= 0; i--)
1709 input_stack::push_back(token_buffer[i]);
1711 if (get_delimited()) {
1712 token_buffer += '\0';
1713 return strsave(token_buffer.contents());
1715 else
1716 return 0;
1719 int lookahead_token = -1;
1720 string old_context_buffer;
1722 void do_lookahead()
1724 if (lookahead_token == -1) {
1725 old_context_buffer = context_buffer;
1726 lookahead_token = get_token(1);
1730 int yylex()
1732 if (delim_flag) {
1733 assert(lookahead_token == -1);
1734 if (delim_flag == 2) {
1735 if ((yylval.str = get_thru_arg()) != 0)
1736 return DELIMITED;
1737 else
1738 return 0;
1740 else {
1741 if (get_delimited()) {
1742 token_buffer += '\0';
1743 yylval.str = strsave(token_buffer.contents());
1744 return DELIMITED;
1746 else
1747 return 0;
1750 for (;;) {
1751 int t;
1752 if (lookahead_token >= 0) {
1753 t = lookahead_token;
1754 lookahead_token = -1;
1756 else
1757 t = get_token(1);
1758 switch (t) {
1759 case '\n':
1760 return ';';
1761 case EOF:
1762 return 0;
1763 case DEFINE:
1764 do_define();
1765 break;
1766 case UNDEF:
1767 do_undef();
1768 break;
1769 case ORDINAL:
1770 yylval.n = token_int;
1771 return t;
1772 case NUMBER:
1773 yylval.x = token_double;
1774 return t;
1775 case COMMAND_LINE:
1776 case TEXT:
1777 token_buffer += '\0';
1778 if (!input_stack::get_location(&yylval.lstr.filename,
1779 &yylval.lstr.lineno)) {
1780 yylval.lstr.filename = 0;
1781 yylval.lstr.lineno = -1;
1783 yylval.lstr.str = strsave(token_buffer.contents());
1784 return t;
1785 case LABEL:
1786 case VARIABLE:
1787 token_buffer += '\0';
1788 yylval.str = strsave(token_buffer.contents());
1789 return t;
1790 case LEFT:
1791 // change LEFT to LEFT_CORNER when followed by OF
1792 old_context_buffer = context_buffer;
1793 lookahead_token = get_token(1);
1794 if (lookahead_token == OF)
1795 return LEFT_CORNER;
1796 else
1797 return t;
1798 case RIGHT:
1799 // change RIGHT to RIGHT_CORNER when followed by OF
1800 old_context_buffer = context_buffer;
1801 lookahead_token = get_token(1);
1802 if (lookahead_token == OF)
1803 return RIGHT_CORNER;
1804 else
1805 return t;
1806 case UPPER:
1807 // recognise UPPER only before LEFT or RIGHT
1808 old_context_buffer = context_buffer;
1809 lookahead_token = get_token(1);
1810 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1811 yylval.str = strsave("upper");
1812 return VARIABLE;
1814 else
1815 return t;
1816 case LOWER:
1817 // recognise LOWER only before LEFT or RIGHT
1818 old_context_buffer = context_buffer;
1819 lookahead_token = get_token(1);
1820 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1821 yylval.str = strsave("lower");
1822 return VARIABLE;
1824 else
1825 return t;
1826 case TOP:
1827 // recognise TOP only before OF
1828 old_context_buffer = context_buffer;
1829 lookahead_token = get_token(1);
1830 if (lookahead_token != OF) {
1831 yylval.str = strsave("top");
1832 return VARIABLE;
1834 else
1835 return t;
1836 case BOTTOM:
1837 // recognise BOTTOM only before OF
1838 old_context_buffer = context_buffer;
1839 lookahead_token = get_token(1);
1840 if (lookahead_token != OF) {
1841 yylval.str = strsave("bottom");
1842 return VARIABLE;
1844 else
1845 return t;
1846 case CENTER:
1847 // recognise CENTER only before OF
1848 old_context_buffer = context_buffer;
1849 lookahead_token = get_token(1);
1850 if (lookahead_token != OF) {
1851 yylval.str = strsave("center");
1852 return VARIABLE;
1854 else
1855 return t;
1856 case START:
1857 // recognise START only before OF
1858 old_context_buffer = context_buffer;
1859 lookahead_token = get_token(1);
1860 if (lookahead_token != OF) {
1861 yylval.str = strsave("start");
1862 return VARIABLE;
1864 else
1865 return t;
1866 case END:
1867 // recognise END only before OF
1868 old_context_buffer = context_buffer;
1869 lookahead_token = get_token(1);
1870 if (lookahead_token != OF) {
1871 yylval.str = strsave("end");
1872 return VARIABLE;
1874 else
1875 return t;
1876 default:
1877 return t;
1882 void lex_error(const char *message,
1883 const errarg &arg1,
1884 const errarg &arg2,
1885 const errarg &arg3)
1887 const char *filename;
1888 int lineno;
1889 if (!input_stack::get_location(&filename, &lineno))
1890 error(message, arg1, arg2, arg3);
1891 else
1892 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1895 void lex_warning(const char *message,
1896 const errarg &arg1,
1897 const errarg &arg2,
1898 const errarg &arg3)
1900 const char *filename;
1901 int lineno;
1902 if (!input_stack::get_location(&filename, &lineno))
1903 warning(message, arg1, arg2, arg3);
1904 else
1905 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1908 void yyerror(const char *s)
1910 const char *filename;
1911 int lineno;
1912 const char *context = 0;
1913 if (lookahead_token == -1) {
1914 if (context_buffer.length() > 0) {
1915 context_buffer += '\0';
1916 context = context_buffer.contents();
1919 else {
1920 if (old_context_buffer.length() > 0) {
1921 old_context_buffer += '\0';
1922 context = old_context_buffer.contents();
1925 if (!input_stack::get_location(&filename, &lineno)) {
1926 if (context) {
1927 if (context[0] == '\n' && context[1] == '\0')
1928 error("%1 before newline", s);
1929 else
1930 error("%1 before `%2'", s, context);
1932 else
1933 error("%1 at end of picture", s);
1935 else {
1936 if (context) {
1937 if (context[0] == '\n' && context[1] == '\0')
1938 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1939 else
1940 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1941 s, context);
1943 else
1944 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);