Source code for boolean_parser.actions.clause

# !/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Filename: clause.py
# Project: actions
# Author: Brian Cherinka
# Created: Sunday, 17th February 2019 12:52:31 pm
# License: BSD 3-clause "New" or "Revised" License
# Copyright (c) 2019 Brian Cherinka
# Last Modified: Sunday, 17th February 2019 12:53:16 pm
# Modified By: Brian Cherinka


from __future__ import print_function, division, absolute_import

#
# Parsing Action classses
#


[docs]class BaseAction(object): ''' Base object representing a clause action An action to perform after parsing a string clause. If set, actions run Actions are attached to clauses with the ``setParseAction`` on a given ``pyparsing`` element. :py:meth:`pyparsing.ParserElement.setParseAction` accepts a function or class to be applied to the ``pyparsing`` element during the parsing process. Multiple actions can be attached by passing a list of functions or classes. This class extracts the parsed data from the ``pyparsing`` element and makes it accessible as a variety of named attributes. Attributes: name: str The name of the extracted parameter base: str The base name of the extracted parameter, if any. fullname: str The full name of the extracted parameter as base + name data: dict The extracted parsed parameters from the pyparse clause parsed_clause: :py:class:`pyparsing.ParseResults` The original pyparsed results object input_clause: str The original input clause element ''' def __init__(self, data): self.parsed_clause = data self.data = data[0].asDict() # parse the basic parameter name self._parse_parameter_name() def _parse_parameter_name(self): ''' parse the parameter name into a base + name ''' name = self.data.get('parameter', None) assert name.count( '.') <= 1, f'parameter {name} cannot have more than one . ' if '.' in name: self.base, self.name = name.split('.', 1) else: self.base = None self.name = name @property def fullname(self): ''' The full parameter name, including any base ''' return f'{self.base}.{self.name}' if self.base else self.name
[docs]class Word(BaseAction): ''' Class action for handling word clauses This action performs a basic word parse. The basic word is assigned as the ``name`` attribute. Example word clauses: "alpha" or "alpha and beta or not charlie". ''' def __init__(self, data): super(Word, self).__init__(data) def __repr__(self): return f'{self.name}' @property def input_clause(self): ''' Original input clause as a string ''' return f'{self.fullname}'
[docs]class Condition(BaseAction): ''' Class action for handling conditional clauses This action performs a basic conditional parse. The syntax for a conditional expressions is defined as "parameter operand value" or for "between" conditions, "parameter between value and value2". The parameter name, operand, and parameter value is assigned as the ``name``, ``operator``, and ``value`` attribute, respectively. Example conditional clauses: "x > 5" or "x > 5 and y < 3". When using a "between" condition, e.g. "x between 3 and 5", an additional ``value2`` attribute is assigned the second parameter value. For bitwise operands of '&' and '|', the value can also accept a negation prefix, e.g. "x & ~256", which evaluates to "x & -257". Allowed operands for conditionals are: '>', '>=, '<', '<=', '==', '=', '!=', '&', '|' In addition to the Base Attributes, the ``Condition`` action provides additional attributes containing the parsed condition parameters. Attributes: operator: str The operand used in the condition value: str The parameter value in the condition value2: str Optional second value, assigned when a "between" condition is used. ''' def __init__(self, data): super(Condition, self).__init__(data) # extract the conditional operator and value self.operator = self.data.get('operator', None) self._extract_values() def __repr__(self): more = 'and' + self.value2 if hasattr(self, 'value2') else '' return self.name + self.operator + self.value + more @property def input_clause(self): ''' Original input clause as a string ''' if self.operator == 'between': return f'{self.fullname} {self.operator} {self.value} and {self.value2}' else: return f'{self.fullname} {self.operator} {self.value}' def _extract_values(self): ''' Extract the value or values from the condition ''' self.value = self.data.get('value', None) if not self.value: if self.operator == 'between': self.value = self._check_bitwise_value(self.data.get('value1')) self.value2 = self._check_bitwise_value( self.data.get('value2')) self.value = self._check_bitwise_value(self.value) def _check_bitwise_value(self, value): ''' Check if value has a bitwise ~ in it Removes any bitwise ~ found in a value for a condition. If the operand is a bitwise & or |, convert the ~value to its integer appropriate. E.g. ~64 -> -65. Parameters: value: str A string numerical value Returns: The str value or value converted to the proper bitwise negative ''' if '~' in value: value = value.replace('~', '') if self.operator in ['&', '|']: value = str(-1 * (int(value)) - 1) return value