package Language::Prolog::Types::Abstract; our $VERSION = '0.10'; =head1 NAME Language::Prolog::Types::Abstract - Abstract classes for Prolog terms in Perl. =head1 SYNOPSIS use Language::Prolog::Types::Abstract; if prolog_is_atom('hello') { print "'hello' is a Prolog atom\n" } ... etc. =head1 ABSTRACT Language::Prolog::Types::Abstract defines a set of abstract classes for Prolog terms. It also includes functions to check for Prolog types and some utility functions to perform explicit conversion between Prolog and Perl. =head1 DESCRIPTION This module define abstract classes for the usual Prolog functors, lists, variables and nil. Atoms are not included because perl scalars do the work. Perl C is equivalent to Prolog nil (C<[]>), although a different representation is allowed for the Prolog term. Perl lists can be directly used as Prolog lists. The inverse is not always true and depends of the implementations used. =head2 EXPORT =over 4 =cut use strict; use warnings; use Carp; require Exporter; our @ISA=qw(Exporter); our @EXPORT=qw( prolog_is_term prolog_is_atom prolog_is_nil prolog_is_functor prolog_is_list prolog_is_list_or_nil prolog_is_variable prolog_is_var prolog_is_ulist prolog_list2perl_list ); # prolog_is functions: # basic perl types ($ and @) are automatically coerced. =item C returns true if C<$term> is a valid Prolog term (actually a perl number, string or array or any object descending from L). =cut sub prolog_is_term ($ ) { !ref($_[0]) or ref($_[0]) eq 'ARRAY' or UNIVERSAL::isa('Language::Prolog::Types::Term',$_[0]) } =item C returns true if C<$term> is a valid Prolog atom (actually a perl number or string). =cut sub prolog_is_atom ($ ) { defined($_[0]) and !ref($_[0]) } =item C returns true if C<$term> is Prolog nil value (C<[]>). =cut sub prolog_is_nil ($ ) { my $self=shift; !defined($self) or UNIVERSAL::isa($self, 'Language::Prolog::Types::Nil') or (ref($self) eq 'ARRAY' and @$self==0) } =item C returns true if $term is a Prolog functor. It should be noted that lists are equivalent to functor '.'/2 and because of that, this function will also return true when $term is a list. =cut sub prolog_is_functor ($ ) { my $self=shift; UNIVERSAL::isa($self, 'Language::Prolog::Types::Functor') or (ref($self) eq 'ARRAY' and @{$self}>0) } =item C returns true if C<$term> is a Prolog list. It should be noted that although Prolog nil is usually represented as the empty list C<[]>, it is not really a Prolog list and this function will return false for it. =cut sub prolog_is_list ($ ) { my $self=shift; UNIVERSAL::isa($self, 'Language::Prolog::Types::List') or (ref($self) eq 'ARRAY' and @$self>0); } =item C returns true if C<$term> is Prolog nil or a list. =cut sub prolog_is_list_or_nil ($ ) { my $self=shift; !defined($self) or UNIVERSAL::isa($self, 'Language::Prolog::Types::ListOrNil') or ref($self) eq 'ARRAY' } =item C =item C return true if C<$term> is a free (unbounded) Prolog variable =cut sub prolog_is_variable ($ ) { UNIVERSAL::isa(shift, 'Language::Prolog::Types::Variable'); } *prolog_is_var=\&prolog_is_variable; =item C returns true if $term is an unfinished Prolog list, that means, one with doesn't end in nil. i.e. difference lists. =cut sub prolog_is_ulist ($ ) { UNIVERSAL::isa(shift, 'Language::Prolog::Types::UList'); } # util functorions: =item C converts a Prolog list or nil to a Perl array. =cut sub prolog_list2perl_list { my $self=shift; return () if !defined($self); return @{$self} if ref($self) eq 'ARRAY'; my @result=eval { $self->largs }; croak "object '$self' is not a valid Prolog list" if $@; @result; } =item C Strings are usually represented in Prolog as lists of numbers. This function do the oposite conversion, from a list of numbers to a Perl string. It should be noted that all the elements in the Prolog list have to be integers in the range [0..255] or an execption will be raised. =cut sub prolog_list2perl_string { pack "C*", ( grep { ( prolog_is_atom($_) and /^\d+$/ and $_<256 ) or croak "Prolog list is not a valid string" } (prolog_list2perl_list $_[0]) ) } # abstract classes for Prolog types: =back =head2 ABSTRACT CLASSES =head3 Language::Prolog::Types::Term common abstract class for every Prolog term. =cut package Language::Prolog::Types::Term; =head3 Language::Prolog::Types::ListOrNil This class is used to account for the intrinsec differences between empty lists in Perl and Prolog. In Prolog, nil although represented as the empty list, is not really a list. This class provides a set of methods that apply both to lists and nil if it is considered to be the empty list. BTW, you should mostly ignore this class and use L or L instead. =head4 Inherits: =over 4 =item L =back =head4 Methods: =over 4 =item C<$lon-Elength()> returns the number of terms in the list. If the list is unfinished, the tail is not counted. =item C<$lon-Elargs()> returns the terms in the list. If the list is unfinished, the tail is ignored. =item C<$lon-Etail()> returns the list tail, that will be nil if the list is finished or is nil =item C<$lon-Elarg($index)> returns element number C<$index> on the list, if $index is negative, the list is indexed from the end. =back =cut package Language::Prolog::Types::ListOrNil; our @ISA=qw(Language::Prolog::Types::Term); use Carp; sub larg { croak "unimplemented virtual method" } sub largs { croak "unimplemented virtual method" } sub length { croak "unimplemented virtual method" } sub tail { croak "unimplemented virtual method" } =head3 Language::Prolog::Types::Nil Common abstract class for Prolog nil term representation. =head4 Inherits =over 4 =item L =back =head4 Methods This class doesn't define any method on its own. =cut package Language::Prolog::Types::Nil; our @ISA=qw(Language::Prolog::Types::ListOrNil); =head3 Language::Prolog::Types::Variable Common abstract class for Prolog variable representation. =head4 Inherits: =over 4 =item L =back =head4 Methods: =over 4 =item C<$var-Ename()> returns the variable name. =back =cut package Language::Prolog::Types::Variable; our @ISA=qw(Language::Prolog::Types::Term); use Carp; sub name { croak "unimplemented virtual method" } sub rename { croak "unimplemented virtual method" } =head3 Language::Prolog::Types::Functor Common abstract class for Prolog functor representations. =head4 Inherits: =over 4 =item L =back =head4 Methods: =over 4 =item C<$f-Efunctor()> returns the functor name. =item C<$f-Earity()> returns the number of arguments of the functor. =item C<$f-Efargs()> returns the arguments of the functor. =item C<$f-Efarg($index)> returns the argument of the functor in the position C<$index>, if C<$index> is negative the arguments are indexed begining from the end. Be aware that arguments are indexed from 0, not from 1 as in prolog. =back =cut package Language::Prolog::Types::Functor; our @ISA=qw(Language::Prolog::Types::Term); use Carp; sub functor { croak "unimplemented virtual method" } sub arity { croak "unimplemented virtual method" } sub farg { croak "unimplemented virtual method" } sub fargs { croak "unimplemented virtual method" } =head3 Language::Prolog::Types::List Common abstract class for Prolog list representations. =head4 Inherits: =over 4 =item L A Prolog list is actually the functor '.'/2. i.e. [1, 4, hello, foo] is equivalent to: '.'(1, '.'(4, '.'(hello, '.'(foo, [])))) =item L List methods are shared with L and this is the reasong, to descent also from this class. =back =head4 Methods: =over 4 =item C<$l-Ecar()> returns the list C. =item C<$l-Ecdr()> returns the list C. =item C<$l-Ecar_cdr()> returns both the C and the C of the list. =back =cut package Language::Prolog::Types::List; our @ISA=qw(Language::Prolog::Types::Functor Language::Prolog::Types::ListOrNil); use Carp; sub car { croak "unimplemented virtual method" } sub cdr { croak "unimplemented virtual method" } sub car_cdr { croak "unimplemented virtual method" } # default implementation of Functor methods for Lists sub functor { '.' } sub fargs { shift->car_cdr } sub farg { my ($self, $index)=@_; return $self->car if $index==0; return $self->cdr if $index==1; croak "farg index $index out of range for '.'/2"; } =head3 Language::Prolog::Types::UList Common abstract class to represent unfinished lists (those whose tail is not nil). =head4 Inherits: =over 4 =item L =back =head4 Methods: None of its own. =cut package Language::Prolog::Types::UList; our @ISA=qw(Language::Prolog::Types::List); use Carp; =head3 Language::Prolog::Types::Unknow just in case... =head4 Inherits: =over 4 =item L =back =head4 Methods: None. =cut package Language::Prolog::Types::Unknow; our @ISA=qw(Language::Prolog::Types::Term); sub id { '*unknow*' } =head3 Language::Prolog::Types::Opaque This class should be only used by Prolog <-> Perl interface authors. Usually Perl objects are converted to Prolog structures when passed to a Prolog implementation. This class defines a proxy that stops the conversion to happen and just pass a reference to the Perl object. Opaque objects should not be returned from Prolog interfaces, they should only be used to indicate to the Prolog implementations to not convert Perl data to Prolog. When returning from Prolog the original object should be directly returned to improve usability. It should be noted that not all prolog implementations would support this type. =head4 Inherits: =over 4 =item L =back =head4 Methods: =over 4 =item C<$this-Eopaque_reference> returns the object that it shields from prolog =item C<$this->Eopaque_comment> returns comment string that will show in Prolog representation =item C<$this->Eopaque_class> returns object class as should been seen from Prolog side =back =cut package Language::Prolog::Types::Opaque; our @ISA=qw(Language::Prolog::Types::Term); use Carp; sub opaque_reference { croak "unimplemented virtual method" } sub opaque_comment { return '-' } sub opaque_class { return ref shift } =head3 Language::Prolog::Types::Opaque::Auto Not really an abstract class but a simple implementation to be used as a base class to provide automatic opacity to objects. So, objects of any class that has it as an ancestor will be passed to prolog as a reference. =cut package Language::Prolog::Types::Opaque::Auto; our @ISA=qw(Language::Prolog::Types::Opaque); sub opaque_reference { return shift } 1; # module ok =head1 SEE ALSO L. L contains an actual implementation for the classes defined in this module. Any good Prolog book will also help :-) =head1 AUTHOR Salvador Fandiņo, Esfandino@yahoo.comE =head1 COPYRIGHT AND LICENSE Copyright 2002-2007 by Salvador Fandiņo This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut