mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-19 03:13:40 +00:00
compiler/typecheck_errors.m:
When there is a difference between actual and expected types
in predicate calls, do not just print the actual and expected types,
but if possible, point out the specific part(s) that cause the difference.
For example, report differences in arities, purity, determinism etc.
Also point out cases where the code is trying to specify the value
of an existentially quantified type variable.
When higher order types are printed as part of an error message,
strip any builtin qualifiers not just from the names of types,
but also from the names of the argument modes.
Start each part of an actual/expected pair at the same level of
indentation.
compiler/error_util.m:
Add a way to print out small integers as english names (one, two etc)
instead as numerals (1, 2 etc).
Add a way to print out purity descriptions.
Add a component, treat_next_as_first, that specifies that the next
format_component should be treated as the first part of a sentence
with respect to (de)capitalization by lower_next_if_not_first,
even if it is preceded by other format_components in a message.
This is useful in cases where the code that generates part of an
error message does not know whether that part will end up at the start
of the sentence, as the
<preceding sentence, ending in a period>
treat_next_as_first <context> lower_next_if_not_first, Words ...
sequence will keep Words capitalized if <context> is empty,
but will decapitalize it if <context> is not empty.
Rename {qual,unqual}_pf_sym_name_orig_arity as
{qual,unqual}_pf_sym_name_pred_form_arity, since this is clearer.
compiler/prog_out.m:
Apply the same s/orig_arity/pred_form_arity/ rename to some functions.
compiler/module_qual.qual_errors.m:
Fix an unrelated problem I came across while working on the changes above:
stray spaces at the ends of words(...) format_components, which screwed up
some error messages.
Simplify the description of classes and predicates by using facilities
in error_util.m that did not yet exist when this code was converted
to use error_util.m.
compiler/prog_type.m:
When stripping builtin qualifiers from types, strip them from the
modes stored in the higher_order inst into of higher order types as well,
for the same reason: to eliminate clutter in error messages.
Make the strip_kind_annotation function return a value that
shows in its type that the result will *not* be a kinded_type.
THIS IS THE FIRST USE OF SUBTYPES IN THE COMPILER.
compiler/type_assign.m:
Fix a long-standing documentation problem.
The type_assign type has long had a field named ta_external_type_params,
whose type was external_type_params, which was defined as equivalent
to a list of type variables. The name does not say what kinds of type
variables it contains, but the only two places that add type vars to
this field add type vars that are existentially quantified, either
by a predicate, or a cons_id. This diff therefore renames this field
to ta_existq_tvars, and changes its type to be just plain list(tvar).
(The external_type_params type has no documentation, so it may be used
to store different kinds of type variables in different places,
some of which may *not* be existentially quantified. Using the same name
for different purposes may be confusing.)
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/check_promise.m:
compiler/check_typeclass.m:
compiler/convert_parse_tree.m:
compiler/hhf.m:
compiler/hlds_desc.m:
compiler/hlds_out_util.m:
compiler/make_hlds_error.m:
compiler/make_hlds_warn.m:
compiler/mark_tail_calls.m:
compiler/module_qual.qualify_items.m:
compiler/post_typecheck.m:
compiler/pred_table.m:
compiler/typecheck.m:
compiler/typecheck_debug.m:
compiler/typeclasses.m:
Conform to the changes above.
tests/invalid/fbnf.{m,err_exp}:
The motivating test case for this diff, slightly modified
from the code posted to m-users.
tests/invalid/type_diff.{m,err_exp}:
A test case to test the parts of the new code in typecheck_errors.m
that are not covered by other tests.
tests/invalid/Mmakefile:
Enable the new test cases.
tests/invalid/ext_type_bug.{m,err_exp}:
Extend this test case to test the pinpointing of errors that try to bind
existentially quantified type variables, at both the top level
and nested inside other type constructors.
tests/invalid/bug197.err_exp:
tests/invalid/higher_order_mode_mismatch.err_exp:
tests/invalid_purity/impure_pred_t1_fixed.err_exp:
tests/invalid_purity/impure_pred_t2.err_exp:
tests/invalid_purity/purity_nonsense.err_exp:
tests/invalid_purity/purity_nonsense2.err_exp:
Expect pinpointed type differences in these error messages.
tests/invalid/abstract_eqv.err_exp:
tests/invalid/integral_constant_no_suffix.err_exp:
tests/invalid/method_impl.err_exp:
tests/invalid/mixed_up_streams.err_exp:
tests/invalid/try_bad_params.err_exp:
tests/invalid/type_error_ambiguous.err_exp:
tests/invalid/types2.err_exp:
tests/invalid_nodepend/errors2.err_exp:
Expected updated formatting in these error messages.
264 lines
7.6 KiB
Mathematica
264 lines
7.6 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
||
%
|
||
% File: fbnf.m
|
||
% Main author: Sean Charles
|
||
% Date: Sat Oct 1 19:48:26 2022
|
||
%
|
||
% FORTH but not FORTH
|
||
%
|
||
%-----------------------------------------------------------------------------%
|
||
|
||
:- module fbnf.
|
||
|
||
:- interface.
|
||
:- import_module io.
|
||
|
||
:- pred main(io::di, io::uo) is det.
|
||
|
||
:- implementation.
|
||
|
||
:- import_module bool.
|
||
:- import_module list.
|
||
:- import_module map.
|
||
:- import_module maybe.
|
||
:- import_module stack.
|
||
:- import_module string.
|
||
|
||
%----------------------------------------------------------------------------%
|
||
|
||
main(!IO) :-
|
||
io.command_line_arguments(Args, !IO),
|
||
Arg = string.join_list(" ", Args),
|
||
io.format("ARG: %s\n", [s(Arg)], !IO),
|
||
|
||
( if string.is_empty(Arg) then
|
||
repl(initial_state, _, !IO)
|
||
else
|
||
interpret(string.words(Arg), initial_state, State1, !IO),
|
||
repl(State1, _, !IO)
|
||
).
|
||
|
||
%----------------------------------------------------------------------------%
|
||
|
||
:- type lstr == list(string).
|
||
|
||
% This is the WORD handler definition
|
||
%
|
||
% :- type word_handler == (pred(fstate, fstate, io, io, lstr, lstr)).
|
||
% :- inst word_handler == (pred(in, out, di, uo, in, out)is det).
|
||
|
||
:- type word_handler
|
||
---> word_handler(
|
||
pred(
|
||
fstate::in, fstate::out,
|
||
io::di, io::uo,
|
||
lstr::in, lstr::out
|
||
) is det
|
||
).
|
||
|
||
:- type word_map == map(string, word_handler).
|
||
:- type stack_entry == stack(int).
|
||
|
||
% FBNF operating state
|
||
%
|
||
:- type fstate
|
||
---> fstate(
|
||
fs_compiling :: bool,
|
||
fs_terminate :: bool,
|
||
fs_dict :: word_map,
|
||
fs_stack :: stack_entry,
|
||
fs_error :: maybe(string)
|
||
).
|
||
|
||
% Create the initial state of the machine.
|
||
%
|
||
:- func initial_state = (fstate::out) is det.
|
||
|
||
initial_state =
|
||
fstate(
|
||
no, % interpret mode
|
||
no, % terminate requested ?
|
||
initial_dictionary, % core words
|
||
stack.init : stack_entry, % initial empty stack
|
||
no % no error message
|
||
).
|
||
|
||
% Create default system WORDS dictionary.
|
||
% Here we pre-load all the stock words that the user can build on
|
||
% during the course of the session.
|
||
%
|
||
:- func initial_dictionary = (word_map::out) is det.
|
||
|
||
initial_dictionary = Map :-
|
||
some [!Words] (
|
||
!:Words = map.init,
|
||
|
||
map.set(":", word_handler(define_word), !Words),
|
||
map.set("bye", word_handler(session_end), !Words),
|
||
map.set("words", word_handler(list_words), !Words),
|
||
|
||
Map = !.Words
|
||
).
|
||
|
||
%----------------------------------------------------------------------------%
|
||
%----------------------------------------------------------------------------%
|
||
%
|
||
% System WORD Implementations
|
||
%
|
||
%----------------------------------------------------------------------------%
|
||
%----------------------------------------------------------------------------%
|
||
|
||
% ":" - define a new word.
|
||
%
|
||
:- pred define_word(fstate::in, fstate::out, io::di, io::uo,
|
||
lstr::in, lstr::out) is det.
|
||
|
||
define_word(!State, !IO) -->
|
||
{
|
||
io.format("DEFINE WORD\n", [], !IO)
|
||
}.
|
||
|
||
% "BYE" - terminate the current session.
|
||
%
|
||
:- pred session_end(fstate::in, fstate::out, io::di, io::uo,
|
||
lstr::in, lstr::out) is det.
|
||
|
||
session_end(!State, !IO) -->
|
||
{
|
||
io.format("BYE\n", [], !IO)
|
||
}.
|
||
|
||
% "WORDS" - list all defined words.
|
||
%
|
||
:- pred list_words(fstate::in, fstate::out, io::di, io::uo,
|
||
lstr::in, lstr::out) is det.
|
||
|
||
list_words(!State, !IO) -->
|
||
{
|
||
io.format("WORDS\n", [], !IO)
|
||
}.
|
||
|
||
%----------------------------------------------------------------------------%
|
||
|
||
% Read-Eval-Print-Loop
|
||
%
|
||
:- pred repl(fstate::in, fstate::out, io::di, io::uo) is det.
|
||
|
||
repl(!State, !IO) :-
|
||
io.format("fbnf> ", [], !IO),
|
||
io.flush_output(!IO),
|
||
io.read_line_as_string(Res, !IO),
|
||
(
|
||
Res = ok(Str),
|
||
interpret(string.words(Str), !State, !IO),
|
||
repl(!State, !IO)
|
||
;
|
||
Res = error(Err),
|
||
io.format("error: %s\n", [s(io.error_message(Err))], !IO),
|
||
repl(!State, !IO)
|
||
;
|
||
Res = eof,
|
||
io.format("Bye!\n", [], !IO)
|
||
).
|
||
|
||
%----------------------------------------------------------------------------%
|
||
|
||
% from "Starting FORTH" ...
|
||
%
|
||
% This will activate a word called INTERPRET, also known as the “text
|
||
% interpreter.” The text interpreter scans the input stream, looking for strings
|
||
% of characters separated by spaces. When a string is found, it is looked up in
|
||
% the dictionary.
|
||
%
|
||
% If the word is in the dictionary, it is pointed out to a word called EXECUTE.
|
||
% EXECUTE executes the definition (in this case an asterisk is printed).
|
||
% Finally, the interpreter says everything’s “ok.”
|
||
|
||
% 'INTERPRET'
|
||
% Process the current input stream of user tokens.
|
||
% If an error is detected then we abort the loop and abandon the input.
|
||
% Anything processed up to the error remains intact though this may or may
|
||
% not lead to a safe state (TBD).
|
||
%
|
||
:- pred interpret(list(string)::in, fstate::in, fstate::out,
|
||
io::di, io::uo) is det.
|
||
|
||
interpret(Words, !State, !IO) :-
|
||
compiling(no, !State),
|
||
interp1(Words, !State, !IO).
|
||
|
||
:- pred interp1(list(string)::in, fstate::in, fstate::out,
|
||
io::di, io::uo) is det.
|
||
|
||
interp1([], !State, !IO).
|
||
interp1([W | Ws], !State, !IO) :-
|
||
io.format("interp1: %s\n", [s(W)], !IO),
|
||
execute(W, Ws, Rest, !State, !IO),
|
||
flush_error(HadError, !State, !IO),
|
||
( if HadError = yes then
|
||
true % abandon ship.
|
||
else
|
||
interp1(Rest, !State, !IO)
|
||
).
|
||
|
||
% 'EXECUTE'
|
||
% Find the word in the dictionary so we can invoke it, if it's not
|
||
% found we want to record the error and terminate processing.
|
||
%
|
||
:- pred execute(string::in, list(string)::in, list(string)::out,
|
||
fstate::in, fstate::out, io::di, io::uo) is det.
|
||
|
||
execute(Word, Words, Rest, !State, !IO) :-
|
||
Dict = fs_dict(!.State),
|
||
( if map.search(Dict, Word, word_handler(Entry)) then
|
||
Entry(!State, !IO)
|
||
else
|
||
set_error(string.format("word not found: %s\n", [s(Word)]), !State)
|
||
),
|
||
Rest = Words.
|
||
|
||
%----------------------------------------------------------------------------%
|
||
%----------------------------------------------------------------------------%
|
||
%
|
||
% STATE change operations
|
||
%
|
||
%----------------------------------------------------------------------------%
|
||
%----------------------------------------------------------------------------%
|
||
|
||
% Set 'compiling' mode as indicated.
|
||
%
|
||
:- pred compiling(bool::in, fstate::in, fstate::out) is det.
|
||
|
||
compiling(Mode, !State) :-
|
||
!:State = !.State ^ fs_compiling := Mode.
|
||
|
||
:- func is_compiling(fstate::in) = (bool::out) is det.
|
||
|
||
is_compiling(S) = S ^ fs_compiling.
|
||
|
||
% Set an error message into the state.
|
||
%
|
||
:- pred set_error(string::in, fstate::in, fstate::out) is det.
|
||
|
||
set_error(Message, !State) :-
|
||
!:State = !.State ^ fs_error := yes(Message).
|
||
|
||
% Flush (by writing) the current error message.
|
||
% Flushed will be 'yes' if there was an error in the state record
|
||
% and this will cause the current processing to abort.
|
||
%
|
||
:- pred flush_error(bool::out, fstate::in, fstate::out, io::di, io::uo) is det.
|
||
|
||
flush_error(Flushed, !State, !IO) :-
|
||
( if yes(Error) = !.State ^ fs_error then
|
||
io.format("error: %s\n", [s(Error)], !IO),
|
||
Flushed = yes
|
||
else
|
||
Flushed = no
|
||
).
|
||
|
||
%----------------------------------------------------------------------------%
|
||
:- end_module fbnf.
|
||
%----------------------------------------------------------------------------%
|
||
|