| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 | 
							- #! /usr/bin/python
 
- #
 
- # Protocol Buffers - Google's data interchange format
 
- # Copyright 2015 Google Inc.  All rights reserved.
 
- # https://developers.google.com/protocol-buffers/
 
- #
 
- # 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.
 
- """PDDM - Poor Developers' Debug-able Macros
 
- A simple markup that can be added in comments of source so they can then be
 
- expanded out into code. Most of this could be done with CPP macros, but then
 
- developers can't really step through them in the debugger, this way they are
 
- expanded to the same code, but you can debug them.
 
- Any file can be processed, but the syntax is designed around a C based compiler.
 
- Processed lines start with "//%".  There are three types of sections you can
 
- create: Text (left alone), Macro Definitions, and Macro Expansions.  There is
 
- no order required between definitions and expansions, all definitions are read
 
- before any expansions are processed (thus, if desired, definitions can be put
 
- at the end of the file to keep them out of the way of the code).
 
- Macro Definitions are started with "//%PDDM-DEFINE Name(args)" and all lines
 
- afterwards that start with "//%" are included in the definition.  Multiple
 
- macros can be defined in one block by just using a another "//%PDDM-DEFINE"
 
- line to start the next macro.  Optionally, a macro can be ended with
 
- "//%PDDM-DEFINE-END", this can be useful when you want to make it clear that
 
- trailing blank lines are included in the macro.  You can also end a definition
 
- with an expansion.
 
- Macro Expansions are started by single lines containing
 
- "//%PDDM-EXPAND Name(args)" and then with "//%PDDM-EXPAND-END" or another
 
- expansions.  All lines in-between are replaced by the result of the expansion.
 
- The first line of the expansion is always a blank like just for readability.
 
- Expansion itself is pretty simple, one macro can invoke another macro, but
 
- you cannot nest the invoke of a macro in another macro (i.e. - can't do
 
- "foo(bar(a))", but you can define foo(a) and bar(b) where bar invokes foo()
 
- within its expansion.
 
- When macros are expanded, the arg references can also add "$O" suffix to the
 
- name (i.e. - "NAME$O") to specify an option to be applied. The options are:
 
-     $S - Replace each character in the value with a space.
 
-     $l - Lowercase the first letter of the value.
 
-     $L - Lowercase the whole value.
 
-     $u - Uppercase the first letter of the value.
 
-     $U - Uppercase the whole value.
 
- Within a macro you can use ## to cause things to get joined together after
 
- expansion (i.e. - "a##b" within a macro will become "ab").
 
- Example:
 
-     int foo(MyEnum x) {
 
-     switch (x) {
 
-     //%PDDM-EXPAND case(Enum_Left, 1)
 
-     //%PDDM-EXPAND case(Enum_Center, 2)
 
-     //%PDDM-EXPAND case(Enum_Right, 3)
 
-     //%PDDM-EXPAND-END
 
-     }
 
-     //%PDDM-DEFINE case(_A, _B)
 
-     //%  case _A:
 
-     //%    return _B;
 
-   A macro ends at the start of the next one, or an optional %PDDM-DEFINE-END
 
-   can be used to avoid adding extra blank lines/returns (or make it clear when
 
-   it is desired).
 
-   One macro can invoke another by simply using its name NAME(ARGS). You cannot
 
-   nest an invoke inside another (i.e. - NAME1(NAME2(ARGS)) isn't supported).
 
-   Within a macro you can use ## to cause things to get joined together after
 
-   processing (i.e. - "a##b" within a macro will become "ab").
 
- """
 
- import optparse
 
- import os
 
- import re
 
- import sys
 
- # Regex for macro definition.
 
- _MACRO_RE = re.compile(r'(?P<name>\w+)\((?P<args>.*?)\)')
 
- # Regex for macro's argument definition.
 
- _MACRO_ARG_NAME_RE = re.compile(r'^\w+$')
 
- # Line inserted after each EXPAND.
 
- _GENERATED_CODE_LINE = (
 
-   '// This block of code is generated, do not edit it directly.'
 
- )
 
- def _MacroRefRe(macro_names):
 
-   # Takes in a list of macro names and makes a regex that will match invokes
 
-   # of those macros.
 
