2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000, 2002 - 2004, 2006, 2007
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 * This 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.
24 #include "pic-config.h"
32 implement_ptable(char)
34 PTABLE(char) macro_table
;
43 macro_input(const char *);
49 class argument_macro_input
59 argument_macro_input(const char *, int, char **);
60 ~argument_macro_input();
74 int input::get_location(const char **, int *)
79 file_input::file_input(file_case
*fcp
, const char *fn
)
80 : _fcp(fcp
), filename(fn
), lineno(0), ptr("")
84 file_input::~file_input()
89 int file_input::read_line() /* TODO lib-roff */
95 int c
= _fcp
->get_c();
96 if (c
== '\r' && (c
= _fcp
->get_c()) != '\n')
97 lex_error("invalid input character CR (carriage return)");
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
;
261 static input
*current_input
;
265 static void push(input
*);
267 static int get_char();
268 static int peek_char();
269 static int get_location(const char **fnp
, int *lnp
);
270 static void push_back(unsigned char c
, int was_bol
= 0);
274 input
*input_stack::current_input
= 0;
275 int input_stack::bol_flag
= 0;
277 inline int input_stack::bol()
282 void input_stack::clear()
284 while (current_input
!= 0) {
285 input
*tem
= current_input
;
286 current_input
= current_input
->next
;
292 void input_stack::push(input
*in
)
294 in
->next
= current_input
;
298 void lex_init(input
*top
)
300 input_stack::clear();
301 input_stack::push(top
);
306 while (input_stack::get_char() != EOF
)
310 int input_stack::get_char()
312 while (current_input
!= 0) {
313 int c
= current_input
->get();
315 bol_flag
= c
== '\n';
318 // don't pop the top-level input off the stack
319 if (current_input
->next
== 0)
321 input
*tem
= current_input
;
322 current_input
= current_input
->next
;
328 int input_stack::peek_char()
330 while (current_input
!= 0) {
331 int c
= current_input
->peek();
334 if (current_input
->next
== 0)
336 input
*tem
= current_input
;
337 current_input
= current_input
->next
;
354 char_input::char_input(int n
)
355 : c((unsigned char)n
)
359 int char_input::get()
366 int char_input::peek()
371 void input_stack::push_back(unsigned char c
, int was_bol
)
373 push(new char_input(c
));
377 int input_stack::get_location(const char **fnp
, int *lnp
)
379 for (input
*p
= current_input
; p
; p
= p
->next
)
380 if (p
->get_location(fnp
, lnp
))
385 string context_buffer
;
391 void interpolate_macro_with_args(const char *body
)
397 for (i
= 0; i
< MAX_ARG
; i
++)
401 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
} state
= NORMAL
;
403 token_buffer
.clear();
405 c
= input_stack::get_char();
407 lex_error("end of input while scanning macro arguments");
410 if (state
== NORMAL
&& level
== 0 && (c
== ',' || c
== ')')) {
411 if (token_buffer
.length() > 0) {
412 token_buffer
+= '\0';
414 if (argc
== MAX_ARG
) {
415 lex_warning("only %1 macro arguments supported", MAX_ARG
);
419 argv
[argc
] = strsave(token_buffer
.contents());
422 // for `foo()', argc = 0
423 if (argc
> 0 || c
!= ')' || i
> 0)
428 token_buffer
+= char(c
);
442 state
= IN_STRING_QUOTED
;
444 case IN_STRING_QUOTED
:
449 } while (c
!= ')' && c
!= EOF
);
450 input_stack::push(new argument_macro_input(body
, argc
, argv
));
453 static int docmp(const char *s1
, int n1
, const char *s2
, int n2
)
456 int r
= memcmp(s1
, s2
, n1
);
460 int r
= memcmp(s1
, s2
, n2
);
464 return memcmp(s1
, s2
, n1
);
467 int lookup_keyword(const char *str
, int len
)
469 static struct keyword
{
472 } /* FIXME const */table
[] = {
475 { "aligned", ALIGNED
},
482 { "between", BETWEEN
},
483 { "bottom", BOTTOM
},
487 { "center", CENTER
},
489 { "circle", CIRCLE
},
490 { "color", COLORED
},
491 { "colored", COLORED
},
492 { "colour", COLORED
},
493 { "coloured", COLORED
},
494 { "command", COMMAND
},
498 { "dashed", DASHED
},
499 { "define", DEFINE
},
500 { "diam", DIAMETER
},
501 { "diameter", DIAMETER
},
503 { "dotted", DOTTED
},
506 { "ellipse", ELLIPSE
},
510 { "figname", FIGNAME
},
515 { "height", HEIGHT
},
519 { "invis", INVISIBLE
},
520 { "invisible", INVISIBLE
},
532 { "outline", OUTLINED
},
533 { "outlined", OUTLINED
},
537 { "radius", RADIUS
},
544 { "shaded", SHADED
},
548 { "spline", SPLINE
},
549 { "sprintf", SPRINTF
},
555 { "thick", THICKNESS
},
556 { "thickness", THICKNESS
},
569 { "xslanted", XSLANTED
},
570 { "yslanted", YSLANTED
},
573 const keyword
*start
= table
;
574 const keyword
*end
= table
+ NELEM(table
);
575 while (start
< end
) {
576 // start <= target < end
577 const keyword
*mid
= start
+ (end
- start
)/2;
579 int cmp
= docmp(str
, len
, mid
->name
, strlen(mid
->name
));
590 int get_token_after_dot(int c
)
592 // get_token deals with the case where c is a digit
595 input_stack::get_char();
596 c
= input_stack::peek_char();
598 input_stack::get_char();
599 context_buffer
= ".ht";
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 c
= input_stack::peek_char();
612 input_stack::get_char();
613 c
= input_stack::peek_char();
615 input_stack::get_char();
616 context_buffer
= ".height";
619 input_stack::push_back('h');
621 input_stack::push_back('g');
623 input_stack::push_back('i');
625 input_stack::push_back('e');
627 input_stack::push_back('h');
630 input_stack::get_char();
631 context_buffer
= ".x";
634 input_stack::get_char();
635 context_buffer
= ".y";
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 c
= input_stack::peek_char();
650 input_stack::get_char();
651 c
= input_stack::peek_char();
653 input_stack::get_char();
654 context_buffer
= ".center";
657 input_stack::push_back('e');
659 input_stack::push_back('t');
661 input_stack::push_back('n');
663 input_stack::push_back('e');
665 context_buffer
= ".c";
668 input_stack::get_char();
669 c
= input_stack::peek_char();
671 input_stack::get_char();
672 context_buffer
= ".ne";
676 input_stack::get_char();
677 context_buffer
= ".nw";
681 context_buffer
= ".n";
686 input_stack::get_char();
687 c
= input_stack::peek_char();
689 input_stack::get_char();
690 c
= input_stack::peek_char();
692 input_stack::get_char();
693 context_buffer
= ".end";
696 input_stack::push_back('n');
697 context_buffer
= ".e";
700 context_buffer
= ".e";
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 c
= input_stack::peek_char();
712 input_stack::get_char();
713 c
= input_stack::peek_char();
715 input_stack::get_char();
716 context_buffer
= ".width";
719 input_stack::push_back('t');
721 context_buffer
= ".wid";
724 input_stack::push_back('i');
726 context_buffer
= ".w";
729 input_stack::get_char();
730 c
= input_stack::peek_char();
732 input_stack::get_char();
733 context_buffer
= ".se";
737 input_stack::get_char();
738 context_buffer
= ".sw";
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 c
= input_stack::peek_char();
752 input_stack::get_char();
753 context_buffer
= ".start";
756 input_stack::push_back('r');
758 input_stack::push_back('a');
760 input_stack::push_back('t');
762 context_buffer
= ".s";
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 context_buffer
= ".top";
777 input_stack::push_back('o');
779 context_buffer
= ".t";
782 input_stack::get_char();
783 c
= input_stack::peek_char();
785 input_stack::get_char();
786 c
= input_stack::peek_char();
788 input_stack::get_char();
789 c
= input_stack::peek_char();
791 input_stack::get_char();
792 context_buffer
= ".left";
795 input_stack::push_back('f');
797 input_stack::push_back('e');
799 context_buffer
= ".l";
802 input_stack::get_char();
803 c
= input_stack::peek_char();
805 input_stack::get_char();
806 c
= input_stack::peek_char();
808 input_stack::get_char();
809 context_buffer
= ".rad";
812 input_stack::push_back('a');
815 input_stack::get_char();
816 c
= input_stack::peek_char();
818 input_stack::get_char();
819 c
= input_stack::peek_char();
821 input_stack::get_char();
822 c
= input_stack::peek_char();
824 input_stack::get_char();
825 context_buffer
= ".right";
828 input_stack::push_back('h');
830 input_stack::push_back('g');
832 input_stack::push_back('i');
834 context_buffer
= ".r";
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 c
= input_stack::peek_char();
849 input_stack::get_char();
850 c
= input_stack::peek_char();
852 input_stack::get_char();
853 context_buffer
= ".bottom";
856 input_stack::push_back('o');
858 input_stack::push_back('t');
860 context_buffer
= ".bot";
863 input_stack::push_back('o');
865 context_buffer
= ".b";
868 context_buffer
= '.';
873 int get_token(int lookup_flag
)
875 context_buffer
.clear();
878 int bol
= input_stack::bol();
879 int c
= input_stack::get_char();
880 if (bol
&& c
== command_char
) {
881 token_buffer
.clear();
883 // the newline is not part of the token
885 c
= input_stack::peek_char();
886 if (c
== EOF
|| c
== '\n')
888 input_stack::get_char();
889 token_buffer
+= char(c
);
891 context_buffer
= token_buffer
;
902 int d
= input_stack::peek_char();
904 context_buffer
= '\\';
907 input_stack::get_char();
912 c
= input_stack::get_char();
913 } while (c
!= '\n' && c
!= EOF
);
915 context_buffer
= '\n';
918 context_buffer
= '"';
919 token_buffer
.clear();
921 c
= input_stack::get_char();
923 context_buffer
+= '\\';
924 c
= input_stack::peek_char();
926 input_stack::get_char();
928 context_buffer
+= '"';
931 token_buffer
+= '\\';
933 else if (c
== '\n') {
934 error("newline in string");
938 error("missing `\"'");
942 context_buffer
+= '"';
946 context_buffer
+= char(c
);
947 token_buffer
+= char(c
);
965 if (n
> (INT_MAX
- 9)/10) {
971 context_buffer
+= char(c
);
972 c
= input_stack::peek_char();
973 if (c
== EOF
|| !csdigit(c
))
975 c
= input_stack::get_char();
980 token_double
*= 10.0;
981 token_double
+= c
- '0';
982 context_buffer
+= char(c
);
983 c
= input_stack::peek_char();
984 if (c
== EOF
|| !csdigit(c
))
986 c
= input_stack::get_char();
988 // if somebody asks for 1000000000000th, we will silently
989 // give them INT_MAXth
990 double temp
= token_double
; // work around gas 1.34/sparc bug
991 if (token_double
> INT_MAX
)
1000 context_buffer
+= char(c
);
1001 input_stack::get_char();
1005 context_buffer
+= '.';
1006 input_stack::get_char();
1008 double factor
= 1.0;
1010 c
= input_stack::peek_char();
1011 if (c
== EOF
|| !csdigit(c
))
1013 input_stack::get_char();
1014 context_buffer
+= char(c
);
1017 token_double
+= factor
*(c
- '0');
1019 if (c
!= 'e' && c
!= 'E') {
1020 if (c
== 'i' || c
== 'I') {
1021 context_buffer
+= char(c
);
1022 input_stack::get_char();
1032 input_stack::get_char();
1033 c
= input_stack::peek_char();
1035 if (c
== '+' || c
== '-') {
1037 input_stack::get_char();
1038 c
= input_stack::peek_char();
1039 if (c
== EOF
|| !csdigit(c
)) {
1040 input_stack::push_back(sign
);
1041 input_stack::push_back(echar
);
1044 context_buffer
+= char(echar
);
1045 context_buffer
+= char(sign
);
1048 if (c
== EOF
|| !csdigit(c
)) {
1049 input_stack::push_back(echar
);
1052 context_buffer
+= char(echar
);
1054 input_stack::get_char();
1055 context_buffer
+= char(c
);
1058 c
= input_stack::peek_char();
1059 if (c
== EOF
|| !csdigit(c
))
1061 input_stack::get_char();
1062 context_buffer
+= char(c
);
1063 n
= n
*10 + (c
- '0');
1067 if (c
== 'i' || c
== 'I') {
1068 context_buffer
+= char(c
);
1069 input_stack::get_char();
1071 token_double
*= pow(10.0, n
);
1075 input_stack::get_char();
1076 c
= input_stack::peek_char();
1078 input_stack::get_char();
1080 context_buffer
+= "nd";
1083 input_stack::push_back('n');
1086 input_stack::get_char();
1087 c
= input_stack::peek_char();
1089 input_stack::get_char();
1091 context_buffer
+= "rd";
1094 input_stack::push_back('r');
1097 input_stack::get_char();
1098 c
= input_stack::peek_char();
1100 input_stack::get_char();
1102 context_buffer
+= "th";
1105 input_stack::push_back('t');
1108 input_stack::get_char();
1109 c
= input_stack::peek_char();
1111 input_stack::get_char();
1113 context_buffer
+= "st";
1116 input_stack::push_back('s');
1124 c
= input_stack::peek_char();
1126 input_stack::get_char();
1127 c
= input_stack::peek_char();
1129 input_stack::get_char();
1130 context_buffer
= "'th";
1134 input_stack::push_back('t');
1136 context_buffer
= "'";
1141 c
= input_stack::peek_char();
1142 if (c
!= EOF
&& csdigit(c
)) {
1145 context_buffer
= '.';
1148 return get_token_after_dot(c
);
1151 c
= input_stack::peek_char();
1153 input_stack::get_char();
1154 c
= input_stack::peek_char();
1156 input_stack::get_char();
1157 context_buffer
= "<->";
1158 return DOUBLE_ARROW_HEAD
;
1160 context_buffer
= "<-";
1161 return LEFT_ARROW_HEAD
;
1163 else if (c
== '=') {
1164 input_stack::get_char();
1165 context_buffer
= "<=";
1168 context_buffer
= "<";
1171 c
= input_stack::peek_char();
1173 input_stack::get_char();
1174 context_buffer
= "->";
1175 return RIGHT_ARROW_HEAD
;
1177 context_buffer
= "-";
1180 c
= input_stack::peek_char();
1182 input_stack::get_char();
1183 context_buffer
= "!=";
1186 context_buffer
= "!";
1189 c
= input_stack::peek_char();
1191 input_stack::get_char();
1192 context_buffer
= ">=";
1193 return GREATEREQUAL
;
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 c
= input_stack::peek_char();
1218 input_stack::get_char();
1219 context_buffer
= "||";
1222 context_buffer
= "|";
1225 if (c
!= EOF
&& csalpha(c
)) {
1226 token_buffer
.clear();
1229 c
= input_stack::peek_char();
1230 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1232 input_stack::get_char();
1233 token_buffer
+= char(c
);
1235 int tok
= lookup_keyword(token_buffer
.contents(),
1236 token_buffer
.length());
1238 context_buffer
= token_buffer
;
1243 token_buffer
+= '\0';
1244 def
= macro_table
.lookup(token_buffer
.contents());
1245 token_buffer
.set_length(token_buffer
.length() - 1);
1248 input_stack::get_char();
1249 interpolate_macro_with_args(def
);
1252 input_stack::push(new macro_input(def
));
1256 context_buffer
= token_buffer
;
1257 if (csupper(token_buffer
[0]))
1264 context_buffer
= char(c
);
1265 return (unsigned char)c
;
1274 token_buffer
.clear();
1275 int c
= input_stack::get_char();
1276 while (c
== ' ' || c
== '\t' || c
== '\n')
1277 c
= input_stack::get_char();
1279 lex_error("missing delimiter");
1282 context_buffer
= char(c
);
1283 int had_newline
= 0;
1286 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
, DELIM_END
} state
= NORMAL
;
1288 c
= input_stack::get_char();
1290 lex_error("missing closing delimiter");
1295 else if (!had_newline
)
1296 context_buffer
+= char(c
);
1319 case IN_STRING_QUOTED
:
1326 if (c
== '"' || c
== '\n')
1329 state
= IN_STRING_QUOTED
;
1332 // This case it just to shut cfront 2.0 up.
1336 if (state
== DELIM_END
)
1345 int t
= get_token(0); // do not expand what we are defining
1346 if (t
!= VARIABLE
&& t
!= LABEL
) {
1347 lex_error("can only define variable or placename");
1350 token_buffer
+= '\0';
1351 string nm
= token_buffer
;
1352 const char *name
= nm
.contents();
1353 if (!get_delimited())
1355 token_buffer
+= '\0';
1356 macro_table
.define(name
, strsave(token_buffer
.contents()));
1361 int t
= get_token(0); // do not expand what we are undefining
1362 if (t
!= VARIABLE
&& t
!= LABEL
) {
1363 lex_error("can only define variable or placename");
1366 token_buffer
+= '\0';
1367 macro_table
.define(token_buffer
.contents(), 0);
1377 int by_is_multiplicative
;
1383 for_input(char *, double, double, int, double, char *);
1389 for_input::for_input(char *vr
, double f
, double t
,
1390 int bim
, double b
, char *bd
)
1391 : var(vr
), body(bd
), from(f
), to(t
), by_is_multiplicative(bim
), by(b
),
1392 p(body
), done_newline(0)
1396 for_input::~for_input()
1402 int for_input::get()
1408 return (unsigned char)*p
++;
1409 if (!done_newline
) {
1414 if (!lookup_variable(var
, &val
)) {
1415 lex_error("body of `for' terminated enclosing block");
1418 if (by_is_multiplicative
)
1422 define_variable(var
, val
);
1423 if ((from
<= to
&& val
> to
)
1424 || (from
>= to
&& val
< to
)) {
1433 int for_input::peek()
1438 return (unsigned char)*p
;
1442 if (!lookup_variable(var
, &val
))
1444 if (by_is_multiplicative
) {
1449 if ((from
<= to
&& val
+ by
> to
)
1450 || (from
>= to
&& val
+ by
< to
))
1455 return (unsigned char)*body
;
1458 void do_for(char *var
, double from
, double to
, int by_is_multiplicative
,
1459 double by
, char *body
)
1461 define_variable(var
, from
);
1462 if ((by_is_multiplicative
&& by
<= 0)
1463 || (by
> 0 && from
> to
)
1464 || (by
< 0 && from
< to
))
1466 input_stack::push(new for_input(var
, from
, to
,
1467 by_is_multiplicative
, by
, body
));
1470 void do_copy(const char *filename
)
1472 file_case
*fcp
= file_case::muxer(filename
);
1474 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1477 input_stack::push(new file_input(fcp
, filename
));
1480 class copy_thru_input
1493 virtual int inget() = 0;
1496 copy_thru_input(const char *b
, const char *u
);
1502 class copy_file_thru_input
1503 : public copy_thru_input
1508 copy_file_thru_input(input
*, const char *b
, const char *u
);
1509 ~copy_file_thru_input();
1513 copy_file_thru_input::copy_file_thru_input(input
*i
, const char *b
,
1515 : copy_thru_input(b
, u
), in(i
)
1519 copy_file_thru_input::~copy_file_thru_input()
1524 int copy_file_thru_input::inget()
1532 class copy_rest_thru_input
1533 : public copy_thru_input
1536 copy_rest_thru_input(const char *, const char *u
);
1540 copy_rest_thru_input::copy_rest_thru_input(const char *b
, const char *u
)
1541 : copy_thru_input(b
, u
)
1545 int copy_rest_thru_input::inget()
1548 int c
= next
->get();
1551 if (next
->next
== 0)
1561 copy_thru_input::copy_thru_input(const char *b
, const char *u
)
1565 body
= process_body(b
);
1570 copy_thru_input::~copy_thru_input()
1576 int copy_thru_input::get()
1580 return (unsigned char)*ap
++;
1593 while ((unsigned char)*p
>= ARG1
1594 && (unsigned char)*p
<= ARG1
+ MAX_ARG
- 1) {
1595 int i
= (unsigned char)*p
++ - ARG1
;
1596 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1597 ap
= line
.contents() + argv
[i
];
1598 return (unsigned char)*ap
++;
1602 return (unsigned char)*p
++;
1607 int copy_thru_input::peek()
1611 return (unsigned char)*ap
;
1622 while ((unsigned char)*p
>= ARG1
1623 && (unsigned char)*p
<= ARG1
+ MAX_ARG
- 1) {
1624 int i
= (unsigned char)*p
++ - ARG1
;
1625 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1626 ap
= line
.contents() + argv
[i
];
1627 return (unsigned char)*ap
;
1631 return (unsigned char)*p
;
1636 int copy_thru_input::get_line()
1646 if (c
== EOF
|| c
== '\n')
1648 if (argc
== MAX_ARG
) {
1651 } while (c
!= '\n' && c
!= EOF
);
1654 argv
[argc
++] = line
.length();
1658 } while (c
!= ' ' && c
!= '\n');
1661 if (until
!= 0 && argc
> 0 && strcmp(&line
[argv
[0]], until
) == 0) {
1665 return argc
> 0 || c
== '\n';
1668 class simple_file_input
1671 const char *filename
;
1676 simple_file_input(file_case
*, const char *);
1677 ~simple_file_input();
1680 int get_location(const char **, int *);
1683 simple_file_input::simple_file_input(file_case
*fcp
, const char *s
)
1684 : filename(s
), lineno(1), _fcp(fcp
)
1688 simple_file_input::~simple_file_input()
1690 // don't delete the filename
1694 int simple_file_input::get()
1696 int c
= _fcp
->get_c();
1697 while (invalid_input_char(c
)) {
1698 error("invalid input character code %1", c
);
1706 int simple_file_input::peek()
1708 int c
= _fcp
->get_c();
1709 while (invalid_input_char(c
)) {
1710 error("invalid input character code %1", c
);
1718 int simple_file_input::get_location(const char **fnp
, int *lnp
)
1725 void copy_file_thru(const char *filename
, const char *body
, const char *until
)
1727 file_case
*fcp
= file_case::muxer(filename
);
1729 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1732 input
*in
= new copy_file_thru_input(new simple_file_input(fcp
, filename
),
1734 input_stack::push(in
);
1737 void copy_rest_thru(const char *body
, const char *until
)
1739 input_stack::push(new copy_rest_thru_input(body
, until
));
1742 void push_body(const char *s
)
1744 input_stack::push(new char_input('\n'));
1745 input_stack::push(new macro_input(s
));
1750 char *get_thru_arg()
1752 int c
= input_stack::peek_char();
1754 input_stack::get_char();
1755 c
= input_stack::peek_char();
1757 if (c
!= EOF
&& csalpha(c
)) {
1758 // looks like a macro
1759 input_stack::get_char();
1762 c
= input_stack::peek_char();
1763 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1765 input_stack::get_char();
1766 token_buffer
+= char(c
);
1768 context_buffer
= token_buffer
;
1769 token_buffer
+= '\0';
1770 char *def
= macro_table
.lookup(token_buffer
.contents());
1772 return strsave(def
);
1773 // I guess it wasn't a macro after all; so push the macro name back.
1774 // -2 because we added a '\0'
1775 for (int i
= token_buffer
.length() - 2; i
>= 0; i
--)
1776 input_stack::push_back(token_buffer
[i
]);
1778 if (get_delimited()) {
1779 token_buffer
+= '\0';
1780 return strsave(token_buffer
.contents());
1786 int lookahead_token
= -1;
1787 string old_context_buffer
;
1791 if (lookahead_token
== -1) {
1792 old_context_buffer
= context_buffer
;
1793 lookahead_token
= get_token(1);
1800 assert(lookahead_token
== -1);
1801 if (delim_flag
== 2) {
1802 if ((yylval
.str
= get_thru_arg()) != 0)
1808 if (get_delimited()) {
1809 token_buffer
+= '\0';
1810 yylval
.str
= strsave(token_buffer
.contents());
1819 if (lookahead_token
>= 0) {
1820 t
= lookahead_token
;
1821 lookahead_token
= -1;
1837 yylval
.n
= token_int
;
1840 yylval
.x
= token_double
;
1844 token_buffer
+= '\0';
1845 if (!input_stack::get_location(&yylval
.lstr
.filename
,
1846 &yylval
.lstr
.lineno
)) {
1847 yylval
.lstr
.filename
= 0;
1848 yylval
.lstr
.lineno
= -1;
1850 yylval
.lstr
.str
= strsave(token_buffer
.contents());
1854 token_buffer
+= '\0';
1855 yylval
.str
= strsave(token_buffer
.contents());
1858 // change LEFT to LEFT_CORNER when followed by OF
1859 old_context_buffer
= context_buffer
;
1860 lookahead_token
= get_token(1);
1861 if (lookahead_token
== OF
)
1866 // change RIGHT to RIGHT_CORNER when followed by OF
1867 old_context_buffer
= context_buffer
;
1868 lookahead_token
= get_token(1);
1869 if (lookahead_token
== OF
)
1870 return RIGHT_CORNER
;
1874 // recognise UPPER only before LEFT or RIGHT
1875 old_context_buffer
= context_buffer
;
1876 lookahead_token
= get_token(1);
1877 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1878 yylval
.str
= strsave("upper");
1884 // recognise LOWER only before LEFT or RIGHT
1885 old_context_buffer
= context_buffer
;
1886 lookahead_token
= get_token(1);
1887 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1888 yylval
.str
= strsave("lower");
1894 // recognise NORTH only before OF
1895 old_context_buffer
= context_buffer
;
1896 lookahead_token
= get_token(1);
1897 if (lookahead_token
!= OF
) {
1898 yylval
.str
= strsave("north");
1904 // recognise SOUTH only before OF
1905 old_context_buffer
= context_buffer
;
1906 lookahead_token
= get_token(1);
1907 if (lookahead_token
!= OF
) {
1908 yylval
.str
= strsave("south");
1914 // recognise EAST only before OF
1915 old_context_buffer
= context_buffer
;
1916 lookahead_token
= get_token(1);
1917 if (lookahead_token
!= OF
) {
1918 yylval
.str
= strsave("east");
1924 // recognise WEST only before OF
1925 old_context_buffer
= context_buffer
;
1926 lookahead_token
= get_token(1);
1927 if (lookahead_token
!= OF
) {
1928 yylval
.str
= strsave("west");
1934 // recognise TOP only before OF
1935 old_context_buffer
= context_buffer
;
1936 lookahead_token
= get_token(1);
1937 if (lookahead_token
!= OF
) {
1938 yylval
.str
= strsave("top");
1944 // recognise BOTTOM only before OF
1945 old_context_buffer
= context_buffer
;
1946 lookahead_token
= get_token(1);
1947 if (lookahead_token
!= OF
) {
1948 yylval
.str
= strsave("bottom");
1954 // recognise CENTER only before OF
1955 old_context_buffer
= context_buffer
;
1956 lookahead_token
= get_token(1);
1957 if (lookahead_token
!= OF
) {
1958 yylval
.str
= strsave("center");
1964 // recognise START only before OF
1965 old_context_buffer
= context_buffer
;
1966 lookahead_token
= get_token(1);
1967 if (lookahead_token
!= OF
) {
1968 yylval
.str
= strsave("start");
1974 // recognise END only before OF
1975 old_context_buffer
= context_buffer
;
1976 lookahead_token
= get_token(1);
1977 if (lookahead_token
!= OF
) {
1978 yylval
.str
= strsave("end");
1989 void lex_error(const char *message
,
1994 const char *filename
;
1996 if (!input_stack::get_location(&filename
, &lineno
))
1997 error(message
, arg1
, arg2
, arg3
);
1999 error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
2002 void lex_warning(const char *message
,
2007 const char *filename
;
2009 if (!input_stack::get_location(&filename
, &lineno
))
2010 warning(message
, arg1
, arg2
, arg3
);
2012 warning_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
2015 void yyerror(const char *s
)
2017 const char *filename
;
2019 const char *context
= 0;
2020 if (lookahead_token
== -1) {
2021 if (context_buffer
.length() > 0) {
2022 context_buffer
+= '\0';
2023 context
= context_buffer
.contents();
2027 if (old_context_buffer
.length() > 0) {
2028 old_context_buffer
+= '\0';
2029 context
= old_context_buffer
.contents();
2032 if (!input_stack::get_location(&filename
, &lineno
)) {
2034 if (context
[0] == '\n' && context
[1] == '\0')
2035 error("%1 before newline", s
);
2037 error("%1 before `%2'", s
, context
);
2040 error("%1 at end of picture", s
);
2044 if (context
[0] == '\n' && context
[1] == '\0')
2045 error_with_file_and_line(filename
, lineno
, "%1 before newline", s
);
2047 error_with_file_and_line(filename
, lineno
, "%1 before `%2'",
2051 error_with_file_and_line(filename
, lineno
, "%1 at end of picture", s
);