diff options
author | Nikita Borodikhin <eliterr@gmail.com> | 2016-07-09 03:23:10 -0700 |
---|---|---|
committer | Nikita Borodikhin <eliterr@gmail.com> | 2016-07-10 01:36:22 -0700 |
commit | 245b2009c66f3b3698773d8c49a834913db7d656 (patch) | |
tree | af9ca48f8de63021131713aa9e8874db30592eeb | |
parent | 3d2168352d3d2125ef0a70bd34c6af025687a5e7 (diff) | |
download | jimtcl-245b2009c66f3b3698773d8c49a834913db7d656.zip jimtcl-245b2009c66f3b3698773d8c49a834913db7d656.tar.gz jimtcl-245b2009c66f3b3698773d8c49a834913db7d656.tar.bz2 |
Boolean contstants in expr, `string is boolean` classification
* named boolean values in `expr` are internally converted to int
* named constants are lower-case only
-rw-r--r-- | jim.c | 109 | ||||
-rw-r--r-- | jim.h | 4 | ||||
-rw-r--r-- | jim_tcl.txt | 21 | ||||
-rw-r--r-- | tests/expr-new.test | 36 | ||||
-rw-r--r-- | tests/string.test | 38 | ||||
-rw-r--r-- | tests/while.test | 4 |
6 files changed, 198 insertions, 14 deletions
@@ -1143,8 +1143,9 @@ void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr)) #define JIM_TT_SUBEXPR_COMMA 13 #define JIM_TT_EXPR_INT 14 #define JIM_TT_EXPR_DOUBLE 15 +#define JIM_TT_EXPR_BOOLEAN 16 -#define JIM_TT_EXPRSUGAR 16 /* $(expression) */ +#define JIM_TT_EXPRSUGAR 17 /* $(expression) */ /* Operator token types start here */ #define JIM_TT_EXPR_OP 20 @@ -2938,13 +2939,13 @@ static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass static const char * const strclassnames[] = { "integer", "alpha", "alnum", "ascii", "digit", "double", "lower", "upper", "space", "xdigit", - "control", "print", "graph", "punct", + "control", "print", "graph", "punct", "boolean", NULL }; enum { STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT, - STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT + STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN, }; int strclass; int len; @@ -2977,6 +2978,13 @@ static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass return JIM_OK; } + case STR_IS_BOOLEAN: + { + int b; + Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK); + return JIM_OK; + } + case STR_IS_ALPHA: isclassfunc = isalpha; break; case STR_IS_ALNUM: isclassfunc = isalnum; break; case STR_IS_ASCII: isclassfunc = jim_isascii; break; @@ -6046,6 +6054,49 @@ Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue) } /* ----------------------------------------------------------------------------- + * Boolean conversion + * ---------------------------------------------------------------------------*/ +static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); + +int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr) +{ + if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) + return JIM_ERR; + *booleanPtr = (int) JimWideValue(objPtr); + return JIM_OK; +} + +static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) +{ + static const char * const falses[] = { + "0", "false", "no", "off", NULL + }; + static const char * const trues[] = { + "1", "true", "yes", "on", NULL + }; + + int boolean; + + int index; + if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) { + boolean = 0; + } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) { + boolean = 1; + } else { + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr); + } + return JIM_ERR; + } + + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &intObjType; + objPtr->internalRep.wideValue = boolean; + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- * List object * ---------------------------------------------------------------------------*/ static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec); @@ -7486,6 +7537,7 @@ int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) static int JimParseExprOperator(struct JimParserCtx *pc); static int JimParseExprNumber(struct JimParserCtx *pc); static int JimParseExprIrrational(struct JimParserCtx *pc); +static int JimParseExprBoolean(struct JimParserCtx *pc); /* Exrp's Stack machine operators opcodes. */ @@ -8117,6 +8169,7 @@ static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) { long l; double d; + int b; if (Jim_GetLong(interp, obj, &l) == JIM_OK) { return l != 0; @@ -8124,6 +8177,9 @@ static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { return d != 0; } + if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) { + return b != 0; + } return -1; } @@ -8440,6 +8496,14 @@ singlechar: case 'n': case 'i': if (JimParseExprIrrational(pc) == JIM_ERR) + if (JimParseExprBoolean(pc) == JIM_ERR) + return JimParseExprOperator(pc); + break; + case 't': + case 'f': + case 'o': + case 'y': + if (JimParseExprBoolean(pc) == JIM_ERR) return JimParseExprOperator(pc); break; default: @@ -8495,6 +8559,27 @@ static int JimParseExprIrrational(struct JimParserCtx *pc) return JIM_ERR; } +static int JimParseExprBoolean(struct JimParserCtx *pc) +{ + const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL }; + const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 }; + int i; + + for (i = 0; booleans[i]; i++) { + const char *boolean = booleans[i]; + int length = lengths[i]; + + if (strncmp(boolean, pc->p, length) == 0) { + pc->p += length; + pc->len -= length; + pc->tend = pc->p - 1; + pc->tt = JIM_TT_EXPR_BOOLEAN; + return JIM_OK; + } + } + return JIM_ERR; +} + static int JimParseExprOperator(struct JimParserCtx *pc) { int i; @@ -8552,7 +8637,7 @@ const char *jim_tt_name(int type) { static const char * const tt_names[JIM_TT_EXPR_OP] = { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT", - "DBL", "$()" }; + "DBL", "BOO", "$()" }; if (type < JIM_TT_EXPR_OP) { return tt_names[type]; } @@ -8968,6 +9053,7 @@ static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList case JIM_TT_DICTSUGAR: case JIM_TT_EXPRSUGAR: case JIM_TT_CMD: + case JIM_TT_EXPR_BOOLEAN: token->type = t->type; strexpr: token->objPtr = Jim_NewStringObj(interp, t->token, t->len); @@ -9367,6 +9453,7 @@ noopt: switch (expr->token[i].type) { case JIM_TT_EXPR_INT: case JIM_TT_EXPR_DOUBLE: + case JIM_TT_EXPR_BOOLEAN: case JIM_TT_STR: ExprPush(&e, expr->token[i].objPtr); break; @@ -9439,6 +9526,7 @@ int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) int retcode; jim_wide wideValue; double doubleValue; + int booleanValue; Jim_Obj *exprResultPtr; retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr); @@ -9447,8 +9535,14 @@ int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) { if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) { - Jim_DecrRefCount(interp, exprResultPtr); - return JIM_ERR; + if (Jim_GetBoolean(interp, exprResultPtr, &booleanValue) != JIM_OK) { + Jim_DecrRefCount(interp, exprResultPtr); + return JIM_ERR; + } else { + Jim_DecrRefCount(interp, exprResultPtr); + *boolPtr = booleanValue; + return JIM_OK; + } } else { Jim_DecrRefCount(interp, exprResultPtr); @@ -12844,6 +12938,9 @@ static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar case JIM_TT_EXPR_DOUBLE: type = "double"; break; + case JIM_TT_EXPR_BOOLEAN: + type = "boolean"; + break; case JIM_TT_CMD: type = "command"; break; @@ -816,6 +816,10 @@ JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr); +/* boolean object */ +JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, + int *booleanPtr); + /* integer object */ JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr); diff --git a/jim_tcl.txt b/jim_tcl.txt index 3c1215d..ca9eae6 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -56,6 +56,8 @@ Changes between 0.76 and 0.77 1. Add support for `aio sync` 2. Add SSL and TLS support in aio 3. Added `zlib` +4. Added support for boolean constants in `expr` +5. `string is` now supports 'boolean' class Changes between 0.75 and 0.76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -706,23 +708,29 @@ If no numeric interpretation is possible, then an operand is left as a string (and only a limited set of operators may be applied to it). +String constants representing boolean constants +(+'0'+, +'1'+, +'false'+, +'off'+, +'no'+, +'true'+, +'on'+, +'yes'+) +are also recognized and can be used in logical operations. + 1. Operands may be specified in any of the following ways: 2. As a numeric value, either integer or floating-point. -3. As a Tcl variable, using standard '$' notation. +3. As one of valid boolean constants + +4. As a Tcl variable, using standard '$' notation. The variable's value will be used as the operand. -4. As a string enclosed in double-quotes. +5. As a string enclosed in double-quotes. The expression parser will perform backslash, variable, and command substitutions on the information between the quotes, and use the resulting value as the operand -5. As a string enclosed in braces. +6. As a string enclosed in braces. The characters between the open brace and matching close brace will be used as the operand without any substitutions. -6. As a Tcl command enclosed in brackets. +7. As a Tcl command enclosed in brackets. The command will be executed and its result will be used as the operand. @@ -860,7 +868,7 @@ In any case, overflow and underflow are generally not detected reliably for intermediate results. Conversion among internal representations for integer, floating-point, -and string operands is done automatically as needed. +string operands is done automatically as needed. For arithmetic computations, integers are used until some floating-point number is introduced, after which floating-point is used. For example, @@ -3906,6 +3914,7 @@ The legal options (which may be abbreviated) are: +alnum+;; Any alphabet or digit character. +alpha+;; Any alphabet character. +ascii+;; Any character with a value less than 128 (those that are in the 7-bit ascii range). + +boolean+;; Any of the valid string formats for a boolean value in Tcl (0, false, no, off, 1, true, yes, on) +control+;; Any control character. +digit+;; Any digit character. +double+;; Any of the valid forms for a double in Tcl, with optional surrounding whitespace. @@ -3920,6 +3929,8 @@ The legal options (which may be abbreviated) are: +xdigit+;; Any hexadecimal digit character ([0-9A-Fa-f]). :: Note that string classification does +'not'+ respect UTF-8. See UTF-8 AND UNICODE + :: + Note that only +'lowercase'+ boolean values are recognized (Tcl accepts any case). +*string last* 'string1 string2 ?lastIndex?'+:: Search +'string2'+ for a sequence of characters that exactly match diff --git a/tests/expr-new.test b/tests/expr-new.test index e3c4378..86e776b 100644 --- a/tests/expr-new.test +++ b/tests/expr-new.test @@ -574,6 +574,42 @@ test expr-20.7 {handling of compile error in runtime case} { list [catch {expr + {[error foo]}} msg] } {1} +test expr-21.1 {checking boolean 0} { + expr 0 +} 0 +test expr-21.2 {checking boolean 0} { + expr {0 || 0} +} 0 +test expr-21.3 {checking boolean false} { + expr {0 || false} +} 0 +test expr-21.4 {checking boolean no} { + expr {0 || no} +} 0 +test expr-21.5 {checking boolean off} { + expr {0 || off} +} 0 +test expr-21.6 {checking boolean 1} { + expr {0 || 1} +} 1 +test expr-21.7 {checking boolean true} { + expr {0 || true} +} 1 +test expr-21.8 {checking boolean yes} { + expr {0 || yes} +} 1 +test expr-21.9 {checking boolean on} { + expr {0 || on} +} 1 +test expr-21.7 {checking boolean mixed case} { + set res 0 + try { + expr {0 || True} + } on error {msg} { + set res 1 + } +} 1 + # cleanup if {[info exists a]} { unset a diff --git a/tests/string.test b/tests/string.test index 33723b7..b095f60 100644 --- a/tests/string.test +++ b/tests/string.test @@ -232,10 +232,10 @@ test string-6.4 {string is, too many args} jim { } {1 {wrong # args: should be "string is class ?-strict? str"}} test string-6.5 {string is, class check} jim { list [catch {string is bogus str} msg] $msg -} {1 {bad class "bogus": must be alnum, alpha, ascii, control, digit, double, graph, integer, lower, print, punct, space, upper, or xdigit}} +} {1 {bad class "bogus": must be alnum, alpha, ascii, boolean, control, digit, double, graph, integer, lower, print, punct, space, upper, or xdigit}} test string-6.6 {string is, ambiguous class} jim { list [catch {string is al str} msg] $msg -} {1 {ambiguous class "al": must be alnum, alpha, ascii, control, digit, double, graph, integer, lower, print, punct, space, upper, or xdigit}} +} {1 {ambiguous class "al": must be alnum, alpha, ascii, boolean, control, digit, double, graph, integer, lower, print, punct, space, upper, or xdigit}} test string-6.10 {string is, ok on empty} { string is alpha {} } 1 @@ -389,7 +389,39 @@ test string-6.88 {string is punct} { test string-6.89 {string is xdigit} { list [string is xdigit 0123456789\u0061bcdefABCDEFg] } 0 - +test string-6.90 {string is boolean, true} { + list [string is boolean 0] +} 1 +test string-6.91 {string is boolean, true} { + list [string is boolean false] +} 1 +test string-6.92 {string is boolean, true} { + list [string is boolean no] +} 1 +test string-6.93 {string is boolean, true} { + list [string is boolean off] +} 1 +test string-6.94 {string is boolean, true} { + list [string is boolean 1] +} 1 +test string-6.95 {string is boolean, true} { + list [string is boolean true] +} 1 +test string-6.96 {string is boolean, true} { + list [string is boolean yes] +} 1 +test string-6.97 {string is boolean, true} { + list [string is boolean on] +} 1 +test string-6.98 {string is boolean, not boolean string, false} { + list [string is boolean a] +} 0 +test string-6.99 {string is boolean, special character, false} { + list [string is boolean -] +} 0 +test string-6.10 {string is boolean, mixed case, false} { + list [string is boolean True] +} 0 test string-7.1 {string last, too few args} { list [catch {string last a} msg] } {1} diff --git a/tests/while.test b/tests/while.test index de6d9b5..165791d 100644 --- a/tests/while.test +++ b/tests/while.test @@ -110,6 +110,10 @@ test while-old-5.2 {while return result} { set x 1 while {$x} {set x 0} } {} +test while-old-5.3 {while return result} { + set x true + while {$x} {set x 0} +} {} # cleanup testreport |