Path: utzoo!mnetor!uunet!munnari!mulga!lee From: lee@mulga.oz (Lee Naish) Newsgroups: comp.lang.prolog Subject: Re: behavior of read/get0 at end_of_file Message-ID: <2643@mulga.oz> Date: 30 Mar 88 07:34:06 GMT References: <608> <1197@kulcs.kulcs.uucp> <783@cresswell.quintus.UUCP> <518@ecrcvax.UUCP> <801@cresswell.quintus.UUCP> <243@gould.doc.ic.ac.uk> Reply-To: lee@mulga.UUCP (Lee Naish) Organization: Comp Sci, Melbourne Uni, Australia Lines: 100 Keywords: get0 read end_of_file In article <243@gould.doc.ic.ac.uk> cdsm@doc.ic.ac.uk (Chris Moss) writes: >e.g. go :- repeat, > read(Term), > (Term=end_of_file -> true; process(Term), fail). > Testing for the end of file term using = is a common error. If a variable is read, it succeeds. A second criticism I would make of this code is that it has a tendency to loop. I think that repeat/0 should also have a matching cut. I posted a nicer way to encapsulate this backtracking style of reading terms a while back. It is also possible to move the read back into the repeat loop, avoiding repeat and the need for cut. With tro, it is just as efficient. Interestingly, it it works whether read fails or succeeds on eof. % returns all terms read by backtracking % (should have stream/file arg and close file at eof?) read_terms(Term) :- read(Term1), \+ isEof(Term1), % if you dont want to return end_of_file ( Term = Term1 ; % \+ isEof(Term1), % if you do want to read_terms(Term) ). Richard metioned some subtle differences between eof/1, is_eof/1 etc in different systems. There is another one which he missed: in NU-Prolog, isEof/1 checks if its argument is the eof term (reading variables works) and eof/1 returns the eof term (which is end_of_file for comatability). Now for my suggestion of a new predicate which can be used to implement your favourite version of read/1: read_term(Stream, Term) % change the name if necessary 1) If it succeeds in reading a term T, Term = term(T, VarNames) where Varnames is some structure which allows mapping between variables and their names. Wrapping a functor around the term enables to distinguish between variables, 'end_of_file' and real end of file easily. It also lets us retain variable name information. 2) If end of file is encountered for the first time, or if an end of file marker occurs next in the stream (like ^Z on a terminal) Term = eof_marker 3) If eof has already been read and multiple eof markers are not possible Term = error(past_eof) Whether this is an error is arguable, but by explicitly returning something, the programmer has the choice of what to do. Rather than having a proliferation of top level functors being returned by read_term, it seems reasonable to wrap the error functor around past_eof. 4) If there is a syntax error Term = error(syntax(X)) where X is some indication of the error 5) If Stream is not a valid stream Term = error(invalid_stream(X)) where X is some indication of the error 6) If there has just been a disk head crash Term = error(unix(hardware(disk_head_crash(X)))) etc, etc. Similarly, reading characters could be done as follows read_character(Stream, Char) % change name if necessary 1) If it succeeds in reading a character C, Char = C where char_to_int(C, I) can map the character to a small integer for get0/1. There is no special functor needed to wrap up Char, assuming the other things returned by read_character/2 can be distinguished from characters (eg, by is_character(Char)). 2) If end of file is encountered for the first time, or if an end of file marker occurs next in the stream (like ^Z on a terminal) Char = eof_marker 3) If eof has already been read and multiple eof markers are not possible Char = error(past_eof) 4) I doubt that there will ever be a need for error(syntax(X)), but it should be reserved anyway. 5) If Stream is not a valid stream Char = error(invalid_stream(X)) etc, etc. I think it would be useful for these (with the details fleshed out a bit more) to be part of the standard. lee