mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 04:42:32 +00:00
[util] Add a mini expression parser
It is capable of parsing single expressions with fairly simple operations. It current supports ints, enums, cvars and (external) data structs. It is also thread-safe (in theory, needs proper testing) and the memory it uses can be mass-freed.
This commit is contained in:
parent
591667c36d
commit
f3682638d4
8 changed files with 1185 additions and 0 deletions
118
include/QF/cexpr.h
Normal file
118
include/QF/cexpr.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
cexpr.h
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
#ifndef __expr_h
|
||||
#define __expr_h
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct exprval_s;
|
||||
struct exprctx_s;
|
||||
|
||||
typedef struct binop_s {
|
||||
int op;
|
||||
struct exprtype_s *other;
|
||||
struct exprtype_s *result;
|
||||
void (*func) (const struct exprval_s *val1,
|
||||
const struct exprval_s *val2,
|
||||
struct exprval_s *result,
|
||||
struct exprctx_s *context);
|
||||
} binop_t;
|
||||
|
||||
typedef struct unop_s {
|
||||
int op;
|
||||
struct exprtype_s *result;
|
||||
void (*func) (const struct exprval_s *val, struct exprval_s *result,
|
||||
struct exprctx_s *context);
|
||||
} unop_t;
|
||||
|
||||
typedef struct exprtype_s {
|
||||
const char *name;
|
||||
size_t size;
|
||||
binop_t *binops;
|
||||
unop_t *unops;
|
||||
void *data;
|
||||
} exprtype_t;
|
||||
|
||||
typedef struct exprval_s {
|
||||
exprtype_t *type;
|
||||
void *value;
|
||||
} exprval_t;
|
||||
|
||||
typedef struct exprsym_s {
|
||||
const char *name;
|
||||
exprtype_t *type;
|
||||
void *value;
|
||||
struct exprsym_s *next;
|
||||
} exprsym_t;
|
||||
|
||||
typedef struct exprtab_s {
|
||||
exprsym_t *symbols;
|
||||
struct hashtab_s *tab;
|
||||
} exprtab_t;
|
||||
|
||||
typedef struct exprctx_s {
|
||||
exprval_t *result;
|
||||
exprtab_t *symtab; // directly accessible symbols
|
||||
exprtab_t *external_variables; // accessible via $id
|
||||
struct memsuper_s *memsuper;
|
||||
const struct plitem_s *item;
|
||||
struct plitem_s *messages;
|
||||
struct hashlink_s *hashlinks;
|
||||
} exprctx_t;
|
||||
|
||||
typedef struct exprenum_s {
|
||||
exprtype_t *type;
|
||||
exprtab_t *symtab;
|
||||
} exprenum_t;
|
||||
|
||||
int cexpr_parse_enum (exprenum_t *enm, const char *str,
|
||||
const exprctx_t *context, void *data);
|
||||
binop_t *cexpr_find_cast (exprtype_t *dst_type, exprtype_t *src_type) __attribute__((pure));
|
||||
exprval_t *cexpr_value (exprtype_t *type, exprctx_t *ctx);
|
||||
exprval_t *cexpr_value_reference (exprtype_t *type, void *data, exprctx_t *ctx);
|
||||
int cexpr_eval_string (const char *str, exprctx_t *context);
|
||||
void cexpr_error(exprctx_t *ctx, const char *fmt, ...) __attribute__((format(printf,2,3)));
|
||||
|
||||
void cexpr_struct_getfield (const exprval_t *a, const exprval_t *b,
|
||||
exprval_t *c, exprctx_t *ctx);
|
||||
exprval_t *cexpr_cvar (const char *name, exprctx_t *ctx);
|
||||
exprval_t *cexpr_cvar_struct (exprctx_t *ctx);
|
||||
|
||||
void cexpr_init_symtab (exprtab_t *symtab, exprctx_t *ctx);
|
||||
|
||||
char *cexpr_yyget_text (void *scanner);
|
||||
|
||||
extern exprtype_t cexpr_int;
|
||||
extern exprtype_t cexpr_uint;
|
||||
extern exprtype_t cexpr_float;
|
||||
extern exprtype_t cexpr_double;
|
||||
extern exprtype_t cexpr_exprval;
|
||||
extern exprtype_t cexpr_field;
|
||||
|
||||
extern binop_t cexpr_struct_binops[];
|
||||
|
||||
#endif
|
|
@ -43,6 +43,10 @@ libs_util_libQFutil_la_SOURCES= \
|
|||
libs/util/bspfile.c \
|
||||
libs/util/buildnum.c \
|
||||
libs/util/cbuf.c \
|
||||
libs/util/cexpr-lex.l \
|
||||
libs/util/cexpr-parse.y \
|
||||
libs/util/cexpr-type.c \
|
||||
libs/util/cexpr-vars.c \
|
||||
libs/util/checksum.c \
|
||||
libs/util/cmd.c \
|
||||
libs/util/cmem.c \
|
||||
|
@ -82,4 +86,13 @@ libs_util_libQFutil_la_SOURCES= \
|
|||
libs/util/zone.c \
|
||||
$(dirent) $(fnmatch) $(getopt)
|
||||
|
||||
BUILT_SOURCES += \
|
||||
libs/util/cexpr-lex.c \
|
||||
libs/util/cexpr-parse.c
|
||||
|
||||
libs/util/cexpr-parse.c: libs/util/cexpr-parse.y
|
||||
$(AM_V_YACC)$(YACCCOMPILE) $< -o $@
|
||||
libs/util/cexpr-lex.c: libs/util/cexpr-lex.l libs/util/cexpr-parse.h
|
||||
$(AM_V_LEX)$(LEXCOMPILE) -o$@ $<
|
||||
|
||||
EXTRA_DIST += $(fnmatch_src) $(getopt_src)
|
||||
|
|
281
libs/util/cexpr-lex.l
Normal file
281
libs/util/cexpr-lex.l
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
cexpr-lex.l
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
%option bison-bridge
|
||||
%option reentrant
|
||||
%option prefix="cexpr_yy"
|
||||
%option noyywrap
|
||||
|
||||
%{
|
||||
|
||||
#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 <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "QF/cmem.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/hash.h"
|
||||
#include "QF/qfplist.h"
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "QF/cexpr.h"
|
||||
#include "libs/util/cexpr-parse.h"
|
||||
|
||||
#define YY_NO_INPUT
|
||||
#define YY_NO_UNPUT
|
||||
|
||||
#define YYSTYPE CEXPR_YYSTYPE
|
||||
#define YY_EXTRA_TYPE exprctx_t *
|
||||
|
||||
exprctx_t *cexpr_yyget_extra (yyscan_t yyscanner) __attribute__((pure));
|
||||
int cexpr_yyget_lineno (yyscan_t yyscanner) __attribute__((pure));
|
||||
int cexpr_yyget_column (yyscan_t yyscanner) __attribute__((pure));
|
||||
YYSTYPE *cexpr_yyget_lval (yyscan_t yyscanner) __attribute__((pure));
|
||||
int cexpr_yyget_debug (yyscan_t yyscanner) __attribute__((pure));
|
||||
char *cexpr_yyget_text (yyscan_t yyscanner) __attribute__((pure));
|
||||
int cexpr_yyget_leng (yyscan_t yyscanner) __attribute__((pure));
|
||||
FILE *cexpr_yyget_out (yyscan_t yyscanner) __attribute__((pure));
|
||||
FILE *cexpr_yyget_in (yyscan_t yyscanner) __attribute__((pure));
|
||||
|
||||
static exprval_t *parse_int (const char *str, exprctx_t *context);
|
||||
static exprval_t *parse_uint (const char *str, exprctx_t *context);
|
||||
static exprval_t *parse_float (const char *str, exprctx_t *context);
|
||||
static exprval_t *parse_double (const char *str, exprctx_t *context);
|
||||
static exprsym_t *parse_name (const char *str, exprctx_t *context);
|
||||
static exprval_t *parse_variable (const char *str, exprctx_t *context);
|
||||
|
||||
VISIBLE void
|
||||
cexpr_error(exprctx_t *ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
dstring_t *string;
|
||||
|
||||
string = dstring_new ();
|
||||
|
||||
va_start (args, fmt);
|
||||
dvsprintf (string, fmt, args);
|
||||
va_end (args);
|
||||
|
||||
if (ctx->messages) {
|
||||
PL_Message (ctx->messages, ctx->item, "%s", string->str);
|
||||
} else {
|
||||
Sys_Printf ("%s\n", string->str);
|
||||
}
|
||||
dstring_delete (string);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
s [ \t]
|
||||
m [\-+]
|
||||
D [0-9]
|
||||
B [01]
|
||||
X [0-9a-fA-F]
|
||||
ID [a-zA-Z_][a-zA-Z_0-9]*
|
||||
FLOAT ({D}+|{D}*\.{D}+|{D}+\.{D}*)([eE]{m}?{D}+)?
|
||||
FLOATf {FLOAT}[fF]
|
||||
FLOATd {FLOAT}[dD]
|
||||
INT ({D}+|0[xX]{X}+|0[bB]{B})
|
||||
STRING \"(\\.|[^"\\])*\"
|
||||
|
||||
%%
|
||||
%{
|
||||
__auto_type context = yyget_extra (yyscanner);
|
||||
%}
|
||||
|
||||
{INT}+ {
|
||||
yylval->value = parse_int (yytext, context);
|
||||
return VALUE;
|
||||
}
|
||||
|
||||
{INT}+[uU] {
|
||||
yylval->value = parse_uint (yytext, context);
|
||||
return VALUE;
|
||||
}
|
||||
|
||||
{FLOAT} {
|
||||
yylval->value = parse_double (yytext, context);
|
||||
return VALUE;
|
||||
}
|
||||
{FLOATf} {
|
||||
yylval->value = parse_float (yytext, context);
|
||||
return VALUE;
|
||||
}
|
||||
{FLOATd} {
|
||||
yylval->value = parse_double (yytext, context);
|
||||
return VALUE;
|
||||
}
|
||||
|
||||
{ID} {
|
||||
yylval->symbol = parse_name (yytext, context);
|
||||
return NAME;
|
||||
}
|
||||
|
||||
\${ID} {
|
||||
yylval->value = parse_variable (yytext + 1, context);
|
||||
return VALUE;
|
||||
}
|
||||
|
||||
{STRING} {
|
||||
}
|
||||
@ return '@';
|
||||
|
||||
'(\\[^xX0-7\r\n]|[^'\r\n]|\\[xX][0-9A-Fa-f]+|\\[0-7]+)*' {
|
||||
}
|
||||
|
||||
[+\-*/&|^%]= {
|
||||
}
|
||||
|
||||
"%%=" {
|
||||
}
|
||||
|
||||
"<<=" {
|
||||
}
|
||||
|
||||
">>=" {
|
||||
}
|
||||
|
||||
[!(){}.*/&|^~+\-=\[\];,#%?:] {
|
||||
return yytext[0];
|
||||
}
|
||||
|
||||
"%%" {
|
||||
}
|
||||
|
||||
"<<" return SHL;
|
||||
">>" return SHR;
|
||||
|
||||
"&&" return AND;
|
||||
"||" return OR;
|
||||
"==" return EQ;
|
||||
"!=" return NE;
|
||||
"<=" return LE;
|
||||
">=" return GE;
|
||||
"<" return LT;
|
||||
">" return GT;
|
||||
|
||||
"++" {
|
||||
}
|
||||
|
||||
"--" {
|
||||
}
|
||||
|
||||
<*>\r*\n {
|
||||
}
|
||||
|
||||
<*>{s}* /* skip */
|
||||
|
||||
<*>. cexpr_error (context, "all your typo are belong to us");
|
||||
|
||||
%%
|
||||
|
||||
VISIBLE int
|
||||
cexpr_eval_string (const char *str, exprctx_t *context)
|
||||
{
|
||||
int status;
|
||||
yyscan_t scanner;
|
||||
cexpr_yypstate *ps = cexpr_yypstate_new ();
|
||||
|
||||
yylex_init_extra (context, &scanner);
|
||||
yy_scan_string (str, scanner);
|
||||
|
||||
do {
|
||||
CEXPR_YYSTYPE lval;
|
||||
int token = yylex (&lval, scanner);
|
||||
status = cexpr_yypush_parse (ps, token, &lval, scanner, context);
|
||||
} while (status == YYPUSH_MORE);
|
||||
|
||||
yylex_destroy (scanner);
|
||||
cexpr_yypstate_delete (ps);
|
||||
return status;
|
||||
}
|
||||
|
||||
static exprval_t *parse_int (const char *str, exprctx_t *context)
|
||||
{
|
||||
exprval_t *val = cexpr_value (&cexpr_int, context);
|
||||
*(int *) val->value = strtoimax (str, 0, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static exprval_t *parse_uint (const char *str, exprctx_t *context)
|
||||
{
|
||||
exprval_t *val = cexpr_value (&cexpr_uint, context);
|
||||
*(unsigned *) val->value = strtoumax (str, 0, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static exprval_t *parse_float (const char *str, exprctx_t *context)
|
||||
{
|
||||
exprval_t *val = cexpr_value (&cexpr_int, context);
|
||||
*(float *) val->value = strtof (str, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static exprval_t *parse_double (const char *str, exprctx_t *context)
|
||||
{
|
||||
exprval_t *val = cexpr_value (&cexpr_int, context);
|
||||
*(double *) val->value = strtod (str, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static exprsym_t *
|
||||
parse_name (const char *name, exprctx_t *context)
|
||||
{
|
||||
exprtab_t *symtab = context->symtab;
|
||||
|
||||
__auto_type sym = (exprsym_t *) Hash_Find (symtab->tab, name);
|
||||
return sym;
|
||||
}
|
||||
|
||||
static exprval_t *
|
||||
parse_variable (const char *name, exprctx_t *context)
|
||||
{
|
||||
exprval_t *val = 0;
|
||||
if (strcmp (name, "cvars") == 0) {
|
||||
val = cexpr_cvar_struct (context);
|
||||
} else {
|
||||
exprtab_t *symtab = context->external_variables;
|
||||
__auto_type sym = (exprsym_t *) Hash_Find (symtab->tab, name);
|
||||
if (sym) {
|
||||
val = cexpr_value_reference (sym->type, sym->value, context);
|
||||
} else {
|
||||
val = cexpr_cvar (name, context);
|
||||
}
|
||||
}
|
||||
if (!val) {
|
||||
cexpr_error (context, "undefined variable %s", name);
|
||||
}
|
||||
return val;
|
||||
}
|
219
libs/util/cexpr-parse.y
Normal file
219
libs/util/cexpr-parse.y
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
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
|
||||
%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/hash.h"
|
||||
#include "QF/qfplist.h"
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "QF/cexpr.h"
|
||||
|
||||
static void assign_expr (exprval_t *dst, const exprval_t *src,
|
||||
exprctx_t *context);
|
||||
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 void
|
||||
yyerror (void *scanner, exprctx_t *context, const char *s)
|
||||
{
|
||||
cexpr_error (context, "%s before %s\n", 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 SIZEOF UNARY INCOP
|
||||
%left HYPERUNARY
|
||||
%left '.' '(' '['
|
||||
|
||||
%token <symbol> NAME
|
||||
%token <value> VALUE
|
||||
|
||||
%type <value> expr field
|
||||
|
||||
%union {
|
||||
int op;
|
||||
exprsym_t *symbol;
|
||||
exprval_t *value;
|
||||
const char *string;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
start
|
||||
: expr { assign_expr (context->result, $1, context); }
|
||||
;
|
||||
|
||||
expr
|
||||
: 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 '.' field { $$ = field_expr ($1, $3, context); }
|
||||
| expr MOD expr { $$ = binary_expr (MOD, $1, $3, context); }
|
||||
| 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
|
||||
;
|
||||
|
||||
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);
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
static void
|
||||
assign_expr (exprval_t *dst, const exprval_t *src, exprctx_t *context)
|
||||
{
|
||||
binop_t *binop;
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
for (binop = dst->type->binops; binop && binop->op; binop++) {
|
||||
if (binop->op == '=' && binop->other == src->type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (binop && binop->op) {
|
||||
binop->func (dst, src, dst, context);
|
||||
} else {
|
||||
if (dst->type != src->type) {
|
||||
cexpr_error (context,
|
||||
"type mismatch in expression result: %s = %s\n",
|
||||
dst->type->name, src->type->name);
|
||||
return;
|
||||
}
|
||||
memcpy (dst->value, src->value, dst->type->size);
|
||||
}
|
||||
}
|
||||
|
||||
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 && binop->other == 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\n",
|
||||
a->type->name, op, b->type->name);
|
||||
} 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;
|
||||
|
||||
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\n",
|
||||
a->type->name, b->type->name);
|
||||
} else {
|
||||
exprval_t c = { 0, &result };
|
||||
binop->func (a, b, &c, context);
|
||||
}
|
||||
return result;
|
||||
}
|
314
libs/util/cexpr-type.c
Normal file
314
libs/util/cexpr-type.c
Normal file
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
cexpr-type.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#include "QF/cexpr.h"
|
||||
|
||||
#include "libs/util/cexpr-parse.h"
|
||||
|
||||
#define BINOP(pre, opname, type, op) \
|
||||
static void \
|
||||
pre##_##opname (const exprval_t *a, const exprval_t *b, exprval_t *c, \
|
||||
exprctx_t *ctx) \
|
||||
{ \
|
||||
(*(type *) c->value) = (*(type *) a->value) op (*(type *) b->value); \
|
||||
}
|
||||
|
||||
#define UNOP(pre, opname, type, op) \
|
||||
static void \
|
||||
pre##_##opname (const exprval_t *a, exprval_t *b, exprctx_t *ctx) \
|
||||
{ \
|
||||
(*(type *) b->value) = op (*(type *) a->value); \
|
||||
}
|
||||
|
||||
BINOP(int, shl, int, <<)
|
||||
BINOP(int, shr, int, >>)
|
||||
BINOP(int, add, int, +)
|
||||
BINOP(int, sub, int, -)
|
||||
BINOP(int, mul, int, *)
|
||||
BINOP(int, div, int, /)
|
||||
BINOP(int, band, int, &)
|
||||
BINOP(int, bor, int, |)
|
||||
BINOP(int, xor, int, ^)
|
||||
BINOP(int, rem, int, %)
|
||||
|
||||
UNOP(int, pos, int, +)
|
||||
UNOP(int, neg, int, -)
|
||||
UNOP(int, tnot, int, !)
|
||||
UNOP(int, bnot, int, ~)
|
||||
|
||||
static void
|
||||
int_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
// implement true modulo for integers:
|
||||
// 5 mod 3 = 2
|
||||
// -5 mod 3 = 1
|
||||
// 5 mod -3 = -1
|
||||
// -5 mod -3 = -2
|
||||
int a = *(int *) val1->value;
|
||||
int b = *(int *) val2->value;
|
||||
int c = a % b;
|
||||
// % is really remainder and so has the same sign rules
|
||||
// as division: -5 % 3 = -2, so need to add b (3 here)
|
||||
// if c's sign is incorrect, but only if c is non-zero
|
||||
int mask = (a ^ b) >> 31;
|
||||
mask &= ~(!!c + 0) + 1; // +0 to convert bool to int (gcc)
|
||||
*(int *) result->value = c + (mask & b);
|
||||
}
|
||||
|
||||
binop_t int_binops[] = {
|
||||
{ SHL, &cexpr_int, &cexpr_int, int_shl },
|
||||
{ SHR, &cexpr_int, &cexpr_int, int_shr },
|
||||
{ '+', &cexpr_int, &cexpr_int, int_add },
|
||||
{ '-', &cexpr_int, &cexpr_int, int_sub },
|
||||
{ '*', &cexpr_int, &cexpr_int, int_mul },
|
||||
{ '/', &cexpr_int, &cexpr_int, int_div },
|
||||
{ '&', &cexpr_int, &cexpr_int, int_band },
|
||||
{ '|', &cexpr_int, &cexpr_int, int_bor },
|
||||
{ '^', &cexpr_int, &cexpr_int, int_xor },
|
||||
{ '%', &cexpr_int, &cexpr_int, int_rem },
|
||||
{ MOD, &cexpr_int, &cexpr_int, int_mod },
|
||||
{}
|
||||
};
|
||||
|
||||
unop_t int_unops[] = {
|
||||
{ '+', &cexpr_int, int_pos },
|
||||
{ '-', &cexpr_int, int_neg },
|
||||
{ '!', &cexpr_int, int_tnot },
|
||||
{ '~', &cexpr_int, int_bnot },
|
||||
{}
|
||||
};
|
||||
|
||||
exprtype_t cexpr_int = {
|
||||
"int",
|
||||
sizeof (int),
|
||||
int_binops,
|
||||
int_unops,
|
||||
};
|
||||
|
||||
BINOP(uint, shl, unsigned, <<)
|
||||
BINOP(uint, shr, unsigned, >>)
|
||||
BINOP(uint, add, unsigned, +)
|
||||
BINOP(uint, sub, unsigned, -)
|
||||
BINOP(uint, mul, unsigned, *)
|
||||
BINOP(uint, div, unsigned, /)
|
||||
BINOP(uint, band, unsigned, &)
|
||||
BINOP(uint, bor, unsigned, |)
|
||||
BINOP(uint, xor, unsigned, ^)
|
||||
BINOP(uint, rem, unsigned, %)
|
||||
|
||||
UNOP(uint, pos, unsigned, +)
|
||||
UNOP(uint, neg, unsigned, -)
|
||||
UNOP(uint, tnot, unsigned, !)
|
||||
UNOP(uint, bnot, unsigned, ~)
|
||||
|
||||
binop_t uint_binops[] = {
|
||||
{ SHL, &cexpr_uint, &cexpr_uint, uint_shl },
|
||||
{ SHR, &cexpr_uint, &cexpr_uint, uint_shr },
|
||||
{ '+', &cexpr_uint, &cexpr_uint, uint_add },
|
||||
{ '-', &cexpr_uint, &cexpr_uint, uint_sub },
|
||||
{ '*', &cexpr_uint, &cexpr_uint, uint_mul },
|
||||
{ '/', &cexpr_uint, &cexpr_uint, uint_div },
|
||||
{ '&', &cexpr_uint, &cexpr_uint, uint_band },
|
||||
{ '|', &cexpr_uint, &cexpr_uint, uint_bor },
|
||||
{ '^', &cexpr_uint, &cexpr_uint, uint_xor },
|
||||
{ '%', &cexpr_uint, &cexpr_uint, uint_rem },
|
||||
{ MOD, &cexpr_uint, &cexpr_uint, uint_rem },
|
||||
{}
|
||||
};
|
||||
|
||||
unop_t uint_unops[] = {
|
||||
{ '+', &cexpr_uint, uint_pos },
|
||||
{ '-', &cexpr_uint, uint_neg },
|
||||
{ '!', &cexpr_uint, uint_tnot },
|
||||
{ '~', &cexpr_uint, uint_bnot },
|
||||
{}
|
||||
};
|
||||
|
||||
exprtype_t cexpr_uint = {
|
||||
"uint",
|
||||
sizeof (unsigned),
|
||||
uint_binops,
|
||||
uint_unops,
|
||||
};
|
||||
|
||||
BINOP(float, add, float, +)
|
||||
BINOP(float, sub, float, -)
|
||||
BINOP(float, mul, float, *)
|
||||
BINOP(float, div, float, /)
|
||||
|
||||
static void
|
||||
float_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
float a = *(float *) val1->value;
|
||||
float b = *(float *) val2->value;
|
||||
*(float *) result->value = a - b * truncf (a / b);
|
||||
}
|
||||
|
||||
static void
|
||||
float_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
// implement true modulo for integers:
|
||||
// 5 mod 3 = 2
|
||||
// -5 mod 3 = 1
|
||||
// 5 mod -3 = -1
|
||||
// -5 mod -3 = -2
|
||||
float a = *(float *) val1->value;
|
||||
float b = *(float *) val2->value;
|
||||
*(float *) result->value = a - b * floorf (a / b);
|
||||
}
|
||||
|
||||
UNOP(float, pos, float, +)
|
||||
UNOP(float, neg, float, -)
|
||||
UNOP(float, tnot, float, !)
|
||||
|
||||
binop_t float_binops[] = {
|
||||
{ '+', &cexpr_float, &cexpr_float, float_add },
|
||||
{ '-', &cexpr_float, &cexpr_float, float_sub },
|
||||
{ '*', &cexpr_float, &cexpr_float, float_mul },
|
||||
{ '/', &cexpr_float, &cexpr_float, float_div },
|
||||
{ '%', &cexpr_float, &cexpr_float, float_rem },
|
||||
{ MOD, &cexpr_float, &cexpr_float, float_mod },
|
||||
{}
|
||||
};
|
||||
|
||||
unop_t float_unops[] = {
|
||||
{ '+', &cexpr_float, float_pos },
|
||||
{ '-', &cexpr_float, float_neg },
|
||||
{ '!', &cexpr_float, float_tnot },
|
||||
{}
|
||||
};
|
||||
|
||||
exprtype_t cexpr_float = {
|
||||
"float",
|
||||
sizeof (float),
|
||||
float_binops,
|
||||
float_unops,
|
||||
};
|
||||
|
||||
BINOP(double, add, double, +)
|
||||
BINOP(double, sub, double, -)
|
||||
BINOP(double, mul, double, *)
|
||||
BINOP(double, div, double, /)
|
||||
|
||||
static void
|
||||
double_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
double a = *(double *) val1->value;
|
||||
double b = *(double *) val2->value;
|
||||
*(double *) result->value = a - b * truncf (a / b);
|
||||
}
|
||||
|
||||
static void
|
||||
double_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
// implement true modulo for integers:
|
||||
// 5 mod 3 = 2
|
||||
// -5 mod 3 = 1
|
||||
// 5 mod -3 = -1
|
||||
// -5 mod -3 = -2
|
||||
double a = *(double *) val1->value;
|
||||
double b = *(double *) val2->value;
|
||||
*(double *) result->value = a - b * floorf (a / b);
|
||||
}
|
||||
|
||||
UNOP(double, pos, double, +)
|
||||
UNOP(double, neg, double, -)
|
||||
UNOP(double, tnot, double, !)
|
||||
|
||||
binop_t double_binops[] = {
|
||||
{ '+', &cexpr_double, &cexpr_double, double_add },
|
||||
{ '-', &cexpr_double, &cexpr_double, double_sub },
|
||||
{ '*', &cexpr_double, &cexpr_double, double_mul },
|
||||
{ '/', &cexpr_double, &cexpr_double, double_div },
|
||||
{ '%', &cexpr_double, &cexpr_double, double_rem },
|
||||
{ MOD, &cexpr_double, &cexpr_double, double_mod },
|
||||
{}
|
||||
};
|
||||
|
||||
unop_t double_unops[] = {
|
||||
{ '+', &cexpr_double, double_pos },
|
||||
{ '-', &cexpr_double, double_neg },
|
||||
{ '!', &cexpr_double, double_tnot },
|
||||
{}
|
||||
};
|
||||
|
||||
exprtype_t cexpr_double = {
|
||||
"double",
|
||||
sizeof (double),
|
||||
double_binops,
|
||||
double_unops,
|
||||
};
|
||||
|
||||
exprtype_t cexpr_exprval = {
|
||||
"exprval",
|
||||
sizeof (exprval_t *),
|
||||
0, // can't actually do anything with an exprval
|
||||
0,
|
||||
};
|
||||
|
||||
exprtype_t cexpr_field = {
|
||||
"field",
|
||||
0, // has no size of its own, rather, it's the length of the name
|
||||
0, // can't actually do anything with a field
|
||||
0,
|
||||
};
|
||||
|
||||
VISIBLE binop_t *
|
||||
cexpr_find_cast (exprtype_t *dst_type, exprtype_t *src_type)
|
||||
{
|
||||
binop_t *binop = 0;
|
||||
|
||||
for (binop = dst_type->binops; binop && binop->op; binop++) {
|
||||
if (binop->op == '=' && binop->other == src_type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (binop && binop->op) {
|
||||
return binop;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VISIBLE int
|
||||
cexpr_parse_enum (exprenum_t *enm, const char *str, const exprctx_t *ctx,
|
||||
void *data)
|
||||
{
|
||||
exprval_t result = { enm->type, data };
|
||||
exprctx_t context = *ctx;
|
||||
context.symtab = enm->symtab;
|
||||
context.result = &result;
|
||||
return cexpr_eval_string (str, &context);
|
||||
}
|
154
libs/util/cexpr-vars.c
Normal file
154
libs/util/cexpr-vars.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
cexpr-vars.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "QF/cexpr.h"
|
||||
#include "QF/cmem.h"
|
||||
#include "QF/cvar.h"
|
||||
#include "QF/hash.h"
|
||||
#include "QF/qfplist.h"
|
||||
|
||||
#include "libs/util/cexpr-parse.h"
|
||||
|
||||
VISIBLE void
|
||||
cexpr_struct_getfield (const exprval_t *a, const exprval_t *b, exprval_t *c,
|
||||
exprctx_t *ctx)
|
||||
{
|
||||
__auto_type symtab = (exprtab_t *) a->type->data;
|
||||
__auto_type name = (const char *) b->value;
|
||||
__auto_type field = (exprsym_t *) Hash_Find (symtab->tab, name);
|
||||
exprval_t *val = 0;
|
||||
if (field) {
|
||||
val = cmemalloc (ctx->memsuper, sizeof (exprval_t));
|
||||
val->type = field->type;
|
||||
val->value = a->value + (ptrdiff_t) field->value;
|
||||
}
|
||||
*(exprval_t **) c->value = val;
|
||||
}
|
||||
|
||||
VISIBLE binop_t cexpr_struct_binops[] = {
|
||||
{ '.', &cexpr_field, &cexpr_exprval, cexpr_struct_getfield },
|
||||
{}
|
||||
};
|
||||
|
||||
static void
|
||||
cvar_get (const exprval_t *a, const exprval_t *b, exprval_t *c, exprctx_t *ctx)
|
||||
{
|
||||
__auto_type name = (const char *) b->value;
|
||||
exprval_t *var = cexpr_cvar (name, ctx);
|
||||
if (!var) {
|
||||
PL_Message (ctx->messages, ctx->item, "unknown cvar %s", name);
|
||||
}
|
||||
*(exprval_t **) c->value = var;
|
||||
}
|
||||
|
||||
static binop_t cvar_binops[] = {
|
||||
{ '.', &cexpr_field, &cexpr_exprval, cvar_get },
|
||||
{}
|
||||
};
|
||||
|
||||
static exprtype_t cvar_type = {
|
||||
"cvar",
|
||||
sizeof (void *), // ref to struct (will always be 0)
|
||||
cvar_binops,
|
||||
0,
|
||||
};
|
||||
|
||||
VISIBLE exprval_t *
|
||||
cexpr_cvar (const char *name, exprctx_t *ctx)
|
||||
{
|
||||
cvar_t *var = Cvar_FindVar (name);
|
||||
if (!var) {
|
||||
var = Cvar_FindAlias (name);
|
||||
}
|
||||
if (!var) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
exprtype_t *type = ctx->result->type;
|
||||
binop_t *cast = cexpr_find_cast (type, &cexpr_int);
|
||||
|
||||
exprval_t *val = 0;
|
||||
if (cast || val->type == &cexpr_int || val->type == &cexpr_uint) {
|
||||
val = cexpr_value (type, ctx);
|
||||
*(int *) val->value = var->int_val;
|
||||
} else if (val->type == &cexpr_float) {
|
||||
val = cexpr_value (type, ctx);
|
||||
*(float *) val->value = var->value;
|
||||
} else if (val->type == &cexpr_double) {
|
||||
val = cexpr_value (type, ctx);
|
||||
//FIXME cvars need to support double values
|
||||
*(double *) val->value = var->value;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
VISIBLE exprval_t *
|
||||
cexpr_cvar_struct (exprctx_t *ctx)
|
||||
{
|
||||
exprval_t *cvars = cexpr_value (&cvar_type, ctx);
|
||||
*(void **) cvars->value = 0;
|
||||
return cvars;
|
||||
}
|
||||
|
||||
static const char *expr_getkey (const void *s, void *unused)
|
||||
{
|
||||
__auto_type sym = (exprsym_t *) s;
|
||||
return sym->name;
|
||||
}
|
||||
|
||||
void cexpr_init_symtab (exprtab_t *symtab, exprctx_t *ctx)
|
||||
{
|
||||
exprsym_t *sym;
|
||||
|
||||
symtab->tab = Hash_NewTable (61, expr_getkey, 0, 0, &ctx->hashlinks);
|
||||
for (sym = symtab->symbols; sym->name; sym++) {
|
||||
Hash_Add (symtab->tab, sym);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE exprval_t *
|
||||
cexpr_value_reference (exprtype_t *type, void *data, exprctx_t *ctx)
|
||||
{
|
||||
__auto_type ref = (exprval_t *) cmemalloc (ctx->memsuper,
|
||||
sizeof (exprval_t));
|
||||
ref->type = type;
|
||||
ref->value = data;
|
||||
return ref;
|
||||
}
|
||||
|
||||
VISIBLE exprval_t *
|
||||
cexpr_value (exprtype_t *type, exprctx_t *ctx)
|
||||
{
|
||||
exprval_t *val = cexpr_value_reference (type, 0, ctx);
|
||||
val->value = cmemalloc (ctx->memsuper, type->size);
|
||||
return val;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
libs_util_tests = \
|
||||
libs/util/test/test-bary \
|
||||
libs/util/test/test-cexpr \
|
||||
libs/util/test/test-cmem \
|
||||
libs/util/test/test-cs \
|
||||
libs/util/test/test-darray \
|
||||
|
@ -24,6 +25,10 @@ libs_util_test_test_bary_SOURCES=libs/util/test/test-bary.c
|
|||
libs_util_test_test_bary_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_bary_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_cexpr_SOURCES=libs/util/test/test-cexpr.c
|
||||
libs_util_test_test_cexpr_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_cexpr_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_cmem_SOURCES=libs/util/test/test-cmem.c
|
||||
libs_util_test_test_cmem_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_cmem_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
|
81
libs/util/test/test-cexpr.c
Normal file
81
libs/util/test/test-cexpr.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
test-cexpr.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
#include "QF/cexpr.h"
|
||||
#include "QF/cmem.h"
|
||||
#include "QF/hash.h"
|
||||
|
||||
int a = 5;
|
||||
int b = 6;
|
||||
int c;
|
||||
|
||||
exprsym_t symbols[] = {
|
||||
{ "a", &cexpr_int, &a },
|
||||
{ "b", &cexpr_int, &b },
|
||||
{}
|
||||
};
|
||||
exprval_t result = { &cexpr_int, &c };
|
||||
|
||||
exprtab_t symtab = {
|
||||
symbols,
|
||||
0
|
||||
};
|
||||
|
||||
exprctx_t context = { &result, &symtab };
|
||||
|
||||
#define TEST_BINOP(op) \
|
||||
do { \
|
||||
c = -4096; \
|
||||
cexpr_eval_string ("a " #op " b", &context); \
|
||||
printf ("c = a %s b -> %d = %d %s %d\n", #op, c, a, #op, b); \
|
||||
if (c != (a op b)) { \
|
||||
ret |= 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cexpr_init_symtab (&symtab, &context);
|
||||
context.memsuper = new_memsuper();
|
||||
|
||||
TEST_BINOP (<<);
|
||||
TEST_BINOP (>>);
|
||||
TEST_BINOP (+);
|
||||
TEST_BINOP (-);
|
||||
TEST_BINOP (*);
|
||||
TEST_BINOP (/);
|
||||
TEST_BINOP (&);
|
||||
TEST_BINOP (|);
|
||||
TEST_BINOP (^);
|
||||
TEST_BINOP (%);
|
||||
|
||||
Hash_DelTable (symtab.tab);
|
||||
delete_memsuper (context.memsuper);
|
||||
return ret;
|
||||
}
|
Loading…
Reference in a new issue