The first thing to notice about expressions is that the notion of precedence does not appear in the abstract syntax. The tree form of the structure is never ambiguous. It is the role of the parser to take into account the precedence rules and to create the correct abstract syntax tree. Parenthesis (and in particular redundant parenthesis) do not appear in an abstract syntax tree. It is the role of an un-parser to add necessary (and only those) parenthesis when producing a textual form.
A first idea to define expressions is to create one operator for each operation.
plus, minus : Exp # Exp -> Exp;
If the number of similar operators is important, one may prefer to share the notion of binary expression.
bexp : Exp # Bop # Exp -> Exp; plus, minus : -> Bop;
Notice here two ``constant'' operators (they was called singletons in Metal): plus and minus.
The two methods may also be mixed. For example, access to an array is also a binary expression, but one prefers to distinguish this operator from arithmetic operations.
bexp : Exp # Bop # Exp -> Exp; plus, minus : -> Bop; index : Exp # Exp -> Exp;