blob: 85253c72c4ecb23b6a4a26cab708dad3ae0a102c [file] [log] [blame]
#
# -*- coding: utf-8 -*-
#
# This file is part of reclass
#
import copy
import pyparsing as pp
from item import Item
from reclass.utils.dictpath import DictPath
from reclass.errors import ExpressionError, ParseError, UndefinedVariableError
_OBJ = 'OBJ'
_TEST = 'TEST'
_VALUE = 'VALUE'
_IF = 'IF'
_EQUAL = '=='
_NOT_EQUAL = '!='
class ExpItem(Item):
def _get_parser():
def _object(string, location, tokens):
token = tokens[0]
tokens[0] = (_OBJ, token)
def _integer(string, location, tokens):
try:
token = int(tokens[0])
except ValueError:
token = tokens[0]
tokens[0] = (_OBJ, token)
def _number(string, location, tokens):
try:
token = float(tokens[0])
except ValueError:
token = tokens[0]
tokens[0] = (_OBJ, token)
def _test(string, location, tokens):
token = tokens[0]
tokens[0] = (_TEST, token)
def _if(string, location, tokens):
token = tokens[0]
tokens[0] = (_IF, token)
def _expr_var(string, location, tokens):
token = tokens[0]
tokens[0] = (_VALUE, token)
def _expr_test(string, location, tokens):
token = tokens[0]
tokens[0] = (_TEST, token)
white_space = pp.White().suppress()
end = pp.StringEnd()
operator = (pp.Literal(_EQUAL) | pp.Literal(_NOT_EQUAL)).setParseAction(_test)
begin_if = pp.CaselessLiteral(_IF, ).setParseAction(_if)
obj = pp.Word(pp.printables).setParseAction(_object)
integer = pp.Word('0123456789-').setParseAction(_integer)
number = pp.Word('0123456789-.').setParseAction(_number)
item = integer | number | obj
expr_var = pp.Group(obj + pp.Optional(white_space) + end).setParseAction(_expr_var)
expr_test = pp.Group(obj + white_space + begin_if + white_space + item + white_space + operator + white_space + item).setParseAction(_expr_test)
expr = pp.Optional(white_space) + (expr_test | expr_var)
return expr
_parser = _get_parser()
def __init__(self, item, delimiter):
self.type = Item.EXPORT
self._delimiter = delimiter
self._type = None
self._refs = []
self._expr = []
self._parse_expression(item.render(None, None))
def _parse_expression(self, expr):
try:
tokens = ExpItem._parser.parseString(expr).asList()
except pp.ParseException as e:
raise ParseError(e.msg, e.line, e.col, e.lineno)
if len(tokens) == 1:
self._type = tokens[0][0]
self._expr = tokens[0][1]
else:
raise ExpressionError('Failed to parse %s' % str(expr))
if self._type == _TEST:
export, parameter, value = self._get_vars(self._expr[2][1], None, None, None)
export, parameter, value = self._get_vars(self._expr[4][1], export, parameter, value)
if parameter is not None:
path = parameter
path.drop_first()
self._refs.append(str(path))
def assembleRefs(self, context):
return
def contents(self):
return self._expr
def has_exports(self):
return True
def has_references(self):
return len(self._refs) > 0
def get_references(self):
return self._refs
def _resolve(self, path, dictionary):
try:
return path.get_value(dictionary)
except KeyError as e:
raise UndefinedVariableError(str(path))
def _value_expression(self, exports):
results = {}
path = DictPath(self._delimiter, self._expr[0][1]).drop_first()
for node, items in exports.iteritems():
if path.exists_in(items):
results[node] = copy.deepcopy(self._resolve(path, items))
return results
def _test_expression(self, context, exports):
export_path = None
parameter_path = None
parameter_value = None
test = None
value_path = DictPath(self._delimiter, self._expr[0][1])
if self._expr[3][1] == _EQUAL:
test = _EQUAL
elif self._expr[3][1] == _NOT_EQUAL:
test = _NOT_EQUAL
export_path, parameter_path, parameter_value = self._get_vars(self._expr[2][1], export_path, parameter_path, parameter_value)
export_path, parameter_path, parameter_value = self._get_vars(self._expr[4][1], export_path, parameter_path, parameter_value)
if parameter_path is not None:
parameter_path.drop_first()
parameter_value = self._resolve(parameter_path, context)
if export_path is None or parameter_value is None or test is None or value_path is None:
ExpressionError('Failed to render %s' % str(self))
export_path.drop_first()
value_path.drop_first()
results = {}
for node, items in exports.iteritems():
if export_path.exists_in(items):
export_value = self._resolve(export_path, items)
test_passed = False
if test == _EQUAL and export_value == parameter_value:
test_passed = True
elif test == _NOT_EQUAL and export_value != parameter_value:
test_passed = True
if test_passed:
results[node] = copy.deepcopy(self._resolve(value_path, items))
return results
def _get_vars(self, var, export, parameter, value):
if isinstance(var, str):
path = DictPath(self._delimiter, var)
if path.path[0].lower() == 'exports':
export = path
elif path.path[0].lower() == 'self':
parameter = path
else:
value = var
else:
value = var
return export, parameter, value
def render(self, context, exports):
if self._type == _VALUE:
return self._value_expression(exports)
elif self._type == _TEST:
return self._test_expression(context, exports)
raise ExpressionError('Failed to render %s' % str(self))
def __str__(self):
return ' '.join(str(j) for i,j in self._expr)
def __repr__(self):
return 'ExpItem(%r)' % self._expr