/* cexpr-lex.l 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 */ %option bison-bridge %option reentrant %option prefix="cexpr_yy" %option noyywrap %{ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #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 exprval_t *parse_string (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} { yylval->value = parse_string (yytext, context); return VALUE; } @ 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 exprval_t *parse_string (const char *str, exprctx_t *context) { exprval_t *val = cexpr_value (&cexpr_string, context); // str includes the quotes, which add 2 to the length of the string, but // only one byte is needed for the terminating nul size_t size = strlen (str) - 1; char *dup = cmemalloc (context->memsuper, size); strncpy (dup, str + 1, size - 1); dup[size - 1] = 0; *(char **) val->value = dup; 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; if (symtab->tab) { sym = Hash_Find (symtab->tab, name); } else { for (exprsym_t *s = symtab->symbols; s->name; s++) { if (!strcmp (s->name, name)) { sym = s; break; } } } } 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; }