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__ |