[m-dev.] for discussion: stream library v2

Peter Ross petdr at miscrit.be
Wed Sep 27 20:42:34 AEDT 2000


Here is my final (?) implementation of a stream library.
Let me know what you think of this design, and then I will clean it all
up and get it ready for inclusion in the standard library.

Pete
-------------- next part --------------
%-----------------------------------------------------------------------------%
% 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: lowlevel.m.
% Main author: petdr
% Stability: exceptionally low.
%
% An impure interface for describing streams.
% We also provide an implementation for read/write one character which
% can be used to define a highlevel stream interface.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- module lowlevel.

:- interface.

:- import_module stream.
:- import_module char.

:- type lowlevel(S) ---> lowlevel(S).

:- typeclass lowlevel(S) where [
		% Did an error occur processing the stream?
	semipure pred lowlevel__is_error(S::ui, string::out) is semidet
].

:- typeclass lowlevel__input(S) <= lowlevel(S) where [
		% Read one character from the stream described by S.
		% Fail if we reach eof or some error condition.
	impure pred lowlevel__read_char(S::ui, char::out) is semidet,

		% Have we reached the eof for S?
	semipure pred lowlevel__is_eof(S::ui) is semidet
].

:- typeclass lowlevel__output(S) <= lowlevel(S) where [
		% Read one character from the current stream.
	impure pred lowlevel__write_char(S::ui, char::in) is semidet
].

:- typeclass lowlevel__duplex(S)
		<= (lowlevel__input(S), lowlevel__output(S)) where [].

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

	% Define read/write one character whose signatures obey the
	% constraints of the highlevel stream type class.

:- pred low_read_char(stream__result(char),
		lowlevel(S), lowlevel(S)) <= lowlevel__input(S).
:- mode low_read_char(out, di, uo) is det.

:- pred low_write_char(char, lowlevel(S), lowlevel(S)) <= lowlevel__output(S).
:- mode low_write_char(in, di, uo) is det.

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

:- implementation.
:- import_module exception.

:- pragma promise_pure(low_read_char/3).
low_read_char(Result, lowlevel(Stream), FinalStream) :-
	( impure lowlevel__read_char(Stream, Chr) ->
		FinalStream = lowlevel(Stream),
		Result = ok(Chr)
	;
		( semipure lowlevel__is_error(Stream, Error) ->
			FinalStream = lowlevel(Stream),
			Result = error(Error)
		; semipure lowlevel__is_eof(Stream) ->
			FinalStream = lowlevel(Stream),
			Result = eof
		;
			FinalStream = lowlevel(Stream),
			Error = "stream not in eof or error state but read char failed.",
			Result = error(Error)
		)
	).

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

:- pragma promise_pure(low_write_char/3).
low_write_char(Chr, lowlevel(Stream), FinalStream) :-
	( impure lowlevel__write_char(Stream, Chr) ->
		FinalStream = lowlevel(Stream)
	;
		( semipure lowlevel__is_error(Stream, Err0) ->
			Err = Err0
		;
			Err = "stream__write_char failed but there is no error message"
		),
		FinalStream = lowlevel(Stream),
		throw(stream_error(Err))
	).

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
-------------- next part --------------
%-----------------------------------------------------------------------------%
% 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: highlevel.m.
% Main author: petdr
% Stability: exceptionally low.
%
% A pure interface for describing streams.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- module highlevel.

:- interface.

:- import_module lowlevel, stream.
:- import_module char.

:- typeclass highlevel(S) where [
].

:- typeclass highlevel__input(S) <= highlevel(S) where [
		% Read one character from the stream S.
	pred highlevel__read_char(stream__result(char), S, S),
	mode highlevel__read_char(out, di, uo) is det
].

:- typeclass highlevel__output(S) <= highlevel(S) where [
		% Write one character to the stream S.
	pred highlevel__write_char(char, S, S),
	mode highlevel__write_char(in, di, uo) is det
].

:- typeclass highlevel__duplex(S)
		<= (highlevel__input(S), highlevel__output(S)) where [].

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
-------------- next part --------------
%-----------------------------------------------------------------------------%*
% 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 high.
%
% This file provides interfaces for stream based I/O.
% This interface is built on top of the pure interface defined in
% highlevel.m
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- module stream.

:- interface.

:- import_module highlevel.
:- import_module char, io, list.

	% The state of the world for 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 of the world from the
	% io__state. XXX This isn't such a good idea for string streams,
	% but is for other streams.
:- pred stream__init(S::in, stream(S)::uo, io__state::di, io__state::uo) is det.

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

% 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 <= highlevel__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 <= highlevel__input(S).

	% Reads a whitespace delimited word from the current input stream.
:- pred stream__read_word(stream__result(list(char)),
		stream(S), stream(S)) <= highlevel__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)) <= highlevel__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)) <= highlevel__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 <= highlevel__output(S).

	% Write the string to the output stream.
:- pred stream__write_string(string,
		stream(S), stream(S)) <= highlevel__output(S).
:- mode stream__write_string(in, di, uo) is det.

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

	% 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 <=
		(highlevel__input(S), highlevel__output(T)).

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

:- implementation.

:- import_module exception, string.

:- type stream(S)
	--->	stream(
			S,		% Handle on the stream
			list(char)	% Putback characters
		).

stream__init(S, stream(Stream, [])) --> { copy(S, Stream) }.

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

stream__read_char(Result, stream(Stream0, PutbackChars),
		stream(Stream, NewPutbackChars)) :-
	(
		PutbackChars = [],
		highlevel__read_char(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)) :-
	highlevel__write_char(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)) <= highlevel__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(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)]))) }
	).
	
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
-------------- next part --------------
%-----------------------------------------------------------------------------%
% 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 highlevel. 

:- type stdio.
:- type stdio_stream == lowlevel(stdio).

:- instance highlevel(stdio_stream).
:- instance highlevel__input(stdio_stream).
:- instance highlevel__output(stdio_stream).
:- instance highlevel__duplex(stdio_stream).

:- pred stdio_stream(stdio_stream::uo) is det.

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

:- implementation.

:- import_module lowlevel. 
:- import_module char. 

:- type stdio ---> stdio.

stdio_stream(lowlevel(stdio)).

	% Define the high-level operations using the generic operations
	% from the lowlevel module.
:- instance highlevel(stdio_stream) where [].
:- instance highlevel__input(stdio_stream) where [
	pred(read_char/3) is low_read_char
].

:- instance highlevel__output(stdio_stream) where [
	pred(write_char/3) is low_write_char
].

:- instance highlevel__duplex(stdio_stream) where [].

	% Define the lowlevel operations
:- instance lowlevel(stdio) where [
	pred(lowlevel__is_error/2) is stdio__is_error
].

:- instance lowlevel__input(stdio) where [
	pred(lowlevel__read_char/2) is stdio__read_char,
	pred(lowlevel__is_eof/1) is stdio__is_eof
].

:- instance lowlevel__output(stdio) where [
	pred(lowlevel__write_char/2) is stdio__write_char
].

:- instance lowlevel__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;
	}
}").

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

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

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

	{ stdio_stream(StdioStream) },
	stream__init(StdioStream, Stream),
	{ generic_io(Stream, _) }.

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

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


More information about the developers mailing list