Age Owner Branch data TLA Line data Source code
1 : : %{
2 : : /*-------------------------------------------------------------------------
3 : : *
4 : : * exprparse.y
5 : : * bison grammar for a simple expression syntax
6 : : *
7 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * src/bin/pgbench/exprparse.y
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : :
15 : : #include "postgres_fe.h"
16 : :
17 : : #include "pgbench.h"
18 : :
19 : : #define PGBENCH_NARGS_VARIABLE (-1)
20 : : #define PGBENCH_NARGS_CASE (-2)
21 : : #define PGBENCH_NARGS_HASH (-3)
22 : : #define PGBENCH_NARGS_PERMUTE (-4)
23 : :
24 : : static PgBenchExprList *make_elist(PgBenchExpr *expr, PgBenchExprList *list);
25 : : static PgBenchExpr *make_null_constant(void);
26 : : static PgBenchExpr *make_boolean_constant(bool bval);
27 : : static PgBenchExpr *make_integer_constant(int64 ival);
28 : : static PgBenchExpr *make_double_constant(double dval);
29 : : static PgBenchExpr *make_variable(char *varname);
30 : : static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
31 : : PgBenchExpr *lexpr, PgBenchExpr *rexpr);
32 : : static PgBenchExpr *make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr);
33 : : static int find_func(yyscan_t yyscanner, const char *fname);
34 : : static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
35 : : static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part);
36 : :
37 : : %}
38 : :
39 : : %pure-parser
40 : : %expect 0
41 : : %name-prefix="expr_yy"
42 : :
43 : : %parse-param {PgBenchExpr **expr_parse_result_p}
44 : : %parse-param {yyscan_t yyscanner}
45 : : %lex-param {yyscan_t yyscanner}
46 : :
47 : : %union
48 : : {
49 : : int64 ival;
50 : : double dval;
51 : : bool bval;
52 : : char *str;
53 : : PgBenchExpr *expr;
54 : : PgBenchExprList *elist;
55 : : }
56 : :
57 : : %type <elist> elist when_then_list
58 : : %type <expr> expr case_control
59 : : %type <ival> INTEGER_CONST function
60 : : %type <dval> DOUBLE_CONST
61 : : %type <bval> BOOLEAN_CONST
62 : : %type <str> VARIABLE FUNCTION
63 : :
64 : : %token NULL_CONST INTEGER_CONST MAXINT_PLUS_ONE_CONST DOUBLE_CONST
65 : : %token BOOLEAN_CONST VARIABLE FUNCTION
66 : : %token AND_OP OR_OP NOT_OP NE_OP LE_OP GE_OP LS_OP RS_OP IS_OP
67 : : %token CASE_KW WHEN_KW THEN_KW ELSE_KW END_KW
68 : :
69 : : /* Precedence: lowest to highest, taken from postgres SQL parser */
70 : : %left OR_OP
71 : : %left AND_OP
72 : : %right NOT_OP
73 : : %nonassoc IS_OP ISNULL_OP NOTNULL_OP
74 : : %nonassoc '<' '>' '=' LE_OP GE_OP NE_OP
75 : : %left '|' '#' '&' LS_OP RS_OP '~'
76 : : %left '+' '-'
77 : : %left '*' '/' '%'
78 : : %right UNARY
79 : :
80 : : %%
81 : :
82 : : result: expr {
225 peter@eisentraut.org 83 :CBC 380 : *expr_parse_result_p = $1;
84 : : (void) yynerrs; /* suppress compiler warning */
85 : : }
86 : :
2797 teodor@sigaev.ru 87 : 5 : elist: { $$ = NULL; }
88 : 393 : | expr { $$ = make_elist($1, NULL); }
3476 rhaas@postgresql.org 89 : 343 : | elist ',' expr { $$ = make_elist($3, $1); }
90 : : ;
91 : :
3841 92 : 52 : expr: '(' expr ')' { $$ = $2; }
2797 teodor@sigaev.ru 93 : 2 : | '+' expr %prec UNARY { $$ = $2; }
94 : : /* unary minus "-x" implemented as "0 - x" */
95 : 46 : | '-' expr %prec UNARY { $$ = make_op(yyscanner, "-",
96 : : make_integer_constant(0), $2); }
97 : : /* special PG_INT64_MIN handling, only after a unary minus */
98 : : | '-' MAXINT_PLUS_ONE_CONST %prec UNARY
2536 andres@anarazel.de 99 : 1 : { $$ = make_integer_constant(PG_INT64_MIN); }
100 : : /* binary ones complement "~x" implemented as 0xffff... xor x" */
2797 teodor@sigaev.ru 101 : 2 : | '~' expr { $$ = make_op(yyscanner, "#",
102 : : make_integer_constant(~INT64CONST(0)), $2); }
103 : 12 : | NOT_OP expr { $$ = make_uop(yyscanner, "!not", $2); }
3458 tgl@sss.pgh.pa.us 104 : 48 : | expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); }
105 : 18 : | expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); }
106 : 204 : | expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); }
107 : 13 : | expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
2797 teodor@sigaev.ru 108 : 2 : | expr '%' expr { $$ = make_op(yyscanner, "mod", $1, $3); }
109 : 10 : | expr '<' expr { $$ = make_op(yyscanner, "<", $1, $3); }
110 : 4 : | expr LE_OP expr { $$ = make_op(yyscanner, "<=", $1, $3); }
111 : 6 : | expr '>' expr { $$ = make_op(yyscanner, "<", $3, $1); }
112 : 3 : | expr GE_OP expr { $$ = make_op(yyscanner, "<=", $3, $1); }
113 : 32 : | expr '=' expr { $$ = make_op(yyscanner, "=", $1, $3); }
114 : 8 : | expr NE_OP expr { $$ = make_op(yyscanner, "<>", $1, $3); }
115 : 1 : | expr '&' expr { $$ = make_op(yyscanner, "&", $1, $3); }
116 : 2 : | expr '|' expr { $$ = make_op(yyscanner, "|", $1, $3); }
117 : 1 : | expr '#' expr { $$ = make_op(yyscanner, "#", $1, $3); }
118 : 7 : | expr LS_OP expr { $$ = make_op(yyscanner, "<<", $1, $3); }
119 : 1 : | expr RS_OP expr { $$ = make_op(yyscanner, ">>", $1, $3); }
120 : 44 : | expr AND_OP expr { $$ = make_op(yyscanner, "!and", $1, $3); }
121 : 5 : | expr OR_OP expr { $$ = make_op(yyscanner, "!or", $1, $3); }
122 : : /* IS variants */
123 : 1 : | expr ISNULL_OP { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
124 : : | expr NOTNULL_OP {
125 : 2 : $$ = make_uop(yyscanner, "!not",
126 : 1 : make_op(yyscanner, "!is", $1, make_null_constant()));
127 : : }
128 : 4 : | expr IS_OP NULL_CONST { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
129 : : | expr IS_OP NOT_OP NULL_CONST
130 : : {
131 : 4 : $$ = make_uop(yyscanner, "!not",
132 : 2 : make_op(yyscanner, "!is", $1, make_null_constant()));
133 : : }
134 : : | expr IS_OP BOOLEAN_CONST
135 : : {
136 : 1 : $$ = make_op(yyscanner, "!is", $1, make_boolean_constant($3));
137 : : }
138 : : | expr IS_OP NOT_OP BOOLEAN_CONST
139 : : {
140 : 1 : $$ = make_uop(yyscanner, "!not",
141 : 1 : make_op(yyscanner, "!is", $1, make_boolean_constant($4)));
142 : : }
143 : : /* constants */
144 : 6 : | NULL_CONST { $$ = make_null_constant(); }
145 : 31 : | BOOLEAN_CONST { $$ = make_boolean_constant($1); }
3448 tgl@sss.pgh.pa.us 146 : 794 : | INTEGER_CONST { $$ = make_integer_constant($1); }
147 : 61 : | DOUBLE_CONST { $$ = make_double_constant($1); }
148 : : /* misc */
2797 teodor@sigaev.ru 149 : 272 : | VARIABLE { $$ = make_variable($1); }
3458 tgl@sss.pgh.pa.us 150 : 398 : | function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
2797 teodor@sigaev.ru 151 : 14 : | case_control { $$ = $1; }
152 : : ;
153 : :
154 : : when_then_list:
155 : 2 : when_then_list WHEN_KW expr THEN_KW expr { $$ = make_elist($5, make_elist($3, $1)); }
156 : 14 : | WHEN_KW expr THEN_KW expr { $$ = make_elist($4, make_elist($2, NULL)); }
157 : :
158 : : case_control:
159 : 4 : CASE_KW when_then_list END_KW { $$ = make_case(yyscanner, $2, make_null_constant()); }
160 : 10 : | CASE_KW when_then_list ELSE_KW expr END_KW { $$ = make_case(yyscanner, $2, $4); }
161 : :
3458 tgl@sss.pgh.pa.us 162 : 399 : function: FUNCTION { $$ = find_func(yyscanner, $1); pg_free($1); }
163 : : ;
164 : :
165 : : %%
166 : :
167 : : static PgBenchExpr *
2797 teodor@sigaev.ru 168 : 18 : make_null_constant(void)
169 : : {
170 : 18 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
171 : :
172 : 18 : expr->etype = ENODE_CONSTANT;
173 : 18 : expr->u.constant.type = PGBT_NULL;
174 : 18 : expr->u.constant.u.ival = 0;
175 : 18 : return expr;
176 : : }
177 : :
178 : : static PgBenchExpr *
3841 rhaas@postgresql.org 179 : 843 : make_integer_constant(int64 ival)
180 : : {
181 : 843 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
182 : :
3449 183 : 843 : expr->etype = ENODE_CONSTANT;
184 : 843 : expr->u.constant.type = PGBT_INT;
185 : 843 : expr->u.constant.u.ival = ival;
186 : 843 : return expr;
187 : : }
188 : :
189 : : static PgBenchExpr *
190 : 61 : make_double_constant(double dval)
191 : : {
192 : 61 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
193 : :
194 : 61 : expr->etype = ENODE_CONSTANT;
195 : 61 : expr->u.constant.type = PGBT_DOUBLE;
196 : 61 : expr->u.constant.u.dval = dval;
3841 197 : 61 : return expr;
198 : : }
199 : :
200 : : static PgBenchExpr *
2797 teodor@sigaev.ru 201 : 33 : make_boolean_constant(bool bval)
202 : : {
203 : 33 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
204 : :
205 : 33 : expr->etype = ENODE_CONSTANT;
206 : 33 : expr->u.constant.type = PGBT_BOOLEAN;
207 : 33 : expr->u.constant.u.bval = bval;
208 : 33 : return expr;
209 : : }
210 : :
211 : : static PgBenchExpr *
3841 rhaas@postgresql.org 212 : 309 : make_variable(char *varname)
213 : : {
214 : 309 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
215 : :
216 : 309 : expr->etype = ENODE_VARIABLE;
217 : 309 : expr->u.variable.varname = varname;
218 : 309 : return expr;
219 : : }
220 : :
221 : : /* binary operators */
222 : : static PgBenchExpr *
3458 tgl@sss.pgh.pa.us 223 : 467 : make_op(yyscan_t yyscanner, const char *operator,
224 : : PgBenchExpr *lexpr, PgBenchExpr *rexpr)
225 : : {
226 : 467 : return make_func(yyscanner, find_func(yyscanner, operator),
227 : : make_elist(rexpr, make_elist(lexpr, NULL)));
228 : : }
229 : :
230 : : /* unary operator */
231 : : static PgBenchExpr *
2797 teodor@sigaev.ru 232 : 16 : make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr)
233 : : {
234 : 16 : return make_func(yyscanner, find_func(yyscanner, operator), make_elist(expr, NULL));
235 : : }
236 : :
237 : : /*
238 : : * List of available functions:
239 : : * - fname: function name, "!..." for special internal functions
240 : : * - nargs: number of arguments. Special cases:
241 : : * - PGBENCH_NARGS_VARIABLE is a special value for least & greatest
242 : : * meaning #args >= 1;
243 : : * - PGBENCH_NARGS_CASE is for the "CASE WHEN ..." function, which
244 : : * has #args >= 3 and odd;
245 : : * - PGBENCH_NARGS_HASH is for hash functions, which have one required
246 : : * and one optional argument;
247 : : * - tag: function identifier from PgBenchFunction enum
248 : : */
249 : : static const struct
250 : : {
251 : : const char *fname;
252 : : int nargs;
253 : : PgBenchFunction tag;
254 : : } PGBENCH_FUNCTIONS[] =
255 : :
256 : : {
257 : : /* parsed as operators, executed as functions */
258 : : {
259 : : "+", 2, PGBENCH_ADD
260 : : },
261 : : {
262 : : "-", 2, PGBENCH_SUB
263 : : },
264 : : {
265 : : "*", 2, PGBENCH_MUL
266 : : },
267 : : {
268 : : "/", 2, PGBENCH_DIV
269 : : },
270 : : {
271 : : "mod", 2, PGBENCH_MOD
272 : : },
273 : : /* actual functions */
274 : : {
275 : : "abs", 1, PGBENCH_ABS
276 : : },
277 : : {
278 : : "least", PGBENCH_NARGS_VARIABLE, PGBENCH_LEAST
279 : : },
280 : : {
281 : : "greatest", PGBENCH_NARGS_VARIABLE, PGBENCH_GREATEST
282 : : },
283 : : {
284 : : "debug", 1, PGBENCH_DEBUG
285 : : },
286 : : {
287 : : "pi", 0, PGBENCH_PI
288 : : },
289 : : {
290 : : "sqrt", 1, PGBENCH_SQRT
291 : : },
292 : : {
293 : : "ln", 1, PGBENCH_LN
294 : : },
295 : : {
296 : : "exp", 1, PGBENCH_EXP
297 : : },
298 : : {
299 : : "int", 1, PGBENCH_INT
300 : : },
301 : : {
302 : : "double", 1, PGBENCH_DOUBLE
303 : : },
304 : : {
305 : : "random", 2, PGBENCH_RANDOM
306 : : },
307 : : {
308 : : "random_gaussian", 3, PGBENCH_RANDOM_GAUSSIAN
309 : : },
310 : : {
311 : : "random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL
312 : : },
313 : : {
314 : : "random_zipfian", 3, PGBENCH_RANDOM_ZIPFIAN
315 : : },
316 : : {
317 : : "pow", 2, PGBENCH_POW
318 : : },
319 : : {
320 : : "power", 2, PGBENCH_POW
321 : : },
322 : : /* logical operators */
323 : : {
324 : : "!and", 2, PGBENCH_AND
325 : : },
326 : : {
327 : : "!or", 2, PGBENCH_OR
328 : : },
329 : : {
330 : : "!not", 1, PGBENCH_NOT
331 : : },
332 : : /* bitwise integer operators */
333 : : {
334 : : "&", 2, PGBENCH_BITAND
335 : : },
336 : : {
337 : : "|", 2, PGBENCH_BITOR
338 : : },
339 : : {
340 : : "#", 2, PGBENCH_BITXOR
341 : : },
342 : : {
343 : : "<<", 2, PGBENCH_LSHIFT
344 : : },
345 : : {
346 : : ">>", 2, PGBENCH_RSHIFT
347 : : },
348 : : /* comparison operators */
349 : : {
350 : : "=", 2, PGBENCH_EQ
351 : : },
352 : : {
353 : : "<>", 2, PGBENCH_NE
354 : : },
355 : : {
356 : : "<=", 2, PGBENCH_LE
357 : : },
358 : : {
359 : : "<", 2, PGBENCH_LT
360 : : },
361 : : {
362 : : "!is", 2, PGBENCH_IS
363 : : },
364 : : /* "case when ... then ... else ... end" construction */
365 : : {
366 : : "!case_end", PGBENCH_NARGS_CASE, PGBENCH_CASE
367 : : },
368 : : {
369 : : "hash", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
370 : : },
371 : : {
372 : : "hash_murmur2", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
373 : : },
374 : : {
375 : : "hash_fnv1a", PGBENCH_NARGS_HASH, PGBENCH_HASH_FNV1A
376 : : },
377 : : {
378 : : "permute", PGBENCH_NARGS_PERMUTE, PGBENCH_PERMUTE
379 : : },
380 : : /* keep as last array element */
381 : : {
382 : : NULL, 0, 0
383 : : }
384 : : };
385 : :
386 : : /*
387 : : * Find a function from its name
388 : : *
389 : : * return the index of the function from the PGBENCH_FUNCTIONS array
390 : : * or fail if the function is unknown.
391 : : */
392 : : static int
3458 tgl@sss.pgh.pa.us 393 : 896 : find_func(yyscan_t yyscanner, const char *fname)
394 : : {
3457 395 : 896 : int i = 0;
396 : :
3476 rhaas@postgresql.org 397 [ + + ]: 12490 : while (PGBENCH_FUNCTIONS[i].fname)
398 : : {
399 [ + + ]: 12489 : if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
400 : 895 : return i;
401 : 11594 : i++;
402 : : }
403 : :
3458 tgl@sss.pgh.pa.us 404 : 1 : expr_yyerror_more(yyscanner, "unexpected function name", fname);
405 : :
406 : : /* not reached */
407 : : return -1;
408 : : }
409 : :
410 : : /* Expression linked list builder */
411 : : static PgBenchExprList *
3476 rhaas@postgresql.org 412 : 1769 : make_elist(PgBenchExpr *expr, PgBenchExprList *list)
413 : : {
414 : : PgBenchExprLink *cons;
415 : :
416 [ + + ]: 1769 : if (list == NULL)
417 : : {
418 : 890 : list = pg_malloc(sizeof(PgBenchExprList));
419 : 890 : list->head = NULL;
420 : 890 : list->tail = NULL;
421 : : }
422 : :
423 : 1769 : cons = pg_malloc(sizeof(PgBenchExprLink));
424 : 1769 : cons->expr = expr;
425 : 1769 : cons->next = NULL;
426 : :
427 [ + + ]: 1769 : if (list->head == NULL)
428 : 890 : list->head = cons;
429 : : else
430 : 879 : list->tail->next = cons;
431 : :
432 : 1769 : list->tail = cons;
433 : :
434 : 1769 : return list;
435 : : }
436 : :
437 : : /* Return the length of an expression list */
438 : : static int
439 : 895 : elist_length(PgBenchExprList *list)
440 : : {
3457 tgl@sss.pgh.pa.us 441 [ + + ]: 895 : PgBenchExprLink *link = list != NULL ? list->head : NULL;
442 : 895 : int len = 0;
443 : :
3476 rhaas@postgresql.org 444 [ + + ]: 2627 : for (; link != NULL; link = link->next)
445 : 1732 : len++;
446 : :
447 : 895 : return len;
448 : : }
449 : :
450 : : /* Build function call expression */
451 : : static PgBenchExpr *
3458 tgl@sss.pgh.pa.us 452 : 895 : make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
453 : : {
255 peter@eisentraut.org 454 : 895 : int len = elist_length(args);
455 : :
3841 rhaas@postgresql.org 456 : 895 : PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
457 : :
3476 458 [ - + ]: 895 : Assert(fnumber >= 0);
459 : :
460 : : /* validate arguments number including few special cases */
2726 teodor@sigaev.ru 461 [ + + + + : 895 : switch (PGBENCH_FUNCTIONS[fnumber].nargs)
+ ]
462 : : {
463 : : /* check at least one arg for least & greatest */
464 : 8 : case PGBENCH_NARGS_VARIABLE:
465 [ + + ]: 8 : if (len == 0)
466 : 3 : expr_yyerror_more(yyscanner, "at least one argument expected",
467 : 3 : PGBENCH_FUNCTIONS[fnumber].fname);
468 : 5 : break;
469 : :
470 : : /* case (when ... then ...)+ (else ...)? end */
471 : 14 : case PGBENCH_NARGS_CASE:
472 : : /* 'else' branch is always present, but could be a NULL-constant */
473 [ + - - + ]: 14 : if (len < 3 || len % 2 != 1)
2726 teodor@sigaev.ru 474 :UBC 0 : expr_yyerror_more(yyscanner,
475 : : "odd and >= 3 number of arguments expected",
476 : : "case control structure");
2726 teodor@sigaev.ru 477 :CBC 14 : break;
478 : :
479 : : /* hash functions with optional seed argument */
480 : 8 : case PGBENCH_NARGS_HASH:
2598 michael@paquier.xyz 481 [ + + + + ]: 8 : if (len < 1 || len > 2)
2726 teodor@sigaev.ru 482 : 2 : expr_yyerror_more(yyscanner, "unexpected number of arguments",
483 : 2 : PGBENCH_FUNCTIONS[fnumber].fname);
484 : :
485 [ + + ]: 6 : if (len == 1)
486 : : {
487 : 2 : PgBenchExpr *var = make_variable("default_seed");
488 : :
489 : 2 : args = make_elist(var, args);
490 : : }
491 : 6 : break;
492 : :
493 : : /* pseudorandom permutation function with optional seed argument */
1614 dean.a.rasheed@gmail 494 : 48 : case PGBENCH_NARGS_PERMUTE:
495 [ + + + + ]: 48 : if (len < 2 || len > 3)
496 : 2 : expr_yyerror_more(yyscanner, "unexpected number of arguments",
497 : 2 : PGBENCH_FUNCTIONS[fnumber].fname);
498 : :
499 [ + + ]: 46 : if (len == 2)
500 : : {
501 : 35 : PgBenchExpr *var = make_variable("default_seed");
502 : :
503 : 35 : args = make_elist(var, args);
504 : : }
505 : 46 : break;
506 : :
507 : : /* common case: positive arguments number */
2726 teodor@sigaev.ru 508 : 817 : default:
509 [ - + ]: 817 : Assert(PGBENCH_FUNCTIONS[fnumber].nargs >= 0);
510 : :
511 [ + + ]: 817 : if (PGBENCH_FUNCTIONS[fnumber].nargs != len)
512 : 1 : expr_yyerror_more(yyscanner, "unexpected number of arguments",
513 : 1 : PGBENCH_FUNCTIONS[fnumber].fname);
514 : : }
515 : :
3476 rhaas@postgresql.org 516 : 887 : expr->etype = ENODE_FUNCTION;
517 : 887 : expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
518 : :
519 : : /* only the link is used, the head/tail is not useful anymore */
3457 tgl@sss.pgh.pa.us 520 [ + + ]: 887 : expr->u.function.args = args != NULL ? args->head : NULL;
3476 rhaas@postgresql.org 521 [ + + ]: 887 : if (args)
522 : 886 : pg_free(args);
523 : :
3841 524 : 887 : return expr;
525 : : }
526 : :
527 : : static PgBenchExpr *
2797 teodor@sigaev.ru 528 : 14 : make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part)
529 : : {
530 : 14 : return make_func(yyscanner,
531 : : find_func(yyscanner, "!case_end"),
532 : : make_elist(else_part, when_then_list));
533 : : }
|