def tokenize_interpret(tokens): """Interpret a list of preliminary tokens, assembling constructs from them.""" ret = [] ii = 0 while len(tokens) > ii: element = tokens[ii] # Try paren. paren = interpret_paren(element) if paren: ret += [paren] ii += 1 continue # Try 2-stage control. if (ii + 1) < len(tokens): control = interpret_control(element, tokens[ii + 1]) if control: ret += [control] ii += 2 continue # Try control. control = interpret_control(element) if control: ret += [control] ii += 1 continue # Try in/out. inout = interpret_inout(element) if inout: ret += [inout] ii += 1 continue # Try 2-stage type. if (ii + 1) < len(tokens): typeid = interpret_type(element, tokens[ii + 1]) if typeid: ret += [typeid] ii += 2 continue # Try type. typeid = interpret_type(element) if typeid: ret += [typeid] ii += 1 continue # Period may signify truncated floating point or member/swizzle access. if "." == tokens[ii] and (ii + 1) < len(tokens): decimal = interpret_int(tokens[ii + 1]) if decimal: ret += [interpret_float(0, decimal)] ii += 2 continue access = interpret_access(tokens[ii + 1]) if access: access.setSource(ret) ret += [access] ii += 2 continue # Number may be just an integer or floating point. number = interpret_int(element) if number: if (ii + 1) < len(tokens) and "." == tokens[ii + 1]: if (ii + 2) < len(tokens): decimal = interpret_int(tokens[ii + 2]) if decimal: ret += [interpret_float(number, decimal)] ii += 3 continue ret += [interpret_float(number, 0)] ii += 2 continue ret += [number] ii += 1 continue # Special characters may be operators, up to two in a row. operator = interpret_operator(element) if operator: if (ii + 1) < len(tokens): extended_operator = interpret_operator(tokens[ii + 1]) if extended_operator and operator.incorporate( extended_operator): ret += [operator] ii += 2 continue ret += [operator] ii += 1 continue # Statement terminator. terminator = interpret_terminator(element) if terminator: ret += [terminator] ii += 1 continue # Try name identifier last. name = interpret_name(element) if name: ret += [name] ii += 1 continue # Fallback is to add token as-is. print("WARNING: GLSL: unknown element '%s'" % element) ret += [element] ii += 1 return ret
def tokenize_interpret(tokens): """Interpret a list of preliminary tokens, assembling constructs from them.""" ret = [] ii = 0 while len(tokens) > ii: element = tokens[ii] # Try paren. paren = interpret_paren(element) if paren: ret += [paren] ii += 1 continue # Try 2-stage control. if (ii + 1) < len(tokens): control = interpret_control(element, tokens[ii + 1]) if control: ret += [control] ii += 2 continue # Try control. control = interpret_control(element) if control: ret += [control] ii += 1 continue # Try in/out. inout = interpret_inout(element) if inout: ret += [inout] ii += 1 continue # Try 2-stage type. if (ii + 1) < len(tokens): typeid = interpret_type(element, tokens[ii + 1]) if typeid: ret += [typeid] ii += 2 continue # Try type. typeid = interpret_type(element) if typeid: ret += [typeid] ii += 1 continue # Period may signify truncated floating point or member/swizzle access. if "." == tokens[ii] and (ii + 1) < len(tokens): decimal = interpret_int(tokens[ii + 1]) if decimal: ret += [interpret_float(0, decimal)] ii += 2 continue access = interpret_access(tokens[ii + 1]) if access: access.setSource(ret) ret += [access] ii += 2 continue # Number may be just an integer or floating point. number = interpret_int(element) if number: if (ii + 1) < len(tokens) and "." == tokens[ii + 1]: if (ii + 2) < len(tokens): decimal = interpret_int(tokens[ii + 2]) if decimal: ret += [interpret_float(number, decimal)] ii += 3 continue ret += [interpret_float(number, 0)] ii += 2 continue ret += [number] ii += 1 continue # Special characters may be operators, up to two in a row. operator = interpret_operator(element) if operator: if (ii + 1) < len(tokens): extended_operator = interpret_operator(tokens[ii + 1]) if extended_operator and operator.incorporate(extended_operator): ret += [operator] ii += 2 continue ret += [operator] ii += 1 continue # Statement terminator. terminator = interpret_terminator(element) if terminator: ret += [terminator] ii += 1 continue # Try name identifier last. name = interpret_name(element) if name: ret += [name] ii += 1 continue # Fallback is to add token as-is. print("WARNING: GLSL: unknown element '%s'" % element) ret += [element] ii += 1 return ret
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