# Function to perform a logical NOT in a string

I am using "tinyexpr", a cute library that can solve mathematical formulas in a string and I extended it such that it does logical operations with two operands as well. However I wasn't sure how to handle the logical NOT as it is performed on a single operand, which is why I just created a small function that does just that outside of tinyexpr:

void handle_logical_not (char * expr)
{
int i;

for(i = 0; expr[i] != '\0'; i++)
{
if(expr[i] == '!')
{
i++;

if(expr[i] == '!')
{
handle_logical_not(&expr[i]); /* Recursively handle subexpressions */
}
if( (expr[i] >= '0' && expr[i] <= '9') || (expr[i] == '-' && (expr[i+1] >= '0' && expr[i+1] <= '9')) )
{
long int    val = 0;
char*       end = NULL;
size_t      len = 0;

if(expr[i] == '0')  val = strtol(&expr[i], &end, 8);
else                val = strtol(&expr[i], &end, 10);

expr[i-1] = '0' + !val;

len = (uintptr_t)&expr[MAX_EXPR] - (uintptr_t)end;

memmove(&expr[i], end, len);
}
}
}
}


You will normally pass a statically allocated string (an expression) to this function and it modifies it directly if it detects a logical NOT operation. For example "!0+!0+!!0" and the function will replace it like that: "1+1+0"

Remarks and Flaws:

• As a bonus it adds a case for octal values.
• It does not skip whitespace characters
• What is the expected result of !(1 + 1)?
– vnp
May 28, 2022 at 5:32
• @vnp !(1) would not be parsed at all. Parenthesizes are not considered and arithmetic is calculated afterwards in the application I am working on. I actually had a remark about that, but I removed it without consideration. May 28, 2022 at 5:36

Declare when needed

// size_t      len = 0;
// ...
// len = (uintptr_t)&expr[MAX_EXPR] - (uintptr_t)end;

size_t len = (uintptr_t)&expr[MAX_EXPR] - (uintptr_t)end;


Consider the string may only be a few bytes, yet MAX_EXPR may be a million. Rather than move lots of bytes, move what is needed.

size_t len = strlen(end) + 1;


(I may have an off-by-one oops here - worthy of more review.)

Note: errno side-effect

Using strtol(), handle_logical_not() may set errno.

+ is a sign too

expr[i] == '-' does not account for + as a sign character.

Octal -123?

if(expr[i] == '0') val = strtol(&expr[i], &end, 8); does not take into account an octal number may be signed. strtol() and "%o" allow negative octal values.

Pedantic: long strings

String array indexing may exceed INT_MAX. Use size_t i.

Nice simply recursion use

Even though a non-recursive solution is possible, this simply handles that issue.

Save time

Use an auto-formatter.

Style

Consider the terser:

            // long int    val = 0;
long val = 0;


Alternative

Rather than if( (expr[i] >= '0' && expr[i] <= '9') || ..., consider calling strtol(&expr[i], &end, 0) and check for conversion success.

Bonus: accepts white-space, + sign and hexadecimal.

Pedantic: -0

For rare non-2's complement char, use for(i = 0; ((unsigned char *)expr)[i] != '\0'; i++) to not stop on a -0.

Likely irrelevant in next C2x version.