-   return re.compile(r'\b(?P<macro_ref>(?P<name>(%s))\((?P<args>.*?)\))' %
 
-                     '|'.join(macro_names))
 
- def _MacroArgRefRe(macro_arg_names):
 
-   # Takes in a list of macro arg names and makes a regex that will match
 
-   # uses of those args.
 
-   return re.compile(r'\b(?P<name>(%s))(\$(?P<option>.))?\b' %
 
-                     '|'.join(macro_arg_names))
 
- class PDDMError(Exception):
 
-   """Error thrown by pddm."""
 
-   pass
 
- class MacroCollection(object):
 
-   """Hold a set of macros and can resolve/expand them."""
 
-   def __init__(self, a_file=None):
 
-     """Initializes the collection.
 
-     Args:
 
-       a_file: The file like stream to parse.
 
-     Raises:
 
-       PDDMError if there are any issues.
 
-     """
 
-     self._macros = dict()
 
-     if a_file:
 
-       self.ParseInput(a_file)
 
-   class MacroDefinition(object):
 
-     """Holds a macro definition."""
 
-     def __init__(self, name, arg_names):
 
-       self._name = name
 
-       self._args = tuple(arg_names)
 
-       self._body = ''
 
-       self._needNewLine = False
 
-     def AppendLine(self, line):
 
-       if self._needNewLine:
 
-         self._body += '\n'
 
-       self._body += line
 
-       self._needNewLine = not line.endswith('\n')
 
-     @property
 
-     def name(self):
 
-       return self._name
 
-     @property
 
-     def args(self):
 
-       return self._args
 
-     @property
 
-     def body(self):
 
-       return self._body
 
-   def ParseInput(self, a_file):
 
-     """Consumes input extracting definitions.
 
-     Args:
 
-       a_file: The file like stream to parse.
 
-     Raises:
 
-       PDDMError if there are any issues.
 
-     """
 
-     input_lines = a_file.read().splitlines()
 
-     self.ParseLines(input_lines)
 
-   def ParseLines(self, input_lines):
 
-     """Parses list of lines.
 
-     Args:
 
-       input_lines: A list of strings of input to parse (no newlines on the
 
-                    strings).
 
-     Raises:
 
-       PDDMError if there are any issues.
 
-     """
 
-     current_macro = None
 
-     for line in input_lines:
 
-       if line.startswith('PDDM-'):
 
-         directive = line.split(' ', 1)[0]
 
-         if directive == 'PDDM-DEFINE':
 
-           name, args = self._ParseDefineLine(line)
 
-           if self._macros.get(name):
 
-             raise PDDMError('Attempt to redefine macro: "%s"' % line)
 
-           current_macro = self.MacroDefinition(name, args)
 
-           self._macros[name] = current_macro
 
-           continue
 
-         if directive == 'PDDM-DEFINE-END':
 
-           if not current_macro:
 
-             raise PDDMError('Got DEFINE-END directive without an active macro:'
 
-                             ' "%s"' % line)
 
-           current_macro = None
 
-           continue
 
-         raise PDDMError('Hit a line with an unknown directive: "%s"' % line)
 
-       if current_macro:
 
-         current_macro.AppendLine(line)
 
-         continue
 
-       # Allow blank lines between macro definitions.
 
-       if line.strip() == '':
 
-         continue
 
-       raise PDDMError('Hit a line that wasn\'t a directive and no open macro'
 
-                       ' definition: "%s"' % line)
 
-   def _ParseDefineLine(self, input_line):
 
-     assert input_line.startswith('PDDM-DEFINE')
 
-     line = input_line[12:].strip()
 
-     match = _MACRO_RE.match(line)
 
-     # Must match full line
 
-     if match is None or match.group(0) != line:
 
-       raise PDDMError('Failed to parse macro definition: "%s"' % input_line)
 
-     name = match.group('name')
 
-     args_str = match.group('args').strip()
 
-     args = []
 
-     if args_str:
 
-       for part in args_str.split(','):
 
-         arg = part.strip()
 
-         if arg == '':
 
-           raise PDDMError('Empty arg name in macro definition: "%s"'
 
-                           % input_line)
 
-         if not _MACRO_ARG_NAME_RE.match(arg):
 
