ART: Refactor generate_operator_out.py

Use generators to delineate the parsing steps better.

Bug: 121245951
Test: m test-art-host
Change-Id: Ib52e8c991e92015c1ae5e0377015b1b89b259de1
diff --git a/tools/generate_operator_out.py b/tools/generate_operator_out.py
index e354ac4..f909f81 100755
--- a/tools/generate_operator_out.py
+++ b/tools/generate_operator_out.py
@@ -39,135 +39,146 @@
 
 def ProcessFile(filename):
     lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
-    in_enum = False
-    is_enum_private = False
-    is_enum_class = False
-    line_number = 0
 
-    namespaces = []
-    enclosing_classes = []
+    class EnumLines:
+        def __init__(self, ns, ec):
+            self.namespaces = ns
+            self.enclosing_classes = ec
+            self.lines = []
 
-    for raw_line in lines:
-        line_number += 1
+    def generate_enum_lines(l):
+        line_number = 0
+        enum_lines = None
+        namespaces = []
+        enclosing_classes = []
 
-        if not in_enum:
-            # Is this the start of a new enum?
-            m = _ENUM_START_RE.search(raw_line)
+        for raw_line in l:
+            line_number += 1
+
+            if enum_lines is None:
+                # Is this the start of a new enum?
+                m = _ENUM_START_RE.search(raw_line)
+                if m:
+                    # Yes, so create new line list.
+                    enum_lines = EnumLines(namespaces[:], enclosing_classes[:])
+                    enum_lines.lines.append((raw_line, line_number))
+                    continue
+
+                # Is this the start or end of a namespace?
+                m = re.search(r'^namespace (\S+) \{', raw_line)
+                if m:
+                    namespaces.append(m.group(1))
+                    continue
+                m = re.search(r'^\}\s+// namespace', raw_line)
+                if m:
+                    namespaces = namespaces[0:len(namespaces) - 1]
+                    continue
+
+                # Is this the start or end of an enclosing class or struct?
+                m = re.search(
+                    r'^\s*(?:class|struct)(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{', raw_line)
+                if m:
+                    enclosing_classes.append(m.group(1))
+                    continue
+
+                # End of class/struct -- be careful not to match "do { ... } while" constructs by accident
+                m = re.search(r'^\s*\}(\s+)?(while)?(.+)?;', raw_line)
+                if m and not m.group(2):
+                    enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1]
+                    continue
+
+                continue
+
+            # Is this the end of the current enum?
+            m = _ENUM_END_RE.search(raw_line)
             if m:
-                # Yes, so add an empty entry to _ENUMS for this enum.
-
-                # Except when it's private
-                if m.group(3) is not None:
-                    is_enum_private = True
-                else:
-                    is_enum_private = False
-                    is_enum_class = m.group(1) is not None
-                    enum_name = m.group(2)
-                    if len(enclosing_classes) > 0:
-                        enum_name = '::'.join(
-                            enclosing_classes) + '::' + enum_name
-                    _ENUMS[enum_name] = []
-                    _NAMESPACES[enum_name] = '::'.join(namespaces)
-                    _ENUM_CLASSES[enum_name] = is_enum_class
-                in_enum = True
+                if enum_lines is None:
+                    Confused(filename, line_number, raw_line)
+                yield enum_lines
+                enum_lines = None
                 continue
 
-            # Is this the start or end of a namespace?
-            m = re.search(r'^namespace (\S+) \{', raw_line)
-            if m:
-                namespaces.append(m.group(1))
-                continue
-            m = re.search(r'^\}\s+// namespace', raw_line)
-            if m:
-                namespaces = namespaces[0:len(namespaces) - 1]
-                continue
+            # Append the line
+            enum_lines.lines.append((raw_line, line_number))
 
-            # Is this the start or end of an enclosing class or struct?
-            m = re.search(
-                r'^\s*(?:class|struct)(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{', raw_line)
-            if m:
-                enclosing_classes.append(m.group(1))
-                continue
-
-            # End of class/struct -- be careful not to match "do { ... } while" constructs by accident
-            m = re.search(r'^\s*\}(\s+)?(while)?(.+)?;', raw_line)
-            if m and not m.group(2):
-                enclosing_classes = enclosing_classes[0:len(
-                    enclosing_classes) - 1]
-                continue
-
+    for enum_lines in generate_enum_lines(lines):
+        m = _ENUM_START_RE.search(enum_lines.lines[0][0])
+        if m.group(3) is not None:
+            # Skip private enums.
             continue
 
