I often see syntax like
expression = term | expression, "+" , term; term = factor | term, "*" , factor; factor = constant | variable | "(" , expression , ")"; variable = "x" | "y" | "z";
You see, all the rules produce an arithmetic value. Each can replace the other. Why not just declare
aexpr = var | num | (expr + expr) | expr * expr;
Certainly I need different production for expression of different type
statements = "if (" bexpr ") body | "while (" bexpr ")" body | a_variable "=" aexpr bexpr = (bexpr "|" bexpr) | bexpr "&" bexpr | aexpr ">" aexpr
Here, I introduce a new production rule (a line per rule) only when I get a new type of expression and see no reason to split them.
I just started to generate random sentences and see that expressions are naturally grouped according their result type (e.g. arithmetic and boolean are different) and that is all. If you need an arithmetic expression, you draw (rewrite) it from arigthmetic pool, if boolean form the boolean pool. Why to split the pools into further groups?