blob: 59657fd6a10048f463a599792a4b82961c095062 [file] [log] [blame]
# Copyright 2017 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
from __future__ import unicode_literals
import itertools
def dnf_convert(dep_struct):
"""
Convert dep_struct to disjunctive normal form (DNF), where dep_struct
is either a conjunction or disjunction of the form produced by
use_reduce(opconvert=True).
"""
# Normalize input to have a top-level conjunction.
if isinstance(dep_struct, list):
if dep_struct and dep_struct[0] == '||':
dep_struct = [dep_struct]
else:
dep_struct = [dep_struct]
conjunction = []
disjunctions = []
for x in dep_struct:
if isinstance (x, list):
assert x and x[0] == '||', \
'Normalization error, nested conjunction found in %s' % (dep_struct,)
if any(isinstance(element, list) for element in x):
x_dnf = ['||']
for element in x[1:]:
if isinstance(element, list):
# Due to normalization, a disjunction must not be
# nested directly in another disjunction, so this
# must be a conjunction.
assert element, 'Normalization error, empty conjunction found in %s' % (x,)
assert element[0] != '||', \
'Normalization error, nested disjunction found in %s' % (x,)
element = dnf_convert(element)
if contains_disjunction(element):
assert (len(element) == 1 and
element[0] and element[0][0] == '||'), \
'Normalization error, expected single disjunction in %s' % (element,)
x_dnf.extend(element[0][1:])
else:
x_dnf.append(element)
else:
x_dnf.append(element)
x = x_dnf
disjunctions.append(x)
else:
conjunction.append(x)
if disjunctions and (conjunction or len(disjunctions) > 1):
dnf_form = ['||']
for x in itertools.product(*[x[1:] for x in disjunctions]):
normalized = conjunction[:]
for element in x:
if isinstance(element, list):
normalized.extend(element)
else:
normalized.append(element)
dnf_form.append(normalized)
result = [dnf_form]
else:
result = conjunction + disjunctions
return result
def contains_disjunction(dep_struct):
"""
Search for a disjunction contained in dep_struct, where dep_struct
is either a conjunction or disjunction of the form produced by
use_reduce(opconvert=True). If dep_struct is a disjunction, then
this only returns True if there is a nested disjunction. Due to
normalization, recursion is only needed when dep_struct is a
disjunction containing a conjunction. If dep_struct is a conjunction,
then it is assumed that normalization has elevated any nested
disjunctions to the top-level.
"""
is_disjunction = dep_struct and dep_struct[0] == '||'
for x in dep_struct:
if isinstance(x, list):
assert x, 'Normalization error, empty conjunction found in %s' % (dep_struct,)
if x[0] == '||':
return True
elif is_disjunction and contains_disjunction(x):
return True
return False