[m-dev.] for review: stream I/O

Peter Ross petdr at miscrit.be
Mon Oct 2 04:51:35 AEDT 2000


Hi,

I have also attached a file main.m which does I/O on string and
stdio streams.

===================================================================


Estimated hours taken: 40

Implement generic stream based I/O.

stream.m:
    Define the stream typeclass and provide some implementations of
    routines which work on streams.

stream.impure.m:
    Define an impure interface which is useful for interfacing streams
    provided by the foreign function interface.

stdio.m:
    Define an instance of the stream typeclass which provides a
    stdin/stdout stream.

string.m:
    Define an instance of the stream typeclass which works on strings.

library.m:
    Include the stream and stdio modules.

Index: library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.50
diff -u -r1.50 library.m
--- library.m	2000/09/20 12:12:36	1.50
+++ library.m	2000/10/01 17:43:17
@@ -31,7 +31,7 @@
 :- import_module math, getopt, graph, group, int.
 :- import_module io, list, map, multi_map, pqueue, queue, random, relation.
 :- import_module require, set, set_bbbtree, set_ordlist, set_unordlist, stack.
-:- import_module std_util, string, term, term_io, tree234, varset.
+:- import_module std_util, stream, string, term, term_io, tree234, varset.
 :- import_module store, rbtree, parser, lexer, ops.
 :- import_module prolog.
 :- import_module integer, rational.
