/src/python/m5/util/code_formatter.py
Python | 315 lines | 277 code | 13 blank | 25 comment | 13 complexity | 6df57f1c29e03e9d2269ff88ef2f66d9 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, WTFPL
- # Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org>
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are
- # met: redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer;
- # redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution;
- # neither the name of the copyright holders nor the names of its
- # contributors may be used to endorse or promote products derived from
- # this software without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- import __builtin__
- import inspect
- import os
- import re
- import string
- class lookup(object):
- def __init__(self, formatter, frame, *args, **kwargs):
- self.frame = frame
- self.formatter = formatter
- self.dict = self.formatter._dict
- self.args = args
- self.kwargs = kwargs
- self.locals = {}
- def __setitem__(self, item, val):
- self.locals[item] = val
- def __getitem__(self, item):
- if item in self.locals:
- return self.locals[item]
- if item in self.kwargs:
- return self.kwargs[item]
- if item == '__file__':
- return self.frame.f_code.co_filename
- if item == '__line__':
- return self.frame.f_lineno
- if self.formatter.locals and item in self.frame.f_locals:
- return self.frame.f_locals[item]
- if item in self.dict:
- return self.dict[item]
- if self.formatter.globals and item in self.frame.f_globals:
- return self.frame.f_globals[item]
- if item in __builtin__.__dict__:
- return __builtin__.__dict__[item]
- try:
- item = int(item)
- return self.args[item]
- except ValueError:
- pass
- raise IndexError, "Could not find '%s'" % item
- class code_formatter_meta(type):
- pattern = r"""
- (?:
- %(delim)s(?P<escaped>%(delim)s) | # escaped delimiter
- ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier
- %(delim)s(?P<ident>%(ident)s) | # identifier
- %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s | # braced identifier
- %(delim)s(?P<pos>%(pos)s) | # positional parameter
- %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s | # braced positional
- %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s | # double braced expression
- %(delim)s(?P<invalid>) # ill-formed delimiter exprs
- )
- """
- def __init__(cls, name, bases, dct):
- super(code_formatter_meta, cls).__init__(name, bases, dct)
- if 'pattern' in dct:
- pat = cls.pattern
- else:
- # tuple expansion to ensure strings are proper length
- lb,rb = cls.braced
- lb1,lb2,rb2,rb1 = cls.double_braced
- pat = code_formatter_meta.pattern % {
- 'delim' : re.escape(cls.delim),
- 'ident' : cls.ident,
- 'pos' : cls.pos,
- 'lb' : re.escape(lb),
- 'rb' : re.escape(rb),
- 'ldb' : re.escape(lb1+lb2),
- 'rdb' : re.escape(rb2+rb1),
- }
- cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE)
- class code_formatter(object):
- __metaclass__ = code_formatter_meta
- delim = r'$'
- ident = r'[_A-z]\w*'
- pos = r'[0-9]+'
- braced = r'{}'
- double_braced = r'{{}}'
- globals = True
- locals = True
- fix_newlines = True
- def __init__(self, *args, **kwargs):
- self._data = []
- self._dict = {}
- self._indent_level = 0
- self._indent_spaces = 4
- self.globals = kwargs.pop('globals', type(self).globals)
- self.locals = kwargs.pop('locals', type(self).locals)
- self._fix_newlines = \
- kwargs.pop('fix_newlines', type(self).fix_newlines)
- if args:
- self.__call__(args)
- def indent(self, count=1):
- self._indent_level += self._indent_spaces * count
- def dedent(self, count=1):
- assert self._indent_level >= (self._indent_spaces * count)
- self._indent_level -= self._indent_spaces * count
- def fix(self, status):
- previous = self._fix_newlines
- self._fix_newlines = status
- return previous
- def nofix(self):
- previous = self._fix_newlines
- self._fix_newlines = False
- return previous
- def clear():
- self._data = []
- def write(self, *args):
- f = file(os.path.join(*args), "w")
- for data in self._data:
- f.write(data)
- f.close()
- def __str__(self):
- data = string.join(self._data, '')
- self._data = [ data ]
- return data
- def __getitem__(self, item):
- return self._dict[item]
- def __setitem__(self, item, value):
- self._dict[item] = value
- def __delitem__(self, item):
- del self._dict[item]
- def __contains__(self, item):
- return item in self._dict
- def __iadd__(self, data):
- self.append(data)
- def append(self, data):
- if isinstance(data, code_formatter):
- self._data.extend(data._data)
- else:
- self._append(str(data))
- def _append(self, data):
- if not self._fix_newlines:
- self._data.append(data)
- return
- initial_newline = not self._data or self._data[-1] == '\n'
- for line in data.splitlines():
- if line:
- if self._indent_level:
- self._data.append(' ' * self._indent_level)
- self._data.append(line)
- if line or not initial_newline:
- self._data.append('\n')
- initial_newline = False
- def __call__(self, *args, **kwargs):
- if not args:
- self._data.append('\n')
- return
- format = args[0]
- args = args[1:]
- frame = inspect.currentframe().f_back
- l = lookup(self, frame, *args, **kwargs)
- def convert(match):
- ident = match.group('lone')
- # check for a lone identifier
- if ident:
- indent = match.group('indent') # must be spaces
- lone = '%s' % (l[ident], )
- def indent_lines(gen):
- for line in gen:
- yield indent
- yield line
- return ''.join(indent_lines(lone.splitlines(True)))
- # check for an identifier, braced or not
- ident = match.group('ident') or match.group('b_ident')
- if ident is not None:
- return '%s' % (l[ident], )
- # check for a positional parameter, braced or not
- pos = match.group('pos') or match.group('b_pos')
- if pos is not None:
- pos = int(pos)
- if pos > len(args):
- raise ValueError \
- ('Positional parameter #%d not found in pattern' % pos,
- code_formatter.pattern)
- return '%s' % (args[int(pos)], )
- # check for a double braced expression
- eval_expr = match.group('eval')
- if eval_expr is not None:
- result = eval(eval_expr, {}, l)
- return '%s' % (result, )
- # check for an escaped delimiter
- if match.group('escaped') is not None:
- return '$'
- # At this point, we have to match invalid
- if match.group('invalid') is None:
- # didn't match invalid!
- raise ValueError('Unrecognized named group in pattern',
- code_formatter.pattern)
- i = match.start('invalid')
- if i == 0:
- colno = 1
- lineno = 1
- else:
- lines = format[:i].splitlines(True)
- colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines))
- lineno = len(lines)
- raise ValueError('Invalid format string: line %d, col %d' %
- (lineno, colno))
- d = code_formatter.pattern.sub(convert, format)
- self._append(d)
- __all__ = [ "code_formatter" ]
- if __name__ == '__main__':
- from code_formatter import code_formatter
- f = code_formatter()
- class Foo(dict):
- def __init__(self, **kwargs):
- self.update(kwargs)
- def __getattr__(self, attr):
- return self[attr]
- x = "this is a test"
- l = [ [Foo(x=[Foo(y=9)])] ]
- y = code_formatter()
- y('''
- {
- this_is_a_test();
- }
- ''')
- f(' $y')
- f('''$__file__:$__line__
- {''')
- f("${{', '.join(str(x) for x in xrange(4))}}")
- f('${x}')
- f('$x')
- f.indent()
- for i in xrange(5):
- f('$x')
- f('$i')
- f('$0', "zero")
- f('$1 $0', "zero", "one")
- f('${0}', "he went")
- f('${0}asdf', "he went")
- f.dedent()
- f('''
- ${{l[0][0]["x"][0].y}}
- }
- ''', 1, 9)
- print f,