MayBMS_Mirror/postgresql-8.3.3/src/interfaces/ecpg/preproc/pgc.l

1391 lines
35 KiB
Plaintext

%{
/*-------------------------------------------------------------------------
*
* pgc.l
* lexical scanner for ecpg
*
* This is a modified version of src/backend/parser/scan.l
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.159.2.3 2008/02/17 18:42:23 meskes Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <ctype.h>
#include <sys/types.h>
#include <limits.h>
#include "extern.h"
extern YYSTYPE yylval;
static int xcdepth = 0; /* depth of nesting in slash-star comments */
static char *dolqstart; /* current $foo$ quote start string */
static YY_BUFFER_STATE scanbufhandle;
static char *scanbuf;
/*
* 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 */
#define startlit() (literalbuf[0] = '\0', literallen = 0)
static void addlit(char *ytext, int yleng);
static void addlitchar (unsigned char);
static void parse_include (void);
static bool ecpg_isspace(char ch);
static bool isdefine(void);
static bool isinformixdefine(void);
char *token_start;
int state_before;
struct _yy_buffer
{
YY_BUFFER_STATE buffer;
long lineno;
char *filename;
struct _yy_buffer *next;
} *yy_buffer = NULL;
static char *old;
#define MAX_NESTED_IF 128
static short preproc_tos;
static short ifcond;
static struct _if_value
{
short condition;
short else_branch;
} stacked_if_value[MAX_NESTED_IF];
%}
%option 8bit
%option never-interactive
%option nodefault
%option noyywrap
%option yylineno
%x C SQL incl def def_ident undef
/*
* 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 - thomas 1997-07-12
* <xd> delimited identifiers (double-quoted identifiers) - thomas 1997-10-27
* <xh> hexadecimal numeric string - thomas 1997-11-16
* <xq> standard quoted strings - thomas 1997-07-30
* <xqc> standard quoted strings in C - michael
* <xe> extended quoted strings (support backslash escape sequences)
* <xn> national character quoted strings
* <xdolq> $foo$ quoted strings
*/
%x xb
%x xc
%x xd
%x xdc
%x xh
%x xe
%x xn
%x xq
%x xqc
%x xdolq
%x xcond
%x xskip
/* 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}
/* C version of hex number */
xch 0[xX][0-9A-Fa-f]*
/* 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 [^"]+
/* 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}|[\[\]\+\-\*\%\/\(\)\>\.])*
typecast "::"
/*
* "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
*
* {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}*))
real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail1 ({integer}|{decimal})[Ee]
realfail2 ({integer}|{decimal})[Ee][-+]
param \${integer}
/*
* 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. SQL92-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.
*/
ccomment "//".*\n
space [ \t\n\r\f]
horiz_space [ \t\f]
newline [\n\r]
non_newline [^\n\r]
comment ("--"{non_newline}*)
whitespace ({space}+|{comment})
/*
* SQL92 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 '
quotestop {quote}{whitespace}*
quotecontinue {quote}{whitespace_with_newline}{quote}
quotefail {quote}{whitespace}*"-"
/* special characters for other dbms */
/* we have to react differently in compat mode */
informix_special [\$]
other .
/* 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]
undef [uU][nN][dD][eE][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}*
/* Take care of cpp lines, they may also be continuated */
cppline {space}*#(.*\\{space})*.*{newline}
/*
* 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. - thomas 1997-09-24
* Note that xcstart must appear before operator, as explained above!
* Also whitespace (comment) must appear before operator.
*/
%%
%{
/* code to execute during start of each call of yylex() */
token_start = NULL;
%}
<SQL>{whitespace} { /* ignore */ }
<C,SQL>{xcstart} {
token_start = yytext;
state_before = YYSTATE;
xcdepth = 0;
BEGIN(xc);
/* Put back any characters past slash-star; see above */
yyless(2);
fputs("/*", yyout);
}
<xc>{xcstart} {
xcdepth++;
/* Put back any characters past slash-star; see above */
yyless(2);
fputs("/*", yyout);
}
<xc>{xcstop} {
ECHO;
if (xcdepth <= 0)
{
BEGIN(state_before);
token_start = NULL;
}
else
xcdepth--;
}
<xc>{xcinside} { ECHO; }
<xc>{op_chars} { ECHO; }
<xc>\*+ { ECHO; }
<xc><<EOF>> { mmerror(PARSE_ERROR, ET_FATAL, "Unterminated /* comment"); }
<SQL>{xbstart} {
token_start = yytext;
BEGIN(xb);
startlit();
addlitchar('b');
}
<xb>{quotestop} |
<xb>{quotefail} {
yyless(1);
BEGIN(SQL);
if (literalbuf[strspn(literalbuf, "01") + 1] != '\0')
mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string input.");
yylval.str = mm_strdup(literalbuf);
return BCONST;
}
<xh>{xhinside} |
<xb>{xbinside} { addlit(yytext, yyleng); }
<xh>{quotecontinue} |
<xb>{quotecontinue} { /* ignore */ }
<xb><<EOF>> { mmerror(PARSE_ERROR, ET_FATAL, "Unterminated bit string"); }
<SQL>{xhstart} {
token_start = yytext;
BEGIN(xh);
startlit();
addlitchar('x');
}
<xh>{quotestop} |
<xh>{quotefail} {
yyless(1);
BEGIN(SQL);
yylval.str = mm_strdup(literalbuf);
return XCONST;
}
<xh><<EOF>> { mmerror(PARSE_ERROR, ET_FATAL, "Unterminated hexadecimal integer"); }
<SQL>{xnstart} {
/* National character.
* Transfer it as-is to the backend.
*/
token_start = yytext;
state_before = YYSTATE;
BEGIN(xn);
startlit();
}
<C>{xqstart} {
token_start = yytext;
state_before = YYSTATE;
BEGIN(xqc);
startlit();
}
<SQL>{xqstart} {
token_start = yytext;
state_before = YYSTATE;
BEGIN(xq);
startlit();
}
<SQL>{xestart} {
token_start = yytext;
state_before = YYSTATE;
BEGIN(xe);
startlit();
}
<xq,xqc>{quotestop} |
<xq,xqc>{quotefail} {
yyless(1);
BEGIN(state_before);
yylval.str = mm_strdup(literalbuf);
return SCONST;
}
<xe>{quotestop} |
<xe>{quotefail} {
yyless(1);
BEGIN(state_before);
yylval.str = mm_strdup(literalbuf);
return ECONST;
}
<xn>{quotestop} |
<xn>{quotefail} {
yyless(1);
BEGIN(state_before);
yylval.str = mm_strdup(literalbuf);
return NCONST;
}
<xq,xe,xn>{xqdouble} { addlitchar('\''); }
<xqc>{xqcquote} {
addlitchar('\\');
addlitchar('\'');
}
<xq,xqc,xn>{xqinside} { addlit(yytext, yyleng); }
<xe>{xeinside} { addlit(yytext, yyleng); }
<xe>{xeescape} { addlit(yytext, yyleng); }
<xe>{xeoctesc} { addlit(yytext, yyleng); }
<xe>{xehexesc} { addlit(yytext, yyleng); }
<xq,xqc,xe,xn>{quotecontinue} { /* ignore */ }
<xe>. {
/* This is only needed for \ just before EOF */
addlitchar(yytext[0]);
}
<xq,xqc,xe,xn><<EOF>> { mmerror(PARSE_ERROR, ET_FATAL, "Unterminated quoted string"); }
<SQL>{dolqfailed} {
/* throw back all but the initial "$" */
yyless(1);
/* and treat it as {other} */
return yytext[0];
}
<SQL>{dolqdelim} {
token_start = yytext;
dolqstart = mm_strdup(yytext);
BEGIN(xdolq);
startlit();
addlit(yytext, yyleng);
}
<xdolq>{dolqdelim} {
if (strcmp(yytext, dolqstart) == 0)
{
addlit(yytext, yyleng);
free(dolqstart);
BEGIN(SQL);
yylval.str = mm_strdup(literalbuf);
return DOLCONST;
}
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>{other} {
/* single quote or dollar sign */
addlitchar(yytext[0]);
}
<xdolq><<EOF>> { base_yyerror("unterminated dollar-quoted string"); }
<SQL>{xdstart} {
state_before = YYSTATE;
BEGIN(xd);
startlit();
}
<xd>{xdstop} {
BEGIN(state_before);
if (literallen == 0)
mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
/* The backend will truncate the idnetifier here. We do not as it does not change the result. */
yylval.str = mm_strdup(literalbuf);
return CSTRING;
}
<xdc>{xdstop} {
BEGIN(state_before);
yylval.str = mm_strdup(literalbuf);
return CSTRING;
}
<xd>{xddouble} { addlitchar('"'); }
<xd>{xdinside} { addlit(yytext, yyleng); }
<xd,xdc><<EOF>> { mmerror(PARSE_ERROR, ET_FATAL, "Unterminated quoted identifier"); }
<C,SQL>{xdstart} {
state_before = YYSTATE;
BEGIN(xdc);
startlit();
}
<xdc>{xdcinside} { addlit(yytext, yyleng); }
<SQL>{typecast} { return TYPECAST; }
<SQL>{informix_special} {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
unput(':');
}
else
return yytext[0];
}
<SQL>{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];
}
<SQL>{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.
*/
while (nchars > 1 &&
(yytext[nchars-1] == '+' ||
yytext[nchars-1] == '-'))
{
int ic;
for (ic = nchars-2; ic >= 0; ic--)
{
if (strchr("~!@#^&|`?%", yytext[ic]))
break;
}
if (ic >= 0)
break; /* found a char that makes it OK */
nchars--; /* else remove the +/-, and check again */
}
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];
}
/* Convert "!=" operator to "<>" for compatibility */
if (strcmp(yytext, "!=") == 0)
yylval.str = mm_strdup("<>");
else
yylval.str = mm_strdup(yytext);
return Op;
}
<SQL>{param} {
yylval.ival = atol(yytext+1);
return PARAM;
}
<C,SQL>{integer} {
long val;
char* endptr;
errno = 0;
val = strtol((char *)yytext, &endptr,10);
if (*endptr != '\0' || errno == ERANGE
#ifdef HAVE_LONG_INT_64
/* if long > 32 bits, check for overflow of int4 */
|| val != (long) ((int32) val)
#endif
)
{
errno = 0;
yylval.str = mm_strdup(yytext);
return FCONST;
}
yylval.ival = val;
return ICONST;
}
<SQL>{ip} {
yylval.str = mm_strdup(yytext);
return IP;
}
<C,SQL>{decimal} {
yylval.str = mm_strdup(yytext);
return FCONST;
}
<C,SQL>{real} {
yylval.str = mm_strdup(yytext);
return FCONST;
}
<SQL>{realfail1} {
yyless(yyleng-1);
yylval.str = mm_strdup(yytext);
return FCONST;
}
<SQL>{realfail2} {
yyless(yyleng-2);
yylval.str = mm_strdup(yytext);
return FCONST;
}
<SQL>:{identifier}((("->"|\.){identifier})|(\[{array}\]))* {
yylval.str = mm_strdup(yytext+1);
return(CVARIABLE);
}
<SQL>{identifier} {
const ScanKeyword *keyword;
if (!isdefine())
{
/* Is it an SQL/ECPG keyword? */
keyword = ScanKeywordLookup(yytext);
if (keyword != NULL)
return keyword->value;
/* Is it a C keyword? */
keyword = ScanCKeywordLookup(yytext);
if (keyword != NULL)
return keyword->value;
/*
* 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.
*/
yylval.str = mm_strdup(yytext);
return IDENT;
}
}
<SQL>{other} { return yytext[0]; }
<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;
yylval.ival = strtoul((char *)yytext,&endptr,16);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
yylval.str = mm_strdup(yytext);
return SCONST;
}
return ICONST;
}
<C>{cppinclude} {
if (system_includes)
{
BEGIN(incl);
}
else
{
yylval.str = mm_strdup(yytext);
return(CPP_LINE);
}
}
<C,SQL>{cppline} {
yylval.str = mm_strdup(yytext);
return(CPP_LINE);
}
<C>{identifier} {
const ScanKeyword *keyword;
/* Informix uses SQL defines only in SQL space */
/* however, some defines have to be taken care of for compatibility */
if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
{
keyword = ScanCKeywordLookup(yytext);
if (keyword != NULL)
return keyword->value;
else
{
yylval.str = mm_strdup(yytext);
return IDENT;
}
}
}
<C>":" { return(':'); }
<C>";" { return(';'); }
<C>"," { return(','); }
<C>"*" { return('*'); }
<C>"%" { return('%'); }
<C>"/" { return('/'); }
<C>"+" { return('+'); }
<C>"-" { return('-'); }
<C>"(" { return('('); }
<C>")" { 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->old) == 0)
{
if (ptr2 == NULL)
defines = ptr->next;
else
ptr2->next = ptr->next;
free(ptr->new);
free(ptr->old);
free(ptr);
break;
}
}
BEGIN(C);
}
<undef>{other}|\n {
mmerror(PARSE_ERROR, ET_FATAL, "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}* { ifcond = TRUE; BEGIN(xcond); }
<C,xskip>{informix_special}{ifdef}{space}* {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
ifcond = TRUE;
BEGIN(xcond);
}
else
{
yyless(1);
return (S_ANYTHING);
}
}
<C,xskip>{exec_sql}{ifndef}{space}* { ifcond = FALSE; BEGIN(xcond); }
<C,xskip>{informix_special}{ifndef}{space}* {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
ifcond = FALSE;
BEGIN(xcond);
}
else
{
yyless(1);
return (S_ANYTHING);
}
}
<C,xskip>{exec_sql}{elif}{space}* { /* pop stack */
if ( preproc_tos == 0 ) {
mmerror(PARSE_ERROR, ET_FATAL, "Missing matching 'EXEC SQL IFDEF / EXEC SQL IFNDEF'");
}
else if ( stacked_if_value[preproc_tos].else_branch )
mmerror(PARSE_ERROR, ET_FATAL, "Missing 'EXEC SQL ENDIF;'");
else
preproc_tos--;
ifcond = TRUE; BEGIN(xcond);
}
<C,xskip>{informix_special}{elif}{space}* {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
if (preproc_tos == 0)
mmerror(PARSE_ERROR, ET_FATAL, "Missing matching 'EXEC SQL IFDEF / EXEC SQL IFNDEF'");
else if (stacked_if_value[preproc_tos].else_branch)
mmerror(PARSE_ERROR, ET_FATAL, "Missing 'EXEC SQL ENDIF;'");
else
preproc_tos--;
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 (stacked_if_value[preproc_tos].else_branch)
mmerror(PARSE_ERROR, ET_FATAL, "Duplicated 'EXEC SQL ELSE;'");
else
{
stacked_if_value[preproc_tos].else_branch = TRUE;
stacked_if_value[preproc_tos].condition =
(stacked_if_value[preproc_tos-1].condition &&
!stacked_if_value[preproc_tos].condition);
if (stacked_if_value[preproc_tos].condition)
BEGIN(C);
else
BEGIN(xskip);
}
}
<C,xskip>{informix_special}{else}{space}* {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
if (stacked_if_value[preproc_tos].else_branch)
mmerror(PARSE_ERROR, ET_FATAL, "Duplicated 'EXEC SQL ELSE;'");
else
{
stacked_if_value[preproc_tos].else_branch = TRUE;
stacked_if_value[preproc_tos].condition =
(stacked_if_value[preproc_tos-1].condition &&
!stacked_if_value[preproc_tos].condition);
if (stacked_if_value[preproc_tos].condition)
BEGIN(C);
else
BEGIN(xskip);
}
}
else
{
yyless(1);
return (S_ANYTHING);
}
}
<C,xskip>{exec_sql}{endif}{space}*";" {
if (preproc_tos == 0)
mmerror(PARSE_ERROR, ET_FATAL, "Unmatched 'EXEC SQL ENDIF;'");
else
preproc_tos--;
if (stacked_if_value[preproc_tos].condition)
BEGIN(C);
else
BEGIN(xskip);
}
<C,xskip>{informix_special}{endif}{space}*";" {
/* are we simulating Informix? */
if (INFORMIX_MODE)
{
if (preproc_tos == 0)
mmerror(PARSE_ERROR, ET_FATAL, "Unmatched 'EXEC SQL ENDIF;'");
else
preproc_tos--;
if (stacked_if_value[preproc_tos].condition)
BEGIN(C);
else
BEGIN(xskip);
}
else
{
yyless(1);
return (S_ANYTHING);
}
}
<xskip>{other} { /* ignore */ }
<xcond>{identifier}{space}*";" {
if (preproc_tos >= MAX_NESTED_IF-1)
mmerror(PARSE_ERROR, ET_FATAL, "Too many nested 'EXEC SQL IFDEF' conditions");
else
{
struct _defines *defptr;
unsigned 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 (defptr = defines;
defptr != NULL && strcmp(yytext, defptr->old) != 0;
defptr = defptr->next);
preproc_tos++;
stacked_if_value[preproc_tos].else_branch = FALSE;
stacked_if_value[preproc_tos].condition =
(defptr ? ifcond : !ifcond) && stacked_if_value[preproc_tos-1].condition;
}
if (stacked_if_value[preproc_tos].condition)
BEGIN(C);
else
BEGIN(xskip);
}
<xcond>{other}|\n {
mmerror(PARSE_ERROR, ET_FATAL, "Missing identifier in 'EXEC SQL IFDEF' command");
yyterminate();
}
<def_ident>{identifier} {
old = mm_strdup(yytext);
BEGIN(def);
startlit();
}
<def_ident>{other}|\n {
mmerror(PARSE_ERROR, ET_FATAL, "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->old) == 0)
{
free(ptr->new);
ptr->new = mm_strdup(literalbuf);
}
}
if (ptr == NULL)
{
this = (struct _defines *) mm_alloc(sizeof(struct _defines));
/* initial definition */
this->old = old;
this->new = 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 {
mmerror(PARSE_ERROR, ET_FATAL, "Incorrect 'EXEC SQL INCLUDE' command");
yyterminate();
}
<<EOF>> {
if (yy_buffer == NULL)
{
if ( preproc_tos > 0 )
{
preproc_tos = 0;
mmerror(PARSE_ERROR, ET_FATAL, "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 { mmerror(PARSE_ERROR, ET_FATAL, "Internal error: unreachable state, please inform pgsql-bugs@postgresql.org"); }
%%
void
lex_init(void)
{
braces_open = 0;
preproc_tos = 0;
yylineno = 1;
ifcond = TRUE;
stacked_if_value[preproc_tos].condition = ifcond;
stacked_if_value[preproc_tos].else_branch = FALSE;
/* initialize literal buffer to a reasonable but expansible size */
if (literalbuf == NULL)
{
literalalloc = 1024;
literalbuf = (char *) malloc(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';
}
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));
strncpy(inc_file, yytext, sizeof(inc_file));
yyin = fopen(inc_file, "r");
if (!yyin)
{
if (strcmp(inc_file + strlen(inc_file) - 2, ".h"))
{
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) + 3 > MAXPGPATH)
{
fprintf(stderr, "Error: Path %s/%s is too long in line %lu, 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"))
{
strcat(inc_file, ".h");
yyin = fopen( inc_file, "r" );
}
}
}
}
if (!yyin)
mmerror(NO_INCLUDE_FILE, ET_FATAL, "Cannot open include file %s in line %lu\n", 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->old) == 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->new);
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;
}
/*
* Called before any actual parsing is done
*/
void
scanner_init(const char *str)
{
Size slen = strlen(str);
/*
* Might be left over after ereport()
*/
if (YY_CURRENT_BUFFER)
yy_delete_buffer(YY_CURRENT_BUFFER);
/*
* Make a scan buffer with special termination needed by flex.
*/
scanbuf = mm_alloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
/* initialize literal buffer to a reasonable but expansible size */
literalalloc = 128;
literalbuf = (char *) mm_alloc(literalalloc);
startlit();
BEGIN(INITIAL);
}
/*
* Called after parsing is done to clean up after scanner_init()
*/
void
scanner_finish(void)
{
yy_delete_buffer(scanbufhandle);
free(scanbuf);
}