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:
parent
143e3a1ab8
commit
dbedc461b4
@ -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; }
|
||||
|
@ -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
|
||||
|
@ -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:/)
|
||||
|
Loading…
x
Reference in New Issue
Block a user