Next: , Previous: one_or_more_map, Up: Top   [Contents]


58 ops

%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 1995-2008, 2010, 2012 The University of Melbourne.
% Copyright (C) 2014-2019, 2021-2022, 2024 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% File: ops.m.
% Main author: fjh.
% Stability: low.
%
% This module exports a typeclass `ops.op_table' which is used to define
% operator precedence tables for use by
% mercury_term_parser.read_term_with_op_table and
% term_io.write_term_with_op_table.
%
% It also exports an instance `ops.mercury_op_table' that implements the
% Mercury operator table defined in the Mercury Language Reference Manual.
%
% See samples/calculator2.m for an example program.
%
%--------------------------------------------------%
%--------------------------------------------------%

:- module ops.
:- interface.

%--------------------------------------------------%

    % An operator table maps strings (the operators themselves) to a value
    % of this type.
    %
    % If the string is an infix operator (term Op term), the info
    % about it is stored in the first field.
    %
    % If the string is a binary prefix operator (Op term term), the info
    % about it is stored in the second field.
    %
    % If the string is a prefix operator (Op term), the info
    % about it is stored in the third field.
    %
    % If the string is a postfix operator (term Op), the info
    % about it is stored in the fourth field.
    %
    % At least one of the fields should contain operator information.
:- type op_infos
    --->    op_infos(
                oi_infix            :: maybe_op_info_infix,
                oi_binary_prefix    :: maybe_op_info_binary_prefix,
                oi_prefix           :: maybe_op_info_prefix,
                oi_postfix          :: maybe_op_info_postfix
            ).

:- type maybe_op_info_infix
    --->    no_in
    ;       in(priority, arg_prio_gt_or_ge, arg_prio_gt_or_ge).

:- type maybe_op_info_binary_prefix
    --->    no_bin_pre
    ;       bin_pre(priority, arg_prio_gt_or_ge, arg_prio_gt_or_ge).

:- type maybe_op_info_prefix
    --->    no_pre
    ;       pre(priority, arg_prio_gt_or_ge).

:- type maybe_op_info_postfix
    --->    no_post
    ;       post(priority, arg_prio_gt_or_ge).

    % When a term appears as an argument of an operator, values of this type
    % specify the relationship that must hold between the priority of the
    % argument (which is the priority of its principal functor as an operator,
    % if it is an operator) and the priority of the operator.
:- type arg_prio_gt_or_ge
    --->    arg_gt
            % This represents an argument whose priority must bind
            % strictly tighter than the priority of the operator.
            % This means that the argument's priority must be strictly
            % greater than the operator's priority.
    ;       arg_ge.
            % This represents an argument whose priority must bind
            % at least as tightly as the priority of the operator.
            % This means that the argument's priority must be either
            % greater than, or equal to, the operator's priority.

    % 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 universal priority 0 describes contexts that accept terms
    % whose principal functor may be any operator.
    %
:- type priority
    --->    prio(uint).

    % min_priority_for_arg(OpPriority, GtOrGe) = MinArgPriority:
    %
    % Given the priority of an operator (OpPriority) and the required
    % relationship between this priority and the priority of a term
    % in given argument position (GtOrGe), return the minimum priority
    % of the term in that argument position (as MinArgPriority).
    %
:- func min_priority_for_arg(priority, arg_prio_gt_or_ge) = priority.

    % Return the priority that is one step looser than the given priority.
    %
:- func decrement_priority(priority) = priority.

    % Return the priority that is one step tighter than the given priority.
    %
:- func increment_priority(priority) = priority.

    % Tests whether the left priority is respectively
    %
    % - less than
    % - less than or equal to
    % - greater than
    % - greater than or equal to
    %
    % the right priority.
    %
:- pred priority_lt(priority::in, priority::in) is semidet.
:- pred priority_le(priority::in, priority::in) is semidet.
:- pred priority_gt(priority::in, priority::in) is semidet.
:- pred priority_ge(priority::in, priority::in) is semidet.

%--------------------------------------------------%

