Skip to content

Add Option to Configure Logical Operator Precedence in Rules #844

Open
@0tkl

Description

@0tkl

Most mainstream programming languages define logical operator precedence such that AND binds more tightly than OR (i.e., AND has higher precedence). An expression A AND B OR C is typically evaluated as (A AND B) OR C. However, control rules in the input file of EPANET currently uses the opposite convention: OR has higher precedence than AND. In EPANET rules, an expression like IF condition1 AND condition2 OR condition3 THEN ... is evaluated as condition1 AND (condition2 OR condition3).

This difference from standard programming practice can be a source of confusion and potential errors, particularly for new users who come from a programming background and expect the standard AND > OR precedence. While the EPANET manual documents the current behaviour, it's easy to overlook or forget, leading to rules that don't function as intended.

We propose adding a new option to the [OPTIONS] section of the EPANET input file:

[OPTIONS]
; To use standard AND > OR precedence:
RULE PRECEDENCE         STANDARD

; To explicitly use the default EPANET OR > AND precedence (or if line is omitted):
; RULE PRECEDENCE       LEGACY

The default behaviour should be LEGACY (until we have EPANET 3.0? ). Feedback on the option names is appreciated.


On the implementation side, refactoring the evalpremises() function is straightforward:

int evalpremises(Project *pr, int i)
{
    Network *net = &pr->network;
    int result;
    Spremise *p = net->Rule[i].Premises;

+    if (legacy precedence) // TODO
+    {
        result = TRUE;
        while (p != NULL)
        {
            if (p->logop == r_OR)
            {
                if (result == FALSE) result = checkpremise(pr, p);
            }
            else
            {
                if (result == FALSE) return (FALSE);
                result = checkpremise(pr, p);
            }
            p = p->next;
        }
+    }
+    else
+    {
+        result = FALSE;
+        while (p != NULL)
+        {
+            if (p->logop == r_AND)
+            {
+                if (result == TRUE) result = checkpremise(pr, p);
+            }
+            else
+            {
+                if (result == TRUE) return (TRUE);
+                result = checkpremise(pr, p);
+            }
+        }
+    }

    return result;
}

To me, the main challenge lies in deciding which struct should hold the precedence policy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions