Copyright 2017
[s-roff.git] / src / pre-pic / lex.cpp
blob49c0c13fec6239c551d914b31036a9afe318d63d
1 /*@
2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000, 2002 - 2004, 2006, 2007
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 * This 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.
23 #include "config.h"
24 #include "pic-config.h"
26 #include "object.h"
27 #include "pic.h"
28 #include "pic_tab.h"
29 #include "ptable.h"
31 declare_ptable(char)
32 implement_ptable(char)
34 PTABLE(char) macro_table;
36 class macro_input
37 : public input
39 char *s;
40 char *p;
42 public:
43 macro_input(const char *);
44 ~macro_input();
45 int get();
46 int peek();
49 class argument_macro_input
50 : public input
52 char *s;
53 char *p;
54 char *ap;
55 int argc;
56 char *argv[MAX_ARG];
58 public:
59 argument_macro_input(const char *, int, char **);
60 ~argument_macro_input();
61 int get();
62 int peek();
65 input::input()
66 : next(0)
70 input::~input()
74 int input::get_location(const char **, int *)
76 return 0;
79 file_input::file_input(file_case *fcp, const char *fn)
80 : _fcp(fcp), filename(fn), lineno(0), ptr("")
84 file_input::~file_input()
86 delete _fcp;
89 int file_input::read_line() /* TODO lib-roff */
91 for (;;) {
92 line.clear();
93 lineno++;
94 for (;;) {
95 int c = _fcp->get_c();
96 if (c == '\r' && (c = _fcp->get_c()) != '\n')
97 lex_error("invalid input character CR (carriage return)");
98 if (c == EOF)
99 break;
100 else if (invalid_input_char(c))
101 lex_error("invalid input character code %1", c);
102 else {
103 line += char(c);
104 if (c == '\n')
105 break;
108 if (line.length() == 0)
109 return 0;
110 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
111 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
112 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
113 || compatible_flag))) {
114 line += '\0';
115 ptr = line.contents();
116 return 1;
121 int file_input::get()
123 if (*ptr != '\0' || read_line())
124 return (unsigned char)*ptr++;
125 else
126 return EOF;
129 int file_input::peek()
131 if (*ptr != '\0' || read_line())
132 return (unsigned char)*ptr;
133 else
134 return EOF;
137 int file_input::get_location(const char **fnp, int *lnp)
139 *fnp = filename;
140 *lnp = lineno;
141 return 1;
144 macro_input::macro_input(const char *str)
146 p = s = strsave(str);
149 macro_input::~macro_input()
151 a_delete s;
154 int macro_input::get()
156 if (p == 0 || *p == '\0')
157 return EOF;
158 else
159 return (unsigned char)*p++;
162 int macro_input::peek()
164 if (p == 0 || *p == '\0')
165 return EOF;
166 else
167 return (unsigned char)*p;
170 char *process_body(const char *body)
172 char *s = strsave(body);
173 int j = 0;
174 for (int i = 0; s[i] != '\0'; i++)
175 if (s[i] == '$' && csdigit(s[i + 1])) {
176 int n = 0;
177 int start = i;
178 i++;
179 while (csdigit(s[i]))
180 if (n > MAX_ARG)
181 i++;
182 else
183 n = 10 * n + s[i++] - '0';
184 if (n > MAX_ARG) {
185 string arg;
186 for (int k = start; k < i; k++)
187 arg += s[k];
188 lex_error("invalid macro argument number %1", arg.contents());
190 else if (n > 0)
191 s[j++] = ARG1 + n - 1;
192 i--;
194 else
195 s[j++] = s[i];
196 s[j] = '\0';
197 return s;
200 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
201 : ap(0), argc(ac)
203 for (int i = 0; i < argc; i++)
204 argv[i] = av[i];
205 p = s = process_body(body);
208 argument_macro_input::~argument_macro_input()
210 for (int i = 0; i < argc; i++)
211 a_delete argv[i];
212 a_delete s;
215 int argument_macro_input::get()
217 if (ap) {
218 if (*ap != '\0')
219 return (unsigned char)*ap++;
220 ap = 0;
222 if (p == 0)
223 return EOF;
224 while ((unsigned char)*p >= ARG1
225 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
226 int i = (unsigned char)*p++ - ARG1;
227 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
228 ap = argv[i];
229 return (unsigned char)*ap++;
232 if (*p == '\0')
233 return EOF;
234 return (unsigned char)*p++;
237 int argument_macro_input::peek()
239 if (ap) {
240 if (*ap != '\0')
241 return (unsigned char)*ap;
242 ap = 0;
244 if (p == 0)
245 return EOF;
246 while ((unsigned char)*p >= ARG1
247 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
248 int i = (unsigned char)*p++ - ARG1;
249 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
250 ap = argv[i];
251 return (unsigned char)*ap;
254 if (*p == '\0')
255 return EOF;
256 return (unsigned char)*p;
259 class input_stack
261 static input *current_input;
262 static int bol_flag;
264 public:
265 static void push(input *);
266 static void clear();
267 static int get_char();
268 static int peek_char();
269 static int get_location(const char **fnp, int *lnp);
270 static void push_back(unsigned char c, int was_bol = 0);
271 static int bol();
274 input *input_stack::current_input = 0;
275 int input_stack::bol_flag = 0;
277 inline int input_stack::bol()
279 return bol_flag;
282 void input_stack::clear()
284 while (current_input != 0) {
285 input *tem = current_input;
286 current_input = current_input->next;
287 delete tem;
289 bol_flag = 1;
292 void input_stack::push(input *in)
294 in->next = current_input;
295 current_input = in;
298 void lex_init(input *top)
300 input_stack::clear();
301 input_stack::push(top);
304 void lex_cleanup()
306 while (input_stack::get_char() != EOF)
310 int input_stack::get_char()
312 while (current_input != 0) {
313 int c = current_input->get();
314 if (c != EOF) {
315 bol_flag = c == '\n';
316 return c;
318 // don't pop the top-level input off the stack
319 if (current_input->next == 0)
320 return EOF;
321 input *tem = current_input;
322 current_input = current_input->next;
323 delete tem;
325 return EOF;
328 int input_stack::peek_char()
330 while (current_input != 0) {
331 int c = current_input->peek();
332 if (c != EOF)
333 return c;
334 if (current_input->next == 0)
335 return EOF;
336 input *tem = current_input;
337 current_input = current_input->next;
338 delete tem;
340 return EOF;
343 class char_input
344 : public input
346 int c;
348 public:
349 char_input(int);
350 int get();
351 int peek();
354 char_input::char_input(int n)
355 : c((unsigned char)n)
359 int char_input::get()
361 int n = c;
362 c = EOF;
363 return n;
366 int char_input::peek()
368 return c;
371 void input_stack::push_back(unsigned char c, int was_bol)
373 push(new char_input(c));
374 bol_flag = was_bol;
377 int input_stack::get_location(const char **fnp, int *lnp)
379 for (input *p = current_input; p; p = p->next)
380 if (p->get_location(fnp, lnp))
381 return 1;
382 return 0;
385 string context_buffer;
387 string token_buffer;
388 double token_double;
389 int token_int;
391 void interpolate_macro_with_args(const char *body)
393 char *argv[MAX_ARG];
394 int argc = 0;
395 int ignore = 0;
396 int i;
397 for (i = 0; i < MAX_ARG; i++)
398 argv[i] = 0;
399 int level = 0;
400 int c;
401 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
402 do {
403 token_buffer.clear();
404 for (;;) {
405 c = input_stack::get_char();
406 if (c == EOF) {
407 lex_error("end of input while scanning macro arguments");
408 break;
410 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
411 if (token_buffer.length() > 0) {
412 token_buffer += '\0';
413 if (!ignore) {
414 if (argc == MAX_ARG) {
415 lex_warning("only %1 macro arguments supported", MAX_ARG);
416 ignore = 1;
418 else
419 argv[argc] = strsave(token_buffer.contents());
422 // for `foo()', argc = 0
423 if (argc > 0 || c != ')' || i > 0)
424 if (!ignore)
425 argc++;
426 break;
428 token_buffer += char(c);
429 switch (state) {
430 case NORMAL:
431 if (c == '"')
432 state = IN_STRING;
433 else if (c == '(')
434 level++;
435 else if (c == ')')
436 level--;
437 break;
438 case IN_STRING:
439 if (c == '"')
440 state = NORMAL;
441 else if (c == '\\')
442 state = IN_STRING_QUOTED;
443 break;
444 case IN_STRING_QUOTED:
445 state = IN_STRING;
446 break;
449 } while (c != ')' && c != EOF);
450 input_stack::push(new argument_macro_input(body, argc, argv));
453 static int docmp(const char *s1, int n1, const char *s2, int n2)
455 if (n1 < n2) {
456 int r = memcmp(s1, s2, n1);
457 return r ? r : -1;
459 else if (n1 > n2) {
460 int r = memcmp(s1, s2, n2);
461 return r ? r : 1;
463 else
464 return memcmp(s1, s2, n1);
467 int lookup_keyword(const char *str, int len)
469 static struct keyword {
470 const char *name;
471 int token;
472 } /* FIXME const */table[] = {
473 { "Here", HERE },
474 { "above", ABOVE },
475 { "aligned", ALIGNED },
476 { "and", AND },
477 { "arc", ARC },
478 { "arrow", ARROW },
479 { "at", AT },
480 { "atan2", ATAN2 },
481 { "below", BELOW },
482 { "between", BETWEEN },
483 { "bottom", BOTTOM },
484 { "box", BOX },
485 { "by", BY },
486 { "ccw", CCW },
487 { "center", CENTER },
488 { "chop", CHOP },
489 { "circle", CIRCLE },
490 { "color", COLORED },
491 { "colored", COLORED },
492 { "colour", COLORED },
493 { "coloured", COLORED },
494 { "command", COMMAND },
495 { "copy", COPY },
496 { "cos", COS },
497 { "cw", CW },
498 { "dashed", DASHED },
499 { "define", DEFINE },
500 { "diam", DIAMETER },
501 { "diameter", DIAMETER },
502 { "do", DO },
503 { "dotted", DOTTED },
504 { "down", DOWN },
505 { "east", EAST },
506 { "ellipse", ELLIPSE },
507 { "else", ELSE },
508 { "end", END },
509 { "exp", EXP },
510 { "figname", FIGNAME },
511 { "fill", FILL },
512 { "filled", FILL },
513 { "for", FOR },
514 { "from", FROM },
515 { "height", HEIGHT },
516 { "ht", HEIGHT },
517 { "if", IF },
518 { "int", INT },
519 { "invis", INVISIBLE },
520 { "invisible", INVISIBLE },
521 { "last", LAST },
522 { "left", LEFT },
523 { "line", LINE },
524 { "ljust", LJUST },
525 { "log", LOG },
526 { "lower", LOWER },
527 { "max", K_MAX },
528 { "min", K_MIN },
529 { "move", MOVE },
530 { "north", NORTH },
531 { "of", OF },
532 { "outline", OUTLINED },
533 { "outlined", OUTLINED },
534 { "plot", PLOT },
535 { "print", PRINT },
536 { "rad", RADIUS },
537 { "radius", RADIUS },
538 { "rand", RAND },
539 { "reset", RESET },
540 { "right", RIGHT },
541 { "rjust", RJUST },
542 { "same", SAME },
543 { "sh", SH },
544 { "shaded", SHADED },
545 { "sin", SIN },
546 { "solid", SOLID },
547 { "south", SOUTH },
548 { "spline", SPLINE },
549 { "sprintf", SPRINTF },
550 { "sqrt", SQRT },
551 { "srand", SRAND },
552 { "start", START },
553 { "the", THE },
554 { "then", THEN },
555 { "thick", THICKNESS },
556 { "thickness", THICKNESS },
557 { "thru", THRU },
558 { "to", TO },
559 { "top", TOP },
560 { "undef", UNDEF },
561 { "until", UNTIL },
562 { "up", UP },
563 { "upper", UPPER },
564 { "way", WAY },
565 { "west", WEST },
566 { "wid", WIDTH },
567 { "width", WIDTH },
568 { "with", WITH },
569 { "xslanted", XSLANTED },
570 { "yslanted", YSLANTED },
573 const keyword *start = table;
574 const keyword *end = table + NELEM(table);
575 while (start < end) {
576 // start <= target < end
577 const keyword *mid = start + (end - start)/2;
579 int cmp = docmp(str, len, mid->name, strlen(mid->name));
580 if (cmp == 0)
581 return mid->token;
582 if (cmp < 0)
583 end = mid;
584 else
585 start = mid + 1;
587 return 0;
590 int get_token_after_dot(int c)
592 // get_token deals with the case where c is a digit
593 switch (c) {
594 case 'h':
595 input_stack::get_char();
596 c = input_stack::peek_char();
597 if (c == 't') {
598 input_stack::get_char();
599 context_buffer = ".ht";
600 return DOT_HT;
602 else if (c == 'e') {
603 input_stack::get_char();
604 c = input_stack::peek_char();
605 if (c == 'i') {
606 input_stack::get_char();
607 c = input_stack::peek_char();
608 if (c == 'g') {
609 input_stack::get_char();
610 c = input_stack::peek_char();
611 if (c == 'h') {
612 input_stack::get_char();
613 c = input_stack::peek_char();
614 if (c == 't') {
615 input_stack::get_char();
616 context_buffer = ".height";
617 return DOT_HT;
619 input_stack::push_back('h');
621 input_stack::push_back('g');
623 input_stack::push_back('i');
625 input_stack::push_back('e');
627 input_stack::push_back('h');
628 return '.';
629 case 'x':
630 input_stack::get_char();
631 context_buffer = ".x";
632 return DOT_X;
633 case 'y':
634 input_stack::get_char();
635 context_buffer = ".y";
636 return DOT_Y;
637 case 'c':
638 input_stack::get_char();
639 c = input_stack::peek_char();
640 if (c == 'e') {
641 input_stack::get_char();
642 c = input_stack::peek_char();
643 if (c == 'n') {
644 input_stack::get_char();
645 c = input_stack::peek_char();
646 if (c == 't') {
647 input_stack::get_char();
648 c = input_stack::peek_char();
649 if (c == 'e') {
650 input_stack::get_char();
651 c = input_stack::peek_char();
652 if (c == 'r') {
653 input_stack::get_char();
654 context_buffer = ".center";
655 return DOT_C;
657 input_stack::push_back('e');
659 input_stack::push_back('t');
661 input_stack::push_back('n');
663 input_stack::push_back('e');
665 context_buffer = ".c";
666 return DOT_C;
667 case 'n':
668 input_stack::get_char();
669 c = input_stack::peek_char();
670 if (c == 'e') {
671 input_stack::get_char();
672 context_buffer = ".ne";
673 return DOT_NE;
675 else if (c == 'w') {
676 input_stack::get_char();
677 context_buffer = ".nw";
678 return DOT_NW;
680 else {
681 context_buffer = ".n";
682 return DOT_N;
684 break;
685 case 'e':
686 input_stack::get_char();
687 c = input_stack::peek_char();
688 if (c == 'n') {
689 input_stack::get_char();
690 c = input_stack::peek_char();
691 if (c == 'd') {
692 input_stack::get_char();
693 context_buffer = ".end";
694 return DOT_END;
696 input_stack::push_back('n');
697 context_buffer = ".e";
698 return DOT_E;
700 context_buffer = ".e";
701 return DOT_E;
702 case 'w':
703 input_stack::get_char();
704 c = input_stack::peek_char();
705 if (c == 'i') {
706 input_stack::get_char();
707 c = input_stack::peek_char();
708 if (c == 'd') {
709 input_stack::get_char();
710 c = input_stack::peek_char();
711 if (c == 't') {
712 input_stack::get_char();
713 c = input_stack::peek_char();
714 if (c == 'h') {
715 input_stack::get_char();
716 context_buffer = ".width";
717 return DOT_WID;
719 input_stack::push_back('t');
721 context_buffer = ".wid";
722 return DOT_WID;
724 input_stack::push_back('i');
726 context_buffer = ".w";
727 return DOT_W;
728 case 's':
729 input_stack::get_char();
730 c = input_stack::peek_char();
731 if (c == 'e') {
732 input_stack::get_char();
733 context_buffer = ".se";
734 return DOT_SE;
736 else if (c == 'w') {
737 input_stack::get_char();
738 context_buffer = ".sw";
739 return DOT_SW;
741 else {
742 if (c == 't') {
743 input_stack::get_char();
744 c = input_stack::peek_char();
745 if (c == 'a') {
746 input_stack::get_char();
747 c = input_stack::peek_char();
748 if (c == 'r') {
749 input_stack::get_char();
750 c = input_stack::peek_char();
751 if (c == 't') {
752 input_stack::get_char();
753 context_buffer = ".start";
754 return DOT_START;
756 input_stack::push_back('r');
758 input_stack::push_back('a');
760 input_stack::push_back('t');
762 context_buffer = ".s";
763 return DOT_S;
765 break;
766 case 't':
767 input_stack::get_char();
768 c = input_stack::peek_char();
769 if (c == 'o') {
770 input_stack::get_char();
771 c = input_stack::peek_char();
772 if (c == 'p') {
773 input_stack::get_char();
774 context_buffer = ".top";
775 return DOT_N;
777 input_stack::push_back('o');
779 context_buffer = ".t";
780 return DOT_N;
781 case 'l':
782 input_stack::get_char();
783 c = input_stack::peek_char();
784 if (c == 'e') {
785 input_stack::get_char();
786 c = input_stack::peek_char();
787 if (c == 'f') {
788 input_stack::get_char();
789 c = input_stack::peek_char();
790 if (c == 't') {
791 input_stack::get_char();
792 context_buffer = ".left";
793 return DOT_W;
795 input_stack::push_back('f');
797 input_stack::push_back('e');
799 context_buffer = ".l";
800 return DOT_W;
801 case 'r':
802 input_stack::get_char();
803 c = input_stack::peek_char();
804 if (c == 'a') {
805 input_stack::get_char();
806 c = input_stack::peek_char();
807 if (c == 'd') {
808 input_stack::get_char();
809 context_buffer = ".rad";
810 return DOT_RAD;
812 input_stack::push_back('a');
814 else if (c == 'i') {
815 input_stack::get_char();
816 c = input_stack::peek_char();
817 if (c == 'g') {
818 input_stack::get_char();
819 c = input_stack::peek_char();
820 if (c == 'h') {
821 input_stack::get_char();
822 c = input_stack::peek_char();
823 if (c == 't') {
824 input_stack::get_char();
825 context_buffer = ".right";
826 return DOT_E;
828 input_stack::push_back('h');
830 input_stack::push_back('g');
832 input_stack::push_back('i');
834 context_buffer = ".r";
835 return DOT_E;
836 case 'b':
837 input_stack::get_char();
838 c = input_stack::peek_char();
839 if (c == 'o') {
840 input_stack::get_char();
841 c = input_stack::peek_char();
842 if (c == 't') {
843 input_stack::get_char();
844 c = input_stack::peek_char();
845 if (c == 't') {
846 input_stack::get_char();
847 c = input_stack::peek_char();
848 if (c == 'o') {
849 input_stack::get_char();
850 c = input_stack::peek_char();
851 if (c == 'm') {
852 input_stack::get_char();
853 context_buffer = ".bottom";
854 return DOT_S;
856 input_stack::push_back('o');
858 input_stack::push_back('t');
860 context_buffer = ".bot";
861 return DOT_S;
863 input_stack::push_back('o');
865 context_buffer = ".b";
866 return DOT_S;
867 default:
868 context_buffer = '.';
869 return '.';
873 int get_token(int lookup_flag)
875 context_buffer.clear();
876 for (;;) {
877 int n = 0;
878 int bol = input_stack::bol();
879 int c = input_stack::get_char();
880 if (bol && c == command_char) {
881 token_buffer.clear();
882 token_buffer += c;
883 // the newline is not part of the token
884 for (;;) {
885 c = input_stack::peek_char();
886 if (c == EOF || c == '\n')
887 break;
888 input_stack::get_char();
889 token_buffer += char(c);
891 context_buffer = token_buffer;
892 return COMMAND_LINE;
894 switch (c) {
895 case EOF:
896 return EOF;
897 case ' ':
898 case '\t':
899 break;
900 case '\\':
902 int d = input_stack::peek_char();
903 if (d != '\n') {
904 context_buffer = '\\';
905 return '\\';
907 input_stack::get_char();
908 break;
910 case '#':
911 do {
912 c = input_stack::get_char();
913 } while (c != '\n' && c != EOF);
914 if (c == '\n')
915 context_buffer = '\n';
916 return c;
917 case '"':
918 context_buffer = '"';
919 token_buffer.clear();
920 for (;;) {
921 c = input_stack::get_char();
922 if (c == '\\') {
923 context_buffer += '\\';
924 c = input_stack::peek_char();
925 if (c == '"') {
926 input_stack::get_char();
927 token_buffer += '"';
928 context_buffer += '"';
930 else
931 token_buffer += '\\';
933 else if (c == '\n') {
934 error("newline in string");
935 break;
937 else if (c == EOF) {
938 error("missing `\"'");
939 break;
941 else if (c == '"') {
942 context_buffer += '"';
943 break;
945 else {
946 context_buffer += char(c);
947 token_buffer += char(c);
950 return TEXT;
951 case '0':
952 case '1':
953 case '2':
954 case '3':
955 case '4':
956 case '5':
957 case '6':
958 case '7':
959 case '8':
960 case '9':
962 int overflow = 0;
963 n = 0;
964 for (;;) {
965 if (n > (INT_MAX - 9)/10) {
966 overflow = 1;
967 break;
969 n *= 10;
970 n += c - '0';
971 context_buffer += char(c);
972 c = input_stack::peek_char();
973 if (c == EOF || !csdigit(c))
974 break;
975 c = input_stack::get_char();
977 token_double = n;
978 if (overflow) {
979 for (;;) {
980 token_double *= 10.0;
981 token_double += c - '0';
982 context_buffer += char(c);
983 c = input_stack::peek_char();
984 if (c == EOF || !csdigit(c))
985 break;
986 c = input_stack::get_char();
988 // if somebody asks for 1000000000000th, we will silently
989 // give them INT_MAXth
990 double temp = token_double; // work around gas 1.34/sparc bug
991 if (token_double > INT_MAX)
992 n = INT_MAX;
993 else
994 n = int(temp);
997 switch (c) {
998 case 'i':
999 case 'I':
1000 context_buffer += char(c);
1001 input_stack::get_char();
1002 return NUMBER;
1003 case '.':
1005 context_buffer += '.';
1006 input_stack::get_char();
1007 got_dot:
1008 double factor = 1.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 factor /= 10.0;
1016 if (c != '0')
1017 token_double += factor*(c - '0');
1019 if (c != 'e' && c != 'E') {
1020 if (c == 'i' || c == 'I') {
1021 context_buffer += char(c);
1022 input_stack::get_char();
1024 return NUMBER;
1027 // fall through
1028 case 'e':
1029 case 'E':
1031 int echar = c;
1032 input_stack::get_char();
1033 c = input_stack::peek_char();
1034 int sign = '+';
1035 if (c == '+' || c == '-') {
1036 sign = c;
1037 input_stack::get_char();
1038 c = input_stack::peek_char();
1039 if (c == EOF || !csdigit(c)) {
1040 input_stack::push_back(sign);
1041 input_stack::push_back(echar);
1042 return NUMBER;
1044 context_buffer += char(echar);
1045 context_buffer += char(sign);
1047 else {
1048 if (c == EOF || !csdigit(c)) {
1049 input_stack::push_back(echar);
1050 return NUMBER;
1052 context_buffer += char(echar);
1054 input_stack::get_char();
1055 context_buffer += char(c);
1056 n = c - '0';
1057 for (;;) {
1058 c = input_stack::peek_char();
1059 if (c == EOF || !csdigit(c))
1060 break;
1061 input_stack::get_char();
1062 context_buffer += char(c);
1063 n = n*10 + (c - '0');
1065 if (sign == '-')
1066 n = -n;
1067 if (c == 'i' || c == 'I') {
1068 context_buffer += char(c);
1069 input_stack::get_char();
1071 token_double *= pow(10.0, n);
1072 return NUMBER;
1074 case 'n':
1075 input_stack::get_char();
1076 c = input_stack::peek_char();
1077 if (c == 'd') {
1078 input_stack::get_char();
1079 token_int = n;
1080 context_buffer += "nd";
1081 return ORDINAL;
1083 input_stack::push_back('n');
1084 return NUMBER;
1085 case 'r':
1086 input_stack::get_char();
1087 c = input_stack::peek_char();
1088 if (c == 'd') {
1089 input_stack::get_char();
1090 token_int = n;
1091 context_buffer += "rd";
1092 return ORDINAL;
1094 input_stack::push_back('r');
1095 return NUMBER;
1096 case 't':
1097 input_stack::get_char();
1098 c = input_stack::peek_char();
1099 if (c == 'h') {
1100 input_stack::get_char();
1101 token_int = n;
1102 context_buffer += "th";
1103 return ORDINAL;
1105 input_stack::push_back('t');
1106 return NUMBER;
1107 case 's':
1108 input_stack::get_char();
1109 c = input_stack::peek_char();
1110 if (c == 't') {
1111 input_stack::get_char();
1112 token_int = n;
1113 context_buffer += "st";
1114 return ORDINAL;
1116 input_stack::push_back('s');
1117 return NUMBER;
1118 default:
1119 return NUMBER;
1121 break;
1122 case '\'':
1124 c = input_stack::peek_char();
1125 if (c == 't') {
1126 input_stack::get_char();
1127 c = input_stack::peek_char();
1128 if (c == 'h') {
1129 input_stack::get_char();
1130 context_buffer = "'th";
1131 return TH;
1133 else
1134 input_stack::push_back('t');
1136 context_buffer = "'";
1137 return '\'';
1139 case '.':
1141 c = input_stack::peek_char();
1142 if (c != EOF && csdigit(c)) {
1143 n = 0;
1144 token_double = 0.0;
1145 context_buffer = '.';
1146 goto got_dot;
1148 return get_token_after_dot(c);
1150 case '<':
1151 c = input_stack::peek_char();
1152 if (c == '-') {
1153 input_stack::get_char();
1154 c = input_stack::peek_char();
1155 if (c == '>') {
1156 input_stack::get_char();
1157 context_buffer = "<->";
1158 return DOUBLE_ARROW_HEAD;
1160 context_buffer = "<-";
1161 return LEFT_ARROW_HEAD;
1163 else if (c == '=') {
1164 input_stack::get_char();
1165 context_buffer = "<=";
1166 return LESSEQUAL;
1168 context_buffer = "<";
1169 return '<';
1170 case '-':
1171 c = input_stack::peek_char();
1172 if (c == '>') {
1173 input_stack::get_char();
1174 context_buffer = "->";
1175 return RIGHT_ARROW_HEAD;
1177 context_buffer = "-";
1178 return '-';
1179 case '!':
1180 c = input_stack::peek_char();
1181 if (c == '=') {
1182 input_stack::get_char();
1183 context_buffer = "!=";
1184 return NOTEQUAL;
1186 context_buffer = "!";
1187 return '!';
1188 case '>':
1189 c = input_stack::peek_char();
1190 if (c == '=') {
1191 input_stack::get_char();
1192 context_buffer = ">=";
1193 return GREATEREQUAL;
1195 context_buffer = ">";
1196 return '>';
1197 case '=':
1198 c = input_stack::peek_char();
1199 if (c == '=') {
1200 input_stack::get_char();
1201 context_buffer = "==";
1202 return EQUALEQUAL;
1204 context_buffer = "=";
1205 return '=';
1206 case '&':
1207 c = input_stack::peek_char();
1208 if (c == '&') {
1209 input_stack::get_char();
1210 context_buffer = "&&";
1211 return ANDAND;
1213 context_buffer = "&";
1214 return '&';
1215 case '|':
1216 c = input_stack::peek_char();
1217 if (c == '|') {
1218 input_stack::get_char();
1219 context_buffer = "||";
1220 return OROR;
1222 context_buffer = "|";
1223 return '|';
1224 default:
1225 if (c != EOF && csalpha(c)) {
1226 token_buffer.clear();
1227 token_buffer = c;
1228 for (;;) {
1229 c = input_stack::peek_char();
1230 if (c == EOF || (!csalnum(c) && c != '_'))
1231 break;
1232 input_stack::get_char();
1233 token_buffer += char(c);
1235 int tok = lookup_keyword(token_buffer.contents(),
1236 token_buffer.length());
1237 if (tok != 0) {
1238 context_buffer = token_buffer;
1239 return tok;
1241 char *def = 0;
1242 if (lookup_flag) {
1243 token_buffer += '\0';
1244 def = macro_table.lookup(token_buffer.contents());
1245 token_buffer.set_length(token_buffer.length() - 1);
1246 if (def) {
1247 if (c == '(') {
1248 input_stack::get_char();
1249 interpolate_macro_with_args(def);
1251 else
1252 input_stack::push(new macro_input(def));
1255 if (!def) {
1256 context_buffer = token_buffer;
1257 if (csupper(token_buffer[0]))
1258 return LABEL;
1259 else
1260 return VARIABLE;
1263 else {
1264 context_buffer = char(c);
1265 return (unsigned char)c;
1267 break;
1272 int get_delimited()
1274 token_buffer.clear();
1275 int c = input_stack::get_char();
1276 while (c == ' ' || c == '\t' || c == '\n')
1277 c = input_stack::get_char();
1278 if (c == EOF) {
1279 lex_error("missing delimiter");
1280 return 0;
1282 context_buffer = char(c);
1283 int had_newline = 0;
1284 int start = c;
1285 int level = 0;
1286 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1287 for (;;) {
1288 c = input_stack::get_char();
1289 if (c == EOF) {
1290 lex_error("missing closing delimiter");
1291 return 0;
1293 if (c == '\n')
1294 had_newline = 1;
1295 else if (!had_newline)
1296 context_buffer += char(c);
1297 switch (state) {
1298 case NORMAL:
1299 if (start == '{') {
1300 if (c == '{') {
1301 level++;
1302 break;
1304 if (c == '}') {
1305 if (--level < 0)
1306 state = DELIM_END;
1307 break;
1310 else {
1311 if (c == start) {
1312 state = DELIM_END;
1313 break;
1316 if (c == '"')
1317 state = IN_STRING;
1318 break;
1319 case IN_STRING_QUOTED:
1320 if (c == '\n')
1321 state = NORMAL;
1322 else
1323 state = IN_STRING;
1324 break;
1325 case IN_STRING:
1326 if (c == '"' || c == '\n')
1327 state = NORMAL;
1328 else if (c == '\\')
1329 state = IN_STRING_QUOTED;
1330 break;
1331 case DELIM_END:
1332 // This case it just to shut cfront 2.0 up.
1333 default:
1334 assert(0);
1336 if (state == DELIM_END)
1337 break;
1338 token_buffer += c;
1340 return 1;
1343 void do_define()
1345 int t = get_token(0); // do not expand what we are defining
1346 if (t != VARIABLE && t != LABEL) {
1347 lex_error("can only define variable or placename");
1348 return;
1350 token_buffer += '\0';
1351 string nm = token_buffer;
1352 const char *name = nm.contents();
1353 if (!get_delimited())
1354 return;
1355 token_buffer += '\0';
1356 macro_table.define(name, strsave(token_buffer.contents()));
1359 void do_undef()
1361 int t = get_token(0); // do not expand what we are undefining
1362 if (t != VARIABLE && t != LABEL) {
1363 lex_error("can only define variable or placename");
1364 return;
1366 token_buffer += '\0';
1367 macro_table.define(token_buffer.contents(), 0);
1370 class for_input
1371 : public input
1373 char *var;
1374 char *body;
1375 double from;
1376 double to;
1377 int by_is_multiplicative;
1378 double by;
1379 const char *p;
1380 int done_newline;
1382 public:
1383 for_input(char *, double, double, int, double, char *);
1384 ~for_input();
1385 int get();
1386 int peek();
1389 for_input::for_input(char *vr, double f, double t,
1390 int bim, double b, char *bd)
1391 : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1392 p(body), done_newline(0)
1396 for_input::~for_input()
1398 a_delete var;
1399 a_delete body;
1402 int for_input::get()
1404 if (p == 0)
1405 return EOF;
1406 for (;;) {
1407 if (*p != '\0')
1408 return (unsigned char)*p++;
1409 if (!done_newline) {
1410 done_newline = 1;
1411 return '\n';
1413 double val;
1414 if (!lookup_variable(var, &val)) {
1415 lex_error("body of `for' terminated enclosing block");
1416 return EOF;
1418 if (by_is_multiplicative)
1419 val *= by;
1420 else
1421 val += by;
1422 define_variable(var, val);
1423 if ((from <= to && val > to)
1424 || (from >= to && val < to)) {
1425 p = 0;
1426 return EOF;
1428 p = body;
1429 done_newline = 0;
1433 int for_input::peek()
1435 if (p == 0)
1436 return EOF;
1437 if (*p != '\0')
1438 return (unsigned char)*p;
1439 if (!done_newline)
1440 return '\n';
1441 double val;
1442 if (!lookup_variable(var, &val))
1443 return EOF;
1444 if (by_is_multiplicative) {
1445 if (val * by > to)
1446 return EOF;
1448 else {
1449 if ((from <= to && val + by > to)
1450 || (from >= to && val + by < to))
1451 return EOF;
1453 if (*body == '\0')
1454 return EOF;
1455 return (unsigned char)*body;
1458 void do_for(char *var, double from, double to, int by_is_multiplicative,
1459 double by, char *body)
1461 define_variable(var, from);
1462 if ((by_is_multiplicative && by <= 0)
1463 || (by > 0 && from > to)
1464 || (by < 0 && from < to))
1465 return;
1466 input_stack::push(new for_input(var, from, to,
1467 by_is_multiplicative, by, body));
1470 void do_copy(const char *filename)
1472 file_case *fcp = file_case::muxer(filename);
1473 if (fcp == NULL) {
1474 lex_error("can't open `%1': %2", filename, strerror(errno));
1475 return;
1477 input_stack::push(new file_input(fcp, filename));
1480 class copy_thru_input
1481 : public input
1483 int done;
1484 char *body;
1485 char *until;
1486 const char *p;
1487 const char *ap;
1488 int argv[MAX_ARG];
1489 int argc;
1490 string line;
1492 int get_line();
1493 virtual int inget() = 0;
1495 public:
1496 copy_thru_input(const char *b, const char *u);
1497 ~copy_thru_input();
1498 int get();
1499 int peek();
1502 class copy_file_thru_input
1503 : public copy_thru_input
1505 input *in;
1507 public:
1508 copy_file_thru_input(input *, const char *b, const char *u);
1509 ~copy_file_thru_input();
1510 int inget();
1513 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1514 const char *u)
1515 : copy_thru_input(b, u), in(i)
1519 copy_file_thru_input::~copy_file_thru_input()
1521 delete in;
1524 int copy_file_thru_input::inget()
1526 if (!in)
1527 return EOF;
1528 else
1529 return in->get();
1532 class copy_rest_thru_input
1533 : public copy_thru_input
1535 public:
1536 copy_rest_thru_input(const char *, const char *u);
1537 int inget();
1540 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1541 : copy_thru_input(b, u)
1545 int copy_rest_thru_input::inget()
1547 while (next != 0) {
1548 int c = next->get();
1549 if (c != EOF)
1550 return c;
1551 if (next->next == 0)
1552 return EOF;
1553 input *tem = next;
1554 next = next->next;
1555 delete tem;
1557 return EOF;
1561 copy_thru_input::copy_thru_input(const char *b, const char *u)
1562 : done(0)
1564 ap = 0;
1565 body = process_body(b);
1566 p = 0;
1567 until = strsave(u);
1570 copy_thru_input::~copy_thru_input()
1572 a_delete body;
1573 a_delete until;
1576 int copy_thru_input::get()
1578 if (ap) {
1579 if (*ap != '\0')
1580 return (unsigned char)*ap++;
1581 ap = 0;
1583 for (;;) {
1584 if (p == 0) {
1585 if (!get_line())
1586 break;
1587 p = body;
1589 if (*p == '\0') {
1590 p = 0;
1591 return '\n';
1593 while ((unsigned char)*p >= ARG1
1594 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1595 int i = (unsigned char)*p++ - ARG1;
1596 if (i < argc && line[argv[i]] != '\0') {
1597 ap = line.contents() + argv[i];
1598 return (unsigned char)*ap++;
1601 if (*p != '\0')
1602 return (unsigned char)*p++;
1604 return EOF;
1607 int copy_thru_input::peek()
1609 if (ap) {
1610 if (*ap != '\0')
1611 return (unsigned char)*ap;
1612 ap = 0;
1614 for (;;) {
1615 if (p == 0) {
1616 if (!get_line())
1617 break;
1618 p = body;
1620 if (*p == '\0')
1621 return '\n';
1622 while ((unsigned char)*p >= ARG1
1623 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1624 int i = (unsigned char)*p++ - ARG1;
1625 if (i < argc && line[argv[i]] != '\0') {
1626 ap = line.contents() + argv[i];
1627 return (unsigned char)*ap;
1630 if (*p != '\0')
1631 return (unsigned char)*p;
1633 return EOF;
1636 int copy_thru_input::get_line()
1638 if (done)
1639 return 0;
1640 line.clear();
1641 argc = 0;
1642 int c = inget();
1643 for (;;) {
1644 while (c == ' ')
1645 c = inget();
1646 if (c == EOF || c == '\n')
1647 break;
1648 if (argc == MAX_ARG) {
1649 do {
1650 c = inget();
1651 } while (c != '\n' && c != EOF);
1652 break;
1654 argv[argc++] = line.length();
1655 do {
1656 line += char(c);
1657 c = inget();
1658 } while (c != ' ' && c != '\n');
1659 line += '\0';
1661 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1662 done = 1;
1663 return 0;
1665 return argc > 0 || c == '\n';
1668 class simple_file_input
1669 : public input
1671 const char *filename;
1672 int lineno;
1673 file_case *_fcp;
1675 public:
1676 simple_file_input(file_case *, const char *);
1677 ~simple_file_input();
1678 int get();
1679 int peek();
1680 int get_location(const char **, int *);
1683 simple_file_input::simple_file_input(file_case *fcp, const char *s)
1684 : filename(s), lineno(1), _fcp(fcp)
1688 simple_file_input::~simple_file_input()
1690 // don't delete the filename
1691 delete _fcp;
1694 int simple_file_input::get()
1696 int c = _fcp->get_c();
1697 while (invalid_input_char(c)) {
1698 error("invalid input character code %1", c);
1699 c = _fcp->get_c();
1701 if (c == '\n')
1702 lineno++;
1703 return c;
1706 int simple_file_input::peek()
1708 int c = _fcp->get_c();
1709 while (invalid_input_char(c)) {
1710 error("invalid input character code %1", c);
1711 c = _fcp->get_c();
1713 if (c != EOF)
1714 _fcp->unget_c(c);
1715 return c;
1718 int simple_file_input::get_location(const char **fnp, int *lnp)
1720 *fnp = filename;
1721 *lnp = lineno;
1722 return 1;
1725 void copy_file_thru(const char *filename, const char *body, const char *until)
1727 file_case *fcp = file_case::muxer(filename);
1728 if (fcp == NULL) {
1729 lex_error("can't open `%1': %2", filename, strerror(errno));
1730 return;
1732 input *in = new copy_file_thru_input(new simple_file_input(fcp, filename),
1733 body, until);
1734 input_stack::push(in);
1737 void copy_rest_thru(const char *body, const char *until)
1739 input_stack::push(new copy_rest_thru_input(body, until));
1742 void push_body(const char *s)
1744 input_stack::push(new char_input('\n'));
1745 input_stack::push(new macro_input(s));
1748 int delim_flag = 0;
1750 char *get_thru_arg()
1752 int c = input_stack::peek_char();
1753 while (c == ' ') {
1754 input_stack::get_char();
1755 c = input_stack::peek_char();
1757 if (c != EOF && csalpha(c)) {
1758 // looks like a macro
1759 input_stack::get_char();
1760 token_buffer = c;
1761 for (;;) {
1762 c = input_stack::peek_char();
1763 if (c == EOF || (!csalnum(c) && c != '_'))
1764 break;
1765 input_stack::get_char();
1766 token_buffer += char(c);
1768 context_buffer = token_buffer;
1769 token_buffer += '\0';
1770 char *def = macro_table.lookup(token_buffer.contents());
1771 if (def)
1772 return strsave(def);
1773 // I guess it wasn't a macro after all; so push the macro name back.
1774 // -2 because we added a '\0'
1775 for (int i = token_buffer.length() - 2; i >= 0; i--)
1776 input_stack::push_back(token_buffer[i]);
1778 if (get_delimited()) {
1779 token_buffer += '\0';
1780 return strsave(token_buffer.contents());
1782 else
1783 return 0;
1786 int lookahead_token = -1;
1787 string old_context_buffer;
1789 void do_lookahead()
1791 if (lookahead_token == -1) {
1792 old_context_buffer = context_buffer;
1793 lookahead_token = get_token(1);
1797 int yylex()
1799 if (delim_flag) {
1800 assert(lookahead_token == -1);
1801 if (delim_flag == 2) {
1802 if ((yylval.str = get_thru_arg()) != 0)
1803 return DELIMITED;
1804 else
1805 return 0;
1807 else {
1808 if (get_delimited()) {
1809 token_buffer += '\0';
1810 yylval.str = strsave(token_buffer.contents());
1811 return DELIMITED;
1813 else
1814 return 0;
1817 for (;;) {
1818 int t;
1819 if (lookahead_token >= 0) {
1820 t = lookahead_token;
1821 lookahead_token = -1;
1823 else
1824 t = get_token(1);
1825 switch (t) {
1826 case '\n':
1827 return ';';
1828 case EOF:
1829 return 0;
1830 case DEFINE:
1831 do_define();
1832 break;
1833 case UNDEF:
1834 do_undef();
1835 break;
1836 case ORDINAL:
1837 yylval.n = token_int;
1838 return t;
1839 case NUMBER:
1840 yylval.x = token_double;
1841 return t;
1842 case COMMAND_LINE:
1843 case TEXT:
1844 token_buffer += '\0';
1845 if (!input_stack::get_location(&yylval.lstr.filename,
1846 &yylval.lstr.lineno)) {
1847 yylval.lstr.filename = 0;
1848 yylval.lstr.lineno = -1;
1850 yylval.lstr.str = strsave(token_buffer.contents());
1851 return t;
1852 case LABEL:
1853 case VARIABLE:
1854 token_buffer += '\0';
1855 yylval.str = strsave(token_buffer.contents());
1856 return t;
1857 case LEFT:
1858 // change LEFT to LEFT_CORNER when followed by OF
1859 old_context_buffer = context_buffer;
1860 lookahead_token = get_token(1);
1861 if (lookahead_token == OF)
1862 return LEFT_CORNER;
1863 else
1864 return t;
1865 case RIGHT:
1866 // change RIGHT to RIGHT_CORNER when followed by OF
1867 old_context_buffer = context_buffer;
1868 lookahead_token = get_token(1);
1869 if (lookahead_token == OF)
1870 return RIGHT_CORNER;
1871 else
1872 return t;
1873 case UPPER:
1874 // recognise UPPER only before LEFT or RIGHT
1875 old_context_buffer = context_buffer;
1876 lookahead_token = get_token(1);
1877 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1878 yylval.str = strsave("upper");
1879 return VARIABLE;
1881 else
1882 return t;
1883 case LOWER:
1884 // recognise LOWER only before LEFT or RIGHT
1885 old_context_buffer = context_buffer;
1886 lookahead_token = get_token(1);
1887 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1888 yylval.str = strsave("lower");
1889 return VARIABLE;
1891 else
1892 return t;
1893 case NORTH:
1894 // recognise NORTH only before OF
1895 old_context_buffer = context_buffer;
1896 lookahead_token = get_token(1);
1897 if (lookahead_token != OF) {
1898 yylval.str = strsave("north");
1899 return VARIABLE;
1901 else
1902 return t;
1903 case SOUTH:
1904 // recognise SOUTH only before OF
1905 old_context_buffer = context_buffer;
1906 lookahead_token = get_token(1);
1907 if (lookahead_token != OF) {
1908 yylval.str = strsave("south");
1909 return VARIABLE;
1911 else
1912 return t;
1913 case EAST:
1914 // recognise EAST only before OF
1915 old_context_buffer = context_buffer;
1916 lookahead_token = get_token(1);
1917 if (lookahead_token != OF) {
1918 yylval.str = strsave("east");
1919 return VARIABLE;
1921 else
1922 return t;
1923 case WEST:
1924 // recognise WEST only before OF
1925 old_context_buffer = context_buffer;
1926 lookahead_token = get_token(1);
1927 if (lookahead_token != OF) {
1928 yylval.str = strsave("west");
1929 return VARIABLE;
1931 else
1932 return t;
1933 case TOP:
1934 // recognise TOP only before OF
1935 old_context_buffer = context_buffer;
1936 lookahead_token = get_token(1);
1937 if (lookahead_token != OF) {
1938 yylval.str = strsave("top");
1939 return VARIABLE;
1941 else
1942 return t;
1943 case BOTTOM:
1944 // recognise BOTTOM only before OF
1945 old_context_buffer = context_buffer;
1946 lookahead_token = get_token(1);
1947 if (lookahead_token != OF) {
1948 yylval.str = strsave("bottom");
1949 return VARIABLE;
1951 else
1952 return t;
1953 case CENTER:
1954 // recognise CENTER only before OF
1955 old_context_buffer = context_buffer;
1956 lookahead_token = get_token(1);
1957 if (lookahead_token != OF) {
1958 yylval.str = strsave("center");
1959 return VARIABLE;
1961 else
1962 return t;
1963 case START:
1964 // recognise START only before OF
1965 old_context_buffer = context_buffer;
1966 lookahead_token = get_token(1);
1967 if (lookahead_token != OF) {
1968 yylval.str = strsave("start");
1969 return VARIABLE;
1971 else
1972 return t;
1973 case END:
1974 // recognise END only before OF
1975 old_context_buffer = context_buffer;
1976 lookahead_token = get_token(1);
1977 if (lookahead_token != OF) {
1978 yylval.str = strsave("end");
1979 return VARIABLE;
1981 else
1982 return t;
1983 default:
1984 return t;
1989 void lex_error(const char *message,
1990 const errarg &arg1,
1991 const errarg &arg2,
1992 const errarg &arg3)
1994 const char *filename;
1995 int lineno;
1996 if (!input_stack::get_location(&filename, &lineno))
1997 error(message, arg1, arg2, arg3);
1998 else
1999 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
2002 void lex_warning(const char *message,
2003 const errarg &arg1,
2004 const errarg &arg2,
2005 const errarg &arg3)
2007 const char *filename;
2008 int lineno;
2009 if (!input_stack::get_location(&filename, &lineno))
2010 warning(message, arg1, arg2, arg3);
2011 else
2012 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
2015 void yyerror(const char *s)
2017 const char *filename;
2018 int lineno;
2019 const char *context = 0;
2020 if (lookahead_token == -1) {
2021 if (context_buffer.length() > 0) {
2022 context_buffer += '\0';
2023 context = context_buffer.contents();
2026 else {
2027 if (old_context_buffer.length() > 0) {
2028 old_context_buffer += '\0';
2029 context = old_context_buffer.contents();
2032 if (!input_stack::get_location(&filename, &lineno)) {
2033 if (context) {
2034 if (context[0] == '\n' && context[1] == '\0')
2035 error("%1 before newline", s);
2036 else
2037 error("%1 before `%2'", s, context);
2039 else
2040 error("%1 at end of picture", s);
2042 else {
2043 if (context) {
2044 if (context[0] == '\n' && context[1] == '\0')
2045 error_with_file_and_line(filename, lineno, "%1 before newline", s);
2046 else
2047 error_with_file_and_line(filename, lineno, "%1 before `%2'",
2048 s, context);
2050 else
2051 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
2055 // s-it2-mode