Reading a string (from a file) in Prolog - prolog

I have written a lexer and a parser in Prolog. It unifies a string with its AST. This is part for a compiler/interpreter project I am working on. Naturally, I now want to read the string from a file to parse it. However, the predicates I have found for this is read, and it only reads Prolog atoms and predicates, like files with
hello.
I have been twiddling with the double_quotes settings, but with no success.
I want to be able to read a file with something like this
let id = \x.x in id (S (S Z))
and then send this string to the parsing predicates.

You can use read_line_to_codes/2 or read_line_to_codes/3. An example program which reads individual lines from stdin and prints them to stdout is the following:
read_lines([H|T]) :-
read_line_to_codes(user_input, H), H \= end_of_file, read_lines(T).
read_lines([]).
write_lines([]).
write_lines([H|T]) :-
writef("%s\n", [H]), write_lines(T).
main :-
read_lines(X), write_lines(X).
(This uses writef/2 for printing.) There are also read_stream_to_codes/2 and read_stream_to_codes/3, which are not concerned with lines. The following code prints all input from stdin in one go to stdout:
main :-
read_stream_to_codes(user_input, X), writef("%s", [X]).
Of course it's also possible to read from a file instead of stdin. For more, see the readutil library.

Related

Reading from textfile in prolog

I have some trouble with reading from text files in GNU Prolog. I want to read a .txt file and move the file to a list. I have tried to follow some previous examples on stackoverflow to read the file, but have not been able to access the file (it seems). Here is what I've got so far:
readFile:-
open('text.txt', read, File),
read_lines(File, Lines),
close(File),
write(Lines), nl.
read_lines(File,[]):-
at_end_of_stream(File).
read_lines(File,[X|L]):-
\+ at_end_of_stream(File),
read(File,X),
read_lines(File,L).
When I try to call: ?- readFile. gives me an errormessage: uncaught exception: error(syntax_error('text.txt:2 (char:1) . or operator expected after expression'),read/2).
Thanks in advance!
Edit:
As provided by David, GNU Prolog's Character input/output library and get_char worked for me!
Working code:
readFile:-
open('text.txt', read, File),
read_lines(File, Lines),
close(File),
write(Lines), nl.
read_lines(File,[]):-
at_end_of_stream(File).
read_lines(File,[X|L]):-
\+ at_end_of_stream(File),
get_char(File,X),
read_lines(File,L).
You can improve on your edit answer a bit. First note that the read_line/2 predicate name is misleading. You're reading a text file to a list of characters, not to a list of individual lines. You're also calling the at_end_of_stream/1 predicate twice and creating a spurious choice-point at each call to your read_lines /2 predicate. Moreover, the at_end_of_stream/1 predicate, albeit a standard predicate, doesn't have a reliable implementation in all Prolog systems. A possible rewrite of your code is:
read_file(File, Chars) :-
open(File, read, Stream),
get_char(Stream, Char),
read_file(Stream, Char, Chars),
close(Stream).
read_file(Stream, Char, Chars) :-
( Char == end_of_file ->
Chars = []
; Chars = [Char| Rest],
get_char(Stream, Next),
read_file(Stream, Next, Rest)
).
Now that your intent is clearer, the same functionality can be accomplished using the Logtalk reader library with a single call:
reader::file_to_chars(File, Chars)
But, if learning is is your primary goal, writing your own solutions instead of relying on existing libraries is a good choice.

Prolog - Write out facts and reading a users input

