Sending the logical NOT message "\" explicitly to a string that is internally
a RexxInteger, such as .true, .false, the result of a comparison, or a
small whole number, returns the truth value itself instead of its
negation. The prefix operator (\x) is not affected. Neither is a string that
is internally a RexxString. A literal "1" or "0" may be either,
depending on whether the same program also uses 1 or 0 as a number, so
"1"~"\" is right in one program and wrong in another.
say .true~"\" -- 1 (should be 0)
say .false~"\" -- 0 (should be 1)
say (1=1)~"\" -- 1 (should be 0)
c = 1 + 0; say c~"\" -- 1 (should be 0)
s = "1" || ""
say s~"\" -- 0 correct (a RexxString)
say \.true -- 0 correct (the prefix operator)
Code that forwards messages to a logical value gets the wrong answer, even
though it never sends "\" itself. That is the usual way to write an object
that behaves like a logical value: an UNKNOWN method that forwards every
message to the value. json.cls and yaml.cls, both in the distribution, do
exactly that, so:
say \.json~true -- 1 (should be 0): JsonBoolean's UNKNOWN forwards "\" to .true
say \.json~false -- 0 (should be 1)
::requires "json.cls"
The same happens with .YamlBoolean~true and ~false from yaml.cls.
Setup.cpp binds the message "\" of the Integer behaviour to
RexxInteger::notOp:
AddMethod("\\", RexxInteger::notOp, 0); // interpreter/memory/Setup.cpp
and RexxInteger::notOp lacks the !:
RexxObject *RexxInteger::notOp()
{
return booleanObject(truthValue(Error_Logical_value_method));
}
The operator entry of the same class, RexxInteger::operatorNot, which the
prefix operator uses, has it. So do RexxString::notOp and
RexxString::operatorNot. That is why \.true and "1"~"\" are right.
notop.patch (one character):
--- a/interpreter/classes/IntegerClass.cpp
+++ b/interpreter/classes/IntegerClass.cpp
@@ -1429,7 +1429,7 @@
*/
RexxObject *RexxInteger::notOp()
{
- return booleanObject(truthValue(Error_Logical_value_method));
+ return booleanObject(!truthValue(Error_Logical_value_method));
}
notop-test.rex checks the message and the operator on integer-backed and
string-backed values, and through a forwarding object like JsonBoolean.
Results:
| Build | notop-test.rex |
Test suite, ooRexx/base (14369 tests, 244723 assertions) |
|---|---|---|
| r13271, official Ubuntu 24.04 package, unpatched | 4 passed, 12 failed | 5 failures |
r13265 source tarball + this patch (IntegerClass.cpp is identical in r13265 and r13271) |
16 passed, 0 failed | the same 5 failures |
The 5 failures are the same tests before and after
(File.testGroup: TEST_FILE_READWRITE_DIRECTORY_EXIST,
TEST_FILE_READWRITE_FILE_EXIST; Stream.testGroup: TEST_RELATIVE_EXISTS2,
TEST_RELATIVE_EXISTS3, TEST_STATE_ERROR). They are about file
permissions: the suite ran as root in a container. Linux x86_64, gcc 13.
Suggested addition to the test suite: the checks in notop-test.rex, in
ooRexx/base/class/RexxInteger.testGroup (message "\" on .true, .false,
a comparison result and a computed integer).
Anonymous
Diff:
Fixed in r13272 (interpreter: RexxInteger::notOp now returns the negation, as RexxInteger::operatorNot already did) and r13273 (tests: ooRexx/base/class/RexxInteger.testGroup, test_integer_not_message and test_integer_not_forwarded).
With the fix, the whole ooRexx/base suite gives the same results as before, plus the two new tests.