1# Copyright 2014-2018 the openage authors. See copying.md for legal info. 2 3# TODO pylint: disable=C,R 4 5from enum import Enum 6 7from ...log import spam 8 9 10class SectionType(Enum): 11 section_header = "header" 12 section_body = "body" 13 14 15class ContentSnippet: 16 """ 17 one part of text for generated files to be saved in "file_name" 18 19 before whole source files can be written, it's content snippets 20 have to be ordered according to their dependency chain. 21 22 also, each snipped can have import requirements that have to be 23 included in top of the source. 24 """ 25 26 def __init__(self, data, file_name, section, orderby=None, reprtxt=None): 27 self.data = data # snippet content 28 self.file_name = file_name # snippet wants to be saved in this file 29 self.typerefs = set() # these types are referenced 30 self.typedefs = set() # these types are defined 31 self.includes = set() # needed snippets, e.g. headers 32 self.section = section # place the snippet in this file section 33 self.orderby = orderby # use this value for ordering snippets 34 self.reprtxt = reprtxt # representation text 35 36 # snippets to be positioned before this one 37 self.required_snippets = set() 38 39 # snippet content is ready by default. 40 # subclasses may require generation. 41 self.data_ready = True 42 43 def get_data(self): 44 if not self.data_ready: 45 self.generate_content() 46 return self.data 47 48 def generate_content(self): 49 # no generation needed by default 50 pass 51 52 def add_required_snippets(self, snippet_list): 53 """ 54 save required snippets for this one by looking at wanted type 55 references. 56 57 the available candidates have to be passed as argument 58 """ 59 60 self.required_snippets |= { 61 s for s in snippet_list if len(self.typerefs & s.typedefs) > 0 62 } 63 64 spam("snippet %s requires %s", 65 repr(self), repr(self.required_snippets)) 66 67 resolved_types = set() 68 for s in self.required_snippets: 69 resolved_types |= (self.typerefs & s.typedefs) 70 71 missing_types = self.typerefs - resolved_types 72 73 return missing_types 74 75 def get_required_snippets(self): 76 """ 77 return all referenced and the snippet itself in the order they 78 need to be put in the file. 79 """ 80 81 # TODO: loop detection 82 ret = list() 83 84 # sort snippets deterministically by __lt__ function 85 for s in sorted(self.required_snippets): 86 ret += s.get_required_snippets() 87 88 ret.append(self) 89 return ret 90 91 def __hash__(self): 92 """ 93 hash all relevant snippet properties 94 """ 95 96 return hash(( 97 self.data, 98 self.file_name, 99 self.section, 100 frozenset(self.typedefs), 101 frozenset(self.typerefs), 102 )) 103 104 def __lt__(self, other): 105 """ 106 comparison of two snippets for their ordering 107 """ 108 109 if isinstance(other, type(self)) or isinstance(self, type(other)): 110 if not (other.orderby and self.orderby): 111 faild = self if other.orderby else other 112 raise Exception("%s doesn't have orderby member set" % ( 113 repr(faild))) 114 else: 115 ret = self.orderby < other.orderby 116 return ret 117 else: 118 raise TypeError("unorderable types: %s < %s" % ( 119 type(self), type(other) 120 )) 121 122 def __eq__(self, other): 123 """ 124 equality check for text snippets 125 """ 126 127 if type(other) is not type(self): 128 return False 129 130 return ( 131 self.file_name == other.file_name and 132 self.data == other.data and 133 self.section == other.section and 134 self.typedefs == other.typedefs and 135 self.typerefs == other.typerefs 136 ) 137 138 def __repr__(self): 139 if self.reprtxt: 140 data = " = %s" % self.reprtxt 141 elif self.data: 142 data = " = %s..." % repr(self.data[:25]) 143 else: 144 data = "" 145 146 return "%s(file=%s)%s" % (self.__class__.__name__, 147 self.file_name, data) 148 149 def __str__(self): 150 if self.data_ready: 151 return "".join(( 152 repr(self), ", " 153 "data = '", str(self.data), "'" 154 )) 155 else: 156 return "".join((repr(self), ": lazy generation pending")) 157