-           raise PDDMError('Invalid arg name "%s" in macro definition: "%s"'
 
-                           % (arg, input_line))
 
-         if arg in args:
 
-           raise PDDMError('Arg name "%s" used more than once in macro'
 
-                           ' definition: "%s"' % (arg, input_line))
 
-         args.append(arg)
 
-     return (name, tuple(args))
 
-   def Expand(self, macro_ref_str):
 
-     """Expands the macro reference.
 
-     Args:
 
-       macro_ref_str: String of a macro reference (i.e. foo(a, b)).
 
-     Returns:
 
-       The text from the expansion.
 
-     Raises:
 
-       PDDMError if there are any issues.
 
-     """
 
-     match = _MACRO_RE.match(macro_ref_str)
 
-     if match is None or match.group(0) != macro_ref_str:
 
-       raise PDDMError('Failed to parse macro reference: "%s"' % macro_ref_str)
 
-     if match.group('name') not in self._macros:
 
-       raise PDDMError('No macro named "%s".' % match.group('name'))
 
-     return self._Expand(match, [], macro_ref_str)
 
-   def _FormatStack(self, macro_ref_stack):
 
-     result = ''
 
-     for _, macro_ref in reversed(macro_ref_stack):
 
-       result += '\n...while expanding "%s".' % macro_ref
 
-     return result
 
-   def _Expand(self, macro_ref_match, macro_stack, macro_ref_str=None):
 
-     if macro_ref_str is None:
 
-       macro_ref_str = macro_ref_match.group('macro_ref')
 
-     name = macro_ref_match.group('name')
 
-     for prev_name, prev_macro_ref in macro_stack:
 
-       if name == prev_name:
 
-         raise PDDMError('Found macro recursion, invoking "%s":%s' %
 
-                         (macro_ref_str, self._FormatStack(macro_stack)))
 
-     macro = self._macros[name]
 
-     args_str = macro_ref_match.group('args').strip()
 
-     args = []
 
-     if args_str or len(macro.args):
 
-       args = [x.strip() for x in args_str.split(',')]
 
-     if len(args) != len(macro.args):
 
-       raise PDDMError('Expected %d args, got: "%s".%s' %
 
-                       (len(macro.args), macro_ref_str,
 
-                        self._FormatStack(macro_stack)))
 
-     # Replace args usages.
 
-     result = self._ReplaceArgValues(macro, args, macro_ref_str, macro_stack)
 
-     # Expand any macro invokes.
 
-     new_macro_stack = macro_stack + [(name, macro_ref_str)]
 
-     while True:
 
-       eval_result = self._EvalMacrosRefs(result, new_macro_stack)
 
-       # Consume all ## directives to glue things together.
 
-       eval_result = eval_result.replace('##', '')
 
-       if eval_result == result:
 
-         break
 
-       result = eval_result
 
-     return result
 
-   def _ReplaceArgValues(self,
 
-                         macro, arg_values, macro_ref_to_report, macro_stack):
 
-     if len(arg_values) == 0:
 
-       # Nothing to do
 
-       return macro.body
 
-     assert len(arg_values) == len(macro.args)
 
-     args = dict(zip(macro.args, arg_values))
 
-     def _lookupArg(match):
 
-       val = args[match.group('name')]
 
-       opt = match.group('option')
 
-       if opt:
 
-         if opt == 'S':  # Spaces for the length
 
-           return ' ' * len(val)
 
-         elif opt == 'l':  # Lowercase first character
 
-           if val:
 
-             return val[0].lower() + val[1:]
 
-           else:
 
-             return val
 
-         elif opt == 'L':  # All Lowercase
 
-           return val.lower()
 
-         elif opt == 'u':  # Uppercase first character
 
-           if val:
 
-             return val[0].upper() + val[1:]
 
-           else:
 
-             return val
 
-         elif opt == 'U':  # All Uppercase
 
-           return val.upper()
 
-         else:
 
-           raise PDDMError('Unknown arg option "%s$%s" while expanding "%s".%s'
 
-                           % (match.group('name'), match.group('option'),
 
-                              macro_ref_to_report,
 
-                              self._FormatStack(macro_stack)))
 
-       return val
 
-     # Let the regex do the work!
 
