source: trunk/essentials/sys-devel/automake-1.10/lib/Automake/DisjConditions.pm

Last change on this file was 3147, checked in by bird, 18 years ago

automake 1.10

File size: 14.1 KB
Line 
1# Copyright (C) 1997, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2, or (at your option)
6# any later version.
7
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16# 02110-1301, USA.
17
18package Automake::DisjConditions;
19
20use Carp;
21use strict;
22use Automake::Condition qw/TRUE FALSE/;
23
24=head1 NAME
25
26Automake::DisjConditions - record a disjunction of Conditions
27
28=head1 SYNOPSIS
29
30 use Automake::Condition;
31 use Automake::DisjConditions;
32
33 # Create a Condition to represent "COND1 and not COND2".
34 my $cond = new Automake::Condition "COND1_TRUE", "COND2_FALSE";
35 # Create a Condition to represent "not COND3".
36 my $other = new Automake::Condition "COND3_FALSE";
37
38 # Create a DisjConditions to represent
39 # "(COND1 and not COND2) or (not COND3)"
40 my $set = new Automake::DisjConditions $cond, $other;
41
42 # Return the list of Conditions involved in $set.
43 my @conds = $set->conds;
44
45 # Return one of the Condition involved in $set.
46 my $cond = $set->one_cond;
47
48 # Return true iff $set is always true (i.e. its subconditions
49 # conver all cases).
50 if ($set->true) { ... }
51
52 # Return false iff $set is always false (i.e. is empty, or contains
53 # only false conditions).
54 if ($set->false) { ... }
55
56 # Return a string representing the DisjConditions.
57 # "COND1_TRUE COND2_FALSE | COND3_FALSE"
58 my $str = $set->string;
59
60 # Return a human readable string representing the DisjConditions.
61 # "(COND1 and !COND2) or (!COND3)"
62 my $str = $set->human;
63
64 # Merge (OR) several DisjConditions.
65 my $all = $set->merge($set2, $set3, ...)
66
67 # Invert a DisjConditions, i.e., create a new DisjConditions
68 # that complements $set.
69 my $inv = $set->invert;
70
71 # Multiply two DisjConditions.
72 my $prod = $set1->multiply ($set2);
73
74 # Return the subconditions of a DisjConditions with respect to
75 # a Condition. See the description for a real example.
76 my $subconds = $set->sub_conditions ($cond);
77
78 # Check whether a new definition in condition $cond would be
79 # ambiguous w.r.t. existing definitions in $set.
80 ($msg, $ambig_cond) = $set->ambiguous_p ($what, $cond);
81
82=head1 DESCRIPTION
83
84A C<DisjConditions> is a disjunction of C<Condition>s. In Automake
85they are used to represent the conditions into which Makefile
86variables and Makefile rules are defined.
87
88If the variable C<VAR> is defined as
89
90 if COND1
91 if COND2
92 VAR = value1
93 endif
94 endif
95 if !COND3
96 if COND4
97 VAR = value2
98 endif
99 endif
100
101then it will be associated a C<DisjConditions> created with
102the following statement.
103
104 new Automake::DisjConditions
105 (new Automake::Condition ("COND1_TRUE", "COND2_TRUE"),
106 new Automake::Condition ("COND3_FALSE", "COND4_TRUE"));
107
108As you can see, a C<DisjConditions> is made from a list of
109C<Condition>s. Since C<DisjConditions> is a disjunction, and
110C<Condition> is a conjunction, the above can be read as
111follows.
112
113 (COND1 and COND2) or ((not COND3) and COND4)
114
115That's indeed the condition into which C<VAR> has a value.
116
117Like C<Condition> objects, a C<DisjConditions> object is unique
118with respect to its conditions. Two C<DisjConditions> objects created
119for the same set of conditions will have the same address. This makes
120it easy to compare C<DisjConditions>s: just compare the references.
121
122=head2 Methods
123
124=over 4
125
126=item C<$set = new Automake::DisjConditions [@conds]>
127
128Create a C<DisjConditions> object from the list of C<Condition>
129objects passed in arguments.
130
131If the C<@conds> list is empty, the C<DisjConditions> is assumed to be
132false.
133
134As explained previously, the reference (object) returned is unique
135with respect to C<@conds>. For this purpose, duplicate elements are
136ignored.
137
138=cut
139
140# Keys in this hash are DisjConditions strings. Values are the
141# associated object DisjConditions. This is used by `new' to reuse
142# DisjConditions objects with identical conditions.
143use vars '%_disjcondition_singletons';
144
145sub new ($;@)
146{
147 my ($class, @conds) = @_;
148 my @filtered_conds = ();
149 for my $cond (@conds)
150 {
151 confess "`$cond' isn't a reference" unless ref $cond;
152 confess "`$cond' isn't an Automake::Condition"
153 unless $cond->isa ("Automake::Condition");
154
155 # This is a disjunction of conditions, so we drop
156 # false conditions. We'll always treat an "empty"
157 # DisjConditions as false for this reason.
158 next if $cond->false;
159
160 push @filtered_conds, $cond;
161 }
162
163 my $string;
164 if (@filtered_conds)
165 {
166 @filtered_conds = sort { $a->string cmp $b->string } @filtered_conds;
167 $string = join (' | ', map { $_->string } @filtered_conds);
168 }
169 else
170 {
171 $string = 'FALSE';
172 }
173
174 # Return any existing identical DisjConditions.
175 my $me = $_disjcondition_singletons{$string};
176 return $me if $me;
177
178 # Else, create a new DisjConditions.
179
180 # Store conditions as keys AND as values, because blessed
181 # objects are converted to string when used as keys (so
182 # at least we still have the value when we need to call
183 # a method).
184 my %h = map {$_ => $_} @filtered_conds;
185
186 my $self = {
187 hash => \%h,
188 string => $string,
189 conds => \@filtered_conds,
190 };
191 bless $self, $class;
192
193 $_disjcondition_singletons{$string} = $self;
194 return $self;
195}
196
197=item C<@conds = $set-E<gt>conds>
198
199Return the list of C<Condition> objects involved in C<$set>.
200
201=cut
202
203sub conds ($ )
204{
205 my ($self) = @_;
206 return @{$self->{'conds'}};
207}
208
209=item C<$cond = $set-E<gt>one_cond>
210
211Return one C<Condition> object involved in C<$set>.
212
213=cut
214
215sub one_cond ($)
216{
217 my ($self) = @_;
218 return (%{$self->{'hash'}},)[1];
219}
220
221=item C<$et = $set-E<gt>false>
222
223Return 1 iff the C<DisjConditions> object is always false (i.e., if it
224is empty, or if it contains only false C<Condition>s). Return 0
225otherwise.
226
227=cut
228
229sub false ($ )
230{
231 my ($self) = @_;
232 return 0 == keys %{$self->{'hash'}};
233}
234
235=item C<$et = $set-E<gt>true>
236
237Return 1 iff the C<DisjConditions> object is always true (i.e. covers all
238conditions). Return 0 otherwise.
239
240=cut
241
242sub true ($ )
243{
244 my ($self) = @_;
245 return $self->invert->false;
246}
247
248=item C<$str = $set-E<gt>string>
249
250Build a string which denotes the C<DisjConditions>.
251
252=cut
253
254sub string ($ )
255{
256 my ($self) = @_;
257 return $self->{'string'};
258}
259
260=item C<$cond-E<gt>human>
261
262Build a human readable string which denotes the C<DisjConditions>.
263
264=cut
265
266sub human ($ )
267{
268 my ($self) = @_;
269
270 return $self->{'human'} if defined $self->{'human'};
271
272 my $res = '';
273 if ($self->false)
274 {
275 $res = 'FALSE';
276 }
277 else
278 {
279 my @c = $self->conds;
280 if (1 == @c)
281 {
282 $res = $c[0]->human;
283 }
284 else
285 {
286 $res = '(' . join (') or (', map { $_->human } $self->conds) . ')';
287 }
288 }
289 $self->{'human'} = $res;
290 return $res;
291}
292
293
294=item C<$newcond = $cond-E<gt>merge (@otherconds)>
295
296Return a new C<DisjConditions> which is the disjunction of
297C<$cond> and C<@otherconds>. Items in C<@otherconds> can be
298@C<Condition>s or C<DisjConditions>.
299
300=cut
301
302sub merge ($@)
303{
304 my ($self, @otherconds) = @_;
305 new Automake::DisjConditions (
306 map { $_->isa ("Automake::DisjConditions") ? $_->conds : $_ }
307 ($self, @otherconds));
308}
309
310
311=item C<$prod = $set1-E<gt>multiply ($set2)>
312
313Multiply two conditional sets.
314
315 my $set1 = new Automake::DisjConditions
316 (new Automake::Condition ("A_TRUE"),
317 new Automake::Condition ("B_TRUE"));
318 my $set2 = new Automake::DisjConditions
319 (new Automake::Condition ("C_FALSE"),
320 new Automake::Condition ("D_FALSE"));
321
322C<$set1-E<gt>multiply ($set2)> will return
323
324 new Automake::DisjConditions
325 (new Automake::Condition ("A_TRUE", "C_FALSE"),
326 new Automake::Condition ("B_TRUE", "C_FALSE"),;
327 new Automake::Condition ("A_TRUE", "D_FALSE"),
328 new Automake::Condition ("B_TRUE", "D_FALSE"));
329
330The argument can also be a C<Condition>.
331
332=cut
333
334# Same as multiply() but take a list of Conditonals as second argument.
335# We use this in invert().
336sub _multiply ($@)
337{
338 my ($self, @set) = @_;
339 my @res = map { $_->multiply (@set) } $self->conds;
340 return new Automake::DisjConditions (Automake::Condition::reduce_or @res);
341}
342
343sub multiply ($$)
344{
345 my ($self, $set) = @_;
346 return $self->_multiply ($set) if $set->isa('Automake::Condition');
347 return $self->_multiply ($set->conds);
348}
349
350=item C<$inv = $set-E<gt>invert>
351
352Invert a C<DisjConditions>. Return a C<DisjConditions> which is true
353when C<$set> is false, and vice-versa.
354
355 my $set = new Automake::DisjConditions
356 (new Automake::Condition ("A_TRUE", "B_TRUE"),
357 new Automake::Condition ("A_FALSE", "B_FALSE"));
358
359Calling C<$set-E<gt>invert> will return the following C<DisjConditions>.
360
361 new Automake::DisjConditions
362 (new Automake::Condition ("A_TRUE", "B_FALSE"),
363 new Automake::Condition ("A_FALSE", "B_TRUE"));
364
365We implement the inversion by a product-of-sums to sum-of-products
366conversion using repeated multiplications. Because of the way we
367implement multiplication, the result of inversion is in canonical
368prime implicant form.
369
370=cut
371
372sub invert($ )
373{
374 my ($self) = @_;
375
376 return $self->{'invert'} if defined $self->{'invert'};
377
378 # The invert of an empty DisjConditions is TRUE.
379 my $res = new Automake::DisjConditions TRUE;
380
381 # !((a.b)+(c.d)+(e.f))
382 # = (!a+!b).(!c+!d).(!e+!f)
383 # We develop this into a sum of product iteratively, starting from TRUE:
384 # 1) TRUE
385 # 2) TRUE.!a + TRUE.!b
386 # 3) TRUE.!a.!c + TRUE.!b.!c + TRUE.!a.!d + TRUE.!b.!d
387 # 4) TRUE.!a.!c.!e + TRUE.!b.!c.!e + TRUE.!a.!d.!e + TRUE.!b.!d.!e
388 # + TRUE.!a.!c.!f + TRUE.!b.!c.!f + TRUE.!a.!d.!f + TRUE.!b.!d.!f
389 foreach my $cond ($self->conds)
390 {
391 $res = $res->_multiply ($cond->not);
392 }
393
394 # Cache result.
395 $self->{'invert'} = $res;
396 # It's tempting to also set $res->{'invert'} to $self, but that
397 # is a bad idea as $self hasn't been normalized in any way.
398 # (Different inputs can produce the same inverted set.)
399 return $res;
400}
401
402=item C<$self-E<gt>simplify>
403
404Return a C<Disjunction> which is a simplified canonical form of C<$self>.
405This canonical form contains only prime implicants, but it can contain
406non-essential prime implicants.
407
408=cut
409
410sub simplify ($)
411{
412 my ($self) = @_;
413 return $self->invert->invert;
414}
415
416=item C<$self-E<gt>sub_conditions ($cond)>
417
418Return the subconditions of C<$self> that contains C<$cond>, with
419C<$cond> stripped. More formally, return C<$res> such that
420C<$res-E<gt>multiply ($cond) == $self-E<gt>multiply ($cond)> and
421C<$res> does not mention any of the variables in C<$cond>.
422
423For instance, consider:
424
425 my $a = new Automake::DisjConditions
426 (new Automake::Condition ("A_TRUE", "B_TRUE"),
427 new Automake::Condition ("A_TRUE", "C_FALSE"),
428 new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
429 new Automake::Condition ("A_FALSE"));
430 my $b = new Automake::DisjConditions
431 (new Automake::Condition ("A_TRUE", "B_FALSE"));
432
433Calling C<$a-E<gt>sub_conditions ($b)> will return the following
434C<DisjConditions>.
435
436 new Automake::DisjConditions
437 (new Automake::Condition ("C_FALSE"), # From A_TRUE C_FALSE
438 new Automake::Condition ("C_TRUE")); # From A_TRUE B_FALSE C_TRUE"
439
440=cut
441
442sub sub_conditions ($$)
443{
444 my ($self, $subcond) = @_;
445
446 # Make $subcond blindingly apparent in the DisjConditions.
447 # For instance `$b->multiply($a->conds)' (from the POD example) is:
448 # (new Automake::Condition ("FALSE"),
449 # new Automake::Condition ("A_TRUE", "B_FALSE", "C_FALSE"),
450 # new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
451 # new Automake::Condition ("FALSE"))
452 my @prodconds = $subcond->multiply ($self->conds);
453
454 # Now, strip $subcond from the remaining (i.e., non-false) Conditions.
455 my @res = map { $_->false ? () : $_->strip ($subcond) } @prodconds;
456
457 return new Automake::DisjConditions @res;
458}
459
460=item C<($string, $ambig_cond) = $condset-E<gt>ambiguous_p ($what, $cond)>
461
462Check for an ambiguous condition. Return an error message and the
463other condition involved if we have an ambiguity. Return an empty
464string and FALSE otherwise.
465
466C<$what> is the name of the thing being defined, to use in the error
467message. C<$cond> is the C<Condition> under which it is being
468defined. C<$condset> is the C<DisjConditions> under which it had
469already been defined.
470
471=cut
472
473sub ambiguous_p ($$$)
474{
475 my ($self, $var, $cond) = @_;
476
477 # Note that these rules don't consider the following
478 # example as ambiguous.
479 #
480 # if COND1
481 # FOO = foo
482 # endif
483 # if COND2
484 # FOO = bar
485 # endif
486 #
487 # It's up to the user to not define COND1 and COND2
488 # simultaneously.
489
490 return ("$var multiply defined in condition " . $cond->human, $cond)
491 if exists $self->{'hash'}{$cond};
492
493 foreach my $vcond ($self->conds)
494 {
495 return ("$var was already defined in condition " . $vcond->human
496 . ", which includes condition ". $cond->human, $vcond)
497 if $vcond->true_when ($cond);
498
499 return ("$var was already defined in condition " . $vcond->human
500 . ", which is included in condition " . $cond->human, $vcond)
501 if $cond->true_when ($vcond);
502 }
503 return ('', FALSE);
504}
505
506=head1 SEE ALSO
507
508L<Automake::Condition>.
509
510=head1 HISTORY
511
512C<AM_CONDITIONAL>s and supporting code were added to Automake 1.1o by
513Ian Lance Taylor <ian@cygnus.org> in 1997. Since then it has been
514improved by Tom Tromey <tromey@redhat.com>, Richard Boulton
515<richard@tartarus.org>, Raja R Harinath <harinath@cs.umn.edu>, Akim
516Demaille <akim@epita.fr>, Pavel Roskin <proski@gnu.org>, and
517Alexandre Duret-Lutz <adl@gnu.org>.
518
519=cut
520
5211;
522
523### Setup "GNU" style for perl-mode and cperl-mode.
524## Local Variables:
525## perl-indent-level: 2
526## perl-continued-statement-offset: 2
527## perl-continued-brace-offset: 0
528## perl-brace-offset: 0
529## perl-brace-imaginary-offset: 0
530## perl-label-offset: -2
531## cperl-indent-level: 2
532## cperl-brace-offset: 0
533## cperl-continued-brace-offset: 0
534## cperl-label-offset: -2
535## cperl-extra-newline-before-brace: t
536## cperl-merge-trailing-else: nil
537## cperl-continued-statement-offset: 2
538## End:
Note: See TracBrowser for help on using the repository browser.