/thirdparty/breakpad/third_party/protobuf/protobuf/gtest/scripts/pump.py

http://github.com/tomahawk-player/tomahawk · Python · 835 lines · 712 code · 49 blank · 74 comment · 44 complexity · 92f0800d1806672a9c18df8611a02de5 MD5 · raw file

  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2008, Google Inc.
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. #
  10. # * Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # * Redistributions in binary form must reproduce the above
  13. # copyright notice, this list of conditions and the following disclaimer
  14. # in the documentation and/or other materials provided with the
  15. # distribution.
  16. # * Neither the name of Google Inc. nor the names of its
  17. # contributors may be used to endorse or promote products derived from
  18. # this software without specific prior written permission.
  19. #
  20. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. """pump v0.1 - Pretty Useful for Meta Programming.
  32. A tool for preprocessor meta programming. Useful for generating
  33. repetitive boilerplate code. Especially useful for writing C++
  34. classes, functions, macros, and templates that need to work with
  35. various number of arguments.
  36. USAGE:
  37. pump.py SOURCE_FILE
  38. EXAMPLES:
  39. pump.py foo.cc.pump
  40. Converts foo.cc.pump to foo.cc.
  41. GRAMMAR:
  42. CODE ::= ATOMIC_CODE*
  43. ATOMIC_CODE ::= $var ID = EXPRESSION
  44. | $var ID = [[ CODE ]]
  45. | $range ID EXPRESSION..EXPRESSION
  46. | $for ID SEPARATOR [[ CODE ]]
  47. | $($)
  48. | $ID
  49. | $(EXPRESSION)
  50. | $if EXPRESSION [[ CODE ]] ELSE_BRANCH
  51. | [[ CODE ]]
  52. | RAW_CODE
  53. SEPARATOR ::= RAW_CODE | EMPTY
  54. ELSE_BRANCH ::= $else [[ CODE ]]
  55. | $elif EXPRESSION [[ CODE ]] ELSE_BRANCH
  56. | EMPTY
  57. EXPRESSION has Python syntax.
  58. """
  59. __author__ = 'wan@google.com (Zhanyong Wan)'
  60. import os
  61. import re
  62. import sys
  63. TOKEN_TABLE = [
  64. (re.compile(r'\$var\s+'), '$var'),
  65. (re.compile(r'\$elif\s+'), '$elif'),
  66. (re.compile(r'\$else\s+'), '$else'),
  67. (re.compile(r'\$for\s+'), '$for'),
  68. (re.compile(r'\$if\s+'), '$if'),
  69. (re.compile(r'\$range\s+'), '$range'),
  70. (re.compile(r'\$[_A-Za-z]\w*'), '$id'),
  71. (re.compile(r'\$\(\$\)'), '$($)'),
  72. (re.compile(r'\$\$.*'), '$$'),
  73. (re.compile(r'\$'), '$'),
  74. (re.compile(r'\[\[\n?'), '[['),
  75. (re.compile(r'\]\]\n?'), ']]'),
  76. ]
  77. class Cursor:
  78. """Represents a position (line and column) in a text file."""
  79. def __init__(self, line=-1, column=-1):
  80. self.line = line
  81. self.column = column
  82. def __eq__(self, rhs):
  83. return self.line == rhs.line and self.column == rhs.column
  84. def __ne__(self, rhs):
  85. return not self == rhs
  86. def __lt__(self, rhs):
  87. return self.line < rhs.line or (
  88. self.line == rhs.line and self.column < rhs.column)
  89. def __le__(self, rhs):
  90. return self < rhs or self == rhs
  91. def __gt__(self, rhs):
  92. return rhs < self
  93. def __ge__(self, rhs):
  94. return rhs <= self
  95. def __str__(self):
  96. if self == Eof():
  97. return 'EOF'
  98. else:
  99. return '%s(%s)' % (self.line + 1, self.column)
  100. def __add__(self, offset):
  101. return Cursor(self.line, self.column + offset)
  102. def __sub__(self, offset):
  103. return Cursor(self.line, self.column - offset)
  104. def Clone(self):
  105. """Returns a copy of self."""
  106. return Cursor(self.line, self.column)
  107. # Special cursor to indicate the end-of-file.
  108. def Eof():
  109. """Returns the special cursor to denote the end-of-file."""
  110. return Cursor(-1, -1)
  111. class Token:
  112. """Represents a token in a Pump source file."""
  113. def __init__(self, start=None, end=None, value=None, token_type=None):
  114. if start is None:
  115. self.start = Eof()
  116. else:
  117. self.start = start
  118. if end is None:
  119. self.end = Eof()
  120. else:
  121. self.end = end
  122. self.value = value
  123. self.token_type = token_type
  124. def __str__(self):
  125. return 'Token @%s: \'%s\' type=%s' % (
  126. self.start, self.value, self.token_type)
  127. def Clone(self):
  128. """Returns a copy of self."""
  129. return Token(self.start.Clone(), self.end.Clone(), self.value,
  130. self.token_type)
  131. def StartsWith(lines, pos, string):
  132. """Returns True iff the given position in lines starts with 'string'."""
  133. return lines[pos.line][pos.column:].startswith(string)
  134. def FindFirstInLine(line, token_table):
  135. best_match_start = -1
  136. for (regex, token_type) in token_table:
  137. m = regex.search(line)
  138. if m:
  139. # We found regex in lines
  140. if best_match_start < 0 or m.start() < best_match_start:
  141. best_match_start = m.start()
  142. best_match_length = m.end() - m.start()
  143. best_match_token_type = token_type
  144. if best_match_start < 0:
  145. return None
  146. return (best_match_start, best_match_length, best_match_token_type)
  147. def FindFirst(lines, token_table, cursor):
  148. """Finds the first occurrence of any string in strings in lines."""
  149. start = cursor.Clone()
  150. cur_line_number = cursor.line
  151. for line in lines[start.line:]:
  152. if cur_line_number == start.line:
  153. line = line[start.column:]
  154. m = FindFirstInLine(line, token_table)
  155. if m:
  156. # We found a regex in line.
  157. (start_column, length, token_type) = m
  158. if cur_line_number == start.line:
  159. start_column += start.column
  160. found_start = Cursor(cur_line_number, start_column)
  161. found_end = found_start + length
  162. return MakeToken(lines, found_start, found_end, token_type)
  163. cur_line_number += 1
  164. # We failed to find str in lines
  165. return None
  166. def SubString(lines, start, end):
  167. """Returns a substring in lines."""
  168. if end == Eof():
  169. end = Cursor(len(lines) - 1, len(lines[-1]))
  170. if start >= end:
  171. return ''
  172. if start.line == end.line:
  173. return lines[start.line][start.column:end.column]
  174. result_lines = ([lines[start.line][start.column:]] +
  175. lines[start.line + 1:end.line] +
  176. [lines[end.line][:end.column]])
  177. return ''.join(result_lines)
  178. def MakeToken(lines, start, end, token_type):
  179. """Creates a new instance of Token."""
  180. return Token(start, end, SubString(lines, start, end), token_type)
  181. def ParseToken(lines, pos, regex, token_type):
  182. line = lines[pos.line][pos.column:]
  183. m = regex.search(line)
  184. if m and not m.start():
  185. return MakeToken(lines, pos, pos + m.end(), token_type)
  186. else:
  187. print 'ERROR: %s expected at %s.' % (token_type, pos)
  188. sys.exit(1)
  189. ID_REGEX = re.compile(r'[_A-Za-z]\w*')
  190. EQ_REGEX = re.compile(r'=')
  191. REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)')
  192. OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*')
  193. WHITE_SPACE_REGEX = re.compile(r'\s')
  194. DOT_DOT_REGEX = re.compile(r'\.\.')
  195. def Skip(lines, pos, regex):
  196. line = lines[pos.line][pos.column:]
  197. m = re.search(regex, line)
  198. if m and not m.start():
  199. return pos + m.end()
  200. else:
  201. return pos
  202. def SkipUntil(lines, pos, regex, token_type):
  203. line = lines[pos.line][pos.column:]
  204. m = re.search(regex, line)
  205. if m:
  206. return pos + m.start()
  207. else:
  208. print ('ERROR: %s expected on line %s after column %s.' %
  209. (token_type, pos.line + 1, pos.column))
  210. sys.exit(1)
  211. def ParseExpTokenInParens(lines, pos):
  212. def ParseInParens(pos):
  213. pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX)
  214. pos = Skip(lines, pos, r'\(')
  215. pos = Parse(pos)
  216. pos = Skip(lines, pos, r'\)')
  217. return pos
  218. def Parse(pos):
  219. pos = SkipUntil(lines, pos, r'\(|\)', ')')
  220. if SubString(lines, pos, pos + 1) == '(':
  221. pos = Parse(pos + 1)
  222. pos = Skip(lines, pos, r'\)')
  223. return Parse(pos)
  224. else:
  225. return pos
  226. start = pos.Clone()
  227. pos = ParseInParens(pos)
  228. return MakeToken(lines, start, pos, 'exp')
  229. def RStripNewLineFromToken(token):
  230. if token.value.endswith('\n'):
  231. return Token(token.start, token.end, token.value[:-1], token.token_type)
  232. else:
  233. return token
  234. def TokenizeLines(lines, pos):
  235. while True:
  236. found = FindFirst(lines, TOKEN_TABLE, pos)
  237. if not found:
  238. yield MakeToken(lines, pos, Eof(), 'code')
  239. return
  240. if found.start == pos:
  241. prev_token = None
  242. prev_token_rstripped = None
  243. else:
  244. prev_token = MakeToken(lines, pos, found.start, 'code')
  245. prev_token_rstripped = RStripNewLineFromToken(prev_token)
  246. if found.token_type == '$$': # A meta comment.
  247. if prev_token_rstripped:
  248. yield prev_token_rstripped
  249. pos = Cursor(found.end.line + 1, 0)
  250. elif found.token_type == '$var':
  251. if prev_token_rstripped:
  252. yield prev_token_rstripped
  253. yield found
  254. id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
  255. yield id_token
  256. pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
  257. eq_token = ParseToken(lines, pos, EQ_REGEX, '=')
  258. yield eq_token
  259. pos = Skip(lines, eq_token.end, r'\s*')
  260. if SubString(lines, pos, pos + 2) != '[[':
  261. exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp')
  262. yield exp_token
  263. pos = Cursor(exp_token.end.line + 1, 0)
  264. elif found.token_type == '$for':
  265. if prev_token_rstripped:
  266. yield prev_token_rstripped
  267. yield found
  268. id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
  269. yield id_token
  270. pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX)
  271. elif found.token_type == '$range':
  272. if prev_token_rstripped:
  273. yield prev_token_rstripped
  274. yield found
  275. id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
  276. yield id_token
  277. pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
  278. dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..')
  279. yield MakeToken(lines, pos, dots_pos, 'exp')
  280. yield MakeToken(lines, dots_pos, dots_pos + 2, '..')
  281. pos = dots_pos + 2
  282. new_pos = Cursor(pos.line + 1, 0)
  283. yield MakeToken(lines, pos, new_pos, 'exp')
  284. pos = new_pos
  285. elif found.token_type == '$':
  286. if prev_token:
  287. yield prev_token
  288. yield found
  289. exp_token = ParseExpTokenInParens(lines, found.end)
  290. yield exp_token
  291. pos = exp_token.end
  292. elif (found.token_type == ']]' or found.token_type == '$if' or
  293. found.token_type == '$elif' or found.token_type == '$else'):
  294. if prev_token_rstripped:
  295. yield prev_token_rstripped
  296. yield found
  297. pos = found.end
  298. else:
  299. if prev_token:
  300. yield prev_token
  301. yield found
  302. pos = found.end
  303. def Tokenize(s):
  304. lines = s.splitlines(True)
  305. return TokenizeLines(lines, Cursor(0, 0))
  306. class CodeNode:
  307. def __init__(self, atomic_code_list=None):
  308. self.atomic_code = atomic_code_list
  309. class VarNode:
  310. def __init__(self, identifier=None, atomic_code=None):
  311. self.identifier = identifier
  312. self.atomic_code = atomic_code
  313. class RangeNode:
  314. def __init__(self, identifier=None, exp1=None, exp2=None):
  315. self.identifier = identifier
  316. self.exp1 = exp1
  317. self.exp2 = exp2
  318. class ForNode:
  319. def __init__(self, identifier=None, sep=None, code=None):
  320. self.identifier = identifier
  321. self.sep = sep
  322. self.code = code
  323. class ElseNode:
  324. def __init__(self, else_branch=None):
  325. self.else_branch = else_branch
  326. class IfNode:
  327. def __init__(self, exp=None, then_branch=None, else_branch=None):
  328. self.exp = exp
  329. self.then_branch = then_branch
  330. self.else_branch = else_branch
  331. class RawCodeNode:
  332. def __init__(self, token=None):
  333. self.raw_code = token
  334. class LiteralDollarNode:
  335. def __init__(self, token):
  336. self.token = token
  337. class ExpNode:
  338. def __init__(self, token, python_exp):
  339. self.token = token
  340. self.python_exp = python_exp
  341. def PopFront(a_list):
  342. head = a_list[0]
  343. a_list[:1] = []
  344. return head
  345. def PushFront(a_list, elem):
  346. a_list[:0] = [elem]
  347. def PopToken(a_list, token_type=None):
  348. token = PopFront(a_list)
  349. if token_type is not None and token.token_type != token_type:
  350. print 'ERROR: %s expected at %s' % (token_type, token.start)
  351. print 'ERROR: %s found instead' % (token,)
  352. sys.exit(1)
  353. return token
  354. def PeekToken(a_list):
  355. if not a_list:
  356. return None
  357. return a_list[0]
  358. def ParseExpNode(token):
  359. python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value)
  360. return ExpNode(token, python_exp)
  361. def ParseElseNode(tokens):
  362. def Pop(token_type=None):
  363. return PopToken(tokens, token_type)
  364. next = PeekToken(tokens)
  365. if not next:
  366. return None
  367. if next.token_type == '$else':
  368. Pop('$else')
  369. Pop('[[')
  370. code_node = ParseCodeNode(tokens)
  371. Pop(']]')
  372. return code_node
  373. elif next.token_type == '$elif':
  374. Pop('$elif')
  375. exp = Pop('code')
  376. Pop('[[')
  377. code_node = ParseCodeNode(tokens)
  378. Pop(']]')
  379. inner_else_node = ParseElseNode(tokens)
  380. return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)])
  381. elif not next.value.strip():
  382. Pop('code')
  383. return ParseElseNode(tokens)
  384. else:
  385. return None
  386. def ParseAtomicCodeNode(tokens):
  387. def Pop(token_type=None):
  388. return PopToken(tokens, token_type)
  389. head = PopFront(tokens)
  390. t = head.token_type
  391. if t == 'code':
  392. return RawCodeNode(head)
  393. elif t == '$var':
  394. id_token = Pop('id')
  395. Pop('=')
  396. next = PeekToken(tokens)
  397. if next.token_type == 'exp':
  398. exp_token = Pop()
  399. return VarNode(id_token, ParseExpNode(exp_token))
  400. Pop('[[')
  401. code_node = ParseCodeNode(tokens)
  402. Pop(']]')
  403. return VarNode(id_token, code_node)
  404. elif t == '$for':
  405. id_token = Pop('id')
  406. next_token = PeekToken(tokens)
  407. if next_token.token_type == 'code':
  408. sep_token = next_token
  409. Pop('code')
  410. else:
  411. sep_token = None
  412. Pop('[[')
  413. code_node = ParseCodeNode(tokens)
  414. Pop(']]')
  415. return ForNode(id_token, sep_token, code_node)
  416. elif t == '$if':
  417. exp_token = Pop('code')
  418. Pop('[[')
  419. code_node = ParseCodeNode(tokens)
  420. Pop(']]')
  421. else_node = ParseElseNode(tokens)
  422. return IfNode(ParseExpNode(exp_token), code_node, else_node)
  423. elif t == '$range':
  424. id_token = Pop('id')
  425. exp1_token = Pop('exp')
  426. Pop('..')
  427. exp2_token = Pop('exp')
  428. return RangeNode(id_token, ParseExpNode(exp1_token),
  429. ParseExpNode(exp2_token))
  430. elif t == '$id':
  431. return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id'))
  432. elif t == '$($)':
  433. return LiteralDollarNode(head)
  434. elif t == '$':
  435. exp_token = Pop('exp')
  436. return ParseExpNode(exp_token)
  437. elif t == '[[':
  438. code_node = ParseCodeNode(tokens)
  439. Pop(']]')
  440. return code_node
  441. else:
  442. PushFront(tokens, head)
  443. return None
  444. def ParseCodeNode(tokens):
  445. atomic_code_list = []
  446. while True:
  447. if not tokens:
  448. break
  449. atomic_code_node = ParseAtomicCodeNode(tokens)
  450. if atomic_code_node:
  451. atomic_code_list.append(atomic_code_node)
  452. else:
  453. break
  454. return CodeNode(atomic_code_list)
  455. def Convert(file_path):
  456. s = file(file_path, 'r').read()
  457. tokens = []
  458. for token in Tokenize(s):
  459. tokens.append(token)
  460. code_node = ParseCodeNode(tokens)
  461. return code_node
  462. class Env:
  463. def __init__(self):
  464. self.variables = []
  465. self.ranges = []
  466. def Clone(self):
  467. clone = Env()
  468. clone.variables = self.variables[:]
  469. clone.ranges = self.ranges[:]
  470. return clone
  471. def PushVariable(self, var, value):
  472. # If value looks like an int, store it as an int.
  473. try:
  474. int_value = int(value)
  475. if ('%s' % int_value) == value:
  476. value = int_value
  477. except Exception:
  478. pass
  479. self.variables[:0] = [(var, value)]
  480. def PopVariable(self):
  481. self.variables[:1] = []
  482. def PushRange(self, var, lower, upper):
  483. self.ranges[:0] = [(var, lower, upper)]
  484. def PopRange(self):
  485. self.ranges[:1] = []
  486. def GetValue(self, identifier):
  487. for (var, value) in self.variables:
  488. if identifier == var:
  489. return value
  490. print 'ERROR: meta variable %s is undefined.' % (identifier,)
  491. sys.exit(1)
  492. def EvalExp(self, exp):
  493. try:
  494. result = eval(exp.python_exp)
  495. except Exception, e:
  496. print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e)
  497. print ('ERROR: failed to evaluate meta expression %s at %s' %
  498. (exp.python_exp, exp.token.start))
  499. sys.exit(1)
  500. return result
  501. def GetRange(self, identifier):
  502. for (var, lower, upper) in self.ranges:
  503. if identifier == var:
  504. return (lower, upper)
  505. print 'ERROR: range %s is undefined.' % (identifier,)
  506. sys.exit(1)
  507. class Output:
  508. def __init__(self):
  509. self.string = ''
  510. def GetLastLine(self):
  511. index = self.string.rfind('\n')
  512. if index < 0:
  513. return ''
  514. return self.string[index + 1:]
  515. def Append(self, s):
  516. self.string += s
  517. def RunAtomicCode(env, node, output):
  518. if isinstance(node, VarNode):
  519. identifier = node.identifier.value.strip()
  520. result = Output()
  521. RunAtomicCode(env.Clone(), node.atomic_code, result)
  522. value = result.string
  523. env.PushVariable(identifier, value)
  524. elif isinstance(node, RangeNode):
  525. identifier = node.identifier.value.strip()
  526. lower = int(env.EvalExp(node.exp1))
  527. upper = int(env.EvalExp(node.exp2))
  528. env.PushRange(identifier, lower, upper)
  529. elif isinstance(node, ForNode):
  530. identifier = node.identifier.value.strip()
  531. if node.sep is None:
  532. sep = ''
  533. else:
  534. sep = node.sep.value
  535. (lower, upper) = env.GetRange(identifier)
  536. for i in range(lower, upper + 1):
  537. new_env = env.Clone()
  538. new_env.PushVariable(identifier, i)
  539. RunCode(new_env, node.code, output)
  540. if i != upper:
  541. output.Append(sep)
  542. elif isinstance(node, RawCodeNode):
  543. output.Append(node.raw_code.value)
  544. elif isinstance(node, IfNode):
  545. cond = env.EvalExp(node.exp)
  546. if cond:
  547. RunCode(env.Clone(), node.then_branch, output)
  548. elif node.else_branch is not None:
  549. RunCode(env.Clone(), node.else_branch, output)
  550. elif isinstance(node, ExpNode):
  551. value = env.EvalExp(node)
  552. output.Append('%s' % (value,))
  553. elif isinstance(node, LiteralDollarNode):
  554. output.Append('$')
  555. elif isinstance(node, CodeNode):
  556. RunCode(env.Clone(), node, output)
  557. else:
  558. print 'BAD'
  559. print node
  560. sys.exit(1)
  561. def RunCode(env, code_node, output):
  562. for atomic_code in code_node.atomic_code:
  563. RunAtomicCode(env, atomic_code, output)
  564. def IsComment(cur_line):
  565. return '//' in cur_line
  566. def IsInPreprocessorDirevative(prev_lines, cur_line):
  567. if cur_line.lstrip().startswith('#'):
  568. return True
  569. return prev_lines != [] and prev_lines[-1].endswith('\\')
  570. def WrapComment(line, output):
  571. loc = line.find('//')
  572. before_comment = line[:loc].rstrip()
  573. if before_comment == '':
  574. indent = loc
  575. else:
  576. output.append(before_comment)
  577. indent = len(before_comment) - len(before_comment.lstrip())
  578. prefix = indent*' ' + '// '
  579. max_len = 80 - len(prefix)
  580. comment = line[loc + 2:].strip()
  581. segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != '']
  582. cur_line = ''
  583. for seg in segs:
  584. if len((cur_line + seg).rstrip()) < max_len:
  585. cur_line += seg
  586. else:
  587. if cur_line.strip() != '':
  588. output.append(prefix + cur_line.rstrip())
  589. cur_line = seg.lstrip()
  590. if cur_line.strip() != '':
  591. output.append(prefix + cur_line.strip())
  592. def WrapCode(line, line_concat, output):
  593. indent = len(line) - len(line.lstrip())
  594. prefix = indent*' ' # Prefix of the current line
  595. max_len = 80 - indent - len(line_concat) # Maximum length of the current line
  596. new_prefix = prefix + 4*' ' # Prefix of a continuation line
  597. new_max_len = max_len - 4 # Maximum length of a continuation line
  598. # Prefers to wrap a line after a ',' or ';'.
  599. segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != '']
  600. cur_line = '' # The current line without leading spaces.
  601. for seg in segs:
  602. # If the line is still too long, wrap at a space.
  603. while cur_line == '' and len(seg.strip()) > max_len:
  604. seg = seg.lstrip()
  605. split_at = seg.rfind(' ', 0, max_len)
  606. output.append(prefix + seg[:split_at].strip() + line_concat)
  607. seg = seg[split_at + 1:]
  608. prefix = new_prefix
  609. max_len = new_max_len
  610. if len((cur_line + seg).rstrip()) < max_len:
  611. cur_line = (cur_line + seg).lstrip()
  612. else:
  613. output.append(prefix + cur_line.rstrip() + line_concat)
  614. prefix = new_prefix
  615. max_len = new_max_len
  616. cur_line = seg.lstrip()
  617. if cur_line.strip() != '':
  618. output.append(prefix + cur_line.strip())
  619. def WrapPreprocessorDirevative(line, output):
  620. WrapCode(line, ' \\', output)
  621. def WrapPlainCode(line, output):
  622. WrapCode(line, '', output)
  623. def IsHeaderGuardOrInclude(line):
  624. return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or
  625. re.match(r'^#include\s', line))
  626. def WrapLongLine(line, output):
  627. line = line.rstrip()
  628. if len(line) <= 80:
  629. output.append(line)
  630. elif IsComment(line):
  631. if IsHeaderGuardOrInclude(line):
  632. # The style guide made an exception to allow long header guard lines
  633. # and includes.
  634. output.append(line)
  635. else:
  636. WrapComment(line, output)
  637. elif IsInPreprocessorDirevative(output, line):
  638. if IsHeaderGuardOrInclude(line):
  639. # The style guide made an exception to allow long header guard lines
  640. # and includes.
  641. output.append(line)
  642. else:
  643. WrapPreprocessorDirevative(line, output)
  644. else:
  645. WrapPlainCode(line, output)
  646. def BeautifyCode(string):
  647. lines = string.splitlines()
  648. output = []
  649. for line in lines:
  650. WrapLongLine(line, output)
  651. output2 = [line.rstrip() for line in output]
  652. return '\n'.join(output2) + '\n'
  653. def main(argv):
  654. if len(argv) == 1:
  655. print __doc__
  656. sys.exit(1)
  657. file_path = argv[-1]
  658. ast = Convert(file_path)
  659. output = Output()
  660. RunCode(Env(), ast, output)
  661. output_str = BeautifyCode(output.string)
  662. if file_path.endswith('.pump'):
  663. output_file_path = file_path[:-5]
  664. else:
  665. output_file_path = '-'
  666. if output_file_path == '-':
  667. print output_str,
  668. else:
  669. output_file = file(output_file_path, 'w')
  670. output_file.write('// This file was GENERATED by command:\n')
  671. output_file.write('// %s %s\n' %
  672. (os.path.basename(__file__), os.path.basename(file_path)))
  673. output_file.write('// DO NOT EDIT BY HAND!!!\n\n')
  674. output_file.write(output_str)
  675. output_file.close()
  676. if __name__ == '__main__':
  677. main(sys.argv)