input :-
read_line_to_codes(user_input, Input),
string_to_atom(Input,Atoms),
atomic_list_concat(Alist, \' \', Atoms),
phrase(sentence(S), Alist),
action(S).
statement(
First, what Prolog responds with is really an implementation detail. SWI responds "true," but some other implementations respond "ok." There's nothing to change about your code because of that, as long as you're getting affirmatives and negatives when you should.
Second, there is a difference between your dog and ham examples. Look at the database after your sample inputs have been processed:
?- listing.
⋮
:- dynamic dog/1.
dog(john).
⋮
Where's ham/1
? Nowhere. When you asserta(dog(john))
, Prolog becomes aware that dog/1
is a predicate, but that never happened with ham/1
. So you have to decide if that query is well-formed, in which case you want to catch the exception or else pre-declare all your possible predicates with dynamic/1
, or be happy that it isn't well-formed. Your use scenario will determine which is appropriate. For instance, you could just do this:
?- [user].
|: :- dynamic ham/1.
|: % user://2 compiled 0.00 sec, 1 clauses
true.
?- input.
|: Is john a ham
false.
I doubt you'll want to do that for everything, so you'll probably want to look at SWI-Prolog's catch facility. See the edit at the bottom of this answer for an example of how to handle it.
Also, I would probably rework the DCG a little bit to make it a little more general and simpler:
article --> [a].
article --> [the].
article --> [].
noun(Noun) --> article, [Noun].
statement(Rule) --> noun(Noun), [is], noun(Object), { Rule =.. [Object, Noun] }.
query(Fact) --> ['Is'], noun(Noun), noun(Object), { Fact =.. [Object, Noun]}.
sentence(statement(S)) --> statement(S).
sentence(query(Q)) --> query(Q).
action(statement(S)) :- asserta(S).
action(query(Q)) :- Q.
Also, there's no real need to make statement1
. You can have a DCG rule with more than one body; even if you did need two bodies, you could have them both generate statement/1
structures to match in action/1
; you certainly don't need to propagate statement1/1
down that far into the rest of your code.
Last remark, you don't need to quote lowercase atoms. :)
Overall I think you're doing a pretty good job here! This is hard stuff to learn, and the material online is pretty sparse. I hope you keep at it, you're probably starting to see the kinds of cool things you can do with Prolog and DCGs!
Edit: You can catch the error and handle it properly by replacing your last action/1
clause with this:
action(query(Q)) :-
catch((Q -> write(yes) ; write(unknown)),
error(existence_error(procedure, _), _),
write(unknown)),
nl.