AI-Prolog

 view release on metacpan or  search on metacpan

lib/AI/Prolog/Engine/Primitives.pm  view on Meta::CPAN

## no critic (RcsKeywords,PodSections,InterpolationOfMetachars,EmptyQuotes,ConstantPragma,InitializationForLocalVars,LocalVars,PunctuationVars)
package AI::Prolog::Engine::Primitives;
$REVISION = '$Id: Primitives.pm,v 1.1 2005/08/06 23:28:40 ovid Exp $';
$VERSION  = '0.3';
use strict;
use warnings;

use base 'AI::Prolog::Engine';
use Scalar::Util 'looks_like_number';

use aliased 'AI::Prolog::Term';
use aliased 'AI::Prolog::Term::Cut';
use aliased 'AI::Prolog::Term::Number';
use aliased 'AI::Prolog::TermList';
use aliased 'AI::Prolog::TermList::Step';
use aliased 'AI::Prolog::ChoicePoint';

my %DESCRIPTION_FOR;
my $LONGEST_PREDICATE = '';

sub _load_builtins {
    return if keys %DESCRIPTION_FOR;
    require Pod::Simple::Text;
    require Pod::Perldoc;
    my $perldoc     = Pod::Perldoc->new;
    my $builtin_pod = 'AI::Prolog::Builtins';

    my ($found) = $perldoc->grand_search_init( [$builtin_pod] )
        or die "Help failed.  Cannot find documentation for $builtin_pod: $!";
    open my $fh, '<', $found
        or die "Cannot open $found for reading: ($!)";
    my @lines = <$fh>;
    close $fh or die "Cannot close $found: ($!)";

    while (@lines) {
        my $line = shift @lines;
        my $predicate;
        if ( $line =~ /\A=item\s*(\S+)/mx ) {
            $predicate = $1;
            if ( $predicate =~ m{.*/\d+}mx ) {
                my @pod = "=head1 $predicate";
                if ( length $predicate > length $LONGEST_PREDICATE ) {
                    $LONGEST_PREDICATE = $predicate;
                }
                while ( $line = shift @lines ) {
                    if ( $line =~ /\A=(?:item|back)/mx ) {
                        unshift @lines => $line;
                        last;
                    }
                    push @pod => $line;
                }
                push @pod => '=cut';

                # XXX I hate instantiating this here, but there
                # appears to be a bug in parsing if I don't :(
                my $parser = Pod::Simple::Text->new;
                my $output;
                $parser->output_string( \$output );
                $parser->parse_lines( @pod, undef );
                $DESCRIPTION_FOR{$predicate} = $output;
                $output = '';
            }
        }
    }

    return;
}

sub _remove_choices {

    # this implements the cut operator
    my ( $self, $varid ) = @_;
    my @stack;
    my $i = @{ $self->{_stack} };
    while ( $i > $varid ) {
        my $o = pop @{ $self->{_stack} };
        if ( not $o->isa(ChoicePoint) ) {
            push @stack => $o;
        }
        $i--;
    }
    while (@stack) {
        push @{ $self->{_stack} } => pop @stack;
    }

    return;
}

sub _splice_goal_list {
    my ( $self, $term ) = @_;
    my ( $t2, $p, $p1, $ptail );
    my @vars;
    my $i = 0;
    $term = $term->getarg(0);
    while ( $term && $term->getfunctor ne 'null' ) {
        $t2 = $term->getarg(0);
        if ( $t2 eq Term->CUT ) {
            $p = TermList->new( Cut->new( scalar @{ $self->{_stack} } ) );
        }
        else {
            $p = TermList->new($t2);
        }
        if ( $i++ == 0 ) {
            $p1 = $ptail = $p;
        }
        else {
            $ptail->next($p);
            $ptail = $p;
        }
        $term = $term->getarg(1);
    }
    $ptail->next( $self->{_goal}->next );
    $self->{_goal} = $p1;
    $self->{_goal}->resolve( $self->{_db} );

    return;
}

