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 findHighestPrioOperatorMiddle(self): """Find highest priority operator from elements in the middle.""" prio = -1 for ii in self.__middle: mid = ii.getSingleChildMiddleNonToken() if is_glsl_operator(mid): prio = max(prio, mid.getPrecedence()) return prio
def findSiblingOperatorRight(self): """Find nearest right operator.""" if not self.__parent: return None mid = self.__parent.getSingleChildMiddleNonToken() if not is_glsl_operator(mid): return None if self.__parent.getSingleChildRight() != self: return mid return self.__parent.findSiblingOperatorRight()
def collapseIdentity(self): """Collapse identity transforms that do not do anything.""" oper = self.getSingleChildMiddleNonToken() if not is_glsl_operator(oper): raise RuntimeError("must be an operator to collapse identities") left_token = self.getSingleChildLeft() right_token = self.getSingleChildRight() # If right or left tokens not found, not eligible for identity collapse. if (not left_token) or (not right_token): return False left = left_token.getSingleChild() right = right_token.getSingleChild() # Can't collapse if either side is in itself an operator. if is_glsl_operator(left) or is_glsl_operator(right): return False # Multiply by one. if oper.getOperator() == "*": if is_glsl_number(left) and (left.getFloat() == 1.0): self.collapseMiddleLeft() return True if is_glsl_number(right) and (right.getFloat() == 1.0): self.collapseMiddleRight() return True # Divide by one. elif oper.getOperator() == "/": if is_glsl_number(right) and (right.getFloat() == 1.0): self.collapseMiddleRight() return True # Substract zero. elif oper.getOperator() == "-": if is_glsl_number(right) and (right.getFloat() == 0.0): self.collapseMiddleRight() return True # Add zero. elif oper.getOperator() == "+": if is_glsl_number(left) and (left.getFloat() == 0.0): self.collapseMiddleLeft() return True if is_glsl_number(right) and (right.getFloat() == 0.0): self.collapseMiddleRight() return True # No collapses found. return False
def findEqualToken(self, orig): """Find a token or a number that has equal priority to given token.""" mid = self.getSingleChildMiddleNonToken() if not mid: return (None, None) if is_glsl_number(mid): return (orig, self) if is_glsl_operator(mid): if mid.getPrecedence() is orig.getPrecedenceIfOperator(): lt = self.getSingleChildLeft() if lt and lt.getSingleChildMiddleNonToken(): (lt_oper, lt_token) = lt.findEqualToken(self) if lt_oper and lt_token: return (lt_oper, lt_token) rt = self.getSingleChildRight() if rt and rt.getSingleChildMiddleNonToken(): (rt_oper, rt_token) = rt.findEqualToken(self) if rt_oper and rt_token: return (rt_oper, rt_token) return (None, None)
def token_tree_build(lst): """Builds and balances a token tree from given list.""" # Ensure all list elements are tokens. lst = token_list_create(lst) # Might be that everything is lost at this point. if not lst: return None # Start iteration over tokenized list. bracket_count = 0 paren_count = 0 first_bracket_index = -1 first_paren_index = -1 lowest_operator = None lowest_operator_index = -1 for ii in range(len(lst)): vv = lst[ii].getSingleChild() # Count parens. if is_glsl_paren(vv): # Bracket case. if vv.isBracket(): new_bracket_count = vv.updateBracket(bracket_count) if new_bracket_count == bracket_count: raise RuntimeError("wut?") bracket_count = new_bracket_count # Split on brackets reaching 0. if 0 >= bracket_count: if 0 > first_bracket_index: raise RuntimeError("bracket inconsistency") return token_tree_split_paren(lst, first_bracket_index, ii) elif (1 == bracket_count) and (0 > first_bracket_index): first_bracket_index = ii # Paren case. elif vv.isParen(): new_paren_count = vv.updateParen(paren_count) if new_paren_count == paren_count: raise RuntimeError("wut?") paren_count = new_paren_count # Split on parens reaching 0. if 0 >= paren_count: if 0 > first_paren_index: raise RuntimeError("paren inconsistency") return token_tree_split_paren(lst, first_paren_index, ii) elif (1 == paren_count) and (0 > first_paren_index): first_paren_index = ii # Curly braces impossible. else: raise RuntimeError("unknown paren object '%s'" % (str(vv))) # If we're not within parens, consider operators. if is_glsl_operator(vv) and (0 >= bracket_count) and (0 >= paren_count): if (not lowest_operator) or (vv < lowest_operator): lowest_operator = vv lowest_operator_index = ii # Iteration done. Make a tiny subtree on the lowest operator position and continue. if lowest_operator: ret = GlslToken(lowest_operator) left_block = [] right_block = [] left = None right = None # Get extending list left and right. if lowest_operator_index >= 2: left_block = lst[:(lowest_operator_index - 1)] if lowest_operator_index <= len(lst) - 3: right_block = lst[(lowest_operator_index + 2):] # Check for left existing. if lowest_operator_index >= 1: left = lst[lowest_operator_index - 1] ret.addLeft(left) elif not (lowest_operator in ("-", "++", "--", "!")): raise RuntimeError("left component nonexistent for operator '%s'" % (str(lowest_operator))) # Check for right existing. if lowest_operator_index <= len(lst) - 2: right = lst[lowest_operator_index + 1] ret.addRight(right) elif not (lowest_operator in ("++", "--")): raise RuntimeError( "right component nonexistent for operator '%s'" % (str(lowest_operator))) return token_tree_build(left_block + [ret] + right_block) # Only option at this point is that the list has no operators and no parens - return as itself. return GlslToken(lst)
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 getPrecedenceIfOperator(self): """Return precedence if middle element is a single child that is an operator.""" mid = self.getSingleChildMiddleNonToken() if mid and is_glsl_operator(mid): return mid.getPrecedence() return None
def token_tree_build(lst): """Builds and balances a token tree from given list.""" # Ensure all list elements are tokens. lst = token_list_create(lst) # Might be that everything is lost at this point. if not lst: return None # Start iteration over tokenized list. bracket_count = 0 paren_count = 0 first_bracket_index = -1 first_paren_index = -1 lowest_operator = None lowest_operator_index = -1 for ii in range(len(lst)): vv = lst[ii].getSingleChild() # Count parens. if is_glsl_paren(vv): # Bracket case. if vv.isBracket(): new_bracket_count = vv.updateBracket(bracket_count) if new_bracket_count == bracket_count: raise RuntimeError("wut?") bracket_count = new_bracket_count # Split on brackets reaching 0. if 0 >= bracket_count: if 0 > first_bracket_index: raise RuntimeError("bracket inconsistency") return token_tree_split_paren(lst, first_bracket_index, ii) elif (1 == bracket_count) and (0 > first_bracket_index): first_bracket_index = ii # Paren case. elif vv.isParen(): new_paren_count = vv.updateParen(paren_count) if new_paren_count == paren_count: raise RuntimeError("wut?") paren_count = new_paren_count # Split on parens reaching 0. if 0 >= paren_count: if 0 > first_paren_index: raise RuntimeError("paren inconsistency") return token_tree_split_paren(lst, first_paren_index, ii) elif (1 == paren_count) and (0 > first_paren_index): first_paren_index = ii # Curly braces impossible. else: raise RuntimeError("unknown paren object '%s'" % (str(vv))) # If we're not within parens, consider operators. if is_glsl_operator(vv) and (0 >= bracket_count) and (0 >= paren_count): if (not lowest_operator) or (vv < lowest_operator): lowest_operator = vv lowest_operator_index = ii # Iteration done. Make a tiny subtree on the lowest operator position and continue. if lowest_operator: ret = GlslToken(lowest_operator) left_block = [] right_block = [] left = None right = None # Get extending list left and right. if lowest_operator_index >= 2: left_block = lst[:(lowest_operator_index - 1)] if lowest_operator_index <= len(lst) - 3: right_block = lst[(lowest_operator_index + 2):] # Check for left existing. if lowest_operator_index >= 1: left = lst[lowest_operator_index - 1] ret.addLeft(left) elif not (lowest_operator in ("-", "++", "--", "!")): raise RuntimeError("left component nonexistent for operator '%s'" % (str(lowest_operator))) # Check for right existing. if lowest_operator_index <= len(lst) - 2: right = lst[lowest_operator_index + 1] ret.addRight(right) elif not (lowest_operator in ("++", "--")): raise RuntimeError("right component nonexistent for operator '%s'" % (str(lowest_operator))) return token_tree_build(left_block + [ret] + right_block) # Only option at this point is that the list has no operators and no parens - return as itself. return GlslToken(lst)
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