File Coverage

blib/lib/Logic/Expr.pm
Criterion Covered Total %
statement 43 43 100.0
branch 22 22 100.0
condition 9 9 100.0
subroutine 9 9 100.0
pod 6 6 100.0
total 89 89 100.0


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # logic expression related routines. see Logic::Expr::Parser for how the
4             # expressions are built
5              
6             package Logic::Expr;
7             our $VERSION = '0.01';
8 3     3   1047 use parent qw(Exporter);
  3         729  
  3         14  
9              
10             our ( @EXPORT_OK, %EXPORT_TAGS );
11              
12             BEGIN {
13 3     3   203 @EXPORT_OK = qw(TRUE FALSE LE_NOT LE_AND LE_OR LE_COND LE_BICOND);
14 3         1481 %EXPORT_TAGS =
15             ( all => [qw(TRUE FALSE LE_NOT LE_AND LE_OR LE_COND LE_BICOND)] );
16             }
17              
18             sub TRUE () { 1 }
19             sub FALSE () { 0 }
20             # -1 is reserved for testing (FAKE_OP); 0 or 1 for ops would conflate
21             # with the prior should a truth value be assigned to an op slot
22             sub LE_NOT () { 2 } # ! ~
23             sub LE_AND () { 3 } # &
24             sub LE_OR () { 4 } # | v
25             sub LE_COND () { 5 } # ->
26             sub LE_BICOND () { 6 } # ==
27              
28             # 'atoms' contains a name to a scalar reference in 'bools'; 'bools' is
29             # an array of unique atoms in an expression (for easy iteration by the
30             # "solutions" method); 'expr' is the parse tree of the expression as
31             # probably generated by Logic::Expr::Parser. 'bools' must be modified
32             # in-place to not break the scalar references from the other two
33             # structures. for example, given X&Y,
34             # * atoms - { X => \$bools[0], Y => \$bools[1] }
35             # * bools - [ 1, 1 ]
36             # * expr - [ LE_AND, ... ]
37              
38 4     4 1 49 sub atoms { $_[0]->{atoms} }
39 10     10 1 39 sub bools { $_[0]->{bools} }
40 4     4 1 19 sub expr { $_[0]->{expr} }
41              
42             sub new
43             {
44 9     9 1 27 my ( $class, %param ) = @_;
45 9         18 my $self = { map { $_ => $param{$_} } qw(atoms bools expr) };
  27         50  
46 9         15 bless $self, $class;
47 9         21 return $self;
48             }
49              
50             # brute force all possible boolean states for an expression
51             sub solutions
52             {
53 5     5 1 41 my ($self) = @_;
54 5         7 my @orig;
55 5         13 my $bools = $self->{bools};
56 5         9 for my $x (@$bools) {
57 10         12 push @orig, $x;
58 10         11 $x = TRUE;
59             }
60 5 100       16 my @solutions = [ [@$bools], _solve( $self->{expr} ) ? TRUE : FALSE ];
61             # the reverse index ordering is to match that of the logic book,
62             # backwards binary counting
63 5         9 my $i = $#$bools;
64 5         11 while ( $i >= 0 ) {
65 30 100       37 if ( $bools->[$i] ) {
66 15         16 $bools->[$i] = FALSE;
67 15 100       24 push @solutions, [ [ $bools->@* ], _solve( $self->{expr} ) ? TRUE : FALSE ];
68 15         23 $i = $#$bools;
69             } else {
70 15         24 $bools->[ $i-- ] = TRUE;
71             }
72             }
73 5         6 for my $x (@$bools) { $x = shift @orig }
  10         12  
74 5         43 return \@solutions;
75             }
76              
77             # solve the expression using the current state in bools
78 4 100   4 1 423 sub solve { _solve( $_[0]->{expr} ) ? TRUE : FALSE }
79              
80             sub _solve
81             {
82 65     65   69 my ($ptr) = @_;
83 65         72 my $rt = ref $ptr;
84 65 100       151 return $$ptr if $rt eq 'SCALAR'; # lookup from bools
85 30 100       42 if ( $rt eq 'ARRAY' ) {
86 29 100       100 if ( $ptr->[0] == LE_NOT ) {
    100          
    100          
    100          
    100          
87 6         10 return !_solve( $ptr->[1] );
88             } elsif ( $ptr->[0] == LE_AND ) {
89 4   100     6 return _solve( $ptr->[1] ) && _solve( $ptr->[2] );
90             } elsif ( $ptr->[0] == LE_OR ) {
91 4   100     6 return _solve( $ptr->[1] ) || _solve( $ptr->[2] );
92             } elsif ( $ptr->[0] == LE_COND ) {
93 10   100     13 return !_solve( $ptr->[1] ) || _solve( $ptr->[2] );
94             } elsif ( $ptr->[0] == LE_BICOND ) {
95 4         8 return !( _solve( $ptr->[1] ) ^ _solve( $ptr->[2] ) );
96             }
97 1         7 die "unknown op $ptr->[0]";
98             }
99 1         7 die "unexpected reference type '$rt'";
100             }
101              
102             1;
103             __END__