| %top{ |
| /*------------------------------------------------------------------------- |
| * |
| * pgc.l |
| * lexical scanner for ecpg |
| * |
| * This is a modified version of src/backend/parser/scan.l |
| * |
| * The ecpg scanner is not backup-free, so the fail rules are |
| * only here to simplify syncing this file with scan.l. |
| * |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/interfaces/ecpg/preproc/pgc.l |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres_fe.h" |
| |
| #include <ctype.h> |
| #include <limits.h> |
| |
| #include "common/string.h" |
| |
| #include "preproc_extern.h" |
| #include "preproc.h" |
| } |
| |
| %{ |
| |
| /* LCOV_EXCL_START */ |
| |
| extern YYSTYPE base_yylval; |
| |
| static int xcdepth = 0; /* depth of nesting in slash-star comments */ |
| static char *dolqstart = NULL; /* current $foo$ quote start string */ |
| |
| /* |
| * literalbuf is used to accumulate literal values when multiple rules |
| * are needed to parse a single literal. Call startlit to reset buffer |
| * to empty, addlit to add text. Note that the buffer is permanently |
| * malloc'd to the largest size needed so far in the current run. |
| */ |
| static char *literalbuf = NULL; /* expandable buffer */ |
| static int literallen; /* actual current length */ |
| static int literalalloc; /* current allocated buffer size */ |
| |
| /* Used for detecting global state together with braces_open */ |
| static int parenths_open; |
| |
| /* Used to tell parse_include() whether the command was #include or #include_next */ |
| static bool include_next; |
| |
| #define startlit() (literalbuf[0] = '\0', literallen = 0) |
| static void addlit(char *ytext, int yleng); |
| static void addlitchar(unsigned char); |
| static int process_integer_literal(const char *token, YYSTYPE *lval); |
| static void parse_include(void); |
| static bool ecpg_isspace(char ch); |
| static bool isdefine(void); |
| static bool isinformixdefine(void); |
| |
| char *token_start; |
| |
| /* vars to keep track of start conditions when scanning literals */ |
| static int state_before_str_start; |
| static int state_before_str_stop; |
| |
| struct _yy_buffer |
| { |
| YY_BUFFER_STATE buffer; |
| long lineno; |
| char *filename; |
| struct _yy_buffer *next; |
| } *yy_buffer = NULL; |
| |
| static char *old; |
| |
| /* |
| * Vars for handling ifdef/elif/endif constructs. preproc_tos is the current |
| * nesting depth of such constructs, and stacked_if_value[preproc_tos] is the |
| * state for the innermost level. (For convenience, stacked_if_value[0] is |
| * initialized as though we are in the active branch of some outermost IF.) |
| * The active field is true if the current branch is active (being expanded). |
| * The saw_active field is true if we have found any successful branch, |
| * so that all subsequent branches of this level should be skipped. |
| * The else_branch field is true if we've found an 'else' (so that another |
| * 'else' or 'elif' at this level is an error.) |
| * For IFs nested within an inactive branch, all branches always have active |
| * set to false, but saw_active and else_branch are maintained normally. |
| * ifcond is valid only while evaluating an if-condition; it's true if we |
| * are doing ifdef, false if ifndef. |
| */ |
| #define MAX_NESTED_IF 128 |
| static short preproc_tos; |
| static bool ifcond; |
| static struct _if_value |
| { |
| bool active; |
| bool saw_active; |
| bool else_branch; |
| } stacked_if_value[MAX_NESTED_IF]; |
| |
| %} |
| |
| %option 8bit |
| %option never-interactive |
| %option nodefault |
| %option noinput |
| %option noyywrap |
| %option warn |
| %option yylineno |
| %option prefix="base_yy" |
| |
| /* |
| * OK, here is a short description of lex/flex rules behavior. |
| * The longest pattern which matches an input string is always chosen. |
| * For equal-length patterns, the first occurring in the rules list is chosen. |
| * INITIAL is the starting state, to which all non-conditional rules apply. |
| * Exclusive states change parsing rules while the state is active. When in |
| * an exclusive state, only those rules defined for that state apply. |
| * |
| * We use exclusive states for quoted strings, extended comments, |
| * and to eliminate parsing troubles for numeric strings. |
| * Exclusive states: |
| * <xb> bit string literal |
| * <xc> extended C-style comments |
| * <xd> delimited identifiers (double-quoted identifiers) |
| * <xdc> double-quoted strings in C |
| * <xh> hexadecimal numeric string |
| * <xn> national character quoted strings |
| * <xq> standard quoted strings |
| * <xqs> quote stop (detect continued strings) |
| * <xe> extended quoted strings (support backslash escape sequences) |
| * <xqc> single-quoted strings in C |
| * <xdolq> $foo$ quoted strings |
| * <xui> quoted identifier with Unicode escapes |
| * <xus> quoted string with Unicode escapes |
| * <xcond> condition of an EXEC SQL IFDEF construct |
| * <xskip> skipping the inactive part of an EXEC SQL IFDEF construct |
| * |
| * Note: we intentionally don't mimic the backend's <xeu> state; we have |
| * no need to distinguish it from <xe> state. |
| * |
| * Remember to add an <<EOF>> case whenever you add a new exclusive state! |
| * The default one is probably not the right thing. |
| */ |
| |
| %x xb |
| %x xc |
| %x xd |
| %x xdc |
| %x xh |
| %x xn |
| %x xq |
| %x xqs |
| %x xe |
| %x xqc |
| %x xdolq |
| %x xui |
| %x xus |
| %x xcond |
| %x xskip |
| |
| /* Additional exclusive states that are specific to ECPG */ |
| %x C SQL incl def def_ident undef |
| |
| /* |
| * In order to make the world safe for Windows and Mac clients as well as |
| * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n |
| * sequence will be seen as two successive newlines, but that doesn't cause |
| * any problems. SQL-style comments, which start with -- and extend to the |
| * next newline, are treated as equivalent to a single whitespace character. |
| * |
| * NOTE a fine point: if there is no newline following --, we will absorb |
| * everything to the end of the input as a comment. This is correct. Older |
| * versions of Postgres failed to recognize -- as a comment if the input |
| * did not end with a newline. |
| * |
| * XXX perhaps \f (formfeed) should be treated as a newline as well? |
| * |
| * XXX if you change the set of whitespace characters, fix ecpg_isspace() |
| * to agree. |
| */ |
| |
| space [ \t\n\r\f] |
| horiz_space [ \t\f] |
| newline [\n\r] |
| non_newline [^\n\r] |
| |
| comment ("--"{non_newline}*) |
| |
| whitespace ({space}+|{comment}) |
| |
| /* |
| * SQL requires at least one newline in the whitespace separating |
| * string literals that are to be concatenated. Silly, but who are we |
| * to argue? Note that {whitespace_with_newline} should not have * after |
| * it, whereas {whitespace} should generally have a * after it... |
| */ |
| |
| horiz_whitespace ({horiz_space}|{comment}) |
| whitespace_with_newline ({horiz_whitespace}*{newline}{whitespace}*) |
| |
| quote ' |
| /* If we see {quote} then {quotecontinue}, the quoted string continues */ |
| quotecontinue {whitespace_with_newline}{quote} |
| |
| /* |
| * {quotecontinuefail} is needed to avoid lexer backup when we fail to match |
| * {quotecontinue}. It might seem that this could just be {whitespace}*, |
| * but if there's a dash after {whitespace_with_newline}, it must be consumed |
| * to see if there's another dash --- which would start a {comment} and thus |
| * allow continuation of the {quotecontinue} token. |
| */ |
| quotecontinuefail {whitespace}*"-"? |
| |
| /* Bit string |
| */ |
| xbstart [bB]{quote} |
| xbinside [^']* |
| |
| /* Hexadecimal number */ |
| xhstart [xX]{quote} |
| xhinside [^']* |
| |
| /* National character */ |
| xnstart [nN]{quote} |
| |
| /* Quoted string that allows backslash escapes */ |
| xestart [eE]{quote} |
| xeinside [^\\']+ |
| xeescape [\\][^0-7] |
| xeoctesc [\\][0-7]{1,3} |
| xehexesc [\\]x[0-9A-Fa-f]{1,2} |
| xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}) |
| |
| /* Extended quote |
| * xqdouble implements embedded quote, '''' |
| */ |
| xqstart {quote} |
| xqdouble {quote}{quote} |
| xqcquote [\\]{quote} |
| xqinside [^']+ |
| |
| /* $foo$ style quotes ("dollar quoting") |
| * The quoted string starts with $foo$ where "foo" is an optional string |
| * in the form of an identifier, except that it may not contain "$", |
| * and extends to the first occurrence of an identical string. |
| * There is *no* processing of the quoted text. |
| * |
| * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim} |
| * fails to match its trailing "$". |
| */ |
| dolq_start [A-Za-z\200-\377_] |
| dolq_cont [A-Za-z\200-\377_0-9] |
| dolqdelim \$({dolq_start}{dolq_cont}*)?\$ |
| dolqfailed \${dolq_start}{dolq_cont}* |
| dolqinside [^$]+ |
| |
| /* Double quote |
| * Allows embedded spaces and other special characters into identifiers. |
| */ |
| dquote \" |
| xdstart {dquote} |
| xdstop {dquote} |
| xddouble {dquote}{dquote} |
| xdinside [^"]+ |
| |
| /* Quoted identifier with Unicode escapes */ |
| xuistart [uU]&{dquote} |
| |
| /* Quoted string with Unicode escapes */ |
| xusstart [uU]&{quote} |
| |
| /* special stuff for C strings */ |
| xdcqq \\\\ |
| xdcqdq \\\" |
| xdcother [^"] |
| xdcinside ({xdcqq}|{xdcqdq}|{xdcother}) |
| |
| |
| /* C-style comments |
| * |
| * The "extended comment" syntax closely resembles allowable operator syntax. |
| * The tricky part here is to get lex to recognize a string starting with |
| * slash-star as a comment, when interpreting it as an operator would produce |
| * a longer match --- remember lex will prefer a longer match! Also, if we |
| * have something like plus-slash-star, lex will think this is a 3-character |
| * operator whereas we want to see it as a + operator and a comment start. |
| * The solution is two-fold: |
| * 1. append {op_chars}* to xcstart so that it matches as much text as |
| * {operator} would. Then the tie-breaker (first matching rule of same |
| * length) ensures xcstart wins. We put back the extra stuff with yyless() |
| * in case it contains a star-slash that should terminate the comment. |
| * 2. In the operator rule, check for slash-star within the operator, and |
| * if found throw it back with yyless(). This handles the plus-slash-star |
| * problem. |
| * Dash-dash comments have similar interactions with the operator rule. |
| */ |
| xcstart \/\*{op_chars}* |
| xcstop \*+\/ |
| xcinside [^*/]+ |
| |
| digit [0-9] |
| ident_start [A-Za-z\200-\377_] |
| ident_cont [A-Za-z\200-\377_0-9\$] |
| |
| identifier {ident_start}{ident_cont}* |
| |
| array ({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])* |
| |
| /* Assorted special-case operators and operator-like tokens */ |
| typecast "::" |
| dot_dot \.\. |
| colon_equals ":=" |
| |
| /* |
| * These operator-like tokens (unlike the above ones) also match the {operator} |
| * rule, which means that they might be overridden by a longer match if they |
| * are followed by a comment start or a + or - character. Accordingly, if you |
| * add to this list, you must also add corresponding code to the {operator} |
| * block to return the correct token in such cases. (This is not needed in |
| * psqlscan.l since the token value is ignored there.) |
| */ |
| equals_greater "=>" |
| less_equals "<=" |
| greater_equals ">=" |
| less_greater "<>" |
| not_equals "!=" |
| |
| /* |
| * "self" is the set of chars that should be returned as single-character |
| * tokens. "op_chars" is the set of chars that can make up "Op" tokens, |
| * which can be one or more characters long (but if a single-char token |
| * appears in the "self" set, it is not to be returned as an Op). Note |
| * that the sets overlap, but each has some chars that are not in the other. |
| * |
| * If you change either set, adjust the character lists appearing in the |
| * rule for "operator"! |
| */ |
| self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] |
| op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=] |
| operator {op_chars}+ |
| |
| /* we no longer allow unary minus in numbers. |
| * instead we pass it separately to parser. there it gets |
| * coerced via doNegate() -- Leon aug 20 1999 |
| * |
| * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10. |
| * |
| * {realfail1} and {realfail2} are added to prevent the need for scanner |
| * backup when the {real} rule fails to match completely. |
| */ |
| |
| integer {digit}+ |
| decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*)) |
| decimalfail {digit}+\.\. |
| real ({integer}|{decimal})[Ee][-+]?{digit}+ |
| realfail1 ({integer}|{decimal})[Ee] |
| realfail2 ({integer}|{decimal})[Ee][-+] |
| |
| param \${integer} |
| |
| /* special characters for other dbms */ |
| /* we have to react differently in compat mode */ |
| informix_special [\$] |
| |
| other . |
| |
| /* |
| * Dollar quoted strings are totally opaque, and no escaping is done on them. |
| * Other quoted strings must allow some special characters such as single-quote |
| * and newline. |
| * Embedded single-quotes are implemented both in the SQL standard |
| * style of two adjacent single quotes "''" and in the Postgres/Java style |
| * of escaped-quote "\'". |
| * Other embedded escaped characters are matched explicitly and the leading |
| * backslash is dropped from the string. |
| * Note that xcstart must appear before operator, as explained above! |
| * Also whitespace (comment) must appear before operator. |
| */ |
| |
| /* some stuff needed for ecpg */ |
| exec [eE][xX][eE][cC] |
| sql [sS][qQ][lL] |
| define [dD][eE][fF][iI][nN][eE] |
| include [iI][nN][cC][lL][uU][dD][eE] |
| include_next [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT] |
| import [iI][mM][pP][oO][rR][tT] |
| undef [uU][nN][dD][eE][fF] |
| |
| /* C version of hex number */ |
| xch 0[xX][0-9A-Fa-f]* |
| |
| ccomment "//".*\n |
| |
| if [iI][fF] |
| ifdef [iI][fF][dD][eE][fF] |
| ifndef [iI][fF][nN][dD][eE][fF] |
| else [eE][lL][sS][eE] |
| elif [eE][lL][iI][fF] |
| endif [eE][nN][dD][iI][fF] |
| |
| struct [sS][tT][rR][uU][cC][tT] |
| |
| exec_sql {exec}{space}*{sql}{space}* |
| ipdigit ({digit}|{digit}{digit}|{digit}{digit}{digit}) |
| ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit} |
| |
| /* we might want to parse all cpp include files */ |
| cppinclude {space}*#{include}{space}* |
| cppinclude_next {space}*#{include_next}{space}* |
| |
| /* take care of cpp lines, they may also be continued */ |
| /* first a general line for all commands not starting with "i" */ |
| /* and then the other commands starting with "i", we have to add these |
| * separately because the cppline production would match on "include" too |
| */ |
| cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline} |
| |
| %% |
| |
| %{ |
| /* code to execute during start of each call of yylex() */ |
| token_start = NULL; |
| %} |
| |
| <SQL>{ |
| {whitespace} { |
| /* ignore */ |
| } |
| } /* <SQL> */ |
| |
| <C,SQL>{ |
| {xcstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| xcdepth = 0; |
| BEGIN(xc); |
| /* Put back any characters past slash-star; see above */ |
| yyless(2); |
| fputs("/*", yyout); |
| } |
| } /* <C,SQL> */ |
| |
| <xc>{ |
| {xcstart} { |
| if (state_before_str_start == SQL) |
| { |
| xcdepth++; |
| /* Put back any characters past slash-star; see above */ |
| yyless(2); |
| fputs("/_*", yyout); |
| } |
| else if (state_before_str_start == C) |
| { |
| ECHO; |
| } |
| } |
| |
| {xcstop} { |
| if (state_before_str_start == SQL) |
| { |
| if (xcdepth <= 0) |
| { |
| ECHO; |
| BEGIN(SQL); |
| token_start = NULL; |
| } |
| else |
| { |
| xcdepth--; |
| fputs("*_/", yyout); |
| } |
| } |
| else if (state_before_str_start == C) |
| { |
| ECHO; |
| BEGIN(C); |
| token_start = NULL; |
| } |
| } |
| |
| {xcinside} { |
| ECHO; |
| } |
| |
| {op_chars} { |
| ECHO; |
| } |
| |
| \*+ { |
| ECHO; |
| } |
| |
| <<EOF>> { |
| mmfatal(PARSE_ERROR, "unterminated /* comment"); |
| } |
| } /* <xc> */ |
| |
| <SQL>{ |
| {xbstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xb); |
| startlit(); |
| } |
| } /* <SQL> */ |
| |
| <xh>{xhinside} | |
| <xb>{xbinside} { |
| addlit(yytext, yyleng); |
| } |
| <xb><<EOF>> { mmfatal(PARSE_ERROR, "unterminated bit string literal"); } |
| |
| <SQL>{xhstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xh); |
| startlit(); |
| } |
| <xh><<EOF>> { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); } |
| |
| <C>{xqstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xqc); |
| startlit(); |
| } |
| |
| <SQL>{ |
| {xnstart} { |
| /* National character. |
| * Transfer it as-is to the backend. |
| */ |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xn); |
| startlit(); |
| } |
| |
| {xqstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xq); |
| startlit(); |
| } |
| {xestart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xe); |
| startlit(); |
| } |
| {xusstart} { |
| token_start = yytext; |
| state_before_str_start = YYSTATE; |
| BEGIN(xus); |
| startlit(); |
| } |
| } /* <SQL> */ |
| |
| <xb,xh,xq,xqc,xe,xn,xus>{quote} { |
| /* |
| * When we are scanning a quoted string and see an end |
| * quote, we must look ahead for a possible continuation. |
| * If we don't see one, we know the end quote was in fact |
| * the end of the string. To reduce the lexer table size, |
| * we use a single "xqs" state to do the lookahead for all |
| * types of strings. |
| */ |
| state_before_str_stop = YYSTATE; |
| BEGIN(xqs); |
| } |
| <xqs>{quotecontinue} { |
| /* |
| * Found a quote continuation, so return to the in-quote |
| * state and continue scanning the literal. Nothing is |
| * added to the literal's contents. |
| */ |
| BEGIN(state_before_str_stop); |
| } |
| <xqs>{quotecontinuefail} | |
| <xqs>{other} | |
| <xqs><<EOF>> { |
| /* |
| * Failed to see a quote continuation. Throw back |
| * everything after the end quote, and handle the string |
| * according to the state we were in previously. |
| */ |
| yyless(0); |
| BEGIN(state_before_str_start); |
| |
| switch (state_before_str_stop) |
| { |
| case xb: |
| if (literalbuf[strspn(literalbuf, "01")] != '\0') |
| mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string literal"); |
| base_yylval.str = psprintf("b'%s'", literalbuf); |
| return BCONST; |
| case xh: |
| if (literalbuf[strspn(literalbuf, "0123456789abcdefABCDEF")] != '\0') |
| mmerror(PARSE_ERROR, ET_ERROR, "invalid hex string literal"); |
| base_yylval.str = psprintf("x'%s'", literalbuf); |
| return XCONST; |
| case xq: |
| /* fallthrough */ |
| case xqc: |
| base_yylval.str = psprintf("'%s'", literalbuf); |
| return SCONST; |
| case xe: |
| base_yylval.str = psprintf("E'%s'", literalbuf); |
| return SCONST; |
| case xn: |
| base_yylval.str = psprintf("N'%s'", literalbuf); |
| return SCONST; |
| case xus: |
| base_yylval.str = psprintf("U&'%s'", literalbuf); |
| return USCONST; |
| default: |
| mmfatal(PARSE_ERROR, "unhandled previous state in xqs\n"); |
| } |
| } |
| |
| <xq,xe,xn,xus>{xqdouble} { addlit(yytext, yyleng); } |
| <xqc>{xqcquote} { addlit(yytext, yyleng); } |
| <xq,xqc,xn,xus>{xqinside} { addlit(yytext, yyleng); } |
| <xe>{xeinside} { |
| addlit(yytext, yyleng); |
| } |
| <xe>{xeunicode} { |
| addlit(yytext, yyleng); |
| } |
| <xe>{xeescape} { |
| addlit(yytext, yyleng); |
| } |
| <xe>{xeoctesc} { |
| addlit(yytext, yyleng); |
| } |
| <xe>{xehexesc} { |
| addlit(yytext, yyleng); |
| } |
| <xe>. { |
| /* This is only needed for \ just before EOF */ |
| addlitchar(yytext[0]); |
| } |
| <xq,xqc,xe,xn,xus><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); } |
| |
| <SQL>{ |
| {dolqdelim} { |
| token_start = yytext; |
| if (dolqstart) |
| free(dolqstart); |
| dolqstart = mm_strdup(yytext); |
| BEGIN(xdolq); |
| startlit(); |
| addlit(yytext, yyleng); |
| } |
| {dolqfailed} { |
| /* throw back all but the initial "$" */ |
| yyless(1); |
| /* and treat it as {other} */ |
| return yytext[0]; |
| } |
| } /* <SQL> */ |
| |
| <xdolq>{dolqdelim} { |
| if (strcmp(yytext, dolqstart) == 0) |
| { |
| addlit(yytext, yyleng); |
| free(dolqstart); |
| dolqstart = NULL; |
| BEGIN(SQL); |
| base_yylval.str = mm_strdup(literalbuf); |
| return SCONST; |
| } |
| else |
| { |
| /* |
| * When we fail to match $...$ to dolqstart, transfer |
| * the $... part to the output, but put back the final |
| * $ for rescanning. Consider $delim$...$junk$delim$ |
| */ |
| addlit(yytext, yyleng - 1); |
| yyless(yyleng - 1); |
| } |
| } |
| <xdolq>{dolqinside} { |
| addlit(yytext, yyleng); |
| } |
| <xdolq>{dolqfailed} { |
| addlit(yytext, yyleng); |
| } |
| <xdolq>. { |
| /* single quote or dollar sign */ |
| addlitchar(yytext[0]); |
| } |
| <xdolq><<EOF>> { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); } |
| |
| <SQL>{ |
| {xdstart} { |
| state_before_str_start = YYSTATE; |
| BEGIN(xd); |
| startlit(); |
| } |
| {xuistart} { |
| state_before_str_start = YYSTATE; |
| BEGIN(xui); |
| startlit(); |
| } |
| } /* <SQL> */ |
| |
| <xd>{xdstop} { |
| BEGIN(state_before_str_start); |
| if (literallen == 0) |
| mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier"); |
| /* |
| * The server will truncate the identifier here. We do |
| * not, as (1) it does not change the result; (2) we don't |
| * know what NAMEDATALEN the server might use; (3) this |
| * code path is also taken for literal query strings in |
| * PREPARE and EXECUTE IMMEDIATE, which can certainly be |
| * longer than NAMEDATALEN. |
| */ |
| base_yylval.str = mm_strdup(literalbuf); |
| return CSTRING; |
| } |
| <xdc>{xdstop} { |
| BEGIN(state_before_str_start); |
| base_yylval.str = mm_strdup(literalbuf); |
| return CSTRING; |
| } |
| <xui>{dquote} { |
| BEGIN(state_before_str_start); |
| if (literallen == 2) /* "U&" */ |
| mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier"); |
| /* The backend will truncate the identifier here. We do not as it does not change the result. */ |
| base_yylval.str = psprintf("U&\"%s\"", literalbuf); |
| return UIDENT; |
| } |
| <xd,xui>{xddouble} { |
| addlit(yytext, yyleng); |
| } |
| <xd,xui>{xdinside} { |
| addlit(yytext, yyleng); |
| } |
| <xd,xui><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); } |
| <C>{xdstart} { |
| state_before_str_start = YYSTATE; |
| BEGIN(xdc); |
| startlit(); |
| } |
| <xdc>{xdcinside} { |
| addlit(yytext, yyleng); |
| } |
| <xdc><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); } |
| |
| <SQL>{ |
| {typecast} { |
| return TYPECAST; |
| } |
| |
| {dot_dot} { |
| return DOT_DOT; |
| } |
| |
| {colon_equals} { |
| return COLON_EQUALS; |
| } |
| |
| {equals_greater} { |
| return EQUALS_GREATER; |
| } |
| |
| {less_equals} { |
| return LESS_EQUALS; |
| } |
| |
| {greater_equals} { |
| return GREATER_EQUALS; |
| } |
| |
| {less_greater} { |
| /* We accept both "<>" and "!=" as meaning NOT_EQUALS */ |
| return NOT_EQUALS; |
| } |
| |
| {not_equals} { |
| /* We accept both "<>" and "!=" as meaning NOT_EQUALS */ |
| return NOT_EQUALS; |
| } |
| |
| {informix_special} { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| unput(':'); |
| } |
| else |
| return yytext[0]; |
| } |
| |
| {self} { |
| /* |
| * We may find a ';' inside a structure |
| * definition in a TYPE or VAR statement. |
| * This is not an EOL marker. |
| */ |
| if (yytext[0] == ';' && struct_level == 0) |
| BEGIN(C); |
| return yytext[0]; |
| } |
| |
| {operator} { |
| /* |
| * Check for embedded slash-star or dash-dash; those |
| * are comment starts, so operator must stop there. |
| * Note that slash-star or dash-dash at the first |
| * character will match a prior rule, not this one. |
| */ |
| int nchars = yyleng; |
| char *slashstar = strstr(yytext, "/*"); |
| char *dashdash = strstr(yytext, "--"); |
| |
| if (slashstar && dashdash) |
| { |
| /* if both appear, take the first one */ |
| if (slashstar > dashdash) |
| slashstar = dashdash; |
| } |
| else if (!slashstar) |
| slashstar = dashdash; |
| if (slashstar) |
| nchars = slashstar - yytext; |
| |
| /* |
| * For SQL compatibility, '+' and '-' cannot be the |
| * last char of a multi-char operator unless the operator |
| * contains chars that are not in SQL operators. |
| * The idea is to lex '=-' as two operators, but not |
| * to forbid operator names like '?-' that could not be |
| * sequences of SQL operators. |
| */ |
| if (nchars > 1 && |
| (yytext[nchars - 1] == '+' || |
| yytext[nchars - 1] == '-')) |
| { |
| int ic; |
| |
| for (ic = nchars - 2; ic >= 0; ic--) |
| { |
| char c = yytext[ic]; |
| if (c == '~' || c == '!' || c == '@' || |
| c == '#' || c == '^' || c == '&' || |
| c == '|' || c == '`' || c == '?' || |
| c == '%') |
| break; |
| } |
| if (ic < 0) |
| { |
| /* |
| * didn't find a qualifying character, so remove |
| * all trailing [+-] |
| */ |
| do { |
| nchars--; |
| } while (nchars > 1 && |
| (yytext[nchars - 1] == '+' || |
| yytext[nchars - 1] == '-')); |
| } |
| } |
| |
| if (nchars < yyleng) |
| { |
| /* Strip the unwanted chars from the token */ |
| yyless(nchars); |
| /* |
| * If what we have left is only one char, and it's |
| * one of the characters matching "self", then |
| * return it as a character token the same way |
| * that the "self" rule would have. |
| */ |
| if (nchars == 1 && |
| strchr(",()[].;:+-*/%^<>=", yytext[0])) |
| return yytext[0]; |
| /* |
| * Likewise, if what we have left is two chars, and |
| * those match the tokens ">=", "<=", "=>", "<>" or |
| * "!=", then we must return the appropriate token |
| * rather than the generic Op. |
| */ |
| if (nchars == 2) |
| { |
| if (yytext[0] == '=' && yytext[1] == '>') |
| return EQUALS_GREATER; |
| if (yytext[0] == '>' && yytext[1] == '=') |
| return GREATER_EQUALS; |
| if (yytext[0] == '<' && yytext[1] == '=') |
| return LESS_EQUALS; |
| if (yytext[0] == '<' && yytext[1] == '>') |
| return NOT_EQUALS; |
| if (yytext[0] == '!' && yytext[1] == '=') |
| return NOT_EQUALS; |
| } |
| } |
| |
| base_yylval.str = mm_strdup(yytext); |
| return Op; |
| } |
| |
| {param} { |
| base_yylval.ival = atol(yytext+1); |
| return PARAM; |
| } |
| |
| {ip} { |
| base_yylval.str = mm_strdup(yytext); |
| return IP; |
| } |
| } /* <SQL> */ |
| |
| <C,SQL>{ |
| {integer} { |
| return process_integer_literal(yytext, &base_yylval); |
| } |
| {decimal} { |
| base_yylval.str = mm_strdup(yytext); |
| return FCONST; |
| } |
| {decimalfail} { |
| /* throw back the .., and treat as integer */ |
| yyless(yyleng - 2); |
| return process_integer_literal(yytext, &base_yylval); |
| } |
| {real} { |
| base_yylval.str = mm_strdup(yytext); |
| return FCONST; |
| } |
| {realfail1} { |
| /* |
| * throw back the [Ee], and figure out whether what |
| * remains is an {integer} or {decimal}. |
| */ |
| yyless(yyleng - 1); |
| return process_integer_literal(yytext, &base_yylval); |
| } |
| {realfail2} { |
| /* throw back the [Ee][+-], and proceed as above */ |
| yyless(yyleng - 2); |
| return process_integer_literal(yytext, &base_yylval); |
| } |
| } /* <C,SQL> */ |
| |
| <SQL>{ |
| :{identifier}((("->"|\.){identifier})|(\[{array}\]))* { |
| base_yylval.str = mm_strdup(yytext+1); |
| return CVARIABLE; |
| } |
| |
| {identifier} { |
| if (!isdefine()) |
| { |
| int kwvalue; |
| |
| /* Is it an SQL/ECPG keyword? */ |
| kwvalue = ScanECPGKeywordLookup(yytext); |
| if (kwvalue >= 0) |
| return kwvalue; |
| |
| /* Is it a C keyword? */ |
| kwvalue = ScanCKeywordLookup(yytext); |
| if (kwvalue >= 0) |
| return kwvalue; |
| |
| /* |
| * None of the above. Return it as an identifier. |
| * |
| * The backend will attempt to truncate and case-fold |
| * the identifier, but I see no good reason for ecpg |
| * to do so; that's just another way that ecpg could get |
| * out of step with the backend. |
| */ |
| base_yylval.str = mm_strdup(yytext); |
| return IDENT; |
| } |
| } |
| |
| {other} { |
| return yytext[0]; |
| } |
| } /* <SQL> */ |
| |
| /* |
| * Begin ECPG-specific rules |
| */ |
| |
| <C>{exec_sql} { BEGIN(SQL); return SQL_START; } |
| <C>{informix_special} { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| BEGIN(SQL); |
| return SQL_START; |
| } |
| else |
| return S_ANYTHING; |
| } |
| <C>{ccomment} { ECHO; } |
| <C>{xch} { |
| char* endptr; |
| |
| errno = 0; |
| base_yylval.ival = strtoul((char *)yytext,&endptr,16); |
| if (*endptr != '\0' || errno == ERANGE) |
| { |
| errno = 0; |
| base_yylval.str = mm_strdup(yytext); |
| return SCONST; |
| } |
| return ICONST; |
| } |
| <C>{cppinclude} { |
| if (system_includes) |
| { |
| include_next = false; |
| BEGIN(incl); |
| } |
| else |
| { |
| base_yylval.str = mm_strdup(yytext); |
| return CPP_LINE; |
| } |
| } |
| <C>{cppinclude_next} { |
| if (system_includes) |
| { |
| include_next = true; |
| BEGIN(incl); |
| } |
| else |
| { |
| base_yylval.str = mm_strdup(yytext); |
| return CPP_LINE; |
| } |
| } |
| <C,SQL>{cppline} { |
| base_yylval.str = mm_strdup(yytext); |
| return CPP_LINE; |
| } |
| <C>{identifier} { |
| /* |
| * Try to detect a function name: |
| * look for identifiers at the global scope |
| * keep the last identifier before the first '(' and '{' |
| */ |
| if (braces_open == 0 && parenths_open == 0) |
| { |
| if (current_function) |
| free(current_function); |
| current_function = mm_strdup(yytext); |
| } |
| /* Informix uses SQL defines only in SQL space */ |
| /* however, some defines have to be taken care of for compatibility */ |
| if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine()) |
| { |
| int kwvalue; |
| |
| kwvalue = ScanCKeywordLookup(yytext); |
| if (kwvalue >= 0) |
| return kwvalue; |
| else |
| { |
| base_yylval.str = mm_strdup(yytext); |
| return IDENT; |
| } |
| } |
| } |
| <C>{xcstop} { mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments"); } |
| <C>":" { return ':'; } |
| <C>";" { return ';'; } |
| <C>"," { return ','; } |
| <C>"*" { return '*'; } |
| <C>"%" { return '%'; } |
| <C>"/" { return '/'; } |
| <C>"+" { return '+'; } |
| <C>"-" { return '-'; } |
| <C>"(" { parenths_open++; return '('; } |
| <C>")" { parenths_open--; return ')'; } |
| <C,xskip>{space} { ECHO; } |
| <C>\{ { return '{'; } |
| <C>\} { return '}'; } |
| <C>\[ { return '['; } |
| <C>\] { return ']'; } |
| <C>\= { return '='; } |
| <C>"->" { return S_MEMBER; } |
| <C>">>" { return S_RSHIFT; } |
| <C>"<<" { return S_LSHIFT; } |
| <C>"||" { return S_OR; } |
| <C>"&&" { return S_AND; } |
| <C>"++" { return S_INC; } |
| <C>"--" { return S_DEC; } |
| <C>"==" { return S_EQUAL; } |
| <C>"!=" { return S_NEQUAL; } |
| <C>"+=" { return S_ADD; } |
| <C>"-=" { return S_SUB; } |
| <C>"*=" { return S_MUL; } |
| <C>"/=" { return S_DIV; } |
| <C>"%=" { return S_MOD; } |
| <C>"->*" { return S_MEMPOINT; } |
| <C>".*" { return S_DOTPOINT; } |
| <C>{other} { return S_ANYTHING; } |
| <C>{exec_sql}{define}{space}* { BEGIN(def_ident); } |
| <C>{informix_special}{define}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| BEGIN(def_ident); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <C>{exec_sql}{undef}{space}* { BEGIN(undef); } |
| <C>{informix_special}{undef}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| BEGIN(undef); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <undef>{identifier}{space}*";" { |
| struct _defines *ptr, *ptr2 = NULL; |
| int i; |
| |
| /* |
| * Skip the ";" and trailing whitespace. Note that yytext |
| * contains at least one non-space character plus the ";" |
| */ |
| for (i = strlen(yytext)-2; |
| i > 0 && ecpg_isspace(yytext[i]); |
| i-- ) |
| ; |
| yytext[i+1] = '\0'; |
| |
| |
| for (ptr = defines; ptr != NULL; ptr2 = ptr, ptr = ptr->next) |
| { |
| if (strcmp(yytext, ptr->olddef) == 0) |
| { |
| if (ptr2 == NULL) |
| defines = ptr->next; |
| else |
| ptr2->next = ptr->next; |
| free(ptr->newdef); |
| free(ptr->olddef); |
| free(ptr); |
| break; |
| } |
| } |
| |
| BEGIN(C); |
| } |
| <undef>{other}|\n { |
| mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command"); |
| yyterminate(); |
| } |
| <C>{exec_sql}{include}{space}* { BEGIN(incl); } |
| <C>{informix_special}{include}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| BEGIN(incl); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <C,xskip>{exec_sql}{ifdef}{space}* { |
| if (preproc_tos >= MAX_NESTED_IF-1) |
| mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); |
| preproc_tos++; |
| stacked_if_value[preproc_tos].active = false; |
| stacked_if_value[preproc_tos].saw_active = false; |
| stacked_if_value[preproc_tos].else_branch = false; |
| ifcond = true; |
| BEGIN(xcond); |
| } |
| <C,xskip>{informix_special}{ifdef}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| if (preproc_tos >= MAX_NESTED_IF-1) |
| mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); |
| preproc_tos++; |
| stacked_if_value[preproc_tos].active = false; |
| stacked_if_value[preproc_tos].saw_active = false; |
| stacked_if_value[preproc_tos].else_branch = false; |
| ifcond = true; |
| BEGIN(xcond); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <C,xskip>{exec_sql}{ifndef}{space}* { |
| if (preproc_tos >= MAX_NESTED_IF-1) |
| mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); |
| preproc_tos++; |
| stacked_if_value[preproc_tos].active = false; |
| stacked_if_value[preproc_tos].saw_active = false; |
| stacked_if_value[preproc_tos].else_branch = false; |
| ifcond = false; |
| BEGIN(xcond); |
| } |
| <C,xskip>{informix_special}{ifndef}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| if (preproc_tos >= MAX_NESTED_IF-1) |
| mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); |
| preproc_tos++; |
| stacked_if_value[preproc_tos].active = false; |
| stacked_if_value[preproc_tos].saw_active = false; |
| stacked_if_value[preproc_tos].else_branch = false; |
| ifcond = false; |
| BEGIN(xcond); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <C,xskip>{exec_sql}{elif}{space}* { |
| if (preproc_tos == 0) |
| mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); |
| if (stacked_if_value[preproc_tos].else_branch) |
| mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\""); |
| ifcond = true; |
| BEGIN(xcond); |
| } |
| <C,xskip>{informix_special}{elif}{space}* { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| if (preproc_tos == 0) |
| mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); |
| if (stacked_if_value[preproc_tos].else_branch) |
| mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\""); |
| ifcond = true; |
| BEGIN(xcond); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| |
| <C,xskip>{exec_sql}{else}{space}*";" { /* only exec sql endif pops the stack, so take care of duplicated 'else' */ |
| if ( preproc_tos == 0 ) |
| mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); |
| else if (stacked_if_value[preproc_tos].else_branch) |
| mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE"); |
| else |
| { |
| stacked_if_value[preproc_tos].else_branch = true; |
| stacked_if_value[preproc_tos].active = |
| (stacked_if_value[preproc_tos-1].active && |
| !stacked_if_value[preproc_tos].saw_active); |
| stacked_if_value[preproc_tos].saw_active = true; |
| |
| if (stacked_if_value[preproc_tos].active) |
| BEGIN(C); |
| else |
| BEGIN(xskip); |
| } |
| } |
| <C,xskip>{informix_special}{else}{space}*";" { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| if ( preproc_tos == 0 ) |
| mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); |
| else if (stacked_if_value[preproc_tos].else_branch) |
| mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE"); |
| else |
| { |
| stacked_if_value[preproc_tos].else_branch = true; |
| stacked_if_value[preproc_tos].active = |
| (stacked_if_value[preproc_tos-1].active && |
| !stacked_if_value[preproc_tos].saw_active); |
| stacked_if_value[preproc_tos].saw_active = true; |
| |
| if (stacked_if_value[preproc_tos].active) |
| BEGIN(C); |
| else |
| BEGIN(xskip); |
| } |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| <C,xskip>{exec_sql}{endif}{space}*";" { |
| if (preproc_tos == 0) |
| mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF"); |
| else |
| preproc_tos--; |
| |
| if (stacked_if_value[preproc_tos].active) |
| BEGIN(C); |
| else |
| BEGIN(xskip); |
| } |
| <C,xskip>{informix_special}{endif}{space}*";" { |
| /* are we simulating Informix? */ |
| if (INFORMIX_MODE) |
| { |
| if (preproc_tos == 0) |
| mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF"); |
| else |
| preproc_tos--; |
| |
| if (stacked_if_value[preproc_tos].active) |
| BEGIN(C); |
| else |
| BEGIN(xskip); |
| } |
| else |
| { |
| yyless(1); |
| return S_ANYTHING; |
| } |
| } |
| |
| <xskip>{other} { /* ignore */ } |
| |
| <xcond>{identifier}{space}*";" { |
| { |
| struct _defines *defptr; |
| unsigned int i; |
| bool this_active; |
| |
| /* |
| * Skip the ";" and trailing whitespace. Note that yytext |
| * contains at least one non-space character plus the ";" |
| */ |
| for (i = strlen(yytext)-2; |
| i > 0 && ecpg_isspace(yytext[i]); |
| i-- ) |
| ; |
| yytext[i+1] = '\0'; |
| |
| for (defptr = defines; |
| defptr != NULL && |
| strcmp(yytext, defptr->olddef) != 0; |
| defptr = defptr->next) |
| /* skip */ ; |
| |
| this_active = (defptr ? ifcond : !ifcond); |
| stacked_if_value[preproc_tos].active = |
| (stacked_if_value[preproc_tos-1].active && |
| !stacked_if_value[preproc_tos].saw_active && |
| this_active); |
| stacked_if_value[preproc_tos].saw_active |= this_active; |
| } |
| |
| if (stacked_if_value[preproc_tos].active) |
| BEGIN(C); |
| else |
| BEGIN(xskip); |
| } |
| |
| <xcond>{other}|\n { |
| mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command"); |
| yyterminate(); |
| } |
| <def_ident>{identifier} { |
| old = mm_strdup(yytext); |
| BEGIN(def); |
| startlit(); |
| } |
| <def_ident>{other}|\n { |
| mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command"); |
| yyterminate(); |
| } |
| <def>{space}*";" { |
| struct _defines *ptr, *this; |
| |
| for (ptr = defines; ptr != NULL; ptr = ptr->next) |
| { |
| if (strcmp(old, ptr->olddef) == 0) |
| { |
| free(ptr->newdef); |
| ptr->newdef = mm_strdup(literalbuf); |
| } |
| } |
| if (ptr == NULL) |
| { |
| this = (struct _defines *) mm_alloc(sizeof(struct _defines)); |
| |
| /* initial definition */ |
| this->olddef = old; |
| this->newdef = mm_strdup(literalbuf); |
| this->next = defines; |
| this->used = NULL; |
| defines = this; |
| } |
| |
| BEGIN(C); |
| } |
| <def>[^;] { addlit(yytext, yyleng); } |
| <incl>\<[^\>]+\>{space}*";"? { parse_include(); } |
| <incl>{dquote}{xdinside}{dquote}{space}*";"? { parse_include(); } |
| <incl>[^;\<\>\"]+";" { parse_include(); } |
| <incl>{other}|\n { |
| mmfatal(PARSE_ERROR, "syntax error in EXEC SQL INCLUDE command"); |
| yyterminate(); |
| } |
| |
| <<EOF>> { |
| if (yy_buffer == NULL) |
| { |
| if ( preproc_tos > 0 ) |
| { |
| preproc_tos = 0; |
| mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\""); |
| } |
| yyterminate(); |
| } |
| else |
| { |
| struct _yy_buffer *yb = yy_buffer; |
| int i; |
| struct _defines *ptr; |
| |
| for (ptr = defines; ptr; ptr = ptr->next) |
| if (ptr->used == yy_buffer) |
| { |
| ptr->used = NULL; |
| break; |
| } |
| |
| if (yyin != NULL) |
| fclose(yyin); |
| |
| yy_delete_buffer( YY_CURRENT_BUFFER ); |
| yy_switch_to_buffer(yy_buffer->buffer); |
| |
| yylineno = yy_buffer->lineno; |
| |
| /* We have to output the filename only if we change files here */ |
| i = strcmp(input_filename, yy_buffer->filename); |
| |
| free(input_filename); |
| input_filename = yy_buffer->filename; |
| |
| yy_buffer = yy_buffer->next; |
| free(yb); |
| |
| if (i != 0) |
| output_line_number(); |
| |
| } |
| } |
| |
| <INITIAL>{other}|\n { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>", PACKAGE_BUGREPORT); } |
| |
| %% |
| |
| /* LCOV_EXCL_STOP */ |
| |
| void |
| lex_init(void) |
| { |
| braces_open = 0; |
| parenths_open = 0; |
| current_function = NULL; |
| |
| yylineno = 1; |
| |
| /* initialize state for if/else/endif */ |
| preproc_tos = 0; |
| stacked_if_value[preproc_tos].active = true; |
| stacked_if_value[preproc_tos].saw_active = true; |
| stacked_if_value[preproc_tos].else_branch = false; |
| |
| /* initialize literal buffer to a reasonable but expansible size */ |
| if (literalbuf == NULL) |
| { |
| literalalloc = 1024; |
| literalbuf = (char *) mm_alloc(literalalloc); |
| } |
| startlit(); |
| |
| BEGIN(C); |
| } |
| |
| static void |
| addlit(char *ytext, int yleng) |
| { |
| /* enlarge buffer if needed */ |
| if ((literallen+yleng) >= literalalloc) |
| { |
| do |
| literalalloc *= 2; |
| while ((literallen+yleng) >= literalalloc); |
| literalbuf = (char *) realloc(literalbuf, literalalloc); |
| } |
| /* append new data, add trailing null */ |
| memcpy(literalbuf+literallen, ytext, yleng); |
| literallen += yleng; |
| literalbuf[literallen] = '\0'; |
| } |
| |
| static void |
| addlitchar(unsigned char ychar) |
| { |
| /* enlarge buffer if needed */ |
| if ((literallen+1) >= literalalloc) |
| { |
| literalalloc *= 2; |
| literalbuf = (char *) realloc(literalbuf, literalalloc); |
| } |
| /* append new data, add trailing null */ |
| literalbuf[literallen] = ychar; |
| literallen += 1; |
| literalbuf[literallen] = '\0'; |
| } |
| |
| /* |
| * Process {integer}. Note this will also do the right thing with {decimal}, |
| * ie digits and a decimal point. |
| */ |
| static int |
| process_integer_literal(const char *token, YYSTYPE *lval) |
| { |
| int val; |
| char *endptr; |
| |
| errno = 0; |
| val = strtoint(token, &endptr, 10); |
| if (*endptr != '\0' || errno == ERANGE) |
| { |
| /* integer too large (or contains decimal pt), treat it as a float */ |
| lval->str = mm_strdup(token); |
| return FCONST; |
| } |
| lval->ival = val; |
| return ICONST; |
| } |
| |
| static void |
| parse_include(void) |
| { |
| /* got the include file name */ |
| struct _yy_buffer *yb; |
| struct _include_path *ip; |
| char inc_file[MAXPGPATH]; |
| unsigned int i; |
| |
| yb = mm_alloc(sizeof(struct _yy_buffer)); |
| |
| yb->buffer = YY_CURRENT_BUFFER; |
| yb->lineno = yylineno; |
| yb->filename = input_filename; |
| yb->next = yy_buffer; |
| |
| yy_buffer = yb; |
| |
| /* |
| * skip the ";" if there is one and trailing whitespace. Note that |
| * yytext contains at least one non-space character plus the ";" |
| */ |
| for (i = strlen(yytext)-2; |
| i > 0 && ecpg_isspace(yytext[i]); |
| i--) |
| ; |
| |
| if (yytext[i] == ';') |
| i--; |
| |
| yytext[i+1] = '\0'; |
| |
| yyin = NULL; |
| |
| /* If file name is enclosed in '"' remove these and look only in '.' */ |
| /* Informix does look into all include paths though, except filename starts with '/' */ |
| if (yytext[0] == '"' && yytext[i] == '"' && |
| ((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/')) |
| { |
| yytext[i] = '\0'; |
| memmove(yytext, yytext+1, strlen(yytext)); |
| |
| strlcpy(inc_file, yytext, sizeof(inc_file)); |
| yyin = fopen(inc_file, "r"); |
| if (!yyin) |
| { |
| if (strlen(inc_file) <= 2 || strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0) |
| { |
| strcat(inc_file, ".h"); |
| yyin = fopen(inc_file, "r"); |
| } |
| } |
| |
| } |
| else |
| { |
| if ((yytext[0] == '"' && yytext[i] == '"') || (yytext[0] == '<' && yytext[i] == '>')) |
| { |
| yytext[i] = '\0'; |
| memmove(yytext, yytext+1, strlen(yytext)); |
| } |
| |
| for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next) |
| { |
| if (strlen(ip->path) + strlen(yytext) + 4 > MAXPGPATH) |
| { |
| fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path, yytext, yylineno); |
| continue; |
| } |
| snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext); |
| yyin = fopen(inc_file, "r"); |
| if (!yyin) |
| { |
| if (strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0) |
| { |
| strcat(inc_file, ".h"); |
| yyin = fopen( inc_file, "r" ); |
| } |
| } |
| /* if the command was "include_next" we have to disregard the first hit */ |
| if (yyin && include_next) |
| { |
| fclose (yyin); |
| yyin = NULL; |
| include_next = false; |
| } |
| } |
| } |
| if (!yyin) |
| mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno); |
| |
| input_filename = mm_strdup(inc_file); |
| yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE )); |
| yylineno = 1; |
| output_line_number(); |
| |
| BEGIN(C); |
| } |
| |
| /* |
| * ecpg_isspace() --- return true if flex scanner considers char whitespace |
| */ |
| static bool |
| ecpg_isspace(char ch) |
| { |
| if (ch == ' ' || |
| ch == '\t' || |
| ch == '\n' || |
| ch == '\r' || |
| ch == '\f') |
| return true; |
| return false; |
| } |
| |
| static bool isdefine(void) |
| { |
| struct _defines *ptr; |
| |
| /* is it a define? */ |
| for (ptr = defines; ptr; ptr = ptr->next) |
| { |
| if (strcmp(yytext, ptr->olddef) == 0 && ptr->used == NULL) |
| { |
| struct _yy_buffer *yb; |
| |
| yb = mm_alloc(sizeof(struct _yy_buffer)); |
| |
| yb->buffer = YY_CURRENT_BUFFER; |
| yb->lineno = yylineno; |
| yb->filename = mm_strdup(input_filename); |
| yb->next = yy_buffer; |
| |
| ptr->used = yy_buffer = yb; |
| |
| yy_scan_string(ptr->newdef); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isinformixdefine(void) |
| { |
| const char *new = NULL; |
| |
| if (strcmp(yytext, "dec_t") == 0) |
| new = "decimal"; |
| else if (strcmp(yytext, "intrvl_t") == 0) |
| new = "interval"; |
| else if (strcmp(yytext, "dtime_t") == 0) |
| new = "timestamp"; |
| |
| if (new) |
| { |
| struct _yy_buffer *yb; |
| |
| yb = mm_alloc(sizeof(struct _yy_buffer)); |
| |
| yb->buffer = YY_CURRENT_BUFFER; |
| yb->lineno = yylineno; |
| yb->filename = mm_strdup(input_filename); |
| yb->next = yy_buffer; |
| yy_buffer = yb; |
| |
| yy_scan_string(new); |
| return true; |
| } |
| |
| return false; |
| } |