text: #!/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.
  
  GMEMMAR:
         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)
  