summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--doc/expr.n26
-rw-r--r--generic/tclCompExpr.c10
-rw-r--r--generic/tclCompile.h9
-rw-r--r--generic/tclExecute.c55
-rw-r--r--generic/tclParseExpr.c42
-rw-r--r--tests/expr-old.test34
-rw-r--r--tests/expr.test33
8 files changed, 203 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 74763a4..4bccce3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,26 @@
2000-05-08 Jeff Hobbs <hobbs@scriptics.com>
+ * doc/expr.n:
+ * tests/expr.test:
+ * tests/expr-old.test: added tests for 'eq' and 'ne'
+ * generic/tclExecute.c:
+ * generic/tclCompile.h: added INST_STREQ and INST_STRNEQ opcodes
+ that do strict string comparisons.
+ * generic/tclCompExpr.c: added 'eq' and 'ne' string comparison
+ operators.
+ * generic/tclParseExpr.c (GetLexeme): added 'eq' and 'ne' expr
+ parse terms (string (in)equality check).
+
+ * generic/tclCmdIL.c (Tcl_LinsertObjCmd): made use of
+ Tcl_DuplicateObj where code was otherwise duplicated. Made
+ special case of inserting one element at the end work again (where
+ index == len).
+ (Tcl_LreplaceObjCmd): moved Tcl_DuplicateObj call lower and
+ cleaned up use of other arguments.
+
+ * generic/tclObj.c (Tcl_DuplicateObj): simplified code to call
+ TclInitStringRep, which the code was just duplicating in part.
+
* doc/Utf.3:
* generic/tclStubInit.c:
* generic/tcl.decls:
@@ -8,6 +29,7 @@
Tcl_UniCharCaseMatch (unicode parallel to Tcl_StringCaseMatch)
* generic/tclUtil.c: rewrote Tcl_StringCaseMatch algorithm for
optimization and made Tcl_StringMatch just call Tcl_StringCaseMatch
+ * tests/string.test: extended string match tests
2000-05-08 Eric Melski <ericm@scriptics.com>
diff --git a/doc/expr.n b/doc/expr.n
index 44bc0b5..0f424b1 100644
--- a/doc/expr.n
+++ b/doc/expr.n
@@ -1,14 +1,14 @@
'\"
'\" Copyright (c) 1993 The Regents of the University of California.
-'\" Copyright (c) 1994-1997 Sun Microsystems, Inc.
+'\" Copyright (c) 1994-2000 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
-'\" RCS: @(#) $Id: expr.n,v 1.3 2000/04/28 00:47:49 ericm Exp $
+'\" RCS: @(#) $Id: expr.n,v 1.4 2000/05/09 00:00:35 hobbs Exp $
'\"
.so man.macros
-.TH expr n 8.0 Tcl "Tcl Built-In Commands"
+.TH expr n 8.4 Tcl "Tcl Built-In Commands"
.BS
'\" Note: do not modify the .SH NAME line immediately below!
.SH NAME
@@ -133,6 +133,12 @@ in which case string comparison is used.
\fB==\0\0!=\fR
Boolean equal and not equal. Each operator produces a zero/one result.
Valid for all operand types.
+.VS 8.4
+.TP 20
+\fBeq\0\0ne\fR
+Boolean string equal and string not equal. Each operator produces a
+zero/one result. The operand types are interpreted only as strings.
+.VE 8.4
.TP 20
\fB&\fR
Bit-wise AND. Valid for integer operands only.
@@ -332,7 +338,10 @@ returns \fB4.0\fR, not \fB4\fR.
.PP
String values may be used as operands of the comparison operators,
although the expression evaluator tries to do comparisons as integer
-or floating-point when it can.
+or floating-point when it can,
+.VS 8.4
+except in the case of the \fBeq\fR and \fBne\fR operators.
+.VE 8.4
If one of the operands of a comparison is a string and the other
has a numeric value, the numeric operand is converted back to
a string using the C \fIsprintf\fR format specifier
@@ -348,11 +357,13 @@ the second operand is converted to the string \fB18\fR.
Because of Tcl's tendency to treat values as numbers whenever
possible, it isn't generally a good idea to use operators like \fB==\fR
when you really want string comparison and the values of the
-operands could be arbitrary; it's better in these cases to use the
-\fBstring compare\fR command instead.
+operands could be arbitrary; it's better in these cases to use
+.VS 8.4
+the \fBeq\fR or \fBne\fR operators, or
+.VE 8.4
+the \fBstring\fR command instead.
.SH "PERFORMANCE CONSIDERATIONS"
-.VS
.PP
Enclose expressions in braces for the best speed and the smallest
storage requirements.
@@ -383,7 +394,6 @@ The most expensive code is required for
unbraced expressions that contain command substitutions.
These expressions must be implemented by generating new code
each time the expression is executed.
-.VE
.SH KEYWORDS
arithmetic, boolean, compare, expression, fuzzy comparison
diff --git a/generic/tclCompExpr.c b/generic/tclCompExpr.c
index 0b8fabf..8a781b9 100644
--- a/generic/tclCompExpr.c
+++ b/generic/tclCompExpr.c
@@ -4,11 +4,12 @@
* This file contains the code to compile Tcl expressions.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompExpr.c,v 1.4 1999/08/19 02:59:08 hobbs Exp $
+ * RCS: @(#) $Id: tclCompExpr.c,v 1.5 2000/05/09 00:00:34 hobbs Exp $
*/
#include "tclInt.h"
@@ -101,6 +102,8 @@ typedef struct ExprInfo {
#define OP_QUESTY 18
#define OP_LNOT 19
#define OP_BITNOT 20
+#define OP_STREQ 21
+#define OP_STRNEQ 22
/*
* Table describing the expression operators. Entries in this table must
@@ -141,6 +144,8 @@ OperatorDesc operatorTable[] = {
{"?", 0},
{"!", 1, INST_LNOT},
{"~", 1, INST_BITNOT},
+ {"eq", 2, INST_STREQ},
+ {"ne", 2, INST_STRNEQ},
{NULL}
};
@@ -536,7 +541,8 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
infoPtr->hasOperators = 1;
infoPtr->exprIsJustVarRef = 0;
infoPtr->exprIsComparison =
- ((opIndex >= OP_LESS) && (opIndex <= OP_NEQ));
+ (((opIndex >= OP_LESS) && (opIndex <= OP_NEQ))
+ || ((opIndex >= OP_STREQ) && (opIndex <= OP_STRNEQ)));
break;
}
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 310e67e..aa72c93 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -2,11 +2,12 @@
* tclCompile.h --
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompile.h,v 1.11 1999/05/23 16:37:14 surles Exp $
+ * RCS: @(#) $Id: tclCompile.h,v 1.12 2000/05/09 00:00:34 hobbs Exp $
*/
#ifndef _TCLCOMPILATION
@@ -493,8 +494,12 @@ typedef struct ByteCode {
#define INST_PUSH_RESULT 71
#define INST_PUSH_RETURN_CODE 72
+/* Opcodes 73 to 74 */
+#define INST_STREQ 73
+#define INST_STRNEQ 74
+
/* The last opcode */
-#define LAST_INST_OPCODE 72
+#define LAST_INST_OPCODE 74
/*
* Table describing the Tcl bytecode instructions: their name (for
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index bc026b3..f19a968 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -5,11 +5,12 @@
* commands.
*
* Copyright (c) 1996-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclExecute.c,v 1.10 2000/03/27 22:18:55 hobbs Exp $
+ * RCS: @(#) $Id: tclExecute.c,v 1.11 2000/05/09 00:00:34 hobbs Exp $
*/
#include "tclInt.h"
@@ -98,7 +99,8 @@ int (*tclMatherrPtr)() = matherr;
static char *operatorStrings[] = {
"||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>",
"+", "-", "*", "/", "%", "+", "-", "~", "!",
- "BUILTIN FUNCTION", "FUNCTION"
+ "BUILTIN FUNCTION", "FUNCTION",
+ "", "", "", "", "", "", "", "", "eq", "ne",
};
/*
@@ -1755,6 +1757,55 @@ TclExecuteByteCode(interp, codePtr)
}
ADJUST_PC(1);
+ case INST_STREQ:
+ case INST_STRNEQ:
+ {
+ /*
+ * String (in)equality check
+ */
+ char *s1, *s2;
+ int s1len, s2len;
+ long iResult;
+
+ value2Ptr = POP_OBJECT();
+ valuePtr = POP_OBJECT();
+
+ s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
+ s2 = Tcl_GetStringFromObj(value2Ptr, &s2len);
+ if (s1len == s2len) {
+ /*
+ * We only need to check (in)equality when we have equal
+ * length strings.
+ */
+ if (*pc == INST_STRNEQ) {
+ iResult = (strcmp(s1, s2) != 0);
+ } else {
+ /* INST_STREQ */
+ iResult = (strcmp(s1, s2) == 0);
+ }
+ } else {
+ iResult = (*pc == INST_STRNEQ);
+ }
+
+ /*
+ * Reuse the valuePtr object already on stack if possible.
+ */
+
+ if (Tcl_IsShared(valuePtr)) {
+ PUSH_OBJECT(Tcl_NewLongObj(iResult));
+ TRACE(("%.20s %.20s => %ld\n",
+ O2S(valuePtr), O2S(value2Ptr), iResult));
+ TclDecrRefCount(valuePtr);
+ } else { /* reuse the valuePtr object */
+ TRACE(("%.20s %.20s => %ld\n",
+ O2S(valuePtr), O2S(value2Ptr), iResult));
+ Tcl_SetLongObj(valuePtr, iResult);
+ ++stackTop; /* valuePtr now on stk top has right r.c. */
+ }
+ TclDecrRefCount(value2Ptr);
+ }
+ ADJUST_PC(1);
+
case INST_EQ:
case INST_NEQ:
case INST_LT:
diff --git a/generic/tclParseExpr.c b/generic/tclParseExpr.c
index b9c9d71..fbdd9ea 100644
--- a/generic/tclParseExpr.c
+++ b/generic/tclParseExpr.c
@@ -7,11 +7,12 @@
* code analysis, etc.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclParseExpr.c,v 1.6 1999/12/04 06:15:42 hobbs Exp $
+ * RCS: @(#) $Id: tclParseExpr.c,v 1.7 2000/05/09 00:00:35 hobbs Exp $
*/
#include "tclInt.h"
@@ -118,6 +119,9 @@ typedef struct ParseInfo {
#define NOT 31
#define BIT_NOT 32
+#define STREQ 33
+#define STRNEQ 34
+
/*
* Mapping from lexemes to strings; used for debugging messages. These
* entries must match the order and number of the lexeme definitions above.
@@ -130,7 +134,7 @@ static char *lexemeStrings[] = {
"*", "/", "%", "+", "-",
"<<", ">>", "<", ">", "<=", ">=", "==", "!=",
"&", "^", "|", "&&", "||", "?", ":",
- "!", "~"
+ "!", "~", "eq", "ne",
};
#endif /* TCL_COMPILE_DEBUG */
@@ -720,7 +724,8 @@ ParseBitAndExpr(infoPtr)
* ParseEqualityExpr --
*
* This procedure parses a Tcl equality (inequality) expression:
- * equalityExpr ::= relationalExpr {('==' | '!=') relationalExpr}
+ * equalityExpr ::= relationalExpr
+ * {('==' | '!=' | 'ne' | 'eq') relationalExpr}
*
* Results:
* The return value is TCL_OK on a successful parse and TCL_ERROR
@@ -754,9 +759,10 @@ ParseEqualityExpr(infoPtr)
}
lexeme = infoPtr->lexeme;
- while ((lexeme == EQUAL) || (lexeme == NEQ)) {
+ while ((lexeme == EQUAL) || (lexeme == NEQ)
+ || (lexeme == STREQ) || (lexeme == STRNEQ)) {
operator = infoPtr->start;
- code = GetLexeme(infoPtr); /* skip over == or != */
+ code = GetLexeme(infoPtr); /* skip over ==, !=, 'eq' or 'ne' */
if (code != TCL_OK) {
return code;
}
@@ -766,7 +772,8 @@ ParseEqualityExpr(infoPtr)
}
/*
- * Generate tokens for the subexpression and '==' or '!=' operator.
+ * Generate tokens for the subexpression and '==', '!=', 'eq' or 'ne'
+ * operator.
*/
PrependSubExprTokens(operator, 2, srcStart,
@@ -1735,7 +1742,30 @@ GetLexeme(infoPtr)
infoPtr->lexeme = BIT_NOT;
return TCL_OK;
+ case 'e':
+ if (src[1] == 'q') {
+ infoPtr->lexeme = STREQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ parsePtr->term = infoPtr->next;
+ return TCL_OK;
+ } else {
+ goto checkFuncName;
+ }
+
+ case 'n':
+ if (src[1] == 'e') {
+ infoPtr->lexeme = STRNEQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ parsePtr->term = infoPtr->next;
+ return TCL_OK;
+ } else {
+ goto checkFuncName;
+ }
+
default:
+ checkFuncName:
offset = Tcl_UtfToUniChar(src, &ch);
c = UCHAR(ch);
if (isalpha(UCHAR(c))) { /* INTL: ISO only. */
diff --git a/tests/expr-old.test b/tests/expr-old.test
index 4d44b55..b810d6f 100644
--- a/tests/expr-old.test
+++ b/tests/expr-old.test
@@ -8,12 +8,12 @@
#
# Copyright (c) 1991-1994 The Regents of the University of California.
# Copyright (c) 1994-1997 Sun Microsystems, Inc.
-# Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 1998-2000 by Scriptics Corporation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: expr-old.test,v 1.8 2000/04/10 17:18:58 ericm Exp $
+# RCS: @(#) $Id: expr-old.test,v 1.9 2000/05/09 00:00:36 hobbs Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
@@ -186,6 +186,16 @@ test expr-old-4.15 {string operators} {expr {"abc" != "abd"}} 1
test expr-old-4.16 {string operators} {expr {"abd" != "abd"}} 0
test expr-old-4.17 {string operators} {expr {"0y" < "0x12"}} 0
test expr-old-4.18 {string operators} {expr {"." < " "}} 0
+test expr-old-4.19 {string operators} {expr {"abc" eq "abd"}} 0
+test expr-old-4.20 {string operators} {expr {"abd" eq "abd"}} 1
+test expr-old-4.21 {string operators} {expr {"abc" ne "abd"}} 1
+test expr-old-4.22 {string operators} {expr {"abd" ne "abd"}} 0
+test expr-old-4.23 {string operators} {expr {"" eq "abd"}} 0
+test expr-old-4.24 {string operators} {expr {"" eq ""}} 1
+test expr-old-4.25 {string operators} {expr {"abd" ne ""}} 1
+test expr-old-4.26 {string operators} {expr {"" ne ""}} 0
+test expr-old-4.26 {string operators} {expr {"longerstring" eq "shorter"}} 0
+test expr-old-4.26 {string operators} {expr {"longerstring" ne "shorter"}} 1
# The following tests are non-portable because on some systems "+"
# and "-" can be parsed as numbers.
@@ -305,14 +315,28 @@ test expr-old-14.5 {precedence checks} {expr 1==4>=3} 1
test expr-old-14.6 {precedence checks} {expr 0!=4>=3} 1
test expr-old-14.7 {precedence checks} {expr 1==3<=4} 1
test expr-old-14.8 {precedence checks} {expr 0!=3<=4} 1
+test expr-old-14.9 {precedence checks} {expr 1eq4>3} 1
+test expr-old-14.10 {precedence checks} {expr 0ne4>3} 1
+test expr-old-14.11 {precedence checks} {expr 1eq3<4} 1
+test expr-old-14.12 {precedence checks} {expr 0ne3<4} 1
+test expr-old-14.13 {precedence checks} {expr 1eq4>=3} 1
+test expr-old-14.14 {precedence checks} {expr 0ne4>=3} 1
+test expr-old-14.15 {precedence checks} {expr 1eq3<=4} 1
+test expr-old-14.16 {precedence checks} {expr 0ne3<=4} 1
test expr-old-15.1 {precedence checks} {expr 1==3==3} 0
test expr-old-15.2 {precedence checks} {expr 3==3!=2} 1
test expr-old-15.3 {precedence checks} {expr 2!=3==3} 0
test expr-old-15.4 {precedence checks} {expr 2!=1!=1} 0
-
-test expr-old-16.1 {precedence checks} {expr 2&3==2} 0
-test expr-old-16.2 {precedence checks} {expr 1&3!=3} 0
+test expr-old-15.5 {precedence checks} {expr 1eq3eq3} 0
+test expr-old-15.6 {precedence checks} {expr 3eq3ne2} 1
+test expr-old-15.7 {precedence checks} {expr 2ne3eq3} 0
+test expr-old-15.8 {precedence checks} {expr 2ne1ne1} 0
+
+test expr-old-16.1 {precedence checks} {expr 2&3eq2} 0
+test expr-old-16.2 {precedence checks} {expr 1&3ne3} 0
+test expr-old-16.3 {precedence checks} {expr 2&3eq2} 0
+test expr-old-16.4 {precedence checks} {expr 1&3ne3} 0
test expr-old-17.1 {precedence checks} {expr 7&3^0x10} 19
test expr-old-17.2 {precedence checks} {expr 7^0x10&3} 7
diff --git a/tests/expr.test b/tests/expr.test
index 13d5583..9ea5026 100644
--- a/tests/expr.test
+++ b/tests/expr.test
@@ -5,12 +5,12 @@
# generates output for errors. No output means no errors were found.
#
# Copyright (c) 1996-1997 Sun Microsystems, Inc.
-# Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 1998-2000 by Scriptics Corporation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: expr.test,v 1.9 2000/04/10 17:18:59 ericm Exp $
+# RCS: @(#) $Id: expr.test,v 1.10 2000/05/09 00:00:37 hobbs Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
@@ -126,6 +126,11 @@ test expr-1.14 {TclCompileExprCmd: second level of substitutions in expr with co
set x 2; set b {$x}; set a [expr $b == 2]
set a
} 1
+test expr-1.15 {TclCompileExprCmd: second level of substitutions in expr with comparison as top-level operator} {
+ set a xxx
+ set x 2; set b {$x}; set a [expr $b eq 2]
+ set a
+} 1
test expr-2.1 {TclCompileExpr: are builtin functions registered?} {
expr double(5*[llength "6 2"])
@@ -269,6 +274,12 @@ test expr-7.12 {CompileBitAndExpr: runtime error in bitand arm} {
test expr-7.13 {CompileBitAndExpr: runtime error in bitand arm} {
list [catch {expr {"a"&"b"}} msg] $msg
} {1 {can't use non-numeric string as operand of "&"}}
+test expr-7.14 {CompileBitAndExpr: equality expr} {expr 3eq2} 0
+test expr-7.18 {CompileBitAndExpr: equality expr} {expr {"abc" eq "abd"}} 0
+test expr-7.20 {CompileBitAndExpr: error in equality expr} {
+ catch {expr xne3} msg
+ set msg
+} {syntax error in expression "xne3"}
test expr-8.1 {CompileEqualityExpr: just relational expr} {expr 3>=2} 1
test expr-8.2 {CompileEqualityExpr: just relational expr} {expr 2<=2.1} 1
@@ -290,7 +301,23 @@ test expr-8.11 {CompileEqualityExpr: error compiling equality arm} {
catch {expr 2!=x} msg
set msg
} {syntax error in expression "2!=x"}
-
+test expr-8.14 {CompileBitAndExpr: equality expr} {expr {"a"eq"a"}} 1
+test expr-8.14 {CompileBitAndExpr: equality expr} {expr {"\374" eq "ü"}} 1
+test expr-8.14 {CompileBitAndExpr: equality expr} {expr 3eq2} 0
+test expr-8.15 {CompileBitAndExpr: equality expr} {expr 2.0eq2} 0
+test expr-8.16 {CompileBitAndExpr: equality expr} {expr 3.2ne2.2} 1
+test expr-8.17 {CompileBitAndExpr: equality expr} {expr 01eq1} 0
+test expr-8.18 {CompileBitAndExpr: equality expr} {expr {"abc" eq "abd"}} 0
+test expr-8.19 {CompileBitAndExpr: equality expr} {expr {"abc" ne "abd"}} 1
+test expr-8.20 {CompileBitAndExpr: error in equality expr} {
+ catch {expr xne3} msg
+ set msg
+} {syntax error in expression "xne3"}
+test expr-8.20 {CompileBitAndExpr: error in equality expr} {
+ # These should be ""ed to avoid the error
+ catch {expr a eq b} msg
+ set msg
+} {syntax error in expression "a eq b"}
test expr-9.1 {CompileRelationalExpr: just shift expr} {expr 3<<2} 12
test expr-9.2 {CompileRelationalExpr: just shift expr} {expr 0xff>>2} 63