Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 """Utilities for code generation. | |
| 5 | |
| 6 Main classes are Code for appending code and | |
| 7 TypeManager for managing types for things in model.""" | |
| 8 | |
| 9 from model import PropertyType | |
| 10 from datetime import datetime | |
| 11 | |
| 12 | |
| 13 CHROMIUM_LICENSE = ( | |
| 14 """// Copyright (c) %d The Chromium Authors. All rights reserved. | |
| 15 // Use of this source code is governed by a BSD-style license that can be | |
| 16 // found in the LICENSE file.""" % datetime.now().year | |
| 17 ) | |
| 18 WARNING_MESSAGE = """// GENERATED FROM THE API DEFINITION IN | |
| 19 // %s | |
| 20 // DO NOT EDIT.""" | |
| 21 | |
| 22 INDENT_SIZE = 2 | |
| 23 COMMENT_LENGTH = 80 | |
| 24 class Code(object): | |
| 25 """A convenience object for appending code. | |
| 26 | |
| 27 Logically each object should be a block of code.""" | |
| 28 def __init__(self): | |
| 29 self.code = [] | |
| 30 # TODO(calamity): Indent stack probably isn't necessary | |
| 31 self._indent_list = [] | |
| 32 self.indent_level = 0 | |
| 33 | |
| 34 def append(self, line=''): | |
| 35 """Appends a line of code at the current indent level or just a | |
| 36 newline if line is not specified. | |
| 37 | |
| 38 This will strip trailing whitespace.""" | |
| 39 self.code.append(((' ' * self.indent_level) + line).rstrip()) | |
| 40 return self | |
| 41 | |
| 42 def add(self, obj): | |
| 43 """Concatenate another Code object onto this one. | |
|
not at google - send to devlin
2012/01/13 00:07:27
call it "concat" then? add is an odd name.
calamity
2012/01/16 04:01:06
Done.
| |
| 44 | |
| 45 Appends the code at the current indent level. Will fail if there are any | |
| 46 un-interpolated format specifiers eg %s, %(something)s which helps | |
| 47 isolate any strings that haven't been substituted. | |
| 48 """ | |
| 49 if not isinstance(obj, Code): | |
| 50 raise TypeError() | |
| 51 for line in obj.code: | |
| 52 self.code.append(((' ' * self.indent_level) + line % ()).rstrip()) | |
| 53 | |
| 54 return self | |
| 55 | |
| 56 # TODO(calamity): is variable indent size necessary/a good idea? | |
|
not at google - send to devlin
2012/01/12 06:01:05
yeah, doesn't look like you're using it anyway. mi
calamity
2012/01/12 22:59:20
Done.
| |
| 57 def sblock(self, line, indent=INDENT_SIZE): | |
|
not at google - send to devlin
2012/01/12 06:01:05
I don't like the names "sblock" and "eblock" much
calamity
2012/01/12 22:59:20
Can you explain what you dislike about the current
not at google - send to devlin
2012/01/13 00:07:27
Oh cool, I didn't know that either. I was assumin
not at google - send to devlin
2012/01/13 00:09:42
Or similarly,
(c.append('foo')
.append('bar1').
calamity
2012/01/13 00:40:23
It was more error proof. Calling unindent _after_
not at google - send to devlin
2012/01/13 02:14:09
I don't understand what you mean... surely it's pr
| |
| 58 """Starts a code block. | |
| 59 | |
| 60 Adds a line of code and then increases the indent level.""" | |
| 61 self.append(line) | |
| 62 self._indent_list.append(self.indent_level) | |
| 63 self.indent_level += indent | |
| 64 return self | |
| 65 | |
| 66 def eblock(self, line=''): | |
| 67 """Ends a code block. | |
| 68 | |
| 69 Decreases the indent level and then adds the line of code. | |
| 70 """ | |
| 71 # TODO(calamity): Decide if type checking is necessary | |
| 72 #if not isinstance(line, basestring): | |
| 73 # raise TypeError | |
| 74 self.indent_level = self._indent_list.pop() | |
| 75 | |
| 76 self.append(line) | |
| 77 return self | |
| 78 | |
| 79 def comment(self, comment): | |
| 80 """Adds the given string as a comment. | |
| 81 | |
| 82 Will split the comment if it's too long. Use mainly for variable length | |
| 83 comments. Otherwise just use code.append('// ...') for comments. | |
| 84 """ | |
| 85 comment_symbol = '// ' | |
| 86 max_len = COMMENT_LENGTH - self.indent_level - len(comment_symbol) | |
| 87 while len(comment) >= max_len: | |
| 88 line = comment[0:max_len] | |
| 89 last_space = line.rfind(' ') | |
| 90 if last_space != -1: | |
| 91 line = line[0:last_space] | |
| 92 comment = comment[last_space + 1:] | |
| 93 else: | |
| 94 comment = comment[max_len:] | |
| 95 self.append(comment_symbol + line) | |
| 96 self.append(comment_symbol + comment) | |
|
not at google - send to devlin
2012/01/12 06:01:05
return self
calamity
2012/01/12 22:59:20
Done.
| |
| 97 | |
| 98 def substitute(self, d): | |
| 99 """Goes through each line and interpolates using the given dict. | |
|
not at google - send to devlin
2012/01/13 00:07:27
Explain why you need to have this method at all, r
calamity
2012/01/16 04:01:06
Done.
| |
| 100 | |
| 101 Raises type error if passed something that isn't a dict.""" | |
| 102 if not isinstance(d, dict): | |
| 103 raise TypeError('Passed argument is not a dictionary: ' + d) | |
| 104 for i, line in enumerate(self.code): | |
| 105 # Only need to check %s because arg is a dict and python will allow | |
| 106 # '%s %(named)s' but just about nothing else | |
| 107 if '%s' in self.code[i] or '%r' in self.code[i]: | |
| 108 raise TypeError('%s or %r found in substitution.' | |
| 109 'Named arguments only. Use %%s to escape') | |
| 110 self.code[i] = line % d | |
|
not at google - send to devlin
2012/01/12 06:01:05
return self
calamity
2012/01/12 22:59:20
Done.
| |
| 111 | |
| 112 def to_string(self): | |
|
not at google - send to devlin
2012/01/12 06:01:05
weird to have this method when there's the python
calamity
2012/01/12 22:59:20
Done.
| |
| 113 """Returns the code joined together as a string.""" | |
| 114 return '\n'.join(self.code) | |
| 115 | |
| 116 class TypeManager(object): | |
|
not at google - send to devlin
2012/01/12 06:01:05
this should be in its own file.
so should most cl
calamity
2012/01/12 22:59:20
Done.
| |
| 117 """Manages the types of properties and provides utlities for getting the | |
| 118 C++ type out of a model.PropertyT.""" | |
| 119 def __init__(self, namespace, model): | |
| 120 self.model = model | |
| 121 self.types = model.types | |
| 122 self.namespace = namespace | |
| 123 | |
| 124 # TODO(calamity): Handle ANY | |
| 125 def get_type(self, prop): | |
| 126 """Translates a json_type into its C++ equivalent. | |
| 127 | |
| 128 If REF types from different namespaces are referenced, will resolve | |
| 129 using self.types. | |
| 130 """ | |
| 131 simple_c_types = { | |
| 132 'boolean': 'bool', | |
| 133 'integer': 'int', | |
| 134 'double': 'double', | |
| 135 'string': 'std::string', | |
| 136 } | |
| 137 if prop.type == PropertyType.REF: | |
| 138 ref_type = self.types.get(prop.json_type) | |
| 139 if not ref_type: | |
| 140 raise KeyError('Cannot find referenced type: %s' % prop.json_type) | |
| 141 if self.namespace != ref_type: | |
| 142 return '%s::%s' % (ref_type.filename, prop.json_type) | |
| 143 else: | |
| 144 return '%s' % prop.json_type | |
| 145 elif prop.type == PropertyType.FUNDAMENTAL: | |
| 146 return simple_c_types[prop.json_type] | |
| 147 elif prop.type == PropertyType.ARRAY: | |
| 148 return 'std::vector<%s>' % self.get_type(prop.item_type) | |
| 149 elif prop.type == PropertyType.OBJECT: | |
| 150 return cpp_name(prop.name) | |
| 151 # TODO(calamity): choices | |
| 152 else: | |
| 153 raise NotImplementedError | |
| 154 | |
| 155 def parameter_declaration(self, param, type_modifiers=None, | |
| 156 default_format='%(type)s %(name)s'): | |
| 157 """Returns a string that declares a parameter. Used where the parameter type | |
| 158 isn't clear and covering all cases would be ugly. | |
| 159 | |
| 160 Can be given a dictionary with PropertyType keys and format string values | |
| 161 where %(type)s and %(name)s denote the C++ type and parameter | |
| 162 name respectively. | |
| 163 | |
| 164 ParamFormat has some common formats. | |
| 165 """ | |
| 166 if not type_modifiers: | |
| 167 type_modifiers = {} | |
| 168 return (type_modifiers.get(param.type, default_format)) % { | |
| 169 'type': self.get_type(param), 'name': param.name} | |
| 170 | |
| 171 def get_generic_type(self, prop): | |
| 172 """Returns the c_type of an object suitable for putting in <%s>. | |
| 173 | |
| 174 Will add a space to inner generics to prevent operator ambiguity. | |
| 175 """ | |
| 176 prop_type = self.get_type(prop) | |
| 177 if prop_type[-1] == '>': | |
| 178 return '%s ' % prop_type | |
| 179 else: | |
| 180 return '%s' % prop_type | |
| 181 | |
| 182 def resolve_generated_includes(self): | |
| 183 """Returns the #include lines for self.namespace using the other | |
| 184 namespaces in self.model. | |
| 185 """ | |
| 186 dependencies = set() | |
| 187 for function in self.namespace.functions.values(): | |
| 188 for param in function.params: | |
| 189 dependencies |= self.type_dependencies(param) | |
| 190 dependencies |= self.type_dependencies(function.callback.param) | |
| 191 for tipe in self.namespace.types.values(): | |
| 192 for prop in tipe.properties.values(): | |
| 193 dependencies |= self.type_dependencies(prop) | |
| 194 | |
| 195 includes = Code() | |
| 196 for dependency in dependencies: | |
| 197 dependency_namespace = self.types[dependency] | |
| 198 if dependency_namespace != self.namespace: | |
| 199 includes.append('#include "%s/%s.h"' % ( | |
| 200 dependency_namespace.parent_dir, | |
| 201 dependency_namespace.filename)) | |
| 202 return includes | |
| 203 | |
| 204 def type_dependencies(self, prop): | |
| 205 """Gets all the type dependencies of a single property. | |
| 206 | |
| 207 Will also get type dependencies from subproperties""" | |
| 208 deps = set() | |
| 209 if prop: | |
| 210 if prop.type == PropertyType.REF: | |
| 211 deps.add(prop.json_type) | |
| 212 elif prop.type == PropertyType.ARRAY: | |
| 213 deps = self.type_dependencies(prop.item_type) | |
| 214 elif prop.type == PropertyType.OBJECT: | |
| 215 for p in prop.properties.values(): | |
| 216 deps |= self.type_dependencies(p) | |
| 217 return deps | |
| 218 | |
| 219 def cpp_name(s): | |
| 220 """Translates a namespace name or function name into something more | |
| 221 suited to C++. | |
| 222 | |
| 223 eg experimental.downloads -> Experimental_Downloads | |
| 224 updateAll -> UpdateAll. | |
| 225 """ | |
| 226 return '_'.join([x[0].upper() + x[1:] for x in s.split('.')]) | |
| 227 | |
| 228 class ParamFormat(object): | |
|
not at google - send to devlin
2012/01/12 06:01:05
ditto
calamity
2012/01/16 04:01:06
Done.
| |
| 229 """Common parameter modifier formats to be used with | |
| 230 TypeManager.parameter_declaration""" | |
| 231 POINTER = '%(type)s* %(name)s' | |
| 232 REFERENCE = '%(type)s& %(name)s' | |
| OLD | NEW |