I am quite new to Prolog and have had some trouble understanding it.
I have some facts named 'problem' I wish to first print out these facts to the user and then ask them to input a value, this value is then read and used later.
From my understanding thus far, it would be best to use a forall to print out these facts and then use read to read the value inputted, but I am having some issue implementing this. Here is what I have so far, any explanation would be appreciated
My question: How do I read in the input from the user regarding the problem and apply that into a variable for later use?
tellMeYourProblem:-
forall(problem(P),
writeln(P)),
answer = read(X),
problem('1').
problem('2').
problem('3').
problem('4').
problem('5').
problem('6').
problem('7').
problem('8').
problem('9').
problem('10').
Note: This answer uses SWI-Prolog.
How do I read in the input from the user regarding the problem?
You are doing that already with read(X), however read/1 reads terms (terms end with periods) and you probably want to read characters. If you are using SWI-Prolog take a look at Primitive character I/O for reading characters and Predicates that operate on strings for reading strings.
How do I apply that into a variable for later use?
When doing basic I/O with a user at a text level, a REPL is a good way to start. Adding a REPL is a bit more complicated so I will give you the code.
tellMeYourProblem :-
output_problems,
read_input.
output_problems :-
forall(problem(P),
writeln(P)).
read_input :-
repeat,
read_string(user_input, "\n", "\r\t ", _, Line),
process_input(Line).
process_input(Line) :-
string(Line),
atom_number(Line, N),
integer(N),
do_something_with(Line),
fail.
process_input("quit") :-
write('Finished'), nl,
!, true.
do_something_with(X) :-
writeln(X).
problem('1').
problem('2').
problem('3').
problem('4').
problem('5').
problem('6').
problem('7').
problem('8').
problem('9').
problem('10').
Also with Prolog, the style is to use snake casing so tellMeYourProblem should be tell_me_your_problem.
Normally in Prolog a REPL is done with ->/2, (Read Input till quit statement Prolog) , but I changed this to add more guard statements so that the exit condition would work, e.g.
string(Line),
atom_number(Line, N),
integer(N)
or putting the guard in the head, e.g.
process_input("quit")
When doing I/O to a screen and keyboard, the thought is to use stdIn and stdOut but for the keyboard SWI-Prolog uses user_input instead. See: Input and output
After all of the boiler plate code for the REPL is the next part you seek which is to do something with the input value, in this case just print it out.
do_something_with(X) :-
writeln(X).
The easiest to write out the facts of problem/1,
is to use the builtin listing/[0,1]. This builtin
accepts a so called predicate indicator. You can
write out the facts via:
?- listing(problem/1).
The predicate is supported by many Prolog systems
such as GNU Prolog, etc.. For how to read input see
for example the post by Guy Coder.

Arguments not sufficiently instantiated when consulting file

I'm running SWI-Prolog on a Mac through the Terminal. I'm trying to access an Atom file by writing the usual after opening up swipl in the terminal:
?- [hwk1-my_name].
Instead of swipl having the knowledge base to play with, it's giving me this:
ERROR: Arguments are not sufficiently instantiated
I'm new to Prolog, and my program as it stands now is simply the copied-and-pasted code provided by my professor to get the assignment started. Does this mean that the error is likely due to something within the code below, and if so, what is prompting this? Here is the code provided to me:
father(Dad, Child) :-
parent(Dad, Child),
male(Dad).
mother(Mom, Child) :-
parent(Mom, Child),
female(Mom).
had_a_child(Man, Woman) :-
father(Man, Child),
mother(Woman, Child).
sibling(Sibling1, Sibling2) :-
parent(Parent, Sibling1),
parent(Parent, Sibling2),
Sibling1 \= Sibling2.
brother(Brother, Sib) :-
sibling(Brother, Sib),
male(Brother).
sister(Sister, Sib) :-
sibling(Sister, Sib),
female(Sister).
Your obvious problem is the - inside the file name. The text editor you are using is completely irrelevant. Even confusing, as one of Prolog's data types is the atom.
You have two options:
Use file names that would be valid Prolog atoms even without quoting. This means that they cannot start with a capital or a digit, and can contain only letters, digits, and underscores (_). Then, your file can still have the .pl extension and you can consult it like you do: foo.pl ---> ?- [foo].
Use the complete filename, extension included, and put single quotes around it: foo-bar.baz ---> ?- ['foo-bar.baz'].. As you will see, you don't even need the .pl extension any more.
Whenever you are in doubt about what Prolog sees, you can try write_canonical/1:
?- write_canonical(hwk1-my_name).
-(hwk1, my_name)
true.
In other words, Prolog takes this as the compound term -/2 with the atoms hwk1 and my_name as the first and second argument.

Converting Terms to Atoms preserving variable names in YAP prolog

