Diff
Not logged in

Differences From Artifact [d88c95fcb2]:

To Artifact [aff5b5845d]:


129
130
131
132
133
134
135
136
137


138
139
140
141

142
143
144
145

146
147
148
149
150
151
152

153

154

155
156

157
158

159
160

161
162
163

164
165

166
167

168
169
170

171
172
173

174
175

176
177

178
179

180
181

182
183

184
185

186
187
188
189

190
191
192
193



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216

217
218
219
220
221
222


223
224

225
226
227


228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243









244
245

246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263










264
265

266
267
268
269


270
271

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289




290
291

292
293
294
295
296

297
298
299
300
301
302
303
129
130
131
132
133
134
135


136
137
138
139
140

141
142
143
144

145
146
147
148
149
150
151
152
153
154
155

156
157

158
159

160
161

162
163
164

165
166

167
168

169
170
171

172
173


174


175


176


177


178


179


180
181
182
183

184
185



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203


204
205
206
207
208


209
210
211
212
213


214
215
216

217
218


219
220


221
222
223
224
225
226









227
228
229
230
231
232
233
234
235


236


237
238
239
240
241
242
243










244
245
246
247
248
249
250
251
252
253


254
255
256


257
258


259
260
261
262
263
264
265
266
267
268
269
270
271
272
273




274
275
276
277


278
279
280
281
282
283
284
285
286
287
288
289
290
291







-
-
+
+



-
+



-
+







+

+
-
+

-
+

-
+

-
+


-
+

-
+

-
+


-
+

-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+



-
+

-
-
-
+
+
+















-
-
+




-
-
+




-
-
+
+

-
+

-
-
+
+
-
-
+





-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+






-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+


-
-
+
+
-
-
+














-
-
-
-
+
+
+
+
-
-
+





+







 * The four category values are LEAF, UNARY, and BINARY, explained below, and
 * "uncategorized", which is used either temporarily, until context determines
 * which of the other three categories is correct, or for lexemes like
 * INVALID, which aren't really lexemes at all, but indicators of a parsing
 * error. Note that the codes must be distinct to distinguish categories, but
 * need not take the form of a bit array.
 */

#define BINARY		0x40	/* This lexeme is a binary operator. An OpNode
enum NodeCategories {
    BINARY = 0x40,		/* This lexeme is a binary operator. An OpNode
				 * representing it should go into the parse
				 * tree, and two operands should be parsed for
				 * it in the expression. */
#define UNARY		0x80	/* This lexeme is a unary operator. An OpNode
    UNARY = 0x80,		/* This lexeme is a unary operator. An OpNode
				 * representing it should go into the parse
				 * tree, and one operand should be parsed for
				 * it in the expression. */
#define LEAF		0xC0	/* This lexeme is a leaf operand in the parse
    LEAF = 0xC0			/* This lexeme is a leaf operand in the parse
				 * tree. No OpNode will be placed in the tree
				 * for it. Either a literal value will be
				 * appended to the list of literals in this
				 * expression, or appropriate Tcl_Tokens will
				 * be appended in a Tcl_Parse struct to
				 * represent those leaves that require some
				 * form of substitution. */
};

