diff options
| author | Enji Cooper <ngie@FreeBSD.org> | 2019-02-13 02:16:52 +0000 | 
|---|---|---|
| committer | Enji Cooper <ngie@FreeBSD.org> | 2019-02-13 02:16:52 +0000 | 
| commit | 83481c8c5c0cd0b3fc86f39b2985efd4e300200a (patch) | |
| tree | 784fc9b3c98d0e853336f1fb982fd243162c922c /googletest/scripts/pump.py | |
Diffstat (limited to 'googletest/scripts/pump.py')
| -rwxr-xr-x | googletest/scripts/pump.py | 855 | 
1 files changed, 855 insertions, 0 deletions
| diff --git a/googletest/scripts/pump.py b/googletest/scripts/pump.py new file mode 100755 index 0000000000000..5efb653c207d1 --- /dev/null +++ b/googletest/scripts/pump.py @@ -0,0 +1,855 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# 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 Google Inc. 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. + +"""pump v0.2.0 - Pretty Useful for Meta Programming. + +A tool for preprocessor meta programming.  Useful for generating +repetitive boilerplate code.  Especially useful for writing C++ +classes, functions, macros, and templates that need to work with +various number of arguments. + +USAGE: +       pump.py SOURCE_FILE + +EXAMPLES: +       pump.py foo.cc.pump +         Converts foo.cc.pump to foo.cc. + +GRAMMAR: +       CODE ::= ATOMIC_CODE* +       ATOMIC_CODE ::= $var ID = EXPRESSION +           | $var ID = [[ CODE ]] +           | $range ID EXPRESSION..EXPRESSION +           | $for ID SEPARATOR [[ CODE ]] +           | $($) +           | $ID +           | $(EXPRESSION) +           | $if EXPRESSION [[ CODE ]] ELSE_BRANCH +           | [[ CODE ]] +           | RAW_CODE +       SEPARATOR ::= RAW_CODE | EMPTY +       ELSE_BRANCH ::= $else [[ CODE ]] +           | $elif EXPRESSION [[ CODE ]] ELSE_BRANCH +           | EMPTY +       EXPRESSION has Python syntax. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sys + + +TOKEN_TABLE = [ +    (re.compile(r'\$var\s+'), '$var'), +    (re.compile(r'\$elif\s+'), '$elif'), +    (re.compile(r'\$else\s+'), '$else'), +    (re.compile(r'\$for\s+'), '$for'), +    (re.compile(r'\$if\s+'), '$if'), +    (re.compile(r'\$range\s+'), '$range'), +    (re.compile(r'\$[_A-Za-z]\w*'), '$id'), +    (re.compile(r'\$\(\$\)'), '$($)'), +    (re.compile(r'\$'), '$'), +    (re.compile(r'\[\[\n?'), '[['), +    (re.compile(r'\]\]\n?'), ']]'), +    ] + + +class Cursor: +  """Represents a position (line and column) in a text file.""" + +  def __init__(self, line=-1, column=-1): +    self.line = line +    self.column = column + +  def __eq__(self, rhs): +    return self.line == rhs.line and self.column == rhs.column + +  def __ne__(self, rhs): +    return not self == rhs + +  def __lt__(self, rhs): +    return self.line < rhs.line or ( +        self.line == rhs.line and self.column < rhs.column) + +  def __le__(self, rhs): +    return self < rhs or self == rhs + +  def __gt__(self, rhs): +    return rhs < self + +  def __ge__(self, rhs): +    return rhs <= self + +  def __str__(self): +    if self == Eof(): +      return 'EOF' +    else: +      return '%s(%s)' % (self.line + 1, self.column) + +  def __add__(self, offset): +    return Cursor(self.line, self.column + offset) + +  def __sub__(self, offset): +    return Cursor(self.line, self.column - offset) + +  def Clone(self): +    """Returns a copy of self.""" + +    return Cursor(self.line, self.column) + + +# Special cursor to indicate the end-of-file. +def Eof(): +  """Returns the special cursor to denote the end-of-file.""" +  return Cursor(-1, -1) + + +class Token: +  """Represents a token in a Pump source file.""" + +  def __init__(self, start=None, end=None, value=None, token_type=None): +    if start is None: +      self.start = Eof() +    else: +      self.start = start +    if end is None: +      self.end = Eof() +    else: +      self.end = end +    self.value = value +    self.token_type = token_type + +  def __str__(self): +    return 'Token @%s: \'%s\' type=%s' % ( +        self.start, self.value, self.token_type) + +  def Clone(self): +    """Returns a copy of self.""" + +    return Token(self.start.Clone(), self.end.Clone(), self.value, +                 self.token_type) + + +def StartsWith(lines, pos, string): +  """Returns True iff the given position in lines starts with 'string'.""" + +  return lines[pos.line][pos.column:].startswith(string) + + +def FindFirstInLine(line, token_table): +  best_match_start = -1 +  for (regex, token_type) in token_table: +    m = regex.search(line) +    if m: +      # We found regex in lines +      if best_match_start < 0 or m.start() < best_match_start: +        best_match_start = m.start() +        best_match_length = m.end() - m.start() +        best_match_token_type = token_type + +  if best_match_start < 0: +    return None + +  return (best_match_start, best_match_length, best_match_token_type) + + +def FindFirst(lines, token_table, cursor): +  """Finds the first occurrence of any string in strings in lines.""" + +  start = cursor.Clone() +  cur_line_number = cursor.line +  for line in lines[start.line:]: +    if cur_line_number == start.line: +      line = line[start.column:] +    m = FindFirstInLine(line, token_table) +    if m: +      # We found a regex in line. +      (start_column, length, token_type) = m +      if cur_line_number == start.line: +        start_column += start.column +      found_start = Cursor(cur_line_number, start_column) +      found_end = found_start + length +      return MakeToken(lines, found_start, found_end, token_type) +    cur_line_number += 1 +  # We failed to find str in lines +  return None + + +def SubString(lines, start, end): +  """Returns a substring in lines.""" + +  if end == Eof(): +    end = Cursor(len(lines) - 1, len(lines[-1])) + +  if start >= end: +    return '' + +  if start.line == end.line: +    return lines[start.line][start.column:end.column] + +  result_lines = ([lines[start.line][start.column:]] + +                  lines[start.line + 1:end.line] + +                  [lines[end.line][:end.column]]) +  return ''.join(result_lines) + + +def StripMetaComments(str): +  """Strip meta comments from each line in the given string.""" + +  # First, completely remove lines containing nothing but a meta +  # comment, including the trailing \n. +  str = re.sub(r'^\s*\$\$.*\n', '', str) + +  # Then, remove meta comments from contentful lines. +  return re.sub(r'\s*\$\$.*', '', str) + + +def MakeToken(lines, start, end, token_type): +  """Creates a new instance of Token.""" + +  return Token(start, end, SubString(lines, start, end), token_type) + + +def ParseToken(lines, pos, regex, token_type): +  line = lines[pos.line][pos.column:] +  m = regex.search(line) +  if m and not m.start(): +    return MakeToken(lines, pos, pos + m.end(), token_type) +  else: +    print 'ERROR: %s expected at %s.' % (token_type, pos) +    sys.exit(1) + + +ID_REGEX = re.compile(r'[_A-Za-z]\w*') +EQ_REGEX = re.compile(r'=') +REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)') +OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*') +WHITE_SPACE_REGEX = re.compile(r'\s') +DOT_DOT_REGEX = re.compile(r'\.\.') + + +def Skip(lines, pos, regex): +  line = lines[pos.line][pos.column:] +  m = re.search(regex, line) +  if m and not m.start(): +    return pos + m.end() +  else: +    return pos + + +def SkipUntil(lines, pos, regex, token_type): +  line = lines[pos.line][pos.column:] +  m = re.search(regex, line) +  if m: +    return pos + m.start() +  else: +    print ('ERROR: %s expected on line %s after column %s.' % +           (token_type, pos.line + 1, pos.column)) +    sys.exit(1) + + +def ParseExpTokenInParens(lines, pos): +  def ParseInParens(pos): +    pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX) +    pos = Skip(lines, pos, r'\(') +    pos = Parse(pos) +    pos = Skip(lines, pos, r'\)') +    return pos + +  def Parse(pos): +    pos = SkipUntil(lines, pos, r'\(|\)', ')') +    if SubString(lines, pos, pos + 1) == '(': +      pos = Parse(pos + 1) +      pos = Skip(lines, pos, r'\)') +      return Parse(pos) +    else: +      return pos + +  start = pos.Clone() +  pos = ParseInParens(pos) +  return MakeToken(lines, start, pos, 'exp') + + +def RStripNewLineFromToken(token): +  if token.value.endswith('\n'): +    return Token(token.start, token.end, token.value[:-1], token.token_type) +  else: +    return token + + +def TokenizeLines(lines, pos): +  while True: +    found = FindFirst(lines, TOKEN_TABLE, pos) +    if not found: +      yield MakeToken(lines, pos, Eof(), 'code') +      return + +    if found.start == pos: +      prev_token = None +      prev_token_rstripped = None +    else: +      prev_token = MakeToken(lines, pos, found.start, 'code') +      prev_token_rstripped = RStripNewLineFromToken(prev_token) + +    if found.token_type == '$var': +      if prev_token_rstripped: +        yield prev_token_rstripped +      yield found +      id_token = ParseToken(lines, found.end, ID_REGEX, 'id') +      yield id_token +      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) + +      eq_token = ParseToken(lines, pos, EQ_REGEX, '=') +      yield eq_token +      pos = Skip(lines, eq_token.end, r'\s*') + +      if SubString(lines, pos, pos + 2) != '[[': +        exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp') +        yield exp_token +        pos = Cursor(exp_token.end.line + 1, 0) +    elif found.token_type == '$for': +      if prev_token_rstripped: +        yield prev_token_rstripped +      yield found +      id_token = ParseToken(lines, found.end, ID_REGEX, 'id') +      yield id_token +      pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX) +    elif found.token_type == '$range': +      if prev_token_rstripped: +        yield prev_token_rstripped +      yield found +      id_token = ParseToken(lines, found.end, ID_REGEX, 'id') +      yield id_token +      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) + +      dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..') +      yield MakeToken(lines, pos, dots_pos, 'exp') +      yield MakeToken(lines, dots_pos, dots_pos + 2, '..') +      pos = dots_pos + 2 +      new_pos = Cursor(pos.line + 1, 0) +      yield MakeToken(lines, pos, new_pos, 'exp') +      pos = new_pos +    elif found.token_type == '$': +      if prev_token: +        yield prev_token +      yield found +      exp_token = ParseExpTokenInParens(lines, found.end) +      yield exp_token +      pos = exp_token.end +    elif (found.token_type == ']]' or found.token_type == '$if' or +          found.token_type == '$elif' or found.token_type == '$else'): +      if prev_token_rstripped: +        yield prev_token_rstripped +      yield found +      pos = found.end +    else: +      if prev_token: +        yield prev_token +      yield found +      pos = found.end + + +def Tokenize(s): +  """A generator that yields the tokens in the given string.""" +  if s != '': +    lines = s.splitlines(True) +    for token in TokenizeLines(lines, Cursor(0, 0)): +      yield token + + +class CodeNode: +  def __init__(self, atomic_code_list=None): +    self.atomic_code = atomic_code_list + + +class VarNode: +  def __init__(self, identifier=None, atomic_code=None): +    self.identifier = identifier +    self.atomic_code = atomic_code + + +class RangeNode: +  def __init__(self, identifier=None, exp1=None, exp2=None): +    self.identifier = identifier +    self.exp1 = exp1 +    self.exp2 = exp2 + + +class ForNode: +  def __init__(self, identifier=None, sep=None, code=None): +    self.identifier = identifier +    self.sep = sep +    self.code = code + + +class ElseNode: +  def __init__(self, else_branch=None): +    self.else_branch = else_branch + + +class IfNode: +  def __init__(self, exp=None, then_branch=None, else_branch=None): +    self.exp = exp +    self.then_branch = then_branch +    self.else_branch = else_branch + + +class RawCodeNode: +  def __init__(self, token=None): +    self.raw_code = token + + +class LiteralDollarNode: +  def __init__(self, token): +    self.token = token + + +class ExpNode: +  def __init__(self, token, python_exp): +    self.token = token +    self.python_exp = python_exp + + +def PopFront(a_list): +  head = a_list[0] +  a_list[:1] = [] +  return head + + +def PushFront(a_list, elem): +  a_list[:0] = [elem] + + +def PopToken(a_list, token_type=None): +  token = PopFront(a_list) +  if token_type is not None and token.token_type != token_type: +    print 'ERROR: %s expected at %s' % (token_type, token.start) +    print 'ERROR: %s found instead' % (token,) +    sys.exit(1) + +  return token + + +def PeekToken(a_list): +  if not a_list: +    return None + +  return a_list[0] + + +def ParseExpNode(token): +  python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value) +  return ExpNode(token, python_exp) + + +def ParseElseNode(tokens): +  def Pop(token_type=None): +    return PopToken(tokens, token_type) + +  next = PeekToken(tokens) +  if not next: +    return None +  if next.token_type == '$else': +    Pop('$else') +    Pop('[[') +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    return code_node +  elif next.token_type == '$elif': +    Pop('$elif') +    exp = Pop('code') +    Pop('[[') +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    inner_else_node = ParseElseNode(tokens) +    return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)]) +  elif not next.value.strip(): +    Pop('code') +    return ParseElseNode(tokens) +  else: +    return None + + +def ParseAtomicCodeNode(tokens): +  def Pop(token_type=None): +    return PopToken(tokens, token_type) + +  head = PopFront(tokens) +  t = head.token_type +  if t == 'code': +    return RawCodeNode(head) +  elif t == '$var': +    id_token = Pop('id') +    Pop('=') +    next = PeekToken(tokens) +    if next.token_type == 'exp': +      exp_token = Pop() +      return VarNode(id_token, ParseExpNode(exp_token)) +    Pop('[[') +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    return VarNode(id_token, code_node) +  elif t == '$for': +    id_token = Pop('id') +    next_token = PeekToken(tokens) +    if next_token.token_type == 'code': +      sep_token = next_token +      Pop('code') +    else: +      sep_token = None +    Pop('[[') +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    return ForNode(id_token, sep_token, code_node) +  elif t == '$if': +    exp_token = Pop('code') +    Pop('[[') +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    else_node = ParseElseNode(tokens) +    return IfNode(ParseExpNode(exp_token), code_node, else_node) +  elif t == '$range': +    id_token = Pop('id') +    exp1_token = Pop('exp') +    Pop('..') +    exp2_token = Pop('exp') +    return RangeNode(id_token, ParseExpNode(exp1_token), +                     ParseExpNode(exp2_token)) +  elif t == '$id': +    return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id')) +  elif t == '$($)': +    return LiteralDollarNode(head) +  elif t == '$': +    exp_token = Pop('exp') +    return ParseExpNode(exp_token) +  elif t == '[[': +    code_node = ParseCodeNode(tokens) +    Pop(']]') +    return code_node +  else: +    PushFront(tokens, head) +    return None + + +def ParseCodeNode(tokens): +  atomic_code_list = [] +  while True: +    if not tokens: +      break +    atomic_code_node = ParseAtomicCodeNode(tokens) +    if atomic_code_node: +      atomic_code_list.append(atomic_code_node) +    else: +      break +  return CodeNode(atomic_code_list) + + +def ParseToAST(pump_src_text): +  """Convert the given Pump source text into an AST.""" +  tokens = list(Tokenize(pump_src_text)) +  code_node = ParseCodeNode(tokens) +  return code_node + + +class Env: +  def __init__(self): +    self.variables = [] +    self.ranges = [] + +  def Clone(self): +    clone = Env() +    clone.variables = self.variables[:] +    clone.ranges = self.ranges[:] +    return clone + +  def PushVariable(self, var, value): +    # If value looks like an int, store it as an int. +    try: +      int_value = int(value) +      if ('%s' % int_value) == value: +        value = int_value +    except Exception: +      pass +    self.variables[:0] = [(var, value)] + +  def PopVariable(self): +    self.variables[:1] = [] + +  def PushRange(self, var, lower, upper): +    self.ranges[:0] = [(var, lower, upper)] + +  def PopRange(self): +    self.ranges[:1] = [] + +  def GetValue(self, identifier): +    for (var, value) in self.variables: +      if identifier == var: +        return value + +    print 'ERROR: meta variable %s is undefined.' % (identifier,) +    sys.exit(1) + +  def EvalExp(self, exp): +    try: +      result = eval(exp.python_exp) +    except Exception, e: +      print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e) +      print ('ERROR: failed to evaluate meta expression %s at %s' % +             (exp.python_exp, exp.token.start)) +      sys.exit(1) +    return result + +  def GetRange(self, identifier): +    for (var, lower, upper) in self.ranges: +      if identifier == var: +        return (lower, upper) + +    print 'ERROR: range %s is undefined.' % (identifier,) +    sys.exit(1) + + +class Output: +  def __init__(self): +    self.string = '' + +  def GetLastLine(self): +    index = self.string.rfind('\n') +    if index < 0: +      return '' + +    return self.string[index + 1:] + +  def Append(self, s): +    self.string += s + + +def RunAtomicCode(env, node, output): +  if isinstance(node, VarNode): +    identifier = node.identifier.value.strip() +    result = Output() +    RunAtomicCode(env.Clone(), node.atomic_code, result) +    value = result.string +    env.PushVariable(identifier, value) +  elif isinstance(node, RangeNode): +    identifier = node.identifier.value.strip() +    lower = int(env.EvalExp(node.exp1)) +    upper = int(env.EvalExp(node.exp2)) +    env.PushRange(identifier, lower, upper) +  elif isinstance(node, ForNode): +    identifier = node.identifier.value.strip() +    if node.sep is None: +      sep = '' +    else: +      sep = node.sep.value +    (lower, upper) = env.GetRange(identifier) +    for i in range(lower, upper + 1): +      new_env = env.Clone() +      new_env.PushVariable(identifier, i) +      RunCode(new_env, node.code, output) +      if i != upper: +        output.Append(sep) +  elif isinstance(node, RawCodeNode): +    output.Append(node.raw_code.value) +  elif isinstance(node, IfNode): +    cond = env.EvalExp(node.exp) +    if cond: +      RunCode(env.Clone(), node.then_branch, output) +    elif node.else_branch is not None: +      RunCode(env.Clone(), node.else_branch, output) +  elif isinstance(node, ExpNode): +    value = env.EvalExp(node) +    output.Append('%s' % (value,)) +  elif isinstance(node, LiteralDollarNode): +    output.Append('$') +  elif isinstance(node, CodeNode): +    RunCode(env.Clone(), node, output) +  else: +    print 'BAD' +    print node +    sys.exit(1) + + +def RunCode(env, code_node, output): +  for atomic_code in code_node.atomic_code: +    RunAtomicCode(env, atomic_code, output) + + +def IsSingleLineComment(cur_line): +  return '//' in cur_line + + +def IsInPreprocessorDirective(prev_lines, cur_line): +  if cur_line.lstrip().startswith('#'): +    return True +  return prev_lines and prev_lines[-1].endswith('\\') + + +def WrapComment(line, output): +  loc = line.find('//') +  before_comment = line[:loc].rstrip() +  if before_comment == '': +    indent = loc +  else: +    output.append(before_comment) +    indent = len(before_comment) - len(before_comment.lstrip()) +  prefix = indent*' ' + '// ' +  max_len = 80 - len(prefix) +  comment = line[loc + 2:].strip() +  segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != ''] +  cur_line = '' +  for seg in segs: +    if len((cur_line + seg).rstrip()) < max_len: +      cur_line += seg +    else: +      if cur_line.strip() != '': +        output.append(prefix + cur_line.rstrip()) +      cur_line = seg.lstrip() +  if cur_line.strip() != '': +    output.append(prefix + cur_line.strip()) + + +def WrapCode(line, line_concat, output): +  indent = len(line) - len(line.lstrip()) +  prefix = indent*' '  # Prefix of the current line +  max_len = 80 - indent - len(line_concat)  # Maximum length of the current line +  new_prefix = prefix + 4*' '  # Prefix of a continuation line +  new_max_len = max_len - 4  # Maximum length of a continuation line +  # Prefers to wrap a line after a ',' or ';'. +  segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != ''] +  cur_line = ''  # The current line without leading spaces. +  for seg in segs: +    # If the line is still too long, wrap at a space. +    while cur_line == '' and len(seg.strip()) > max_len: +      seg = seg.lstrip() +      split_at = seg.rfind(' ', 0, max_len) +      output.append(prefix + seg[:split_at].strip() + line_concat) +      seg = seg[split_at + 1:] +      prefix = new_prefix +      max_len = new_max_len + +    if len((cur_line + seg).rstrip()) < max_len: +      cur_line = (cur_line + seg).lstrip() +    else: +      output.append(prefix + cur_line.rstrip() + line_concat) +      prefix = new_prefix +      max_len = new_max_len +      cur_line = seg.lstrip() +  if cur_line.strip() != '': +    output.append(prefix + cur_line.strip()) + + +def WrapPreprocessorDirective(line, output): +  WrapCode(line, ' \\', output) + + +def WrapPlainCode(line, output): +  WrapCode(line, '', output) + + +def IsMultiLineIWYUPragma(line): +  return re.search(r'/\* IWYU pragma: ', line) + + +def IsHeaderGuardIncludeOrOneLineIWYUPragma(line): +  return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or +          re.match(r'^#include\s', line) or +          # Don't break IWYU pragmas, either; that causes iwyu.py problems. +          re.search(r'// IWYU pragma: ', line)) + + +def WrapLongLine(line, output): +  line = line.rstrip() +  if len(line) <= 80: +    output.append(line) +  elif IsSingleLineComment(line): +    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): +      # The style guide made an exception to allow long header guard lines, +      # includes and IWYU pragmas. +      output.append(line) +    else: +      WrapComment(line, output) +  elif IsInPreprocessorDirective(output, line): +    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): +      # The style guide made an exception to allow long header guard lines, +      # includes and IWYU pragmas. +      output.append(line) +    else: +      WrapPreprocessorDirective(line, output) +  elif IsMultiLineIWYUPragma(line): +    output.append(line) +  else: +    WrapPlainCode(line, output) + + +def BeautifyCode(string): +  lines = string.splitlines() +  output = [] +  for line in lines: +    WrapLongLine(line, output) +  output2 = [line.rstrip() for line in output] +  return '\n'.join(output2) + '\n' + + +def ConvertFromPumpSource(src_text): +  """Return the text generated from the given Pump source text.""" +  ast = ParseToAST(StripMetaComments(src_text)) +  output = Output() +  RunCode(Env(), ast, output) +  return BeautifyCode(output.string) + + +def main(argv): +  if len(argv) == 1: +    print __doc__ +    sys.exit(1) + +  file_path = argv[-1] +  output_str = ConvertFromPumpSource(file(file_path, 'r').read()) +  if file_path.endswith('.pump'): +    output_file_path = file_path[:-5] +  else: +    output_file_path = '-' +  if output_file_path == '-': +    print output_str, +  else: +    output_file = file(output_file_path, 'w') +    output_file.write('// This file was GENERATED by command:\n') +    output_file.write('//     %s %s\n' % +                      (os.path.basename(__file__), os.path.basename(file_path))) +    output_file.write('// DO NOT EDIT BY HAND!!!\n\n') +    output_file.write(output_str) +    output_file.close() + + +if __name__ == '__main__': +  main(sys.argv) | 
