Age Owner Branch data TLA 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-2024, 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)
9565 scrappy@hub.org 1537 :CBC 66 : {
1538 : : braces_open = 0;
5192 meskes@postgresql.or 1539 : 66 : parenths_open = 0;
1540 : 66 : current_function = NULL;
8881 bruce@momjian.us 1541 : 66 :
1542 : : yylineno = 1;
1350 tgl@sss.pgh.pa.us 1543 : 66 :
1544 : : /* initialize state for if/else/endif */
1545 : : preproc_tos = 0;
1546 : 66 : stacked_if_value[preproc_tos].active = true;
1547 : 66 : stacked_if_value[preproc_tos].saw_active = true;
2433 peter_e@gmx.net 1548 : 66 : stacked_if_value[preproc_tos].else_branch = false;
8941 tgl@sss.pgh.pa.us 1549 : 66 :
1550 : : /* initialize literal buffer to a reasonable but expansible size */
1551 : : if (literalbuf == NULL)
1552 [ + - ]: 66 : {
1553 : : literalalloc = 1024;
1979 1554 : 66 : literalbuf = (char *) mm_alloc(literalalloc);
8941 1555 : 66 : }
1556 : : startlit();
1557 : 66 :
1558 : : BEGIN(C);
9565 scrappy@hub.org 1559 : 66 : }
1560 : 66 :
1561 : : static void
1562 : : addlit(char *ytext, int yleng)
8941 tgl@sss.pgh.pa.us 1563 : 23859 : {
1564 : : /* enlarge buffer if needed */
1565 : : if ((literallen+yleng) >= literalalloc)
1566 [ - + ]: 23859 : {
1567 : : do
1568 : : literalalloc *= 2;
8066 bruce@momjian.us 1569 :UBC 0 : while ((literallen+yleng) >= literalalloc);
8941 tgl@sss.pgh.pa.us 1570 [ # # ]: 0 : literalbuf = (char *) realloc(literalbuf, literalalloc);
1571 : 0 : }
1572 : : /* append new data, add trailing null */
1573 : : memcpy(literalbuf+literallen, ytext, yleng);
8941 tgl@sss.pgh.pa.us 1574 :CBC 23859 : literallen += yleng;
8255 1575 : 23859 : literalbuf[literallen] = '\0';
8941 1576 : 23859 : }
1577 : 23859 :
1578 : : static void
1579 : : addlitchar(unsigned char ychar)
8881 bruce@momjian.us 1580 :UBC 0 : {
1581 : : /* enlarge buffer if needed */
1582 : : if ((literallen+1) >= literalalloc)
3808 peter_e@gmx.net 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;
8001 meskes@postgresql.or 1589 : 0 : literallen += 1;
1590 : 0 : literalbuf[literallen] = '\0';
1591 : 0 : }
7730 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)
1979 tgl@sss.pgh.pa.us 1599 :CBC 1190 : {
1600 : : int val;
1601 : : char *endptr;
1602 : :
1603 : : errno = 0;
487 peter@eisentraut.org 1604 : 1190 : val = strtoint(base == 10 ? token : token + 2, &endptr, base);
1979 tgl@sss.pgh.pa.us 1605 [ + + ]: 1190 : if (*endptr != '\0' || errno == ERANGE)
1606 [ + - + + ]: 1190 : {
1607 : : /* integer too large (or contains decimal pt), treat it as a float */
1608 : : lval->str = mm_strdup(token);
1609 : 6 : return FCONST;
1610 : 6 : }
1611 : : lval->ival = val;
1612 : 1184 : return ICONST;
1613 : 1184 : }
1614 : :
1615 : : static void
1616 : : parse_include(void)
7654 meskes@postgresql.or 1617 : 88 : {
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 : 88 :
1626 : : yb->buffer = YY_CURRENT_BUFFER;
4548 peter_e@gmx.net 1627 [ + - ]: 88 : yb->lineno = yylineno;
1628 : 88 : yb->filename = input_filename;
1629 : 88 : yb->next = yy_buffer;
7654 meskes@postgresql.or 1630 : 88 :
1631 : : yy_buffer = yb;
1632 : 88 :
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;
6414 tgl@sss.pgh.pa.us 1638 : 88 : i > 0 && ecpg_isspace(yytext[i]);
6647 bruce@momjian.us 1639 [ + - + + ]: 89 : i--)
1640 : 1 : ;
1641 : :
1642 : : if (yytext[i] == ';')
7654 meskes@postgresql.or 1643 [ - + ]: 88 : i--;
7654 meskes@postgresql.or 1644 :UBC 0 :
1645 : : yytext[i+1] = '\0';
4891 peter_e@gmx.net 1646 :CBC 88 :
1647 : : yyin = NULL;
7654 meskes@postgresql.or 1648 : 88 :
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] == '"' &&
4548 peter_e@gmx.net 1652 [ - + - - ]: 88 : ((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/'))
7654 meskes@postgresql.or 1653 [ # # # # :UBC 0 : {
# # ]
1654 : : yytext[i] = '\0';
1655 : 0 : memmove(yytext, yytext+1, strlen(yytext));
4891 peter_e@gmx.net 1656 : 0 :
1657 : : strlcpy(inc_file, yytext, sizeof(inc_file));
7654 meskes@postgresql.or 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] == '>'))
7654 meskes@postgresql.or 1672 [ - + - - :CBC 88 : {
+ + + - ]
1673 : : yytext[i] = '\0';
1674 : 2 : memmove(yytext, yytext+1, strlen(yytext));
1675 : 2 : }
1676 : :
1677 : : for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
4548 peter_e@gmx.net 1678 [ + + + - ]: 201 : {
1679 : : if (strlen(ip->path) + strlen(yytext) + 4 > MAXPGPATH)
7654 meskes@postgresql.or 1680 [ - + ]: 113 : {
1681 : : fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path, yytext, yylineno);
7654 meskes@postgresql.or 1682 :UBC 0 : continue;
1683 : 0 : }
1684 : : snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
7654 meskes@postgresql.or 1685 :CBC 113 : yyin = fopen(inc_file, "r");
1686 : 113 : if (!yyin)
1687 [ + + ]: 113 : {
1688 : : if (strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
1689 [ + + ]: 101 : {
1690 : : strcat(inc_file, ".h");
702 peter@eisentraut.org 1691 : 92 : yyin = fopen(inc_file, "r");
7654 meskes@postgresql.or 1692 : 92 : }
1693 : : }
1694 : : /* if the command was "include_next" we have to disregard the first hit */
1695 : : if (yyin && include_next)
5138 1696 [ + + - + ]: 113 : {
1697 : : fclose (yyin);
5138 meskes@postgresql.or 1698 :UBC 0 : yyin = NULL;
1699 : 0 : include_next = false;
1700 : 0 : }
1701 : : }
1702 : : }
1703 : : if (!yyin)
3806 peter_e@gmx.net 1704 [ - + ]:CBC 88 : mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno);
7654 meskes@postgresql.or 1705 :UBC 0 :
1706 : : input_filename = mm_strdup(inc_file);
702 peter@eisentraut.org 1707 :CBC 88 : yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
7654 meskes@postgresql.or 1708 : 88 : yylineno = 1;
1709 : 88 : output_line_number();
1710 : 88 :
1711 : : BEGIN(C);
1712 : 88 : }
6766 1713 : 88 :
1714 : : /*
1715 : : * ecpg_isspace() --- return true if flex scanner considers char whitespace
1716 : : */
1717 : : static bool
1718 : : ecpg_isspace(char ch)
6414 tgl@sss.pgh.pa.us 1719 : 102 : {
1720 : : if (ch == ' ' ||
1721 [ + - + - ]: 102 : ch == '\t' ||
1722 [ + + ]: 102 : ch == '\n' ||
1723 [ + - ]: 101 : ch == '\r' ||
283 michael@paquier.xyz 1724 [ + - ]:GNC 101 : ch == '\f' ||
1725 [ - + ]: 101 : ch == '\v')
1726 : : return true;
6414 tgl@sss.pgh.pa.us 1727 :CBC 1 : return false;
1728 : 101 : }
1729 : :
1730 : : static bool isdefine(void)
6080 meskes@postgresql.or 1731 : 14044 : {
1732 : : struct _defines *ptr;
1733 : :
1734 : : /* is it a define? */
1735 : : for (ptr = defines; ptr; ptr = ptr->next)
1736 [ + + ]: 63933 : {
1737 : : if (strcmp(yytext, ptr->olddef) == 0 && ptr->used == NULL)
1738 [ + + + - ]: 49970 : {
1739 : : struct _yy_buffer *yb;
1740 : :
1741 : : yb = mm_alloc(sizeof(struct _yy_buffer));
1742 : 81 :
1743 : : yb->buffer = YY_CURRENT_BUFFER;
1744 [ + - ]: 81 : yb->lineno = yylineno;
1745 : 81 : yb->filename = mm_strdup(input_filename);
1746 : 81 : yb->next = yy_buffer;
1747 : 81 :
1748 : : ptr->used = yy_buffer = yb;
1749 : 81 :
1750 : : yy_scan_string(ptr->newdef);
1751 : 81 : return true;
1752 : 81 : }
1753 : : }
1754 : :
1755 : : return false;
1756 : 13963 : }
1757 : :
1758 : : static bool isinformixdefine(void)
5901 1759 : 1759 : {
1760 : : const char *new = NULL;
1761 : 1759 :
1762 : : if (strcmp(yytext, "dec_t") == 0)
1763 [ + + ]: 1759 : new = "decimal";
1764 : 1 : else if (strcmp(yytext, "intrvl_t") == 0)
4548 peter_e@gmx.net 1765 [ - + ]: 1758 : new = "interval";
5901 meskes@postgresql.or 1766 :UBC 0 : else if (strcmp(yytext, "dtime_t") == 0)
4548 peter_e@gmx.net 1767 [ - + ]:CBC 1758 : new = "timestamp";
5901 meskes@postgresql.or 1768 :UBC 0 :
1769 : : if (new)
5901 meskes@postgresql.or 1770 [ + + ]:CBC 1759 : {
1771 : : struct _yy_buffer *yb;
1772 : :
1773 : : yb = mm_alloc(sizeof(struct _yy_buffer));
1774 : 1 :
1775 : : yb->buffer = YY_CURRENT_BUFFER;
1776 [ + - ]: 1 : yb->lineno = yylineno;
1777 : 1 : yb->filename = mm_strdup(input_filename);
1778 : 1 : yb->next = yy_buffer;
1779 : 1 : yy_buffer = yb;
1780 : 1 :
1781 : : yy_scan_string(new);
1782 : 1 : return true;
1783 : 1 : }
1784 : :
1785 : : return false;
1786 : 1758 : }
|