view release on metacpan or search on metacpan
Fixed bug where code was not warning on unknown predicates.
(Doug Wilson)
Fixed bug where tracing fails when it tries to print a non-
existent "next_clause". (Doug Wilson)
retract/1 now works correctly. (Doug Wilson)
Fixed unification bug that crept in in .62.
Added data/sleepy.pro, a game that wasn't working because of
the retract/1 bug.
0.7 Mon June 20, 2005 (Happy Birthday to me)
Parsing errors now attempt to report the line number of the
error.
Added new predicates:
consult/1
listing/1
ne/2
Updated the license information display for aiprolog shell.
Modified listing/0 to not show builtins.
Added math preprocessor. This transforms proper Prolog math
into internal predicates the parser recognizes:
X is N + 1. % is(X, plus(N, 1)).
lib/AI/Prolog.pm view on Meta::CPAN
owns(VICTIM,STUFF),
not(knows(PERP,VICTIM)).
thief(badguy).
valuable(gold).
valuable(rubies).
owns(merlyn,gold).
owns(ovid,rubies).
knows(badguy,merlyn).
END_PROLOG
Side note: in Prolog, programs are often referred to as databases.
=head2 Creating a query
To create a query for the database, use C<query>.
$prolog->query("steals(badguy,X).");
=head2 Running a query
Call the C<results> method and inspect the C<results> object:
lib/AI/Prolog.pm view on Meta::CPAN
are stabilized.
=item * Add "sugar" interface.
=item * Better docs.
=item * Tutorial.
=item * Data structure cookbook.
=item * Better error reporting.
=back
=head1 EXPORT
None by default. However, for convenience, you can choose ":all" functions to
be exported. That will provide you with C<Term>, C<Parser>, and C<Engine>
classes. This is not recommended and most support and documentation will now
target the C<AI::Prolog> interface.
lib/AI/Prolog/Article.pod view on Meta::CPAN
Create a new C<AI::Prolog> object, passing Prolog code as the argument. If you
prefer, you can wrap the constructor in a C<BEGIN> block:
my $prolog;
BEGIN {
$prolog = AI::Prolog->new(<<' END_PROLOG');
% some Prolog code goes here
END_PROLOG
}
This is not strictly necessary, but if your Prolog code has a syntax error, it
will be a compile-time error, not a run-time error, and you'll get an error
message similar to:
Unexpected character: (Expecting: ')'. Got (.)) at line number 12.
BEGIN failed--compilation aborted at test.pl line 7.
Note that the line number for "Unexpected character" is relative to the Prolog
code, not the Perl code.
After the contructor, issue your query:
lib/AI/Prolog/Article.pod view on Meta::CPAN
with Prolog. Further, there's one huge reason why one might choose Prolog:
recursion. Some SQL implementations have non-standard support for recursion,
but usually recursive procedures are simulated by multiple calls from the
programming language using SQL.
Let's take a look at recursion and see why this is a win.
=head2 Recursion and Lists
In Prolog, lists are not data structures. They're actually implemented in
terms of something referred to as the "dot functor" (./2). For our purposes,
we'll ignore this and pretend they're really data types. In fact, there's
going to be a lot of handwaving here so forgive me if you already know Prolog.
If you know LISP or Scheme, the following will be very familiar.
A list in Prolog is usually represented by a series of terms enclosed in square
brackets:
owns(alice, [bookcase, cats, dogs]).
A list consists of a head and a tail. The tail, in turn, is another list with
lib/AI/Prolog/Article.pod view on Meta::CPAN
=back
=head2 Books
=over 4
=item Programming in Prolog
http://portal.acm.org/citation.cfm?id=39071
This book is sometimes referred to simply as "Clocksin/Mellish". First
published in the early 80s, this is the book that brought Prolog into the
mainstream.
=item The Art of Prolog
htp://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=8327
This excellent MIT textbook is very in-depth and covers "proving" Prolog
programming, second-order logic, grammars, and many working examples.
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
write(Something). % prints whatever variable Something is bound to
=item writeln/1
Same as C<write(Term)>, but automatically prints a newline at the end.
=back
=head1 LIMITATIONS
These are known limitations that I am not terribly inclined to fix. See the
TODO list for those I am inclined to fix.
IF -> THEN; ELSE not allowed.
Use C<if(IF, THEN, ELSE)> instead.
Chaining terms with a semicolon for "or" does not work. Use C<or/2> instead.
=head1 TODO
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
X is 5 + 7.
% internally converted to
% is(X, plus(5, 7)).
The math predicates are officially deprecated and I<cannot> be used in the same
expression with regular Prolog math.
Number may be integers, floats, doubles, etc. A number that starts with a
minus sign (-) is considered negative. No number may end in a decimal point as
the period is interpreted as the end of a clause. The following is therefore a
syntax error:
X is 5. + 7.
Unfortunately, the parser doesn't yet yell about that. We'll try and figure
out why later.
Omit the period after the number or put a zero after it:
X is 5.0 + 7.
X is 5 + 7.
lib/AI/Prolog/Engine.pm view on Meta::CPAN
ne(X, Y) :- not(eq(X,Y)).
if(X,Y,Z) :- once(wprologtest(X,R)) , wprologcase(R,Y,Z).
wprologtest(X,yes) :- call(X). wprologtest(X,no).
wprologcase(yes,X,Y) :- call(X).
wprologcase(no,X,Y) :- call(Y).
not(X) :- if(X,fail,true).
or(X,Y) :- call(X).
or(X,Y) :- call(Y).
true.
% the following are handled internally. Don't use the
% := operator. Eventually, I'll make this a fatal error.
% See AI::Prolog::Engine::Builtins to see the code for these
! := 1.
call(X) := 2.
fail := 3.
consult(X) := 4.
assert(X) := 5.
retract(X) := 7.
retract(X) :- retract(X).
listing := 8.
listing(X) := 9.
lib/AI/Prolog/Engine.pm view on Meta::CPAN
}
else {
my @results = $self->_call->to_data;
return $self->raw_results
? $results[1]
: $results[0];
}
}
unless ( $self->{_goal} && $self->{_goal}{term} ) {
croak("Engine->run fatal error. goal->term is null!");
}
unless ( $self->{_goal}->{next_clause} ) {
my $predicate = $self->{_goal}{term}->predicate;
_warn("WARNING: undefined predicate ($predicate)\n");
next if $self->backtrack; # if we backtracked, try again
return; # otherwise, we failed
}
my $clause = $self->{_goal}->{next_clause};
if ( my $next_clause = $clause->{next_clause} ) {
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
&{'---'};
};
my $e = $@;
# Undefined subroutine &main::--- called at .../Primitives.pm line 12.
my ($msg) = $e =~ / \A
(.+) # 'Undefined subroutine'
(?<=\s) # ' '
\S* # &main::
---/mx
or die q[Perl's error message changed! Damn! Fix this regex.];
$msg;
};
$PRIMITIVES[34] = sub { # perlcall2/2
my ( $self, $term ) = @_;
# Get a function name...
my $function_term = $term->getarg(0);
if ( not $function_term->is_bound ) {
lib/AI/Prolog/Parser.pm view on Meta::CPAN
my $string = '___' . $ANON++;
$self->advance;
my $term = $self->{_vardict}{$string};
unless ($term) {
$term = Term->new( $self->{_varnum}++ ); # XXX wrong _varnum?
$self->{_vardict}{$string} = $term;
}
return ( $term, $string );
}
# handle errors in one place
sub parseerror {
my ( $self, $character ) = @_;
my $linenum = $self->linenum;
croak "Unexpected character: ($character) at line number $linenum";
}
# skips whitespace and prolog comments
sub skipspace {
my $self = shift;
$self->advance while $self->current =~ /[[:space:]]/;
_skipcomment($self);
lib/AI/Prolog/Parser.pm view on Meta::CPAN
my $self = shift;
if ( $self->current eq '%' ) {
while ( $self->current ne "\n" && $self->current ne "#" ) {
$self->advance;
}
$self->skipspace;
}
if ( $self->current eq "/" ) {
$self->advance;
if ( $self->current ne "*" ) {
$self->parseerror("Expecting '*' after '/'");
}
$self->advance;
while ( $self->current ne "*" && $self->current ne "#" ) {
$self->advance;
}
$self->advance;
if ( $self->current ne "/" ) {
$self->parseerror("Expecting terminating '/' on comment");
}
$self->advance;
$self->skipspace;
}
}
# reset the variable dictionary
sub nextclause {
my $self = shift;
$self->{_vardict} = {};
lib/AI/Prolog/Parser.pm view on Meta::CPAN
# we're parsing a primitive
$self->advance;
$self->skipspace;
my $id = $self->getnum;
$self->skipspace;
$termlist->{term} = $ts[0];
$termlist->{next} = Primitive->new($id);
}
elsif ( $self->current ne '-' ) {
$self->parseerror("Expected '-' after ':'");
}
else {
$self->advance;
$self->skipspace;
push @ts => $self->_term;
$self->skipspace;
while ( $self->current eq ',' ) {
$self->advance;
lib/AI/Prolog/Parser.pm view on Meta::CPAN
$termlist->{term} = $ts[0];
$termlist->{next} = $tsl[1];
}
}
else {
$termlist->{term} = $ts[0];
$termlist->{next} = undef;
}
if ( $self->current ne '.' ) {
$self->parseerror("Expected '.' Got '@{[$self->current]}'");
}
$self->advance;
return $termlist;
}
# This constructor is the simplest way to construct a term. The term is given
# in standard notation.
# Example: my $term = Term->new(Parser->new("p(1,a(X,b))"));
sub _term {
my ($self) = @_;
lib/AI/Prolog/Parser.pm view on Meta::CPAN
$self->skipspace;
while ( ',' eq $self->current ) {
$self->advance;
$self->skipspace;
$ts->[ $i++ ] = $self->_term;
$self->skipspace;
}
if ( ')' ne $self->current ) {
$self->parseerror(
"Expecting: ')'. Got (@{[$self->current]})");
}
$self->advance;
$term->{args} = [];
$term->{args}[$_] = $ts->[$_] for 0 .. ( $i - 1 );
$term->{arity} = $i;
}
else {
lib/AI/Prolog/Parser.pm view on Meta::CPAN
$self->advance;
$self->skipspace;
$ts->[ $i++ ] = $self->_term;
$self->skipspace;
}
else {
$ts->[ $i++ ] = $term->new( NULL, 0 );
}
if ( ']' ne $self->current ) {
$self->parseerror("Expecting ']'");
}
$self->advance;
$term->{bound} = 1;
$term->{deref} = 0;
$term->{functor} = "cons";
$term->{arity} = 2;
$term->{args} = [];
for my $j ( reverse 1 .. $i - 2 ) {
my $term = $term->new( "cons", 2 );
lib/AI/Prolog/Parser.pm view on Meta::CPAN
}
$term->{args}[0] = $ts->[0];
$term->{args}[1] = $ts->[1];
}
}
elsif ( '!' eq $self->current ) {
$self->advance;
return $term->CUT;
}
else {
$self->parseerror(
"Term should begin with a letter, a digit, or '[', not a @{[$self->current]}"
);
}
return $term;
}
1;
__END__
lib/AI/Prolog/Parser/PreProcessor/Math.pm view on Meta::CPAN
next unless $token;
if ( "(" eq _as_string($token) ) {
$first = $i;
}
if ( ")" eq _as_string($token) ) {
unless ( defined $first ) {
# XXX I should probably cache the string and show it.
# XXX But it doesn't matter because that shouldn't happen here
croak(
"Parse error in math pre-processor. Mismatched parens"
);
}
$last = $i;
$tokens->[$first] = $class->_parse_group(
[ @{$tokens}[ $first + 1 .. $last - 1 ] ] );
undef $tokens->[$_] for $first + 1 .. $last;
@$tokens = grep $_ => @$tokens;
undef $first;
undef $last;
redo REDUCE;
lib/AI/Prolog/Term.pm view on Meta::CPAN
}
# unbinds a term -- i.e., resets it to a variable
sub unbind {
my $self = shift;
$self->{bound} = 0;
$self->{ref} = undef;
# XXX Now possible for a bind to have had no effect so ignore safety test
# XXX if (bound) bound = false;
# XXX else IO.error("Term.unbind","Can't unbind var!");
}
# set specific arguments. A primitive way of constructing terms is to
# create them with Term(s,f) and then build up the arguments. Using the
# parser is much simpler
sub setarg {
my ( $self, $pos, $val ) = @_;
if ( $self->{bound} && !$self->{deref} ) {
$self->{args}[$pos] = $val;
}
can_ok $CLASS, 'occurcheck';
is $CLASS->occurcheck, 0, '... and it should return a false value';
$CLASS->occurcheck(1);
is $CLASS->occurcheck, 1, '... but we should be able to set it to a true value';
can_ok $CLASS, 'new';
eval { $CLASS->new(1,2,3,4) };
ok $@, 'Calling new with arguments it does not expect should croak()';
like $@, qr/Unknown arguments to Term->new/,
'... with an appropriate error message';
# new, unbound term
ok my $term = $CLASS->new, 'Calling it without arguments should succeed';
isa_ok $term, $CLASS, '... and the object it returns';
#diag $term->to_string;
my $term2 = $term->refresh([undef, $term]);
#diag $term2->to_string;
can_ok $term, 'functor';
t/40parser.t view on Meta::CPAN
$parser = $CLASS->new(' /* comment */ p(x)');
$parser->skipspace;
is $parser->current, 'p',
'skipspace() should ignore multiline comments';
is $parser->_start, 0, '... and it will not change the starting position';
is $parser->_posn, 16, '... but the position will indicate the new position';
eval {$CLASS->new('/* this is an unterminated comment')->skipspace};
ok $@, 'skipspace() should die if it encounters a comment with no end';
like $@, qr{Expecting terminating '/' on comment},
'... with an appropriate error message';
eval {$CLASS->new('/ * this is an unterminated comment')->skipspace};
ok $@, 'skipspace() should die if it encounters a poorly formed comment';
like $@, qr{Expecting '\*' after '/'},
'... with an appropriate error message';
$parser = $CLASS->new(<<'END_PROLOG');
% this is a comment
flies(pig, 789).
END_PROLOG
$parser->skipspace;
is $parser->current, 'f',
'skipspace() should ignore single line comments';
is $parser->_start, 0, '... and it will not change the starting position';
is $parser->_posn, 28, '... but the position will indicate the new position';