1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-08 07:21:33 +03:00

ecpg: invent a saner syntax for ecpg.addons entries.

Put the rule type at the start not the end, and put spaces
between the constitutent token names instead of smashing them
into an illegible mess.  This has no functional impact but
I think it makes the rules a great deal more readable.

Discussion: https://postgr.es/m/1185216.1724001216@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2024-10-14 16:13:48 -04:00
parent 143e3a1ab8
commit dbedc461b4
3 changed files with 107 additions and 98 deletions

View File

@ -40,19 +40,19 @@ continue to use the normal Bison notations.)
ecpg.addons contains entries that begin with a line like ecpg.addons contains entries that begin with a line like
ECPG: concattokens ruletype ECPG: ruletype tokenlist
and typically have one or more following lines that are the code and typically have one or more following lines that are the code
for a grammar action. Any line not starting with "ECPG:" is taken for a grammar action. Any line not starting with "ECPG:" is taken
to be part of the code block for the preceding "ECPG:" line. to be part of the code block for the preceding "ECPG:" line.
"concattokens" identifies which gram.y production this entry affects. "tokenlist" identifies which gram.y production this entry affects.
It is simply the target nonterminal and the tokens from the gram.y rule It is simply a list of the target nonterminal and the input tokens
concatenated together. For example, to modify the action for a gram.y from the gram.y rule. For example, to modify the action for a
rule like this: gram.y rule like this:
target: tokenA tokenB tokenC {...} target: tokenA tokenB tokenC {...}
"concattokens" would be "targettokenAtokenBtokenC". If we want to "tokenlist" would be "target tokenA tokenB tokenC". If we want to
modify a non-first alternative for a nonterminal, we still write the modify a non-first alternative for a nonterminal, we still write the
nonterminal. For example, "concattokens" should be "targettokenDtokenE" nonterminal. For example, "tokenlist" should be "target tokenD tokenE"
to affect the second alternative in: to affect the second alternative in:
target: tokenA tokenB tokenC {...} target: tokenA tokenB tokenC {...}
| tokenD tokenE {...} | tokenD tokenE {...}
@ -72,7 +72,7 @@ c) "rule" - the automatic action is emitted, but then the entry's
code block is added verbatim afterwards. This typically is code block is added verbatim afterwards. This typically is
used to add new alternatives to a nonterminal of the core grammar. used to add new alternatives to a nonterminal of the core grammar.
For example, given the entry: For example, given the entry:
ECPG: targettokenAtokenBtokenC rule ECPG: rule target tokenA tokenB tokenC
| tokenD tokenE { custom_action; } | tokenD tokenE { custom_action; }
what will be emitted is what will be emitted is
target: tokenA tokenB tokenC { automatic_action; } target: tokenA tokenB tokenC { automatic_action; }

View File

