mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 22:31:05 +00:00
6f429311c9
While the cexpr parser itself doesn't support void functions, they have their uses when used with the system, and mixing them into the list of function overloads shouldn't break non-void functions.
437 lines
11 KiB
Text
437 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->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;
|
|
}
|