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