VBS logical operators initialise empty variables? - vbscript

Consider the following bit of VBS:
dim msg, myVar, myVar2
msg = "myVar = " & myVar
msg = msg & vbcrlf & "myVar = empty: " & isempty(myVar)
msg = msg & vbcrlf & "TypeName(myVar) = " & TypeName(myVar)
msgbox msg, , "BEFORE"
if not myVar then myVar2 = true
msg = "myVar = " & myVar
msg = msg & vbcrlf & "myVar = empty: " & isempty(myVar)
msg = msg & vbcrlf & "TypeName(myVar) = " & TypeName(myVar)
msgbox msg, , "AFTER"
I would expect the output from "BEFORE" and "AFTER" to be the same... all we're doing is making a comparison to an uninitialised (empty) variant right?
However - it seems like the "if not" actually initialises it to a (long)zero!
I've been coding in VBS (ASP) for donkey's years, and this is a new one on me!
A few things to note:
The behaviour is the same in both a .vbs and the equivalent ASP code (on my Win 7 desktop and on Server 2008 R2.)
All logical operators - and/or/not/xor produce this effect
Comparison operators do not.
It seems like a potential trap for the unwary... Can anyone explain this behaviour?

I couldn't find anything official about this issue.
After doing some test, I decided that this is a bug or an un-documented effect.
This behaviour does not apply on other similar platforms like VBA and VB6.
Visual Basic for Application:
Visual Basic 6:
VBScript:
As a workaround, passing expressions by value works.
If Not (exp) Then
'or
If Not CBool(exp) Then
ByRef and ByVal Parameters
CBool Function

If you change that statement in the middle to
if not (myVar) then myVar2 = true 'with parenthesis around myVar
then you will not witness the same behavior. BEFORE and AFTER are the same now.
That's because apparently the parenthesis force the Not operator to only perform a logical test and will skip the side effect of Not.
On https://msdn.microsoft.com/en-us/subscriptions/9cy86sfb%28v=vs.84%29.aspx you will find the following about Not
In addition, the Not operator inverts the bit values of any variable and
sets the corresponding bit in result according to the following table:
+-------------------+---------------+
| Bit in expression | Bit in result |
+-------------------+---------------+
| 0 | 1 |
| 1 | 0 |
+-------------------+---------------+
For example
Msgbox Not 2 ' is -3
Msgbox Not -3 ' is 2
That makes sense if you consider that internally the values are stored as signed bytes/words.
000 -4
001 -3 --> 001 inverted is 110
010 -2
011 -1
100 0
101 1
110 2 --> 110 inverted is 001
111 3
Let's convert Empty to Long with
x = CLng(myVar)
You will find that the value of x is 0.
If you use
if not myVar then myVar2 = true
then the result of not myVar will be evaluated (and the resulting value of -1 will subsequently be thrown away). But the calculation takes place anyhow and for this it is necessary to convert Empty to a long first.

https://technet.microsoft.com/en-us/library/ee198865.aspx
So, if you create a variable without initializing it, the variable will take on one of these default values:
If you use the variable as a string, the initial value will be Empty.
If you use the variable as a number, the initial value will be 0.
I would think that since you are doing a Boolean check, you are essentially using myVar as a number, and your statement is read like:
if not 0 then myVar2 = true
'same as: if not FALSE then myVar2 = true
And so myVar is initialized to 0

