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: concattokens ruletype
ECPG: ruletype tokenlist
and typically have one or more following lines that are the code
for a grammar action. Any line not starting with "ECPG:" is taken
to be part of the code block for the preceding "ECPG:" line.
"concattokens" identifies which gram.y production this entry affects.
It is simply the target nonterminal and the tokens from the gram.y rule
concatenated together. For example, to modify the action for a gram.y
rule like this:
"tokenlist" identifies which gram.y production this entry affects.
It is simply a list of the target nonterminal and the input tokens
from the gram.y rule. For example, to modify the action for a
gram.y rule like this:
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
nonterminal. For example, "concattokens" should be "targettokenDtokenE"
nonterminal. For example, "tokenlist" should be "target tokenD tokenE"
to affect the second alternative in:
target: tokenA tokenB tokenC {...}
| 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
used to add new alternatives to a nonterminal of the core grammar.
For example, given the entry:
ECPG: targettokenAtokenBtokenC rule
ECPG: rule target tokenA tokenB tokenC
| tokenD tokenE { custom_action; }
what will be emitted is
target: tokenA tokenB tokenC { automatic_action; }

View File

@ -1,5 +1,5 @@
/* src/interfaces/ecpg/preproc/ecpg.addons */
ECPG: stmtClosePortalStmt block
ECPG: block stmt ClosePortalStmt
{
if (INFORMIX_MODE)
{
@ -16,23 +16,23 @@ ECPG: stmtClosePortalStmt block
output_statement(@1, 0, ECPGst_normal);
}
ECPG: stmtDeallocateStmt block
ECPG: block stmt DeallocateStmt
{
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);
}
ECPG: stmtDiscardStmt block
ECPG: stmtFetchStmt block
ECPG: block stmt DiscardStmt
ECPG: block stmt FetchStmt
{ output_statement(@1, 1, ECPGst_normal); }
ECPG: stmtDeleteStmt block
ECPG: stmtInsertStmt block
ECPG: stmtSelectStmt block
ECPG: stmtUpdateStmt block
ECPG: block stmt DeleteStmt
ECPG: block stmt InsertStmt
ECPG: block stmt SelectStmt
ECPG: block stmt UpdateStmt
{ output_statement(@1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
ECPG: block stmt ExecuteStmt
{
check_declared_list($1.name);
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);
}
}
ECPG: stmtPrepareStmt block
ECPG: block stmt PrepareStmt
{
check_declared_list($1.name);
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);
}
}
ECPG: stmtTransactionStmt block
ECPG: block stmt TransactionStmt
{
fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
whenever_action(2);
}
ECPG: toplevel_stmtTransactionStmtLegacy block
ECPG: block toplevel_stmt TransactionStmtLegacy
{
fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
whenever_action(2);
}
ECPG: stmtViewStmt rule
ECPG: rule stmt ViewStmt
| ECPGAllocateDescr
{
fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", @1);
@ -231,45 +231,45 @@ ECPG: stmtViewStmt rule
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;
@$ = 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 &&
(strcmp(@7, "stdin") == 0 || strcmp(@7, "stdout") == 0))
mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
ECPG: var_valueNumericOnly addon
ECPG: addon var_value NumericOnly
if (@1[0] == '$')
@$ = "$0";
ECPG: fetch_argscursor_name addon
ECPG: addon fetch_args cursor_name
struct cursor *ptr = add_additional_variables(@1, false);
if (ptr->connection)
connection = mm_strdup(ptr->connection);
if (@1[0] == ':')
@$ = "$0";
ECPG: fetch_argsfrom_incursor_name addon
ECPG: addon fetch_args from_in cursor_name
struct cursor *ptr = add_additional_variables(@2, false);
if (ptr->connection)
connection = mm_strdup(ptr->connection);
if (@2[0] == ':')
@$ = cat2_str(@1, "$0");
ECPG: fetch_argsNEXTopt_from_incursor_name addon
ECPG: fetch_argsPRIORopt_from_incursor_name addon
ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
ECPG: fetch_argsLAST_Popt_from_incursor_name addon
ECPG: fetch_argsALLopt_from_incursor_name addon
ECPG: addon fetch_args NEXT opt_from_in cursor_name
ECPG: addon fetch_args PRIOR opt_from_in cursor_name
ECPG: addon fetch_args FIRST_P opt_from_in cursor_name
ECPG: addon fetch_args LAST_P opt_from_in cursor_name
ECPG: addon fetch_args ALL opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@3, false);
if (ptr->connection)
connection = mm_strdup(ptr->connection);
if (@3[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);
bool replace = false;
@ -287,18 +287,18 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
}
if (replace)
@$ = cat_str(3, @1, @2, @3);
ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
ECPG: addon fetch_args FORWARD ALL opt_from_in cursor_name
ECPG: addon fetch_args BACKWARD ALL opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@4, false);
if (ptr->connection)
connection = mm_strdup(ptr->connection);
if (@4[0] == ':')
@$ = cat_str(4, @1, @2, @3, "$0");
ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
ECPG: addon fetch_args ABSOLUTE_P SignedIconst opt_from_in cursor_name
ECPG: addon fetch_args RELATIVE_P SignedIconst opt_from_in cursor_name
ECPG: addon fetch_args FORWARD SignedIconst opt_from_in cursor_name
ECPG: addon fetch_args BACKWARD SignedIconst opt_from_in cursor_name
struct cursor *ptr = add_additional_variables(@4, false);
bool replace = false;
@ -316,7 +316,7 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
}
if (replace)
@$ = cat_str(4, @1, @2, @3, @4);
ECPG: cursor_namename block
ECPG: block cursor_name name
| char_civar
{
char *curname = loc_alloc(strlen(@1) + 2);
@ -324,11 +324,11 @@ ECPG: cursor_namename block
sprintf(curname, ":%s", @1);
@$ = curname;
}
ECPG: ExplainableStmtExecuteStmt block
ECPG: block ExplainableStmt ExecuteStmt
{
@$ = $1.name;
}
ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
ECPG: block PrepareStmt PREPARE prepared_name prep_type_clause AS PreparableStmt
{
$$.name = @2;
$$.type = @3;
@ -340,20 +340,20 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
$$.type = NULL;
$$.stmt = @4;
}
ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
ECPG: block ExecuteStmt EXECUTE prepared_name execute_param_clause execute_rest
{
$$.name = @2;
$$.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 = @$;
}
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 = @$;
}
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
ECPG: block DeclareCursorStmt DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt
{
struct cursor *ptr,
*this;
@ -403,7 +403,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
@$ = 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;
struct cursor *ptr = NULL;
@ -419,14 +419,14 @@ ECPG: ClosePortalStmtCLOSEcursor_name block
}
@$ = cat2_str("close", cursor_marker);
}
ECPG: opt_hold block
ECPG: block opt_hold
{
if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
@$ = "with hold";
else
@$ = "";
}
ECPG: into_clauseINTOOptTempTableName block
ECPG: block into_clause INTO OptTempTableName
{
FoundInto = 1;
@$ = 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);
}
ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
ECPG: block Typename SETOF SimpleTypename opt_array_bounds
{
@$ = 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;
$$.index2 = $1.index2;
@ -463,20 +463,20 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block
$$.index2 = @3;
$$.str = cat_str(4, $1.str, "[", @3, "]");
}
ECPG: opt_array_bounds block
ECPG: block opt_array_bounds
{
$$.index1 = "-1";
$$.index2 = "-1";
$$.str = "";
}
ECPG: AexprConstNULL_P rule
ECPG: rule AexprConst NULL_P
| civar
| civarind
ECPG: VariableShowStmtSHOWALL block
ECPG: block VariableShowStmt SHOW ALL
{
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 FORWARD cursor_name opt_ecpg_fetch_into
{
@ -558,9 +558,9 @@ ECPG: FetchStmtMOVEfetch_args rule
@$ = 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");
}
ECPG: SignedIconstIconst rule
ECPG: rule SignedIconst Iconst
| civar

View File

@ -75,7 +75,7 @@ my %replace_types = (
my %replace_types_used;
# 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.
my %replace_line = (
# These entries excise certain keywords from the core keyword lists.
@ -645,8 +645,9 @@ sub emit_default_action
sub emit_rule
{
# 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 = join(' ', split(/\s+/, $tag));
# apply replace_line substitution if any
my $rep = $replace_line{$tag};
@ -671,8 +672,9 @@ sub emit_rule
}
# recompute tag for use in emit_rule_action
$tag = $non_term_id . $line;
$tag = $non_term_id . ' ' . $line;
$tag =~ tr/|//d;
$tag = join(' ', split(/\s+/, $tag));
}
# Emit $line, then print the appropriate action.
@ -684,8 +686,8 @@ sub emit_rule
=top
load ecpg.addons into %addons hash. The result is something like
%addons = {
stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 },
stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 }
'stmt ClosePortalStmt' => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 },
'stmt ViewStmt' => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 }
}
=cut
@ -704,14 +706,21 @@ sub preload_addons
my $skip = 1;
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
$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
die "invalid record type $2 in addon rule for $1\n"
unless ($2 eq 'block' or $2 eq 'addon' or $2 eq 'rule');
die "duplicate addon rule for $1\n" if (exists $addons{$1});
die "invalid record type $type in addon rule for $target\n"
unless ($type eq 'block'
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
# as-yet-unfinished records.
if (@code)
@ -724,10 +733,10 @@ sub preload_addons
@needsRules = ();
}
my $record = {};
$record->{type} = $2;
$record->{type} = $type;
$record->{lines} = [];
$record->{used} = 0;
$addons{$1} = $record;
$addons{$target} = $record;
push(@needsRules, $record);
}
elsif (/^ECPG:/)