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 |