Skip to content

Commit 4d4de37

Browse files
committed
HRW4U: Allow for explicit state variable slot decl
1 parent b5bb8d7 commit 4d4de37

15 files changed

+99
-22
lines changed

doc/admin-guide/configuration/hrw4u.en.rst

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,29 @@ TXN_CLOSE_HOOK TXN_CLOSE End of transaction
323323
A special section `VARS` is used to declare variables. There is no equivalent in
324324
`header_rewrite`, where you managed the variables manually.
325325

326-
.. note::
327-
The section name is always required in HRW4U, there are no implicit or default hooks. There
328-
can be several if/else block per section block.
326+
Variables and State Slots
327+
^^^^^^^^^^^^^^^^^^^^^^^^^^
328+
329+
Each variable type has a limited number of slots available:
330+
331+
- ``bool`` - 16 slots (0-15)
332+
- ``int8`` - 4 slots (0-3)
333+
- ``int16`` - 1 slot (0)
334+
335+
By default, slots are assigned automatically in declaration order. You can explicitly assign
336+
a slot number using the ``@`` syntax::
337+
338+
VARS {
339+
priority: bool @7; # Explicitly use slot 7
340+
active: bool; # Auto-assigned to slot 0
341+
config: bool @12; # Explicitly use slot 12
342+
counter: int8 @2; # Explicitly use int8 slot 2
343+
}
344+
345+
Explicit slot assignment is useful when you need predictable slot numbers across configurations
346+
or when integrating with existing header_rewrite rules that reference specific slot numbers. In
347+
addition, a remap configuration can use ``@PPARAM`` to set one of these slot variables explicitly
348+
as part of the configuration.
329349

330350
Groups
331351
------

tools/hrw4u/grammar/hrw4u.g4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ NOT_TILDE : '!~';
8080
COLON : ':';
8181
COMMA : ',';
8282
SEMICOLON : ';';
83+
AT : '@';
8384
8485
COMMENT : '#' ~[\r\n]* ;
8586
WS : [ \t\r\n]+ -> skip ;
@@ -121,7 +122,7 @@ variablesItem
121122
;
122123
123124
variableDecl
124-
: name=IDENT COLON typeName=IDENT SEMICOLON
125+
: name=IDENT COLON typeName=IDENT (AT slot=NUMBER)? SEMICOLON
125126
;
126127
127128
statement

