aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Borodikhin <eliterr@gmail.com>2016-07-09 03:23:10 -0700
committerNikita Borodikhin <eliterr@gmail.com>2016-07-10 01:36:22 -0700
commit245b2009c66f3b3698773d8c49a834913db7d656 (patch)
treeaf9ca48f8de63021131713aa9e8874db30592eeb
parent3d2168352d3d2125ef0a70bd34c6af025687a5e7 (diff)
downloadjimtcl-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.c109
-rw-r--r--jim.h4
-rw-r--r--jim_tcl.txt21
-rw-r--r--tests/expr-new.test36
-rw-r--r--tests/string.test38
-rw-r--r--tests/while.test4
6 files changed, 198 insertions, 14 deletions
diff --git a/jim.c b/jim.c
index ad38140..4760caf 100644
--- a/jim.c
+++ b/jim.c
@@ -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;
diff --git a/jim.h b/jim.h
index 976cf67..dd2b9ab 100644
--- a/jim.h
+++ b/jim.h
@@ -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