Align ECPG lexer more closely with the core and psql lexers.

Make a bunch of basically-cosmetic changes to reduce the diffs between
the flex rules in scan.l, psqlscan.l, and pgc.l.  Reorder some code,
adjust a lot of whitespace, sync some comments, make use of flex start
condition scopes to do that.

There are a few non-cosmetic changes in the ECPG lexer:

* Bring over the decimalfail rule (and support function
process_integer_literal) so that ECPG will lex "1..10" into
the same tokens as the backend would.  I'm not sure this makes any
visible difference to users, but I'm not sure it doesn't, either.

* <xdc><<EOF>> gets its own rule so as to produce a more on-point
error message.

* Remove duplicate <SQL>{xdstart} rule.

John Naylor, with a few additional changes by me

Discussion: https://postgr.es/m/CAJVSVGWGqY9YBs2EwtRUkbNv=hXkN8yRPOoD1wxE6COgvvrz5g@mail.gmail.com
This commit is contained in:
Tom Lane 2018-11-13 12:57:52 -05:00
parent d20dceaf50
commit ec937d0805
3 changed files with 623 additions and 471 deletions

View File

@ -6,7 +6,8 @@
* *
* NOTE NOTE NOTE: * NOTE NOTE NOTE:
* *
* The rules in this file must be kept in sync with src/fe_utils/psqlscan.l! * The rules in this file must be kept in sync with src/fe_utils/psqlscan.l
* and src/interfaces/ecpg/preproc/pgc.l!
* *
* The rules are designed so that the scanner never has to backtrack, * The rules are designed so that the scanner never has to backtrack,
* in the sense that there is always a rule that can match the input * in the sense that there is always a rule that can match the input
@ -168,8 +169,8 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner);
%x xc %x xc
%x xd %x xd
%x xh %x xh
%x xe
%x xq %x xq
%x xe
%x xdolq %x xdolq
%x xui %x xui
%x xuiend %x xuiend
@ -192,7 +193,7 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner);
* XXX perhaps \f (formfeed) should be treated as a newline as well? * XXX perhaps \f (formfeed) should be treated as a newline as well?
* *
* XXX if you change the set of whitespace characters, fix scanner_isspace() * XXX if you change the set of whitespace characters, fix scanner_isspace()
* to agree, and see also the plpgsql lexer. * to agree.
*/ */
space [ \t\n\r\f] space [ \t\n\r\f]
@ -417,32 +418,36 @@ other .
yyless(2); yyless(2);
} }
<xc>{xcstart} { <xc>{
{xcstart} {
(yyextra->xcdepth)++; (yyextra->xcdepth)++;
/* Put back any characters past slash-star; see above */ /* Put back any characters past slash-star; see above */
yyless(2); yyless(2);
} }
<xc>{xcstop} { {xcstop} {
if (yyextra->xcdepth <= 0) if (yyextra->xcdepth <= 0)
BEGIN(INITIAL); BEGIN(INITIAL);
else else
(yyextra->xcdepth)--; (yyextra->xcdepth)--;
} }
<xc>{xcinside} { {xcinside} {
/* ignore */ /* ignore */
} }
<xc>{op_chars} { {op_chars} {
/* ignore */ /* ignore */
} }
<xc>\*+ { \*+ {
/* ignore */ /* ignore */
} }
<xc><<EOF>> { yyerror("unterminated /* comment"); } <<EOF>> {
yyerror("unterminated /* comment");
}
} /* <xc> */
{xbstart} { {xbstart} {
/* Binary bit type. /* Binary bit type.

View File

@ -23,6 +23,7 @@
* *
* See psqlscan_int.h for additional commentary. * See psqlscan_int.h for additional commentary.
* *
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
@ -39,6 +40,9 @@
} }
%{ %{
/* LCOV_EXCL_START */
#include "fe_utils/psqlscan_int.h" #include "fe_utils/psqlscan_int.h"
/* /*
@ -71,8 +75,6 @@ typedef int YYSTYPE;
extern int psql_yyget_column(yyscan_t yyscanner); extern int psql_yyget_column(yyscan_t yyscanner);
extern void psql_yyset_column(int column_no, yyscan_t yyscanner); extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
/* LCOV_EXCL_START */
%} %}
%option reentrant %option reentrant
@ -128,8 +130,8 @@ extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
%x xc %x xc
%x xd %x xd
%x xh %x xh
%x xe
%x xq %x xq
%x xe
%x xdolq %x xdolq
%x xui %x xui
%x xuiend %x xuiend
@ -151,7 +153,7 @@ extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
* XXX perhaps \f (formfeed) should be treated as a newline as well? * XXX perhaps \f (formfeed) should be treated as a newline as well?
* *
* XXX if you change the set of whitespace characters, fix scanner_isspace() * XXX if you change the set of whitespace characters, fix scanner_isspace()
* to agree, and see also the plpgsql lexer. * to agree.
*/ */
space [ \t\n\r\f] space [ \t\n\r\f]
@ -402,14 +404,15 @@ other .
ECHO; ECHO;
} }
<xc>{xcstart} { <xc>{
{xcstart} {
cur_state->xcdepth++; cur_state->xcdepth++;
/* Put back any characters past slash-star; see above */ /* Put back any characters past slash-star; see above */
yyless(2); yyless(2);
ECHO; ECHO;
} }
<xc>{xcstop} { {xcstop} {
if (cur_state->xcdepth <= 0) if (cur_state->xcdepth <= 0)
BEGIN(INITIAL); BEGIN(INITIAL);
else else
@ -417,17 +420,18 @@ other .
ECHO; ECHO;
} }
<xc>{xcinside} { {xcinside} {
ECHO; ECHO;
} }
<xc>{op_chars} { {op_chars} {
ECHO; ECHO;
} }
<xc>\*+ { \*+ {
ECHO; ECHO;
} }
} /* <xc> */
{xbstart} { {xbstart} {
BEGIN(xb); BEGIN(xb);

View File

@ -10,7 +10,6 @@
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
*
* IDENTIFICATION * IDENTIFICATION
* src/interfaces/ecpg/preproc/pgc.l * src/interfaces/ecpg/preproc/pgc.l
* *
@ -28,6 +27,9 @@
} }
%{ %{
/* LCOV_EXCL_START */
extern YYSTYPE base_yylval; extern YYSTYPE base_yylval;
static int xcdepth = 0; /* depth of nesting in slash-star comments */ static int xcdepth = 0; /* depth of nesting in slash-star comments */
@ -54,6 +56,7 @@ static bool include_next;
#define startlit() (literalbuf[0] = '\0', literallen = 0) #define startlit() (literalbuf[0] = '\0', literallen = 0)
static void addlit(char *ytext, int yleng); static void addlit(char *ytext, int yleng);
static void addlitchar(unsigned char); static void addlitchar(unsigned char);
static int process_integer_literal(const char *token, YYSTYPE *lval);
static void parse_include(void); static void parse_include(void);
static bool ecpg_isspace(char ch); static bool ecpg_isspace(char ch);
static bool isdefine(void); static bool isdefine(void);
@ -81,8 +84,6 @@ static struct _if_value
short else_branch; short else_branch;
} stacked_if_value[MAX_NESTED_IF]; } stacked_if_value[MAX_NESTED_IF];
/* LCOV_EXCL_START */
%} %}
%option 8bit %option 8bit
@ -91,11 +92,8 @@ static struct _if_value
%option noinput %option noinput
%option noyywrap %option noyywrap
%option warn %option warn
%option prefix="base_yy"
%option yylineno %option yylineno
%option prefix="base_yy"
%x C SQL incl def def_ident undef
/* /*
* OK, here is a short description of lex/flex rules behavior. * OK, here is a short description of lex/flex rules behavior.
@ -111,15 +109,21 @@ static struct _if_value
* <xb> bit string literal * <xb> bit string literal
* <xcc> extended C-style comments in C * <xcc> extended C-style comments in C
* <xcsql> extended C-style comments in SQL * <xcsql> extended C-style comments in SQL
* <xd> delimited identifiers (double-quoted identifiers) - thomas 1997-10-27 * <xd> delimited identifiers (double-quoted identifiers)
* <xh> hexadecimal numeric string - thomas 1997-11-16 * <xdc> double-quoted strings in C
* <xq> standard quoted strings - thomas 1997-07-30 * <xh> hexadecimal numeric string
* <xqc> standard quoted strings in C - michael
* <xe> extended quoted strings (support backslash escape sequences)
* <xn> national character quoted strings * <xn> national character quoted strings
* <xq> standard quoted strings
* <xe> extended quoted strings (support backslash escape sequences)
* <xqc> single-quoted strings in C
* <xdolq> $foo$ quoted strings * <xdolq> $foo$ quoted strings
* <xui> quoted identifier with Unicode escapes * <xui> quoted identifier with Unicode escapes
* <xus> quoted string 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
*
* 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 xb
@ -128,15 +132,60 @@ static struct _if_value
%x xd %x xd
%x xdc %x xdc
%x xh %x xh
%x xe
%x xn %x xn
%x xq %x xq
%x xe
%x xqc %x xqc
%x xdolq %x xdolq
%x xcond
%x xskip
%x xui %x xui
%x xus %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 '
quotestop {quote}{whitespace}*
quotecontinue {quote}{whitespace_with_newline}{quote}
quotefail {quote}{whitespace}*"-"
/* Bit string /* Bit string
*/ */
@ -158,9 +207,6 @@ xeoctesc [\\][0-7]{1,3}
xehexesc [\\]x[0-9A-Fa-f]{1,2} xehexesc [\\]x[0-9A-Fa-f]{1,2}
xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}) xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
/* C version of hex number */
xch 0[xX][0-9A-Fa-f]*
/* Extended quote /* Extended quote
* xqdouble implements embedded quote, '''' * xqdouble implements embedded quote, ''''
*/ */
@ -194,7 +240,9 @@ xddouble {dquote}{dquote}
xdinside [^"]+ xdinside [^"]+
/* Unicode escapes */ /* Unicode escapes */
/* (The ecpg scanner is not backup-free, so the fail rules in scan.l are not needed here, but could be added if desired.) */ /* (The ecpg scanner is not backup-free, so the fail rules in scan.l are
* not needed here, but could be added if desired.)
*/
uescape [uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']{quote} uescape [uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']{quote}
/* Quoted identifier with Unicode escapes */ /* Quoted identifier with Unicode escapes */
@ -211,6 +259,7 @@ xdcqdq \\\"
xdcother [^"] xdcother [^"]
xdcinside ({xdcqq}|{xdcqdq}|{xdcother}) xdcinside ({xdcqq}|{xdcqdq}|{xdcother})
/* C-style comments /* C-style comments
* *
* The "extended comment" syntax closely resembles allowable operator syntax. * The "extended comment" syntax closely resembles allowable operator syntax.
@ -278,68 +327,40 @@ operator {op_chars}+
* instead we pass it separately to parser. there it gets * instead we pass it separately to parser. there it gets
* coerced via doNegate() -- Leon aug 20 1999 * 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 * {realfail1} and {realfail2} are added to prevent the need for scanner
* backup when the {real} rule fails to match completely. * backup when the {real} rule fails to match completely.
*/ */
integer {digit}+ integer {digit}+
decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*)) decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
decimalfail {digit}+\.\.
real ({integer}|{decimal})[Ee][-+]?{digit}+ real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail1 ({integer}|{decimal})[Ee] realfail1 ({integer}|{decimal})[Ee]
realfail2 ({integer}|{decimal})[Ee][-+] realfail2 ({integer}|{decimal})[Ee][-+]
param \${integer} 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. 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.
*/
ccomment "//".*\n
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 '
quotestop {quote}{whitespace}*
quotecontinue {quote}{whitespace_with_newline}{quote}
quotefail {quote}{whitespace}*"-"
/* special characters for other dbms */ /* special characters for other dbms */
/* we have to react differently in compat mode */ /* we have to react differently in compat mode */
informix_special [\$] informix_special [\$]
other . 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 */ /* some stuff needed for ecpg */
exec [eE][xX][eE][cC] exec [eE][xX][eE][cC]
sql [sS][qQ][lL] sql [sS][qQ][lL]
@ -349,6 +370,11 @@ include_next [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
import [iI][mM][pP][oO][rR][tT] import [iI][mM][pP][oO][rR][tT]
undef [uU][nN][dD][eE][fF] undef [uU][nN][dD][eE][fF]
/* C version of hex number */
xch 0[xX][0-9A-Fa-f]*
ccomment "//".*\n
if [iI][fF] if [iI][fF]
ifdef [iI][fF][dD][eE][fF] ifdef [iI][fF][dD][eE][fF]
ifndef [iI][fF][nN][dD][eE][fF] ifndef [iI][fF][nN][dD][eE][fF]
@ -366,24 +392,12 @@ ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
cppinclude {space}*#{include}{space}* cppinclude {space}*#{include}{space}*
cppinclude_next {space}*#{include_next}{space}* cppinclude_next {space}*#{include_next}{space}*
/* take care of cpp lines, they may also be continuated */ /* take care of cpp lines, they may also be continued */
/* first a general line for all commands not starting with "i" */ /* first a general line for all commands not starting with "i" */
/* and then the other commands starting with "i", we have to add these /* and then the other commands starting with "i", we have to add these
* separately because the cppline production would match on "include" too */ * separately because the cppline production would match on "include" too
cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{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.
*/ */
cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
%% %%
@ -392,7 +406,21 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
token_start = NULL; token_start = NULL;
%} %}
<SQL>{whitespace} { /* ignore */ } <SQL>{
{whitespace} {
/* ignore */
}
{xcstart} {
token_start = yytext;
state_before = YYSTATE;
xcdepth = 0;
BEGIN(xcsql);
/* Put back any characters past slash-star; see above */
yyless(2);
fputs("/*", yyout);
}
} /* <SQL> */
<C>{xcstart} { <C>{xcstart} {
token_start = yytext; token_start = yytext;
@ -403,15 +431,6 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
yyless(2); yyless(2);
fputs("/*", yyout); fputs("/*", yyout);
} }
<SQL>{xcstart} {
token_start = yytext;
state_before = YYSTATE;
xcdepth = 0;
BEGIN(xcsql);
/* Put back any characters past slash-star; see above */
yyless(2);
fputs("/*", yyout);
}
<xcc>{xcstart} { ECHO; } <xcc>{xcstart} { ECHO; }
<xcsql>{xcstart} { <xcsql>{xcstart} {
xcdepth++; xcdepth++;
@ -437,18 +456,34 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
BEGIN(state_before); BEGIN(state_before);
token_start = NULL; token_start = NULL;
} }
<xcc,xcsql>{xcinside} { ECHO; }
<xcc,xcsql>{op_chars} { ECHO; }
<xcc,xcsql>\*+ { ECHO; }
<xcc,xcsql><<EOF>> { mmfatal(PARSE_ERROR, "unterminated /* comment"); } <xcc,xcsql>{
{xcinside} {
ECHO;
}
<SQL>{xbstart} { {op_chars} {
ECHO;
}
\*+ {
ECHO;
}
<<EOF>> {
mmfatal(PARSE_ERROR, "unterminated /* comment");
}
} /* <xcc,xcsql> */
<SQL>{
{xbstart} {
token_start = yytext; token_start = yytext;
BEGIN(xb); BEGIN(xb);
startlit(); startlit();
addlitchar('b'); addlitchar('b');
} }
} /* <SQL> */
<xb>{quotestop} | <xb>{quotestop} |
<xb>{quotefail} { <xb>{quotefail} {
yyless(1); yyless(1);
@ -458,11 +493,14 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
base_yylval.str = mm_strdup(literalbuf); base_yylval.str = mm_strdup(literalbuf);
return BCONST; return BCONST;
} }
<xh>{xhinside} | <xh>{xhinside} |
<xb>{xbinside} { addlit(yytext, yyleng); } <xb>{xbinside} {
addlit(yytext, yyleng);
}
<xh>{quotecontinue} | <xh>{quotecontinue} |
<xb>{quotecontinue} { /* ignore */ } <xb>{quotecontinue} {
/* ignore */
}
<xb><<EOF>> { mmfatal(PARSE_ERROR, "unterminated bit string literal"); } <xb><<EOF>> { mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
<SQL>{xhstart} { <SQL>{xhstart} {
@ -480,7 +518,16 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
} }
<xh><<EOF>> { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); } <xh><<EOF>> { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
<SQL>{xnstart} {
<C>{xqstart} {
token_start = yytext;
state_before = YYSTATE;
BEGIN(xqc);
startlit();
}
<SQL>{
{xnstart} {
/* National character. /* National character.
* Transfer it as-is to the backend. * Transfer it as-is to the backend.
*/ */
@ -489,31 +536,28 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
BEGIN(xn); BEGIN(xn);
startlit(); startlit();
} }
<C>{xqstart} {
token_start = yytext; {xqstart} {
state_before = YYSTATE;
BEGIN(xqc);
startlit();
}
<SQL>{xqstart} {
token_start = yytext; token_start = yytext;
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xq); BEGIN(xq);
startlit(); startlit();
} }
<SQL>{xestart} { {xestart} {
token_start = yytext; token_start = yytext;
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xe); BEGIN(xe);
startlit(); startlit();
} }
<SQL>{xusstart} { {xusstart} {
token_start = yytext; token_start = yytext;
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xus); BEGIN(xus);
startlit(); startlit();
addlit(yytext, yyleng); addlit(yytext, yyleng);
} }
} /* <SQL> */
<xq,xqc>{quotestop} | <xq,xqc>{quotestop} |
<xq,xqc>{quotefail} { <xq,xqc>{quotefail} {
yyless(1); yyless(1);
@ -547,24 +591,32 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
addlitchar('\''); addlitchar('\'');
} }
<xq,xqc,xn,xus>{xqinside} { addlit(yytext, yyleng); } <xq,xqc,xn,xus>{xqinside} { addlit(yytext, yyleng); }
<xe>{xeinside} { addlit(yytext, yyleng); } <xe>{xeinside} {
<xe>{xeunicode} { addlit(yytext, yyleng); } addlit(yytext, yyleng);
<xe>{xeescape} { addlit(yytext, yyleng); } }
<xe>{xeoctesc} { addlit(yytext, yyleng); } <xe>{xeunicode} {
<xe>{xehexesc} { addlit(yytext, yyleng); } addlit(yytext, yyleng);
<xq,xqc,xe,xn,xus>{quotecontinue} { /* ignore */ } }
<xe>{xeescape} {
addlit(yytext, yyleng);
}
<xe>{xeoctesc} {
addlit(yytext, yyleng);
}
<xe>{xehexesc} {
addlit(yytext, yyleng);
}
<xq,xqc,xe,xn,xus>{quotecontinue} {
/* ignore */
}
<xe>. { <xe>. {
/* This is only needed for \ just before EOF */ /* This is only needed for \ just before EOF */
addlitchar(yytext[0]); addlitchar(yytext[0]);
} }
<xq,xqc,xe,xn,xus><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); } <xq,xqc,xe,xn,xus><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
<SQL>{dolqfailed} {
/* throw back all but the initial "$" */ <SQL>{
yyless(1); {dolqdelim} {
/* and treat it as {other} */
return yytext[0];
}
<SQL>{dolqdelim} {
token_start = yytext; token_start = yytext;
if (dolqstart) if (dolqstart)
free(dolqstart); free(dolqstart);
@ -573,6 +625,14 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
startlit(); startlit();
addlit(yytext, yyleng); addlit(yytext, yyleng);
} }
{dolqfailed} {
/* throw back all but the initial "$" */
yyless(1);
/* and treat it as {other} */
return yytext[0];
}
} /* <SQL> */
<xdolq>{dolqdelim} { <xdolq>{dolqdelim} {
if (strcmp(yytext, dolqstart) == 0) if (strcmp(yytext, dolqstart) == 0)
{ {
@ -594,24 +654,32 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
yyless(yyleng - 1); yyless(yyleng - 1);
} }
} }
<xdolq>{dolqinside} { addlit(yytext, yyleng); } <xdolq>{dolqinside} {
<xdolq>{dolqfailed} { addlit(yytext, yyleng); } addlit(yytext, yyleng);
<xdolq>{other} { }
<xdolq>{dolqfailed} {
addlit(yytext, yyleng);
}
<xdolq>. {
/* single quote or dollar sign */ /* single quote or dollar sign */
addlitchar(yytext[0]); addlitchar(yytext[0]);
} }
<xdolq><<EOF>> { base_yyerror("unterminated dollar-quoted string"); } <xdolq><<EOF>> { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
<SQL>{xdstart} {
<SQL>{
{xdstart} {
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xd); BEGIN(xd);
startlit(); startlit();
} }
<SQL>{xuistart} { {xuistart} {
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xui); BEGIN(xui);
startlit(); startlit();
addlit(yytext, yyleng); addlit(yytext, yyleng);
} }
} /* <SQL> */
<xd>{xdstop} { <xd>{xdstop} {
BEGIN(state_before); BEGIN(state_before);
if (literallen == 0) if (literallen == 0)
@ -634,24 +702,59 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
base_yylval.str = mm_strdup(literalbuf); base_yylval.str = mm_strdup(literalbuf);
return UIDENT; return UIDENT;
} }
<xd,xui>{xddouble} { addlitchar('"'); } <xd,xui>{xddouble} {
<xd,xui>{xdinside} { addlit(yytext, yyleng); } addlitchar('"');
<xd,xdc,xui><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); } }
<C,SQL>{xdstart} { <xd,xui>{xdinside} {
addlit(yytext, yyleng);
}
<xd,xui><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
<C>{xdstart} {
state_before = YYSTATE; state_before = YYSTATE;
BEGIN(xdc); BEGIN(xdc);
startlit(); startlit();
} }
<xdc>{xdcinside} { addlit(yytext, yyleng); } <xdc>{xdcinside} {
<SQL>{typecast} { return TYPECAST; } addlit(yytext, yyleng);
<SQL>{dot_dot} { return DOT_DOT; } }
<SQL>{colon_equals} { return COLON_EQUALS; } <xdc><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
<SQL>{equals_greater} { return EQUALS_GREATER; }
<SQL>{less_equals} { return LESS_EQUALS; } <SQL>{
<SQL>{greater_equals} { return GREATER_EQUALS; } {typecast} {
<SQL>{less_greater} { return NOT_EQUALS; } return TYPECAST;
<SQL>{not_equals} { return NOT_EQUALS; } }
<SQL>{informix_special} {
{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? */ /* are we simulating Informix? */
if (INFORMIX_MODE) if (INFORMIX_MODE)
{ {
@ -660,7 +763,9 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
else else
return yytext[0]; return yytext[0];
} }
<SQL>{self} { /*
{self} {
/*
* We may find a ';' inside a structure * We may find a ';' inside a structure
* definition in a TYPE or VAR statement. * definition in a TYPE or VAR statement.
* This is not an EOL marker. * This is not an EOL marker.
@ -669,7 +774,8 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
BEGIN(C); BEGIN(C);
return yytext[0]; return yytext[0];
} }
<SQL>{operator} {
{operator} {
/* /*
* Check for embedded slash-star or dash-dash; those * Check for embedded slash-star or dash-dash; those
* are comment starts, so operator must stop there. * are comment starts, so operator must stop there.
@ -765,52 +871,61 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
base_yylval.str = mm_strdup(yytext); base_yylval.str = mm_strdup(yytext);
return Op; return Op;
} }
<SQL>{param} {
{param} {
base_yylval.ival = atol(yytext+1); base_yylval.ival = atol(yytext+1);
return PARAM; return PARAM;
} }
<C,SQL>{integer} {
int val;
char* endptr;
errno = 0; {ip} {
val = strtoint(yytext, &endptr, 10);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
base_yylval.str = mm_strdup(yytext);
return FCONST;
}
base_yylval.ival = val;
return ICONST;
}
<SQL>{ip} {
base_yylval.str = mm_strdup(yytext); base_yylval.str = mm_strdup(yytext);
return IP; return IP;
} }
<C,SQL>{decimal} { } /* <SQL> */
<C,SQL>{
{integer} {
return process_integer_literal(yytext, &base_yylval);
}
{decimal} {
base_yylval.str = mm_strdup(yytext); base_yylval.str = mm_strdup(yytext);
return FCONST; return FCONST;
} }
<C,SQL>{real} { {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); base_yylval.str = mm_strdup(yytext);
return FCONST; return FCONST;
} }
<SQL>{realfail1} { {realfail1} {
/*
* throw back the [Ee], and treat as {decimal}. Note
* that it is possible the input is actually {integer},
* but since this case will almost certainly lead to a
* syntax error anyway, we don't bother to distinguish.
*/
yyless(yyleng - 1); yyless(yyleng - 1);
base_yylval.str = mm_strdup(yytext); base_yylval.str = mm_strdup(yytext);
return FCONST; return FCONST;
} }
<SQL>{realfail2} { {realfail2} {
/* throw back the [Ee][+-], and proceed as above */
yyless(yyleng - 2); yyless(yyleng - 2);
base_yylval.str = mm_strdup(yytext); base_yylval.str = mm_strdup(yytext);
return FCONST; return FCONST;
} }
<SQL>:{identifier}((("->"|\.){identifier})|(\[{array}\]))* { } /* <C,SQL> */
<SQL>{
:{identifier}((("->"|\.){identifier})|(\[{array}\]))* {
base_yylval.str = mm_strdup(yytext+1); base_yylval.str = mm_strdup(yytext+1);
return CVARIABLE; return CVARIABLE;
} }
<SQL>{identifier} {
{identifier} {
const ScanKeyword *keyword; const ScanKeyword *keyword;
if (!isdefine()) if (!isdefine())
@ -837,7 +952,16 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
return IDENT; return IDENT;
} }
} }
<SQL>{other} { return yytext[0]; }
{other} {
return yytext[0];
}
} /* <SQL> */
/*
* Begin ECPG-specific rules
*/
<C>{exec_sql} { BEGIN(SQL); return SQL_START; } <C>{exec_sql} { BEGIN(SQL); return SQL_START; }
<C>{informix_special} { <C>{informix_special} {
/* are we simulating Informix? */ /* are we simulating Informix? */
@ -1288,6 +1412,7 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
} }
} }
<INITIAL>{other}|\n { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <pgsql-bugs@postgresql.org>"); } <INITIAL>{other}|\n { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <pgsql-bugs@postgresql.org>"); }
%% %%
@ -1350,6 +1475,24 @@ addlitchar(unsigned char ychar)
literalbuf[literallen] = '\0'; literalbuf[literallen] = '\0';
} }
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, treat it as a float */
lval->str = mm_strdup(token);
return FCONST;
}
lval->ival = val;
return ICONST;
}
static void static void
parse_include(void) parse_include(void)
{ {