You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.3 KiB
162 lines
4.3 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Parser used to parse the boolean expression."""
|
|
|
|
from __future__ import print_function
|
|
|
|
import ast
|
|
import pyparsing
|
|
import re
|
|
|
|
|
|
class BoolParseError(Exception):
|
|
"""Base exception for this module."""
|
|
|
|
|
|
class _BoolOperand(object):
|
|
"""Read pyparsing.Keyword as operand and evaluate its boolean value."""
|
|
|
|
def __init__(self, t):
|
|
"""Initialize the object.
|
|
|
|
Read boolean operands from pyparsing.Keyword and evaluate into the
|
|
corresponding boolean values.
|
|
|
|
Args:
|
|
t: t[0] is pyparsing.Keyword corresponding to False or True.
|
|
"""
|
|
self.label = t[0]
|
|
self.value = ast.literal_eval(t[0])
|
|
|
|
def __bool__(self):
|
|
return self.value
|
|
|
|
def __str__(self):
|
|
return self.label
|
|
|
|
# Python 2 glue.
|
|
__nonzero__ = __bool__
|
|
|
|
|
|
class _BoolBinOp(object):
|
|
"""General class for binary operation."""
|
|
|
|
def __init__(self, t):
|
|
"""Initialize object.
|
|
|
|
Extract the operand from the input. The operand is the
|
|
pyparsing.Keyword.
|
|
|
|
Args:
|
|
t: A list containing a list of operand and operator, such as
|
|
[[True, 'and', False]]. t[0] is [True, 'and', False]. t[0][0::2] are
|
|
the two operands.
|
|
"""
|
|
self.args = t[0][0::2]
|
|
|
|
def __bool__(self):
|
|
"""Evaluate the boolean value of the binary boolean expression.
|
|
|
|
evalop is the method used to evaluate, which is overwritten in the
|
|
children class of _BoolBinOp.
|
|
|
|
Returns:
|
|
boolean result.
|
|
"""
|
|
return self.evalop(bool(a) for a in self.args)
|
|
|
|
# Python 2 glue.
|
|
__nonzero__ = __bool__
|
|
|
|
|
|
class _BoolAnd(_BoolBinOp):
|
|
"""And boolean binary operation."""
|
|
|
|
evalop = all
|
|
|
|
|
|
class _BoolOr(_BoolBinOp):
|
|
"""Or boolean binary operation."""
|
|
|
|
evalop = any
|
|
|
|
|
|
class _BoolNot(object):
|
|
"""Not operation."""
|
|
|
|
def __init__(self, t):
|
|
self.arg = t[0][1]
|
|
|
|
def __bool__(self):
|
|
v = bool(self.arg)
|
|
return not v
|
|
|
|
# Python 2 glue.
|
|
__nonzero__ = __bool__
|
|
|
|
|
|
def _ExprOverwrite(expr, true_variables):
|
|
"""Overwrite variables in |expr| based on |true_variables|.
|
|
|
|
Overwrite variables in |expr| with 'True' if they occur in |true_variables|,
|
|
'False' otherwise.
|
|
|
|
Args:
|
|
expr: The orginal boolean expression, like 'A and B'.
|
|
true_variables: Collection of variable names to be considered True, e.g.
|
|
{'A'}.
|
|
|
|
Returns:
|
|
A boolean expression string with pyparsing.Keyword, like 'True and False'.
|
|
"""
|
|
# When true_variables is None, replace it with empty collection ()
|
|
target_set = set(true_variables or ())
|
|
items = {
|
|
x.strip() for x in re.split(r'(?i) and | or |not |\(|\)', expr)
|
|
if x.strip()}
|
|
boolstr = expr
|
|
for item in items:
|
|
boolstr = boolstr.replace(
|
|
item, 'True' if item in target_set else 'False')
|
|
|
|
return boolstr
|
|
|
|
|
|
def BoolstrResult(expr, true_variables):
|
|
"""Determine if a boolean expression is satisfied.
|
|
|
|
BoolstrResult('A and B and not C', {'A', 'C'}) -> False
|
|
|
|
Args:
|
|
expr: The orginal boolean expression, like 'A and B'.
|
|
true_variables: Collection to be checked whether satisfy the boolean expr.
|
|
|
|
Returns:
|
|
True if the given |true_variables| cause the boolean expression |expr| to
|
|
be satisfied, False otherwise.
|
|
"""
|
|
boolstr = _ExprOverwrite(expr, true_variables)
|
|
|
|
# Define the boolean logic
|
|
TRUE = pyparsing.Keyword('True')
|
|
FALSE = pyparsing.Keyword('False')
|
|
boolOperand = TRUE | FALSE
|
|
boolOperand.setParseAction(_BoolOperand)
|
|
|
|
# Define expression, based on expression operand and list of operations in
|
|
# precedence order.
|
|
boolExpr = pyparsing.infixNotation(
|
|
boolOperand, [('not', 1, pyparsing.opAssoc.RIGHT, _BoolNot),
|
|
('and', 2, pyparsing.opAssoc.LEFT, _BoolAnd),
|
|
('or', 2, pyparsing.opAssoc.LEFT, _BoolOr),])
|
|
|
|
try:
|
|
res = boolExpr.parseString(boolstr)[0]
|
|
return bool(res)
|
|
except (AttributeError, pyparsing.ParseException):
|
|
raise BoolParseError('Cannot parse the boolean expression string "%s".'
|
|
% expr)
|