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
);