| # -*- 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) |