Skip to content

Commit b3cf9b0

Browse files
committed
Update error messages and support empty body in rule
1 parent c846b1a commit b3cf9b0

2 files changed

Lines changed: 24 additions & 30 deletions

File tree

pyreason/scripts/utils/rule_parser.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,33 @@ def parse_rule(rule_text: str, name: str, custom_thresholds: Union[None, list, d
3737
# Separate into head and body
3838
head, body = rule_str.split('<-')
3939

40-
# V4: Head and body cannot be empty after split
40+
# V4: Head cannot be empty after split
4141
if not head:
4242
raise ValueError("Rule head cannot be empty")
43-
if not body:
44-
raise ValueError("Rule body cannot be empty")
45-
46-
# Extract delta_t of rule if it exists else set it to 0
47-
delta_t = ''
48-
is_digit = True
49-
while is_digit:
50-
if body[0].isdigit():
51-
delta_t += body[0]
52-
body = body[1:]
53-
else:
54-
is_digit = False
5543

56-
if delta_t == '':
44+
# Handle empty body (valid rule - always fires unconditionally)
45+
if not body:
5746
delta_t = 0
47+
body_clauses = []
48+
body_bounds = []
5849
else:
59-
delta_t = int(delta_t)
50+
# Extract delta_t of rule if it exists else set it to 0
51+
delta_t = ''
52+
is_digit = True
53+
while is_digit:
54+
if body[0].isdigit():
55+
delta_t += body[0]
56+
body = body[1:]
57+
else:
58+
is_digit = False
59+
60+
if delta_t == '':
61+
delta_t = 0
62+
else:
63+
delta_t = int(delta_t)
6064

61-
# Split the body into clauses and their bounds
62-
body_clauses, body_bounds = _split_body_into_clauses(body)
65+
# Split the body into clauses and their bounds
66+
body_clauses, body_bounds = _split_body_into_clauses(body)
6367

6468
# Handle forall quantifier in body clauses
6569
for i, clause_str in enumerate(body_clauses.copy()):
@@ -94,7 +98,7 @@ def parse_rule(rule_text: str, name: str, custom_thresholds: Union[None, list, d
9498

9599
# Validate head has at least one variable
96100
if len(head_variables) < 1:
97-
raise ValueError("Rule head must contain at least one variable inside parentheses")
101+
raise ValueError("Rule head must contain at least one argument inside parentheses")
98102

99103
# Validate head variable names
100104
for var in head_variables:
@@ -111,7 +115,7 @@ def parse_rule(rule_text: str, name: str, custom_thresholds: Union[None, list, d
111115
# V8: Body clause must contain parentheses
112116
start_idx = clause_str.find('(')
113117
if start_idx == -1:
114-
raise ValueError(f"Body clause '{clause_str}' must contain parentheses around variables")
118+
raise ValueError(f"Body clause '{clause_str}' must contain parentheses around argument")
115119

116120
end_idx = clause_str.find(')')
117121
if end_idx == -1:

tests/unit/dont_disable_jit/test_rule_parser.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -397,16 +397,6 @@ def test_empty_head(self):
397397
with pytest.raises(ValueError, match="head"):
398398
parse_rule("<- person(X)", "r", None)
399399

400-
def test_empty_body(self):
401-
"""Empty body raises ValueError."""
402-
with pytest.raises(ValueError, match="body"):
403-
parse_rule("friend(X) <-", "r", None)
404-
405-
def test_empty_body_spaces(self):
406-
"""Body with only spaces raises ValueError."""
407-
with pytest.raises(ValueError, match="body"):
408-
parse_rule("friend(X) <- ", "r", None)
409-
410400
def test_head_missing_parens(self):
411401
"""Head without parentheses raises ValueError."""
412402
with pytest.raises(ValueError, match="parentheses"):
@@ -697,7 +687,7 @@ def test_body_variable_invalid_chars(self):
697687

698688
def test_empty_head_parentheses(self):
699689
"""Empty head parentheses raises ValueError."""
700-
with pytest.raises(ValueError, match="at least one variable"):
690+
with pytest.raises(ValueError, match="at least one argument"):
701691
parse_rule("p() <- b(X)", "r", None)
702692

703693
def test_head_missing_closing_paren(self):

0 commit comments

Comments
 (0)