File Coverage

blib/lib/Math/Logic/Ternary/Expression.pm
Criterion Covered Total %
statement 84 84 100.0
branch 48 48 100.0
condition 12 12 100.0
subroutine 15 15 100.0
pod 7 7 100.0
total 166 166 100.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2012-2017 Martin Becker, Blaubeuren. All rights reserved.
2             # This package is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4              
5             package Math::Logic::Ternary::Expression;
6              
7 1     1   4766 use strict;
  1         2  
  1         25  
8 1     1   5 use warnings;
  1         2  
  1         82  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13             our @EXPORT_OK = qw(not3 and3 or3 xor3 eqv3 bool3 val3);
14             our %EXPORT_TAGS = ('all' => \@EXPORT_OK);
15             our $VERSION = '0.004';
16              
17             # ----- object definition -----
18              
19             # Math::Logic::Ternary::Expression=ARRAY(...)
20              
21             # M::L::T::Expression ARRAY
22             # +----------+---------+ +----------+----------+
23             # | VALUEREF | o----------->| VALUE | anything |
24             # +----------+---------+ +----------+----------+
25             # | NEGATED | boolean | | DEFERRED | boolean |
26             # +----------+---------+ +----------+----------+
27              
28             # .......... index .......... # .............. value ..............
29 1     1   5 use constant VALUEREF => 0; # ref of lazily accessible value
  1         2  
  1         84  
30 1     1   5 use constant NEGATED => 1; # boolean
  1         1  
  1         38  
31              
32             # lazily accessible value: ARRAY(...)
33              
34             # .......... index .......... # .............. value ..............
35 1     1   4 use constant VALUE => 0; # coderef while deferred, then value
  1         2  
  1         67  
36 1     1   7 use constant DEFERRED => 1; # boolean
  1         2  
  1         567  
37              
38             my $undef = bless [[undef, ''], ''];
39              
40             # turn any value into an MLTE object
41             sub _object {
42 70     70   113 my ($this) = @_;
43 70 100       135 return $undef if !defined $this;
44 69 100       212 return $this if __PACKAGE__ eq ref $this;
45 6         33 return bless [[$this, 'CODE' eq ref $this], ''];
46             }
47              
48             # get the actual value from an MLTE object
49             sub _evaluate {
50 151     151   230 my ($this) = @_;
51 151         238 my $vr = $this->[VALUEREF];
52 151         199 my $v = $vr->[VALUE];
53 151         241 my $n = $this->[NEGATED];
54 151 100       269 if ($vr->[DEFERRED]) {
55 10         15 $vr->[DEFERRED] = '';
56 10         23 $v = $vr->[VALUE] = $v->();
57 10 100       57 if (!defined $v) {
58 3         5 $n = $this->[NEGATED] = '';
59             }
60             }
61             # die "assertion failed" unless defined($v) || !$n;
62 151 100       463 return $n? !$v: $v;
63             }
64              
65             # get the actual value from anything
66             sub val3 {
67 97     97 1 1215 my ($this) = @_;
68 97 100 100     358 return $this if !defined($this) || __PACKAGE__ ne ref $this;
69 93         166 return _evaluate($this);
70             }
71              
72             # replace actual value by a truth value
73             sub bool3 {
74 10     10 1 1550 my ($this) = @_;
75 10         19 my $val = _evaluate(_object($this));
76 10 100       32 return $undef if !defined $val;
77 7         46 return bless [[!!$val, ''], ''];
78             }
79              
80             # negate without evaluating
81             sub not3 {
82 53     53 1 2882 my ($this) = @_;
83 53 100       102 return $undef if !defined $this;
84 51 100       114 if (__PACKAGE__ eq ref $this) {
85 41         60 my $vr = $this->[VALUEREF];
86 41 100 100     132 return $undef if !($vr->[DEFERRED] || defined $vr->[VALUE]);
87 40         141 return bless [$vr, !$this->[NEGATED]];
88             }
89 10         45 return bless [[$this, 'CODE' eq ref $this], 1];
90             }
91              
92             # a && b: tt => b, tf => b, ft => a, ff => a
93             sub and3 {
94 9     9 1 1261 my ($this, $that) = @_;
95 9         20 my $obj = _object($this);
96 9         19 my $val = _evaluate($obj);
97 9 100 100     48 return $obj if defined($val) && !$val; # false, * => LHS
98 6         10 $obj = _object($that);
99 6 100       46 return $obj if $val; # true, * => RHS
100 3         7 $val = _evaluate($obj);
101 3 100 100     23 return $obj if defined($val) && !$val; # undef, false => RHS
102 2         9 return $undef; # else => undef
103             }
104              
105             # a || b: tt => a, tf => a, ft => b, ff => b
106             sub or3 {
107 9     9 1 1376 my ($this, $that) = @_;
108 9         19 my $obj = _object($this);
109 9         20 my $val = _evaluate($obj);
110 9 100       26 return $obj if $val; # true, * => LHS
111 6         12 $obj = _object($that);
112 6 100       18 return $obj if defined $val; # false, * => RHS
113 3         7 $val = _evaluate($obj);
114 3 100       13 return $obj if $val; # undef, true => RHS
115 2         9 return $undef; # else => undef
116             }
117              
118             # a? !b: b tt => !b, tf => !b, ft => b, ff => b
119             # !a && b || !b && a tt => !b, tf => a, ft => b, ff => a
120             # !b && a || !a && b tt => !a, tf => a, ft => b, ff => b
121             # !(a && b) && (a || b) tt => !b, tf => a, ft => b, ff => b (implemented)
122              
123             sub xor3 {
124 9     9 1 1526 my ($this, $that) = @_;
125 9         16 my $obj1 = _object($this);
126 9         18 my $val = _evaluate($obj1);
127 9 100       34 return $undef if !defined $val; # undef, * => undef
128 6         10 my $obj2 = _object($that);
129 6 100       22 return $obj2 if !$val; # false, * => RHS
130 3         7 $val = _evaluate($obj2);
131 3 100       9 return $undef if !defined $val; # true, undef => undef
132 2 100       12 return $obj1 if !$val; # true, false => LHS
133 1         3 return not3($obj2); # true, true => not RHS
134             }
135              
136             # !(a || b) || (a && b) tt => b, tf => b, ft => a, ff => !b
137              
138             sub eqv3 {
139 9     9 1 1442 my ($this, $that) = @_;
140 9         18 my $obj1 = _object($this);
141 9         17 my $val = _evaluate($obj1);
142 9 100       30 return $undef if !defined $val; # undef, * => undef
143 6         11 my $obj2 = _object($that);
144 6 100       28 return $obj2 if $val; # true, * => RHS
145 3         6 $val = _evaluate($obj2);
146 3 100       10 return $undef if !defined $val; # false, undef => undef
147 2 100       9 return $obj1 if $val; # false, true => LHS
148 1         5 return not3($obj2); # false, false => not RHS
149             }
150              
151             1;
152             __END__