@ -1,5 +1,5 @@
/* src/interfaces/ecpg/preproc/ecpg.addons */ /* src/interfaces/ecpg/preproc/ecpg.addons */
ECPG: stmtClosePortalStmt block ECPG: block stmt ClosePortalStmt
{ {
if (INFORMIX_MODE) if (INFORMIX_MODE)
{ {
@ -16,23 +16,23 @@ ECPG: stmtClosePortalStmt block
output_statement(@1, 0, ECPGst_normal); output_statement(@1, 0, ECPGst_normal);
} }
ECPG: stmtDeallocateStmt block ECPG: block stmt DeallocateStmt
{ {
output_deallocate_prepare_statement(@1); output_deallocate_prepare_statement(@1);
} }
ECPG: stmtDeclareCursorStmt block ECPG: block stmt DeclareCursorStmt
{ {
output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0); output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
} }
ECPG: stmtDiscardStmt block ECPG: block stmt DiscardStmt
ECPG: stmtFetchStmt block ECPG: block stmt FetchStmt
{ output_statement(@1, 1, ECPGst_normal); } { output_statement(@1, 1, ECPGst_normal); }
ECPG: stmtDeleteStmt block ECPG: block stmt DeleteStmt
ECPG: stmtInsertStmt block ECPG: block stmt InsertStmt
ECPG: stmtSelectStmt block ECPG: block stmt SelectStmt
ECPG: stmtUpdateStmt block ECPG: block stmt UpdateStmt
{ output_statement(@1, 1, ECPGst_prepnormal); } { output_statement(@1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block ECPG: block stmt ExecuteStmt
{ {
check_declared_list($1.name); check_declared_list($1.name);
if ($1.type == NULL || strlen($1.type) == 0) if ($1.type == NULL || strlen($1.type) == 0)
@ -57,7 +57,7 @@ ECPG: stmtExecuteStmt block
output_statement(cat_str(3, "execute", "$0", $1.type), 0, ECPGst_exec_with_exprlist); output_statement(cat_str(3, "execute", "$0", $1.type), 0, ECPGst_exec_with_exprlist);
} }
} }
ECPG: stmtPrepareStmt block ECPG: block stmt PrepareStmt
{ {
check_declared_list($1.name); check_declared_list($1.name);
if ($1.type == NULL) if ($1.type == NULL)
@ -87,17 +87,17 @@ ECPG: stmtPrepareStmt block
output_statement(cat_str(5, "prepare", "$0", $1.type, "as", $1.stmt), 0, ECPGst_prepare); output_statement(cat_str(5, "prepare", "$0", $1.type, "as", $1.stmt), 0, ECPGst_prepare);
} }
} }
ECPG: stmtTransactionStmt block ECPG: block stmt TransactionStmt
{ {
fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1); fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
whenever_action(2); whenever_action(2);
} }
ECPG: toplevel_stmtTransactionStmtLegacy block ECPG: block toplevel_stmt TransactionStmtLegacy
{ {
fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1); fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
whenever_action(2); whenever_action(2);
} }
ECPG: stmtViewStmt rule ECPG: rule stmt ViewStmt
| ECPGAllocateDescr | ECPGAllocateDescr
{ {
fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", @1); fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", @1);
@ -231,45 +231,45 @@ ECPG: stmtViewStmt rule
output_simple_statement(@1, 0); output_simple_statement(@1, 0);
} }
ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block ECPG: block where_or_current_clause WHERE CURRENT_P OF cursor_name
{ {
const char *cursor_marker = @4[0] == ':' ? "$0" : @4; const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
@$ = cat_str(2, "where current of", cursor_marker); @$ = cat_str(2, "where current of", cursor_marker);
} }
ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listcopy_fromopt_programcopy_file_namecopy_delimiteropt_withcopy_optionswhere_clause addon ECPG: addon CopyStmt COPY opt_binary qualified_name opt_column_list copy_from opt_program copy_file_name copy_delimiter opt_with copy_options where_clause
if (strcmp(@6, "from") == 0 && if (strcmp(@6, "from") == 0 &&
(strcmp(@7, "stdin") == 0 || strcmp(@7, "stdout") == 0)) (strcmp(@7, "stdin") == 0 || strcmp(@7, "stdout") == 0))
mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented"); mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
ECPG: var_valueNumericOnly addon ECPG: addon var_value NumericOnly
if (@1[0] == '$') if (@1[0] == '$')
@$ = "$0"; @$ = "$0";
ECPG: fetch_argscursor_name addon ECPG: addon fetch_args cursor_name
struct cursor *ptr = add_additional_variables(@1, false); struct cursor *ptr = add_additional_variables(@1, false);
if (ptr->connection) if (ptr->connection)
connection = mm_strdup(ptr->connection); connection = mm_strdup(ptr->connection);
if (@1[0] == ':') if (@1[0] == ':')
@$ = "$0"; @$ = "$0";
ECPG: fetch_argsfrom_incursor_name addon ECPG: addon fetch_args from_in cursor_name
struct cursor *ptr = add_additional_variables(@2, false); struct cursor *ptr = add_additional_variables(@2, false);
if (ptr->connection) if (ptr->connection)
connection = mm_strdup(ptr->connection); connection = mm_strdup(ptr->connection);
if (@2[0] == ':') if (@2[0] == ':')
@$ = cat2_str(@1, "$0"); @$ = cat2_str(@1, "$0");
ECPG: fetch_argsNEXTopt_from_incursor_name addon ECPG: addon fetch_args NEXT opt_from_in cursor_name
ECPG: fetch_argsPRIORopt_from_incursor_name addon ECPG: addon fetch_args PRIOR opt_from_in cursor_name
ECPG: fetch_argsFIRST_Popt_from_incursor_name addon ECPG: addon fetch_args FIRST_P opt_from_in cursor_name
ECPG: fetch_argsLAST_Popt_from_incursor_name addon ECPG: addon fetch_args LAST_P opt_from_in cursor_name
ECPG: fetch_argsALLopt_from_incursor_name addon ECPG: addon fetch_args ALL opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@3, false); struct cursor *ptr = add_additional_variables(@3, false);
if (ptr->connection) if (ptr->connection)
connection = mm_strdup(ptr->connection); connection = mm_strdup(ptr->connection);
if (@3[0] == ':') if (@3[0] == ':')
@$ = cat_str(3, @1, @2, "$0"); @$ = cat_str(3, @1, @2, "$0");
ECPG: fetch_argsSignedIconstopt_from_incursor_name addon ECPG: addon fetch_args SignedIconst opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@3, false); struct cursor *ptr = add_additional_variables(@3, false);
bool replace = false; bool replace = false;
@ -287,18 +287,18 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
} }
if (replace) if (replace)
@$ = cat_str(3, @1, @2, @3); @$ = cat_str(3, @1, @2, @3);
ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon ECPG: addon fetch_args FORWARD ALL opt_from_in cursor_name
ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon ECPG: addon fetch_args BACKWARD ALL opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@4, false); struct cursor *ptr = add_additional_variables(@4, false);
if (ptr->connection) if (ptr->connection)
connection = mm_strdup(ptr->connection); connection = mm_strdup(ptr->connection);
if (@4[0] == ':') if (@4[0] == ':')
@$ = cat_str(4, @1, @2, @3, "$0"); @$ = cat_str(4, @1, @2, @3, "$0");
ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon ECPG: addon fetch_args ABSOLUTE_P SignedIconst opt_from_in cursor_name
ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon ECPG: addon fetch_args RELATIVE_P SignedIconst opt_from_in cursor_name
ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon ECPG: addon fetch_args FORWARD SignedIconst opt_from_in cursor_name
ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon ECPG: addon fetch_args BACKWARD SignedIconst opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@4, false); struct cursor *ptr = add_additional_variables(@4, false);
bool replace = false; bool replace = false;
@ -316,7 +316,7 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
} }
if (replace) if (replace)
@$ = cat_str(4, @1, @2, @3, @4); @$ = cat_str(4, @1, @2, @3, @4);
ECPG: cursor_namename block ECPG: block cursor_name name
| char_civar | char_civar
{ {
char *curname = loc_alloc(strlen(@1) + 2); char *curname = loc_alloc(strlen(@1) + 2);
@ -324,11 +324,11 @@ ECPG: cursor_namename block
sprintf(curname, ":%s", @1); sprintf(curname, ":%s", @1);
@$ = curname; @$ = curname;
} }
ECPG: ExplainableStmtExecuteStmt block ECPG: block ExplainableStmt ExecuteStmt
{ {
@$ = $1.name; @$ = $1.name;
} }
ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block ECPG: block PrepareStmt PREPARE prepared_name prep_type_clause AS PreparableStmt
{ {
$$.name = @2; $$.name = @2;
$$.type = @3; $$.type = @3;
@ -340,20 +340,20 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
$$.type = NULL; $$.type = NULL;
$$.stmt = @4; $$.stmt = @4;
} }
ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block ECPG: block ExecuteStmt EXECUTE prepared_name execute_param_clause execute_rest
{ {
$$.name = @2; $$.name = @2;
$$.type = @3; $$.type = @3;
} }
ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block ECPG: block ExecuteStmt CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest
{ {
$$.name = @$; $$.name = @$;
} }
ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block ECPG: block ExecuteStmt CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest
{ {
$$.name = @$; $$.name = @$;
} }
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block ECPG: block DeclareCursorStmt DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt
{ {
struct cursor *ptr, struct cursor *ptr,
*this; *this;
@ -403,7 +403,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
@$ = cat2_str(adjust_outofscope_cursor_vars(this), comment); @$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
} }
ECPG: ClosePortalStmtCLOSEcursor_name block ECPG: block ClosePortalStmt CLOSE cursor_name
{ {
const char *cursor_marker = @2[0] == ':' ? "$0" : @2; const char *cursor_marker = @2[0] == ':' ? "$0" : @2;
struct cursor *ptr = NULL; struct cursor *ptr = NULL;
@ -419,14 +419,14 @@ ECPG: ClosePortalStmtCLOSEcursor_name block
} }
@$ = cat2_str("close", cursor_marker); @$ = cat2_str("close", cursor_marker);
} }
ECPG: opt_hold block ECPG: block opt_hold
{ {
if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit) if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
@$ = "with hold"; @$ = "with hold";
else else
@$ = ""; @$ = "";
} }
ECPG: into_clauseINTOOptTempTableName block ECPG: block into_clause INTO OptTempTableName
{ {
FoundInto = 1; FoundInto = 1;
@$ = cat2_str("into", @2); @$ = cat2_str("into", @2);
@ -435,15 +435,15 @@ ECPG: into_clauseINTOOptTempTableName block
{ {
@$ = ""; @$ = "";
} }
ECPG: TypenameSimpleTypenameopt_array_bounds block ECPG: block Typename SimpleTypename opt_array_bounds
{ {
@$ = cat2_str(@1, $2.str); @$ = cat2_str(@1, $2.str);
} }
ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block ECPG: block Typename SETOF SimpleTypename opt_array_bounds
{ {
@$ = cat_str(3, "setof", @2, $3.str); @$ = cat_str(3, "setof", @2, $3.str);
} }
ECPG: opt_array_boundsopt_array_bounds'['']' block ECPG: block opt_array_bounds opt_array_bounds '[' ']'
{ {
$$.index1 = $1.index1; $$.index1 = $1.index1;
$$.index2 = $1.index2; $$.index2 = $1.index2;
@ -463,20 +463,20 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block
$$.index2 = @3; $$.index2 = @3;
$$.str = cat_str(4, $1.str, "[", @3, "]"); $$.str = cat_str(4, $1.str, "[", @3, "]");
} }
ECPG: opt_array_bounds block ECPG: block opt_array_bounds
{ {
$$.index1 = "-1"; $$.index1 = "-1";
$$.index2 = "-1"; $$.index2 = "-1";
$$.str = ""; $$.str = "";
} }
ECPG: AexprConstNULL_P rule ECPG: rule AexprConst NULL_P
| civar | civar
| civarind | civarind
ECPG: VariableShowStmtSHOWALL block ECPG: block VariableShowStmt SHOW ALL
{ {
mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented"); mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
} }
ECPG: FetchStmtMOVEfetch_args rule ECPG: rule FetchStmt MOVE fetch_args
| FETCH fetch_args ecpg_fetch_into | FETCH fetch_args ecpg_fetch_into
| FETCH FORWARD cursor_name opt_ecpg_fetch_into | FETCH FORWARD cursor_name opt_ecpg_fetch_into
{ {
@ -558,9 +558,9 @@ ECPG: FetchStmtMOVEfetch_args rule
@$ = cat_str(2, "move backward from", cursor_marker); @$ = cat_str(2, "move backward from", cursor_marker);
} }
ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block ECPG: block limit_clause LIMIT select_limit_value ',' select_offset_value
{ {
mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server"); mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server");
} }
ECPG: SignedIconstIconst rule ECPG: rule SignedIconst Iconst
| civar | civar

View File

@ -75,47 +75,47 @@ my %replace_types = (
my %replace_types_used; my %replace_types_used;
# This hash provides an "ignore" option or substitute expansion for any # This hash provides an "ignore" option or substitute expansion for any
# rule or rule alternative. The hash key is the same "concattokens" tag # rule or rule alternative. The hash key is the same "tokenlist" tag
# used for lookup in ecpg.addons. # used for lookup in ecpg.addons.
my %replace_line = ( my %replace_line = (
# These entries excise certain keywords from the core keyword lists. # These entries excise certain keywords from the core keyword lists.
# Be sure to account for these in ColLabel and related productions. # Be sure to account for these in ColLabel and related productions.
'unreserved_keywordCONNECTION' => 'ignore', 'unreserved_keyword CONNECTION' => 'ignore',
'unreserved_keywordCURRENT_P' => 'ignore', 'unreserved_keyword CURRENT_P' => 'ignore',
'unreserved_keywordDAY_P' => 'ignore', 'unreserved_keyword DAY_P' => 'ignore',
'unreserved_keywordHOUR_P' => 'ignore', 'unreserved_keyword HOUR_P' => 'ignore',
'unreserved_keywordINPUT_P' => 'ignore', 'unreserved_keyword INPUT_P' => 'ignore',
'unreserved_keywordMINUTE_P' => 'ignore', 'unreserved_keyword MINUTE_P' => 'ignore',
'unreserved_keywordMONTH_P' => 'ignore', 'unreserved_keyword MONTH_P' => 'ignore',
'unreserved_keywordSECOND_P' => 'ignore', 'unreserved_keyword SECOND_P' => 'ignore',
'unreserved_keywordYEAR_P' => 'ignore', 'unreserved_keyword YEAR_P' => 'ignore',
'col_name_keywordCHAR_P' => 'ignore', 'col_name_keyword CHAR_P' => 'ignore',
'col_name_keywordINT_P' => 'ignore', 'col_name_keyword INT_P' => 'ignore',
'col_name_keywordVALUES' => 'ignore', 'col_name_keyword VALUES' => 'ignore',
'reserved_keywordTO' => 'ignore', 'reserved_keyword TO' => 'ignore',
'reserved_keywordUNION' => 'ignore', 'reserved_keyword UNION' => 'ignore',
# some other production rules have to be ignored or replaced # some other production rules have to be ignored or replaced
'fetch_argsFORWARDopt_from_incursor_name' => 'ignore', 'fetch_args FORWARD opt_from_in cursor_name' => 'ignore',
'fetch_argsBACKWARDopt_from_incursor_name' => 'ignore', 'fetch_args BACKWARD opt_from_in cursor_name' => 'ignore',
"opt_array_boundsopt_array_bounds'['Iconst']'" => 'ignore', "opt_array_bounds opt_array_bounds '[' Iconst ']'" => 'ignore',
'VariableShowStmtSHOWvar_name' => 'SHOW var_name ecpg_into', 'VariableShowStmt SHOW var_name' => 'SHOW var_name ecpg_into',
'VariableShowStmtSHOWTIMEZONE' => 'SHOW TIME ZONE ecpg_into', 'VariableShowStmt SHOW TIME ZONE' => 'SHOW TIME ZONE ecpg_into',
'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL' => 'VariableShowStmt SHOW TRANSACTION ISOLATION LEVEL' =>
'SHOW TRANSACTION ISOLATION LEVEL ecpg_into', 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into',
'VariableShowStmtSHOWSESSIONAUTHORIZATION' => 'VariableShowStmt SHOW SESSION AUTHORIZATION' =>
'SHOW SESSION AUTHORIZATION ecpg_into', 'SHOW SESSION AUTHORIZATION ecpg_into',
'returning_clauseRETURNINGtarget_list' => 'returning_clause RETURNING target_list' =>
'RETURNING target_list opt_ecpg_into', 'RETURNING target_list opt_ecpg_into',
'ExecuteStmtEXECUTEnameexecute_param_clause' => 'ExecuteStmt EXECUTE name execute_param_clause' =>
'EXECUTE prepared_name execute_param_clause execute_rest', 'EXECUTE prepared_name execute_param_clause execute_rest',
'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' 'ExecuteStmt CREATE OptTemp TABLE create_as_target AS EXECUTE name execute_param_clause opt_with_data'
=> 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest', => 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
'ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' 'ExecuteStmt CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE name execute_param_clause opt_with_data'
=> 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest', => 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' => 'PrepareStmt PREPARE name prep_type_clause AS PreparableStmt' =>
'PREPARE prepared_name prep_type_clause AS PreparableStmt', 'PREPARE prepared_name prep_type_clause AS PreparableStmt',
'var_nameColId' => 'ECPGColId'); 'var_name ColId' => 'ECPGColId');
my %replace_line_used; my %replace_line_used;
@ -645,8 +645,9 @@ sub emit_default_action
sub emit_rule sub emit_rule
{ {
# compute tag to be used as lookup key in %replace_line and %addons # compute tag to be used as lookup key in %replace_line and %addons
my $tag = $non_term_id . $line; my $tag = $non_term_id . ' ' . $line;
$tag =~ tr/ |//d; $tag =~ tr/|//d;
$tag = join(' ', split(/\s+/, $tag));
# apply replace_line substitution if any # apply replace_line substitution if any
my $rep = $replace_line{$tag}; my $rep = $replace_line{$tag};
@ -671,8 +672,9 @@ sub emit_rule
} }
# recompute tag for use in emit_rule_action # recompute tag for use in emit_rule_action
$tag = $non_term_id . $line; $tag = $non_term_id . ' ' . $line;
$tag =~ tr/ |//d; $tag =~ tr/|//d;
$tag = join(' ', split(/\s+/, $tag));
} }
# Emit $line, then print the appropriate action. # Emit $line, then print the appropriate action.
@ -684,8 +686,8 @@ sub emit_rule
=top =top
load ecpg.addons into %addons hash. The result is something like load ecpg.addons into %addons hash. The result is something like
%addons = { %addons = {
stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 }, 'stmt ClosePortalStmt' => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 },
stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 } 'stmt ViewStmt' => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 }
} }
=cut =cut
@ -704,14 +706,21 @@ sub preload_addons
my $skip = 1; my $skip = 1;
while (<$fh>) while (<$fh>)
{ {
if (/^ECPG:\s+(\S+)\s+(\w+)\s*$/) if (/^ECPG:\s+(\w+)\s+(.*)$/)
{ {
# Found an "ECPG:" line, so we're done skipping the header # Found an "ECPG:" line, so we're done skipping the header
$skip = 0; $skip = 0;
my $type = $1;
my $target = $2;
# Normalize target so there's exactly one space between tokens
$target = join(' ', split(/\s+/, $target));
# Validate record type and target # Validate record type and target
die "invalid record type $2 in addon rule for $1\n" die "invalid record type $type in addon rule for $target\n"
unless ($2 eq 'block' or $2 eq 'addon' or $2 eq 'rule'); unless ($type eq 'block'
die "duplicate addon rule for $1\n" if (exists $addons{$1}); or $type eq 'addon'
or $type eq 'rule');
die "duplicate addon rule for $target\n"
if (exists $addons{$target});
# If we had some preceding code lines, attach them to all # If we had some preceding code lines, attach them to all
# as-yet-unfinished records. # as-yet-unfinished records.
if (@code) if (@code)
@ -724,10 +733,10 @@ sub preload_addons
@needsRules = (); @needsRules = ();
} }
my $record = {}; my $record = {};
$record->{type} = $2; $record->{type} = $type;
$record->{lines} = []; $record->{lines} = [];
$record->{used} = 0; $record->{used} = 0;
$addons{$1} = $record; $addons{$target} = $record;
push(@needsRules, $record); push(@needsRules, $record);
} }
elsif (/^ECPG:/) elsif (/^ECPG:/)