enum TclLexemes {
/* Uncategorized lexemes */
    /* Uncategorized lexemes */

#define PLUS		1	/* Ambiguous. Resolves to UNARY_PLUS or
    PLUS = 1,			/* Ambiguous. Resolves to UNARY_PLUS or
				 * BINARY_PLUS according to context. */
#define MINUS		2	/* Ambiguous. Resolves to UNARY_MINUS or
    MINUS = 2,			/* Ambiguous. Resolves to UNARY_MINUS or
				 * BINARY_MINUS according to context. */
#define BAREWORD	3	/* Ambiguous. Resolves to BOOLEAN or to
    BAREWORD = 3,		/* Ambiguous. Resolves to BOOLWORD or to
				 * FUNCTION or a parse error according to
				 * context and value. */
#define INCOMPLETE	4	/* A parse error. Used only when the single
    INCOMPLETE = 4,		/* A parse error. Used only when the single
				 * "=" is encountered.  */
#define INVALID		5	/* A parse error. Used when any punctuation
    INVALID = 5,		/* A parse error. Used when any punctuation
				 * appears that's not a supported operator. */
#define COMMENT		6	/* Comment. Lasts to end of line or end of
    COMMENT = 6,		/* Comment. Lasts to end of line or end of
				 * expression, whichever comes first. */

/* Leaf lexemes */
    /* Leaf lexemes */

#define NUMBER		(LEAF | 1)
				/* For literal numbers */
    NUMBER = (LEAF | 1),	/* For literal numbers */
#define SCRIPT		(LEAF | 2)
				/* Script substitution; [foo] */
    SCRIPT = (LEAF | 2),	/* Script substitution; [foo] */
#define BOOLEAN		(LEAF | BAREWORD)
				/* For literal booleans */
    BOOLWORD = (LEAF | BAREWORD),/* For literal booleans (false, true, etc.) */
#define BRACED		(LEAF | 4)
				/* Braced string; {foo bar} */
    BRACED = (LEAF | 4),	/* Braced string; {foo bar} */
#define VARIABLE	(LEAF | 5)
				/* Variable substitution; $x */
    VARIABLE = (LEAF | 5),	/* Variable substitution; $x */
#define QUOTED		(LEAF | 6)
				/* Quoted string; "foo $bar [soom]" */
    QUOTED = (LEAF | 6),	/* Quoted string; "foo $bar [soom]" */
#define EMPTY		(LEAF | 7)
				/* Used only for an empty argument list to a
    EMPTY = (LEAF | 7),		/* Used only for an empty argument list to a
				 * function. Represents the empty string
				 * within parens in the expression: rand() */

/* Unary operator lexemes */
    /* Unary operator lexemes */

#define UNARY_PLUS	(UNARY | PLUS)
#define UNARY_MINUS	(UNARY | MINUS)
#define FUNCTION	(UNARY | BAREWORD)
    UNARY_PLUS = (UNARY | PLUS),
    UNARY_MINUS = (UNARY | MINUS),
    FUNCTION = (UNARY | BAREWORD),
				/* This is a bit of "creative interpretation"
				 * on the part of the parser. A function call
				 * is parsed into the parse tree according to
				 * the perspective that the function name is a
				 * unary operator and its argument list,
				 * enclosed in parens, is its operand. The
				 * additional requirements not implied
				 * generally by treatment as a unary operator
				 * -- for example, the requirement that the
				 * operand be enclosed in parens -- are hard
				 * coded in the relevant portions of
				 * ParseExpr(). We trade off the need to
				 * include such exceptional handling in the
				 * code against the need we would otherwise
				 * have for more lexeme categories. */
#define START		(UNARY | 4)
				/* This lexeme isn't parsed from the
    START = (UNARY | 4),	/* This lexeme isn't parsed from the
				 * expression text at all. It represents the
				 * start of the expression and sits at the
				 * root of the parse tree where it serves as
				 * the start/end point of traversals. */
#define OPEN_PAREN	(UNARY | 5)
				/* Another bit of creative interpretation,
    OPEN_PAREN = (UNARY | 5),	/* Another bit of creative interpretation,
				 * where we treat "(" as a unary operator with
				 * the sub-expression between it and its
				 * matching ")" as its operand. See
				 * CLOSE_PAREN below. */
#define NOT		(UNARY | 6)
#define BIT_NOT		(UNARY | 7)
    NOT = (UNARY | 6),
    BIT_NOT = (UNARY | 7),

/* Binary operator lexemes */
    /* Binary operator lexemes */

#define BINARY_PLUS	(BINARY |  PLUS)
#define BINARY_MINUS	(BINARY |  MINUS)
    BINARY_PLUS = (BINARY |  PLUS),
    BINARY_MINUS = (BINARY |  MINUS),
#define COMMA		(BINARY |  3)
				/* The "," operator is a low precedence binary
    COMMA = (BINARY |  3),	/* The "," operator is a low precedence binary
				 * operator that separates the arguments in a
				 * function call. The additional constraint
				 * that this operator can only legally appear
				 * at the right places within a function call
				 * argument list are hard coded within
				 * ParseExpr().  */
#define MULT		(BINARY |  4)
#define DIVIDE		(BINARY |  5)
#define MOD		(BINARY |  6)
#define LESS		(BINARY |  7)
#define GREATER		(BINARY |  8)
#define BIT_AND		(BINARY |  9)
#define BIT_XOR		(BINARY | 10)
#define BIT_OR		(BINARY | 11)
				 * ParseExpr(). */
    MULT = (BINARY |  4),
    DIVIDE = (BINARY |  5),
    MOD = (BINARY |  6),
    LESS = (BINARY |  7),
    GREATER = (BINARY |  8),
    BIT_AND = (BINARY |  9),
    BIT_XOR = (BINARY | 10),
    BIT_OR = (BINARY | 11),
#define QUESTION	(BINARY | 12)
				/* These two lexemes make up the */
    QUESTION = (BINARY | 12),	/* These two lexemes make up the */
#define COLON		(BINARY | 13)
				/* ternary conditional operator, $x ? $y : $z.
    COLON = (BINARY | 13),	/* ternary conditional operator, $x ? $y : $z.
				 * We treat them as two binary operators to
				 * avoid another lexeme category, and code the
				 * additional constraints directly in
				 * ParseExpr(). For instance, the right
				 * operand of a "?" operator must be a ":"
				 * operator. */
#define LEFT_SHIFT	(BINARY | 14)
#define RIGHT_SHIFT	(BINARY | 15)
#define LEQ		(BINARY | 16)
#define GEQ		(BINARY | 17)
#define EQUAL		(BINARY | 18)
#define NEQ		(BINARY | 19)
#define AND		(BINARY | 20)
#define OR		(BINARY | 21)
#define STREQ		(BINARY | 22)
#define STRNEQ		(BINARY | 23)
    LEFT_SHIFT = (BINARY | 14),
    RIGHT_SHIFT = (BINARY | 15),
    LEQ = (BINARY | 16),
    GEQ = (BINARY | 17),
    EQUAL = (BINARY | 18),
    NEQ = (BINARY | 19),
    AND = (BINARY | 20),
    OR = (BINARY | 21),
    STREQ = (BINARY | 22),
    STRNEQ = (BINARY | 23),
#define EXPON		(BINARY | 24)
				/* Unlike the other binary operators, EXPON is
    EXPON = (BINARY | 24),	/* Unlike the other binary operators, EXPON is
				 * right associative and this distinction is
				 * coded directly in ParseExpr(). */
#define IN_LIST		(BINARY | 25)
#define NOT_IN_LIST	(BINARY | 26)
    IN_LIST = (BINARY | 25),
    NOT_IN_LIST = (BINARY | 26),
#define CLOSE_PAREN	(BINARY | 27)
				/* By categorizing the CLOSE_PAREN lexeme as a
    CLOSE_PAREN = (BINARY | 27),/* By categorizing the CLOSE_PAREN lexeme as a
				 * BINARY operator, the normal parsing rules
				 * for binary operators assure that a close
				 * paren will not directly follow another
				 * operator, and the machinery already in
				 * place to connect operands to operators
				 * according to precedence performs most of
				 * the work of matching open and close parens
				 * for us. In the end though, a close paren is
				 * not really a binary operator, and some
				 * special coding in ParseExpr() make sure we
				 * never put an actual CLOSE_PAREN node in the
				 * parse tree. The sub-expression between
				 * parens becomes the single argument of the
				 * matching OPEN_PAREN unary operator. */
#define STR_LT		(BINARY | 28)
#define STR_GT		(BINARY | 29)
#define STR_LEQ		(BINARY | 30)
#define STR_GEQ		(BINARY | 31)
    STR_LT = (BINARY | 28),
    STR_GT = (BINARY | 29),
    STR_LEQ = (BINARY | 30),
    STR_GEQ = (BINARY | 31),
#define END		(BINARY | 32)
				/* This lexeme represents the end of the
    END = (BINARY | 32)		/* This lexeme represents the end of the
				 * string being parsed. Treating it as a
				 * binary operator follows the same logic as
				 * the CLOSE_PAREN lexeme and END pairs with
				 * START, in the same way that CLOSE_PAREN
				 * pairs with OPEN_PAREN. */
};

