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
2913712Sandreas.sandberg@arm.comtry:
3013712Sandreas.sandberg@arm.com    import builtins
3113712Sandreas.sandberg@arm.comexcept ImportError:
3213712Sandreas.sandberg@arm.com    # Python 2 fallback
3313712Sandreas.sandberg@arm.com    import __builtin__ as builtins
346502Snate@binkert.orgimport inspect
356502Snate@binkert.orgimport os
366502Snate@binkert.orgimport re
376502Snate@binkert.orgimport string
386502Snate@binkert.org
396502Snate@binkert.orgclass lookup(object):
406502Snate@binkert.org    def __init__(self, formatter, frame, *args, **kwargs):
416502Snate@binkert.org        self.frame = frame
426502Snate@binkert.org        self.formatter = formatter
436502Snate@binkert.org        self.dict = self.formatter._dict
446502Snate@binkert.org        self.args = args
456502Snate@binkert.org        self.kwargs = kwargs
466502Snate@binkert.org        self.locals = {}
476502Snate@binkert.org
486502Snate@binkert.org    def __setitem__(self, item, val):
496502Snate@binkert.org        self.locals[item] = val
506502Snate@binkert.org
516502Snate@binkert.org    def __getitem__(self, item):
526502Snate@binkert.org        if item in self.locals:
536502Snate@binkert.org            return self.locals[item]
546502Snate@binkert.org
556502Snate@binkert.org        if item in self.kwargs:
566502Snate@binkert.org            return self.kwargs[item]
576502Snate@binkert.org
586502Snate@binkert.org        if item == '__file__':
596502Snate@binkert.org            return self.frame.f_code.co_filename
606502Snate@binkert.org
616502Snate@binkert.org        if item == '__line__':
626502Snate@binkert.org            return self.frame.f_lineno
636502Snate@binkert.org
646999Snate@binkert.org        if self.formatter.locals and item in self.frame.f_locals:
656999Snate@binkert.org            return self.frame.f_locals[item]
666999Snate@binkert.org
676502Snate@binkert.org        if item in self.dict:
686502Snate@binkert.org            return self.dict[item]
696502Snate@binkert.org
706999Snate@binkert.org        if self.formatter.globals and item in self.frame.f_globals:
716999Snate@binkert.org            return self.frame.f_globals[item]
726502Snate@binkert.org
7313712Sandreas.sandberg@arm.com        if item in builtins.__dict__:
7413712Sandreas.sandberg@arm.com            return builtins.__dict__[item]
756502Snate@binkert.org
766502Snate@binkert.org        try:
776502Snate@binkert.org            item = int(item)
786502Snate@binkert.org            return self.args[item]
796502Snate@binkert.org        except ValueError:
806502Snate@binkert.org            pass
8113663Sandreas.sandberg@arm.com        raise IndexError("Could not find '%s'" % item)
826502Snate@binkert.org
836502Snate@binkert.orgclass code_formatter_meta(type):
846502Snate@binkert.org    pattern = r"""
856502Snate@binkert.org    (?:
866502Snate@binkert.org      %(delim)s(?P<escaped>%(delim)s)              | # escaped delimiter
876502Snate@binkert.org      ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier
886502Snate@binkert.org      %(delim)s(?P<ident>%(ident)s)                | # identifier
896502Snate@binkert.org      %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s  | # braced identifier
906502Snate@binkert.org      %(delim)s(?P<pos>%(pos)s)                    | # positional parameter
916502Snate@binkert.org      %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s      | # braced positional
926502Snate@binkert.org      %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s         | # double braced expression
936502Snate@binkert.org      %(delim)s(?P<invalid>)                       # ill-formed delimiter exprs
946502Snate@binkert.org    )
956502Snate@binkert.org    """
966502Snate@binkert.org    def __init__(cls, name, bases, dct):
976502Snate@binkert.org        super(code_formatter_meta, cls).__init__(name, bases, dct)
986502Snate@binkert.org        if 'pattern' in dct:
996502Snate@binkert.org            pat = cls.pattern
1006502Snate@binkert.org        else:
1016502Snate@binkert.org            # tuple expansion to ensure strings are proper length
1026502Snate@binkert.org            lb,rb = cls.braced
1036502Snate@binkert.org            lb1,lb2,rb2,rb1 = cls.double_braced
1046502Snate@binkert.org            pat = code_formatter_meta.pattern % {
1056502Snate@binkert.org                'delim' : re.escape(cls.delim),
1066502Snate@binkert.org                'ident' : cls.ident,
1076502Snate@binkert.org                'pos' : cls.pos,
1086502Snate@binkert.org                'lb' : re.escape(lb),
1096502Snate@binkert.org                'rb' : re.escape(rb),
1106502Snate@binkert.org                'ldb' : re.escape(lb1+lb2),
1116502Snate@binkert.org                'rdb' : re.escape(rb2+rb1),
1126502Snate@binkert.org                }
1136502Snate@binkert.org        cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE)
1146502Snate@binkert.org
1156502Snate@binkert.orgclass code_formatter(object):
1166502Snate@binkert.org    __metaclass__ = code_formatter_meta
1176502Snate@binkert.org
1186502Snate@binkert.org    delim = r'$'
1196502Snate@binkert.org    ident = r'[_A-z]\w*'
1206502Snate@binkert.org    pos = r'[0-9]+'
1216502Snate@binkert.org    braced = r'{}'
1226502Snate@binkert.org    double_braced = r'{{}}'
1236502Snate@binkert.org
1246502Snate@binkert.org    globals = True
1256502Snate@binkert.org    locals = True
1266502Snate@binkert.org    fix_newlines = True
1276502Snate@binkert.org    def __init__(self, *args, **kwargs):
1286502Snate@binkert.org        self._data = []
1296502Snate@binkert.org        self._dict = {}
1306502Snate@binkert.org        self._indent_level = 0
1316502Snate@binkert.org        self._indent_spaces = 4
1326999Snate@binkert.org        self.globals = kwargs.pop('globals', type(self).globals)
1336502Snate@binkert.org        self.locals = kwargs.pop('locals', type(self).locals)
1346502Snate@binkert.org        self._fix_newlines = \
1356502Snate@binkert.org                kwargs.pop('fix_newlines', type(self).fix_newlines)
1366502Snate@binkert.org
1376502Snate@binkert.org        if args:
1386502Snate@binkert.org            self.__call__(args)
1396502Snate@binkert.org
1407672Snate@binkert.org    def indent(self, count=1):
1417672Snate@binkert.org        self._indent_level += self._indent_spaces * count
1426502Snate@binkert.org
1437672Snate@binkert.org    def dedent(self, count=1):
1447672Snate@binkert.org        assert self._indent_level >= (self._indent_spaces * count)
1457672Snate@binkert.org        self._indent_level -= self._indent_spaces * count
1466502Snate@binkert.org
1476502Snate@binkert.org    def fix(self, status):
1486502Snate@binkert.org        previous = self._fix_newlines
1496502Snate@binkert.org        self._fix_newlines = status
1506502Snate@binkert.org        return previous
1516502Snate@binkert.org
1526502Snate@binkert.org    def nofix(self):
1536502Snate@binkert.org        previous = self._fix_newlines
1546502Snate@binkert.org        self._fix_newlines = False
1556502Snate@binkert.org        return previous
1566502Snate@binkert.org
1576502Snate@binkert.org    def clear():
1586502Snate@binkert.org        self._data = []
1596502Snate@binkert.org
1606502Snate@binkert.org    def write(self, *args):
16113670Sandreas.sandberg@arm.com        f = open(os.path.join(*args), "w")
1626502Snate@binkert.org        for data in self._data:
1636502Snate@binkert.org            f.write(data)
1646502Snate@binkert.org        f.close()
1656502Snate@binkert.org
1666502Snate@binkert.org    def __str__(self):
1676502Snate@binkert.org        data = string.join(self._data, '')
1686502Snate@binkert.org        self._data = [ data ]
1696502Snate@binkert.org        return data
1706502Snate@binkert.org
1716502Snate@binkert.org    def __getitem__(self, item):
1726502Snate@binkert.org        return self._dict[item]
1736502Snate@binkert.org
1746502Snate@binkert.org    def __setitem__(self, item, value):
1756502Snate@binkert.org        self._dict[item] = value
1766502Snate@binkert.org
1776502Snate@binkert.org    def __delitem__(self, item):
1786502Snate@binkert.org        del self._dict[item]
1796502Snate@binkert.org
1806502Snate@binkert.org    def __contains__(self, item):
1816502Snate@binkert.org        return item in self._dict
1826502Snate@binkert.org
1836502Snate@binkert.org    def __iadd__(self, data):
1846502Snate@binkert.org        self.append(data)
1856502Snate@binkert.org
1866502Snate@binkert.org    def append(self, data):
1876502Snate@binkert.org        if isinstance(data, code_formatter):
1886502Snate@binkert.org            self._data.extend(data._data)
1896502Snate@binkert.org        else:
1906502Snate@binkert.org            self._append(str(data))
1916502Snate@binkert.org
1926502Snate@binkert.org    def _append(self, data):
1936502Snate@binkert.org        if not self._fix_newlines:
1946502Snate@binkert.org            self._data.append(data)
1956502Snate@binkert.org            return
1966502Snate@binkert.org
1976502Snate@binkert.org        initial_newline = not self._data or self._data[-1] == '\n'
1986502Snate@binkert.org        for line in data.splitlines():
1996502Snate@binkert.org            if line:
2006502Snate@binkert.org                if self._indent_level:
2016502Snate@binkert.org                    self._data.append(' ' * self._indent_level)
2026502Snate@binkert.org                self._data.append(line)
2036502Snate@binkert.org
2046502Snate@binkert.org            if line or not initial_newline:
2056502Snate@binkert.org                self._data.append('\n')
2066502Snate@binkert.org
2076502Snate@binkert.org            initial_newline = False
2086502Snate@binkert.org
2097672Snate@binkert.org    def __call__(self, *args, **kwargs):
2107672Snate@binkert.org        if not args:
2117672Snate@binkert.org            self._data.append('\n')
2127672Snate@binkert.org            return
2136502Snate@binkert.org
2147672Snate@binkert.org        format = args[0]
2157672Snate@binkert.org        args = args[1:]
2167672Snate@binkert.org
2176502Snate@binkert.org        frame = inspect.currentframe().f_back
2186502Snate@binkert.org
2196502Snate@binkert.org        l = lookup(self, frame, *args, **kwargs)
2206502Snate@binkert.org        def convert(match):
2216502Snate@binkert.org            ident = match.group('lone')
2226502Snate@binkert.org            # check for a lone identifier
2236502Snate@binkert.org            if ident:
2246502Snate@binkert.org                indent = match.group('indent') # must be spaces
2256502Snate@binkert.org                lone = '%s' % (l[ident], )
2266502Snate@binkert.org
2276502Snate@binkert.org                def indent_lines(gen):
2286502Snate@binkert.org                    for line in gen:
2296502Snate@binkert.org                        yield indent
2306502Snate@binkert.org                        yield line
2316502Snate@binkert.org                return ''.join(indent_lines(lone.splitlines(True)))
2326502Snate@binkert.org
2336502Snate@binkert.org            # check for an identifier, braced or not
2346502Snate@binkert.org            ident = match.group('ident') or match.group('b_ident')
2356502Snate@binkert.org            if ident is not None:
2366502Snate@binkert.org                return '%s' % (l[ident], )
2376502Snate@binkert.org
2386502Snate@binkert.org            # check for a positional parameter, braced or not
2396502Snate@binkert.org            pos = match.group('pos') or match.group('b_pos')
2406502Snate@binkert.org            if pos is not None:
2416502Snate@binkert.org                pos = int(pos)
2426502Snate@binkert.org                if pos > len(args):
2436502Snate@binkert.org                    raise ValueError \
2446502Snate@binkert.org                        ('Positional parameter #%d not found in pattern' % pos,
2456502Snate@binkert.org                         code_formatter.pattern)
2466502Snate@binkert.org                return '%s' % (args[int(pos)], )
2476502Snate@binkert.org
2486502Snate@binkert.org            # check for a double braced expression
2496502Snate@binkert.org            eval_expr = match.group('eval')
2506502Snate@binkert.org            if eval_expr is not None:
2516502Snate@binkert.org                result = eval(eval_expr, {}, l)
2526502Snate@binkert.org                return '%s' % (result, )
2536502Snate@binkert.org
2546502Snate@binkert.org            # check for an escaped delimiter
2556502Snate@binkert.org            if match.group('escaped') is not None:
2566502Snate@binkert.org                return '$'
2576502Snate@binkert.org
2586502Snate@binkert.org            # At this point, we have to match invalid
2596502Snate@binkert.org            if match.group('invalid') is None:
2606502Snate@binkert.org                # didn't match invalid!
2616502Snate@binkert.org                raise ValueError('Unrecognized named group in pattern',
2626502Snate@binkert.org                                 code_formatter.pattern)
2636502Snate@binkert.org
2646502Snate@binkert.org            i = match.start('invalid')
2656502Snate@binkert.org            if i == 0:
2666502Snate@binkert.org                colno = 1
2676502Snate@binkert.org                lineno = 1
2686502Snate@binkert.org            else:
2696502Snate@binkert.org                lines = format[:i].splitlines(True)
2706502Snate@binkert.org                colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines))
2716502Snate@binkert.org                lineno = len(lines)
2726502Snate@binkert.org
2736502Snate@binkert.org                raise ValueError('Invalid format string: line %d, col %d' %
2746502Snate@binkert.org                                 (lineno, colno))
2756502Snate@binkert.org
2766502Snate@binkert.org        d = code_formatter.pattern.sub(convert, format)
2776502Snate@binkert.org        self._append(d)
2786502Snate@binkert.org
2796502Snate@binkert.org__all__ = [ "code_formatter" ]
2806502Snate@binkert.org
2816502Snate@binkert.orgif __name__ == '__main__':
28213714Sandreas.sandberg@arm.com    from .code_formatter import code_formatter
2836502Snate@binkert.org    f = code_formatter()
2846502Snate@binkert.org
2856502Snate@binkert.org    class Foo(dict):
2866502Snate@binkert.org        def __init__(self, **kwargs):
2876502Snate@binkert.org            self.update(kwargs)
2886502Snate@binkert.org        def __getattr__(self, attr):
2896502Snate@binkert.org            return self[attr]
2906502Snate@binkert.org
2916502Snate@binkert.org    x = "this is a test"
2926502Snate@binkert.org    l = [ [Foo(x=[Foo(y=9)])] ]
2936502Snate@binkert.org
2946502Snate@binkert.org    y = code_formatter()
2956502Snate@binkert.org    y('''
2966502Snate@binkert.org{
2976502Snate@binkert.org    this_is_a_test();
2986502Snate@binkert.org}
2996502Snate@binkert.org''')
3006502Snate@binkert.org    f('    $y')
3016502Snate@binkert.org    f('''$__file__:$__line__
3026502Snate@binkert.org{''')
30313709Sandreas.sandberg@arm.com    f("${{', '.join(str(x) for x in range(4))}}")
3046502Snate@binkert.org    f('${x}')
3056502Snate@binkert.org    f('$x')
3066502Snate@binkert.org    f.indent()
30713709Sandreas.sandberg@arm.com    for i in range(5):
3086502Snate@binkert.org        f('$x')
3096502Snate@binkert.org        f('$i')
3106502Snate@binkert.org        f('$0', "zero")
3116502Snate@binkert.org        f('$1 $0', "zero", "one")
3126502Snate@binkert.org        f('${0}', "he went")
3136502Snate@binkert.org        f('${0}asdf', "he went")
3146502Snate@binkert.org    f.dedent()
3156502Snate@binkert.org
3166502Snate@binkert.org    f('''
3176502Snate@binkert.org    ${{l[0][0]["x"][0].y}}
3186502Snate@binkert.org}
3196502Snate@binkert.org''', 1, 9)
3206502Snate@binkert.org
32112563Sgabeblack@google.com    print(f, end=' ')
322