def inlinePass(self, allow_inline): """Run inline pass. Return list of merged names if no inlining could be done.""" # Collect identifiers. First pass - collect from generic sources and append from non-generic. collected = [] for ii in self.__sources: if not ii.getType(): collect_pass = ii.collect() for jj in self.__sources: if jj.getType(): for kk in collect_pass: jj.collectAppend(kk) collected += collect_pass # Second pass - collect from non-generic sources. Do not append. for ii in self.__sources: if ii.getType(): collected += ii.collect() # Merge multiple matching inout names. merged = sorted(merge_collected_names(collected), key=len, reverse=True) # Collect all member accesses for members and set them to the blocks. for ii in merged: block = ii[0] if is_listing(block): block = block[0] if not (is_glsl_block_inout_struct(block) or is_glsl_block_struct(block)): continue lst = collect_member_accesses(ii[0], ii[1:]) block.setMemberAccesses(lst) # If inlining is not allowed, just return merged block. if not allow_inline: return merged # Perform inlining if possible. for ii in range(len(merged)): vv = merged[ii] block = vv[0] if is_listing(block) or (not is_glsl_block_declaration(block)): continue names = vv[1:] if not is_inline_name(names[0]): continue # If no inline conflict, perform inline and return nothing to signify another pass can be done. if not self.hasInlineConflict(block, names): self.inline(block, names) return None # Return merged list. return merged
def deconstruct(self): """Deconstruct into byte stream.""" lst = [] if is_listing(self.__value): for ii in self.__value: if not is_deconstructable(ii): break lst += self.deconstruct_single(int(ii)) elif is_deconstructable(self.__value): lst = self.deconstruct_single(int(self.__value)) if 0 >= len(lst): return None if 1 >= len(lst): return [self] ret = [] for ii in range(len(lst)): struct_elem = lst[ii] if isinstance(struct_elem, str): var = AssemblerVariable(("", 1, ord(struct_elem))) else: var = AssemblerVariable(("", 1, int(struct_elem))) if 0 == ii: var.__desc = self.__desc var.__name = self.__name var.__original_size = self.__size var.__label_pre = self.__label_pre elif len(lst) - 1 == ii: var.__label_post = self.__label_post ret += [var] return ret
def merge_collected_names_inout(lst): """Merge inout blocks from given list of names.""" ret = [] for ii in lst: if is_glsl_block_inout(ii[0]): found = False for jj in range(len(ret)): vv = ret[jj] block = vv[0] if is_listing(block): block = block[0] if is_glsl_block_inout(block) and block.isMergableWith(ii[0]): if ii[1] != ii[0].getName(): raise RuntimeError( "inout block inconsistency: '%s' vs. '%s'" % (ii[1], ii[0].getName())) if vv[1] != block.getName(): raise RuntimeError( "inout block inconsistency: '%s' vs. '%s'" % (vv[1], vv[0].getName())) ret[jj] = merge_collected_name_lists(vv, ii) found = True break if found: continue ret += [ii] return ret
def addAccesses(self, op): if is_listing(op): for ii in op: self.addAccesses(ii) return if not is_glsl_access(op): return self.__accesses += [op]
def addExtraFlags(self, op): """Add extra flags to use when linking.""" if is_listing(op): for ii in op: self.addExtraFlags(ii) return if not (op in self.__linker_flags_extra): self.__linker_flags_extra += [op]
def addExtraFlags(self, op): """Add extra flags to use when assembling.""" if is_listing(op): for ii in op: self.addExtraFlags(ii) return if not (op in self.__assembler_flags_extra): self.__assembler_flags_extra += [op]
def add_extra_compiler_flags(self, op): """Add extra compiler flags.""" if is_listing(op): for ii in op: self.add_extra_compiler_flags(ii) return if not (op in self._compiler_flags_extra): if (not (op in self._include_directories)) and (not (op in self._definitions)): self._compiler_flags_extra += [op]
def addNamesUsed(self, op): """Add given names as names used by this block.""" if is_listing(op): for ii in op: self.addNamesUsed(ii) return if not is_glsl_name(op): return self.__names_used += [op]
def addAccesses(self, op): """Adds access elements in the block to the block's access list.""" if is_listing(op): for ii in op: self.addAccesses(ii) return if not is_glsl_access(op): return self.__accesses += [op]
def __init__(self, lst, terminator = ""): """Constructor.""" GlslBlock.__init__(self) self.__content = lst self.__terminator = terminator if (not is_listing(self.__content)) or (None in self.__content): raise RuntimeError("content must be a listing") # Hierarchy. self.addAccesses(lst) self.addNamesUsed(lst)
def __init__(self, lst, terminator=""): """Constructor.""" GlslBlock.__init__(self) self.__content = lst self.__terminator = terminator if (not is_listing(self.__content)) or (None in self.__content): raise RuntimeError("content must be a listing") # Hierarchy. self.addAccesses(lst) self.addNamesUsed(lst)
def addNamesDeclared(self, op): """Add given names as names declared by this block.""" if is_listing(op): for ii in op: self.addNamesDeclared(ii) return if not is_glsl_name(op): return if op in self.__names_declared: raise RuntimeError("declaring name '%s' twice" % (op)) self.__names_declared.add(op)
def format_label(self, op): """Generate name labels.""" if not op: return "" ret = "" if is_listing(op): for ii in op: ret += format_label(ii) else: ret += ".globl %s\n%s:\n" % (op, op) return ret
def __init__(self, op): """Constructor.""" self.__name = None self.__desc = None self.__data = [] if isinstance(op, str): self.__name = op self.__desc = None elif is_listing(op): for ii in op: if is_listing(ii): self.add_data(ii) elif not self.__name: self.__name = ii elif not self.__desc: self.__desc = ii else: raise RuntimeError("too many string arguments for list constructor") self.refresh_name_label() self.refresh_name_end_label()
def addRight(self, op): """Add right child token.""" # List case. if is_listing(op): for ii in op: self.addRight(ii) return # Single case. if not is_glsl_token(op): raise RuntimeError("trying to add right non-token '%s'" % (str(left))) self.__right += [op] op.setParent(self)
def merge_collected_name_lists(lst1, lst2): """Merge two collected names lists.""" if is_listing(lst2[0]): raise RuntimeError( "expected non-listing as first element of collected name list 2, got: %s" % (str(lst2[0]))) if is_listing(lst1[0]): ret = [lst1[0] + [lst2[0]]] else: ret = [[lst1[0], lst2[0]]] ret += lst1[1:] # It is possible both listings contain some exact same following values, do not simply catenate. for ii in lst2[1:]: found = False for jj in ret[1:]: if ii is jj: found = True break if not found: ret += [ii] return ret
def __init__(self, op, name=None): """Constructor.""" if not is_listing(op): raise RuntimeError("only argument passed is not a list") self.__desc = op[0] self.__size = op[1] self.__value = op[2] self.__name = name self.__original_size = -1 self.__label_pre = [] self.__label_post = [] if 3 < len(op): self.add_label_pre(op[3])
def check_token(token, req): """Check if token is acceptable but do not compare against types.""" # Check against list, any option is ok. if is_listing(req): for ii in req: if check_token(token, ii): return True return False if not isinstance(req, str): raise RuntimeError("request '%s' is not a string" % (str(req))) # Tokens are converted to strings for comparison. if isinstance(token, str) and (token == req): return True return (token.format(False) == req)
def addMiddle(self, op): """Add middle child token.""" # List case. if is_listing(op): for ii in op: self.addMiddle(ii) return # Tokens added normally. if is_glsl_token(op): self.__middle += [op] op.setParent(self) # Non-tokens added as-is. else: self.__middle += [op]
def collect_member_uses(op, uses): """Collect member uses from inout struct blocks.""" # List case. if is_listing(op): for ii in op: collect_member_uses(ii, uses) return # Actual inout block. for ii in op.getMembers(): name_object = ii.getName() name_string = name_object.getName() if name_string in uses: uses[name_string] += [name_object] else: uses[name_string] = [name_object]
def __init__(self, typeid, name, lst, scope): """Constructor.""" GlslBlock.__init__(self) self.__typeid = typeid self.__name = name self.__parameters = lst self.__scope = scope if not is_listing(self.__parameters): raise RuntimeError("parameters must be a listing") # Hierarchy. name.setType(typeid) self.addNamesDeclared(name) self.addNamesUsed(name) self.addChildren(lst) self.addChildren(scope)
def glsl_parse_parameter_list(source): """Parse list of parameters.""" # Empty parameter list is ok. if is_listing(source) and (0 >= len(source)): return [] ret = [] parameters = glsl_split_parameter_list(source) for ii in parameters: (parameter, remaining) = glsl_parse_parameter(ii) if not parameter: raise RuntimeError("could not parse parameter from '%s'" % (str(map(str, ii)))) if remaining: raise RuntimeError("extra content after parameter: '%s'" % (str(map(str, remaining)))) ret += [parameter] return ret
def addChildren(self, lst, prepend=False): """Add another block as a child of this.""" if not is_listing(lst): self.addChildren([lst], prepend) return for ii in lst: if not is_glsl_block(ii): raise RuntimeError("element '%s' to be added is not of type GlslBlock" % (str(ii))) if ii.getParent(): raise RuntimeError("block '%s' to be added already has parent '%s'" % (str(ii), str(ii.getParent()))) if prepend: self._children = [ii] + self._children else: self._children += [ii] ii.setParent(self)
def glsl_parse_member_list(source): """Parse list of members.""" # Empty member list is ok. if is_listing(source) and (0 >= len(source)): return [] (member, content) = glsl_parse_member(source) if not member: raise RuntimeError("error parsing members: %s" % (str(map(str, source)))) ret = [member] while content: (member, remaining) = glsl_parse_member(content) if not member: raise RuntimeError("error parsing members: %s" % (str(map(str, content)))) ret += [member] content = remaining return ret
def getSingleChild(self): """Degeneration preventation. If token only has a single child, return that instead.""" lr = len(self.__left) + len(self.__right) # If left and right exist, return node itself - there is no single child here. if 0 < lr: if not self.__middle and ((not self.__left) or (not self.__right)): raise RuntimeError("empty middle only allowed if bothe left and right exist") return self # If left and right did not exist, return middle. elif self.__middle: if is_listing(self.__middle): if 1 >= len(self.__middle): return self.__middle[0] return self.__middle # Should never happen. raise RuntimeError("token has no content")
def hasInlineConflict(self, block, names): """Tell if given block has an inlining conflict.""" # If block is a listing, just go over all options. if is_listing(block): for ii in block: if self.hasInlineConflict(ii, names): return True return False # Check for inline conflicts within this block. parent = find_parent_scope(block) if is_glsl_block_source(parent): for ii in self.__sources: if (ii != parent) and ((not parent.getType()) or (not ii.getType())): if has_inline_conflict(ii, block, names): return True return has_inline_conflict(parent, block, names)
def extract_tokens(tokens, required): """Require tokens from token string, return selected elements and the rest of tokens.""" # If required is just a string, make it a listing of length one. if not is_listing(required): required = (required,) # Generate array for returning on failure. failure_array = [] for ii in required: if "?" == ii[:1]: failure_array += [None] failure_array += [tokens] # For straight-out incompatible request, get out immediately. if len(required) > len(tokens): return failure_array # Iterate over requests. content = tokens[:] required = list(required) ret = [] success = True while content and required: curr = content.pop(0) req = required.pop(0) # Token request. if "?" == req[:1]: desc = req[1:] # Extracting scope. if desc in ("{", "[", "("): if curr.format(False) == desc: (scope, remaining) = extract_scope(content, curr) if not (scope is None): ret += [scope] content = remaining continue # Scope not found. return failure_array # Extracting singular element. validated = validate_token(curr, desc) if validated: ret += [validated] else: return failure_array # Not a request, compare verbatim. Names can be compared verbatim. elif not check_token(curr, req): return failure_array # Successful, return the remaining elements. return ret + [content]
def extract_tokens(tokens, required): """Require tokens from token string, return selected elements and the rest of tokens.""" # If required is just a string, make it a listing of length one. if not is_listing(required): required = (required, ) # Generate array for returning on failure. failure_array = [] for ii in required: if "?" == ii[:1]: failure_array += [None] failure_array += [tokens] # For straight-out incompatible request, get out immediately. if len(required) > len(tokens): return failure_array # Iterate over requests. content = tokens[:] required = list(required) ret = [] success = True while content and required: curr = content.pop(0) req = required.pop(0) # Token request. if "?" == req[:1]: desc = req[1:] # Extracting scope. if desc in ("{", "[", "("): if curr.format(False) == desc: (scope, remaining) = extract_scope(content, curr) if not (scope is None): ret += [scope] content = remaining continue # Scope not found. return failure_array # Extracting singular element. validated = validate_token(curr, desc) if validated: ret += [validated] else: return failure_array # Not a request, compare verbatim. Names can be compared verbatim. elif not check_token(curr, req): return failure_array # Successful, return the remaining elements. return ret + [content]
def hasNameConflict(self, block, name): """Tell if given block would have a conflict if renamed into given name.""" # If block is a listing, just go over all options. if is_listing(block): for ii in block: if self.hasNameConflict(ii, name): return True return False # Check for conflicts within this block. parent = find_parent_scope(block) if is_glsl_block_source(parent): for ii in self.__sources: if (ii != parent) and ((not parent.getType()) or (not ii.getType())): if has_name_conflict(ii, block, name): return True return has_name_conflict(parent, block, name)
def getSingleChild(self): """Degeneration preventation. If token only has a single child, return that instead.""" lr = len(self.__left) + len(self.__right) # If left and right exist, return node itself - there is no single child here. if 0 < lr: if not self.__middle and ((not self.__left) or (not self.__right)): raise RuntimeError( "empty middle only allowed if bothe left and right exist") return self # If left and right did not exist, return middle. elif self.__middle: if is_listing(self.__middle): if 1 >= len(self.__middle): return self.__middle[0] return self.__middle # Should never happen. raise RuntimeError("token has no content")
def token_descend(token): """Descend token or token list.""" # Single element case. if is_glsl_token(token): token.setParent(None) single = token.getSingleChild() if single == token: return token return token_descend(single) # Listing case. if is_listing(token): for ii in token: ii.setParent(None) if not is_glsl_token(ii): raise RuntimeError("non-token '%s' found in descend" % (str(ii))) if len(token) == 1: return token_descend(token[0]) # Could not descend. return token
def merge_collected_names_function(lst): """Merge inout blocks from given list of names.""" ret = [] for ii in lst: if is_glsl_block_function(ii[0]): found = False for jj in range(len(ret)): vv = ret[jj] block = vv[0] if is_listing(block): block = block[0] if is_glsl_block_function(block) and (block.getName() == ii[0].getName()): ret[jj] = merge_collected_name_lists(vv, ii) found = True break if found: continue ret += [ii] return ret
def renameBlockType(self, block): """Rename block type for given name strip.""" # Select name to rename to. if not target_name: counted = self.countSorted() # Single-character names first. for letter in counted: if not self.hasNameConflict(block, letter): target_name = letter break # None of the letters was free, invent new one. if not target_name: target_name = self.inventName(block, counted) # Listing case. if is_listing(block): for ii in block: self.renameBlock(ii, target_name) return # Just select first name. block.getTypeName().lock(target_name)
def add_label_pre(self, op): """Add pre-label(s).""" if is_listing(op): self.__label_pre += op else: self.__label_pre += [op]
def add_label_post(self, op): """Add post-label(s).""" if is_listing(op): self.__label_post += op else: self.__label_post += [op]
def crunch(self, mode="full", max_inlines=-1, max_renames=-1, max_simplifys=-1): """Crunch the source code to smaller state.""" combines = None inlines = None renames = None simplifys = None # Expand unless crunching completely disabled. if "none" != mode: for ii in self.__sources: ii.expandRecursive() # Perform inlining passes. inlines = 0 while True: inline_pass_rv = self.inlinePass((max_inlines < 0) or (inlines < max_inlines)) # Last pass will return a listing of merged variable names. if is_listing(inline_pass_rv): merged = inline_pass_rv break # Inlining was done, another round. inlines += 1 # Perform simplification passes. simplifys = 0 for ii in self.__sources: if (0 <= max_simplifys) and (simplifys >= max_simplifys): break simplifys += simplify_pass(ii, max_simplifys - simplifys) # After all names have been collected, it's possible to select the best swizzle. swizzle = self.selectSwizzle() for ii in self.__sources: ii.selectSwizzle(swizzle) # Print number of inout merges. if is_verbose(): inout_merges = [] for ii in merged: block = ii[0] if is_listing(block): inout_merges += [block[0]] if inout_merges: print("GLSL inout connections found: %s" % (str(map(str, inout_merges)))) # Run rename passes until done. renames = 0 for ii in merged: if (0 <= max_renames) and (renames >= max_renames): break self.renamePass(ii[0], ii[1:]) renames += 1 # Run member rename passes until done. for ii in merged: block = ii[0] if is_listing(block): block = block[0] if not is_glsl_block_inout_struct(block): continue renames += self.renameMembers(block, max_renames - renames) # Also rename block type. if (0 > max_renames) or (renames < max_renames): self.renameBlock(ii[0]) renames += 1 # Perform recombine passes. for ii in self.__sources: combines = ii.collapseRecursive(mode) # Print summary of operations. if is_verbose(): operations = [] if inlines: operations += ["%i inlines" % (inlines)] if simplifys: operations += ["%i simplifys" % (simplifys)] if renames: operations += ["%i renames" % (renames)] if combines: operations += ["%i combines" % (combines)] if operations: print("GLSL processing done: %s" % (", ".join(operations)))