Chromium Code Reviews| Index: tools/json_schema_compiler/cc_generator.py |
| diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..45277813ec01431f7d994146da4b578a12038ff9 |
| --- /dev/null |
| +++ b/tools/json_schema_compiler/cc_generator.py |
| @@ -0,0 +1,294 @@ |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +from model import PropertyType |
| +import code |
| +import cpp_type_generator |
|
Yoyo Zhou
2012/01/23 23:16:42
Looks like you're not using this import. (Same in
calamity
2012/01/24 22:57:22
Done.
|
| +import cpp_util |
| + |
| +class CCGenerator(object): |
| + """A .cc generator for a namespace. |
| + """ |
| + def __init__(self, namespace, model, cpp_type_generator): |
| + self._cpp_type_generator = cpp_type_generator |
| + self._namespace = namespace |
| + self._target_namespace = ( |
| + self._cpp_type_generator.getCppNamespaceName(self._namespace)) |
| + |
| + def generate(self): |
|
Yoyo Zhou
2012/01/23 23:16:42
Method names should start with caps.
calamity
2012/01/24 22:57:22
Done.
|
| + """Generates a code.Code object with the .cc for a single namespace. |
| + """ |
| + c = code.Code() |
| + (c.append(cpp_util.CHROMIUM_LICENSE) |
| + .append() |
| + .append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file) |
| + .append() |
| + .append('#include "tools/json_schema_compiler/util.h"') |
| + .append('#include "%s/%s.h"' % |
| + (self._namespace.source_file_dir, self._target_namespace)) |
| + .append() |
| + .concat(self._cpp_type_generator.getCppNamespaceStart()) |
| + .append() |
| + .append('//') |
| + .append('// Types') |
| + .append('//') |
| + .append() |
| + ) |
| + for type_ in self._namespace.types.values(): |
| + (c.concat(self._generateType(type_)) |
| + .append() |
| + ) |
| + (c.append('//') |
| + .append('// Functions') |
| + .append('//') |
| + .append() |
| + ) |
| + for function in self._namespace.functions.values(): |
| + (c.concat(self._generateFunction(function)) |
| + .append() |
| + ) |
| + (c.concat(self._cpp_type_generator.getCppNamespaceEnd()) |
| + .append() |
| + ) |
| + # TODO(calamity): Events |
| + return c |
| + |
| + def _generateType(self, type_): |
| + """Generates the function definitions for a type. |
| + """ |
| + c = code.Code() |
| + |
| + (c.append('%(classname)s::%(classname)s() {}') |
| + .append('%(classname)s::~%(classname)s() {}') |
| + .append() |
| + ) |
| + c.substitute({'classname': type_.name}) |
| + |
| + c.concat(self._generateTypePopulate(type_)) |
| + c.append() |
| + # TODO(calamity): deal with non-serializable |
| + c.concat(self._generateTypeTovalue(type_)) |
| + c.append() |
| + |
| + return c |
| + |
| + def _generateTypePopulate(self, type_): |
| + """Generates the function for populating a type given a pointer to it. |
| + """ |
| + c = code.Code() |
| + (c.append('// static') |
| + .sblock('bool %(name)s::Populate(const Value& value, %(name)s* out) {') |
| + .append('if (!value.IsType(Value::TYPE_DICTIONARY))') |
| + .append(' return false;') |
| + .append('const DictionaryValue* dict = ' |
| + 'static_cast<const DictionaryValue*>(&value);') |
| + .append() |
| + ) |
| + c.substitute({'name': type_.name}) |
| + |
| + # TODO(calamity): this handle single PropertyType.REF properties. |
|
Yoyo Zhou
2012/01/23 23:16:42
This comment seems incomplete.
calamity
2012/01/24 22:57:22
Done.
|
| + # add ALL the types |
| + for prop in type_.properties.values(): |
| + sub = {'name': prop.name} |
| + if prop.type_ == PropertyType.ARRAY: |
| + if prop.item_type.type_ == PropertyType.REF: |
| + if prop.optional: |
| + (c.append('if (!json_schema_compiler::util::' |
| + 'GetOptionalTypes<%(type)s>(*dict,') |
| + .append(' "%(name)s", &out->%(name)s))') |
| + .append(' return false;') |
| + ) |
| + else: |
| + (c.append('if (!json_schema_compiler::util::' |
| + 'GetTypes<%(type)s>(*dict,') |
| + .append(' "%(name)s", &out->%(name)s))') |
| + .append(' return false;') |
| + ) |
| + sub['type'] = self._cpp_type_generator.getType(prop.item_type, |
| + pad_for_generics=True) |
| + elif prop.item_type.type_ == PropertyType.STRING: |
| + if prop.optional: |
| + (c.append('if (!json_schema_compiler::util::GetOptionalStrings' |
| + '(*dict, "%(name)s", &out->%(name)s))') |
| + .append(' return false;') |
| + ) |
| + else: |
| + (c.append('if (!json_schema_compiler::util::GetStrings' |
| + '(*dict, "%(name)s", &out->%(name)s))') |
| + .append(' return false;') |
| + ) |
| + else: |
| + raise NotImplementedError(prop.item_type.type_) |
| + elif proptype_.is_fundamental: |
|
Yoyo Zhou
2012/01/23 23:16:42
prop.type_?
calamity
2012/01/24 22:57:22
Done.
|
| + c.append('if (!dict->%s)' % |
| + cpp_util.getFundamentalValue(prop, '&out->%s' % prop.name)) |
| + c.append(' return false;') |
| + else: |
| + raise NotImplementedError(prop.type_) |
| + c.substitute(sub) |
| + (c.append('return true;') |
| + .eblock('}') |
| + ) |
| + return c |
| + |
| + def _generateTypeTovalue(self, type_): |
|
Yoyo Zhou
2012/01/23 23:16:42
ToValue
calamity
2012/01/24 22:57:22
Done.
|
| + """Generates a function that serializes the type into a |DictionaryValue|. |
| + """ |
| + c = code.Code() |
| + (c.sblock('DictionaryValue* %s::ToValue() const {' % type_.name) |
| + .append('DictionaryValue* value = new DictionaryValue();') |
| + .append() |
| + ) |
| + name = type_.name.lower() |
| + for prop in type_.properties.values(): |
| + prop_name = name + '_' + prop.name if name else prop.name |
| + this_var = prop.name |
| + c.concat(self._createValueFromProperty(prop_name, prop, this_var)) |
| + (c.append() |
| + .append('return value;') |
| + .eblock('}') |
| + ) |
| + return c |
| + |
| + # TODO(calamity): object and choices proptypes |
| + def _createValueFromProperty(self, name, prop, var): |
| + """Generates code to serialize a single property in a type. |
| + """ |
| + c = code.Code() |
| + if prop.type_.is_fundamental: |
| + c.append('Value* %s_value = %s;' % |
| + (name, cpp_util.createFundamentalValue(prop, var))) |
| + elif prop.type_ == PropertyType.ARRAY: |
| + if prop.item_type.type_ == PropertyType.STRING: |
| + if prop.optional: |
| + c.append('json_schema_compiler::util::' |
| + 'SetOptionalStrings(%s, "%s", value);' % (var, prop.name)) |
| + else: |
| + c.append('json_schema_compiler::util::' |
| + 'SetStrings(%s, "%s", value);' % (var, prop.name)) |
| + else: |
| + item_name = name + '_single' |
| + (c.append('ListValue* %(name)s_value = new ListValue();') |
| + .append('for (%(it_type)s::iterator it = %(var)s->begin();') |
| + .sblock(' it != %(var)s->end(); ++it) {') |
| + .concat(self._createValueFromProperty(item_name, prop.item_type, |
| + '*it')) |
| + .append('%(name)s_value->Append(%(prop_val)s_value);') |
| + .eblock('}') |
| + ) |
| + c.substitute( |
| + {'it_type': self._cpp_type_generator.getType(prop), |
| + 'name': name, 'var': var, 'prop_val': item_name}) |
| + elif prop.type_ == PropertyType.REF: |
| + c.append('Value* %s_value = %s.ToValue();' % (name, var)) |
| + else: |
| + raise NotImplementedError |
| + return c |
| + |
| + def _generateFunction(self, function): |
| + """Generates the definitions for function structs. |
| + """ |
| + classname = cpp_util.cppName(function.name) |
| + c = code.Code() |
| + |
| + # Params::Populate function |
| + if function.params: |
| + (c.append('%(name)s::Params::Params() {}') |
| + .append('%(name)s::Params::~Params() {}') |
| + .append() |
| + .concat(self._generateFunctionParamsCreate(function)) |
| + .append() |
| + ) |
| + |
| + # Result::Create function |
| + c.concat(self._generateFunctionResultCreate(function)) |
| + |
| + c.substitute({'name': classname}) |
| + |
| + return c |
| + |
| + def _generateFunctionParamsCreate(self, function): |
| + """Generate function to create an instance of Params given a pointer. |
| + """ |
| + classname = cpp_util.cppName(function.name) |
| + c = code.Code() |
| + c.append('// static') |
| + c.append('%(classname)s::Params* %(classname)s::Params::Create' |
| + '(const ListValue& args) {') |
| + c.substitute({'classname': classname}) |
| + c.append('if (args.GetSize() != %d)' % len(function.params)) |
| + c.append(' return NULL;') |
| + |
| + # TODO(calamity): generalize, needs to move to function to do populates for |
| + # wider variety of args |
| + for i, param in enumerate(function.params): |
| + sub = {'name': param.name, 'pos': i} |
| + c.append() |
| + # TODO(calamity): Make valid for not just objects |
| + c.append('DictionaryValue* %(name)s_param = NULL;') |
| + c.append('if (!args.GetDictionary(%(pos)d, &%(name)s_param))') |
| + c.append(' return NULL;') |
| + c.append('scoped_ptr<Params> out;') |
|
Yoyo Zhou
2012/01/23 23:16:42
Should this be outside the for loop?
Why not use
calamity
2012/01/24 22:57:22
Done. I made it a scoped_ptr so it would clean up
|
| + if param.type_ == PropertyType.REF: |
| + c.append('if (!%(ctype)s::Populate(*%(name)s_param, &out->%(name)s))') |
| + c.append(' return NULL;') |
| + sub['ctype'] = self._cpp_type_generator.getType(param) |
| + elif param.type_.is_fundamental: |
| + raise NotImplementedError('Fundamental types are unimplemented') |
| + elif param.type_ == PropertyType.OBJECT: |
| + c.append('if (!%(ctype)s::Populate(*%(name)s_param, &out->%(name)s))') |
| + c.append(' return NULL;') |
| + sub['ctype'] = self._cpp_type_generator.getType(param) |
| + elif param.type_ == PropertyType.CHOICES: |
| + raise NotImplementedError('Choices is unimplemented') |
| + else: |
| + raise NotImplementedError(param.type_) |
| + c.substitute(sub) |
| + c.append() |
| + c.append('return out.release();') |
| + c.eblock('}') |
| + |
| + return c |
| + |
| + def _generateFunctionResultCreate(self, function): |
| + """Generate function to create a Result given the return value. |
| + """ |
| + classname = cpp_util.cppName(function.name) |
| + c = code.Code() |
| + c.append('// static') |
| + param = function.callback.param |
| + arg = '' |
| + if param: |
| + if param.type_ == PropertyType.REF: |
| + arg = 'const %(type)s& %(name)s' |
| + else: |
| + arg = 'const %(type)s %(name)s' |
| + arg = arg % { |
| + 'type': self._cpp_type_generator.getType(param), |
| + 'name': param.name |
| + } |
| + c.sblock('Value* %(classname)s::Result::Create(%(arg)s) {') |
| + sub = {'classname': classname, 'arg': arg} |
| + # TODO(calamity): Choices |
| + if not param: |
| + c.append('return Value::CreateNullValue();') |
| + else: |
| + sub['argname'] = param.name |
| + if param.type_.is_fundamental: |
| + c.append('return %s;' % |
| + cpp_util.createFundamentalValue(param, param.name)) |
| + elif param.type_ == PropertyType.REF: |
| + c.append('DictionaryValue* result = new DictionaryValue();') |
| + c.append('result->SetWithoutPathExpansion("%(argname)s",' |
| + '%(argname)s.ToValue());') |
| + c.append('return result;') |
| + elif param.type_ == PropertyType.OBJECT: |
| + raise NotImplementedError('Objects not implemented') |
| + elif param.type_ == PropertyType.ARRAY: |
| + raise NotImplementedError('Arrays not implemented') |
| + else: |
| + raise NotImplementedError(param.type_) |
| + c.substitute(sub) |
| + c.eblock('}') |
| + return c |