Skip to content

Support the logical not operator in profile activation conditions - #12737

Open
SEPURI-SAI-KRISHNA wants to merge 1 commit into
apache:masterfrom
SEPURI-SAI-KRISHNA:fix-condition-not-operator
Open

Support the logical not operator in profile activation conditions#12737
SEPURI-SAI-KRISHNA wants to merge 1 commit into
apache:masterfrom
SEPURI-SAI-KRISHNA:fix-condition-not-operator

Conversation

@SEPURI-SAI-KRISHNA

Copy link
Copy Markdown

Fixes #12736

What

Adds handling for the ! operator to ConditionParser.parseUnary().

Why

The tokenizer already treats ! as an operator and emits a bare ! token, but no parser rule
consumes it. Writing the natural form of a negated condition:

<condition>!exists('some/path')</condition>

fails with Unknown variable: !, which is confusing — nothing in the expression is a variable.

! is defined here as sugar for the not() function that already exists, using the same
toBoolean() coercion, so this adds no new capability and no new semantics. It is handled in
parseUnary(), giving it the same precedence as in Java: tighter than arithmetic, comparison,
&& and ||.

!= is unaffected — the tokenizer emits it as a single token before a bare ! can be produced.

Tests

Four tests added to ConditionParserTest, all failing before the change:

  • testLogicalNotOperator — basic negation, stacking (!!, !!!), and negating a function call
  • testLogicalNotOperatorMatchesNotFunction — pins !x to the same result as not(x)
  • testLogicalNotOperatorPrecedence — precedence against &&, || and parenthesised groups
  • testLogicalNotOperatorCoercion — string/number coercion matches not()

The negation tests deliberately use contains('Hello, World!', ..), which puts a ! inside a
string literal, to confirm the tokenizer still leaves quoted ! alone.


Out of scope — a separate bug found while testing this

compare() handles Number vs Number and String vs String, but has no Boolean branch, so
comparing two booleans throws on the unmodified parser:

true == true    => RuntimeException: Cannot compare true and true with operator ==
true != false   => RuntimeException: Cannot compare true and false with operator !=

This is pre-existing and independent of !. It is deliberately NOT fixed in this PR, and the
tests here avoid boolean-to-boolean comparison. Candidate for a fourth PR.


PR checklist — APPEND THIS TO THE END OF THE PR DESCRIPTION

This is the repo's pull_request_template.md checklist. Paste it below the body above.

Following this checklist to help us incorporate your
contribution quickly and easily:

  • Your pull request should address just one issue, without pulling in other changes.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body.
    Note that commits might be squashed by a maintainer on merge.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied.
    This may not always be possible but is a best-practice.
  • Run mvn verify to make sure basic checks pass.
    A more thorough check will be performed on your pull request automatically.
  • You have run the Core IT successfully.

If your pull request is about ~20 lines of code you don't need to sign an
Individual Contributor License Agreement if you are unsure
please ask on the developers list.

To make clear that you license your contribution under
the Apache License Version 2.0, January 2004
you have to acknowledge this by using the following check-box.

The condition tokenizer already treats ! as an operator character and
emits a bare "!" token when it is not followed by "=", but no parser
rule ever consumed it. parseUnary() handled only "-", so a "!" fell
through to parseTerm(), failed to parse as a number, and ended up in
parseVariableOrUnknownFunction().

Writing the natural form of a negated condition:

    <condition>!exists('some/path')</condition>

therefore failed with "Unknown variable: !", which is confusing since
nothing in the expression is a variable. The workaround was the not()
function.

Handle "!" in parseUnary() as sugar for the existing not() function,
reusing the same toBoolean() coercion so no new semantics are
introduced. Handling it in parseUnary() gives it the same precedence as
in Java: tighter than arithmetic, comparison, && and ||. Recursing into
parseUnary() allows stacking such as !!x.

!= is unaffected, because the tokenizer emits it as a single token
before a bare "!" can be produced.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-implemented addition of the logical ! operator to the ConditionParser. The implementation correctly mirrors the existing not() function by calling the same toBoolean() coercion, and the recursive parseUnary() call naturally supports stacking (!!, !!!).

The placement in parseUnary() gives ! the expected Java-like precedence (tighter than arithmetic, comparison, &&, and ||), and the tokenizer already correctly emits != as a single token before a bare ! can be produced, so no regression is possible there.

The four new test methods provide strong coverage of basic negation, equivalence with not(), precedence interactions, and type coercion. Nice touch using contains('Hello, World!', ...) to exercise ! inside string literals.

Minor nit: testNotEqualsStillParsesAsOneOperator is redundant with existing assertions in testStringComparison and testArithmeticComparisons, though it serves as a self-documenting regression guard for this change.

Thorough PR description with correct scoping — the pre-existing compare() issue is correctly identified as out of scope.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

! is tokenized as an operator in profile activation conditions but cannot be parsed

2 participants