PageRenderTime 36ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/src/python/m5/util/code_formatter.py

https://bitbucket.org/musleh123/ece565
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
  1. # Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org>
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met: redistributions of source code must retain the above copyright
  7. # notice, this list of conditions and the following disclaimer;
  8. # redistributions in binary form must reproduce the above copyright
  9. # notice, this list of conditions and the following disclaimer in the
  10. # documentation and/or other materials provided with the distribution;
  11. # neither the name of the copyright holders nor the names of its
  12. # contributors may be used to endorse or promote products derived from
  13. # this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. import __builtin__
  27. import inspect
  28. import os
  29. import re
  30. import string
  31. class lookup(object):
  32. def __init__(self, formatter, frame, *args, **kwargs):
  33. self.frame = frame
  34. self.formatter = formatter
  35. self.dict = self.formatter._dict
  36. self.args = args
  37. self.kwargs = kwargs
  38. self.locals = {}
  39. def __setitem__(self, item, val):
  40. self.locals[item] = val
  41. def __getitem__(self, item):
  42. if item in self.locals:
  43. return self.locals[item]
  44. if item in self.kwargs:
  45. return self.kwargs[item]
  46. if item == '__file__':
  47. return self.frame.f_code.co_filename
  48. if item == '__line__':
  49. return self.frame.f_lineno
  50. if self.formatter.locals and item in self.frame.f_locals:
  51. return self.frame.f_locals[item]
  52. if item in self.dict:
  53. return self.dict[item]
  54. if self.formatter.globals and item in self.frame.f_globals:
  55. return self.frame.f_globals[item]
  56. if item in __builtin__.__dict__:
  57. return __builtin__.__dict__[item]
  58. try:
  59. item = int(item)
  60. return self.args[item]
  61. except ValueError:
  62. pass
  63. raise IndexError, "Could not find '%s'" % item
  64. class code_formatter_meta(type):
  65. pattern = r"""
  66. (?:
  67. %(delim)s(?P<escaped>%(delim)s) | # escaped delimiter
  68. ^(?P<indent>[ ]*)%(delim)s(?P<lone>%(ident)s)$ | # lone identifier
  69. %(delim)s(?P<ident>%(ident)s) | # identifier
  70. %(delim)s%(lb)s(?P<b_ident>%(ident)s)%(rb)s | # braced identifier
  71. %(delim)s(?P<pos>%(pos)s) | # positional parameter
  72. %(delim)s%(lb)s(?P<b_pos>%(pos)s)%(rb)s | # braced positional
  73. %(delim)s%(ldb)s(?P<eval>.*?)%(rdb)s | # double braced expression
  74. %(delim)s(?P<invalid>) # ill-formed delimiter exprs
  75. )
  76. """
  77. def __init__(cls, name, bases, dct):
  78. super(code_formatter_meta, cls).__init__(name, bases, dct)
  79. if 'pattern' in dct:
  80. pat = cls.pattern
  81. else:
  82. # tuple expansion to ensure strings are proper length
  83. lb,rb = cls.braced
  84. lb1,lb2,rb2,rb1 = cls.double_braced
  85. pat = code_formatter_meta.pattern % {
  86. 'delim' : re.escape(cls.delim),
  87. 'ident' : cls.ident,
  88. 'pos' : cls.pos,
  89. 'lb' : re.escape(lb),
  90. 'rb' : re.escape(rb),
  91. 'ldb' : re.escape(lb1+lb2),
  92. 'rdb' : re.escape(rb2+rb1),
  93. }
  94. cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE)
  95. class code_formatter(object):
  96. __metaclass__ = code_formatter_meta
  97. delim = r'$'
  98. ident = r'[_A-z]\w*'
  99. pos = r'[0-9]+'
  100. braced = r'{}'
  101. double_braced = r'{{}}'
  102. globals = True
  103. locals = True
  104. fix_newlines = True
  105. def __init__(self, *args, **kwargs):
  106. self._data = []
  107. self._dict = {}
  108. self._indent_level = 0
  109. self._indent_spaces = 4
  110. self.globals = kwargs.pop('globals', type(self).globals)
  111. self.locals = kwargs.pop('locals', type(self).locals)
  112. self._fix_newlines = \
  113. kwargs.pop('fix_newlines', type(self).fix_newlines)
  114. if args:
  115. self.__call__(args)
  116. def indent(self, count=1):
  117. self._indent_level += self._indent_spaces * count
  118. def dedent(self, count=1):
  119. assert self._indent_level >= (self._indent_spaces * count)
  120. self._indent_level -= self._indent_spaces * count
  121. def fix(self, status):
  122. previous = self._fix_newlines
  123. self._fix_newlines = status
  124. return previous
  125. def nofix(self):
  126. previous = self._fix_newlines
  127. self._fix_newlines = False
  128. return previous
  129. def clear():
  130. self._data = []
  131. def write(self, *args):
  132. f = file(os.path.join(*args), "w")
  133. for data in self._data:
  134. f.write(data)
  135. f.close()
  136. def __str__(self):
  137. data = string.join(self._data, '')
  138. self._data = [ data ]
  139. return data
  140. def __getitem__(self, item):
  141. return self._dict[item]
  142. def __setitem__(self, item, value):
  143. self._dict[item] = value
  144. def __delitem__(self, item):
  145. del self._dict[item]
  146. def __contains__(self, item):
  147. return item in self._dict
  148. def __iadd__(self, data):
  149. self.append(data)
  150. def append(self, data):
  151. if isinstance(data, code_formatter):
  152. self._data.extend(data._data)
  153. else:
  154. self._append(str(data))
  155. def _append(self, data):
  156. if not self._fix_newlines:
  157. self._data.append(data)
  158. return
  159. initial_newline = not self._data or self._data[-1] == '\n'
  160. for line in data.splitlines():
  161. if line:
  162. if self._indent_level:
  163. self._data.append(' ' * self._indent_level)
  164. self._data.append(line)
  165. if line or not initial_newline:
  166. self._data.append('\n')
  167. initial_newline = False
  168. def __call__(self, *args, **kwargs):
  169. if not args:
  170. self._data.append('\n')
  171. return
  172. format = args[0]
  173. args = args[1:]
  174. frame = inspect.currentframe().f_back
  175. l = lookup(self, frame, *args, **kwargs)
  176. def convert(match):
  177. ident = match.group('lone')
  178. # check for a lone identifier
  179. if ident:
  180. indent = match.group('indent') # must be spaces
  181. lone = '%s' % (l[ident], )
  182. def indent_lines(gen):
  183. for line in gen:
  184. yield indent
  185. yield line
  186. return ''.join(indent_lines(lone.splitlines(True)))
  187. # check for an identifier, braced or not
  188. ident = match.group('ident') or match.group('b_ident')
  189. if ident is not None:
  190. return '%s' % (l[ident], )
  191. # check for a positional parameter, braced or not
  192. pos = match.group('pos') or match.group('b_pos')
  193. if pos is not None:
  194. pos = int(pos)
  195. if pos > len(args):
  196. raise ValueError \
  197. ('Positional parameter #%d not found in pattern' % pos,
  198. code_formatter.pattern)
  199. return '%s' % (args[int(pos)], )
  200. # check for a double braced expression
  201. eval_expr = match.group('eval')
  202. if eval_expr is not None:
  203. result = eval(eval_expr, {}, l)
  204. return '%s' % (result, )
  205. # check for an escaped delimiter
  206. if match.group('escaped') is not None:
  207. return '$'
  208. # At this point, we have to match invalid
  209. if match.group('invalid') is None:
  210. # didn't match invalid!
  211. raise ValueError('Unrecognized named group in pattern',
  212. code_formatter.pattern)
  213. i = match.start('invalid')
  214. if i == 0:
  215. colno = 1
  216. lineno = 1
  217. else:
  218. lines = format[:i].splitlines(True)
  219. colno = i - reduce(lambda x,y: x+y, (len(z) for z in lines))
  220. lineno = len(lines)
  221. raise ValueError('Invalid format string: line %d, col %d' %
  222. (lineno, colno))
  223. d = code_formatter.pattern.sub(convert, format)
  224. self._append(d)
  225. __all__ = [ "code_formatter" ]
  226. if __name__ == '__main__':
  227. from code_formatter import code_formatter
  228. f = code_formatter()
  229. class Foo(dict):
  230. def __init__(self, **kwargs):
  231. self.update(kwargs)
  232. def __getattr__(self, attr):
  233. return self[attr]
  234. x = "this is a test"
  235. l = [ [Foo(x=[Foo(y=9)])] ]
  236. y = code_formatter()
  237. y('''
  238. {
  239. this_is_a_test();
  240. }
  241. ''')
  242. f(' $y')
  243. f('''$__file__:$__line__
  244. {''')
  245. f("${{', '.join(str(x) for x in xrange(4))}}")
  246. f('${x}')
  247. f('$x')
  248. f.indent()
  249. for i in xrange(5):
  250. f('$x')
  251. f('$i')
  252. f('$0', "zero")
  253. f('$1 $0', "zero", "one")
  254. f('${0}', "he went")
  255. f('${0}asdf', "he went")
  256. f.dedent()
  257. f('''
  258. ${{l[0][0]["x"][0].y}}
  259. }
  260. ''', 1, 9)
  261. print f,