Problem when reading backslash in Prolog - prolog

I'm writing a lexer in Prolog which will be used as a part of functional language interpreter. Language spec allows expressions like for example let \x = x + 2; to occur. What I want lexer to do for such input is to "return":
[tokLet, tokLambda, tokVar(x), tokEq, tokVar(x), tokPlus, tokNumber(2), tokSColon]
and the problem is, that Prolog seems to ignore the \ character and "returns" the line written above except for tokLambda.
One approach to solve this would be to somehow add second backslash before/after every occurrence of one in the program code (because everything works fine if I change the original input to let \\x = x + 2;) but I don't really like it.
Any ideas?
EDIT:
If anyone should have similar problems, that's how I solved it:
main(File) :-
open(File,read,Stream),
read_stream_to_codes(Stream, Codes),
lexer(X,Codes,[]),
... invoke other methods

Where did you get the string let \x = x + 2; from?
If it is in your Prolog program: yes, you have to double the backslashes.
If it is from an external file: How do you read it from there? Maybe that predicate is interpreting the backslash specially.
I got inspired by that problem and wrote a bit of code, which should be portable to all Prolog implementations:
% readline(-Line)
%
% Reads one line from the current input. The line is then returned as a list
% of one-character atoms, excluding the newline character.
% The returned line doesn't tell you whether the end of input has been reached
% or not.
readline(Line) :-
'readline:read'([], Reversed),
reverse(Line, Reversed).
'readline:read'(Current, Out) :-
get_char(C), 'readline:append'(C, Current, Out).
'readline:append'(-1, Current, Current) :- !.
'readline:append'('\n', Current, Current) :- !.
'readline:append'(C, Current, Line) :-
'readline:read'([C | Current], Line).
I tried it, and it worked for me.
Of course, as explained in question 1846199, you can also use read_line_to_codes/2.

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.

Alternative to string_upper/2

Machine, which my code is supposed to work on, for some reason doesn't know predicate string_upper/2. Are there any alternatives? Eventually, what would code for this predicate look like?
The example you gave in the comments is actually not a string but a pair of atoms. Since you already opted to use upcase_atom/2, in the code you deleted from your post, I'd point out that this built-in is only working in one direction, that is if the the first argument is atomic. Consider the following queries:
?- upcase_atom(bo,'BO'). % Is 'BO' the uppercase version of bo?
true. % Yes, it is.
?- upcase_atom(bo,B). % What's the uppercase version of bo?
B = 'BO'. % It's 'BO'
?- upcase_atom(B,'BO'). % What atom has 'BO' as uppercase version?
ERROR: upcase_atom/2: Arguments are not sufficiently instantiated
?- upcase_atom(B,1). % What atom has 1 as uppercase version?
ERROR: upcase_atom/2: Arguments are not sufficiently instantiated
Your example query, from your deleted code, is ?- divideIt(a-b).. The atom a is unified with the variable V in the head of the rule divideIt/1. Then Prolog calls the first goal upcase_atom(V,ve) that has to fail because the atom ve is not the uppercase version of the atom a. Consequently your example query fails as well. If you apply the following minor changes to your predicate, it yields the desired result:
divideIt(V-W) :-
upcase_atom(V,Ve), % <- Ve (=variable) instead of ve (=atom)
write(Ve),
write('-'), % <- '-' instead of "-"
upcase_atom(W,We), % <- We (=variable) instead of we (=atom)
write(We).
?- divideIt(a-b).
A-B
true.
Break the string into a list of character codes. Then convert those codes into the appropriate upper case code if necessary before reconstructing the string. A more complete answer would require knowing which prolog you're using, its available predicates & how it represents strings.
You might want to read up on different Prolog's handling of characters, Unicode and strings. For this, the SWI-Prolog implementation has some good documentation:
How traditional(Edinburgh), ISO & SWI prolog handles characters: 4.2 Character Representation
How ISO & SWI prologs support Unicode 2.16.1.8 Unicode Prolog source
How SWI has a specific string-type object which uses quotes to denote them: 5.2 The string type and its double quoted syntax

Check if any element in List begins with a specific character

