[m-rev.] for review: reversing operator priorities
Julien Fischer
jfischer at opturion.com
Tue Nov 8 20:02:56 AEDT 2022
Hi Zoltan,
On Sun, 6 Nov 2022, Zoltan Somogyi wrote:
> This implements what we discussed recently.
>
> The diff contains several "XXX OPS" markers noting further
> opportunities for improvement; I would like your feedback on these.
> I would like to resolve the ones in ops.m before commit; but intend
> to leave the others for a later commit.
> We probably should keep the old form of the ops module around,
> possibly named old_ops.m or prolog_ops.m, for anyone who
> wants it. I will make the necessary changes, including to NEWS,
> once we get to a concensus on this.
I suggest adding old_ops to extras (similar to what we until recently
had with old_term_parser.)
...
> Make higher operator priorities bind tighter.
>
> Mercury inherited its original system of operator priorities from Prolog,
> because during its initial development, we wanted to be able execute
> the Mercury compiler using NU-Prolog and later SICStus Prolog.
> That consideration has long been obsolete, and now we may fix the
> design error that gifted Prolog with its counter-intuitive system
> of operator priorities, in which higher *numerical* operator priorities
> mean lower *actual* priorities. This diff does that.
>
> library/ops.m:
> Change the meaning of operator priorities, to make higher numerical
> priorities mean also higher actual priorities.
>
> This semantic change requires corresponding changes in any other module
> that uses ops.m. To force this change to occur, change the type
> representing priorities from being a synonym for a bare int to being
> a notag wrapper around an uint.
>
> Add a method named tightest_priority to replace the use of 0 as a priority.
>
> Rename the max_priority method as the loosest_priority method.
>
> Add a method named universal_priority to replace the use of
> max_priority + 1 as a priority.
>
> Add a function to return the priority of the comma operator,
> to allow other modules to stop hardcoding it.
>
> Add operations for comparing priorities and for incrementing/decrementing
> priorities.
> diff --git a/library/ops.m b/library/ops.m
> index 653362ce5..8cb920916 100644
> --- a/library/ops.m
> +++ b/library/ops.m
> @@ -32,6 +32,11 @@
>
> % An class describes what structure terms constructed with an operator
> % of that class are allowed to take.
> + %
> + % XXX OPS For binary ops, we should specify the arg priorities as one of
> + % left_assoc, right_assoc and non_assoc.
> + % XXX OPS For unary ops, we should specify the arg priority using
> + % the arg_priority type.
I'm not quite sure what you are proposing here.
> :- type class
> ---> infix(ops.assoc, ops.assoc) % term Op term
> ; prefix(ops.assoc) % Op term
> @@ -46,14 +51,29 @@
> ---> x
> ; y.
>
> - % Operators with a low "priority" bind more tightly than those
> - % with a high "priority". For example, given that `+' has
> - % priority 500 and `*' has priority 400, the term `2 * X + Y'
> - % would parse as `(2 * X) + Y'.
> +% XXX OPS not yet used
> +:- type arg_priority
> + ---> arg_prio_gt_op_prio
> + % `The argument's priority must be higher than the priority
> + % of the operator, i.e. the argument must bind tighter.
> + ; arg_prio_ge_op_prio.
> + % `The argument's priority must be higher than or equal to
> + % the priority of the operator, i.e. the argument must bind
> + % at least as tightly as the operator.
> +
> + % Operators with a higher priority bind more tightly than those
> + % with a low priority. For example, given that `+' has priority 1000
> + % and `*' has priority 1100, the string "2 + X * Y" would parse as
> + % `2 + (X * Y)'.
> + %
> + % The range of valid operator priorities is 1 to 1500, with 1 being
> + % the loosest and 1500 being the tightest.
> %
> - % The lowest priority is 0.
> + % The universal priority 0 describes contexts that accept terms
> + % whose principal functor may be any operator.
> %
> -:- type priority == int.
> +:- type priority
> + ---> prio(uint).
>
> :- type op_info
> ---> op_info(
> @@ -108,15 +128,31 @@
> pred lookup_operator_term(Table::in, ops.priority::out,
> ops.assoc::out, ops.assoc::out) is semidet,
>
> - % Returns the highest priority number (the lowest is zero).
> + % Returns a priority that accepts even terms whose top functor
> + % has the loosest priority as arguments.
> + % XXX OPS The terminology and documentation here could be improved.
> %
> - func max_priority(Table) = ops.priority,
> + func universal_priority(Table) = ops.priority,
>
> - % The maximum priority of an operator appearing as the top-level
> + % Returns the loosest priority.
> + % XXX OPS Should this be loosest_OP_priority?
For consistency with the other method names? Yes, although that does
then leave lookup_operator_term being the odd one out.
> + %
> + func loosest_priority(Table) = ops.priority,
> +
> + % Returns the tightest priority.
> + % XXX OPS Should this be tightest_OP_priority?
> + %
> + func tightest_priority(Table) = ops.priority,
> +
> + % XXX OPS Should there be a separate method for comma_priority?
> + % All users of this module need it, since they compute arg_priority
> + % from it.
Doesn't that assume that comma is in your operator table?
> +
> + % The minimum priority of an operator appearing as the top-level
> % functor of an argument of a compound term.
> %
> - % This will generally be the precedence of `,/2' less one.
> - % If `,/2' does not appear in the op_table, ops.max_priority plus one
> + % This will generally be the precedence of `,/2' plus one.
> + % If `,/2' does not appear in the op_table, ops.loosest_priority - 1
> % may be a reasonable value.
> %
> func arg_priority(Table) = ops.priority
> @@ -395,8 +477,51 @@ mercury_op_table_postfix_op(Info, MaybeOtherInfo, Priority, LeftAssoc) :-
>
> :- pragma inline(pred(adjust_priority_for_assoc/3)).
>
> -adjust_priority_for_assoc(Priority, y, Priority).
> -adjust_priority_for_assoc(Priority, x, Priority - 1).
> +adjust_priority_for_assoc(Priority, Assoc, AdjustedPriority) :-
> + (
> + Assoc = y,
> + AdjustedPriority = Priority
> + ;
> + Assoc = x,
> + AdjustedPriority = increment_priority(Priority)
> + ).
> +
> +% XXX OPS This predicate is unused.
Specifically, it is unused by the Mercury compiler; user code
may have a use for it.
> +decrement_priority(prio(P)) = prio(DecP) :-
> + trace [compile_time(flag("enforce_ops_priority_bounds"))] (
> + ( if prio(P) = mercury_op_table_loosest_priority then
> + unexpected($pred, "decrementing loosest priority")
> + else
> + true
> + )
> + ),
> + ( if P = 0u then
> + unexpected($pred, "decrementing 0")
> + else
> + DecP = P - 1u
> + ).
That fine otherwise; I only gave your conversion of the priorities in
the table a cursory glance -- I'm assuming a bootcheck would catch
any non-obvious errors.
Julien.
More information about the reviews
mailing list