tools/hrw4u/src/symbols.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,32 @@ def get_statement_spec(self, name: str) -> tuple[str, Callable[[str], None] | No
4343
return params.target, params.validate
4444
raise SymbolResolutionError(name, "Unknown operator or invalid standalone use")
4545

46-
def declare_variable(self, name: str, type_name: str) -> str:
46+
def declare_variable(self, name: str, type_name: str, explicit_slot: int | None = None) -> str:
4747
try:
4848
var_type = types.VarType.from_str(type_name)
4949
except ValueError as e:
5050
error = SymbolResolutionError(name, f"Invalid type '{type_name}'")
5151
error.add_note(f"Available types: {', '.join([vt.name for vt in types.VarType])}")
5252
raise error
5353

54-
if self._var_counter[var_type] >= var_type.limit:
55-
error = SymbolResolutionError(name, f"Too many '{type_name}' variables (max {var_type.limit})")
56-
error.add_note(f"Current count: {self._var_counter[var_type]}")
57-
raise error
54+
# Determine slot number
55+
if explicit_slot is not None:
56+
if explicit_slot < 0 or explicit_slot >= var_type.limit:
57+
raise SymbolResolutionError(
58+
name, f"Slot @{explicit_slot} out of range for type '{type_name}' (valid: 0-{var_type.limit-1})")
59+
for var_name, sym in self._symbols.items():
60+
if sym.var_type == var_type and sym.slot == explicit_slot:
61+
raise SymbolResolutionError(name, f"Slot @{explicit_slot} already used by variable '{var_name}'")
62+
63+
slot = explicit_slot
64+
else:
65+
used_slots = {sym.slot for sym in self._symbols.values() if sym.var_type == var_type}
66+
slot = next((i for i in range(var_type.limit) if i not in used_slots), None)
67+
68+
if slot is None:
69+
raise SymbolResolutionError(name, f"No available slots for type '{type_name}' (max {var_type.limit})")
5870

59-
symbol = types.Symbol(var_type, self._var_counter[var_type])
60-
self._var_counter[var_type] += 1
71+
symbol = types.Symbol(var_type, slot)
6172
self._symbols[name] = symbol
6273
return symbol.as_cond()
6374

tools/hrw4u/src/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,13 @@ def from_str(cls, type_str: str) -> Self:
163163
@dataclass(slots=True, frozen=True)
164164
class Symbol:
165165
var_type: VarType
166-
index: int
166+
slot: int
167167

168168
def as_cond(self) -> str:
169-
return f"%{{STATE-{self.var_type.cond_tag}:{self.index}}}"
169+
return f"%{{STATE-{self.var_type.cond_tag}:{self.slot}}}"
170170

171171
def as_operator(self, value: str) -> str:
172-
return f"{self.var_type.op_tag} {self.index} {value}"
172+
return f"{self.var_type.op_tag} {self.slot} {value}"
173173

174174

175175
class MapParams:

tools/hrw4u/src/visitor.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,17 +399,21 @@ def visitVariableDecl(self, ctx) -> None:
399399
if ctx.typeName is None:
400400
raise SymbolResolutionError("variable", "Missing type name in declaration")
401401
name = ctx.name.text
402-
type = ctx.typeName.text
402+
type_name = ctx.typeName.text
403+
explicit_slot = int(ctx.slot.text) if ctx.slot else None
403404

404405
if '.' in name or ':' in name:
405406
raise SymbolResolutionError("variable", f"Variable name '{name}' cannot contain '.' or ':' characters")
406407

407-
symbol = self.symbol_resolver.declare_variable(name, type)
408-
self._dbg(f"bind `{name}' to {symbol}")
408+
symbol = self.symbol_resolver.declare_variable(name, type_name, explicit_slot)
409+
slot_info = f" @{explicit_slot}" if explicit_slot is not None else ""
410+
self._dbg(f"bind `{name}' to {symbol}{slot_info}")
409411
except Exception as e:
410412
name = getattr(ctx, 'name', None)
411413
type_name = getattr(ctx, 'typeName', None)
412-
note = f"Variable declaration: {name.text}:{type_name.text}" if name and type_name else None
414+
slot = getattr(ctx, 'slot', None)
415+
note = f"Variable declaration: {name.text}:{type_name.text}" + \
416+
(f" @{slot.text}" if slot else "") if name and type_name else None
413417
with self.trap(ctx, note=note):
414418
raise e
415419
return
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
Invalid boolean value 'invalid_value'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES
1+
tests/data/ops/http_cntl_invalid_bool.fail.input.txt:2:4: error: Invalid boolean value 'invalid_value'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES
2+
2 | http.cntl.LOGGING = invalid_value;
3+
| ^
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
Invalid boolean value '"true"'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES and must not be quoted
1+
tests/data/ops/http_cntl_quoted_bool.fail.input.txt:2:4: error: Invalid boolean value '"true"'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES and must not be quoted
2+
2 | http.cntl.LOGGING = "true";
3+
| ^
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
Invalid boolean value '"true"'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES and must not be quoted
1+
tests/data/ops/skip_remap_quoted_bool.fail.input.txt:2:4: error: Invalid boolean value '"true"'. Must be one of: 0, 1, FALSE, NO, OFF, ON, TRUE, YES and must not be quoted
2+
2 | skip-remap("true");
3+
| ^
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Operations tests direction exceptions
2+
# Format: test_name: direction
3+
#
4+
# Explicit slot assignment syntax cannot be reversed
5+
explicit_slots.input: hrw4u
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(program (programItem (section (varSection VARS { (variables (variablesItem (variableDecl parent_config : bool @ 7 ;)) (variablesItem (variableDecl parent_child : bool @ 12 ;)) (variablesItem (variableDecl match : bool ;)) (variablesItem (variableDecl active_flag : bool @ 3 ;)) (variablesItem (variableDecl counter : int8 @ 2 ;)) (variablesItem (variableDecl priority : int8 ;)) (variablesItem (variableDecl status : int16 ;))) }))) (programItem (section SEND_RESPONSE { (sectionBody (conditional (ifStatement if (condition (expression (term (factor parent_config)))) (block { (blockItem (statement inbound.resp.X-Parent = (value true) ;)) })))) })) <EOF>)

0 commit comments

Comments
 (0)