1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2012 The Android Open Source Project 4*795d594fSAndroid Build Coastguard Worker# 5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*795d594fSAndroid Build Coastguard Worker# 11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*795d594fSAndroid Build Coastguard Worker# limitations under the License. 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker"""Generates default implementations of operator<< for enum types.""" 18*795d594fSAndroid Build Coastguard Worker 19*795d594fSAndroid Build Coastguard Workerimport codecs 20*795d594fSAndroid Build Coastguard Workerimport re 21*795d594fSAndroid Build Coastguard Workerimport sys 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Worker 24*795d594fSAndroid Build Coastguard Worker_ENUM_START_RE = re.compile( 25*795d594fSAndroid Build Coastguard Worker r'\benum\b\s+(class\s+)?(?:HIDDEN |EXPORT )?(\S+)\s+:?.*\{(\s+// private)?') 26*795d594fSAndroid Build Coastguard Worker_ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)') 27*795d594fSAndroid Build Coastguard Worker_ENUM_END_RE = re.compile(r'^\s*\};$') 28*795d594fSAndroid Build Coastguard Worker_ENUMS = {} 29*795d594fSAndroid Build Coastguard Worker_NAMESPACES = {} 30*795d594fSAndroid Build Coastguard Worker_ENUM_CLASSES = {} 31*795d594fSAndroid Build Coastguard Worker 32*795d594fSAndroid Build Coastguard Worker 33*795d594fSAndroid Build Coastguard Workerdef Confused(filename, line_number, line): 34*795d594fSAndroid Build Coastguard Worker sys.stderr.write('%s:%d: confused by:\n%s\n' % 35*795d594fSAndroid Build Coastguard Worker (filename, line_number, line)) 36*795d594fSAndroid Build Coastguard Worker raise Exception("giving up!") 37*795d594fSAndroid Build Coastguard Worker sys.exit(1) 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Worker 40*795d594fSAndroid Build Coastguard Workerdef ProcessFile(filename): 41*795d594fSAndroid Build Coastguard Worker lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') 42*795d594fSAndroid Build Coastguard Worker 43*795d594fSAndroid Build Coastguard Worker class EnumLines: 44*795d594fSAndroid Build Coastguard Worker def __init__(self, ns, ec): 45*795d594fSAndroid Build Coastguard Worker self.namespaces = ns 46*795d594fSAndroid Build Coastguard Worker self.enclosing_classes = ec 47*795d594fSAndroid Build Coastguard Worker self.lines = [] 48*795d594fSAndroid Build Coastguard Worker 49*795d594fSAndroid Build Coastguard Worker def generate_enum_lines(l): 50*795d594fSAndroid Build Coastguard Worker line_number = 0 51*795d594fSAndroid Build Coastguard Worker enum_lines = None 52*795d594fSAndroid Build Coastguard Worker namespaces = [] 53*795d594fSAndroid Build Coastguard Worker enclosing_classes = [] 54*795d594fSAndroid Build Coastguard Worker 55*795d594fSAndroid Build Coastguard Worker for raw_line in l: 56*795d594fSAndroid Build Coastguard Worker line_number += 1 57*795d594fSAndroid Build Coastguard Worker 58*795d594fSAndroid Build Coastguard Worker if enum_lines is None: 59*795d594fSAndroid Build Coastguard Worker # Is this the start of a new enum? 60*795d594fSAndroid Build Coastguard Worker m = _ENUM_START_RE.search(raw_line) 61*795d594fSAndroid Build Coastguard Worker if m: 62*795d594fSAndroid Build Coastguard Worker # Yes, so create new line list. 63*795d594fSAndroid Build Coastguard Worker enum_lines = EnumLines(namespaces[:], enclosing_classes[:]) 64*795d594fSAndroid Build Coastguard Worker enum_lines.lines.append((raw_line, line_number)) 65*795d594fSAndroid Build Coastguard Worker continue 66*795d594fSAndroid Build Coastguard Worker 67*795d594fSAndroid Build Coastguard Worker # Is this the start or end of a namespace? 68*795d594fSAndroid Build Coastguard Worker m = re.search(r'^namespace (\S+) (HIDDEN |EXPORT )?\{', raw_line) 69*795d594fSAndroid Build Coastguard Worker if m: 70*795d594fSAndroid Build Coastguard Worker namespaces.append(m.group(1)) 71*795d594fSAndroid Build Coastguard Worker continue 72*795d594fSAndroid Build Coastguard Worker m = re.search(r'^\}\s+// namespace', raw_line) 73*795d594fSAndroid Build Coastguard Worker if m: 74*795d594fSAndroid Build Coastguard Worker namespaces = namespaces[0:len(namespaces) - 1] 75*795d594fSAndroid Build Coastguard Worker continue 76*795d594fSAndroid Build Coastguard Worker 77*795d594fSAndroid Build Coastguard Worker # Is this the start or end of an enclosing class or struct? 78*795d594fSAndroid Build Coastguard Worker m = re.search( 79*795d594fSAndroid Build Coastguard Worker r'^\s*(?:class|struct)(?: HIDDEN| EXPORT)?(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{', raw_line) 80*795d594fSAndroid Build Coastguard Worker if m: 81*795d594fSAndroid Build Coastguard Worker enclosing_classes.append(m.group(1)) 82*795d594fSAndroid Build Coastguard Worker continue 83*795d594fSAndroid Build Coastguard Worker 84*795d594fSAndroid Build Coastguard Worker # End of class/struct -- be careful not to match "do { ... } while" constructs by accident 85*795d594fSAndroid Build Coastguard Worker m = re.search(r'^\s*\}(\s+)?(while)?(.+)?;', raw_line) 86*795d594fSAndroid Build Coastguard Worker if m and not m.group(2): 87*795d594fSAndroid Build Coastguard Worker enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1] 88*795d594fSAndroid Build Coastguard Worker continue 89*795d594fSAndroid Build Coastguard Worker 90*795d594fSAndroid Build Coastguard Worker continue 91*795d594fSAndroid Build Coastguard Worker 92*795d594fSAndroid Build Coastguard Worker # Is this the end of the current enum? 93*795d594fSAndroid Build Coastguard Worker m = _ENUM_END_RE.search(raw_line) 94*795d594fSAndroid Build Coastguard Worker if m: 95*795d594fSAndroid Build Coastguard Worker if enum_lines is None: 96*795d594fSAndroid Build Coastguard Worker Confused(filename, line_number, raw_line) 97*795d594fSAndroid Build Coastguard Worker yield enum_lines 98*795d594fSAndroid Build Coastguard Worker enum_lines = None 99*795d594fSAndroid Build Coastguard Worker continue 100*795d594fSAndroid Build Coastguard Worker 101*795d594fSAndroid Build Coastguard Worker # Append the line 102*795d594fSAndroid Build Coastguard Worker enum_lines.lines.append((raw_line, line_number)) 103*795d594fSAndroid Build Coastguard Worker 104*795d594fSAndroid Build Coastguard Worker for enum_lines in generate_enum_lines(lines): 105*795d594fSAndroid Build Coastguard Worker m = _ENUM_START_RE.search(enum_lines.lines[0][0]) 106*795d594fSAndroid Build Coastguard Worker if m.group(3) is not None: 107*795d594fSAndroid Build Coastguard Worker # Skip private enums. 108*795d594fSAndroid Build Coastguard Worker continue 109*795d594fSAndroid Build Coastguard Worker 110*795d594fSAndroid Build Coastguard Worker # Add an empty entry to _ENUMS for this enum. 111*795d594fSAndroid Build Coastguard Worker is_enum_class = m.group(1) is not None 112*795d594fSAndroid Build Coastguard Worker enum_name = m.group(2) 113*795d594fSAndroid Build Coastguard Worker if len(enum_lines.enclosing_classes) > 0: 114*795d594fSAndroid Build Coastguard Worker enum_name = '::'.join(enum_lines.enclosing_classes) + '::' + enum_name 115*795d594fSAndroid Build Coastguard Worker _ENUMS[enum_name] = [] 116*795d594fSAndroid Build Coastguard Worker _NAMESPACES[enum_name] = '::'.join(enum_lines.namespaces) 117*795d594fSAndroid Build Coastguard Worker _ENUM_CLASSES[enum_name] = is_enum_class 118*795d594fSAndroid Build Coastguard Worker 119*795d594fSAndroid Build Coastguard Worker def generate_non_empty_line(lines): 120*795d594fSAndroid Build Coastguard Worker for raw_line, line_number in lines: 121*795d594fSAndroid Build Coastguard Worker # Strip // comments. 122*795d594fSAndroid Build Coastguard Worker line = re.sub(r'//.*', '', raw_line) 123*795d594fSAndroid Build Coastguard Worker # Strip whitespace. 124*795d594fSAndroid Build Coastguard Worker line = line.strip() 125*795d594fSAndroid Build Coastguard Worker # Skip blank lines. 126*795d594fSAndroid Build Coastguard Worker if len(line) == 0: 127*795d594fSAndroid Build Coastguard Worker continue 128*795d594fSAndroid Build Coastguard Worker 129*795d594fSAndroid Build Coastguard Worker # The only useful thing in comments is the <<alternate text>> syntax for 130*795d594fSAndroid Build Coastguard Worker # overriding the default enum value names. Pull that out... 131*795d594fSAndroid Build Coastguard Worker enum_text = None 132*795d594fSAndroid Build Coastguard Worker m_comment = re.search(r'// <<(.*?)>>', raw_line) 133*795d594fSAndroid Build Coastguard Worker if m_comment: 134*795d594fSAndroid Build Coastguard Worker enum_text = m_comment.group(1) 135*795d594fSAndroid Build Coastguard Worker 136*795d594fSAndroid Build Coastguard Worker yield (line, enum_text, raw_line, line_number) 137*795d594fSAndroid Build Coastguard Worker 138*795d594fSAndroid Build Coastguard Worker for line, enum_text, raw_line, line_number in generate_non_empty_line(enum_lines.lines[1:]): 139*795d594fSAndroid Build Coastguard Worker # Since we know we're in an enum type, and we're not looking at a comment 140*795d594fSAndroid Build Coastguard Worker # or a blank line, this line should be the next enum value... 141*795d594fSAndroid Build Coastguard Worker m = _ENUM_VALUE_RE.search(line) 142*795d594fSAndroid Build Coastguard Worker if not m: 143*795d594fSAndroid Build Coastguard Worker Confused(filename, line_number, raw_line) 144*795d594fSAndroid Build Coastguard Worker enum_value = m.group(1) 145*795d594fSAndroid Build Coastguard Worker 146*795d594fSAndroid Build Coastguard Worker # By default, we turn "kSomeValue" into "SomeValue". 147*795d594fSAndroid Build Coastguard Worker if enum_text is None: 148*795d594fSAndroid Build Coastguard Worker enum_text = enum_value 149*795d594fSAndroid Build Coastguard Worker if enum_text.startswith('k'): 150*795d594fSAndroid Build Coastguard Worker enum_text = enum_text[1:] 151*795d594fSAndroid Build Coastguard Worker 152*795d594fSAndroid Build Coastguard Worker # Check that we understand the line (and hopefully do not parse incorrectly), or should 153*795d594fSAndroid Build Coastguard Worker # filter. 154*795d594fSAndroid Build Coastguard Worker rest = m.group(2).strip() 155*795d594fSAndroid Build Coastguard Worker 156*795d594fSAndroid Build Coastguard Worker # With "kSomeValue = kOtherValue," we take the original and skip later synonyms. 157*795d594fSAndroid Build Coastguard Worker # TODO: check that the rhs is actually an existing value. 158*795d594fSAndroid Build Coastguard Worker if rest.startswith('= k'): 159*795d594fSAndroid Build Coastguard Worker continue 160*795d594fSAndroid Build Coastguard Worker 161*795d594fSAndroid Build Coastguard Worker # Remove trailing comma. 162*795d594fSAndroid Build Coastguard Worker if rest.endswith(','): 163*795d594fSAndroid Build Coastguard Worker rest = rest[:-1] 164*795d594fSAndroid Build Coastguard Worker 165*795d594fSAndroid Build Coastguard Worker # We now expect rest to be empty, or an assignment to an "expression." 166*795d594fSAndroid Build Coastguard Worker if len(rest): 167*795d594fSAndroid Build Coastguard Worker # We want to lose the expression "= [exp]". As we do not have a real C parser, just 168*795d594fSAndroid Build Coastguard Worker # assume anything without a comma is valid. 169*795d594fSAndroid Build Coastguard Worker m_exp = re.match('= [^,]+$', rest) 170*795d594fSAndroid Build Coastguard Worker if m_exp is None: 171*795d594fSAndroid Build Coastguard Worker sys.stderr.write('%s\n' % (rest)) 172*795d594fSAndroid Build Coastguard Worker Confused(filename, line_number, raw_line) 173*795d594fSAndroid Build Coastguard Worker 174*795d594fSAndroid Build Coastguard Worker # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed 175*795d594fSAndroid Build Coastguard Worker # by enclosing classes). 176*795d594fSAndroid Build Coastguard Worker if is_enum_class: 177*795d594fSAndroid Build Coastguard Worker enum_value = enum_name + '::' + enum_value 178*795d594fSAndroid Build Coastguard Worker else: 179*795d594fSAndroid Build Coastguard Worker if len(enum_lines.enclosing_classes) > 0: 180*795d594fSAndroid Build Coastguard Worker enum_value = '::'.join(enum_lines.enclosing_classes) + '::' + enum_value 181*795d594fSAndroid Build Coastguard Worker 182*795d594fSAndroid Build Coastguard Worker _ENUMS[enum_name].append((enum_value, enum_text)) 183*795d594fSAndroid Build Coastguard Worker 184*795d594fSAndroid Build Coastguard Worker 185*795d594fSAndroid Build Coastguard Workerdef main(): 186*795d594fSAndroid Build Coastguard Worker local_path = sys.argv[1] 187*795d594fSAndroid Build Coastguard Worker header_files = [] 188*795d594fSAndroid Build Coastguard Worker for header_file in sys.argv[2:]: 189*795d594fSAndroid Build Coastguard Worker header_files.append(header_file) 190*795d594fSAndroid Build Coastguard Worker ProcessFile(header_file) 191*795d594fSAndroid Build Coastguard Worker 192*795d594fSAndroid Build Coastguard Worker print('#include <iostream>') 193*795d594fSAndroid Build Coastguard Worker print('') 194*795d594fSAndroid Build Coastguard Worker 195*795d594fSAndroid Build Coastguard Worker for header_file in header_files: 196*795d594fSAndroid Build Coastguard Worker header_file = header_file.replace(local_path + '/', '') 197*795d594fSAndroid Build Coastguard Worker print('#include "%s"' % header_file) 198*795d594fSAndroid Build Coastguard Worker 199*795d594fSAndroid Build Coastguard Worker print('') 200*795d594fSAndroid Build Coastguard Worker 201*795d594fSAndroid Build Coastguard Worker for enum_name in _ENUMS: 202*795d594fSAndroid Build Coastguard Worker print('// This was automatically generated by art/tools/generate_operator_out.py --- do not edit!') 203*795d594fSAndroid Build Coastguard Worker 204*795d594fSAndroid Build Coastguard Worker namespaces = _NAMESPACES[enum_name].split('::') 205*795d594fSAndroid Build Coastguard Worker for namespace in namespaces: 206*795d594fSAndroid Build Coastguard Worker print('namespace %s {' % namespace) 207*795d594fSAndroid Build Coastguard Worker 208*795d594fSAndroid Build Coastguard Worker print( 209*795d594fSAndroid Build Coastguard Worker 'std::ostream& operator<<(std::ostream& os, %s rhs) {' % enum_name) 210*795d594fSAndroid Build Coastguard Worker print(' switch (rhs) {') 211*795d594fSAndroid Build Coastguard Worker for (enum_value, enum_text) in _ENUMS[enum_name]: 212*795d594fSAndroid Build Coastguard Worker print(' case %s: os << "%s"; break;' % (enum_value, enum_text)) 213*795d594fSAndroid Build Coastguard Worker if not _ENUM_CLASSES[enum_name]: 214*795d594fSAndroid Build Coastguard Worker print( 215*795d594fSAndroid Build Coastguard Worker ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name) 216*795d594fSAndroid Build Coastguard Worker print(' }') 217*795d594fSAndroid Build Coastguard Worker print(' return os;') 218*795d594fSAndroid Build Coastguard Worker print('}') 219*795d594fSAndroid Build Coastguard Worker 220*795d594fSAndroid Build Coastguard Worker for namespace in reversed(namespaces): 221*795d594fSAndroid Build Coastguard Worker print('} // namespace %s' % namespace) 222*795d594fSAndroid Build Coastguard Worker print('') 223*795d594fSAndroid Build Coastguard Worker 224*795d594fSAndroid Build Coastguard Worker sys.exit(0) 225*795d594fSAndroid Build Coastguard Worker 226*795d594fSAndroid Build Coastguard Worker 227*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__': 228*795d594fSAndroid Build Coastguard Worker main() 229