1

I have an issue with this code that works in Swi Prolog but doesn't in GNU Prolog.

:- dynamic libro/3.
:- dynamic prestito/3.

trim_whitespace(Input, Output) :-
    atom_string(InputAtom, Input),
    string_codes(InputAtom, InputCodes),
    phrase(trimmed_string(OutputCodes), InputCodes),
    string_codes(OutputAtom, OutputCodes),
    atom_string(Output, OutputAtom).

trimmed_string([]) --> [].
trimmed_string([H|T]) --> whitespace, [H], trimmed_string(T).

whitespace --> [C], { code_type(C, space) }, !, whitespace.
whitespace --> [].

% Definizioni dei fatti
libro('Il Signore degli Anelli', 'J.R.R. Tolkien', 5).
libro('1984', 'George Orwell', 3).
libro('La strada', 'Cormac McCarthy', 2).

% Predicati per la ricerca dei libri per titolo o autore
cercaLibriPerTitolo(Titolo, Libri) :-
    findall((Titolo, Autore, CopieDisponibili), 
            libro(Titolo, Autore, CopieDisponibili), Libri),
    Libri \= [].

cercaLibriPerAutore(Autore, Libri) :-
    trim_whitespace(Autore, AutoreTrimmed),
    downcase_atom(AutoreTrimmed, AutoreMinuscolo),
    debug(cerca_libri, 'Autore inserito: ~w', [AutoreMinuscolo]),
    findall((Titolo, AutoreDB, CopieDisponibili), (
        libro(Titolo, AutoreDB, CopieDisponibili),
        trim_whitespace(AutoreDB, AutoreDBTrimmed),
        downcase_atom(AutoreDBTrimmed, AutoreDBMinuscolo),
        sub_string(AutoreDBMinuscolo, _, _, _, AutoreMinuscolo)
    ), Libri),
    debug(cerca_libri, 'Libri trovati: ~w', [Libri]).

% Predicati per prendere in prestito un libro o restituire un libro
prendiInPrestito(Utente, Titolo, Autore) :-
    libro(Titolo, Autore, CopieDisponibili),
    CopieDisponibili > 0,
    retract(libro(Titolo, Autore, CopieDisponibili)),
    NuoveCopieDisponibili is CopieDisponibili - 1,
    assertz(libro(Titolo, Autore, NuoveCopieDisponibili)),
    (prestito(Utente, Titolo, VecchieCopiePrese) ->
        NuoveCopiePrese is VecchieCopiePrese + 1,
        retract(prestito(Utente, Titolo, VecchieCopiePrese)),
        assertz(prestito(Utente, Titolo, NuoveCopiePrese))
    ; assertz(prestito(Utente, Titolo, 1))).

restituisciLibro(Utente, Titolo, Autore) :-
    retract(prestito(Utente, Titolo, CopiePrese)),
    (
        CopiePrese > 1 ->
        NuoveCopiePrese is CopiePrese - 1,
        assertz(prestito(Utente, Titolo, NuoveCopiePrese))
    ;
        true
    ),
    libro(Titolo, Autore, CopieDisponibili),
    NuoveCopieDisponibili is CopieDisponibili + 1,
    retract(libro(Titolo, Autore, CopieDisponibili)),
    assertz(libro(Titolo, Autore, NuoveCopieDisponibili)).

% Predicato per controllare i prestiti di un utente
controllaPrestiti(Utente, Prestiti) :-
    findall((Titolo, Autore, CopiePrese), (
        prestito(Utente, Titolo, CopiePrese),
        libro(Titolo, Autore, _)
    ), Prestiti),
    Prestiti \= [].

% Predicato per l'interazione con l'utente
interazioneUtente :-
    write('Ciao! Inserisci il tuo nome utente: '),
    flush_output,
    read_line_to_string(user_input, Utente),
    menu(Utente).

menu(Utente) :-
    nl, write('Benvenuto, '), write(Utente), write('. Cosa desideri fare?'), nl,
    write('1. Cerca libri per titolo'), nl,
    write('2. Cerca libri per autore'), nl,
    write('3. Prendi in prestito un libro'), nl,
    write('4. Restituisci un libro'), nl,
    write('5. Controlla i prestiti'), nl,
    write('0. Esci'), nl,
    read_line_to_codes(user_input, SceltaCodes),
    atom_codes(SceltaAtom, SceltaCodes),
    atom_number(SceltaAtom, Scelta),
    eseguiScelta(Utente, Scelta).

