def applyOperator(self, oper, left_token, right_token): """Apply operator, collapsing into one number if possible.""" left = left_token.getSingleChild() right = right_token.getSingleChild() if (not is_glsl_number(left)) or (not is_glsl_number(right)): return None # Resolve to float if either is a float. if is_glsl_float(left) or is_glsl_float(right): left_number = left.getFloat() right_number = right.getFloat() else: left_number = left.getInt() right_number = right.getInt() if (left_number is None) or (right_number is None): raise RuntimeError("error getting number values") # Perform operation. result_number = oper.applyOperator(left_number, right_number) # Replace content of this with the result number if is_glsl_float(left) or is_glsl_float(right): number_string = str(float(result_number)) (integer_part, decimal_part) = number_string.split(".") result_number = interpret_float(integer_part, decimal_part) else: result_number = interpret_int(str(result_number)) # Not all operations require truncation afterwards. if oper.requiresTruncation(): lower_precision = min(left.getPrecision(), right.getPrecision()) + 1 precision = max(max(left.getPrecision(), right.getPrecision()), lower_precision) result_number.truncatePrecision(precision) return result_number
def validate_token(token, validation): """Validate that token matches given requirement.""" # Unsigned int. if "u" == validation: if not is_glsl_int_unsigned(token): return None # Int. elif "i" == validation: if not is_glsl_int(token): return None # Float. elif "f" == validation: if not is_glsl_float(token): return None # Access. elif "a" == validation: if not is_glsl_access(token): return None # Operator =. elif validation in ("="): if (not is_glsl_operator(token)) or (not token.isAssignment()): return None # Control. elif "c" == validation: if not is_glsl_control(token): return None # In/out. elif "o" == validation: if not is_glsl_inout(token): return None # Operator. elif "p" == validation: if not is_glsl_operator(token): return None # Type. elif "t" == validation: if not is_glsl_type(token): return None # Terminator. elif ";" == validation: if not is_glsl_terminator(token): return None # Valid identifier name. elif "n" == validation: if not is_glsl_name(token): return None # Select from options. elif validation.find("|") >= 0: variations = list(filter(lambda x: x, validation.split("|"))) if not check_token(token, variations): return None # Unknown validation. else: raise RuntimeError("unknown token request '%s'" % (validation)) # On success, return token as-is. return token
def is_glsl_number(op): """Tell if given object is a number.""" if is_glsl_float(op) or is_glsl_int(op): return True return False
def simplify(self): """Perform any simple simplification and stop.""" # Remove parens. if self.isSurroundedByParens(): middle_lst = self.flattenMiddle() #debug_str = " ".join(map(lambda x: str(x), middle_lst)) #if debug_str.find("normalize") > -1: # print(debug_str) # Single expression. if len(middle_lst) == 1: if self.removeParens(): return True # Number or name with access. elif len(middle_lst) == 2: mid_lt = middle_lst[0] mid_rt = middle_lst[1] if (is_glsl_name(mid_lt) or is_glsl_number(mid_lt)) and is_glsl_access(mid_rt): if self.removeParens(): return True # Single function call or indexing (with potential access). elif len(middle_lst) >= 3: mid_name = middle_lst[0] mid_opening = middle_lst[1] last_index = -1 mid_ending = middle_lst[last_index] # If last part is access, try the element before that instead. if is_glsl_access(mid_ending) and (len(middle_lst) >= 4): last_index = -2 mid_ending = middle_lst[last_index] # Check for function call or indexing format. if (is_glsl_name(mid_name) or is_glsl_type(mid_name)) and is_glsl_paren( mid_opening) and mid_opening.matches(mid_ending): if is_single_call_or_access_list(middle_lst[2:last_index], mid_opening): if self.removeParens(): return True # Only contains lower-priority operators compared to outside. paren_rt = self.__right[0].getSingleChild() elem_rt = self.findRightSiblingElementFromParentTree(paren_rt) prio = self.findHighestPrioOperatorMiddle() # Right element cannot be access or bracket. if (prio >= 0) and (not is_glsl_access(elem_rt)) and (elem_rt != "["): left = self.findSiblingOperatorLeft() right = self.findSiblingOperatorRight() if left: if left.getPrecedence() > prio: if right: if right.getPrecedence() >= prio: if self.removeParens(): return True else: if self.removeParens(): return True elif right: if right.getPrecedence() >= prio: if self.removeParens(): return True else: if self.removeParens(): return True # Recurse down. for ii in self.__left: if ii.simplify(): return True for ii in self.__right: if ii.simplify(): return True for ii in self.__middle: if is_glsl_token(ii): if ii.simplify(): return True # Perform operations only after removing any possible parens. if (len(self.__middle) == 1): oper = self.__middle[0] if is_glsl_operator(oper) and oper.isApplicable(): # Try to remove trivial cases. if self.collapseIdentity(): return True (left_parent, left_token) = self.findEqualTokenLeft(self) (right_parent, right_token) = self.findEqualTokenRight(self) if left_parent and left_token and right_parent and right_token: # Trivial case - leaf entry that can just be applied. if left_parent is right_parent: if not (left_parent is self): raise RuntimeError( "left and right operator resolve as '%s' not matching self '%s'" % (str(left_parent), str(self))) result = self.applyOperator(oper, left_token, right_token) if not (result is None): # Remove sides from parent. left_token.removeFromParent() right_token.removeFromParent() self.__middle = [result] return True # Nontrivial cases. left_oper = left_parent.getSingleChildMiddleNonToken() right_oper = right_parent.getSingleChildMiddleNonToken() result = None # Double divide -> multiply. if (left_oper == "/") and ( right_oper == "/") and left_token.isSingleChildRight(): result = self.applyOperator(interpret_operator("*"), left_token, right_token) # Same operation -> can be just applied. elif left_parent == right_parent: result = self.applyOperator(left_oper, left_token, right_token) # Substract addition: <something> - a + b => <something> + (b - a) elif (left_oper == "-") and (oper == "+") and (oper is right_oper): result = self.applyOperator(left_oper, right_token, left_token) # If b - a is negative, replace it with its absolute value which is going to get subtracted. if result.getFloat() < 0.0: right_oper.setOperator("-") if is_glsl_int(result): result = interpret_int( str(abs(result.getInt()))) elif is_glsl_float(result): number_string = str(abs(result.getFloat())) (integer_part, decimal_part) = number_string.split(".") result = interpret_float( integer_part, decimal_part) else: raise RuntimeError( "unknown result object '%s'" % (str(result))) # TODO: further cases. # On success, eliminate upper token (left only if necessary) and replace other token with result. if result: if left_parent is self: right_token.collapseUp() left_token.replaceMiddle(result) else: left_token.collapseUp() right_token.replaceMiddle(result) return True # Operations are done. Allow simplification of remaining constans, if possible. mid = self.getSingleChildMiddleNonToken() if mid and is_glsl_float(mid) and (not mid.isIntegrifyAllowed()): # No operators, left or right. left = self.findSiblingOperatorLeft() right = self.findSiblingOperatorRight() if not left and not right: mid.setAllowIntegrify(True) return True # Alone in vecN() directive. left = self.getSingleChildLeft() right = self.getSingleChildRight() if left and left.isTypeOpen() and right and ( right.getSingleChildMiddleNonToken() == ")"): mid.setAllowIntegrify(True) return True # If could not be integrified, at least ensure that float precision is not exceeded. if mid.getPrecision() > 6: mid.truncatePrecision(6) return True return False
def simplify(self): """Perform any simple simplification and stop.""" # Remove parens. if self.isSurroundedByParens(): middle_lst = self.flattenMiddle() # Single expression. if len(middle_lst) == 1: if self.removeParens(): return True # Number or name with access. elif len(middle_lst) == 2: mid_lt = middle_lst[0] mid_rt = middle_lst[1] if (is_glsl_name(mid_lt) or is_glsl_number(mid_lt)) and is_glsl_access(mid_rt): if self.removeParens(): return True # Single function call or indexing (with potential access). elif len(middle_lst) >= 3: mid_name = middle_lst[0] mid_opening = middle_lst[1] last_index = -1 mid_ending = middle_lst[last_index] # If last part is access, try the element before that instead. if is_glsl_access(mid_ending) and (len(middle_lst) >= 4): last_index = -2 mid_ending = middle_lst[last_index] # Check for function call or indexing format. if (is_glsl_name(mid_name) or is_glsl_type(mid_name)) and is_glsl_paren(mid_opening) and mid_opening.matches(mid_ending): if is_single_call_or_access_list(middle_lst[2:last_index], mid_opening): if self.removeParens(): return True # Only contains lower-priority operators compared to outside. paren_rt = self.__right[0].getSingleChild() elem_rt = self.findRightSiblingElementFromParentTree(paren_rt) prio = self.findHighestPrioOperatorMiddle() # Right element cannot be access or bracket. if (prio >= 0) and (not is_glsl_access(elem_rt)) and (elem_rt != "["): left = self.findSiblingOperatorLeft() right = self.findSiblingOperatorRight() if left: if left.getPrecedence() > prio: if right: if right.getPrecedence() >= prio: if self.removeParens(): return True else: if self.removeParens(): return True elif right: if right.getPrecedence() >= prio: if self.removeParens(): return True else: if self.removeParens(): return True # Recurse down. for ii in self.__left: if ii.simplify(): return True for ii in self.__right: if ii.simplify(): return True for ii in self.__middle: if is_glsl_token(ii): if ii.simplify(): return True # Perform operations only after removing any possible parens. if (len(self.__middle) == 1): oper = self.__middle[0] if is_glsl_operator(oper) and oper.isApplicable(): # Try to remove trivial cases. if self.collapseIdentity(): return True (left_parent, left_token) = self.findEqualTokenLeft(self) (right_parent, right_token) = self.findEqualTokenRight(self) if left_parent and left_token and right_parent and right_token: # Trivial case - leaf entry that can just be applied. if left_parent is right_parent: if not (left_parent is self): raise RuntimeError("left and right operator resolve as '%s' not matching self '%s'" % (str(left_parent), str(self))) result = self.applyOperator(oper, left_token, right_token) if not (result is None): # Remove sides from parent. left_token.removeFromParent() right_token.removeFromParent() self.__middle = [result] return True # Nontrivial cases. left_oper = left_parent.getSingleChildMiddleNonToken() right_oper = right_parent.getSingleChildMiddleNonToken() result = None # Double divide -> multiply. if (left_oper == "/") and (right_oper == "/") and left_token.isSingleChildRight(): result = self.applyOperator(interpret_operator("*"), left_token, right_token) # Same operation -> can be just applied. elif left_parent == right_parent: result = self.applyOperator(left_oper, left_token, right_token) # Substract addition: <something> - a + b => <something> + (b - a) elif (left_oper == "-") and (oper == "+") and (oper is right_oper): result = self.applyOperator(left_oper, right_token, left_token) # If b - a is negative, replace it with its absolute value which is going to get subtracted. if result.getFloat() < 0.0: right_oper.setOperator("-") if is_glsl_int(result): result = interpret_int(str(abs(result.getInt()))) elif is_glsl_float(result): number_string = str(abs(result.getFloat())) (integer_part, decimal_part) = number_string.split(".") result = interpret_float(integer_part, decimal_part) else: raise RuntimeError("unknown result object '%s'" % (str(result))) # TODO: further cases. # On success, eliminate upper token (left only if necessary) and replace other token with result. if result: if left_parent is self: right_token.collapseUp() left_token.replaceMiddle(result) else: left_token.collapseUp() right_token.replaceMiddle(result) return True # Operations are done. Allow simplification of remaining constans, if possible. mid = self.getSingleChildMiddleNonToken() if mid and is_glsl_float(mid) and (abs(mid.getFloat()) <= 2147483647.0) and (not mid.isIntegrifyAllowed()): # No operators, left or right. left = self.findSiblingOperatorLeft() right = self.findSiblingOperatorRight() if (not left) and (not right): mid.setAllowIntegrify(True) return True # Alone in vecN() directive. left = self.getSingleChildLeft() right = self.getSingleChildRight() if left and left.isTypeOpen() and right and (right.getSingleChildMiddleNonToken() == ")"): mid.setAllowIntegrify(True) return True # If could not be integrified, at least ensure that float precision is not exceeded. if mid.getPrecision() > 6: mid.truncatePrecision(6) return True return False