]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/esi/Expression.cc
5 * DEBUG: section 86 ESI processing
6 * AUTHOR: Robert Collins
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #include "esi/Expression.h"
39 #include "profiler/Profiler.h"
48 /* stack precedence rules:
49 * before pushing an operator onto the stack, the
50 * top 2 elements are checked. if either has a higher
51 * or equal precedence than the current operator, they
53 * Start of expression has 5 precedence,
54 * end of expression has 0 precedence
55 * literal has 1 as does expression results
59 * == != < > <= >= has 5
64 typedef struct _stackmember stackmember
;
66 typedef int evaluate(stackmember
* stack
, int *depth
, int whereAmI
,
67 stackmember
* candidate
);
83 ESI_EXPR_EXPR
/* the result of an expr PRI 1 */
101 literalhint valuestored
;
106 static void cleanmember(stackmember
*);
107 static void stackpop(stackmember
* s
, int *depth
);
110 cleanmember(stackmember
* s
)
112 if (s
->valuetype
== ESI_EXPR_LITERAL
113 && s
->valuestored
== ESI_LITERAL_STRING
) {
114 safe_free(s
->value
.string
);
115 s
->value
.string
= NULL
;
121 stackpop(stackmember
* s
, int *depth
)
126 cleanmember(&s
[*depth
]);
129 static evaluate evalnegate
;
130 static evaluate evalliteral
;
131 static evaluate evalor
;
132 static evaluate evaland
;
133 static evaluate evallesseq
;
134 static evaluate evallessthan
;
135 static evaluate evalmoreeq
;
136 static evaluate evalmorethan
;
137 static evaluate evalequals
;
138 static evaluate evalnotequals
;
139 static evaluate evalstartexpr
;
140 static evaluate evalendexpr
;
141 static evaluate evalexpr
;
142 static void dumpstack(stackmember
* stack
, int depth
);
143 static int addmember(stackmember
* stack
, int *stackdepth
,
144 stackmember
* candidate
);
145 static int membercompare(stackmember a
, stackmember b
);
146 static char const *trim(char const *s
);
147 static stackmember
getsymbol(const char *s
, char const **endptr
);
148 static void printliteral(stackmember s
);
149 static void printmember(stackmember s
);
151 /* -2 = failed to compate
157 membercompare(stackmember a
, stackmember b
)
159 /* we can compare: sub expressions to sub expressions ,
160 * literals to literals
163 if (!((a
.valuetype
== ESI_EXPR_LITERAL
&& b
.valuetype
== ESI_EXPR_LITERAL
&&
164 a
.valuestored
!= ESI_LITERAL_INVALID
&& b
.valuestored
!= ESI_LITERAL_INVALID
) ||
165 (a
.valuetype
== ESI_EXPR_EXPR
&& b
.valuetype
== ESI_EXPR_EXPR
)))
168 if (a
.valuetype
== ESI_EXPR_EXPR
) {
169 if (a
.value
.integral
== b
.value
.integral
)
173 } else if (a
.valuestored
== ESI_LITERAL_STRING
) {
174 if (b
.valuestored
== ESI_LITERAL_STRING
) {
175 int i
=strcmp(a
.value
.string
, b
.value
.string
);
185 /* TODO: numeric to string conversion ? */
186 debugs(86, DBG_IMPORTANT
, "strcmp with non-string");
189 } else if (a
.valuestored
== ESI_LITERAL_FLOAT
) {
190 if (b
.valuestored
== ESI_LITERAL_INT
) {
191 if (fabs(a
.value
.floating
- b
.value
.integral
) < 0.00001)
193 else if (a
.value
.floating
< b
.value
.integral
)
197 } else if (b
.valuestored
== ESI_LITERAL_FLOAT
) {
198 if (a
.value
.floating
== b
.value
.floating
)
200 else if (a
.value
.floating
< b
.value
.floating
)
205 /* TODO: attempt numeric converson again? */
206 debugs(86, DBG_IMPORTANT
, "floatcomp with non float or int");
209 } else if (a
.valuestored
== ESI_LITERAL_INT
) {
210 if (b
.valuestored
== ESI_LITERAL_INT
) {
211 if (a
.value
.integral
== b
.value
.integral
)
213 else if (a
.value
.integral
< b
.value
.integral
)
217 } else if (b
.valuestored
== ESI_LITERAL_FLOAT
) {
218 if (fabs(a
.value
.integral
- b
.value
.floating
) < 0.00001)
220 else if (a
.value
.integral
< b
.value
.floating
)
225 /* TODO: attempt numeric converson again? */
226 debugs(86, DBG_IMPORTANT
, "intcomp vs non float non int");
234 /* return 0 on success, 1 on failure */
236 evalnegate(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
238 if (whereAmI
!= *depth
- 2)
242 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
)
243 /* invalid operand */
249 stack
[whereAmI
] = stack
[(*depth
)];
251 cleanmember(candidate
);
253 if (stack
[whereAmI
].value
.integral
== 1)
254 stack
[whereAmI
].value
.integral
= 0;
256 stack
[whereAmI
].value
.integral
= 1;
262 evalliteral(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
264 debugs(86, DBG_IMPORTANT
, "attempt to evaluate a literal");
265 /* literals can't be evaluated */
270 evalexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
272 debugs(86, DBG_IMPORTANT
, "attempt to evaluate a sub-expression result");
273 /* sub-scpr's can't be evaluated */
278 evalor(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
284 /* Not enough operands */
287 if (whereAmI
!= *depth
- 2)
291 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
||
292 stack
[whereAmI
- 1].valuetype
!= ESI_EXPR_EXPR
)
293 /* invalid operand */
296 rv
= stack
[whereAmI
- 1].value
.integral
|| stack
[whereAmI
+ 1].value
.integral
;
299 /* invalid comparison */
302 stackpop(stack
, depth
); /* arg rhs */
304 stackpop(stack
, depth
); /* me */
306 stackpop(stack
, depth
); /* arg lhs */
308 srv
.valuetype
= ESI_EXPR_EXPR
;
310 srv
.eval
= evalliteral
;
312 srv
.valuestored
= ESI_LITERAL_BOOL
;
314 srv
.value
.integral
= rv
? 1 : 0;
318 stack
[(*depth
)++] = srv
;
320 /* we're out of way, try adding now */
321 if (!addmember(stack
, depth
, candidate
))
322 /* Something wrong upstream */
329 evaland(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
335 /* Not enough operands */
338 if (whereAmI
!= *depth
- 2)
342 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
||
343 stack
[whereAmI
- 1].valuetype
!= ESI_EXPR_EXPR
)
344 /* invalid operand */
347 rv
= stack
[whereAmI
- 1].value
.integral
&& stack
[whereAmI
+ 1].value
.integral
;
350 /* invalid comparison */
353 stackpop(stack
, depth
); /* arg rhs */
355 stackpop(stack
, depth
); /* me */
357 stackpop(stack
, depth
); /* arg lhs */
359 srv
.valuetype
= ESI_EXPR_EXPR
;
363 srv
.valuestored
= ESI_LITERAL_BOOL
;
365 srv
.value
.integral
= rv
? 1 : 0;
369 stack
[(*depth
)++] = srv
;
371 /* we're out of way, try adding now */
372 if (!addmember(stack
, depth
, candidate
))
373 /* Something wrong upstream */
380 evallesseq(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
386 /* Not enough operands */
389 if (whereAmI
!= *depth
- 2)
393 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
396 /* invalid comparison */
399 stackpop(stack
, depth
); /* arg rhs */
401 stackpop(stack
, depth
); /* me */
403 stackpop(stack
, depth
); /* arg lhs */
405 srv
.valuetype
= ESI_EXPR_EXPR
;
409 srv
.valuestored
= ESI_LITERAL_BOOL
;
411 srv
.value
.integral
= rv
<= 0 ? 1 : 0;
415 stack
[(*depth
)++] = srv
;
417 /* we're out of way, try adding now */
418 if (!addmember(stack
, depth
, candidate
))
419 /* Something wrong upstream */
422 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
428 evallessthan(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
434 /* Not enough operands */
437 if (whereAmI
!= *depth
- 2)
441 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
444 /* invalid comparison */
447 stackpop(stack
, depth
); /* arg rhs */
449 stackpop(stack
, depth
); /* me */
451 stackpop(stack
, depth
); /* arg lhs */
453 srv
.valuetype
= ESI_EXPR_EXPR
;
457 srv
.valuestored
= ESI_LITERAL_BOOL
;
459 srv
.value
.integral
= rv
< 0 ? 1 : 0;
463 stack
[(*depth
)++] = srv
;
465 /* we're out of way, try adding now */
466 if (!addmember(stack
, depth
, candidate
))
467 /* Something wrong upstream */
470 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
476 evalmoreeq(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
482 /* Not enough operands */
485 if (whereAmI
!= *depth
- 2)
489 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
492 /* invalid comparison */
495 stackpop(stack
, depth
); /* arg rhs */
497 stackpop(stack
, depth
); /* me */
499 stackpop(stack
, depth
); /* arg lhs */
501 srv
.valuetype
= ESI_EXPR_EXPR
;
505 srv
.valuestored
= ESI_LITERAL_BOOL
;
507 srv
.value
.integral
= rv
>= 0 ? 1 : 0;
511 stack
[(*depth
)++] = srv
;
513 /* we're out of way, try adding now */
514 if (!addmember(stack
, depth
, candidate
))
515 /* Something wrong upstream */
518 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
524 evalmorethan(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
530 /* Not enough operands */
533 if (whereAmI
!= *depth
- 2)
537 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
540 /* invalid comparison */
543 stackpop(stack
, depth
); /* arg rhs */
545 stackpop(stack
, depth
); /* me */
547 stackpop(stack
, depth
); /* arg lhs */
549 srv
.valuetype
= ESI_EXPR_EXPR
;
553 srv
.valuestored
= ESI_LITERAL_BOOL
;
555 srv
.value
.integral
= rv
> 0 ? 1 : 0;
559 stack
[(*depth
)++] = srv
;
561 /* we're out of way, try adding now */
562 if (!addmember(stack
, depth
, candidate
))
563 /* Something wrong upstream */
566 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
572 evalequals(stackmember
* stack
, int *depth
, int whereAmI
,
573 stackmember
* candidate
)
579 /* Not enough operands */
582 if (whereAmI
!= *depth
- 2)
586 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
589 /* invalid comparison */
592 stackpop(stack
, depth
); /* arg rhs */
594 stackpop(stack
, depth
); /* me */
596 stackpop(stack
, depth
); /* arg lhs */
598 srv
.valuetype
= ESI_EXPR_EXPR
;
602 srv
.valuestored
= ESI_LITERAL_BOOL
;
604 srv
.value
.integral
= rv
? 0 : 1;
608 stack
[(*depth
)++] = srv
;
610 /* we're out of way, try adding now */
611 if (!addmember(stack
, depth
, candidate
))
612 /* Something wrong upstream */
615 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
620 evalnotequals(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
626 /* Not enough operands */
629 if (whereAmI
!= *depth
- 2)
633 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
636 /* invalid comparison */
639 stackpop(stack
, depth
); /* arg rhs */
641 stackpop(stack
, depth
); /* me */
643 stackpop(stack
, depth
); /* arg lhs */
645 srv
.valuetype
= ESI_EXPR_EXPR
;
649 srv
.valuestored
= ESI_LITERAL_BOOL
;
651 srv
.value
.integral
= rv
? 1 : 0;
655 stack
[(*depth
)++] = srv
;
657 /* we're out of way, try adding now */
658 if (!addmember(stack
, depth
, candidate
))
659 /* Something wrong upstream */
662 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
667 evalstartexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
669 /* debugs(86, DBG_IMPORTANT, "?("); */
671 if (whereAmI
!= *depth
- 2)
675 /* Only valid when RHS is an end bracket */
676 if (candidate
->valuetype
!= ESI_EXPR_END
)
681 stack
[whereAmI
] = stack
[(*depth
)];
683 cleanmember(candidate
);
689 evalendexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
691 /* Can't evaluate ) brackets */
705 getsymbol(const char *s
, char const **endptr
)
709 char const *origs
= s
;
710 /* trim whitespace */
712 rv
.eval
= NULL
; /* A literal */
713 rv
.valuetype
= ESI_EXPR_INVALID
;
714 rv
.valuestored
= ESI_LITERAL_INVALID
;
715 rv
.precedence
= 1; /* A literal */
717 if (('0' <= *s
&& *s
<= '9') || *s
== '-') {
718 size_t length
= strspn(s
, "0123456789.");
721 if ((point
= strchr(s
, '.')) && point
- s
< (ssize_t
)length
) {
723 errno
=0; /* reset errno */
724 rv
.value
.floating
= strtod(s
, &end
);
726 if (s
== end
|| errno
) {
727 /* Couldn't convert to float */
728 debugs(86, DBG_IMPORTANT
, "failed to convert '" << s
<< "' to float ");
731 debugs(86,6, "found " << rv
.value
.floating
<< " of length " << end
- s
);
733 rv
.eval
= evalliteral
;
734 rv
.valuestored
= ESI_LITERAL_FLOAT
;
735 rv
.valuetype
= ESI_EXPR_LITERAL
;
740 errno
=0; /* reset errno */
741 rv
.value
.integral
= strtol(s
, &end
, 0);
743 if (s
== end
|| errno
) {
744 /* Couldn't convert to int */
745 debugs(86, DBG_IMPORTANT
, "failed to convert '" << s
<< "' to int ");
748 debugs(86,6, "found " << rv
.value
.integral
<< " of length " << end
- s
);
750 rv
.eval
= evalliteral
;
751 rv
.valuestored
= ESI_LITERAL_INT
;
752 rv
.valuetype
= ESI_EXPR_LITERAL
;
756 } else if ('!' == *s
) {
757 if ('=' == *(s
+ 1)) {
758 debugs(86, 6, "found !=");
760 rv
.eval
= evalnotequals
;
761 rv
.valuetype
= ESI_EXPR_NOTEQ
;
764 debugs(86, 6, "found !");
766 rv
.valuetype
= ESI_EXPR_NOT
;
768 rv
.eval
= evalnegate
;
770 } else if ('\'' == *s
) {
771 char const *t
= s
+ 1;
772 debugs(86, 6, "found \'");
774 while (*t
!= '\'' && *t
)
778 debugs(86, DBG_IMPORTANT
, "missing end \' in '" << s
<< "'");
782 /* Special case for zero length strings */
785 rv
.value
.string
= xstrndup(s
+ 1, t
- s
- 1);
787 rv
.value
.string
= static_cast<char *>(xcalloc(1,1));
789 rv
.eval
= evalliteral
;
791 rv
.valuestored
= ESI_LITERAL_STRING
;
793 rv
.valuetype
= ESI_EXPR_LITERAL
;
797 debugs(86, 6, "found string '" << rv
.value
.string
<< "'");
799 } else if ('(' == *s
) {
800 debugs(86, 6, "found subexpr start");
802 rv
.valuetype
= ESI_EXPR_START
;
804 rv
.eval
= evalstartexpr
;
805 } else if (')' == *s
) {
806 debugs(86, 6, "found subexpr end");
808 rv
.valuetype
= ESI_EXPR_END
;
810 rv
.eval
= evalendexpr
;
811 } else if ('&' == *s
) {
812 debugs(86, 6, "found AND");
814 rv
.valuetype
= ESI_EXPR_AND
;
817 } else if ('|' == *s
) {
818 debugs(86, 6, "found OR");
820 rv
.valuetype
= ESI_EXPR_OR
;
823 } else if ('=' == *s
) {
824 if ('=' == *(s
+ 1)) {
825 debugs(86, 6, "found equals");
827 rv
.valuetype
= ESI_EXPR_EQ
;
829 rv
.eval
= evalequals
;
831 debugs(86, DBG_IMPORTANT
, "invalid expr '" << s
<< "'");
834 } else if ('<' == *s
) {
835 if ('=' == *(s
+ 1)) {
836 debugs(86, 6, "found less-equals");
838 rv
.valuetype
= ESI_EXPR_LESSEQ
;
840 rv
.eval
= evallesseq
;
842 debugs(86, 6, "found less than");
844 rv
.valuetype
= ESI_EXPR_LESS
;
846 rv
.eval
= evallessthan
;
848 } else if ('>' == *s
) {
849 if ('=' == *(s
+ 1)) {
850 debugs(86, 6, "found more-equals");
852 rv
.valuetype
= ESI_EXPR_MOREEQ
;
854 rv
.eval
= evalmoreeq
;
856 debugs(86, 6, "found more than");
858 rv
.valuetype
= ESI_EXPR_MORE
;
860 rv
.eval
= evalmorethan
;
862 } else if (!strncmp(s
, "false", 5)) {
863 debugs(86, 5, "getsymbol: found variable result 'false'");
865 rv
.valuetype
= ESI_EXPR_EXPR
;
866 rv
.valuestored
= ESI_LITERAL_BOOL
;
867 rv
.value
.integral
= 0;
870 } else if (!strncmp(s
, "true", 4)) {
871 debugs(86, 5, "getsymbol: found variable result 'true'");
873 rv
.valuetype
= ESI_EXPR_EXPR
;
874 rv
.valuestored
= ESI_LITERAL_BOOL
;
875 rv
.value
.integral
= 1;
879 debugs(86, DBG_IMPORTANT
, "invalid expr '" << s
<< "'");
887 printliteral(stackmember s
)
889 switch (s
.valuestored
) {
891 case ESI_LITERAL_INVALID
:
892 old_debug(86, 1)( " Invalid " );
895 case ESI_LITERAL_FLOAT
:
896 old_debug(86,1)("%f", s
.value
.floating
);
899 case ESI_LITERAL_STRING
:
900 old_debug(86,1)("'%s'", s
.value
.string
);
903 case ESI_LITERAL_INT
:
904 old_debug(86,1)("%d", s
.value
.integral
);
907 case ESI_LITERAL_BOOL
:
908 old_debug(86,1)("%s",s
.value
.integral
? "true" : "false");
913 printmember(stackmember s
)
915 switch (s
.valuetype
) {
917 case ESI_EXPR_INVALID
:
918 old_debug(86,1)(" Invalid ");
921 case ESI_EXPR_LITERAL
:
926 old_debug(86,1)("%s", s
.value
.integral
? "true" : "false");
930 old_debug(86,1)("|");
934 old_debug(86,1)("&");
938 old_debug(86,1)("!");
942 old_debug(86,1)("(");
946 old_debug(86,1)(")");
950 old_debug(86,1)("==");
954 old_debug(86,1)("!=");
958 old_debug(86,1)("<");
961 case ESI_EXPR_LESSEQ
:
962 old_debug(86,1)("<=");
966 old_debug(86,1)(">");
969 case ESI_EXPR_MOREEQ
:
970 old_debug(86,1)(">=");
976 dumpstack(stackmember
* stack
, int depth
)
980 for (i
= 0; i
< depth
; ++i
)
981 printmember(stack
[i
]);
984 old_debug(86,1)("\n");
988 addmember(stackmember
* stack
, int *stackdepth
, stackmember
* candidate
)
990 if (candidate
->valuetype
!= ESI_EXPR_LITERAL
&& *stackdepth
> 1) {
991 /* !(!(a==b))) is why thats safe */
992 /* strictly less than until we unwind */
994 if (candidate
->precedence
< stack
[*stackdepth
- 1].precedence
||
995 candidate
->precedence
< stack
[*stackdepth
- 2].precedence
) {
996 /* must be an operator */
998 if (stack
[*stackdepth
- 2].valuetype
== ESI_EXPR_LITERAL
||
999 stack
[*stackdepth
- 2].valuetype
== ESI_EXPR_INVALID
||
1000 stack
[*stackdepth
- 2].eval(stack
, stackdepth
,
1001 *stackdepth
- 2, candidate
)) {
1002 /* cleanup candidate and stack */
1003 dumpstack(stack
, *stackdepth
);
1004 cleanmember(candidate
);
1005 debugs(86, DBG_IMPORTANT
, "invalid expression");
1009 stack
[(*stackdepth
)++] = *candidate
;
1011 } else if (candidate
->valuetype
!= ESI_EXPR_INVALID
)
1012 stack
[(*stackdepth
)++] = *candidate
;
1018 ESIExpression::Evaluate(char const *s
)
1020 stackmember stack
[20];
1023 PROF_start(esiExpressionEval
);
1026 stackmember candidate
= getsymbol(s
, &end
);
1028 if (candidate
.valuetype
!= ESI_EXPR_INVALID
) {
1031 if (!addmember(stack
, &stackdepth
, &candidate
)) {
1032 PROF_stop(esiExpressionEval
);
1039 debugs(86, DBG_IMPORTANT
, "failed parsing expression");
1040 PROF_stop(esiExpressionEval
);
1045 if (stackdepth
> 1) {
1047 rv
.valuetype
= ESI_EXPR_INVALID
;
1050 if (stack
[stackdepth
- 2].
1051 eval(stack
, &stackdepth
, stackdepth
- 2, &rv
)) {
1052 /* special case - leading operator failed */
1053 debugs(86, DBG_IMPORTANT
, "invalid expression");
1054 PROF_stop(esiExpressionEval
);
1059 if (stackdepth
== 0) {
1060 /* Empty expression - evaluate to false */
1061 PROF_stop(esiExpressionEval
);
1065 /* if we hit here, we think we have a valid result */
1066 assert(stackdepth
== 1);
1068 assert(stack
[0].valuetype
== ESI_EXPR_EXPR
);
1070 PROF_stop(esiExpressionEval
);
1072 return stack
[0].value
.integral
? 1 : 0;