/* cexpr-parse.y Config expression parser. Or concurrent. Copyright (C) 2020 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ %define api.prefix {cexpr_yy} %define api.pure full %define api.push-pull push %define parse.trace %parse-param {void *scanner} {exprctx_t *context} %{ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/cmem.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/plist.h" #include "QF/sys.h" #include "QF/cexpr.h" static exprval_t *binary_expr (int op, const exprval_t *a, const exprval_t *b, exprctx_t *context); static exprval_t *field_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context); static exprval_t *index_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context); static exprval_t *unary_expr (int op, const exprval_t *val, exprctx_t *context); static exprval_t *vector_expr (exprlist_t *list, exprctx_t *context); static exprval_t *function_expr (exprsym_t *fsym, exprlist_t *list, exprctx_t *context); static exprlist_t *expr_item (exprval_t *val, exprctx_t *context); static void yyerror (void *scanner, exprctx_t *context, const char *s) { cexpr_error (context, "%s before %s", s, cexpr_yyget_text (scanner)); } %} %left COMMA %right '=' ASX %right '?' ':' %left OR %left AND %left '|' %left '^' %left '&' %left EQ NE %left LE GE LT GT %left SHL SHR %left '+' '-' %left '*' '/' '%' MOD %right SIZEOF UNARY INCOP %left HYPERUNARY %left '.' '(' '[' %token NAME %token VALUE %type expr field uexpr %type opt_arg_list arg_list arg_expr %union { int op; exprsym_t *symbol; exprval_t *value; exprlist_t *list; const char *string; } %% start : expr { cexpr_assign_value (context->result, $1, context); } ; uexpr : NAME { if ($1->value) { $$ = (exprval_t *) cmemalloc (context->memsuper, sizeof (*$$)); $$->type = $1->type; $$->value = $1->value; } else { cexpr_error (context, "undefined identifier %s", $1->name); $$ = 0; } } | VALUE | '[' arg_list ']' { $$ = vector_expr ($2, context); } | '(' expr ')' { $$ = $2; } | NAME '(' opt_arg_list ')' { $$ = function_expr ($1, $3, context); } | uexpr '.' field { $$ = field_expr ($1, $3, context); } | uexpr '[' expr ']' { $$ = index_expr ($1, $3, context); } | '+' uexpr %prec UNARY { $$ = $2; } | '-' uexpr %prec UNARY { $$ = unary_expr ('-', $2, context); } | '!' uexpr %prec UNARY { $$ = unary_expr ('!', $2, context); } | '~' uexpr %prec UNARY { $$ = unary_expr ('~', $2, context); } ; expr : uexpr | expr '=' expr { $$ = cexpr_assign_value ($1, $3, context); } | expr SHL expr { $$ = binary_expr (SHL, $1, $3, context); } | expr SHR expr { $$ = binary_expr (SHR, $1, $3, context); } | expr '+' expr { $$ = binary_expr ('+', $1, $3, context); } | expr '-' expr { $$ = binary_expr ('-', $1, $3, context); } | expr '*' expr { $$ = binary_expr ('*', $1, $3, context); } | expr '/' expr { $$ = binary_expr ('/', $1, $3, context); } | expr '&' expr { $$ = binary_expr ('&', $1, $3, context); } | expr '|' expr { $$ = binary_expr ('|', $1, $3, context); } | expr '^' expr { $$ = binary_expr ('^', $1, $3, context); } | expr '%' expr { $$ = binary_expr ('%', $1, $3, context); } | expr MOD expr { $$ = binary_expr (MOD, $1, $3, context); } ; field : NAME { exprctx_t *ctx = context; const char *name = cexpr_yyget_text (scanner); size_t size = strlen (name) + 1; //FIXME reuse strings $$ = (exprval_t *) cmemalloc (ctx->memsuper, sizeof (exprval_t)); $$->type = &cexpr_field; $$->value = cmemalloc (ctx->memsuper, size); memcpy ($$->value, name, size); } ; opt_arg_list : { $$ = 0; } | arg_list { $$ = $1; } ; arg_list : arg_expr | arg_list ',' arg_expr { $3-> next = $1; $$ = $3; } arg_expr : expr { $$ = expr_item ($1, context); } ; %% exprval_t * cexpr_assign_value (exprval_t *dst, const exprval_t *src, exprctx_t *context) { binop_t *binop = 0; if (!dst || !src) { return 0; } if (dst->type == &cexpr_exprval) { *(exprval_t **) dst->value = (exprval_t *) src; return dst; } if (dst->type) { binop = cexpr_find_cast (dst->type, src->type); } if (binop && binop->op) { binop->func (dst, src, dst, context); } else { if (!dst->type) { dst->type = src->type; dst->value = src->value; } else { if (dst->type != src->type) { cexpr_error (context, "type mismatch in expression result: %s = %s", dst->type->name, src->type->name); return dst; } memcpy (dst->value, src->value, dst->type->size); } } return dst; } static exprval_t * binary_expr (int op, const exprval_t *a, const exprval_t *b, exprctx_t *context) { binop_t *binop; for (binop = a->type->binops; binop->op; binop++) { exprtype_t *otype = binop->other; if (!otype) { otype = a->type; } if (binop->op == op && otype == b->type) { break; } } exprtype_t *rtype = binop->result; if (!rtype) { rtype = a->type; } exprval_t *result = cexpr_value (rtype, context); if (!binop->op) { cexpr_error (context, "invalid binary expression: %s %c %s", a->type->name, op, b->type->name); memset (result->value, 0, rtype->size); } else { binop->func (a, b, result, context); } return result; } static exprval_t * field_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context) { binop_t *binop; exprval_t *result = 0; if (!a) { return 0; } for (binop = a->type->binops; binop->op; binop++) { if (binop->op == '.' && binop->other == b->type) { break; } } if (!binop->op) { cexpr_error (context, "invalid binary expression: %s.%s", a->type->name, b->type->name); result = cexpr_value (&cexpr_int, context); *(int *) result->value = 0; } else { exprval_t c = { 0, &result }; binop->func (a, b, &c, context); } return result; } static exprval_t * index_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context) { binop_t *binop; exprval_t *result = 0; if (!a || !b) { return 0; } for (binop = a->type->binops; binop->op; binop++) { if (binop->op == '[' && binop->other == b->type) { break; } } if (!binop->op) { cexpr_error (context, "invalid index expression: %s.%s", a->type->name, b->type->name); result = cexpr_value (&cexpr_int, context); *(int *) result->value = 0; } else { exprval_t c = { 0, &result }; binop->func (a, b, &c, context); } return result; } static exprval_t * unary_expr (int op, const exprval_t *val, exprctx_t *context) { unop_t *unop; for (unop = val->type->unops; unop->op; unop++) { if (unop->op == op) { break; } } exprtype_t *rtype = unop->result; if (!rtype) { rtype = val->type; } exprval_t *result = cexpr_value (rtype, context); if (!unop->op) { cexpr_error (context, "invalid unary expression: %c %s", op, val->type->name); } else { unop->func (val, result, context); } return result; } exprval_t * vector_expr (exprlist_t *list, exprctx_t *context) { exprlist_t *l; exprval_t *val = cexpr_value (&cexpr_vector, context); float *vector = val->value; int i; exprlist_t *rlist = 0; // list is built in reverse order, so need to reverse it to make converting // to an array easier while (list) { exprlist_t *t = list->next; list->next = rlist; rlist = list; list = t; } list = rlist; for (i = 0; i < 4 && list; i++, list = l) { exprval_t dst = { &cexpr_float, &vector[i] }; exprval_t *src = list->value; binop_t *cast = cexpr_find_cast (&cexpr_float, src->type); if (cast) { cast->func (&dst, src, &dst, context); } else { cexpr_error (context, "invalid vector expression type: [%d] %s", i, val->type->name); } l = list->next; cmemfree (context->memsuper, list); } if (i == 4 && list) { cexpr_error (context, "excess elements in vector expression"); } for ( ; i < 4; i++) { vector[i] = 0; } return val; } static exprval_t *function_expr (exprsym_t *fsym, exprlist_t *list, exprctx_t *context) { exprlist_t *l; int num_args = 1;// one extra for terminating null exprfunc_t *func = 0; exprval_t *result; for (l = list; l; l = l->next) { num_args++; } __auto_type args = (const exprval_t **) alloca (num_args * sizeof (exprval_t *)); args[num_args - 1] = 0; // terminate array of args for varargs functions __auto_type types = (exprtype_t **) alloca (num_args * sizeof (exprtype_t *)); for (num_args = 0; list; list = l, num_args++) { args[num_args] = list->value; types[num_args] = list->value->type; l = list->next; cmemfree (context->memsuper, list); } if (fsym->type != &cexpr_function) { cexpr_error (context, "invalid function %s", fsym->name); result = cexpr_value (&cexpr_int, context); *(int *) result->value = 0; return result; } for (exprfunc_t *f = fsym->value; f->func; f++) { if (!f->result) { continue; } int num_params = f->num_params; if (num_params >= 0 && num_args == num_params) { } else if (num_params < 0 && num_args >= ~num_params) { num_params = ~num_params; } else { continue; } if (!num_params || memcmp (f->param_types, types, num_args * sizeof (exprtype_t *)) == 0) { func = f; break; } } if (!func) { dstring_t *argstr = dstring_newstr(); for (int i = 0; i < num_args; i++) { dasprintf (argstr, "%s%s", types[i]->name, i + 1 < num_args ? ", ": ""); } cexpr_error (context, "no overload for %s(%s)", fsym->name, argstr->str); dstring_delete (argstr); result = cexpr_value (&cexpr_int, context); *(int *) result->value = 0; return result; } result = cexpr_value (func->result, context); func->func (args, result, context); return result; } static exprlist_t * expr_item (exprval_t *val, exprctx_t *context) { __auto_type item = (exprlist_t *) cmemalloc (context->memsuper, sizeof (exprlist_t)); item->next = 0; item->value = val; return item; }