Files
mercury/compiler/parse_vars.m
Zoltan Somogyi 748f597527 Factor out common new code.
compiler/parse_vars.m:
    Move parse_and_check_quant_vars here from parse_item.m, since
    parse_type_defn.m can also use it (after a slight generalization).

    Change its argument order to put related arguments next to each other.

    Replace the "existentially quantified/universally quantified" wording
    with just "quantified", since the prohibition on repeated variables
    exists regardless of the existential/universal distinction. The context
    will still say whether the error is inside a "some" scope or
    an "all" scope.

    Include a final period in a color region, as we do in other diagnostics.

compiler/parse_item.m:
    Delete the moved code.

compiler/parse_type_defn.m:
    Call parse_and_check_quant_vars instead of reimplementing it.

tests/invalid_nodepend/exist_cons_repeated_tvar.err_exp:
tests/invalid_nodepend/quant_pred_repeated_tvar.err_exp:
    Update the expected diagnostics.
2024-07-23 15:26:08 +02:00

391 lines
15 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2016, 2019, 2022, 2024 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% This module defines predicates that parse lists of variables.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- module parse_tree.parse_vars.
:- interface.
:- import_module parse_tree.error_spec.
:- import_module parse_tree.maybe_error.
:- import_module cord.
:- import_module list.
:- import_module term.
:- import_module varset.
%---------------------------------------------------------------------------%
:- type quantifier_type
---> quant_type_exist
; quant_type_univ.
:- type var_or_type_var
---> ordinary_var
; type_var.
:- pred parse_and_check_quant_vars(quantifier_type::in, var_or_type_var::in,
cord(format_piece)::in, varset::in, term::in, maybe1(list(var))::out)
is det.
%---------------------------------------------------------------------------%
% parse_possibly_repeated_vars(Term, VarSet, ContextPieces, MaybeVars):
%
% Parse Term as a list of quantified variables Vars. If successful,
% return ok1(Vars). If not, return an error message that has
% ContextPieces as a prefix.
%
:- pred parse_possibly_repeated_vars(term(T)::in, varset(T)::in,
cord(format_piece)::in, maybe1(list(var(T)))::out) is det.
:- type plain_state_vars(T)
---> plain_state_vars(
list(var(T)), % plain variables
list(var(T)) % !V state variables
).
% parse_vars_state_vars(Term, VarSet, ContextPieces, MaybeVars):
%
% The same as parse_vars, but parse not just ordinary variables V,
% but also state variables !SV.
%
:- pred parse_vars_state_vars(term(T)::in, varset(T)::in,
cord(format_piece)::in, maybe1(plain_state_vars(T))::out) is det.
:- type plain_state_dot_colon_vars(T)
---> plain_state_dot_colon_vars(
list(var(T)), % plain variables
list(var(T)), % !V state variables
list(var(T)), % !.V state variables
list(var(T)) % !:V state variables
).
% parse_vars_state_dot_colon_vars(Term, VarSet, ContextPieces, MaybeVars):
%
% The same as parse_vars, but parse not just ordinary variables V,
% but also state variables !SV, !.SV and !:SV.
%
:- pred parse_vars_state_dot_colon_vars(term(T)::in, varset(T)::in,
cord(format_piece)::in, maybe1(plain_state_dot_colon_vars(T))::out)
is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.parse_tree_out_term.
:- import_module bag.
%---------------------------------------------------------------------------%
parse_and_check_quant_vars(QuantType, VarOrTypeVar, InitContextPieces,
VarSet, VarsTerm, MaybeVars) :-
% Both versions of VarContextPieces should be statically allocated terms.
(
QuantType = quant_type_exist,
VarsContextPieces = [lower_case_next_if_not_first,
words("In first argument of"), quote("some"), suffix(":"), nl]
;
QuantType = quant_type_univ,
VarsContextPieces = [lower_case_next_if_not_first,
words("In first argument of"), quote("all"), suffix(":"), nl]
),
ContextPieces = InitContextPieces ++ cord.from_list(VarsContextPieces),
parse_possibly_repeated_vars(VarsTerm, VarSet, ContextPieces, MaybeVars0),
(
MaybeVars0 = ok1(QuantVars),
QuantVarsBag = bag.from_list(QuantVars),
DuplicateQuantVars = bag.to_list_only_duplicates(QuantVarsBag),
(
DuplicateQuantVars = [],
MaybeVars = MaybeVars0
;
DuplicateQuantVars = [_ | _],
list.map(varset.lookup_name(VarSet), DuplicateQuantVars,
DuplicateQuantVarNames),
(
VarOrTypeVar = ordinary_var,
Vars = "variables"
;
VarOrTypeVar = type_var,
Vars = "type variables"
),
Pieces = VarsContextPieces ++ [lower_case_next_if_not_first,
words("Error: a list of"), words(Vars),
words("being quantified may include each variable just once,"),
words("but here,")] ++
fixed_list_to_color_pieces(color_subject, "and", [],
DuplicateQuantVarNames) ++
[words(choose_number(DuplicateQuantVarNames, "is", "are"))] ++
color_as_incorrect([words("repeated.")]) ++ [nl],
Spec = spec($pred, severity_error, phase_t2pt,
get_term_context(VarsTerm), Pieces),
MaybeVars = error1([Spec])
)
;
MaybeVars0 = error1(_),
MaybeVars = MaybeVars0
).
%---------------------------------------------------------------------------%
parse_possibly_repeated_vars(Term, VarSet, ContextPieces, MaybeVars) :-
( if Term = term.functor(term.atom("[]"), [], _) then
MaybeVars = ok1([])
else if Term = term.functor(term.atom("[|]"), [HeadTerm, TailTerm], _) then
(
HeadTerm = term.variable(HeadVar0, _),
MaybeHeadVar = ok1(HeadVar0)
;
HeadTerm = term.functor(_, _, _),
generate_unexpected_term_message(ContextPieces, VarSet,
"variable", HeadTerm, Spec),
MaybeHeadVar = error1([Spec])
),
parse_possibly_repeated_vars(TailTerm, VarSet, ContextPieces,
MaybeTailVars),
( if
MaybeHeadVar = ok1(HeadVar),
MaybeTailVars = ok1(TailVars)
then
MaybeVars = ok1([HeadVar | TailVars])
else
Specs = get_any_errors1(MaybeHeadVar) ++
get_any_errors1(MaybeTailVars),
MaybeVars = error1(Specs)
)
else
generate_unexpected_term_message(ContextPieces, VarSet,
"list of variables", Term, Spec),
MaybeVars = error1([Spec])
).
%---------------------------------------------------------------------------%
:- type ordinary_state_var(T)
---> os_ordinary_var(var(T))
; os_state_var(var(T)).
parse_vars_state_vars(Term, VarSet, ContextPieces, MaybeVars) :-
( if Term = functor(atom("[]"), [], _) then
MaybeVars = ok1(plain_state_vars([], []))
else if Term = functor(atom("[|]"), [HeadTerm, TailTerm], _) then
( if
(
HeadTerm = variable(V0, _),
VarKind0 = os_ordinary_var(V0)
;
HeadTerm = functor(atom("!"), [variable(SV0, _)], _),
VarKind0 = os_state_var(SV0)
)
then
MaybeHeadVar = ok1(VarKind0)
else
generate_unexpected_term_message(ContextPieces, VarSet,
"variable or state variable", HeadTerm, HeadSpec),
MaybeHeadVar = error1([HeadSpec])
),
parse_vars_state_vars(TailTerm, VarSet, ContextPieces, MaybeTailVars),
( if
MaybeHeadVar = ok1(VarKind),
MaybeTailVars = ok1(plain_state_vars(TailVars, TailStateVars))
then
(
VarKind = os_ordinary_var(V),
( if list.member(V, TailVars) then
generate_repeated_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
Vars = [V | TailVars],
MaybeVars = ok1(plain_state_vars(Vars, TailStateVars))
)
;
VarKind = os_state_var(SV),
( if list.member(SV, TailStateVars) then
generate_repeated_state_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
StateVars = [SV | TailStateVars],
MaybeVars = ok1(plain_state_vars(TailVars, StateVars))
)
)
else
Specs = get_any_errors1(MaybeHeadVar) ++
get_any_errors1(MaybeTailVars),
MaybeVars = error1(Specs)
)
else
generate_unexpected_term_message(ContextPieces, VarSet,
"list of variables and/or state variables", Term, Spec),
MaybeVars = error1([Spec])
).
%---------------------------------------------------------------------------%
:- type ordinary_state_dot_colon_var(T)
---> osdc_ordinary_var(var(T))
; osdc_state_var(var(T))
; osdc_dot_var(var(T))
; osdc_colon_var(var(T)).
parse_vars_state_dot_colon_vars(Term, VarSet, ContextPieces, MaybeVars) :-
( if Term = functor(atom("[]"), [], _) then
MaybeVars = ok1(plain_state_dot_colon_vars([], [], [], []))
else if Term = functor(atom("[|]"), [HeadTerm, Tail], _) then
( if
(
HeadTerm = variable(V0, _),
VarKind0 = osdc_ordinary_var(V0)
;
HeadTerm = functor(atom("!"), [variable(SV0, _)], _),
VarKind0 = osdc_state_var(SV0)
;
HeadTerm = functor(atom("!."), [variable(SV0, _)], _),
VarKind0 = osdc_dot_var(SV0)
;
HeadTerm = functor(atom("!:"), [variable(SV0, _)], _),
VarKind0 = osdc_colon_var(SV0)
)
then
MaybeHeadVar = ok1(VarKind0)
else
generate_unexpected_term_message(ContextPieces, VarSet,
"variable or state variable", HeadTerm, HeadSpec),
MaybeHeadVar = error1([HeadSpec])
),
parse_vars_state_dot_colon_vars(Tail, VarSet, ContextPieces,
MaybeTailVars),
( if
MaybeHeadVar = ok1(VarKind),
MaybeTailVars = ok1(plain_state_dot_colon_vars(TailVars,
TailStateVars, TailDotVars, TailColonVars))
then
(
VarKind = osdc_ordinary_var(V),
( if list.member(V, TailVars) then
generate_repeated_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
Vars = [V | TailVars],
MaybeVars = ok1(plain_state_dot_colon_vars(Vars,
TailStateVars, TailDotVars, TailColonVars))
)
;
VarKind = osdc_state_var(SV),
( if
( list.member(SV, TailStateVars )
; list.member(SV, TailDotVars )
; list.member(SV, TailColonVars )
)
then
generate_repeated_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
StateVars = [SV | TailStateVars],
MaybeVars = ok1(plain_state_dot_colon_vars(TailVars,
StateVars, TailDotVars, TailColonVars))
)
;
VarKind = osdc_dot_var(SV),
( if
( list.member(SV, TailStateVars )
; list.member(SV, TailDotVars )
; list.member(SV, TailColonVars )
)
then
generate_repeated_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
DotVars = [SV | TailDotVars],
MaybeVars = ok1(plain_state_dot_colon_vars(TailVars,
TailStateVars, DotVars, TailColonVars))
)
;
VarKind = osdc_colon_var(SV),
( if
( list.member(SV, TailStateVars )
; list.member(SV, TailDotVars )
; list.member(SV, TailColonVars )
)
then
generate_repeated_var_msg(ContextPieces, VarSet,
HeadTerm, Spec),
MaybeVars = error1([Spec])
else
ColonVars = [SV | TailColonVars],
MaybeVars = ok1(plain_state_dot_colon_vars(TailVars,
TailStateVars, TailDotVars, ColonVars))
)
)
else
HeadSpecs = get_any_errors1(MaybeHeadVar),
TailSpecs = get_any_errors1(MaybeTailVars),
MaybeVars = error1(HeadSpecs ++ TailSpecs)
)
else
generate_unexpected_term_message(ContextPieces, VarSet,
"list of variables and/or state variables", Term, Spec),
MaybeVars = error1([Spec])
).
%---------------------------------------------------------------------------%
:- pred generate_repeated_var_msg(cord(format_piece)::in,
varset(T)::in, term(T)::in, error_spec::out) is det.
generate_repeated_var_msg(ContextPieces, VarSet, Term, Spec) :-
TermStr = describe_error_term(VarSet, Term),
Pieces = cord.list(ContextPieces) ++ [lower_case_next_if_not_first] ++
color_as_incorrect([words("Repeated variable")]) ++
color_as_subject([words(TermStr), suffix(".")]) ++
[nl],
Spec = spec($pred, severity_error, phase_t2pt,
get_term_context(Term), Pieces).
:- pred generate_repeated_state_var_msg(cord(format_piece)::in,
varset(T)::in, term(T)::in, error_spec::out) is det.
generate_repeated_state_var_msg(ContextPieces, VarSet, Term, Spec) :-
TermStr = describe_error_term(VarSet, Term),
Pieces = cord.list(ContextPieces) ++ [lower_case_next_if_not_first] ++
color_as_incorrect([words("Repeated state variable")]) ++
color_as_subject([words(TermStr), suffix(".")]) ++
[nl],
Spec = spec($pred, severity_error, phase_t2pt,
get_term_context(Term), Pieces).
:- pred generate_unexpected_term_message(cord(format_piece)::in,
varset(T)::in, string::in, term(T)::in, error_spec::out) is det.
generate_unexpected_term_message(ContextPieces, VarSet, Expected, Term,
Spec) :-
TermStr = describe_error_term(VarSet, Term),
Pieces = cord.list(ContextPieces) ++ [lower_case_next_if_not_first,
words("Expected a")] ++
color_as_correct([words(Expected), suffix(",")]) ++
[words("got")] ++
color_as_incorrect([quote(TermStr), suffix(".")]) ++
[nl],
Spec = spec($pred, severity_error, phase_t2pt,
get_term_context(Term), Pieces).
%---------------------------------------------------------------------------%
:- end_module parse_tree.parse_vars.
%---------------------------------------------------------------------------%