I've a list of words, for example [cola,fanta,pepsi] and I want to write a predicate that checks if any of the elements begins with the character specified.
My code so far is as follows:
chk_first_letter(Char,[]):-fail.
chk_first_letter(Char, [H|T]):-
perform_check(Char, H);
chk_first_letter(Char, T).
perform_check(Char,[First|_]):-memberchk(Char, First).
However consulting my file and calling chk_first_letter(p,[cola,fanta,pepsi]) gives me no even if pepsi begins with a p.
I've tried with Char==First instead of memberchk(Char,First) but it didn't work either. I'm not sure about the difference.
You have a list of atoms, and your perform_check/2 compares two atoms. An atom is not a list of characters! You need to use atom processing, for example:
perform_check(First, Word) :-
sub_atom(Word, 0, 1, _After, First).
http://gprolog.univ-paris1.fr/manual/html_node/gprolog043.html#sec200
There are a bunch of other built-ins in this section that could be used, for example for breaking the atom into characters or character codes (atom_chars/2 and atom_codes/2). But what sub_atom/5 also allows you to do easily:
prefixes of any length:
sub_atom(Word, 0, _Length, _After, Prefix).
suffixes:
sub_atom(Word, _Before, _Length, 0, Suffix).
First attempt:
chk_first_letter(Char, Atoms) :- member(A, Atoms), atom_chars(A, [Char|_]).
atom_chars/2 it's an ISO predicate.
Your code it's almost working, can be simplified this way:
chk_first_letter(Char, [H|T]):-
atom_chars(H, [Char|_]);
chk_first_letter(Char, T).
memberchk expects to be called with a list as the second argument. In your case, you're providing it with a single character.
And then you can probably do away with it altogether by taking advantage of unification:
perform_check(Char,[Char|_]).
This assumes your string type is a list of characters (whatever the character format). If you intend to operate on atoms directly, you could do it this way instead:
perform_check(Char,String) :- atom_concat(Char,_,String)
There would be a few more steps to make your code more idiomatic, but this seems to be the actual wrong part of it.

Prolog character stream error

My code is supposed to stop when it finds stop in the file its reading from, but its not. I keep getting an error:
% reads in a character and then checks whether this character is a blank,
% a carriage return or the end of the stream. In any of these cases a
% complete word has been read otherwise the next character is read.
calculate([stop],_) :- !.
calculate([],_):-!.
calculate([Word|Rest],X) :-
word_to_number(Word,Symbol),
concat(X,Symbol,NewX),
calculate(Rest,NewX),
atom_to_term(NewX,Eq,[]),
print('Calculating '),print(NewX),print(' The result is: '),
Result is Eq,
print(Result),nl,
execute.
Any help would be greatly appreciated!
Declare "plus", "minus" and "times" as operators, and you can use read/1 to read Prolog terms directly, since the input is then valid Prolog syntax.
The problem is that calculate is recursive. At some point, calculate([one], '03+') called, which in turn calls calculate([], '03+1'), which gives a result (4). It then invokes execute and processes the rest of the input.
Then, the calling calculate succeeds, and now goes on to applying atom_to_term to '03+', which gives the error.
You can fix this by moving the conversion to an atom into a separate predicate:
to_atom([Word], Symbol) :- word_to_number(Word, Symbol).
to_atom([Word|Rest], Term) :-
word_to_number(Word,Symbol),
to_atom(Rest, Symbol2),
concat(Symbol,Symbol2,Term).
...
calculate(List) :-
to_atom(List, NewX),
atom_to_term(NewX,Eq,[]),
...
Then you won't need the dummy 0 in the beginning, either.

Translate Prolog Words

I'm working on this this wonderful Prolog project and I'm stuck at this situation where I need to translate certain words into other words (e.g "i" into "you". "my into "your")
This is what I've done and I'm pretty sure it's kinda iffy. I enter the sentence and when It goes to convert it only changes the one word then goes on wacky on me. (e.g. "i feel happy" changes to "you" then it crashes.)
translate([]).
translate([H|T], [NewH|NewT]):-
means(H,NewH);
spit(T,NewT).
means(i,you).
means(my,your).
means(mine,yours).
Here's the fix:
translate([], []).
translate([H|T], [NewH|NewT]):-
means(H, NewH),
translate(T,NewT).
means(i, you) :- !.
means(my, your) :- !.
means(mine, yours) :- !.
means(X, X).
The changes are as follows:
I fixed the missing parameter to the first definition of translate (it's considered a completely independent function if the number of parameters don't match).
I'm calling translate on the rest of the list when the first item is translated.
I'm asserting that every word means itself unless specified otherwise. The :- ! part means if you match this, don't try the rest (this is to avoid lists always matching with themselves).
Example usage:
?- translate([this, is, i], X).
X = [this, is, you].

Resources