-     macro_arg_ref_re = _MacroArgRefRe(macro.args)
 
-     return macro_arg_ref_re.sub(_lookupArg, macro.body)
 
-   def _EvalMacrosRefs(self, text, macro_stack):
 
-     macro_ref_re = _MacroRefRe(self._macros.keys())
 
-     def _resolveMacro(match):
 
-       return self._Expand(match, macro_stack)
 
-     return macro_ref_re.sub(_resolveMacro, text)
 
- class SourceFile(object):
 
-   """Represents a source file with PDDM directives in it."""
 
-   def __init__(self, a_file, import_resolver=None):
 
-     """Initializes the file reading in the file.
 
-     Args:
 
-       a_file: The file to read in.
 
-       import_resolver: a function that given a path will return a stream for
 
-         the contents.
 
-     Raises:
 
-       PDDMError if there are any issues.
 
-     """
 
-     self._sections = []
 
-     self._original_content = a_file.read()
 
-     self._import_resolver = import_resolver
 
-     self._processed_content = None
 
-   class SectionBase(object):
 
-     def __init__(self, first_line_num):
 
-       self._lines = []
 
-       self._first_line_num = first_line_num
 
-     def TryAppend(self, line, line_num):
 
-       """Try appending a line.
 
-       Args:
 
-         line: The line to append.
 
-         line_num: The number of the line.
 
-       Returns:
 
-         A tuple of (SUCCESS, CAN_ADD_MORE).  If SUCCESS if False, the line
 
-         wasn't append.  If SUCCESS is True, then CAN_ADD_MORE is True/False to
 
-         indicate if more lines can be added after this one.
 
-       """
 
-       assert False, "subclass should have overridden"
 
-       return (False, False)
 
-     def HitEOF(self):
 
-       """Called when the EOF was reached for for a given section."""
 
-       pass
 
-     def BindMacroCollection(self, macro_collection):
 
-       """Binds the chunk to a macro collection.
 
-       Args:
 
-         macro_collection: The collection to bind too.
 
-       """
 
-       pass
 
-     def Append(self, line):
 
-       self._lines.append(line)
 
-     @property
 
-     def lines(self):
 
-       return self._lines
 
-     @property
 
-     def num_lines_captured(self):
 
-       return len(self._lines)
 
-     @property
 
-     def first_line_num(self):
 
-       return self._first_line_num
 
-     @property
 
-     def first_line(self):
 
-       if not self._lines:
 
-         return ''
 
-       return self._lines[0]
 
-     @property
 
-     def text(self):
 
-       return '\n'.join(self.lines) + '\n'
 
-   class TextSection(SectionBase):
 
-     """Text section that is echoed out as is."""
 
-     def TryAppend(self, line, line_num):
 
-       if line.startswith('//%PDDM'):
 
-         return (False, False)
 
-       self.Append(line)
 
-       return (True, True)
 
-   class ExpansionSection(SectionBase):
 
-     """Section that is the result of an macro expansion."""
 
-     def __init__(self, first_line_num):
 
-       SourceFile.SectionBase.__init__(self, first_line_num)
 
-       self._macro_collection = None
 
-     def TryAppend(self, line, line_num):
 
-       if line.startswith('//%PDDM'):
 
-         directive = line.split(' ', 1)[0]
 
-         if directive == '//%PDDM-EXPAND':
 
-           self.Append(line)
 
-           return (True, True)
 
-         if directive == '//%PDDM-EXPAND-END':
 
-           assert self.num_lines_captured > 0
 
-           return (True, False)
 
-         raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
 
-                         (directive, line_num, self.first_line))
 
-       # Eat other lines.
 
-       return (True, True)
 
-     def HitEOF(self):
 
-       raise PDDMError('Hit the end of the file while in "%s".' %
 
-                       self.first_line)
 
-     def BindMacroCollection(self, macro_collection):
 
-       self._macro_collection = macro_collection
 
-     @property
 
-     def lines(self):
 
-       captured_lines = SourceFile.SectionBase.lines.fget(self)
 
-       directive_len = len('//%PDDM-EXPAND')
 
-       result = []
 
-       for line in captured_lines:
 
-         result.append(line)
 
-         if self._macro_collection:
 
