1# -*- coding: utf-8 -*- 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Parser used to parse the boolean expression.""" 7 8from __future__ import print_function 9 10import ast 11import pyparsing 12import re 13 14 15class BoolParseError(Exception): 16 """Base exception for this module.""" 17 18 19class _BoolOperand(object): 20 """Read pyparsing.Keyword as operand and evaluate its boolean value.""" 21 22 def __init__(self, t): 23 """Initialize the object. 24 25 Read boolean operands from pyparsing.Keyword and evaluate into the 26 corresponding boolean values. 27 28 Args: 29 t: t[0] is pyparsing.Keyword corresponding to False or True. 30 """ 31 self.label = t[0] 32 self.value = ast.literal_eval(t[0]) 33 34 def __bool__(self): 35 return self.value 36 37 def __str__(self): 38 return self.label 39 40 # Python 2 glue. 41 __nonzero__ = __bool__ 42 43 44class _BoolBinOp(object): 45 """General class for binary operation.""" 46 47 def __init__(self, t): 48 """Initialize object. 49 50 Extract the operand from the input. The operand is the 51 pyparsing.Keyword. 52 53 Args: 54 t: A list containing a list of operand and operator, such as 55 [[True, 'and', False]]. t[0] is [True, 'and', False]. t[0][0::2] are 56 the two operands. 57 """ 58 self.args = t[0][0::2] 59 60 def __bool__(self): 61 """Evaluate the boolean value of the binary boolean expression. 62 63 evalop is the method used to evaluate, which is overwritten in the 64 children class of _BoolBinOp. 65 66 Returns: 67 boolean result. 68 """ 69 return self.evalop(bool(a) for a in self.args) 70 71 # Python 2 glue. 72 __nonzero__ = __bool__ 73 74 75class _BoolAnd(_BoolBinOp): 76 """And boolean binary operation.""" 77 78 evalop = all 79 80 81class _BoolOr(_BoolBinOp): 82 """Or boolean binary operation.""" 83 84 evalop = any 85 86 87class _BoolNot(object): 88 """Not operation.""" 89 90 def __init__(self, t): 91 self.arg = t[0][1] 92 93 def __bool__(self): 94 v = bool(self.arg) 95 return not v 96 97 # Python 2 glue. 98 __nonzero__ = __bool__ 99 100 101def _ExprOverwrite(expr, true_variables): 102 """Overwrite variables in |expr| based on |true_variables|. 103 104 Overwrite variables in |expr| with 'True' if they occur in |true_variables|, 105 'False' otherwise. 106 107 Args: 108 expr: The orginal boolean expression, like 'A and B'. 109 true_variables: Collection of variable names to be considered True, e.g. 110 {'A'}. 111 112 Returns: 113 A boolean expression string with pyparsing.Keyword, like 'True and False'. 114 """ 115 # When true_variables is None, replace it with empty collection () 116 target_set = set(true_variables or ()) 117 items = { 118 x.strip() for x in re.split(r'(?i) and | or |not |\(|\)', expr) 119 if x.strip()} 120 boolstr = expr 121 for item in items: 122 boolstr = boolstr.replace( 123 item, 'True' if item in target_set else 'False') 124 125 return boolstr 126 127 128def BoolstrResult(expr, true_variables): 129 """Determine if a boolean expression is satisfied. 130 131 BoolstrResult('A and B and not C', {'A', 'C'}) -> False 132 133 Args: 134 expr: The orginal boolean expression, like 'A and B'. 135 true_variables: Collection to be checked whether satisfy the boolean expr. 136 137 Returns: 138 True if the given |true_variables| cause the boolean expression |expr| to 139 be satisfied, False otherwise. 140 """ 141 boolstr = _ExprOverwrite(expr, true_variables) 142 143 # Define the boolean logic 144 TRUE = pyparsing.Keyword('True') 145 FALSE = pyparsing.Keyword('False') 146 boolOperand = TRUE | FALSE 147 boolOperand.setParseAction(_BoolOperand) 148 149 # Define expression, based on expression operand and list of operations in 150 # precedence order. 151 boolExpr = pyparsing.infixNotation( 152 boolOperand, [('not', 1, pyparsing.opAssoc.RIGHT, _BoolNot), 153 ('and', 2, pyparsing.opAssoc.LEFT, _BoolAnd), 154 ('or', 2, pyparsing.opAssoc.LEFT, _BoolOr),]) 155 156 try: 157 res = boolExpr.parseString(boolstr)[0] 158 return bool(res) 159 except (AttributeError, pyparsing.ParseException): 160 raise BoolParseError('Cannot parse the boolean expression string "%s".' 161 % expr) 162