@@ -1304,6 +1304,169 @@ sub _best_path {
13041304 return ($best , $rest );
13051305}
13061306
1307+ sub HeaderLine ::reduce_conds {
1308+
1309+ # Reduce the preprocessor conditionals that guard the HeaderLine $self
1310+ # object, given a hash that says the values of certain preprocessor
1311+ # conditions, and a corresponding regular expression pattern that matches
1312+ # any of those conditions.
1313+ #
1314+ # This currently returns 0 if the conditionals as a whole evaluate to
1315+ # false; and 1 if they might evaluate to true.
1316+
1317+ my ($self , $constraints_pat , $constraints_ref ) = @_ ;
1318+
1319+ # Short cut the common case of there being no conditions in effect.
1320+ return 1 if $self -> {cond }-> @* == 0;
1321+
1322+ # We need a copy so we don't destroy the input.
1323+ my @cond = copy_aoa($self -> {cond }-> @*);
1324+
1325+ return _reduce_conds(\@cond , $constraints_pat , $constraints_ref );
1326+ }
1327+
1328+ sub _reduce_conds {
1329+ my ($cond_ref , $constraints_pat , $constraints_ref , $recursed ) = @_ ;
1330+
1331+ # This does the heavy lifting for HeaderLine::reduce_conds(). It
1332+ # recursively descends the array $cond_ref of conditions. These have been
1333+ # normalized by HeaderParser so that each element is ANDed with all the
1334+ # other ones. Hence if any element evaluates to false, the whole array
1335+ # must.
1336+ #
1337+ # Each leaf node will have a series of conditionals (typically linked by
1338+ # '||') , each of which has been normalized by HeaderParser to look like
1339+ # either of;
1340+ # defined(foo)
1341+ # !defined(foo)
1342+ # All the ones of these that match $constraints_pat are substituted by
1343+ # their corresponding value in %constraints_ref, which will be either 0 or
1344+ # 1. The result is looked at to see if it has to evaluate to 0 or not.
1345+
1346+ # At leaf nodes, just change all defined(foo) whose values of foo are
1347+ # known to be those values, reducing them to "#if 0" or "#if 1".
1348+ if (ref $cond_ref ne " ARRAY" ) {
1349+ die " Expecting an array at top-level call" unless $recursed ;
1350+
1351+ $cond_ref =~ s / $constraints_pat/ $constraints_ref ->{$1 }/ g ;
1352+ reduce_ones_and_zeros(\$cond_ref );
1353+
1354+ # Assume 1 if doesn't evaluate completely to 0.
1355+ return $cond_ref ne ' 0' ;
1356+ }
1357+
1358+ # Here, the conditions are in an array. We recurse to handle each
1359+ # element of the array.
1360+ for my $cond ($cond_ref -> @*) {
1361+ return 0 unless _reduce_conds($cond ,
1362+ $constraints_pat ,
1363+ $constraints_ref ,
1364+ ($recursed // 0) + 1 # is recursed
1365+ );
1366+ }
1367+
1368+ # Didn't do an early return. Nothing conclusively evaluated to 0.
1369+ return 1;
1370+ }
1371+
1372+ sub reduce_ones_and_zeros {
1373+ my $ref = shift ;
1374+
1375+ # This reduces as much as possible a symbolic C preprocessor expression
1376+ # that hopefully has terms that are either 0 or 1. We know that anything
1377+ # ANDed with 0 is 0 and anything OR'd with 1 is 1, for example. By
1378+ # repeating these and other rules, until nothing more changes, we reduce
1379+ # it as much as possible, given what is known. In many cases that this is
1380+ # called in, the value gets down to simply 0 or 1.
1381+ #
1382+ # This is simplistic, not knowing about precedence, for example. khw took
1383+ # it from Devel::PPPort. But it works well enough. HeaderParser could be
1384+ # enlisted to make it work better.
1385+ my $any_changed = 0;
1386+
1387+ if (ref $ref eq ' ARRAY' ) {
1388+ foreach my $element ($ref -> @*) {
1389+ $any_changed |= reduce_ones_and_zeros(\$element )
1390+ if $element =~ / ^ # \s * (?: if | el ) /x ;
1391+ }
1392+
1393+ return $any_changed ;
1394+ }
1395+
1396+ my $changed ;
1397+ do {
1398+ $changed = 0;
1399+
1400+ # (0) -> 0
1401+ $changed |= $$ref =~ s / \( \s * 0 \s * \) / 0/ xg ;
1402+
1403+ # !0 -> 1
1404+ $changed |= $$ref =~ s / ! \s * 0 \b / 1/ xg ;
1405+
1406+ # '|| 0 ||' -> ||
1407+ $changed |= $$ref =~ s / \s * \|\| \s * 0 \s * \|\| / ||/ xg ;
1408+
1409+ # '^ 0 || foo' -> foo
1410+ # '(0 || foo' -> (foo
1411+ $changed |= $$ref =~ s / ^ \s * 0 \s * \|\| \s * // xg ;
1412+ $changed |= $$ref =~ s / \( \s * 0 \s * \|\| \s * / (/ xg ;
1413+
1414+ # 'foo || 0 ) ' -> foo
1415+ # 'foo || 0 $ ' -> foo
1416+ $changed |= $$ref =~ s / \s * \|\| \s * 0 \s * (?= $ | \) ) // xg ;
1417+
1418+ # '^ 0 && foo' doesn't work because of precedence: '0 && anything || 1'
1419+ # Similarly for 'foo && 0 $'
1420+
1421+ # (1) -> 1
1422+ $changed |= $$ref =~ s / \( \s * 1 \s * \) / 1/ xg ;
1423+
1424+ # !1 -> 0
1425+ $changed |= $$ref =~ s / ! \s * 1 \b / 0/ xg ;
1426+
1427+ # '&& 1 &&' -> &&
1428+ $changed |= $$ref =~ s / \s * && \s * 1 \s * && / &&/ xg ;
1429+
1430+ # '^ 1 && foo' -> foo
1431+ # '^ 1 || foo' -> 1 # Works cause || lower precedence than &&
1432+ $changed |= $$ref =~ s / ^ \s * 1 \s * && \s * // xg ;
1433+ # $changed |= $$ref =~ s/ ^ \s* 1 \s* \|\| .* /1/xg;
1434+
1435+ # '(1 && foo' -> (foo
1436+ $changed |= $$ref =~ s / \( \s * 1 \s * && \s * / (/ xg ;
1437+
1438+ # 'foo && 1 [ )$ ] ' -> foo
1439+ $changed |= $$ref =~ s / \s * && \s * 1 \s * (?= $ | \) ) // xg ;
1440+
1441+ # There are other things that could be reduced, but looking for
1442+ # just the defined(foo) case doesn't involve fancy parsing,
1443+ # and catches just about everything
1444+
1445+ # 'defined(foo) && 0' -> 0
1446+ $changed |= $$ref
1447+ =~ s / (?: ! \s *)? \b defined \s * \(\w +\)
1448+ \s * && \s * 0 \b / 0/ xg ;
1449+
1450+ # 'defined(foo) || 1' -> 1
1451+ $changed |= $$ref
1452+ =~ s / (?: ! \s *)? \b defined \s * \(\w +\)
1453+ \s * \|\| \s * 1 \b / 1/ xg ;
1454+
1455+ # '0 && defined(foo)' -> 0
1456+ $changed |= $$ref
1457+ =~ s / \b 0 \s * && \s * (?: ! \s *)? defined
1458+ \s * \(\w +\) / 0/ xg ;
1459+
1460+ # '1 || defined(foo)' -> 1
1461+ $changed |= $$ref
1462+ =~ s / \b 1 \s * \|\| \s * (?: ! \s *)? defined
1463+ \s * \(\w +\) / 1/ xg ;
1464+ $any_changed |= $changed ;
1465+ } while ($changed );
1466+
1467+ return $any_changed ;
1468+ }
1469+
13071470# This builds a group content tree from a set of lines. each content line in
13081471# the original file is added to the file based on the conditions that apply to
13091472# the content.
0 commit comments