-           # Always add a blank line, seems to read better. (If need be, add an
 
-           # option to the EXPAND to indicate if this should be done.)
 
-           result.extend([_GENERATED_CODE_LINE, '// clang-format off', ''])
 
-           macro = line[directive_len:].strip()
 
-           try:
 
-             expand_result = self._macro_collection.Expand(macro)
 
-             # Since expansions are line oriented, strip trailing whitespace
 
-             # from the lines.
 
-             lines = [x.rstrip() for x in expand_result.split('\n')]
 
-             lines.append('// clang-format on')
 
-             result.append('\n'.join(lines))
 
-           except PDDMError as e:
 
-             raise PDDMError('%s\n...while expanding "%s" from the section'
 
-                             ' that started:\n   Line %d: %s' %
 
-                             (e.message, macro,
 
-                              self.first_line_num, self.first_line))
 
-       # Add the ending marker.
 
-       if len(captured_lines) == 1:
 
-         result.append('//%%PDDM-EXPAND-END %s' %
 
-                       captured_lines[0][directive_len:].strip())
 
-       else:
 
-         result.append('//%%PDDM-EXPAND-END (%s expansions)' %
 
-                       len(captured_lines))
 
-       return result
 
-   class DefinitionSection(SectionBase):
 
-     """Section containing macro definitions"""
 
-     def TryAppend(self, line, line_num):
 
-       if not line.startswith('//%'):
 
-         return (False, False)
 
-       if line.startswith('//%PDDM'):
 
-         directive = line.split(' ', 1)[0]
 
-         if directive == "//%PDDM-EXPAND":
 
-           return False, False
 
-         if directive not in ('//%PDDM-DEFINE', '//%PDDM-DEFINE-END'):
 
-           raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
 
-                           (directive, line_num, self.first_line))
 
-       self.Append(line)
 
-       return (True, True)
 
-     def BindMacroCollection(self, macro_collection):
 
-       if macro_collection:
 
-         try:
 
-           # Parse the lines after stripping the prefix.
 
-           macro_collection.ParseLines([x[3:] for x in self.lines])
 
-         except PDDMError as e:
 
-           raise PDDMError('%s\n...while parsing section that started:\n'
 
-                           '  Line %d: %s' %
 
-                           (e.message, self.first_line_num, self.first_line))
 
-   class ImportDefinesSection(SectionBase):
 
-     """Section containing an import of PDDM-DEFINES from an external file."""
 
-     def __init__(self, first_line_num, import_resolver):
 
-       SourceFile.SectionBase.__init__(self, first_line_num)
 
-       self._import_resolver = import_resolver
 
-     def TryAppend(self, line, line_num):
 
-       if not line.startswith('//%PDDM-IMPORT-DEFINES '):
 
-         return (False, False)
 
-       assert self.num_lines_captured == 0
 
-       self.Append(line)
 
-       return (True, False)
 
-     def BindMacroCollection(self, macro_colletion):
 
-       if not macro_colletion:
 
-         return
 
-       if self._import_resolver is None:
 
-         raise PDDMError('Got an IMPORT-DEFINES without a resolver (line %d):'
 
-                         ' "%s".' % (self.first_line_num, self.first_line))
 
-       import_name = self.first_line.split(' ', 1)[1].strip()
 
-       imported_file = self._import_resolver(import_name)
 
-       if imported_file is None:
 
-         raise PDDMError('Resolver failed to find "%s" (line %d):'
 
-                         ' "%s".' %
 
-                         (import_name, self.first_line_num, self.first_line))
 
-       try:
 
-         imported_src_file = SourceFile(imported_file, self._import_resolver)
 
-         imported_src_file._ParseFile()
 
-         for section in imported_src_file._sections:
 
-           section.BindMacroCollection(macro_colletion)
 
-       except PDDMError as e:
 
-         raise PDDMError('%s\n...while importing defines:\n'
 
-                         '  Line %d: %s' %
 
-                         (e.message, self.first_line_num, self.first_line))
 
-   def _ParseFile(self):
 
-     self._sections = []
 
-     lines = self._original_content.splitlines()
 
-     cur_section = None
 
-     for line_num, line in enumerate(lines, 1):
 
-       if not cur_section:
 
