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

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

automake 1.9.6

File size: 14.1 KB
Line 
1# Copyright (C) 1997, 2001, 2002, 2003, 2004 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 adress. 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 $self = {
149 hash => {},
150 };
151 bless $self, $class;
152
153 for my $cond (@conds)
154 {
155 confess "`$cond' isn't a reference" unless ref $cond;
156 confess "`$cond' isn't an Automake::Condition"
157 unless $cond->isa ("Automake::Condition");
158
159 # This is a disjunction of conditions, so we drop
160 # false conditions. We'll always treat an "empty"
161 # DisjConditions as false for this reason.
162 next if $cond->false;
163
164 # Store conditions as keys AND as values, because blessed
165 # objects are converted to string when used as keys (so
166 # at least we still have the value when we need to call
167 # a method).
168 $self->{'hash'}{$cond} = $cond;
169 }
170
171 my $key = $self->string;
172 if (exists $_disjcondition_singletons{$key})
173 {
174 return $_disjcondition_singletons{$key};
175 }
176 $_disjcondition_singletons{$key} = $self;
177 return $self;
178}
179
180=item C<@conds = $set-E<gt>conds>
181
182Return the list of C<Condition> objects involved in C<$set>.
183
184=cut
185
186sub conds ($ )
187{
188 my ($self) = @_;
189 return @{$self->{'conds'}} if exists $self->{'conds'};
190 my @conds = values %{$self->{'hash'}};
191 @conds = sort { $a->string cmp $b->string } @conds;
192 $self->{'conds'} = [@conds];
193 return @conds;
194}
195
196=item C<$cond = $set-E<gt>one_cond>
197
198Return one C<Condition> object involved in C<$set>.
199
200=cut
201
202sub one_cond ($)
203{
204 my ($self) = @_;
205 return (%{$self->{'hash'}},)[1];
206}
207
208=item C<$et = $set-E<gt>false>
209
210Return 1 iff the C<DisjConditions> object is always false (i.e., if it
211is empty, or if it contains only false C<Condition>s). Return 0
212otherwise.
213
214=cut
215
216sub false ($ )
217{
218 my ($self) = @_;
219 return 0 == keys %{$self->{'hash'}};
220}
221
222=item C<$et = $set-E<gt>true>
223
224Return 1 iff the C<DisjConditions> object is always true (i.e. covers all
225conditions). Return 0 otherwise.
226
227=cut
228
229sub true ($ )
230{
231 my ($self) = @_;
232 return $self->invert->false;
233}
234
235=item C<$str = $set-E<gt>string>
236
237Build a string which denotes the C<DisjConditions>.
238
239=cut
240
241sub string ($ )
242{
243 my ($self) = @_;
244
245 return $self->{'string'} if defined $self->{'string'};
246
247 my $res = '';
248 if ($self->false)
249 {
250 $res = 'FALSE';
251 }
252 else
253 {
254 $res = join (' | ', map { $_->string } $self->conds);
255 }
256
257 $self->{'string'} = $res;
258 return $res;
259}
260
261=item C<$cond-E<gt>human>
262
263Build a human readable string which denotes the C<DisjConditions>.
264
265=cut
266
267sub human ($ )
268{
269 my ($self) = @_;
270
271 return $self->{'human'} if defined $self->{'human'};
272
273 my $res = '';
274 if ($self->false)
275 {
276 $res = 'FALSE';
277 }
278 else
279 {
280 my @c = $self->conds;
281 if (1 == @c)
282 {
283 $res = $c[0]->human;
284 }
285 else
286 {
287 $res = '(' . join (') or (', map { $_->human } $self->conds) . ')';
288 }
289 }
290 $self->{'human'} = $res;
291 return $res;
292}
293
294
295=item C<$newcond = $cond-E<gt>merge (@otherconds)>
296
297Return a new C<DisjConditions> which is the disjunction of
298C<$cond> and C<@otherconds>. Items in C<@otherconds> can be
299@C<Condition>s or C<DisjConditions>.
300
301=cut
302
303sub merge ($@)
304{
305 my ($self, @otherconds) = @_;
306 new Automake::DisjConditions (
307 map { $_->isa ("Automake::DisjConditions") ? $_->conds : $_ }
308 ($self, @otherconds));
309}
310
311
312=item C<$prod = $set1-E<gt>multiply ($set2)>
313
314Multiply two conditional sets.
315
316 my $set1 = new Automake::DisjConditions
317 (new Automake::Condition ("A_TRUE"),
318 new Automake::Condition ("B_TRUE"));
319 my $set2 = new Automake::DisjConditions
320 (new Automake::Condition ("C_FALSE"),
321 new Automake::Condition ("D_FALSE"));
322
323C<$set1-E<gt>multiply ($set2)> will return
324
325 new Automake::DisjConditions
326 (new Automake::Condition ("A_TRUE", "C_FALSE"),
327 new Automake::Condition ("B_TRUE", "C_FALSE"),;
328 new Automake::Condition ("A_TRUE", "D_FALSE"),
329 new Automake::Condition ("B_TRUE", "D_FALSE"));
330
331The argument can also be a C<Condition>.
332
333=cut
334
335# Same as multiply() but take a list of Conditonals as second argument.
336# We use this in invert().
337sub _multiply ($@)
338{
339 my ($self, @set) = @_;
340 my @res = map { $_->multiply (@set) } $self->conds;
341 return new Automake::DisjConditions (Automake::Condition::reduce_or @res);
342}
343
344sub multiply ($$)
345{
346 my ($self, $set) = @_;
347 return $self->_multiply ($set) if $set->isa('Automake::Condition');
348 return $self->_multiply ($set->conds);
349}
350
351=item C<$inv = $set-E<gt>invert>
352
353Invert a C<DisjConditions>. Return a C<DisjConditions> which is true
354when C<$set> is false, and vice-versa.
355
356 my $set = new Automake::DisjConditions
357 (new Automake::Condition ("A_TRUE", "B_TRUE"),
358 new Automake::Condition ("A_FALSE", "B_FALSE"));
359
360Calling C<$set-E<gt>invert> will return the following C<DisjConditions>.
361
362 new Automake::DisjConditions
363 (new Automake::Condition ("A_TRUE", "B_FALSE"),
364 new Automake::Condition ("A_FALSE", "B_TRUE"));
365
366We implement the inversion by a product-of-sums to sum-of-products
367conversion using repeated multiplications. Because of the way we
368implement multiplication, the result of inversion is in canonical
369prime implicant form.
370
371=cut
372
373sub invert($ )
374{
375 my ($self) = @_;
376
377 return $self->{'invert'} if defined $self->{'invert'};
378
379 # The invert of an empty DisjConditions is TRUE.
380 my $res = new Automake::DisjConditions TRUE;
381
382 # !((a.b)+(c.d)+(e.f))
383 # = (!a+!b).(!c+!d).(!e+!f)
384 # We develop this into a sum of product iteratively, starting from TRUE:
385 # 1) TRUE
386 # 2) TRUE.!a + TRUE.!b
387 # 3) TRUE.!a.!c + TRUE.!b.!c + TRUE.!a.!d + TRUE.!b.!d
388 # 4) TRUE.!a.!c.!e + TRUE.!b.!c.!e + TRUE.!a.!d.!e + TRUE.!b.!d.!e
389 # + TRUE.!a.!c.!f + TRUE.!b.!c.!f + TRUE.!a.!d.!f + TRUE.!b.!d.!f
390 foreach my $cond ($self->conds)
391 {
392 $res = $res->_multiply ($cond->not);
393 }
394
395 # Cache result.
396 $self->{'invert'} = $res;
397 # It's tempting to also set $res->{'invert'} to $self, but that
398 # is a bad idea as $self hasn't been normalized in any way.
399 # (Different inputs can produce the same inverted set.)
400 return $res;
401}
402
403=item C<$self-E<gt>simplify>
404
405Return a C<Disjunction> which is a simplified canonical form of C<$self>.
406This canonical form contains only prime implicants, but it can contain
407non-essential prime implicants.
408
409=cut
410
411sub simplify ($)
412{
413 my ($self) = @_;
414 return $self->invert->invert;
415}
416
417=item C<$self-E<gt>sub_conditions ($cond)>
418
419Return the subconditions of C<$self> that contains C<$cond>, with
420C<$cond> stripped. More formally, return C<$res> such that
421C<$res-E<gt>multiply ($cond) == $self-E<gt>multiply ($cond)> and
422C<$res> does not mention any of the variables in C<$cond>.
423
424For instance, consider:
425
426 my $a = new Automake::DisjConditions
427 (new Automake::Condition ("A_TRUE", "B_TRUE"),
428 new Automake::Condition ("A_TRUE", "C_FALSE"),
429 new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
430 new Automake::Condition ("A_FALSE"));
431 my $b = new Automake::DisjConditions
432 (new Automake::Condition ("A_TRUE", "B_FALSE"));
433
434Calling C<$a-E<gt>sub_conditions ($b)> will return the following
435C<DisjConditions>.
436
437 new Automake::DisjConditions
438 (new Automake::Condition ("C_FALSE"), # From A_TRUE C_FALSE
439 new Automake::Condition ("C_TRUE")); # From A_TRUE B_FALSE C_TRUE"
440
441=cut
442
443sub sub_conditions ($$)
444{
445 my ($self, $subcond) = @_;
446
447 # Make $subcond blindingly apparent in the DisjConditions.
448 # For instance `$b->multiply($a->conds)' (from the POD example) is:
449 # (new Automake::Condition ("FALSE"),
450 # new Automake::Condition ("A_TRUE", "B_FALSE", "C_FALSE"),
451 # new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
452 # new Automake::Condition ("FALSE"))
453 my @prodconds = $subcond->multiply ($self->conds);
454
455 # Now, strip $subcond from the remaining (i.e., non-false) Conditions.
456 my @res = map { $_->false ? () : $_->strip ($subcond) } @prodconds;
457
458 return new Automake::DisjConditions @res;
459}
460
461=item C<($string, $ambig_cond) = $condset-E<gt>ambiguous_p ($what, $cond)>
462
463Check for an ambiguous condition. Return an error message and the
464other condition involved if we have an ambiguity. Return an empty
465string and FALSE otherwise.
466
467C<$what> is the name of the thing being defined, to use in the error
468message. C<$cond> is the C<Condition> under which it is being
469defined. C<$condset> is the C<DisjConditions> under which it had
470already been defined.
471
472=cut
473
474sub ambiguous_p ($$$)
475{
476 my ($self, $var, $cond) = @_;
477
478 # Note that these rules don't consider the following
479 # example as ambiguous.
480 #
481 # if COND1
482 # FOO = foo
483 # endif
484 # if COND2
485 # FOO = bar
486 # endif
487 #
488 # It's up to the user to not define COND1 and COND2
489 # simultaneously.
490
491 return ("$var multiply defined in condition " . $cond->human, $cond)
492 if exists $self->{'hash'}{$cond};
493
494 foreach my $vcond ($self->conds)
495 {
496 return ("$var was already defined in condition " . $vcond->human
497 . ", which includes condition ". $cond->human, $vcond)
498 if $vcond->true_when ($cond);
499
500 return ("$var was already defined in condition " . $vcond->human
501 . ", which is included in condition " . $cond->human, $vcond)
502 if $cond->true_when ($vcond);
503 }
504 return ('', FALSE);
505}
506
507=head1 SEE ALSO
508
509L<Automake::Condition>.
510
511=head1 HISTORY
512
513C<AM_CONDITIONAL>s and supporting code were added to Automake 1.1o by
514Ian Lance Taylor <ian@cygnus.org> in 1997. Since then it has been
515improved by Tom Tromey <tromey@redhat.com>, Richard Boulton
516<richard@tartarus.org>, Raja R Harinath <harinath@cs.umn.edu>, Akim
517Demaille <akim@epita.fr>, Pavel Roskin <proski@gnu.org>, and
518Alexandre Duret-Lutz <adl@gnu.org>.
519
520=cut
521
5221;
523
524### Setup "GNU" style for perl-mode and cperl-mode.
525## Local Variables:
526## perl-indent-level: 2
527## perl-continued-statement-offset: 2
528## perl-continued-brace-offset: 0
529## perl-brace-offset: 0
530## perl-brace-imaginary-offset: 0
531## perl-label-offset: -2
532## cperl-indent-level: 2
533## cperl-brace-offset: 0
534## cperl-continued-brace-offset: 0
535## cperl-label-offset: -2
536## cperl-extra-newline-before-brace: t
537## cperl-merge-trailing-else: nil
538## cperl-continued-statement-offset: 2
539## End:
Note: See TracBrowser for help on using the repository browser.