:- typeclass op_table(Table) where [

        % Check whether a string is the name of an infix operator,
        % and if it is, return its precedence and associativity.
        %
    pred lookup_infix_op(Table::in, string::in, priority::out,
        arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet,

        % Check whether a string is the name of a prefix operator,
        % and if it is, return its precedence and associativity.
        %
    pred lookup_prefix_op(Table::in, string::in,
        priority::out, arg_prio_gt_or_ge::out) is semidet,

        % Check whether a string is the name of a binary prefix operator,
        % and if it is, return its precedence and associativity.
        %
    pred lookup_binary_prefix_op(Table::in, string::in,
        priority::out, arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out)
        is semidet,

        % Check whether a string is the name of a postfix operator,
        % and if it is, return its precedence and associativity.
        %
    pred lookup_postfix_op(Table::in, string::in, priority::out,
        arg_prio_gt_or_ge::out) is semidet,

        % Is the given string the name of an operator?
        %
    pred is_op(Table::in, string::in) is semidet,

        % Check whether a string is the name of an operator, and if it is,
        % return the op_infos describing that operator, in all its guises,
        % in the third argument.
        %
    pred lookup_op_infos(Table::in, string::in, op_infos::out) is semidet,

        % Operator terms are terms of the form `X `Op` Y', where `Op' is
        % a variable or a name and X and Y are terms. If operator terms
        % are included in Table, return their precedence and associativity.
        %
    pred lookup_operator_term(Table::in, priority::out,
        arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet,

        % Returns a priority that accepts even terms whose top functor
        % has the loosest op priority as arguments.
        %
    func universal_priority(Table) = priority,

        % Returns the loosest priority that an operator can have.
        %
    func loosest_op_priority(Table) = priority,

        % Returns the tightest priority that an operator can have.
        %
    func tightest_op_priority(Table) = priority,

        % Returns the priority of comma (',/2') as an operator,
        % if that operator exists in the table. If it does not,
        % it should return a priority one step looser than arg_priority.
        %
    func comma_priority(Table) = priority,

        % The minimum priority of an operator appearing as the top-level
        % functor of an argument of a compound term.
        %
        % This will generally be one step tighter than comma_priority.
        % If comma is not in the op table, then ops.universal_priority
        % may be a reasonable value.
        %
    func arg_priority(Table) = priority
].

%--------------------------------------------------%

    % The table of Mercury operators.
    % See the "Builtin Operators" section of the "Syntax" chapter
    % of the Mercury Language Reference Manual for details.
    %
:- type mercury_op_table.
:- instance ops.op_table(ops.mercury_op_table).

:- func init_mercury_op_table = (ops.mercury_op_table::uo) is det.

    % The implementations of the op_table type class for mercury_op_tables.
    % Each predicate or function here implements the method whose name
    % is the name of the predicate or function without the
    % "mercury_op_table" prefix, and (in some cases) with the "search"
    % replaced by "lookup". (Actually, all the methods that can fail
    % *should* have the "lookup" part of their name replaced by "search").
    % The Table argument is not needed by any of the predicates and functions,
    % since it is implicitly init_mercury_op_table.
    %
:- pred mercury_op_table_search_infix_op(string::in,
    priority::out, arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet.
:- pred mercury_op_table_search_prefix_op(string::in,
    priority::out, arg_prio_gt_or_ge::out) is semidet.
:- pred mercury_op_table_search_binary_prefix_op(string::in,
    priority::out, arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet.
:- pred mercury_op_table_search_postfix_op(string::in,
    priority::out, arg_prio_gt_or_ge::out) is semidet.
:- pred mercury_op_table_is_op(string::in) is semidet.
:- pred mercury_op_table_search_op_infos(string::in, op_infos::out) is semidet.
:- pred mercury_op_table_lookup_operator_term(priority::out,
    arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is det.
:- func mercury_op_table_universal_priority = priority.
:- func mercury_op_table_loosest_op_priority = priority.
:- func mercury_op_table_tightest_op_priority = priority.
:- func mercury_op_table_comma_priority = priority.
:- func mercury_op_table_arg_priority = priority.

    % These predicates do the same job as the corresponding
    % mercury_op_table_search_* predicates, but instead of looking up
    % the operator name in the Mercury op_table, they get it from
    % their callers, who presumably got them by calling
    % mercury_op_table_search_op_infos.
    %
    % This allows the cost of the table lookup to be paid just once
    % even if you are looking for more than one kind of op.
    %
:- pred op_infos_infix_op(op_infos::in,
    priority::out, arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet.
:- pred op_infos_prefix_op(op_infos::in,
    priority::out, arg_prio_gt_or_ge::out) is semidet.
:- pred op_infos_binary_prefix_op(op_infos::in,
    priority::out, arg_prio_gt_or_ge::out, arg_prio_gt_or_ge::out) is semidet.
:- pred op_infos_postfix_op(op_infos::in,
    priority::out, arg_prio_gt_or_ge::out) is semidet.

%--------------------------------------------------%
%--------------------------------------------------%


Next: , Previous: one_or_more_map, Up: Top   [Contents]