code_formatter.py revision 13663:9b64aeabf9a5
12292SN/A# Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org>
27597Sminkyu.jeong@arm.com# All rights reserved.
37597Sminkyu.jeong@arm.com#
47597Sminkyu.jeong@arm.com# Redistribution and use in source and binary forms, with or without
57597Sminkyu.jeong@arm.com# modification, are permitted provided that the following conditions are
67597Sminkyu.jeong@arm.com# met: redistributions of source code must retain the above copyright
77597Sminkyu.jeong@arm.com# notice, this list of conditions and the following disclaimer;
87597Sminkyu.jeong@arm.com# redistributions in binary form must reproduce the above copyright
97597Sminkyu.jeong@arm.com# notice, this list of conditions and the following disclaimer in the
107597Sminkyu.jeong@arm.com# documentation and/or other materials provided with the distribution;
117597Sminkyu.jeong@arm.com# neither the name of the copyright holders nor the names of its
127597Sminkyu.jeong@arm.com# contributors may be used to endorse or promote products derived from
137597Sminkyu.jeong@arm.com# this software without specific prior written permission.
142292SN/A#
152292SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
162292SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
172292SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
182292SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
192292SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
202292SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
212292SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
222292SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232292SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
242292SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
252292SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
262292SN/A
272292SN/Afrom __future__ import print_function
282292SN/A
292292SN/Aimport __builtin__
302292SN/Aimport inspect
312292SN/Aimport os
322292SN/Aimport re
332292SN/Aimport string
342292SN/A
352292SN/Aclass lookup(object):
362292SN/A    def __init__(self, formatter, frame, *args, **kwargs):
372292SN/A        self.frame = frame
382292SN/A        self.formatter = formatter
392689Sktlim@umich.edu        self.dict = self.formatter._dict
402689Sktlim@umich.edu        self.args = args
412689Sktlim@umich.edu        self.kwargs = kwargs
422292SN/A        self.locals = {}
432292SN/A
443326Sktlim@umich.edu    def __setitem__(self, item, val):
458229Snate@binkert.org        self.locals[item] = val
466658Snate@binkert.org
472733Sktlim@umich.edu    def __getitem__(self, item):
482907Sktlim@umich.edu        if item in self.locals:
492292SN/A            return self.locals[item]
508232Snate@binkert.org
518232Snate@binkert.org        if item in self.kwargs:
528232Snate@binkert.org            return self.kwargs[item]
532722Sktlim@umich.edu
542669Sktlim@umich.edu        if item == '__file__':
552292SN/A            return self.frame.f_code.co_filename
562790Sktlim@umich.edu
572790Sktlim@umich.edu        if item == '__line__':
582790Sktlim@umich.edu            return self.frame.f_lineno
592790Sktlim@umich.edu
602669Sktlim@umich.edu        if self.formatter.locals and item in self.frame.f_locals:
612678Sktlim@umich.edu            return self.frame.f_locals[item]
622678Sktlim@umich.edu
635606Snate@binkert.org        if item in self.dict:
642292SN/A            return self.dict[item]
652678Sktlim@umich.edu
662292SN/A        if self.formatter.globals and item in self.frame.f_globals:
672292SN/A            return self.frame.f_globals[item]
682669Sktlim@umich.edu
692292SN/A        if item in __builtin__.__dict__:
702678Sktlim@umich.edu            return __builtin__.__dict__[item]
712292SN/A
722678Sktlim@umich.edu        try:
732678Sktlim@umich.edu            item = int(item)
742678Sktlim@umich.edu            return self.args[item]
754319Sktlim@umich.edu        except ValueError:
764319Sktlim@umich.edu            pass
774319Sktlim@umich.edu        raise IndexError("Could not find '%s'" % item)
784319Sktlim@umich.edu
794319Sktlim@umich.educlass code_formatter_meta(type):
802678Sktlim@umich.edu    pattern = r"""
812678Sktlim@umich.edu    (?:
822292SN/A      %(delim)s(?P<escaped>%(delim)s)              | # escaped delimiter
832678Sktlim@umich.edu      ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier
842678Sktlim@umich.edu      %(delim)s(?P<ident>%(ident)s)                | # identifier
855336Shines@cs.fsu.edu      %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s  | # braced identifier
862678Sktlim@umich.edu      %(delim)s(?P<pos>%(pos)s)                    | # positional parameter
874873Sstever@eecs.umich.edu      %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s      | # braced positional
882678Sktlim@umich.edu      %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s         | # double braced expression
892292SN/A      %(delim)s(?P<invalid>)                       # ill-formed delimiter exprs
902678Sktlim@umich.edu    )
912678Sktlim@umich.edu    """
922678Sktlim@umich.edu    def __init__(cls, name, bases, dct):
932678Sktlim@umich.edu        super(code_formatter_meta, cls).__init__(name, bases, dct)
942678Sktlim@umich.edu        if 'pattern' in dct:
952678Sktlim@umich.edu            pat = cls.pattern
967852SMatt.Horsnell@arm.com        else:
977852SMatt.Horsnell@arm.com            # tuple expansion to ensure strings are proper length
982344SN/A            lb,rb = cls.braced
992678Sktlim@umich.edu            lb1,lb2,rb2,rb1 = cls.double_braced
1002678Sktlim@umich.edu            pat = code_formatter_meta.pattern % {
1014986Ssaidi@eecs.umich.edu                'delim' : re.escape(cls.delim),
1024986Ssaidi@eecs.umich.edu                'ident' : cls.ident,
1036974Stjones1@inf.ed.ac.uk                'pos' : cls.pos,
1046974Stjones1@inf.ed.ac.uk                'lb' : re.escape(lb),
1056974Stjones1@inf.ed.ac.uk                'rb' : re.escape(rb),
1066974Stjones1@inf.ed.ac.uk                'ldb' : re.escape(lb1+lb2),
1076974Stjones1@inf.ed.ac.uk                'rdb' : re.escape(rb2+rb1),
1086974Stjones1@inf.ed.ac.uk                }
1096974Stjones1@inf.ed.ac.uk        cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE)
1102678Sktlim@umich.edu
1112820Sktlim@umich.educlass code_formatter(object):
1122678Sktlim@umich.edu    __metaclass__ = code_formatter_meta
1132678Sktlim@umich.edu
1146974Stjones1@inf.ed.ac.uk    delim = r'$'
1156974Stjones1@inf.ed.ac.uk    ident = r'[_A-z]\w*'
1166974Stjones1@inf.ed.ac.uk    pos = r'[0-9]+'
1176974Stjones1@inf.ed.ac.uk    braced = r'{}'
1186974Stjones1@inf.ed.ac.uk    double_braced = r'{{}}'
1196974Stjones1@inf.ed.ac.uk
1202678Sktlim@umich.edu    globals = True
1212678Sktlim@umich.edu    locals = True
1222678Sktlim@umich.edu    fix_newlines = True
1232678Sktlim@umich.edu    def __init__(self, *args, **kwargs):
1242678Sktlim@umich.edu        self._data = []
1252344SN/A        self._dict = {}
1262307SN/A        self._indent_level = 0
1276974Stjones1@inf.ed.ac.uk        self._indent_spaces = 4
1286974Stjones1@inf.ed.ac.uk        self.globals = kwargs.pop('globals', type(self).globals)
1296974Stjones1@inf.ed.ac.uk        self.locals = kwargs.pop('locals', type(self).locals)
1306974Stjones1@inf.ed.ac.uk        self._fix_newlines = \
1312678Sktlim@umich.edu                kwargs.pop('fix_newlines', type(self).fix_newlines)
1324032Sktlim@umich.edu
1332678Sktlim@umich.edu        if args:
1342292SN/A            self.__call__(args)
1352292SN/A
1362292SN/A    def indent(self, count=1):
1372292SN/A        self._indent_level += self._indent_spaces * count
1382678Sktlim@umich.edu
1392678Sktlim@umich.edu    def dedent(self, count=1):
1406974Stjones1@inf.ed.ac.uk        assert self._indent_level >= (self._indent_spaces * count)
1412292SN/A        self._indent_level -= self._indent_spaces * count
1422292SN/A
1432292SN/A    def fix(self, status):
1442292SN/A        previous = self._fix_newlines
1452292SN/A        self._fix_newlines = status
1465529Snate@binkert.org        return previous
1475529Snate@binkert.org
1485529Snate@binkert.org    def nofix(self):
1492292SN/A        previous = self._fix_newlines
1504329Sktlim@umich.edu        self._fix_newlines = False
1514329Sktlim@umich.edu        return previous
1524329Sktlim@umich.edu
1534329Sktlim@umich.edu    def clear():
1542292SN/A        self._data = []
1552307SN/A
1562307SN/A    def write(self, *args):
1572907Sktlim@umich.edu        f = file(os.path.join(*args), "w")
1582907Sktlim@umich.edu        for data in self._data:
1592292SN/A            f.write(data)
1602292SN/A        f.close()
1612329SN/A
1622329SN/A    def __str__(self):
1632329SN/A        data = string.join(self._data, '')
1642292SN/A        self._data = [ data ]
1652292SN/A        return data
1662292SN/A
1672292SN/A    def __getitem__(self, item):
1688199SAli.Saidi@ARM.com        return self._dict[item]
1698199SAli.Saidi@ARM.com
1708199SAli.Saidi@ARM.com    def __setitem__(self, item, value):
1712292SN/A        self._dict[item] = value
1722292SN/A
1732292SN/A    def __delitem__(self, item):
1742292SN/A        del self._dict[item]
1752292SN/A
1762292SN/A    def __contains__(self, item):
1772292SN/A        return item in self._dict
1783492Sktlim@umich.edu
1792329SN/A    def __iadd__(self, data):
1802292SN/A        self.append(data)
1812292SN/A
1822292SN/A    def append(self, data):
1832292SN/A        if isinstance(data, code_formatter):
1842292SN/A            self._data.extend(data._data)
1852292SN/A        else:
1862292SN/A            self._append(str(data))
1872292SN/A
1882292SN/A    def _append(self, data):
1892292SN/A        if not self._fix_newlines:
1902292SN/A            self._data.append(data)
1918247Snate@binkert.org            return
1922292SN/A
1932292SN/A        initial_newline = not self._data or self._data[-1] == '\n'
1942292SN/A        for line in data.splitlines():
1952292SN/A            if line:
1962292SN/A                if self._indent_level:
1972727Sktlim@umich.edu                    self._data.append(' ' * self._indent_level)
1982727Sktlim@umich.edu                self._data.append(line)
1992727Sktlim@umich.edu
2002727Sktlim@umich.edu            if line or not initial_newline:
2012727Sktlim@umich.edu                self._data.append('\n')
2022727Sktlim@umich.edu
2032727Sktlim@umich.edu            initial_newline = False
2042727Sktlim@umich.edu
2052727Sktlim@umich.edu    def __call__(self, *args, **kwargs):
2062727Sktlim@umich.edu        if not args:
2072727Sktlim@umich.edu            self._data.append('\n')
2082727Sktlim@umich.edu            return
2092727Sktlim@umich.edu
2102727Sktlim@umich.edu        format = args[0]
2112727Sktlim@umich.edu        args = args[1:]
2122727Sktlim@umich.edu
2132727Sktlim@umich.edu        frame = inspect.currentframe().f_back
2142727Sktlim@umich.edu
2152361SN/A        l = lookup(self, frame, *args, **kwargs)
2162361SN/A        def convert(match):
2172361SN/A            ident = match.group('lone')
2182361SN/A            # check for a lone identifier
2192727Sktlim@umich.edu            if ident:
2202727Sktlim@umich.edu                indent = match.group('indent') # must be spaces
2212727Sktlim@umich.edu                lone = '%s' % (l[ident], )
2222727Sktlim@umich.edu
2232727Sktlim@umich.edu                def indent_lines(gen):
2242727Sktlim@umich.edu                    for line in gen:
2252727Sktlim@umich.edu                        yield indent
2262727Sktlim@umich.edu                        yield line
2272727Sktlim@umich.edu                return ''.join(indent_lines(lone.splitlines(True)))
2282727Sktlim@umich.edu
2292727Sktlim@umich.edu            # check for an identifier, braced or not
2302727Sktlim@umich.edu            ident = match.group('ident') or match.group('b_ident')
2312727Sktlim@umich.edu            if ident is not None:
2322727Sktlim@umich.edu                return '%s' % (l[ident], )
2332727Sktlim@umich.edu
2342727Sktlim@umich.edu            # check for a positional parameter, braced or not
2352727Sktlim@umich.edu            pos = match.group('pos') or match.group('b_pos')
2362727Sktlim@umich.edu            if pos is not None:
2372727Sktlim@umich.edu                pos = int(pos)
2382727Sktlim@umich.edu                if pos > len(args):
2392727Sktlim@umich.edu                    raise ValueError \
2402727Sktlim@umich.edu                        ('Positional parameter #%d not found in pattern' % pos,
2412727Sktlim@umich.edu                         code_formatter.pattern)
2424329Sktlim@umich.edu                return '%s' % (args[int(pos)], )
2434329Sktlim@umich.edu
2444329Sktlim@umich.edu            # check for a double braced expression
2454329Sktlim@umich.edu            eval_expr = match.group('eval')
2464329Sktlim@umich.edu            if eval_expr is not None:
2474329Sktlim@umich.edu                result = eval(eval_expr, {}, l)
2484329Sktlim@umich.edu                return '%s' % (result, )
2494329Sktlim@umich.edu
2504329Sktlim@umich.edu            # check for an escaped delimiter
2514329Sktlim@umich.edu            if match.group('escaped') is not None:
2524329Sktlim@umich.edu                return '$'
2534329Sktlim@umich.edu
2544329Sktlim@umich.edu            # At this point, we have to match invalid
2552292SN/A            if match.group('invalid') is None:
2562292SN/A                # didn't match invalid!
2572292SN/A                raise ValueError('Unrecognized named group in pattern',
2582292SN/A                                 code_formatter.pattern)
2592292SN/A
2602292SN/A            i = match.start('invalid')
2612292SN/A            if i == 0:
2622292SN/A                colno = 1
2632292SN/A                lineno = 1
2642292SN/A            else:
2652292SN/A                lines = format[:i].splitlines(True)
2662292SN/A                colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines))
2672292SN/A                lineno = len(lines)
2682292SN/A
2692307SN/A                raise ValueError('Invalid format string: line %d, col %d' %
2702307SN/A                                 (lineno, colno))
2712307SN/A
2722367SN/A        d = code_formatter.pattern.sub(convert, format)
2732367SN/A        self._append(d)
2742307SN/A
2752367SN/A__all__ = [ "code_formatter" ]
2762307SN/A
2772329SN/Aif __name__ == '__main__':
2782307SN/A    from code_formatter import code_formatter
2792307SN/A    f = code_formatter()
2802307SN/A
2812307SN/A    class Foo(dict):
2822307SN/A        def __init__(self, **kwargs):
2832307SN/A            self.update(kwargs)
2842307SN/A        def __getattr__(self, attr):
2852307SN/A            return self[attr]
2862307SN/A
2872307SN/A    x = "this is a test"
2882307SN/A    l = [ [Foo(x=[Foo(y=9)])] ]
2892307SN/A
2902307SN/A    y = code_formatter()
2912307SN/A    y('''
2922307SN/A{
2932329SN/A    this_is_a_test();
2942307SN/A}
2952307SN/A''')
2962307SN/A    f('    $y')
2972307SN/A    f('''$__file__:$__line__
2982307SN/A{''')
2992307SN/A    f("${{', '.join(str(x) for x in xrange(4))}}")
3002307SN/A    f('${x}')
3012307SN/A    f('$x')
3022307SN/A    f.indent()
3032307SN/A    for i in xrange(5):
3042292SN/A        f('$x')
3052292SN/A        f('$i')
3062329SN/A        f('$0', "zero")
3072329SN/A        f('$1 $0', "zero", "one")
3082292SN/A        f('${0}', "he went")
3092329SN/A        f('${0}asdf', "he went")
3102329SN/A    f.dedent()
3112292SN/A
3122292SN/A    f('''
3132292SN/A    ${{l[0][0]["x"][0].y}}
3142292SN/A}
3152292SN/A''', 1, 9)
3162329SN/A
3172292SN/A    print(f, end=' ')
3182292SN/A