Is there a way to configure YAP (and/or SWI prolog) so they will preserve variable names in any call to term_to_atom/2 ?.
For example, when I execute this:
term_to_atom(member(X, [1,2]), A).
I obtain this answer:
A = 'member(_131405,[1,2])'
Where X has been replaced by its internal representation.
However, I would like to get this answer instead:
A = 'member(X,[1,2])'
Thanks for any help!
There are two issues involved. How to get the variable name X into the system, and how to get a term with such a variable into the atom.
The X you type in is read by the top level which converts it to a regular variable which does not have a name associated. Let's see that in YAP:
?- read(Term).
|: X+3*Y+X.
Term = _A+3*_B+_A
The |: is YAP's prompt for input. And we have entered X+3*Y+X. However, the variable Term contains _A and _B (names chosen by the top level) in place of X and Y. So the information is lost and cannot be restored once it is read by read/1.
You have to access that information differently with the more general built-in for reading read_term/2,3 and the option variable_names/1.
?- read_term(T,[variable_names(Eqs)]).
|: X+3*Y+X.
Eqs = ['X'=_A,'Y'=_B],
T = _A+3*_B+_A
So the read option variable_names/1 gives you the information to restore the variable names. For each named variable read by read_term/2 there is a structure Name = Variable where Name is an atom representing the variable name. Above, 'X' is the name capital X.
Anonymous variables, that is variables whose name is _, do not occur in the list of variable names. They can be rapidly extracted like so:
?- read_term(T,[variable_names(Eqs)]),
term_variables(Eqs, Named),
term_variables(Named+T, Vars),
append(Named, Anons, Vars).
So much for the reading.
Now for the writing. We cannot write the term directly but have to accompany it with the list Eqs. Let's call the new predicate term_to_atom(Term, Eqs, Atom). In both YAP and SWI there is with_output_to(Output, Goal) which writes the output of Goal to different destinations like atom(A). So you can now use write_term/2 to write the term as you please. An example:
?- with_output_to(atom(A),write_term('a b'+X,[quoted(true)])).
A = '\'a b\'+_131284'.
The variable _131284 looks very ugly. To get variables associated with their names for printing we can implement term_to_atom/3 as follows:
term_to_atom(T, Eqs, A) :-
with_output_to(atom(A), write_term(T,[variable_names(Eqs),quoted(true)]) ).
And use it like so:
?- read_term(T,[variable_names(Eqs)]), term_to_atom(T, Eqs, Atom).
|: X+3*Y+X.
Atom = 'X+3*Y+X',
Eqs = ['X'=_A,'Y'=_B],
T = _A+3*_B+_A
variable_names/1 exists as a write option in ISO, Minerva, Jekejeke, GNU, B, SWI, YAP, and SICStus.
In SICStus, the originator of writing terms to lists, one writes:
:- use_module(library(codesio)).
term_to_atom(T, Eqs, Atom) :-
write_term_to_codes(T, Codes, [variable_names(Eqs),quoted(true)]),
atom_codes(Atom, Codes).
The following was an ISO incompatible work around for YAP prior to 6.3.4. It is no longer necessary. As for the differences to a separate write option: term_to_atom/3 as defined below interferes with constraints and does not correctly render '$VAR'/1.
But for the moment we can only approximate the ideal option
variable_names/1. To print terms with our own variable names,
variables have to be substituted in YAP by '$VAR'(Codes) where
Codes is a list of character codes. This does not do exactly the
same, but it is very close. This goes into a file:
:- use_module(library(apply)).
:- use_module(library(lambda)).
write_eqs_term(T, Eqs) :-
\+ \+ (
maplist(\Eq^( Eq = (N='$VAR'(Chs)), atom_codes(N,Chs)), Eqs),
write_term(T,[numbervars(true),quoted(true)])
).
term_to_atom(T, Eqs, A) :-
with_output_to(atom(A), write_eqs_term(T, Eqs) ).
For SWI, you would have to replace atom_codes(N,Chs) by N = Ch.
and install library(lambda) first. It's pre-installed in YAP.

Prolog print statement does not work as expected

I was trying to define a functor and print each individual items of list in Prolog, but Prolog is not printing in correct format.
rint(L):-
write(H).
the output is like
rint([a, s,v ,c]).
_L139
true.
This is what I expect to achieve by calling the functor, any help or thought is appreciated, I'm new to Prolog and learning it.
?- rint([a,b,c,d]).
.(a, .(b, .(c, .(d, []))))
I think it should be
rint(L) :- write(L).
Also if you want .(a, .(b, .(c, .(d, [])))) and not [a, b, c, d] in output, use display:
rint(L) :- display(L).
The problem is an error in your rule for rint.
Your definition says that rint(L) succeeds if write(H) succeeds. At that point, the interpreter knows nothing about H. So it writes a value it doesn't know, which is why you see the _L139, the internal representation of an uninitialised variable.
Having done that, write(H) has succeed, is true, so rint(L) is true. The interpreter tells you that: true.
To define your own rint/1 without relying on built-ins such as display/1, you would need to do something like
rint([]) :-
write([]).
rint([H|T]) :-
write('.('),
write(H),
write(', '),
rint(T),
write(')').
If you're trying to display an empty list, just write it. If you're trying to display any other list, write the opening period and parenthesis, write the Head, write the following comma and space, then call itself for the Tail of the list, then write the closing parenthesis.

Resources