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

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

automake 1.8.5

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