code_formatter.py revision 13709
16502Snate@binkert.org# Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org> 26502Snate@binkert.org# All rights reserved. 36502Snate@binkert.org# 46502Snate@binkert.org# Redistribution and use in source and binary forms, with or without 56502Snate@binkert.org# modification, are permitted provided that the following conditions are 66502Snate@binkert.org# met: redistributions of source code must retain the above copyright 76502Snate@binkert.org# notice, this list of conditions and the following disclaimer; 86502Snate@binkert.org# redistributions in binary form must reproduce the above copyright 96502Snate@binkert.org# notice, this list of conditions and the following disclaimer in the 106502Snate@binkert.org# documentation and/or other materials provided with the distribution; 116502Snate@binkert.org# neither the name of the copyright holders nor the names of its 126502Snate@binkert.org# contributors may be used to endorse or promote products derived from 136502Snate@binkert.org# this software without specific prior written permission. 146502Snate@binkert.org# 156502Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 166502Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 176502Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 186502Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 196502Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 206502Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 216502Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 226502Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 236502Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 246502Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 256502Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 266502Snate@binkert.org 2712563Sgabeblack@google.comfrom __future__ import print_function 2812563Sgabeblack@google.com 296651Snate@binkert.orgimport __builtin__ 306502Snate@binkert.orgimport inspect 316502Snate@binkert.orgimport os 326502Snate@binkert.orgimport re 336502Snate@binkert.orgimport string 346502Snate@binkert.org 356502Snate@binkert.orgclass lookup(object): 366502Snate@binkert.org def __init__(self, formatter, frame, *args, **kwargs): 376502Snate@binkert.org self.frame = frame 386502Snate@binkert.org self.formatter = formatter 396502Snate@binkert.org self.dict = self.formatter._dict 406502Snate@binkert.org self.args = args 416502Snate@binkert.org self.kwargs = kwargs 426502Snate@binkert.org self.locals = {} 436502Snate@binkert.org 446502Snate@binkert.org def __setitem__(self, item, val): 456502Snate@binkert.org self.locals[item] = val 466502Snate@binkert.org 476502Snate@binkert.org def __getitem__(self, item): 486502Snate@binkert.org if item in self.locals: 496502Snate@binkert.org return self.locals[item] 506502Snate@binkert.org 516502Snate@binkert.org if item in self.kwargs: 526502Snate@binkert.org return self.kwargs[item] 536502Snate@binkert.org 546502Snate@binkert.org if item == '__file__': 556502Snate@binkert.org return self.frame.f_code.co_filename 566502Snate@binkert.org 576502Snate@binkert.org if item == '__line__': 586502Snate@binkert.org return self.frame.f_lineno 596502Snate@binkert.org 606999Snate@binkert.org if self.formatter.locals and item in self.frame.f_locals: 616999Snate@binkert.org return self.frame.f_locals[item] 626999Snate@binkert.org 636502Snate@binkert.org if item in self.dict: 646502Snate@binkert.org return self.dict[item] 656502Snate@binkert.org 666999Snate@binkert.org if self.formatter.globals and item in self.frame.f_globals: 676999Snate@binkert.org return self.frame.f_globals[item] 686502Snate@binkert.org 696651Snate@binkert.org if item in __builtin__.__dict__: 706651Snate@binkert.org return __builtin__.__dict__[item] 716502Snate@binkert.org 726502Snate@binkert.org try: 736502Snate@binkert.org item = int(item) 746502Snate@binkert.org return self.args[item] 756502Snate@binkert.org except ValueError: 766502Snate@binkert.org pass 7713663Sandreas.sandberg@arm.com raise IndexError("Could not find '%s'" % item) 786502Snate@binkert.org 796502Snate@binkert.orgclass code_formatter_meta(type): 806502Snate@binkert.org pattern = r""" 816502Snate@binkert.org (?: 826502Snate@binkert.org %(delim)s(?P<escaped>%(delim)s) | # escaped delimiter 836502Snate@binkert.org ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier 846502Snate@binkert.org %(delim)s(?P<ident>%(ident)s) | # identifier 856502Snate@binkert.org %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s | # braced identifier 866502Snate@binkert.org %(delim)s(?P<pos>%(pos)s) | # positional parameter 876502Snate@binkert.org %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s | # braced positional 886502Snate@binkert.org %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s | # double braced expression 896502Snate@binkert.org %(delim)s(?P<invalid>) # ill-formed delimiter exprs 906502Snate@binkert.org ) 916502Snate@binkert.org """ 926502Snate@binkert.org def __init__(cls, name, bases, dct): 936502Snate@binkert.org super(code_formatter_meta, cls).__init__(name, bases, dct) 946502Snate@binkert.org if 'pattern' in dct: 956502Snate@binkert.org pat = cls.pattern 966502Snate@binkert.org else: 976502Snate@binkert.org # tuple expansion to ensure strings are proper length 986502Snate@binkert.org lb,rb = cls.braced 996502Snate@binkert.org lb1,lb2,rb2,rb1 = cls.double_braced 1006502Snate@binkert.org pat = code_formatter_meta.pattern % { 1016502Snate@binkert.org 'delim' : re.escape(cls.delim), 1026502Snate@binkert.org 'ident' : cls.ident, 1036502Snate@binkert.org 'pos' : cls.pos, 1046502Snate@binkert.org 'lb' : re.escape(lb), 1056502Snate@binkert.org 'rb' : re.escape(rb), 1066502Snate@binkert.org 'ldb' : re.escape(lb1+lb2), 1076502Snate@binkert.org 'rdb' : re.escape(rb2+rb1), 1086502Snate@binkert.org } 1096502Snate@binkert.org cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE) 1106502Snate@binkert.org 1116502Snate@binkert.orgclass code_formatter(object): 1126502Snate@binkert.org __metaclass__ = code_formatter_meta 1136502Snate@binkert.org 1146502Snate@binkert.org delim = r'$' 1156502Snate@binkert.org ident = r'[_A-z]\w*' 1166502Snate@binkert.org pos = r'[0-9]+' 1176502Snate@binkert.org braced = r'{}' 1186502Snate@binkert.org double_braced = r'{{}}' 1196502Snate@binkert.org 1206502Snate@binkert.org globals = True 1216502Snate@binkert.org locals = True 1226502Snate@binkert.org fix_newlines = True 1236502Snate@binkert.org def __init__(self, *args, **kwargs): 1246502Snate@binkert.org self._data = [] 1256502Snate@binkert.org self._dict = {} 1266502Snate@binkert.org self._indent_level = 0 1276502Snate@binkert.org self._indent_spaces = 4 1286999Snate@binkert.org self.globals = kwargs.pop('globals', type(self).globals) 1296502Snate@binkert.org self.locals = kwargs.pop('locals', type(self).locals) 1306502Snate@binkert.org self._fix_newlines = \ 1316502Snate@binkert.org kwargs.pop('fix_newlines', type(self).fix_newlines) 1326502Snate@binkert.org 1336502Snate@binkert.org if args: 1346502Snate@binkert.org self.__call__(args) 1356502Snate@binkert.org 1367672Snate@binkert.org def indent(self, count=1): 1377672Snate@binkert.org self._indent_level += self._indent_spaces * count 1386502Snate@binkert.org 1397672Snate@binkert.org def dedent(self, count=1): 1407672Snate@binkert.org assert self._indent_level >= (self._indent_spaces * count) 1417672Snate@binkert.org self._indent_level -= self._indent_spaces * count 1426502Snate@binkert.org 1436502Snate@binkert.org def fix(self, status): 1446502Snate@binkert.org previous = self._fix_newlines 1456502Snate@binkert.org self._fix_newlines = status 1466502Snate@binkert.org return previous 1476502Snate@binkert.org 1486502Snate@binkert.org def nofix(self): 1496502Snate@binkert.org previous = self._fix_newlines 1506502Snate@binkert.org self._fix_newlines = False 1516502Snate@binkert.org return previous 1526502Snate@binkert.org 1536502Snate@binkert.org def clear(): 1546502Snate@binkert.org self._data = [] 1556502Snate@binkert.org 1566502Snate@binkert.org def write(self, *args): 15713670Sandreas.sandberg@arm.com f = open(os.path.join(*args), "w") 1586502Snate@binkert.org for data in self._data: 1596502Snate@binkert.org f.write(data) 1606502Snate@binkert.org f.close() 1616502Snate@binkert.org 1626502Snate@binkert.org def __str__(self): 1636502Snate@binkert.org data = string.join(self._data, '') 1646502Snate@binkert.org self._data = [ data ] 1656502Snate@binkert.org return data 1666502Snate@binkert.org 1676502Snate@binkert.org def __getitem__(self, item): 1686502Snate@binkert.org return self._dict[item] 1696502Snate@binkert.org 1706502Snate@binkert.org def __setitem__(self, item, value): 1716502Snate@binkert.org self._dict[item] = value 1726502Snate@binkert.org 1736502Snate@binkert.org def __delitem__(self, item): 1746502Snate@binkert.org del self._dict[item] 1756502Snate@binkert.org 1766502Snate@binkert.org def __contains__(self, item): 1776502Snate@binkert.org return item in self._dict 1786502Snate@binkert.org 1796502Snate@binkert.org def __iadd__(self, data): 1806502Snate@binkert.org self.append(data) 1816502Snate@binkert.org 1826502Snate@binkert.org def append(self, data): 1836502Snate@binkert.org if isinstance(data, code_formatter): 1846502Snate@binkert.org self._data.extend(data._data) 1856502Snate@binkert.org else: 1866502Snate@binkert.org self._append(str(data)) 1876502Snate@binkert.org 1886502Snate@binkert.org def _append(self, data): 1896502Snate@binkert.org if not self._fix_newlines: 1906502Snate@binkert.org self._data.append(data) 1916502Snate@binkert.org return 1926502Snate@binkert.org 1936502Snate@binkert.org initial_newline = not self._data or self._data[-1] == '\n' 1946502Snate@binkert.org for line in data.splitlines(): 1956502Snate@binkert.org if line: 1966502Snate@binkert.org if self._indent_level: 1976502Snate@binkert.org self._data.append(' ' * self._indent_level) 1986502Snate@binkert.org self._data.append(line) 1996502Snate@binkert.org 2006502Snate@binkert.org if line or not initial_newline: 2016502Snate@binkert.org self._data.append('\n') 2026502Snate@binkert.org 2036502Snate@binkert.org initial_newline = False 2046502Snate@binkert.org 2057672Snate@binkert.org def __call__(self, *args, **kwargs): 2067672Snate@binkert.org if not args: 2077672Snate@binkert.org self._data.append('\n') 2087672Snate@binkert.org return 2096502Snate@binkert.org 2107672Snate@binkert.org format = args[0] 2117672Snate@binkert.org args = args[1:] 2127672Snate@binkert.org 2136502Snate@binkert.org frame = inspect.currentframe().f_back 2146502Snate@binkert.org 2156502Snate@binkert.org l = lookup(self, frame, *args, **kwargs) 2166502Snate@binkert.org def convert(match): 2176502Snate@binkert.org ident = match.group('lone') 2186502Snate@binkert.org # check for a lone identifier 2196502Snate@binkert.org if ident: 2206502Snate@binkert.org indent = match.group('indent') # must be spaces 2216502Snate@binkert.org lone = '%s' % (l[ident], ) 2226502Snate@binkert.org 2236502Snate@binkert.org def indent_lines(gen): 2246502Snate@binkert.org for line in gen: 2256502Snate@binkert.org yield indent 2266502Snate@binkert.org yield line 2276502Snate@binkert.org return ''.join(indent_lines(lone.splitlines(True))) 2286502Snate@binkert.org 2296502Snate@binkert.org # check for an identifier, braced or not 2306502Snate@binkert.org ident = match.group('ident') or match.group('b_ident') 2316502Snate@binkert.org if ident is not None: 2326502Snate@binkert.org return '%s' % (l[ident], ) 2336502Snate@binkert.org 2346502Snate@binkert.org # check for a positional parameter, braced or not 2356502Snate@binkert.org pos = match.group('pos') or match.group('b_pos') 2366502Snate@binkert.org if pos is not None: 2376502Snate@binkert.org pos = int(pos) 2386502Snate@binkert.org if pos > len(args): 2396502Snate@binkert.org raise ValueError \ 2406502Snate@binkert.org ('Positional parameter #%d not found in pattern' % pos, 2416502Snate@binkert.org code_formatter.pattern) 2426502Snate@binkert.org return '%s' % (args[int(pos)], ) 2436502Snate@binkert.org 2446502Snate@binkert.org # check for a double braced expression 2456502Snate@binkert.org eval_expr = match.group('eval') 2466502Snate@binkert.org if eval_expr is not None: 2476502Snate@binkert.org result = eval(eval_expr, {}, l) 2486502Snate@binkert.org return '%s' % (result, ) 2496502Snate@binkert.org 2506502Snate@binkert.org # check for an escaped delimiter 2516502Snate@binkert.org if match.group('escaped') is not None: 2526502Snate@binkert.org return '$' 2536502Snate@binkert.org 2546502Snate@binkert.org # At this point, we have to match invalid 2556502Snate@binkert.org if match.group('invalid') is None: 2566502Snate@binkert.org # didn't match invalid! 2576502Snate@binkert.org raise ValueError('Unrecognized named group in pattern', 2586502Snate@binkert.org code_formatter.pattern) 2596502Snate@binkert.org 2606502Snate@binkert.org i = match.start('invalid') 2616502Snate@binkert.org if i == 0: 2626502Snate@binkert.org colno = 1 2636502Snate@binkert.org lineno = 1 2646502Snate@binkert.org else: 2656502Snate@binkert.org lines = format[:i].splitlines(True) 2666502Snate@binkert.org colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines)) 2676502Snate@binkert.org lineno = len(lines) 2686502Snate@binkert.org 2696502Snate@binkert.org raise ValueError('Invalid format string: line %d, col %d' % 2706502Snate@binkert.org (lineno, colno)) 2716502Snate@binkert.org 2726502Snate@binkert.org d = code_formatter.pattern.sub(convert, format) 2736502Snate@binkert.org self._append(d) 2746502Snate@binkert.org 2756502Snate@binkert.org__all__ = [ "code_formatter" ] 2766502Snate@binkert.org 2776502Snate@binkert.orgif __name__ == '__main__': 2786502Snate@binkert.org from code_formatter import code_formatter 2796502Snate@binkert.org f = code_formatter() 2806502Snate@binkert.org 2816502Snate@binkert.org class Foo(dict): 2826502Snate@binkert.org def __init__(self, **kwargs): 2836502Snate@binkert.org self.update(kwargs) 2846502Snate@binkert.org def __getattr__(self, attr): 2856502Snate@binkert.org return self[attr] 2866502Snate@binkert.org 2876502Snate@binkert.org x = "this is a test" 2886502Snate@binkert.org l = [ [Foo(x=[Foo(y=9)])] ] 2896502Snate@binkert.org 2906502Snate@binkert.org y = code_formatter() 2916502Snate@binkert.org y(''' 2926502Snate@binkert.org{ 2936502Snate@binkert.org this_is_a_test(); 2946502Snate@binkert.org} 2956502Snate@binkert.org''') 2966502Snate@binkert.org f(' $y') 2976502Snate@binkert.org f('''$__file__:$__line__ 2986502Snate@binkert.org{''') 29913709Sandreas.sandberg@arm.com f("${{', '.join(str(x) for x in range(4))}}") 3006502Snate@binkert.org f('${x}') 3016502Snate@binkert.org f('$x') 3026502Snate@binkert.org f.indent() 30313709Sandreas.sandberg@arm.com for i in range(5): 3046502Snate@binkert.org f('$x') 3056502Snate@binkert.org f('$i') 3066502Snate@binkert.org f('$0', "zero") 3076502Snate@binkert.org f('$1 $0', "zero", "one") 3086502Snate@binkert.org f('${0}', "he went") 3096502Snate@binkert.org f('${0}asdf', "he went") 3106502Snate@binkert.org f.dedent() 3116502Snate@binkert.org 3126502Snate@binkert.org f(''' 3136502Snate@binkert.org ${{l[0][0]["x"][0].y}} 3146502Snate@binkert.org} 3156502Snate@binkert.org''', 1, 9) 3166502Snate@binkert.org 31712563Sgabeblack@google.com print(f, end=' ') 318