-         cur_section = self._MakeSection(line, line_num)
 
-       was_added, accept_more = cur_section.TryAppend(line, line_num)
 
-       if not was_added:
 
-         cur_section = self._MakeSection(line, line_num)
 
-         was_added, accept_more = cur_section.TryAppend(line, line_num)
 
-         assert was_added
 
-       if not accept_more:
 
-         cur_section = None
 
-     if cur_section:
 
-       cur_section.HitEOF()
 
-   def _MakeSection(self, line, line_num):
 
-     if not line.startswith('//%PDDM'):
 
-       section = self.TextSection(line_num)
 
-     else:
 
-       directive = line.split(' ', 1)[0]
 
-       if directive == '//%PDDM-EXPAND':
 
-         section = self.ExpansionSection(line_num)
 
-       elif directive == '//%PDDM-DEFINE':
 
-         section = self.DefinitionSection(line_num)
 
-       elif directive == '//%PDDM-IMPORT-DEFINES':
 
-         section = self.ImportDefinesSection(line_num, self._import_resolver)
 
-       else:
 
-         raise PDDMError('Unexpected line %d: "%s".' % (line_num, line))
 
-     self._sections.append(section)
 
-     return section
 
-   def ProcessContent(self, strip_expansion=False):
 
-     """Processes the file contents."""
 
-     self._ParseFile()
 
-     if strip_expansion:
 
-       # Without a collection the expansions become blank, removing them.
 
-       collection = None
 
-     else:
 
-       collection = MacroCollection()
 
-     for section in self._sections:
 
-       section.BindMacroCollection(collection)
 
-     result = ''
 
-     for section in self._sections:
 
-       result += section.text
 
-     self._processed_content = result
 
-   @property
 
-   def original_content(self):
 
-     return self._original_content
 
-   @property
 
-   def processed_content(self):
 
-     return self._processed_content
 
- def main(args):
 
-   usage = '%prog [OPTIONS] PATH ...'
 
-   description = (
 
-       'Processes PDDM directives in the given paths and write them back out.'
 
-   )
 
-   parser = optparse.OptionParser(usage=usage, description=description)
 
-   parser.add_option('--dry-run',
 
-                     default=False, action='store_true',
 
-                     help='Don\'t write back to the file(s), just report if the'
 
-                     ' contents needs an update and exit with a value of 1.')
 
-   parser.add_option('--verbose',
 
-                     default=False, action='store_true',
 
-                     help='Reports is a file is already current.')
 
-   parser.add_option('--collapse',
 
-                     default=False, action='store_true',
 
-                     help='Removes all the generated code.')
 
-   opts, extra_args = parser.parse_args(args)
 
-   if not extra_args:
 
-     parser.error('Need at least one file to process')
 
-   result = 0
 
-   for a_path in extra_args:
 
-     if not os.path.exists(a_path):
 
-       sys.stderr.write('ERROR: File not found: %s\n' % a_path)
 
-       return 100
 
-     def _ImportResolver(name):
 
-       # resolve based on the file being read.
 
-       a_dir = os.path.dirname(a_path)
 
-       import_path = os.path.join(a_dir, name)
 
-       if not os.path.exists(import_path):
 
-         return None
 
-       return open(import_path, 'r')
 
-     with open(a_path, 'r') as f:
 
-       src_file = SourceFile(f, _ImportResolver)
 
-     try:
 
-       src_file.ProcessContent(strip_expansion=opts.collapse)
 
-     except PDDMError as e:
 
-       sys.stderr.write('ERROR: %s\n...While processing "%s"\n' %
 
-                        (e.message, a_path))
 
-       return 101
 
-     if src_file.processed_content != src_file.original_content:
 
-       if not opts.dry_run:
 
-         print('Updating for "%s".' % a_path)
 
-         with open(a_path, 'w') as f:
 
-           f.write(src_file.processed_content)
 
-       else:
 
-         # Special result to indicate things need updating.
 
-         print('Update needed for "%s".' % a_path)
 
-         result = 1
 
-     elif opts.verbose:
 
-       print('No update for "%s".' % a_path)
 
-   return result
 
- if __name__ == '__main__':
 
-   sys.exit(main(sys.argv[1:]))
 
 
  |