1 #!/usr/bin/env python 2 3 """ 4 Encoder functions, producing representations of program objects. 5 6 Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from common import first 23 24 # Output encoding and decoding for the summary files. 25 26 def encode_attrnames(attrnames): 27 28 "Encode the 'attrnames' representing usage." 29 30 return ", ".join(attrnames) or "{}" 31 32 def encode_constrained(constrained): 33 34 "Encode the 'constrained' status for program summaries." 35 36 return constrained and "constrained" or "deduced" 37 38 def encode_usage(usage): 39 40 "Encode attribute details from 'usage'." 41 42 all_attrnames = [] 43 for t in usage: 44 attrname, invocation, assignment = t 45 all_attrnames.append("%s%s" % (attrname, invocation and "!" or assignment and "=" or "")) 46 return ", ".join(all_attrnames) or "{}" 47 48 def decode_usage(s): 49 50 "Decode attribute details from 's'." 51 52 all_attrnames = set() 53 for attrname_str in s.split(", "): 54 all_attrnames.add((attrname_str.rstrip("!="), attrname_str.endswith("!"), attrname_str.endswith("="))) 55 56 all_attrnames = list(all_attrnames) 57 all_attrnames.sort() 58 return tuple(all_attrnames) 59 60 def encode_access_location(t): 61 62 "Encode the access location 't'." 63 64 path, name, attrname, version = t 65 return "%s %s %s:%d" % (path, name or "{}", attrname, version) 66 67 def encode_location(t): 68 69 "Encode the general location 't' in a concise form." 70 71 path, name, attrname, version = t 72 if name is not None and version is not None: 73 return "%s %s:%d" % (path, name, version) 74 elif name is not None: 75 return "%s %s" % (path, name) 76 else: 77 return "%s :%s" % (path, attrname) 78 79 def encode_modifiers(modifiers): 80 81 "Encode assignment details from 'modifiers'." 82 83 all_modifiers = [] 84 for t in modifiers: 85 all_modifiers.append(encode_modifier_term(t)) 86 return "".join(all_modifiers) 87 88 def encode_modifier_term(t): 89 90 "Encode modifier 't' representing assignment status." 91 92 assignment, invocation = t 93 return assignment and "=" or invocation and "!" or "_" 94 95 def decode_modifier_term(s): 96 97 "Decode modifier term 's' representing assignment status." 98 99 return (s == "=", s == "!") 100 101 102 103 # Test generation functions. 104 105 def get_kinds(all_types): 106 107 """ 108 Return object kind details for 'all_types', being a collection of 109 references for program types. 110 """ 111 112 return map(lambda ref: ref.get_kind(), all_types) 113 114 def test_for_kind(prefix, kind): 115 116 "Return a test condition identifier featuring 'prefix' and 'kind'." 117 118 return "%s-%s" % (prefix, kind == "<instance>" and "instance" or "type") 119 120 def test_for_kinds(prefix, all_kinds): 121 122 """ 123 Return an identifier describing test conditions incorporating the given 124 'prefix' and involving 'all_kinds', being a collection of object kinds. 125 """ 126 127 return test_for_kind(prefix, first(all_kinds)) 128 129 def test_for_type(prefix, ref): 130 131 """ 132 Return an identifier describing a test condition incorporating the given 133 'prefix' and involving 'ref', being a program type reference. The kind of 134 the reference is employed in the identifier. 135 """ 136 137 return test_for_kind(prefix, ref.get_kind()) 138 139 140 141 # Instruction representation encoding. 142 143 def encode_instruction(instruction): 144 145 """ 146 Encode the 'instruction' - a sequence starting with an operation and 147 followed by arguments, each of which may be an instruction sequence or a 148 plain value - to produce a function call string representation. 149 """ 150 151 op = instruction[0] 152 args = instruction[1:] 153 154 if args: 155 a = [] 156 for arg in args: 157 if isinstance(arg, tuple): 158 a.append(encode_instruction(arg)) 159 else: 160 a.append(arg or "{}") 161 argstr = "(%s)" % ", ".join(a) 162 return "%s%s" % (op, argstr) 163 else: 164 return op 165 166 167 168 # Output program encoding. 169 170 attribute_loading_ops = ( 171 "__load_via_class", "__load_via_object", "__get_class_and_load", 172 ) 173 174 attribute_ops = attribute_loading_ops + ( 175 "__store_via_object", 176 ) 177 178 checked_loading_ops = ( 179 "__check_and_load_via_class", "__check_and_load_via_object", "__check_and_load_via_any", 180 ) 181 182 checked_ops = checked_loading_ops + ( 183 "__check_and_store_via_class", "__check_and_store_via_object", "__check_and_store_via_any", 184 ) 185 186 typename_ops = ( 187 "__test_common_instance", "__test_common_object", "__test_common_type", 188 ) 189 190 encoding_ops = ( 191 "__encode_callable", 192 ) 193 194 static_ops = ( 195 "__load_static", 196 ) 197 198 reference_acting_ops = attribute_ops + checked_ops + typename_ops 199 attribute_producing_ops = attribute_loading_ops + checked_loading_ops 200 201 def encode_access_instruction(instruction, subs): 202 203 """ 204 Encode the 'instruction' - a sequence starting with an operation and 205 followed by arguments, each of which may be an instruction sequence or a 206 plain value - to produce a function call string representation. 207 208 The 'subs' parameter defines a mapping of substitutions for special values 209 used in instructions. 210 """ 211 212 op = instruction[0] 213 args = instruction[1:] 214 215 if not args: 216 argstr = "" 217 218 else: 219 # Encode the arguments. 220 221 a = [] 222 converting_op = op 223 for arg in args: 224 a.append(encode_access_instruction_arg(arg, subs, converting_op)) 225 converting_op = None 226 227 # Modify certain arguments. 228 229 # Convert attribute name arguments to position symbols. 230 231 if op in attribute_ops: 232 arg = a[1] 233 a[1] = encode_symbol("pos", arg) 234 235 # Convert attribute name arguments to position and code symbols. 236 237 elif op in checked_ops: 238 arg = a[1] 239 a[1] = encode_symbol("pos", arg) 240 a.insert(2, encode_symbol("code", arg)) 241 242 # Convert type name arguments to position and code symbols. 243 244 elif op in typename_ops: 245 arg = encode_type_attribute(a[1]) 246 a[1] = encode_symbol("pos", arg) 247 a.insert(2, encode_symbol("code", arg)) 248 249 # Replace encoded operations. 250 251 elif op in encoding_ops: 252 origin = a[0] 253 kind = a[1] 254 op = "__load_function" 255 a = [kind == "<class>" and encode_instantiator_pointer(origin) or encode_function_pointer(origin)] 256 257 # Obtain addresses of static objects. 258 259 elif op in static_ops: 260 a[0] = "&%s" % a[0] 261 262 argstr = "(%s)" % ", ".join(a) 263 264 # Substitute the first element of the instruction, which may not be an 265 # operation at all. 266 267 if subs.has_key(op): 268 op = subs[op] 269 elif not args: 270 op = "&%s" % encode_path(op) 271 272 return "%s%s" % (op, argstr) 273 274 def encode_access_instruction_arg(arg, subs, op): 275 276 "Encode 'arg' using 'subs' to define substitutions." 277 278 if isinstance(arg, tuple): 279 encoded = encode_access_instruction(arg, subs) 280 281 # Convert attribute results to references where required. 282 283 if op and op in reference_acting_ops and arg[0] in attribute_producing_ops: 284 return "%s.value" % encoded 285 else: 286 return encoded 287 288 # Special values only need replacing, not encoding. 289 290 elif subs.has_key(arg): 291 return subs.get(arg) 292 293 # Other values may need encoding. 294 295 else: 296 return encode_path(arg) 297 298 def encode_bound_reference(path): 299 300 "Encode 'path' as a bound method name." 301 302 return "__bound_%s" % encode_path(path) 303 304 def encode_function_pointer(path): 305 306 "Encode 'path' as a reference to an output program function." 307 308 return "__fn_%s" % encode_path(path) 309 310 def encode_initialiser_pointer(path): 311 312 "Encode 'path' as a reference to an initialiser function structure." 313 314 return encode_path("%s.__init__" % path) 315 316 def encode_instantiator_pointer(path): 317 318 "Encode 'path' as a reference to an output program instantiator." 319 320 return "__new_%s" % encode_path(path) 321 322 def encode_literal_constant(n): 323 324 "Encode a name for the literal constant with the number 'n'." 325 326 return "__const%d" % n 327 328 def encode_literal_constant_member(value): 329 330 "Encode the member name for the 'value' in the final program." 331 332 return "%svalue" % value.__class__.__name__ 333 334 def encode_literal_constant_value(value): 335 336 "Encode the given 'value' in the final program." 337 338 if isinstance(value, (int, float)): 339 return str(value) 340 else: 341 return '"%s"' % str(value).replace('"', '\\"') 342 343 def encode_literal_instantiator(path): 344 345 """ 346 Encode a reference to an instantiator for a literal having the given 'path'. 347 """ 348 349 return "__newliteral_%s" % encode_path(path) 350 351 def encode_literal_reference(n): 352 353 "Encode a reference to a literal constant with the number 'n'." 354 355 return "__constvalue%d" % n 356 357 def encode_path(path): 358 359 "Encode 'path' as an output program object, translating special symbols." 360 361 if path in reserved_words: 362 return "__%s" % path 363 else: 364 return path.replace("#", "__").replace("$", "__").replace(".", "_") 365 366 def encode_predefined_reference(path): 367 368 "Encode a reference to a predefined constant value for 'path'." 369 370 return "__predefined_%s" % encode_path(path) 371 372 def encode_size(kind, path=None): 373 374 """ 375 Encode a structure size reference for the given 'kind' of structure, with 376 'path' indicating a specific structure name. 377 """ 378 379 return "__%ssize%s" % (structure_size_prefixes.get(kind, kind), path and "_%s" % encode_path(path) or "") 380 381 def encode_symbol(symbol_type, path=None): 382 383 "Encode a symbol with the given 'symbol_type' and optional 'path'." 384 385 return "__%s%s" % (symbol_type, path and "_%s" % encode_path(path) or "") 386 387 def encode_tablename(kind, path): 388 389 """ 390 Encode a table reference for the given 'kind' of table structure, indicating 391 a 'path' for the specific object concerned. 392 """ 393 394 return "__%sTable_%s" % (table_name_prefixes[kind], encode_path(path)) 395 396 def encode_type_attribute(path): 397 398 "Encode the special type attribute for 'path'." 399 400 return "#%s" % path 401 402 403 404 # A mapping from kinds to structure size reference prefixes. 405 406 structure_size_prefixes = { 407 "<class>" : "c", 408 "<module>" : "m", 409 "<instance>" : "i" 410 } 411 412 # A mapping from kinds to table name prefixes. 413 414 table_name_prefixes = { 415 "<class>" : "Class", 416 "<function>" : "Function", 417 "<module>" : "Module", 418 "<instance>" : "Instance" 419 } 420 421 422 423 # Output language reserved words. 424 425 reserved_words = [ 426 "break", "char", "const", "continue", 427 "default", "double", "else", 428 "float", "for", 429 "if", "int", "long", 430 "NULL", 431 "return", "struct", 432 "typedef", 433 "void", "while", 434 ] 435 436 # vim: tabstop=4 expandtab shiftwidth=4