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
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
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. */
28 implement_ptable(char)
30 PTABLE(char) macro_table;
32 class macro_input : public input {
36 macro_input(const char *);
42 class argument_macro_input : public input {
49 argument_macro_input(const char *, int, char **);
50 ~argument_macro_input();
55 input::input() : next(0)
63 int input::get_location(const char **, int *)
68 file_input::file_input(FILE *f, const char *fn)
69 : fp(f), filename(fn), lineno(0), ptr("")
73 file_input::~file_input()
78 int file_input::read_line()
87 else if (invalid_input_char(c))
88 lex_error("invalid input character code %1", c);
95 if (line.length() == 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))) {
102 ptr = line.contents();
108 int file_input::get()
110 if (*ptr != '\0' || read_line())
111 return (unsigned char)*ptr++;
116 int file_input::peek()
118 if (*ptr != '\0' || read_line())
119 return (unsigned char)*ptr;
124 int file_input::get_location(const char **fnp, int *lnp)
131 macro_input::macro_input(const char *str)
133 p = s = strsave(str);
136 macro_input::~macro_input()
141 int macro_input::get()
143 if (p == 0 || *p == '\0')
146 return (unsigned char)*p++;
149 int macro_input::peek()
151 if (p == 0 || *p == '\0')
154 return (unsigned char)*p;
157 // Character representing $1. Must be invalid input character.
160 char *process_body(const char *body)
162 char *s = strsave(body);
164 for (int i = 0; s[i] != '\0'; i++)
165 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
167 s[j++] = ARG1 + s[++i] - '1';
176 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
179 for (int i = 0; i < argc; i++)
181 p = s = process_body(body);
185 argument_macro_input::~argument_macro_input()
187 for (int i = 0; i < argc; i++)
192 int argument_macro_input::get()
196 return (unsigned char)*ap++;
201 while (*p >= ARG1 && *p <= ARG1 + 8) {
203 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
205 return (unsigned char)*ap++;
210 return (unsigned char)*p++;
213 int argument_macro_input::peek()
217 return (unsigned char)*ap;
222 while (*p >= ARG1 && *p <= ARG1 + 8) {
224 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
226 return (unsigned char)*ap;
231 return (unsigned char)*p;
235 static input *current_input;
238 static void push(input *);
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);
247 input *input_stack::current_input = 0;
248 int input_stack::bol_flag = 0;
250 inline int input_stack::bol()
255 void input_stack::clear()
257 while (current_input != 0) {
258 input *tem = current_input;
259 current_input = current_input->next;
265 void input_stack::push(input *in)
267 in->next = current_input;
271 void lex_init(input *top)
273 input_stack::clear();
274 input_stack::push(top);
279 while (input_stack::get_char() != EOF)
283 int input_stack::get_char()
285 while (current_input != 0) {
286 int c = current_input->get();
288 bol_flag = c == '\n';
291 // don't pop the top-level input off the stack
292 if (current_input->next == 0)
294 input *tem = current_input;
295 current_input = current_input->next;
301 int input_stack::peek_char()
303 while (current_input != 0) {
304 int c = current_input->peek();
307 if (current_input->next == 0)
309 input *tem = current_input;
310 current_input = current_input->next;
316 class char_input : public input {
324 char_input::char_input(int n) : c((unsigned char)n)
328 int char_input::get()
335 int char_input::peek()
340 void input_stack::push_back(unsigned char c, int was_bol)
342 push(new char_input(c));
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))
354 string context_buffer;
360 void interpolate_macro_with_args(const char *body)
365 for (i = 0; i < 9; i++)
369 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
371 token_buffer.clear();
373 c = input_stack::get_char();
375 lex_error("end of input while scanning macro arguments");
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)
388 token_buffer += char(c);
402 state = IN_STRING_QUOTED;
404 case IN_STRING_QUOTED:
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)
416 int r = memcmp(s1, s2, n1);
420 int r = memcmp(s1, s2, n2);
424 return memcmp(s1, s2, n1);
427 int lookup_keyword(const char *str, int len)
429 static struct keyword {
435 { "aligned", ALIGNED },
442 { "between", BETWEEN },
443 { "bottom", BOTTOM },
447 { "center", CENTER },
449 { "circle", CIRCLE },
450 { "color", COLORED },
451 { "colored", COLORED },
452 { "colour", COLORED },
453 { "coloured", COLORED },
454 { "command", COMMAND },
458 { "dashed", DASHED },
459 { "define", DEFINE },
460 { "diam", DIAMETER },
461 { "diameter", DIAMETER },
463 { "dotted", DOTTED },
466 { "ellipse", ELLIPSE },
470 { "figname", FIGNAME },
475 { "height", HEIGHT },
479 { "invis", INVISIBLE },
480 { "invisible", INVISIBLE },
492 { "outline", OUTLINED },
493 { "outlined", OUTLINED },
497 { "radius", RADIUS },
504 { "shaded", SHADED },
508 { "spline", SPLINE },
509 { "sprintf", SPRINTF },
515 { "thick", THICKNESS },
516 { "thickness", THICKNESS },
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));
548 int get_token_after_dot(int c)
550 // get_token deals with the case where c is a digit
553 input_stack::get_char();
554 c = input_stack::peek_char();
556 input_stack::get_char();
557 context_buffer = ".ht";
561 input_stack::get_char();
562 c = input_stack::peek_char();
564 input_stack::get_char();
565 c = input_stack::peek_char();
567 input_stack::get_char();
568 c = input_stack::peek_char();
570 input_stack::get_char();
571 c = input_stack::peek_char();
573 input_stack::get_char();
574 context_buffer = ".height";
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');
588 input_stack::get_char();
589 context_buffer = ".x";
592 input_stack::get_char();
593 context_buffer = ".y";
596 input_stack::get_char();
597 c = input_stack::peek_char();
599 input_stack::get_char();
600 c = input_stack::peek_char();
602 input_stack::get_char();
603 c = input_stack::peek_char();
605 input_stack::get_char();
606 c = input_stack::peek_char();
608 input_stack::get_char();
609 c = input_stack::peek_char();
611 input_stack::get_char();
612 context_buffer = ".center";
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";
626 input_stack::get_char();
627 c = input_stack::peek_char();
629 input_stack::get_char();
630 context_buffer = ".ne";
634 input_stack::get_char();
635 context_buffer = ".nw";
639 context_buffer = ".n";
644 input_stack::get_char();
645 c = input_stack::peek_char();
647 input_stack::get_char();
648 c = input_stack::peek_char();
650 input_stack::get_char();
651 context_buffer = ".end";
654 input_stack::push_back('n');
655 context_buffer = ".e";
658 context_buffer = ".e";
661 input_stack::get_char();
662 c = input_stack::peek_char();
664 input_stack::get_char();
665 c = input_stack::peek_char();
667 input_stack::get_char();
668 c = input_stack::peek_char();
670 input_stack::get_char();
671 c = input_stack::peek_char();
673 input_stack::get_char();
674 context_buffer = ".width";
677 input_stack::push_back('t');
679 context_buffer = ".wid";
682 input_stack::push_back('i');
684 context_buffer = ".w";
687 input_stack::get_char();
688 c = input_stack::peek_char();
690 input_stack::get_char();
691 context_buffer = ".se";
695 input_stack::get_char();
696 context_buffer = ".sw";
701 input_stack::get_char();
702 c = input_stack::peek_char();
704 input_stack::get_char();
705 c = input_stack::peek_char();
707 input_stack::get_char();
708 c = input_stack::peek_char();
710 input_stack::get_char();
711 context_buffer = ".start";
714 input_stack::push_back('r');
716 input_stack::push_back('a');
718 input_stack::push_back('t');
720 context_buffer = ".s";
725 input_stack::get_char();
726 c = input_stack::peek_char();
728 input_stack::get_char();
729 c = input_stack::peek_char();
731 input_stack::get_char();
732 context_buffer = ".top";
735 input_stack::push_back('o');
737 context_buffer = ".t";
740 input_stack::get_char();
741 c = input_stack::peek_char();
743 input_stack::get_char();
744 c = input_stack::peek_char();
746 input_stack::get_char();
747 c = input_stack::peek_char();
749 input_stack::get_char();
750 context_buffer = ".left";
753 input_stack::push_back('f');
755 input_stack::push_back('e');
757 context_buffer = ".l";
760 input_stack::get_char();
761 c = input_stack::peek_char();
763 input_stack::get_char();
764 c = input_stack::peek_char();
766 input_stack::get_char();
767 context_buffer = ".rad";
770 input_stack::push_back('a');
773 input_stack::get_char();
774 c = input_stack::peek_char();
776 input_stack::get_char();
777 c = input_stack::peek_char();
779 input_stack::get_char();
780 c = input_stack::peek_char();
782 input_stack::get_char();
783 context_buffer = ".right";
786 input_stack::push_back('h');
788 input_stack::push_back('g');
790 input_stack::push_back('i');
792 context_buffer = ".r";
795 input_stack::get_char();
796 c = input_stack::peek_char();
798 input_stack::get_char();
799 c = input_stack::peek_char();
801 input_stack::get_char();
802 c = input_stack::peek_char();
804 input_stack::get_char();
805 c = input_stack::peek_char();
807 input_stack::get_char();
808 c = input_stack::peek_char();
810 input_stack::get_char();
811 context_buffer = ".bottom";
814 input_stack::push_back('o');
816 input_stack::push_back('t');
818 context_buffer = ".bot";
821 input_stack::push_back('o');
823 context_buffer = ".b";
826 context_buffer = '.';
831 int get_token(int lookup_flag)
833 context_buffer.clear();
836 int bol = input_stack::bol();
837 int c = input_stack::get_char();
838 if (bol && c == command_char) {
839 token_buffer.clear();
841 // the newline is not part of the token
843 c = input_stack::peek_char();
844 if (c == EOF || c == '\n')
846 input_stack::get_char();
847 token_buffer += char(c);
849 context_buffer = token_buffer;
860 int d = input_stack::peek_char();
862 context_buffer = '\\';
865 input_stack::get_char();
870 c = input_stack::get_char();
871 } while (c != '\n' && c != EOF);
873 context_buffer = '\n';
876 context_buffer = '"';
877 token_buffer.clear();
879 c = input_stack::get_char();
881 context_buffer += '\\';
882 c = input_stack::peek_char();
884 input_stack::get_char();
886 context_buffer += '"';
889 token_buffer += '\\';
891 else if (c == '\n') {
892 error("newline in string");
896 error("missing `\"'");
900 context_buffer += '"';
904 context_buffer += char(c);
905 token_buffer += char(c);
923 if (n > (INT_MAX - 9)/10) {
929 context_buffer += char(c);
930 c = input_stack::peek_char();
931 if (c == EOF || !csdigit(c))
933 c = input_stack::get_char();
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))
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)
958 context_buffer += char(c);
959 input_stack::get_char();
963 context_buffer += '.';
964 input_stack::get_char();
968 c = input_stack::peek_char();
969 if (c == EOF || !csdigit(c))
971 input_stack::get_char();
972 context_buffer += char(c);
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();
990 input_stack::get_char();
991 c = input_stack::peek_char();
993 if (c == '+' || 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);
1002 context_buffer += char(echar);
1003 context_buffer += char(sign);
1006 if (c == EOF || !csdigit(c)) {
1007 input_stack::push_back(echar);
1010 context_buffer += char(echar);
1012 input_stack::get_char();
1013 context_buffer += char(c);
1016 c = input_stack::peek_char();
1017 if (c == EOF || !csdigit(c))
1019 input_stack::get_char();
1020 context_buffer += char(c);
1021 n = n*10 + (c - '0');
1025 if (c == 'i' || c == 'I') {
1026 context_buffer += char(c);
1027 input_stack::get_char();
1029 token_double *= pow(10.0, n);
1033 input_stack::get_char();
1034 c = input_stack::peek_char();
1036 input_stack::get_char();
1038 context_buffer += "nd";
1041 input_stack::push_back('n');
1044 input_stack::get_char();
1045 c = input_stack::peek_char();
1047 input_stack::get_char();
1049 context_buffer += "rd";
1052 input_stack::push_back('r');
1055 input_stack::get_char();
1056 c = input_stack::peek_char();
1058 input_stack::get_char();
1060 context_buffer += "th";
1063 input_stack::push_back('t');
1066 input_stack::get_char();
1067 c = input_stack::peek_char();
1069 input_stack::get_char();
1071 context_buffer += "st";
1074 input_stack::push_back('s');
1082 c = input_stack::peek_char();
1084 input_stack::get_char();
1085 c = input_stack::peek_char();
1087 input_stack::get_char();
1088 context_buffer = "'th";
1092 input_stack::push_back('t');
1094 context_buffer = "'";
1099 c = input_stack::peek_char();
1100 if (c != EOF && csdigit(c)) {
1103 context_buffer = '.';
1106 return get_token_after_dot(c);
1109 c = input_stack::peek_char();
1111 input_stack::get_char();
1112 c = input_stack::peek_char();
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 = "<=";
1126 context_buffer = "<";
1129 c = input_stack::peek_char();
1131 input_stack::get_char();
1132 context_buffer = "->";
1133 return RIGHT_ARROW_HEAD;
1135 context_buffer = "-";
1138 c = input_stack::peek_char();
1140 input_stack::get_char();
1141 context_buffer = "!=";
1144 context_buffer = "!";
1147 c = input_stack::peek_char();
1149 input_stack::get_char();
1150 context_buffer = ">=";
1151 return GREATEREQUAL;
1153 context_buffer = ">";
1156 c = input_stack::peek_char();
1158 input_stack::get_char();
1159 context_buffer = "==";
1162 context_buffer = "=";
1165 c = input_stack::peek_char();
1167 input_stack::get_char();
1168 context_buffer = "&&";
1171 context_buffer = "&";
1174 c = input_stack::peek_char();
1176 input_stack::get_char();
1177 context_buffer = "||";
1180 context_buffer = "|";
1183 if (c != EOF && csalpha(c)) {
1184 token_buffer.clear();
1187 c = input_stack::peek_char();
1188 if (c == EOF || (!csalnum(c) && c != '_'))
1190 input_stack::get_char();
1191 token_buffer += char(c);
1193 int tok = lookup_keyword(token_buffer.contents(),
1194 token_buffer.length());
1196 context_buffer = token_buffer;
1201 token_buffer += '\0';
1202 def = macro_table.lookup(token_buffer.contents());
1203 token_buffer.set_length(token_buffer.length() - 1);
1206 input_stack::get_char();
1207 interpolate_macro_with_args(def);
1210 input_stack::push(new macro_input(def));
1214 context_buffer = token_buffer;
1215 if (csupper(token_buffer[0]))
1222 context_buffer = char(c);
1223 return (unsigned char)c;
1232 token_buffer.clear();
1233 int c = input_stack::get_char();
1234 while (c == ' ' || c == '\t' || c == '\n')
1235 c = input_stack::get_char();
1237 lex_error("missing delimiter");
1240 context_buffer = char(c);
1241 int had_newline = 0;
1244 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1246 c = input_stack::get_char();
1248 lex_error("missing closing delimiter");
1253 else if (!had_newline)
1254 context_buffer += char(c);
1277 case IN_STRING_QUOTED:
1284 if (c == '"' || c == '\n')
1287 state = IN_STRING_QUOTED;
1290 // This case it just to shut cfront 2.0 up.
1294 if (state == DELIM_END)
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");
1308 token_buffer += '\0';
1309 string nm = token_buffer;
1310 const char *name = nm.contents();
1311 if (!get_delimited())
1313 token_buffer += '\0';
1314 macro_table.define(name, strsave(token_buffer.contents()));
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");
1324 token_buffer += '\0';
1325 macro_table.define(token_buffer.contents(), 0);
1329 class for_input : public input {
1334 int by_is_multiplicative;
1339 for_input(char *, double, double, int, double, char *);
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()
1358 int for_input::get()
1364 return (unsigned char)*p++;
1365 if (!done_newline) {
1370 if (!lookup_variable(var, &val)) {
1371 lex_error("body of `for' terminated enclosing block");
1374 if (by_is_multiplicative)
1378 define_variable(var, val);
1379 if ((from <= to && val > to)
1380 || (from >= to && val < to)) {
1389 int for_input::peek()
1394 return (unsigned char)*p;
1398 if (!lookup_variable(var, &val))
1400 if (by_is_multiplicative) {
1405 if ((from <= to && val + by > to)
1406 || (from >= to && val + by < to))
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))
1422 input_stack::push(new for_input(var, from, to,
1423 by_is_multiplicative, by, body));
1427 void do_copy(const char *filename)
1430 FILE *fp = fopen(filename, "r");
1432 lex_error("can't open `%1': %2", filename, strerror(errno));
1435 input_stack::push(new file_input(fp, filename));
1438 class copy_thru_input : public input {
1448 virtual int inget() = 0;
1450 copy_thru_input(const char *b, const char *u);
1456 class copy_file_thru_input : public copy_thru_input {
1459 copy_file_thru_input(input *, const char *b, const char *u);
1460 ~copy_file_thru_input();
1464 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1466 : copy_thru_input(b, u), in(i)
1470 copy_file_thru_input::~copy_file_thru_input()
1475 int copy_file_thru_input::inget()
1483 class copy_rest_thru_input : public copy_thru_input {
1485 copy_rest_thru_input(const char *, const char *u);
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()
1497 int c = next->get();
1500 if (next->next == 0)
1510 copy_thru_input::copy_thru_input(const char *b, const char *u)
1514 body = process_body(b);
1520 copy_thru_input::~copy_thru_input()
1526 int copy_thru_input::get()
1530 return (unsigned char)*ap++;
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++;
1551 return (unsigned char)*p++;
1556 int copy_thru_input::peek()
1560 return (unsigned char)*ap;
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;
1579 return (unsigned char)*p;
1584 int copy_thru_input::get_line()
1594 if (c == EOF || c == '\n')
1599 } while (c != '\n' && c != EOF);
1602 argv[argc++] = line.length();
1606 } while (c != ' ' && c != '\n');
1609 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1613 return argc > 0 || c == '\n';
1616 class simple_file_input : public input {
1617 const char *filename;
1621 simple_file_input(FILE *, const char *);
1622 ~simple_file_input();
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
1639 int simple_file_input::get()
1642 while (invalid_input_char(c)) {
1643 error("invalid input character code %1", c);
1651 int simple_file_input::peek()
1654 while (invalid_input_char(c)) {
1655 error("invalid input character code %1", c);
1663 int simple_file_input::get_location(const char **fnp, int *lnp)
1671 void copy_file_thru(const char *filename, const char *body, const char *until)
1674 FILE *fp = fopen(filename, "r");
1676 lex_error("can't open `%1': %2", filename, strerror(errno));
1679 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
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));
1697 char *get_thru_arg()
1699 int c = input_stack::peek_char();
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();
1709 c = input_stack::peek_char();
1710 if (c == EOF || (!csalnum(c) && c != '_'))
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());
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());
1733 int lookahead_token = -1;
1734 string old_context_buffer;
1738 if (lookahead_token == -1) {
1739 old_context_buffer = context_buffer;
1740 lookahead_token = get_token(1);
1747 assert(lookahead_token == -1);
1748 if (delim_flag == 2) {
1749 if ((yylval.str = get_thru_arg()) != 0)
1755 if (get_delimited()) {
1756 token_buffer += '\0';
1757 yylval.str = strsave(token_buffer.contents());
1766 if (lookahead_token >= 0) {
1767 t = lookahead_token;
1768 lookahead_token = -1;
1784 yylval.n = token_int;
1787 yylval.x = token_double;
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());
1801 token_buffer += '\0';
1802 yylval.str = strsave(token_buffer.contents());
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)
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;
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
1936 void lex_error(const char *message,
1941 const char *filename;
1943 if (!input_stack::get_location(&filename, &lineno))
1944 error(message, arg1, arg2, arg3);
1946 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1949 void lex_warning(const char *message,
1954 const char *filename;
1956 if (!input_stack::get_location(&filename, &lineno))
1957 warning(message, arg1, arg2, arg3);
1959 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1962 void yyerror(const char *s)
1964 const char *filename;
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();
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)) {
1981 if (context[0] == '\n' && context[1] == '\0')
1982 error("%1 before newline", s);
1984 error("%1 before `%2'", s, context);
1987 error("%1 at end of picture", s);
1991 if (context[0] == '\n' && context[1] == '\0')
1992 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1994 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1998 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);