def __recurser(node, permutation, inCondition=False): # Support block commands # These come with their own node.type if node.type == "if": # Pre-process condition # We manually process each child in for if-types __recurser(node.condition, permutation, True) # Cast condition to Python boolean type resultValue = None try: resultValue = Operation.castToBool(node.condition) except Operation.OperationError as ex: Console.debug("Walked into unprocessed condition. Waiting for actual execution. Message: %s", ex) if not resultValue is None: # Process relevant part of the sub tree if resultValue is True: # Fix missing processing of result node __recurser(node.thenPart, permutation) # Finally replace if-node with result node node.parent.replace(node, node.thenPart) elif resultValue is False and hasattr(node, "elsePart"): # Fix missing processing of result node __recurser(node.elsePart, permutation) # Finally replace if-node with result node node.parent.replace(node, node.elsePart) else: # Cleanup original if-node node.parent.remove(node) # All done including child nodes return # Process children first (resolve logic is inner-out) for child in list(node): if child is not None: __recurser(child, permutation, inCondition) # Inside of conditions replace identifiers with their actual value (from current permutation) if inCondition and node.type == "identifier": if not permutation.has(node.value): raise ResolverError("Could not find field %s" % node.value, node) repl = Util.castNativeToNode(permutation.get(node.value)) node.parent.replace(node, repl) # Support inline @field() commands elif node.type == "command" and node.name == "field": if len(node.params) == 0: raise ResolverError("Missing parameter to insert field via @field.", node) identifierNode = node.params[0] if identifierNode.type != "identifier": raise ResolverError("Invalid parameter to @field call: %s" % identifierNode.type, identifierNode) identifier = identifierNode.value if not permutation.has(identifier): raise ResolverError("Could not find field with the name %s" % identifier, identifierNode) repl = Util.castNativeToNode(permutation.get(identifier)) node.parent.replace(node, repl) # Support typical operators elif node.type in Util.ALL_OPERATORS: repl = Operation.compute(node) if repl is not None: node.parent.replace(node, repl)
def __recurser(node, scope, values, profile): # Replace variable with actual value if node.type == "variable" and not (node.parent.type == "assign" and node.parent[0] is node): name = node.name if name not in values: raise ExecuterError( "Could not resolve variable %s! Missing value!" % name, node) value = values[name] if value is None: raise ExecuterError( "Could not resolve variable %s! Value is none!" % name, node) Console.debug("Resolving variable: %s at line %s with %s from %s", name, node.line, values[name].type, values[name].line) node.parent.replace(node, copy.deepcopy(values[name])) # Decide which sub tree of an if-condition is relevant based on current variable situation elif node.type == "if": Console.debug("Processing if-condition at %s", node.line) # Pre-process condition # We manually process each child in for if-types __recurser(node.condition, scope, values, profile) # Cast condition to Python boolean type resultValue = Operation.castToBool(node.condition) # Process relevant part of the sub tree if resultValue is True: # Fix missing processing of result node __recurser(node.thenPart, scope, values, profile) # Finally replace if-node with result node node.parent.replace(node, node.thenPart) elif resultValue is False and hasattr(node, "elsePart"): # Fix missing processing of result node __recurser(node.elsePart, scope, values, profile) # Finally replace if-node with result node node.parent.replace(node, node.elsePart) else: # Cleanup original if-node node.parent.remove(node) # Nothing to do here as content is already processed return # Update scope of new block starts if hasattr(node, "scope"): relation = getattr(node, "rel", None) # Conditional blocks are not exactly blocks in this variable resolution engine if not relation in ("thenPart", "elsePart"): scope = node.scope values = copy.copy(values) node.values = values # Reset all local variables to None # which enforces not to keep values from outer scope for name in scope.modified: values[name] = None # Process children / content for child in list(node): # Ignore non-children... through possible interactive structure changes if child and child.parent is node: __recurser(child, scope, values, profile) # Update values of variables # This happens after processing children to possibly reduce child structure to an easy to assign (aka preprocessed value) if (node.type == "declaration" and hasattr(node, "initializer")) or node.type == "assign": if node.type == "declaration": name = node.name init = node.initializer Console.debug("Found declaration of %s at line %s", name, node.line) else: name = node[0].name init = node[1] Console.debug("Found assignment of %s at line %s", name, node.line) # Modify value instead of replace when assign operator is set if hasattr(node, "assignOp") and node.assignOp is not None: if name not in values: raise ExecuterError( "Assign operator is not supported as left hand variable is missing: %s" % name, node) repl = Operation.compute(node, values[name], init, node.assignOp) if repl is not None: values[name] = repl else: # Update internal variable mapping # Console.debug("Update value of %s to %s" % (name, init)) values[name] = init # Remove declaration node from tree node.parent.remove(node) # Support for variables inside property names or selectors elif node.type in ("property", "selector") and getattr( node, "dynamic", False): def replacer(matchObj): name = matchObj.group(1) if name not in values: raise ExecuterError( "Could not resolve variable %s! Missing value!" % name, node) value = values[name] if value is None: raise ExecuterError( "Could not resolve variable %s! Value is none!" % name, node) if value.type == "identifier": return value.value elif value.type == "string": return value.value elif value.type == "number": return "%s%s" % (value.value, getattr(value, "unit", "")) else: raise ExecuterError( "Could not replace property inline variable with value of type: %s" % value.type, node) # Fix all selectors if node.type == "selector": selectors = node.name for pos, selector in enumerate(selectors): selectors[pos] = RE_INLINE_VARIABLE.sub(replacer, selector) else: node.name = RE_INLINE_VARIABLE.sub(replacer, node.name) # Execute system commands elif node.type == "command": repl = Util.executeCommand(node, profile) if not repl is node: node.parent.replace(node, repl) # Support typical operators elif node.type in Util.ALL_OPERATORS: repl = Operation.compute(node) if repl is not None: node.parent.replace(node, repl)