Line data Source code
1 : %top{
2 : /*-------------------------------------------------------------------------
3 : *
4 : * pgc.l
5 : * lexical scanner for ecpg
6 : *
7 : * This is a modified version of src/backend/parser/scan.l
8 : *
9 : * The ecpg scanner is not backup-free, so the fail rules are
10 : * only here to simplify syncing this file with scan.l.
11 : *
12 : *
13 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
14 : * Portions Copyright (c) 1994, Regents of the University of California
15 : *
16 : * IDENTIFICATION
17 : * src/interfaces/ecpg/preproc/pgc.l
18 : *
19 : *-------------------------------------------------------------------------
20 : */
21 : #include "postgres_fe.h"
22 :
23 : #include <ctype.h>
24 : #include <limits.h>
25 :
26 : #include "common/string.h"
27 :
28 : #include "preproc_extern.h"
29 : #include "preproc.h"
30 : }
31 :
32 : %{
33 :
34 : /* LCOV_EXCL_START */
35 :
36 : extern YYSTYPE base_yylval;
37 :
38 : static int xcdepth = 0; /* depth of nesting in slash-star comments */
39 : static char *dolqstart = NULL; /* current $foo$ quote start string */
40 :
41 : /*
42 : * literalbuf is used to accumulate literal values when multiple rules
43 : * are needed to parse a single literal. Call startlit to reset buffer
44 : * to empty, addlit to add text. Note that the buffer is permanently
45 : * malloc'd to the largest size needed so far in the current run.
46 : */
47 : static char *literalbuf = NULL; /* expandable buffer */
48 : static int literallen; /* actual current length */
49 : static int literalalloc; /* current allocated buffer size */
50 :
51 : /* Used for detecting global state together with braces_open */
52 : static int parenths_open;
53 :
54 : /* Used to tell parse_include() whether the command was #include or #include_next */
55 : static bool include_next;
56 :
57 : #define startlit() (literalbuf[0] = '\0', literallen = 0)
58 : static void addlit(char *ytext, int yleng);
59 : static void addlitchar(unsigned char ychar);
60 : static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
61 : static void parse_include(void);
62 : static bool ecpg_isspace(char ch);
63 : static bool isdefine(void);
64 : static bool isinformixdefine(void);
65 :
66 : char *token_start;
67 :
68 : /* vars to keep track of start conditions when scanning literals */
69 : static int state_before_str_start;
70 : static int state_before_str_stop;
71 :
72 : struct _yy_buffer
73 : {
74 : YY_BUFFER_STATE buffer;
75 : long lineno;
76 : char *filename;
77 : struct _yy_buffer *next;
78 : } *yy_buffer = NULL;
79 :
80 : static char *old;
81 :
82 : /*
83 : * Vars for handling ifdef/elif/endif constructs. preproc_tos is the current
84 : * nesting depth of such constructs, and stacked_if_value[preproc_tos] is the
85 : * state for the innermost level. (For convenience, stacked_if_value[0] is
86 : * initialized as though we are in the active branch of some outermost IF.)
87 : * The active field is true if the current branch is active (being expanded).
88 : * The saw_active field is true if we have found any successful branch,
89 : * so that all subsequent branches of this level should be skipped.
90 : * The else_branch field is true if we've found an 'else' (so that another
91 : * 'else' or 'elif' at this level is an error.)
92 : * For IFs nested within an inactive branch, all branches always have active
93 : * set to false, but saw_active and else_branch are maintained normally.
94 : * ifcond is valid only while evaluating an if-condition; it's true if we
95 : * are doing ifdef, false if ifndef.
96 : */
97 : #define MAX_NESTED_IF 128
98 : static short preproc_tos;
99 : static bool ifcond;
100 : static struct _if_value
101 : {
102 : bool active;
103 : bool saw_active;
104 : bool else_branch;
105 : } stacked_if_value[MAX_NESTED_IF];
106 :
107 : %}
108 :
109 : %option 8bit
110 : %option never-interactive
111 : %option nodefault
112 : %option noinput
113 : %option noyywrap
114 : %option warn
115 : %option yylineno
116 : %option prefix="base_yy"
117 :
118 : /*
119 : * OK, here is a short description of lex/flex rules behavior.
120 : * The longest pattern which matches an input string is always chosen.
121 : * For equal-length patterns, the first occurring in the rules list is chosen.
122 : * INITIAL is the starting state, to which all non-conditional rules apply.
123 : * Exclusive states change parsing rules while the state is active. When in
124 : * an exclusive state, only those rules defined for that state apply.
125 : *
126 : * We use exclusive states for quoted strings, extended comments,
127 : * and to eliminate parsing troubles for numeric strings.
128 : * Exclusive states:
129 : * <xb> bit string literal
130 : * <xc> extended C-style comments
131 : * <xd> delimited identifiers (double-quoted identifiers)
132 : * <xdc> double-quoted strings in C
133 : * <xh> hexadecimal byte string
134 : * <xn> national character quoted strings
135 : * <xq> standard quoted strings
136 : * <xqs> quote stop (detect continued strings)
137 : * <xe> extended quoted strings (support backslash escape sequences)
138 : * <xqc> single-quoted strings in C
139 : * <xdolq> $foo$ quoted strings
140 : * <xui> quoted identifier with Unicode escapes
141 : * <xus> quoted string with Unicode escapes
142 : * <xcond> condition of an EXEC SQL IFDEF construct
143 : * <xskip> skipping the inactive part of an EXEC SQL IFDEF construct
144 : *
145 : * Note: we intentionally don't mimic the backend's <xeu> state; we have
146 : * no need to distinguish it from <xe> state.
147 : *
148 : * Remember to add an <<EOF>> case whenever you add a new exclusive state!
149 : * The default one is probably not the right thing.
150 : */
151 :
152 : %x xb
153 : %x xc
154 : %x xd
155 : %x xdc
156 : %x xh
157 : %x xn
158 : %x xq
159 : %x xqs
160 : %x xe
161 : %x xqc
162 : %x xdolq
163 : %x xui
164 : %x xus
165 : %x xcond
166 : %x xskip
167 :
168 : /* Additional exclusive states that are specific to ECPG */
169 : %x C SQL incl def def_ident undef
170 :
171 : /*
172 : * In order to make the world safe for Windows and Mac clients as well as
173 : * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n
174 : * sequence will be seen as two successive newlines, but that doesn't cause
175 : * any problems. SQL-style comments, which start with -- and extend to the
176 : * next newline, are treated as equivalent to a single whitespace character.
177 : *
178 : * NOTE a fine point: if there is no newline following --, we will absorb
179 : * everything to the end of the input as a comment. This is correct. Older
180 : * versions of Postgres failed to recognize -- as a comment if the input
181 : * did not end with a newline.
182 : *
183 : * non_newline_space tracks all space characters except newlines.
184 : *
185 : * XXX if you change the set of whitespace characters, fix ecpg_isspace()
186 : * to agree.
187 : */
188 :
189 : space [ \t\n\r\f\v]
190 : non_newline_space [ \t\f\v]
191 : newline [\n\r]
192 : non_newline [^\n\r]
193 :
194 : comment ("--"{non_newline}*)
195 :
196 : whitespace ({space}+|{comment})
197 :
198 : /*
199 : * SQL requires at least one newline in the whitespace separating
200 : * string literals that are to be concatenated. Silly, but who are we
201 : * to argue? Note that {whitespace_with_newline} should not have * after
202 : * it, whereas {whitespace} should generally have a * after it...
203 : */
204 :
205 : non_newline_whitespace ({non_newline_space}|{comment})
206 : whitespace_with_newline ({non_newline_whitespace}*{newline}{whitespace}*)
207 :
208 : quote '
209 : /* If we see {quote} then {quotecontinue}, the quoted string continues */
210 : quotecontinue {whitespace_with_newline}{quote}
211 :
212 : /*
213 : * {quotecontinuefail} is needed to avoid lexer backup when we fail to match
214 : * {quotecontinue}. It might seem that this could just be {whitespace}*,
215 : * but if there's a dash after {whitespace_with_newline}, it must be consumed
216 : * to see if there's another dash --- which would start a {comment} and thus
217 : * allow continuation of the {quotecontinue} token.
218 : */
219 : quotecontinuefail {whitespace}*"-"?
220 :
221 : /* Bit string
222 : */
223 : xbstart [bB]{quote}
224 : xbinside [^']*
225 :
226 : /* Hexadecimal byte string */
227 : xhstart [xX]{quote}
228 : xhinside [^']*
229 :
230 : /* National character */
231 : xnstart [nN]{quote}
232 :
233 : /* Quoted string that allows backslash escapes */
234 : xestart [eE]{quote}
235 : xeinside [^\\']+
236 : xeescape [\\][^0-7]
237 : xeoctesc [\\][0-7]{1,3}
238 : xehexesc [\\]x[0-9A-Fa-f]{1,2}
239 : xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
240 :
241 : /* Extended quote
242 : * xqdouble implements embedded quote, ''''
243 : */
244 : xqstart {quote}
245 : xqdouble {quote}{quote}
246 : xqcquote [\\]{quote}
247 : xqinside [^']+
248 :
249 : /* $foo$ style quotes ("dollar quoting")
250 : * The quoted string starts with $foo$ where "foo" is an optional string
251 : * in the form of an identifier, except that it may not contain "$",
252 : * and extends to the first occurrence of an identical string.
253 : * There is *no* processing of the quoted text.
254 : *
255 : * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim}
256 : * fails to match its trailing "$".
257 : */
258 : dolq_start [A-Za-z\200-\377_]
259 : dolq_cont [A-Za-z\200-\377_0-9]
260 : dolqdelim \$({dolq_start}{dolq_cont}*)?\$
261 : dolqfailed \${dolq_start}{dolq_cont}*
262 : dolqinside [^$]+
263 :
264 : /* Double quote
265 : * Allows embedded spaces and other special characters into identifiers.
266 : */
267 : dquote \"
268 : xdstart {dquote}
269 : xdstop {dquote}
270 : xddouble {dquote}{dquote}
271 : xdinside [^"]+
272 :
273 : /* Quoted identifier with Unicode escapes */
274 : xuistart [uU]&{dquote}
275 :
276 : /* Quoted string with Unicode escapes */
277 : xusstart [uU]&{quote}
278 :
279 : /* special stuff for C strings */
280 : xdcqq \\\\
281 : xdcqdq \\\"
282 : xdcother [^"]
283 : xdcinside ({xdcqq}|{xdcqdq}|{xdcother})
284 :
285 :
286 : /* C-style comments
287 : *
288 : * The "extended comment" syntax closely resembles allowable operator syntax.
289 : * The tricky part here is to get lex to recognize a string starting with
290 : * slash-star as a comment, when interpreting it as an operator would produce
291 : * a longer match --- remember lex will prefer a longer match! Also, if we
292 : * have something like plus-slash-star, lex will think this is a 3-character
293 : * operator whereas we want to see it as a + operator and a comment start.
294 : * The solution is two-fold:
295 : * 1. append {op_chars}* to xcstart so that it matches as much text as
296 : * {operator} would. Then the tie-breaker (first matching rule of same
297 : * length) ensures xcstart wins. We put back the extra stuff with yyless()
298 : * in case it contains a star-slash that should terminate the comment.
299 : * 2. In the operator rule, check for slash-star within the operator, and
300 : * if found throw it back with yyless(). This handles the plus-slash-star
301 : * problem.
302 : * Dash-dash comments have similar interactions with the operator rule.
303 : */
304 : xcstart \/\*{op_chars}*
305 : xcstop \*+\/
306 : xcinside [^*/]+
307 :
308 : ident_start [A-Za-z\200-\377_]
309 : ident_cont [A-Za-z\200-\377_0-9\$]
310 :
311 : identifier {ident_start}{ident_cont}*
312 :
313 : array ({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])*
314 :
315 : /* Assorted special-case operators and operator-like tokens */
316 : typecast "::"
317 : dot_dot \.\.
318 : colon_equals ":="
319 :
320 : /*
321 : * These operator-like tokens (unlike the above ones) also match the {operator}
322 : * rule, which means that they might be overridden by a longer match if they
323 : * are followed by a comment start or a + or - character. Accordingly, if you
324 : * add to this list, you must also add corresponding code to the {operator}
325 : * block to return the correct token in such cases. (This is not needed in
326 : * psqlscan.l since the token value is ignored there.)
327 : */
328 : equals_greater "=>"
329 : less_equals "<="
330 : greater_equals ">="
331 : less_greater "<>"
332 : not_equals "!="
333 :
334 : /*
335 : * "self" is the set of chars that should be returned as single-character
336 : * tokens. "op_chars" is the set of chars that can make up "Op" tokens,
337 : * which can be one or more characters long (but if a single-char token
338 : * appears in the "self" set, it is not to be returned as an Op). Note
339 : * that the sets overlap, but each has some chars that are not in the other.
340 : *
341 : * If you change either set, adjust the character lists appearing in the
342 : * rule for "operator"!
343 : */
344 : self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
345 : op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
346 : operator {op_chars}+
347 :
348 : /*
349 : * Numbers
350 : *
351 : * Unary minus is not part of a number here. Instead we pass it separately to
352 : * the parser, and there it gets coerced via doNegate().
353 : *
354 : * {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
355 : *
356 : * {realfail} is added to prevent the need for scanner
357 : * backup when the {real} rule fails to match completely.
358 : */
359 : decdigit [0-9]
360 : hexdigit [0-9A-Fa-f]
361 : octdigit [0-7]
362 : bindigit [0-1]
363 :
364 : decinteger {decdigit}(_?{decdigit})*
365 : hexinteger 0[xX](_?{hexdigit})+
366 : octinteger 0[oO](_?{octdigit})+
367 : bininteger 0[bB](_?{bindigit})+
368 :
369 : hexfail 0[xX]_?
370 : octfail 0[oO]_?
371 : binfail 0[bB]_?
372 :
373 : numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
374 : numericfail {decdigit}+\.\.
375 :
376 : real ({decinteger}|{numeric})[Ee][-+]?{decinteger}
377 : realfail ({decinteger}|{numeric})[Ee][-+]
378 :
379 : decinteger_junk {decinteger}{ident_start}
380 : hexinteger_junk {hexinteger}{ident_start}
381 : octinteger_junk {octinteger}{ident_start}
382 : bininteger_junk {bininteger}{ident_start}
383 : numeric_junk {numeric}{ident_start}
384 : real_junk {real}{ident_start}
385 :
386 : param \${decinteger}
387 : param_junk \${decinteger}{ident_start}
388 :
389 : /* special characters for other dbms */
390 : /* we have to react differently in compat mode */
391 : informix_special [\$]
392 :
393 : other .
394 :
395 : /*
396 : * Dollar quoted strings are totally opaque, and no escaping is done on them.
397 : * Other quoted strings must allow some special characters such as single-quote
398 : * and newline.
399 : * Embedded single-quotes are implemented both in the SQL standard
400 : * style of two adjacent single quotes "''" and in the Postgres/Java style
401 : * of escaped-quote "\'".
402 : * Other embedded escaped characters are matched explicitly and the leading
403 : * backslash is dropped from the string.
404 : * Note that xcstart must appear before operator, as explained above!
405 : * Also whitespace (comment) must appear before operator.
406 : */
407 :
408 : /* some stuff needed for ecpg */
409 : exec [eE][xX][eE][cC]
410 : sql [sS][qQ][lL]
411 : define [dD][eE][fF][iI][nN][eE]
412 : include [iI][nN][cC][lL][uU][dD][eE]
413 : include_next [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
414 : import [iI][mM][pP][oO][rR][tT]
415 : undef [uU][nN][dD][eE][fF]
416 :
417 : ccomment "//".*\n
418 :
419 : if [iI][fF]
420 : ifdef [iI][fF][dD][eE][fF]
421 : ifndef [iI][fF][nN][dD][eE][fF]
422 : else [eE][lL][sS][eE]
423 : elif [eE][lL][iI][fF]
424 : endif [eE][nN][dD][iI][fF]
425 :
426 : struct [sS][tT][rR][uU][cC][tT]
427 :
428 : exec_sql {exec}{space}*{sql}{space}*
429 : ipdigit ({decdigit}|{decdigit}{decdigit}|{decdigit}{decdigit}{decdigit})
430 : ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
431 :
432 : /* we might want to parse all cpp include files */
433 : cppinclude {space}*#{include}{space}*
434 : cppinclude_next {space}*#{include_next}{space}*
435 :
436 : /* take care of cpp lines, they may also be continued */
437 : /* first a general line for all commands not starting with "i" */
438 : /* and then the other commands starting with "i", we have to add these
439 : * separately because the cppline production would match on "include" too
440 : */
441 : cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
442 :
443 : %%
444 :
445 : %{
446 : /* code to execute during start of each call of yylex() */
447 : token_start = NULL;
448 : %}
449 :
450 : <SQL>{
451 : {whitespace} {
452 : /* ignore */
453 : }
454 : } /* <SQL> */
455 :
456 : <C,SQL>{
457 : {xcstart} {
458 : token_start = yytext;
459 : state_before_str_start = YYSTATE;
460 : xcdepth = 0;
461 : BEGIN(xc);
462 : /* Put back any characters past slash-star; see above */
463 : yyless(2);
464 : fputs("/*", yyout);
465 : }
466 : } /* <C,SQL> */
467 :
468 : <xc>{
469 : {xcstart} {
470 : if (state_before_str_start == SQL)
471 : {
472 : xcdepth++;
473 : /* Put back any characters past slash-star; see above */
474 : yyless(2);
475 : fputs("/_*", yyout);
476 : }
477 : else if (state_before_str_start == C)
478 : {
479 : ECHO;
480 : }
481 : }
482 :
483 : {xcstop} {
484 : if (state_before_str_start == SQL)
485 : {
486 : if (xcdepth <= 0)
487 : {
488 : ECHO;
489 : BEGIN(SQL);
490 : token_start = NULL;
491 : }
492 : else
493 : {
494 : xcdepth--;
495 : fputs("*_/", yyout);
496 : }
497 : }
498 : else if (state_before_str_start == C)
499 : {
500 : ECHO;
501 : BEGIN(C);
502 : token_start = NULL;
503 : }
504 : }
505 :
506 : {xcinside} {
507 : ECHO;
508 : }
509 :
510 : {op_chars} {
511 : ECHO;
512 : }
513 :
514 : \*+ {
515 : ECHO;
516 : }
517 :
518 : <<EOF>> {
519 : mmfatal(PARSE_ERROR, "unterminated /* comment");
520 : }
521 : } /* <xc> */
522 :
523 : <SQL>{
524 : {xbstart} {
525 : token_start = yytext;
526 : state_before_str_start = YYSTATE;
527 : BEGIN(xb);
528 : startlit();
529 : }
530 : } /* <SQL> */
531 :
532 : <xh>{xhinside} |
533 : <xb>{xbinside} {
534 : addlit(yytext, yyleng);
535 : }
536 : <xb><<EOF>> { mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
537 :
538 : <SQL>{xhstart} {
539 : token_start = yytext;
540 : state_before_str_start = YYSTATE;
541 : BEGIN(xh);
542 : startlit();
543 : }
544 : <xh><<EOF>> { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
545 :
546 : <C>{xqstart} {
547 : token_start = yytext;
548 : state_before_str_start = YYSTATE;
549 : BEGIN(xqc);
550 : startlit();
551 : }
552 :
553 : <SQL>{
554 : {xnstart} {
555 : /* National character.
556 : * Transfer it as-is to the backend.
557 : */
558 : token_start = yytext;
559 : state_before_str_start = YYSTATE;
560 : BEGIN(xn);
561 : startlit();
562 : }
563 :
564 : {xqstart} {
565 : token_start = yytext;
566 : state_before_str_start = YYSTATE;
567 : BEGIN(xq);
568 : startlit();
569 : }
570 : {xestart} {
571 : token_start = yytext;
572 : state_before_str_start = YYSTATE;
573 : BEGIN(xe);
574 : startlit();
575 : }
576 : {xusstart} {
577 : token_start = yytext;
578 : state_before_str_start = YYSTATE;
579 : BEGIN(xus);
580 : startlit();
581 : }
582 : } /* <SQL> */
583 :
584 : <xb,xh,xq,xqc,xe,xn,xus>{quote} {
585 : /*
586 : * When we are scanning a quoted string and see an end
587 : * quote, we must look ahead for a possible continuation.
588 : * If we don't see one, we know the end quote was in fact
589 : * the end of the string. To reduce the lexer table size,
590 : * we use a single "xqs" state to do the lookahead for all
591 : * types of strings.
592 : */
593 : state_before_str_stop = YYSTATE;
594 : BEGIN(xqs);
595 : }
596 : <xqs>{quotecontinue} {
597 : /*
598 : * Found a quote continuation, so return to the in-quote
599 : * state and continue scanning the literal. Nothing is
600 : * added to the literal's contents.
601 : */
602 : BEGIN(state_before_str_stop);
603 : }
604 : <xqs>{quotecontinuefail} |
605 : <xqs>{other} |
606 : <xqs><<EOF>> {
607 : /*
608 : * Failed to see a quote continuation. Throw back
609 : * everything after the end quote, and handle the string
610 : * according to the state we were in previously.
611 : */
612 : yyless(0);
613 : BEGIN(state_before_str_start);
614 :
615 : switch (state_before_str_stop)
616 : {
617 : case xb:
618 : if (literalbuf[strspn(literalbuf, "01")] != '\0')
619 : mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string literal");
620 : base_yylval.str = psprintf("b'%s'", literalbuf);
621 : return BCONST;
622 : case xh:
623 : if (literalbuf[strspn(literalbuf, "0123456789abcdefABCDEF")] != '\0')
624 : mmerror(PARSE_ERROR, ET_ERROR, "invalid hexadecimal string literal");
625 : base_yylval.str = psprintf("x'%s'", literalbuf);
626 : return XCONST;
627 : case xq:
628 : /* fallthrough */
629 : case xqc:
630 : base_yylval.str = psprintf("'%s'", literalbuf);
631 : return SCONST;
632 : case xe:
633 : base_yylval.str = psprintf("E'%s'", literalbuf);
634 : return SCONST;
635 : case xn:
636 : base_yylval.str = psprintf("N'%s'", literalbuf);
637 : return SCONST;
638 : case xus:
639 : base_yylval.str = psprintf("U&'%s'", literalbuf);
640 : return USCONST;
641 : default:
642 : mmfatal(PARSE_ERROR, "unhandled previous state in xqs\n");
643 : }
644 : }
645 :
646 : <xq,xe,xn,xus>{xqdouble} { addlit(yytext, yyleng); }
647 : <xqc>{xqcquote} { addlit(yytext, yyleng); }
648 : <xq,xqc,xn,xus>{xqinside} { addlit(yytext, yyleng); }
649 : <xe>{xeinside} {
650 : addlit(yytext, yyleng);
651 : }
652 : <xe>{xeunicode} {
653 : addlit(yytext, yyleng);
654 : }
655 : <xe>{xeescape} {
656 : addlit(yytext, yyleng);
657 : }
658 : <xe>{xeoctesc} {
659 : addlit(yytext, yyleng);
660 : }
661 : <xe>{xehexesc} {
662 : addlit(yytext, yyleng);
663 : }
664 : <xe>. {
665 : /* This is only needed for \ just before EOF */
666 : addlitchar(yytext[0]);
667 : }
668 : <xq,xqc,xe,xn,xus><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
669 :
670 : <SQL>{
671 : {dolqdelim} {
672 : token_start = yytext;
673 : if (dolqstart)
674 : free(dolqstart);
675 : dolqstart = mm_strdup(yytext);
676 : BEGIN(xdolq);
677 : startlit();
678 : addlit(yytext, yyleng);
679 : }
680 : {dolqfailed} {
681 : /* throw back all but the initial "$" */
682 : yyless(1);
683 : /* and treat it as {other} */
684 : return yytext[0];
685 : }
686 : } /* <SQL> */
687 :
688 : <xdolq>{dolqdelim} {
689 : if (strcmp(yytext, dolqstart) == 0)
690 : {
691 : addlit(yytext, yyleng);
692 : free(dolqstart);
693 : dolqstart = NULL;
694 : BEGIN(SQL);
695 : base_yylval.str = mm_strdup(literalbuf);
696 : return SCONST;
697 : }
698 : else
699 : {
700 : /*
701 : * When we fail to match $...$ to dolqstart, transfer
702 : * the $... part to the output, but put back the final
703 : * $ for rescanning. Consider $delim$...$junk$delim$
704 : */
705 : addlit(yytext, yyleng - 1);
706 : yyless(yyleng - 1);
707 : }
708 : }
709 : <xdolq>{dolqinside} {
710 : addlit(yytext, yyleng);
711 : }
712 : <xdolq>{dolqfailed} {
713 : addlit(yytext, yyleng);
714 : }
715 : <xdolq>. {
716 : /* single quote or dollar sign */
717 : addlitchar(yytext[0]);
718 : }
719 : <xdolq><<EOF>> { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
720 :
721 : <SQL>{
722 : {xdstart} {
723 : state_before_str_start = YYSTATE;
724 : BEGIN(xd);
725 : startlit();
726 : }
727 : {xuistart} {
728 : state_before_str_start = YYSTATE;
729 : BEGIN(xui);
730 : startlit();
731 : }
732 : } /* <SQL> */
733 :
734 : <xd>{xdstop} {
735 : BEGIN(state_before_str_start);
736 : if (literallen == 0)
737 : mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
738 : /*
739 : * The server will truncate the identifier here. We do
740 : * not, as (1) it does not change the result; (2) we don't
741 : * know what NAMEDATALEN the server might use; (3) this
742 : * code path is also taken for literal query strings in
743 : * PREPARE and EXECUTE IMMEDIATE, which can certainly be
744 : * longer than NAMEDATALEN.
745 : */
746 : base_yylval.str = mm_strdup(literalbuf);
747 : return CSTRING;
748 : }
749 : <xdc>{xdstop} {
750 : BEGIN(state_before_str_start);
751 : base_yylval.str = mm_strdup(literalbuf);
752 : return CSTRING;
753 : }
754 : <xui>{dquote} {
755 : BEGIN(state_before_str_start);
756 : if (literallen == 0)
757 : mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
758 : /* The backend will truncate the identifier here. We do not as it does not change the result. */
759 : base_yylval.str = psprintf("U&\"%s\"", literalbuf);
760 : return UIDENT;
761 : }
762 : <xd,xui>{xddouble} {
763 : addlit(yytext, yyleng);
764 : }
765 : <xd,xui>{xdinside} {
766 : addlit(yytext, yyleng);
767 : }
768 : <xd,xui><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
769 : <C>{xdstart} {
770 : state_before_str_start = YYSTATE;
771 : BEGIN(xdc);
772 : startlit();
773 : }
774 : <xdc>{xdcinside} {
775 : addlit(yytext, yyleng);
776 : }
777 : <xdc><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
778 :
779 : <SQL>{
780 : {typecast} {
781 : return TYPECAST;
782 : }
783 :
784 : {dot_dot} {
785 : return DOT_DOT;
786 : }
787 :
788 : {colon_equals} {
789 : return COLON_EQUALS;
790 : }
791 :
792 : {equals_greater} {
793 : return EQUALS_GREATER;
794 : }
795 :
796 : {less_equals} {
797 : return LESS_EQUALS;
798 : }
799 :
800 : {greater_equals} {
801 : return GREATER_EQUALS;
802 : }
803 :
804 : {less_greater} {
805 : /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
806 : return NOT_EQUALS;
807 : }
808 :
809 : {not_equals} {
810 : /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
811 : return NOT_EQUALS;
812 : }
813 :
814 : {informix_special} {
815 : /* are we simulating Informix? */
816 : if (INFORMIX_MODE)
817 : {
818 : unput(':');
819 : }
820 : else
821 : return yytext[0];
822 : }
823 :
824 : {self} {
825 : /*
826 : * We may find a ';' inside a structure
827 : * definition in a TYPE or VAR statement.
828 : * This is not an EOL marker.
829 : */
830 : if (yytext[0] == ';' && struct_level == 0)
831 : BEGIN(C);
832 : return yytext[0];
833 : }
834 :
835 : {operator} {
836 : /*
837 : * Check for embedded slash-star or dash-dash; those
838 : * are comment starts, so operator must stop there.
839 : * Note that slash-star or dash-dash at the first
840 : * character will match a prior rule, not this one.
841 : */
842 : int nchars = yyleng;
843 : char *slashstar = strstr(yytext, "/*");
844 : char *dashdash = strstr(yytext, "--");
845 :
846 : if (slashstar && dashdash)
847 : {
848 : /* if both appear, take the first one */
849 : if (slashstar > dashdash)
850 : slashstar = dashdash;
851 : }
852 : else if (!slashstar)
853 : slashstar = dashdash;
854 : if (slashstar)
855 : nchars = slashstar - yytext;
856 :
857 : /*
858 : * For SQL compatibility, '+' and '-' cannot be the
859 : * last char of a multi-char operator unless the operator
860 : * contains chars that are not in SQL operators.
861 : * The idea is to lex '=-' as two operators, but not
862 : * to forbid operator names like '?-' that could not be
863 : * sequences of SQL operators.
864 : */
865 : if (nchars > 1 &&
866 : (yytext[nchars - 1] == '+' ||
867 : yytext[nchars - 1] == '-'))
868 : {
869 : int ic;
870 :
871 : for (ic = nchars - 2; ic >= 0; ic--)
872 : {
873 : char c = yytext[ic];
874 : if (c == '~' || c == '!' || c == '@' ||
875 : c == '#' || c == '^' || c == '&' ||
876 : c == '|' || c == '`' || c == '?' ||
877 : c == '%')
878 : break;
879 : }
880 : if (ic < 0)
881 : {
882 : /*
883 : * didn't find a qualifying character, so remove
884 : * all trailing [+-]
885 : */
886 : do {
887 : nchars--;
888 : } while (nchars > 1 &&
889 : (yytext[nchars - 1] == '+' ||
890 : yytext[nchars - 1] == '-'));
891 : }
892 : }
893 :
894 : if (nchars < yyleng)
895 : {
896 : /* Strip the unwanted chars from the token */
897 : yyless(nchars);
898 : /*
899 : * If what we have left is only one char, and it's
900 : * one of the characters matching "self", then
901 : * return it as a character token the same way
902 : * that the "self" rule would have.
903 : */
904 : if (nchars == 1 &&
905 : strchr(",()[].;:+-*/%^<>=", yytext[0]))
906 : return yytext[0];
907 : /*
908 : * Likewise, if what we have left is two chars, and
909 : * those match the tokens ">=", "<=", "=>", "<>" or
910 : * "!=", then we must return the appropriate token
911 : * rather than the generic Op.
912 : */
913 : if (nchars == 2)
914 : {
915 : if (yytext[0] == '=' && yytext[1] == '>')
916 : return EQUALS_GREATER;
917 : if (yytext[0] == '>' && yytext[1] == '=')
918 : return GREATER_EQUALS;
919 : if (yytext[0] == '<' && yytext[1] == '=')
920 : return LESS_EQUALS;
921 : if (yytext[0] == '<' && yytext[1] == '>')
922 : return NOT_EQUALS;
923 : if (yytext[0] == '!' && yytext[1] == '=')
924 : return NOT_EQUALS;
925 : }
926 : }
927 :
928 : base_yylval.str = mm_strdup(yytext);
929 : return Op;
930 : }
931 :
932 : {param} {
933 : base_yylval.ival = atol(yytext+1);
934 : return PARAM;
935 : }
936 : {param_junk} {
937 : mmfatal(PARSE_ERROR, "trailing junk after parameter");
938 : }
939 :
940 : {ip} {
941 : base_yylval.str = mm_strdup(yytext);
942 : return IP;
943 : }
944 : } /* <SQL> */
945 :
946 : <C,SQL>{
947 : {decinteger} {
948 : return process_integer_literal(yytext, &base_yylval, 10);
949 : }
950 : {hexinteger} {
951 : return process_integer_literal(yytext, &base_yylval, 16);
952 : }
953 : {numeric} {
954 : base_yylval.str = mm_strdup(yytext);
955 : return FCONST;
956 : }
957 : {numericfail} {
958 : /* throw back the .., and treat as integer */
959 : yyless(yyleng - 2);
960 : return process_integer_literal(yytext, &base_yylval, 10);
961 : }
962 : {real} {
963 : base_yylval.str = mm_strdup(yytext);
964 : return FCONST;
965 : }
966 : {realfail} {
967 : /*
968 : * throw back the [Ee][+-], and figure out whether what
969 : * remains is an {decinteger} or {numeric}.
970 : */
971 : yyless(yyleng - 2);
972 : return process_integer_literal(yytext, &base_yylval, 10);
973 : }
974 : } /* <C,SQL> */
975 :
976 : <SQL>{
977 : {octinteger} {
978 : return process_integer_literal(yytext, &base_yylval, 8);
979 : }
980 : {bininteger} {
981 : return process_integer_literal(yytext, &base_yylval, 2);
982 : }
983 :
984 : /*
985 : * Note that some trailing junk is valid in C (such as 100LL), so we
986 : * contain this to SQL mode.
987 : */
988 : {decinteger_junk} {
989 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
990 : }
991 : {hexinteger_junk} {
992 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
993 : }
994 : {octinteger_junk} {
995 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
996 : }
997 : {bininteger_junk} {
998 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
999 : }
1000 : {numeric_junk} {
1001 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
1002 : }
1003 : {real_junk} {
1004 : mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
1005 : }
1006 :
1007 : :{identifier}((("->"|\.){identifier})|(\[{array}\]))* {
1008 : base_yylval.str = mm_strdup(yytext+1);
1009 : return CVARIABLE;
1010 : }
1011 :
1012 : {identifier} {
1013 : if (!isdefine())
1014 : {
1015 : int kwvalue;
1016 :
1017 : /*
1018 : * User-defined typedefs override SQL keywords, but
1019 : * not C keywords. Currently, a typedef name is just
1020 : * reported as IDENT, but someday we might need to
1021 : * return a distinct token type.
1022 : */
1023 : if (get_typedef(yytext, true) == NULL)
1024 : {
1025 : /* Is it an SQL/ECPG keyword? */
1026 : kwvalue = ScanECPGKeywordLookup(yytext);
1027 : if (kwvalue >= 0)
1028 : return kwvalue;
1029 : }
1030 :
1031 : /* Is it a C keyword? */
1032 : kwvalue = ScanCKeywordLookup(yytext);
1033 : if (kwvalue >= 0)
1034 : return kwvalue;
1035 :
1036 : /*
1037 : * None of the above. Return it as an identifier.
1038 : *
1039 : * The backend will attempt to truncate and case-fold
1040 : * the identifier, but I see no good reason for ecpg
1041 : * to do so; that's just another way that ecpg could get
1042 : * out of step with the backend.
1043 : */
1044 : base_yylval.str = mm_strdup(yytext);
1045 : return IDENT;
1046 : }
1047 : }
1048 :
1049 : {other} {
1050 : return yytext[0];
1051 : }
1052 : } /* <SQL> */
1053 :
1054 : /*
1055 : * Begin ECPG-specific rules
1056 : */
1057 :
1058 : <C>{exec_sql} { BEGIN(SQL); return SQL_START; }
1059 : <C>{informix_special} {
1060 : /* are we simulating Informix? */
1061 : if (INFORMIX_MODE)
1062 : {
1063 : BEGIN(SQL);
1064 : return SQL_START;
1065 : }
1066 : else
1067 : return S_ANYTHING;
1068 : }
1069 : <C>{ccomment} { ECHO; }
1070 : <C>{cppinclude} {
1071 : if (system_includes)
1072 : {
1073 : include_next = false;
1074 : BEGIN(incl);
1075 : }
1076 : else
1077 : {
1078 : base_yylval.str = mm_strdup(yytext);
1079 : return CPP_LINE;
1080 : }
1081 : }
1082 : <C>{cppinclude_next} {
1083 : if (system_includes)
1084 : {
1085 : include_next = true;
1086 : BEGIN(incl);
1087 : }
1088 : else
1089 : {
1090 : base_yylval.str = mm_strdup(yytext);
1091 : return CPP_LINE;
1092 : }
1093 : }
1094 : <C,SQL>{cppline} {
1095 : base_yylval.str = mm_strdup(yytext);
1096 : return CPP_LINE;
1097 : }
1098 : <C>{identifier} {
1099 : /*
1100 : * Try to detect a function name:
1101 : * look for identifiers at the global scope
1102 : * keep the last identifier before the first '(' and '{'
1103 : */
1104 : if (braces_open == 0 && parenths_open == 0)
1105 : {
1106 : if (current_function)
1107 : free(current_function);
1108 : current_function = mm_strdup(yytext);
1109 : }
1110 : /* Informix uses SQL defines only in SQL space */
1111 : /* however, some defines have to be taken care of for compatibility */
1112 : if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
1113 : {
1114 : int kwvalue;
1115 :
1116 : kwvalue = ScanCKeywordLookup(yytext);
1117 : if (kwvalue >= 0)
1118 : return kwvalue;
1119 : else
1120 : {
1121 : base_yylval.str = mm_strdup(yytext);
1122 : return IDENT;
1123 : }
1124 : }
1125 : }
1126 : <C>{xcstop} { mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments"); }
1127 : <C>":" { return ':'; }
1128 : <C>";" { return ';'; }
1129 : <C>"," { return ','; }
1130 : <C>"*" { return '*'; }
1131 : <C>"%" { return '%'; }
1132 : <C>"/" { return '/'; }
1133 : <C>"+" { return '+'; }
1134 : <C>"-" { return '-'; }
1135 : <C>"(" { parenths_open++; return '('; }
1136 : <C>")" { parenths_open--; return ')'; }
1137 : <C,xskip>{space} { ECHO; }
1138 : <C>\{ { return '{'; }
1139 : <C>\} { return '}'; }
1140 : <C>\[ { return '['; }
1141 : <C>\] { return ']'; }
1142 : <C>\= { return '='; }
1143 : <C>"->" { return S_MEMBER; }
1144 : <C>">>" { return S_RSHIFT; }
1145 : <C>"<<" { return S_LSHIFT; }
1146 : <C>"||" { return S_OR; }
1147 : <C>"&&" { return S_AND; }
1148 : <C>"++" { return S_INC; }
1149 : <C>"--" { return S_DEC; }
1150 : <C>"==" { return S_EQUAL; }
1151 : <C>"!=" { return S_NEQUAL; }
1152 : <C>"+=" { return S_ADD; }
1153 : <C>"-=" { return S_SUB; }
1154 : <C>"*=" { return S_MUL; }
1155 : <C>"/=" { return S_DIV; }
1156 : <C>"%=" { return S_MOD; }
1157 : <C>"->*" { return S_MEMPOINT; }
1158 : <C>".*" { return S_DOTPOINT; }
1159 : <C>{other} { return S_ANYTHING; }
1160 : <C>{exec_sql}{define}{space}* { BEGIN(def_ident); }
1161 : <C>{informix_special}{define}{space}* {
1162 : /* are we simulating Informix? */
1163 : if (INFORMIX_MODE)
1164 : {
1165 : BEGIN(def_ident);
1166 : }
1167 : else
1168 : {
1169 : yyless(1);
1170 : return S_ANYTHING;
1171 : }
1172 : }
1173 : <C>{exec_sql}{undef}{space}* { BEGIN(undef); }
1174 : <C>{informix_special}{undef}{space}* {
1175 : /* are we simulating Informix? */
1176 : if (INFORMIX_MODE)
1177 : {
1178 : BEGIN(undef);
1179 : }
1180 : else
1181 : {
1182 : yyless(1);
1183 : return S_ANYTHING;
1184 : }
1185 : }
1186 : <undef>{identifier}{space}*";" {
1187 : struct _defines *ptr, *ptr2 = NULL;
1188 : int i;
1189 :
1190 : /*
1191 : * Skip the ";" and trailing whitespace. Note that yytext
1192 : * contains at least one non-space character plus the ";"
1193 : */
1194 : for (i = strlen(yytext)-2;
1195 : i > 0 && ecpg_isspace(yytext[i]);
1196 : i--)
1197 : ;
1198 : yytext[i+1] = '\0';
1199 :
1200 :
1201 : for (ptr = defines; ptr != NULL; ptr2 = ptr, ptr = ptr->next)
1202 : {
1203 : if (strcmp(yytext, ptr->olddef) == 0)
1204 : {
1205 : if (ptr2 == NULL)
1206 : defines = ptr->next;
1207 : else
1208 : ptr2->next = ptr->next;
1209 : free(ptr->newdef);
1210 : free(ptr->olddef);
1211 : free(ptr);
1212 : break;
1213 : }
1214 : }
1215 :
1216 : BEGIN(C);
1217 : }
1218 : <undef>{other}|\n {
1219 : mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command");
1220 : yyterminate();
1221 : }
1222 : <C>{exec_sql}{include}{space}* { BEGIN(incl); }
1223 : <C>{informix_special}{include}{space}* {
1224 : /* are we simulating Informix? */
1225 : if (INFORMIX_MODE)
1226 : {
1227 : BEGIN(incl);
1228 : }
1229 : else
1230 : {
1231 : yyless(1);
1232 : return S_ANYTHING;
1233 : }
1234 : }
1235 : <C,xskip>{exec_sql}{ifdef}{space}* {
1236 : if (preproc_tos >= MAX_NESTED_IF-1)
1237 : mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1238 : preproc_tos++;
1239 : stacked_if_value[preproc_tos].active = false;
1240 : stacked_if_value[preproc_tos].saw_active = false;
1241 : stacked_if_value[preproc_tos].else_branch = false;
1242 : ifcond = true;
1243 : BEGIN(xcond);
1244 : }
1245 : <C,xskip>{informix_special}{ifdef}{space}* {
1246 : /* are we simulating Informix? */
1247 : if (INFORMIX_MODE)
1248 : {
1249 : if (preproc_tos >= MAX_NESTED_IF-1)
1250 : mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1251 : preproc_tos++;
1252 : stacked_if_value[preproc_tos].active = false;
1253 : stacked_if_value[preproc_tos].saw_active = false;
1254 : stacked_if_value[preproc_tos].else_branch = false;
1255 : ifcond = true;
1256 : BEGIN(xcond);
1257 : }
1258 : else
1259 : {
1260 : yyless(1);
1261 : return S_ANYTHING;
1262 : }
1263 : }
1264 : <C,xskip>{exec_sql}{ifndef}{space}* {
1265 : if (preproc_tos >= MAX_NESTED_IF-1)
1266 : mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1267 : preproc_tos++;
1268 : stacked_if_value[preproc_tos].active = false;
1269 : stacked_if_value[preproc_tos].saw_active = false;
1270 : stacked_if_value[preproc_tos].else_branch = false;
1271 : ifcond = false;
1272 : BEGIN(xcond);
1273 : }
1274 : <C,xskip>{informix_special}{ifndef}{space}* {
1275 : /* are we simulating Informix? */
1276 : if (INFORMIX_MODE)
1277 : {
1278 : if (preproc_tos >= MAX_NESTED_IF-1)
1279 : mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1280 : preproc_tos++;
1281 : stacked_if_value[preproc_tos].active = false;
1282 : stacked_if_value[preproc_tos].saw_active = false;
1283 : stacked_if_value[preproc_tos].else_branch = false;
1284 : ifcond = false;
1285 : BEGIN(xcond);
1286 : }
1287 : else
1288 : {
1289 : yyless(1);
1290 : return S_ANYTHING;
1291 : }
1292 : }
1293 : <C,xskip>{exec_sql}{elif}{space}* {
1294 : if (preproc_tos == 0)
1295 : mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1296 : if (stacked_if_value[preproc_tos].else_branch)
1297 : mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1298 : ifcond = true;
1299 : BEGIN(xcond);
1300 : }
1301 : <C,xskip>{informix_special}{elif}{space}* {
1302 : /* are we simulating Informix? */
1303 : if (INFORMIX_MODE)
1304 : {
1305 : if (preproc_tos == 0)
1306 : mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1307 : if (stacked_if_value[preproc_tos].else_branch)
1308 : mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1309 : ifcond = true;
1310 : BEGIN(xcond);
1311 : }
1312 : else
1313 : {
1314 : yyless(1);
1315 : return S_ANYTHING;
1316 : }
1317 : }
1318 :
1319 : <C,xskip>{exec_sql}{else}{space}*";" { /* only exec sql endif pops the stack, so take care of duplicated 'else' */
1320 : if (preproc_tos == 0)
1321 : mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1322 : else if (stacked_if_value[preproc_tos].else_branch)
1323 : mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
1324 : else
1325 : {
1326 : stacked_if_value[preproc_tos].else_branch = true;
1327 : stacked_if_value[preproc_tos].active =
1328 : (stacked_if_value[preproc_tos-1].active &&
1329 : !stacked_if_value[preproc_tos].saw_active);
1330 : stacked_if_value[preproc_tos].saw_active = true;
1331 :
1332 : if (stacked_if_value[preproc_tos].active)
1333 : BEGIN(C);
1334 : else
1335 : BEGIN(xskip);
1336 : }
1337 : }
1338 : <C,xskip>{informix_special}{else}{space}*";" {
1339 : /* are we simulating Informix? */
1340 : if (INFORMIX_MODE)
1341 : {
1342 : if (preproc_tos == 0)
1343 : mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1344 : else if (stacked_if_value[preproc_tos].else_branch)
1345 : mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
1346 : else
1347 : {
1348 : stacked_if_value[preproc_tos].else_branch = true;
1349 : stacked_if_value[preproc_tos].active =
1350 : (stacked_if_value[preproc_tos-1].active &&
1351 : !stacked_if_value[preproc_tos].saw_active);
1352 : stacked_if_value[preproc_tos].saw_active = true;
1353 :
1354 : if (stacked_if_value[preproc_tos].active)
1355 : BEGIN(C);
1356 : else
1357 : BEGIN(xskip);
1358 : }
1359 : }
1360 : else
1361 : {
1362 : yyless(1);
1363 : return S_ANYTHING;
1364 : }
1365 : }
1366 : <C,xskip>{exec_sql}{endif}{space}*";" {
1367 : if (preproc_tos == 0)
1368 : mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
1369 : else
1370 : preproc_tos--;
1371 :
1372 : if (stacked_if_value[preproc_tos].active)
1373 : BEGIN(C);
1374 : else
1375 : BEGIN(xskip);
1376 : }
1377 : <C,xskip>{informix_special}{endif}{space}*";" {
1378 : /* are we simulating Informix? */
1379 : if (INFORMIX_MODE)
1380 : {
1381 : if (preproc_tos == 0)
1382 : mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
1383 : else
1384 : preproc_tos--;
1385 :
1386 : if (stacked_if_value[preproc_tos].active)
1387 : BEGIN(C);
1388 : else
1389 : BEGIN(xskip);
1390 : }
1391 : else
1392 : {
1393 : yyless(1);
1394 : return S_ANYTHING;
1395 : }
1396 : }
1397 :
1398 : <xskip>{other} { /* ignore */ }
1399 :
1400 : <xcond>{identifier}{space}*";" {
1401 : {
1402 : struct _defines *defptr;
1403 : unsigned int i;
1404 : bool this_active;
1405 :
1406 : /*
1407 : * Skip the ";" and trailing whitespace. Note that yytext
1408 : * contains at least one non-space character plus the ";"
1409 : */
1410 : for (i = strlen(yytext)-2;
1411 : i > 0 && ecpg_isspace(yytext[i]);
1412 : i--)
1413 : ;
1414 : yytext[i+1] = '\0';
1415 :
1416 : for (defptr = defines;
1417 : defptr != NULL &&
1418 : strcmp(yytext, defptr->olddef) != 0;
1419 : defptr = defptr->next)
1420 : /* skip */ ;
1421 :
1422 : this_active = (defptr ? ifcond : !ifcond);
1423 : stacked_if_value[preproc_tos].active =
1424 : (stacked_if_value[preproc_tos-1].active &&
1425 : !stacked_if_value[preproc_tos].saw_active &&
1426 : this_active);
1427 : stacked_if_value[preproc_tos].saw_active |= this_active;
1428 : }
1429 :
1430 : if (stacked_if_value[preproc_tos].active)
1431 : BEGIN(C);
1432 : else
1433 : BEGIN(xskip);
1434 : }
1435 :
1436 : <xcond>{other}|\n {
1437 : mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command");
1438 : yyterminate();
1439 : }
1440 : <def_ident>{identifier} {
1441 : old = mm_strdup(yytext);
1442 : BEGIN(def);
1443 : startlit();
1444 : }
1445 : <def_ident>{other}|\n {
1446 : mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command");
1447 : yyterminate();
1448 : }
1449 : <def>{space}*";" {
1450 : struct _defines *ptr, *this;
1451 :
1452 : for (ptr = defines; ptr != NULL; ptr = ptr->next)
1453 : {
1454 : if (strcmp(old, ptr->olddef) == 0)
1455 : {
1456 : free(ptr->newdef);
1457 : ptr->newdef = mm_strdup(literalbuf);
1458 : }
1459 : }
1460 : if (ptr == NULL)
1461 : {
1462 : this = (struct _defines *) mm_alloc(sizeof(struct _defines));
1463 :
1464 : /* initial definition */
1465 : this->olddef = old;
1466 : this->newdef = mm_strdup(literalbuf);
1467 : this->next = defines;
1468 : this->used = NULL;
1469 : defines = this;
1470 : }
1471 :
1472 : BEGIN(C);
1473 : }
1474 : <def>[^;] { addlit(yytext, yyleng); }
1475 : <incl>\<[^\>]+\>{space}*";"? { parse_include(); }
1476 : <incl>{dquote}{xdinside}{dquote}{space}*";"? { parse_include(); }
1477 : <incl>[^;\<\>\"]+";" { parse_include(); }
1478 : <incl>{other}|\n {
1479 : mmfatal(PARSE_ERROR, "syntax error in EXEC SQL INCLUDE command");
1480 : yyterminate();
1481 : }
1482 :
1483 : <<EOF>> {
1484 : if (yy_buffer == NULL)
1485 : {
1486 : if (preproc_tos > 0)
1487 : {
1488 : preproc_tos = 0;
1489 : mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1490 : }
1491 : yyterminate();
1492 : }
1493 : else
1494 : {
1495 : struct _yy_buffer *yb = yy_buffer;
1496 : int i;
1497 : struct _defines *ptr;
1498 :
1499 : for (ptr = defines; ptr; ptr = ptr->next)
1500 : if (ptr->used == yy_buffer)
1501 : {
1502 : ptr->used = NULL;
1503 : break;
1504 : }
1505 :
1506 : if (yyin != NULL)
1507 : fclose(yyin);
1508 :
1509 : yy_delete_buffer(YY_CURRENT_BUFFER);
1510 : yy_switch_to_buffer(yy_buffer->buffer);
1511 :
1512 : yylineno = yy_buffer->lineno;
1513 :
1514 : /* We have to output the filename only if we change files here */
1515 : i = strcmp(input_filename, yy_buffer->filename);
1516 :
1517 : free(input_filename);
1518 : input_filename = yy_buffer->filename;
1519 :
1520 : yy_buffer = yy_buffer->next;
1521 : free(yb);
1522 :
1523 : if (i != 0)
1524 : output_line_number();
1525 :
1526 : }
1527 : }
1528 :
1529 : <INITIAL>{other}|\n { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>", PACKAGE_BUGREPORT); }
1530 :
1531 : %%
1532 :
1533 : /* LCOV_EXCL_STOP */
1534 :
1535 : void
1536 : lex_init(void)
1537 128 : {
1538 : braces_open = 0;
1539 128 : parenths_open = 0;
1540 128 : current_function = NULL;
1541 128 :
1542 : yylineno = 1;
1543 128 :
1544 : /* initialize state for if/else/endif */
1545 : preproc_tos = 0;
1546 128 : stacked_if_value[preproc_tos].active = true;
1547 128 : stacked_if_value[preproc_tos].saw_active = true;
1548 128 : stacked_if_value[preproc_tos].else_branch = false;
1549 128 :
1550 : /* initialize literal buffer to a reasonable but expansible size */
1551 : if (literalbuf == NULL)
1552 128 : {
1553 : literalalloc = 1024;
1554 128 : literalbuf = (char *) mm_alloc(literalalloc);
1555 128 : }
1556 : startlit();
1557 128 :
1558 : BEGIN(C);
1559 128 : }
1560 128 :
1561 : static void
1562 : addlit(char *ytext, int yleng)
1563 47236 : {
1564 : /* enlarge buffer if needed */
1565 : if ((literallen+yleng) >= literalalloc)
1566 47236 : {
1567 : do
1568 : literalalloc *= 2;
1569 0 : while ((literallen+yleng) >= literalalloc);
1570 0 : literalbuf = (char *) realloc(literalbuf, literalalloc);
1571 0 : }
1572 : /* append new data, add trailing null */
1573 : memcpy(literalbuf+literallen, ytext, yleng);
1574 47236 : literallen += yleng;
1575 47236 : literalbuf[literallen] = '\0';
1576 47236 : }
1577 47236 :
1578 : static void
1579 : addlitchar(unsigned char ychar)
1580 0 : {
1581 : /* enlarge buffer if needed */
1582 : if ((literallen+1) >= literalalloc)
1583 0 : {
1584 : literalalloc *= 2;
1585 0 : literalbuf = (char *) realloc(literalbuf, literalalloc);
1586 0 : }
1587 : /* append new data, add trailing null */
1588 : literalbuf[literallen] = ychar;
1589 0 : literallen += 1;
1590 0 : literalbuf[literallen] = '\0';
1591 0 : }
1592 0 :
1593 : /*
1594 : * Process {decinteger}, {hexinteger}, etc. Note this will also do the right
1595 : * thing with {numeric}, ie digits and a decimal point.
1596 : */
1597 : static int
1598 : process_integer_literal(const char *token, YYSTYPE *lval, int base)
1599 2358 : {
1600 : int val;
1601 : char *endptr;
1602 :
1603 : errno = 0;
1604 2358 : val = strtoint(base == 10 ? token : token + 2, &endptr, base);
1605 2358 : if (*endptr != '\0' || errno == ERANGE)
1606 2358 : {
1607 : /* integer too large (or contains decimal pt), treat it as a float */
1608 : lval->str = mm_strdup(token);
1609 12 : return FCONST;
1610 12 : }
1611 : lval->ival = val;
1612 2346 : return ICONST;
1613 2346 : }
1614 :
1615 : static void
1616 : parse_include(void)
1617 172 : {
1618 : /* got the include file name */
1619 : struct _yy_buffer *yb;
1620 : struct _include_path *ip;
1621 : char inc_file[MAXPGPATH];
1622 : unsigned int i;
1623 :
1624 : yb = mm_alloc(sizeof(struct _yy_buffer));
1625 172 :
1626 : yb->buffer = YY_CURRENT_BUFFER;
1627 172 : yb->lineno = yylineno;
1628 172 : yb->filename = input_filename;
1629 172 : yb->next = yy_buffer;
1630 172 :
1631 : yy_buffer = yb;
1632 172 :
1633 : /*
1634 : * skip the ";" if there is one and trailing whitespace. Note that
1635 : * yytext contains at least one non-space character plus the ";"
1636 : */
1637 : for (i = strlen(yytext)-2;
1638 174 : i > 0 && ecpg_isspace(yytext[i]);
1639 174 : i--)
1640 2 : ;
1641 :
1642 : if (yytext[i] == ';')
1643 172 : i--;
1644 0 :
1645 : yytext[i+1] = '\0';
1646 172 :
1647 : yyin = NULL;
1648 172 :
1649 : /* If file name is enclosed in '"' remove these and look only in '.' */
1650 : /* Informix does look into all include paths though, except filename starts with '/' */
1651 : if (yytext[0] == '"' && yytext[i] == '"' &&
1652 172 : ((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/'))
1653 0 : {
1654 : yytext[i] = '\0';
1655 0 : memmove(yytext, yytext+1, strlen(yytext));
1656 0 :
1657 : strlcpy(inc_file, yytext, sizeof(inc_file));
1658 0 : yyin = fopen(inc_file, "r");
1659 0 : if (!yyin)
1660 0 : {
1661 : if (strlen(inc_file) <= 2 || strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
1662 0 : {
1663 : strcat(inc_file, ".h");
1664 0 : yyin = fopen(inc_file, "r");
1665 0 : }
1666 : }
1667 :
1668 : }
1669 : else
1670 : {
1671 : if ((yytext[0] == '"' && yytext[i] == '"') || (yytext[0] == '<' && yytext[i] == '>'))
1672 172 : {
1673 : yytext[i] = '\0';
1674 4 : memmove(yytext, yytext+1, strlen(yytext));
1675 4 : }
1676 :
1677 : for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
1678 468 : {
1679 : if (strlen(ip->path) + strlen(yytext) + 4 > MAXPGPATH)
1680 296 : {
1681 : fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path, yytext, yylineno);
1682 0 : continue;
1683 0 : }
1684 : snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
1685 296 : yyin = fopen(inc_file, "r");
1686 296 : if (!yyin)
1687 296 : {
1688 : if (strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
1689 272 : {
1690 : strcat(inc_file, ".h");
1691 266 : yyin = fopen(inc_file, "r");
1692 266 : }
1693 : }
1694 : /* if the command was "include_next" we have to disregard the first hit */
1695 : if (yyin && include_next)
1696 296 : {
1697 : fclose (yyin);
1698 0 : yyin = NULL;
1699 0 : include_next = false;
1700 0 : }
1701 : }
1702 : }
1703 : if (!yyin)
1704 172 : mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno);
1705 0 :
1706 : input_filename = mm_strdup(inc_file);
1707 172 : yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
1708 172 : yylineno = 1;
1709 172 : output_line_number();
1710 172 :
1711 : BEGIN(C);
1712 172 : }
1713 172 :
1714 : /*
1715 : * ecpg_isspace() --- return true if flex scanner considers char whitespace
1716 : */
1717 : static bool
1718 : ecpg_isspace(char ch)
1719 200 : {
1720 : if (ch == ' ' ||
1721 200 : ch == '\t' ||
1722 200 : ch == '\n' ||
1723 198 : ch == '\r' ||
1724 198 : ch == '\f' ||
1725 198 : ch == '\v')
1726 : return true;
1727 2 : return false;
1728 198 : }
1729 :
1730 : static bool isdefine(void)
1731 27836 : {
1732 : struct _defines *ptr;
1733 :
1734 : /* is it a define? */
1735 : for (ptr = defines; ptr; ptr = ptr->next)
1736 127112 : {
1737 : if (strcmp(yytext, ptr->olddef) == 0 && ptr->used == NULL)
1738 99436 : {
1739 : struct _yy_buffer *yb;
1740 :
1741 : yb = mm_alloc(sizeof(struct _yy_buffer));
1742 160 :
1743 : yb->buffer = YY_CURRENT_BUFFER;
1744 160 : yb->lineno = yylineno;
1745 160 : yb->filename = mm_strdup(input_filename);
1746 160 : yb->next = yy_buffer;
1747 160 :
1748 : ptr->used = yy_buffer = yb;
1749 160 :
1750 : yy_scan_string(ptr->newdef);
1751 160 : return true;
1752 160 : }
1753 : }
1754 :
1755 : return false;
1756 27676 : }
1757 :
1758 : static bool isinformixdefine(void)
1759 3464 : {
1760 : const char *new = NULL;
1761 3464 :
1762 : if (strcmp(yytext, "dec_t") == 0)
1763 3464 : new = "decimal";
1764 2 : else if (strcmp(yytext, "intrvl_t") == 0)
1765 3462 : new = "interval";
1766 0 : else if (strcmp(yytext, "dtime_t") == 0)
1767 3462 : new = "timestamp";
1768 0 :
1769 : if (new)
1770 3464 : {
1771 : struct _yy_buffer *yb;
1772 :
1773 : yb = mm_alloc(sizeof(struct _yy_buffer));
1774 2 :
1775 : yb->buffer = YY_CURRENT_BUFFER;
1776 2 : yb->lineno = yylineno;
1777 2 : yb->filename = mm_strdup(input_filename);
1778 2 : yb->next = yy_buffer;
1779 2 : yy_buffer = yb;
1780 2 :
1781 : yy_scan_string(new);
1782 2 : return true;
1783 2 : }
1784 :
1785 : return false;
1786 3462 : }
|