* All affected files: Update postal address of FSF.
[s-roff.git] / src / preproc / pic / lex.cpp
blobceca36854648b1e12603616c103067f0bd6d227c
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "pic.h"
23 #include "ptable.h"
24 #include "object.h"
25 #include "pic_tab.h"
27 declare_ptable(char)
28 implement_ptable(char)
30 PTABLE(char) macro_table;
32 class macro_input : public input {
33 char *s;
34 char *p;
35 public:
36 macro_input(const char *);
37 ~macro_input();
38 int get();
39 int peek();
42 class argument_macro_input : public input {
43 char *s;
44 char *p;
45 char *ap;
46 int argc;
47 char *argv[9];
48 public:
49 argument_macro_input(const char *, int, char **);
50 ~argument_macro_input();
51 int get();
52 int peek();
55 input::input() : next(0)
59 input::~input()
63 int input::get_location(const char **, int *)
65 return 0;
68 file_input::file_input(FILE *f, const char *fn)
69 : fp(f), filename(fn), lineno(0), ptr("")
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 (invalid_input_char(c))
88 lex_error("invalid 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 representing $1. Must be invalid 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 : ap(0), argc(ac)
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 int i;
365 for (i = 0; i < 9; i++)
366 argv[i] = 0;
367 int level = 0;
368 int c;
369 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
370 do {
371 token_buffer.clear();
372 for (;;) {
373 c = input_stack::get_char();
374 if (c == EOF) {
375 lex_error("end of input while scanning macro arguments");
376 break;
378 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
379 if (token_buffer.length() > 0) {
380 token_buffer += '\0';
381 argv[argc] = strsave(token_buffer.contents());
383 // for `foo()', argc = 0
384 if (argc > 0 || c != ')' || i > 0)
385 argc++;
386 break;
388 token_buffer += char(c);
389 switch (state) {
390 case NORMAL:
391 if (c == '"')
392 state = IN_STRING;
393 else if (c == '(')
394 level++;
395 else if (c == ')')
396 level--;
397 break;
398 case IN_STRING:
399 if (c == '"')
400 state = NORMAL;
401 else if (c == '\\')
402 state = IN_STRING_QUOTED;
403 break;
404 case IN_STRING_QUOTED:
405 state = IN_STRING;
406 break;
409 } while (c != ')' && c != EOF);
410 input_stack::push(new argument_macro_input(body, argc, argv));
413 static int docmp(const char *s1, int n1, const char *s2, int n2)
415 if (n1 < n2) {
416 int r = memcmp(s1, s2, n1);
417 return r ? r : -1;
419 else if (n1 > n2) {
420 int r = memcmp(s1, s2, n2);
421 return r ? r : 1;
423 else
424 return memcmp(s1, s2, n1);
427 int lookup_keyword(const char *str, int len)
429 static struct keyword {
430 const char *name;
431 int token;
432 } table[] = {
433 { "Here", HERE },
434 { "above", ABOVE },
435 { "aligned", ALIGNED },
436 { "and", AND },
437 { "arc", ARC },
438 { "arrow", ARROW },
439 { "at", AT },
440 { "atan2", ATAN2 },
441 { "below", BELOW },
442 { "between", BETWEEN },
443 { "bottom", BOTTOM },
444 { "box", BOX },
445 { "by", BY },
446 { "ccw", CCW },
447 { "center", CENTER },
448 { "chop", CHOP },
449 { "circle", CIRCLE },
450 { "color", COLORED },
451 { "colored", COLORED },
452 { "colour", COLORED },
453 { "coloured", COLORED },
454 { "command", COMMAND },
455 { "copy", COPY },
456 { "cos", COS },
457 { "cw", CW },
458 { "dashed", DASHED },
459 { "define", DEFINE },
460 { "diam", DIAMETER },
461 { "diameter", DIAMETER },
462 { "do", DO },
463 { "dotted", DOTTED },
464 { "down", DOWN },
465 { "east", EAST },
466 { "ellipse", ELLIPSE },
467 { "else", ELSE },
468 { "end", END },
469 { "exp", EXP },
470 { "figname", FIGNAME },
471 { "fill", FILL },
472 { "filled", FILL },
473 { "for", FOR },
474 { "from", FROM },
475 { "height", HEIGHT },
476 { "ht", HEIGHT },
477 { "if", IF },
478 { "int", INT },
479 { "invis", INVISIBLE },
480 { "invisible", INVISIBLE },
481 { "last", LAST },
482 { "left", LEFT },
483 { "line", LINE },
484 { "ljust", LJUST },
485 { "log", LOG },
486 { "lower", LOWER },
487 { "max", K_MAX },
488 { "min", K_MIN },
489 { "move", MOVE },
490 { "north", NORTH },
491 { "of", OF },
492 { "outline", OUTLINED },
493 { "outlined", OUTLINED },
494 { "plot", PLOT },
495 { "print", PRINT },
496 { "rad", RADIUS },
497 { "radius", RADIUS },
498 { "rand", RAND },
499 { "reset", RESET },
500 { "right", RIGHT },
501 { "rjust", RJUST },
502 { "same", SAME },
503 { "sh", SH },
504 { "shaded", SHADED },
505 { "sin", SIN },
506 { "solid", SOLID },
507 { "south", SOUTH },
508 { "spline", SPLINE },
509 { "sprintf", SPRINTF },
510 { "sqrt", SQRT },
511 { "srand", SRAND },
512 { "start", START },
513 { "the", THE },
514 { "then", THEN },
515 { "thick", THICKNESS },
516 { "thickness", THICKNESS },
517 { "thru", THRU },
518 { "to", TO },
519 { "top", TOP },
520 { "undef", UNDEF },
521 { "until", UNTIL },
522 { "up", UP },
523 { "upper", UPPER },
524 { "way", WAY },
525 { "west", WEST },
526 { "wid", WIDTH },
527 { "width", WIDTH },
528 { "with", WITH },
531 const keyword *start = table;
532 const keyword *end = table + sizeof(table)/sizeof(table[0]);
533 while (start < end) {
534 // start <= target < end
535 const keyword *mid = start + (end - start)/2;
537 int cmp = docmp(str, len, mid->name, strlen(mid->name));
538 if (cmp == 0)
539 return mid->token;
540 if (cmp < 0)
541 end = mid;
542 else
543 start = mid + 1;
545 return 0;
548 int get_token_after_dot(int c)
550 // get_token deals with the case where c is a digit
551 switch (c) {
552 case 'h':
553 input_stack::get_char();
554 c = input_stack::peek_char();
555 if (c == 't') {
556 input_stack::get_char();
557 context_buffer = ".ht";
558 return DOT_HT;
560 else if (c == 'e') {
561 input_stack::get_char();
562 c = input_stack::peek_char();
563 if (c == 'i') {
564 input_stack::get_char();
565 c = input_stack::peek_char();
566 if (c == 'g') {
567 input_stack::get_char();
568 c = input_stack::peek_char();
569 if (c == 'h') {
570 input_stack::get_char();
571 c = input_stack::peek_char();
572 if (c == 't') {
573 input_stack::get_char();
574 context_buffer = ".height";
575 return DOT_HT;
577 input_stack::push_back('h');
579 input_stack::push_back('g');
581 input_stack::push_back('i');
583 input_stack::push_back('e');
585 input_stack::push_back('h');
586 return '.';
587 case 'x':
588 input_stack::get_char();
589 context_buffer = ".x";
590 return DOT_X;
591 case 'y':
592 input_stack::get_char();
593 context_buffer = ".y";
594 return DOT_Y;
595 case 'c':
596 input_stack::get_char();
597 c = input_stack::peek_char();
598 if (c == 'e') {
599 input_stack::get_char();
600 c = input_stack::peek_char();
601 if (c == 'n') {
602 input_stack::get_char();
603 c = input_stack::peek_char();
604 if (c == 't') {
605 input_stack::get_char();
606 c = input_stack::peek_char();
607 if (c == 'e') {
608 input_stack::get_char();
609 c = input_stack::peek_char();
610 if (c == 'r') {
611 input_stack::get_char();
612 context_buffer = ".center";
613 return DOT_C;
615 input_stack::push_back('e');
617 input_stack::push_back('t');
619 input_stack::push_back('n');
621 input_stack::push_back('e');
623 context_buffer = ".c";
624 return DOT_C;
625 case 'n':
626 input_stack::get_char();
627 c = input_stack::peek_char();
628 if (c == 'e') {
629 input_stack::get_char();
630 context_buffer = ".ne";
631 return DOT_NE;
633 else if (c == 'w') {
634 input_stack::get_char();
635 context_buffer = ".nw";
636 return DOT_NW;
638 else {
639 context_buffer = ".n";
640 return DOT_N;
642 break;
643 case 'e':
644 input_stack::get_char();
645 c = input_stack::peek_char();
646 if (c == 'n') {
647 input_stack::get_char();
648 c = input_stack::peek_char();
649 if (c == 'd') {
650 input_stack::get_char();
651 context_buffer = ".end";
652 return DOT_END;
654 input_stack::push_back('n');
655 context_buffer = ".e";
656 return DOT_E;
658 context_buffer = ".e";
659 return DOT_E;
660 case 'w':
661 input_stack::get_char();
662 c = input_stack::peek_char();
663 if (c == 'i') {
664 input_stack::get_char();
665 c = input_stack::peek_char();
666 if (c == 'd') {
667 input_stack::get_char();
668 c = input_stack::peek_char();
669 if (c == 't') {
670 input_stack::get_char();
671 c = input_stack::peek_char();
672 if (c == 'h') {
673 input_stack::get_char();
674 context_buffer = ".width";
675 return DOT_WID;
677 input_stack::push_back('t');
679 context_buffer = ".wid";
680 return DOT_WID;
682 input_stack::push_back('i');
684 context_buffer = ".w";
685 return DOT_W;
686 case 's':
687 input_stack::get_char();
688 c = input_stack::peek_char();
689 if (c == 'e') {
690 input_stack::get_char();
691 context_buffer = ".se";
692 return DOT_SE;
694 else if (c == 'w') {
695 input_stack::get_char();
696 context_buffer = ".sw";
697 return DOT_SW;
699 else {
700 if (c == 't') {
701 input_stack::get_char();
702 c = input_stack::peek_char();
703 if (c == 'a') {
704 input_stack::get_char();
705 c = input_stack::peek_char();
706 if (c == 'r') {
707 input_stack::get_char();
708 c = input_stack::peek_char();
709 if (c == 't') {
710 input_stack::get_char();
711 context_buffer = ".start";
712 return DOT_START;
714 input_stack::push_back('r');
716 input_stack::push_back('a');
718 input_stack::push_back('t');
720 context_buffer = ".s";
721 return DOT_S;
723 break;
724 case 't':
725 input_stack::get_char();
726 c = input_stack::peek_char();
727 if (c == 'o') {
728 input_stack::get_char();
729 c = input_stack::peek_char();
730 if (c == 'p') {
731 input_stack::get_char();
732 context_buffer = ".top";
733 return DOT_N;
735 input_stack::push_back('o');
737 context_buffer = ".t";
738 return DOT_N;
739 case 'l':
740 input_stack::get_char();
741 c = input_stack::peek_char();
742 if (c == 'e') {
743 input_stack::get_char();
744 c = input_stack::peek_char();
745 if (c == 'f') {
746 input_stack::get_char();
747 c = input_stack::peek_char();
748 if (c == 't') {
749 input_stack::get_char();
750 context_buffer = ".left";
751 return DOT_W;
753 input_stack::push_back('f');
755 input_stack::push_back('e');
757 context_buffer = ".l";
758 return DOT_W;
759 case 'r':
760 input_stack::get_char();
761 c = input_stack::peek_char();
762 if (c == 'a') {
763 input_stack::get_char();
764 c = input_stack::peek_char();
765 if (c == 'd') {
766 input_stack::get_char();
767 context_buffer = ".rad";
768 return DOT_RAD;
770 input_stack::push_back('a');
772 else if (c == 'i') {
773 input_stack::get_char();
774 c = input_stack::peek_char();
775 if (c == 'g') {
776 input_stack::get_char();
777 c = input_stack::peek_char();
778 if (c == 'h') {
779 input_stack::get_char();
780 c = input_stack::peek_char();
781 if (c == 't') {
782 input_stack::get_char();
783 context_buffer = ".right";
784 return DOT_E;
786 input_stack::push_back('h');
788 input_stack::push_back('g');
790 input_stack::push_back('i');
792 context_buffer = ".r";
793 return DOT_E;
794 case 'b':
795 input_stack::get_char();
796 c = input_stack::peek_char();
797 if (c == 'o') {
798 input_stack::get_char();
799 c = input_stack::peek_char();
800 if (c == 't') {
801 input_stack::get_char();
802 c = input_stack::peek_char();
803 if (c == 't') {
804 input_stack::get_char();
805 c = input_stack::peek_char();
806 if (c == 'o') {
807 input_stack::get_char();
808 c = input_stack::peek_char();
809 if (c == 'm') {
810 input_stack::get_char();
811 context_buffer = ".bottom";
812 return DOT_S;
814 input_stack::push_back('o');
816 input_stack::push_back('t');
818 context_buffer = ".bot";
819 return DOT_S;
821 input_stack::push_back('o');
823 context_buffer = ".b";
824 return DOT_S;
825 default:
826 context_buffer = '.';
827 return '.';
831 int get_token(int lookup_flag)
833 context_buffer.clear();
834 for (;;) {
835 int n = 0;
836 int bol = input_stack::bol();
837 int c = input_stack::get_char();
838 if (bol && c == command_char) {
839 token_buffer.clear();
840 token_buffer += c;
841 // the newline is not part of the token
842 for (;;) {
843 c = input_stack::peek_char();
844 if (c == EOF || c == '\n')
845 break;
846 input_stack::get_char();
847 token_buffer += char(c);
849 context_buffer = token_buffer;
850 return COMMAND_LINE;
852 switch (c) {
853 case EOF:
854 return EOF;
855 case ' ':
856 case '\t':
857 break;
858 case '\\':
860 int d = input_stack::peek_char();
861 if (d != '\n') {
862 context_buffer = '\\';
863 return '\\';
865 input_stack::get_char();
866 break;
868 case '#':
869 do {
870 c = input_stack::get_char();
871 } while (c != '\n' && c != EOF);
872 if (c == '\n')
873 context_buffer = '\n';
874 return c;
875 case '"':
876 context_buffer = '"';
877 token_buffer.clear();
878 for (;;) {
879 c = input_stack::get_char();
880 if (c == '\\') {
881 context_buffer += '\\';
882 c = input_stack::peek_char();
883 if (c == '"') {
884 input_stack::get_char();
885 token_buffer += '"';
886 context_buffer += '"';
888 else
889 token_buffer += '\\';
891 else if (c == '\n') {
892 error("newline in string");
893 break;
895 else if (c == EOF) {
896 error("missing `\"'");
897 break;
899 else if (c == '"') {
900 context_buffer += '"';
901 break;
903 else {
904 context_buffer += char(c);
905 token_buffer += char(c);
908 return TEXT;
909 case '0':
910 case '1':
911 case '2':
912 case '3':
913 case '4':
914 case '5':
915 case '6':
916 case '7':
917 case '8':
918 case '9':
920 int overflow = 0;
921 n = 0;
922 for (;;) {
923 if (n > (INT_MAX - 9)/10) {
924 overflow = 1;
925 break;
927 n *= 10;
928 n += c - '0';
929 context_buffer += char(c);
930 c = input_stack::peek_char();
931 if (c == EOF || !csdigit(c))
932 break;
933 c = input_stack::get_char();
935 token_double = n;
936 if (overflow) {
937 for (;;) {
938 token_double *= 10.0;
939 token_double += c - '0';
940 context_buffer += char(c);
941 c = input_stack::peek_char();
942 if (c == EOF || !csdigit(c))
943 break;
944 c = input_stack::get_char();
946 // if somebody asks for 1000000000000th, we will silently
947 // give them INT_MAXth
948 double temp = token_double; // work around gas 1.34/sparc bug
949 if (token_double > INT_MAX)
950 n = INT_MAX;
951 else
952 n = int(temp);
955 switch (c) {
956 case 'i':
957 case 'I':
958 context_buffer += char(c);
959 input_stack::get_char();
960 return NUMBER;
961 case '.':
963 context_buffer += '.';
964 input_stack::get_char();
965 got_dot:
966 double factor = 1.0;
967 for (;;) {
968 c = input_stack::peek_char();
969 if (c == EOF || !csdigit(c))
970 break;
971 input_stack::get_char();
972 context_buffer += char(c);
973 factor /= 10.0;
974 if (c != '0')
975 token_double += factor*(c - '0');
977 if (c != 'e' && c != 'E') {
978 if (c == 'i' || c == 'I') {
979 context_buffer += char(c);
980 input_stack::get_char();
982 return NUMBER;
985 // fall through
986 case 'e':
987 case 'E':
989 int echar = c;
990 input_stack::get_char();
991 c = input_stack::peek_char();
992 int sign = '+';
993 if (c == '+' || c == '-') {
994 sign = c;
995 input_stack::get_char();
996 c = input_stack::peek_char();
997 if (c == EOF || !csdigit(c)) {
998 input_stack::push_back(sign);
999 input_stack::push_back(echar);
1000 return NUMBER;
1002 context_buffer += char(echar);
1003 context_buffer += char(sign);
1005 else {
1006 if (c == EOF || !csdigit(c)) {
1007 input_stack::push_back(echar);
1008 return NUMBER;
1010 context_buffer += char(echar);
1012 input_stack::get_char();
1013 context_buffer += char(c);
1014 n = c - '0';
1015 for (;;) {
1016 c = input_stack::peek_char();
1017 if (c == EOF || !csdigit(c))
1018 break;
1019 input_stack::get_char();
1020 context_buffer += char(c);
1021 n = n*10 + (c - '0');
1023 if (sign == '-')
1024 n = -n;
1025 if (c == 'i' || c == 'I') {
1026 context_buffer += char(c);
1027 input_stack::get_char();
1029 token_double *= pow(10.0, n);
1030 return NUMBER;
1032 case 'n':
1033 input_stack::get_char();
1034 c = input_stack::peek_char();
1035 if (c == 'd') {
1036 input_stack::get_char();
1037 token_int = n;
1038 context_buffer += "nd";
1039 return ORDINAL;
1041 input_stack::push_back('n');
1042 return NUMBER;
1043 case 'r':
1044 input_stack::get_char();
1045 c = input_stack::peek_char();
1046 if (c == 'd') {
1047 input_stack::get_char();
1048 token_int = n;
1049 context_buffer += "rd";
1050 return ORDINAL;
1052 input_stack::push_back('r');
1053 return NUMBER;
1054 case 't':
1055 input_stack::get_char();
1056 c = input_stack::peek_char();
1057 if (c == 'h') {
1058 input_stack::get_char();
1059 token_int = n;
1060 context_buffer += "th";
1061 return ORDINAL;
1063 input_stack::push_back('t');
1064 return NUMBER;
1065 case 's':
1066 input_stack::get_char();
1067 c = input_stack::peek_char();
1068 if (c == 't') {
1069 input_stack::get_char();
1070 token_int = n;
1071 context_buffer += "st";
1072 return ORDINAL;
1074 input_stack::push_back('s');
1075 return NUMBER;
1076 default:
1077 return NUMBER;
1079 break;
1080 case '\'':
1082 c = input_stack::peek_char();
1083 if (c == 't') {
1084 input_stack::get_char();
1085 c = input_stack::peek_char();
1086 if (c == 'h') {
1087 input_stack::get_char();
1088 context_buffer = "'th";
1089 return TH;
1091 else
1092 input_stack::push_back('t');
1094 context_buffer = "'";
1095 return '\'';
1097 case '.':
1099 c = input_stack::peek_char();
1100 if (c != EOF && csdigit(c)) {
1101 n = 0;
1102 token_double = 0.0;
1103 context_buffer = '.';
1104 goto got_dot;
1106 return get_token_after_dot(c);
1108 case '<':
1109 c = input_stack::peek_char();
1110 if (c == '-') {
1111 input_stack::get_char();
1112 c = input_stack::peek_char();
1113 if (c == '>') {
1114 input_stack::get_char();
1115 context_buffer = "<->";
1116 return DOUBLE_ARROW_HEAD;
1118 context_buffer = "<-";
1119 return LEFT_ARROW_HEAD;
1121 else if (c == '=') {
1122 input_stack::get_char();
1123 context_buffer = "<=";
1124 return LESSEQUAL;
1126 context_buffer = "<";
1127 return '<';
1128 case '-':
1129 c = input_stack::peek_char();
1130 if (c == '>') {
1131 input_stack::get_char();
1132 context_buffer = "->";
1133 return RIGHT_ARROW_HEAD;
1135 context_buffer = "-";
1136 return '-';
1137 case '!':
1138 c = input_stack::peek_char();
1139 if (c == '=') {
1140 input_stack::get_char();
1141 context_buffer = "!=";
1142 return NOTEQUAL;
1144 context_buffer = "!";
1145 return '!';
1146 case '>':
1147 c = input_stack::peek_char();
1148 if (c == '=') {
1149 input_stack::get_char();
1150 context_buffer = ">=";
1151 return GREATEREQUAL;
1153 context_buffer = ">";
1154 return '>';
1155 case '=':
1156 c = input_stack::peek_char();
1157 if (c == '=') {
1158 input_stack::get_char();
1159 context_buffer = "==";
1160 return EQUALEQUAL;
1162 context_buffer = "=";
1163 return '=';
1164 case '&':
1165 c = input_stack::peek_char();
1166 if (c == '&') {
1167 input_stack::get_char();
1168 context_buffer = "&&";
1169 return ANDAND;
1171 context_buffer = "&";
1172 return '&';
1173 case '|':
1174 c = input_stack::peek_char();
1175 if (c == '|') {
1176 input_stack::get_char();
1177 context_buffer = "||";
1178 return OROR;
1180 context_buffer = "|";
1181 return '|';
1182 default:
1183 if (c != EOF && csalpha(c)) {
1184 token_buffer.clear();
1185 token_buffer = c;
1186 for (;;) {
1187 c = input_stack::peek_char();
1188 if (c == EOF || (!csalnum(c) && c != '_'))
1189 break;
1190 input_stack::get_char();
1191 token_buffer += char(c);
1193 int tok = lookup_keyword(token_buffer.contents(),
1194 token_buffer.length());
1195 if (tok != 0) {
1196 context_buffer = token_buffer;
1197 return tok;
1199 char *def = 0;
1200 if (lookup_flag) {
1201 token_buffer += '\0';
1202 def = macro_table.lookup(token_buffer.contents());
1203 token_buffer.set_length(token_buffer.length() - 1);
1204 if (def) {
1205 if (c == '(') {
1206 input_stack::get_char();
1207 interpolate_macro_with_args(def);
1209 else
1210 input_stack::push(new macro_input(def));
1213 if (!def) {
1214 context_buffer = token_buffer;
1215 if (csupper(token_buffer[0]))
1216 return LABEL;
1217 else
1218 return VARIABLE;
1221 else {
1222 context_buffer = char(c);
1223 return (unsigned char)c;
1225 break;
1230 int get_delimited()
1232 token_buffer.clear();
1233 int c = input_stack::get_char();
1234 while (c == ' ' || c == '\t' || c == '\n')
1235 c = input_stack::get_char();
1236 if (c == EOF) {
1237 lex_error("missing delimiter");
1238 return 0;
1240 context_buffer = char(c);
1241 int had_newline = 0;
1242 int start = c;
1243 int level = 0;
1244 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1245 for (;;) {
1246 c = input_stack::get_char();
1247 if (c == EOF) {
1248 lex_error("missing closing delimiter");
1249 return 0;
1251 if (c == '\n')
1252 had_newline = 1;
1253 else if (!had_newline)
1254 context_buffer += char(c);
1255 switch (state) {
1256 case NORMAL:
1257 if (start == '{') {
1258 if (c == '{') {
1259 level++;
1260 break;
1262 if (c == '}') {
1263 if (--level < 0)
1264 state = DELIM_END;
1265 break;
1268 else {
1269 if (c == start) {
1270 state = DELIM_END;
1271 break;
1274 if (c == '"')
1275 state = IN_STRING;
1276 break;
1277 case IN_STRING_QUOTED:
1278 if (c == '\n')
1279 state = NORMAL;
1280 else
1281 state = IN_STRING;
1282 break;
1283 case IN_STRING:
1284 if (c == '"' || c == '\n')
1285 state = NORMAL;
1286 else if (c == '\\')
1287 state = IN_STRING_QUOTED;
1288 break;
1289 case DELIM_END:
1290 // This case it just to shut cfront 2.0 up.
1291 default:
1292 assert(0);
1294 if (state == DELIM_END)
1295 break;
1296 token_buffer += c;
1298 return 1;
1301 void do_define()
1303 int t = get_token(0); // do not expand what we are defining
1304 if (t != VARIABLE && t != LABEL) {
1305 lex_error("can only define variable or placename");
1306 return;
1308 token_buffer += '\0';
1309 string nm = token_buffer;
1310 const char *name = nm.contents();
1311 if (!get_delimited())
1312 return;
1313 token_buffer += '\0';
1314 macro_table.define(name, strsave(token_buffer.contents()));
1317 void do_undef()
1319 int t = get_token(0); // do not expand what we are undefining
1320 if (t != VARIABLE && t != LABEL) {
1321 lex_error("can only define variable or placename");
1322 return;
1324 token_buffer += '\0';
1325 macro_table.define(token_buffer.contents(), 0);
1329 class for_input : public input {
1330 char *var;
1331 char *body;
1332 double from;
1333 double to;
1334 int by_is_multiplicative;
1335 double by;
1336 const char *p;
1337 int done_newline;
1338 public:
1339 for_input(char *, double, double, int, double, char *);
1340 ~for_input();
1341 int get();
1342 int peek();
1345 for_input::for_input(char *vr, double f, double t,
1346 int bim, double b, char *bd)
1347 : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1348 p(body), done_newline(0)
1352 for_input::~for_input()
1354 a_delete var;
1355 a_delete body;
1358 int for_input::get()
1360 if (p == 0)
1361 return EOF;
1362 for (;;) {
1363 if (*p != '\0')
1364 return (unsigned char)*p++;
1365 if (!done_newline) {
1366 done_newline = 1;
1367 return '\n';
1369 double val;
1370 if (!lookup_variable(var, &val)) {
1371 lex_error("body of `for' terminated enclosing block");
1372 return EOF;
1374 if (by_is_multiplicative)
1375 val *= by;
1376 else
1377 val += by;
1378 define_variable(var, val);
1379 if ((from <= to && val > to)
1380 || (from >= to && val < to)) {
1381 p = 0;
1382 return EOF;
1384 p = body;
1385 done_newline = 0;
1389 int for_input::peek()
1391 if (p == 0)
1392 return EOF;
1393 if (*p != '\0')
1394 return (unsigned char)*p;
1395 if (!done_newline)
1396 return '\n';
1397 double val;
1398 if (!lookup_variable(var, &val))
1399 return EOF;
1400 if (by_is_multiplicative) {
1401 if (val * by > to)
1402 return EOF;
1404 else {
1405 if ((from <= to && val + by > to)
1406 || (from >= to && val + by < to))
1407 return EOF;
1409 if (*body == '\0')
1410 return EOF;
1411 return (unsigned char)*body;
1414 void do_for(char *var, double from, double to, int by_is_multiplicative,
1415 double by, char *body)
1417 define_variable(var, from);
1418 if ((by_is_multiplicative && by <= 0)
1419 || (by > 0 && from > to)
1420 || (by < 0 && from < to))
1421 return;
1422 input_stack::push(new for_input(var, from, to,
1423 by_is_multiplicative, by, body));
1427 void do_copy(const char *filename)
1429 errno = 0;
1430 FILE *fp = fopen(filename, "r");
1431 if (fp == 0) {
1432 lex_error("can't open `%1': %2", filename, strerror(errno));
1433 return;
1435 input_stack::push(new file_input(fp, filename));
1438 class copy_thru_input : public input {
1439 int done;
1440 char *body;
1441 char *until;
1442 const char *p;
1443 const char *ap;
1444 int argv[9];
1445 int argc;
1446 string line;
1447 int get_line();
1448 virtual int inget() = 0;
1449 public:
1450 copy_thru_input(const char *b, const char *u);
1451 ~copy_thru_input();
1452 int get();
1453 int peek();
1456 class copy_file_thru_input : public copy_thru_input {
1457 input *in;
1458 public:
1459 copy_file_thru_input(input *, const char *b, const char *u);
1460 ~copy_file_thru_input();
1461 int inget();
1464 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1465 const char *u)
1466 : copy_thru_input(b, u), in(i)
1470 copy_file_thru_input::~copy_file_thru_input()
1472 delete in;
1475 int copy_file_thru_input::inget()
1477 if (!in)
1478 return EOF;
1479 else
1480 return in->get();
1483 class copy_rest_thru_input : public copy_thru_input {
1484 public:
1485 copy_rest_thru_input(const char *, const char *u);
1486 int inget();
1489 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1490 : copy_thru_input(b, u)
1494 int copy_rest_thru_input::inget()
1496 while (next != 0) {
1497 int c = next->get();
1498 if (c != EOF)
1499 return c;
1500 if (next->next == 0)
1501 return EOF;
1502 input *tem = next;
1503 next = next->next;
1504 delete tem;
1506 return EOF;
1510 copy_thru_input::copy_thru_input(const char *b, const char *u)
1511 : done(0)
1513 ap = 0;
1514 body = process_body(b);
1515 p = 0;
1516 until = strsave(u);
1520 copy_thru_input::~copy_thru_input()
1522 a_delete body;
1523 a_delete until;
1526 int copy_thru_input::get()
1528 if (ap) {
1529 if (*ap != '\0')
1530 return (unsigned char)*ap++;
1531 ap = 0;
1533 for (;;) {
1534 if (p == 0) {
1535 if (!get_line())
1536 break;
1537 p = body;
1539 if (*p == '\0') {
1540 p = 0;
1541 return '\n';
1543 while (*p >= ARG1 && *p <= ARG1 + 8) {
1544 int i = *p++ - ARG1;
1545 if (i < argc && line[argv[i]] != '\0') {
1546 ap = line.contents() + argv[i];
1547 return (unsigned char)*ap++;
1550 if (*p != '\0')
1551 return (unsigned char)*p++;
1553 return EOF;
1556 int copy_thru_input::peek()
1558 if (ap) {
1559 if (*ap != '\0')
1560 return (unsigned char)*ap;
1561 ap = 0;
1563 for (;;) {
1564 if (p == 0) {
1565 if (!get_line())
1566 break;
1567 p = body;
1569 if (*p == '\0')
1570 return '\n';
1571 while (*p >= ARG1 && *p <= ARG1 + 8) {
1572 int i = *p++ - ARG1;
1573 if (i < argc && line[argv[i]] != '\0') {
1574 ap = line.contents() + argv[i];
1575 return (unsigned char)*ap;
1578 if (*p != '\0')
1579 return (unsigned char)*p;
1581 return EOF;
1584 int copy_thru_input::get_line()
1586 if (done)
1587 return 0;
1588 line.clear();
1589 argc = 0;
1590 int c = inget();
1591 for (;;) {
1592 while (c == ' ')
1593 c = inget();
1594 if (c == EOF || c == '\n')
1595 break;
1596 if (argc == 9) {
1597 do {
1598 c = inget();
1599 } while (c != '\n' && c != EOF);
1600 break;
1602 argv[argc++] = line.length();
1603 do {
1604 line += char(c);
1605 c = inget();
1606 } while (c != ' ' && c != '\n');
1607 line += '\0';
1609 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1610 done = 1;
1611 return 0;
1613 return argc > 0 || c == '\n';
1616 class simple_file_input : public input {
1617 const char *filename;
1618 int lineno;
1619 FILE *fp;
1620 public:
1621 simple_file_input(FILE *, const char *);
1622 ~simple_file_input();
1623 int get();
1624 int peek();
1625 int get_location(const char **, int *);
1628 simple_file_input::simple_file_input(FILE *p, const char *s)
1629 : filename(s), lineno(1), fp(p)
1633 simple_file_input::~simple_file_input()
1635 // don't delete the filename
1636 fclose(fp);
1639 int simple_file_input::get()
1641 int c = getc(fp);
1642 while (invalid_input_char(c)) {
1643 error("invalid input character code %1", c);
1644 c = getc(fp);
1646 if (c == '\n')
1647 lineno++;
1648 return c;
1651 int simple_file_input::peek()
1653 int c = getc(fp);
1654 while (invalid_input_char(c)) {
1655 error("invalid input character code %1", c);
1656 c = getc(fp);
1658 if (c != EOF)
1659 ungetc(c, fp);
1660 return c;
1663 int simple_file_input::get_location(const char **fnp, int *lnp)
1665 *fnp = filename;
1666 *lnp = lineno;
1667 return 1;
1671 void copy_file_thru(const char *filename, const char *body, const char *until)
1673 errno = 0;
1674 FILE *fp = fopen(filename, "r");
1675 if (fp == 0) {
1676 lex_error("can't open `%1': %2", filename, strerror(errno));
1677 return;
1679 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1680 body, until);
1681 input_stack::push(in);
1684 void copy_rest_thru(const char *body, const char *until)
1686 input_stack::push(new copy_rest_thru_input(body, until));
1689 void push_body(const char *s)
1691 input_stack::push(new char_input('\n'));
1692 input_stack::push(new macro_input(s));
1695 int delim_flag = 0;
1697 char *get_thru_arg()
1699 int c = input_stack::peek_char();
1700 while (c == ' ') {
1701 input_stack::get_char();
1702 c = input_stack::peek_char();
1704 if (c != EOF && csalpha(c)) {
1705 // looks like a macro
1706 input_stack::get_char();
1707 token_buffer = c;
1708 for (;;) {
1709 c = input_stack::peek_char();
1710 if (c == EOF || (!csalnum(c) && c != '_'))
1711 break;
1712 input_stack::get_char();
1713 token_buffer += char(c);
1715 context_buffer = token_buffer;
1716 token_buffer += '\0';
1717 char *def = macro_table.lookup(token_buffer.contents());
1718 if (def)
1719 return strsave(def);
1720 // I guess it wasn't a macro after all; so push the macro name back.
1721 // -2 because we added a '\0'
1722 for (int i = token_buffer.length() - 2; i >= 0; i--)
1723 input_stack::push_back(token_buffer[i]);
1725 if (get_delimited()) {
1726 token_buffer += '\0';
1727 return strsave(token_buffer.contents());
1729 else
1730 return 0;
1733 int lookahead_token = -1;
1734 string old_context_buffer;
1736 void do_lookahead()
1738 if (lookahead_token == -1) {
1739 old_context_buffer = context_buffer;
1740 lookahead_token = get_token(1);
1744 int yylex()
1746 if (delim_flag) {
1747 assert(lookahead_token == -1);
1748 if (delim_flag == 2) {
1749 if ((yylval.str = get_thru_arg()) != 0)
1750 return DELIMITED;
1751 else
1752 return 0;
1754 else {
1755 if (get_delimited()) {
1756 token_buffer += '\0';
1757 yylval.str = strsave(token_buffer.contents());
1758 return DELIMITED;
1760 else
1761 return 0;
1764 for (;;) {
1765 int t;
1766 if (lookahead_token >= 0) {
1767 t = lookahead_token;
1768 lookahead_token = -1;
1770 else
1771 t = get_token(1);
1772 switch (t) {
1773 case '\n':
1774 return ';';
1775 case EOF:
1776 return 0;
1777 case DEFINE:
1778 do_define();
1779 break;
1780 case UNDEF:
1781 do_undef();
1782 break;
1783 case ORDINAL:
1784 yylval.n = token_int;
1785 return t;
1786 case NUMBER:
1787 yylval.x = token_double;
1788 return t;
1789 case COMMAND_LINE:
1790 case TEXT:
1791 token_buffer += '\0';
1792 if (!input_stack::get_location(&yylval.lstr.filename,
1793 &yylval.lstr.lineno)) {
1794 yylval.lstr.filename = 0;
1795 yylval.lstr.lineno = -1;
1797 yylval.lstr.str = strsave(token_buffer.contents());
1798 return t;
1799 case LABEL:
1800 case VARIABLE:
1801 token_buffer += '\0';
1802 yylval.str = strsave(token_buffer.contents());
1803 return t;
1804 case LEFT:
1805 // change LEFT to LEFT_CORNER when followed by OF
1806 old_context_buffer = context_buffer;
1807 lookahead_token = get_token(1);
1808 if (lookahead_token == OF)
1809 return LEFT_CORNER;
1810 else
1811 return t;
1812 case RIGHT:
1813 // change RIGHT to RIGHT_CORNER when followed by OF
1814 old_context_buffer = context_buffer;
1815 lookahead_token = get_token(1);
1816 if (lookahead_token == OF)
1817 return RIGHT_CORNER;
1818 else
1819 return t;
1820 case UPPER:
1821 // recognise UPPER only before LEFT or RIGHT
1822 old_context_buffer = context_buffer;
1823 lookahead_token = get_token(1);
1824 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1825 yylval.str = strsave("upper");
1826 return VARIABLE;
1828 else
1829 return t;
1830 case LOWER:
1831 // recognise LOWER only before LEFT or RIGHT
1832 old_context_buffer = context_buffer;
1833 lookahead_token = get_token(1);
1834 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1835 yylval.str = strsave("lower");
1836 return VARIABLE;
1838 else
1839 return t;
1840 case NORTH:
1841 // recognise NORTH only before OF
1842 old_context_buffer = context_buffer;
1843 lookahead_token = get_token(1);
1844 if (lookahead_token != OF) {
1845 yylval.str = strsave("north");
1846 return VARIABLE;
1848 else
1849 return t;
1850 case SOUTH:
1851 // recognise SOUTH only before OF
1852 old_context_buffer = context_buffer;
1853 lookahead_token = get_token(1);
1854 if (lookahead_token != OF) {
1855 yylval.str = strsave("south");
1856 return VARIABLE;
1858 else
1859 return t;
1860 case EAST:
1861 // recognise EAST only before OF
1862 old_context_buffer = context_buffer;
1863 lookahead_token = get_token(1);
1864 if (lookahead_token != OF) {
1865 yylval.str = strsave("east");
1866 return VARIABLE;
1868 else
1869 return t;
1870 case WEST:
1871 // recognise WEST only before OF
1872 old_context_buffer = context_buffer;
1873 lookahead_token = get_token(1);
1874 if (lookahead_token != OF) {
1875 yylval.str = strsave("west");
1876 return VARIABLE;
1878 else
1879 return t;
1880 case TOP:
1881 // recognise TOP only before OF
1882 old_context_buffer = context_buffer;
1883 lookahead_token = get_token(1);
1884 if (lookahead_token != OF) {
1885 yylval.str = strsave("top");
1886 return VARIABLE;
1888 else
1889 return t;
1890 case BOTTOM:
1891 // recognise BOTTOM only before OF
1892 old_context_buffer = context_buffer;
1893 lookahead_token = get_token(1);
1894 if (lookahead_token != OF) {
1895 yylval.str = strsave("bottom");
1896 return VARIABLE;
1898 else
1899 return t;
1900 case CENTER:
1901 // recognise CENTER only before OF
1902 old_context_buffer = context_buffer;
1903 lookahead_token = get_token(1);
1904 if (lookahead_token != OF) {
1905 yylval.str = strsave("center");
1906 return VARIABLE;
1908 else
1909 return t;
1910 case START:
1911 // recognise START only before OF
1912 old_context_buffer = context_buffer;
1913 lookahead_token = get_token(1);
1914 if (lookahead_token != OF) {
1915 yylval.str = strsave("start");
1916 return VARIABLE;
1918 else
1919 return t;
1920 case END:
1921 // recognise END only before OF
1922 old_context_buffer = context_buffer;
1923 lookahead_token = get_token(1);
1924 if (lookahead_token != OF) {
1925 yylval.str = strsave("end");
1926 return VARIABLE;
1928 else
1929 return t;
1930 default:
1931 return t;
1936 void lex_error(const char *message,
1937 const errarg &arg1,
1938 const errarg &arg2,
1939 const errarg &arg3)
1941 const char *filename;
1942 int lineno;
1943 if (!input_stack::get_location(&filename, &lineno))
1944 error(message, arg1, arg2, arg3);
1945 else
1946 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1949 void lex_warning(const char *message,
1950 const errarg &arg1,
1951 const errarg &arg2,
1952 const errarg &arg3)
1954 const char *filename;
1955 int lineno;
1956 if (!input_stack::get_location(&filename, &lineno))
1957 warning(message, arg1, arg2, arg3);
1958 else
1959 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1962 void yyerror(const char *s)
1964 const char *filename;
1965 int lineno;
1966 const char *context = 0;
1967 if (lookahead_token == -1) {
1968 if (context_buffer.length() > 0) {
1969 context_buffer += '\0';
1970 context = context_buffer.contents();
1973 else {
1974 if (old_context_buffer.length() > 0) {
1975 old_context_buffer += '\0';
1976 context = old_context_buffer.contents();
1979 if (!input_stack::get_location(&filename, &lineno)) {
1980 if (context) {
1981 if (context[0] == '\n' && context[1] == '\0')
1982 error("%1 before newline", s);
1983 else
1984 error("%1 before `%2'", s, context);
1986 else
1987 error("%1 at end of picture", s);
1989 else {
1990 if (context) {
1991 if (context[0] == '\n' && context[1] == '\0')
1992 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1993 else
1994 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1995 s, context);
1997 else
1998 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);