These are the rules from VBA https://msdn.microsoft.com/en-us/library/ee177324.aspx?f=255&MSPPError=-2147217396
The point is that variables (though not objects) always have a usuable value (objects do have a value of nothing).
5.5.1.2.2 Let-coercion to and from Boolean
When not stored as a Boolean value, False is represented by 0, and True is represented by nonzero values, usually -1.
The semantics of Boolean Let-coercion depend on the source’s value type and the destination’s declared type:
Source Value Type Destination Declared Type Semantics
Boolean Boolean The result is a copy of the source value.
Boolean Any numeric type except Byte If the source value is False, the result is 0. Otherwise, the result is -1.
Boolean Byte If the source value is False, the result is 0. Otherwise, the result is 255.
Any numeric type Boolean If the source value is 0, the result is False. Otherwise, the result is True
5.5.1.2.11 Let-coercion from Empty
The semantics of Empty Let-coercion depend on the destination’s declared type:
Source Value Type Destination Declared Type Semantics
Empty Any numeric type The result is 0.
Empty Boolean The result is False.
Empty Date The result is 12/30/1899 00:00:00.
Empty String The result is a 0-length string.
Empty String * length The result is a string containing length spaces.
Empty Any class or Object Runtime error 424 (Object required) is raised.
Empty Any other type except Variant Runtime error 13 (Type mismatch) is raised.
Your variable is coerced as a string when you first messagebox it.
Then it is coerced as false in line above.
5.6.9.5 Relational Operators
Relational operators are simple data operators that perform comparisons between their operands.
relational-operator = equality-operator / inequality-operator / less-than-operator / greaterthan-operator / less-than-equal-operator / greater-than-equal-operator
Static semantics:
Relational operators are statically resolved as simple data operators.
A relational operator is invalid if the declared type of any operand is an array or UDT.
A relational operator has the following declared type, based on the declared type of its operands:
Left Operand Declared Type Right Operand Declared Type Operator Declared Type
Any type except an array, UDT or Variant Any type except an array, UDT or Variant Boolean Any type except an array or UDT Variant
Variant Any type except an array or UDT Variant
Runtime semantics:
Relational operators are first evaluated as simple data operators.
If the value type of any operand is an array or UDT, runtime error 13 (Type mismatch) is raised.
Before evaluating the relational operator, its non-Null operands undergo Let-coercion to the operator’s effective value type.
The effective value type is determined as follows, based on the value types of the operands:
5.6.9.5.1 = Operator
The = operator performs a value equality comparison on its operands.
equality-operator = expression "=" expression
Runtime semantics:
If the operands are considered equal, True is returned. Otherwise, False is returned.

Related

|| and && aren't methods on Object -- what are they?

The single pipe "or" | exists as a method on TrueClass and FalseClass, but the short circuit || operator does not. Neither does it exist as a method on Object.
This seems to be an exception to ruby's "everything is an object" metaphor.
Main Question: Syntactically speaking, what are || and &&? Are they just baked bits of global syntax?
Secondary Question: I'm flagging this as not part of the main question, because it is potentially subjective, though I suspect it probably isn't.
Is there a language design or performance reason for this asymmetry? It seems to me both operators could have been implemented as methods on Object. Something like:
class Object
def short_circuit_or(other)
!nil? ? true :
!other.nil? ? true : false
end
end
I assume there is a reason they were not. What is it?
Both | and || are operators. || is part of the language while | is implemented as a method by some classes (Array, FalseClass, Integer, NilClass and TrueClass) .
In programming languages, | is used in general as the bitwise OR operator. It combines the bits of its integer operands and produces a new integer value. When used with non-integer operands, some languages convert them to integer, others prohibit such usage.
|| is the logical OR operator. It combines two boolean values (true or false) and produces another boolean value. When its operands are not boolean values, they are converted to boolean by some languages. Ruby (and JavaScript and other languages) evaluate its first operand as boolean and the value of the expression is the value of its first operand if its boolean value is true or the value of its second operand if the logical value of its first one is false. The type of the resulting value is its original type, it is not converted to boolean.
Each language uses its own rules to decide what non-boolean values are converted to false (usually the number 0, the empty string '' and null or undefined); all the other values are converted to true. The only "false" values in Ruby are false (boolean) and nil (non-boolean); all the other values (including 0) are "true".
Because true || anything is true and false && anything is false, many programming languages including Ruby implement short-circuit evaluation for logical expressions.
Using short-circuit evaluation, a logical expression is evaluated from left to right, one operand at a time until the value of the expression can be computed without the need to compute the other operands. In the examples above, the value of anything doesn't change the value of the entire expression. Using short-circuit evaluation, the value of anything is not computed at all because it does not influence the value of the entire expression. Being anything a method call that takes considerable time to execute, the short-circuit evaluation avoids calling it and saves execution time.
As others already mentioned in comments to the question, implementing || as a method of some class is not possible. The value of its second operand must be evaluated in order to be passed as argument to the method and this breaks the short-circuiting behaviour.
The usual representation of the logical values in programming languages uses only one bit (and I guess Ruby does the same.) Results of | and || are the same for operands stored on one bit.
Ruby uses the | symbol to implement different flavors of the OR operation as follows:
bitwise OR for integers;
non-short-circuit logical OR for booleans and nil;
union for arrays.
An expression like:
x = false | a | b | c
ensures that all a, b and c expressions are evaluated (no short-circuit) and the value of x is the logical OR of the logical values of a, b and c.
If a, b and c are method calls, to achieve the same result using the logical OR operator (||) the code needs to look like this:
aa = a
bb = b
cc = c
x = aa || bb || cc
This way each method is called no matter what values are returned by the methods called before it.
For TrueClass, FalseClass and NilClass, the | operator is useful when short-circuit evaluation is not desired.
Also, for Array (an array is just an ordered set), the | operator implements union, an operation that is the semantically equivalent of logical OR for sets.

