code_formatter.py revision 6999
111723Sar4jc@virginia.edu# Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org> 211723Sar4jc@virginia.edu# All rights reserved. 311723Sar4jc@virginia.edu# 411723Sar4jc@virginia.edu# Redistribution and use in source and binary forms, with or without 511723Sar4jc@virginia.edu# modification, are permitted provided that the following conditions are 611723Sar4jc@virginia.edu# met: redistributions of source code must retain the above copyright 711723Sar4jc@virginia.edu# notice, this list of conditions and the following disclaimer; 811723Sar4jc@virginia.edu# redistributions in binary form must reproduce the above copyright 911723Sar4jc@virginia.edu# notice, this list of conditions and the following disclaimer in the 1011723Sar4jc@virginia.edu# documentation and/or other materials provided with the distribution; 1111723Sar4jc@virginia.edu# neither the name of the copyright holders nor the names of its 1211723Sar4jc@virginia.edu# contributors may be used to endorse or promote products derived from 1311723Sar4jc@virginia.edu# this software without specific prior written permission. 1411723Sar4jc@virginia.edu# 1511723Sar4jc@virginia.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1611723Sar4jc@virginia.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1711723Sar4jc@virginia.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1811723Sar4jc@virginia.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1911723Sar4jc@virginia.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2011723Sar4jc@virginia.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2111723Sar4jc@virginia.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2211723Sar4jc@virginia.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2311723Sar4jc@virginia.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2411723Sar4jc@virginia.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2511723Sar4jc@virginia.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2611723Sar4jc@virginia.edu 2711723Sar4jc@virginia.eduimport __builtin__ 2811723Sar4jc@virginia.eduimport inspect 2911723Sar4jc@virginia.eduimport os 3011723Sar4jc@virginia.eduimport re 3111723Sar4jc@virginia.eduimport string 3211723Sar4jc@virginia.edu 3311723Sar4jc@virginia.educlass lookup(object): 3411723Sar4jc@virginia.edu def __init__(self, formatter, frame, *args, **kwargs): 3511723Sar4jc@virginia.edu self.frame = frame 3611723Sar4jc@virginia.edu self.formatter = formatter 3711723Sar4jc@virginia.edu self.dict = self.formatter._dict 3811723Sar4jc@virginia.edu self.args = args 3911723Sar4jc@virginia.edu self.kwargs = kwargs 4011723Sar4jc@virginia.edu self.locals = {} 4111723Sar4jc@virginia.edu 4211723Sar4jc@virginia.edu def __setitem__(self, item, val): 4311725Sar4jc@virginia.edu self.locals[item] = val 4411725Sar4jc@virginia.edu 4511725Sar4jc@virginia.edu def __getitem__(self, item): 4611725Sar4jc@virginia.edu if item in self.locals: 4711725Sar4jc@virginia.edu return self.locals[item] 4811725Sar4jc@virginia.edu 4911723Sar4jc@virginia.edu if item in self.kwargs: 5011723Sar4jc@virginia.edu return self.kwargs[item] 5111723Sar4jc@virginia.edu 5211723Sar4jc@virginia.edu if item == '__file__': 5311723Sar4jc@virginia.edu return self.frame.f_code.co_filename 5411723Sar4jc@virginia.edu 5511723Sar4jc@virginia.edu if item == '__line__': 5611723Sar4jc@virginia.edu return self.frame.f_lineno 5711723Sar4jc@virginia.edu 5811723Sar4jc@virginia.edu if self.formatter.locals and item in self.frame.f_locals: 5911723Sar4jc@virginia.edu return self.frame.f_locals[item] 6011723Sar4jc@virginia.edu 6111723Sar4jc@virginia.edu if item in self.dict: 6211723Sar4jc@virginia.edu return self.dict[item] 6311723Sar4jc@virginia.edu 6411723Sar4jc@virginia.edu if self.formatter.globals and item in self.frame.f_globals: 6511723Sar4jc@virginia.edu return self.frame.f_globals[item] 6611723Sar4jc@virginia.edu 6711723Sar4jc@virginia.edu if item in __builtin__.__dict__: 6811723Sar4jc@virginia.edu return __builtin__.__dict__[item] 6911723Sar4jc@virginia.edu 7011723Sar4jc@virginia.edu try: 7111723Sar4jc@virginia.edu item = int(item) 7211723Sar4jc@virginia.edu return self.args[item] 7311723Sar4jc@virginia.edu except ValueError: 7411723Sar4jc@virginia.edu pass 7511723Sar4jc@virginia.edu raise IndexError, "Could not find '%s'" % item 7611723Sar4jc@virginia.edu 7711723Sar4jc@virginia.educlass code_formatter_meta(type): 7811723Sar4jc@virginia.edu pattern = r""" 7911723Sar4jc@virginia.edu (?: 8011723Sar4jc@virginia.edu %(delim)s(?P<escaped>%(delim)s) | # escaped delimiter 8111723Sar4jc@virginia.edu ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier 8211723Sar4jc@virginia.edu %(delim)s(?P<ident>%(ident)s) | # identifier 8311723Sar4jc@virginia.edu %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s | # braced identifier 8411723Sar4jc@virginia.edu %(delim)s(?P<pos>%(pos)s) | # positional parameter 8511723Sar4jc@virginia.edu %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s | # braced positional 8611723Sar4jc@virginia.edu %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s | # double braced expression 8711723Sar4jc@virginia.edu %(delim)s(?P<invalid>) # ill-formed delimiter exprs 8811723Sar4jc@virginia.edu ) 8911723Sar4jc@virginia.edu """ 9011723Sar4jc@virginia.edu def __init__(cls, name, bases, dct): 9111723Sar4jc@virginia.edu super(code_formatter_meta, cls).__init__(name, bases, dct) 9211723Sar4jc@virginia.edu if 'pattern' in dct: 9311723Sar4jc@virginia.edu pat = cls.pattern 9411723Sar4jc@virginia.edu else: 9511723Sar4jc@virginia.edu # tuple expansion to ensure strings are proper length 9611723Sar4jc@virginia.edu lb,rb = cls.braced 9711723Sar4jc@virginia.edu lb1,lb2,rb2,rb1 = cls.double_braced 9811723Sar4jc@virginia.edu pat = code_formatter_meta.pattern % { 9911723Sar4jc@virginia.edu 'delim' : re.escape(cls.delim), 10011723Sar4jc@virginia.edu 'ident' : cls.ident, 10111723Sar4jc@virginia.edu 'pos' : cls.pos, 10211723Sar4jc@virginia.edu 'lb' : re.escape(lb), 10311723Sar4jc@virginia.edu 'rb' : re.escape(rb), 10411723Sar4jc@virginia.edu 'ldb' : re.escape(lb1+lb2), 10511723Sar4jc@virginia.edu 'rdb' : re.escape(rb2+rb1), 10611723Sar4jc@virginia.edu } 10711723Sar4jc@virginia.edu cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE) 10811723Sar4jc@virginia.edu 10911723Sar4jc@virginia.educlass code_formatter(object): 11011723Sar4jc@virginia.edu __metaclass__ = code_formatter_meta 11111723Sar4jc@virginia.edu 11211723Sar4jc@virginia.edu delim = r'$' 11311723Sar4jc@virginia.edu ident = r'[_A-z]\w*' 11411723Sar4jc@virginia.edu pos = r'[0-9]+' 11511723Sar4jc@virginia.edu braced = r'{}' 11611723Sar4jc@virginia.edu double_braced = r'{{}}' 11711723Sar4jc@virginia.edu 11811723Sar4jc@virginia.edu globals = True 11912136Sar4jc@virginia.edu locals = True 12012136Sar4jc@virginia.edu fix_newlines = True 12112136Sar4jc@virginia.edu def __init__(self, *args, **kwargs): 12212136Sar4jc@virginia.edu self._data = [] 12312136Sar4jc@virginia.edu self._dict = {} 12412136Sar4jc@virginia.edu self._indent_level = 0 12512136Sar4jc@virginia.edu self._indent_spaces = 4 12612136Sar4jc@virginia.edu self.globals = kwargs.pop('globals', type(self).globals) 12712136Sar4jc@virginia.edu self.locals = kwargs.pop('locals', type(self).locals) 12812136Sar4jc@virginia.edu self._fix_newlines = \ 12912136Sar4jc@virginia.edu kwargs.pop('fix_newlines', type(self).fix_newlines) 13012136Sar4jc@virginia.edu 13112136Sar4jc@virginia.edu if args: 13211723Sar4jc@virginia.edu self.__call__(args) 13311723Sar4jc@virginia.edu 13411723Sar4jc@virginia.edu def indent(self): 13511723Sar4jc@virginia.edu self._indent_level += self._indent_spaces 13611723Sar4jc@virginia.edu 13711723Sar4jc@virginia.edu def dedent(self): 13811723Sar4jc@virginia.edu assert self._indent_level >= self._indent_spaces 13911723Sar4jc@virginia.edu self._indent_level -= self._indent_spaces 14011723Sar4jc@virginia.edu 14111723Sar4jc@virginia.edu def fix(self, status): 14211723Sar4jc@virginia.edu previous = self._fix_newlines 14311723Sar4jc@virginia.edu self._fix_newlines = status 14411723Sar4jc@virginia.edu return previous 14511723Sar4jc@virginia.edu 14611725Sar4jc@virginia.edu def nofix(self): 14711725Sar4jc@virginia.edu previous = self._fix_newlines 14811725Sar4jc@virginia.edu self._fix_newlines = False 14911725Sar4jc@virginia.edu return previous 15011725Sar4jc@virginia.edu 15111725Sar4jc@virginia.edu def clear(): 15211725Sar4jc@virginia.edu self._data = [] 15311725Sar4jc@virginia.edu 15411725Sar4jc@virginia.edu def write(self, *args): 15511725Sar4jc@virginia.edu f = file(os.path.join(*args), "w") 15611725Sar4jc@virginia.edu for data in self._data: 15711725Sar4jc@virginia.edu f.write(data) 15811725Sar4jc@virginia.edu f.close() 15911725Sar4jc@virginia.edu 16011723Sar4jc@virginia.edu def __str__(self): 16111723Sar4jc@virginia.edu data = string.join(self._data, '') 16211723Sar4jc@virginia.edu self._data = [ data ] 16311723Sar4jc@virginia.edu return data 16411723Sar4jc@virginia.edu 16511723Sar4jc@virginia.edu def __getitem__(self, item): 16611723Sar4jc@virginia.edu return self._dict[item] 16711723Sar4jc@virginia.edu 16811723Sar4jc@virginia.edu def __setitem__(self, item, value): 16911723Sar4jc@virginia.edu self._dict[item] = value 17011723Sar4jc@virginia.edu 17111723Sar4jc@virginia.edu def __delitem__(self, item): 17211723Sar4jc@virginia.edu del self._dict[item] 17311723Sar4jc@virginia.edu 17411723Sar4jc@virginia.edu def __contains__(self, item): 17511723Sar4jc@virginia.edu return item in self._dict 17611723Sar4jc@virginia.edu 17711723Sar4jc@virginia.edu def __iadd__(self, data): 17811723Sar4jc@virginia.edu self.append(data) 17911723Sar4jc@virginia.edu 18011723Sar4jc@virginia.edu def append(self, data): 18111723Sar4jc@virginia.edu if isinstance(data, code_formatter): 18211723Sar4jc@virginia.edu self._data.extend(data._data) 18311723Sar4jc@virginia.edu else: 18411723Sar4jc@virginia.edu self._append(str(data)) 185 186 def _append(self, data): 187 if not self._fix_newlines: 188 self._data.append(data) 189 return 190 191 initial_newline = not self._data or self._data[-1] == '\n' 192 for line in data.splitlines(): 193 if line: 194 if self._indent_level: 195 self._data.append(' ' * self._indent_level) 196 self._data.append(line) 197 198 if line or not initial_newline: 199 self._data.append('\n') 200 201 initial_newline = False 202 203 def insert_newline(self): 204 self._data.append('\n') 205 206 def __call__(self, format, *args, **kwargs): 207 frame = inspect.currentframe().f_back 208 209 l = lookup(self, frame, *args, **kwargs) 210 def convert(match): 211 ident = match.group('lone') 212 # check for a lone identifier 213 if ident: 214 indent = match.group('indent') # must be spaces 215 lone = '%s' % (l[ident], ) 216 217 def indent_lines(gen): 218 for line in gen: 219 yield indent 220 yield line 221 return ''.join(indent_lines(lone.splitlines(True))) 222 223 # check for an identifier, braced or not 224 ident = match.group('ident') or match.group('b_ident') 225 if ident is not None: 226 return '%s' % (l[ident], ) 227 228 # check for a positional parameter, braced or not 229 pos = match.group('pos') or match.group('b_pos') 230 if pos is not None: 231 pos = int(pos) 232 if pos > len(args): 233 raise ValueError \ 234 ('Positional parameter #%d not found in pattern' % pos, 235 code_formatter.pattern) 236 return '%s' % (args[int(pos)], ) 237 238 # check for a double braced expression 239 eval_expr = match.group('eval') 240 if eval_expr is not None: 241 result = eval(eval_expr, {}, l) 242 return '%s' % (result, ) 243 244 # check for an escaped delimiter 245 if match.group('escaped') is not None: 246 return '$' 247 248 # At this point, we have to match invalid 249 if match.group('invalid') is None: 250 # didn't match invalid! 251 raise ValueError('Unrecognized named group in pattern', 252 code_formatter.pattern) 253 254 i = match.start('invalid') 255 if i == 0: 256 colno = 1 257 lineno = 1 258 else: 259 lines = format[:i].splitlines(True) 260 colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines)) 261 lineno = len(lines) 262 263 raise ValueError('Invalid format string: line %d, col %d' % 264 (lineno, colno)) 265 266 d = code_formatter.pattern.sub(convert, format) 267 self._append(d) 268 269__all__ = [ "code_formatter" ] 270 271if __name__ == '__main__': 272 from code_formatter import code_formatter 273 f = code_formatter() 274 275 class Foo(dict): 276 def __init__(self, **kwargs): 277 self.update(kwargs) 278 def __getattr__(self, attr): 279 return self[attr] 280 281 x = "this is a test" 282 l = [ [Foo(x=[Foo(y=9)])] ] 283 284 y = code_formatter() 285 y(''' 286{ 287 this_is_a_test(); 288} 289''') 290 f(' $y') 291 f('''$__file__:$__line__ 292{''') 293 f("${{', '.join(str(x) for x in xrange(4))}}") 294 f('${x}') 295 f('$x') 296 f.indent() 297 for i in xrange(5): 298 f('$x') 299 f('$i') 300 f('$0', "zero") 301 f('$1 $0', "zero", "one") 302 f('${0}', "he went") 303 f('${0}asdf', "he went") 304 f.dedent() 305 306 f(''' 307 ${{l[0][0]["x"][0].y}} 308} 309''', 1, 9) 310 311 print f, 312