diff --git a/compiler/format_call.m b/compiler/format_call.m index e7486d6b4..32fda57ca 100644 --- a/compiler/format_call.m +++ b/compiler/format_call.m @@ -9,11 +9,11 @@ % File: format_call.m. % Author: zs. % -% The job of this module is to generate warnings about calls to string.format -% and io.format in which the format string and the supplied lists of values -% do not agree. The difficult part of this job is actually finding the values -% of the variables representing the format string and the list of values to -% be printed. +% The job of this module is to generate warnings about calls to +% string.format, io.format and stream.format in which the format string and +% the supplied lists of values do not agree. The difficult part of this job +% is actually finding the values of the variables representing the format +% string and the list of values to be printed. % % The general approach is a backwards traversal of the procedure body. During % this traversal, we assign an id to every conjunction (considering a cond and @@ -194,6 +194,11 @@ is_format_call(ModuleName, Name, Args, FormatStringVar, FormattedValuesVar) :- ( Args = [FormatStringVar, FormattedValuesVar, _IOIn, _IOOut] ; Args = [_Stream, FormatStringVar, FormattedValuesVar, _IOIn, _IOOut] ) + ; ModuleName = mercury_std_lib_module_name("stream") -> + % Since we do this check after polymorphism there will have been + % a typeclassinfo inserted at the front of the argument list. + Args = [_TC_InfoForStream, _Stream, FormatStringVar, + FormattedValuesVar, _StateIn, _StateOut] ; fail ). diff --git a/compiler/simplify.m b/compiler/simplify.m index daed55fe4..486d0fa2d 100644 --- a/compiler/simplify.m +++ b/compiler/simplify.m @@ -2777,7 +2777,8 @@ case_list_contains_trace([Case0 | Cases0], [Case | Cases], !ContainsTrace) :- % typeclass_infos. format_calls :: bool, % Do we have any calls to - % string.format and io.format? + % string.format, stream.format and + % io.format? inside_dupl_for_switch :: bool, % Are we currently inside a goal % that was duplicated for a switch? diff --git a/library/Mercury.options b/library/Mercury.options index 9455a3c04..c9cdb38ec 100644 --- a/library/Mercury.options +++ b/library/Mercury.options @@ -21,8 +21,10 @@ MCFLAGS-std_util += --no-halt-at-warn MCFLAGS-dir += --no-halt-at-warn MCFLAGS-exception += --no-halt-at-warn -# We need a better way to ignore obsolete procedures. -MCFLAGS-bintree_set += --no-halt-at-warn +# Ignore warnings about obsolete procedures from this module since +# the entire module is obsolete. +# +MCFLAGS-bintree_set += --no-warn-obsolete # io.m uses library features that are supported by POSIX but which are not # part of ANSI C, such as `struct stat', fileno(), and putenv(). @@ -33,6 +35,7 @@ MGNUCFLAGS-io = --no-ansi # in terms of io.format/4, and string.format/2 in terms of string.format/3. # varset.trans_opt includes the relevant part of string.opt. MCFLAGS-io = --no-warn-unknown-format-calls +MCFLAGS-stream = --no-warn-unknown-format-calls MCFLAGS-string = --no-warn-unknown-format-calls MCFLAGS-mer_std = --no-warn-nothing-exported diff --git a/library/stream.m b/library/stream.m index 0ec85842d..f65ebbec4 100644 --- a/library/stream.m +++ b/library/stream.m @@ -20,6 +20,8 @@ :- interface. :- import_module bool. +:- import_module char. +:- import_module list. :- import_module string. %-----------------------------------------------------------------------------% @@ -29,6 +31,11 @@ :- type stream.name == string. +:- type stream.result(Error) + ---> ok + ; eof + ; error(Error). + :- type stream.result(T, Error) ---> ok(T) ; eof @@ -269,6 +276,22 @@ in(pred(in, out, in, out, di, uo) is cc_multi), in, out, di, uo) is cc_multi. +%-----------------------------------------------------------------------------% +% +% Misc. operations on streams +% + + % A version of io.format that works for arbitrary string writers. + % +:- pred stream.format(Stream::in, string::in, list(poly_type)::in, + State::di, State::uo) is det <= stream.writer(Stream, string, State). + + % Discard all the whitespace from the specified stream. + % +:- pred stream.ignore_whitespace(Stream::in, stream.result(Error)::out, + State::di, State::uo) + is det <= stream.putback(Stream, char, State, Error). + %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% @@ -347,6 +370,32 @@ stream.input_stream_fold2_state_maybe_stop(Stream, Pred, T0, Res, !S) :- Res = error(T0, Error) ). +%-----------------------------------------------------------------------------% + +stream.format(Stream, FormatString, Arguments, !State) :- + string.format(FormatString, Arguments, String), + put(Stream, String, !State). + +%-----------------------------------------------------------------------------% + +stream.ignore_whitespace(Stream, Result, !State) :- + get(Stream, CharResult, !State), + ( + CharResult = error(Error), + Result = error(Error) + ; + CharResult = eof, + Result = eof + ; + CharResult = ok(Char), + ( is_whitespace(Char) -> + stream.ignore_whitespace(Stream, Result, !State) + ; + unget(Stream, Char, !State), + Result = ok + ) + ). + %-----------------------------------------------------------------------------% :- end_module stream. %-----------------------------------------------------------------------------% diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile index b94a604ef..7200f6ab5 100644 --- a/tests/hard_coded/Mmakefile +++ b/tests/hard_coded/Mmakefile @@ -185,6 +185,8 @@ ORDINARY_PROGS= \ solver_ite_inits \ space \ stable_sort \ + stream_format \ + stream_ignore_ws \ stream_test \ string_alignment \ string_alignment_bug \ @@ -578,6 +580,9 @@ nonascii_gen: nonascii_gen.c stream_test.out: stream_test ./stream_test < stream_test.data > stream_test.out +stream_ignore_ws.out: stream_ignore_ws + ./stream_ignore_ws < stream_ignore_ws.data > stream_ignore_ws.out + # The trace_goal_env_1 and trace_goal_env_2 test cases differ from each other # only in that the latter is executed with the TRACE_ABC environment variable # set. diff --git a/tests/hard_coded/stream_format.exp b/tests/hard_coded/stream_format.exp new file mode 100644 index 000000000..080cb0b34 --- /dev/null +++ b/tests/hard_coded/stream_format.exp @@ -0,0 +1 @@ +foo561a3.141000 diff --git a/tests/hard_coded/stream_format.m b/tests/hard_coded/stream_format.m new file mode 100644 index 000000000..d31ea2e6b --- /dev/null +++ b/tests/hard_coded/stream_format.m @@ -0,0 +1,17 @@ +:- module stream_format. +:- interface. + +:- import_module io. + +:- pred main(io::di, io::uo) is det. + +:- implementation. + +:- import_module list. +:- import_module stream. +:- import_module string. + +main(!IO) :- + io.stdout_stream(Stdout, !IO), + stream.format(Stdout, "%s%d%c%f\n", + [s("foo"), i(561), c('a'), f(3.141)], !IO). diff --git a/tests/hard_coded/stream_ignore_ws.data b/tests/hard_coded/stream_ignore_ws.data new file mode 100644 index 000000000..bde492841 --- /dev/null +++ b/tests/hard_coded/stream_ignore_ws.data @@ -0,0 +1,7 @@ + + + + + + + foo bar baz diff --git a/tests/hard_coded/stream_ignore_ws.exp b/tests/hard_coded/stream_ignore_ws.exp new file mode 100644 index 000000000..1aeaedbf4 --- /dev/null +++ b/tests/hard_coded/stream_ignore_ws.exp @@ -0,0 +1 @@ +foo bar baz diff --git a/tests/hard_coded/stream_ignore_ws.m b/tests/hard_coded/stream_ignore_ws.m new file mode 100644 index 000000000..8f46592d6 --- /dev/null +++ b/tests/hard_coded/stream_ignore_ws.m @@ -0,0 +1,24 @@ +:- module stream_ignore_ws. +:- interface. + +:- import_module io. + +:- pred main(io::di, io::uo) is det. + +:- implementation. + +:- import_module stream. + +main(!IO) :- + io.stdin_stream(Stdin, !IO), + stream.ignore_whitespace(Stdin, IgnoreResult, !IO), + ( IgnoreResult = ok -> + io.read_file_as_string(Stdin, MaybePartialRes, !IO), + ( MaybePartialRes = ok(String) -> + io.write_string(String, !IO) + ; + io.write_string("io.read_file_as_string FAILED\n", !IO) + ) + ; + io.write_string("stream.ignore_whitespace FAILED\n", !IO) + ). diff --git a/tests/invalid/string_format_bad.err_exp b/tests/invalid/string_format_bad.err_exp index 5db740583..9d1f494a2 100644 --- a/tests/invalid/string_format_bad.err_exp +++ b/tests/invalid/string_format_bad.err_exp @@ -1,16 +1,19 @@ -string_format_bad.m:020: Mismatched format and values in call to -string_format_bad.m:020: `string.format'/3: -string_format_bad.m:020: format string invalid. -string_format_bad.m:022: Mismatched format and values in call to -string_format_bad.m:022: `string.format'/3: -string_format_bad.m:022: invalid conversion specifier. -string_format_bad.m:025: Mismatched format and values in call to `io.format'/4: -string_format_bad.m:025: invalid conversion specifier. -string_format_bad.m:026: Mismatched format and values in call to `io.format'/5: +string_format_bad.m:021: Mismatched format and values in call to +string_format_bad.m:021: `string.format'/3: +string_format_bad.m:021: format string invalid. +string_format_bad.m:023: Mismatched format and values in call to +string_format_bad.m:023: `string.format'/3: +string_format_bad.m:023: invalid conversion specifier. +string_format_bad.m:026: Mismatched format and values in call to `io.format'/4: string_format_bad.m:026: invalid conversion specifier. -string_format_bad.m:027: Mismatched format and values in call to `io.format'/4: +string_format_bad.m:027: Mismatched format and values in call to `io.format'/5: string_format_bad.m:027: invalid conversion specifier. -string_format_bad.m:036: Mismatched format and values in call to `io.format'/5: -string_format_bad.m:036: invalid conversion specifier. -string_format_bad.m:041: Mismatched format and values in call to `io.format'/5: -string_format_bad.m:041: invalid conversion specifier. +string_format_bad.m:028: Mismatched format and values in call to +string_format_bad.m:028: `stream.format'/5: +string_format_bad.m:028: invalid conversion specifier. +string_format_bad.m:029: Mismatched format and values in call to `io.format'/4: +string_format_bad.m:029: invalid conversion specifier. +string_format_bad.m:038: Mismatched format and values in call to `io.format'/5: +string_format_bad.m:038: invalid conversion specifier. +string_format_bad.m:043: Mismatched format and values in call to `io.format'/5: +string_format_bad.m:043: invalid conversion specifier. diff --git a/tests/invalid/string_format_bad.m b/tests/invalid/string_format_bad.m index d41d18657..7bed3f3b7 100644 --- a/tests/invalid/string_format_bad.m +++ b/tests/invalid/string_format_bad.m @@ -14,6 +14,7 @@ :- import_module float. :- import_module int. :- import_module list. +:- import_module stream. :- import_module string. main(!IO) :- @@ -24,6 +25,7 @@ main(!IO) :- io.stdout_stream(OutputStream, !IO), io.format("%d", [s("x3")], !IO), io.format(OutputStream, "%d", [s("x4")], !IO), + stream.format(OutputStream, "%d", [s("x4")], !IO), io.format("%w", [i(5)], !IO), io.write_string(p(s("five")), !IO), F6 = "%s %f", diff --git a/tests/invalid/string_format_unknown.err_exp b/tests/invalid/string_format_unknown.err_exp index 340527bc7..8e2076698 100644 --- a/tests/invalid/string_format_unknown.err_exp +++ b/tests/invalid/string_format_unknown.err_exp @@ -1,18 +1,20 @@ -string_format_unknown.m:021: Unknown format string in call to -string_format_unknown.m:021: `string.format'/3. -string_format_unknown.m:023: Mismatched format and values in call to -string_format_unknown.m:023: `string.format'/3: -string_format_unknown.m:023: invalid conversion specifier. -string_format_unknown.m:026: Mismatched format and values in call to -string_format_unknown.m:026: `io.format'/4: -string_format_unknown.m:026: invalid conversion specifier. +string_format_unknown.m:022: Unknown format string in call to +string_format_unknown.m:022: `string.format'/3. +string_format_unknown.m:024: Mismatched format and values in call to +string_format_unknown.m:024: `string.format'/3: +string_format_unknown.m:024: invalid conversion specifier. string_format_unknown.m:027: Mismatched format and values in call to -string_format_unknown.m:027: `io.format'/5: +string_format_unknown.m:027: `io.format'/4: string_format_unknown.m:027: invalid conversion specifier. string_format_unknown.m:028: Mismatched format and values in call to -string_format_unknown.m:028: `io.format'/4: +string_format_unknown.m:028: `io.format'/5: string_format_unknown.m:028: invalid conversion specifier. -string_format_unknown.m:038: Unknown format values in call to `io.format'/5. -string_format_unknown.m:043: Mismatched format and values in call to -string_format_unknown.m:043: `io.format'/5: -string_format_unknown.m:043: invalid conversion specifier. +string_format_unknown.m:029: Mismatched format and values in call to +string_format_unknown.m:029: `io.format'/4: +string_format_unknown.m:029: invalid conversion specifier. +string_format_unknown.m:039: Unknown format values in call to `io.format'/5. +string_format_unknown.m:040: Unknown format values in call to +string_format_unknown.m:040: `stream.format'/5. +string_format_unknown.m:045: Mismatched format and values in call to +string_format_unknown.m:045: `io.format'/5: +string_format_unknown.m:045: invalid conversion specifier. diff --git a/tests/invalid/string_format_unknown.m b/tests/invalid/string_format_unknown.m index 3cd37115d..088be6cb0 100644 --- a/tests/invalid/string_format_unknown.m +++ b/tests/invalid/string_format_unknown.m @@ -14,6 +14,7 @@ :- import_module float. :- import_module int. :- import_module list. +:- import_module stream. :- import_module string. main(!IO) :- @@ -36,6 +37,7 @@ main(!IO) :- V6 = [s("six"), V6A], copy(V6, C6), io.format(OutputStream, F6, C6, !IO), + stream.format(OutputStream, F6, C6, !IO), make_bool(7, T7), F7 = "%d %s %d", (