summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2022-10-03 20:56:12 +0200
committerSébastien Helleu <flashcode@flashtux.org>2022-11-05 22:34:38 +0100
commit196a0511418a7a1f406db45d23abd4697ad7f905 (patch)
tree85cac7f2989f04222823f1910438bf40ea415907 /src/core
parentbc2fb071e22589aa219ce551b0112bacbd3cca8f (diff)
downloadweechat-196a0511418a7a1f406db45d23abd4697ad7f905.zip
core: add range of chars in evaluation of expressions with `chars:xxx`
Diffstat (limited to 'src/core')
-rw-r--r--src/core/wee-command.c61
-rw-r--r--src/core/wee-eval.c241
-rw-r--r--src/core/wee-eval.h7
3 files changed, 198 insertions, 111 deletions
diff --git a/src/core/wee-command.c b/src/core/wee-command.c
index 5994ebbe7..c9052676b 100644
--- a/src/core/wee-command.c
+++ b/src/core/wee-command.c
@@ -7753,45 +7753,48 @@ command_init ()
"\n"
"Some variables are replaced in expression, using the format "
"${variable}, variable can be, by order of priority:\n"
- " 1. the string itself without evaluation (format: \"raw:xxx\")\n"
- " 2. a user-defined variable (format: \"name\")\n"
- " 3. an evaluated sub-string (format: \"eval:xxx\")\n"
- " 4. an evaluated condition (format: \"eval_cond:xxx\")\n"
- " 5. a string with escaped chars (format: \"esc:xxx\" or \"\\xxx\")\n"
- " 6. a string converted to lower case (format: \"lower:xxx\")\n"
- " 7. a string converted to upper case (format: \"upper:xxx\")\n"
- " 8. a string with chars to hide (format: \"hide:char,string\")\n"
- " 9. a string with max chars (format: \"cut:max,suffix,string\" "
+ " - the string itself without evaluation (format: \"raw:xxx\")\n"
+ " - a user-defined variable (format: \"name\")\n"
+ " - an evaluated sub-string (format: \"eval:xxx\")\n"
+ " - an evaluated condition (format: \"eval_cond:xxx\")\n"
+ " - a string with escaped chars (format: \"esc:xxx\" or \"\\xxx\")\n"
+ " - a string with a range of chars (format: \"chars:xxx\" or "
+ "\"chars:c1-c2\" where \"xxx\" is one of: \"digit\", \"xdigit\", "
+ "\"lower\", \"upper\", \"alpha\", \"alnum\")\n"
+ " - a string converted to lower case (format: \"lower:xxx\")\n"
+ " - a string converted to upper case (format: \"upper:xxx\")\n"
+ " - a string with chars to hide (format: \"hide:char,string\")\n"
+ " - a string with max chars (format: \"cut:max,suffix,string\" "
"or \"cut:+max,suffix,string\")\n"
" or max chars displayed on screen "
"(format: \"cutscr:max,suffix,string\" or "
"\"cutscr:+max,suffix,string\")\n"
- " 10. a reversed string (format: \"rev:xxx\" or \"revscr:xxx\")\n"
- " 11. a repeated string (format: \"repeat:count,string\")\n"
- " 12. length of a string (format: \"length:xxx\" or "
+ " - a reversed string (format: \"rev:xxx\" or \"revscr:xxx\")\n"
+ " - a repeated string (format: \"repeat:count,string\")\n"
+ " - length of a string (format: \"length:xxx\" or "
"\"lengthscr:xxx\")\n"
- " 13. split of a string (format: "
+ " - split of a string (format: "
"\"split:number,separators,flags,xxx\")\n"
- " 14. split of shell argmuents (format: \"split_shell:number,xxx\")\n"
- " 15. a color (format: \"color:xxx\", see \"Plugin API "
+ " - split of shell argmuents (format: \"split_shell:number,xxx\")\n"
+ " - a color (format: \"color:xxx\", see \"Plugin API "
"reference\", function \"color\")\n"
- " 16. a modifier (format: \"modifier:name,data,string\")\n"
- " 17. an info (format: \"info:name,arguments\", arguments are "
+ " - a modifier (format: \"modifier:name,data,string\")\n"
+ " - an info (format: \"info:name,arguments\", arguments are "
"optional)\n"
- " 18. a base 16/32/64 encoded/decoded string (format: "
+ " - a base 16/32/64 encoded/decoded string (format: "
"\"base_encode:base,xxx\" or \"base_decode:base,xxx\")\n"
- " 19. current date/time (format: \"date\" or \"date:format\")\n"
- " 20. an environment variable (format: \"env:XXX\")\n"
- " 21. a ternary operator (format: "
+ " - current date/time (format: \"date\" or \"date:format\")\n"
+ " - an environment variable (format: \"env:XXX\")\n"
+ " - a ternary operator (format: "
"\"if:condition?value_if_true:value_if_false\")\n"
- " 22. result of an expression with parentheses and operators "
+ " - result of an expression with parentheses and operators "
"+ - * / // % ** (format: \"calc:xxx\")\n"
- " 23. a random integer number (format: \"random:min,max\")\n"
- " 24. a translated string (format: \"translate:xxx\")\n"
- " 25. define a user variable (format: \"define:name,value\")\n"
- " 26. an option (format: \"file.section.option\")\n"
- " 27. a local variable in buffer\n"
- " 28. a hdata name/variable (the value is automatically converted "
+ " - a random integer number (format: \"random:min,max\")\n"
+ " - a translated string (format: \"translate:xxx\")\n"
+ " - define a user variable (format: \"define:name,value\")\n"
+ " - an option (format: \"file.section.option\")\n"
+ " - a local variable in buffer\n"
+ " - a hdata name/variable (the value is automatically converted "
"to string), by default \"window\" and \"buffer\" point to current "
"window/buffer.\n"
"Format for hdata can be one of following:\n"
@@ -7824,6 +7827,8 @@ command_init ()
" /eval -n ${window.buffer.full_name} ==> core.weechat\n"
" /eval -n ${window.buffer.number} ==> 1\n"
" /eval -n ${\\t} ==> <tab>\n"
+ " /eval -n ${chars:digit} ==> 0123456789\n"
+ " /eval -n ${chars:J-T} ==> JKLMNOPQRST\n"
" /eval -n ${lower:TEST} ==> test\n"
" /eval -n ${upper:test} ==> TEST\n"
" /eval -n ${hide:-,${relay.network.password}} ==> --------\n"
diff --git a/src/core/wee-eval.c b/src/core/wee-eval.c
index dbec3a39f..7d892b125 100644
--- a/src/core/wee-eval.c
+++ b/src/core/wee-eval.c
@@ -63,10 +63,10 @@
}
-char *logical_ops[EVAL_NUM_LOGICAL_OPS] =
+char *eval_logical_ops[EVAL_NUM_LOGICAL_OPS] =
{ "||", "&&" };
-char *comparisons[EVAL_NUM_COMPARISONS] =
+char *eval_comparisons[EVAL_NUM_COMPARISONS] =
{ "=~", "!~", /* regex */
"==*", "!!*", "=*", "!*", /* string match */
"==-", "!!-", "=-", "!-", /* includes */
@@ -74,6 +74,16 @@ char *comparisons[EVAL_NUM_COMPARISONS] =
"<=", "<", ">=", ">", /* less than, greater than */
};
+char *eval_range_chars[][2] =
+{
+ { "digit", EVAL_RANGE_DIGIT },
+ { "xdigit", EVAL_RANGE_XDIGIT },
+ { "lower", EVAL_RANGE_LOWER },
+ { "upper", EVAL_RANGE_UPPER },
+ { "alpha", EVAL_RANGE_ALPHA },
+ { "alnum", EVAL_RANGE_ALNUM },
+ { NULL, NULL },
+};
char *eval_replace_vars (const char *expr,
struct t_eval_context *eval_context);
@@ -274,6 +284,65 @@ eval_string_eval_cond (const char *text, struct t_eval_context *eval_context)
}
/*
+ * Adds range of chars.
+ *
+ * Note: result must be freed after use.
+ */
+
+char *
+eval_string_range_chars (const char *range)
+{
+ int i, char1, char2;
+ const char *ptr_char;
+ char **string, str_char[16], *result;
+
+ string = NULL;
+ result = NULL;
+
+ for (i = 0; eval_range_chars[i][0]; i++)
+ {
+ if (strcmp (range, eval_range_chars[i][0]) == 0)
+ return strdup (eval_range_chars[i][1]);
+ }
+
+ char1 = utf8_char_int (range);
+
+ /* next char must be '-' */
+ ptr_char = utf8_next_char (range);
+ if (!ptr_char || !ptr_char[0] || (ptr_char[0] != '-'))
+ goto end;
+
+ /* next char is the char2 */
+ ptr_char = utf8_next_char (ptr_char);
+ if (!ptr_char || !ptr_char[0])
+ goto end;
+ char2 = utf8_char_int (ptr_char);
+
+ /* output is limited to 1024 chars (not bytes) */
+ if ((char1 > char2) || (char2 - char1 + 1 > 4096))
+ goto end;
+
+ string = string_dyn_alloc (128);
+ if (!string)
+ goto end;
+
+ for (i = char1; i <= char2; i++)
+ {
+ utf8_int_string (i, str_char);
+ string_dyn_concat (string, str_char, -1);
+ }
+
+end:
+ if (string)
+ {
+ result = *string;
+ string_dyn_free (string, 0);
+ }
+
+ return (result) ? result : strdup ("");
+}
+
+/*
* Converts string to lower case.
*
* Note: result must be freed after use.
@@ -1414,48 +1483,49 @@ end:
/*
* Replaces variables, which can be, by order of priority:
- * 1. the string itself without evaluation (format: raw:xxx)
- * 2. a variable from hashtable "user_vars" or "extra_vars"
- * 3. a WeeChat home directory, one of: "weechat_config_dir",
- * "weechat_data_dir", "weechat_cache_dir", "weechat_runtime_dir"
- * 4. a string to evaluate (format: eval:xxx)
- * 5. a condition to evaluate (format: eval_cond:xxx)
- * 6. a string with escaped chars (format: esc:xxx or \xxx)
- * 7. a string converted to lower case (format: lower:xxx)
- * 8. a string converted to upper case (format: upper:xxx)
- * 9. a string with chars to hide (format: hide:char,string)
- * 10. a string with max chars (format: cut:max,suffix,string or
- * cut:+max,suffix,string) or max chars on screen
- * (format: cutscr:max,suffix,string or cutscr:+max,suffix,string)
- * 11. a reversed string (format: rev:xxx) or reversed string for screen,
- * color codes are not reversed (format: revscr:xxx)
- * 12. a repeated string (format: repeat:count,string)
- * 13. length of a string (format: length:xxx) or length of a string on screen
- * (format: lengthscr:xxx); color codes are ignored
- * 14. split string (format: split:number,separators,flags,xxx
- * or split:count,separators,flags,xxx
- * or split:random,separators,flags,xxx)
- * 15. split shell arguments (format: split:number,xxx or split:count,xxx
- * or split:random,xxx)
- * 16. a regex group captured (format: re:N (0.99) or re:+)
- * 17. a color (format: color:xxx)
- * 18. a modifier (format: modifier:name,data,xxx)
- * 19. an info (format: info:name,arguments)
- * 20. a base 16/32/64 encoded/decoded string (format: base_encode:base,xxx
- * or base_decode:base,xxx)
- * 21. current date/time (format: date or date:xxx)
- * 22. an environment variable (format: env:XXX)
- * 23. a ternary operator (format: if:condition?value_if_true:value_if_false)
- * 24. calculate result of an expression (format: calc:xxx)
- * 25. a random integer number in the range from "min" to "max"
- * (format: random:min,max)
- * 26. a translated string (format: translate:xxx)
- * 27. define a new variable (format: define:name,value)
- * 28. an option (format: file.section.option)
- * 29. a buffer local variable
- * 30. a pointer name from hashtable "pointers"
- * 31. a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2
- * or hdata[ptr].var1.var2 or hdata[ptr_name].var1.var2)
+ * - the string itself without evaluation (format: raw:xxx)
+ * - a variable from hashtable "user_vars" or "extra_vars"
+ * - a WeeChat home directory, one of: "weechat_config_dir",
+ * "weechat_data_dir", "weechat_cache_dir", "weechat_runtime_dir"
+ * - a string to evaluate (format: eval:xxx)
+ * - a condition to evaluate (format: eval_cond:xxx)
+ * - a string with escaped chars (format: esc:xxx or \xxx)
+ * - a string with a range of chars (format: chars:xxx)
+ * - a string converted to lower case (format: lower:xxx)
+ * - a string converted to upper case (format: upper:xxx)
+ * - a string with chars to hide (format: hide:char,string)
+ * - a string with max chars (format: cut:max,suffix,string or
+ * cut:+max,suffix,string) or max chars on screen
+ * (format: cutscr:max,suffix,string or cutscr:+max,suffix,string)
+ * - a reversed string (format: rev:xxx) or reversed string for screen,
+ * color codes are not reversed (format: revscr:xxx)
+ * - a repeated string (format: repeat:count,string)
+ * - length of a string (format: length:xxx) or length of a string on screen
+ * (format: lengthscr:xxx); color codes are ignored
+ * - split string (format: split:number,separators,flags,xxx
+ * or split:count,separators,flags,xxx
+ * or split:random,separators,flags,xxx)
+ * - split shell arguments (format: split:number,xxx or split:count,xxx
+ * or split:random,xxx)
+ * - a regex group captured (format: re:N (0.99) or re:+)
+ * - a color (format: color:xxx)
+ * - a modifier (format: modifier:name,data,xxx)
+ * - an info (format: info:name,arguments)
+ * - a base 16/32/64 encoded/decoded string (format: base_encode:base,xxx
+ * or base_decode:base,xxx)
+ * - current date/time (format: date or date:xxx)
+ * - an environment variable (format: env:XXX)
+ * - a ternary operator (format: if:condition?value_if_true:value_if_false)
+ * - calculate result of an expression (format: calc:xxx)
+ * - a random integer number in the range from "min" to "max"
+ * (format: random:min,max)
+ * - a translated string (format: translate:xxx)
+ * - define a new variable (format: define:name,value)
+ * - an option (format: file.section.option)
+ * - a buffer local variable
+ * - a pointer name from hashtable "pointers"
+ * - a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2
+ * or hdata[ptr].var1.var2 or hdata[ptr_name].var1.var2)
*
* See /help in WeeChat for examples.
*
@@ -1478,16 +1548,14 @@ eval_replace_vars_cb (void *data, const char *text)
EVAL_DEBUG_MSG(1, "eval_replace_vars_cb(\"%s\")", text);
- /*
- * 1. raw text (no evaluation at all)
- */
+ /* raw text (no evaluation at all) */
if (strncmp (text, "raw:", 4) == 0)
{
value = strdup (text + 4);
goto end;
}
- /* 2. variable in hashtable "user_vars" or "extra_vars" */
+ /* variable in hashtable "user_vars" or "extra_vars" */
ptr_value = hashtable_get (eval_context->user_vars, text);
if (ptr_value)
{
@@ -1518,7 +1586,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
}
- /* 3. WeeChat home directory */
+ /* WeeChat home directory */
if (strcmp (text, "weechat_config_dir") == 0)
{
value = strdup (weechat_config_dir);
@@ -1541,8 +1609,8 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 4. force evaluation of string (recursive call)
- * --> use with caution: the text must be safe!
+ * force evaluation of string (recursive call)
+ * --> use with caution: the text must be safe!
*/
if (strncmp (text, "eval:", 5) == 0)
{
@@ -1551,8 +1619,8 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 5. force evaluation of condition (recursive call)
- * --> use with caution: the text must be safe!
+ * force evaluation of condition (recursive call)
+ * --> use with caution: the text must be safe!
*/
if (strncmp (text, "eval_cond:", 10) == 0)
{
@@ -1560,7 +1628,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 6. convert escaped chars */
+ /* convert escaped chars */
if (strncmp (text, "esc:", 4) == 0)
{
value = string_convert_escaped_chars (text + 4);
@@ -1572,21 +1640,28 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 7. convert to lower case */
+ /* range of chars */
+ if (strncmp (text, "chars:", 6) == 0)
+ {
+ value = eval_string_range_chars (text + 6);
+ goto end;
+ }
+
+ /* convert to lower case */
if (strncmp (text, "lower:", 6) == 0)
{
value = eval_string_lower (text + 6);
goto end;
}
- /* 8. convert to upper case */
+ /* convert to upper case */
if (strncmp (text, "upper:", 6) == 0)
{
value = eval_string_upper (text + 6);
goto end;
}
- /* 9. hide chars: replace all chars by a given char/string */
+ /* hide chars: replace all chars by a given char/string */
if (strncmp (text, "hide:", 5) == 0)
{
value = eval_string_hide (text + 5);
@@ -1594,7 +1669,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 10. cut chars:
+ * cut chars:
* cut: max number of chars, and add an optional suffix when the
* string is cut
* cutscr: max number of chars displayed on screen, and add an optional
@@ -1611,7 +1686,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 11. reverse string */
+ /* reverse string */
if (strncmp (text, "rev:", 4) == 0)
{
value = string_reverse (text + 4);
@@ -1623,7 +1698,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 12. repeated string */
+ /* repeated string */
if (strncmp (text, "repeat:", 7) == 0)
{
value = eval_string_repeat (text + 7);
@@ -1631,7 +1706,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 13. length of string:
+ * length of string:
* length: number of chars
* lengthscr: number of chars displayed on screen
*/
@@ -1650,49 +1725,49 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 14: split string */
+ /* split string */
if (strncmp (text, "split:", 6) == 0)
{
value = eval_string_split (text + 6);
goto end;
}
- /* 15: split shell */
+ /* split shell */
if (strncmp (text, "split_shell:", 12) == 0)
{
value = eval_string_split_shell (text + 12);
goto end;
}
- /* 16. regex group captured */
+ /* regex group captured */
if (strncmp (text, "re:", 3) == 0)
{
value = eval_string_regex_group (text + 3, eval_context);
goto end;
}
- /* 17. color code */
+ /* color code */
if (strncmp (text, "color:", 6) == 0)
{
value = eval_string_color (text + 6);
goto end;
}
- /* 18. modifier */
+ /* modifier */
if (strncmp (text, "modifier:", 9) == 0)
{
value = eval_string_modifier (text + 9);
goto end;
}
- /* 19. info */
+ /* info */
if (strncmp (text, "info:", 5) == 0)
{
value = eval_string_info (text + 5);
goto end;
}
- /* 20. base_encode/base_decode */
+ /* base_encode/base_decode */
if (strncmp (text, "base_encode:", 12) == 0)
{
value = eval_string_base_encode (text + 12);
@@ -1704,14 +1779,14 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 21. current date/time */
+ /* current date/time */
if ((strncmp (text, "date", 4) == 0) && (!text[4] || (text[4] == ':')))
{
value = eval_string_date (text + 4);
goto end;
}
- /* 22. environment variable */
+ /* environment variable */
if (strncmp (text, "env:", 4) == 0)
{
ptr_value = getenv (text + 4);
@@ -1719,7 +1794,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 23: ternary operator: if:condition?value_if_true:value_if_false */
+ /* ternary operator: if:condition?value_if_true:value_if_false */
if (strncmp (text, "if:", 3) == 0)
{
value = eval_string_if (text + 3, eval_context);
@@ -1727,7 +1802,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 24. calculate the result of an expression
+ * calculate the result of an expression
* (with number, operators and parentheses)
*/
if (strncmp (text, "calc:", 5) == 0)
@@ -1737,7 +1812,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 25. random number
+ * random number
*/
if (strncmp (text, "random:", 7) == 0)
{
@@ -1746,7 +1821,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
/*
- * 26. translated text
+ * translated text
*/
if (strncmp (text, "translate:", 10) == 0)
{
@@ -1754,7 +1829,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 27. define a variable */
+ /* define a variable */
if (strncmp (text, "define:", 7) == 0)
{
eval_string_define (text + 7, eval_context);
@@ -1762,7 +1837,7 @@ eval_replace_vars_cb (void *data, const char *text)
goto end;
}
- /* 28. option: if found, return this value */
+ /* option: if found, return this value */
if (strncmp (text, "sec.data.", 9) == 0)
{
ptr_value = hashtable_get (secure_hashtable_data, text + 9);
@@ -1805,7 +1880,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
}
- /* 29. local variable in buffer */
+ /* local variable in buffer */
ptr_buffer = hashtable_get (eval_context->pointers, "buffer");
if (ptr_buffer)
{
@@ -1817,7 +1892,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
}
- /* 30. hdata */
+ /* hdata */
value = eval_string_hdata (text, eval_context);
end:
@@ -1889,7 +1964,7 @@ eval_compare (const char *expr1, int comparison, const char *expr2,
char *error, *value;
EVAL_DEBUG_MSG(1, "eval_compare(\"%s\", \"%s\", \"%s\")",
- expr1, comparisons[comparison], expr2);
+ expr1, eval_comparisons[comparison], expr2);
rc = 0;
string_compare = 0;
@@ -2070,7 +2145,7 @@ eval_expression_condition (const char *expr,
*/
for (logic = 0; logic < EVAL_NUM_LOGICAL_OPS; logic++)
{
- pos = eval_strstr_level (expr2, logical_ops[logic], eval_context,
+ pos = eval_strstr_level (expr2, eval_logical_ops[logic], eval_context,
"(", ")", 0);
if (pos > expr2)
{
@@ -2097,7 +2172,7 @@ eval_expression_condition (const char *expr,
value = strdup ((rc) ? EVAL_STR_TRUE : EVAL_STR_FALSE);
goto end;
}
- pos += strlen (logical_ops[logic]);
+ pos += strlen (eval_logical_ops[logic]);
while (pos[0] == ' ')
{
pos++;
@@ -2120,7 +2195,7 @@ eval_expression_condition (const char *expr,
*/
for (comp = 0; comp < EVAL_NUM_COMPARISONS; comp++)
{
- pos = eval_strstr_level (expr2, comparisons[comp], eval_context,
+ pos = eval_strstr_level (expr2, eval_comparisons[comp], eval_context,
"(", ")", 0);
if (pos >= expr2)
{
@@ -2139,7 +2214,7 @@ eval_expression_condition (const char *expr,
}
if (!sub_expr)
goto end;
- pos += strlen (comparisons[comp]);
+ pos += strlen (eval_comparisons[comp]);
while (pos[0] == ' ')
{
pos++;
diff --git a/src/core/wee-eval.h b/src/core/wee-eval.h
index 22ec58403..944390190 100644
--- a/src/core/wee-eval.h
+++ b/src/core/wee-eval.h
@@ -30,6 +30,13 @@
#define EVAL_RECURSION_MAX 32
+#define EVAL_RANGE_DIGIT "0123456789"
+#define EVAL_RANGE_XDIGIT EVAL_RANGE_DIGIT "abcdefABCDEF"
+#define EVAL_RANGE_LOWER "abcdefghijklmnopqrstuvwxyz"
+#define EVAL_RANGE_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define EVAL_RANGE_ALPHA EVAL_RANGE_LOWER EVAL_RANGE_UPPER
+#define EVAL_RANGE_ALNUM EVAL_RANGE_ALPHA EVAL_RANGE_DIGIT
+
struct t_hashtable;
enum t_eval_logical_op