15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Code(object): 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """A convenience object for constructing code. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Logically each object should be a block of code. All methods except |Render| 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and |IsEmpty| return self. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, indent_size=2, comment_length=80): 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._code = [] 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._indent_level = 0 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._indent_size = indent_size 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._comment_length = comment_length 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Append(self, line='', substitute=True, indent_level=None): 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Appends a line of code at the current indent level or just a newline if 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line is not specified. Trailing whitespace is stripped. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) substitute: indicated whether this line should be affected by 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) code.Substitute(). 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if indent_level is None: 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) indent_level = self._indent_level 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._code.append(Line(((' ' * indent_level) + line).rstrip(), 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) substitute=substitute)) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def IsEmpty(self): 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns True if the Code object is empty. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return not bool(self._code) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Concat(self, obj): 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Concatenate another Code object onto this one. Trailing whitespace is 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stripped. 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Appends the code at the current indent level. Will fail if there are any 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) un-interpolated format specifiers eg %s, %(something)s which helps 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) isolate any strings that haven't been substituted. 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not isinstance(obj, Code): 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise TypeError(type(obj)) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert self is not obj 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in obj._code: 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # line % () will fail if any substitution tokens are left in line 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.substitute: 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line.value %= () 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except TypeError: 52c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch raise TypeError('Unsubstituted value when concatting\n' + line.value) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except ValueError: 54c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch raise ValueError('Stray % character when concatting\n' + line.value) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Append(line.value, line.substitute) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Cblock(self, code): 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Concatenates another Code object |code| onto this one followed by a 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) blank line, if |code| is non-empty.""" 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if not code.IsEmpty(): 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Concat(code).Append() 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return self 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Sblock(self, line=None): 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Starts a code block. 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Appends a line of code and then increases the indent level. 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if line is not None: 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Append(line) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._indent_level += self._indent_size 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Eblock(self, line=None): 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Ends a code block by decreasing and then appending a line (or a blank 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line if not given). 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO(calamity): Decide if type checking is necessary 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) #if not isinstance(line, basestring): 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # raise TypeError 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._indent_level -= self._indent_size 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if line is not None: 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Append(line) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Comment(self, comment, comment_prefix='// '): 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds the given string as a comment. 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Will split the comment if it's too long. Use mainly for variable length 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) comments. Otherwise just use code.Append('// ...') for comments. 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Unaffected by code.Substitute(). 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) max_len = self._comment_length - self._indent_level - len(comment_prefix) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while len(comment) >= max_len: 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = comment[0:max_len] 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_space = line.rfind(' ') 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if last_space != -1: 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line[0:last_space] 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) comment = comment[last_space + 1:] 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) comment = comment[max_len:] 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Append(comment_prefix + line, substitute=False) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Append(comment_prefix + comment, substitute=False) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Substitute(self, d): 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Goes through each line and interpolates using the given dict. 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Raises type error if passed something that isn't a dict 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Use for long pieces of code using interpolation with the same variables 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repeatedly. This will reduce code and allow for named placeholders which 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) are more clear. 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not isinstance(d, dict): 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise TypeError('Passed argument is not a dictionary: ' + d) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i, line in enumerate(self._code): 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self._code[i].substitute: 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Only need to check %s because arg is a dict and python will allow 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # '%s %(named)s' but just about nothing else 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if '%s' in self._code[i].value or '%r' in self._code[i].value: 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise TypeError('"%s" or "%r" found in substitution. ' 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Named arguments only. Use "%" to escape') 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._code[i].value = line.value % d 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._code[i].substitute = False 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Render(self): 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Renders Code as a string. 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return '\n'.join([l.value for l in self._code]) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Line(object): 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """A line of code. 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, value, substitute=True): 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.value = value 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.substitute = substitute 143