code_formatter.py revision 13663
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):
1576502Snate@binkert.org        f = file(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{''')
2996502Snate@binkert.org    f("${{', '.join(str(x) for x in xrange(4))}}")
3006502Snate@binkert.org    f('${x}')
3016502Snate@binkert.org    f('$x')
3026502Snate@binkert.org    f.indent()
3036502Snate@binkert.org    for i in xrange(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