This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 49557875c9 hrw4u: More fixes for complex groups and logical operations 
(#12907)
49557875c9 is described below

commit 49557875c90a288cf09bce1171e642a432996f61
Author: Leif Hedstrom <[email protected]>
AuthorDate: Tue Mar 3 11:43:05 2026 -0700

    hrw4u: More fixes for complex groups and logical operations (#12907)
    
    * hrw4u: More fixes for complex groups and logical operations
    
    * Fixes from reviews
---
 tools/hrw4u/src/hrw_visitor.py                     | 15 ++++++++++++++-
 tools/hrw4u/src/visitor.py                         | 19 +++++++++++++++----
 .../tests/data/conds/double-negation.output.txt    |  4 ++--
 tools/hrw4u/tests/data/conds/exceptions.txt        |  2 +-
 tools/hrw4u/tests/data/conds/group-and-not.ast.txt |  1 +
 .../hrw4u/tests/data/conds/group-and-not.input.txt |  5 +++++
 .../tests/data/conds/group-and-not.output.txt      |  6 ++++++
 tools/hrw4u/tests/data/conds/group-in-if.ast.txt   |  1 +
 tools/hrw4u/tests/data/conds/group-in-if.input.txt | 11 +++++++++++
 .../hrw4u/tests/data/conds/group-in-if.output.txt  | 15 +++++++++++++++
 tools/hrw4u/tests/data/conds/group-nested.ast.txt  |  1 +
 .../hrw4u/tests/data/conds/group-nested.input.txt  |  5 +++++
 .../hrw4u/tests/data/conds/group-nested.output.txt |  7 +++++++
 tools/hrw4u/tests/data/conds/group-or.ast.txt      |  1 +
 tools/hrw4u/tests/data/conds/group-or.input.txt    |  5 +++++
 tools/hrw4u/tests/data/conds/group-or.output.txt   |  6 ++++++
 tools/hrw4u/tests/data/conds/group-two.ast.txt     |  1 +
 tools/hrw4u/tests/data/conds/group-two.input.txt   |  5 +++++
 tools/hrw4u/tests/data/conds/group-two.output.txt  | 10 ++++++++++
 .../tests/data/examples/all-nonsense.output.txt    | 22 ++++++----------------
 20 files changed, 118 insertions(+), 24 deletions(-)

diff --git a/tools/hrw4u/src/hrw_visitor.py b/tools/hrw4u/src/hrw_visitor.py
index cbaa8f7eff..453fe062b6 100644
--- a/tools/hrw4u/src/hrw_visitor.py
+++ b/tools/hrw4u/src/hrw_visitor.py
@@ -57,6 +57,7 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
         self._if_depth = 0  # Track nesting depth of if blocks
         self._in_elif_mode = False
         self._just_closed_nested = False
+        self._deferred_comments: list[str] = []
 
     @lru_cache(maxsize=128)
     def _cached_percent_parsing(self, pct_text: str) -> tuple[str, str | None]:
@@ -83,6 +84,12 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
         self._in_group = False
         self._group_terms.clear()
 
+    def _flush_deferred_comments(self) -> None:
+        """Emit comments that were deferred while inside a rule's if-block."""
+        for comment in self._deferred_comments:
+            self.output.append(comment)
+        self._deferred_comments.clear()
+
     def _start_new_section(self, section_type: SectionType) -> None:
         """Start a new section, handling continuation of existing sections."""
         with self.debug_context(f"start_section {section_type.value}"):
@@ -93,6 +100,7 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
                     self.emit("}")
                     self._if_depth -= 1
                 self._reset_condition_state()
+                self._flush_deferred_comments()
                 if self.output and self.output[-1] != "":
                     self.output.append("")
                 return
@@ -105,6 +113,8 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
             if had_section and self.output and self.output[-1] != "":
                 self.output.append("")
 
+            self._flush_deferred_comments()
+
             self._section_label = section_type
             self.emit(f"{section_type.value} {{")
             self._section_opened = True
@@ -152,6 +162,7 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
             for line in ctx.line():
                 self.visit(line)
             self._close_if_and_section()
+            self._flush_deferred_comments()
 
             var_declarations = self.symbol_resolver.get_var_declarations()
             if var_declarations:
@@ -167,7 +178,9 @@ class HRWInverseVisitor(u4wrhVisitor, BaseHRWVisitor):
         with self.debug_context("visitCommentLine"):
             comment_text = ctx.COMMENT().getText()
             self._flush_pending_condition()
-            if self._section_opened:
+            if self._if_depth > 0:
+                self._deferred_comments.append(comment_text)
+            elif self._section_opened:
                 self.emit(comment_text)
             else:
                 self.output.append(comment_text)
diff --git a/tools/hrw4u/src/visitor.py b/tools/hrw4u/src/visitor.py
index ad240db05d..e62f222078 100644
--- a/tools/hrw4u/src/visitor.py
+++ b/tools/hrw4u/src/visitor.py
@@ -283,7 +283,7 @@ class HRW4UVisitor(hrw4uVisitor, BaseHRWVisitor):
                     else:
                         self.visit(body)
             elif is_conditional or not in_statement_block:
-                if idx > 0:
+                if first_hook_emitted:
                     self._flush_condition()
                     self.output.append("")
 
@@ -603,14 +603,25 @@ class HRW4UVisitor(hrw4uVisitor, BaseHRWVisitor):
             match ctx:
                 case _ if ctx.getChildCount() == 2 and 
ctx.getChild(0).getText() == "!":
                     self._dbg("`NOT' detected")
-                    self._cond_state.not_ = True
-                    self.emit_factor(ctx.getChild(1), last=last)
+                    child = ctx.getChild(1)
+                    if child.LPAREN():
+                        self._dbg("GROUP-START (negated)")
+                        self.emit_condition("cond %{GROUP}", final=True)
+                        with self.cond_indented():
+                            self.emit_expression(child.expression(), 
nested=False, last=True, grouped=False)
+                        self._cond_state.last = last
+                        self._cond_state.not_ = True
+                        self.emit_condition("cond %{GROUP:END}")
+                    else:
+                        self._cond_state.not_ = True
+                        self.emit_factor(child, last=last)
 
                 case _ if ctx.LPAREN():
                     self._dbg("GROUP-START")
                     self.emit_condition("cond %{GROUP}", final=True)
                     with self.cond_indented():
-                        self.emit_expression(ctx.expression(), nested=False, 
last=True, grouped=True)
+                        # grouped=False: GROUP is already open; no second wrap 
for OR.
+                        self.emit_expression(ctx.expression(), nested=False, 
last=True, grouped=False)
                     self._cond_state.last = last
                     self.emit_condition("cond %{GROUP:END}")
 
diff --git a/tools/hrw4u/tests/data/conds/double-negation.output.txt 
b/tools/hrw4u/tests/data/conds/double-negation.output.txt
index 9ee7a7dfe0..81c4e86754 100644
--- a/tools/hrw4u/tests/data/conds/double-negation.output.txt
+++ b/tools/hrw4u/tests/data/conds/double-negation.output.txt
@@ -1,5 +1,5 @@
 cond %{REMAP_PSEUDO_HOOK} [AND]
 cond %{GROUP}
-    cond %{CLIENT-HEADER:X-Debug} ="keep"
-cond %{GROUP:END}
+    cond %{CLIENT-HEADER:X-Debug} ="keep" [NOT]
+cond %{GROUP:END} [NOT]
     set-header X-Found "yes"
diff --git a/tools/hrw4u/tests/data/conds/exceptions.txt 
b/tools/hrw4u/tests/data/conds/exceptions.txt
index 85aecfe361..86e44106ba 100644
--- a/tools/hrw4u/tests/data/conds/exceptions.txt
+++ b/tools/hrw4u/tests/data/conds/exceptions.txt
@@ -3,5 +3,5 @@
 #
 # Implicit = comparisons
 implicit-cmp.input: u4wrh
-# Double negation !(x != y) cancels to x == y, reverse can't reconstruct the 
original form
+# Double negation !(x != y) emits GROUP/GROUP:END [NOT] pairs; reverse can't 
reconstruct the original form
 double-negation.input: hrw4u
diff --git a/tools/hrw4u/tests/data/conds/group-and-not.ast.txt 
b/tools/hrw4u/tests/data/conds/group-and-not.ast.txt
new file mode 100644
index 0000000000..eef1424430
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-and-not.ast.txt
@@ -0,0 +1 @@
+(program (programItem (section SEND_RESPONSE { (sectionBody (conditional 
(ifStatement if (condition (expression (term (factor ! (factor ( (expression 
(term (term (factor inbound.req.X-Foo)) && (factor inbound.req.X-Bar))) )))))) 
(block { (blockItem (statement inbound.resp.X-Result = (value "matched") ;)) 
})))) })) <EOF>)
diff --git a/tools/hrw4u/tests/data/conds/group-and-not.input.txt 
b/tools/hrw4u/tests/data/conds/group-and-not.input.txt
new file mode 100644
index 0000000000..c517ebae9b
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-and-not.input.txt
@@ -0,0 +1,5 @@
+SEND_RESPONSE {
+    if !(inbound.req.X-Foo && inbound.req.X-Bar) {
+        inbound.resp.X-Result = "matched";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/group-and-not.output.txt 
b/tools/hrw4u/tests/data/conds/group-and-not.output.txt
new file mode 100644
index 0000000000..b9f6292210
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-and-not.output.txt
@@ -0,0 +1,6 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Foo} ="" [AND,NOT]
+    cond %{CLIENT-HEADER:X-Bar} ="" [NOT]
+cond %{GROUP:END} [NOT]
+    set-header X-Result "matched"
diff --git a/tools/hrw4u/tests/data/conds/group-in-if.ast.txt 
b/tools/hrw4u/tests/data/conds/group-in-if.ast.txt
new file mode 100644
index 0000000000..0e833a389f
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-in-if.ast.txt
@@ -0,0 +1 @@
+(program (programItem (section SEND_RESPONSE { (sectionBody (conditional 
(ifStatement if (condition (expression (term (factor (comparison (comparable 
inbound.url.path) ~ (regex /\/alpha\//)))))) (block { (blockItem (conditional 
(ifStatement if (condition (expression (term (term (factor (comparison 
(comparable inbound.req.X-Branch) == (value "alpha")))) && (factor ( 
(expression (expression (term (factor inbound.req.X-Sub-A))) || (term (factor 
inbound.req.X-Sub-B))) ))))) (block { (blockIt [...]
diff --git a/tools/hrw4u/tests/data/conds/group-in-if.input.txt 
b/tools/hrw4u/tests/data/conds/group-in-if.input.txt
new file mode 100644
index 0000000000..51be6387b5
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-in-if.input.txt
@@ -0,0 +1,11 @@
+SEND_RESPONSE {
+    if inbound.url.path ~ /\/alpha\// {
+        if inbound.req.X-Branch == "alpha" && (inbound.req.X-Sub-A || 
inbound.req.X-Sub-B) {
+            inbound.resp.X-Result = "alpha";
+        } elif inbound.req.X-Branch == "beta" {
+            inbound.resp.X-Result = "beta";
+        } else {
+            inbound.resp.X-Result = "other";
+        }
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/group-in-if.output.txt 
b/tools/hrw4u/tests/data/conds/group-in-if.output.txt
new file mode 100644
index 0000000000..c74ee840b5
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-in-if.output.txt
@@ -0,0 +1,15 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/alpha\//
+    if
+        cond %{CLIENT-HEADER:X-Branch} ="alpha" [AND]
+        cond %{GROUP}
+            cond %{CLIENT-HEADER:X-Sub-A} ="" [OR,NOT]
+            cond %{CLIENT-HEADER:X-Sub-B} ="" [NOT]
+        cond %{GROUP:END}
+            set-header X-Result "alpha"
+        elif
+            cond %{CLIENT-HEADER:X-Branch} ="beta"
+                set-header X-Result "beta"
+        else
+            set-header X-Result "other"
+    endif
diff --git a/tools/hrw4u/tests/data/conds/group-nested.ast.txt 
b/tools/hrw4u/tests/data/conds/group-nested.ast.txt
new file mode 100644
index 0000000000..cd9fc6d5f1
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-nested.ast.txt
@@ -0,0 +1 @@
+(program (programItem (section SEND_RESPONSE { (sectionBody (conditional 
(ifStatement if (condition (expression (term (term (factor 
inbound.req.X-Outer)) && (factor ( (expression (expression (term (factor 
inbound.req.X-Inner-A))) || (term (factor inbound.req.X-Inner-B))) ))))) (block 
{ (blockItem (statement inbound.resp.X-Result = (value "matched") ;)) })))) })) 
<EOF>)
diff --git a/tools/hrw4u/tests/data/conds/group-nested.input.txt 
b/tools/hrw4u/tests/data/conds/group-nested.input.txt
new file mode 100644
index 0000000000..ebf8d563f8
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-nested.input.txt
@@ -0,0 +1,5 @@
+SEND_RESPONSE {
+    if inbound.req.X-Outer && (inbound.req.X-Inner-A || inbound.req.X-Inner-B) 
{
+        inbound.resp.X-Result = "matched";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/group-nested.output.txt 
b/tools/hrw4u/tests/data/conds/group-nested.output.txt
new file mode 100644
index 0000000000..18a7d8f917
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-nested.output.txt
@@ -0,0 +1,7 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-HEADER:X-Outer} ="" [AND,NOT]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Inner-A} ="" [OR,NOT]
+    cond %{CLIENT-HEADER:X-Inner-B} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Result "matched"
diff --git a/tools/hrw4u/tests/data/conds/group-or.ast.txt 
b/tools/hrw4u/tests/data/conds/group-or.ast.txt
new file mode 100644
index 0000000000..f08d9740d2
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-or.ast.txt
@@ -0,0 +1 @@
+(program (programItem (section SEND_RESPONSE { (sectionBody (conditional 
(ifStatement if (condition (expression (term (factor ( (expression (expression 
(term (factor inbound.req.X-Foo))) || (term (factor inbound.req.X-Bar))) ))))) 
(block { (blockItem (statement inbound.resp.X-Result = (value "matched") ;)) 
})))) })) <EOF>)
diff --git a/tools/hrw4u/tests/data/conds/group-or.input.txt 
b/tools/hrw4u/tests/data/conds/group-or.input.txt
new file mode 100644
index 0000000000..74abbefead
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-or.input.txt
@@ -0,0 +1,5 @@
+SEND_RESPONSE {
+    if (inbound.req.X-Foo || inbound.req.X-Bar) {
+        inbound.resp.X-Result = "matched";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/group-or.output.txt 
b/tools/hrw4u/tests/data/conds/group-or.output.txt
new file mode 100644
index 0000000000..8d7a03187b
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-or.output.txt
@@ -0,0 +1,6 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Foo} ="" [OR,NOT]
+    cond %{CLIENT-HEADER:X-Bar} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Result "matched"
diff --git a/tools/hrw4u/tests/data/conds/group-two.ast.txt 
b/tools/hrw4u/tests/data/conds/group-two.ast.txt
new file mode 100644
index 0000000000..d87d0e1db8
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-two.ast.txt
@@ -0,0 +1 @@
+(program (programItem (section SEND_RESPONSE { (sectionBody (conditional 
(ifStatement if (condition (expression (expression (term (factor ( (expression 
(term (term (factor inbound.req.X-Left-A)) && (factor inbound.req.X-Left-B))) 
)))) || (term (factor ( (expression (term (term (factor inbound.req.X-Right-A)) 
&& (factor inbound.req.X-Right-B))) ))))) (block { (blockItem (statement 
inbound.resp.X-Result = (value "matched") ;)) })))) })) <EOF>)
diff --git a/tools/hrw4u/tests/data/conds/group-two.input.txt 
b/tools/hrw4u/tests/data/conds/group-two.input.txt
new file mode 100644
index 0000000000..30b477cada
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-two.input.txt
@@ -0,0 +1,5 @@
+SEND_RESPONSE {
+    if (inbound.req.X-Left-A && inbound.req.X-Left-B) || 
(inbound.req.X-Right-A && inbound.req.X-Right-B) {
+        inbound.resp.X-Result = "matched";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/group-two.output.txt 
b/tools/hrw4u/tests/data/conds/group-two.output.txt
new file mode 100644
index 0000000000..45c3e09781
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/group-two.output.txt
@@ -0,0 +1,10 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Left-A} ="" [AND,NOT]
+    cond %{CLIENT-HEADER:X-Left-B} ="" [NOT]
+cond %{GROUP:END} [OR]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Right-A} ="" [AND,NOT]
+    cond %{CLIENT-HEADER:X-Right-B} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Result "matched"
diff --git a/tools/hrw4u/tests/data/examples/all-nonsense.output.txt 
b/tools/hrw4u/tests/data/examples/all-nonsense.output.txt
index 0134d0aa96..a51a6fbd1e 100644
--- a/tools/hrw4u/tests/data/examples/all-nonsense.output.txt
+++ b/tools/hrw4u/tests/data/examples/all-nonsense.output.txt
@@ -1,6 +1,5 @@
 # Boolean and integer state you can flip/use across sections
 
-
 cond %{REMAP_PSEUDO_HOOK} [AND]
 # Plugin controls
     set-http-cntl TXN_DEBUG true
@@ -30,19 +29,15 @@ cond %{READ_REQUEST_PRE_REMAP_HOOK} [AND]
 cond %{GROUP}
     cond %{METHOD} ="GET" [AND,NOCASE]
     cond %{GROUP}
-        cond %{GROUP}
-            cond %{CLIENT-URL:PATH} ("mp3","m3u","m3u8") [OR,NOCASE,EXT]
-            cond %{CLIENT-URL:HOST} /(?i)^api\./
-        cond %{GROUP:END} [AND]
+        cond %{CLIENT-URL:PATH} ("mp3","m3u","m3u8") [OR,NOCASE,EXT]
+        cond %{CLIENT-URL:HOST} /(?i)^api\./
     cond %{GROUP:END}
 cond %{GROUP:END}
     set-header X-Prefilter "static-or-api"
 elif
     cond %{GROUP}
-        cond %{GROUP}
-            cond %{ACCESS:/var/developertesting} [OR]
-            cond %{INTERNAL-TRANSACTION}
-        cond %{GROUP:END} [AND]
+        cond %{ACCESS:/var/developertesting} [OR]
+        cond %{INTERNAL-TRANSACTION}
     cond %{GROUP:END}
         set-header X-Prefilter "dev-or-internal"
 elif
@@ -56,10 +51,8 @@ else
 
 cond %{READ_REQUEST_PRE_REMAP_HOOK} [AND]
 cond %{GROUP}
-    cond %{GROUP}
-        cond %{CIDR:16,48} ="10.0.0.0" [OR]
-        cond %{CIDR:8,8} ="fd00::"
-    cond %{GROUP:END} [AND]
+    cond %{CIDR:16,48} ="10.0.0.0" [OR]
+    cond %{CIDR:8,8} ="fd00::"
 cond %{GROUP:END}
     set-header X-Network "private"
 
@@ -102,7 +95,6 @@ cond %{GROUP:END}
     no-op [L]
 # like [L]
 
-
 cond %{READ_REQUEST_HDR_HOOK} [AND]
 # Header presence / equality and capture groups
 cond %{GROUP}
@@ -144,7 +136,6 @@ cond %{GROUP:END}
     set-state-int16 0 6553
 # assign int16
 
-
 cond %{SEND_REQUEST_HDR_HOOK} [AND]
 # Use NEXT-HOP information to adjust Host header
 cond %{GROUP}
@@ -167,7 +158,6 @@ cond %{GROUP:END}
 cond %{REMAP_PSEUDO_HOOK} [AND]
 cond %{CLIENT-HEADER:X-Bar} ="fie" [MID]
 
-
 cond %{READ_RESPONSE_HDR_HOOK} [AND]
 # Cache lookup status checks
 cond %{GROUP}

Reply via email to