2015-03-02 20:21:41 +01:00
|
|
|
%{
|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* exprparse.y
|
|
|
|
* bison grammar for a simple expression syntax
|
|
|
|
*
|
2016-01-02 19:33:40 +01:00
|
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
2015-03-02 20:21:41 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2016-03-19 21:35:41 +01:00
|
|
|
* src/bin/pgbench/exprparse.y
|
|
|
|
*
|
2015-03-02 20:21:41 +01:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
|
|
|
|
#include "pgbench.h"
|
|
|
|
|
|
|
|
PgBenchExpr *expr_parse_result;
|
|
|
|
|
2016-03-01 19:04:09 +01:00
|
|
|
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
|
2015-03-02 20:21:41 +01:00
|
|
|
static PgBenchExpr *make_integer_constant(int64 ival);
|
|
|
|
static PgBenchExpr *make_variable(char *varname);
|
2016-03-19 21:35:41 +01:00
|
|
|
static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
|
|
|
|
PgBenchExpr *lexpr, PgBenchExpr *rexpr);
|
|
|
|
static int find_func(yyscan_t yyscanner, const char *fname);
|
|
|
|
static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
|
2015-03-02 20:21:41 +01:00
|
|
|
|
|
|
|
%}
|
|
|
|
|
|
|
|
%expect 0
|
|
|
|
%name-prefix="expr_yy"
|
|
|
|
|
2016-03-19 21:35:41 +01:00
|
|
|
%parse-param {yyscan_t yyscanner}
|
|
|
|
%lex-param {yyscan_t yyscanner}
|
|
|
|
|
2015-03-02 20:21:41 +01:00
|
|
|
%union
|
|
|
|
{
|
|
|
|
int64 ival;
|
|
|
|
char *str;
|
|
|
|
PgBenchExpr *expr;
|
2016-03-01 19:04:09 +01:00
|
|
|
PgBenchExprList *elist;
|
2015-03-02 20:21:41 +01:00
|
|
|
}
|
|
|
|
|
2016-03-01 19:04:09 +01:00
|
|
|
%type <elist> elist
|
2015-03-02 20:21:41 +01:00
|
|
|
%type <expr> expr
|
2016-03-01 19:04:09 +01:00
|
|
|
%type <ival> INTEGER function
|
|
|
|
%type <str> VARIABLE FUNCTION
|
2015-03-29 19:06:59 +02:00
|
|
|
|
2016-03-01 19:04:09 +01:00
|
|
|
%token INTEGER VARIABLE FUNCTION
|
2015-03-02 20:21:41 +01:00
|
|
|
|
2015-03-29 19:06:59 +02:00
|
|
|
/* Precedence: lowest to highest */
|
2015-03-02 20:21:41 +01:00
|
|
|
%left '+' '-'
|
|
|
|
%left '*' '/' '%'
|
|
|
|
%right UMINUS
|
|
|
|
|
|
|
|
%%
|
|
|
|
|
|
|
|
result: expr { expr_parse_result = $1; }
|
|
|
|
|
2016-03-01 19:04:09 +01:00
|
|
|
elist: { $$ = NULL; }
|
|
|
|
| expr { $$ = make_elist($1, NULL); }
|
|
|
|
| elist ',' expr { $$ = make_elist($3, $1); }
|
|
|
|
;
|
|
|
|
|
2015-03-02 20:21:41 +01:00
|
|
|
expr: '(' expr ')' { $$ = $2; }
|
|
|
|
| '+' expr %prec UMINUS { $$ = $2; }
|
2016-03-19 21:35:41 +01:00
|
|
|
| '-' expr %prec UMINUS { $$ = make_op(yyscanner, "-",
|
|
|
|
make_integer_constant(0), $2); }
|
|
|
|
| expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); }
|
|
|
|
| expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); }
|
|
|
|
| expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); }
|
|
|
|
| expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
|
|
|
|
| expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); }
|
2015-03-02 20:21:41 +01:00
|
|
|
| INTEGER { $$ = make_integer_constant($1); }
|
|
|
|
| VARIABLE { $$ = make_variable($1); }
|
2016-03-19 21:35:41 +01:00
|
|
|
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
|
2016-03-01 19:04:09 +01:00
|
|
|
;
|
|
|
|
|
2016-03-19 21:35:41 +01:00
|
|
|
function: FUNCTION { $$ = find_func(yyscanner, $1); pg_free($1); }
|
2015-03-02 20:21:41 +01:00
|
|
|
;
|
|
|
|
|
|
|
|
%%
|
|
|
|
|
|
|
|
static PgBenchExpr *
|
|
|
|
make_integer_constant(int64 ival)
|
|
|
|
{
|
|
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
|
|
|
|
expr->etype = ENODE_INTEGER_CONSTANT;
|
|
|
|
expr->u.integer_constant.ival = ival;
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PgBenchExpr *
|
|
|
|
make_variable(char *varname)
|
|
|
|
{
|
|
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
|
|
|
|
expr->etype = ENODE_VARIABLE;
|
|
|
|
expr->u.variable.varname = varname;
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PgBenchExpr *
|
2016-03-19 21:35:41 +01:00
|
|
|
make_op(yyscan_t yyscanner, const char *operator,
|
|
|
|
PgBenchExpr *lexpr, PgBenchExpr *rexpr)
|
2016-03-01 19:04:09 +01:00
|
|
|
{
|
2016-03-19 21:35:41 +01:00
|
|
|
return make_func(yyscanner, find_func(yyscanner, operator),
|
2016-03-01 19:04:09 +01:00
|
|
|
make_elist(rexpr, make_elist(lexpr, NULL)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List of available functions:
|
|
|
|
* - fname: function name
|
|
|
|
* - nargs: number of arguments
|
|
|
|
* -1 is a special value for min & max meaning #args >= 1
|
|
|
|
* - tag: function identifier from PgBenchFunction enum
|
|
|
|
*/
|
|
|
|
static struct
|
|
|
|
{
|
|
|
|
char * fname;
|
|
|
|
int nargs;
|
|
|
|
PgBenchFunction tag;
|
|
|
|
} PGBENCH_FUNCTIONS[] = {
|
|
|
|
/* parsed as operators, executed as functions */
|
|
|
|
{ "+", 2, PGBENCH_ADD },
|
|
|
|
{ "-", 2, PGBENCH_SUB },
|
|
|
|
{ "*", 2, PGBENCH_MUL },
|
|
|
|
{ "/", 2, PGBENCH_DIV },
|
|
|
|
{ "%", 2, PGBENCH_MOD },
|
|
|
|
/* actual functions */
|
|
|
|
{ "abs", 1, PGBENCH_ABS },
|
|
|
|
{ "min", -1, PGBENCH_MIN },
|
|
|
|
{ "max", -1, PGBENCH_MAX },
|
|
|
|
{ "debug", 1, PGBENCH_DEBUG },
|
|
|
|
/* keep as last array element */
|
|
|
|
{ NULL, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a function from its name
|
|
|
|
*
|
|
|
|
* return the index of the function from the PGBENCH_FUNCTIONS array
|
|
|
|
* or fail if the function is unknown.
|
|
|
|
*/
|
|
|
|
static int
|
2016-03-19 21:35:41 +01:00
|
|
|
find_func(yyscan_t yyscanner, const char *fname)
|
2016-03-01 19:04:09 +01:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (PGBENCH_FUNCTIONS[i].fname)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
|
|
|
|
return i;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2016-03-19 21:35:41 +01:00
|
|
|
expr_yyerror_more(yyscanner, "unexpected function name", fname);
|
2016-03-01 19:04:09 +01:00
|
|
|
|
|
|
|
/* not reached */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expression linked list builder */
|
|
|
|
static PgBenchExprList *
|
|
|
|
make_elist(PgBenchExpr *expr, PgBenchExprList *list)
|
|
|
|
{
|
|
|
|
PgBenchExprLink * cons;
|
|
|
|
|
|
|
|
if (list == NULL)
|
|
|
|
{
|
|
|
|
list = pg_malloc(sizeof(PgBenchExprList));
|
|
|
|
list->head = NULL;
|
|
|
|
list->tail = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cons = pg_malloc(sizeof(PgBenchExprLink));
|
|
|
|
cons->expr = expr;
|
|
|
|
cons->next = NULL;
|
|
|
|
|
|
|
|
if (list->head == NULL)
|
|
|
|
list->head = cons;
|
|
|
|
else
|
|
|
|
list->tail->next = cons;
|
|
|
|
|
|
|
|
list->tail = cons;
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the length of an expression list */
|
|
|
|
static int
|
|
|
|
elist_length(PgBenchExprList *list)
|
|
|
|
{
|
|
|
|
PgBenchExprLink *link = list != NULL? list->head: NULL;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
for (; link != NULL; link = link->next)
|
|
|
|
len++;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build function call expression */
|
|
|
|
static PgBenchExpr *
|
2016-03-19 21:35:41 +01:00
|
|
|
make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
|
2015-03-02 20:21:41 +01:00
|
|
|
{
|
|
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
|
2016-03-01 19:04:09 +01:00
|
|
|
Assert(fnumber >= 0);
|
|
|
|
|
|
|
|
if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
|
|
|
|
PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
|
2016-03-19 21:35:41 +01:00
|
|
|
expr_yyerror_more(yyscanner, "unexpected number of arguments",
|
2016-03-01 19:04:09 +01:00
|
|
|
PGBENCH_FUNCTIONS[fnumber].fname);
|
|
|
|
|
|
|
|
/* check at least one arg for min & max */
|
|
|
|
if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
|
|
|
|
elist_length(args) == 0)
|
2016-03-19 21:35:41 +01:00
|
|
|
expr_yyerror_more(yyscanner, "at least one argument expected",
|
2016-03-01 19:04:09 +01:00
|
|
|
PGBENCH_FUNCTIONS[fnumber].fname);
|
|
|
|
|
|
|
|
expr->etype = ENODE_FUNCTION;
|
|
|
|
expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
|
|
|
|
|
|
|
|
/* only the link is used, the head/tail is not useful anymore */
|
|
|
|
expr->u.function.args = args != NULL? args->head: NULL;
|
|
|
|
if (args)
|
|
|
|
pg_free(args);
|
|
|
|
|
2015-03-02 20:21:41 +01:00
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2016-03-19 21:35:41 +01:00
|
|
|
/*
|
|
|
|
* exprscan.l is compiled as part of exprparse.y. Currently, this is
|
|
|
|
* unavoidable because exprparse does not create a .h file to export
|
|
|
|
* its token symbols. If these files ever grow large enough to be
|
|
|
|
* worth compiling separately, that could be fixed; but for now it
|
|
|
|
* seems like useless complication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* First, get rid of "#define yyscan_t" from pgbench.h */
|
|
|
|
#undef yyscan_t
|
|
|
|
|
2015-03-02 20:21:41 +01:00
|
|
|
#include "exprscan.c"
|