eseguiScelta(Utente, 1) :-
    write('Inserisci il titolo del libro: '),
    flush_output,
    read_line_to_string(user_input, Titolo),
    atom_string(TitoloAtom, Titolo),
    cercaLibriPerTitolo(TitoloAtom, Libri),
    (Libri == [] ->
        write('Nessun libro trovato con questo titolo.'), nl
    ;
        stampaLibri(Libri)
    ),
    menu(Utente), !.

eseguiScelta(Utente, 2) :-
    write('Inserisci l\'autore del libro: '),
    flush_output,
    read_line_to_string(user_input, Autore),
    trim_whitespace(Autore, AutoreTrimmed),
    cercaLibriPerAutore(AutoreTrimmed, Libri),
    (Libri == [] ->
        write('Nessun libro trovato per questo autore.'), nl
    ;
        stampaLibri(Libri)
    ),
    menu(Utente), !.

eseguiScelta(Utente, 3) :-
    write('Inserisci il titolo del libro da prendere in prestito: '),
    flush_output(current_output),
    read_line_to_string(user_input, Titolo),
    atom_string(TitoloAtom, Titolo),
    write('Inserisci l\'autore del libro da prendere in prestito: '),
    flush_output(current_output),
    read_line_to_string(user_input, Autore),
    atom_string(AutoreAtom, Autore),
    prendiInPrestito(Utente, TitoloAtom, AutoreAtom),
    write('Libro preso in prestito con successo.'), nl,
    menu(Utente).

eseguiScelta(Utente, 4) :-
    write('Inserisci il titolo del libro da restituire: '),
    flush_output(current_output),
    read_line_to_string(user_input, Titolo),
    atom_string(TitoloAtom, Titolo),
    write('Inserisci l\'autore del libro da restituire: '),
    flush_output(current_output),
    read_line_to_string(user_input, Autore),
    atom_string(AutoreAtom, Autore),
    restituisciLibro(Utente, TitoloAtom, AutoreAtom),
    write('Libro restituito con successo.'), nl,
    menu(Utente).

eseguiScelta(Utente, 5) :-
    (controllaPrestiti(Utente, Prestiti) ->
        stampaPrestiti(Prestiti)
    ;
        write('Non hai alcun libro in prestito al momento.'), nl
    ),
    menu(Utente),
    !.

eseguiScelta(_, 0) :-
    write('Grazie per aver utilizzato il nostro servizio. Arrivederci!').

eseguiScelta(Utente, _) :-
    write('Scelta non valida. Riprova.'), nl,
    menu(Utente).

% Funzione stampaLibri
stampaLibri(Libri) :-
    (Libri == [] ->
        true
    ;
        stampaLibriDettagli(Libri)
    ).

stampaLibriDettagli([]).
stampaLibriDettagli([(Titolo, Autore, CopieDisponibili)|T]) :-
    write('Titolo: '), write(Titolo), nl,
    write('Autore: '), write(Autore), nl,
    write('Copie disponibili: '), write(CopieDisponibili), nl,
    stampaLibriDettagli(T).

stampaPrestiti(Prestiti) :-
    calcolaTotalePrestiti(Prestiti, 0, TotalePrestiti),
    write('Hai '), write(TotalePrestiti), write(' libro/i in prestito:'), nl,
    stampaPrestitiDettagli(Prestiti).

calcolaTotalePrestiti([], Totale, Totale).
calcolaTotalePrestiti([(_, _, CopiePrese)|T], Accumulatore, Totale) :-
    NuovoAccumulatore is Accumulatore + CopiePrese,
    calcolaTotalePrestiti(T, NuovoAccumulatore, Totale).

stampaPrestitiDettagli([(Titolo, Autore, CopiePrese)|T]) :-
    write('Titolo: '), write(Titolo), nl,
    write('Autore: '), write(Autore), nl,
    write('Copie prese in prestito: '), write(CopiePrese), nl,
    nl,
    stampaPrestitiDettagli(T).

:- initialization(interazioneUtente).

When I execute the project with SwiProlog it works, but with GNU Prolog:

