/*  READ_LINE.PL  */
 
 
:- module read_line.
 
 
:- public read_line_as_chars/1,
          read_line_as_atom/1,
          read_line_as_atom_and_chars/2.
 
 
/*
SPECIFICATION
-------------
 
This module implements predicates for reading lines as character lists
or atoms.
 
PUBLIC read_line_as_chars( Chars- ):
Unifies Chars with a list of all characters up to but not including the
end of the current line on the CIS. If end-of-file has been reached,
Chars becomes the atom 'end_of_file' instead.
 
PUBLIC read_line_as_atom( Atom- ):
Unifies Atom with all the characters up to but not including the end of
the current line on the CIS. If end-of-file has been reached, Chars
becomes the atom 'end_of_file' instead.
 
PUBLIC read_line_as_atom_and_chars( Atom-, Chars- ):
Combines the two previous predicates, returning both the atom and the
characters.
 
 
Example:
--------
 
Given the file
    Line 1
    abcd
    last line
(where I've indented the lines 4 places to make them stand out), then
the question
    ?- see('dummy.txt'),
       read_line_as_chars(L1),
       read_line_as_chars(L2),
       read_line_as_chars(L3),
       read_line_as_chars(L4).
gives
    L1 = [76, 105, 110, 101, 32, 49]
    L2 = [97, 98, 99, 100]
    L3 = [108, 97, 115, 116, 32, 108, 105, 110, 101]
    L4 = end_of_file
*/
 
 
/*
IMPLEMENTATION
--------------
 
See Richard O'Keefe's "The Craft of Prolog" chapter 10 for an excellent
introduction to this and other character transput problems. My structure
is slightly different from his because of the end-of-file check im
read_line_as_chars.
 
 
The two read_line_as_atom predicates call read_line_as_chars and then
convert the resulting character list to an atom (unless
read_line_as_chars returned the atom 'end_of_file' instead of a list,
when they do the same). Note that they rely on the BIP name/2 converting
between lists and atoms rather than (as in ESL Prolog2) between strings
and atoms.
 
read_line_as_chars examines the first character of the line. If it's an
end-of-file, read_line_as_char stops, returning 'end_of_file'. On my
system, it seems that end-of-file characters only occur immediately at
the start of a line, so it's not necessary to check at any other time.
(If you do get0 four times and then type
    aaa<control Z>
then get0 never sees the control-Z. It seemingly is only noticed in
interactive input if it occurs just after a RETURN).
 
 
Otherwise, read_line_as_chars calls
    read_rest_as_chars( C, Rest )
which reads into Rest whatever part of the line remains after C. The 2nd
clause of read_rest_as_chars checks for end-of-file, and signals a bug
(i.e. an indication that my assumption was after all mistaken) if one
occurs.
 
These predicates depend on module CHARS which defines the character code
values.
*/
 
 
:- needs    bug/1,
            is_end_of_file_char/1,
            is_newline_char/1.
 
 
read_line_as_atom( Atom ) :-
    read_line_as_atom_and_chars( Atom, _ ).
 
 
read_line_as_atom_and_chars( Atom, Chars ) :-
    read_line_as_chars( Chars ),
    (
        Chars = end_of_file
    ->
        Atom = Chars
    ;
        name( Atom, Chars )
    ).
 
 
read_line_as_chars( List ) :-
    get0( C ),
    (
        is_end_of_file_char( C )
    ->
        List = end_of_file
    ;
        read_rest_as_chars( C, List )
    ).
 
 
read_rest_as_chars( C, [] ) :-
    is_newline_char( C ),
    !.
 
read_rest_as_chars( C, _ ) :-
    is_end_of_file_char( C ),
    !,
    bug( 'read_line_as_chars: end of file part way through line' ).
 
read_rest_as_chars( C, [C|Tail] ) :-
    get0( CNext ),
    read_rest_as_chars( CNext, Tail ).
 
 
:- endmodule.