use constant CONTINUE => 1;

lib/AI/Prolog/Engine/Primitives.pm  view on Meta::CPAN

        $PRIMITIVES[31]->();
    }
    CONTINUE;
};

my $gensym_int = 0;
$PRIMITIVES[33] = sub {    # gemsym/1
    my ( $self, $term, $c ) = @_;
    my $t2 = Term->new( 'v' . $gensym_int++, 0 );
    return $t2->unify( $term->getarg(0), $self->{_stack} )
        ? CONTINUE
        : FAIL;
};

use constant UNDEFINED_SUBROUTINE_ERROR => do {
    eval {
        no strict 'refs';    ## no critic NoStrict
        &{'---'};
    };
    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 ) {
        return FAIL;
    }
    my $function_name = $function_term->to_string;

    # Lookup a fully qualified function name...
    my $function_ref;
    if ( $function_name =~ /[:\']/mx ) {
        $function_ref = $function_name;
    }
    elsif ( defined( my $package = $self->{_perlpackage} ) ) {
        $function_name = "$package\::$function_name";
    }

    # Search the call stack...
    if ( not defined $function_ref ) {
        my $cx = 1;
        my %packages;
    CX:
        while ( my $package = caller $cx ) {

            # Don't retry packages...
            next if exists $packages{$package};
            $packages{$package} = undef;

            # AUTOLOAD using packages are expected to provide a
            # ->can() that works. I don't know if that's a widely
            # known expectation but it's what I'm going to go
            # with. Hash::AsObject gets this wrong.
            if (do {
                    no strict 'refs';    ## no critic NoStrict
                    defined &{"$package\::$function_name"};
                }
                or $package->can($function_name)
                )
            {
                $function_ref = "$package\::$function_name";
                last CX;
            }
        }
        continue {
            ++$cx;
        }
    }

    # We got nuthin! Damn! I'll try for the first AUTOLOAD.
    if ( not defined $function_ref ) {
        my $cx = 1;
        my %packages;
    AUTOLOAD_CX:
        while ( my ($package) = caller $cx ) {
            next if exists $packages{$package};
            $packages{$package} = undef;

            if (do {
                    no strict 'refs';    ## no critic NoStrict
                    defined &{"$package\::AUTOLOAD"};
                }
                or $package->can('AUTOLOAD')
                )
            {
                $function_ref = "$package\::$function_name";
                last AUTOLOAD_CX;
            }
        }
        continue {
            ++$cx;
        }
    }

    if ( not defined $function_ref ) {
	return FAIL;
    }

    # XXX What do to with the first arg?
    my ( undef, $results_ref ) = $term->getarg(1)->to_data;
    my @results = @{ $results_ref->[0] };

    eval {

        no strict 'refs';    ## no critic NoStrict
        $function_ref->(@results);
    };
    if ( my $e = $@ ) {
    
        # Extreme caution here.
        if ( $e =~ UNDEFINED_SUBROUTINE_ERROR ) {
            return FAIL;
        }
    }

    return CONTINUE;
};

sub find { return $PRIMITIVES[ $_[1] ] }

1;

__END__

=head1 NAME

AI::Prolog::Engine::Primitives - The code for running aiprolog builtins

=head1 SYNOPSIS

 my $builtin = AI::Prolog::Engine::Primitives ->find($builtin_id);

=head1 DESCRIPTION

This module contains the code to handle the built-in predicates.  The
L<AI::Prolog::Engine|AI::Prolog::Engine> assigns many builtins an ID
number and this number is used to lookup the sub necessary to execute
the built-in.

=head1 AUTHOR

Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>

Reverse the name to email me.

=head1 COPYRIGHT AND LICENSE

Copyright 2005 by Curtis "Ovid" Poe

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut



( run in 0.768 second using v1.01-cache-2.11-cpan-d80b1682f3f )