No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / pic / lex.cpp
blobd077e424962070787fc489325b42bf9468276bc7
1 /* $NetBSD$ */
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "pic.h"
25 #include "ptable.h"
26 #include "object.h"
27 #include "pic_tab.h"
29 declare_ptable(char)
30 implement_ptable(char)
32 PTABLE(char) macro_table;
34 class macro_input : public input {
35 char *s;
36 char *p;
37 public:
38 macro_input(const char *);
39 ~macro_input();
40 int get();
41 int peek();
44 class argument_macro_input : public input {
45 char *s;
46 char *p;
47 char *ap;
48 int argc;
49 char *argv[9];
50 public:
51 argument_macro_input(const char *, int, char **);
52 ~argument_macro_input();
53 int get();
54 int peek();
57 input::input() : next(0)
61 input::~input()
65 int input::get_location(const char **, int *)
67 return 0;
70 file_input::file_input(FILE *f, const char *fn)
71 : fp(f), filename(fn), lineno(0), ptr("")
75 file_input::~file_input()
77 fclose(fp);
80 int file_input::read_line()
82 for (;;) {
83 line.clear();
84 lineno++;
85 for (;;) {
86 int c = getc(fp);
87 if (c == EOF)
88 break;
89 else if (invalid_input_char(c))
90 lex_error("invalid input character code %1", c);
91 else {
92 line += char(c);
93 if (c == '\n')
94 break;
97 if (line.length() == 0)
98 return 0;
99 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
100 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
101 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
102 || compatible_flag))) {
103 line += '\0';
104 ptr = line.contents();
105 return 1;
110 int file_input::get()
112 if (*ptr != '\0' || read_line())
113 return (unsigned char)*ptr++;
114 else
115 return EOF;
118 int file_input::peek()
120 if (*ptr != '\0' || read_line())
121 return (unsigned char)*ptr;
122 else
123 return EOF;
126 int file_input::get_location(const char **fnp, int *lnp)
128 *fnp = filename;
129 *lnp = lineno;
130 return 1;
133 macro_input::macro_input(const char *str)
135 p = s = strsave(str);
138 macro_input::~macro_input()
140 a_delete s;
143 int macro_input::get()
145 if (p == 0 || *p == '\0')
146 return EOF;
147 else
148 return (unsigned char)*p++;
151 int macro_input::peek()
153 if (p == 0 || *p == '\0')
154 return EOF;
155 else
156 return (unsigned char)*p;
159 // Character representing $1. Must be invalid input character.
160 #define ARG1 14
162 char *process_body(const char *body)
164 char *s = strsave(body);
165 int j = 0;
166 for (int i = 0; s[i] != '\0'; i++)
167 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
168 if (s[i+1] != '0')
169 s[j++] = ARG1 + s[++i] - '1';
171 else
172 s[j++] = s[i];
173 s[j] = '\0';
174 return s;
178 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
179 : ap(0), argc(ac)
181 for (int i = 0; i < argc; i++)
182 argv[i] = av[i];
183 p = s = process_body(body);
187 argument_macro_input::~argument_macro_input()
189 for (int i = 0; i < argc; i++)
190 a_delete argv[i];
191 a_delete s;
194 int argument_macro_input::get()
196 if (ap) {
197 if (*ap != '\0')
198 return (unsigned char)*ap++;
199 ap = 0;
201 if (p == 0)
202 return EOF;
203 while (*p >= ARG1 && *p <= ARG1 + 8) {
204 int i = *p++ - ARG1;
205 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
206 ap = argv[i];
207 return (unsigned char)*ap++;
210 if (*p == '\0')
211 return EOF;
212 return (unsigned char)*p++;
215 int argument_macro_input::peek()
217 if (ap) {
218 if (*ap != '\0')
219 return (unsigned char)*ap;
220 ap = 0;
222 if (p == 0)
223 return EOF;
224 while (*p >= ARG1 && *p <= ARG1 + 8) {
225 int i = *p++ - ARG1;
226 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
227 ap = argv[i];
228 return (unsigned char)*ap;
231 if (*p == '\0')
232 return EOF;
233 return (unsigned char)*p;
236 class input_stack {
237 static input *current_input;
238 static int bol_flag;
239 public:
240 static void push(input *);
241 static void clear();
242 static int get_char();
243 static int peek_char();
244 static int get_location(const char **fnp, int *lnp);
245 static void push_back(unsigned char c, int was_bol = 0);
246 static int bol();
249 input *input_stack::current_input = 0;
250 int input_stack::bol_flag = 0;
252 inline int input_stack::bol()
254 return bol_flag;
257 void input_stack::clear()
259 while (current_input != 0) {
260 input *tem = current_input;
261 current_input = current_input->next;
262 delete tem;
264 bol_flag = 1;
267 void input_stack::push(input *in)
269 in->next = current_input;
270 current_input = in;
273 void lex_init(input *top)
275 input_stack::clear();
276 input_stack::push(top);
279 void lex_cleanup()
281 while (input_stack::get_char() != EOF)
285 int input_stack::get_char()
287 while (current_input != 0) {
288 int c = current_input->get();
289 if (c != EOF) {
290 bol_flag = c == '\n';
291 return c;
293 // don't pop the top-level input off the stack
294 if (current_input->next == 0)
295 return EOF;
296 input *tem = current_input;
297 current_input = current_input->next;
298 delete tem;
300 return EOF;
303 int input_stack::peek_char()
305 while (current_input != 0) {
306 int c = current_input->peek();
307 if (c != EOF)
308 return c;
309 if (current_input->next == 0)
310 return EOF;
311 input *tem = current_input;
312 current_input = current_input->next;
313 delete tem;
315 return EOF;
318 class char_input : public input {
319 int c;
320 public:
321 char_input(int);
322 int get();
323 int peek();
326 char_input::char_input(int n) : c((unsigned char)n)
330 int char_input::get()
332 int n = c;
333 c = EOF;
334 return n;
337 int char_input::peek()
339 return c;
342 void input_stack::push_back(unsigned char c, int was_bol)
344 push(new char_input(c));
345 bol_flag = was_bol;
348 int input_stack::get_location(const char **fnp, int *lnp)
350 for (input *p = current_input; p; p = p->next)
351 if (p->get_location(fnp, lnp))
352 return 1;
353 return 0;
356 string context_buffer;
358 string token_buffer;
359 double token_double;
360 int token_int;
362 void interpolate_macro_with_args(const char *body)
364 char *argv[9];
365 int argc = 0;
366 int i;
367 for (i = 0; i < 9; i++)
368 argv[i] = 0;
369 int level = 0;
370 int c;
371 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
372 do {
373 token_buffer.clear();
374 for (;;) {
375 c = input_stack::get_char();
376 if (c == EOF) {
377 lex_error("end of input while scanning macro arguments");
378 break;
380 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
381 if (token_buffer.length() > 0) {
382 token_buffer += '\0';
383 argv[argc] = strsave(token_buffer.contents());
385 // for `foo()', argc = 0
386 if (argc > 0 || c != ')' || i > 0)
387 argc++;
388 break;
390 token_buffer += char(c);
391 switch (state) {
392 case NORMAL:
393 if (c == '"')
394 state = IN_STRING;
395 else if (c == '(')
396 level++;
397 else if (c == ')')
398 level--;
399 break;
400 case IN_STRING:
401 if (c == '"')
402 state = NORMAL;
403 else if (c == '\\')
404 state = IN_STRING_QUOTED;
405 break;
406 case IN_STRING_QUOTED:
407 state = IN_STRING;
408 break;
411 } while (c != ')' && c != EOF);
412 input_stack::push(new argument_macro_input(body, argc, argv));
415 static int docmp(const char *s1, int n1, const char *s2, int n2)
417 if (n1 < n2) {
418 int r = memcmp(s1, s2, n1);
419 return r ? r : -1;
421 else if (n1 > n2) {
422 int r = memcmp(s1, s2, n2);
423 return r ? r : 1;
425 else
426 return memcmp(s1, s2, n1);
429 int lookup_keyword(const char *str, int len)
431 static struct keyword {
432 const char *name;
433 int token;
434 } table[] = {
435 { "Here", HERE },
436 { "above", ABOVE },
437 { "aligned", ALIGNED },
438 { "and", AND },
439 { "arc", ARC },
440 { "arrow", ARROW },
441 { "at", AT },
442 { "atan2", ATAN2 },
443 { "below", BELOW },
444 { "between", BETWEEN },
445 { "bottom", BOTTOM },
446 { "box", BOX },
447 { "by", BY },
448 { "ccw", CCW },
449 { "center", CENTER },
450 { "chop", CHOP },
451 { "circle", CIRCLE },
452 { "color", COLORED },
453 { "colored", COLORED },
454 { "colour", COLORED },
455 { "coloured", COLORED },
456 { "command", COMMAND },
457 { "copy", COPY },
458 { "cos", COS },
459 { "cw", CW },
460 { "dashed", DASHED },
461 { "define", DEFINE },
462 { "diam", DIAMETER },
463 { "diameter", DIAMETER },
464 { "do", DO },
465 { "dotted", DOTTED },
466 { "down", DOWN },
467 { "east", EAST },
468 { "ellipse", ELLIPSE },
469 { "else", ELSE },
470 { "end", END },
471 { "exp", EXP },
472 { "figname", FIGNAME },
473 { "fill", FILL },
474 { "filled", FILL },
475 { "for", FOR },
476 { "from", FROM },
477 { "height", HEIGHT },
478 { "ht", HEIGHT },
479 { "if", IF },
480 { "int", INT },
481 { "invis", INVISIBLE },
482 { "invisible", INVISIBLE },
483 { "last", LAST },
484 { "left", LEFT },
485 { "line", LINE },
486 { "ljust", LJUST },
487 { "log", LOG },
488 { "lower", LOWER },
489 { "max", K_MAX },
490 { "min", K_MIN },
491 { "move", MOVE },
492 { "north", NORTH },
493 { "of", OF },
494 { "outline", OUTLINED },
495 { "outlined", OUTLINED },
496 { "plot", PLOT },
497 { "print", PRINT },
498 { "rad", RADIUS },
499 { "radius", RADIUS },
500 { "rand", RAND },
501 { "reset", RESET },
502 { "right", RIGHT },
503 { "rjust", RJUST },
504 { "same", SAME },
505 { "sh", SH },
506 { "shaded", SHADED },
507 { "sin", SIN },
508 { "solid", SOLID },
509 { "south", SOUTH },
510 { "spline", SPLINE },
511 { "sprintf", SPRINTF },
512 { "sqrt", SQRT },
513 { "srand", SRAND },
514 { "start", START },
515 { "the", THE },
516 { "then", THEN },
517 { "thick", THICKNESS },
518 { "thickness", THICKNESS },
519 { "thru", THRU },
520 { "to", TO },
521 { "top", TOP },
522 { "undef", UNDEF },
523 { "until", UNTIL },
524 { "up", UP },
525 { "upper", UPPER },
526 { "way", WAY },
527 { "west", WEST },
528 { "wid", WIDTH },
529 { "width", WIDTH },
530 { "with", WITH },
533 const keyword *start = table;
534 const keyword *end = table + sizeof(table)/sizeof(table[0]);
535 while (start < end) {
536 // start <= target < end
537 const keyword *mid = start + (end - start)/2;
539 int cmp = docmp(str, len, mid->name, strlen(mid->name));
540 if (cmp == 0)
541 return mid->token;
542 if (cmp < 0)
543 end = mid;
544 else
545 start = mid + 1;
547 return 0;
550 int get_token_after_dot(int c)
552 // get_token deals with the case where c is a digit
553 switch (c) {
554 case 'h':
555 input_stack::get_char();
556 c = input_stack::peek_char();
557 if (c == 't') {
558 input_stack::get_char();
559 context_buffer = ".ht";
560 return DOT_HT;
562 else if (c == 'e') {
563 input_stack::get_char();
564 c = input_stack::peek_char();
565 if (c == 'i') {
566 input_stack::get_char();
567 c = input_stack::peek_char();
568 if (c == 'g') {
569 input_stack::get_char();
570 c = input_stack::peek_char();
571 if (c == 'h') {
572 input_stack::get_char();
573 c = input_stack::peek_char();
574 if (c == 't') {
575 input_stack::get_char();
576 context_buffer = ".height";
577 return DOT_HT;
579 input_stack::push_back('h');
581 input_stack::push_back('g');
583 input_stack::push_back('i');
585 input_stack::push_back('e');
587 input_stack::push_back('h');
588 return '.';
589 case 'x':
590 input_stack::get_char();
591 context_buffer = ".x";
592 return DOT_X;
593 case 'y':
594 input_stack::get_char();
595 context_buffer = ".y";
596 return DOT_Y;
597 case 'c':
598 input_stack::get_char();
599 c = input_stack::peek_char();
600 if (c == 'e') {
601 input_stack::get_char();
602 c = input_stack::peek_char();
603 if (c == 'n') {
604 input_stack::get_char();
605 c = input_stack::peek_char();
606 if (c == 't') {
607 input_stack::get_char();
608 c = input_stack::peek_char();
609 if (c == 'e') {
610 input_stack::get_char();
611 c = input_stack::peek_char();
612 if (c == 'r') {
613 input_stack::get_char();
614 context_buffer = ".center";
615 return DOT_C;
617 input_stack::push_back('e');
619 input_stack::push_back('t');
621 input_stack::push_back('n');
623 input_stack::push_back('e');
625 context_buffer = ".c";
626 return DOT_C;
627 case 'n':
628 input_stack::get_char();
629 c = input_stack::peek_char();
630 if (c == 'e') {
631 input_stack::get_char();
632 context_buffer = ".ne";
633 return DOT_NE;
635 else if (c == 'w') {
636 input_stack::get_char();
637 context_buffer = ".nw";
638 return DOT_NW;
640 else {
641 context_buffer = ".n";
642 return DOT_N;
644 break;
645 case 'e':
646 input_stack::get_char();
647 c = input_stack::peek_char();
648 if (c == 'n') {
649 input_stack::get_char();
650 c = input_stack::peek_char();
651 if (c == 'd') {
652 input_stack::get_char();
653 context_buffer = ".end";
654 return DOT_END;
656 input_stack::push_back('n');
657 context_buffer = ".e";
658 return DOT_E;
660 context_buffer = ".e";
661 return DOT_E;
662 case 'w':
663 input_stack::get_char();
664 c = input_stack::peek_char();
665 if (c == 'i') {
666 input_stack::get_char();
667 c = input_stack::peek_char();
668 if (c == 'd') {
669 input_stack::get_char();
670 c = input_stack::peek_char();
671 if (c == 't') {
672 input_stack::get_char();
673 c = input_stack::peek_char();
674 if (c == 'h') {
675 input_stack::get_char();
676 context_buffer = ".width";
677 return DOT_WID;
679 input_stack::push_back('t');
681 context_buffer = ".wid";
682 return DOT_WID;
684 input_stack::push_back('i');
686 context_buffer = ".w";
687 return DOT_W;
688 case 's':
689 input_stack::get_char();
690 c = input_stack::peek_char();
691 if (c == 'e') {
692 input_stack::get_char();
693 context_buffer = ".se";
694 return DOT_SE;
696 else if (c == 'w') {
697 input_stack::get_char();
698 context_buffer = ".sw";
699 return DOT_SW;
701 else {
702 if (c == 't') {
703 input_stack::get_char();
704 c = input_stack::peek_char();
705 if (c == 'a') {
706 input_stack::get_char();
707 c = input_stack::peek_char();
708 if (c == 'r') {
709 input_stack::get_char();
710 c = input_stack::peek_char();
711 if (c == 't') {
712 input_stack::get_char();
713 context_buffer = ".start";
714 return DOT_START;
716 input_stack::push_back('r');
718 input_stack::push_back('a');
720 input_stack::push_back('t');
722 context_buffer = ".s";
723 return DOT_S;
725 break;
726 case 't':
727 input_stack::get_char();
728 c = input_stack::peek_char();
729 if (c == 'o') {
730 input_stack::get_char();
731 c = input_stack::peek_char();
732 if (c == 'p') {
733 input_stack::get_char();
734 context_buffer = ".top";
735 return DOT_N;
737 input_stack::push_back('o');
739 context_buffer = ".t";
740 return DOT_N;
741 case 'l':
742 input_stack::get_char();
743 c = input_stack::peek_char();
744 if (c == 'e') {
745 input_stack::get_char();
746 c = input_stack::peek_char();
747 if (c == 'f') {
748 input_stack::get_char();
749 c = input_stack::peek_char();
750 if (c == 't') {
751 input_stack::get_char();
752 context_buffer = ".left";
753 return DOT_W;
755 input_stack::push_back('f');
757 input_stack::push_back('e');
759 context_buffer = ".l";
760 return DOT_W;
761 case 'r':
762 input_stack::get_char();
763 c = input_stack::peek_char();
764 if (c == 'a') {
765 input_stack::get_char();
766 c = input_stack::peek_char();
767 if (c == 'd') {
768 input_stack::get_char();
769 context_buffer = ".rad";
770 return DOT_RAD;
772 input_stack::push_back('a');
774 else if (c == 'i') {
775 input_stack::get_char();
776 c = input_stack::peek_char();
777 if (c == 'g') {
778 input_stack::get_char();
779 c = input_stack::peek_char();
780 if (c == 'h') {
781 input_stack::get_char();
782 c = input_stack::peek_char();
783 if (c == 't') {
784 input_stack::get_char();
785 context_buffer = ".right";
786 return DOT_E;
788 input_stack::push_back('h');
790 input_stack::push_back('g');
792 input_stack::push_back('i');
794 context_buffer = ".r";
795 return DOT_E;
796 case 'b':
797 input_stack::get_char();
798 c = input_stack::peek_char();
799 if (c == 'o') {
800 input_stack::get_char();
801 c = input_stack::peek_char();
802 if (c == 't') {
803 input_stack::get_char();
804 c = input_stack::peek_char();
805 if (c == 't') {
806 input_stack::get_char();
807 c = input_stack::peek_char();
808 if (c == 'o') {
809 input_stack::get_char();
810 c = input_stack::peek_char();
811 if (c == 'm') {
812 input_stack::get_char();
813 context_buffer = ".bottom";
814 return DOT_S;
816 input_stack::push_back('o');
818 input_stack::push_back('t');
820 context_buffer = ".bot";
821 return DOT_S;
823 input_stack::push_back('o');
825 context_buffer = ".b";
826 return DOT_S;
827 default:
828 context_buffer = '.';
829 return '.';
833 int get_token(int lookup_flag)
835 context_buffer.clear();
836 for (;;) {
837 int n = 0;
838 int bol = input_stack::bol();
839 int c = input_stack::get_char();
840 if (bol && c == command_char) {
841 token_buffer.clear();
842 token_buffer += c;
843 // the newline is not part of the token
844 for (;;) {
845 c = input_stack::peek_char();
846 if (c == EOF || c == '\n')
847 break;
848 input_stack::get_char();
849 token_buffer += char(c);
851 context_buffer = token_buffer;
852 return COMMAND_LINE;
854 switch (c) {
855 case EOF:
856 return EOF;
857 case ' ':
858 case '\t':
859 break;
860 case '\\':
862 int d = input_stack::peek_char();
863 if (d != '\n') {
864 context_buffer = '\\';
865 return '\\';
867 input_stack::get_char();
868 break;
870 case '#':
871 do {
872 c = input_stack::get_char();
873 } while (c != '\n' && c != EOF);
874 if (c == '\n')
875 context_buffer = '\n';
876 return c;
877 case '"':
878 context_buffer = '"';
879 token_buffer.clear();
880 for (;;) {
881 c = input_stack::get_char();
882 if (c == '\\') {
883 context_buffer += '\\';
884 c = input_stack::peek_char();
885 if (c == '"') {
886 input_stack::get_char();
887 token_buffer += '"';
888 context_buffer += '"';
890 else
891 token_buffer += '\\';
893 else if (c == '\n') {
894 error("newline in string");
895 break;
897 else if (c == EOF) {
898 error("missing `\"'");
899 break;
901 else if (c == '"') {
902 context_buffer += '"';
903 break;
905 else {
906 context_buffer += char(c);
907 token_buffer += char(c);
910 return TEXT;
911 case '0':
912 case '1':
913 case '2':
914 case '3':
915 case '4':
916 case '5':
917 case '6':
918 case '7':
919 case '8':
920 case '9':
922 int overflow = 0;
923 n = 0;
924 for (;;) {
925 if (n > (INT_MAX - 9)/10) {
926 overflow = 1;
927 break;
929 n *= 10;
930 n += c - '0';
931 context_buffer += char(c);
932 c = input_stack::peek_char();
933 if (c == EOF || !csdigit(c))
934 break;
935 c = input_stack::get_char();
937 token_double = n;
938 if (overflow) {
939 for (;;) {
940 token_double *= 10.0;
941 token_double += c - '0';
942 context_buffer += char(c);
943 c = input_stack::peek_char();
944 if (c == EOF || !csdigit(c))
945 break;
946 c = input_stack::get_char();
948 // if somebody asks for 1000000000000th, we will silently
949 // give them INT_MAXth
950 double temp = token_double; // work around gas 1.34/sparc bug
951 if (token_double > INT_MAX)
952 n = INT_MAX;
953 else
954 n = int(temp);
957 switch (c) {
958 case 'i':
959 case 'I':
960 context_buffer += char(c);
961 input_stack::get_char();
962 return NUMBER;
963 case '.':
965 context_buffer += '.';
966 input_stack::get_char();
967 got_dot:
968 double factor = 1.0;
969 for (;;) {
970 c = input_stack::peek_char();
971 if (c == EOF || !csdigit(c))
972 break;
973 input_stack::get_char();
974 context_buffer += char(c);
975 factor /= 10.0;
976 if (c != '0')
977 token_double += factor*(c - '0');
979 if (c != 'e' && c != 'E') {
980 if (c == 'i' || c == 'I') {
981 context_buffer += char(c);
982 input_stack::get_char();
984 return NUMBER;
987 // fall through
988 case 'e':
989 case 'E':
991 int echar = c;
992 input_stack::get_char();
993 c = input_stack::peek_char();
994 int sign = '+';
995 if (c == '+' || c == '-') {
996 sign = c;
997 input_stack::get_char();
998 c = input_stack::peek_char();
999 if (c == EOF || !csdigit(c)) {
1000 input_stack::push_back(sign);
1001 input_stack::push_back(echar);
1002 return NUMBER;
1004 context_buffer += char(echar);
1005 context_buffer += char(sign);
1007 else {
1008 if (c == EOF || !csdigit(c)) {
1009 input_stack::push_back(echar);
1010 return NUMBER;
1012 context_buffer += char(echar);
1014 input_stack::get_char();
1015 context_buffer += char(c);
1016 n = c - '0';
1017 for (;;) {
1018 c = input_stack::peek_char();
1019 if (c == EOF || !csdigit(c))
1020 break;
1021 input_stack::get_char();
1022 context_buffer += char(c);
1023 n = n*10 + (c - '0');
1025 if (sign == '-')
1026 n = -n;
1027 if (c == 'i' || c == 'I') {
1028 context_buffer += char(c);
1029 input_stack::get_char();
1031 token_double *= pow(10.0, n);
1032 return NUMBER;
1034 case 'n':
1035 input_stack::get_char();
1036 c = input_stack::peek_char();
1037 if (c == 'd') {
1038 input_stack::get_char();
1039 token_int = n;
1040 context_buffer += "nd";
1041 return ORDINAL;
1043 input_stack::push_back('n');
1044 return NUMBER;
1045 case 'r':
1046 input_stack::get_char();
1047 c = input_stack::peek_char();
1048 if (c == 'd') {
1049 input_stack::get_char();
1050 token_int = n;
1051 context_buffer += "rd";
1052 return ORDINAL;
1054 input_stack::push_back('r');
1055 return NUMBER;
1056 case 't':
1057 input_stack::get_char();
1058 c = input_stack::peek_char();
1059 if (c == 'h') {
1060 input_stack::get_char();
1061 token_int = n;
1062 context_buffer += "th";
1063 return ORDINAL;
1065 input_stack::push_back('t');
1066 return NUMBER;
1067 case 's':
1068 input_stack::get_char();
1069 c = input_stack::peek_char();
1070 if (c == 't') {
1071 input_stack::get_char();
1072 token_int = n;
1073 context_buffer += "st";
1074 return ORDINAL;
1076 input_stack::push_back('s');
1077 return NUMBER;
1078 default:
1079 return NUMBER;
1081 break;
1082 case '\'':
1084 c = input_stack::peek_char();
1085 if (c == 't') {
1086 input_stack::get_char();
1087 c = input_stack::peek_char();
1088 if (c == 'h') {
1089 input_stack::get_char();
1090 context_buffer = "'th";
1091 return TH;
1093 else
1094 input_stack::push_back('t');
1096 context_buffer = "'";
1097 return '\'';
1099 case '.':
1101 c = input_stack::peek_char();
1102 if (c != EOF && csdigit(c)) {
1103 n = 0;
1104 token_double = 0.0;
1105 context_buffer = '.';
1106 goto got_dot;
1108 return get_token_after_dot(c);
1110 case '<':
1111 c = input_stack::peek_char();
1112 if (c == '-') {
1113 input_stack::get_char();
1114 c = input_stack::peek_char();
1115 if (c == '>') {
1116 input_stack::get_char();
1117 context_buffer = "<->";
1118 return DOUBLE_ARROW_HEAD;
1120 context_buffer = "<-";
1121 return LEFT_ARROW_HEAD;
1123 else if (c == '=') {
1124 input_stack::get_char();
1125 context_buffer = "<=";
1126 return LESSEQUAL;
1128 context_buffer = "<";
1129 return '<';
1130 case '-':
1131 c = input_stack::peek_char();
1132 if (c == '>') {
1133 input_stack::get_char();
1134 context_buffer = "->";
1135 return RIGHT_ARROW_HEAD;
1137 context_buffer = "-";
1138 return '-';
1139 case '!':
1140 c = input_stack::peek_char();
1141 if (c == '=') {
1142 input_stack::get_char();
1143 context_buffer = "!=";
1144 return NOTEQUAL;
1146 context_buffer = "!";
1147 return '!';
1148 case '>':
1149 c = input_stack::peek_char();
1150 if (c == '=') {
1151 input_stack::get_char();
1152 context_buffer = ">=";
1153 return GREATEREQUAL;
1155 context_buffer = ">";
1156 return '>';
1157 case '=':
1158 c = input_stack::peek_char();
1159 if (c == '=') {
1160 input_stack::get_char();
1161 context_buffer = "==";
1162 return EQUALEQUAL;
1164 context_buffer = "=";
1165 return '=';
1166 case '&':
1167 c = input_stack::peek_char();
1168 if (c == '&') {
1169 input_stack::get_char();
1170 context_buffer = "&&";
1171 return ANDAND;
1173 context_buffer = "&";
1174 return '&';
1175 case '|':
1176 c = input_stack::peek_char();
1177 if (c == '|') {
1178 input_stack::get_char();
1179 context_buffer = "||";
1180 return OROR;
1182 context_buffer = "|";
1183 return '|';
1184 default:
1185 if (c != EOF && csalpha(c)) {
1186 token_buffer.clear();
1187 token_buffer = c;
1188 for (;;) {
1189 c = input_stack::peek_char();
1190 if (c == EOF || (!csalnum(c) && c != '_'))
1191 break;
1192 input_stack::get_char();
1193 token_buffer += char(c);
1195 int tok = lookup_keyword(token_buffer.contents(),
1196 token_buffer.length());
1197 if (tok != 0) {
1198 context_buffer = token_buffer;
1199 return tok;
1201 char *def = 0;
1202 if (lookup_flag) {
1203 token_buffer += '\0';
1204 def = macro_table.lookup(token_buffer.contents());
1205 token_buffer.set_length(token_buffer.length() - 1);
1206 if (def) {
1207 if (c == '(') {
1208 input_stack::get_char();
1209 interpolate_macro_with_args(def);
1211 else
1212 input_stack::push(new macro_input(def));
1215 if (!def) {
1216 context_buffer = token_buffer;
1217 if (csupper(token_buffer[0]))
1218 return LABEL;
1219 else
1220 return VARIABLE;
1223 else {
1224 context_buffer = char(c);
1225 return (unsigned char)c;
1227 break;
1232 int get_delimited()
1234 token_buffer.clear();
1235 int c = input_stack::get_char();
1236 while (c == ' ' || c == '\t' || c == '\n')
1237 c = input_stack::get_char();
1238 if (c == EOF) {
1239 lex_error("missing delimiter");
1240 return 0;
1242 context_buffer = char(c);
1243 int had_newline = 0;
1244 int start = c;
1245 int level = 0;
1246 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1247 for (;;) {
1248 c = input_stack::get_char();
1249 if (c == EOF) {
1250 lex_error("missing closing delimiter");
1251 return 0;
1253 if (c == '\n')
1254 had_newline = 1;
1255 else if (!had_newline)
1256 context_buffer += char(c);
1257 switch (state) {
1258 case NORMAL:
1259 if (start == '{') {
1260 if (c == '{') {
1261 level++;
1262 break;
1264 if (c == '}') {
1265 if (--level < 0)
1266 state = DELIM_END;
1267 break;
1270 else {
1271 if (c == start) {
1272 state = DELIM_END;
1273 break;
1276 if (c == '"')
1277 state = IN_STRING;
1278 break;
1279 case IN_STRING_QUOTED:
1280 if (c == '\n')
1281 state = NORMAL;
1282 else
1283 state = IN_STRING;
1284 break;
1285 case IN_STRING:
1286 if (c == '"' || c == '\n')
1287 state = NORMAL;
1288 else if (c == '\\')
1289 state = IN_STRING_QUOTED;
1290 break;
1291 case DELIM_END:
1292 // This case it just to shut cfront 2.0 up.
1293 default:
1294 assert(0);
1296 if (state == DELIM_END)
1297 break;
1298 token_buffer += c;
1300 return 1;
1303 void do_define()
1305 int t = get_token(0); // do not expand what we are defining
1306 if (t != VARIABLE && t != LABEL) {
1307 lex_error("can only define variable or placename");
1308 return;
1310 token_buffer += '\0';
1311 string nm = token_buffer;
1312 const char *name = nm.contents();
1313 if (!get_delimited())
1314 return;
1315 token_buffer += '\0';
1316 macro_table.define(name, strsave(token_buffer.contents()));
1319 void do_undef()
1321 int t = get_token(0); // do not expand what we are undefining
1322 if (t != VARIABLE && t != LABEL) {
1323 lex_error("can only define variable or placename");
1324 return;
1326 token_buffer += '\0';
1327 macro_table.define(token_buffer.contents(), 0);
1331 class for_input : public input {
1332 char *var;
1333 char *body;
1334 double from;
1335 double to;
1336 int by_is_multiplicative;
1337 double by;
1338 const char *p;
1339 int done_newline;
1340 public:
1341 for_input(char *, double, double, int, double, char *);
1342 ~for_input();
1343 int get();
1344 int peek();
1347 for_input::for_input(char *vr, double f, double t,
1348 int bim, double b, char *bd)
1349 : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1350 p(body), done_newline(0)
1354 for_input::~for_input()
1356 a_delete var;
1357 a_delete body;
1360 int for_input::get()
1362 if (p == 0)
1363 return EOF;
1364 for (;;) {
1365 if (*p != '\0')
1366 return (unsigned char)*p++;
1367 if (!done_newline) {
1368 done_newline = 1;
1369 return '\n';
1371 double val;
1372 if (!lookup_variable(var, &val)) {
1373 lex_error("body of `for' terminated enclosing block");
1374 return EOF;
1376 if (by_is_multiplicative)
1377 val *= by;
1378 else
1379 val += by;
1380 define_variable(var, val);
1381 if ((from <= to && val > to)
1382 || (from >= to && val < to)) {
1383 p = 0;
1384 return EOF;
1386 p = body;
1387 done_newline = 0;
1391 int for_input::peek()
1393 if (p == 0)
1394 return EOF;
1395 if (*p != '\0')
1396 return (unsigned char)*p;
1397 if (!done_newline)
1398 return '\n';
1399 double val;
1400 if (!lookup_variable(var, &val))
1401 return EOF;
1402 if (by_is_multiplicative) {
1403 if (val * by > to)
1404 return EOF;
1406 else {
1407 if ((from <= to && val + by > to)
1408 || (from >= to && val + by < to))
1409 return EOF;
1411 if (*body == '\0')
1412 return EOF;
1413 return (unsigned char)*body;
1416 void do_for(char *var, double from, double to, int by_is_multiplicative,
1417 double by, char *body)
1419 define_variable(var, from);
1420 if ((by_is_multiplicative && by <= 0)
1421 || (by > 0 && from > to)
1422 || (by < 0 && from < to))
1423 return;
1424 input_stack::push(new for_input(var, from, to,
1425 by_is_multiplicative, by, body));
1429 void do_copy(const char *filename)
1431 errno = 0;
1432 FILE *fp = fopen(filename, "r");
1433 if (fp == 0) {
1434 lex_error("can't open `%1': %2", filename, strerror(errno));
1435 return;
1437 input_stack::push(new file_input(fp, filename));
1440 class copy_thru_input : public input {
1441 int done;
1442 char *body;
1443 char *until;
1444 const char *p;
1445 const char *ap;
1446 int argv[9];
1447 int argc;
1448 string line;
1449 int get_line();
1450 virtual int inget() = 0;
1451 public:
1452 copy_thru_input(const char *b, const char *u);
1453 ~copy_thru_input();
1454 int get();
1455 int peek();
1458 class copy_file_thru_input : public copy_thru_input {
1459 input *in;
1460 public:
1461 copy_file_thru_input(input *, const char *b, const char *u);
1462 ~copy_file_thru_input();
1463 int inget();
1466 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1467 const char *u)
1468 : copy_thru_input(b, u), in(i)
1472 copy_file_thru_input::~copy_file_thru_input()
1474 delete in;
1477 int copy_file_thru_input::inget()
1479 if (!in)
1480 return EOF;
1481 else
1482 return in->get();
1485 class copy_rest_thru_input : public copy_thru_input {
1486 public:
1487 copy_rest_thru_input(const char *, const char *u);
1488 int inget();
1491 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1492 : copy_thru_input(b, u)
1496 int copy_rest_thru_input::inget()
1498 while (next != 0) {
1499 int c = next->get();
1500 if (c != EOF)
1501 return c;
1502 if (next->next == 0)
1503 return EOF;
1504 input *tem = next;
1505 next = next->next;
1506 delete tem;
1508 return EOF;
1512 copy_thru_input::copy_thru_input(const char *b, const char *u)
1513 : done(0)
1515 ap = 0;
1516 body = process_body(b);
1517 p = 0;
1518 until = strsave(u);
1522 copy_thru_input::~copy_thru_input()
1524 a_delete body;
1525 a_delete until;
1528 int copy_thru_input::get()
1530 if (ap) {
1531 if (*ap != '\0')
1532 return (unsigned char)*ap++;
1533 ap = 0;
1535 for (;;) {
1536 if (p == 0) {
1537 if (!get_line())
1538 break;
1539 p = body;
1541 if (*p == '\0') {
1542 p = 0;
1543 return '\n';
1545 while (*p >= ARG1 && *p <= ARG1 + 8) {
1546 int i = *p++ - ARG1;
1547 if (i < argc && line[argv[i]] != '\0') {
1548 ap = line.contents() + argv[i];
1549 return (unsigned char)*ap++;
1552 if (*p != '\0')
1553 return (unsigned char)*p++;
1555 return EOF;
1558 int copy_thru_input::peek()
1560 if (ap) {
1561 if (*ap != '\0')
1562 return (unsigned char)*ap;
1563 ap = 0;
1565 for (;;) {
1566 if (p == 0) {
1567 if (!get_line())
1568 break;
1569 p = body;
1571 if (*p == '\0')
1572 return '\n';
1573 while (*p >= ARG1 && *p <= ARG1 + 8) {
1574 int i = *p++ - ARG1;
1575 if (i < argc && line[argv[i]] != '\0') {
1576 ap = line.contents() + argv[i];
1577 return (unsigned char)*ap;
1580 if (*p != '\0')
1581 return (unsigned char)*p;
1583 return EOF;
1586 int copy_thru_input::get_line()
1588 if (done)
1589 return 0;
1590 line.clear();
1591 argc = 0;
1592 int c = inget();
1593 for (;;) {
1594 while (c == ' ')
1595 c = inget();
1596 if (c == EOF || c == '\n')
1597 break;
1598 if (argc == 9) {
1599 do {
1600 c = inget();
1601 } while (c != '\n' && c != EOF);
1602 break;
1604 argv[argc++] = line.length();
1605 do {
1606 line += char(c);
1607 c = inget();
1608 } while (c != ' ' && c != '\n');
1609 line += '\0';
1611 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1612 done = 1;
1613 return 0;
1615 return argc > 0 || c == '\n';
1618 class simple_file_input : public input {
1619 const char *filename;
1620 int lineno;
1621 FILE *fp;
1622 public:
1623 simple_file_input(FILE *, const char *);
1624 ~simple_file_input();
1625 int get();
1626 int peek();
1627 int get_location(const char **, int *);
1630 simple_file_input::simple_file_input(FILE *p, const char *s)
1631 : filename(s), lineno(1), fp(p)
1635 simple_file_input::~simple_file_input()
1637 // don't delete the filename
1638 fclose(fp);
1641 int simple_file_input::get()
1643 int c = getc(fp);
1644 while (invalid_input_char(c)) {
1645 error("invalid input character code %1", c);
1646 c = getc(fp);
1648 if (c == '\n')
1649 lineno++;
1650 return c;
1653 int simple_file_input::peek()
1655 int c = getc(fp);
1656 while (invalid_input_char(c)) {
1657 error("invalid input character code %1", c);
1658 c = getc(fp);
1660 if (c != EOF)
1661 ungetc(c, fp);
1662 return c;
1665 int simple_file_input::get_location(const char **fnp, int *lnp)
1667 *fnp = filename;
1668 *lnp = lineno;
1669 return 1;
1673 void copy_file_thru(const char *filename, const char *body, const char *until)
1675 errno = 0;
1676 FILE *fp = fopen(filename, "r");
1677 if (fp == 0) {
1678 lex_error("can't open `%1': %2", filename, strerror(errno));
1679 return;
1681 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1682 body, until);
1683 input_stack::push(in);
1686 void copy_rest_thru(const char *body, const char *until)
1688 input_stack::push(new copy_rest_thru_input(body, until));
1691 void push_body(const char *s)
1693 input_stack::push(new char_input('\n'));
1694 input_stack::push(new macro_input(s));
1697 int delim_flag = 0;
1699 char *get_thru_arg()
1701 int c = input_stack::peek_char();
1702 while (c == ' ') {
1703 input_stack::get_char();
1704 c = input_stack::peek_char();
1706 if (c != EOF && csalpha(c)) {
1707 // looks like a macro
1708 input_stack::get_char();
1709 token_buffer = c;
1710 for (;;) {
1711 c = input_stack::peek_char();
1712 if (c == EOF || (!csalnum(c) && c != '_'))
1713 break;
1714 input_stack::get_char();
1715 token_buffer += char(c);
1717 context_buffer = token_buffer;
1718 token_buffer += '\0';
1719 char *def = macro_table.lookup(token_buffer.contents());
1720 if (def)
1721 return strsave(def);
1722 // I guess it wasn't a macro after all; so push the macro name back.
1723 // -2 because we added a '\0'
1724 for (int i = token_buffer.length() - 2; i >= 0; i--)
1725 input_stack::push_back(token_buffer[i]);
1727 if (get_delimited()) {
1728 token_buffer += '\0';
1729 return strsave(token_buffer.contents());
1731 else
1732 return 0;
1735 int lookahead_token = -1;
1736 string old_context_buffer;
1738 void do_lookahead()
1740 if (lookahead_token == -1) {
1741 old_context_buffer = context_buffer;
1742 lookahead_token = get_token(1);
1746 int yylex()
1748 if (delim_flag) {
1749 assert(lookahead_token == -1);
1750 if (delim_flag == 2) {
1751 if ((yylval.str = get_thru_arg()) != 0)
1752 return DELIMITED;
1753 else
1754 return 0;
1756 else {
1757 if (get_delimited()) {
1758 token_buffer += '\0';
1759 yylval.str = strsave(token_buffer.contents());
1760 return DELIMITED;
1762 else
1763 return 0;
1766 for (;;) {
1767 int t;
1768 if (lookahead_token >= 0) {
1769 t = lookahead_token;
1770 lookahead_token = -1;
1772 else
1773 t = get_token(1);
1774 switch (t) {
1775 case '\n':
1776 return ';';
1777 case EOF:
1778 return 0;
1779 case DEFINE:
1780 do_define();
1781 break;
1782 case UNDEF:
1783 do_undef();
1784 break;
1785 case ORDINAL:
1786 yylval.n = token_int;
1787 return t;
1788 case NUMBER:
1789 yylval.x = token_double;
1790 return t;
1791 case COMMAND_LINE:
1792 case TEXT:
1793 token_buffer += '\0';
1794 if (!input_stack::get_location(&yylval.lstr.filename,
1795 &yylval.lstr.lineno)) {
1796 yylval.lstr.filename = 0;
1797 yylval.lstr.lineno = -1;
1799 yylval.lstr.str = strsave(token_buffer.contents());
1800 return t;
1801 case LABEL:
1802 case VARIABLE:
1803 token_buffer += '\0';
1804 yylval.str = strsave(token_buffer.contents());
1805 return t;
1806 case LEFT:
1807 // change LEFT to LEFT_CORNER when followed by OF
1808 old_context_buffer = context_buffer;
1809 lookahead_token = get_token(1);
1810 if (lookahead_token == OF)
1811 return LEFT_CORNER;
1812 else
1813 return t;
1814 case RIGHT:
1815 // change RIGHT to RIGHT_CORNER when followed by OF
1816 old_context_buffer = context_buffer;
1817 lookahead_token = get_token(1);
1818 if (lookahead_token == OF)
1819 return RIGHT_CORNER;
1820 else
1821 return t;
1822 case UPPER:
1823 // recognise UPPER only before LEFT or RIGHT
1824 old_context_buffer = context_buffer;
1825 lookahead_token = get_token(1);
1826 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1827 yylval.str = strsave("upper");
1828 return VARIABLE;
1830 else
1831 return t;
1832 case LOWER:
1833 // recognise LOWER only before LEFT or RIGHT
1834 old_context_buffer = context_buffer;
1835 lookahead_token = get_token(1);
1836 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1837 yylval.str = strsave("lower");
1838 return VARIABLE;
1840 else
1841 return t;
1842 case NORTH:
1843 // recognise NORTH only before OF
1844 old_context_buffer = context_buffer;
1845 lookahead_token = get_token(1);
1846 if (lookahead_token != OF) {
1847 yylval.str = strsave("north");
1848 return VARIABLE;
1850 else
1851 return t;
1852 case SOUTH:
1853 // recognise SOUTH only before OF
1854 old_context_buffer = context_buffer;
1855 lookahead_token = get_token(1);
1856 if (lookahead_token != OF) {
1857 yylval.str = strsave("south");
1858 return VARIABLE;
1860 else
1861 return t;
1862 case EAST:
1863 // recognise EAST only before OF
1864 old_context_buffer = context_buffer;
1865 lookahead_token = get_token(1);
1866 if (lookahead_token != OF) {
1867 yylval.str = strsave("east");
1868 return VARIABLE;
1870 else
1871 return t;
1872 case WEST:
1873 // recognise WEST only before OF
1874 old_context_buffer = context_buffer;
1875 lookahead_token = get_token(1);
1876 if (lookahead_token != OF) {
1877 yylval.str = strsave("west");
1878 return VARIABLE;
1880 else
1881 return t;
1882 case TOP:
1883 // recognise TOP only before OF
1884 old_context_buffer = context_buffer;
1885 lookahead_token = get_token(1);
1886 if (lookahead_token != OF) {
1887 yylval.str = strsave("top");
1888 return VARIABLE;
1890 else
1891 return t;
1892 case BOTTOM:
1893 // recognise BOTTOM only before OF
1894 old_context_buffer = context_buffer;
1895 lookahead_token = get_token(1);
1896 if (lookahead_token != OF) {
1897 yylval.str = strsave("bottom");
1898 return VARIABLE;
1900 else
1901 return t;
1902 case CENTER:
1903 // recognise CENTER only before OF
1904 old_context_buffer = context_buffer;
1905 lookahead_token = get_token(1);
1906 if (lookahead_token != OF) {
1907 yylval.str = strsave("center");
1908 return VARIABLE;
1910 else
1911 return t;
1912 case START:
1913 // recognise START only before OF
1914 old_context_buffer = context_buffer;
1915 lookahead_token = get_token(1);
1916 if (lookahead_token != OF) {
1917 yylval.str = strsave("start");
1918 return VARIABLE;
1920 else
1921 return t;
1922 case END:
1923 // recognise END only before OF
1924 old_context_buffer = context_buffer;
1925 lookahead_token = get_token(1);
1926 if (lookahead_token != OF) {
1927 yylval.str = strsave("end");
1928 return VARIABLE;
1930 else
1931 return t;
1932 default:
1933 return t;
1938 void lex_error(const char *message,
1939 const errarg &arg1,
1940 const errarg &arg2,
1941 const errarg &arg3)
1943 const char *filename;
1944 int lineno;
1945 if (!input_stack::get_location(&filename, &lineno))
1946 error(message, arg1, arg2, arg3);
1947 else
1948 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1951 void lex_warning(const char *message,
1952 const errarg &arg1,
1953 const errarg &arg2,
1954 const errarg &arg3)
1956 const char *filename;
1957 int lineno;
1958 if (!input_stack::get_location(&filename, &lineno))
1959 warning(message, arg1, arg2, arg3);
1960 else
1961 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1964 void yyerror(const char *s)
1966 const char *filename;
1967 int lineno;
1968 const char *context = 0;
1969 if (lookahead_token == -1) {
1970 if (context_buffer.length() > 0) {
1971 context_buffer += '\0';
1972 context = context_buffer.contents();
1975 else {
1976 if (old_context_buffer.length() > 0) {
1977 old_context_buffer += '\0';
1978 context = old_context_buffer.contents();
1981 if (!input_stack::get_location(&filename, &lineno)) {
1982 if (context) {
1983 if (context[0] == '\n' && context[1] == '\0')
1984 error("%1 before newline", s);
1985 else
1986 error("%1 before `%2'", s, context);
1988 else
1989 error("%1 at end of picture", s);
1991 else {
1992 if (context) {
1993 if (context[0] == '\n' && context[1] == '\0')
1994 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1995 else
1996 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1997 s, context);
1999 else
2000 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);