2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004, 2006,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
28 implement_ptable(char)
30 PTABLE(char) macro_table;
32 // First character of the range representing $1-$<MAX_ARG>.
33 // All of them must be invalid input characters.
34 #ifndef IS_EBCDIC_HOST
42 class macro_input : public input {
46 macro_input(const char *);
52 class argument_macro_input : public input {
59 argument_macro_input(const char *, int, char **);
60 ~argument_macro_input();
65 input::input() : next(0)
73 int input::get_location(const char **, int *)
78 file_input::file_input(FILE *f, const char *fn)
79 : fp(f), filename(fn), lineno(0), ptr("")
83 file_input::~file_input()
88 int file_input::read_line()
97 else if (invalid_input_char(c))
98 lex_error("invalid input character code %1", c);
105 if (line.length() == 0)
107 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
108 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
109 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
110 || compatible_flag))) {
112 ptr = line.contents();
118 int file_input::get()
120 if (*ptr != '\0' || read_line())
121 return (unsigned char)*ptr++;
126 int file_input::peek()
128 if (*ptr != '\0' || read_line())
129 return (unsigned char)*ptr;
134 int file_input::get_location(const char **fnp, int *lnp)
141 macro_input::macro_input(const char *str)
143 p = s = strsave(str);
146 macro_input::~macro_input()
151 int macro_input::get()
153 if (p == 0 || *p == '\0')
156 return (unsigned char)*p++;
159 int macro_input::peek()
161 if (p == 0 || *p == '\0')
164 return (unsigned char)*p;
167 char *process_body(const char *body)
169 char *s = strsave(body);
171 for (int i = 0; s[i] != '\0'; i++)
172 if (s[i] == '$' && csdigit(s[i + 1])) {
176 while (csdigit(s[i]))
180 n = 10 * n + s[i++] - '0';
183 for (int k = start; k < i; k++)
185 lex_error("invalid macro argument number %1", arg.contents());
188 s[j++] = ARG1 + n - 1;
197 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
200 for (int i = 0; i < argc; i++)
202 p = s = process_body(body);
205 argument_macro_input::~argument_macro_input()
207 for (int i = 0; i < argc; i++)
212 int argument_macro_input::get()
216 return (unsigned char)*ap++;
221 while ((unsigned char)*p >= ARG1
222 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
223 int i = (unsigned char)*p++ - ARG1;
224 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
226 return (unsigned char)*ap++;
231 return (unsigned char)*p++;
234 int argument_macro_input::peek()
238 return (unsigned char)*ap;
243 while ((unsigned char)*p >= ARG1
244 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
245 int i = (unsigned char)*p++ - ARG1;
246 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
248 return (unsigned char)*ap;
253 return (unsigned char)*p;
257 static input *current_input;
260 static void push(input *);
262 static int get_char();
263 static int peek_char();
264 static int get_location(const char **fnp, int *lnp);
265 static void push_back(unsigned char c, int was_bol = 0);
269 input *input_stack::current_input = 0;
270 int input_stack::bol_flag = 0;
272 inline int input_stack::bol()
277 void input_stack::clear()
279 while (current_input != 0) {
280 input *tem = current_input;
281 current_input = current_input->next;
287 void input_stack::push(input *in)
289 in->next = current_input;
293 void lex_init(input *top)
295 input_stack::clear();
296 input_stack::push(top);
301 while (input_stack::get_char() != EOF)
305 int input_stack::get_char()
307 while (current_input != 0) {
308 int c = current_input->get();
310 bol_flag = c == '\n';
313 // don't pop the top-level input off the stack
314 if (current_input->next == 0)
316 input *tem = current_input;
317 current_input = current_input->next;
323 int input_stack::peek_char()
325 while (current_input != 0) {
326 int c = current_input->peek();
329 if (current_input->next == 0)
331 input *tem = current_input;
332 current_input = current_input->next;
338 class char_input : public input {
346 char_input::char_input(int n) : c((unsigned char)n)
350 int char_input::get()
357 int char_input::peek()
362 void input_stack::push_back(unsigned char c, int was_bol)
364 push(new char_input(c));
368 int input_stack::get_location(const char **fnp, int *lnp)
370 for (input *p = current_input; p; p = p->next)
371 if (p->get_location(fnp, lnp))
376 string context_buffer;
382 void interpolate_macro_with_args(const char *body)
388 for (i = 0; i < MAX_ARG; i++)
392 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
394 token_buffer.clear();
396 c = input_stack::get_char();
398 lex_error("end of input while scanning macro arguments");
401 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
402 if (token_buffer.length() > 0) {
403 token_buffer += '\0';
405 if (argc == MAX_ARG) {
406 lex_warning("only %1 macro arguments supported", MAX_ARG);
410 argv[argc] = strsave(token_buffer.contents());
413 // for `foo()', argc = 0
414 if (argc > 0 || c != ')' || i > 0)
419 token_buffer += char(c);
433 state = IN_STRING_QUOTED;
435 case IN_STRING_QUOTED:
440 } while (c != ')' && c != EOF);
441 input_stack::push(new argument_macro_input(body, argc, argv));
444 static int docmp(const char *s1, int n1, const char *s2, int n2)
447 int r = memcmp(s1, s2, n1);
451 int r = memcmp(s1, s2, n2);
455 return memcmp(s1, s2, n1);
458 int lookup_keyword(const char *str, int len)
460 static struct keyword {
466 { "aligned", ALIGNED },
473 { "between", BETWEEN },
474 { "bottom", BOTTOM },
478 { "center", CENTER },
480 { "circle", CIRCLE },
481 { "color", COLORED },
482 { "colored", COLORED },
483 { "colour", COLORED },
484 { "coloured", COLORED },
485 { "command", COMMAND },
489 { "dashed", DASHED },
490 { "define", DEFINE },
491 { "diam", DIAMETER },
492 { "diameter", DIAMETER },
494 { "dotted", DOTTED },
497 { "ellipse", ELLIPSE },
501 { "figname", FIGNAME },
506 { "height", HEIGHT },
510 { "invis", INVISIBLE },
511 { "invisible", INVISIBLE },
523 { "outline", OUTLINED },
524 { "outlined", OUTLINED },
528 { "radius", RADIUS },
535 { "shaded", SHADED },
539 { "spline", SPLINE },
540 { "sprintf", SPRINTF },
546 { "thick", THICKNESS },
547 { "thickness", THICKNESS },
560 { "xslanted", XSLANTED },
561 { "yslanted", YSLANTED },
564 const keyword *start = table;
565 const keyword *end = table + sizeof(table)/sizeof(table[0]);
566 while (start < end) {
567 // start <= target < end
568 const keyword *mid = start + (end - start)/2;
570 int cmp = docmp(str, len, mid->name, strlen(mid->name));
581 int get_token_after_dot(int c)
583 // get_token deals with the case where c is a digit
586 input_stack::get_char();
587 c = input_stack::peek_char();
589 input_stack::get_char();
590 context_buffer = ".ht";
594 input_stack::get_char();
595 c = input_stack::peek_char();
597 input_stack::get_char();
598 c = input_stack::peek_char();
600 input_stack::get_char();
601 c = input_stack::peek_char();
603 input_stack::get_char();
604 c = input_stack::peek_char();
606 input_stack::get_char();
607 context_buffer = ".height";
610 input_stack::push_back('h');
612 input_stack::push_back('g');
614 input_stack::push_back('i');
616 input_stack::push_back('e');
618 input_stack::push_back('h');
621 input_stack::get_char();
622 context_buffer = ".x";
625 input_stack::get_char();
626 context_buffer = ".y";
629 input_stack::get_char();
630 c = input_stack::peek_char();
632 input_stack::get_char();
633 c = input_stack::peek_char();
635 input_stack::get_char();
636 c = input_stack::peek_char();
638 input_stack::get_char();
639 c = input_stack::peek_char();
641 input_stack::get_char();
642 c = input_stack::peek_char();
644 input_stack::get_char();
645 context_buffer = ".center";
648 input_stack::push_back('e');
650 input_stack::push_back('t');
652 input_stack::push_back('n');
654 input_stack::push_back('e');
656 context_buffer = ".c";
659 input_stack::get_char();
660 c = input_stack::peek_char();
662 input_stack::get_char();
663 context_buffer = ".ne";
667 input_stack::get_char();
668 context_buffer = ".nw";
672 context_buffer = ".n";
677 input_stack::get_char();
678 c = input_stack::peek_char();
680 input_stack::get_char();
681 c = input_stack::peek_char();
683 input_stack::get_char();
684 context_buffer = ".end";
687 input_stack::push_back('n');
688 context_buffer = ".e";
691 context_buffer = ".e";
694 input_stack::get_char();
695 c = input_stack::peek_char();
697 input_stack::get_char();
698 c = input_stack::peek_char();
700 input_stack::get_char();
701 c = input_stack::peek_char();
703 input_stack::get_char();
704 c = input_stack::peek_char();
706 input_stack::get_char();
707 context_buffer = ".width";
710 input_stack::push_back('t');
712 context_buffer = ".wid";
715 input_stack::push_back('i');
717 context_buffer = ".w";
720 input_stack::get_char();
721 c = input_stack::peek_char();
723 input_stack::get_char();
724 context_buffer = ".se";
728 input_stack::get_char();
729 context_buffer = ".sw";
734 input_stack::get_char();
735 c = input_stack::peek_char();
737 input_stack::get_char();
738 c = input_stack::peek_char();
740 input_stack::get_char();
741 c = input_stack::peek_char();
743 input_stack::get_char();
744 context_buffer = ".start";
747 input_stack::push_back('r');
749 input_stack::push_back('a');
751 input_stack::push_back('t');
753 context_buffer = ".s";
758 input_stack::get_char();
759 c = input_stack::peek_char();
761 input_stack::get_char();
762 c = input_stack::peek_char();
764 input_stack::get_char();
765 context_buffer = ".top";
768 input_stack::push_back('o');
770 context_buffer = ".t";
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 = ".left";
786 input_stack::push_back('f');
788 input_stack::push_back('e');
790 context_buffer = ".l";
793 input_stack::get_char();
794 c = input_stack::peek_char();
796 input_stack::get_char();
797 c = input_stack::peek_char();
799 input_stack::get_char();
800 context_buffer = ".rad";
803 input_stack::push_back('a');
806 input_stack::get_char();
807 c = input_stack::peek_char();
809 input_stack::get_char();
810 c = input_stack::peek_char();
812 input_stack::get_char();
813 c = input_stack::peek_char();
815 input_stack::get_char();
816 context_buffer = ".right";
819 input_stack::push_back('h');
821 input_stack::push_back('g');
823 input_stack::push_back('i');
825 context_buffer = ".r";
828 input_stack::get_char();
829 c = input_stack::peek_char();
831 input_stack::get_char();
832 c = input_stack::peek_char();
834 input_stack::get_char();
835 c = input_stack::peek_char();
837 input_stack::get_char();
838 c = input_stack::peek_char();
840 input_stack::get_char();
841 c = input_stack::peek_char();
843 input_stack::get_char();
844 context_buffer = ".bottom";
847 input_stack::push_back('o');
849 input_stack::push_back('t');
851 context_buffer = ".bot";
854 input_stack::push_back('o');
856 context_buffer = ".b";
859 context_buffer = '.';
864 int get_token(int lookup_flag)
866 context_buffer.clear();
869 int bol = input_stack::bol();
870 int c = input_stack::get_char();
871 if (bol && c == command_char) {
872 token_buffer.clear();
874 // the newline is not part of the token
876 c = input_stack::peek_char();
877 if (c == EOF || c == '\n')
879 input_stack::get_char();
880 token_buffer += char(c);
882 context_buffer = token_buffer;
893 int d = input_stack::peek_char();
895 context_buffer = '\\';
898 input_stack::get_char();
903 c = input_stack::get_char();
904 } while (c != '\n' && c != EOF);
906 context_buffer = '\n';
909 context_buffer = '"';
910 token_buffer.clear();
912 c = input_stack::get_char();
914 context_buffer += '\\';
915 c = input_stack::peek_char();
917 input_stack::get_char();
919 context_buffer += '"';
922 token_buffer += '\\';
924 else if (c == '\n') {
925 error("newline in string");
929 error("missing `\"'");
933 context_buffer += '"';
937 context_buffer += char(c);
938 token_buffer += char(c);
956 if (n > (INT_MAX - 9)/10) {
962 context_buffer += char(c);
963 c = input_stack::peek_char();
964 if (c == EOF || !csdigit(c))
966 c = input_stack::get_char();
971 token_double *= 10.0;
972 token_double += c - '0';
973 context_buffer += char(c);
974 c = input_stack::peek_char();
975 if (c == EOF || !csdigit(c))
977 c = input_stack::get_char();
979 // if somebody asks for 1000000000000th, we will silently
980 // give them INT_MAXth
981 double temp = token_double; // work around gas 1.34/sparc bug
982 if (token_double > INT_MAX)
991 context_buffer += char(c);
992 input_stack::get_char();
996 context_buffer += '.';
997 input_stack::get_char();
1001 c = input_stack::peek_char();
1002 if (c == EOF || !csdigit(c))
1004 input_stack::get_char();
1005 context_buffer += char(c);
1008 token_double += factor*(c - '0');
1010 if (c != 'e' && c != 'E') {
1011 if (c == 'i' || c == 'I') {
1012 context_buffer += char(c);
1013 input_stack::get_char();
1023 input_stack::get_char();
1024 c = input_stack::peek_char();
1026 if (c == '+' || c == '-') {
1028 input_stack::get_char();
1029 c = input_stack::peek_char();
1030 if (c == EOF || !csdigit(c)) {
1031 input_stack::push_back(sign);
1032 input_stack::push_back(echar);
1035 context_buffer += char(echar);
1036 context_buffer += char(sign);
1039 if (c == EOF || !csdigit(c)) {
1040 input_stack::push_back(echar);
1043 context_buffer += char(echar);
1045 input_stack::get_char();
1046 context_buffer += char(c);
1049 c = input_stack::peek_char();
1050 if (c == EOF || !csdigit(c))
1052 input_stack::get_char();
1053 context_buffer += char(c);
1054 n = n*10 + (c - '0');
1058 if (c == 'i' || c == 'I') {
1059 context_buffer += char(c);
1060 input_stack::get_char();
1062 token_double *= pow(10.0, n);
1066 input_stack::get_char();
1067 c = input_stack::peek_char();
1069 input_stack::get_char();
1071 context_buffer += "nd";
1074 input_stack::push_back('n');
1077 input_stack::get_char();
1078 c = input_stack::peek_char();
1080 input_stack::get_char();
1082 context_buffer += "rd";
1085 input_stack::push_back('r');
1088 input_stack::get_char();
1089 c = input_stack::peek_char();
1091 input_stack::get_char();
1093 context_buffer += "th";
1096 input_stack::push_back('t');
1099 input_stack::get_char();
1100 c = input_stack::peek_char();
1102 input_stack::get_char();
1104 context_buffer += "st";
1107 input_stack::push_back('s');
1115 c = input_stack::peek_char();
1117 input_stack::get_char();
1118 c = input_stack::peek_char();
1120 input_stack::get_char();
1121 context_buffer = "'th";
1125 input_stack::push_back('t');
1127 context_buffer = "'";
1132 c = input_stack::peek_char();
1133 if (c != EOF && csdigit(c)) {
1136 context_buffer = '.';
1139 return get_token_after_dot(c);
1142 c = input_stack::peek_char();
1144 input_stack::get_char();
1145 c = input_stack::peek_char();
1147 input_stack::get_char();
1148 context_buffer = "<->";
1149 return DOUBLE_ARROW_HEAD;
1151 context_buffer = "<-";
1152 return LEFT_ARROW_HEAD;
1154 else if (c == '=') {
1155 input_stack::get_char();
1156 context_buffer = "<=";
1159 context_buffer = "<";
1162 c = input_stack::peek_char();
1164 input_stack::get_char();
1165 context_buffer = "->";
1166 return RIGHT_ARROW_HEAD;
1168 context_buffer = "-";
1171 c = input_stack::peek_char();
1173 input_stack::get_char();
1174 context_buffer = "!=";
1177 context_buffer = "!";
1180 c = input_stack::peek_char();
1182 input_stack::get_char();
1183 context_buffer = ">=";
1184 return GREATEREQUAL;
1186 context_buffer = ">";
1189 c = input_stack::peek_char();
1191 input_stack::get_char();
1192 context_buffer = "==";
1195 context_buffer = "=";
1198 c = input_stack::peek_char();
1200 input_stack::get_char();
1201 context_buffer = "&&";
1204 context_buffer = "&";
1207 c = input_stack::peek_char();
1209 input_stack::get_char();
1210 context_buffer = "||";
1213 context_buffer = "|";
1216 if (c != EOF && csalpha(c)) {
1217 token_buffer.clear();
1220 c = input_stack::peek_char();
1221 if (c == EOF || (!csalnum(c) && c != '_'))
1223 input_stack::get_char();
1224 token_buffer += char(c);
1226 int tok = lookup_keyword(token_buffer.contents(),
1227 token_buffer.length());
1229 context_buffer = token_buffer;
1234 token_buffer += '\0';
1235 def = macro_table.lookup(token_buffer.contents());
1236 token_buffer.set_length(token_buffer.length() - 1);
1239 input_stack::get_char();
1240 interpolate_macro_with_args(def);
1243 input_stack::push(new macro_input(def));
1247 context_buffer = token_buffer;
1248 if (csupper(token_buffer[0]))
1255 context_buffer = char(c);
1256 return (unsigned char)c;
1265 token_buffer.clear();
1266 int c = input_stack::get_char();
1267 while (c == ' ' || c == '\t' || c == '\n')
1268 c = input_stack::get_char();
1270 lex_error("missing delimiter");
1273 context_buffer = char(c);
1274 int had_newline = 0;
1277 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1279 c = input_stack::get_char();
1281 lex_error("missing closing delimiter");
1286 else if (!had_newline)
1287 context_buffer += char(c);
1310 case IN_STRING_QUOTED:
1317 if (c == '"' || c == '\n')
1320 state = IN_STRING_QUOTED;
1323 // This case it just to shut cfront 2.0 up.
1327 if (state == DELIM_END)
1336 int t = get_token(0); // do not expand what we are defining
1337 if (t != VARIABLE && t != LABEL) {
1338 lex_error("can only define variable or placename");
1341 token_buffer += '\0';
1342 string nm = token_buffer;
1343 const char *name = nm.contents();
1344 if (!get_delimited())
1346 token_buffer += '\0';
1347 macro_table.define(name, strsave(token_buffer.contents()));
1352 int t = get_token(0); // do not expand what we are undefining
1353 if (t != VARIABLE && t != LABEL) {
1354 lex_error("can only define variable or placename");
1357 token_buffer += '\0';
1358 macro_table.define(token_buffer.contents(), 0);
1362 class for_input : public input {
1367 int by_is_multiplicative;
1372 for_input(char *, double, double, int, double, char *);
1378 for_input::for_input(char *vr, double f, double t,
1379 int bim, double b, char *bd)
1380 : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1381 p(body), done_newline(0)
1385 for_input::~for_input()
1391 int for_input::get()
1397 return (unsigned char)*p++;
1398 if (!done_newline) {
1403 if (!lookup_variable(var, &val)) {
1404 lex_error("body of `for' terminated enclosing block");
1407 if (by_is_multiplicative)
1411 define_variable(var, val);
1412 if ((from <= to && val > to)
1413 || (from >= to && val < to)) {
1422 int for_input::peek()
1427 return (unsigned char)*p;
1431 if (!lookup_variable(var, &val))
1433 if (by_is_multiplicative) {
1438 if ((from <= to && val + by > to)
1439 || (from >= to && val + by < to))
1444 return (unsigned char)*body;
1447 void do_for(char *var, double from, double to, int by_is_multiplicative,
1448 double by, char *body)
1450 define_variable(var, from);
1451 if ((by_is_multiplicative && by <= 0)
1452 || (by > 0 && from > to)
1453 || (by < 0 && from < to))
1455 input_stack::push(new for_input(var, from, to,
1456 by_is_multiplicative, by, body));
1460 void do_copy(const char *filename)
1463 FILE *fp = fopen(filename, "r");
1465 lex_error("can't open `%1': %2", filename, strerror(errno));
1468 input_stack::push(new file_input(fp, filename));
1471 class copy_thru_input : public input {
1481 virtual int inget() = 0;
1483 copy_thru_input(const char *b, const char *u);
1489 class copy_file_thru_input : public copy_thru_input {
1492 copy_file_thru_input(input *, const char *b, const char *u);
1493 ~copy_file_thru_input();
1497 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1499 : copy_thru_input(b, u), in(i)
1503 copy_file_thru_input::~copy_file_thru_input()
1508 int copy_file_thru_input::inget()
1516 class copy_rest_thru_input : public copy_thru_input {
1518 copy_rest_thru_input(const char *, const char *u);
1522 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1523 : copy_thru_input(b, u)
1527 int copy_rest_thru_input::inget()
1530 int c = next->get();
1533 if (next->next == 0)
1543 copy_thru_input::copy_thru_input(const char *b, const char *u)
1547 body = process_body(b);
1553 copy_thru_input::~copy_thru_input()
1559 int copy_thru_input::get()
1563 return (unsigned char)*ap++;
1576 while ((unsigned char)*p >= ARG1
1577 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1578 int i = (unsigned char)*p++ - ARG1;
1579 if (i < argc && line[argv[i]] != '\0') {
1580 ap = line.contents() + argv[i];
1581 return (unsigned char)*ap++;
1585 return (unsigned char)*p++;
1590 int copy_thru_input::peek()
1594 return (unsigned char)*ap;
1605 while ((unsigned char)*p >= ARG1
1606 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1607 int i = (unsigned char)*p++ - ARG1;
1608 if (i < argc && line[argv[i]] != '\0') {
1609 ap = line.contents() + argv[i];
1610 return (unsigned char)*ap;
1614 return (unsigned char)*p;
1619 int copy_thru_input::get_line()
1629 if (c == EOF || c == '\n')
1631 if (argc == MAX_ARG) {
1634 } while (c != '\n' && c != EOF);
1637 argv[argc++] = line.length();
1641 } while (c != ' ' && c != '\n');
1644 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1648 return argc > 0 || c == '\n';
1651 class simple_file_input : public input {
1652 const char *filename;
1656 simple_file_input(FILE *, const char *);
1657 ~simple_file_input();
1660 int get_location(const char **, int *);
1663 simple_file_input::simple_file_input(FILE *p, const char *s)
1664 : filename(s), lineno(1), fp(p)
1668 simple_file_input::~simple_file_input()
1670 // don't delete the filename
1674 int simple_file_input::get()
1677 while (invalid_input_char(c)) {
1678 error("invalid input character code %1", c);
1686 int simple_file_input::peek()
1689 while (invalid_input_char(c)) {
1690 error("invalid input character code %1", c);
1698 int simple_file_input::get_location(const char **fnp, int *lnp)
1706 void copy_file_thru(const char *filename, const char *body, const char *until)
1709 FILE *fp = fopen(filename, "r");
1711 lex_error("can't open `%1': %2", filename, strerror(errno));
1714 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1716 input_stack::push(in);
1719 void copy_rest_thru(const char *body, const char *until)
1721 input_stack::push(new copy_rest_thru_input(body, until));
1724 void push_body(const char *s)
1726 input_stack::push(new char_input('\n'));
1727 input_stack::push(new macro_input(s));
1732 char *get_thru_arg()
1734 int c = input_stack::peek_char();
1736 input_stack::get_char();
1737 c = input_stack::peek_char();
1739 if (c != EOF && csalpha(c)) {
1740 // looks like a macro
1741 input_stack::get_char();
1744 c = input_stack::peek_char();
1745 if (c == EOF || (!csalnum(c) && c != '_'))
1747 input_stack::get_char();
1748 token_buffer += char(c);
1750 context_buffer = token_buffer;
1751 token_buffer += '\0';
1752 char *def = macro_table.lookup(token_buffer.contents());
1754 return strsave(def);
1755 // I guess it wasn't a macro after all; so push the macro name back.
1756 // -2 because we added a '\0'
1757 for (int i = token_buffer.length() - 2; i >= 0; i--)
1758 input_stack::push_back(token_buffer[i]);
1760 if (get_delimited()) {
1761 token_buffer += '\0';
1762 return strsave(token_buffer.contents());
1768 int lookahead_token = -1;
1769 string old_context_buffer;
1773 if (lookahead_token == -1) {
1774 old_context_buffer = context_buffer;
1775 lookahead_token = get_token(1);
1782 assert(lookahead_token == -1);
1783 if (delim_flag == 2) {
1784 if ((yylval.str = get_thru_arg()) != 0)
1790 if (get_delimited()) {
1791 token_buffer += '\0';
1792 yylval.str = strsave(token_buffer.contents());
1801 if (lookahead_token >= 0) {
1802 t = lookahead_token;
1803 lookahead_token = -1;
1819 yylval.n = token_int;
1822 yylval.x = token_double;
1826 token_buffer += '\0';
1827 if (!input_stack::get_location(&yylval.lstr.filename,
1828 &yylval.lstr.lineno)) {
1829 yylval.lstr.filename = 0;
1830 yylval.lstr.lineno = -1;
1832 yylval.lstr.str = strsave(token_buffer.contents());
1836 token_buffer += '\0';
1837 yylval.str = strsave(token_buffer.contents());
1840 // change LEFT to LEFT_CORNER when followed by OF
1841 old_context_buffer = context_buffer;
1842 lookahead_token = get_token(1);
1843 if (lookahead_token == OF)
1848 // change RIGHT to RIGHT_CORNER when followed by OF
1849 old_context_buffer = context_buffer;
1850 lookahead_token = get_token(1);
1851 if (lookahead_token == OF)
1852 return RIGHT_CORNER;
1856 // recognise UPPER only before LEFT or RIGHT
1857 old_context_buffer = context_buffer;
1858 lookahead_token = get_token(1);
1859 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1860 yylval.str = strsave("upper");
1866 // recognise LOWER only before LEFT or RIGHT
1867 old_context_buffer = context_buffer;
1868 lookahead_token = get_token(1);
1869 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1870 yylval.str = strsave("lower");
1876 // recognise NORTH only before OF
1877 old_context_buffer = context_buffer;
1878 lookahead_token = get_token(1);
1879 if (lookahead_token != OF) {
1880 yylval.str = strsave("north");
1886 // recognise SOUTH only before OF
1887 old_context_buffer = context_buffer;
1888 lookahead_token = get_token(1);
1889 if (lookahead_token != OF) {
1890 yylval.str = strsave("south");
1896 // recognise EAST only before OF
1897 old_context_buffer = context_buffer;
1898 lookahead_token = get_token(1);
1899 if (lookahead_token != OF) {
1900 yylval.str = strsave("east");
1906 // recognise WEST only before OF
1907 old_context_buffer = context_buffer;
1908 lookahead_token = get_token(1);
1909 if (lookahead_token != OF) {
1910 yylval.str = strsave("west");
1916 // recognise TOP only before OF
1917 old_context_buffer = context_buffer;
1918 lookahead_token = get_token(1);
1919 if (lookahead_token != OF) {
1920 yylval.str = strsave("top");
1926 // recognise BOTTOM only before OF
1927 old_context_buffer = context_buffer;
1928 lookahead_token = get_token(1);
1929 if (lookahead_token != OF) {
1930 yylval.str = strsave("bottom");
1936 // recognise CENTER only before OF
1937 old_context_buffer = context_buffer;
1938 lookahead_token = get_token(1);
1939 if (lookahead_token != OF) {
1940 yylval.str = strsave("center");
1946 // recognise START only before OF
1947 old_context_buffer = context_buffer;
1948 lookahead_token = get_token(1);
1949 if (lookahead_token != OF) {
1950 yylval.str = strsave("start");
1956 // recognise END only before OF
1957 old_context_buffer = context_buffer;
1958 lookahead_token = get_token(1);
1959 if (lookahead_token != OF) {
1960 yylval.str = strsave("end");
1971 void lex_error(const char *message,
1976 const char *filename;
1978 if (!input_stack::get_location(&filename, &lineno))
1979 error(message, arg1, arg2, arg3);
1981 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1984 void lex_warning(const char *message,
1989 const char *filename;
1991 if (!input_stack::get_location(&filename, &lineno))
1992 warning(message, arg1, arg2, arg3);
1994 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1997 void yyerror(const char *s)
1999 const char *filename;
2001 const char *context = 0;
2002 if (lookahead_token == -1) {
2003 if (context_buffer.length() > 0) {
2004 context_buffer += '\0';
2005 context = context_buffer.contents();
2009 if (old_context_buffer.length() > 0) {
2010 old_context_buffer += '\0';
2011 context = old_context_buffer.contents();
2014 if (!input_stack::get_location(&filename, &lineno)) {
2016 if (context[0] == '\n' && context[1] == '\0')
2017 error("%1 before newline", s);
2019 error("%1 before `%2'", s, context);
2022 error("%1 at end of picture", s);
2026 if (context[0] == '\n' && context[1] == '\0')
2027 error_with_file_and_line(filename, lineno, "%1 before newline", s);
2029 error_with_file_and_line(filename, lineno, "%1 before `%2'",
2033 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);