/*
 * When ParseExpr() builds the parse tree it must choose which operands to
 * connect to which operators.  This is done according to operator precedence.
 * The greater an operator's precedence the greater claim it has to link to an
 * available operand.  The Precedence enumeration lists the precedence values
 * used by Tcl expression operators, from lowest to highest claim.  Each
743
744
745
746
747
748
749
750

751
752
753
754
755
756
757
731
732
733
734
735
736
737

738
739
740
741
742
743
744
745







-
+







		     * name, and there's no place in the parse tree to store
		     * it, so we keep a separate list of all the function
		     * names we've parsed in the order we found them.
		     */

		    Tcl_ListObjAppendElement(NULL, funcList, literal);
		} else if (Tcl_GetBooleanFromObj(NULL,literal,&b) == TCL_OK) {
		    lexeme = BOOLEAN;
		    lexeme = BOOLWORD;
		} else {
		    /*
		     * Tricky case: see test expr-62.10
		     */

		    int scanned2 = scanned;
		    do {
869
870
871
872
873
874
875
876

877
878
879
880
881
882
883
884

885
886
887
888
889
890
891
857
858
859
860
861
862
863

864
865
866
867
868
869
870
871

872
873
874
875
876
877
878
879







-
+







-
+







		scanned = 0;
		insertMark = 1;

		/*
		 * Free any literal to avoid a memleak.
		 */

		if ((lexeme == NUMBER) || (lexeme == BOOLEAN)) {
		if ((lexeme == NUMBER) || (lexeme == BOOLWORD)) {
		    Tcl_DecrRefCount(literal);
		}
		goto error;
	    }

	    switch (lexeme) {
	    case NUMBER:
	    case BOOLEAN:
	    case BOOLWORD:
		/*
		 * TODO: Consider using a dict or hash to collapse all
		 * duplicate literals into a single representative value.
		 * (Like what is done with [split $s {}]).
		 * Pro:	~75% memory saving on expressions like
		 *	{1+1+1+1+1+.....+1} (Convert "pointer + Tcl_Obj" cost
		 *	to "pointer" cost only)
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1040
1041
1042
1043
1044
1045
1046

1047
1048
1049
1050
1051
1052
1053







-







		Tcl_DecrRefCount(literal);
	    }
	    complete = lastParsed = OT_TOKENS;
	    break;
	} /* case LEAF */

	case UNARY:

	    /*
	     * A unary operator appearing just after something that's not an
	     * operator is a syntax error -- something trying to be the left
	     * operand of an operator that doesn't take one.
	     */

	    if (NotOperator(lastParsed)) {
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1522
1523
1524
1525
1526
1527
1528

1529
1530
1531
1532

1533
1534
1535
1536
1537
1538
1539







-




-








	/*
	 * Handle next child node or leaf.
	 */

	switch (next) {
	case OT_EMPTY:

	    /* No tokens and no characters for the OT_EMPTY leaf. */
	    break;

	case OT_LITERAL:

	    /*
	     * Skip any white space that comes before the literal.
	     */

	    scanned = TclParseAllWhiteSpace(start, numBytes);
	    start += scanned;
	    numBytes -= scanned;
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1611
1612
1613
1614
1615
1616
1617

1618
1619
1620
1621
1622
1623
1624







-







	    start += scanned;
	    numBytes -= scanned;
	    tokenPtr += toCopy;
	    break;
	}

	default:

	    /*
	     * Advance to the child node, which is an operator.
	     */

	    nodePtr = nodes + next;

	    /*
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664

1665
1666
1667
1668
1669
1670
1671
1672
1633
1634
1635
1636
1637
1638
1639

1640
1641
1642
1643
1644
1645
1646

1647

1648
1649
1650
1651
1652
1653
1654







-







-
+
-







	     * Generate tokens for the operator / subexpression...
	     */

	    switch (nodePtr->lexeme) {
	    case OPEN_PAREN:
	    case COMMA:
	    case COLON:

		/*
		 * Historical practice has been to have no Tcl_Tokens for
		 * these operators.
		 */

		break;

	    default: {
	    default:

		/*
		 * Remember the index of the last subexpression we were
		 * working on -- that of our parent. We'll stack it later.
		 */

		parentIdx = subExprTokenIdx;

1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1678
1679
1680
1681
1682
1683
1684

1685
1686
1687
1688
1689
1690
1691







-







		 * Tcl_Token of type TCL_TOKEN_OPERATOR will be 0. This means
		 * we can make other use of this field for now to track the
		 * stack of subexpressions we have pending.
		 */

		subExprTokenPtr[1].numComponents = parentIdx;
		break;
	    }
	    }
	    break;
	}

	/* Determine which way to exit the node on this pass. */
    router:
	switch (nodePtr->mark) {
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1738
1739
1740
1741
1742
1743
1744

1745
1746
1747
1748
1749

1750
1751
1752
1753

1754
1755
1756
1757
1758
1759
1760







-





-




-







	    start += scanned;
	    numBytes -= scanned;
	    break;

	case MARK_PARENT:
	    switch (nodePtr->lexeme) {
	    case START:

		/* When we get back to the START node, we're done. */
		return;

	    case COMMA:
	    case COLON:

		/* No tokens for these lexemes -> nothing to do. */
		break;

	    case OPEN_PAREN:

		/*
		 * Skip past matching close paren.
		 */

		scanned = TclParseAllWhiteSpace(start, numBytes);
		start += scanned;
		numBytes -= scanned;
1865
1866
1867
1868
1869
1870
1871
1872
1873


1874
1875
1876
1877
1878
1879
1880
1843
1844
1845
1846
1847
1848
1849


1850
1851
1852
1853
1854
1855
1856
1857
1858







-
-
+
+







				 * first null character. */
    Tcl_Parse *parsePtr)	/* Structure to fill with information about
				 * the parsed expression; any previous
				 * information in the structure is ignored. */
{
    int code;
    OpNode *opTree = NULL;	/* Will point to the tree of operators. */
    Tcl_Obj *litList;	/* List to hold the literals. */
    Tcl_Obj *funcList;	/* List to hold the functon names. */
    Tcl_Obj *litList;		/* List to hold the literals. */
    Tcl_Obj *funcList;		/* List to hold the functon names. */
    Tcl_Parse *exprParsePtr = (Tcl_Parse *)TclStackAlloc(interp, sizeof(Tcl_Parse));
				/* Holds the Tcl_Tokens of substitutions. */

    TclNewObj(litList);
    TclNewObj(funcList);
    if (numBytes < 0) {
	numBytes = (start ? strlen(start) : 0);
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2065
2066
2067
2068
2069
2070
2071

2072
2073
2074
2075
2076
2077
2078







-







	break;
    }

    TclNewObj(literal);
    if (TclParseNumber(NULL, literal, NULL, start, numBytes, &end,
	    TCL_PARSE_NO_WHITESPACE) == TCL_OK) {
	if (end < start + numBytes && !TclIsBareword(*end)) {

	number:
	    *lexemePtr = NUMBER;
	    if (literalPtr) {
		TclInitStringRep(literal, start, end-start);
		*literalPtr = literal;
	    } else {
		Tcl_DecrRefCount(literal);
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688




2689
2690
2691
2692
2693
2694
2695
2656
2657
2658
2659
2660
2661
2662



2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673







-
-
-
+
+
+
+







{
    int code = TCL_OK;

    if (objc < 3) {
	Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
    } else {
	TclOpCmdClientData *occdPtr = (TclOpCmdClientData *)clientData;
	Tcl_Obj **litObjv = (Tcl_Obj **)TclStackAlloc(interp,
		2 * (objc-2) * sizeof(Tcl_Obj *));
	OpNode *nodes = (OpNode *)TclStackAlloc(interp, 2 * (objc-2) * sizeof(OpNode));
	Tcl_Obj **litObjv = (Tcl_Obj **)
		TclStackAlloc(interp, 2 * (objc-2) * sizeof(Tcl_Obj *));
	OpNode *nodes = (OpNode *)
		TclStackAlloc(interp, 2 * (objc-2) * sizeof(OpNode));
	unsigned char lexeme;
	int i, lastAnd = 1;
	Tcl_Obj *const *litObjPtrPtr = litObjv;

	ParseLexeme(occdPtr->op, strlen(occdPtr->op), &lexeme, NULL);

	litObjv[0] = objv[1];