-        # Is this the end of the current enum?
-        m = _ENUM_END_RE.search(raw_line)
-        if m:
-            if not in_enum:
+        # Add an empty entry to _ENUMS for this enum.
+        is_enum_class = m.group(1) is not None
+        enum_name = m.group(2)
+        if len(enum_lines.enclosing_classes) > 0:
+            enum_name = '::'.join(enum_lines.enclosing_classes) + '::' + enum_name
+        _ENUMS[enum_name] = []
+        _NAMESPACES[enum_name] = '::'.join(enum_lines.namespaces)
+        _ENUM_CLASSES[enum_name] = is_enum_class
+
+        def generate_non_empty_line(lines):
+            for raw_line, line_number in lines:
+                # Strip // comments.
+                line = re.sub(r'//.*', '', raw_line)
+                # Strip whitespace.
+                line = line.strip()
+                # Skip blank lines.
+                if len(line) == 0:
+                    continue
+
+                # The only useful thing in comments is the <<alternate text>> syntax for
+                # overriding the default enum value names. Pull that out...
+                enum_text = None
+                m_comment = re.search(r'// <<(.*?)>>', raw_line)
+                if m_comment:
+                    enum_text = m_comment.group(1)
+
+                yield (line, enum_text, raw_line, line_number)
+
+        for line, enum_text, raw_line, line_number in generate_non_empty_line(enum_lines.lines[1:]):
+            # Since we know we're in an enum type, and we're not looking at a comment
+            # or a blank line, this line should be the next enum value...
+            m = _ENUM_VALUE_RE.search(line)
+            if not m:
                 Confused(filename, line_number, raw_line)
-            in_enum = False
-            continue
+            enum_value = m.group(1)
 
-        if is_enum_private:
-            continue
+            # By default, we turn "kSomeValue" into "SomeValue".
+            if enum_text is None:
+                enum_text = enum_value
+                if enum_text.startswith('k'):
+                    enum_text = enum_text[1:]
 
-        # The only useful thing in comments is the <<alternate text>> syntax for
-        # overriding the default enum value names. Pull that out...
-        enum_text = None
-        m_comment = re.search(r'// <<(.*?)>>', raw_line)
-        if m_comment:
-            enum_text = m_comment.group(1)
-        # ...and then strip // comments.
-        line = re.sub(r'//.*', '', raw_line)
+            # Lose literal values because we don't care; turn "= 123, // blah" into ", // blah".
+            rest = m.group(2).strip()
+            m_literal = re.search(r'= (0x[0-9a-f]+|-?[0-9]+|\'.\')', rest)
+            if m_literal:
+                rest = rest[(len(m_literal.group(0))):]
 
-        # Strip whitespace.
-        line = line.strip()
+            # With "kSomeValue = kOtherValue," we take the original and skip later synonyms.
+            # TODO: check that the rhs is actually an existing value.
+            if rest.startswith('= k'):
+                continue
 
-        # Skip blank lines.
-        if len(line) == 0:
-            continue
+            # Remove any trailing comma and whitespace
+            if rest.startswith(','):
+                rest = rest[1:]
+            rest = rest.strip()
 
-        # Since we know we're in an enum type, and we're not looking at a comment
-        # or a blank line, this line should be the next enum value...
-        m = _ENUM_VALUE_RE.search(line)
-        if not m:
-            Confused(filename, line_number, raw_line)
-        enum_value = m.group(1)
+            # There shouldn't be anything left.
+            if len(rest):
+                sys.stderr.write('%s\n' % (rest))
+                Confused(filename, line_number, raw_line)
 
-        # By default, we turn "kSomeValue" into "SomeValue".
-        if enum_text is None:
-            enum_text = enum_value
-            if enum_text.startswith('k'):
-                enum_text = enum_text[1:]
+            # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed
+            # by enclosing classes).
+            if is_enum_class:
+                enum_value = enum_name + '::' + enum_value
+            else:
+                if len(enum_lines.enclosing_classes) > 0:
+                    enum_value = '::'.join(enum_lines.enclosing_classes) + '::' + enum_value
 
-        # Lose literal values because we don't care; turn "= 123, // blah" into ", // blah".
-        rest = m.group(2).strip()
-        m_literal = re.search(r'= (0x[0-9a-f]+|-?[0-9]+|\'.\')', rest)
-        if m_literal:
-            rest = rest[(len(m_literal.group(0))):]
-
-        # With "kSomeValue = kOtherValue," we take the original and skip later synonyms.
-        # TODO: check that the rhs is actually an existing value.
-        if rest.startswith('= k'):
-            continue
-
-        # Remove any trailing comma and whitespace
-        if rest.startswith(','):
-            rest = rest[1:]
-        rest = rest.strip()
-
-        # There shouldn't be anything left.
-        if len(rest):
-            sys.stderr.write('%s\n' % (rest))
-            Confused(filename, line_number, raw_line)
-
-        # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed
-        # by enclosing classes).
-        if is_enum_class:
-            enum_value = enum_name + '::' + enum_value
-        else:
-            if len(enclosing_classes) > 0:
-                enum_value = '::'.join(enclosing_classes) + '::' + enum_value
-
-        _ENUMS[enum_name].append((enum_value, enum_text))
+            _ENUMS[enum_name].append((enum_value, enum_text))
 
 
 def main():