mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 06:34:11 +00:00
65e15c2dd0
At least with a push-parser, by the time the parser has figured out it has an identifier, the lexer has forgotten the token, thus the annoying and uninformative "undefined identifier " error messages. Since identifiers should always have a value (and functions need a function type), setting up a dummy symbol with just the identifier name duplicated seems to do the trick. It is a bit wasteful of memory if there are a lot of such errors between cmem resets, though.
354 lines
7.7 KiB
Text
354 lines
7.7 KiB
Text
/*
|
|
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/plist.h"
|
|
#include "QF/sys.h"
|
|
|
|
#define CEXPR_YYDEBUG 1
|
|
|
|
#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_size_t (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;
|
|
|
|
ctx->errors++;
|
|
|
|
string = dstring_new ();
|
|
|
|
va_start (args, fmt);
|
|
dvsprintf (string, fmt, args);
|
|
va_end (args);
|
|
|
|
if (ctx->messages) {
|
|
if (ctx->msg_prefix) {
|
|
PL_Message (ctx->messages, ctx->item, "%s:%s",
|
|
ctx->msg_prefix, string->str);
|
|
} else {
|
|
PL_Message (ctx->messages, ctx->item, "%s", string->str);
|
|
}
|
|
} else {
|
|
if (ctx->msg_prefix) {
|
|
Sys_Printf ("%s:%s\n", ctx->msg_prefix, 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_0-9]*
|
|
ID [a-zA-Z_]{id}
|
|
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}+[uU] {
|
|
yylval->value = parse_uint (yytext, context);
|
|
return VALUE;
|
|
}
|
|
|
|
{INT}+ {
|
|
yylval->value = parse_int (yytext, context);
|
|
return VALUE;
|
|
}
|
|
|
|
{INT}+[zZ] {
|
|
yylval->value = parse_size_t (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->symbol = parse_name (yytext + 1, 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]+)*' {
|
|
}
|
|
|
|
[+\-*/&|^%]= {
|
|
yylval->op = yytext[0];
|
|
return ASX;
|
|
}
|
|
|
|
"%%=" {
|
|
yylval->op = MOD;
|
|
return ASX;
|
|
}
|
|
|
|
"<<=" {
|
|
yylval->op = SHL;
|
|
return ASX;
|
|
}
|
|
|
|
">>=" {
|
|
yylval->op = SHR;
|
|
return ASX;
|
|
}
|
|
|
|
[!(){}.*/&|^~+\-=\[\];,#%?:] {
|
|
return yytext[0];
|
|
}
|
|
|
|
"%%" {
|
|
return MOD;
|
|
}
|
|
|
|
"<<" 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);
|
|
|
|
context->errors = 0;
|
|
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 || context->errors;
|
|
}
|
|
|
|
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_size_t (const char *str, exprctx_t *context)
|
|
{
|
|
exprval_t *val = cexpr_value (&cexpr_size_t, 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_float, 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_double, context);
|
|
*(double *) val->value = strtod (str, 0);
|
|
return val;
|
|
}
|
|
|
|
static exprsym_t *
|
|
parse_name (const char *name, exprctx_t *context)
|
|
{
|
|
exprsym_t *sym = 0;
|
|
exprtab_t *prev_tab = 0;
|
|
|
|
for (exprctx_t *ctx = context; ctx && !sym; ctx = ctx->parent) {
|
|
exprtab_t *symtab = ctx->symtab;
|
|
if (!symtab) {
|
|
// scope barrier
|
|
break;
|
|
}
|
|
if (symtab == prev_tab) {
|
|
// already checked this symtab
|
|
continue;
|
|
}
|
|
prev_tab = symtab;
|
|
|
|
sym = Hash_Find (symtab->tab, name);
|
|
}
|
|
if (!sym) {
|
|
sym = cmemalloc (context->memsuper, sizeof (exprsym_t));
|
|
size_t size = strlen (name) + 1;
|
|
char *sym_name = cmemalloc (context->memsuper, size);
|
|
memcpy (sym_name, name, size);
|
|
*sym = (exprsym_t) { .name = sym_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 {
|
|
exprsym_t *sym = 0;
|
|
exprtab_t *prev_tab = 0;
|
|
for (exprctx_t *ctx = context; ctx && !sym; ctx = ctx->parent) {
|
|
exprtab_t *symtab = ctx->external_variables;
|
|
if (!symtab) {
|
|
// scope barrier
|
|
break;
|
|
}
|
|
if (symtab == prev_tab) {
|
|
// already checked this symtab
|
|
continue;
|
|
}
|
|
sym = Hash_Find (symtab->tab, name);
|
|
}
|
|
if (sym) {
|
|
val = cexpr_value_reference (sym->type, sym->value, context);
|
|
}
|
|
}
|
|
if (!val) {
|
|
cexpr_error (context, "undefined variable %s", name);
|
|
}
|
|
return val;
|
|
}
|