2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 2, or (at your option) any later
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 along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 implement_ptable(char)
29 PTABLE(char) macro_table
;
31 class macro_input
: public input
{
35 macro_input(const char *);
41 class argument_macro_input
: public input
{
48 argument_macro_input(const char *, int, char **);
49 ~argument_macro_input();
54 input::input() : next(0)
62 int input::get_location(const char **, int *)
67 file_input::file_input(FILE *f
, const char *fn
)
68 : fp(f
), filename(fn
), lineno(0), ptr("")
72 file_input::~file_input()
77 int file_input::read_line()
86 else if (illegal_input_char(c
))
87 lex_error("illegal input character code %1", c
);
94 if (line
.length() == 0)
96 if (!(line
.length() >= 3 && line
[0] == '.' && line
[1] == 'P'
97 && (line
[2] == 'S' || line
[2] == 'E' || line
[2] == 'F')
98 && (line
.length() == 3 || line
[3] == ' ' || line
[3] == '\n'
99 || compatible_flag
))) {
101 ptr
= line
.contents();
107 int file_input::get()
109 if (*ptr
!= '\0' || read_line())
110 return (unsigned char)*ptr
++;
115 int file_input::peek()
117 if (*ptr
!= '\0' || read_line())
118 return (unsigned char)*ptr
;
123 int file_input::get_location(const char **fnp
, int *lnp
)
130 macro_input::macro_input(const char *str
)
132 p
= s
= strsave(str
);
135 macro_input::~macro_input()
140 int macro_input::get()
142 if (p
== 0 || *p
== '\0')
145 return (unsigned char)*p
++;
148 int macro_input::peek()
150 if (p
== 0 || *p
== '\0')
153 return (unsigned char)*p
;
156 // Character representing $1. Must be illegal input character.
159 char *process_body(const char *body
)
161 char *s
= strsave(body
);
163 for (int i
= 0; s
[i
] != '\0'; i
++)
164 if (s
[i
] == '$' && s
[i
+1] >= '0' && s
[i
+1] <= '9') {
166 s
[j
++] = ARG1
+ s
[++i
] - '1';
175 argument_macro_input::argument_macro_input(const char *body
, int ac
, char **av
)
178 for (int i
= 0; i
< argc
; i
++)
180 p
= s
= process_body(body
);
184 argument_macro_input::~argument_macro_input()
186 for (int i
= 0; i
< argc
; i
++)
191 int argument_macro_input::get()
195 return (unsigned char)*ap
++;
200 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
202 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
204 return (unsigned char)*ap
++;
209 return (unsigned char)*p
++;
212 int argument_macro_input::peek()
216 return (unsigned char)*ap
;
221 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
223 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
225 return (unsigned char)*ap
;
230 return (unsigned char)*p
;
234 static input
*current_input
;
237 static void push(input
*);
239 static int get_char();
240 static int peek_char();
241 static int get_location(const char **fnp
, int *lnp
);
242 static void push_back(unsigned char c
, int was_bol
= 0);
246 input
*input_stack::current_input
= 0;
247 int input_stack::bol_flag
= 0;
249 inline int input_stack::bol()
254 void input_stack::clear()
256 while (current_input
!= 0) {
257 input
*tem
= current_input
;
258 current_input
= current_input
->next
;
264 void input_stack::push(input
*in
)
266 in
->next
= current_input
;
270 void lex_init(input
*top
)
272 input_stack::clear();
273 input_stack::push(top
);
278 while (input_stack::get_char() != EOF
)
282 int input_stack::get_char()
284 while (current_input
!= 0) {
285 int c
= current_input
->get();
287 bol_flag
= c
== '\n';
290 // don't pop the top-level input off the stack
291 if (current_input
->next
== 0)
293 input
*tem
= current_input
;
294 current_input
= current_input
->next
;
300 int input_stack::peek_char()
302 while (current_input
!= 0) {
303 int c
= current_input
->peek();
306 if (current_input
->next
== 0)
308 input
*tem
= current_input
;
309 current_input
= current_input
->next
;
315 class char_input
: public input
{
323 char_input::char_input(int n
) : c((unsigned char)n
)
327 int char_input::get()
334 int char_input::peek()
339 void input_stack::push_back(unsigned char c
, int was_bol
)
341 push(new char_input(c
));
345 int input_stack::get_location(const char **fnp
, int *lnp
)
347 for (input
*p
= current_input
; p
; p
= p
->next
)
348 if (p
->get_location(fnp
, lnp
))
353 string context_buffer
;
359 void interpolate_macro_with_args(const char *body
)
364 for (i
= 0; i
< 9; i
++)
368 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
} state
= NORMAL
;
370 token_buffer
.clear();
372 c
= input_stack::get_char();
374 lex_error("end of input while scanning macro arguments");
377 if (state
== NORMAL
&& level
== 0 && (c
== ',' || c
== ')')) {
378 if (token_buffer
.length() > 0) {
379 token_buffer
+= '\0';
380 argv
[argc
] = strsave(token_buffer
.contents());
382 // for `foo()', argc = 0
383 if (argc
> 0 || c
!= ')' || i
> 0)
387 token_buffer
+= char(c
);
401 state
= IN_STRING_QUOTED
;
403 case IN_STRING_QUOTED
:
408 } while (c
!= ')' && c
!= EOF
);
409 input_stack::push(new argument_macro_input(body
, argc
, argv
));
412 static int docmp(const char *s1
, int n1
, const char *s2
, int n2
)
415 int r
= memcmp(s1
, s2
, n1
);
419 int r
= memcmp(s1
, s2
, n2
);
423 return memcmp(s1
, s2
, n1
);
426 int lookup_keyword(const char *str
, int len
)
428 static struct keyword
{
434 { "aligned", ALIGNED
},
441 { "between", BETWEEN
},
442 { "bottom", BOTTOM
},
446 { "center", CENTER
},
448 { "circle", CIRCLE
},
449 { "color", COLORED
},
450 { "colored", COLORED
},
451 { "colour", COLORED
},
452 { "coloured", COLORED
},
453 { "command", COMMAND
},
457 { "dashed", DASHED
},
458 { "define", DEFINE
},
459 { "diam", DIAMETER
},
460 { "diameter", DIAMETER
},
462 { "dotted", DOTTED
},
464 { "ellipse", ELLIPSE
},
472 { "height", HEIGHT
},
476 { "invis", INVISIBLE
},
477 { "invisible", INVISIBLE
},
488 { "outline", OUTLINED
},
489 { "outlined", OUTLINED
},
493 { "radius", RADIUS
},
500 { "shaded", SHADED
},
503 { "spline", SPLINE
},
504 { "sprintf", SPRINTF
},
510 { "thick", THICKNESS
},
511 { "thickness", THICKNESS
},
525 const keyword
*start
= table
;
526 const keyword
*end
= table
+ sizeof(table
)/sizeof(table
[0]);
527 while (start
< end
) {
528 // start <= target < end
529 const keyword
*mid
= start
+ (end
- start
)/2;
531 int cmp
= docmp(str
, len
, mid
->name
, strlen(mid
->name
));
542 int get_token_after_dot(int c
)
544 // get_token deals with the case where c is a digit
547 input_stack::get_char();
548 c
= input_stack::peek_char();
550 input_stack::get_char();
551 context_buffer
= ".ht";
555 input_stack::get_char();
556 c
= input_stack::peek_char();
558 input_stack::get_char();
559 c
= input_stack::peek_char();
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 context_buffer
= ".height";
571 input_stack::push_back('h');
573 input_stack::push_back('g');
575 input_stack::push_back('i');
577 input_stack::push_back('e');
579 input_stack::push_back('h');
582 input_stack::get_char();
583 context_buffer
= ".x";
586 input_stack::get_char();
587 context_buffer
= ".y";
590 input_stack::get_char();
591 c
= input_stack::peek_char();
593 input_stack::get_char();
594 c
= input_stack::peek_char();
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 context_buffer
= ".center";
609 input_stack::push_back('e');
611 input_stack::push_back('t');
613 input_stack::push_back('n');
615 input_stack::push_back('e');
617 context_buffer
= ".c";
620 input_stack::get_char();
621 c
= input_stack::peek_char();
623 input_stack::get_char();
624 context_buffer
= ".ne";
628 input_stack::get_char();
629 context_buffer
= ".nw";
633 context_buffer
= ".n";
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
= ".end";
648 input_stack::push_back('n');
649 context_buffer
= ".e";
652 context_buffer
= ".e";
655 input_stack::get_char();
656 c
= input_stack::peek_char();
658 input_stack::get_char();
659 c
= input_stack::peek_char();
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 context_buffer
= ".width";
671 input_stack::push_back('t');
673 context_buffer
= ".wid";
676 input_stack::push_back('i');
678 context_buffer
= ".w";
681 input_stack::get_char();
682 c
= input_stack::peek_char();
684 input_stack::get_char();
685 context_buffer
= ".se";
689 input_stack::get_char();
690 context_buffer
= ".sw";
695 input_stack::get_char();
696 c
= input_stack::peek_char();
698 input_stack::get_char();
699 c
= input_stack::peek_char();
701 input_stack::get_char();
702 c
= input_stack::peek_char();
704 input_stack::get_char();
705 context_buffer
= ".start";
708 input_stack::push_back('r');
710 input_stack::push_back('a');
712 input_stack::push_back('t');
714 context_buffer
= ".s";
719 input_stack::get_char();
720 c
= input_stack::peek_char();
722 input_stack::get_char();
723 c
= input_stack::peek_char();
725 input_stack::get_char();
726 context_buffer
= ".top";
729 input_stack::push_back('o');
731 context_buffer
= ".t";
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
= ".left";
747 input_stack::push_back('f');
749 input_stack::push_back('e');
751 context_buffer
= ".l";
754 input_stack::get_char();
755 c
= input_stack::peek_char();
757 input_stack::get_char();
758 c
= input_stack::peek_char();
760 input_stack::get_char();
761 context_buffer
= ".rad";
764 input_stack::push_back('a');
767 input_stack::get_char();
768 c
= input_stack::peek_char();
770 input_stack::get_char();
771 c
= input_stack::peek_char();
773 input_stack::get_char();
774 c
= input_stack::peek_char();
776 input_stack::get_char();
777 context_buffer
= ".right";
780 input_stack::push_back('h');
782 input_stack::push_back('g');
784 input_stack::push_back('i');
786 context_buffer
= ".r";
789 input_stack::get_char();
790 c
= input_stack::peek_char();
792 input_stack::get_char();
793 c
= input_stack::peek_char();
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 context_buffer
= ".bottom";
808 input_stack::push_back('o');
810 input_stack::push_back('t');
812 context_buffer
= ".bot";
815 input_stack::push_back('o');
817 context_buffer
= ".b";
820 context_buffer
= '.';
825 int get_token(int lookup_flag
)
827 context_buffer
.clear();
830 int bol
= input_stack::bol();
831 int c
= input_stack::get_char();
832 if (bol
&& c
== command_char
) {
833 token_buffer
.clear();
835 // the newline is not part of the token
837 c
= input_stack::peek_char();
838 if (c
== EOF
|| c
== '\n')
840 input_stack::get_char();
841 token_buffer
+= char(c
);
843 context_buffer
= token_buffer
;
854 int d
= input_stack::peek_char();
856 context_buffer
= '\\';
859 input_stack::get_char();
864 c
= input_stack::get_char();
865 } while (c
!= '\n' && c
!= EOF
);
867 context_buffer
= '\n';
870 context_buffer
= '"';
871 token_buffer
.clear();
873 c
= input_stack::get_char();
875 context_buffer
+= '\\';
876 c
= input_stack::peek_char();
878 input_stack::get_char();
880 context_buffer
+= '"';
883 token_buffer
+= '\\';
885 else if (c
== '\n') {
886 error("newline in string");
890 error("missing `\"'");
894 context_buffer
+= '"';
898 context_buffer
+= char(c
);
899 token_buffer
+= char(c
);
917 if (n
> (INT_MAX
- 9)/10) {
923 context_buffer
+= char(c
);
924 c
= input_stack::peek_char();
925 if (c
== EOF
|| !csdigit(c
))
927 c
= input_stack::get_char();
932 token_double
*= 10.0;
933 token_double
+= c
- '0';
934 context_buffer
+= char(c
);
935 c
= input_stack::peek_char();
936 if (c
== EOF
|| !csdigit(c
))
938 c
= input_stack::get_char();
940 // if somebody asks for 1000000000000th, we will silently
941 // give them INT_MAXth
942 double temp
= token_double
; // work around gas 1.34/sparc bug
943 if (token_double
> INT_MAX
)
952 context_buffer
+= char(c
);
953 input_stack::get_char();
957 context_buffer
+= '.';
958 input_stack::get_char();
962 c
= input_stack::peek_char();
963 if (c
== EOF
|| !csdigit(c
))
965 input_stack::get_char();
966 context_buffer
+= char(c
);
969 token_double
+= factor
*(c
- '0');
971 if (c
!= 'e' && c
!= 'E') {
972 if (c
== 'i' || c
== 'I') {
973 context_buffer
+= char(c
);
974 input_stack::get_char();
984 input_stack::get_char();
985 c
= input_stack::peek_char();
987 if (c
== '+' || c
== '-') {
989 input_stack::get_char();
990 c
= input_stack::peek_char();
991 if (c
== EOF
|| !csdigit(c
)) {
992 input_stack::push_back(sign
);
993 input_stack::push_back(echar
);
996 context_buffer
+= char(echar
);
997 context_buffer
+= char(sign
);
1000 if (c
== EOF
|| !csdigit(c
)) {
1001 input_stack::push_back(echar
);
1004 context_buffer
+= char(echar
);
1006 input_stack::get_char();
1007 context_buffer
+= char(c
);
1010 c
= input_stack::peek_char();
1011 if (c
== EOF
|| !csdigit(c
))
1013 input_stack::get_char();
1014 context_buffer
+= char(c
);
1015 n
= n
*10 + (c
- '0');
1019 if (c
== 'i' || c
== 'I') {
1020 context_buffer
+= char(c
);
1021 input_stack::get_char();
1023 token_double
*= pow(10.0, n
);
1027 input_stack::get_char();
1028 c
= input_stack::peek_char();
1030 input_stack::get_char();
1032 context_buffer
+= "nd";
1035 input_stack::push_back('n');
1038 input_stack::get_char();
1039 c
= input_stack::peek_char();
1041 input_stack::get_char();
1043 context_buffer
+= "rd";
1046 input_stack::push_back('r');
1049 input_stack::get_char();
1050 c
= input_stack::peek_char();
1052 input_stack::get_char();
1054 context_buffer
+= "th";
1057 input_stack::push_back('t');
1060 input_stack::get_char();
1061 c
= input_stack::peek_char();
1063 input_stack::get_char();
1065 context_buffer
+= "st";
1068 input_stack::push_back('s');
1076 c
= input_stack::peek_char();
1078 input_stack::get_char();
1079 c
= input_stack::peek_char();
1081 input_stack::get_char();
1082 context_buffer
= "'th";
1086 input_stack::push_back('t');
1088 context_buffer
= "'";
1093 c
= input_stack::peek_char();
1094 if (c
!= EOF
&& csdigit(c
)) {
1097 context_buffer
= '.';
1100 return get_token_after_dot(c
);
1103 c
= input_stack::peek_char();
1105 input_stack::get_char();
1106 c
= input_stack::peek_char();
1108 input_stack::get_char();
1109 context_buffer
= "<->";
1110 return DOUBLE_ARROW_HEAD
;
1112 context_buffer
= "<-";
1113 return LEFT_ARROW_HEAD
;
1115 else if (c
== '=') {
1116 input_stack::get_char();
1117 context_buffer
= "<=";
1120 context_buffer
= "<";
1123 c
= input_stack::peek_char();
1125 input_stack::get_char();
1126 context_buffer
= "->";
1127 return RIGHT_ARROW_HEAD
;
1129 context_buffer
= "-";
1132 c
= input_stack::peek_char();
1134 input_stack::get_char();
1135 context_buffer
= "!=";
1138 context_buffer
= "!";
1141 c
= input_stack::peek_char();
1143 input_stack::get_char();
1144 context_buffer
= ">=";
1145 return GREATEREQUAL
;
1147 context_buffer
= ">";
1150 c
= input_stack::peek_char();
1152 input_stack::get_char();
1153 context_buffer
= "==";
1156 context_buffer
= "=";
1159 c
= input_stack::peek_char();
1161 input_stack::get_char();
1162 context_buffer
= "&&";
1165 context_buffer
= "&";
1168 c
= input_stack::peek_char();
1170 input_stack::get_char();
1171 context_buffer
= "||";
1174 context_buffer
= "|";
1177 if (c
!= EOF
&& csalpha(c
)) {
1178 token_buffer
.clear();
1181 c
= input_stack::peek_char();
1182 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1184 input_stack::get_char();
1185 token_buffer
+= char(c
);
1187 int tok
= lookup_keyword(token_buffer
.contents(),
1188 token_buffer
.length());
1190 context_buffer
= token_buffer
;
1195 token_buffer
+= '\0';
1196 def
= macro_table
.lookup(token_buffer
.contents());
1197 token_buffer
.set_length(token_buffer
.length() - 1);
1200 input_stack::get_char();
1201 interpolate_macro_with_args(def
);
1204 input_stack::push(new macro_input(def
));
1208 context_buffer
= token_buffer
;
1209 if (csupper(token_buffer
[0]))
1216 context_buffer
= char(c
);
1217 return (unsigned char)c
;
1226 token_buffer
.clear();
1227 int c
= input_stack::get_char();
1228 while (c
== ' ' || c
== '\t' || c
== '\n')
1229 c
= input_stack::get_char();
1231 lex_error("missing delimiter");
1234 context_buffer
= char(c
);
1235 int had_newline
= 0;
1238 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
, DELIM_END
} state
= NORMAL
;
1240 c
= input_stack::get_char();
1242 lex_error("missing closing delimiter");
1247 else if (!had_newline
)
1248 context_buffer
+= char(c
);
1271 case IN_STRING_QUOTED
:
1278 if (c
== '"' || c
== '\n')
1281 state
= IN_STRING_QUOTED
;
1284 // This case it just to shut cfront 2.0 up.
1288 if (state
== DELIM_END
)
1297 int t
= get_token(0); // do not expand what we are defining
1298 if (t
!= VARIABLE
&& t
!= LABEL
) {
1299 lex_error("can only define variable or placename");
1302 token_buffer
+= '\0';
1303 string nm
= token_buffer
;
1304 const char *name
= nm
.contents();
1305 if (!get_delimited())
1307 token_buffer
+= '\0';
1308 macro_table
.define(name
, strsave(token_buffer
.contents()));
1313 int t
= get_token(0); // do not expand what we are undefining
1314 if (t
!= VARIABLE
&& t
!= LABEL
) {
1315 lex_error("can only define variable or placename");
1318 token_buffer
+= '\0';
1319 macro_table
.define(token_buffer
.contents(), 0);
1323 class for_input
: public input
{
1327 int by_is_multiplicative
;
1332 for_input(char *, double, int, double, char *);
1338 for_input::for_input(char *vr
, double t
, int bim
, double b
, char *bd
)
1339 : var(vr
), body(bd
), to(t
), by_is_multiplicative(bim
), by(b
), p(body
),
1344 for_input::~for_input()
1350 int for_input::get()
1356 return (unsigned char)*p
++;
1357 if (!done_newline
) {
1362 if (!lookup_variable(var
, &val
)) {
1363 lex_error("body of `for' terminated enclosing block");
1366 if (by_is_multiplicative
)
1370 define_variable(var
, val
);
1380 int for_input::peek()
1385 return (unsigned char)*p
;
1389 if (!lookup_variable(var
, &val
))
1391 if (by_is_multiplicative
) {
1401 return (unsigned char)*body
;
1404 void do_for(char *var
, double from
, double to
, int by_is_multiplicative
,
1405 double by
, char *body
)
1407 define_variable(var
, from
);
1409 input_stack::push(new for_input(var
, to
, by_is_multiplicative
, by
, body
));
1413 void do_copy(const char *filename
)
1416 FILE *fp
= fopen(filename
, "r");
1418 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1421 input_stack::push(new file_input(fp
, filename
));
1424 class copy_thru_input
: public input
{
1434 virtual int inget() = 0;
1436 copy_thru_input(const char *b
, const char *u
);
1442 class copy_file_thru_input
: public copy_thru_input
{
1445 copy_file_thru_input(input
*, const char *b
, const char *u
);
1446 ~copy_file_thru_input();
1450 copy_file_thru_input::copy_file_thru_input(input
*i
, const char *b
,
1452 : copy_thru_input(b
, u
), in(i
)
1456 copy_file_thru_input::~copy_file_thru_input()
1461 int copy_file_thru_input::inget()
1469 class copy_rest_thru_input
: public copy_thru_input
{
1471 copy_rest_thru_input(const char *, const char *u
);
1475 copy_rest_thru_input::copy_rest_thru_input(const char *b
, const char *u
)
1476 : copy_thru_input(b
, u
)
1480 int copy_rest_thru_input::inget()
1483 int c
= next
->get();
1486 if (next
->next
== 0)
1496 copy_thru_input::copy_thru_input(const char *b
, const char *u
)
1500 body
= process_body(b
);
1506 copy_thru_input::~copy_thru_input()
1512 int copy_thru_input::get()
1516 return (unsigned char)*ap
++;
1529 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
1530 int i
= *p
++ - ARG1
;
1531 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1532 ap
= line
.contents() + argv
[i
];
1533 return (unsigned char)*ap
++;
1537 return (unsigned char)*p
++;
1542 int copy_thru_input::peek()
1546 return (unsigned char)*ap
;
1557 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
1558 int i
= *p
++ - ARG1
;
1559 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1560 ap
= line
.contents() + argv
[i
];
1561 return (unsigned char)*ap
;
1565 return (unsigned char)*p
;
1570 int copy_thru_input::get_line()
1580 if (c
== EOF
|| c
== '\n')
1585 } while (c
!= '\n' && c
!= EOF
);
1588 argv
[argc
++] = line
.length();
1592 } while (c
!= ' ' && c
!= '\n');
1595 if (until
!= 0 && argc
> 0 && strcmp(&line
[argv
[0]], until
) == 0) {
1599 return argc
> 0 || c
== '\n';
1602 class simple_file_input
: public input
{
1603 const char *filename
;
1607 simple_file_input(FILE *, const char *);
1608 ~simple_file_input();
1611 int get_location(const char **, int *);
1614 simple_file_input::simple_file_input(FILE *p
, const char *s
)
1615 : filename(s
), lineno(1), fp(p
)
1619 simple_file_input::~simple_file_input()
1621 // don't delete the filename
1625 int simple_file_input::get()
1628 while (illegal_input_char(c
)) {
1629 error("illegal input character code %1", c
);
1637 int simple_file_input::peek()
1640 while (illegal_input_char(c
)) {
1641 error("illegal input character code %1", c
);
1649 int simple_file_input::get_location(const char **fnp
, int *lnp
)
1657 void copy_file_thru(const char *filename
, const char *body
, const char *until
)
1660 FILE *fp
= fopen(filename
, "r");
1662 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1665 input
*in
= new copy_file_thru_input(new simple_file_input(fp
, filename
),
1667 input_stack::push(in
);
1670 void copy_rest_thru(const char *body
, const char *until
)
1672 input_stack::push(new copy_rest_thru_input(body
, until
));
1675 void push_body(const char *s
)
1677 input_stack::push(new char_input('\n'));
1678 input_stack::push(new macro_input(s
));
1683 char *get_thru_arg()
1685 int c
= input_stack::peek_char();
1687 input_stack::get_char();
1688 c
= input_stack::peek_char();
1690 if (c
!= EOF
&& csalpha(c
)) {
1691 // looks like a macro
1692 input_stack::get_char();
1695 c
= input_stack::peek_char();
1696 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1698 input_stack::get_char();
1699 token_buffer
+= char(c
);
1701 context_buffer
= token_buffer
;
1702 token_buffer
+= '\0';
1703 char *def
= macro_table
.lookup(token_buffer
.contents());
1705 return strsave(def
);
1706 // I guess it wasn't a macro after all; so push the macro name back.
1707 // -2 because we added a '\0'
1708 for (int i
= token_buffer
.length() - 2; i
>= 0; i
--)
1709 input_stack::push_back(token_buffer
[i
]);
1711 if (get_delimited()) {
1712 token_buffer
+= '\0';
1713 return strsave(token_buffer
.contents());
1719 int lookahead_token
= -1;
1720 string old_context_buffer
;
1724 if (lookahead_token
== -1) {
1725 old_context_buffer
= context_buffer
;
1726 lookahead_token
= get_token(1);
1733 assert(lookahead_token
== -1);
1734 if (delim_flag
== 2) {
1735 if ((yylval
.str
= get_thru_arg()) != 0)
1741 if (get_delimited()) {
1742 token_buffer
+= '\0';
1743 yylval
.str
= strsave(token_buffer
.contents());
1752 if (lookahead_token
>= 0) {
1753 t
= lookahead_token
;
1754 lookahead_token
= -1;
1770 yylval
.n
= token_int
;
1773 yylval
.x
= token_double
;
1777 token_buffer
+= '\0';
1778 if (!input_stack::get_location(&yylval
.lstr
.filename
,
1779 &yylval
.lstr
.lineno
)) {
1780 yylval
.lstr
.filename
= 0;
1781 yylval
.lstr
.lineno
= -1;
1783 yylval
.lstr
.str
= strsave(token_buffer
.contents());
1787 token_buffer
+= '\0';
1788 yylval
.str
= strsave(token_buffer
.contents());
1791 // change LEFT to LEFT_CORNER when followed by OF
1792 old_context_buffer
= context_buffer
;
1793 lookahead_token
= get_token(1);
1794 if (lookahead_token
== OF
)
1799 // change RIGHT to RIGHT_CORNER when followed by OF
1800 old_context_buffer
= context_buffer
;
1801 lookahead_token
= get_token(1);
1802 if (lookahead_token
== OF
)
1803 return RIGHT_CORNER
;
1807 // recognise UPPER only before LEFT or RIGHT
1808 old_context_buffer
= context_buffer
;
1809 lookahead_token
= get_token(1);
1810 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1811 yylval
.str
= strsave("upper");
1817 // recognise LOWER only before LEFT or RIGHT
1818 old_context_buffer
= context_buffer
;
1819 lookahead_token
= get_token(1);
1820 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1821 yylval
.str
= strsave("lower");
1827 // recognise TOP only before OF
1828 old_context_buffer
= context_buffer
;
1829 lookahead_token
= get_token(1);
1830 if (lookahead_token
!= OF
) {
1831 yylval
.str
= strsave("top");
1837 // recognise BOTTOM only before OF
1838 old_context_buffer
= context_buffer
;
1839 lookahead_token
= get_token(1);
1840 if (lookahead_token
!= OF
) {
1841 yylval
.str
= strsave("bottom");
1847 // recognise CENTER only before OF
1848 old_context_buffer
= context_buffer
;
1849 lookahead_token
= get_token(1);
1850 if (lookahead_token
!= OF
) {
1851 yylval
.str
= strsave("center");
1857 // recognise START only before OF
1858 old_context_buffer
= context_buffer
;
1859 lookahead_token
= get_token(1);
1860 if (lookahead_token
!= OF
) {
1861 yylval
.str
= strsave("start");
1867 // recognise END only before OF
1868 old_context_buffer
= context_buffer
;
1869 lookahead_token
= get_token(1);
1870 if (lookahead_token
!= OF
) {
1871 yylval
.str
= strsave("end");
1882 void lex_error(const char *message
,
1887 const char *filename
;
1889 if (!input_stack::get_location(&filename
, &lineno
))
1890 error(message
, arg1
, arg2
, arg3
);
1892 error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1895 void lex_warning(const char *message
,
1900 const char *filename
;
1902 if (!input_stack::get_location(&filename
, &lineno
))
1903 warning(message
, arg1
, arg2
, arg3
);
1905 warning_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1908 void yyerror(const char *s
)
1910 const char *filename
;
1912 const char *context
= 0;
1913 if (lookahead_token
== -1) {
1914 if (context_buffer
.length() > 0) {
1915 context_buffer
+= '\0';
1916 context
= context_buffer
.contents();
1920 if (old_context_buffer
.length() > 0) {
1921 old_context_buffer
+= '\0';
1922 context
= old_context_buffer
.contents();
1925 if (!input_stack::get_location(&filename
, &lineno
)) {
1927 if (context
[0] == '\n' && context
[1] == '\0')
1928 error("%1 before newline", s
);
1930 error("%1 before `%2'", s
, context
);
1933 error("%1 at end of picture", s
);
1937 if (context
[0] == '\n' && context
[1] == '\0')
1938 error_with_file_and_line(filename
, lineno
, "%1 before newline", s
);
1940 error_with_file_and_line(filename
, lineno
, "%1 before `%2'",
1944 error_with_file_and_line(filename
, lineno
, "%1 at end of picture", s
);