quakeforge/libs/util/cexpr-parse.y
Bill Currie 0dab26ce8f [cexpr] Allow untyped result values
If the result object type pointer is null, then the parsed result type
and value pointers are written directly to the result object rather than
testing the parsed result type against the object type and copying the
parsed result value data to the location of the object value. It is then
up to the caller to check the type and copy the value data.
2022-11-26 22:10:29 +09:00

434 lines
11 KiB
Text

/*
cexpr-parse.y
Config expression parser. Or concurrent.
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
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 <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdio.h>
#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 <op> '=' ASX
%right '?' ':'
%left OR
%left AND
%left '|'
%left '^'
%left '&'
%left EQ NE
%left LE GE LT GT
%left SHL SHR
%left '+' '-'
%left '*' '/' '%' MOD
%right <op> SIZEOF UNARY INCOP
%left HYPERUNARY
%left '.' '(' '['
%token <symbol> NAME
%token <value> VALUE
%type <value> expr field uexpr
%type <list> 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) {
$$ = (exprval_t *) cmemalloc (context->memsuper, sizeof (*$$));
$$->type = $1->type;
$$->value = $1->value;
} else {
cexpr_error (context, "undefined identifier %s",
cexpr_yyget_text (scanner));
}
}
| 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->result; f++) {
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;
}