Index: stdio.m
===================================================================
RCS file: stdio.m
diff -N stdio.m
--- /dev/null	Sat Aug  7 21:45:41 1999
+++ stdio.m	Mon Oct  2 04:43:22 2000
@@ -0,0 +1,135 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2000 The University of Melbourne.
+% This file may only be copied under the terms of the GNU Library General
+% Public License - see the file COPYING.LIB
+%-----------------------------------------------------------------------------%
+%
+% File: stdio.m
+% Main author: petdr
+% Stability: exceptionally low.
+%
+% A stdin/stdout stream.
+%
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- module stdio.
+
+:- interface.
+:- import_module stream, stream__impure.
+:- import_module io.
+
+:- type stdio.
+
+	% XXX This should be an abstract type, but currently you aren't
+	% allowed to have instance declarations that refer to abstract
+	% types which are defined as equivalence types, and this needs
+	% to be an equivalence type so that we can use the typeclass
+	% interface defined in `stream.impure.m'.
+:- type stdio_stream == impure(stdio).
+
+:- instance stream(stdio_stream).
+:- instance stream__input(stdio_stream).
+:- instance stream__output(stdio_stream).
+:- instance stream__duplex(stdio_stream).
+
+:- pred stdio_stream(stdio_stream::uo, io__state::di, io__state::uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module char. 
+
+:- type stdio ---> stdio.
+
+stdio_stream(impure(stdio)) --> [].
+
+	% Define the high-level operations using the generic operations
+	% from the stream__impure module.
+:- instance stream(stdio_stream) where [].
+:- instance stream__input(stdio_stream) where [
+	pred(read_character/3) is pure_read_char
+].
+
+:- instance stream__output(stdio_stream) where [
+	pred(write_character/3) is pure_write_char
+].
+
+:- instance stream__duplex(stdio_stream) where [].
+
+%-----------------------------------------------------------------------------%
+
+	% Define the impure lowlevel operations
+:- instance impure(stdio) where [
+	pred(impure__is_error/2) is stdio__is_error
+].
+
+:- instance impure__input(stdio) where [
+	pred(impure__read_char/2) is stdio__read_char,
+	pred(impure__is_eof/1) is stdio__is_eof
+].
+
+:- instance impure__output(stdio) where [
+	pred(impure__write_char/2) is stdio__write_char
+].
+
+:- instance impure__duplex(stdio) where [].
+
+%-----------------------------------------------------------------------------%
+	
+:- pred stdio__read_char(stdio::ui, char::out) is semidet.
+:- pragma c_code(stdio__read_char(_File::ui, Chr::out),
+		[will_not_call_mercury, thread_safe], "{
+	int chr;
+
+	chr = fgetc(stdin);
+	if (chr == EOF) {
+		SUCCESS_INDICATOR = FALSE;
+	} else {
+		SUCCESS_INDICATOR = TRUE;
+		Chr = chr;
+	}
+}").
+
+%-----------------------------------------------------------------------------%
+
+:- pred stdio__write_char(stdio::ui, char::in) is semidet.
+:- pragma c_code(stdio__write_char(_File::ui, Chr::in),
+		[will_not_call_mercury, thread_safe], "{
+	if (fputc(Chr, stdout) == EOF) {
+		SUCCESS_INDICATOR = FALSE;
+	} else {
+		SUCCESS_INDICATOR = TRUE;
+	}
+}").
+
+%-----------------------------------------------------------------------------%
+
+:- pragma c_header_code("#include <errno.h>").
+
+:- pred stdio__is_error(stdio::ui, string::out) is semidet.
+:- pragma c_code(stdio__is_error(_File::ui, Msg::out),
+		[will_not_call_mercury, thread_safe], "{
+	if (ferror(stdin) || ferror(stdout)) {
+		SUCCESS_INDICATOR = TRUE;
+		save_transient_hp();
+		MR_make_aligned_string_copy(Msg, strerror(errno));
+		restore_transient_hp();
+	} else {
+		SUCCESS_INDICATOR = FALSE;
+	}
+}").
+
+:- pred stdio__is_eof(stdio::ui) is semidet.
+:- pragma c_code(stdio__is_eof(_File::ui),
+		[will_not_call_mercury, thread_safe], "{
+	if (feof(stdin)) {
+		SUCCESS_INDICATOR = TRUE;
+	} else {
+		SUCCESS_INDICATOR = FALSE;
+	}
+}").
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
Index: stream.impure.m
===================================================================
RCS file: stream.impure.m
diff -N stream.impure.m
--- /dev/null	Sat Aug  7 21:45:41 1999
+++ stream.impure.m	Mon Oct  2 04:43:22 2000
@@ -0,0 +1,112 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2000 The University of Melbourne.
+% This file may only be copied under the terms of the GNU Library General
+% Public License - see the file COPYING.LIB
+%-----------------------------------------------------------------------------%
+%
+% File: stream.impure.m.
+% Main author: petdr
+% Stability: exceptionally low.
+%
+% An impure interface for describing streams.
+%
+% This file provides a typeclass for people who want to map streams
+% to a foreign language binding while doing the minimum amount of work.  In
+% particular you need to write much less foreign language code, since
+% you only need to implement a few impure predicates with a well defined
+% interface.
+%
+% This file provides throwing exceptions, grabbing error messages,
+% results packaged into ok/error/eof, and turning C style handle based
+% IO into Mercury di/uo.  That's all it does, but it's something you'll
+% have to do and get right every time you implement a stream, so we have
+% done it for you.
+%
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- module stream__impure.
+
+:- interface.
+
+:- import_module char.
+
+:- type impure(S) ---> impure(S).
+
+:- typeclass impure(S) where [
+		% Did an error occur processing the stream?
+	semipure pred impure__is_error(S::ui, string::out) is semidet
+].
+
+:- typeclass impure__input(S) <= impure(S) where [
+		% Read one character from the stream described by S.
+		% Fail if we reach eof or some error condition.
+	impure pred impure__read_char(S::ui, char::out) is semidet,
+
+		% Have we reached the eof for S?
+	semipure pred impure__is_eof(S::ui) is semidet
+].
+
+:- typeclass impure__output(S) <= impure(S) where [
+		% Read one character from the current stream.
+	impure pred impure__write_char(S::ui, char::in) is semidet
+].
+
+:- typeclass impure__duplex(S)
+		<= (impure__input(S), impure__output(S)) where [].
+
+%-----------------------------------------------------------------------------%
+
+	% Define read/write one character whose signatures obey the
+	% constraints of the stream type classes.
+
+:- pred pure_read_char(stream__result(char),
+		impure(S), impure(S)) <= impure__input(S).
+:- mode pure_read_char(out, di, uo) is det.
+
+:- pred pure_write_char(char, impure(S), impure(S)) <= impure__output(S).
+:- mode pure_write_char(in, di, uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+:- import_module exception.
+
+:- pragma promise_pure(pure_read_char/3).
+pure_read_char(Result, impure(Stream), FinalStream) :-
+	( impure impure__read_char(Stream, Chr) ->
+		FinalStream = impure(Stream),
+		Result = ok(Chr)
+	;
+		( semipure impure__is_error(Stream, Error) ->
+			FinalStream = impure(Stream),
+			Result = error(Error)
+		; semipure impure__is_eof(Stream) ->
+			FinalStream = impure(Stream),
+			Result = eof
+		;
+			FinalStream = impure(Stream),
+			Error = "read char failed for an unknown reason",
+			Result = error(Error)
+		)
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- pragma promise_pure(pure_write_char/3).
+pure_write_char(Chr, impure(Stream), FinalStream) :-
+	( impure impure__write_char(Stream, Chr) ->
+		FinalStream = impure(Stream)
+	;
+		( semipure impure__is_error(Stream, Err0) ->
+			Err = Err0
+		;
+			Err = "write char failed but there is no error message"
+		),
+		FinalStream = impure(Stream),
+		throw(stream_error(Err))
+	).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
Index: stream.m
===================================================================
RCS file: stream.m
diff -N stream.m
--- /dev/null	Sat Aug  7 21:45:41 1999
+++ stream.m	Mon Oct  2 04:43:22 2000
@@ -0,0 +1,338 @@
+%-----------------------------------------------------------------------------%*
+% Copyright (C) 2000 The University of Melbourne.
+% This file may only be copied under the terms of the GNU Library General
+% Public License - see the file COPYING.LIB
+%-----------------------------------------------------------------------------%
+%
+% File: stream.m.
+% Main author: petdr
+% Stability: exceptionally low.
+%
+% This file provides a typeclass for defining streams in Mercury.
+% It is completely pure and you are encouraged to use it to write
+% streams in Mercury.  If however you are a library implementor then you
+% may want to look at the impure interface described in stream.impure.m
+%
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- module stream.
+
+:- interface.
+
+:- import_module char, io, list.
+:- include_module stream__impure.
+
+	% The state of one stream of type S.
+:- type stream(S).
+
+	% The type of exceptions thrown by this module.
+:- type stream_error ---> stream_error(string).
+
+:- type stream__result(T)
+	--->	ok(T)
+	;	eof
+	;	error(string)
+	.
+
+:- type stream__result
+	--->	ok
+	;	eof
+	;	error(string)
+	.
+
+	% Given a handle to a stream construct a unique stream object
+	% which can be used to do IO on the stream.
+	% This object initialises its state from the io__state.
+:- pred stream__init(S::in, stream(S)::uo, io__state::di, io__state::uo) is det.
+
+	% Tie the stream back into the io__state.  Omitting this call
+	% may allow the compiler to optimize away the stream I/O.
+:- pred stream__destroy(stream(S)::di, io__state::di, io__state::uo) is det.
+
+	% Initialise a stream which is side effect free.
+:- pred stream__init(S::in, stream(S)::uo) is det <= side_effect_free(S).
+
+	% Destroy a side effect free stream.
+:- pred stream__destroy(stream(S)::di, S::out) is det <= side_effect_free(S).
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% The pure interface to streams.
+	% Note if you are using streams you probably want to use the
+	% read_char and write_char routines, this interface should only
+	% be used to define new stream types.
+	%
+:- typeclass stream(S) where [
+].
+
+:- typeclass stream__input(S) <= stream(S) where [
+		% Read one character from the stream S.
+	pred stream__read_character(stream__result(char), S, S),
+	mode stream__read_character(out, di, uo) is det
+].
+
+:- typeclass stream__output(S) <= stream(S) where [
+		% Write one character to the stream S.
+	pred stream__write_character(char, S, S),
+	mode stream__write_character(in, di, uo) is det
+].
+
+:- typeclass stream__duplex(S)
+		<= (stream__input(S), stream__output(S)) where [].
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% A type which doesn't need to be linked with an io__state when
+	% initialising the stream, ie string.
+	%
+:- typeclass stream__side_effect_free(S) where [].
+
+%-----------------------------------------------------------------------------%
+
+% Predicates which require an input stream.
+
+	% Read one character from the input stream.
+:- pred stream__read_char(stream__result(char)::out,
+		stream(S)::di, stream(S)::uo) is det <= stream__input(S).
+
+	% Putback one character into the input stream.
+	% You can putback as many characters as required.
+:- pred stream__putback_char(char::in,
+		stream(S)::di, stream(S)::uo) is det <= stream__input(S).
+
+	% Reads a whitespace delimited word from the current input stream.
+:- pred stream__read_word(stream__result(list(char)),
+		stream(S), stream(S)) <= stream__input(S).
+:- mode stream__read_word(out, di, uo) is det.
+
+	% Reads one line of input from the current input stream.
+:- pred stream__read_line(stream__result(list(char)),
+		stream(S), stream(S)) <= stream__input(S).
+:- mode stream__read_line(out, di, uo) is det.
+
+	% Discards all the whitespace from the current stream.
+:- pred stream__ignore_whitespace(stream__result,
+		stream(S), stream(S)) <= stream__input(S).
+:- mode stream__ignore_whitespace(out, di, uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+% Predicates which require an output stream.
+% On failure these predicates will throw an stream_error exception.
+
+	% Write one char to the output stream.
+:- pred stream__write_char(char::in,
+		stream(S)::di, stream(S)::uo) is det <= stream__output(S).
+
+	% Write the string to the output stream.
+:- pred stream__write_string(string,
+		stream(S), stream(S)) <= stream__output(S).
+:- mode stream__write_string(in, di, uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+	% Echo the input of a duplex stream to the output of the duplex
+	% stream.
+:- pred stream__cat(stream(S)::di, stream(S)::uo) is det <= stream__duplex(S).
+
+	% Echo the input stream to the output stream.
+:- pred stream__cat(stream(S)::di, stream(S)::uo,
+		stream(T)::di, stream(T)::uo) is det <=
+		(stream__input(S), stream__output(T)).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception, string.
+
+:- type stream(S)
+	--->	stream(
+			S,		% the stream state
+			list(char)	% Putback characters
+		).
+
+stream__init(S, stream(Stream, [])) --> { copy(S, Stream) }.
+stream__init(S, stream(Stream, [])) :- copy(S, Stream).
+
+stream__destroy(_S) --> [].
+stream__destroy(stream(S, _), S).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+stream__read_char(Result, stream(Stream0, PutbackChars),
+		stream(Stream, NewPutbackChars)) :-
+	(
+		PutbackChars = [],
+		stream__read_character(Result, Stream0, Stream),
+		NewPutbackChars = PutbackChars
+	;
+		PutbackChars = [Chr | NewPutbackChars],
+		Stream = Stream0,
+		Result = ok(Chr)
+	).
+
+%-----------------------------------------------------------------------------%
+
+stream__putback_char(Chr, stream(Stream, PutbackChars), 
+		stream(Stream, [UniqueChr | PutbackChars])) :-
+	copy(Chr, UniqueChr).
+
+%-----------------------------------------------------------------------------%
+
+stream__write_char(Chr, stream(Stream0, Cs), stream(Stream, Cs)) :-
+	stream__write_character(Chr, Stream0, Stream).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+stream__read_word(Result) -->
+	stream__ignore_whitespace(WSResult),
+	(
+		{ WSResult = error(Error) },
+		{ Result = error(Error) }
+	;
+		{ WSResult = eof },
+		{ Result = eof }
+	;
+		{ WSResult = ok },
+		stream__read_word_2(Result)
+	).
+
+:- pred read_word_2(stream__result(list(char)),
+		stream(S), stream(S)) <= stream__input(S).
+:- mode read_word_2(out, di, uo) is det.
+
+read_word_2(Result) -->
+	stream__read_char(CharResult),
+	(
+		{ CharResult = error(Error) },
+		{ Result = error(Error) }
+	;
+		{ CharResult = eof },
+		{ Result = eof }
+	;
+		{ CharResult = ok(Char) },
+		( { char__is_whitespace(Char) } ->
+			stream__putback_char(Char),
+			{ Result = ok([]) }
+		;
+			read_word_2(Result0),
+			(
+				{ Result0 = ok(Chars) },
+				{ Result = ok([Char | Chars]) }
+			;
+				{ Result0 = error(_) },
+				{ Result = Result0 }
+			;
+				{ Result0 = eof },
+				{ Result = ok([Char]) }
+			)
+		)	
+	).
+
+read_line(Result) -->
+	stream__read_char(CharResult),
+	(
+		{ CharResult = error(Error) },
+		{ Result = error(Error) }
+	;
+		{ CharResult = eof },
+		{ Result = eof }
+	;
+		{ CharResult = ok(Char) },
+		( { Char = '\n' } ->
+			{ Result = ok([Char]) }
+		;
+			read_line(Result0),
+			(
+				{ Result0 = ok(Chars) },
+				{ Result = ok([Char | Chars]) }
+			;
+				{ Result0 = error(_) },
+				{ Result = Result0 }
+			;
+				{ Result0 = eof },
+				{ Result = ok([Char]) }
+			)
+		)	
+	).
+
+stream__ignore_whitespace(Result) -->
+	stream__read_char(CharResult),
+	(
+		{ CharResult = error(Error) },
+		{ Result = error(Error) }
+	;
+		{ CharResult = eof },
+		{ Result = eof }
+	;
+		{ CharResult = ok(Char) },
+		( { char__is_whitespace(Char) } ->
+			stream__ignore_whitespace(Result)
+		;
+			stream__putback_char(Char),
+			{ Result = ok }
+		)	
+	).
+
+%-----------------------------------------------------------------------------%
+
+stream__write_string(String) -->
+	{ string__to_char_list(String, CharList) },
+	list__foldl(stream__write_char, CharList).
+
+%-----------------------------------------------------------------------------%
+
+stream__cat -->
+	stream__read_char(Result),
+	(
+		{ Result = ok(Char) },
+		stream__write_char(Char),
+		cat
+	;
+		{ Result = eof }
+	;
+		{ Result = error(String) },
+		{ throw(stream_error(
+			string__format("stream__cat: %s.", [s(String)]))) }
+	).
+	
+stream__cat(S0, S) -->
+	{ stream__read_char(Result, S0, S1) },
+	(
+		{ Result = ok(Char) },
+		stream__write_char(Char),
+		cat(S1, S)
+	;
+		{ Result = eof },
+		{ S = S1 }
+	;
+		{ Result = error(String) },
+		{ throw(stream_error(
+			string__format("stream__cat: %s.", [s(String)]))) }
+	).
+	
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- func state_init = c_pointer.
+:- mode state_init = uo is det.
+:- pragma c_code(state_init = (State::uo),
+		[will_not_call_mercury, thread_safe], "{
+	State = 0;
+}").
+
+:- pred state_update(c_pointer::di, c_pointer::uo) is det.
+:- pragma c_code(state_update(State0::di, State::uo),
+		[will_not_call_mercury, thread_safe], "{
+	State = State0;
+}").
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
Index: string.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/string.m,v
retrieving revision 1.131
diff -u -r1.131 string.m
--- string.m	2000/09/28 07:40:40	1.131
+++ string.m	2000/10/01 17:43:28
@@ -20,7 +20,14 @@
 
 :- interface.
 :- import_module list, char.
+:- import_module stream.
 
+:- instance stream(string).
+:- instance stream__input(string).
+:- instance stream__output(string).
+:- instance stream__duplex(string).
+:- instance stream__side_effect_free(string).
+
 :- pred string__length(string, int).
 :- mode string__length(in, uo) is det.
 	% Determine the length of a string.
@@ -362,6 +369,33 @@
 
 :- implementation.
 :- import_module bool, std_util, int, float, require.
+
+:- instance stream(string) where [].
+:- instance stream__input(string) where [
+	pred(read_character/3) is read_char
+].
+:- instance stream__output(string) where [
+	pred(write_character/3) is write_char
+].
+:- instance stream__duplex(string) where [].
+:- instance stream__side_effect_free(string) where [].
+
+:- pred read_char(stream__result(char)::out, string::di, string::uo) is det.
+
+read_char(Result, String0, String) :-
+	( string__first_char(String0, Char, String1) ->
+		Result = ok(Char),
+		copy(String1, String)
+	;
+		Result = eof,
+		copy(String0, String)
+	).
+		
+:- pred write_char(char::in, string::di, string::uo) is det.
+
+write_char(Char, String0, String) :-
+	string__append(String0, string__char_to_string(Char), String1),
+	copy(String1, String).
 
 :- pred string__to_int_list(string, list(int)).
 :- mode string__to_int_list(out, in) is det.

-------------- next part --------------
:- module main.
:- interface.
:- import_module io.
:- pred main(io__state::di, io__state::uo) is det.

:- implementation.
:- import_module stdio, stream, string.
:- import_module list.

main -->
	io__write_string("Hello world.\n"),

	stdio_stream(StdioStream),
	stream__init(StdioStream, Stream0),
	{ generic_io(Stream0, Stream) },
	stream__destroy(Stream),

	{ stream__init("", Stream0A) },
	{ generic_io(Stream0A, StreamA) },
	{ stream__destroy(StreamA, String) },
	io__write_string(String).

:- pred generic_io(stream(S)::di, stream(S)::uo) is det <= stream__output(S).

generic_io -->
	stream__write_string("Hello world from stream library.\n").


More information about the developers mailing list