GNU Prolog 1.4.5 (64 bits)
Compiled Jul 14 2018, 12:58:46 with cl
By Daniel Diaz
Copyright (C) 1999-2018 Daniel Diaz
compiling C:/Users/--/OneDrive/Desktop/progettobiblioteca.pl for byte code...
C:/Users/--/OneDrive/Desktop/progettobiblioteca.pl:1:12: syntax error: . or operator expected after expression
C:/Users/--/OneDrive/Desktop/progettobiblioteca.pl:2:12: syntax error: . or operator expected after expression
    2 error(s)
compilation failed
warning: command-line goal 'consult(`C:\\Users\\lallo\\OneDrive\\Desktop\\progettobiblioteca.pl`)' failed
| ?- 
false
  • 10,264
  • 13
  • 101
  • 209
Carlo27
  • 11
  • 1
  • 2
    Predicate `dynamic/1` is not declared as operator in GNU-Prolog, so parentheses must be used in directives; for example, write `:- dynamic(libro/3).` instead of `:- dynamic libro/3.`. Also, I think the predicate `read_line_to_string/2` is SWI-Prolog specific. – slago Jun 18 '23 at 01:04
  • Parenthesis solved the problem. And what is the similar gnu predicate of read_line_to_string/2? – Carlo27 Jun 18 '23 at 10:10
  • Please, see http://www.gprolog.org/manual/gprolog.html. – slago Jun 18 '23 at 14:27

1 Answers1

2

String is not a type in ISO Prolog. A string constant can be used (surrounded with double quotes) but it is converted as list of character codes, a list of characters or an atom (depending on the value of the double_quotes Prolog flag). In SWI Prolog, a string is a native type with specific built-in predicates to handle it.

Here is a possible implementation of read_line_to_chars/2 returning a list of characters (or the atom end_of_file when the stream is exhausted).

read_line_to_chars(Stream, Chars) :-
    get_char(Stream, Char),
    (   Char = end_of_file ->
        Chars = end_of_file
    ;
        read_line_to_chars1(Char, Stream, Chars1),
        Chars = Chars1
    ).


read_line_to_chars1(end_of_file, _, []) :-
    !.

read_line_to_chars1('\n', _, []) :-
    !.

read_line_to_chars1('\r', Stream, []) :-
    peek_char(Stream, '\n'), !,
    get_char(Stream, _).

read_line_to_chars1(Char, Stream, [Char|Chars]) :-
    get_char(Stream, Char1),
    read_line_to_chars1(Char1, Stream, Chars).

To handle both Unix and Windows end of line convention, the end of line is detected when \n or \r\n is read (i.e. \r is ignored only if followed by \n) or when EOF is reached.

It is easy to adapt the above code to provide a read_line_to_codes/2 returning a list of character codes replacing:

  • char by code (this includes get_char/2 -> get_code/2, peek_char/2 -> peek_code/2).
  • Charby Code
  • end_of_file (as first argument of read_line_to_chars1/2) by -1.
  • a character constant by its code: '\n' by 10 or 0'\n and '\r' by 13 or 0'\r.

It is then possible to define read_line_to_string/2, e.g.:

read_line_to_string(SIn, Chars) :-
    read_line_to_chars(SIn, Chars).
didou
  • 692
  • 3
  • 7
  • Why would you ever want to unify `Chars` with `end_of_file`? `Chars` is meant to be a list, nothing else. – brebs Jun 20 '23 at 06:03
  • I follow SWI-Prolog: at EOF, the atom `end_of_file` is returned (an empty list corresponds to an empty line). – didou Jun 20 '23 at 06:42
  • Ah OK, following https://www.swi-prolog.org/pldoc/man?predicate=read_line_to_string/2 - I suspect it could be made a bit neater by removing the `empty` vs `non-empty` arg (using more predicates, instead). – brebs Jun 20 '23 at 09:03
  • `read_line_to_chars1(end_of_file, _, empty, end_of_file)` should set Chars *after* the cut, to be *steadfast* - https://swi-prolog.discourse.group/t/why-steadfastness/3313 – brebs Jun 21 '23 at 08:07
  • A possible type for these `Chars` that are sometimes `end_of_file` could be `in_chars` – false Jul 08 '23 at 06:44