From 427c904742d9d5aec8068fce38a28be9ae65af08 Mon Sep 17 00:00:00 2001 From: hobbs Date: Tue, 9 May 2000 00:00:34 +0000 Subject: * 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). --- ChangeLog | 22 ++++++++++++++++++++ doc/expr.n | 26 ++++++++++++++++-------- generic/tclCompExpr.c | 10 +++++++-- generic/tclCompile.h | 9 +++++++-- generic/tclExecute.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++-- generic/tclParseExpr.c | 42 ++++++++++++++++++++++++++++++++------ tests/expr-old.test | 34 ++++++++++++++++++++++++++----- tests/expr.test | 33 +++++++++++++++++++++++++++--- 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 + * 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 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 -- cgit v0.12