Integer and String comparison conflict in VBScript

The below VBScript code while trying to run in HP-UFT confused me because the first statement prints True instead of False (which does not seem logical), while the second one prints False (which seems logical)
Code:
print 40 = "40"
a = 40
b = "40"
print a = b
Output:
True
False
It's perfectly logical (cough), there is only one data type in VBScript and that is Variant. However VBScript can handle many different sub types of the Variant data type.
When you compare
40 = "40"
VBScript is implicitly converting the String sub type to an Integer sub type and comparing the result which is the same as performing the following explicit conversion;
40 = CInt("40")
If you already have your variants defined however VBScript only attempts to implicitly convert them if the execution context fits (when it fits is a bit hazy and in some cases a straight up bug - See Ref).
To avoid this use explicit conversions when necessary.
a = CInt(b)
Useful Links
A: VBScript implicit conversion in IF statement different from variable to literals?
MSDN Blog - Typing Hard Can Trip You Up (Eric Lippert)

How to use CASE statement and a parameter in the WHERE clause?

I have an SSRS report where there is a parameter that asks the user to include records where revenue is greater than zero, or records with revenue values that are just zero.
Since the query is not a stored procedure and it is not an option to put it into a procedure, I need to use some case logic for the embedded query. I need to do this in the where clause in the end.
I am trying to do something like this:
SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND
CASE
WHEN :REVENUE = 1 THEN REV != 0
WHEN :REVENUE = 2 THEN REV = 0
END
However, when I run this query I get the following error:
ORA-00905: missing keyword
Is what I am doing not possible? Or is there an error that someone can see and help me with?
Please help. Thanks!
UPDATE: Just to clarify, the user is passing a value of 1 or 2. And the query should filter the data according to what value is passed to it. If 1 is passed in the parameter, then filter out all revenue not equal to zero. Else if two is passed, then filter so that only records where revenue is zero is returned.
You can write it better with a bit of boolean logic:
SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND (
(:REVENUE = 1 AND REV != 0)
OR
(:REVENUE = 2 AND REV = 0 )
)
CASE is meant to extract different values based on conditions, so you can use it to check conditions, but you need to use it as a value to check against a condition
It's not necessary to use a CASE expression to get this particular result.
But it is possible to make use of one.
The problem in the original query is that Oracle is more strict than other databases (like MySQL) in that Oracle doesn't implicitly convert a boolean expression to a value, or convert a value into boolean.
I suspect that Oracle is choking in a couple of places. The error message is only showing us one of those.
The CASE expression returns a value, and Oracle is balking that he won't evaluate the value as a boolean.
To get that value evaluated as a boolean, we could do a comparison of the value to some other value.
If we fix that, I think Oracle is still going to choke on the expression following THEN. Oracle is expecting to return a value, and it's finding a comparison, which evaluates to a boolean.
Okay, so we know the CASE expression needs to return a value, and we need to use that in a boolean expression. If we move that conditional test into the WHEN part, and specify a value to be returned in the THEN, we can compare the return from the CASE expression to another value.
(As an aside... I strongly recommend that you qualify the column references in the SQL statement. That makes the intent more clear. Looking at the statement, it looks like MY_DATE, D_START and D_END are all column references. That's perfectly valid, it just seems a bit odd to me.)
As an example, we could do something like this with the CASE expression:
SELECT t.*
FROM TABLE t
WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
AND CASE
WHEN ( :REVENUE = 1 AND t.REV != 0 ) THEN 1
WHEN ( :REVENUE = 2 AND t.REV = 0 ) THEN 1
ELSE NULL
END = 1
The parens inside the CASE aren't necessary; I just included them to highlight the part that Oracle is evaluating in a boolean context.
So, does that work? If the value passed in for :REVENUE is 2, the condition in the first WHEN won't evaluate to TRUE (the result of first comparison is guaranteed to be FALSE). The condition in the second WHEN may evaluate to TRUE (first comparison will yield TRUE, the result from second comparison will depend on the value in the REV column.)
That CASE expression is either going to return a value of 1 or NULL. (We could just as easily use a 0 or a -1, or 999 in place of NULL if we wanted.)
Once the CASE expression is evaluated, the value returned will be compared to a literal value, as if we wrote e.g. val = 1. That comparison is evaluated as boolean. If it evaluates to TRUE, the row will be returned...
To get Oracle to behave similarly to other databases (like MySQL), we would need to make the conversion from boolean to value and value to boolean explicit. We would still need the return from the CASE compared to 1, like we did above. In place of REV != 0 we could use another CASE expression. I'm not recommending this, just shown here for illustration, converting a boolean to a value.
WHERE CASE
WHEN ( :REVENUE = 1 )
THEN CASE WHEN ( t.REV != 0 ) THEN 1 ELSE NULL END
WHEN ( :REVENUE = 2 )
THEN CASE WHEN ( t.REV = 0 ) THEN 1 ELSE NULL END
ELSE
NULL
END = 1
Note that the return from the outermost CASE expression is being compared to a value, so we get a boolean (where Oracle expects a boolean.)
All of the ELSE NULL in the statements above can be omitted for an equivalent result, since that's the default when ELSE is omitted.)
Again, it's not necessary to use a CASE expression. You can get equivalent results without it. For example:
SELECT t.*
FROM TABLE t
WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
AND ( ( :REVENUE = 1 AND t.REV != 0 )
OR ( :REVENUE = 2 AND t.REV = 0 )
)
In these queries that all return an equivalent result, the CASE expression doesn't buy us anything. But in some circumstances, it can have some advantages over a regular OR, because the CASE expression stops evaluation when a condition in a WHEN clause evaluates to TRUE.
The problem is that Oracle SQL does not have the boolean data type, so you cannot have columns of type boolean, pass boolean parameters to a query, have boolean expressions etc. So they have the somewhat unnatural concept of "condition" which is something that goes into logical conditions (like in the WHERE clause). Unfortunately, when they introduced the case EXPRESSION, which can be used wherever any other expression can be used (but this excludes boolean), they DID NOT introduce a "case CONDITION" - which could be used where other conditions can be used. This omission is odd, since the code for a case condition would probably use 95% of the code for the case expression. All the more weird since PL/SQL does have the boolean type, and the case expression there works seamlessly for Booleans.

vbscript Type mismatch error when calling function

I am running into the Type Mismatch error when I attempt to call a function I created.
Example:
Function DoThis(paramA, paramB, paramC)
If paramA = "Something" Then
DoThis = DoSomething
ElseIf paramA = "This" Then
DoThis = DoSomethingDifferent
Else
DoThis = DoThisOtherThing
End If
End Function
Dim result: result = DoThis(valueA, ValueB, ValueC)
Can anyone see what my mistake could be? Other functions are working correctly. I have double checked the spelling by actually copying and pasting the function name where I call it. I have verified that the function name is not used anywhere else, i.e., as a constant or something else.
Note that when debugging this the ValType for all arguments is vbString. Also I am never able to enter the function, so it is not like I am debugging the function, enter it and then get the type mismatch.
ty.
VBScript has only one data type called a Variant. A Variant is a special kind of data type that can contain different kinds of information, depending on how it is used. Because Variant is the only data type in VBScript, it is also the data type returned by all functions in VBScript.
There are some subtypes of data that a Variant can contain (e.g. Empty, Null, string, integer, object, array etc.) You can use some conversion functions to convert data from one subtype to another, if that conversion is not implicit in VBScript. Now, pay your attention to real, factual data subtype of True and vbTrue.
The True keyword (boolean literal) has a value (inner representation) equal to -1.
On the other hand, vbTrue is one of few built-in constants and, in despite of it's name, has a subtype of Integer! It's one of so-called Tristate Constants:
Constant Value Description
vbUseDefault -2 Use default from computer's regional settings.
vbTrue -1 True
vbFalse 0 False
I hope next code could make clear all above statements:
Wscript.Echo _
vbTrue, CStr( vbTrue), VarType( vbTrue), TypeName( vbTrue) , _
vbNewLine, True, CStr( True), VarType( True), TypeName( True)
However, used with If _condition_ Then ..., there are some particularities; in brief:
The Then part of the If ... statement conditionally executes groups of statements only when a single test If condition is not False, i.e. any non-zero number esteems to be true, not only -1. Therefore you are able to use whatever variable or expression (numeric or string) you choose as long as the result is numeric...
Summarizing: If _expr_ Then ... is the same as
If CBool(_expr_) Then ...
The reason why retval is retuning mismatch error because it has a numeric value and an alpha value and wsh does not like that.
A sure way to get a type mismatch error for the published code is to define DoSomething etc. as Subs (which seems probable, given the names).
I cannot explain why this was a problem, but today I reduced the function down to a simple boolean return value and I still got the type mismatch error.
So I then created a new function with the same parameters and such. When I changed the call to the new function the error goes away.
My original function with the simple boolean return:(MISMATCH ERROR)
Function IsInstalledCheck(valueToCheck, expectedValue, checkType)
IsInstalledCheck = vbFalse
End Function
My new function with the a simple return:(Works)
Function IsItemInstalled(valueToCheck, expectedValue, checkType)
IsItemInstalled = vbFalse
End Function
EDIT
Note that I had tried this with the standard True / False values as well. The solution was to simply recreated the same function with a new name and for whatever magical reason that worked. The function signature was the same, the order of variables, variable names, the test conditions, everything in the body of the new function is the same.

VBScript: Finding the number of non-null elements in an array

What is the "best" way to determine the number of elements in an array in VBScript?
UBound() tells you how many slots have been allocated for the array, but not how many are filled--depending on the situation, those may or may not be the same numbers.
First off there is no predefined identifier called vbUndefined as the currently accepted answer appears to imply. That code only works when there is not an Option Explicit at the top of the script. If you are not yet using Option Explicit then start doing so, it will save you all manner of grief.
The value you could use in place of vbUndefined is Empty, e.g.,:-
If arr(x) = Empty Then ...
Empty is a predefined identify and is the default value of a variable or array element that has not yet had a value assigned to it.
However there is Gotcha to watch out for. The following statements all display true:-
MsgBox 0 = Empty
MsgBox "" = Empty
MsgBox CDate("30 Dec 1899") = True
Hence if you expect any of these values to be a valid defined value of an array element then comparing to Empty doesn't cut it.
If you really want to be sure that the element is truely "undefined" that is "empty" use the IsEmpty function:-
If IsEmpty(arr(x)) Then
IsEmpty will only return true if the parameter it actually properly Empty.
There is also another issue, Null is a possible value that can be held in an array or variable. However:-
MsgBox Null = Empty
Is a runtime error, "invalid use of null" and :-
MsgBox IsEmpty(Null)
is false. So you need to decide if Null means undefined or is a valid value. If Null also means undefined you need your If statement to look like:-
If IsEmpty(arr(x)) Or IsNull(arr(x)) Then ....
You might be able to waive this if you know a Null will never be assigned into the array.
I'm not aware of a non-iterative method to do this, so here's the iterative method:
Function countEmptySlots(arr)
Dim x, c
c = 0
For x = 0 To ubound(arr)
If arr(x) = vbUndefined Then c = c + 1
Next
countEmptySlots = c
End Function
As Spencer Ruport says, it's probably better to keep track yourself to begin with.
There's nothing built in to tell you what elements are filled. The best way is to keep track of this yourself using a count variable as you add/remove elements from the array.

Resources