2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 implement_ptable(char)
28 PTABLE(char) macro_table;
30 // First character of the range representing $1-$<MAX_ARG>.
31 // All of them must be invalid input characters.
32 #ifndef IS_EBCDIC_HOST
40 class macro_input : public input {
44 macro_input(const char *);
50 class argument_macro_input : public input {
57 argument_macro_input(const char *, int, char **);
58 ~argument_macro_input();
63 input::input() : next(0)
71 int input::get_location(const char **, int *)
76 file_input::file_input(FILE *f, const char *fn)
77 : fp(f), filename(fn), lineno(0), ptr("")
81 file_input::~file_input()
86 int file_input::read_line()
96 lex_error("invalid input character code %1", '\r');
100 else if (invalid_input_char(c))
101 lex_error("invalid input character code %1", c);
108 if (line.length() == 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))) {
115 ptr = line.contents();
121 int file_input::get()
123 if (*ptr != '\0' || read_line())
124 return (unsigned char)*ptr++;
129 int file_input::peek()
131 if (*ptr != '\0' || read_line())
132 return (unsigned char)*ptr;
137 int file_input::get_location(const char **fnp, int *lnp)
144 macro_input::macro_input(const char *str)
146 p = s = strsave(str);
149 macro_input::~macro_input()
154 int macro_input::get()
156 if (p == 0 || *p == '\0')
159 return (unsigned char)*p++;
162 int macro_input::peek()
164 if (p == 0 || *p == '\0')
167 return (unsigned char)*p;
170 char *process_body(const char *body)
172 char *s = strsave(body);
174 for (int i = 0; s[i] != '\0'; i++)
175 if (s[i] == '$' && csdigit(s[i + 1])) {
179 while (csdigit(s[i]))
183 n = 10 * n + s[i++] - '0';
186 for (int k = start; k < i; k++)
188 lex_error("invalid macro argument number %1", arg.contents());
191 s[j++] = ARG1 + n - 1;
200 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
203 for (int i = 0; i < argc; i++)
205 p = s = process_body(body);
208 argument_macro_input::~argument_macro_input()
210 for (int i = 0; i < argc; i++)
215 int argument_macro_input::get()
219 return (unsigned char)*ap++;
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') {
229 return (unsigned char)*ap++;
234 return (unsigned char)*p++;
237 int argument_macro_input::peek()
241 return (unsigned char)*ap;
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') {
251 return (unsigned char)*ap;
256 return (unsigned char)*p;
260 static input *current_input;
263 static void push(input *);
265 static int get_char();
266 static int peek_char();
267 static int get_location(const char **fnp, int *lnp);
268 static void push_back(unsigned char c, int was_bol = 0);
272 input *input_stack::current_input = 0;
273 int input_stack::bol_flag = 0;
275 inline int input_stack::bol()
280 void input_stack::clear()
282 while (current_input != 0) {
283 input *tem = current_input;
284 current_input = current_input->next;
290 void input_stack::push(input *in)
292 in->next = current_input;
296 void lex_init(input *top)
298 input_stack::clear();
299 input_stack::push(top);
304 while (input_stack::get_char() != EOF)
308 int input_stack::get_char()
310 while (current_input != 0) {
311 int c = current_input->get();
313 bol_flag = c == '\n';
316 // don't pop the top-level input off the stack
317 if (current_input->next == 0)
319 input *tem = current_input;
320 current_input = current_input->next;
326 int input_stack::peek_char()
328 while (current_input != 0) {
329 int c = current_input->peek();
332 if (current_input->next == 0)
334 input *tem = current_input;
335 current_input = current_input->next;
341 class char_input : public input {
349 char_input::char_input(int n) : c((unsigned char)n)
353 int char_input::get()
360 int char_input::peek()
365 void input_stack::push_back(unsigned char c, int was_bol)
367 push(new char_input(c));
371 int input_stack::get_location(const char **fnp, int *lnp)
373 for (input *p = current_input; p; p = p->next)
374 if (p->get_location(fnp, lnp))
379 string context_buffer;
385 void interpolate_macro_with_args(const char *body)
391 for (i = 0; i < MAX_ARG; i++)
395 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
397 token_buffer.clear();
399 c = input_stack::get_char();
401 lex_error("end of input while scanning macro arguments");
404 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
405 if (token_buffer.length() > 0) {
406 token_buffer += '\0';
408 if (argc == MAX_ARG) {
409 lex_warning("only %1 macro arguments supported", MAX_ARG);
413 argv[argc] = strsave(token_buffer.contents());
416 // for `foo()', argc = 0
417 if (argc > 0 || c != ')' || i > 0)
422 token_buffer += char(c);
436 state = IN_STRING_QUOTED;
438 case IN_STRING_QUOTED:
443 } while (c != ')' && c != EOF);
444 input_stack::push(new argument_macro_input(body, argc, argv));
447 static int docmp(const char *s1, int n1, const char *s2, int n2)
450 int r = memcmp(s1, s2, n1);
454 int r = memcmp(s1, s2, n2);
458 return memcmp(s1, s2, n1);
461 int lookup_keyword(const char *str, int len)
463 static struct keyword {
469 { "aligned", ALIGNED },
476 { "between", BETWEEN },
477 { "bottom", BOTTOM },
481 { "center", CENTER },
483 { "circle", CIRCLE },
484 { "color", COLORED },
485 { "colored", COLORED },
486 { "colour", COLORED },
487 { "coloured", COLORED },
488 { "command", COMMAND },
492 { "dashed", DASHED },
493 { "define", DEFINE },
494 { "diam", DIAMETER },
495 { "diameter", DIAMETER },
497 { "dotted", DOTTED },
500 { "ellipse", ELLIPSE },
504 { "figname", FIGNAME },
509 { "height", HEIGHT },
513 { "invis", INVISIBLE },
514 { "invisible", INVISIBLE },
526 { "outline", OUTLINED },
527 { "outlined", OUTLINED },
531 { "radius", RADIUS },
538 { "shaded", SHADED },
542 { "spline", SPLINE },
543 { "sprintf", SPRINTF },
549 { "thick", THICKNESS },
550 { "thickness", THICKNESS },
563 { "xslanted", XSLANTED },
564 { "yslanted", YSLANTED },
567 const keyword *start = table;
568 const keyword *end = table + sizeof(table)/sizeof(table[0]);
569 while (start < end) {
570 // start <= target < end
571 const keyword *mid = start + (end - start)/2;
573 int cmp = docmp(str, len, mid->name, strlen(mid->name));
584 int get_token_after_dot(int c)
586 // get_token deals with the case where c is a digit
589 input_stack::get_char();
590 c = input_stack::peek_char();
592 input_stack::get_char();
593 context_buffer = ".ht";
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 c = input_stack::peek_char();
609 input_stack::get_char();
610 context_buffer = ".height";
613 input_stack::push_back('h');
615 input_stack::push_back('g');
617 input_stack::push_back('i');
619 input_stack::push_back('e');
621 input_stack::push_back('h');
624 input_stack::get_char();
625 context_buffer = ".x";
628 input_stack::get_char();
629 context_buffer = ".y";
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 c = input_stack::peek_char();
647 input_stack::get_char();
648 context_buffer = ".center";
651 input_stack::push_back('e');
653 input_stack::push_back('t');
655 input_stack::push_back('n');
657 input_stack::push_back('e');
659 context_buffer = ".c";
662 input_stack::get_char();
663 c = input_stack::peek_char();
665 input_stack::get_char();
666 context_buffer = ".ne";
670 input_stack::get_char();
671 context_buffer = ".nw";
675 context_buffer = ".n";
680 input_stack::get_char();
681 c = input_stack::peek_char();
683 input_stack::get_char();
684 c = input_stack::peek_char();
686 input_stack::get_char();
687 context_buffer = ".end";
690 input_stack::push_back('n');
691 context_buffer = ".e";
694 context_buffer = ".e";
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 c = input_stack::peek_char();
709 input_stack::get_char();
710 context_buffer = ".width";
713 input_stack::push_back('t');
715 context_buffer = ".wid";
718 input_stack::push_back('i');
720 context_buffer = ".w";
723 input_stack::get_char();
724 c = input_stack::peek_char();
726 input_stack::get_char();
727 context_buffer = ".se";
731 input_stack::get_char();
732 context_buffer = ".sw";
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 c = input_stack::peek_char();
746 input_stack::get_char();
747 context_buffer = ".start";
750 input_stack::push_back('r');
752 input_stack::push_back('a');
754 input_stack::push_back('t');
756 context_buffer = ".s";
761 input_stack::get_char();
762 c = input_stack::peek_char();
764 input_stack::get_char();
765 c = input_stack::peek_char();
767 input_stack::get_char();
768 context_buffer = ".top";
771 input_stack::push_back('o');
773 context_buffer = ".t";
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 c = input_stack::peek_char();
785 input_stack::get_char();
786 context_buffer = ".left";
789 input_stack::push_back('f');
791 input_stack::push_back('e');
793 context_buffer = ".l";
796 input_stack::get_char();
797 c = input_stack::peek_char();
799 input_stack::get_char();
800 c = input_stack::peek_char();
802 input_stack::get_char();
803 context_buffer = ".rad";
806 input_stack::push_back('a');
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 c = input_stack::peek_char();
818 input_stack::get_char();
819 context_buffer = ".right";
822 input_stack::push_back('h');
824 input_stack::push_back('g');
826 input_stack::push_back('i');
828 context_buffer = ".r";
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 c = input_stack::peek_char();
846 input_stack::get_char();
847 context_buffer = ".bottom";
850 input_stack::push_back('o');
852 input_stack::push_back('t');
854 context_buffer = ".bot";
857 input_stack::push_back('o');
859 context_buffer = ".b";
862 context_buffer = '.';
867 int get_token(int lookup_flag)
869 context_buffer.clear();
872 int bol = input_stack::bol();
873 int c = input_stack::get_char();
874 if (bol && c == command_char) {
875 token_buffer.clear();
877 // the newline is not part of the token
879 c = input_stack::peek_char();
880 if (c == EOF || c == '\n')
882 input_stack::get_char();
883 token_buffer += char(c);
885 context_buffer = token_buffer;
896 int d = input_stack::peek_char();
898 context_buffer = '\\';
901 input_stack::get_char();
906 c = input_stack::get_char();
907 } while (c != '\n' && c != EOF);
909 context_buffer = '\n';
912 context_buffer = '"';
913 token_buffer.clear();
915 c = input_stack::get_char();
917 context_buffer += '\\';
918 c = input_stack::peek_char();
920 input_stack::get_char();
922 context_buffer += '"';
925 token_buffer += '\\';
927 else if (c == '\n') {
928 error("newline in string");
932 error("missing `\"'");
936 context_buffer += '"';
940 context_buffer += char(c);
941 token_buffer += char(c);
959 if (n > (INT_MAX - 9)/10) {
965 context_buffer += char(c);
966 c = input_stack::peek_char();
967 if (c == EOF || !csdigit(c))
969 c = input_stack::get_char();
974 token_double *= 10.0;
975 token_double += c - '0';
976 context_buffer += char(c);
977 c = input_stack::peek_char();
978 if (c == EOF || !csdigit(c))
980 c = input_stack::get_char();
982 // if somebody asks for 1000000000000th, we will silently
983 // give them INT_MAXth
984 double temp = token_double; // work around gas 1.34/sparc bug
985 if (token_double > INT_MAX)
994 context_buffer += char(c);
995 input_stack::get_char();
999 context_buffer += '.';
1000 input_stack::get_char();
1002 double factor = 1.0;
1004 c = input_stack::peek_char();
1005 if (c == EOF || !csdigit(c))
1007 input_stack::get_char();
1008 context_buffer += char(c);
1011 token_double += factor*(c - '0');
1013 if (c != 'e' && c != 'E') {
1014 if (c == 'i' || c == 'I') {
1015 context_buffer += char(c);
1016 input_stack::get_char();
1026 input_stack::get_char();
1027 c = input_stack::peek_char();
1029 if (c == '+' || c == '-') {
1031 input_stack::get_char();
1032 c = input_stack::peek_char();
1033 if (c == EOF || !csdigit(c)) {
1034 input_stack::push_back(sign);
1035 input_stack::push_back(echar);
1038 context_buffer += char(echar);
1039 context_buffer += char(sign);
1042 if (c == EOF || !csdigit(c)) {
1043 input_stack::push_back(echar);
1046 context_buffer += char(echar);
1048 input_stack::get_char();
1049 context_buffer += char(c);
1052 c = input_stack::peek_char();
1053 if (c == EOF || !csdigit(c))
1055 input_stack::get_char();
1056 context_buffer += char(c);
1057 n = n*10 + (c - '0');
1061 if (c == 'i' || c == 'I') {
1062 context_buffer += char(c);
1063 input_stack::get_char();
1065 token_double *= pow(10.0, n);
1069 input_stack::get_char();
1070 c = input_stack::peek_char();
1072 input_stack::get_char();
1074 context_buffer += "nd";
1077 input_stack::push_back('n');
1080 input_stack::get_char();
1081 c = input_stack::peek_char();
1083 input_stack::get_char();
1085 context_buffer += "rd";
1088 input_stack::push_back('r');
1091 input_stack::get_char();
1092 c = input_stack::peek_char();
1094 input_stack::get_char();
1096 context_buffer += "th";
1099 input_stack::push_back('t');
1102 input_stack::get_char();
1103 c = input_stack::peek_char();
1105 input_stack::get_char();
1107 context_buffer += "st";
1110 input_stack::push_back('s');
1118 c = input_stack::peek_char();
1120 input_stack::get_char();
1121 c = input_stack::peek_char();
1123 input_stack::get_char();
1124 context_buffer = "'th";
1128 input_stack::push_back('t');
1130 context_buffer = "'";
1135 c = input_stack::peek_char();
1136 if (c != EOF && csdigit(c)) {
1139 context_buffer = '.';
1142 return get_token_after_dot(c);
1145 c = input_stack::peek_char();
1147 input_stack::get_char();
1148 c = input_stack::peek_char();
1150 input_stack::get_char();
1151 context_buffer = "<->";
1152 return DOUBLE_ARROW_HEAD;
1154 context_buffer = "<-";
1155 return LEFT_ARROW_HEAD;
1157 else if (c == '=') {
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 = "->";
1169 return RIGHT_ARROW_HEAD;
1171 context_buffer = "-";
1174 c = input_stack::peek_char();
1176 input_stack::get_char();
1177 context_buffer = "!=";
1180 context_buffer = "!";
1183 c = input_stack::peek_char();
1185 input_stack::get_char();
1186 context_buffer = ">=";
1187 return GREATEREQUAL;
1189 context_buffer = ">";
1192 c = input_stack::peek_char();
1194 input_stack::get_char();
1195 context_buffer = "==";
1198 context_buffer = "=";
1201 c = input_stack::peek_char();
1203 input_stack::get_char();
1204 context_buffer = "&&";
1207 context_buffer = "&";
1210 c = input_stack::peek_char();
1212 input_stack::get_char();
1213 context_buffer = "||";
1216 context_buffer = "|";
1219 if (c != EOF && csalpha(c)) {
1220 token_buffer.clear();
1223 c = input_stack::peek_char();
1224 if (c == EOF || (!csalnum(c) && c != '_'))
1226 input_stack::get_char();
1227 token_buffer += char(c);
1229 int tok = lookup_keyword(token_buffer.contents(),
1230 token_buffer.length());
1232 context_buffer = token_buffer;
1237 token_buffer += '\0';
1238 def = macro_table.lookup(token_buffer.contents());
1239 token_buffer.set_length(token_buffer.length() - 1);
1242 input_stack::get_char();
1243 interpolate_macro_with_args(def);
1246 input_stack::push(new macro_input(def));
1250 context_buffer = token_buffer;
1251 if (csupper(token_buffer[0]))
1258 context_buffer = char(c);
1259 return (unsigned char)c;
1268 token_buffer.clear();
1269 int c = input_stack::get_char();
1270 while (c == ' ' || c == '\t' || c == '\n')
1271 c = input_stack::get_char();
1273 lex_error("missing delimiter");
1276 context_buffer = char(c);
1277 int had_newline = 0;
1280 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1282 c = input_stack::get_char();
1284 lex_error("missing closing delimiter");
1289 else if (!had_newline)
1290 context_buffer += char(c);
1313 case IN_STRING_QUOTED:
1320 if (c == '"' || c == '\n')
1323 state = IN_STRING_QUOTED;
1326 // This case it just to shut cfront 2.0 up.
1330 if (state == DELIM_END)
1339 int t = get_token(0); // do not expand what we are defining
1340 if (t != VARIABLE && t != LABEL) {
1341 lex_error("can only define variable or placename");
1344 token_buffer += '\0';
1345 string nm = token_buffer;
1346 const char *name = nm.contents();
1347 if (!get_delimited())
1349 token_buffer += '\0';
1350 macro_table.define(name, strsave(token_buffer.contents()));
1355 int t = get_token(0); // do not expand what we are undefining
1356 if (t != VARIABLE && t != LABEL) {
1357 lex_error("can only define variable or placename");
1360 token_buffer += '\0';
1361 macro_table.define(token_buffer.contents(), 0);
1365 class for_input : public input {
1370 int by_is_multiplicative;
1375 for_input(char *, double, double, int, double, char *);
1381 for_input::for_input(char *vr, double f, double t,
1382 int bim, double b, char *bd)
1383 : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1384 p(body), done_newline(0)
1388 for_input::~for_input()
1394 int for_input::get()
1400 return (unsigned char)*p++;
1401 if (!done_newline) {
1406 if (!lookup_variable(var, &val)) {
1407 lex_error("body of `for' terminated enclosing block");
1410 if (by_is_multiplicative)
1414 define_variable(var, val);
1415 if ((from <= to && val > to)
1416 || (from >= to && val < to)) {
1425 int for_input::peek()
1430 return (unsigned char)*p;
1434 if (!lookup_variable(var, &val))
1436 if (by_is_multiplicative) {
1441 if ((from <= to && val + by > to)
1442 || (from >= to && val + by < to))
1447 return (unsigned char)*body;
1450 void do_for(char *var, double from, double to, int by_is_multiplicative,
1451 double by, char *body)
1453 define_variable(var, from);
1454 if ((by_is_multiplicative && by <= 0)
1455 || (by > 0 && from > to)
1456 || (by < 0 && from < to))
1458 input_stack::push(new for_input(var, from, to,
1459 by_is_multiplicative, by, body));
1463 void do_copy(const char *filename)
1466 FILE *fp = fopen(filename, "r");
1468 lex_error("can't open `%1': %2", filename, strerror(errno));
1471 input_stack::push(new file_input(fp, filename));
1474 class copy_thru_input : public input {
1484 virtual int inget() = 0;
1486 copy_thru_input(const char *b, const char *u);
1492 class copy_file_thru_input : public copy_thru_input {
1495 copy_file_thru_input(input *, const char *b, const char *u);
1496 ~copy_file_thru_input();
1500 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1502 : copy_thru_input(b, u), in(i)
1506 copy_file_thru_input::~copy_file_thru_input()
1511 int copy_file_thru_input::inget()
1519 class copy_rest_thru_input : public copy_thru_input {
1521 copy_rest_thru_input(const char *, const char *u);
1525 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1526 : copy_thru_input(b, u)
1530 int copy_rest_thru_input::inget()
1533 int c = next->get();
1536 if (next->next == 0)
1546 copy_thru_input::copy_thru_input(const char *b, const char *u)
1550 body = process_body(b);
1556 copy_thru_input::~copy_thru_input()
1562 int copy_thru_input::get()
1566 return (unsigned char)*ap++;
1579 while ((unsigned char)*p >= ARG1
1580 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1581 int i = (unsigned char)*p++ - ARG1;
1582 if (i < argc && line[argv[i]] != '\0') {
1583 ap = line.contents() + argv[i];
1584 return (unsigned char)*ap++;
1588 return (unsigned char)*p++;
1593 int copy_thru_input::peek()
1597 return (unsigned char)*ap;
1608 while ((unsigned char)*p >= ARG1
1609 && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1610 int i = (unsigned char)*p++ - ARG1;
1611 if (i < argc && line[argv[i]] != '\0') {
1612 ap = line.contents() + argv[i];
1613 return (unsigned char)*ap;
1617 return (unsigned char)*p;
1622 int copy_thru_input::get_line()
1632 if (c == EOF || c == '\n')
1634 if (argc == MAX_ARG) {
1637 } while (c != '\n' && c != EOF);
1640 argv[argc++] = line.length();
1644 } while (c != ' ' && c != '\n');
1647 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1651 return argc > 0 || c == '\n';
1654 class simple_file_input : public input {
1655 const char *filename;
1659 simple_file_input(FILE *, const char *);
1660 ~simple_file_input();
1663 int get_location(const char **, int *);
1666 simple_file_input::simple_file_input(FILE *p, const char *s)
1667 : filename(s), lineno(1), fp(p)
1671 simple_file_input::~simple_file_input()
1673 // don't delete the filename
1677 int simple_file_input::get()
1680 while (invalid_input_char(c)) {
1681 error("invalid input character code %1", c);
1689 int simple_file_input::peek()
1692 while (invalid_input_char(c)) {
1693 error("invalid input character code %1", c);
1701 int simple_file_input::get_location(const char **fnp, int *lnp)
1709 void copy_file_thru(const char *filename, const char *body, const char *until)
1712 FILE *fp = fopen(filename, "r");
1714 lex_error("can't open `%1': %2", filename, strerror(errno));
1717 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1719 input_stack::push(in);
1722 void copy_rest_thru(const char *body, const char *until)
1724 input_stack::push(new copy_rest_thru_input(body, until));
1727 void push_body(const char *s)
1729 input_stack::push(new char_input('\n'));
1730 input_stack::push(new macro_input(s));
1735 char *get_thru_arg()
1737 int c = input_stack::peek_char();
1739 input_stack::get_char();
1740 c = input_stack::peek_char();
1742 if (c != EOF && csalpha(c)) {
1743 // looks like a macro
1744 input_stack::get_char();
1747 c = input_stack::peek_char();
1748 if (c == EOF || (!csalnum(c) && c != '_'))
1750 input_stack::get_char();
1751 token_buffer += char(c);
1753 context_buffer = token_buffer;
1754 token_buffer += '\0';
1755 char *def = macro_table.lookup(token_buffer.contents());
1757 return strsave(def);
1758 // I guess it wasn't a macro after all; so push the macro name back.
1759 // -2 because we added a '\0'
1760 for (int i = token_buffer.length() - 2; i >= 0; i--)
1761 input_stack::push_back(token_buffer[i]);
1763 if (get_delimited()) {
1764 token_buffer += '\0';
1765 return strsave(token_buffer.contents());
1771 int lookahead_token = -1;
1772 string old_context_buffer;
1776 if (lookahead_token == -1) {
1777 old_context_buffer = context_buffer;
1778 lookahead_token = get_token(1);
1785 assert(lookahead_token == -1);
1786 if (delim_flag == 2) {
1787 if ((yylval.str = get_thru_arg()) != 0)
1793 if (get_delimited()) {
1794 token_buffer += '\0';
1795 yylval.str = strsave(token_buffer.contents());
1804 if (lookahead_token >= 0) {
1805 t = lookahead_token;
1806 lookahead_token = -1;
1822 yylval.n = token_int;
1825 yylval.x = token_double;
1829 token_buffer += '\0';
1830 if (!input_stack::get_location(&yylval.lstr.filename,
1831 &yylval.lstr.lineno)) {
1832 yylval.lstr.filename = 0;
1833 yylval.lstr.lineno = -1;
1835 yylval.lstr.str = strsave(token_buffer.contents());
1839 token_buffer += '\0';
1840 yylval.str = strsave(token_buffer.contents());
1843 // change LEFT to LEFT_CORNER when followed by OF
1844 old_context_buffer = context_buffer;
1845 lookahead_token = get_token(1);
1846 if (lookahead_token == OF)
1851 // change RIGHT to RIGHT_CORNER when followed by OF
1852 old_context_buffer = context_buffer;
1853 lookahead_token = get_token(1);
1854 if (lookahead_token == OF)
1855 return RIGHT_CORNER;
1859 // recognise UPPER only before LEFT or RIGHT
1860 old_context_buffer = context_buffer;
1861 lookahead_token = get_token(1);
1862 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1863 yylval.str = strsave("upper");
1869 // recognise LOWER only before LEFT or RIGHT
1870 old_context_buffer = context_buffer;
1871 lookahead_token = get_token(1);
1872 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1873 yylval.str = strsave("lower");
1879 // recognise NORTH only before OF
1880 old_context_buffer = context_buffer;
1881 lookahead_token = get_token(1);
1882 if (lookahead_token != OF) {
1883 yylval.str = strsave("north");
1889 // recognise SOUTH only before OF
1890 old_context_buffer = context_buffer;
1891 lookahead_token = get_token(1);
1892 if (lookahead_token != OF) {
1893 yylval.str = strsave("south");
1899 // recognise EAST only before OF
1900 old_context_buffer = context_buffer;
1901 lookahead_token = get_token(1);
1902 if (lookahead_token != OF) {
1903 yylval.str = strsave("east");
1909 // recognise WEST only before OF
1910 old_context_buffer = context_buffer;
1911 lookahead_token = get_token(1);
1912 if (lookahead_token != OF) {
1913 yylval.str = strsave("west");
1919 // recognise TOP only before OF
1920 old_context_buffer = context_buffer;
1921 lookahead_token = get_token(1);
1922 if (lookahead_token != OF) {
1923 yylval.str = strsave("top");
1929 // recognise BOTTOM only before OF
1930 old_context_buffer = context_buffer;
1931 lookahead_token = get_token(1);
1932 if (lookahead_token != OF) {
1933 yylval.str = strsave("bottom");
1939 // recognise CENTER only before OF
1940 old_context_buffer = context_buffer;
1941 lookahead_token = get_token(1);
1942 if (lookahead_token != OF) {
1943 yylval.str = strsave("center");
1949 // recognise START only before OF
1950 old_context_buffer = context_buffer;
1951 lookahead_token = get_token(1);
1952 if (lookahead_token != OF) {
1953 yylval.str = strsave("start");
1959 // recognise END only before OF
1960 old_context_buffer = context_buffer;
1961 lookahead_token = get_token(1);
1962 if (lookahead_token != OF) {
1963 yylval.str = strsave("end");
1974 void lex_error(const char *message,
1979 const char *filename;
1981 if (!input_stack::get_location(&filename, &lineno))
1982 error(message, arg1, arg2, arg3);
1984 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1987 void lex_warning(const char *message,
1992 const char *filename;
1994 if (!input_stack::get_location(&filename, &lineno))
1995 warning(message, arg1, arg2, arg3);
1997 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
2000 void yyerror(const char *s)
2002 const char *filename;
2004 const char *context = 0;
2005 if (lookahead_token == -1) {
2006 if (context_buffer.length() > 0) {
2007 context_buffer += '\0';
2008 context = context_buffer.contents();
2012 if (old_context_buffer.length() > 0) {
2013 old_context_buffer += '\0';
2014 context = old_context_buffer.contents();
2017 if (!input_stack::get_location(&filename, &lineno)) {
2019 if (context[0] == '\n' && context[1] == '\0')
2020 error("%1 before newline", s);
2022 error("%1 before `%2'", s, context);
2025 error("%1 at end of picture", s);
2029 if (context[0] == '\n' && context[1] == '\0')
2030 error_with_file_and_line(filename, lineno, "%1 before newline", s);
2032 error_with_file_and_line(filename, lineno, "%1 before `%2'",
2036 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);